commit 330f81172340b3a484f98fd9969ab3e1ad080e22
parent 3e8fc8711ca6686c64c15690e6b18bd98bf907fa
Author: Edgar Chen <echen@mozilla.com>
Date: Thu, 27 Nov 2025 19:37:06 +0000
Bug 2002020 - Make queryCommandEnabled() and queryCommandSupported() for clipboard comments returning proper result for Web Extension; r=webidl,smaug
Web Extension with clipboard permission have greater privileges to use clipboard
commands, queryCommandSupported() and queryCommandEnabled() should reflect that.
Differential Revision: https://phabricator.services.mozilla.com/D273904
Diffstat:
6 files changed, 52 insertions(+), 63 deletions(-)
diff --git a/dom/base/Document.cpp b/dom/base/Document.cpp
@@ -5988,13 +5988,16 @@ bool Document::QueryCommandEnabled(const nsAString& aHTMLCommandName,
break;
}
- // cut & copy are always allowed
+ // Report false for restricted commands
if (commandData.IsCutOrCopyCommand()) {
+ // XXX: should we report "disabled" when the target is not editable for cut
+ // command?
return nsContentUtils::IsCutCopyAllowed(this, aSubjectPrincipal);
}
- // Report false for restricted commands
- if (commandData.IsPasteCommand() && !aSubjectPrincipal.IsSystemPrincipal()) {
+ if (commandData.IsPasteCommand() &&
+ !nsContentUtils::PrincipalHasPermission(aSubjectPrincipal,
+ nsGkAtoms::clipboardRead)) {
return false;
}
@@ -6182,7 +6185,7 @@ bool Document::QueryCommandState(const nsAString& aHTMLCommandName,
}
bool Document::QueryCommandSupported(const nsAString& aHTMLCommandName,
- CallerType aCallerType, ErrorResult& aRv) {
+ nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
// Only allow on HTML documents.
if (!IsHTMLOrXHTML()) {
aRv.ThrowInvalidStateError(
@@ -6213,17 +6216,15 @@ bool Document::QueryCommandSupported(const nsAString& aHTMLCommandName,
// may also be disallowed to be called from non-privileged content.
// For that reason, we report the support status of corresponding
// command accordingly.
- if (aCallerType != CallerType::System) {
- if (commandData.IsPasteCommand()) {
- return false;
- }
- if (commandData.IsCutOrCopyCommand() &&
- !StaticPrefs::dom_allow_cut_copy()) {
- // XXXbz should we worry about correctly reporting "true" in the
- // "restricted, but we're an addon with clipboardWrite permissions" case?
- // See also nsContentUtils::IsCutCopyAllowed.
- return false;
- }
+ if (commandData.IsPasteCommand() &&
+ !nsContentUtils::PrincipalHasPermission(aSubjectPrincipal,
+ nsGkAtoms::clipboardRead)) {
+ return false;
+ }
+ if (commandData.IsCutOrCopyCommand() && !StaticPrefs::dom_allow_cut_copy() &&
+ !nsContentUtils::PrincipalHasPermission(aSubjectPrincipal,
+ nsGkAtoms::clipboardWrite)) {
+ return false;
}
// aHTMLCommandName is supported if it can be converted to a Midas command
diff --git a/dom/base/Document.h b/dom/base/Document.h
@@ -3549,7 +3549,7 @@ class Document : public nsINode,
MOZ_CAN_RUN_SCRIPT bool QueryCommandState(const nsAString& aHTMLCommandName,
mozilla::ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT bool QueryCommandSupported(
- const nsAString& aHTMLCommandName, mozilla::dom::CallerType aCallerType,
+ const nsAString& aHTMLCommandName, nsIPrincipal& aSubjectPrincipal,
mozilla::ErrorResult& aRv);
MOZ_CAN_RUN_SCRIPT void QueryCommandValue(const nsAString& aHTMLCommandName,
nsAString& aValue,
diff --git a/dom/events/test/clipboard/browser_document_command_copy.js b/dom/events/test/clipboard/browser_document_command_copy.js
@@ -165,11 +165,9 @@ const kContentFileUrl = kBaseUrlForContent + "simple_page_ext.html";
await BrowserTestUtils.withNewTab(kContentFileUrl, async browser => {
let [supported, enabled, succeed] =
await extension.awaitMessage("ready");
- // XXX: should "copy" command be supported if extension has
- // clipboardWrite permission?
is(
supported,
- aPrefValue,
+ aPrefValue || aPermission,
"Check if the 'copy' command is supported"
);
is(
@@ -246,11 +244,9 @@ const kContentFileUrl = kBaseUrlForContent + "simple_page_ext.html";
await BrowserTestUtils.withNewTab(kContentFileUrl, async browser => {
let [supported, enabled, succeed] =
await extension.awaitMessage("ready");
- // XXX: should "copy" command be supported if extension has
- // clipboardWrite permission?
is(
supported,
- aPrefValue,
+ aPrefValue || aPermission,
"Check if the 'copy' command is supported"
);
is(
@@ -301,11 +297,9 @@ const kContentFileUrl = kBaseUrlForContent + "simple_page_ext.html";
await BrowserTestUtils.withNewTab(kContentFileUrl, async browser => {
let [supported, enabled, succeed] =
await extension.awaitMessage("ready");
- // XXX: should "copy" command be supported if extension has
- // clipboardWrite permission.
is(
supported,
- aPrefValue,
+ aPrefValue || aPermission,
"Check if the 'copy' command is supported"
);
is(enabled, aPermission, "Check if the 'copy' command is enabled");
diff --git a/dom/events/test/clipboard/browser_document_command_cut.js b/dom/events/test/clipboard/browser_document_command_cut.js
@@ -172,11 +172,9 @@ const kContentFileUrl = kBaseUrlForContent + "simple_page_ext.html";
await BrowserTestUtils.withNewTab(kContentFileUrl, async browser => {
let [supported, enabled, succeed] =
await extension.awaitMessage("ready");
- // XXX: should "cut" command be supported if extension has
- // clipboardWrite permission?
is(
supported,
- aPrefValue,
+ aPrefValue || aPermission,
"Check if the 'cut' command is supported"
);
@@ -252,11 +250,9 @@ const kContentFileUrl = kBaseUrlForContent + "simple_page_ext.html";
await BrowserTestUtils.withNewTab(kContentFileUrl, async browser => {
let [supported, enabled, succeed] =
await extension.awaitMessage("ready");
- // XXX: should "cut" command be supported if extension has
- // clipboardWrite permission?
is(
supported,
- aPrefValue,
+ aPrefValue || aPermission,
"Check if the 'cut' command is supported"
);
@@ -309,11 +305,9 @@ const kContentFileUrl = kBaseUrlForContent + "simple_page_ext.html";
await BrowserTestUtils.withNewTab(kContentFileUrl, async browser => {
let [supported, enabled, succeed] =
await extension.awaitMessage("ready");
- // XXX: should "cut" command be supported if extension has
- // clipboardWrite permission.
is(
supported,
- aPrefValue,
+ aPrefValue || aPermission,
"Check if the 'cut' command is supported"
);
// XXX: should "cut" command be disabled without an editing target?
diff --git a/dom/events/test/clipboard/browser_document_command_paste.js b/dom/events/test/clipboard/browser_document_command_paste.js
@@ -159,13 +159,14 @@ describe("test paste comment", () => {
await BrowserTestUtils.withNewTab(kContentFileUrl, async browser => {
let [supported, enabled, succeed] =
await extension.awaitMessage("ready");
- // XXX: should "paste" command be supported if extension has
- // clipboardRead permission?
- ok(!supported, "Check if the 'paste' command is supported");
- // XXX: should "paste" command be enabled if extension has
- // clipboardRead permission?
- ok(
- !enabled,
+ is(
+ supported,
+ aPermission,
+ "Check if the 'paste' command is supported"
+ );
+ is(
+ enabled,
+ aPermission,
"Check if the 'paste' command is enabled without user activation"
);
is(
@@ -178,10 +179,9 @@ describe("test paste comment", () => {
promiseClickContentElement(browser, "btn");
[supported, enabled, succeed] =
await extension.awaitMessage("result");
- // XXX: should "paste" command be enabled if extension has
- // clipboardRead permission?
- ok(
- !enabled,
+ is(
+ enabled,
+ aPermission,
"Check if the 'paste' command is enabled with user activation"
);
is(
@@ -235,13 +235,14 @@ describe("test paste comment", () => {
await BrowserTestUtils.withNewTab(kContentFileUrl, async browser => {
let [supported, enabled, succeed] =
await extension.awaitMessage("ready");
- // XXX: should "paste" command be supported if extension has
- // clipboardRead permission?
- ok(!supported, "Check if the 'paste' command is supported");
- // XXX: should "paste" command be enabled if extension has
- // clipboardRead permission?
- ok(
- !enabled,
+ is(
+ supported,
+ aPermission,
+ "Check if the 'paste' command is supported"
+ );
+ is(
+ enabled,
+ aPermission,
"Check if the 'paste' command is enabled without user activation"
);
is(
@@ -254,10 +255,9 @@ describe("test paste comment", () => {
promiseClickContentElement(browser, "btn");
[supported, enabled, succeed] =
await extension.awaitMessage("result");
- // XXX: should "paste" command be enabled if extension has
- // clipboardRead permission?
- ok(
- !enabled,
+ is(
+ enabled,
+ aPermission,
"Check if the 'paste' command is enabled with user activation"
);
is(
@@ -288,12 +288,12 @@ describe("test paste comment", () => {
await BrowserTestUtils.withNewTab(kContentFileUrl, async browser => {
let [supported, enabled, succeed] =
await extension.awaitMessage("ready");
- // XXX: should "paste" command be supported if extension has
- // clipboardRead permission?
- ok(!supported, "Check if the 'paste' command is supported");
- // XXX: should "paste" command be enabled if extension has
- // clipboardRead permission?
- ok(!enabled, "Check if the 'paste' command is enabled");
+ is(
+ supported,
+ aPermission,
+ "Check if the 'paste' command is supported"
+ );
+ is(enabled, aPermission, "Check if the 'paste' command is enabled");
is(succeed, aPermission, "Check if the 'paste' command is succeed");
});
await extension.unload();
diff --git a/dom/webidl/Document.webidl b/dom/webidl/Document.webidl
@@ -176,7 +176,7 @@ partial interface Document {
boolean queryCommandIndeterm(DOMString commandId);
[Throws]
boolean queryCommandState(DOMString commandId);
- [Throws, NeedsCallerType]
+ [Throws, NeedsSubjectPrincipal]
boolean queryCommandSupported(DOMString commandId);
[Throws]
DOMString queryCommandValue(DOMString commandId);