<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[224512] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/224512">224512</a></dd>
<dt>Author</dt> <dd>wenson_hsieh@apple.com</dd>
<dt>Date</dt> <dd>2017-11-06 13:46:54 -0800 (Mon, 06 Nov 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Attachment Support] Implement delegate hooks for attachment element insertion and removal
https://bugs.webkit.org/show_bug.cgi?id=179016
<rdar://problem/35250890>

Reviewed by Tim Horton.

Source/WebCore:

Implements a mechanism for notifying WebKit2 clients when attachment elements are inserted into or removed from
the document. See per-change comments below for more details.

API tests: WKAttachmentTests.AttachmentElementInsertion
           WKAttachmentTests.AttachmentUpdatesWhenInsertingAndDeletingNewline
           WKAttachmentTests.AttachmentUpdatesWhenUndoingAndRedoing
           WKAttachmentTests.AttachmentUpdatesWhenChangingFontStyles
           WKAttachmentTests.AttachmentUpdatesWhenInsertingLists
           WKAttachmentTests.AttachmentUpdatesWhenInsertingRichMarkup

* editing/Editor.cpp:
(WebCore::Editor::respondToChangedSelection):
(WebCore::Editor::editorUIUpdateTimerFired):

Additionally notify the client of any attachment updates.

(WebCore::Editor::scheduleEditorUIUpdate):

Add a new helper that starts the editor UI update timer with 0 delay, and use it everywhere we schedule an
editor UI update.

(WebCore::Editor::didInsertAttachmentElement):
(WebCore::Editor::didRemoveAttachmentElement):

Maintain two sets of attachment element identifiers -- the first one tracking insertions, and the second one
tracking removals. When an attachment element is inserted, we first check to see if that attachment element has
just been removed; if so, we don't add it to the inserted identifiers set, but instead remove it from the set of
removed identifiers. We perform a similar check in the opposite case. This prevents us from notifying the client
of extraneous insertions and removals during certain editing commands which may reparent and move attachment
elements around. In both cases, we schedule an editor UI update afterwards, where we will notify the client of
attachment updates.

(WebCore::Editor::notifyClientOfAttachmentUpdates):
(WebCore::Editor::insertAttachmentFromFile):
* editing/Editor.h:
* html/HTMLAttachmentElement.cpp:
(WebCore::HTMLAttachmentElement::HTMLAttachmentElement):

Remove the version of HTMLAttachmentElement's constructor that takes a unique identifier.

(WebCore::HTMLAttachmentElement::insertedIntoAncestor):
(WebCore::HTMLAttachmentElement::removedFromAncestor):

Implement these hooks to observe insertion into and removal from the DOM. If the element was attached to or
removed from an ancestor that was connected to the document, call out to the document's frame's editor. This
"document-connected" rule prevents us from calling out to the client in cases where (for instance) we append an
attachment element to a newly created DocumentFragment in preparation for executing a ReplaceSelectionCommand.

(WebCore::HTMLAttachmentElement::uniqueIdentifier const):
(WebCore::HTMLAttachmentElement::setUniqueIdentifier):

Refactor unique identifier to refer to the new attachment identifier attribute instead of a member variable.

* html/HTMLAttachmentElement.h:
* html/HTMLAttributeNames.in:

Add a new attribute representing an attachment element's identifier. This enables us to keep track of particular
attachments as they are destroyed and recreated as different objects, as a result of some editing commands.

* page/EditorClient.h:
(WebCore::EditorClient::didInsertAttachment):
(WebCore::EditorClient::didRemoveAttachment):

Add boilerplate editor client hooks for attachment insertion and removal.

Source/WebKit:

Adds boilerplate plumbing to WebEditorClient, WebPage, and the usual machinery in the UI process to notify
WebKit2 clients when attachment elements have been inserted or removed from the document. See the WebCore
ChangeLog for more details about the implementation, or the Tools ChangeLog for more information about new API
tests.

* UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
* UIProcess/API/Cocoa/WKWebView.mm:
(-[WKWebView _didInsertAttachment:]):
(-[WKWebView _didRemoveAttachment:]):
* UIProcess/API/Cocoa/WKWebViewInternal.h:
* UIProcess/Cocoa/PageClientImplCocoa.h:
* UIProcess/Cocoa/PageClientImplCocoa.mm:
(WebKit::PageClientImplCocoa::didInsertAttachment):
(WebKit::PageClientImplCocoa::didRemoveAttachment):
* UIProcess/PageClient.h:
(WebKit::PageClient::didInsertAttachment):
(WebKit::PageClient::didRemoveAttachment):
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::didInsertAttachment):
(WebKit::WebPageProxy::didRemoveAttachment):
* UIProcess/WebPageProxy.h:
* UIProcess/WebPageProxy.messages.in:
* WebProcess/WebCoreSupport/WebEditorClient.cpp:
(WebKit::WebEditorClient::didInsertAttachment):
(WebKit::WebEditorClient::didRemoveAttachment):
* WebProcess/WebCoreSupport/WebEditorClient.h:

Tools:

Introduces new API tests to check that various editing operations will or won't result in the new attachment
insertion and removal delegate hooks being fired. Additionally refactors an existing test to verify that
attachments insertion and removal is observable by the UI delegate.

* TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
(-[AttachmentUpdateObserver init]):
(-[AttachmentUpdateObserver inserted]):
(-[AttachmentUpdateObserver removed]):
(-[AttachmentUpdateObserver _webView:didInsertAttachment:]):
(-[AttachmentUpdateObserver _webView:didRemoveAttachment:]):
(TestWebKitAPI::ObserveAttachmentUpdatesForScope::ObserveAttachmentUpdatesForScope):
(TestWebKitAPI::ObserveAttachmentUpdatesForScope::~ObserveAttachmentUpdatesForScope):
(TestWebKitAPI::ObserveAttachmentUpdatesForScope::expectAttachmentUpdates):

Implement a testing mechanism to temporarily bind a UI delegate to a given WKWebView and listen for inserted or
removed attachments over the course of a particular scope. The API tests use this mechanism to check that the UI
delegate hooks added in this patch are invoked with the right attachments when performing edit commands.

(-[TestWKWebView _synchronouslyExecuteEditCommand:argument:]):
(-[TestWKWebView expectUpdatesAfterCommand:withArgument:expectedRemovals:expectedInsertions:]):
(TestWebKitAPI::TEST):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreeditingEditorcpp">trunk/Source/WebCore/editing/Editor.cpp</a></li>
<li><a href="#trunkSourceWebCoreeditingEditorh">trunk/Source/WebCore/editing/Editor.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLAttachmentElementcpp">trunk/Source/WebCore/html/HTMLAttachmentElement.cpp</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLAttachmentElementh">trunk/Source/WebCore/html/HTMLAttachmentElement.h</a></li>
<li><a href="#trunkSourceWebCorehtmlHTMLAttributeNamesin">trunk/Source/WebCore/html/HTMLAttributeNames.in</a></li>
<li><a href="#trunkSourceWebCorepageEditorClienth">trunk/Source/WebCore/page/EditorClient.h</a></li>
<li><a href="#trunkSourceWebKitChangeLog">trunk/Source/WebKit/ChangeLog</a></li>
<li><a href="#trunkSourceWebKitUIProcessAPICocoaWKUIDelegatePrivateh">trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h</a></li>
<li><a href="#trunkSourceWebKitUIProcessAPICocoaWKWebViewmm">trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm</a></li>
<li><a href="#trunkSourceWebKitUIProcessAPICocoaWKWebViewInternalh">trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h</a></li>
<li><a href="#trunkSourceWebKitUIProcessCocoaPageClientImplCocoah">trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.h</a></li>
<li><a href="#trunkSourceWebKitUIProcessCocoaPageClientImplCocoamm">trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.mm</a></li>
<li><a href="#trunkSourceWebKitUIProcessPageClienth">trunk/Source/WebKit/UIProcess/PageClient.h</a></li>
<li><a href="#trunkSourceWebKitUIProcessWebPageProxycpp">trunk/Source/WebKit/UIProcess/WebPageProxy.cpp</a></li>
<li><a href="#trunkSourceWebKitUIProcessWebPageProxyh">trunk/Source/WebKit/UIProcess/WebPageProxy.h</a></li>
<li><a href="#trunkSourceWebKitUIProcessWebPageProxymessagesin">trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in</a></li>
<li><a href="#trunkSourceWebKitWebProcessWebCoreSupportWebEditorClientcpp">trunk/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.cpp</a></li>
<li><a href="#trunkSourceWebKitWebProcessWebCoreSupportWebEditorClienth">trunk/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebKitCocoaWKAttachmentTestsmm">trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog   2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebCore/ChangeLog      2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -1,3 +1,76 @@
</span><ins>+2017-11-06  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Attachment Support] Implement delegate hooks for attachment element insertion and removal
+        https://bugs.webkit.org/show_bug.cgi?id=179016
+        <rdar://problem/35250890>
+
+        Reviewed by Tim Horton.
+
+        Implements a mechanism for notifying WebKit2 clients when attachment elements are inserted into or removed from
+        the document. See per-change comments below for more details.
+
+        API tests: WKAttachmentTests.AttachmentElementInsertion
+                   WKAttachmentTests.AttachmentUpdatesWhenInsertingAndDeletingNewline
+                   WKAttachmentTests.AttachmentUpdatesWhenUndoingAndRedoing
+                   WKAttachmentTests.AttachmentUpdatesWhenChangingFontStyles
+                   WKAttachmentTests.AttachmentUpdatesWhenInsertingLists
+                   WKAttachmentTests.AttachmentUpdatesWhenInsertingRichMarkup
+
+        * editing/Editor.cpp:
+        (WebCore::Editor::respondToChangedSelection):
+        (WebCore::Editor::editorUIUpdateTimerFired):
+
+        Additionally notify the client of any attachment updates.
+
+        (WebCore::Editor::scheduleEditorUIUpdate):
+
+        Add a new helper that starts the editor UI update timer with 0 delay, and use it everywhere we schedule an
+        editor UI update.
+
+        (WebCore::Editor::didInsertAttachmentElement):
+        (WebCore::Editor::didRemoveAttachmentElement):
+
+        Maintain two sets of attachment element identifiers -- the first one tracking insertions, and the second one
+        tracking removals. When an attachment element is inserted, we first check to see if that attachment element has
+        just been removed; if so, we don't add it to the inserted identifiers set, but instead remove it from the set of
+        removed identifiers. We perform a similar check in the opposite case. This prevents us from notifying the client
+        of extraneous insertions and removals during certain editing commands which may reparent and move attachment
+        elements around. In both cases, we schedule an editor UI update afterwards, where we will notify the client of
+        attachment updates.
+
+        (WebCore::Editor::notifyClientOfAttachmentUpdates):
+        (WebCore::Editor::insertAttachmentFromFile):
+        * editing/Editor.h:
+        * html/HTMLAttachmentElement.cpp:
+        (WebCore::HTMLAttachmentElement::HTMLAttachmentElement):
+
+        Remove the version of HTMLAttachmentElement's constructor that takes a unique identifier.
+
+        (WebCore::HTMLAttachmentElement::insertedIntoAncestor):
+        (WebCore::HTMLAttachmentElement::removedFromAncestor):
+
+        Implement these hooks to observe insertion into and removal from the DOM. If the element was attached to or
+        removed from an ancestor that was connected to the document, call out to the document's frame's editor. This
+        "document-connected" rule prevents us from calling out to the client in cases where (for instance) we append an
+        attachment element to a newly created DocumentFragment in preparation for executing a ReplaceSelectionCommand.
+
+        (WebCore::HTMLAttachmentElement::uniqueIdentifier const):
+        (WebCore::HTMLAttachmentElement::setUniqueIdentifier):
+
+        Refactor unique identifier to refer to the new attachment identifier attribute instead of a member variable.
+
+        * html/HTMLAttachmentElement.h:
+        * html/HTMLAttributeNames.in:
+
+        Add a new attribute representing an attachment element's identifier. This enables us to keep track of particular
+        attachments as they are destroyed and recreated as different objects, as a result of some editing commands.
+
+        * page/EditorClient.h:
+        (WebCore::EditorClient::didInsertAttachment):
+        (WebCore::EditorClient::didRemoveAttachment):
+
+        Add boilerplate editor client hooks for attachment insertion and removal.
+
</ins><span class="cx"> 2017-11-06  Ryan Haddad  <ryanhaddad@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Unreviewed, rolling out r224494.
</span></span></pre></div>
<a id="trunkSourceWebCoreeditingEditorcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/editing/Editor.cpp (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/editing/Editor.cpp  2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebCore/editing/Editor.cpp     2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -3428,7 +3428,7 @@
</span><span class="cx">     m_editorUIUpdateTimerShouldCheckSpellingAndGrammar = options & FrameSelection::CloseTyping
</span><span class="cx">         && !(options & FrameSelection::SpellCorrectionTriggered);
</span><span class="cx">     m_editorUIUpdateTimerWasTriggeredByDictation = options & FrameSelection::DictationTriggered;
</span><del>-    m_editorUIUpdateTimer.startOneShot(0_s);
</del><ins>+    scheduleEditorUIUpdate();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(TELEPHONE_NUMBER_DETECTION) && !PLATFORM(IOS)
</span><span class="lines">@@ -3601,6 +3601,10 @@
</span><span class="cx">         m_alternativeTextController->respondToChangedSelection(oldSelection);
</span><span class="cx"> 
</span><span class="cx">     m_oldSelectionForEditorUIUpdate = m_frame.selection().selection();
</span><ins>+
+#if ENABLE(ATTACHMENT_ELEMENT)
+    notifyClientOfAttachmentUpdates();
+#endif
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static Node* findFirstMarkable(Node* node)
</span><span class="lines">@@ -3733,8 +3737,48 @@
</span><span class="cx">     return TextIterator::subrange(*contextRange, result.location, result.length);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void Editor::scheduleEditorUIUpdate()
+{
+    m_editorUIUpdateTimer.startOneShot(0_s);
+}
+
</ins><span class="cx"> #if ENABLE(ATTACHMENT_ELEMENT)
</span><span class="cx"> 
</span><ins>+void Editor::didInsertAttachmentElement(HTMLAttachmentElement& attachment)
+{
+    auto identifier = attachment.uniqueIdentifier();
+    if (identifier.isEmpty())
+        return;
+
+    if (!m_removedAttachmentIdentifiers.take(identifier))
+        m_insertedAttachmentIdentifiers.add(identifier);
+    scheduleEditorUIUpdate();
+}
+
+void Editor::didRemoveAttachmentElement(HTMLAttachmentElement& attachment)
+{
+    auto identifier = attachment.uniqueIdentifier();
+    if (identifier.isEmpty())
+        return;
+
+    if (!m_insertedAttachmentIdentifiers.take(identifier))
+        m_removedAttachmentIdentifiers.add(identifier);
+    scheduleEditorUIUpdate();
+}
+
+void Editor::notifyClientOfAttachmentUpdates()
+{
+    if (auto* editorClient = client()) {
+        for (auto& identifier : m_removedAttachmentIdentifiers)
+            editorClient->didRemoveAttachment(identifier);
+        for (auto& identifier : m_insertedAttachmentIdentifiers)
+            editorClient->didInsertAttachment(identifier);
+    }
+
+    m_removedAttachmentIdentifiers.clear();
+    m_insertedAttachmentIdentifiers.clear();
+}
+
</ins><span class="cx"> void Editor::insertAttachment(const String& identifier, const String& filename, const String& filepath, std::optional<String> contentType)
</span><span class="cx"> {
</span><span class="cx">     if (!contentType)
</span><span class="lines">@@ -3751,7 +3795,7 @@
</span><span class="cx"> 
</span><span class="cx"> void Editor::insertAttachmentFromFile(const String& identifier, const String& filename, const String& contentType, Ref<File>&& file)
</span><span class="cx"> {
</span><del>-    auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document(), identifier);
</del><ins>+    auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document());
</ins><span class="cx">     attachment->setAttribute(HTMLNames::titleAttr, filename);
</span><span class="cx">     attachment->setAttribute(HTMLNames::subtitleAttr, fileSizeDescription(file->size()));
</span><span class="cx">     attachment->setAttribute(HTMLNames::typeAttr, contentType);
</span></span></pre></div>
<a id="trunkSourceWebCoreeditingEditorh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/editing/Editor.h (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/editing/Editor.h    2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebCore/editing/Editor.h       2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -503,6 +503,8 @@
</span><span class="cx"> #if ENABLE(ATTACHMENT_ELEMENT)
</span><span class="cx">     WEBCORE_EXPORT void insertAttachment(const String& identifier, const String& filename, const String& filepath, std::optional<String> contentType = std::nullopt);
</span><span class="cx">     WEBCORE_EXPORT void insertAttachment(const String& identifier, const String& filename, Ref<SharedBuffer>&& data, std::optional<String> contentType = std::nullopt);
</span><ins>+    void didInsertAttachmentElement(HTMLAttachmentElement&);
+    void didRemoveAttachmentElement(HTMLAttachmentElement&);
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> private:
</span><span class="lines">@@ -550,6 +552,12 @@
</span><span class="cx">     static RefPtr<SharedBuffer> dataInRTFFormat(NSAttributedString *);
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+    void scheduleEditorUIUpdate();
+
+#if ENABLE(ATTACHMENT_ELEMENT)
+    void notifyClientOfAttachmentUpdates();
+#endif
+
</ins><span class="cx">     void postTextStateChangeNotificationForCut(const String&, const VisibleSelection&);
</span><span class="cx"> 
</span><span class="cx">     Frame& m_frame;
</span><span class="lines">@@ -569,6 +577,11 @@
</span><span class="cx">     EditorParagraphSeparator m_defaultParagraphSeparator { EditorParagraphSeparatorIsDiv };
</span><span class="cx">     bool m_overwriteModeEnabled { false };
</span><span class="cx"> 
</span><ins>+#if ENABLE(ATTACHMENT_ELEMENT)
+    HashSet<String> m_insertedAttachmentIdentifiers;
+    HashSet<String> m_removedAttachmentIdentifiers;
+#endif
+
</ins><span class="cx">     VisibleSelection m_oldSelectionForEditorUIUpdate;
</span><span class="cx">     Timer m_editorUIUpdateTimer;
</span><span class="cx">     bool m_editorUIUpdateTimerShouldCheckSpellingAndGrammar { false };
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLAttachmentElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLAttachmentElement.cpp (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLAttachmentElement.cpp      2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebCore/html/HTMLAttachmentElement.cpp 2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -33,24 +33,17 @@
</span><span class="cx"> #include "Frame.h"
</span><span class="cx"> #include "HTMLNames.h"
</span><span class="cx"> #include "RenderAttachment.h"
</span><del>-#include <wtf/UUID.h>
</del><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> using namespace HTMLNames;
</span><span class="cx"> 
</span><del>-HTMLAttachmentElement::HTMLAttachmentElement(const QualifiedName& tagName, Document& document, const String& identifier)
</del><ins>+HTMLAttachmentElement::HTMLAttachmentElement(const QualifiedName& tagName, Document& document)
</ins><span class="cx">     : HTMLElement(tagName, document)
</span><del>-    , m_uniqueIdentifier(identifier)
</del><span class="cx"> {
</span><span class="cx">     ASSERT(hasTagName(attachmentTag));
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-HTMLAttachmentElement::HTMLAttachmentElement(const QualifiedName& tagName, Document& document)
-    : HTMLAttachmentElement(tagName, document, createCanonicalUUIDString())
-{
-}
-
</del><span class="cx"> HTMLAttachmentElement::~HTMLAttachmentElement() = default;
</span><span class="cx"> 
</span><span class="cx"> Ref<HTMLAttachmentElement> HTMLAttachmentElement::create(const QualifiedName& tagName, Document& document)
</span><span class="lines">@@ -58,11 +51,6 @@
</span><span class="cx">     return adoptRef(*new HTMLAttachmentElement(tagName, document));
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-Ref<HTMLAttachmentElement> HTMLAttachmentElement::create(const QualifiedName& tagName, Document& document, const String& identifier)
-{
-    return adoptRef(*new HTMLAttachmentElement(tagName, document, identifier));
-}
-
</del><span class="cx"> RenderPtr<RenderElement> HTMLAttachmentElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
</span><span class="cx"> {
</span><span class="cx">     return createRenderer<RenderAttachment>(*this, WTFMove(style));
</span><span class="lines">@@ -81,6 +69,25 @@
</span><span class="cx">         renderer->invalidate();
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+Node::InsertedIntoAncestorResult HTMLAttachmentElement::insertedIntoAncestor(InsertionType type, ContainerNode& ancestor)
+{
+    auto result = HTMLElement::insertedIntoAncestor(type, ancestor);
+    if (auto* frame = document().frame()) {
+        if (type.connectedToDocument)
+            frame->editor().didInsertAttachmentElement(*this);
+    }
+    return result;
+}
+
+void HTMLAttachmentElement::removedFromAncestor(RemovalType type, ContainerNode& ancestor)
+{
+    HTMLElement::removedFromAncestor(type, ancestor);
+    if (auto* frame = document().frame()) {
+        if (type.disconnectedFromDocument)
+            frame->editor().didRemoveAttachmentElement(*this);
+    }
+}
+
</ins><span class="cx"> void HTMLAttachmentElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
</span><span class="cx"> {
</span><span class="cx">     if (name == progressAttr || name == subtitleAttr || name == titleAttr || name == typeAttr) {
</span><span class="lines">@@ -91,6 +98,16 @@
</span><span class="cx">     HTMLElement::parseAttribute(name, value);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+String HTMLAttachmentElement::uniqueIdentifier() const
+{
+    return attributeWithoutSynchronization(HTMLNames::webkitattachmentidAttr);
+}
+
+void HTMLAttachmentElement::setUniqueIdentifier(const String& identifier)
+{
+    setAttributeWithoutSynchronization(HTMLNames::webkitattachmentidAttr, identifier);
+}
+
</ins><span class="cx"> String HTMLAttachmentElement::attachmentTitle() const
</span><span class="cx"> {
</span><span class="cx">     auto& title = attributeWithoutSynchronization(titleAttr);
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLAttachmentElementh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLAttachmentElement.h (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLAttachmentElement.h        2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebCore/html/HTMLAttachmentElement.h   2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -37,14 +37,16 @@
</span><span class="cx"> class HTMLAttachmentElement final : public HTMLElement {
</span><span class="cx"> public:
</span><span class="cx">     static Ref<HTMLAttachmentElement> create(const QualifiedName&, Document&);
</span><del>-    static Ref<HTMLAttachmentElement> create(const QualifiedName&, Document&, const String& identifier);
</del><span class="cx"> 
</span><span class="cx">     WEBCORE_EXPORT File* file() const;
</span><span class="cx">     void setFile(File*);
</span><span class="cx"> 
</span><del>-    WEBCORE_EXPORT String uniqueIdentifier() const { return m_uniqueIdentifier; }
-    void setUniqueIdentifier(const String& identifier) { m_uniqueIdentifier = identifier; }
</del><ins>+    WEBCORE_EXPORT String uniqueIdentifier() const;
+    void setUniqueIdentifier(const String&);
</ins><span class="cx"> 
</span><ins>+    InsertedIntoAncestorResult insertedIntoAncestor(InsertionType, ContainerNode&) final;
+    void removedFromAncestor(RemovalType, ContainerNode&) final;
+
</ins><span class="cx">     WEBCORE_EXPORT String attachmentTitle() const;
</span><span class="cx">     String attachmentType() const;
</span><span class="cx"> 
</span><span class="lines">@@ -52,7 +54,6 @@
</span><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     HTMLAttachmentElement(const QualifiedName&, Document&);
</span><del>-    HTMLAttachmentElement(const QualifiedName&, Document&, const String& identifier);
</del><span class="cx">     virtual ~HTMLAttachmentElement();
</span><span class="cx"> 
</span><span class="cx">     RenderPtr<RenderElement> createElementRenderer(RenderStyle&&, const RenderTreePosition&) final;
</span><span class="lines">@@ -68,7 +69,6 @@
</span><span class="cx">     void parseAttribute(const QualifiedName&, const AtomicString&) final;
</span><span class="cx">     
</span><span class="cx">     RefPtr<File> m_file;
</span><del>-    String m_uniqueIdentifier;
</del><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorehtmlHTMLAttributeNamesin"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/html/HTMLAttributeNames.in (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/html/HTMLAttributeNames.in  2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebCore/html/HTMLAttributeNames.in     2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -381,6 +381,7 @@
</span><span class="cx"> vlink
</span><span class="cx"> vspace
</span><span class="cx"> webkitallowfullscreen
</span><ins>+webkitattachmentid
</ins><span class="cx"> webkitattachmentpath
</span><span class="cx"> webkitdirectory
</span><span class="cx"> width
</span></span></pre></div>
<a id="trunkSourceWebCorepageEditorClienth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/page/EditorClient.h (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/page/EditorClient.h 2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebCore/page/EditorClient.h    2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -72,6 +72,11 @@
</span><span class="cx">     virtual void didApplyStyle() = 0;
</span><span class="cx">     virtual bool shouldMoveRangeAfterDelete(Range*, Range*) = 0;
</span><span class="cx"> 
</span><ins>+#if ENABLE(ATTACHMENT_ELEMENT)
+    virtual void didInsertAttachment(const String&) { }
+    virtual void didRemoveAttachment(const String&) { }
+#endif
+
</ins><span class="cx">     virtual void didBeginEditing() = 0;
</span><span class="cx">     virtual void respondToChangedContents() = 0;
</span><span class="cx">     virtual void respondToChangedSelection(Frame*) = 0;
</span></span></pre></div>
<a id="trunkSourceWebKitChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/ChangeLog (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/ChangeLog    2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/ChangeLog       2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -1,3 +1,38 @@
</span><ins>+2017-11-06  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Attachment Support] Implement delegate hooks for attachment element insertion and removal
+        https://bugs.webkit.org/show_bug.cgi?id=179016
+        <rdar://problem/35250890>
+
+        Reviewed by Tim Horton.
+
+        Adds boilerplate plumbing to WebEditorClient, WebPage, and the usual machinery in the UI process to notify
+        WebKit2 clients when attachment elements have been inserted or removed from the document. See the WebCore
+        ChangeLog for more details about the implementation, or the Tools ChangeLog for more information about new API
+        tests.
+
+        * UIProcess/API/Cocoa/WKUIDelegatePrivate.h:
+        * UIProcess/API/Cocoa/WKWebView.mm:
+        (-[WKWebView _didInsertAttachment:]):
+        (-[WKWebView _didRemoveAttachment:]):
+        * UIProcess/API/Cocoa/WKWebViewInternal.h:
+        * UIProcess/Cocoa/PageClientImplCocoa.h:
+        * UIProcess/Cocoa/PageClientImplCocoa.mm:
+        (WebKit::PageClientImplCocoa::didInsertAttachment):
+        (WebKit::PageClientImplCocoa::didRemoveAttachment):
+        * UIProcess/PageClient.h:
+        (WebKit::PageClient::didInsertAttachment):
+        (WebKit::PageClient::didRemoveAttachment):
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::didInsertAttachment):
+        (WebKit::WebPageProxy::didRemoveAttachment):
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/WebPageProxy.messages.in:
+        * WebProcess/WebCoreSupport/WebEditorClient.cpp:
+        (WebKit::WebEditorClient::didInsertAttachment):
+        (WebKit::WebEditorClient::didRemoveAttachment):
+        * WebProcess/WebCoreSupport/WebEditorClient.h:
+
</ins><span class="cx"> 2017-11-06  Jeremy Jones  <jeremyj@apple.com>
</span><span class="cx"> 
</span><span class="cx">         iOS element fullscreen should use a UIGestureRecognizer to detect user interaction.
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessAPICocoaWKUIDelegatePrivateh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h    2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h       2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -110,6 +110,9 @@
</span><span class="cx"> - (void)_webView:(WKWebView *)webView runBeforeUnloadConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL result))completionHandler WK_API_AVAILABLE(macosx(10.13), ios(11.0));
</span><span class="cx"> - (void)_webView:(WKWebView *)webView editorStateDidChange:(NSDictionary *)editorState WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
</span><span class="cx"> 
</span><ins>+- (void)_webView:(WKWebView *)webView didRemoveAttachment:(_WKAttachment *)attachment WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+- (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA));
+
</ins><span class="cx"> #if TARGET_OS_IPHONE
</span><span class="cx"> - (BOOL)_webView:(WKWebView *)webView shouldIncludeAppLinkActionsForElement:(_WKActivatedElementInfo *)element WK_API_AVAILABLE(ios(9.0));
</span><span class="cx"> - (NSArray *)_webView:(WKWebView *)webView actionsForElement:(_WKActivatedElementInfo *)element defaultActions:(NSArray<_WKElementAction *> *)defaultActions;
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessAPICocoaWKWebViewmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm     2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm        2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -1197,6 +1197,24 @@
</span><span class="cx">         [uiDelegate _webView:self editorStateDidChange:dictionaryRepresentationForEditorState(_page->editorState())];
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if ENABLE(ATTACHMENT_ELEMENT)
+
+- (void)_didInsertAttachment:(NSString *)identifier
+{
+    id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
+    if ([uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:)])
+        [uiDelegate _webView:self didInsertAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease]];
+}
+
+- (void)_didRemoveAttachment:(NSString *)identifier
+{
+    id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate;
+    if ([uiDelegate respondsToSelector:@selector(_webView:didRemoveAttachment:)])
+        [uiDelegate _webView:self didRemoveAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease]];
+}
+
+#endif // ENABLE(ATTACHMENT_ELEMENT)
+
</ins><span class="cx"> #pragma mark iOS-specific methods
</span><span class="cx"> 
</span><span class="cx"> #if PLATFORM(IOS)
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessAPICocoaWKWebViewInternalh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h      2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h 2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -148,6 +148,11 @@
</span><span class="cx"> @property (nonatomic, readonly) UIEdgeInsets _computedUnobscuredSafeAreaInset;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#if ENABLE(ATTACHMENT_ELEMENT)
+- (void)_didRemoveAttachment:(NSString *)identifier;
+- (void)_didInsertAttachment:(NSString *)identifier;
+#endif
+
</ins><span class="cx"> - (WKPageRef)_pageForTesting;
</span><span class="cx"> - (WebKit::WebPageProxy*)_page;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessCocoaPageClientImplCocoah"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.h (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.h        2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.h   2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -37,6 +37,12 @@
</span><span class="cx">         : m_webView(webView) { }
</span><span class="cx">     void isPlayingAudioWillChange() final;
</span><span class="cx">     void isPlayingAudioDidChange() final;
</span><ins>+
+#if ENABLE(ATTACHMENT_ELEMENT)
+    void didInsertAttachment(const String& identifier) final;
+    void didRemoveAttachment(const String& identifier) final;
+#endif
+
</ins><span class="cx"> protected:
</span><span class="cx">     WKWebView *m_webView;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessCocoaPageClientImplCocoamm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.mm (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.mm       2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.mm  2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -26,7 +26,7 @@
</span><span class="cx"> #import "config.h"
</span><span class="cx"> #import "PageClientImplCocoa.h"
</span><span class="cx"> 
</span><del>-#import "WKWebViewPrivate.h"
</del><ins>+#import "WKWebViewInternal.h"
</ins><span class="cx"> 
</span><span class="cx"> namespace WebKit {
</span><span class="cx"> 
</span><span class="lines">@@ -43,5 +43,27 @@
</span><span class="cx">     [m_webView didChangeValueForKey:NSStringFromSelector(@selector(_isPlayingAudio))];
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><ins>+
+#if ENABLE(ATTACHMENT_ELEMENT)
+
+void PageClientImplCocoa::didInsertAttachment(const String& identifier)
+{
+#if WK_API_ENABLED
+    [m_webView _didInsertAttachment:identifier];
+#else
+    UNUSED_PARAM(identifier);
+#endif
+}
+
+void PageClientImplCocoa::didRemoveAttachment(const String& identifier)
+{
+#if WK_API_ENABLED
+    [m_webView _didRemoveAttachment:identifier];
+#else
+    UNUSED_PARAM(identifier);
+#endif
+}
+
+#endif
</ins><span class="cx">     
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessPageClienth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/PageClient.h (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/PageClient.h       2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/UIProcess/PageClient.h  2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -384,6 +384,11 @@
</span><span class="cx">     virtual void didChangeDataInteractionCaretRect(const WebCore::IntRect& previousCaretRect, const WebCore::IntRect& caretRect) = 0;
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#if ENABLE(ATTACHMENT_ELEMENT)
+    virtual void didInsertAttachment(const String& identifier) { }
+    virtual void didRemoveAttachment(const String& identifier) { }
+#endif
+
</ins><span class="cx"> #if PLATFORM(GTK) || PLATFORM(WPE)
</span><span class="cx">     virtual JSGlobalContextRef javascriptGlobalContext() { return nullptr; }
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessWebPageProxycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.cpp (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/WebPageProxy.cpp   2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.cpp      2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -7113,6 +7113,16 @@
</span><span class="cx">     m_process->send(Messages::WebPage::InsertAttachment(identifier, filename, contentType, IPC::SharedBufferDataReference { &data }, callbackID), m_pageID);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void WebPageProxy::didInsertAttachment(const String& identifier)
+{
+    m_pageClient.didInsertAttachment(identifier);
+}
+
+void WebPageProxy::didRemoveAttachment(const String& identifier)
+{
+    m_pageClient.didRemoveAttachment(identifier);
+}
+
</ins><span class="cx"> #endif // ENABLE(ATTACHMENT_ELEMENT)
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebKit
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessWebPageProxyh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/WebPageProxy.h     2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h        2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -1637,6 +1637,11 @@
</span><span class="cx"> 
</span><span class="cx">     void stopAllURLSchemeTasks();
</span><span class="cx"> 
</span><ins>+#if ENABLE(ATTACHMENT_ELEMENT)
+    void didInsertAttachment(const String& identifier);
+    void didRemoveAttachment(const String& identifier);
+#endif
+
</ins><span class="cx">     PageClient& m_pageClient;
</span><span class="cx">     Ref<API::PageConfiguration> m_configuration;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebKitUIProcessWebPageProxymessagesin"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in   2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in      2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -498,4 +498,9 @@
</span><span class="cx">     StopURLSchemeTask(uint64_t handlerIdentifier, uint64_t taskIdentifier)
</span><span class="cx"> 
</span><span class="cx">     RequestStorageAccess(String subFrameHost, String topFrameHost, uint64_t contextID)
</span><ins>+
+#if ENABLE(ATTACHMENT_ELEMENT)
+    DidInsertAttachment(String identifier)
+    DidRemoveAttachment(String identifier)
+#endif
</ins><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebKitWebProcessWebCoreSupportWebEditorClientcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.cpp (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.cpp        2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.cpp   2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -157,6 +157,20 @@
</span><span class="cx">     return result;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+#if ENABLE(ATTACHMENT_ELEMENT)
+
+void WebEditorClient::didInsertAttachment(const String& identifier)
+{
+    m_page->send(Messages::WebPageProxy::DidInsertAttachment(identifier));
+}
+
+void WebEditorClient::didRemoveAttachment(const String& identifier)
+{
+    m_page->send(Messages::WebPageProxy::DidRemoveAttachment(identifier));
+}
+
+#endif
+
</ins><span class="cx"> void WebEditorClient::didApplyStyle()
</span><span class="cx"> {
</span><span class="cx">     m_page->didApplyStyle();
</span></span></pre></div>
<a id="trunkSourceWebKitWebProcessWebCoreSupportWebEditorClienth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h  2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h     2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -59,6 +59,11 @@
</span><span class="cx">     void didApplyStyle() final;
</span><span class="cx">     bool shouldMoveRangeAfterDelete(WebCore::Range*, WebCore::Range*) final;
</span><span class="cx"> 
</span><ins>+#if ENABLE(ATTACHMENT_ELEMENT)
+    void didInsertAttachment(const String& identifier) final;
+    void didRemoveAttachment(const String& identifier) final;
+#endif
+
</ins><span class="cx">     void didBeginEditing() final;
</span><span class="cx">     void respondToChangedContents() final;
</span><span class="cx">     void respondToChangedSelection(WebCore::Frame*) final;
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog    2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Tools/ChangeLog       2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -1,3 +1,33 @@
</span><ins>+2017-11-06  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [Attachment Support] Implement delegate hooks for attachment element insertion and removal
+        https://bugs.webkit.org/show_bug.cgi?id=179016
+        <rdar://problem/35250890>
+
+        Reviewed by Tim Horton.
+
+        Introduces new API tests to check that various editing operations will or won't result in the new attachment
+        insertion and removal delegate hooks being fired. Additionally refactors an existing test to verify that
+        attachments insertion and removal is observable by the UI delegate.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm:
+        (-[AttachmentUpdateObserver init]):
+        (-[AttachmentUpdateObserver inserted]):
+        (-[AttachmentUpdateObserver removed]):
+        (-[AttachmentUpdateObserver _webView:didInsertAttachment:]):
+        (-[AttachmentUpdateObserver _webView:didRemoveAttachment:]):
+        (TestWebKitAPI::ObserveAttachmentUpdatesForScope::ObserveAttachmentUpdatesForScope):
+        (TestWebKitAPI::ObserveAttachmentUpdatesForScope::~ObserveAttachmentUpdatesForScope):
+        (TestWebKitAPI::ObserveAttachmentUpdatesForScope::expectAttachmentUpdates):
+
+        Implement a testing mechanism to temporarily bind a UI delegate to a given WKWebView and listen for inserted or
+        removed attachments over the course of a particular scope. The API tests use this mechanism to check that the UI
+        delegate hooks added in this patch are invoked with the right attachments when performing edit commands.
+
+        (-[TestWKWebView _synchronouslyExecuteEditCommand:argument:]):
+        (-[TestWKWebView expectUpdatesAfterCommand:withArgument:expectedRemovals:expectedInsertions:]):
+        (TestWebKitAPI::TEST):
+
</ins><span class="cx"> 2017-11-06  Christopher Reid  <chris.reid@sony.com>
</span><span class="cx"> 
</span><span class="cx">         Use enum classes within FileSystem
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebKitCocoaWKAttachmentTestsmm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm (224511 => 224512)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm 2017-11-06 21:44:34 UTC (rev 224511)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm    2017-11-06 21:46:54 UTC (rev 224512)
</span><span class="lines">@@ -33,6 +33,90 @@
</span><span class="cx"> 
</span><span class="cx"> #if WK_API_ENABLED
</span><span class="cx"> 
</span><ins>+@interface AttachmentUpdateObserver : NSObject <WKUIDelegatePrivate>
+@property (nonatomic, readonly) NSArray *inserted;
+@property (nonatomic, readonly) NSArray *removed;
+@end
+
+@implementation AttachmentUpdateObserver {
+    RetainPtr<NSMutableArray<_WKAttachment *>> _inserted;
+    RetainPtr<NSMutableArray<_WKAttachment *>> _removed;
+}
+
+- (instancetype)init
+{
+    if (self = [super init]) {
+        _inserted = adoptNS([[NSMutableArray alloc] init]);
+        _removed = adoptNS([[NSMutableArray alloc] init]);
+    }
+    return self;
+}
+
+- (NSArray<_WKAttachment *> *)inserted
+{
+    return _inserted.get();
+}
+
+- (NSArray<_WKAttachment *> *)removed
+{
+    return _removed.get();
+}
+
+- (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment
+{
+    [_inserted addObject:attachment];
+}
+
+- (void)_webView:(WKWebView *)webView didRemoveAttachment:(_WKAttachment *)attachment
+{
+    [_removed addObject:attachment];
+}
+
+@end
+
+namespace TestWebKitAPI {
+
+class ObserveAttachmentUpdatesForScope {
+public:
+    ObserveAttachmentUpdatesForScope(TestWKWebView *webView)
+        : m_webView(webView)
+    {
+        m_previousDelegate = retainPtr(webView.UIDelegate);
+        m_observer = adoptNS([[AttachmentUpdateObserver alloc] init]);
+        webView.UIDelegate = m_observer.get();
+    }
+
+    ~ObserveAttachmentUpdatesForScope()
+    {
+        [m_webView setUIDelegate:m_previousDelegate.get()];
+    }
+
+    AttachmentUpdateObserver *observer() const { return m_observer.get(); }
+
+    void expectAttachmentUpdates(NSArray<_WKAttachment *> *removed, NSArray<_WKAttachment *> *inserted)
+    {
+        BOOL removedAttachmentsMatch = [observer().removed isEqual:removed];
+        if (!removedAttachmentsMatch)
+            NSLog(@"Expected removed attachments: %@ to match %@.", observer().removed, removed);
+        EXPECT_TRUE(removedAttachmentsMatch);
+
+        BOOL insertedAttachmentsMatch = [observer().inserted isEqual:inserted];
+        if (!insertedAttachmentsMatch)
+            NSLog(@"Expected inserted attachments: %@ to match %@.", observer().inserted, inserted);
+        EXPECT_TRUE(insertedAttachmentsMatch);
+    }
+
+private:
+    RetainPtr<AttachmentUpdateObserver> m_observer;
+    RetainPtr<TestWKWebView> m_webView;
+    RetainPtr<id> m_previousDelegate;
+};
+
+}
+
+@interface TestWKWebView (AttachmentTesting)
+@end
+
</ins><span class="cx"> static RetainPtr<TestWKWebView> webViewForTestingAttachments()
</span><span class="cx"> {
</span><span class="cx">     auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
</span><span class="lines">@@ -57,6 +141,18 @@
</span><span class="cx"> 
</span><span class="cx"> @implementation TestWKWebView (AttachmentTesting)
</span><span class="cx"> 
</span><ins>+- (BOOL)_synchronouslyExecuteEditCommand:(NSString *)command argument:(NSString *)argument
+{
+    __block bool done = false;
+    __block bool success;
+    [self _executeEditCommand:command argument:argument completion:^(BOOL completionSuccess) {
+        done = true;
+        success = completionSuccess;
+    }];
+    TestWebKitAPI::Util::run(&done);
+    return success;
+}
+
</ins><span class="cx"> - (_WKAttachment *)synchronouslyInsertAttachmentWithFilename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data options:(_WKAttachmentDisplayOptions *)options
</span><span class="cx"> {
</span><span class="cx">     __block bool done = false;
</span><span class="lines">@@ -72,6 +168,13 @@
</span><span class="cx">     return [self stringByEvaluatingJavaScript:[NSString stringWithFormat:@"document.querySelector('%@').getAttribute('%@')", querySelector, attributeName]];
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+- (void)expectUpdatesAfterCommand:(NSString *)command withArgument:(NSString *)argument expectedRemovals:(NSArray<_WKAttachment *> *)removed expectedInsertions:(NSArray<_WKAttachment *> *)inserted
+{
+    TestWebKitAPI::ObserveAttachmentUpdatesForScope observer(self);
+    EXPECT_TRUE([self _synchronouslyExecuteEditCommand:command argument:argument]);
+    observer.expectAttachmentUpdates(removed, inserted);
+}
+
</ins><span class="cx"> @end
</span><span class="cx"> 
</span><span class="cx"> namespace TestWebKitAPI {
</span><span class="lines">@@ -79,21 +182,116 @@
</span><span class="cx"> TEST(WKAttachmentTests, AttachmentElementInsertion)
</span><span class="cx"> {
</span><span class="cx">     auto webView = webViewForTestingAttachments();
</span><ins>+    RetainPtr<_WKAttachment> firstAttachment;
+    RetainPtr<_WKAttachment> secondAttachment;
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        // Use the given content type for the attachment element's type.
+        firstAttachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo" contentType:@"text/html" data:testHTMLData() options:nil]);
+        EXPECT_WK_STREQ(@"foo", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
+        EXPECT_WK_STREQ(@"text/html", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
+        EXPECT_WK_STREQ(@"38 bytes", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
+        observer.expectAttachmentUpdates(@[ ], @[ firstAttachment.get() ]);
+    }
+    {
+        ObserveAttachmentUpdatesForScope scope(webView.get());
+        // Since no content type is explicitly specified, compute it from the file extension.
+        [webView _executeEditCommand:@"DeleteBackward" argument:nil completion:nil];
+        secondAttachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"bar.png" contentType:nil data:testImageData() options:nil]);
+        EXPECT_WK_STREQ(@"bar.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
+        EXPECT_WK_STREQ(@"image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
+        EXPECT_WK_STREQ(@"37 KB", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
+        scope.expectAttachmentUpdates(@[ firstAttachment.get() ], @[ secondAttachment.get() ]);
+    }
+}
</ins><span class="cx"> 
</span><del>-    // Use the given content type for the attachment element's type.
-    [webView synchronouslyInsertAttachmentWithFilename:@"foo" contentType:@"text/html" data:testHTMLData() options:nil];
-    EXPECT_WK_STREQ(@"foo", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
-    EXPECT_WK_STREQ(@"text/html", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
-    EXPECT_WK_STREQ(@"38 bytes", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
</del><ins>+TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingAndDeletingNewline)
+{
+    auto webView = webViewForTestingAttachments();
+    RetainPtr<_WKAttachment> attachment;
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil]);
+        observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
+    }
+    [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+    [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+    [webView stringByEvaluatingJavaScript:@"getSelection().collapse(document.body)"];
+    [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+    [webView expectUpdatesAfterCommand:@"DeleteForward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
+}
</ins><span class="cx"> 
</span><del>-    // Since no content type is explicitly specified, compute it from the file extension.
-    [webView _executeEditCommand:@"DeleteBackward" argument:nil completion:nil];
-    [webView synchronouslyInsertAttachmentWithFilename:@"bar.png" contentType:nil data:testImageData() options:nil];
-    EXPECT_WK_STREQ(@"bar.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]);
-    EXPECT_WK_STREQ(@"image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]);
-    EXPECT_WK_STREQ(@"37 KB", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]);
</del><ins>+TEST(WKAttachmentTests, AttachmentUpdatesWhenUndoingAndRedoing)
+{
+    auto webView = webViewForTestingAttachments();
+    RetainPtr<_WKAttachment> attachment;
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil]);
+        observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
+    }
+    [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
+    [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
+    [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
+    [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]];
+    [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+TEST(WKAttachmentTests, AttachmentUpdatesWhenChangingFontStyles)
+{
+    auto webView = webViewForTestingAttachments();
+    RetainPtr<_WKAttachment> attachment;
+    [webView _synchronouslyExecuteEditCommand:@"InsertText" argument:@"Hello"];
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil]);
+        observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
+    }
+    [webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"World" expectedRemovals:@[] expectedInsertions:@[]];
+    [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil];
+    [webView expectUpdatesAfterCommand:@"ToggleBold" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+    [webView expectUpdatesAfterCommand:@"ToggleItalic" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+    [webView expectUpdatesAfterCommand:@"ToggleUnderline" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+
+    // Inserting text should delete the current selection, removing the attachment in the process.
+    [webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"foo" expectedRemovals:@[attachment.get()] expectedInsertions:@[]];
+}
+
+TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingLists)
+{
+    auto webView = webViewForTestingAttachments();
+    RetainPtr<_WKAttachment> attachment;
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil]);
+        observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
+        NSLog(@"The markup is now %@", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]);
+    }
+    [webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+    // This edit command behaves more like a "toggle", and will actually break us out of the list we just inserted.
+    [webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+    [webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+    [webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]];
+}
+
+TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingRichMarkup)
+{
+    auto webView = webViewForTestingAttachments();
+    RetainPtr<_WKAttachment> attachment;
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        [webView _synchronouslyExecuteEditCommand:@"InsertHTML" argument:@"<div><strong><attachment title='a' webkitattachmentid='a06fec41-9aa0-4c2c-ba3a-0149b54aad99'></attachment></strong></div>"];
+        attachment = observer.observer().inserted[0];
+        observer.expectAttachmentUpdates(@[ ], @[attachment.get()]);
+    }
+    {
+        ObserveAttachmentUpdatesForScope observer(webView.get());
+        [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').remove()"];
+        [webView waitForNextPresentationUpdate];
+        observer.expectAttachmentUpdates(@[attachment.get()], @[ ]);
+    }
+}
+
</ins><span class="cx"> } // namespace TestWebKitAPI
</span><span class="cx"> 
</span><span class="cx"> #endif // WK_API_ENABLED
</span></span></pre>
</div>
</div>

</body>
</html>