Changeset 224512 in webkit
- Timestamp:
- Nov 6, 2017, 1:46:54 PM (7 years ago)
- Location:
- trunk
- Files:
-
- 21 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r224511 r224512 1 2017-11-06 Wenson Hsieh <wenson_hsieh@apple.com> 2 3 [Attachment Support] Implement delegate hooks for attachment element insertion and removal 4 https://bugs.webkit.org/show_bug.cgi?id=179016 5 <rdar://problem/35250890> 6 7 Reviewed by Tim Horton. 8 9 Implements a mechanism for notifying WebKit2 clients when attachment elements are inserted into or removed from 10 the document. See per-change comments below for more details. 11 12 API tests: WKAttachmentTests.AttachmentElementInsertion 13 WKAttachmentTests.AttachmentUpdatesWhenInsertingAndDeletingNewline 14 WKAttachmentTests.AttachmentUpdatesWhenUndoingAndRedoing 15 WKAttachmentTests.AttachmentUpdatesWhenChangingFontStyles 16 WKAttachmentTests.AttachmentUpdatesWhenInsertingLists 17 WKAttachmentTests.AttachmentUpdatesWhenInsertingRichMarkup 18 19 * editing/Editor.cpp: 20 (WebCore::Editor::respondToChangedSelection): 21 (WebCore::Editor::editorUIUpdateTimerFired): 22 23 Additionally notify the client of any attachment updates. 24 25 (WebCore::Editor::scheduleEditorUIUpdate): 26 27 Add a new helper that starts the editor UI update timer with 0 delay, and use it everywhere we schedule an 28 editor UI update. 29 30 (WebCore::Editor::didInsertAttachmentElement): 31 (WebCore::Editor::didRemoveAttachmentElement): 32 33 Maintain two sets of attachment element identifiers -- the first one tracking insertions, and the second one 34 tracking removals. When an attachment element is inserted, we first check to see if that attachment element has 35 just been removed; if so, we don't add it to the inserted identifiers set, but instead remove it from the set of 36 removed identifiers. We perform a similar check in the opposite case. This prevents us from notifying the client 37 of extraneous insertions and removals during certain editing commands which may reparent and move attachment 38 elements around. In both cases, we schedule an editor UI update afterwards, where we will notify the client of 39 attachment updates. 40 41 (WebCore::Editor::notifyClientOfAttachmentUpdates): 42 (WebCore::Editor::insertAttachmentFromFile): 43 * editing/Editor.h: 44 * html/HTMLAttachmentElement.cpp: 45 (WebCore::HTMLAttachmentElement::HTMLAttachmentElement): 46 47 Remove the version of HTMLAttachmentElement's constructor that takes a unique identifier. 48 49 (WebCore::HTMLAttachmentElement::insertedIntoAncestor): 50 (WebCore::HTMLAttachmentElement::removedFromAncestor): 51 52 Implement these hooks to observe insertion into and removal from the DOM. If the element was attached to or 53 removed from an ancestor that was connected to the document, call out to the document's frame's editor. This 54 "document-connected" rule prevents us from calling out to the client in cases where (for instance) we append an 55 attachment element to a newly created DocumentFragment in preparation for executing a ReplaceSelectionCommand. 56 57 (WebCore::HTMLAttachmentElement::uniqueIdentifier const): 58 (WebCore::HTMLAttachmentElement::setUniqueIdentifier): 59 60 Refactor unique identifier to refer to the new attachment identifier attribute instead of a member variable. 61 62 * html/HTMLAttachmentElement.h: 63 * html/HTMLAttributeNames.in: 64 65 Add a new attribute representing an attachment element's identifier. This enables us to keep track of particular 66 attachments as they are destroyed and recreated as different objects, as a result of some editing commands. 67 68 * page/EditorClient.h: 69 (WebCore::EditorClient::didInsertAttachment): 70 (WebCore::EditorClient::didRemoveAttachment): 71 72 Add boilerplate editor client hooks for attachment insertion and removal. 73 1 74 2017-11-06 Ryan Haddad <ryanhaddad@apple.com> 2 75 -
trunk/Source/WebCore/editing/Editor.cpp
r224459 r224512 3429 3429 && !(options & FrameSelection::SpellCorrectionTriggered); 3430 3430 m_editorUIUpdateTimerWasTriggeredByDictation = options & FrameSelection::DictationTriggered; 3431 m_editorUIUpdateTimer.startOneShot(0_s);3431 scheduleEditorUIUpdate(); 3432 3432 } 3433 3433 … … 3602 3602 3603 3603 m_oldSelectionForEditorUIUpdate = m_frame.selection().selection(); 3604 3605 #if ENABLE(ATTACHMENT_ELEMENT) 3606 notifyClientOfAttachmentUpdates(); 3607 #endif 3604 3608 } 3605 3609 … … 3734 3738 } 3735 3739 3740 void Editor::scheduleEditorUIUpdate() 3741 { 3742 m_editorUIUpdateTimer.startOneShot(0_s); 3743 } 3744 3736 3745 #if ENABLE(ATTACHMENT_ELEMENT) 3746 3747 void Editor::didInsertAttachmentElement(HTMLAttachmentElement& attachment) 3748 { 3749 auto identifier = attachment.uniqueIdentifier(); 3750 if (identifier.isEmpty()) 3751 return; 3752 3753 if (!m_removedAttachmentIdentifiers.take(identifier)) 3754 m_insertedAttachmentIdentifiers.add(identifier); 3755 scheduleEditorUIUpdate(); 3756 } 3757 3758 void Editor::didRemoveAttachmentElement(HTMLAttachmentElement& attachment) 3759 { 3760 auto identifier = attachment.uniqueIdentifier(); 3761 if (identifier.isEmpty()) 3762 return; 3763 3764 if (!m_insertedAttachmentIdentifiers.take(identifier)) 3765 m_removedAttachmentIdentifiers.add(identifier); 3766 scheduleEditorUIUpdate(); 3767 } 3768 3769 void Editor::notifyClientOfAttachmentUpdates() 3770 { 3771 if (auto* editorClient = client()) { 3772 for (auto& identifier : m_removedAttachmentIdentifiers) 3773 editorClient->didRemoveAttachment(identifier); 3774 for (auto& identifier : m_insertedAttachmentIdentifiers) 3775 editorClient->didInsertAttachment(identifier); 3776 } 3777 3778 m_removedAttachmentIdentifiers.clear(); 3779 m_insertedAttachmentIdentifiers.clear(); 3780 } 3737 3781 3738 3782 void Editor::insertAttachment(const String& identifier, const String& filename, const String& filepath, std::optional<String> contentType) … … 3752 3796 void Editor::insertAttachmentFromFile(const String& identifier, const String& filename, const String& contentType, Ref<File>&& file) 3753 3797 { 3754 auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document() , identifier);3798 auto attachment = HTMLAttachmentElement::create(HTMLNames::attachmentTag, document()); 3755 3799 attachment->setAttribute(HTMLNames::titleAttr, filename); 3756 3800 attachment->setAttribute(HTMLNames::subtitleAttr, fileSizeDescription(file->size())); -
trunk/Source/WebCore/editing/Editor.h
r224238 r224512 504 504 WEBCORE_EXPORT void insertAttachment(const String& identifier, const String& filename, const String& filepath, std::optional<String> contentType = std::nullopt); 505 505 WEBCORE_EXPORT void insertAttachment(const String& identifier, const String& filename, Ref<SharedBuffer>&& data, std::optional<String> contentType = std::nullopt); 506 void didInsertAttachmentElement(HTMLAttachmentElement&); 507 void didRemoveAttachmentElement(HTMLAttachmentElement&); 506 508 #endif 507 509 … … 549 551 static RefPtr<SharedBuffer> dataInRTFDFormat(NSAttributedString *); 550 552 static RefPtr<SharedBuffer> dataInRTFFormat(NSAttributedString *); 553 #endif 554 555 void scheduleEditorUIUpdate(); 556 557 #if ENABLE(ATTACHMENT_ELEMENT) 558 void notifyClientOfAttachmentUpdates(); 551 559 #endif 552 560 … … 570 578 bool m_overwriteModeEnabled { false }; 571 579 580 #if ENABLE(ATTACHMENT_ELEMENT) 581 HashSet<String> m_insertedAttachmentIdentifiers; 582 HashSet<String> m_removedAttachmentIdentifiers; 583 #endif 584 572 585 VisibleSelection m_oldSelectionForEditorUIUpdate; 573 586 Timer m_editorUIUpdateTimer; -
trunk/Source/WebCore/html/HTMLAttachmentElement.cpp
r224238 r224512 34 34 #include "HTMLNames.h" 35 35 #include "RenderAttachment.h" 36 #include <wtf/UUID.h>37 36 38 37 namespace WebCore { … … 40 39 using namespace HTMLNames; 41 40 42 HTMLAttachmentElement::HTMLAttachmentElement(const QualifiedName& tagName, Document& document , const String& identifier)41 HTMLAttachmentElement::HTMLAttachmentElement(const QualifiedName& tagName, Document& document) 43 42 : HTMLElement(tagName, document) 44 , m_uniqueIdentifier(identifier)45 43 { 46 44 ASSERT(hasTagName(attachmentTag)); 47 }48 49 HTMLAttachmentElement::HTMLAttachmentElement(const QualifiedName& tagName, Document& document)50 : HTMLAttachmentElement(tagName, document, createCanonicalUUIDString())51 {52 45 } 53 46 … … 57 50 { 58 51 return adoptRef(*new HTMLAttachmentElement(tagName, document)); 59 }60 61 Ref<HTMLAttachmentElement> HTMLAttachmentElement::create(const QualifiedName& tagName, Document& document, const String& identifier)62 {63 return adoptRef(*new HTMLAttachmentElement(tagName, document, identifier));64 52 } 65 53 … … 82 70 } 83 71 72 Node::InsertedIntoAncestorResult HTMLAttachmentElement::insertedIntoAncestor(InsertionType type, ContainerNode& ancestor) 73 { 74 auto result = HTMLElement::insertedIntoAncestor(type, ancestor); 75 if (auto* frame = document().frame()) { 76 if (type.connectedToDocument) 77 frame->editor().didInsertAttachmentElement(*this); 78 } 79 return result; 80 } 81 82 void HTMLAttachmentElement::removedFromAncestor(RemovalType type, ContainerNode& ancestor) 83 { 84 HTMLElement::removedFromAncestor(type, ancestor); 85 if (auto* frame = document().frame()) { 86 if (type.disconnectedFromDocument) 87 frame->editor().didRemoveAttachmentElement(*this); 88 } 89 } 90 84 91 void HTMLAttachmentElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 85 92 { … … 90 97 91 98 HTMLElement::parseAttribute(name, value); 99 } 100 101 String HTMLAttachmentElement::uniqueIdentifier() const 102 { 103 return attributeWithoutSynchronization(HTMLNames::webkitattachmentidAttr); 104 } 105 106 void HTMLAttachmentElement::setUniqueIdentifier(const String& identifier) 107 { 108 setAttributeWithoutSynchronization(HTMLNames::webkitattachmentidAttr, identifier); 92 109 } 93 110 -
trunk/Source/WebCore/html/HTMLAttachmentElement.h
r224238 r224512 38 38 public: 39 39 static Ref<HTMLAttachmentElement> create(const QualifiedName&, Document&); 40 static Ref<HTMLAttachmentElement> create(const QualifiedName&, Document&, const String& identifier);41 40 42 41 WEBCORE_EXPORT File* file() const; 43 42 void setFile(File*); 44 43 45 WEBCORE_EXPORT String uniqueIdentifier() const { return m_uniqueIdentifier; } 46 void setUniqueIdentifier(const String& identifier) { m_uniqueIdentifier = identifier; } 44 WEBCORE_EXPORT String uniqueIdentifier() const; 45 void setUniqueIdentifier(const String&); 46 47 InsertedIntoAncestorResult insertedIntoAncestor(InsertionType, ContainerNode&) final; 48 void removedFromAncestor(RemovalType, ContainerNode&) final; 47 49 48 50 WEBCORE_EXPORT String attachmentTitle() const; … … 53 55 private: 54 56 HTMLAttachmentElement(const QualifiedName&, Document&); 55 HTMLAttachmentElement(const QualifiedName&, Document&, const String& identifier);56 57 virtual ~HTMLAttachmentElement(); 57 58 … … 69 70 70 71 RefPtr<File> m_file; 71 String m_uniqueIdentifier;72 72 }; 73 73 -
trunk/Source/WebCore/html/HTMLAttributeNames.in
r223190 r224512 382 382 vspace 383 383 webkitallowfullscreen 384 webkitattachmentid 384 385 webkitattachmentpath 385 386 webkitdirectory -
trunk/Source/WebCore/page/EditorClient.h
r223728 r224512 72 72 virtual void didApplyStyle() = 0; 73 73 virtual bool shouldMoveRangeAfterDelete(Range*, Range*) = 0; 74 75 #if ENABLE(ATTACHMENT_ELEMENT) 76 virtual void didInsertAttachment(const String&) { } 77 virtual void didRemoveAttachment(const String&) { } 78 #endif 74 79 75 80 virtual void didBeginEditing() = 0; -
trunk/Source/WebKit/ChangeLog
r224509 r224512 1 2017-11-06 Wenson Hsieh <wenson_hsieh@apple.com> 2 3 [Attachment Support] Implement delegate hooks for attachment element insertion and removal 4 https://bugs.webkit.org/show_bug.cgi?id=179016 5 <rdar://problem/35250890> 6 7 Reviewed by Tim Horton. 8 9 Adds boilerplate plumbing to WebEditorClient, WebPage, and the usual machinery in the UI process to notify 10 WebKit2 clients when attachment elements have been inserted or removed from the document. See the WebCore 11 ChangeLog for more details about the implementation, or the Tools ChangeLog for more information about new API 12 tests. 13 14 * UIProcess/API/Cocoa/WKUIDelegatePrivate.h: 15 * UIProcess/API/Cocoa/WKWebView.mm: 16 (-[WKWebView _didInsertAttachment:]): 17 (-[WKWebView _didRemoveAttachment:]): 18 * UIProcess/API/Cocoa/WKWebViewInternal.h: 19 * UIProcess/Cocoa/PageClientImplCocoa.h: 20 * UIProcess/Cocoa/PageClientImplCocoa.mm: 21 (WebKit::PageClientImplCocoa::didInsertAttachment): 22 (WebKit::PageClientImplCocoa::didRemoveAttachment): 23 * UIProcess/PageClient.h: 24 (WebKit::PageClient::didInsertAttachment): 25 (WebKit::PageClient::didRemoveAttachment): 26 * UIProcess/WebPageProxy.cpp: 27 (WebKit::WebPageProxy::didInsertAttachment): 28 (WebKit::WebPageProxy::didRemoveAttachment): 29 * UIProcess/WebPageProxy.h: 30 * UIProcess/WebPageProxy.messages.in: 31 * WebProcess/WebCoreSupport/WebEditorClient.cpp: 32 (WebKit::WebEditorClient::didInsertAttachment): 33 (WebKit::WebEditorClient::didRemoveAttachment): 34 * WebProcess/WebCoreSupport/WebEditorClient.h: 35 1 36 2017-11-06 Jeremy Jones <jeremyj@apple.com> 2 37 -
trunk/Source/WebKit/UIProcess/API/Cocoa/WKUIDelegatePrivate.h
r223267 r224512 111 111 - (void)_webView:(WKWebView *)webView editorStateDidChange:(NSDictionary *)editorState WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA)); 112 112 113 - (void)_webView:(WKWebView *)webView didRemoveAttachment:(_WKAttachment *)attachment WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA)); 114 - (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment WK_API_AVAILABLE(macosx(WK_MAC_TBA), ios(WK_IOS_TBA)); 115 113 116 #if TARGET_OS_IPHONE 114 117 - (BOOL)_webView:(WKWebView *)webView shouldIncludeAppLinkActionsForElement:(_WKActivatedElementInfo *)element WK_API_AVAILABLE(ios(9.0)); -
trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm
r224313 r224512 1197 1197 [uiDelegate _webView:self editorStateDidChange:dictionaryRepresentationForEditorState(_page->editorState())]; 1198 1198 } 1199 1200 #if ENABLE(ATTACHMENT_ELEMENT) 1201 1202 - (void)_didInsertAttachment:(NSString *)identifier 1203 { 1204 id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate; 1205 if ([uiDelegate respondsToSelector:@selector(_webView:didInsertAttachment:)]) 1206 [uiDelegate _webView:self didInsertAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease]]; 1207 } 1208 1209 - (void)_didRemoveAttachment:(NSString *)identifier 1210 { 1211 id <WKUIDelegatePrivate> uiDelegate = (id <WKUIDelegatePrivate>)self.UIDelegate; 1212 if ([uiDelegate respondsToSelector:@selector(_webView:didRemoveAttachment:)]) 1213 [uiDelegate _webView:self didRemoveAttachment:[wrapper(API::Attachment::create(identifier, *_page).leakRef()) autorelease]]; 1214 } 1215 1216 #endif // ENABLE(ATTACHMENT_ELEMENT) 1199 1217 1200 1218 #pragma mark iOS-specific methods -
trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h
r224313 r224512 149 149 #endif 150 150 151 #if ENABLE(ATTACHMENT_ELEMENT) 152 - (void)_didRemoveAttachment:(NSString *)identifier; 153 - (void)_didInsertAttachment:(NSString *)identifier; 154 #endif 155 151 156 - (WKPageRef)_pageForTesting; 152 157 - (WebKit::WebPageProxy*)_page; -
trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.h
r221502 r224512 38 38 void isPlayingAudioWillChange() final; 39 39 void isPlayingAudioDidChange() final; 40 41 #if ENABLE(ATTACHMENT_ELEMENT) 42 void didInsertAttachment(const String& identifier) final; 43 void didRemoveAttachment(const String& identifier) final; 44 #endif 45 40 46 protected: 41 47 WKWebView *m_webView; -
trunk/Source/WebKit/UIProcess/Cocoa/PageClientImplCocoa.mm
r221502 r224512 27 27 #import "PageClientImplCocoa.h" 28 28 29 #import "WKWebView Private.h"29 #import "WKWebViewInternal.h" 30 30 31 31 namespace WebKit { … … 44 44 #endif 45 45 } 46 47 #if ENABLE(ATTACHMENT_ELEMENT) 48 49 void PageClientImplCocoa::didInsertAttachment(const String& identifier) 50 { 51 #if WK_API_ENABLED 52 [m_webView _didInsertAttachment:identifier]; 53 #else 54 UNUSED_PARAM(identifier); 55 #endif 56 } 57 58 void PageClientImplCocoa::didRemoveAttachment(const String& identifier) 59 { 60 #if WK_API_ENABLED 61 [m_webView _didRemoveAttachment:identifier]; 62 #else 63 UNUSED_PARAM(identifier); 64 #endif 65 } 66 67 #endif 46 68 47 69 } -
trunk/Source/WebKit/UIProcess/PageClient.h
r224057 r224512 385 385 #endif 386 386 387 #if ENABLE(ATTACHMENT_ELEMENT) 388 virtual void didInsertAttachment(const String& identifier) { } 389 virtual void didRemoveAttachment(const String& identifier) { } 390 #endif 391 387 392 #if PLATFORM(GTK) || PLATFORM(WPE) 388 393 virtual JSGlobalContextRef javascriptGlobalContext() { return nullptr; } -
trunk/Source/WebKit/UIProcess/WebPageProxy.cpp
r224238 r224512 7114 7114 } 7115 7115 7116 void WebPageProxy::didInsertAttachment(const String& identifier) 7117 { 7118 m_pageClient.didInsertAttachment(identifier); 7119 } 7120 7121 void WebPageProxy::didRemoveAttachment(const String& identifier) 7122 { 7123 m_pageClient.didRemoveAttachment(identifier); 7124 } 7125 7116 7126 #endif // ENABLE(ATTACHMENT_ELEMENT) 7117 7127 -
trunk/Source/WebKit/UIProcess/WebPageProxy.h
r224238 r224512 1638 1638 void stopAllURLSchemeTasks(); 1639 1639 1640 #if ENABLE(ATTACHMENT_ELEMENT) 1641 void didInsertAttachment(const String& identifier); 1642 void didRemoveAttachment(const String& identifier); 1643 #endif 1644 1640 1645 PageClient& m_pageClient; 1641 1646 Ref<API::PageConfiguration> m_configuration; -
trunk/Source/WebKit/UIProcess/WebPageProxy.messages.in
r224057 r224512 499 499 500 500 RequestStorageAccess(String subFrameHost, String topFrameHost, uint64_t contextID) 501 502 #if ENABLE(ATTACHMENT_ELEMENT) 503 DidInsertAttachment(String identifier) 504 DidRemoveAttachment(String identifier) 505 #endif 501 506 } -
trunk/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.cpp
r221064 r224512 158 158 } 159 159 160 #if ENABLE(ATTACHMENT_ELEMENT) 161 162 void WebEditorClient::didInsertAttachment(const String& identifier) 163 { 164 m_page->send(Messages::WebPageProxy::DidInsertAttachment(identifier)); 165 } 166 167 void WebEditorClient::didRemoveAttachment(const String& identifier) 168 { 169 m_page->send(Messages::WebPageProxy::DidRemoveAttachment(identifier)); 170 } 171 172 #endif 173 160 174 void WebEditorClient::didApplyStyle() 161 175 { -
trunk/Source/WebKit/WebProcess/WebCoreSupport/WebEditorClient.h
r222477 r224512 59 59 void didApplyStyle() final; 60 60 bool shouldMoveRangeAfterDelete(WebCore::Range*, WebCore::Range*) final; 61 62 #if ENABLE(ATTACHMENT_ELEMENT) 63 void didInsertAttachment(const String& identifier) final; 64 void didRemoveAttachment(const String& identifier) final; 65 #endif 61 66 62 67 void didBeginEditing() final; -
trunk/Tools/ChangeLog
r224505 r224512 1 2017-11-06 Wenson Hsieh <wenson_hsieh@apple.com> 2 3 [Attachment Support] Implement delegate hooks for attachment element insertion and removal 4 https://bugs.webkit.org/show_bug.cgi?id=179016 5 <rdar://problem/35250890> 6 7 Reviewed by Tim Horton. 8 9 Introduces new API tests to check that various editing operations will or won't result in the new attachment 10 insertion and removal delegate hooks being fired. Additionally refactors an existing test to verify that 11 attachments insertion and removal is observable by the UI delegate. 12 13 * TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm: 14 (-[AttachmentUpdateObserver init]): 15 (-[AttachmentUpdateObserver inserted]): 16 (-[AttachmentUpdateObserver removed]): 17 (-[AttachmentUpdateObserver _webView:didInsertAttachment:]): 18 (-[AttachmentUpdateObserver _webView:didRemoveAttachment:]): 19 (TestWebKitAPI::ObserveAttachmentUpdatesForScope::ObserveAttachmentUpdatesForScope): 20 (TestWebKitAPI::ObserveAttachmentUpdatesForScope::~ObserveAttachmentUpdatesForScope): 21 (TestWebKitAPI::ObserveAttachmentUpdatesForScope::expectAttachmentUpdates): 22 23 Implement a testing mechanism to temporarily bind a UI delegate to a given WKWebView and listen for inserted or 24 removed attachments over the course of a particular scope. The API tests use this mechanism to check that the UI 25 delegate hooks added in this patch are invoked with the right attachments when performing edit commands. 26 27 (-[TestWKWebView _synchronouslyExecuteEditCommand:argument:]): 28 (-[TestWKWebView expectUpdatesAfterCommand:withArgument:expectedRemovals:expectedInsertions:]): 29 (TestWebKitAPI::TEST): 30 1 31 2017-11-06 Christopher Reid <chris.reid@sony.com> 2 32 -
trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/WKAttachmentTests.mm
r224238 r224512 34 34 #if WK_API_ENABLED 35 35 36 @interface AttachmentUpdateObserver : NSObject <WKUIDelegatePrivate> 37 @property (nonatomic, readonly) NSArray *inserted; 38 @property (nonatomic, readonly) NSArray *removed; 39 @end 40 41 @implementation AttachmentUpdateObserver { 42 RetainPtr<NSMutableArray<_WKAttachment *>> _inserted; 43 RetainPtr<NSMutableArray<_WKAttachment *>> _removed; 44 } 45 46 - (instancetype)init 47 { 48 if (self = [super init]) { 49 _inserted = adoptNS([[NSMutableArray alloc] init]); 50 _removed = adoptNS([[NSMutableArray alloc] init]); 51 } 52 return self; 53 } 54 55 - (NSArray<_WKAttachment *> *)inserted 56 { 57 return _inserted.get(); 58 } 59 60 - (NSArray<_WKAttachment *> *)removed 61 { 62 return _removed.get(); 63 } 64 65 - (void)_webView:(WKWebView *)webView didInsertAttachment:(_WKAttachment *)attachment 66 { 67 [_inserted addObject:attachment]; 68 } 69 70 - (void)_webView:(WKWebView *)webView didRemoveAttachment:(_WKAttachment *)attachment 71 { 72 [_removed addObject:attachment]; 73 } 74 75 @end 76 77 namespace TestWebKitAPI { 78 79 class ObserveAttachmentUpdatesForScope { 80 public: 81 ObserveAttachmentUpdatesForScope(TestWKWebView *webView) 82 : m_webView(webView) 83 { 84 m_previousDelegate = retainPtr(webView.UIDelegate); 85 m_observer = adoptNS([[AttachmentUpdateObserver alloc] init]); 86 webView.UIDelegate = m_observer.get(); 87 } 88 89 ~ObserveAttachmentUpdatesForScope() 90 { 91 [m_webView setUIDelegate:m_previousDelegate.get()]; 92 } 93 94 AttachmentUpdateObserver *observer() const { return m_observer.get(); } 95 96 void expectAttachmentUpdates(NSArray<_WKAttachment *> *removed, NSArray<_WKAttachment *> *inserted) 97 { 98 BOOL removedAttachmentsMatch = [observer().removed isEqual:removed]; 99 if (!removedAttachmentsMatch) 100 NSLog(@"Expected removed attachments: %@ to match %@.", observer().removed, removed); 101 EXPECT_TRUE(removedAttachmentsMatch); 102 103 BOOL insertedAttachmentsMatch = [observer().inserted isEqual:inserted]; 104 if (!insertedAttachmentsMatch) 105 NSLog(@"Expected inserted attachments: %@ to match %@.", observer().inserted, inserted); 106 EXPECT_TRUE(insertedAttachmentsMatch); 107 } 108 109 private: 110 RetainPtr<AttachmentUpdateObserver> m_observer; 111 RetainPtr<TestWKWebView> m_webView; 112 RetainPtr<id> m_previousDelegate; 113 }; 114 115 } 116 117 @interface TestWKWebView (AttachmentTesting) 118 @end 119 36 120 static RetainPtr<TestWKWebView> webViewForTestingAttachments() 37 121 { … … 57 141 58 142 @implementation TestWKWebView (AttachmentTesting) 143 144 - (BOOL)_synchronouslyExecuteEditCommand:(NSString *)command argument:(NSString *)argument 145 { 146 __block bool done = false; 147 __block bool success; 148 [self _executeEditCommand:command argument:argument completion:^(BOOL completionSuccess) { 149 done = true; 150 success = completionSuccess; 151 }]; 152 TestWebKitAPI::Util::run(&done); 153 return success; 154 } 59 155 60 156 - (_WKAttachment *)synchronouslyInsertAttachmentWithFilename:(NSString *)filename contentType:(NSString *)contentType data:(NSData *)data options:(_WKAttachmentDisplayOptions *)options … … 73 169 } 74 170 171 - (void)expectUpdatesAfterCommand:(NSString *)command withArgument:(NSString *)argument expectedRemovals:(NSArray<_WKAttachment *> *)removed expectedInsertions:(NSArray<_WKAttachment *> *)inserted 172 { 173 TestWebKitAPI::ObserveAttachmentUpdatesForScope observer(self); 174 EXPECT_TRUE([self _synchronouslyExecuteEditCommand:command argument:argument]); 175 observer.expectAttachmentUpdates(removed, inserted); 176 } 177 75 178 @end 76 179 … … 80 183 { 81 184 auto webView = webViewForTestingAttachments(); 82 83 // Use the given content type for the attachment element's type. 84 [webView synchronouslyInsertAttachmentWithFilename:@"foo" contentType:@"text/html" data:testHTMLData() options:nil]; 85 EXPECT_WK_STREQ(@"foo", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]); 86 EXPECT_WK_STREQ(@"text/html", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]); 87 EXPECT_WK_STREQ(@"38 bytes", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]); 88 89 // Since no content type is explicitly specified, compute it from the file extension. 90 [webView _executeEditCommand:@"DeleteBackward" argument:nil completion:nil]; 91 [webView synchronouslyInsertAttachmentWithFilename:@"bar.png" contentType:nil data:testImageData() options:nil]; 92 EXPECT_WK_STREQ(@"bar.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]); 93 EXPECT_WK_STREQ(@"image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]); 94 EXPECT_WK_STREQ(@"37 KB", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]); 185 RetainPtr<_WKAttachment> firstAttachment; 186 RetainPtr<_WKAttachment> secondAttachment; 187 { 188 ObserveAttachmentUpdatesForScope observer(webView.get()); 189 // Use the given content type for the attachment element's type. 190 firstAttachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo" contentType:@"text/html" data:testHTMLData() options:nil]); 191 EXPECT_WK_STREQ(@"foo", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]); 192 EXPECT_WK_STREQ(@"text/html", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]); 193 EXPECT_WK_STREQ(@"38 bytes", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]); 194 observer.expectAttachmentUpdates(@[ ], @[ firstAttachment.get() ]); 195 } 196 { 197 ObserveAttachmentUpdatesForScope scope(webView.get()); 198 // Since no content type is explicitly specified, compute it from the file extension. 199 [webView _executeEditCommand:@"DeleteBackward" argument:nil completion:nil]; 200 secondAttachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"bar.png" contentType:nil data:testImageData() options:nil]); 201 EXPECT_WK_STREQ(@"bar.png", [webView valueOfAttribute:@"title" forQuerySelector:@"attachment"]); 202 EXPECT_WK_STREQ(@"image/png", [webView valueOfAttribute:@"type" forQuerySelector:@"attachment"]); 203 EXPECT_WK_STREQ(@"37 KB", [webView valueOfAttribute:@"subtitle" forQuerySelector:@"attachment"]); 204 scope.expectAttachmentUpdates(@[ firstAttachment.get() ], @[ secondAttachment.get() ]); 205 } 206 } 207 208 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingAndDeletingNewline) 209 { 210 auto webView = webViewForTestingAttachments(); 211 RetainPtr<_WKAttachment> attachment; 212 { 213 ObserveAttachmentUpdatesForScope observer(webView.get()); 214 attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil]); 215 observer.expectAttachmentUpdates(@[ ], @[attachment.get()]); 216 } 217 [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]]; 218 [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]]; 219 [webView stringByEvaluatingJavaScript:@"getSelection().collapse(document.body)"]; 220 [webView expectUpdatesAfterCommand:@"InsertParagraph" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]]; 221 [webView expectUpdatesAfterCommand:@"DeleteForward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]]; 222 } 223 224 TEST(WKAttachmentTests, AttachmentUpdatesWhenUndoingAndRedoing) 225 { 226 auto webView = webViewForTestingAttachments(); 227 RetainPtr<_WKAttachment> attachment; 228 { 229 ObserveAttachmentUpdatesForScope observer(webView.get()); 230 attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil]); 231 observer.expectAttachmentUpdates(@[ ], @[attachment.get()]); 232 } 233 [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]]; 234 [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]]; 235 [webView expectUpdatesAfterCommand:@"DeleteBackward" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]]; 236 [webView expectUpdatesAfterCommand:@"Undo" withArgument:nil expectedRemovals:@[] expectedInsertions:@[attachment.get()]]; 237 [webView expectUpdatesAfterCommand:@"Redo" withArgument:nil expectedRemovals:@[attachment.get()] expectedInsertions:@[]]; 238 } 239 240 TEST(WKAttachmentTests, AttachmentUpdatesWhenChangingFontStyles) 241 { 242 auto webView = webViewForTestingAttachments(); 243 RetainPtr<_WKAttachment> attachment; 244 [webView _synchronouslyExecuteEditCommand:@"InsertText" argument:@"Hello"]; 245 { 246 ObserveAttachmentUpdatesForScope observer(webView.get()); 247 attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil]); 248 observer.expectAttachmentUpdates(@[ ], @[attachment.get()]); 249 } 250 [webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"World" expectedRemovals:@[] expectedInsertions:@[]]; 251 [webView _synchronouslyExecuteEditCommand:@"SelectAll" argument:nil]; 252 [webView expectUpdatesAfterCommand:@"ToggleBold" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]]; 253 [webView expectUpdatesAfterCommand:@"ToggleItalic" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]]; 254 [webView expectUpdatesAfterCommand:@"ToggleUnderline" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]]; 255 256 // Inserting text should delete the current selection, removing the attachment in the process. 257 [webView expectUpdatesAfterCommand:@"InsertText" withArgument:@"foo" expectedRemovals:@[attachment.get()] expectedInsertions:@[]]; 258 } 259 260 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingLists) 261 { 262 auto webView = webViewForTestingAttachments(); 263 RetainPtr<_WKAttachment> attachment; 264 { 265 ObserveAttachmentUpdatesForScope observer(webView.get()); 266 attachment = retainPtr([webView synchronouslyInsertAttachmentWithFilename:@"foo.txt" contentType:@"text/plain" data:testHTMLData() options:nil]); 267 observer.expectAttachmentUpdates(@[ ], @[attachment.get()]); 268 NSLog(@"The markup is now %@", [webView stringByEvaluatingJavaScript:@"document.body.innerHTML"]); 269 } 270 [webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]]; 271 // This edit command behaves more like a "toggle", and will actually break us out of the list we just inserted. 272 [webView expectUpdatesAfterCommand:@"InsertOrderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]]; 273 [webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]]; 274 [webView expectUpdatesAfterCommand:@"InsertUnorderedList" withArgument:nil expectedRemovals:@[] expectedInsertions:@[]]; 275 } 276 277 TEST(WKAttachmentTests, AttachmentUpdatesWhenInsertingRichMarkup) 278 { 279 auto webView = webViewForTestingAttachments(); 280 RetainPtr<_WKAttachment> attachment; 281 { 282 ObserveAttachmentUpdatesForScope observer(webView.get()); 283 [webView _synchronouslyExecuteEditCommand:@"InsertHTML" argument:@"<div><strong><attachment title='a' webkitattachmentid='a06fec41-9aa0-4c2c-ba3a-0149b54aad99'></attachment></strong></div>"]; 284 attachment = observer.observer().inserted[0]; 285 observer.expectAttachmentUpdates(@[ ], @[attachment.get()]); 286 } 287 { 288 ObserveAttachmentUpdatesForScope observer(webView.get()); 289 [webView stringByEvaluatingJavaScript:@"document.querySelector('attachment').remove()"]; 290 [webView waitForNextPresentationUpdate]; 291 observer.expectAttachmentUpdates(@[attachment.get()], @[ ]); 292 } 95 293 } 96 294
Note:
See TracChangeset
for help on using the changeset viewer.