WebKit Bugzilla
Attachment 369389 Details for
Bug 197683
: [iOS] Add a quirk to synthesize mouse events when modifying the selection
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-197683-20190508090401.patch (text/plain), 36.22 KB, created by
Wenson Hsieh
on 2019-05-08 09:04:02 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
Wenson Hsieh
Created:
2019-05-08 09:04:02 PDT
Size:
36.22 KB
patch
obsolete
>Subversion Revision: 245009 >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 26177d0480c0ee5f982bbef8fd38d16ff051bbee..583f75d7f431c05a52b367c4e6d71184241019ab 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,48 @@ >+2019-05-07 Wenson Hsieh <wenson_hsieh@apple.com> >+ >+ [iOS] Add a quirk to synthesize mouse events when modifying the selection >+ https://bugs.webkit.org/show_bug.cgi?id=197683 >+ <rdar://problem/48003980> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ See WebKit ChangeLog for more details. >+ >+ Test: editing/selection/ios/dispatch-mouse-events-when-modifying-selection-quirk.html >+ >+ * page/EventHandler.cpp: >+ (WebCore::EventHandler::handleMousePressEvent): >+ (WebCore::EventHandler::supportsSelectionUpdatesOnMouseDrag const): >+ >+ Add some platform hooks to prevent mousemove events from updating the selection on iOS. >+ >+ (WebCore::EventHandler::shouldAllowMouseDownToStartDrag const): >+ >+ Add some platform hooks to prevent drag and drop from kicking in when sending synthetic mousemove events to the >+ page on iOS (drag and drop is instead triggered by EventHandler::tryToBeginDragAtPoint). >+ >+ (WebCore::EventHandler::updateSelectionForMouseDrag): >+ * page/EventHandler.h: >+ * page/Quirks.cpp: >+ (WebCore::Quirks::shouldDispatchSyntheticMouseEventsWhenModifyingSelection const): >+ * page/Quirks.h: >+ >+ Add the new site-specific quirk. >+ >+ * page/Settings.yaml: >+ * page/ios/EventHandlerIOS.mm: >+ (WebCore::EventHandler::tryToBeginDragAtPoint): >+ (WebCore::EventHandler::supportsSelectionUpdatesOnMouseDrag const): >+ (WebCore::EventHandler::shouldAllowMouseDownToStartDrag const): >+ * testing/InternalSettings.cpp: >+ (WebCore::InternalSettings::Backup::Backup): >+ (WebCore::InternalSettings::Backup::restoreTo): >+ (WebCore::InternalSettings::setShouldDispatchSyntheticMouseEventsWhenModifyingSelection): >+ * testing/InternalSettings.h: >+ * testing/InternalSettings.idl: >+ >+ Add an internal settings hook to opt into this quirk, for use in layout tests. >+ > 2019-05-07 Antti Koivisto <antti@apple.com> > > <body> with overflow:hidden CSS is scrollable on iOS >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index 88ce45ee303d8a8e33e9b3d41a62ad179616778e..ac6541665eb1dc58518c777a71a07ce8a9a7ed7b 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,58 @@ >+2019-05-07 Wenson Hsieh <wenson_hsieh@apple.com> >+ >+ [iOS] Add a quirk to synthesize mouse events when modifying the selection >+ https://bugs.webkit.org/show_bug.cgi?id=197683 >+ <rdar://problem/48003980> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Introduces support for dispatching synthetic mouse events when modifying the selection on some websites. See >+ below for more details. >+ >+ * UIProcess/WebPageProxy.cpp: >+ (WebKit::WebPageProxy::selectAll): >+ * UIProcess/WebPageProxy.h: >+ >+ Instead of executing a "SelectAll" editing command using the generic WebPage::executeEditCommand method, >+ introduce a separate method for selectAll that executes the "SelectAll" edit command and then does some >+ platform-specific work. See platformDidSelectAll. >+ >+ * UIProcess/ios/WKContentViewInteraction.mm: >+ (-[WKContentView selectAllForWebView:]): >+ * WebProcess/WebPage/WebPage.cpp: >+ (WebKit::WebPage::selectAll): >+ (WebKit::WebPage::shouldDispatchSyntheticMouseEventsWhenModifyingSelection const): >+ >+ Add a helper method to determine whether the quirk should be enabled. >+ >+ (WebKit::WebPage::platformDidSelectAll): >+ * WebProcess/WebPage/WebPage.h: >+ * WebProcess/WebPage/WebPage.messages.in: >+ * WebProcess/WebPage/ios/WebPageIOS.mm: >+ (WebKit::elementRectInRootViewCoordinates): >+ >+ Move this function closer to the top of the file so that it can be used in >+ dispatchSyntheticMouseEventsForSelectionGesture. >+ >+ (WebKit::WebPage::clearSelection): >+ (WebKit::WebPage::dispatchSyntheticMouseEventsForSelectionGesture): >+ >+ Add a helper method to dispatch a synthetic mouse event for a given selection gesture type. Used in several >+ places in WebPageIOS to synthesize and dispatch mouse events during selection. >+ >+ (WebKit::WebPage::updateSelectionWithTouches): >+ >+ When changing the selection with selection handles, fake mousedown when the user first touches down on the >+ selection handle; mousemove as the user is moving the handle around; and finally, mouseup when the user lets go. >+ >+ (WebKit::WebPage::extendSelection): >+ (WebKit::WebPage::platformDidSelectAll): >+ >+ When tapping "Select All" and/or "Select" in the callout menu, fake a mousedown at the selection start, then a >+ mousemove at selection end, and finally, a mouseup at selection end. >+ >+ (WebKit::WebPage::getFocusedElementInformation): >+ > 2019-05-07 Wenson Hsieh <wenson_hsieh@apple.com> > > [macOS] Avoid crashing the UI process when writing empty data to the pasteboard >diff --git a/Source/WebCore/page/EventHandler.cpp b/Source/WebCore/page/EventHandler.cpp >index 22f099cb2b85e0859b1974be9d3336667d48b9f1..75870d7d145cd78b5bfbe602a961be62d164eb3b 100644 >--- a/Source/WebCore/page/EventHandler.cpp >+++ b/Source/WebCore/page/EventHandler.cpp >@@ -776,7 +776,7 @@ bool EventHandler::handleMousePressEvent(const MouseEventWithHitTestResults& eve > > // Single mouse down on links or images can always trigger drag-n-drop. > bool isMouseDownOnLinkOrImage = event.isOverLink() || event.hitTestResult().image(); >- m_mouseDownMayStartDrag = singleClick && (!event.event().shiftKey() || isMouseDownOnLinkOrImage); >+ m_mouseDownMayStartDrag = singleClick && (!event.event().shiftKey() || isMouseDownOnLinkOrImage) && shouldAllowMouseDownToStartDrag(); > #endif > > m_mouseDownWasSingleClickInSelection = false; >@@ -847,6 +847,21 @@ VisiblePosition EventHandler::selectionExtentRespectingEditingBoundary(const Vis > } > > #if ENABLE(DRAG_SUPPORT) >+ >+#if !PLATFORM(IOS_FAMILY) >+ >+bool EventHandler::supportsSelectionUpdatesOnMouseDrag() const >+{ >+ return true; >+} >+ >+bool EventHandler::shouldAllowMouseDownToStartDrag() const >+{ >+ return true; >+} >+ >+#endif >+ > bool EventHandler::handleMouseDraggedEvent(const MouseEventWithHitTestResults& event, CheckDragHysteresis checkDragHysteresis) > { > if (!m_mousePressed) >@@ -926,6 +941,9 @@ bool EventHandler::eventMayStartDrag(const PlatformMouseEvent& event) const > > void EventHandler::updateSelectionForMouseDrag() > { >+ if (!supportsSelectionUpdatesOnMouseDrag()) >+ return; >+ > FrameView* view = m_frame.view(); > if (!view) > return; >@@ -941,6 +959,9 @@ void EventHandler::updateSelectionForMouseDrag() > > void EventHandler::updateSelectionForMouseDrag(const HitTestResult& hitTestResult) > { >+ if (!supportsSelectionUpdatesOnMouseDrag()) >+ return; >+ > if (!m_mouseDownMayStartSelect) > return; > >diff --git a/Source/WebCore/page/EventHandler.h b/Source/WebCore/page/EventHandler.h >index 7fe97a483c177a84cce4e3b68a7d9589d4d2e29d..f6c3fc816dbc82e05ea9189d05e6e442e7024ef1 100644 >--- a/Source/WebCore/page/EventHandler.h >+++ b/Source/WebCore/page/EventHandler.h >@@ -365,6 +365,7 @@ private: > > #if ENABLE(DRAG_SUPPORT) > bool handleMouseDraggedEvent(const MouseEventWithHitTestResults&, CheckDragHysteresis = ShouldCheckDragHysteresis); >+ bool shouldAllowMouseDownToStartDrag() const; > #endif > > WEBCORE_EXPORT bool handleMouseReleaseEvent(const MouseEventWithHitTestResults&); >@@ -457,6 +458,7 @@ private: > > #if ENABLE(DRAG_SUPPORT) > DragSourceAction updateDragSourceActionsAllowed() const; >+ bool supportsSelectionUpdatesOnMouseDrag() const; > #endif > > // The following are called at the beginning of handleMouseUp and handleDrag. >@@ -616,6 +618,10 @@ private: > bool m_didStartDrag { false }; > bool m_isHandlingWheelEvent { false }; > >+#if PLATFORM(IOS_FAMILY) >+ bool m_shouldAllowMouseDownToStartDrag { false }; >+#endif >+ > #if ENABLE(CURSOR_VISIBILITY) > Timer m_autoHideCursorTimer; > #endif >diff --git a/Source/WebCore/page/Quirks.cpp b/Source/WebCore/page/Quirks.cpp >index b1f1ffdb5ae4011c0a96f1f19c92b4e2e9cbfe94..522a33a7bb90a191972cb0b52714d338a00ea73b 100644 >--- a/Source/WebCore/page/Quirks.cpp >+++ b/Source/WebCore/page/Quirks.cpp >@@ -237,6 +237,24 @@ static bool shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAre > > #endif > >+bool Quirks::shouldDispatchSyntheticMouseEventsWhenModifyingSelection() const >+{ >+ if (m_document->settings().shouldDispatchSyntheticMouseEventsWhenModifyingSelection()) >+ return true; >+ >+ if (!needsQuirks()) >+ return false; >+ >+ auto host = m_document->topDocument().url().host(); >+ if (equalLettersIgnoringASCIICase(host, "medium.com") || host.endsWithIgnoringASCIICase(".medium.com")) >+ return true; >+ >+ if (equalLettersIgnoringASCIICase(host, "weebly.com") || host.endsWithIgnoringASCIICase(".weebly.com")) >+ return true; >+ >+ return false; >+} >+ > bool Quirks::shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreas() const > { > if (!needsQuirks()) >diff --git a/Source/WebCore/page/Quirks.h b/Source/WebCore/page/Quirks.h >index e920bc326797ee888842e91fd36b76461343c4c8..7689f70af659b3119a6d8cdf4f7da8c250339000 100644 >--- a/Source/WebCore/page/Quirks.h >+++ b/Source/WebCore/page/Quirks.h >@@ -52,6 +52,7 @@ public: > bool shouldDisablePointerEventsQuirk() const; > bool needsInputModeNoneImplicitly(const HTMLElement&) const; > >+ WEBCORE_EXPORT bool shouldDispatchSyntheticMouseEventsWhenModifyingSelection() const; > WEBCORE_EXPORT bool shouldSuppressAutocorrectionAndAutocaptializationInHiddenEditableAreas() const; > WEBCORE_EXPORT bool isTouchBarUpdateSupressedForHiddenContentEditable() const; > WEBCORE_EXPORT bool isNeverRichlyEditableForTouchBar() const; >diff --git a/Source/WebCore/page/Settings.yaml b/Source/WebCore/page/Settings.yaml >index d30c815e9cc68eb27367111619f49a712bb80225..a34b82d30e64c2ee36b46d92cf9700e1e45f806e 100644 >--- a/Source/WebCore/page/Settings.yaml >+++ b/Source/WebCore/page/Settings.yaml >@@ -834,6 +834,9 @@ blockingOfSmallPluginsEnabled: > shouldDecidePolicyBeforeLoadingQuickLookPreview: > initial: false > >+shouldDispatchSyntheticMouseEventsWhenModifyingSelection: >+ initial: false >+ > # Deprecated > > iceCandidateFilteringEnabled: >diff --git a/Source/WebCore/page/ios/EventHandlerIOS.mm b/Source/WebCore/page/ios/EventHandlerIOS.mm >index 51a2d5e662fa373d73c3134c416d8df96b811625..f91dd58d45431f6f73c69e8f1219876313e78127 100644 >--- a/Source/WebCore/page/ios/EventHandlerIOS.mm >+++ b/Source/WebCore/page/ios/EventHandlerIOS.mm >@@ -671,6 +671,8 @@ bool EventHandler::tryToBeginDragAtPoint(const IntPoint& clientPosition, const I > if (!document) > return false; > >+ SetForScope<bool> shouldAllowMouseDownToStartDrag { m_shouldAllowMouseDownToStartDrag, true }; >+ > document->updateLayoutIgnorePendingStylesheets(); > > FloatPoint adjustedClientPositionAsFloatPoint(clientPosition); >@@ -699,7 +701,17 @@ bool EventHandler::tryToBeginDragAtPoint(const IntPoint& clientPosition, const I > return handledDrag; > } > >-#endif >+bool EventHandler::supportsSelectionUpdatesOnMouseDrag() const >+{ >+ return false; >+} >+ >+bool EventHandler::shouldAllowMouseDownToStartDrag() const >+{ >+ return m_shouldAllowMouseDownToStartDrag; >+} >+ >+#endif // ENABLE(DRAG_SUPPORT) > > } > >diff --git a/Source/WebCore/testing/InternalSettings.cpp b/Source/WebCore/testing/InternalSettings.cpp >index 13ccd5db902e79beee59ce8c19fa5d319d27f708..39cffd56a51f3f95b515d7c7345cd358f437b1dc 100644 >--- a/Source/WebCore/testing/InternalSettings.cpp >+++ b/Source/WebCore/testing/InternalSettings.cpp >@@ -100,6 +100,7 @@ InternalSettings::Backup::Backup(Settings& settings) > , m_deferredCSSParserEnabled(settings.deferredCSSParserEnabled()) > , m_inputEventsEnabled(settings.inputEventsEnabled()) > , m_incompleteImageBorderEnabled(settings.incompleteImageBorderEnabled()) >+ , m_shouldDispatchSyntheticMouseEventsWhenModifyingSelection(settings.shouldDispatchSyntheticMouseEventsWhenModifyingSelection()) > , m_shouldDeactivateAudioSession(PlatformMediaSessionManager::shouldDeactivateAudioSession()) > , m_userInterfaceDirectionPolicy(settings.userInterfaceDirectionPolicy()) > , m_systemLayoutDirection(settings.systemLayoutDirection()) >@@ -207,6 +208,7 @@ void InternalSettings::Backup::restoreTo(Settings& settings) > FontCache::singleton().setShouldMockBoldSystemFontForAccessibility(m_shouldMockBoldSystemFontForAccessibility); > settings.setFrameFlattening(m_frameFlattening); > settings.setIncompleteImageBorderEnabled(m_incompleteImageBorderEnabled); >+ settings.setShouldDispatchSyntheticMouseEventsWhenModifyingSelection(m_shouldDispatchSyntheticMouseEventsWhenModifyingSelection); > PlatformMediaSessionManager::setShouldDeactivateAudioSession(m_shouldDeactivateAudioSession); > > #if ENABLE(INDEXED_DATABASE_IN_WORKERS) >@@ -926,6 +928,14 @@ ExceptionOr<void> InternalSettings::setIncompleteImageBorderEnabled(bool enabled > return { }; > } > >+ExceptionOr<void> InternalSettings::setShouldDispatchSyntheticMouseEventsWhenModifyingSelection(bool shouldDispatch) >+{ >+ if (!m_page) >+ return Exception { InvalidAccessError }; >+ settings().setShouldDispatchSyntheticMouseEventsWhenModifyingSelection(shouldDispatch); >+ return { }; >+} >+ > static InternalSettings::ForcedAccessibilityValue settingsToInternalSettingsValue(Settings::ForcedAccessibilityValue value) > { > switch (value) { >diff --git a/Source/WebCore/testing/InternalSettings.h b/Source/WebCore/testing/InternalSettings.h >index 8ff0f4bdc8c2ca00db7aa7f4c476685316ad0bd9..fa73394d06e8751b3c73d0724aa8c280c50beff5 100644 >--- a/Source/WebCore/testing/InternalSettings.h >+++ b/Source/WebCore/testing/InternalSettings.h >@@ -103,6 +103,7 @@ public: > ExceptionOr<void> setShouldManageAudioSessionCategory(bool); > ExceptionOr<void> setCustomPasteboardDataEnabled(bool); > ExceptionOr<void> setIncompleteImageBorderEnabled(bool); >+ ExceptionOr<void> setShouldDispatchSyntheticMouseEventsWhenModifyingSelection(bool); > > using FrameFlatteningValue = FrameFlattening; > ExceptionOr<void> setFrameFlattening(FrameFlatteningValue); >@@ -198,6 +199,7 @@ private: > bool m_deferredCSSParserEnabled; > bool m_inputEventsEnabled; > bool m_incompleteImageBorderEnabled; >+ bool m_shouldDispatchSyntheticMouseEventsWhenModifyingSelection; > bool m_shouldDeactivateAudioSession; > UserInterfaceDirectionPolicy m_userInterfaceDirectionPolicy; > TextDirection m_systemLayoutDirection; >diff --git a/Source/WebCore/testing/InternalSettings.idl b/Source/WebCore/testing/InternalSettings.idl >index 20a3785837692e88f1be2118291473ff0b65cddc..aaf24a9920be9e0fca97e3fb65957e4b39c27b4d 100644 >--- a/Source/WebCore/testing/InternalSettings.idl >+++ b/Source/WebCore/testing/InternalSettings.idl >@@ -88,6 +88,7 @@ enum FontLoadTimingOverride { "Block", "Swap", "Failure" }; > [MayThrowException] void setInlineMediaPlaybackRequiresPlaysInlineAttribute(boolean requires); > [MayThrowException] void setFrameFlattening(FrameFlatteningValue frameFlattening); > [MayThrowException] void setIncompleteImageBorderEnabled(boolean enabled); >+ [MayThrowException] void setShouldDispatchSyntheticMouseEventsWhenModifyingSelection(boolean shouldDispatch); > > // RuntimeEnabledFeatures. > void setIndexedDBWorkersEnabled(boolean enabled); >diff --git a/Source/WebKit/UIProcess/WebPageProxy.cpp b/Source/WebKit/UIProcess/WebPageProxy.cpp >index 4e1aef9a6b4452f31659088b1e7cc94919ac6e94..313a9fae050b538d7b7564882ca15ff44d07aa04 100644 >--- a/Source/WebKit/UIProcess/WebPageProxy.cpp >+++ b/Source/WebKit/UIProcess/WebPageProxy.cpp >@@ -2014,6 +2014,14 @@ void WebPageProxy::scheduleFullEditorStateUpdate() > m_process->send(Messages::WebPage::ScheduleFullEditorStateUpdate(), m_pageID); > } > >+void WebPageProxy::selectAll() >+{ >+ if (!hasRunningProcess()) >+ return; >+ >+ m_process->send(Messages::WebPage::SelectAll(), m_pageID); >+} >+ > void WebPageProxy::executeEditCommand(const String& commandName, const String& argument, WTF::Function<void(CallbackBase::Error)>&& callbackFunction) > { > if (!hasRunningProcess()) { >diff --git a/Source/WebKit/UIProcess/WebPageProxy.h b/Source/WebKit/UIProcess/WebPageProxy.h >index 8720eebd7fb7ccf0b37388ab8bf2fdfed7b1c19b..f2c431559a07a3a74890272141a5a99a2680600f 100644 >--- a/Source/WebKit/UIProcess/WebPageProxy.h >+++ b/Source/WebKit/UIProcess/WebPageProxy.h >@@ -595,6 +595,7 @@ public: > > void addMIMETypeWithCustomContentProvider(const String& mimeType); > >+ void selectAll(); > void executeEditCommand(const String& commandName, const String& argument = String()); > void validateCommand(const String& commandName, WTF::Function<void (const String&, bool, int32_t, CallbackBase::Error)>&&); > >diff --git a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm >index 77de7c8a4c310e0c47daf44129110f046e66dfd0..75b93d61039872f44d994459edff6431ca69f1a9 100644 >--- a/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm >+++ b/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm >@@ -3079,7 +3079,7 @@ - (void)selectForWebView:(id)sender > - (void)selectAllForWebView:(id)sender > { > [_textSelectionAssistant selectAll:sender]; >- _page->executeEditCommand("selectAll"_s); >+ _page->selectAll(); > } > > - (void)toggleBoldfaceForWebView:(id)sender >diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.cpp b/Source/WebKit/WebProcess/WebPage/WebPage.cpp >index 4370222ae8d38776799307f8903916735cf80a80..fff5983aa0849dbbab77fdbe50f9c4a51351427d 100644 >--- a/Source/WebKit/WebProcess/WebPage/WebPage.cpp >+++ b/Source/WebKit/WebProcess/WebPage/WebPage.cpp >@@ -1109,6 +1109,26 @@ void WebPage::executeEditCommandWithCallback(const String& commandName, const St > send(Messages::WebPageProxy::VoidCallback(callbackID)); > } > >+void WebPage::selectAll() >+{ >+ executeEditingCommand("SelectAll"_s, { }); >+ platformDidSelectAll(); >+} >+ >+bool WebPage::shouldDispatchSyntheticMouseEventsWhenModifyingSelection() const >+{ >+ auto* document = m_page->mainFrame().document(); >+ return document && document->quirks().shouldDispatchSyntheticMouseEventsWhenModifyingSelection(); >+} >+ >+#if !PLATFORM(IOS_FAMILY) >+ >+void WebPage::platformDidSelectAll() >+{ >+} >+ >+#endif // !PLATFORM(IOS_FAMILY) >+ > void WebPage::updateEditorStateAfterLayoutIfEditabilityChanged() > { > // FIXME: We should update EditorStateIsContentEditable to track whether the state is richly >diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.h b/Source/WebKit/WebProcess/WebPage/WebPage.h >index d9d804cbd637991052c8c6d2c1ce229e67d36a98..4ff859c755500b51ad1943b75cafe938d9adba79 100644 >--- a/Source/WebKit/WebProcess/WebPage/WebPage.h >+++ b/Source/WebKit/WebProcess/WebPage/WebPage.h >@@ -597,6 +597,7 @@ public: > void disabledAdaptationsDidChange(const OptionSet<WebCore::DisabledAdaptations>&); > void viewportPropertiesDidChange(const WebCore::ViewportArguments&); > void executeEditCommandWithCallback(const String&, const String& argument, CallbackID); >+ void selectAll(); > > void textInputContextsInRect(WebCore::FloatRect, CompletionHandler<void(const Vector<WebKit::TextInputContext>&)>&&); > void focusTextInputContext(const TextInputContext&, CompletionHandler<void(bool)>&&); >@@ -1227,6 +1228,7 @@ private: > void resetTextAutosizing(); > WebCore::VisiblePosition visiblePositionInFocusedNodeForPoint(const WebCore::Frame&, const WebCore::IntPoint&, bool isInteractingWithFocusedElement); > RefPtr<WebCore::Range> rangeForGranularityAtPoint(WebCore::Frame&, const WebCore::IntPoint&, uint32_t granularity, bool isInteractingWithFocusedElement); >+ void dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch, const WebCore::IntPoint&); > > void sendPositionInformation(InteractionInformationAtPosition&&); > InteractionInformationAtPosition positionInformation(const InteractionInformationRequest&); >@@ -1465,6 +1467,9 @@ private: > void capitalizeWord(); > #endif > >+ bool shouldDispatchSyntheticMouseEventsWhenModifyingSelection() const; >+ void platformDidSelectAll(); >+ > #if ENABLE(CONTEXT_MENUS) > void didSelectItemFromActiveContextMenu(const WebContextMenuItemData&); > #endif >diff --git a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in >index 0123fd9d4fb4b0a19097677046c4dceed6c980ac..c012631f0c82bc2bbd42d5c538abe36b5689703f 100644 >--- a/Source/WebKit/WebProcess/WebPage/WebPage.messages.in >+++ b/Source/WebKit/WebProcess/WebPage/WebPage.messages.in >@@ -202,6 +202,7 @@ messages -> WebPage LegacyReceiver { > RunJavaScriptInFrame(uint64_t frameID, String script, bool forceUserGesture, WebKit::CallbackID callbackID) > ForceRepaint(WebKit::CallbackID callbackID) > >+ SelectAll() > ScheduleFullEditorStateUpdate() > > #if PLATFORM(COCOA) >diff --git a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm >index c2db1f5b30b3c70e1993fe65d63e13581c2f8568..0d05744c9beb6f02f04c7afc1cf4c982f638132e 100644 >--- a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm >+++ b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm >@@ -1479,12 +1479,61 @@ static RefPtr<Range> rangeAtWordBoundaryForPosition(Frame* frame, const VisibleP > return (base < extent) ? Range::create(*frame->document(), base, extent) : Range::create(*frame->document(), extent, base); > } > >-void WebPage::clearSelection(){ >+static IntRect elementRectInRootViewCoordinates(const Element& element) >+{ >+ auto* frame = element.document().frame(); >+ if (!frame) >+ return { }; >+ >+ auto* view = frame->view(); >+ if (!view) >+ return { }; >+ >+ auto* renderer = element.renderer(); >+ if (!renderer) >+ return { }; >+ >+ return view->contentsToRootView(renderer->absoluteBoundingBoxRect()); >+} >+ >+void WebPage::clearSelection() >+{ > m_startingGestureRange = nullptr; > m_currentBlockSelection = nullptr; > m_page->focusController().focusedOrMainFrame().selection().clear(); > } > >+void WebPage::dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch touch, const IntPoint& point) >+{ >+ auto frame = makeRef(m_page->focusController().focusedOrMainFrame()); >+ if (!frame->selection().selection().isContentEditable()) >+ return; >+ >+ IntRect focusedElementRect; >+ if (m_focusedElement) >+ focusedElementRect = elementRectInRootViewCoordinates(*m_focusedElement); >+ >+ if (focusedElementRect.isEmpty()) >+ return; >+ >+ auto adjustedPoint = point.constrainedBetween(focusedElementRect.minXMinYCorner(), focusedElementRect.maxXMaxYCorner()); >+ auto& eventHandler = m_page->mainFrame().eventHandler(); >+ switch (touch) { >+ case SelectionTouch::Started: >+ eventHandler.handleMousePressEvent({ adjustedPoint, adjustedPoint, LeftButton, PlatformEvent::MousePressed, 1, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, NoTap }); >+ break; >+ case SelectionTouch::Moved: >+ eventHandler.dispatchSyntheticMouseMove({ adjustedPoint, adjustedPoint, LeftButton, PlatformEvent::MouseMoved, 0, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, NoTap }); >+ break; >+ case SelectionTouch::Ended: >+ case SelectionTouch::EndedMovingForward: >+ case SelectionTouch::EndedMovingBackward: >+ case SelectionTouch::EndedNotMoving: >+ eventHandler.handleMouseReleaseEvent({ adjustedPoint, adjustedPoint, LeftButton, PlatformEvent::MouseReleased, 1, false, false, false, false, WallTime::now(), WebCore::ForceAtClick, NoTap }); >+ break; >+ } >+} >+ > void WebPage::updateSelectionWithTouches(const IntPoint& point, uint32_t touches, bool baseIsStart, CallbackID callbackID) > { > Frame& frame = m_page->focusController().focusedOrMainFrame(); >@@ -1499,7 +1548,11 @@ void WebPage::updateSelectionWithTouches(const IntPoint& point, uint32_t touches > VisiblePosition result; > SelectionFlags flags = None; > >- switch (static_cast<SelectionTouch>(touches)) { >+ auto selectionTouch = static_cast<SelectionTouch>(touches); >+ if (shouldDispatchSyntheticMouseEventsWhenModifyingSelection()) >+ dispatchSyntheticMouseEventsForSelectionGesture(selectionTouch, point); >+ >+ switch (selectionTouch) { > case SelectionTouch::Started: > case SelectionTouch::EndedNotMoving: > break; >@@ -1557,7 +1610,36 @@ void WebPage::extendSelection(uint32_t granularity) > return; > > VisiblePosition position = frame.selection().selection().start(); >- frame.selection().setSelectedRange(wordRangeFromPosition(position).get(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered); >+ auto wordRange = wordRangeFromPosition(position); >+ if (!wordRange) >+ return; >+ >+ IntPoint endLocationForSyntheticMouseEvents; >+ bool shouldDispatchMouseEvents = shouldDispatchSyntheticMouseEventsWhenModifyingSelection(); >+ if (shouldDispatchMouseEvents) { >+ auto startLocationForSyntheticMouseEvents = frame.view()->contentsToRootView(VisiblePosition(wordRange->startPosition()).absoluteCaretBounds()).center(); >+ endLocationForSyntheticMouseEvents = frame.view()->contentsToRootView(VisiblePosition(wordRange->endPosition()).absoluteCaretBounds()).center(); >+ dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Started, startLocationForSyntheticMouseEvents); >+ dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Moved, endLocationForSyntheticMouseEvents); >+ } >+ >+ frame.selection().setSelectedRange(wordRange.get(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered); >+ >+ if (shouldDispatchMouseEvents) >+ dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Ended, endLocationForSyntheticMouseEvents); >+} >+ >+void WebPage::platformDidSelectAll() >+{ >+ if (!shouldDispatchSyntheticMouseEventsWhenModifyingSelection()) >+ return; >+ >+ auto frame = makeRef(m_page->focusController().focusedOrMainFrame()); >+ auto startCaretRect = frame->view()->contentsToRootView(VisiblePosition(frame->selection().selection().start()).absoluteCaretBounds()); >+ auto endCaretRect = frame->view()->contentsToRootView(VisiblePosition(frame->selection().selection().end()).absoluteCaretBounds()); >+ dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Started, startCaretRect.center()); >+ dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Moved, endCaretRect.center()); >+ dispatchSyntheticMouseEventsForSelectionGesture(SelectionTouch::Ended, endCaretRect.center()); > } > > void WebPage::selectWordBackward() >@@ -2584,19 +2666,6 @@ void WebPage::focusNextFocusedElement(bool isForward, CallbackID callbackID) > send(Messages::WebPageProxy::VoidCallback(callbackID)); > } > >-static IntRect elementRectInRootViewCoordinates(const Node& node, const Frame& frame) >-{ >- auto* view = frame.view(); >- if (!view) >- return { }; >- >- auto* renderer = node.renderer(); >- if (!renderer) >- return { }; >- >- return view->contentsToRootView(renderer->absoluteBoundingBoxRect()); >-} >- > void WebPage::getFocusedElementInformation(FocusedElementInformation& information) > { > layoutIfNeeded(); >@@ -2604,8 +2673,7 @@ void WebPage::getFocusedElementInformation(FocusedElementInformation& informatio > information.lastInteractionLocation = m_lastInteractionLocation; > > if (auto* renderer = m_focusedElement->renderer()) { >- auto& elementFrame = m_page->focusController().focusedOrMainFrame(); >- information.elementRect = elementRectInRootViewCoordinates(*m_focusedElement, elementFrame); >+ information.elementRect = elementRectInRootViewCoordinates(*m_focusedElement); > information.nodeFontSize = renderer->style().fontDescription().computedSize(); > > bool inFixed = false; >@@ -2621,13 +2689,11 @@ void WebPage::getFocusedElementInformation(FocusedElementInformation& informatio > information.allowsUserScaling = m_viewportConfiguration.allowsUserScaling(); > information.allowsUserScalingIgnoringAlwaysScalable = m_viewportConfiguration.allowsUserScalingIgnoringAlwaysScalable(); > if (auto* nextElement = nextAssistableElement(m_focusedElement.get(), *m_page, true)) { >- if (auto* frame = nextElement->document().frame()) >- information.nextNodeRect = elementRectInRootViewCoordinates(*nextElement, *frame); >+ information.nextNodeRect = elementRectInRootViewCoordinates(*nextElement); > information.hasNextNode = true; > } > if (auto* previousElement = nextAssistableElement(m_focusedElement.get(), *m_page, false)) { >- if (auto* frame = previousElement->document().frame()) >- information.previousNodeRect = elementRectInRootViewCoordinates(*previousElement, *frame); >+ information.previousNodeRect = elementRectInRootViewCoordinates(*previousElement); > information.hasPreviousNode = true; > } > information.focusedElementIdentifier = m_currentFocusedElementIdentifier; >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index db7df12c15a32883dd7484ab1d9c324fd627a6a6..9b21c33d47f6652816c6d7a3fb9404616d1f2e56 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,22 @@ >+2019-05-07 Wenson Hsieh <wenson_hsieh@apple.com> >+ >+ [iOS] Add a quirk to synthesize mouse events when modifying the selection >+ https://bugs.webkit.org/show_bug.cgi?id=197683 >+ <rdar://problem/48003980> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Adds a new layout test to enable the site-specific quirk and verify that mouse events are dispatched when >+ changing selection, both via the callout menu and by moving the selection grabber using gestures. >+ >+ * editing/selection/ios/dispatch-mouse-events-when-modifying-selection-quirk-expected.txt: Added. >+ * editing/selection/ios/dispatch-mouse-events-when-modifying-selection-quirk.html: Added. >+ * resources/ui-helper.js: >+ (window.UIHelper.waitForMenuToHide.return.new.Promise): >+ (window.UIHelper.waitForMenuToHide): >+ >+ Introduce a new helper method to wait for the menu to hide (on iOS, this refers to the callout menu). >+ > 2019-05-08 Wenson Hsieh <wenson_hsieh@apple.com> > > TestRunnerWKWebView's menu callbacks should be cleared upon UI script completion >diff --git a/LayoutTests/editing/selection/ios/dispatch-mouse-events-when-modifying-selection-quirk-expected.txt b/LayoutTests/editing/selection/ios/dispatch-mouse-events-when-modifying-selection-quirk-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..dfbdfddea8b0221453f2ab521210a4290da6c090 >--- /dev/null >+++ b/LayoutTests/editing/selection/ios/dispatch-mouse-events-when-modifying-selection-quirk-expected.txt >@@ -0,0 +1,30 @@ >+This test verifies that when the 'mouse event synthesis on selection' quirk is enabled, text selection dispatches mouse events that mimic the user selecting text. To run the test manually, use the callout menu to select text or selection handles, and verify that mousedown, mousemove and mouseup are dispatched and logged in the output area below. >+ >+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". >+ >+ >+Attempting to show the callout bar. >+Observed event type: mousedown >+Observed event type: mouseup >+Observed event type: mousedown >+Observed event type: mouseup >+PASS Displayed the callout bar. >+ >+Attempting to select the last word. >+Observed event type: mousedown >+Observed event type: mousemove >+Observed event type: mouseup >+PASS Selected the last word. >+ >+Attempting to dismiss the callout bar by executing 'SelectAll'. >+PASS Dismissed the callout bar. >+ >+Attempting to move the selection grabber. >+Observed event type: mousedown >+Observed event type: mousemove >+Observed event type: mouseup >+PASS Moved the selection grabber. >+PASS successfullyParsed is true >+ >+TEST COMPLETE >+ >diff --git a/LayoutTests/editing/selection/ios/dispatch-mouse-events-when-modifying-selection-quirk.html b/LayoutTests/editing/selection/ios/dispatch-mouse-events-when-modifying-selection-quirk.html >new file mode 100644 >index 0000000000000000000000000000000000000000..0a2fb95a5dfa4c247adfab45be1f11b5b59cbf88 >--- /dev/null >+++ b/LayoutTests/editing/selection/ios/dispatch-mouse-events-when-modifying-selection-quirk.html >@@ -0,0 +1,98 @@ >+<!DOCTYPE html><!-- webkit-test-runner [ useFlexibleViewport=true ] --> >+<html> >+<head> >+<script src="../../../resources/ui-helper.js"></script> >+<script src="../../../resources/basic-gestures.js"></script> >+<script src="../../../resources/js-test.js"></script> >+<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no"> >+<style> >+body, html { >+ margin: 0; >+} >+.editor { >+ border: 2px solid tomato; >+ width: 300px; >+ height: 300px; >+ box-sizing: border-box; >+ font-size: 32px; >+ padding: 10px; >+} >+ >+#console, #description { >+ width: 300px; >+ height: 100px; >+ overflow: scroll; >+} >+</style> >+<script> >+if (window.internals) >+ internals.settings.setShouldDispatchSyntheticMouseEventsWhenModifyingSelection(true); >+ >+lastEvent = null; >+jsTestIsAsync = true; >+ >+function recordEvent() { >+ if (!lastEvent || event.type !== lastEvent.type) >+ debug(`Observed event type: ${event.type}`); >+ lastEvent = event; >+} >+ >+addEventListener("mousedown", recordEvent); >+addEventListener("mousemove", recordEvent); >+addEventListener("mouseup", recordEvent); >+ >+async function waitForSelectionToAppear() >+{ >+ while (true) { >+ const rects = await UIHelper.getUISelectionViewRects(); >+ if (rects.length) >+ return rects; >+ } >+} >+ >+function midPointOfRect(rect) { >+ return [rect.left + (rect.width / 2), rect.top + (rect.height / 2)]; >+} >+ >+addEventListener("load", async () => { >+ if (!window.testRunner) >+ return; >+ >+ description("This test verifies that when the 'mouse event synthesis on selection' quirk is enabled, text selection dispatches mouse events that mimic the user selecting text. To run the test manually, use the callout menu to select text or selection handles, and verify that mousedown, mousemove and mouseup are dispatched and logged in the output area below."); >+ >+ debug("\nAttempting to show the callout bar."); >+ const editor = document.querySelector(".editor"); >+ await UIHelper.activateElementAndWaitForInputSession(editor); >+ await UIHelper.activateElement(editor); >+ await UIHelper.waitForMenuToShow(); >+ testPassed("Displayed the callout bar."); >+ >+ debug("\nAttempting to select the last word."); >+ await UIHelper.chooseMenuAction("Select"); >+ const rects = await waitForSelectionToAppear(); >+ testPassed("Selected the last word."); >+ >+ debug("\nAttempting to dismiss the callout bar by executing 'SelectAll'."); >+ document.execCommand("SelectAll"); >+ await UIHelper.waitForMenuToHide(); >+ testPassed("Dismissed the callout bar."); >+ >+ debug("\nAttempting to move the selection grabber."); >+ const [grabberX, grabberY] = midPointOfRect(await UIHelper.getSelectionEndGrabberViewRect()); >+ const touchDestinationX = grabberX - rects[0].width; >+ >+ await touchAndDragFromPointToPoint(grabberX, grabberY, touchDestinationX, grabberY); >+ await liftUpAtPoint(touchDestinationX, grabberY); >+ testPassed("Moved the selection grabber."); >+ >+ document.querySelector(".editor").remove(); >+ finishJSTest(); >+}); >+</script> >+</head> >+<body> >+<div class="editor" contenteditable>The quick brown fox jumped over the lazy dog.</div> >+<div id="description"></div> >+<div id="console"></div> >+</body> >+</html> >diff --git a/LayoutTests/resources/ui-helper.js b/LayoutTests/resources/ui-helper.js >index 30ab8b521e60daa7f8cde29cdf90addf684af3ca..eea193e38ec8e7ed853a09368938c2d4b13ee360 100644 >--- a/LayoutTests/resources/ui-helper.js >+++ b/LayoutTests/resources/ui-helper.js >@@ -827,6 +827,19 @@ window.UIHelper = class UIHelper { > }); > } > >+ static waitForMenuToHide() >+ { >+ return new Promise(resolve => { >+ testRunner.runUIScript(` >+ (function() { >+ if (uiController.isShowingMenu) >+ uiController.didHideMenuCallback = () => uiController.uiScriptComplete(); >+ else >+ uiController.uiScriptComplete(); >+ })()`, resolve); >+ }); >+ } >+ > static isShowingMenu() > { > return new Promise(resolve => {
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 197683
:
369354
| 369389