Commit 2bf8565a authored by Darwin Huang's avatar Darwin Huang Committed by Commit Bot

Clipboard API: Consistently return on detached contexts.

Consistently fail on detached contexts. Previously, a detached context
could allow for a nullptr deref, and a renderer crash.

Consistently return nullptr for navigator.clipboard, and ensure that
a nullptr navigator.clipboard function will terminate immediately in a
consistent manner.

Also, add tests to ensure that regressions don't occur in the future.

Bug: 10285919, 1034023
Change-Id: I67f8a76dc2901e7447049ff99e0b27ea6afeba63
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2057817
Commit-Queue: Darwin Huang <huangdarwin@chromium.org>
Reviewed-by: default avatarVictor Costan <pwnall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#742941}
parent e30f61ea
...@@ -44,6 +44,8 @@ using mojom::blink::PermissionService; ...@@ -44,6 +44,8 @@ using mojom::blink::PermissionService;
// static // static
ScriptPromise ClipboardPromise::CreateForRead(ExecutionContext* context, ScriptPromise ClipboardPromise::CreateForRead(ExecutionContext* context,
ScriptState* script_state) { ScriptState* script_state) {
if (!script_state->ContextIsValid())
return ScriptPromise();
ClipboardPromise* clipboard_promise = ClipboardPromise* clipboard_promise =
MakeGarbageCollected<ClipboardPromise>(context, script_state); MakeGarbageCollected<ClipboardPromise>(context, script_state);
clipboard_promise->GetTaskRunner()->PostTask( clipboard_promise->GetTaskRunner()->PostTask(
...@@ -55,6 +57,8 @@ ScriptPromise ClipboardPromise::CreateForRead(ExecutionContext* context, ...@@ -55,6 +57,8 @@ ScriptPromise ClipboardPromise::CreateForRead(ExecutionContext* context,
// static // static
ScriptPromise ClipboardPromise::CreateForReadText(ExecutionContext* context, ScriptPromise ClipboardPromise::CreateForReadText(ExecutionContext* context,
ScriptState* script_state) { ScriptState* script_state) {
if (!script_state->ContextIsValid())
return ScriptPromise();
ClipboardPromise* clipboard_promise = ClipboardPromise* clipboard_promise =
MakeGarbageCollected<ClipboardPromise>(context, script_state); MakeGarbageCollected<ClipboardPromise>(context, script_state);
clipboard_promise->GetTaskRunner()->PostTask( clipboard_promise->GetTaskRunner()->PostTask(
...@@ -68,6 +72,8 @@ ScriptPromise ClipboardPromise::CreateForWrite( ...@@ -68,6 +72,8 @@ ScriptPromise ClipboardPromise::CreateForWrite(
ExecutionContext* context, ExecutionContext* context,
ScriptState* script_state, ScriptState* script_state,
const HeapVector<Member<ClipboardItem>>& items) { const HeapVector<Member<ClipboardItem>>& items) {
if (!script_state->ContextIsValid())
return ScriptPromise();
ClipboardPromise* clipboard_promise = ClipboardPromise* clipboard_promise =
MakeGarbageCollected<ClipboardPromise>(context, script_state); MakeGarbageCollected<ClipboardPromise>(context, script_state);
HeapVector<Member<ClipboardItem>>* items_copy = HeapVector<Member<ClipboardItem>>* items_copy =
...@@ -83,6 +89,8 @@ ScriptPromise ClipboardPromise::CreateForWrite( ...@@ -83,6 +89,8 @@ ScriptPromise ClipboardPromise::CreateForWrite(
ScriptPromise ClipboardPromise::CreateForWriteText(ExecutionContext* context, ScriptPromise ClipboardPromise::CreateForWriteText(ExecutionContext* context,
ScriptState* script_state, ScriptState* script_state,
const String& data) { const String& data) {
if (!script_state->ContextIsValid())
return ScriptPromise();
ClipboardPromise* clipboard_promise = ClipboardPromise* clipboard_promise =
MakeGarbageCollected<ClipboardPromise>(context, script_state); MakeGarbageCollected<ClipboardPromise>(context, script_state);
clipboard_promise->GetTaskRunner()->PostTask( clipboard_promise->GetTaskRunner()->PostTask(
...@@ -110,6 +118,8 @@ void ClipboardPromise::CompleteWriteRepresentation() { ...@@ -110,6 +118,8 @@ void ClipboardPromise::CompleteWriteRepresentation() {
void ClipboardPromise::StartWriteRepresentation() { void ClipboardPromise::StartWriteRepresentation() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!GetExecutionContext())
return;
LocalFrame* local_frame = GetLocalFrame(); LocalFrame* local_frame = GetLocalFrame();
// Commit to system clipboard when all representations are written. // Commit to system clipboard when all representations are written.
// This is in the start flow so that a |clipboard_item_data_| with 0 items // This is in the start flow so that a |clipboard_item_data_| with 0 items
...@@ -141,6 +151,8 @@ void ClipboardPromise::StartWriteRepresentation() { ...@@ -141,6 +151,8 @@ void ClipboardPromise::StartWriteRepresentation() {
void ClipboardPromise::RejectFromReadOrDecodeFailure() { void ClipboardPromise::RejectFromReadOrDecodeFailure() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!GetExecutionContext())
return;
script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kDataError, DOMExceptionCode::kDataError,
"Failed to read or decode Blob for clipboard item type " + "Failed to read or decode Blob for clipboard item type " +
...@@ -165,6 +177,8 @@ void ClipboardPromise::HandleWrite( ...@@ -165,6 +177,8 @@ void ClipboardPromise::HandleWrite(
HeapVector<Member<ClipboardItem>>* clipboard_items) { HeapVector<Member<ClipboardItem>>* clipboard_items) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(clipboard_items); DCHECK(clipboard_items);
if (!GetExecutionContext())
return;
if (clipboard_items->size() > 1) { if (clipboard_items->size() > 1) {
script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>(
...@@ -200,6 +214,8 @@ void ClipboardPromise::HandleWriteText(const String& data) { ...@@ -200,6 +214,8 @@ void ClipboardPromise::HandleWriteText(const String& data) {
void ClipboardPromise::HandleReadWithPermission(PermissionStatus status) { void ClipboardPromise::HandleReadWithPermission(PermissionStatus status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!GetExecutionContext())
return;
if (status != PermissionStatus::GRANTED) { if (status != PermissionStatus::GRANTED) {
script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotAllowedError, "Read permission denied.")); DOMExceptionCode::kNotAllowedError, "Read permission denied."));
...@@ -232,6 +248,9 @@ void ClipboardPromise::HandleReadWithPermission(PermissionStatus status) { ...@@ -232,6 +248,9 @@ void ClipboardPromise::HandleReadWithPermission(PermissionStatus status) {
} }
void ClipboardPromise::HandleReadTextWithPermission(PermissionStatus status) { void ClipboardPromise::HandleReadTextWithPermission(PermissionStatus status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!GetExecutionContext())
return;
if (status != PermissionStatus::GRANTED) { if (status != PermissionStatus::GRANTED) {
script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotAllowedError, "Read permission denied.")); DOMExceptionCode::kNotAllowedError, "Read permission denied."));
...@@ -245,6 +264,8 @@ void ClipboardPromise::HandleReadTextWithPermission(PermissionStatus status) { ...@@ -245,6 +264,8 @@ void ClipboardPromise::HandleReadTextWithPermission(PermissionStatus status) {
void ClipboardPromise::HandleWriteWithPermission(PermissionStatus status) { void ClipboardPromise::HandleWriteWithPermission(PermissionStatus status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!GetExecutionContext())
return;
if (status != PermissionStatus::GRANTED) { if (status != PermissionStatus::GRANTED) {
script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotAllowedError, "Write permission denied.")); DOMExceptionCode::kNotAllowedError, "Write permission denied."));
...@@ -279,6 +300,8 @@ void ClipboardPromise::HandleWriteWithPermission(PermissionStatus status) { ...@@ -279,6 +300,8 @@ void ClipboardPromise::HandleWriteWithPermission(PermissionStatus status) {
void ClipboardPromise::HandleWriteTextWithPermission(PermissionStatus status) { void ClipboardPromise::HandleWriteTextWithPermission(PermissionStatus status) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!GetExecutionContext())
return;
if (status != PermissionStatus::GRANTED) { if (status != PermissionStatus::GRANTED) {
script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>( script_promise_resolver_->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNotAllowedError, "Write permission denied.")); DOMExceptionCode::kNotAllowedError, "Write permission denied."));
...@@ -311,7 +334,8 @@ void ClipboardPromise::RequestPermission( ...@@ -311,7 +334,8 @@ void ClipboardPromise::RequestPermission(
permission == mojom::blink::PermissionName::CLIPBOARD_WRITE); permission == mojom::blink::PermissionName::CLIPBOARD_WRITE);
ExecutionContext* context = ExecutionContext::From(script_state_); ExecutionContext* context = ExecutionContext::From(script_state_);
DCHECK(context); if (!context)
return;
const Document& document = *Document::From(context); const Document& document = *Document::From(context);
DCHECK(document.IsSecureContext()); // [SecureContext] in IDL DCHECK(document.IsSecureContext()); // [SecureContext] in IDL
......
...@@ -22,6 +22,9 @@ Clipboard* NavigatorClipboard::clipboard(ScriptState* script_state, ...@@ -22,6 +22,9 @@ Clipboard* NavigatorClipboard::clipboard(ScriptState* script_state,
ProvideTo(navigator, supplement); ProvideTo(navigator, supplement);
} }
if (!supplement->GetSupplementable()->GetFrame())
return nullptr;
return supplement->clipboard_; return supplement->clipboard_;
} }
......
...@@ -1421,6 +1421,10 @@ external/wpt/clipboard-apis/async-navigator-clipboard-basics.https.html [ Skip ] ...@@ -1421,6 +1421,10 @@ external/wpt/clipboard-apis/async-navigator-clipboard-basics.https.html [ Skip ]
external/wpt/clipboard-apis/async-raw-write-read.tentative.https.html [ Skip ] external/wpt/clipboard-apis/async-raw-write-read.tentative.https.html [ Skip ]
external/wpt/clipboard-apis/async-write-blobs-read-blobs-manual.https.html [ Skip ] external/wpt/clipboard-apis/async-write-blobs-read-blobs-manual.https.html [ Skip ]
external/wpt/clipboard-apis/async-write-image-read-image-manual.https.html [ Skip ] external/wpt/clipboard-apis/async-write-image-read-image-manual.https.html [ Skip ]
external/wpt/clipboard-apis/detached-iframe/read-on-detaching-iframe-manual.https.html [ Skip ]
external/wpt/clipboard-apis/detached-iframe/write-on-detaching-iframe-manual.https.html [ Skip ]
external/wpt/clipboard-apis/detached-iframe/write-read-on-detached-iframe-manual.https.html [ Skip ]
external/wpt/clipboard-apis/detached-iframe/writeText-readText-on-detached-iframe-manual.https.html [ Skip ]
external/wpt/clipboard-apis/events/copy-event-manual.html [ Skip ] external/wpt/clipboard-apis/events/copy-event-manual.html [ Skip ]
external/wpt/clipboard-apis/events/cut-event-manual.html [ Skip ] external/wpt/clipboard-apis/events/cut-event-manual.html [ Skip ]
external/wpt/clipboard-apis/events/paste-event-manual.html [ Skip ] external/wpt/clipboard-apis/events/paste-event-manual.html [ Skip ]
......
<!DOCTYPE html>
<meta charset=utf-8>
<title>navigator.clipboard read on detaching iframe</title>
<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../http/tests/resources/permissions-helper.js"></script>
<iframe id="iframe"></iframe>
<script>
'use strict';
promise_test(async t => {
await PermissionsHelper.setPermission('clipboard-read-write', 'granted');
await PermissionsHelper.setPermission('clipboard-sanitized-write',
'granted');
// Note: This tests proper behavior on a detaching iframe. text/plain is
// chosen for simplicity, and the test should fail the same way no matter what
// the input type is.
const iframe = document.getElementById('iframe');
const iframeClipboard = iframe.contentWindow.navigator.clipboard;
const blobInput = new Blob(["test string"], {type: 'text/plain'});
const clipboardItemInput = new ClipboardItem({'text/plain': blobInput});
// Clipboard API must only be available in focused documents.
// reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
iframe.focus();
// An iframe detaching while writing to the clipboard should fail, but not
// crash. Note the lack of await here, meaning that the iframe will detach
// while the read operation is running.
iframeClipboard.read([clipboardItemInput]);
iframe.parentNode.removeChild(iframe);
}, "Verify read fails on detaching iframe");
</script>
<!DOCTYPE html>
<meta charset=utf-8>
<title>navigator.clipboard write on detaching iframe</title>
<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../http/tests/resources/permissions-helper.js"></script>
<iframe id="iframe"></iframe>
<script>
'use strict';
promise_test(async t => {
await PermissionsHelper.setPermission('clipboard-read-write', 'granted');
await PermissionsHelper.setPermission('clipboard-sanitized-write',
'granted');
// Note: This tests proper behavior on a detaching iframe. text/plain is
// chosen for simplicity, and the test should fail the same way no matter what
// the input type is.
const iframe = document.getElementById('iframe');
const iframeClipboard = iframe.contentWindow.navigator.clipboard;
const blobInput = new Blob(["test string"], {type: 'text/plain'});
const clipboardItemInput = new ClipboardItem({'text/plain': blobInput});
// Clipboard API must only be available in focused documents.
// reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
iframe.focus();
// An iframe detaching while writing to the clipboard should fail, but not
// crash. Note the lack of await here, meaning that the iframe will detach
// while the write operation is running.
iframeClipboard.write([clipboardItemInput]);
iframe.parentNode.removeChild(iframe);
}, "Verify write fails on detaching iframe");
</script>
<!DOCTYPE html>
<meta charset=utf-8>
<title>navigator.clipboard read and write on detached iframe</title>
<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../http/tests/resources/permissions-helper.js"></script>
<iframe id="iframe"></iframe>
<script>
'use strict';
promise_test(async t => {
await PermissionsHelper.setPermission('clipboard-read-write', 'granted');
await PermissionsHelper.setPermission('clipboard-sanitized-write',
'granted');
// Note: This tests proper detached iframe behavior. text/plain is chosen for
// simplicity, and the test should fail the same way no matter what the input
// type is.
const iframe = document.getElementById('iframe');
const iframeClipboard = iframe.contentWindow.navigator.clipboard;
const blobInput = new Blob(["test string"], {type: 'text/plain'});
const clipboardItemInput = new ClipboardItem({'text/plain': blobInput});
// Clipboard API must only be available in focused documents.
// reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
iframe.focus();
// Writing and reading should succeed on same-origin iframes.
await iframeClipboard.write([clipboardItemInput]);
const readResultAttached = await iframeClipboard.read();
assert_not_equals(readResultAttached, undefined);
assert_equals(readResultAttached.length, 1,
"attached iframes should be able to read and write normally");
iframe.parentNode.removeChild(iframe);
// Writing onto a detached iframe's clipboard should fail, but not crash.
await iframeClipboard.write([clipboardItemInput]);
const readResultDetached = await iframeClipboard.read();
assert_equals(readResultDetached, undefined,
"reading from detached iframes should output undefined");
}, "Verify read and write fail on detached iframe");
</script>
<!DOCTYPE html>
<meta charset=utf-8>
<title>navigator.clipboard readText and writeText on detached iframe</title>
<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../../../http/tests/resources/permissions-helper.js"></script>
<iframe id="iframe"></iframe>
<script>
'use strict';
promise_test(async t => {
await PermissionsHelper.setPermission('clipboard-read-write', 'granted');
await PermissionsHelper.setPermission('clipboard-sanitized-write',
'granted');
const iframe = document.getElementById('iframe');
// Clipboard API must only be available in focused documents.
// reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
iframe.focus();
const iframeClipboard = iframe.contentWindow.navigator.clipboard;
// Writing and reading should succeed on same-origin iframes.
const attachedWriteText = "attached write text"
await iframeClipboard.writeText(attachedWriteText);
const attachedWriteResult = await iframeClipboard.readText();
assert_equals(attachedWriteResult, attachedWriteText,
"attached iframes should be able to readText and writeText normally");
iframe.parentNode.removeChild(iframe);
// Writing onto a detached iframe's clipboard should fail, but not crash.
const detachedWriteText = "detached write text";
await iframeClipboard.writeText(detachedWriteText);
const readResultDetached = await iframeClipboard.readText();
assert_equals(readResultDetached, undefined,
"reading from detached iframes should output undefined");
}, "Verify readText and writeText fails on detached iframe");
</script>
<!DOCTYPE html>
<meta charset=utf-8>
<title>Clipboard API on detached iframe</title>
<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="iframe"></iframe>
<script>
'use strict';
promise_test(async () => {
const iframe = document.getElementById('iframe');
const iframeNavigator = iframe.contentWindow.navigator;
assert_not_equals(navigator.clipboard, null,
"parent frame's clipboard should exist with iframe attached");
assert_not_equals(iframeNavigator.clipboard, null,
"attached child iframe's clipboard should exist");
iframe.parentNode.removeChild(iframe);
assert_not_equals(navigator.clipboard, null,
"parent frame's clipboard should exist with iframe detached");
assert_equals(iframeNavigator.clipboard, null,
"detached child iframe's clipboard should not exist");
}, 'Verify navigator.clipboard attached only on attached frames');
</script>
\ No newline at end of file
<!DOCTYPE html>
<meta charset=utf-8>
<title>navigator.clipboard read on detaching iframe</title>
<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="iframe"></iframe>
<script>
'use strict';
promise_test(async t => {
// Note: This tests proper behavior on a detaching iframe. text/plain is
// chosen for simplicity, and the test should fail the same way no matter what
// the input type is.
const iframe = document.getElementById('iframe');
const iframeClipboard = iframe.contentWindow.navigator.clipboard;
const blobInput = new Blob(["test string"], {type: 'text/plain'});
const clipboardItemInput = new ClipboardItem({'text/plain': blobInput});
// Clipboard API must only be available in focused documents.
// reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
iframe.focus();
// An iframe detaching while writing to the clipboard should fail, but not
// crash. Note the lack of await here, meaning that the iframe will detach
// while the read operation is running.
iframeClipboard.read([clipboardItemInput]);
iframe.parentNode.removeChild(iframe);
}, "Verify read fails on detaching iframe");
</script>
<p>
Note: This is a manual test because it writes/reads to the shared system
clipboard and thus cannot be run async with other tests that might interact
with the clipboard.
</p>
<!DOCTYPE html>
<meta charset=utf-8>
<title>navigator.clipboard write on detaching iframe</title>
<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="iframe"></iframe>
<script>
'use strict';
promise_test(async t => {
// Note: This tests proper behavior on a detaching iframe. text/plain is
// chosen for simplicity, and the test should fail the same way no matter what
// the input type is.
const iframe = document.getElementById('iframe');
const iframeClipboard = iframe.contentWindow.navigator.clipboard;
const blobInput = new Blob(["test string"], {type: 'text/plain'});
const clipboardItemInput = new ClipboardItem({'text/plain': blobInput});
// Clipboard API must only be available in focused documents.
// reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
iframe.focus();
// An iframe detaching while writing to the clipboard should fail, but not
// crash. Note the lack of await here, meaning that the iframe will detach
// while the write operation is running.
iframeClipboard.write([clipboardItemInput]);
iframe.parentNode.removeChild(iframe);
}, "Verify write fails on detaching iframe");
</script>
<p>
Note: This is a manual test because it writes/reads to the shared system
clipboard and thus cannot be run async with other tests that might interact
with the clipboard.
</p>
<!DOCTYPE html>
<meta charset=utf-8>
<title>navigator.clipboard read and write on detached iframe</title>
<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="iframe"></iframe>
<script>
'use strict';
promise_test(async t => {
// Note: This tests proper detached iframe behavior. text/plain is chosen for
// simplicity, and the test should fail the same way no matter what the input
// type is.
const iframe = document.getElementById('iframe');
const iframeClipboard = iframe.contentWindow.navigator.clipboard;
const blobInput = new Blob(["test string"], {type: 'text/plain'});
const clipboardItemInput = new ClipboardItem({'text/plain': blobInput});
// Clipboard API must only be available in focused documents.
// reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
iframe.focus();
// Writing and reading should succeed on same-origin iframes.
await iframeClipboard.write([clipboardItemInput]);
const readResultAttached = await iframeClipboard.read();
assert_not_equals(readResultAttached, undefined);
assert_equals(readResultAttached.length, 1,
"attached iframes should be able to read and write normally");
iframe.parentNode.removeChild(iframe);
// Writing onto a detached iframe's clipboard should fail, but not crash.
await iframeClipboard.write([clipboardItemInput]);
const readResultDetached = await iframeClipboard.read();
assert_equals(readResultDetached, undefined,
"reading from detached iframes should output undefined");
}, "Verify read and write fail on detached iframe");
</script>
<p>
Note: This is a manual test because it writes/reads to the shared system
clipboard and thus cannot be run async with other tests that might interact
with the clipboard.
</p>
<!DOCTYPE html>
<meta charset=utf-8>
<title>navigator.clipboard readText and writeText on detached iframe</title>
<link rel='help' href='https://w3c.github.io/clipboard-apis/#async-clipboard-api'>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<iframe id="iframe"></iframe>
<script>
'use strict';
promise_test(async t => {
const iframe = document.getElementById('iframe');
// Clipboard API must only be available in focused documents.
// reference: https://www.w3.org/TR/clipboard-apis/#privacy-async
iframe.focus();
const iframeClipboard = iframe.contentWindow.navigator.clipboard;
// Writing and reading should succeed on same-origin iframes.
const attachedWriteText = "attached write text"
await iframeClipboard.writeText(attachedWriteText);
const attachedWriteResult = await iframeClipboard.readText();
assert_equals(attachedWriteResult, attachedWriteText,
"attached iframes should be able to readText and writeText normally");
iframe.parentNode.removeChild(iframe);
// Writing onto a detached iframe's clipboard should fail, but not crash.
const detachedWriteText = "detached write text";
await iframeClipboard.writeText(detachedWriteText);
const readResultDetached = await iframeClipboard.readText();
assert_equals(readResultDetached, undefined,
"reading from detached iframes should output undefined");
}, "Verify readText and writeText fails on detached iframe");
</script>
<p>
Note: This is a manual test because it writes/reads to the shared system
clipboard and thus cannot be run async with other tests that might interact
with the clipboard.
</p>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment