Title: [220314] trunk/Source
Revision
220314
Author
carlo...@webkit.org
Date
2017-08-05 01:11:11 -0700 (Sat, 05 Aug 2017)

Log Message

WebDriver: use in-view center point for clicks instead of bounding box center point
https://bugs.webkit.org/show_bug.cgi?id=174863

Reviewed by Simon Fraser.

Source/WebCore:

Make DOMRect, and FloatPoint::narrowPrecision() available to WebKit layer. Also add
FrameView::clientToDocumentPoint().

* WebCore.xcodeproj/project.pbxproj:
* dom/Element.h:
* page/FrameView.h:
* platform/graphics/FloatPoint.h:

Source/WebDriver:

The center of the element bounding box is not always part of the element, like in multiline links, for example.

11.1 Element Interactability.
https://www.w3.org/TR/webdriver/#dfn-in-view-center-point

* CommandResult.cpp:
(WebDriver::CommandResult::httpStatusCode): Add ElementClickIntercepted and ElementNotInteractable errors.
(WebDriver::CommandResult::errorString): Ditto.
* CommandResult.h: Ditto.
* Session.cpp:
(WebDriver::Session::computeElementLayout): Get the in-view center point and isObscured from the result too.
(WebDriver::Session::getElementRect): Ignore in-view center point and isObscured.
(WebDriver::Session::elementClick): Fail in case the element is not interactable or is obscured.
* Session.h:

Source/WebKit:

Change computeElementLayout to also return the in-view center point and whether it's obscured by another
element.

* UIProcess/Automation/Automation.json: Add optional inViewCenterPoint to the result and isObscured.
* UIProcess/Automation/WebAutomationSession.cpp:
(WebKit::WebAutomationSession::didComputeElementLayout): Handle inViewCenterPoint and isObscured.
* UIProcess/Automation/WebAutomationSession.h:
* UIProcess/Automation/WebAutomationSession.messages.in:
* WebProcess/Automation/WebAutomationSessionProxy.cpp:
(WebKit::elementInViewClientCenterPoint): Get the client in-view center point and whether it's obscured
according to the spec.
(WebKit::WebAutomationSessionProxy::computeElementLayout): Pass inViewCenterPoint and isObscured to
DidComputeElementLayout message.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (220313 => 220314)


--- trunk/Source/WebCore/ChangeLog	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebCore/ChangeLog	2017-08-05 08:11:11 UTC (rev 220314)
@@ -1,5 +1,20 @@
 2017-08-05  Carlos Garcia Campos  <cgar...@igalia.com>
 
+        WebDriver: use in-view center point for clicks instead of bounding box center point
+        https://bugs.webkit.org/show_bug.cgi?id=174863
+
+        Reviewed by Simon Fraser.
+
+        Make DOMRect, and FloatPoint::narrowPrecision() available to WebKit layer. Also add
+        FrameView::clientToDocumentPoint().
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * dom/Element.h:
+        * page/FrameView.h:
+        * platform/graphics/FloatPoint.h:
+
+2017-08-05  Carlos Garcia Campos  <cgar...@igalia.com>
+
         getClientRects doesn't work with list box option elements
         https://bugs.webkit.org/show_bug.cgi?id=175016
 

Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (220313 => 220314)


--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj	2017-08-05 08:11:11 UTC (rev 220314)
@@ -430,9 +430,9 @@
 		0F3F0E5A157030C3006DA57F /* RenderGeometryMap.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3F0E58157030C3006DA57F /* RenderGeometryMap.h */; };
 		0F43C85D189E10CF00019AE2 /* PerformanceTiming.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F43C85C189E10CF00019AE2 /* PerformanceTiming.cpp */; };
 		0F43C85F189E15A600019AE2 /* JSPerformanceTiming.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F43C85E189E15A600019AE2 /* JSPerformanceTiming.cpp */; };
-		0F4710AF1DB56AFC002DCEC3 /* DOMRect.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4710A91DB56AFC002DCEC3 /* DOMRect.h */; };
-		0F4710B11DB56AFC002DCEC3 /* DOMRectInit.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4710AB1DB56AFC002DCEC3 /* DOMRectInit.h */; };
-		0F4710B31DB56AFC002DCEC3 /* DOMRectReadOnly.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4710AD1DB56AFC002DCEC3 /* DOMRectReadOnly.h */; };
+		0F4710AF1DB56AFC002DCEC3 /* DOMRect.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4710A91DB56AFC002DCEC3 /* DOMRect.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		0F4710B11DB56AFC002DCEC3 /* DOMRectInit.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4710AB1DB56AFC002DCEC3 /* DOMRectInit.h */; settings = {ATTRIBUTES = (Private, ); }; };
+		0F4710B31DB56AFC002DCEC3 /* DOMRectReadOnly.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4710AD1DB56AFC002DCEC3 /* DOMRectReadOnly.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		0F4710BB1DB56BE8002DCEC3 /* JSDOMRect.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F4710B51DB56BE8002DCEC3 /* JSDOMRect.cpp */; };
 		0F4710BC1DB56BE8002DCEC3 /* JSDOMRect.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4710B61DB56BE8002DCEC3 /* JSDOMRect.h */; };
 		0F4710BD1DB56BE8002DCEC3 /* JSDOMRectInit.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F4710B71DB56BE8002DCEC3 /* JSDOMRectInit.cpp */; };
@@ -1955,7 +1955,7 @@
 		4671E0661D67A59600C6B497 /* CanvasPath.h in Headers */ = {isa = PBXBuildFile; fileRef = 4671E0641D67A57B00C6B497 /* CanvasPath.h */; };
 		467302021C4EFE7800BCB357 /* IgnoreOpensDuringUnloadCountIncrementer.h in Headers */ = {isa = PBXBuildFile; fileRef = 467302011C4EFE6600BCB357 /* IgnoreOpensDuringUnloadCountIncrementer.h */; };
 		468344DF1EDDFAAA00B7795B /* DOMRectList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 468344DD1EDDFA5F00B7795B /* DOMRectList.cpp */; };
-		468344E01EDDFAAA00B7795B /* DOMRectList.h in Headers */ = {isa = PBXBuildFile; fileRef = 468344DE1EDDFA5F00B7795B /* DOMRectList.h */; };
+		468344E01EDDFAAA00B7795B /* DOMRectList.h in Headers */ = {isa = PBXBuildFile; fileRef = 468344DE1EDDFA5F00B7795B /* DOMRectList.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		4689F1AF1267BAE100E8D380 /* FileMetadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 4689F1AE1267BAE100E8D380 /* FileMetadata.h */; };
 		46B63F6C1C6E8D19002E914B /* JSEventTargetCustom.h in Headers */ = {isa = PBXBuildFile; fileRef = 46B63F6B1C6E8CDF002E914B /* JSEventTargetCustom.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		46C696CB1E7205F700597937 /* CPUMonitor.h in Headers */ = {isa = PBXBuildFile; fileRef = 46C696C91E7205E400597937 /* CPUMonitor.h */; settings = {ATTRIBUTES = (Private, ); }; };

Modified: trunk/Source/WebCore/dom/Element.h (220313 => 220314)


--- trunk/Source/WebCore/dom/Element.h	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebCore/dom/Element.h	2017-08-05 08:11:11 UTC (rev 220314)
@@ -175,7 +175,7 @@
 
     FloatRect boundingClientRect();
 
-    Ref<DOMRectList> getClientRects();
+    WEBCORE_EXPORT Ref<DOMRectList> getClientRects();
     Ref<DOMRect> getBoundingClientRect();
 
     // Returns the absolute bounding box translated into client coordinates.

Modified: trunk/Source/WebCore/page/FrameView.cpp (220313 => 220314)


--- trunk/Source/WebCore/page/FrameView.cpp	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebCore/page/FrameView.cpp	2017-08-05 08:11:11 UTC (rev 220314)
@@ -4951,6 +4951,12 @@
     return p;
 }
 
+FloatPoint FrameView::clientToDocumentPoint(FloatPoint point) const
+{
+    point.move(-documentToClientOffset());
+    return point;
+}
+
 FloatRect FrameView::layoutViewportToAbsoluteRect(FloatRect rect) const
 {
     ASSERT(frame().settings().visualViewportEnabled());

Modified: trunk/Source/WebCore/page/FrameView.h (220313 => 220314)


--- trunk/Source/WebCore/page/FrameView.h	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebCore/page/FrameView.h	2017-08-05 08:11:11 UTC (rev 220314)
@@ -477,6 +477,7 @@
     FloatSize documentToClientOffset() const;
     FloatRect documentToClientRect(FloatRect) const;
     FloatPoint documentToClientPoint(FloatPoint) const;
+    WEBCORE_EXPORT FloatPoint clientToDocumentPoint(FloatPoint) const;
 
     FloatRect layoutViewportToAbsoluteRect(FloatRect) const;
     FloatPoint layoutViewportToAbsolutePoint(FloatPoint) const;

Modified: trunk/Source/WebCore/platform/graphics/FloatPoint.h (220313 => 220314)


--- trunk/Source/WebCore/platform/graphics/FloatPoint.h	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebCore/platform/graphics/FloatPoint.h	2017-08-05 08:11:11 UTC (rev 220314)
@@ -68,7 +68,7 @@
 
     static FloatPoint zero() { return FloatPoint(); }
 
-    static FloatPoint narrowPrecision(double x, double y);
+    WEBCORE_EXPORT static FloatPoint narrowPrecision(double x, double y);
 
     float x() const { return m_x; }
     float y() const { return m_y; }

Modified: trunk/Source/WebDriver/ChangeLog (220313 => 220314)


--- trunk/Source/WebDriver/ChangeLog	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebDriver/ChangeLog	2017-08-05 08:11:11 UTC (rev 220314)
@@ -1,3 +1,25 @@
+2017-08-05  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        WebDriver: use in-view center point for clicks instead of bounding box center point
+        https://bugs.webkit.org/show_bug.cgi?id=174863
+
+        Reviewed by Simon Fraser.
+
+        The center of the element bounding box is not always part of the element, like in multiline links, for example.
+
+        11.1 Element Interactability.
+        https://www.w3.org/TR/webdriver/#dfn-in-view-center-point
+
+        * CommandResult.cpp:
+        (WebDriver::CommandResult::httpStatusCode): Add ElementClickIntercepted and ElementNotInteractable errors.
+        (WebDriver::CommandResult::errorString): Ditto.
+        * CommandResult.h: Ditto.
+        * Session.cpp:
+        (WebDriver::Session::computeElementLayout): Get the in-view center point and isObscured from the result too.
+        (WebDriver::Session::getElementRect): Ignore in-view center point and isObscured.
+        (WebDriver::Session::elementClick): Fail in case the element is not interactable or is obscured.
+        * Session.h:
+
 2017-08-01  Michael Catanzaro  <mcatanz...@igalia.com>
 
         [CMake] WebKitFS.cmake depends on options set in Option cmake files that are included later

Modified: trunk/Source/WebDriver/CommandResult.cpp (220313 => 220314)


--- trunk/Source/WebDriver/CommandResult.cpp	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebDriver/CommandResult.cpp	2017-08-05 08:11:11 UTC (rev 220314)
@@ -122,6 +122,8 @@
     // §6.6 Handling Errors.
     // https://www.w3.org/TR/webdriver/#handling-errors
     switch (m_errorCode.value()) {
+    case ErrorCode::ElementClickIntercepted:
+    case ErrorCode::ElementNotInteractable:
     case ErrorCode::InvalidArgument:
     case ErrorCode::InvalidElementState:
     case ErrorCode::InvalidSelector:
@@ -152,6 +154,10 @@
     ASSERT(isError());
 
     switch (m_errorCode.value()) {
+    case ErrorCode::ElementClickIntercepted:
+        return ASCIILiteral("element click intercepted");
+    case ErrorCode::ElementNotInteractable:
+        return ASCIILiteral("element not interactable");
     case ErrorCode::InvalidArgument:
         return ASCIILiteral("invalid argument");
     case ErrorCode::InvalidElementState:

Modified: trunk/Source/WebDriver/CommandResult.h (220313 => 220314)


--- trunk/Source/WebDriver/CommandResult.h	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebDriver/CommandResult.h	2017-08-05 08:11:11 UTC (rev 220314)
@@ -39,6 +39,8 @@
     // §6.6 Handling Errors.
     // https://www.w3.org/TR/webdriver/#handling-errors
     enum class ErrorCode {
+        ElementClickIntercepted,
+        ElementNotInteractable,
         InvalidArgument,
         InvalidElementState,
         InvalidSelector,

Modified: trunk/Source/WebDriver/Session.cpp (220313 => 220314)


--- trunk/Source/WebDriver/Session.cpp	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebDriver/Session.cpp	2017-08-05 08:11:11 UTC (rev 220314)
@@ -580,7 +580,7 @@
     return elementID;
 }
 
-void Session::computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption> options, Function<void (std::optional<Rect>&&, RefPtr<InspectorObject>&&)>&& completionHandler)
+void Session::computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption> options, Function<void (std::optional<Rect>&&, std::optional<Point>&&, bool, RefPtr<InspectorObject>&&)>&& completionHandler)
 {
     ASSERT(m_toplevelBrowsingContext.value());
 
@@ -592,12 +592,12 @@
     parameters->setBoolean(ASCIILiteral("useViewportCoordinates"), options.contains(ElementLayoutOption::UseViewportCoordinates));
     m_host->sendCommandToBackend(ASCIILiteral("computeElementLayout"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
         if (response.isError || !response.responseObject) {
-            completionHandler(std::nullopt, WTFMove(response.responseObject));
+            completionHandler(std::nullopt, std::nullopt, false, WTFMove(response.responseObject));
             return;
         }
         RefPtr<InspectorObject> rectObject;
         if (!response.responseObject->getObject(ASCIILiteral("rect"), rectObject)) {
-            completionHandler(std::nullopt, nullptr);
+            completionHandler(std::nullopt, std::nullopt, false, nullptr);
             return;
         }
         std::optional<int> elementX;
@@ -611,7 +611,7 @@
             }
         }
         if (!elementX || !elementY) {
-            completionHandler(std::nullopt, nullptr);
+            completionHandler(std::nullopt, std::nullopt, false, nullptr);
             return;
         }
         std::optional<int> elementWidth;
@@ -625,11 +625,29 @@
             }
         }
         if (!elementWidth || !elementHeight) {
-            completionHandler(std::nullopt, nullptr);
+            completionHandler(std::nullopt, std::nullopt, false, nullptr);
             return;
         }
         Rect rect = { { elementX.value(), elementY.value() }, { elementWidth.value(), elementHeight.value() } };
-        completionHandler(rect, nullptr);
+
+        bool isObscured;
+        if (!response.responseObject->getBoolean(ASCIILiteral("isObscured"), isObscured)) {
+            completionHandler(std::nullopt, std::nullopt, false, nullptr);
+            return;
+        }
+        RefPtr<InspectorObject> inViewCenterPointObject;
+        if (!response.responseObject->getObject(ASCIILiteral("inViewCenterPoint"), inViewCenterPointObject)) {
+            completionHandler(rect, std::nullopt, isObscured, nullptr);
+            return;
+        }
+        int inViewCenterPointX, inViewCenterPointY;
+        if (!inViewCenterPointObject->getInteger(ASCIILiteral("x"), inViewCenterPointX)
+            || !inViewCenterPointObject->getInteger(ASCIILiteral("y"), inViewCenterPointY)) {
+            completionHandler(std::nullopt, std::nullopt, isObscured, nullptr);
+            return;
+        }
+        Point inViewCenterPoint = { inViewCenterPointX, inViewCenterPointY };
+        completionHandler(rect, inViewCenterPoint, isObscured, nullptr);
     });
 }
 
@@ -830,7 +848,7 @@
         return;
     }
 
-    computeElementLayout(elementID, { }, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, RefPtr<InspectorObject>&& error) {
+    computeElementLayout(elementID, { }, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&&, bool, RefPtr<InspectorObject>&& error) {
         if (!rect || error) {
             completionHandler(CommandResult::fail(WTFMove(error)));
             return;
@@ -987,15 +1005,21 @@
 
     OptionSet<ElementLayoutOption> options = ElementLayoutOption::ScrollIntoViewIfNeeded;
     options |= ElementLayoutOption::UseViewportCoordinates;
-    computeElementLayout(elementID, options, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, RefPtr<InspectorObject>&& error) mutable {
+    computeElementLayout(elementID, options, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&& inViewCenter, bool isObscured, RefPtr<InspectorObject>&& error) mutable {
         if (!rect || error) {
             completionHandler(CommandResult::fail(WTFMove(error)));
             return;
         }
+        if (isObscured) {
+            completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted));
+            return;
+        }
+        if (!inViewCenter) {
+            completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable));
+            return;
+        }
 
-        // FIXME: the center of the bounding box is not always part of the element.
-        performMouseInteraction(rect.value().origin.x + rect.value().size.width / 2, rect.value().origin.y + rect.value().size.height / 2,
-            MouseButton::Left, MouseInteraction::SingleClick, WTFMove(completionHandler));
+        performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(completionHandler));
 
         waitForNavigationToComplete(WTFMove(completionHandler));
     });

Modified: trunk/Source/WebDriver/Session.h (220313 => 220314)


--- trunk/Source/WebDriver/Session.h	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebDriver/Session.h	2017-08-05 08:11:11 UTC (rev 220314)
@@ -133,7 +133,7 @@
         ScrollIntoViewIfNeeded = 1 << 0,
         UseViewportCoordinates = 1 << 1,
     };
-    void computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption>, Function<void (std::optional<Rect>&&, RefPtr<Inspector::InspectorObject>&&)>&&);
+    void computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption>, Function<void (std::optional<Rect>&&, std::optional<Point>&&, bool, RefPtr<Inspector::InspectorObject>&&)>&&);
 
     enum class MouseButton { None, Left, Middle, Right };
     enum class MouseInteraction { Move, Down, Up, SingleClick, DoubleClick };

Modified: trunk/Source/WebKit/ChangeLog (220313 => 220314)


--- trunk/Source/WebKit/ChangeLog	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebKit/ChangeLog	2017-08-05 08:11:11 UTC (rev 220314)
@@ -1,3 +1,24 @@
+2017-08-05  Carlos Garcia Campos  <cgar...@igalia.com>
+
+        WebDriver: use in-view center point for clicks instead of bounding box center point
+        https://bugs.webkit.org/show_bug.cgi?id=174863
+
+        Reviewed by Simon Fraser.
+
+        Change computeElementLayout to also return the in-view center point and whether it's obscured by another
+        element.
+
+        * UIProcess/Automation/Automation.json: Add optional inViewCenterPoint to the result and isObscured.
+        * UIProcess/Automation/WebAutomationSession.cpp:
+        (WebKit::WebAutomationSession::didComputeElementLayout): Handle inViewCenterPoint and isObscured.
+        * UIProcess/Automation/WebAutomationSession.h:
+        * UIProcess/Automation/WebAutomationSession.messages.in:
+        * WebProcess/Automation/WebAutomationSessionProxy.cpp:
+        (WebKit::elementInViewClientCenterPoint): Get the client in-view center point and whether it's obscured
+        according to the spec.
+        (WebKit::WebAutomationSessionProxy::computeElementLayout): Pass inViewCenterPoint and isObscured to
+        DidComputeElementLayout message.
+
 2017-08-05  Brian Burg  <bb...@apple.com>
 
         Web Automation: files selected for upload should be matched against 'accept' attribute values case-insensitively

Modified: trunk/Source/WebKit/UIProcess/Automation/Automation.json (220313 => 220314)


--- trunk/Source/WebKit/UIProcess/Automation/Automation.json	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebKit/UIProcess/Automation/Automation.json	2017-08-05 08:11:11 UTC (rev 220314)
@@ -422,7 +422,9 @@
                 { "name": "useViewportCoordinates", "optional": true, "type": "boolean", "description": "If the result coordinates should be represented as viewport coordinates or not. Defaults to false, which means coordinates should be represented as page coordinates." }
             ],
             "returns": [
-                { "name": "rect", "$ref": "Rect", "description": "The layout rect for the requested element. Specified in page or viewport coordinates based on the useViewportCoordinates parameter." }
+                { "name": "rect", "$ref": "Rect", "description": "The layout rect for the requested element. Specified in page or viewport coordinates based on the useViewportCoordinates parameter." },
+                { "name": "inViewCenterPoint", "optional": true, "$ref": "Point", "description": "The in-view center point for the requested element. Specified in page or viewport coordinates based on the useViewportCoordinates parameter." },
+                { "name": "isObscured", "type": "boolean", "description": "If the in-view center point of the requested element is obscured by another element." }
             ],
             "async": true
         },

Modified: trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp (220313 => 220314)


--- trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.cpp	2017-08-05 08:11:11 UTC (rev 220314)
@@ -743,7 +743,7 @@
     page->process().send(Messages::WebAutomationSessionProxy::ComputeElementLayout(page->pageID(), frameID.value(), nodeHandle, scrollIntoViewIfNeeded, useViewportCoordinates, callbackID), 0);
 }
 
-void WebAutomationSession::didComputeElementLayout(uint64_t callbackID, WebCore::IntRect rect, const String& errorType)
+void WebAutomationSession::didComputeElementLayout(uint64_t callbackID, WebCore::IntRect rect, std::optional<WebCore::IntPoint> inViewCenterPoint, bool isObscured, const String& errorType)
 {
     auto callback = m_computeElementLayoutCallbacks.take(callbackID);
     if (!callback)
@@ -769,7 +769,17 @@
         .setSize(WTFMove(sizeObject))
         .release();
 
-    callback->sendSuccess(WTFMove(rectObject));
+    if (!inViewCenterPoint) {
+        callback->sendSuccess(WTFMove(rectObject), nullptr, isObscured);
+        return;
+    }
+
+    auto inViewCenterPointObject = Inspector::Protocol::Automation::Point::create()
+        .setX(inViewCenterPoint.value().x())
+        .setY(inViewCenterPoint.value().y())
+        .release();
+
+    callback->sendSuccess(WTFMove(rectObject), WTFMove(inViewCenterPointObject), isObscured);
 }
 
 void WebAutomationSession::isShowingJavaScriptDialog(Inspector::ErrorString& errorString, const String& browsingContextHandle, bool* result)

Modified: trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h (220313 => 220314)


--- trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.h	2017-08-05 08:11:11 UTC (rev 220314)
@@ -174,7 +174,7 @@
     void didEvaluateJavaScriptFunction(uint64_t callbackID, const String& result, const String& errorType);
     void didResolveChildFrame(uint64_t callbackID, uint64_t frameID, const String& errorType);
     void didResolveParentFrame(uint64_t callbackID, uint64_t frameID, const String& errorType);
-    void didComputeElementLayout(uint64_t callbackID, WebCore::IntRect, const String& errorType);
+    void didComputeElementLayout(uint64_t callbackID, WebCore::IntRect, std::optional<WebCore::IntPoint>, bool isObscured, const String& errorType);
     void didTakeScreenshot(uint64_t callbackID, const ShareableBitmap::Handle&, const String& errorType);
     void didGetCookiesForFrame(uint64_t callbackID, Vector<WebCore::Cookie>, const String& errorType);
     void didDeleteCookie(uint64_t callbackID, const String& errorType);

Modified: trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.messages.in (220313 => 220314)


--- trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.messages.in	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebKit/UIProcess/Automation/WebAutomationSession.messages.in	2017-08-05 08:11:11 UTC (rev 220314)
@@ -26,7 +26,7 @@
     DidResolveChildFrame(uint64_t callbackID, uint64_t frameID, String errorType)
     DidResolveParentFrame(uint64_t callbackID, uint64_t frameID, String errorType)
 
-    DidComputeElementLayout(uint64_t callbackID, WebCore::IntRect rect, String errorType)
+    DidComputeElementLayout(uint64_t callbackID, WebCore::IntRect rect, std::optional<WebCore::IntPoint> inViewCenterPoint, bool isObscured, String errorType)
 
     DidTakeScreenshot(uint64_t callbackID, WebKit::ShareableBitmap::Handle imageDataHandle, String errorType)
 

Modified: trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp (220313 => 220314)


--- trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp	2017-08-05 08:08:16 UTC (rev 220313)
+++ trunk/Source/WebKit/WebProcess/Automation/WebAutomationSessionProxy.cpp	2017-08-05 08:11:11 UTC (rev 220314)
@@ -41,6 +41,8 @@
 #include <_javascript_Core/JSStringRefPrivate.h>
 #include <_javascript_Core/OpaqueJSString.h>
 #include <WebCore/CookieJar.h>
+#include <WebCore/DOMRect.h>
+#include <WebCore/DOMRectList.h>
 #include <WebCore/DOMWindow.h>
 #include <WebCore/Frame.h>
 #include <WebCore/FrameTree.h>
@@ -470,12 +472,43 @@
     coreDOMWindow->focus(true);
 }
 
+static std::optional<WebCore::FloatPoint> elementInViewClientCenterPoint(WebCore::Element& element, bool& isObscured)
+{
+    // §11.1 Element Interactability.
+    // https://www.w3.org/TR/webdriver/#dfn-in-view-center-point
+    auto* clientRect = element.getClientRects()->item(0);
+    if (!clientRect)
+        return std::nullopt;
+
+    auto clientCenterPoint = WebCore::FloatPoint::narrowPrecision(0.5 * (clientRect->left() + clientRect->right()), 0.5 * (clientRect->top() + clientRect->bottom()));
+    auto elementList = element.treeScope().elementsFromPoint(clientCenterPoint.x(), clientCenterPoint.y());
+    if (elementList.isEmpty()) {
+        // An element is obscured if the pointer-interactable paint tree at its center point is empty,
+        // or the first element in this tree is not an inclusive descendant of itself.
+        // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-obscured
+        isObscured = true;
+        return clientCenterPoint;
+    }
+
+    auto index = elementList.findMatching([&element] (auto& item) { return item.get() == &element; });
+    if (index == notFound)
+        return std::nullopt;
+
+    if (index) {
+        // Element is not the first one in the list.
+        auto firstElement = elementList[0];
+        isObscured = !firstElement->isDescendantOf(element);
+    }
+
+    return clientCenterPoint;
+}
+
 void WebAutomationSessionProxy::computeElementLayout(uint64_t pageID, uint64_t frameID, String nodeHandle, bool scrollIntoViewIfNeeded, bool useViewportCoordinates, uint64_t callbackID)
 {
     WebPage* page = WebProcess::singleton().webPage(pageID);
     if (!page) {
         String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
-        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, WebCore::IntRect(), windowNotFoundErrorType), 0);
+        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, { }, std::nullopt, false, windowNotFoundErrorType), 0);
         return;
     }
 
@@ -483,40 +516,39 @@
     String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);
 
     WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame();
-    if (!frame) {
-        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, WebCore::IntRect(), frameNotFoundErrorType), 0);
+    if (!frame || !frame->coreFrame() || !frame->coreFrame()->view()) {
+        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, { }, std::nullopt, false, frameNotFoundErrorType), 0);
         return;
     }
 
     WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle);
     if (!coreElement) {
-        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, WebCore::IntRect(), nodeNotFoundErrorType), 0);
+        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, { }, std::nullopt, false, nodeNotFoundErrorType), 0);
         return;
     }
 
-    if (scrollIntoViewIfNeeded)
+    if (scrollIntoViewIfNeeded) {
+        // FIXME: Wait in an implementation-specific way up to the session implicit wait timeout for the element to become in view.
         coreElement->scrollIntoViewIfNeeded(false);
+    }
 
     WebCore::IntRect rect = coreElement->clientRect();
 
-    WebCore::Frame* coreFrame = frame->coreFrame();
-    if (!coreFrame) {
-        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, WebCore::IntRect(), frameNotFoundErrorType), 0);
-        return;
-    }
-
-    WebCore::FrameView *coreFrameView = coreFrame->view();
-    if (!coreFrameView) {
-        WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, WebCore::IntRect(), frameNotFoundErrorType), 0);
-        return;
-    }
-
+    auto* coreFrameView = frame->coreFrame()->view();
     if (useViewportCoordinates)
         rect.moveBy(WebCore::IntPoint(0, -coreFrameView->topContentInset()));
     else
         rect = coreFrameView->rootViewToContents(rect);
 
-    WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, rect, String()), 0);
+    bool isObscured = false;
+    std::optional<WebCore::IntPoint> inViewCenter;
+    if (auto clientCenterPoint = elementInViewClientCenterPoint(*coreElement, isObscured)) {
+        inViewCenter = WebCore::IntPoint(coreFrameView->clientToDocumentPoint(clientCenterPoint.value()));
+        if (useViewportCoordinates)
+            inViewCenter = coreFrameView->contentsToRootView(inViewCenter.value());
+    }
+
+    WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, rect, inViewCenter, isObscured, String()), 0);
 }
 
 void WebAutomationSessionProxy::takeScreenshot(uint64_t pageID, uint64_t callbackID)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to