Bug 1558674 - Allow chrome privileged documents to enable/disable copy/paste. r=Ehsan,bgrins
authorBrendan Dahl <bdahl@mozilla.com>
Tue, 18 Jun 2019 20:40:19 +0000
changeset 479126 a107e0c09120150c2733f1d40200fb43066c8e7a
parent 479125 bd82743a3e4e570eec6f014d8c5455c080614614
child 479127 f9c7f4ab3500eca465acd2414dba68268fe6324c
push id36170
push usercbrindusan@mozilla.com
push dateWed, 19 Jun 2019 03:56:45 +0000
treeherdermozilla-central@5f0f37756053 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersEhsan, bgrins
bugs1558674
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1558674 - Allow chrome privileged documents to enable/disable copy/paste. r=Ehsan,bgrins For content HTML/XHTML copy/paste should always be enabled, but for chrome docs we can support enabling/disabling copy/paste. Also, restores tests to how they were before copy/paste was always enabled. Differential Revision: https://phabricator.services.mozilla.com/D34805
devtools/client/framework/test/browser_toolbox_textbox_context_menu.js
devtools/client/inspector/computed/test/browser_computed_search-filter_context-menu.js
devtools/client/inspector/rules/test/browser_rules_search-filter_context-menu.js
devtools/client/inspector/test/browser_inspector_search-filter_context-menu.js
devtools/client/scratchpad/test/browser_scratchpad_edit_ui_updates.js
devtools/client/webconsole/test/mochitest/browser_console_context_menu_entries.js
dom/base/nsGlobalWindowCommands.cpp
editor/libeditor/HTMLEditorDataTransfer.cpp
editor/libeditor/TextEditor.cpp
editor/libeditor/TextEditorDataTransfer.cpp
--- a/devtools/client/framework/test/browser_toolbox_textbox_context_menu.js
+++ b/devtools/client/framework/test/browser_toolbox_textbox_context_menu.js
@@ -41,22 +41,23 @@ add_task(async function checkMenuEntrySt
   const cmdSelectAll = textboxContextMenu.querySelector("#editmenu-selectAll");
   const cmdCut = textboxContextMenu.querySelector("#editmenu-cut");
   const cmdCopy = textboxContextMenu.querySelector("#editmenu-copy");
   const cmdPaste = textboxContextMenu.querySelector("#editmenu-paste");
 
   is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled");
   is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled");
   is(cmdSelectAll.getAttribute("disabled"), "true", "cmdSelectAll is disabled");
+  is(cmdCut.getAttribute("disabled"), "true", "cmdCut is disabled");
+  is(cmdCopy.getAttribute("disabled"), "true", "cmdCopy is disabled");
 
-  // Cut/Copy/Paste items are enabled in context menu even if there
-  // is no selection. See also Bug 1303033, and 1317322
-  is(cmdCut.getAttribute("disabled"), "", "cmdCut is enabled");
-  is(cmdCopy.getAttribute("disabled"), "", "cmdCopy is enabled");
-  is(cmdPaste.getAttribute("disabled"), "", "cmdPaste is enabled");
+  if (isWindows()) {
+    // emptyClipboard only works on Windows (666254), assert paste only for this OS.
+    is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled");
+  }
 
   const onContextMenuHidden = toolbox.once("menu-close");
   EventUtils.sendKey("ESCAPE", toolbox.win);
   await onContextMenuHidden;
 });
 
 add_task(async function automaticallyBindTexbox() {
   info("Registering a tool with an input field and making sure the context menu works");
--- a/devtools/client/inspector/computed/test/browser_computed_search-filter_context-menu.js
+++ b/devtools/client/inspector/computed/test/browser_computed_search-filter_context-menu.js
@@ -39,21 +39,22 @@ add_task(async function() {
   let cmdCut = searchContextMenu.querySelector("#editmenu-cut");
   let cmdCopy = searchContextMenu.querySelector("#editmenu-copy");
   let cmdPaste = searchContextMenu.querySelector("#editmenu-paste");
 
   is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled");
   is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled");
   is(cmdSelectAll.getAttribute("disabled"), "true", "cmdSelectAll is disabled");
 
-  // Cut/Copy/Paste items are enabled in context menu even if there is no
-  // selection. See also Bug 1303033, and 1317322
-  is(cmdCut.getAttribute("disabled"), "", "cmdCut is enabled");
-  is(cmdCopy.getAttribute("disabled"), "", "cmdCopy is enabled");
-  is(cmdPaste.getAttribute("disabled"), "", "cmdPaste is enabled");
+  is(cmdCut.getAttribute("disabled"), "true", "cmdCut is disabled");
+  is(cmdCopy.getAttribute("disabled"), "true", "cmdCopy is disabled");
+  if (isWindows()) {
+    // emptyClipboard only works on Windows (666254), assert paste only for this OS.
+    is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled");
+  }
 
   info("Closing context menu");
   let onContextMenuClose = toolbox.once("menu-close");
   EventUtils.sendKey("ESCAPE", toolbox.win);
   await onContextMenuClose;
 
   info("Copy text in search field using the context menu");
   searchField.setUserInput(TEST_INPUT);
--- a/devtools/client/inspector/rules/test/browser_rules_search-filter_context-menu.js
+++ b/devtools/client/inspector/rules/test/browser_rules_search-filter_context-menu.js
@@ -37,22 +37,23 @@ add_task(async function() {
   let cmdSelectAll = searchContextMenu.querySelector("#editmenu-selectAll");
   let cmdCut = searchContextMenu.querySelector("#editmenu-cut");
   let cmdCopy = searchContextMenu.querySelector("#editmenu-copy");
   let cmdPaste = searchContextMenu.querySelector("#editmenu-paste");
 
   is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled");
   is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled");
   is(cmdSelectAll.getAttribute("disabled"), "true", "cmdSelectAll is disabled");
+  is(cmdCut.getAttribute("disabled"), "true", "cmdCut is disabled");
+  is(cmdCopy.getAttribute("disabled"), "true", "cmdCopy is disabled");
 
-  // Cut/Copy/Paste items are enabled in context menu even if there is no
-  // selection. See also Bug 1303033, and 1317322
-  is(cmdCut.getAttribute("disabled"), "", "cmdCut is enabled");
-  is(cmdCopy.getAttribute("disabled"), "", "cmdCopy is enabled");
-  is(cmdPaste.getAttribute("disabled"), "", "cmdPaste is enabled");
+  if (isWindows()) {
+    // emptyClipboard only works on Windows (666254), assert paste only for this OS.
+    is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled");
+  }
 
   info("Closing context menu");
   let onContextMenuClose = toolbox.once("menu-close");
   EventUtils.sendKey("ESCAPE", toolbox.win);
   await onContextMenuClose;
 
   info("Copy text in search field using the context menu");
   searchField.setUserInput(TEST_INPUT);
--- a/devtools/client/inspector/test/browser_inspector_search-filter_context-menu.js
+++ b/devtools/client/inspector/test/browser_inspector_search-filter_context-menu.js
@@ -34,22 +34,23 @@ add_task(async function() {
   let cmdSelectAll = searchContextMenu.querySelector("#editmenu-selectAll");
   let cmdCut = searchContextMenu.querySelector("#editmenu-cut");
   let cmdCopy = searchContextMenu.querySelector("#editmenu-copy");
   let cmdPaste = searchContextMenu.querySelector("#editmenu-paste");
 
   is(cmdUndo.getAttribute("disabled"), "true", "cmdUndo is disabled");
   is(cmdDelete.getAttribute("disabled"), "true", "cmdDelete is disabled");
   is(cmdSelectAll.getAttribute("disabled"), "true", "cmdSelectAll is disabled");
+  is(cmdCut.getAttribute("disabled"), "true", "cmdCut is disabled");
+  is(cmdCopy.getAttribute("disabled"), "true", "cmdCopy is disabled");
 
-  // Cut/Copy items are enabled in context menu even if there
-  // is no selection. See also Bug 1303033, and 1317322
-  is(cmdCut.getAttribute("disabled"), "", "cmdCut is enabled");
-  is(cmdCopy.getAttribute("disabled"), "", "cmdCopy is enabled");
-  is(cmdPaste.getAttribute("disabled"), "", "cmdPaste is enabled");
+  if (isWindows()) {
+    // emptyClipboard only works on Windows (666254), assert paste only for this OS.
+    is(cmdPaste.getAttribute("disabled"), "true", "cmdPaste is disabled");
+  }
 
   info("Closing context menu");
   let onContextMenuClose = toolbox.once("menu-close");
   EventUtils.sendKey("ESCAPE", toolbox.win);
   await onContextMenuClose;
 
   info("Copy text in search field using the context menu");
   searchBox.setUserInput(TEST_INPUT);
--- a/devtools/client/scratchpad/test/browser_scratchpad_edit_ui_updates.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_edit_ui_updates.js
@@ -108,17 +108,17 @@ function runTests() {
         }
       } else {
         menuPopup.hidePopup();
       }
     });
   };
 
   const firstShow = function() {
-    ok(!cutItem.hasAttribute("disabled"), "cut menuitem is enabled");
+    ok(cutItem.hasAttribute("disabled"), "cut menuitem is disabled");
     closeMenu(firstHide);
   };
 
   const firstHide = function() {
     sp.editor.setSelection({ line: 0, ch: 0 }, { line: 0, ch: 10 });
     openMenu(11, 11, showAfterSelect);
   };
 
@@ -138,17 +138,17 @@ function runTests() {
   };
 
   const onCut = function() {
     sp.editor.off("change", onCut);
     openMenu(12, 12, showAfterCut);
   };
 
   const showAfterCut = function() {
-    ok(!cutItem.hasAttribute("disabled"), "cut menuitem is enabled after cut");
+    ok(cutItem.hasAttribute("disabled"), "cut menuitem is disabled after cut");
     ok(!pasteItem.hasAttribute("disabled"), "paste menuitem is enabled after cut");
     closeMenu(hideAfterCut);
   };
 
   const hideAfterCut = function() {
     waitForFocus(function() {
       sp.editor.on("change", onPaste);
       EventUtils.synthesizeKey("v", {accelKey: true}, gScratchpadWindow);
@@ -156,17 +156,17 @@ function runTests() {
   };
 
   const onPaste = function() {
     sp.editor.off("change", onPaste);
     openMenu(13, 13, showAfterPaste);
   };
 
   const showAfterPaste = function() {
-    ok(!cutItem.hasAttribute("disabled"), "cut menuitem is enabled after paste");
+    ok(cutItem.hasAttribute("disabled"), "cut menuitem is disabled after paste");
     ok(!pasteItem.hasAttribute("disabled"), "paste menuitem is enabled after paste");
     closeMenu(hideAfterPaste);
   };
 
   const hideAfterPaste = function() {
     if (pass == 0) {
       pass++;
       testContextMenu();
--- a/devtools/client/webconsole/test/mochitest/browser_console_context_menu_entries.js
+++ b/devtools/client/webconsole/test/mochitest/browser_console_context_menu_entries.js
@@ -67,41 +67,41 @@ async function performTests() {
     "#console-menu-select (A)",
     "#console-menu-export-clipboard ()",
   ]);
   is(getSimplifiedContextMenu(menuPopup).join("\n"), expectedContextMenu.join("\n"),
     "The context menu has the expected entries for a simple log message");
 
   menuPopup = await openContextMenu(hud, hud.jsterm.node || hud.jsterm.inputNode);
 
-  expectedContextMenu = [
-    "#editmenu-undo (editmenu-undo) [disabled]",
-    "#editmenu-cut (editmenu-cut)",
-    "#editmenu-copy (editmenu-copy)",
-    "#editmenu-paste (editmenu-paste)",
-    "#editmenu-delete (editmenu-delete) [disabled]",
-    "#editmenu-selectAll (editmenu-select-all) [disabled]",
-  ];
-  is(getL10NContextMenu(menuPopup).join("\n"), expectedContextMenu.join("\n"),
-    "The context menu has the correct edit menu items");
+  let actualEntries = getL10NContextMenu(menuPopup);
+  is(actualEntries.length, 6, "The context menu has the right number of entries.");
+  is(actualEntries[0], "#editmenu-undo (editmenu-undo) [disabled]");
+  is(actualEntries[1], "#editmenu-cut (editmenu-cut) [disabled]");
+  is(actualEntries[2], "#editmenu-copy (editmenu-copy) [disabled]");
+  // Paste may or may not be enabled depending on what ran before this.
+  // If emptyClipboard is fixed (666254) we could assert if it's enabled/disabled.
+  ok(actualEntries[3].startsWith("#editmenu-paste (editmenu-paste)"));
+  is(actualEntries[4], "#editmenu-delete (editmenu-delete) [disabled]");
+  is(actualEntries[5], "#editmenu-selectAll (editmenu-select-all) [disabled]");
 
   const node = hud.jsterm.inputNode || hud.jsterm.node;
   const inputContainer = node.closest(".jsterm-input-container");
   await openContextMenu(hud, inputContainer);
 
-  expectedContextMenu = [
-    "#editmenu-undo (editmenu-undo) [disabled]",
-    "#editmenu-cut (editmenu-cut)",
-    "#editmenu-copy (editmenu-copy)",
-    "#editmenu-paste (editmenu-paste)",
-    "#editmenu-delete (editmenu-delete) [disabled]",
-    "#editmenu-selectAll (editmenu-select-all) [disabled]",
-  ];
-  is(getL10NContextMenu(menuPopup).join("\n"), expectedContextMenu.join("\n"),
-    "The context menu has the required elements");
+  actualEntries = getL10NContextMenu(menuPopup);
+  is(actualEntries.length, 6, "The context menu has the right number of entries.");
+  is(actualEntries[0], "#editmenu-undo (editmenu-undo) [disabled]");
+  is(actualEntries[1], "#editmenu-cut (editmenu-cut) [disabled]");
+  is(actualEntries[2], "#editmenu-copy (editmenu-copy) [disabled]");
+  // Paste may or may not be enabled depending on what ran before this.
+  // If emptyClipboard is fixed (666254) we could assert if it's enabled/disabled.
+  ok(actualEntries[3].startsWith("#editmenu-paste (editmenu-paste)"));
+  is(actualEntries[4], "#editmenu-delete (editmenu-delete) [disabled]");
+  is(actualEntries[5], "#editmenu-selectAll (editmenu-select-all) [disabled]");
 
   await hideContextMenu(hud);
   // Close the browser console.
   await HUDService.toggleBrowserConsole();
 }
 
 function addPrefBasedEntries(expectedEntries) {
   if (Services.prefs.getBoolPref("devtools.webconsole.sidebarToggle", false)) {
--- a/dom/base/nsGlobalWindowCommands.cpp
+++ b/dom/base/nsGlobalWindowCommands.cpp
@@ -483,19 +483,19 @@ nsresult nsClipboardCommand::IsCommandEn
     return NS_OK;
   }
 
   nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(aContext);
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   RefPtr<dom::Document> doc = window->GetExtantDoc();
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
-  if (doc->IsHTMLOrXHTML()) {
+  if (doc->IsHTMLOrXHTML() && !nsContentUtils::IsChromeDoc(doc)) {
     // In HTML and XHTML documents, we always want the cut, copy and paste
-    // commands to be enabled.
+    // commands to be enabled, but if the document is chrome, let it control it.
     *outCmdEnabled = true;
   } else {
     // Cut isn't enabled in xul documents which use nsClipboardCommand
     if (strcmp(aCommandName, "cmd_copy") == 0) {
       *outCmdEnabled = nsCopySupport::CanCopy(doc);
     }
   }
   return NS_OK;
--- a/editor/libeditor/HTMLEditorDataTransfer.cpp
+++ b/editor/libeditor/HTMLEditorDataTransfer.cpp
@@ -1637,19 +1637,21 @@ nsresult HTMLEditor::PasteNoFormattingAs
 // are used by CanPaste() and CanPasteTransferable() below.
 
 static const char* textEditorFlavors[] = {kUnicodeMime};
 static const char* textHtmlEditorFlavors[] = {kUnicodeMime,   kHTMLMime,
                                               kJPEGImageMime, kJPGImageMime,
                                               kPNGImageMime,  kGIFImageMime};
 
 bool HTMLEditor::CanPaste(int32_t aClipboardType) const {
-  // Always enable the paste command when inside of a HTML or XHTML document.
+  // Always enable the paste command when inside of a HTML or XHTML document,
+  // but if the document is chrome, let it control it.
   Document* document = GetDocument();
-  if (document && document->IsHTMLOrXHTML()) {
+  if (document && document->IsHTMLOrXHTML() &&
+      !nsContentUtils::IsChromeDoc(document)) {
     return true;
   }
 
   // can't paste if readonly
   if (!IsModifiable()) {
     return false;
   }
 
--- a/editor/libeditor/TextEditor.cpp
+++ b/editor/libeditor/TextEditor.cpp
@@ -1789,19 +1789,21 @@ nsresult TextEditor::CutAsAction(nsIPrin
 }
 
 bool TextEditor::CanCut() const {
   AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return false;
   }
 
-  // Cut is always enabled in HTML documents
+  // Cut is always enabled in HTML documents, but if the document is chrome,
+  // let it control it.
   Document* document = GetDocument();
-  if (document && document->IsHTMLOrXHTML()) {
+  if (document && document->IsHTMLOrXHTML() &&
+      !nsContentUtils::IsChromeDoc(document)) {
     return true;
   }
 
   return IsModifiable() && CanCutOrCopy(ePasswordFieldNotAllowed);
 }
 
 NS_IMETHODIMP
 TextEditor::Copy() {
@@ -1818,19 +1820,21 @@ TextEditor::Copy() {
 }
 
 bool TextEditor::CanCopy() const {
   AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
   if (NS_WARN_IF(!editActionData.CanHandle())) {
     return false;
   }
 
-  // Copy is always enabled in HTML documents
+  // Copy is always enabled in HTML documents, but if the document is chrome,
+  // let it control it.
   Document* document = GetDocument();
-  if (document && document->IsHTMLOrXHTML()) {
+  if (document && document->IsHTMLOrXHTML() &&
+      !nsContentUtils::IsChromeDoc(document)) {
     return true;
   }
 
   return CanCutOrCopy(ePasswordFieldNotAllowed);
 }
 
 bool TextEditor::CanDelete() const {
   AutoEditActionDataSetter editActionData(*this, EditAction::eNotEditing);
--- a/editor/libeditor/TextEditorDataTransfer.cpp
+++ b/editor/libeditor/TextEditorDataTransfer.cpp
@@ -438,19 +438,20 @@ nsresult TextEditor::PasteTransferableAs
   nsresult rv = InsertTextFromTransferable(aTransferable);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return EditorBase::ToGenericNSResult(rv);
   }
   return NS_OK;
 }
 
 bool TextEditor::CanPaste(int32_t aClipboardType) const {
-  // Always enable the paste command when inside of a HTML or XHTML document.
+  // Always enable the paste command when inside of a HTML or XHTML document,
+  // but if the document is chrome, let it control it.
   RefPtr<Document> doc = GetDocument();
-  if (doc && doc->IsHTMLOrXHTML()) {
+  if (doc && doc->IsHTMLOrXHTML() && !nsContentUtils::IsChromeDoc(doc)) {
     return true;
   }
 
   // can't paste if readonly
   if (!IsModifiable()) {
     return false;
   }