+2017-08-05 Carlos Garcia Campos <cgarcia@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 <cgarcia@igalia.com>
getClientRects doesn't work with list box option elements
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 */; };
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, ); }; };
FloatRect boundingClientRect();
- Ref<DOMRectList> getClientRects();
+ WEBCORE_EXPORT Ref<DOMRectList> getClientRects();
Ref<DOMRect> getBoundingClientRect();
// Returns the absolute bounding box translated into client coordinates.
return p;
}
+FloatPoint FrameView::clientToDocumentPoint(FloatPoint point) const
+{
+ point.move(-documentToClientOffset());
+ return point;
+}
+
FloatRect FrameView::layoutViewportToAbsoluteRect(FloatRect rect) const
{
ASSERT(frame().settings().visualViewportEnabled());
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;
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; }
+2017-08-05 Carlos Garcia Campos <cgarcia@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 <mcatanzaro@igalia.com>
[CMake] WebKitFS.cmake depends on options set in Option cmake files that are included later
// §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:
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:
// §6.6 Handling Errors.
// https://www.w3.org/TR/webdriver/#handling-errors
enum class ErrorCode {
+ ElementClickIntercepted,
+ ElementNotInteractable,
InvalidArgument,
InvalidElementState,
InvalidSelector,
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());
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;
}
}
if (!elementX || !elementY) {
- completionHandler(std::nullopt, nullptr);
+ completionHandler(std::nullopt, std::nullopt, false, nullptr);
return;
}
std::optional<int> elementWidth;
}
}
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);
});
}
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;
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));
});
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 };
+2017-08-05 Carlos Garcia Campos <cgarcia@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 <bburg@apple.com>
Web Automation: files selected for upload should be matched against 'accept' attribute values case-insensitively
{ "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
},
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)
.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)
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);
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)
#include <JavaScriptCore/JSStringRefPrivate.h>
#include <JavaScriptCore/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>
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;
}
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;
- }
+ WebCore::IntRect rect = coreElement->clientRect();
+ 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)