diff --git a/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html b/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html
index 37e2e6a7bde3ef..bf32f4851b341c 100644
--- a/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html
+++ b/clipboard-apis/async-navigator-clipboard-change-event.tentative.https.html
@@ -25,48 +25,79 @@
});
}
+ let typesToSet_ = ["text/html", "web txt/csv"];
button.onclick = () => document.execCommand("copy");
document.oncopy = (ev) => {
ev.preventDefault();
- ev.clipboardData.setData("text/html", `
Test html
`);
+ for (let i = 0; i < typesToSet_.length; i++) {
+ const type = typesToSet_[i];
+ const data = new Blob([`Test data for ${type}`], {type: type});
+ ev.clipboardData.setData(type, data);
+ }
};
- function triggerCopyToClipboard() {
+ function triggerCopyToClipboard(typesToSet) {
+ if (typesToSet) {
+ typesToSet_ = typesToSet;
+ }
return test_driver.click(button);
}
promise_test(async (test) => {
let clipboardChangeEventCount = 0;
let eventType = "";
+ let capturedEventTypes = null;
navigator.clipboard.addEventListener("clipboardchange", (ev) => {
clipboardChangeEventCount++;
eventType = ev.type;
+ capturedEventTypes = ev.types;
});
await triggerCopyToClipboard();
assert_equals(clipboardChangeEventCount, 1, "clipboardchange event should be called exactly once");
assert_equals(eventType, "clipboardchange", "Event type should be 'clipboardchange'");
+ assert_true(capturedEventTypes.includes("text/html"), "types should contain 'text/html'");
+ assert_false(capturedEventTypes.includes("web txt/csv"), "types should not contain custom MIME type");
}, "clipboardchange event is invoked");
promise_test(async (test) => {
await tryGrantWritePermission();
let clipboardChangeEventCount = 0;
+ let capturedEventTypes = null;
navigator.clipboard.addEventListener("clipboardchange", (ev) => {
clipboardChangeEventCount++;
+ capturedEventTypes = ev.types;
});
await navigator.clipboard.writeText("Test text");
await waitForRender();
assert_equals(clipboardChangeEventCount, 1, "clipboardchange event should be called exactly once");
+ assert_true(capturedEventTypes.includes("text/plain"), "types should contain 'text/plain'");
}, "clipboardchange event is invoked with async clipboard API");
promise_test(async (test) => {
let onClipboardChangeAttributeCount = 0;
- navigator.clipboard.onclipboardchange = () => {
+ let capturedEventTypes = null;
+ navigator.clipboard.onclipboardchange = (ev) => {
onClipboardChangeAttributeCount++;
+ capturedEventTypes = ev.types;
};
await triggerCopyToClipboard();
assert_equals(onClipboardChangeAttributeCount, 1, "onclipboardchange attribute should be called exactly once");
+ assert_true(capturedEventTypes.includes("text/html"), "types should contain 'text/html'");
+ assert_false(capturedEventTypes.includes("web txt/csv"), "types should not contain custom MIME type");
}, "clipboardchange event is invoked using onclipboardchange attribute");
+ promise_test(async (test) => {
+ let onClipboardChangeAttributeCount = 0;
+ let capturedEventTypes = null;
+ navigator.clipboard.onclipboardchange = (ev) => {
+ onClipboardChangeAttributeCount++;
+ capturedEventTypes = ev.types;
+ };
+ await triggerCopyToClipboard(["web txt/csv"]);
+ assert_equals(onClipboardChangeAttributeCount, 1, "onclipboardchange attribute should be called exactly once");
+ assert_equals(capturedEventTypes.length, 0, "clipboardchange event should have no types");
+ }, "clipboardchange event is invoked even when only custom MIME types are set");
+
promise_test(async (test) => {
let listenerCallCount = 0;
function clipboardChangeListener() {
@@ -89,6 +120,55 @@
assert_equals(listenerCallCount, 2, "Event listener should be called exactly once after re-adding");
}, "clipboardchange event listener behavior when adding, removing, and re-adding");
+ promise_test(async (test) => {
+ // https://w3c.github.io/clipboard-apis/#mandatory-data-types-x
+ const standardTypes = [
+ "text/plain",
+ "text/html",
+ "image/png",
+ ];
+ const unsupportedTypes = [
+ "web application/custom",
+ "web web/proprietary",
+ "web x-custom/type",
+ "txt/json",
+ "text/rtf",
+ "image/svg+xml",
+ "text/uri-list",
+ ];
+ const allTypesToSet = [...standardTypes, ...unsupportedTypes];
+
+ let clipboardChangeEventCount = 0;
+ let capturedEventTypes = null;
+
+ navigator.clipboard.addEventListener("clipboardchange", (ev) => {
+ clipboardChangeEventCount++;
+ capturedEventTypes = ev.types;
+ });
+
+ await triggerCopyToClipboard(allTypesToSet);
+
+ assert_true(clipboardChangeEventCount == 1, "clipboardchange event should be invoked once");
+
+ // Check that types is a frozen array
+ assert_true(Array.isArray(capturedEventTypes), "types should be an array");
+ assert_true(Object.isFrozen(capturedEventTypes), "types should be frozen");
+
+ // Verify all standard types are included
+ for (const type of standardTypes) {
+ assert_true(capturedEventTypes.includes(type), `types should contain standard MIME type '${type}'`);
+ }
+
+ // Verify custom types are filtered out
+ for (const type of unsupportedTypes) {
+ assert_false(capturedEventTypes.includes(type), `types should not contain custom MIME type '${type}'`);
+ }
+
+ // Verify we have exactly the standard types and nothing else
+ assert_equals(capturedEventTypes.length, standardTypes.length,
+ "clipboardchange event types should contain exactly the standard MIME types");
+ }, "clipboardchange event exposes all standard MIME types and filters non-standard ones");
+
promise_test(async (test) => {
// Focus the document and acquire permission to write to the clipboard
await test_driver.click(document.body);
@@ -97,6 +177,7 @@
const iframe = document.getElementById('iframe');
let frameEventCount = 0;
+ let capturedEventTypes = null;
let focusEventFired = false;
iframe.contentWindow.addEventListener("focus", () => {
focusEventFired = true;
@@ -106,6 +187,7 @@
iframe.contentWindow.navigator.clipboard.addEventListener("clipboardchange", () => {
assert_true(focusEventFired, "focus event should fire before clipboardchange event");
frameEventCount++;
+ capturedEventTypes = event.types;
});
// Ensure iFrame doesn't have the focus
@@ -114,7 +196,13 @@
// Trigger multiple clipboard changes
await navigator.clipboard.writeText("Test text");
- await navigator.clipboard.writeText("Test text 2");
+
+ // Write HTML to clipboard to ensure the event captured only html and not txt
+ await navigator.clipboard.write([
+ new ClipboardItem({
+ "text/html": new Blob(["Test HTML
"], {type: "text/html"})
+ })
+ ]);
await waitForRender();
assert_equals(frameEventCount, 0, "iframe should not recieve any clipboardchange event yet");
@@ -122,6 +210,9 @@
iframe.focus();
assert_true(iframe.contentWindow.document.hasFocus(), "iFrame should have focus");
assert_equals(frameEventCount, 1, "iframe should receive event only 1 event after focus");
+ assert_equals(capturedEventTypes.length, 1, "clipboardchange event should only have one type");
+ assert_true(capturedEventTypes.includes("text/html"), "clipboardchange event should only have text/html type");
}, "clipboardchange event should only fire in the focused context");
+