[Cocoa] Complete support for Paste as Quotation
authormitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Sep 2018 22:26:12 +0000 (22:26 +0000)
committermitz@apple.com <mitz@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Sep 2018 22:26:12 +0000 (22:26 +0000)
https://bugs.webkit.org/show_bug.cgi?id=189504

Reviewed by Wenson Hsieh.

Source/WebCore:

Tests: editing/pasteboard/4930986-1-paste-as-quotation.html
       editing/pasteboard/4930986-2-paste-as-quotation.html
       editing/pasteboard/4930986-3-paste-as-quotation.html

* editing/Editor.cpp:
  Added ClipboardEventKind::PasteAsQuotation.
(WebCore::eventNameForClipboardEvent): Map PasteAsQuotation to the "paste" DOM event name.
(WebCore::createDataTransferForClipboardEvent): Place the unquoted content in the event.
  This means that currently event handlers can’t emulate pasting as quotation, because they
  neither have the quoted content nor knowledge that quoting has been requested. We could
  change this in the future if needed.
(WebCore::Editor::paste): Updated for change in pasteWithPasteboard’s argument type.
(WebCore::Editor::pasteAsQuotation): Added. Similar to paste, but passes
  PasteOption::AsQuotation to pasteWithPasteboard.
(WebCore::Editor::quoteFragmentForPasting): Added. Quoting for pasting consists of enclosing
  the fragment in a blockquote element with the "type" attribute set to "cite" and the
  "class" attribute set to a well-known value, which is used to trigger special behavior in
  ReplaceSelectionCommand. The behavior includes removing the "class" attribute in the end,
  so eventually, we could stop using this form of in-band signaling.
* editing/Editor.h: Declared PasteOption enum class to encompass the existing allowPlainText
  and MailBlockquoteHandling arguments to pasteWithPasteboard as well as the new AsQuotation
  behavior.

* editing/EditorCommand.cpp:
(WebCore::executePasteAsQuotation): Added. Similar to executing Paste.
(WebCore::createCommandMap): Added an entry for PasteAsQuotation, based on the Paste entry.

* editing/cocoa/EditorCocoa.mm:
(WebCore::Editor::webContentFromPasteboard): Moved from EditorIOS.mm and EditorMac.mm to
  here.

* editing/gtk/EditorGtk.cpp:
(WebCore::Editor::pasteWithPasteboard): Updated for new OptionSet argument, added a call to
  quote the fragment if needed.

* editing/ios/EditorIOS.mm:
(WebCore::Editor::pasteWithPasteboard): Ditto.
(WebCore::Editor::webContentFromPasteboard): Moved to EditorCocoa.mm.

* editing/mac/EditorMac.mm:
(WebCore::Editor::pasteWithPasteboard): Updated for new OptionSet argument, added a call to
  quote the fragment if needed.
(WebCore::Editor::readSelectionFromPasteboard): Updated for new OptionSet argument to
  pasteWithPasteboard.
(WebCore::Editor::webContentFromPasteboard): Moved to EditorCocoa.mm.

* editing/win/EditorWin.cpp:
(WebCore::Editor::pasteWithPasteboard): Updated for new OptionSet argument, added a call to
  quote the fragment if needed.

* editing/wpe/EditorWPE.cpp:
(WebCore::Editor::pasteWithPasteboard): Ditto.

Source/WebKit:

* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView canPerformAction:withSender:]): Handle _pasteAsQuotation:. It’s not included
  in FOR_EACH_WKCONTENTVIEW_ACTION, because it’s declared and implemented in the WKPrivate
  category. If we add more actions in the category, it could make sense to fold them into
  a new FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION.
(-[WKWebView targetForAction:withSender:]): Handle _pasteAsQuotation:.
(-[WKWebView _pasteAsQuotation:]): Send to the WebViewImpl or the WKContentView.
* UIProcess/API/Cocoa/WKWebViewPrivate.h: Declared a new _pasteAsQuotation: action.

* UIProcess/Cocoa/WebViewImpl.mm:
(WebKit::selectorExceptionMap): Added a custom mapping from the new selector to the
  PasteAsQuotation command.

* UIProcess/ios/WKContentViewInteraction.h: Declare methods for the new action.
* UIProcess/ios/WKContentViewInteraction.mm:
  Forward _pasteAsQuotation: to the WKWebView so that clients get a chance to override its
  behavior.
(-[WKContentView _pasteAsQuotationForWebView:]): Send the command to the page.

Tools:

* MiniBrowser/mac/MainMenu.xib: Added a Paste as Quotation command in the Edit menu.

LayoutTests:

Took a few existing tests of the Paste as Quotation behavior and modified them to use the
new PasteAsQuotation command. The only difference in the results is that the blockquote has
the "type" attribute set to "cite".

* editing/pasteboard/4930986-1-paste-as-quotation-expected.txt: Added.
* editing/pasteboard/4930986-1-paste-as-quotation.html: Added.
* editing/pasteboard/4930986-2-paste-as-quotation-expected.txt: Added.
* editing/pasteboard/4930986-2-paste-as-quotation.html: Added.
* editing/pasteboard/4930986-3-paste-as-quotation-expected.txt: Added.
* editing/pasteboard/4930986-3-paste-as-quotation.html: Added.

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

25 files changed:
LayoutTests/ChangeLog
LayoutTests/editing/pasteboard/4930986-1-paste-as-quotation-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/4930986-1-paste-as-quotation.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/4930986-2-paste-as-quotation-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/4930986-2-paste-as-quotation.html [new file with mode: 0644]
LayoutTests/editing/pasteboard/4930986-3-paste-as-quotation-expected.txt [new file with mode: 0644]
LayoutTests/editing/pasteboard/4930986-3-paste-as-quotation.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/editing/Editor.cpp
Source/WebCore/editing/Editor.h
Source/WebCore/editing/EditorCommand.cpp
Source/WebCore/editing/cocoa/EditorCocoa.mm
Source/WebCore/editing/gtk/EditorGtk.cpp
Source/WebCore/editing/ios/EditorIOS.mm
Source/WebCore/editing/mac/EditorMac.mm
Source/WebCore/editing/win/EditorWin.cpp
Source/WebCore/editing/wpe/EditorWPE.cpp
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h
Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm
Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Tools/ChangeLog
Tools/MiniBrowser/mac/MainMenu.xib

index 552f5df..ab4cc27 100644 (file)
@@ -1,3 +1,21 @@
+2018-09-12  Dan Bernstein  <mitz@apple.com>
+
+        [Cocoa] Complete support for Paste as Quotation
+        https://bugs.webkit.org/show_bug.cgi?id=189504
+
+        Reviewed by Wenson Hsieh.
+
+        Took a few existing tests of the Paste as Quotation behavior and modified them to use the
+        new PasteAsQuotation command. The only difference in the results is that the blockquote has
+        the "type" attribute set to "cite".
+
+        * editing/pasteboard/4930986-1-paste-as-quotation-expected.txt: Added.
+        * editing/pasteboard/4930986-1-paste-as-quotation.html: Added.
+        * editing/pasteboard/4930986-2-paste-as-quotation-expected.txt: Added.
+        * editing/pasteboard/4930986-2-paste-as-quotation.html: Added.
+        * editing/pasteboard/4930986-3-paste-as-quotation-expected.txt: Added.
+        * editing/pasteboard/4930986-3-paste-as-quotation.html: Added.
+
 2018-09-12  Sihui Liu  <sihui_liu@apple.com>
 
         Move IndexedDB to Network Process
diff --git a/LayoutTests/editing/pasteboard/4930986-1-paste-as-quotation-expected.txt b/LayoutTests/editing/pasteboard/4930986-1-paste-as-quotation-expected.txt
new file mode 100644 (file)
index 0000000..690c531
--- /dev/null
@@ -0,0 +1,2 @@
+This tests to make sure that content that has the document default color is pasted as blue (or whatever the color for quoted content is) during a Paste as Quotation.
+<blockquote type="cite">This text should be blue (it should not be wrapped in a style span).</blockquote>
diff --git a/LayoutTests/editing/pasteboard/4930986-1-paste-as-quotation.html b/LayoutTests/editing/pasteboard/4930986-1-paste-as-quotation.html
new file mode 100644 (file)
index 0000000..699ad6f
--- /dev/null
@@ -0,0 +1,34 @@
+<html>
+<head>
+<style>
+blockquote {
+    color: blue;
+    border-left: 2px solid blue;
+    margin-left: 0px;
+    padding-left: 10px;
+}
+</style>
+</head>
+<body>
+<div id="description">This tests to make sure that content that has the document default color is pasted as blue (or whatever the color for quoted content is) during a Paste as Quotation.</div>
+<div id="edit" contenteditable="true"></div>
+
+<script>
+document.addEventListener("beforecopy", (event) => event.preventDefault());
+document.addEventListener("copy", (event) => {
+    event.clipboardData.setData("text/html", "<span class='Apple-style-span' style='color: black;'>This text should be blue (it should not be wrapped in a style span).</span>");
+    event.preventDefault();
+});
+document.execCommand("Copy", false);
+
+edit = document.getElementById("edit");
+description = document.getElementById("description");
+edit.focus();
+document.execCommand("PasteAsQuotation", false);
+if (window.testRunner) {
+    window.testRunner.dumpAsText();
+    document.body.innerText = description.innerText + "\n" + edit.innerHTML;
+}
+</script>
+</body>
+</html>
diff --git a/LayoutTests/editing/pasteboard/4930986-2-paste-as-quotation-expected.txt b/LayoutTests/editing/pasteboard/4930986-2-paste-as-quotation-expected.txt
new file mode 100644 (file)
index 0000000..925ee1d
--- /dev/null
@@ -0,0 +1,2 @@
+This tests to make sure that content that is colored by the user is pasted with that color during a Paste as Quotation.
+<blockquote type="cite"><span class="Apple-style-span" style="color: red;">This text should be red (it should be wrapped in a style span).</span></blockquote>
diff --git a/LayoutTests/editing/pasteboard/4930986-2-paste-as-quotation.html b/LayoutTests/editing/pasteboard/4930986-2-paste-as-quotation.html
new file mode 100644 (file)
index 0000000..7cdecad
--- /dev/null
@@ -0,0 +1,34 @@
+<html>
+<head>
+<style>
+blockquote {
+    color: blue;
+    border-left: 2px solid blue;
+    margin-left: 0px;
+    padding-left: 10px;
+}
+</style>
+<script>
+function runTest() {
+    document.addEventListener("beforecopy", (event) => event.preventDefault());
+    document.addEventListener("copy", (event) => {
+        event.clipboardData.setData("text/html", "<span class='Apple-style-span' style='color: black;'><span class='Apple-style-span' style='color: red;'>This text should be red (it should be wrapped in a style span).</span></span>");
+        event.preventDefault();
+    });
+    document.execCommand("Copy", false);
+
+    div = document.getElementById("edit");
+    div.focus();
+    document.execCommand("PasteAsQuotation", false);
+    if (window.testRunner) {
+        window.testRunner.dumpAsText();
+        document.body.innerText = document.getElementById("description").innerText + "\n" + div.innerHTML;
+    }
+}
+</script>
+</head>
+<body onload="runTest();">
+<div id="description">This tests to make sure that content that is colored by the user is pasted with that color during a Paste as Quotation.</div>
+<div id="edit" contenteditable="true"></div>
+</body>
+</html>
diff --git a/LayoutTests/editing/pasteboard/4930986-3-paste-as-quotation-expected.txt b/LayoutTests/editing/pasteboard/4930986-3-paste-as-quotation-expected.txt
new file mode 100644 (file)
index 0000000..0b8af69
--- /dev/null
@@ -0,0 +1,2 @@
+This tests to make sure that an Apple-paste-as-quotation blockquote can override document default styles even if they are different than the insertion position.
+<blockquote type="cite">This text should have the blockquote color (blue). There should be no style spans around it.</blockquote>
diff --git a/LayoutTests/editing/pasteboard/4930986-3-paste-as-quotation.html b/LayoutTests/editing/pasteboard/4930986-3-paste-as-quotation.html
new file mode 100644 (file)
index 0000000..4989189
--- /dev/null
@@ -0,0 +1,34 @@
+<html>
+<head>
+<style>
+blockquote {
+    color: blue;
+    padding-left: 10px;
+    margin-left: 0px;
+    border-left: 2px solid blue;
+}
+</style>
+</head>
+<body>
+<div id="description">This tests to make sure that an Apple-paste-as-quotation blockquote can override document default styles even if they are different than the insertion position.</div>
+<div id="edit" contenteditable="true" style="color: red;"></div>
+
+<script>
+document.addEventListener("beforecopy", (event) => event.preventDefault());
+document.addEventListener("copy", (event) => {
+    event.clipboardData.setData("text/html", "<span class='Apple-style-span' style='color:black;'>This text should have the blockquote color (blue). There should be no style spans around it.</span>");
+    event.preventDefault();
+});
+document.execCommand("Copy", false);
+
+edit = document.getElementById("edit");
+description = document.getElementById("description");
+edit.focus();
+document.execCommand("PasteAsQuotation", false);
+if (window.testRunner) {
+    window.testRunner.dumpAsText();
+    document.body.innerText = description.innerText + "\n" + edit.innerHTML;
+}
+</script>
+</body>
+</html>
index 0f525f3..888ca18 100644 (file)
@@ -1,3 +1,63 @@
+2018-09-12  Dan Bernstein  <mitz@apple.com>
+
+        [Cocoa] Complete support for Paste as Quotation
+        https://bugs.webkit.org/show_bug.cgi?id=189504
+
+        Reviewed by Wenson Hsieh.
+
+        Tests: editing/pasteboard/4930986-1-paste-as-quotation.html
+               editing/pasteboard/4930986-2-paste-as-quotation.html
+               editing/pasteboard/4930986-3-paste-as-quotation.html
+
+        * editing/Editor.cpp:
+          Added ClipboardEventKind::PasteAsQuotation.
+        (WebCore::eventNameForClipboardEvent): Map PasteAsQuotation to the "paste" DOM event name.
+        (WebCore::createDataTransferForClipboardEvent): Place the unquoted content in the event.
+          This means that currently event handlers can’t emulate pasting as quotation, because they
+          neither have the quoted content nor knowledge that quoting has been requested. We could
+          change this in the future if needed.
+        (WebCore::Editor::paste): Updated for change in pasteWithPasteboard’s argument type.
+        (WebCore::Editor::pasteAsQuotation): Added. Similar to paste, but passes
+          PasteOption::AsQuotation to pasteWithPasteboard.
+        (WebCore::Editor::quoteFragmentForPasting): Added. Quoting for pasting consists of enclosing
+          the fragment in a blockquote element with the "type" attribute set to "cite" and the
+          "class" attribute set to a well-known value, which is used to trigger special behavior in
+          ReplaceSelectionCommand. The behavior includes removing the "class" attribute in the end,
+          so eventually, we could stop using this form of in-band signaling.
+        * editing/Editor.h: Declared PasteOption enum class to encompass the existing allowPlainText
+          and MailBlockquoteHandling arguments to pasteWithPasteboard as well as the new AsQuotation
+          behavior.
+
+        * editing/EditorCommand.cpp:
+        (WebCore::executePasteAsQuotation): Added. Similar to executing Paste.
+        (WebCore::createCommandMap): Added an entry for PasteAsQuotation, based on the Paste entry.
+
+        * editing/cocoa/EditorCocoa.mm:
+        (WebCore::Editor::webContentFromPasteboard): Moved from EditorIOS.mm and EditorMac.mm to
+          here.
+
+        * editing/gtk/EditorGtk.cpp:
+        (WebCore::Editor::pasteWithPasteboard): Updated for new OptionSet argument, added a call to
+          quote the fragment if needed.
+
+        * editing/ios/EditorIOS.mm:
+        (WebCore::Editor::pasteWithPasteboard): Ditto.
+        (WebCore::Editor::webContentFromPasteboard): Moved to EditorCocoa.mm.
+
+        * editing/mac/EditorMac.mm:
+        (WebCore::Editor::pasteWithPasteboard): Updated for new OptionSet argument, added a call to
+          quote the fragment if needed.
+        (WebCore::Editor::readSelectionFromPasteboard): Updated for new OptionSet argument to
+          pasteWithPasteboard.
+        (WebCore::Editor::webContentFromPasteboard): Moved to EditorCocoa.mm.
+
+        * editing/win/EditorWin.cpp:
+        (WebCore::Editor::pasteWithPasteboard): Updated for new OptionSet argument, added a call to
+          quote the fragment if needed.
+
+        * editing/wpe/EditorWPE.cpp:
+        (WebCore::Editor::pasteWithPasteboard): Ditto.
+
 2018-09-11  Simon Fraser  <simon.fraser@apple.com>
 
         Make GraphicsLayers ref-counted, so their tree can persist when disconnected from RenderLayerBackings
index cf5da79..2d47dc7 100644 (file)
 #include "FrameView.h"
 #include "GraphicsContext.h"
 #include "HTMLAttachmentElement.h"
+#include "HTMLBRElement.h"
 #include "HTMLCollection.h"
 #include "HTMLFormControlElement.h"
 #include "HTMLFrameOwnerElement.h"
 #include "HTMLImageElement.h"
 #include "HTMLInputElement.h"
 #include "HTMLNames.h"
+#include "HTMLQuoteElement.h"
 #include "HTMLSpanElement.h"
 #include "HitTestResult.h"
 #include "IndentOutdentCommand.h"
@@ -327,6 +329,7 @@ enum class ClipboardEventKind {
     Cut,
     Paste,
     PasteAsPlainText,
+    PasteAsQuotation,
     BeforeCopy,
     BeforeCut,
     BeforePaste,
@@ -341,6 +344,7 @@ static AtomicString eventNameForClipboardEvent(ClipboardEventKind kind)
         return eventNames().cutEvent;
     case ClipboardEventKind::Paste:
     case ClipboardEventKind::PasteAsPlainText:
+    case ClipboardEventKind::PasteAsQuotation:
         return eventNames().pasteEvent;
     case ClipboardEventKind::BeforeCopy:
         return eventNames().beforecopyEvent;
@@ -369,6 +373,7 @@ static Ref<DataTransfer> createDataTransferForClipboardEvent(Document& document,
         }
         FALLTHROUGH;
     case ClipboardEventKind::Paste:
+    case ClipboardEventKind::PasteAsQuotation:
         return DataTransfer::createForCopyAndPaste(document, DataTransfer::StoreMode::Readonly, Pasteboard::createForCopyAndPaste());
     case ClipboardEventKind::BeforeCopy:
     case ClipboardEventKind::BeforeCut:
@@ -1404,7 +1409,7 @@ void Editor::paste(Pasteboard& pasteboard)
     updateMarkersForWordsAffectedByEditing(false);
     ResourceCacheValidationSuppressor validationSuppressor(document().cachedResourceLoader());
     if (m_frame.selection().selection().isContentRichlyEditable())
-        pasteWithPasteboard(&pasteboard, true);
+        pasteWithPasteboard(&pasteboard, { PasteOption::AllowPlainText });
     else
         pasteAsPlainTextWithPasteboard(pasteboard);
 }
@@ -1419,6 +1424,40 @@ void Editor::pasteAsPlainText()
     pasteAsPlainTextWithPasteboard(*Pasteboard::createForCopyAndPaste());
 }
 
+void Editor::pasteAsQuotation()
+{
+    if (!dispatchClipboardEvent(findEventTargetFromSelection(), ClipboardEventKind::PasteAsQuotation))
+        return;
+    if (!canPaste())
+        return;
+    updateMarkersForWordsAffectedByEditing(false);
+    ResourceCacheValidationSuppressor validationSuppressor(document().cachedResourceLoader());
+    auto pasteboard = Pasteboard::createForCopyAndPaste();
+    if (m_frame.selection().selection().isContentRichlyEditable())
+        pasteWithPasteboard(pasteboard.get(), { PasteOption::AllowPlainText, PasteOption::AsQuotation });
+    else
+        pasteAsPlainTextWithPasteboard(*pasteboard);
+}
+
+void Editor::quoteFragmentForPasting(DocumentFragment& fragment)
+{
+    auto blockQuote = HTMLQuoteElement::create(blockquoteTag, document());
+    blockQuote->setAttributeWithoutSynchronization(typeAttr, AtomicString("cite"));
+    blockQuote->setAttributeWithoutSynchronization(classAttr, AtomicString(ApplePasteAsQuotation));
+
+    auto childNode = fragment.firstChild();
+
+    if (childNode) {
+        while (childNode) {
+            blockQuote->appendChild(*childNode);
+            childNode = fragment.firstChild();
+        }
+    } else
+        blockQuote->appendChild(HTMLBRElement::create(document()));
+
+    fragment.appendChild(blockQuote);
+}
+
 void Editor::performDelete()
 {
     if (!canDelete()) {
index 282e62c..3323a33 100644 (file)
@@ -129,6 +129,12 @@ public:
     explicit Editor(Frame&);
     ~Editor();
 
+    enum class PasteOption : uint8_t {
+        AllowPlainText = 1 << 0,
+        IgnoreMailBlockquote = 1 << 1,
+        AsQuotation = 1 << 2,
+    };
+
     WEBCORE_EXPORT EditorClient* client() const;
     WEBCORE_EXPORT TextCheckerClient* textChecker() const;
 
@@ -158,6 +164,7 @@ public:
     WEBCORE_EXPORT void paste();
     void paste(Pasteboard&);
     WEBCORE_EXPORT void pasteAsPlainText();
+    void pasteAsQuotation();
     WEBCORE_EXPORT void performDelete();
 
     WEBCORE_EXPORT void copyURL(const URL&, const String& title);
@@ -519,9 +526,11 @@ private:
     bool canDeleteRange(Range*) const;
     bool canSmartReplaceWithPasteboard(Pasteboard&);
     void pasteAsPlainTextWithPasteboard(Pasteboard&);
-    void pasteWithPasteboard(Pasteboard*, bool allowPlainText, MailBlockquoteHandling = MailBlockquoteHandling::RespectBlockquote);
+    void pasteWithPasteboard(Pasteboard*, OptionSet<PasteOption>);
     String plainTextFromPasteboard(const PasteboardPlainText&);
 
+    void quoteFragmentForPasting(DocumentFragment&);
+
     void revealSelectionAfterEditingOperation(const ScrollAlignment& = ScrollAlignment::alignCenterIfNeeded, RevealExtentOption = DoNotRevealExtent);
     void markMisspellingsOrBadGrammar(const VisibleSelection&, bool checkSpelling, RefPtr<Range>& firstMisspellingRange);
     OptionSet<TextCheckingType> resolveTextCheckingTypeMask(const Node& rootEditableElement, OptionSet<TextCheckingType>);
index cd9ef76..ff1ad30 100644 (file)
@@ -926,6 +926,16 @@ static bool executePasteAsPlainText(Frame& frame, Event*, EditorCommandSource so
     return true;
 }
 
+static bool executePasteAsQuotation(Frame& frame, Event*, EditorCommandSource source, const String&)
+{
+    if (source == CommandFromMenuOrKeyBinding) {
+        UserTypingGestureIndicator typingGestureIndicator(frame);
+        frame.editor().pasteAsQuotation();
+    } else
+        frame.editor().pasteAsQuotation();
+    return true;
+}
+
 static bool executePrint(Frame& frame, Event*, EditorCommandSource, const String&)
 {
     Page* page = frame.page();
@@ -1636,6 +1646,7 @@ static const CommandMap& createCommandMap()
         { "Paste", { executePaste, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabledPaste } },
         { "PasteAndMatchStyle", { executePasteAndMatchStyle, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabledPaste } },
         { "PasteAsPlainText", { executePasteAsPlainText, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabledPaste } },
+        { "PasteAsQuotation", { executePasteAsQuotation, supportedPaste, enabledPaste, stateNone, valueNull, notTextInsertion, allowExecutionWhenDisabledPaste } },
         { "Print", { executePrint, supported, enabled, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "Redo", { executeRedo, supported, enabledRedo, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
         { "RemoveFormat", { executeRemoveFormat, supported, enabledRangeInEditableText, stateNone, valueNull, notTextInsertion, doNotAllowExecutionWhenDisabled } },
index 03f1115..e3771b7 100644 (file)
@@ -291,4 +291,14 @@ RefPtr<SharedBuffer> Editor::dataInRTFFormat(NSAttributedString *string)
     return nullptr;
 }
 
+// FIXME: Should give this function a name that makes it clear it adds resources to the document loader as a side effect.
+// Or refactor so it does not do that.
+RefPtr<DocumentFragment> Editor::webContentFromPasteboard(Pasteboard& pasteboard, Range& context, bool allowPlainText, bool& chosePlainText)
+{
+    WebContentReader reader(m_frame, context, allowPlainText);
+    pasteboard.read(reader);
+    chosePlainText = reader.madeFragmentFromPlainText;
+    return WTFMove(reader.fragment);
+}
+
 }
index be463a8..890bde6 100644 (file)
@@ -86,16 +86,20 @@ static RefPtr<DocumentFragment> createFragmentFromPasteboardData(Pasteboard& pas
     return nullptr;
 }
 
-void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText, MailBlockquoteHandling mailBlockquoteHandling)
+void Editor::pasteWithPasteboard(Pasteboard* pasteboard, OptionSet<PasteOption> options)
 {
     RefPtr<Range> range = selectedRange();
     if (!range)
         return;
 
     bool chosePlainText;
-    RefPtr<DocumentFragment> fragment = createFragmentFromPasteboardData(*pasteboard, m_frame, *range, allowPlainText, chosePlainText);
+    RefPtr<DocumentFragment> fragment = createFragmentFromPasteboardData(*pasteboard, m_frame, *range, options.contains(PasteOption::AllowPlainText), chosePlainText);
+
+    if (fragment && options.contains(PasteOption::AsQuotation))
+        quoteFragmentForPasting(*fragment);
+
     if (fragment && shouldInsertFragment(*fragment, range.get(), EditorInsertAction::Pasted))
-        pasteAsFragment(fragment.releaseNonNull(), canSmartReplaceWithPasteboard(*pasteboard), chosePlainText, mailBlockquoteHandling);
+        pasteAsFragment(fragment.releaseNonNull(), canSmartReplaceWithPasteboard(*pasteboard), chosePlainText, options.contains(PasteOption::IgnoreMailBlockquote) ? MailBlockquoteHandling::IgnoreBlockquote : MailBlockquoteHandling::RespectBlockquote);
 }
 
 static const AtomicString& elementURL(Element& element)
index a17892b..ce5c951 100644 (file)
@@ -215,19 +215,10 @@ void Editor::writeImageToPasteboard(Pasteboard& pasteboard, Element& imageElemen
     pasteboard.write(pasteboardImage);
 }
 
-// FIXME: Should give this function a name that makes it clear it adds resources to the document loader as a side effect.
-// Or refactor so it does not do that.
-RefPtr<DocumentFragment> Editor::webContentFromPasteboard(Pasteboard& pasteboard, Range& context, bool allowPlainText, bool& chosePlainText)
-{
-    WebContentReader reader(m_frame, context, allowPlainText);
-    pasteboard.read(reader);
-    chosePlainText = reader.madeFragmentFromPlainText;
-    return WTFMove(reader.fragment);
-}
-
-void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText, MailBlockquoteHandling mailBlockquoteHandling)
+void Editor::pasteWithPasteboard(Pasteboard* pasteboard, OptionSet<PasteOption> options)
 {
     RefPtr<Range> range = selectedRange();
+    bool allowPlainText = options.contains(PasteOption::AllowPlainText);
     WebContentReader reader(m_frame, *range, allowPlainText);
     int numberOfPasteboardItems = client()->getPasteboardItemsCount();
     for (int i = 0; i < numberOfPasteboardItems; ++i) {
@@ -243,8 +234,11 @@ void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText, Ma
         fragment = webContentFromPasteboard(*pasteboard, *range, allowPlainText, chosePlainTextIgnored);
     }
 
+    if (fragment && options.contains(PasteOption::AsQuotation))
+        quoteFragmentForPasting(*fragment);
+
     if (fragment && shouldInsertFragment(*fragment, range.get(), EditorInsertAction::Pasted))
-        pasteAsFragment(fragment.releaseNonNull(), canSmartReplaceWithPasteboard(*pasteboard), false, mailBlockquoteHandling);
+        pasteAsFragment(fragment.releaseNonNull(), canSmartReplaceWithPasteboard(*pasteboard), false, options.contains(PasteOption::IgnoreMailBlockquote) ? MailBlockquoteHandling::IgnoreBlockquote : MailBlockquoteHandling::RespectBlockquote);
 }
 
 void Editor::insertDictationPhrases(Vector<Vector<String>>&& dictationPhrases, RetainPtr<id> metadata)
index 4158ec7..f1cd53c 100644 (file)
@@ -77,7 +77,7 @@ void Editor::showColorPanel()
     [[NSApplication sharedApplication] orderFrontColorPanel:nil];
 }
 
-void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText, MailBlockquoteHandling mailBlockquoteHandling)
+void Editor::pasteWithPasteboard(Pasteboard* pasteboard, OptionSet<PasteOption> options)
 {
     RefPtr<Range> range = selectedRange();
 
@@ -87,10 +87,13 @@ void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText, Ma
     ALLOW_DEPRECATED_DECLARATIONS_END
 
     bool chosePlainText;
-    RefPtr<DocumentFragment> fragment = webContentFromPasteboard(*pasteboard, *range, allowPlainText, chosePlainText);
+    RefPtr<DocumentFragment> fragment = webContentFromPasteboard(*pasteboard, *range, options.contains(PasteOption::AllowPlainText), chosePlainText);
+
+    if (fragment && options.contains(PasteOption::AsQuotation))
+        quoteFragmentForPasting(*fragment);
 
     if (fragment && shouldInsertFragment(*fragment, range.get(), EditorInsertAction::Pasted))
-        pasteAsFragment(fragment.releaseNonNull(), canSmartReplaceWithPasteboard(*pasteboard), false, mailBlockquoteHandling);
+        pasteAsFragment(fragment.releaseNonNull(), canSmartReplaceWithPasteboard(*pasteboard), false, options.contains(PasteOption::IgnoreMailBlockquote) ? MailBlockquoteHandling::IgnoreBlockquote : MailBlockquoteHandling::RespectBlockquote );
 
     client()->setInsertionPasteboard(String());
 }
@@ -120,7 +123,7 @@ void Editor::readSelectionFromPasteboard(const String& pasteboardName)
 {
     Pasteboard pasteboard(pasteboardName);
     if (m_frame.selection().selection().isContentRichlyEditable())
-        pasteWithPasteboard(&pasteboard, true);
+        pasteWithPasteboard(&pasteboard, { PasteOption::AllowPlainText });
     else
         pasteAsPlainTextWithPasteboard(pasteboard);
 }
@@ -276,16 +279,6 @@ void Editor::writeImageToPasteboard(Pasteboard& pasteboard, Element& imageElemen
     pasteboard.write(pasteboardImage);
 }
 
-// FIXME: Should give this function a name that makes it clear it adds resources to the document loader as a side effect.
-// Or refactor so it does not do that.
-RefPtr<DocumentFragment> Editor::webContentFromPasteboard(Pasteboard& pasteboard, Range& context, bool allowPlainText, bool& chosePlainText)
-{
-    WebContentReader reader(m_frame, context, allowPlainText);
-    pasteboard.read(reader);
-    chosePlainText = reader.madeFragmentFromPlainText;
-    return WTFMove(reader.fragment);
-}
-
 } // namespace WebCore
 
 #endif // PLATFORM(MAC)
index 543a98a..0964ccb 100644 (file)
 
 namespace WebCore {
 
-void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText, MailBlockquoteHandling mailBlockquoteHandling)
+void Editor::pasteWithPasteboard(Pasteboard* pasteboard, OptionSet<PasteOption> options)
 {
     RefPtr<Range> range = selectedRange();
     if (!range)
         return;
 
     bool chosePlainText;
-    RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, *range, allowPlainText, chosePlainText);
+    RefPtr<DocumentFragment> fragment = pasteboard->documentFragment(m_frame, *range, options.contains(PasteOption::AllowPlainText), chosePlainText);
+
+    if (fragment && options.contains(PasteOption::AsQuotation))
+        quoteFragmentForPasting(*fragment);
+
     if (fragment && shouldInsertFragment(*fragment, range.get(), EditorInsertAction::Pasted))
-        pasteAsFragment(fragment.releaseNonNull(), canSmartReplaceWithPasteboard(*pasteboard), chosePlainText, mailBlockquoteHandling);
+        pasteAsFragment(fragment.releaseNonNull(), canSmartReplaceWithPasteboard(*pasteboard), chosePlainText, options.contains(PasteOption::IgnoreMailBlockquote) ? MailBlockquoteHandling::IgnoreBlockquote : MailBlockquoteHandling::RespectBlockquote);
 }
 
 template <typename PlatformDragData>
index bb0cfc9..60aada9 100644 (file)
@@ -73,16 +73,20 @@ void Editor::writeImageToPasteboard(Pasteboard&, Element&, const URL&, const Str
     notImplemented();
 }
 
-void Editor::pasteWithPasteboard(Pasteboard* pasteboard, bool allowPlainText, MailBlockquoteHandling mailBlockquoteHandling)
+void Editor::pasteWithPasteboard(Pasteboard* pasteboard, OptionSet<PasteOption> options)
 {
     RefPtr<Range> range = selectedRange();
     if (!range)
         return;
 
     bool chosePlainText;
-    RefPtr<DocumentFragment> fragment = createFragmentFromPasteboardData(*pasteboard, m_frame, *range, allowPlainText, chosePlainText);
+    RefPtr<DocumentFragment> fragment = createFragmentFromPasteboardData(*pasteboard, m_frame, *range, options.contains(PasteOption::AllowPlainText), chosePlainText);
+
+    if (fragment && options.contains(PasteOption::AsQuotation))
+        quoteFragmentForPasting(*fragment);
+
     if (fragment && shouldInsertFragment(*fragment, range.get(), EditorInsertAction::Pasted))
-        pasteAsFragment(*fragment, canSmartReplaceWithPasteboard(*pasteboard), chosePlainText, mailBlockquoteHandling);
+        pasteAsFragment(*fragment, canSmartReplaceWithPasteboard(*pasteboard), chosePlainText, options.contains(PasteOption::IgnoreMailBlockquote) ? MailBlockquoteHandling::IgnoreBlockquote : MailBlockquoteHandling::RespectBlockquote);
 }
 
 } // namespace WebCore
index 4a65db5..b3578f9 100644 (file)
@@ -1,3 +1,29 @@
+2018-09-12  Dan Bernstein  <mitz@apple.com>
+
+        [Cocoa] Complete support for Paste as Quotation
+        https://bugs.webkit.org/show_bug.cgi?id=189504
+
+        Reviewed by Wenson Hsieh.
+
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView canPerformAction:withSender:]): Handle _pasteAsQuotation:. It’s not included
+          in FOR_EACH_WKCONTENTVIEW_ACTION, because it’s declared and implemented in the WKPrivate
+          category. If we add more actions in the category, it could make sense to fold them into
+          a new FOR_EACH_PRIVATE_WKCONTENTVIEW_ACTION.
+        (-[WKWebView targetForAction:withSender:]): Handle _pasteAsQuotation:.
+        (-[WKWebView _pasteAsQuotation:]): Send to the WebViewImpl or the WKContentView.
+        * UIProcess/API/Cocoa/WKWebViewPrivate.h: Declared a new _pasteAsQuotation: action.
+
+        * UIProcess/Cocoa/WebViewImpl.mm:
+        (WebKit::selectorExceptionMap): Added a custom mapping from the new selector to the
+          PasteAsQuotation command.
+
+        * UIProcess/ios/WKContentViewInteraction.h: Declare methods for the new action.
+        * UIProcess/ios/WKContentViewInteraction.mm:
+          Forward _pasteAsQuotation: to the WKWebView so that clients get a chance to override its
+          behavior.
+        (-[WKContentView _pasteAsQuotationForWebView:]): Send the command to the page.
+
 2018-09-12  Sihui Liu  <sihui_liu@apple.com>
 
         Move IndexedDB to Network Process
index 2ccb013..31c9516 100644 (file)
@@ -1387,6 +1387,8 @@ FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKCONTENTVIEW)
 
     FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW)
 
+    FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW(_pasteAsQuotation)
+
     #undef FORWARD_CANPERFORMACTION_TO_WKCONTENTVIEW
 
     return [super canPerformAction:action withSender:sender];
@@ -1400,6 +1402,8 @@ FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKCONTENTVIEW)
 
     FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW)
 
+    FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW(_pasteAsQuotation)
+
     #undef FORWARD_TARGETFORACTION_TO_WKCONTENTVIEW
 
     return [super targetForAction:action withSender:sender];
@@ -4547,6 +4551,16 @@ WEBCORE_COMMAND(yankAndSelect)
 #endif
 }
 
+- (void)_pasteAsQuotation:(id)sender
+{
+#if PLATFORM(MAC)
+    _impl->executeEditCommandForSelector(_cmd);
+#else
+    if (self.usesStandardContentView)
+        [_contentView _pasteAsQuotationForWebView:sender];
+#endif
+}
+
 - (void)_evaluateJavaScriptWithoutUserGesture:(NSString *)javaScriptString completionHandler:(void (^)(id, NSError *))completionHandler
 {
     [self _evaluateJavaScript:javaScriptString forceUserGesture:NO completionHandler:completionHandler];
index 9b81723..f898cb9 100644 (file)
@@ -185,6 +185,8 @@ typedef NS_OPTIONS(NSUInteger, _WKRectEdge) {
 - (_WKAttachment *)_insertAttachmentWithFileWrapper:(NSFileWrapper *)fileWrapper contentType:(NSString *)contentType options:(_WKAttachmentDisplayOptions *)options completion:(void(^)(BOOL success))completionHandler WK_API_DEPRECATED_WITH_REPLACEMENT("-_insertAttachmentWithFileWrapper:contentType:completion:", macosx(WK_MAC_TBA, WK_MAC_TBA), ios(WK_IOS_TBA, WK_IOS_TBA));
 - (_WKAttachment *)_insertAttachmentWithFileWrapper:(NSFileWrapper *)fileWrapper contentType:(NSString *)contentType completion:(void(^)(BOOL success))completionHandler WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
 
+- (IBAction)_pasteAsQuotation:(id)sender WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+
 #if TARGET_OS_IPHONE
 // DERECATED: The setters of the three following function are deprecated, please use overrideLayoutParameters.
 // Define the smallest size a page take with a regular viewport.
index ac163a6..a6f6eca 100644 (file)
@@ -2624,7 +2624,10 @@ static const SelectorNameMap& selectorExceptionMap()
         { @selector(pageUp:), "MovePageUp"_s },
         { @selector(pageUpAndModifySelection:), "MovePageUpAndModifySelection"_s },
         { @selector(scrollPageDown:), "ScrollPageForward"_s },
-        { @selector(scrollPageUp:), "ScrollPageBackward"_s }
+        { @selector(scrollPageUp:), "ScrollPageBackward"_s },
+#if WK_API_ENABLED
+        { @selector(_pasteAsQuotation:), "PasteAsQuotation"_s },
+#endif
     };
 
     for (auto& name : names)
index d71562c..009be5c 100644 (file)
@@ -316,6 +316,7 @@ struct WKAutoCorrectionData {
 #define DECLARE_WKCONTENTVIEW_ACTION_FOR_WEB_VIEW(_action) \
     - (void)_action ## ForWebView:(id)sender;
 FOR_EACH_WKCONTENTVIEW_ACTION(DECLARE_WKCONTENTVIEW_ACTION_FOR_WEB_VIEW)
+DECLARE_WKCONTENTVIEW_ACTION_FOR_WEB_VIEW(_pasteAsQuotation)
 #undef DECLARE_WKCONTENTVIEW_ACTION_FOR_WEB_VIEW
 
 #if ENABLE(TOUCH_EVENTS)
index 6798766..603af17 100644 (file)
@@ -2161,6 +2161,8 @@ static void cancelPotentialTapIfNecessary(WKContentView* contentView)
 
 FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKWEBVIEW)
 
+FORWARD_ACTION_TO_WKWEBVIEW(_pasteAsQuotation)
+
 #undef FORWARD_ACTION_TO_WKWEBVIEW
 
 - (void)_lookupForWebView:(id)sender
@@ -2462,6 +2464,11 @@ FOR_EACH_WKCONTENTVIEW_ACTION(FORWARD_ACTION_TO_WKWEBVIEW)
     _page->executeEditCommand("paste"_s);
 }
 
+- (void)_pasteAsQuotationForWebView:(id)sender
+{
+    _page->executeEditCommand("PasteAsQuotation"_s);
+}
+
 - (void)selectForWebView:(id)sender
 {
     [_textSelectionAssistant selectWord];
index 016e777..f8e96e8 100644 (file)
@@ -1,3 +1,12 @@
+2018-09-12  Dan Bernstein  <mitz@apple.com>
+
+        [Cocoa] Complete support for Paste as Quotation
+        https://bugs.webkit.org/show_bug.cgi?id=189504
+
+        Reviewed by Wenson Hsieh.
+
+        * MiniBrowser/mac/MainMenu.xib: Added a Paste as Quotation command in the Edit menu.
+
 2018-09-12  Sihui Liu  <sihui_liu@apple.com>
 
         Move IndexedDB to Network Process
index 388a2f1..546a60f 100644 (file)
                                     <action selector="paste:" target="-1" id="226"/>
                                 </connections>
                             </menuItem>
+                            <menuItem title="Paste as Quotation" keyEquivalent="V" id="8Ng-DB-z0Y">
+                                <connections>
+                                    <action selector="_pasteAsQuotation:" target="-1" id="6Mn-XC-2qO"/>
+                                </connections>
+                            </menuItem>
                             <menuItem title="Paste and Match Style" keyEquivalent="V" id="485">
                                 <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                 <connections>