Implement unprivileged execCommand("copy") and execCommand("cut")
authorweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 Mar 2016 04:43:58 +0000 (04:43 +0000)
committerweinig@apple.com <weinig@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 14 Mar 2016 04:43:58 +0000 (04:43 +0000)
<rdar://problem/24354406>
https://bugs.webkit.org/show_bug.cgi?id=146336

Reviewed by Dean Jackson.

Source/WebCore:

Test: editing/execCommand/clipboard-access-with-user-gesture.html

* WebCore.xcodeproj/project.pbxproj:
Add new files.

* editing/ClipboardAccessPolicy.h:
Added.

* editing/EditorCommand.cpp:
(WebCore::defaultValueForSupportedCopyCut):
(WebCore::supportedCopyCut):
Match other browsers and allow the copy and cut commands
to be executed when there is a user gesture.

* page/Settings.h:
Add include of ClipboardAccessPolicy.h.

* page/Settings.in:
Add new setting for ClipboardAccessPolicy

LayoutTests:

* editing/execCommand/clipboard-access-with-user-gesture-expected.txt: Added.
* editing/execCommand/clipboard-access-with-user-gesture.html: Added.
Add test for using execCommand("copy") and execCommand("cut") during a user gesture.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@198092 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/editing/execCommand/clipboard-access-with-user-gesture-expected.txt [new file with mode: 0644]
LayoutTests/editing/execCommand/clipboard-access-with-user-gesture.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/editing/ClipboardAccessPolicy.h [new file with mode: 0644]
Source/WebCore/editing/EditorCommand.cpp
Source/WebCore/page/Settings.h
Source/WebCore/page/Settings.in

index 3901a20..fe5f5ca 100644 (file)
@@ -1,3 +1,15 @@
+2016-03-13  Sam Weinig  <sam@webkit.org>
+
+        Implement unprivileged execCommand("copy") and execCommand("cut")
+        <rdar://problem/24354406>
+        https://bugs.webkit.org/show_bug.cgi?id=146336
+
+        Reviewed by Dean Jackson.
+
+        * editing/execCommand/clipboard-access-with-user-gesture-expected.txt: Added.
+        * editing/execCommand/clipboard-access-with-user-gesture.html: Added.
+        Add test for using execCommand("copy") and execCommand("cut") during a user gesture.
+
 2016-03-13  Dean Jackson  <dino@apple.com>
 
         DRT should enable WebGL by default on Mac
diff --git a/LayoutTests/editing/execCommand/clipboard-access-with-user-gesture-expected.txt b/LayoutTests/editing/execCommand/clipboard-access-with-user-gesture-expected.txt
new file mode 100644 (file)
index 0000000..6247ad0
--- /dev/null
@@ -0,0 +1,121 @@
+This test checks that JavaScript programs can execute copy and paste commands, but only during a user gesture.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+Testing copying an editableParagraph range without a user gesture.
+PASS document.queryCommandEnabled('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.queryCommandSupported('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.execCommand('copy') is false
+PASS eventSeen['copy'] is false
+
+Testing copying an editableParagraph caret without a user gesture.
+PASS document.queryCommandEnabled('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.queryCommandSupported('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.execCommand('copy') is false
+PASS eventSeen['copy'] is false
+
+Testing copying an non-editable range without a user gesture.
+PASS document.queryCommandEnabled('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.queryCommandSupported('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.execCommand('copy') is false
+PASS eventSeen['copy'] is false
+
+Testing copying an non-editable caret without a user gesture.
+PASS document.queryCommandEnabled('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.queryCommandSupported('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.execCommand('copy') is false
+PASS eventSeen['copy'] is false
+
+Testing copying an editable plaint-text range without a user gesture.
+PASS document.queryCommandEnabled('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.queryCommandSupported('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.execCommand('copy') is false
+PASS eventSeen['copy'] is false
+
+Testing copying an editable plaint-text caret without a user gesture.
+PASS document.queryCommandEnabled('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.queryCommandSupported('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.execCommand('copy') is false
+PASS eventSeen['copy'] is false
+
+Testing copying when there is no selection without a user gesture.
+PASS document.queryCommandEnabled('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.queryCommandSupported('copy') is false
+PASS eventSeen['copy'] is false
+PASS document.execCommand('copy') is false
+PASS eventSeen['copy'] is false
+
+Testing cutting an editableParagraph range without a user gesture.
+PASS document.queryCommandEnabled('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.queryCommandSupported('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.execCommand('cut') is false
+PASS eventSeen['cut'] is false
+
+Testing cutting an editableParagraph caret without a user gesture.
+PASS document.queryCommandEnabled('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.queryCommandSupported('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.execCommand('cut') is false
+PASS eventSeen['cut'] is false
+
+Testing cutting an non-editable range without a user gesture.
+PASS document.queryCommandEnabled('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.queryCommandSupported('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.execCommand('cut') is false
+PASS eventSeen['cut'] is false
+
+Testing cutting an non-editable caret without a user gesture.
+PASS document.queryCommandEnabled('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.queryCommandSupported('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.execCommand('cut') is false
+PASS eventSeen['cut'] is false
+
+Testing cutting an editable plaint-text range without a user gesture.
+PASS document.queryCommandEnabled('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.queryCommandSupported('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.execCommand('cut') is false
+PASS eventSeen['cut'] is false
+
+Testing cutting an editable plaint-text caret without a user gesture.
+PASS document.queryCommandEnabled('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.queryCommandSupported('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.execCommand('cut') is false
+PASS eventSeen['cut'] is false
+
+Testing cutting when there is no selection without a user gesture.
+PASS document.queryCommandEnabled('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.queryCommandSupported('cut') is false
+PASS eventSeen['cut'] is false
+PASS document.execCommand('cut') is false
+PASS eventSeen['cut'] is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/editing/execCommand/clipboard-access-with-user-gesture.html b/LayoutTests/editing/execCommand/clipboard-access-with-user-gesture.html
new file mode 100644 (file)
index 0000000..ebe4820
--- /dev/null
@@ -0,0 +1,275 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script>
+
+description("This test checks that JavaScript programs can execute copy and paste commands, but only during a user gesture.");
+
+// Override the test runner default befavior of allowing all keyboard access, and act more like
+// a browser.
+if (window.testRunner)
+    window.testRunner.setJavaScriptCanAccessClipboard(false);
+
+function clearSelectionContainer()
+{
+    var container = document.getElementById('container');
+    if (container)
+        container.remove();
+}
+
+function resetSelectionContainer()
+{
+    clearSelectionContainer();
+
+    var container = document.createElement("div");
+    container.id = "container";
+
+    var nonEditableParagraph = document.createElement("p");
+    nonEditableParagraph.id = "nonEditableParagraph";
+    nonEditableParagraph.appendChild(document.createTextNode("x"));
+    container.appendChild(nonEditableParagraph);
+
+    var editableParagraph = document.createElement("p");
+    editableParagraph.id = "editableParagraph";
+    editableParagraph.appendChild(document.createTextNode("x"));
+    editableParagraph.setAttribute("contentEditable", "true");
+    container.appendChild(editableParagraph);
+
+    var editablePlainTextParagraph = document.createElement("p");
+    editablePlainTextParagraph.id = "editablePlainTextParagraph";
+    editablePlainTextParagraph.appendChild(document.createTextNode("x"));
+    editablePlainTextParagraph.setAttribute("contentEditable", "plaintext-only");
+    container.appendChild(editablePlainTextParagraph);
+
+    document.body.appendChild(container);
+}
+
+function makeSelection(elementId, selectionStart, selectionEnd)
+{
+    resetSelectionContainer();
+
+    var selection = window.getSelection();
+    selection.removeAllRanges();
+
+    var element = document.getElementById(elementId);
+
+    var range = document.createRange();
+    range.setStart(element.firstChild, selectionStart);
+    range.setEnd(element.firstChild, selectionEnd);
+    selection.addRange(range);
+}
+
+var eventSeen = {
+    "copy": false,
+    "cut": false,
+    "beforecopy": false,
+    "beforecut": false
+}
+
+function resetSeenFlags()
+{
+    eventSeen.copy = false;
+    eventSeen.cut = false;
+    eventSeen.beforecopy = false;
+    eventSeen.beforecut = false;
+}
+
+document.body.oncopy = function(event)
+{
+    eventSeen["copy"] = true;
+}
+
+document.body.oncut = function()
+{
+    eventSeen["cut"] = true;
+}
+
+document.body.onbeforecopy = function()
+{
+    eventSeen["beforecopy"] = true;
+}
+
+document.body.onbeforecut = function()
+{
+    eventSeen["beforecut"] = true;
+}
+
+function test(command, enabledExpected, supportedExpected, executedExpected)
+{
+    resetSeenFlags();
+    shouldBe("document.queryCommandEnabled('" + command +"')", "" + enabledExpected);
+    shouldBeFalse("eventSeen['" + command +"']");
+
+    resetSeenFlags();
+    shouldBe("document.queryCommandSupported('" + command +"')", "" + supportedExpected);
+    shouldBeFalse("eventSeen['" + command +"']");
+
+    resetSeenFlags();
+    shouldBe("document.execCommand('" + command +"')", "" + executedExpected);
+    shouldBe("eventSeen['" + command +"']", "" + executedExpected);
+}
+
+function header(msg)
+{
+    debug("");
+    debug(msg);
+}
+
+var buttonForCopy = document.createElement("button");
+buttonForCopy.id = "copyButton";
+buttonForCopy.textContent = "Copy";
+buttonForCopy.onclick = function()
+{
+    header("Testing copying an editableParagraph range in a user gesture.")
+    makeSelection("editableParagraph", 0, 1);
+    test("copy", true, true, true);
+
+    header("Testing copying an editableParagraph caret in a user gesture.")
+    makeSelection("editableParagraph", 0, 0);
+    test("copy", false, true, true);
+
+    header("Testing copying an non-editable range in a user gesture.")
+    makeSelection("nonEditableParagraph", 0, 1);
+    test("copy", true, true, true);
+
+    header("Testing copying an non-editable caret in a user gesture.")
+    makeSelection("nonEditableParagraph", 0, 0);
+    test("copy", false, true, true);
+
+    header("Testing copying an editable plaint-text range in a user gesture.")
+    makeSelection("editablePlainTextParagraph", 0, 1);
+    test("copy", true, true, true);
+
+    header("Testing copying an editable plaint-text caret in a user gesture.")
+    makeSelection("editablePlainTextParagraph", 0, 0);
+    test("copy", false, true, true);
+
+    header("Testing copying when there is no selection in a user gesture.")
+    window.getSelection().removeAllRanges();
+    test("copy", false, true, true);
+
+    clearSelectionContainer();
+}
+document.body.appendChild(buttonForCopy);
+
+var buttonForCut = document.createElement("button");
+buttonForCut.id = "cutButton";
+buttonForCut.textContent = "Cut";
+buttonForCut.onclick = function()
+{
+    header("Testing cutting an editableParagraph range in a user gesture.")
+    makeSelection("editableParagraph", 0, 1);
+    test("cut", true, true, true);
+
+    header("Testing cutting an editableParagraph caret in a user gesture.")
+    makeSelection("editableParagraph", 0, 0);
+    test("cut", false, true, true);
+
+    header("Testing cutting an non-editable range in a user gesture.")
+    makeSelection("nonEditableParagraph", 0, 1);
+    test("cut", false, true, true);
+
+    header("Testing cutting an non-editable caret in a user gesture.")
+    makeSelection("nonEditableParagraph", 0, 0);
+    test("cut", false, true, true);
+
+    header("Testing cutting an editable plaint-text range in a user gesture.")
+    makeSelection("editablePlainTextParagraph", 0, 1);
+    test("cut", true, true, true);
+
+    header("Testing cutting an editable plaint-text caret in a user gesture.")
+    makeSelection("editablePlainTextParagraph", 0, 0);
+    test("cut", false, true, true);
+
+    header("Testing cutting when there is no selection in a user gesture.")
+    window.getSelection().removeAllRanges();
+    test("cut", false, true, true);
+
+    clearSelectionContainer();
+}
+document.body.appendChild(buttonForCut);
+
+// First test copy/cut without user gestures.
+header("Testing copying an editableParagraph range without a user gesture.")
+makeSelection("editableParagraph", 0, 1);
+test("copy", false, false, false);
+
+header("Testing copying an editableParagraph caret without a user gesture.")
+makeSelection("editableParagraph", 0, 0);
+test("copy", false, false, false);
+
+header("Testing copying an non-editable range without a user gesture.")
+makeSelection("nonEditableParagraph", 0, 1);
+test("copy", false, false, false);
+
+header("Testing copying an non-editable caret without a user gesture.")
+makeSelection("nonEditableParagraph", 0, 0);
+test("copy", false, false, false);
+
+header("Testing copying an editable plaint-text range without a user gesture.")
+makeSelection("editablePlainTextParagraph", 0, 1);
+test("copy", false, false, false);
+
+header("Testing copying an editable plaint-text caret without a user gesture.")
+makeSelection("editablePlainTextParagraph", 0, 0);
+test("copy", false, false, false);
+
+header("Testing copying when there is no selection without a user gesture.")
+window.getSelection().removeAllRanges();
+test("copy", false, false, false);
+
+clearSelectionContainer();
+
+header("Testing cutting an editableParagraph range without a user gesture.")
+makeSelection("editableParagraph", 0, 1);
+test("cut", false, false, false);
+
+header("Testing cutting an editableParagraph caret without a user gesture.")
+makeSelection("editableParagraph", 0, 0);
+test("cut", false, false, false);
+
+header("Testing cutting an non-editable range without a user gesture.")
+makeSelection("nonEditableParagraph", 0, 1);
+test("cut", false, false, false);
+
+header("Testing cutting an non-editable caret without a user gesture.")
+makeSelection("nonEditableParagraph", 0, 0);
+test("cut", false, false, false);
+
+header("Testing cutting an editable plaint-text range without a user gesture.")
+makeSelection("editablePlainTextParagraph", 0, 1);
+test("cut", false, false, false);
+
+header("Testing cutting an editable plaint-text caret without a user gesture.")
+makeSelection("editablePlainTextParagraph", 0, 0);
+test("cut", false, false, false);
+
+header("Testing cutting when there is no selection without a user gesture.")
+window.getSelection().removeAllRanges();
+test("cut", false, false, false);
+
+clearSelectionContainer();
+
+// Then test copy/cut with user gestures.
+if (window.testRunner) {
+    function clickButton(button)
+    {
+        eventSender.mouseMoveTo(button.offsetLeft + button.offsetWidth / 2, button.offsetTop + button.offsetHeight / 2);
+        eventSender.mouseDown();
+        eventSender.mouseUp();
+    }
+
+    clickButton(buttonForCopy);
+    clickButton(buttonForCut);
+
+    buttonForCopy.remove();
+    buttonForCut.remove();
+}
+</script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 71bd401..f944332 100644 (file)
@@ -1,3 +1,31 @@
+2016-03-13  Sam Weinig  <sam@webkit.org>
+
+        Implement unprivileged execCommand("copy") and execCommand("cut")
+        <rdar://problem/24354406>
+        https://bugs.webkit.org/show_bug.cgi?id=146336
+
+        Reviewed by Dean Jackson.
+
+        Test: editing/execCommand/clipboard-access-with-user-gesture.html
+
+        * WebCore.xcodeproj/project.pbxproj:
+        Add new files.
+
+        * editing/ClipboardAccessPolicy.h:
+        Added.
+
+        * editing/EditorCommand.cpp:
+        (WebCore::defaultValueForSupportedCopyCut):
+        (WebCore::supportedCopyCut):
+        Match other browsers and allow the copy and cut commands
+        to be executed when there is a user gesture.
+
+        * page/Settings.h:
+        Add include of ClipboardAccessPolicy.h.
+
+        * page/Settings.in:
+        Add new setting for ClipboardAccessPolicy
+
 2016-03-13  Ryosuke Niwa  <rniwa@webkit.org>
 
         REGRESSION (r190840): crash inside details element's slotNameFunction
index cc82b8d..4718ab2 100644 (file)
                7C33F35A1B4A044800502CAF /* JSCharacterDataCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C33F3581B4A044800502CAF /* JSCharacterDataCustom.cpp */; };
                7C33F35E1B4A04CE00502CAF /* JSDocumentTypeCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C33F35C1B4A04CE00502CAF /* JSDocumentTypeCustom.cpp */; };
                7C33F3621B4A050400502CAF /* JSDocumentFragmentCustom.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C33F3601B4A050400502CAF /* JSDocumentFragmentCustom.cpp */; };
+               7C3A91E61C963B8800D1A7E3 /* ClipboardAccessPolicy.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C3A91E51C963B8800D1A7E3 /* ClipboardAccessPolicy.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7C3B79711908757B00B47A2D /* UserMessageHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7C3B796F1908757B00B47A2D /* UserMessageHandler.cpp */; };
                7C3B79721908757B00B47A2D /* UserMessageHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C3B79701908757B00B47A2D /* UserMessageHandler.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7C3E510A18DF8F3500C112F7 /* HTMLConverter.h in Headers */ = {isa = PBXBuildFile; fileRef = 7C3E510818DF8F3500C112F7 /* HTMLConverter.h */; settings = {ATTRIBUTES = (Private, ); }; };
                7C33F3581B4A044800502CAF /* JSCharacterDataCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSCharacterDataCustom.cpp; sourceTree = "<group>"; };
                7C33F35C1B4A04CE00502CAF /* JSDocumentTypeCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDocumentTypeCustom.cpp; sourceTree = "<group>"; };
                7C33F3601B4A050400502CAF /* JSDocumentFragmentCustom.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSDocumentFragmentCustom.cpp; sourceTree = "<group>"; };
+               7C3A91E51C963B8800D1A7E3 /* ClipboardAccessPolicy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClipboardAccessPolicy.h; sourceTree = "<group>"; };
                7C3B796F1908757B00B47A2D /* UserMessageHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = UserMessageHandler.cpp; sourceTree = "<group>"; };
                7C3B79701908757B00B47A2D /* UserMessageHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = UserMessageHandler.h; sourceTree = "<group>"; };
                7C3E510818DF8F3500C112F7 /* HTMLConverter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTMLConverter.h; sourceTree = "<group>"; };
                                93309D8A099E64910056E581 /* ApplyStyleCommand.h */,
                                93309D8B099E64910056E581 /* BreakBlockquoteCommand.cpp */,
                                93309D8C099E64910056E581 /* BreakBlockquoteCommand.h */,
+                               7C3A91E51C963B8800D1A7E3 /* ClipboardAccessPolicy.h */,
                                93309D8D099E64910056E581 /* CompositeEditCommand.cpp */,
                                93309D8E099E64910056E581 /* CompositeEditCommand.h */,
                                D0B0556709C6700100307E43 /* CreateLinkCommand.cpp */,
                                BC46C1FB0C0DDC8F0020CFC3 /* JSCSSCharsetRule.h in Headers */,
                                409EBDC316B7F3A600CBA3FC /* JSCSSFontFaceLoadEvent.h in Headers */,
                                BC46C1FD0C0DDC8F0020CFC3 /* JSCSSFontFaceRule.h in Headers */,
+                               7C3A91E61C963B8800D1A7E3 /* ClipboardAccessPolicy.h in Headers */,
                                BC46C1FF0C0DDC8F0020CFC3 /* JSCSSImportRule.h in Headers */,
                                316FE0720E6CCBEE00BF6088 /* JSCSSKeyframeRule.h in Headers */,
                                316FE0740E6CCBEE00BF6088 /* JSCSSKeyframesRule.h in Headers */,
diff --git a/Source/WebCore/editing/ClipboardAccessPolicy.h b/Source/WebCore/editing/ClipboardAccessPolicy.h
new file mode 100644 (file)
index 0000000..f1c6c52
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+namespace WebCore {
+
+enum class ClipboardAccessPolicy {
+    Allow,
+    Deny,
+    RequiresUserGesture
+};
+
+} // namespace WebCore
index 8871151..fba2466 100644 (file)
@@ -57,6 +57,7 @@
 #include "StyleProperties.h"
 #include "TypingCommand.h"
 #include "UnlinkCommand.h"
+#include "UserGestureIndicator.h"
 #include "UserTypingGestureIndicator.h"
 #include "htmlediting.h"
 #include "markup.h"
@@ -1157,12 +1158,31 @@ static bool supportedFromMenuOrKeyBinding(Frame*)
     return false;
 }
 
+static bool defaultValueForSupportedCopyCut(Frame& frame)
+{
+    auto& settings = frame.settings();
+    if (settings.javaScriptCanAccessClipboard())
+        return true;
+    
+    switch (settings.clipboardAccessPolicy()) {
+    case ClipboardAccessPolicy::Allow:
+        return true;
+    case ClipboardAccessPolicy::Deny:
+        return false;
+    case ClipboardAccessPolicy::RequiresUserGesture:
+        return UserGestureIndicator::processingUserGesture();
+    }
+
+    ASSERT_NOT_REACHED();
+    return false;
+}
+
 static bool supportedCopyCut(Frame* frame)
 {
     if (!frame)
         return false;
 
-    bool defaultValue = frame->settings().javaScriptCanAccessClipboard();
+    bool defaultValue = defaultValueForSupportedCopyCut(*frame);
 
     EditorClient* client = frame->editor().client();
     return client ? client->canCopyCut(frame, defaultValue) : defaultValue;
index 0cb884c..33db5fe 100644 (file)
 #ifndef Settings_h
 #define Settings_h
 
+#include "ClipboardAccessPolicy.h"
 #include "EditingBehaviorTypes.h"
 #include "IntSize.h"
-#include "URL.h"
 #include "SecurityOrigin.h"
 #include "SettingsMacros.h"
 #include "TextFlags.h"
 #include "Timer.h"
+#include "URL.h"
 #include <chrono>
 #include <runtime/RuntimeFlags.h>
 #include <unicode/uscript.h>
index 6ab274d..04f1387 100644 (file)
@@ -53,6 +53,7 @@ javaScriptCanOpenWindowsAutomatically initial=false
 javaScriptCanAccessClipboard initial=false
 shouldPrintBackgrounds initial=false
 usesDashboardBackwardCompatibilityMode initial=false, conditional=DASHBOARD_SUPPORT
+clipboardAccessPolicy type=ClipboardAccessPolicy, initial=ClipboardAccessPolicy::RequiresUserGesture
 
 textAreasAreResizable initial=false, setNeedsStyleRecalcInAllFrames=1
 authorAndUserStylesEnabled initial=true, setNeedsStyleRecalcInAllFrames=1