Changeset 245320 in webkit
- Timestamp:
- May 15, 2019 1:37:02 AM (5 years ago)
- Location:
- trunk/Source
- Files:
-
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebCore/ChangeLog
r245317 r245320 1 2019-05-15 Devin Rousso <drousso@apple.com> 2 3 Web Automation: elements larger than the viewport have incorrect in-view center point 4 https://bugs.webkit.org/show_bug.cgi?id=195696 5 <rdar://problem/48737122> 6 7 Reviewed by Simon Fraser. 8 9 Original patch by Brian Burg <bburg@apple.com>. 10 11 Some conversion methods do not exist for `FloatRect`/`FloatPoint`. Fill them in as needed, 12 and export some symbols used by WebDriver code to compute an element's in-view center point 13 in various coordinate systems. 14 15 * dom/TreeScope.h: 16 * dom/TreeScope.cpp: 17 (WebCore::TreeScope::elementsFromPoint): Added. 18 * page/FrameView.h: 19 * page/FrameView.cpp: 20 (WebCore::FrameView::absoluteToLayoutViewportPoint const): Added. 21 (WebCore::FrameView::layoutViewportToAbsoluteRect const): Added. 22 (WebCore::FrameView::absoluteToLayoutViewportRect const): Added. 23 * platform/ScrollView.h: 24 * platform/ScrollView.cpp: 25 (WebCore::ScrollView::viewToContents const): Added. 26 (WebCore::ScrollView::contentsToView const): Added. 27 (WebCore::ScrollView::contentsToRootView const): Added. 28 * platform/Widget.h: 29 * platform/Widget.cpp: 30 (WebCore::Widget::convertToRootView const): Added. 31 (WebCore::Widget::convertFromRootView const): Added. 32 (WebCore::Widget::convertToContainingView const): Added. 33 (WebCore::Widget::convertFromContainingView const): Added. 34 1 35 2019-05-14 Wenson Hsieh <wenson_hsieh@apple.com> 2 36 -
trunk/Source/WebCore/dom/TreeScope.cpp
r239427 r245320 442 442 } 443 443 444 Vector<RefPtr<Element>> TreeScope::elementsFromPoint(const FloatPoint& p) 445 { 446 return elementsFromPoint(p.x(), p.y()); 447 } 448 444 449 Element* TreeScope::findAnchor(const String& name) 445 450 { -
trunk/Source/WebCore/dom/TreeScope.h
r233778 r245320 38 38 class Document; 39 39 class Element; 40 class FloatPoint; 40 41 class HTMLImageElement; 41 42 class HTMLLabelElement; … … 96 97 WEBCORE_EXPORT RefPtr<Element> elementFromPoint(double clientX, double clientY); 97 98 WEBCORE_EXPORT Vector<RefPtr<Element>> elementsFromPoint(double clientX, double clientY); 99 WEBCORE_EXPORT Vector<RefPtr<Element>> elementsFromPoint(const FloatPoint&); 98 100 99 101 // Find first anchor with the given name. -
trunk/Source/WebCore/page/FrameView.cpp
r244731 r245320 4792 4792 } 4793 4793 4794 FloatPoint FrameView::absoluteToLayoutViewportPoint(FloatPoint p) const 4795 { 4796 ASSERT(frame().settings().visualViewportEnabled()); 4797 p.scale(1 / frame().frameScaleFactor()); 4798 p.moveBy(-layoutViewportRect().location()); 4799 return p; 4800 } 4801 4794 4802 FloatPoint FrameView::layoutViewportToAbsolutePoint(FloatPoint p) const 4795 4803 { … … 4797 4805 p.moveBy(layoutViewportRect().location()); 4798 4806 return p.scaled(frame().frameScaleFactor()); 4807 } 4808 4809 FloatRect FrameView::layoutViewportToAbsoluteRect(FloatRect rect) const 4810 { 4811 ASSERT(frame().settings().visualViewportEnabled()); 4812 rect.moveBy(layoutViewportRect().location()); 4813 rect.scale(frame().frameScaleFactor()); 4814 return rect; 4815 } 4816 4817 FloatRect FrameView::absoluteToLayoutViewportRect(FloatRect rect) const 4818 { 4819 ASSERT(frame().settings().visualViewportEnabled()); 4820 rect.scale(1 / frame().frameScaleFactor()); 4821 rect.moveBy(-layoutViewportRect().location()); 4822 return rect; 4799 4823 } 4800 4824 -
trunk/Source/WebCore/page/FrameView.h
r244633 r245320 476 476 float absoluteToDocumentScaleFactor(Optional<float> effectiveZoom = WTF::nullopt) const; 477 477 478 FloatRect absoluteToDocumentRect(FloatRect, Optional<float> effectiveZoom = WTF::nullopt) const;479 FloatPoint absoluteToDocumentPoint(FloatPoint, Optional<float> effectiveZoom = WTF::nullopt) const;478 WEBCORE_EXPORT FloatRect absoluteToDocumentRect(FloatRect, Optional<float> effectiveZoom = WTF::nullopt) const; 479 WEBCORE_EXPORT FloatPoint absoluteToDocumentPoint(FloatPoint, Optional<float> effectiveZoom = WTF::nullopt) const; 480 480 481 481 FloatRect absoluteToClientRect(FloatRect, Optional<float> effectiveZoom = WTF::nullopt) const; 482 482 483 483 FloatSize documentToClientOffset() const; 484 FloatRect documentToClientRect(FloatRect) const;484 WEBCORE_EXPORT FloatRect documentToClientRect(FloatRect) const; 485 485 FloatPoint documentToClientPoint(FloatPoint) const; 486 486 WEBCORE_EXPORT FloatRect clientToDocumentRect(FloatRect) const; 487 487 WEBCORE_EXPORT FloatPoint clientToDocumentPoint(FloatPoint) const; 488 488 489 WEBCORE_EXPORT FloatPoint absoluteToLayoutViewportPoint(FloatPoint) const; 489 490 FloatPoint layoutViewportToAbsolutePoint(FloatPoint) const; 491 492 WEBCORE_EXPORT FloatRect absoluteToLayoutViewportRect(FloatRect) const; 493 FloatRect layoutViewportToAbsoluteRect(FloatRect) const; 490 494 491 495 // Unlike client coordinates, layout viewport coordinates are affected by page zoom. -
trunk/Source/WebCore/platform/ScrollView.cpp
r243919 r245320 843 843 } 844 844 845 FloatPoint ScrollView::viewToContents(const FloatPoint& point) const 846 { 847 if (delegatesScrolling()) 848 return point; 849 850 return viewToContents(IntPoint(point)); 851 } 852 853 FloatPoint ScrollView::contentsToView(const FloatPoint& point) const 854 { 855 if (delegatesScrolling()) 856 return point; 857 858 return contentsToView(IntPoint(point)); 859 } 860 845 861 IntRect ScrollView::viewToContents(IntRect rect) const 846 862 { … … 909 925 } 910 926 927 FloatPoint ScrollView::contentsToRootView(const FloatPoint& contentsPoint) const 928 { 929 return convertToRootView(contentsToView(contentsPoint)); 930 } 931 911 932 IntRect ScrollView::rootViewToContents(const IntRect& rootViewRect) const 912 933 { … … 917 938 { 918 939 return viewToContents(convertFromRootView(rootViewRect)); 940 } 941 942 FloatRect ScrollView::contentsToRootView(const FloatRect& contentsRect) const 943 { 944 return convertToRootView(contentsToView(contentsRect)); 919 945 } 920 946 -
trunk/Source/WebCore/platform/ScrollView.h
r243905 r245320 231 231 232 232 // Scroll position used by web-exposed features (has legacy iOS behavior). 233 IntPoint contentsScrollPosition() const;233 WEBCORE_EXPORT IntPoint contentsScrollPosition() const; 234 234 void setContentsScrollPosition(const IntPoint&); 235 235 … … 280 280 WEBCORE_EXPORT IntPoint rootViewToContents(const IntPoint&) const; 281 281 WEBCORE_EXPORT IntPoint contentsToRootView(const IntPoint&) const; 282 WEBCORE_EXPORT FloatPoint contentsToRootView(const FloatPoint&) const; 282 283 WEBCORE_EXPORT IntRect rootViewToContents(const IntRect&) const; 283 284 WEBCORE_EXPORT IntRect contentsToRootView(const IntRect&) const; 284 285 WEBCORE_EXPORT FloatRect rootViewToContents(const FloatRect&) const; 286 WEBCORE_EXPORT FloatRect contentsToRootView(const FloatRect&) const; 285 287 286 288 IntPoint viewToContents(const IntPoint&) const; 287 289 IntPoint contentsToView(const IntPoint&) const; 290 291 FloatPoint viewToContents(const FloatPoint&) const; 292 FloatPoint contentsToView(const FloatPoint&) const; 288 293 289 294 IntRect viewToContents(IntRect) const; -
trunk/Source/WebCore/platform/Widget.cpp
r238336 r245320 96 96 } 97 97 98 FloatRect Widget::convertToRootView(const FloatRect& localRect) const 99 { 100 if (const ScrollView* parentScrollView = parent()) { 101 FloatRect parentRect = convertToContainingView(localRect); 102 return parentScrollView->convertToRootView(parentRect); 103 } 104 return localRect; 105 } 106 98 107 IntPoint Widget::convertFromRootView(const IntPoint& rootPoint) const 99 108 { … … 109 118 if (const ScrollView* parentScrollView = parent()) { 110 119 IntPoint parentPoint = convertToContainingView(localPoint); 120 return parentScrollView->convertToRootView(parentPoint); 121 } 122 return localPoint; 123 } 124 125 126 FloatPoint Widget::convertFromRootView(const FloatPoint& rootPoint) const 127 { 128 if (const ScrollView* parentScrollView = parent()) { 129 FloatPoint parentPoint = parentScrollView->convertFromRootView(rootPoint); 130 return convertFromContainingView(parentPoint); 131 } 132 return rootPoint; 133 } 134 135 FloatPoint Widget::convertToRootView(const FloatPoint& localPoint) const 136 { 137 if (const ScrollView* parentScrollView = parent()) { 138 FloatPoint parentPoint = convertToContainingView(localPoint); 111 139 return parentScrollView->convertToRootView(parentPoint); 112 140 } … … 205 233 } 206 234 235 FloatRect Widget::convertToContainingView(const FloatRect& localRect) const 236 { 237 return convertToContainingView(IntRect(localRect)); 238 } 239 207 240 FloatRect Widget::convertFromContainingView(const FloatRect& parentRect) const 208 241 { … … 226 259 } 227 260 261 FloatPoint Widget::convertToContainingView(const FloatPoint& localPoint) const 262 { 263 return convertToContainingView(IntPoint(localPoint)); 264 } 265 266 FloatPoint Widget::convertFromContainingView(const FloatPoint& parentPoint) const 267 { 268 return convertFromContainingView(IntPoint(parentPoint)); 269 } 270 228 271 #if !PLATFORM(COCOA) && !PLATFORM(GTK) && !PLATFORM(WIN) 229 272 -
trunk/Source/WebCore/platform/Widget.h
r240901 r245320 151 151 IntRect convertFromRootView(const IntRect&) const; 152 152 153 FloatRect convertToRootView(const FloatRect&) const; 153 154 FloatRect convertFromRootView(const FloatRect&) const; 154 155 155 156 IntPoint convertToRootView(const IntPoint&) const; 156 157 IntPoint convertFromRootView(const IntPoint&) const; 158 159 FloatPoint convertToRootView(const FloatPoint&) const; 160 FloatPoint convertFromRootView(const FloatPoint&) const; 157 161 158 162 // It is important for cross-platform code to realize that Mac has flipped coordinates. Therefore any code … … 187 191 WEBCORE_EXPORT virtual IntRect convertToContainingView(const IntRect&) const; 188 192 WEBCORE_EXPORT virtual IntRect convertFromContainingView(const IntRect&) const; 193 WEBCORE_EXPORT virtual FloatRect convertToContainingView(const FloatRect&) const; 189 194 WEBCORE_EXPORT virtual FloatRect convertFromContainingView(const FloatRect&) const; 190 195 WEBCORE_EXPORT virtual IntPoint convertToContainingView(const IntPoint&) const; 191 196 WEBCORE_EXPORT virtual IntPoint convertFromContainingView(const IntPoint&) const; 197 WEBCORE_EXPORT virtual FloatPoint convertToContainingView(const FloatPoint&) const; 198 WEBCORE_EXPORT virtual FloatPoint convertFromContainingView(const FloatPoint&) const; 192 199 193 200 private: -
trunk/Source/WebKit/ChangeLog
r245301 r245320 1 2019-05-15 Devin Rousso <drousso@apple.com> 2 3 Web Automation: elements larger than the viewport have incorrect in-view center point 4 https://bugs.webkit.org/show_bug.cgi?id=195696 5 <rdar://problem/48737122> 6 7 Reviewed by Simon Fraser. 8 9 Original patch by Brian Burg <bburg@apple.com>. 10 11 This seems to be an omission in the specification. While it does mention that the in-view 12 center point (IVCP) must be within the viewport, the algorithm never intersects the element 13 bounding box with the viewport rect. 14 15 * WebProcess/Automation/WebAutomationSessionProxy.cpp: 16 (WebKit::WebAutomationSessionProxy::computeElementLayout): 17 This code is incorrect. For `CoordinateSystem::LayoutViewport`, coordinates should be in 18 root view coordinates so that it can be later converted to screen and synthesized as a HID 19 event in screen coordinates. Intersect the element rect and the viewport rect before finding 20 the center point of the part of the element that's visible in the viewport. 21 22 (WebKit::convertRectFromFrameClientToRootView): Added. 23 (WebKit::convertPointFromFrameClientToRootView): Added. 24 Added helpers to properly account for scroll contents position on iOS. 25 26 * UIProcess/Automation/WebAutomationSession.cpp: 27 (WebKit::WebAutomationSession::viewportInViewCenterPointOfElement): 28 Now that we determine whether the element is inside the viewport much earlier, if the 29 element has no `inViewCenterPoint`, we can return a `TargetOutOfBounds` instead of a more 30 "generic" `ElementNotInteractable`. 31 32 (WebKit::WebAutomationSession::simulateMouseInteraction): 33 Rename `locationInView` -> `locationInViewport`. 34 35 (WebKit::WebAutomationSession::simulateTouchInteraction): 36 This code is incorrect. The `unobscuredContentRect` is in screen coordinates, but 37 we are trying to see if (x, y) is outside the size of the viewport assumed to be at (0, 0). 38 Grab the visual viewport rect and see if the location exceeds the viewport size. 39 40 * UIProcess/Automation/ios/WebAutomationSessionIOS.mm: 41 (WebKit::operator<<): 42 Add logging helper for `TouchInteraction` enum. 43 44 (WebKit::WebAutomationSession::platformSimulateTouchInteraction): 45 Move local variable. 46 47 * UIProcess/Automation/SimulatedInputDispatcher.cpp: 48 (WebKit::SimulatedInputDispatcher::transitionInputSourceToState): 49 Fix a typo in logging. 50 51 * UIProcess/Automation/Automation.json: 52 Simplify enum name. 53 54 * Platform/Logging.h: 55 Add logging channel to dump fully resolved interaction details. 56 1 57 2019-05-14 Ross Kirsling <ross.kirsling@sony.com> 2 58 -
trunk/Source/WebKit/Platform/Logging.h
r244849 r245320 43 43 M(AdClickAttribution) \ 44 44 M(Automation) \ 45 M(AutomationInteractions) \ 45 46 M(ActivityState) \ 46 47 M(BackForward) \ -
trunk/Source/WebKit/UIProcess/Automation/Automation.json
r243340 r245320 33 33 "enum": [ 34 34 "Page", 35 " LayoutViewport"35 "Viewport" 36 36 ] 37 37 }, -
trunk/Source/WebKit/UIProcess/Automation/SimulatedInputDispatcher.cpp
r244814 r245320 291 291 #if !LOG_DISABLED 292 292 String mouseButtonName = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(a.pressedMouseButton.value()); 293 LOG(Automation, "SimulatedInputDispatcher[%p]: simulating MouseUp[button=%s] @ (%d, %d) for transition to %d.%d", this, mouseButtonName.utf8().data(), a.location.value().x(), a.location.value().y(), m_keyframeIndex, m_inputSourceStateIndex);293 LOG(Automation, "SimulatedInputDispatcher[%p]: simulating MouseUp[button=%s] @ (%d, %d) for transition to %d.%d", this, mouseButtonName.utf8().data(), b.location.value().x(), b.location.value().y(), m_keyframeIndex, m_inputSourceStateIndex); 294 294 #endif 295 295 m_client.simulateMouseInteraction(m_page, MouseInteraction::Up, a.pressedMouseButton.value(), b.location.value(), WTFMove(eventDispatchFinished)); … … 325 325 m_client.simulateTouchInteraction(m_page, TouchInteraction::TouchDown, b.location.value(), WTF::nullopt, WTFMove(eventDispatchFinished)); 326 326 } else if (a.pressedMouseButton && !b.pressedMouseButton) { 327 LOG(Automation, "SimulatedInputDispatcher[%p]: simulating LiftUp @ (%d, %d) for transition to %d.%d", this, a.location.value().x(), a.location.value().y(), m_keyframeIndex, m_inputSourceStateIndex);327 LOG(Automation, "SimulatedInputDispatcher[%p]: simulating LiftUp @ (%d, %d) for transition to %d.%d", this, b.location.value().x(), b.location.value().y(), m_keyframeIndex, m_inputSourceStateIndex); 328 328 m_client.simulateTouchInteraction(m_page, TouchInteraction::LiftUp, b.location.value(), WTF::nullopt, WTFMove(eventDispatchFinished)); 329 329 } else if (a.location != b.location) { -
trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp
r245046 r245320 1465 1465 1466 1466 if (!inViewCenterPoint) { 1467 completionHandler(WTF::nullopt, AUTOMATION_COMMAND_ERROR_WITH_NAME( ElementNotInteractable));1467 completionHandler(WTF::nullopt, AUTOMATION_COMMAND_ERROR_WITH_NAME(TargetOutOfBounds)); 1468 1468 return; 1469 1469 } … … 1478 1478 void WebAutomationSession::simulateMouseInteraction(WebPageProxy& page, MouseInteraction interaction, WebMouseEvent::Button mouseButton, const WebCore::IntPoint& locationInViewport, CompletionHandler<void(Optional<AutomationCommandError>)>&& completionHandler) 1479 1479 { 1480 WebCore::IntPoint locationInView = WebCore::IntPoint(locationInViewport.x(), locationInViewport.y() + page.topContentInset()); 1481 page.getWindowFrameWithCallback([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler), page = makeRef(page), interaction, mouseButton, locationInView](WebCore::FloatRect windowFrame) mutable { 1482 auto clippedX = std::min(std::max(0.0f, (float)locationInView.x()), windowFrame.size().width()); 1483 auto clippedY = std::min(std::max(0.0f, (float)locationInView.y()), windowFrame.size().height()); 1484 if (clippedX != locationInView.x() || clippedY != locationInView.y()) { 1480 page.getWindowFrameWithCallback([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler), page = makeRef(page), interaction, mouseButton, locationInViewport](WebCore::FloatRect windowFrame) mutable { 1481 auto clippedX = std::min(std::max(0.0f, (float)locationInViewport.x()), windowFrame.size().width()); 1482 auto clippedY = std::min(std::max(0.0f, (float)locationInViewport.y()), windowFrame.size().height()); 1483 if (clippedX != locationInViewport.x() || clippedY != locationInViewport.y()) { 1485 1484 completionHandler(AUTOMATION_COMMAND_ERROR_WITH_NAME(TargetOutOfBounds)); 1486 1485 return; … … 1497 1496 callbackInMap = WTFMove(mouseEventsFlushedCallback); 1498 1497 1499 platformSimulateMouseInteraction(page, interaction, mouseButton, locationInView , OptionSet<WebEvent::Modifier>::fromRaw(m_currentModifiers));1498 platformSimulateMouseInteraction(page, interaction, mouseButton, locationInViewport, OptionSet<WebEvent::Modifier>::fromRaw(m_currentModifiers)); 1500 1499 1501 1500 // If the event does not hit test anything in the window, then it may not have been delivered. … … 1514 1513 { 1515 1514 #if PLATFORM(IOS_FAMILY) 1516 if (!page.unobscuredContentRect().contains(locationInViewport)) { 1515 WebCore::FloatRect visualViewportBounds = WebCore::FloatRect({ }, page.unobscuredContentRect().size()); 1516 if (!visualViewportBounds.contains(locationInViewport)) { 1517 1517 completionHandler(AUTOMATION_COMMAND_ERROR_WITH_NAME(TargetOutOfBounds)); 1518 1518 return; -
trunk/Source/WebKit/UIProcess/Automation/ios/WebAutomationSessionIOS.mm
r244975 r245320 29 29 #if PLATFORM(IOS_FAMILY) 30 30 31 #import "Logging.h" 31 32 #import "NativeWebKeyboardEvent.h" 32 33 #import "WebAutomationSessionMacros.h" … … 176 177 177 178 #if ENABLE(WEBDRIVER_TOUCH_INTERACTIONS) 179 #if !LOG_DISABLED 180 static TextStream& operator<<(TextStream& ts, TouchInteraction interaction) 181 { 182 switch (interaction) { 183 case TouchInteraction::TouchDown: 184 ts << "TouchDown"; 185 break; 186 case TouchInteraction::MoveTo: 187 ts << "MoveTo"; 188 break; 189 case TouchInteraction::LiftUp: 190 ts << "LiftUp"; 191 break; 192 } 193 return ts; 194 } 195 #endif // !LOG_DISABLED 196 178 197 void WebAutomationSession::platformSimulateTouchInteraction(WebPageProxy& page, TouchInteraction interaction, const WebCore::IntPoint& locationInViewport, Optional<Seconds> duration, AutomationCompletionHandler&& completionHandler) 179 198 { 180 199 WebCore::IntPoint locationOnScreen = page.syncRootViewToScreen(IntRect(locationInViewport, IntSize())).location(); 181 _WKTouchEventGenerator *generator = [_WKTouchEventGenerator sharedTouchEventGenerator];200 LOG_WITH_STREAM(AutomationInteractions, stream << "platformSimulateTouchInteraction: interaction=" << interaction << ", locationInViewport=" << locationInViewport << ", locationOnScreen=" << locationOnScreen << ", duration=" << duration.valueOr(0_s).seconds()); 182 201 183 202 auto interactionFinished = makeBlockPtr([completionHandler = WTFMove(completionHandler)] () mutable { 184 203 completionHandler(WTF::nullopt); 185 204 }); 186 205 206 _WKTouchEventGenerator *generator = [_WKTouchEventGenerator sharedTouchEventGenerator]; 187 207 switch (interaction) { 188 208 case TouchInteraction::TouchDown: -
trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp
r244033 r245320 478 478 } 479 479 480 static Optional<WebCore::FloatPoint> elementInViewClientCenterPoint(WebCore::Element& element, bool& isObscured)481 {482 // §11.1 Element Interactability.483 // https://www.w3.org/TR/webdriver/#dfn-in-view-center-point484 auto* clientRect = element.getClientRects()->item(0);485 if (!clientRect)486 return WTF::nullopt;487 488 auto clientCenterPoint = WebCore::FloatPoint::narrowPrecision(0.5 * (clientRect->left() + clientRect->right()), 0.5 * (clientRect->top() + clientRect->bottom()));489 auto elementList = element.treeScope().elementsFromPoint(clientCenterPoint.x(), clientCenterPoint.y());490 if (elementList.isEmpty()) {491 // An element is obscured if the pointer-interactable paint tree at its center point is empty,492 // or the first element in this tree is not an inclusive descendant of itself.493 // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-obscured494 isObscured = true;495 return clientCenterPoint;496 }497 498 auto index = elementList.findMatching([&element] (auto& item) { return item.get() == &element; });499 if (index == notFound)500 return WTF::nullopt;501 502 if (index) {503 // Element is not the first one in the list.504 auto firstElement = elementList[0];505 isObscured = !firstElement->isDescendantOf(element);506 }507 508 return clientCenterPoint;509 }510 511 480 static WebCore::Element* containerElementForElement(WebCore::Element& element) 512 481 { … … 535 504 } 536 505 506 static WebCore::FloatRect convertRectFromFrameClientToRootView(FrameView* frameView, WebCore::FloatRect clientRect) 507 { 508 if (!frameView->delegatesScrolling()) 509 return frameView->contentsToRootView(frameView->clientToDocumentRect(clientRect)); 510 511 // If the frame delegates scrolling, contentsToRootView doesn't take into account scroll/zoom/scale. 512 auto& frame = frameView->frame(); 513 clientRect.scale(frame.pageZoomFactor() * frame.frameScaleFactor()); 514 clientRect.moveBy(frameView->contentsScrollPosition()); 515 return clientRect; 516 } 517 518 static WebCore::FloatPoint convertPointFromFrameClientToRootView(FrameView* frameView, WebCore::FloatPoint clientPoint) 519 { 520 if (!frameView->delegatesScrolling()) 521 return frameView->contentsToRootView(frameView->clientToDocumentPoint(clientPoint)); 522 523 // If the frame delegates scrolling, contentsToRootView doesn't take into account scroll/zoom/scale. 524 auto& frame = frameView->frame(); 525 clientPoint.scale(frame.pageZoomFactor() * frame.frameScaleFactor()); 526 clientPoint.moveBy(frameView->contentsScrollPosition()); 527 return clientPoint; 528 } 529 537 530 void WebAutomationSessionProxy::computeElementLayout(uint64_t pageID, uint64_t frameID, String nodeHandle, bool scrollIntoViewIfNeeded, CoordinateSystem coordinateSystem, CompletionHandler<void(Optional<String>, WebCore::IntRect, Optional<WebCore::IntPoint>, bool)>&& completionHandler) 538 531 { … … 544 537 } 545 538 546 String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);547 String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);548 String notImplementedErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NotImplemented);549 550 539 WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame(); 551 540 if (!frame || !frame->coreFrame() || !frame->coreFrame()->view()) { 541 String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound); 552 542 completionHandler(frameNotFoundErrorType, { }, WTF::nullopt, false); 553 543 return; … … 556 546 WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle); 557 547 if (!coreElement) { 548 String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound); 558 549 completionHandler(nodeNotFoundErrorType, { }, WTF::nullopt, false); 559 550 return; … … 570 561 WebCore::FrameView* frameView = frame->coreFrame()->view(); 571 562 WebCore::FrameView* mainView = frame->coreFrame()->mainFrame().view(); 572 WebCore::IntRect frameElementBounds = roundedIntRect(coreElement->boundingClientRect()); 573 WebCore::IntRect rootElementBounds = mainView->rootViewToContents(frameView->contentsToRootView(frameElementBounds)); 563 574 564 WebCore::IntRect resultElementBounds; 565 Optional<WebCore::IntPoint> resultInViewCenterPoint; 566 bool isObscured = false; 567 568 auto elementBoundsInRootCoordinates = convertRectFromFrameClientToRootView(frameView, coreElement->boundingClientRect()); 575 569 switch (coordinateSystem) { 576 570 case CoordinateSystem::Page: 577 resultElementBounds = WebCore::IntRect(mainView->clientToDocumentRect(WebCore::FloatRect(rootElementBounds)));571 resultElementBounds = enclosingIntRect(mainView->absoluteToDocumentRect(mainView->rootViewToContents(elementBoundsInRootCoordinates))); 578 572 break; 579 573 case CoordinateSystem::LayoutViewport: 580 // The element bounds are already in client coordinates. 581 resultElementBounds = WebCore::IntRect(mainView->clientToLayoutViewportRect(WebCore::FloatRect(rootElementBounds))); 574 resultElementBounds = enclosingIntRect(mainView->absoluteToLayoutViewportRect(elementBoundsInRootCoordinates)); 582 575 break; 583 576 } 584 577 585 Optional<WebCore::IntPoint> resultInViewCenterPoint; 586 bool isObscured = false; 587 if (containerElement) { 588 Optional<WebCore::FloatPoint> frameInViewCenterPoint = elementInViewClientCenterPoint(*containerElement, isObscured); 589 if (frameInViewCenterPoint.hasValue()) { 590 WebCore::IntPoint rootInViewCenterPoint = mainView->rootViewToContents(frameView->contentsToRootView(WebCore::IntPoint(frameInViewCenterPoint.value()))); 591 switch (coordinateSystem) { 592 case CoordinateSystem::Page: 593 resultInViewCenterPoint = WebCore::IntPoint(mainView->clientToDocumentPoint(rootInViewCenterPoint)); 594 break; 595 case CoordinateSystem::LayoutViewport: 596 // The point is already in client coordinates. 597 resultInViewCenterPoint = WebCore::IntPoint(mainView->clientToLayoutViewportPoint(rootInViewCenterPoint)); 598 break; 599 } 600 } 578 // If an <option> or <optgroup> does not have an associated <select> or <datalist> element, then give up. 579 if (!containerElement) { 580 String elementNotInteractableErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ElementNotInteractable); 581 completionHandler(elementNotInteractableErrorType, resultElementBounds, resultInViewCenterPoint, isObscured); 582 return; 583 } 584 585 // §12.1 Element Interactability. 586 // https://www.w3.org/TR/webdriver/#dfn-in-view-center-point 587 auto* firstElementRect = containerElement->getClientRects()->item(0); 588 if (!firstElementRect) { 589 String elementNotInteractableErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ElementNotInteractable); 590 completionHandler(elementNotInteractableErrorType, resultElementBounds, resultInViewCenterPoint, isObscured); 591 return; 592 } 593 594 // The W3C WebDriver specification does not explicitly intersect the element with the visual viewport. 595 // Do that here so that the IVCP for an element larger than the viewport is within the viewport. 596 // See spec bug here: https://github.com/w3c/webdriver/issues/1402 597 auto viewportRect = frameView->documentToClientRect(frameView->visualViewportRect()); 598 auto elementRect = FloatRect(firstElementRect->x(), firstElementRect->y(), firstElementRect->width(), firstElementRect->height()); 599 auto visiblePortionOfElementRect = intersection(viewportRect, elementRect); 600 601 // If the element is entirely outside the viewport, still calculate it's bounds. 602 if (visiblePortionOfElementRect.isEmpty()) { 603 completionHandler(WTF::nullopt, resultElementBounds, resultInViewCenterPoint, isObscured); 604 return; 605 } 606 607 auto elementInViewCenterPoint = visiblePortionOfElementRect.center(); 608 auto elementList = containerElement->treeScope().elementsFromPoint(elementInViewCenterPoint); 609 auto index = elementList.findMatching([containerElement] (auto& item) { return item.get() == containerElement; }); 610 if (elementList.isEmpty() || index == notFound) { 611 // We hit this case if the element is visibility:hidden or opacity:0, in which case it will not hit test 612 // at the calculated IVCP. An element is technically not "in view" if it is not within its own paint/hit test tree, 613 // so it cannot have an in-view center point either. And without an IVCP, the definition of 'obscured' makes no sense. 614 // See <https://w3c.github.io/webdriver/webdriver-spec.html#dfn-in-view>. 615 String elementNotInteractableErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ElementNotInteractable); 616 completionHandler(elementNotInteractableErrorType, resultElementBounds, resultInViewCenterPoint, isObscured); 617 return; 618 } 619 620 // Check the case where a non-descendant element hit tests before the target element. For example, a child <option> 621 // of a <select> does not obscure the <select>, but two sibling <div> that overlap at the IVCP will obscure each other. 622 // Node::isDescendantOf() is not self-inclusive, so that is explicitly checked here. 623 isObscured = elementList[0] != containerElement && !elementList[0]->isDescendantOf(containerElement); 624 625 auto inViewCenterPointInRootCoordinates = convertPointFromFrameClientToRootView(frameView, elementInViewCenterPoint); 626 switch (coordinateSystem) { 627 case CoordinateSystem::Page: 628 resultInViewCenterPoint = roundedIntPoint(mainView->absoluteToDocumentPoint(inViewCenterPointInRootCoordinates)); 629 break; 630 case CoordinateSystem::LayoutViewport: 631 resultInViewCenterPoint = roundedIntPoint(mainView->absoluteToLayoutViewportPoint(inViewCenterPointInRootCoordinates)); 632 break; 601 633 } 602 634
Note: See TracChangeset
for help on using the changeset viewer.