WebAutomationSessionProxy.cpp [plain text]
#include "config.h"
#include "WebAutomationSessionProxy.h"
#include "AutomationProtocolObjects.h"
#include "CoordinateSystem.h"
#include "WebAutomationSessionMessages.h"
#include "WebAutomationSessionProxyMessages.h"
#include "WebAutomationSessionProxyScriptSource.h"
#include "WebCoreArgumentCoders.h"
#include "WebFrame.h"
#include "WebImage.h"
#include "WebPage.h"
#include "WebProcess.h"
#include <JavaScriptCore/APICast.h>
#include <JavaScriptCore/JSObject.h>
#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/File.h>
#include <WebCore/FileList.h>
#include <WebCore/Frame.h>
#include <WebCore/FrameTree.h>
#include <WebCore/FrameView.h>
#include <WebCore/HTMLFrameElement.h>
#include <WebCore/HTMLIFrameElement.h>
#include <WebCore/HTMLInputElement.h>
#include <WebCore/HTMLOptGroupElement.h>
#include <WebCore/HTMLOptionElement.h>
#include <WebCore/HTMLSelectElement.h>
#include <WebCore/JSElement.h>
#include <WebCore/RenderElement.h>
#include <wtf/UUID.h>
#if ENABLE(DATALIST_ELEMENT)
#include <WebCore/HTMLDataListElement.h>
#endif
namespace WebKit {
template <typename T>
static JSObjectRef toJSArray(JSContextRef context, const Vector<T>& data, JSValueRef (*converter)(JSContextRef, const T&), JSValueRef* exception)
{
ASSERT_ARG(converter, converter);
if (data.isEmpty())
return JSObjectMakeArray(context, 0, nullptr, exception);
Vector<JSValueRef, 8> convertedData;
convertedData.reserveCapacity(data.size());
for (auto& originalValue : data) {
JSValueRef convertedValue = converter(context, originalValue);
JSValueProtect(context, convertedValue);
convertedData.uncheckedAppend(convertedValue);
}
JSObjectRef array = JSObjectMakeArray(context, convertedData.size(), convertedData.data(), exception);
for (auto& convertedValue : convertedData)
JSValueUnprotect(context, convertedValue);
return array;
}
static inline JSValueRef toJSValue(JSContextRef context, const String& string)
{
return JSValueMakeString(context, OpaqueJSString::tryCreate(string).get());
}
static inline JSValueRef callPropertyFunction(JSContextRef context, JSObjectRef object, const String& propertyName, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
ASSERT_ARG(object, object);
ASSERT_ARG(object, JSValueIsObject(context, object));
JSObjectRef function = const_cast<JSObjectRef>(JSObjectGetProperty(context, object, OpaqueJSString::tryCreate(propertyName).get(), exception));
ASSERT(JSObjectIsFunction(context, function));
return JSObjectCallAsFunction(context, function, object, argumentCount, arguments, exception);
}
WebAutomationSessionProxy::WebAutomationSessionProxy(const String& sessionIdentifier)
: m_sessionIdentifier(sessionIdentifier)
{
WebProcess::singleton().addMessageReceiver(Messages::WebAutomationSessionProxy::messageReceiverName(), *this);
}
WebAutomationSessionProxy::~WebAutomationSessionProxy()
{
WebProcess::singleton().removeMessageReceiver(Messages::WebAutomationSessionProxy::messageReceiverName());
}
static JSValueRef evaluate(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
ASSERT_ARG(argumentCount, argumentCount == 1);
ASSERT_ARG(arguments, JSValueIsString(context, arguments[0]));
if (argumentCount != 1)
return JSValueMakeUndefined(context);
auto script = adoptRef(JSValueToStringCopy(context, arguments[0], exception));
return JSEvaluateScript(context, script.get(), nullptr, nullptr, 0, exception);
}
static JSValueRef createUUID(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
return toJSValue(context, createCanonicalUUIDString().convertToASCIIUppercase());
}
static JSValueRef evaluateJavaScriptCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
{
ASSERT_ARG(argumentCount, argumentCount == 4);
ASSERT_ARG(arguments, JSValueIsNumber(context, arguments[0]));
ASSERT_ARG(arguments, JSValueIsNumber(context, arguments[1]));
ASSERT_ARG(arguments, JSValueIsString(context, arguments[2]));
ASSERT_ARG(arguments, JSValueIsBoolean(context, arguments[3]));
auto automationSessionProxy = WebProcess::singleton().automationSessionProxy();
if (!automationSessionProxy)
return JSValueMakeUndefined(context);
WebCore::FrameIdentifier frameID = WebCore::frameIdentifierFromID(JSValueToNumber(context, arguments[0], exception));
uint64_t callbackID = JSValueToNumber(context, arguments[1], exception);
auto result = adoptRef(JSValueToStringCopy(context, arguments[2], exception));
bool resultIsErrorName = JSValueToBoolean(context, arguments[3]);
if (resultIsErrorName) {
if (result->string() == "JavaScriptTimeout") {
String errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::JavaScriptTimeout);
automationSessionProxy->didEvaluateJavaScriptFunction(frameID, callbackID, String(), errorType);
} else {
ASSERT_NOT_REACHED();
String errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InternalError);
automationSessionProxy->didEvaluateJavaScriptFunction(frameID, callbackID, String(), errorType);
}
} else
automationSessionProxy->didEvaluateJavaScriptFunction(frameID, callbackID, result->string(), String());
return JSValueMakeUndefined(context);
}
JSObjectRef WebAutomationSessionProxy::scriptObjectForFrame(WebFrame& frame)
{
if (JSObjectRef scriptObject = m_webFrameScriptObjectMap.get(frame.frameID()))
return scriptObject;
JSValueRef exception = nullptr;
JSGlobalContextRef context = frame.jsContext();
JSValueRef sessionIdentifier = toJSValue(context, m_sessionIdentifier);
JSObjectRef evaluateFunction = JSObjectMakeFunctionWithCallback(context, nullptr, evaluate);
JSObjectRef createUUIDFunction = JSObjectMakeFunctionWithCallback(context, nullptr, createUUID);
String script = StringImpl::createWithoutCopying(WebAutomationSessionProxyScriptSource, sizeof(WebAutomationSessionProxyScriptSource));
JSObjectRef scriptObjectFunction = const_cast<JSObjectRef>(JSEvaluateScript(context, OpaqueJSString::tryCreate(script).get(), nullptr, nullptr, 0, &exception));
ASSERT(JSValueIsObject(context, scriptObjectFunction));
JSValueRef arguments[] = { sessionIdentifier, evaluateFunction, createUUIDFunction };
JSObjectRef scriptObject = const_cast<JSObjectRef>(JSObjectCallAsFunction(context, scriptObjectFunction, nullptr, WTF_ARRAY_LENGTH(arguments), arguments, &exception));
ASSERT(JSValueIsObject(context, scriptObject));
JSValueProtect(context, scriptObject);
m_webFrameScriptObjectMap.add(frame.frameID(), scriptObject);
return scriptObject;
}
WebCore::Element* WebAutomationSessionProxy::elementForNodeHandle(WebFrame& frame, const String& nodeHandle)
{
JSObjectRef scriptObject = m_webFrameScriptObjectMap.get(frame.frameID());
if (!scriptObject)
return nullptr;
JSGlobalContextRef context = frame.jsContext();
JSValueRef functionArguments[] = {
toJSValue(context, nodeHandle)
};
JSValueRef result = callPropertyFunction(context, scriptObject, "nodeForIdentifier"_s, WTF_ARRAY_LENGTH(functionArguments), functionArguments, nullptr);
JSObjectRef element = JSValueToObject(context, result, nullptr);
if (!element)
return nullptr;
auto elementWrapper = JSC::jsDynamicCast<WebCore::JSElement*>(toJS(context)->vm(), toJS(element));
if (!elementWrapper)
return nullptr;
return &elementWrapper->wrapped();
}
void WebAutomationSessionProxy::didClearWindowObjectForFrame(WebFrame& frame)
{
WebCore::FrameIdentifier frameID = frame.frameID();
if (JSObjectRef scriptObject = m_webFrameScriptObjectMap.take(frameID))
JSValueUnprotect(frame.jsContext(), scriptObject);
String errorMessage = "Callback was not called before the unload event."_s;
String errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::JavaScriptError);
auto pendingFrameCallbacks = m_webFramePendingEvaluateJavaScriptCallbacksMap.take(frameID);
for (uint64_t callbackID : pendingFrameCallbacks)
WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidEvaluateJavaScriptFunction(callbackID, errorMessage, errorType), 0);
}
void WebAutomationSessionProxy::evaluateJavaScriptFunction(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> optionalFrameID, const String& function, Vector<String> arguments, bool expectsImplicitCallbackArgument, Optional<double> callbackTimeout, uint64_t callbackID)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page) {
WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidEvaluateJavaScriptFunction(callbackID, { },
Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound)), 0);
return;
}
WebFrame* frame = optionalFrameID ? WebProcess::singleton().webFrame(*optionalFrameID) : page->mainWebFrame();
if (!frame) {
WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidEvaluateJavaScriptFunction(callbackID, { },
Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound)), 0);
return;
}
JSObjectRef scriptObject = scriptObjectForFrame(*frame);
ASSERT(scriptObject);
auto frameID = frame->frameID();
JSValueRef exception = nullptr;
JSGlobalContextRef context = frame->jsContext();
if (expectsImplicitCallbackArgument) {
auto result = m_webFramePendingEvaluateJavaScriptCallbacksMap.add(frameID, Vector<uint64_t>());
result.iterator->value.append(callbackID);
}
JSValueRef functionArguments[] = {
toJSValue(context, function),
toJSArray(context, arguments, toJSValue, &exception),
JSValueMakeBoolean(context, expectsImplicitCallbackArgument),
JSValueMakeNumber(context, frameID.toUInt64()),
JSValueMakeNumber(context, callbackID),
JSObjectMakeFunctionWithCallback(context, nullptr, evaluateJavaScriptCallback),
JSValueMakeNumber(context, callbackTimeout.valueOr(-1))
};
{
WebCore::UserGestureIndicator gestureIndicator(WebCore::ProcessingUserGesture, frame->coreFrame()->document());
callPropertyFunction(context, scriptObject, "evaluateJavaScriptFunction"_s, WTF_ARRAY_LENGTH(functionArguments), functionArguments, &exception);
}
if (!exception)
return;
String errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::JavaScriptError);
String exceptionMessage;
if (JSValueIsObject(context, exception)) {
JSValueRef nameValue = JSObjectGetProperty(context, const_cast<JSObjectRef>(exception), OpaqueJSString::tryCreate("name"_s).get(), nullptr);
auto exceptionName = adoptRef(JSValueToStringCopy(context, nameValue, nullptr))->string();
if (exceptionName == "NodeNotFound")
errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);
else if (exceptionName == "InvalidElementState")
errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InvalidElementState);
else if (exceptionName == "InvalidParameter")
errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InvalidParameter);
else if (exceptionName == "InvalidSelector")
errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InvalidSelector);
else if (exceptionName == "ElementNotInteractable")
errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ElementNotInteractable);
JSValueRef messageValue = JSObjectGetProperty(context, const_cast<JSObjectRef>(exception), OpaqueJSString::tryCreate("message"_s).get(), nullptr);
exceptionMessage = adoptRef(JSValueToStringCopy(context, messageValue, nullptr))->string();
} else
exceptionMessage = adoptRef(JSValueToStringCopy(context, exception, nullptr))->string();
didEvaluateJavaScriptFunction(frameID, callbackID, exceptionMessage, errorType);
}
void WebAutomationSessionProxy::didEvaluateJavaScriptFunction(WebCore::FrameIdentifier frameID, uint64_t callbackID, const String& result, const String& errorType)
{
auto findResult = m_webFramePendingEvaluateJavaScriptCallbacksMap.find(frameID);
if (findResult != m_webFramePendingEvaluateJavaScriptCallbacksMap.end()) {
findResult->value.removeFirst(callbackID);
ASSERT(!findResult->value.contains(callbackID));
if (findResult->value.isEmpty())
m_webFramePendingEvaluateJavaScriptCallbacksMap.remove(findResult);
}
WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidEvaluateJavaScriptFunction(callbackID, result, errorType), 0);
}
void WebAutomationSessionProxy::resolveChildFrameWithOrdinal(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, uint32_t ordinal, CompletionHandler<void(Optional<String>, Optional<WebCore::FrameIdentifier>)>&& completionHandler)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page) {
String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
completionHandler(windowNotFoundErrorType, WTF::nullopt);
return;
}
String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
if (!frame) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
WebCore::Frame* coreFrame = frame->coreFrame();
if (!coreFrame) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
WebCore::Frame* coreChildFrame = coreFrame->tree().scopedChild(ordinal);
if (!coreChildFrame) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
WebFrame* childFrame = WebFrame::fromCoreFrame(*coreChildFrame);
if (!childFrame) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
completionHandler(WTF::nullopt, childFrame->frameID());
}
void WebAutomationSessionProxy::resolveChildFrameWithNodeHandle(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, const String& nodeHandle, CompletionHandler<void(Optional<String>, Optional<WebCore::FrameIdentifier>)>&& completionHandler)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page) {
String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
completionHandler(windowNotFoundErrorType, WTF::nullopt);
return;
}
String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
if (!frame) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle);
if (!is<WebCore::HTMLFrameElementBase>(coreElement)) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
WebCore::Frame* coreFrameFromElement = downcast<WebCore::HTMLFrameElementBase>(*coreElement).contentFrame();
if (!coreFrameFromElement) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
WebFrame* frameFromElement = WebFrame::fromCoreFrame(*coreFrameFromElement);
if (!frameFromElement) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
completionHandler(WTF::nullopt, frameFromElement->frameID());
}
void WebAutomationSessionProxy::resolveChildFrameWithName(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, const String& name, CompletionHandler<void(Optional<String>, Optional<WebCore::FrameIdentifier>)>&& completionHandler)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page) {
String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
completionHandler(windowNotFoundErrorType, WTF::nullopt);
return;
}
String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
if (!frame) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
WebCore::Frame* coreFrame = frame->coreFrame();
if (!coreFrame) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
WebCore::Frame* coreChildFrame = coreFrame->tree().scopedChild(name);
if (!coreChildFrame) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
WebFrame* childFrame = WebFrame::fromCoreFrame(*coreChildFrame);
if (!childFrame) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
completionHandler(WTF::nullopt, childFrame->frameID());
}
void WebAutomationSessionProxy::resolveParentFrame(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, CompletionHandler<void(Optional<String>, Optional<WebCore::FrameIdentifier>)>&& completionHandler)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page) {
String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
completionHandler(windowNotFoundErrorType, WTF::nullopt);
return;
}
String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
if (!frame) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
WebFrame* parentFrame = frame->parentFrame();
if (!parentFrame) {
completionHandler(frameNotFoundErrorType, WTF::nullopt);
return;
}
completionHandler(WTF::nullopt, parentFrame->frameID());
}
void WebAutomationSessionProxy::focusFrame(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page)
return;
WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
if (!frame)
return;
WebCore::Frame* coreFrame = frame->coreFrame();
if (!coreFrame)
return;
WebCore::Document* coreDocument = coreFrame->document();
if (!coreDocument)
return;
WebCore::DOMWindow* coreDOMWindow = coreDocument->domWindow();
if (!coreDOMWindow)
return;
coreDOMWindow->focus(true);
}
static WebCore::Element* containerElementForElement(WebCore::Element& element)
{
if (is<WebCore::HTMLOptionElement>(element)) {
auto& optionElement = downcast<WebCore::HTMLOptionElement>(element);
#if ENABLE(DATALIST_ELEMENT)
if (auto* parentElement = optionElement.ownerDataListElement())
return parentElement;
#endif
if (auto* parentElement = optionElement.ownerSelectElement())
return parentElement;
return nullptr;
}
if (is<WebCore::HTMLOptGroupElement>(element)) {
if (auto* parentElement = downcast<WebCore::HTMLOptGroupElement>(element).ownerSelectElement())
return parentElement;
return nullptr;
}
return &element;
}
static WebCore::FloatRect convertRectFromFrameClientToRootView(WebCore::FrameView* frameView, WebCore::FloatRect clientRect)
{
if (!frameView->delegatesScrolling())
return frameView->contentsToRootView(frameView->clientToDocumentRect(clientRect));
auto& frame = frameView->frame();
clientRect.scale(frame.pageZoomFactor() * frame.frameScaleFactor());
clientRect.moveBy(frameView->contentsScrollPosition());
return clientRect;
}
static WebCore::FloatPoint convertPointFromFrameClientToRootView(WebCore::FrameView* frameView, WebCore::FloatPoint clientPoint)
{
if (!frameView->delegatesScrolling())
return frameView->contentsToRootView(frameView->clientToDocumentPoint(clientPoint));
auto& frame = frameView->frame();
clientPoint.scale(frame.pageZoomFactor() * frame.frameScaleFactor());
clientPoint.moveBy(frameView->contentsScrollPosition());
return clientPoint;
}
void WebAutomationSessionProxy::computeElementLayout(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle, bool scrollIntoViewIfNeeded, CoordinateSystem coordinateSystem, CompletionHandler<void(Optional<String>, WebCore::IntRect, Optional<WebCore::IntPoint>, bool)>&& completionHandler)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page) {
String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
completionHandler(windowNotFoundErrorType, { }, WTF::nullopt, false);
return;
}
WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
if (!frame || !frame->coreFrame() || !frame->coreFrame()->view()) {
String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
completionHandler(frameNotFoundErrorType, { }, WTF::nullopt, false);
return;
}
WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle);
if (!coreElement) {
String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);
completionHandler(nodeNotFoundErrorType, { }, WTF::nullopt, false);
return;
}
auto* containerElement = containerElementForElement(*coreElement);
if (scrollIntoViewIfNeeded && containerElement) {
containerElement->scrollIntoViewIfNotVisible(false);
}
WebCore::FrameView* frameView = frame->coreFrame()->view();
WebCore::FrameView* mainView = frame->coreFrame()->mainFrame().view();
WebCore::IntRect resultElementBounds;
Optional<WebCore::IntPoint> resultInViewCenterPoint;
bool isObscured = false;
auto elementBoundsInRootCoordinates = convertRectFromFrameClientToRootView(frameView, coreElement->boundingClientRect());
switch (coordinateSystem) {
case CoordinateSystem::Page:
resultElementBounds = enclosingIntRect(mainView->absoluteToDocumentRect(mainView->rootViewToContents(elementBoundsInRootCoordinates)));
break;
case CoordinateSystem::LayoutViewport:
resultElementBounds = enclosingIntRect(mainView->absoluteToLayoutViewportRect(elementBoundsInRootCoordinates));
break;
}
if (!containerElement) {
String elementNotInteractableErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ElementNotInteractable);
completionHandler(elementNotInteractableErrorType, resultElementBounds, resultInViewCenterPoint, isObscured);
return;
}
auto* firstElementRect = containerElement->getClientRects()->item(0);
if (!firstElementRect) {
String elementNotInteractableErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ElementNotInteractable);
completionHandler(elementNotInteractableErrorType, resultElementBounds, resultInViewCenterPoint, isObscured);
return;
}
auto viewportRect = frameView->documentToClientRect(frameView->visualViewportRect());
auto elementRect = WebCore::FloatRect(firstElementRect->x(), firstElementRect->y(), firstElementRect->width(), firstElementRect->height());
auto visiblePortionOfElementRect = intersection(viewportRect, elementRect);
if (visiblePortionOfElementRect.isEmpty()) {
completionHandler(WTF::nullopt, resultElementBounds, resultInViewCenterPoint, isObscured);
return;
}
auto elementInViewCenterPoint = visiblePortionOfElementRect.center();
auto elementList = containerElement->treeScope().elementsFromPoint(elementInViewCenterPoint);
auto index = elementList.findMatching([containerElement] (auto& item) { return item.get() == containerElement; });
if (elementList.isEmpty() || index == notFound) {
String elementNotInteractableErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ElementNotInteractable);
completionHandler(elementNotInteractableErrorType, resultElementBounds, resultInViewCenterPoint, isObscured);
return;
}
isObscured = elementList[0] != containerElement && !elementList[0]->isDescendantOf(containerElement);
auto inViewCenterPointInRootCoordinates = convertPointFromFrameClientToRootView(frameView, elementInViewCenterPoint);
switch (coordinateSystem) {
case CoordinateSystem::Page:
resultInViewCenterPoint = roundedIntPoint(mainView->absoluteToDocumentPoint(inViewCenterPointInRootCoordinates));
break;
case CoordinateSystem::LayoutViewport:
resultInViewCenterPoint = roundedIntPoint(mainView->absoluteToLayoutViewportPoint(inViewCenterPointInRootCoordinates));
break;
}
completionHandler(WTF::nullopt, resultElementBounds, resultInViewCenterPoint, isObscured);
}
void WebAutomationSessionProxy::selectOptionElement(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle, CompletionHandler<void(Optional<String>)>&& completionHandler)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page) {
String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
completionHandler(windowNotFoundErrorType);
return;
}
WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
if (!frame || !frame->coreFrame() || !frame->coreFrame()->view()) {
String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
completionHandler(frameNotFoundErrorType);
return;
}
WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle);
if (!coreElement || (!is<WebCore::HTMLOptionElement>(coreElement) && !is<WebCore::HTMLOptGroupElement>(coreElement))) {
String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);
completionHandler(nodeNotFoundErrorType);
return;
}
String elementNotInteractableErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ElementNotInteractable);
if (is<WebCore::HTMLOptGroupElement>(coreElement)) {
completionHandler(elementNotInteractableErrorType);
return;
}
auto& optionElement = downcast<WebCore::HTMLOptionElement>(*coreElement);
auto* selectElement = optionElement.ownerSelectElement();
if (!selectElement) {
completionHandler(elementNotInteractableErrorType);
return;
}
if (!selectElement->isDisabledFormControl() && !optionElement.isDisabledFormControl()) {
selectElement->optionSelectedByUser(optionElement.index(), true, selectElement->multiple());
}
completionHandler(WTF::nullopt);
}
void WebAutomationSessionProxy::setFilesForInputFileUpload(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle, Vector<String>&& filenames, CompletionHandler<void(Optional<String>)>&& completionHandler)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page) {
String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
completionHandler(windowNotFoundErrorType);
return;
}
WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
if (!frame || !frame->coreFrame() || !frame->coreFrame()->view()) {
String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
completionHandler(frameNotFoundErrorType);
return;
}
WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle);
if (!coreElement || !is<WebCore::HTMLInputElement>(coreElement) || !downcast<WebCore::HTMLInputElement>(*coreElement).isFileUpload()) {
String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);
completionHandler(nodeNotFoundErrorType);
return;
}
auto& inputElement = downcast<WebCore::HTMLInputElement>(*coreElement);
Vector<Ref<WebCore::File>> fileObjects;
if (inputElement.multiple()) {
if (auto* files = inputElement.files()) {
for (auto& file : files->files())
fileObjects.append(file.copyRef());
}
}
for (const auto& path : filenames)
fileObjects.append(WebCore::File::create(path));
inputElement.setFiles(WebCore::FileList::create(WTFMove(fileObjects)));
completionHandler(WTF::nullopt);
}
static WebCore::IntRect snapshotRectForScreenshot(WebPage& page, WebCore::Element* element, bool clipToViewport)
{
auto* frameView = page.mainFrameView();
if (!frameView)
return { };
if (element) {
if (!element->renderer())
return { };
WebCore::LayoutRect topLevelRect;
WebCore::IntRect elementRect = WebCore::snappedIntRect(element->renderer()->paintingRootRect(topLevelRect));
if (clipToViewport)
elementRect.intersect(frameView->visibleContentRect());
return elementRect;
}
if (auto* frameView = page.mainFrameView())
return clipToViewport ? frameView->visibleContentRect() : WebCore::IntRect(WebCore::IntPoint(0, 0), frameView->contentsSize());
return { };
}
void WebAutomationSessionProxy::takeScreenshot(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String nodeHandle, bool scrollIntoViewIfNeeded, bool clipToViewport, uint64_t callbackID)
{
ShareableBitmap::Handle handle;
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::DidTakeScreenshot(callbackID, handle, windowNotFoundErrorType), 0);
return;
}
WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
if (!frame || !frame->coreFrame()) {
String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, frameNotFoundErrorType), 0);
return;
}
WebCore::Element* coreElement = nullptr;
if (!nodeHandle.isEmpty()) {
coreElement = elementForNodeHandle(*frame, nodeHandle);
if (!coreElement) {
String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);
WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, nodeNotFoundErrorType), 0);
return;
}
}
if (coreElement && scrollIntoViewIfNeeded)
coreElement->scrollIntoViewIfNeeded(false);
String screenshotErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::ScreenshotError);
WebCore::IntRect snapshotRect = snapshotRectForScreenshot(*page, coreElement, clipToViewport);
if (snapshotRect.isEmpty()) {
WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, screenshotErrorType), 0);
return;
}
RefPtr<WebImage> image = page->scaledSnapshotWithOptions(snapshotRect, 1, SnapshotOptionsShareable);
if (!image) {
WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, screenshotErrorType), 0);
return;
}
image->bitmap().createHandle(handle, SharedMemory::Protection::ReadOnly);
WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, { }), 0);
}
void WebAutomationSessionProxy::getCookiesForFrame(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, CompletionHandler<void(Optional<String>, Vector<WebCore::Cookie>)>&& completionHandler)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page) {
String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
completionHandler(windowNotFoundErrorType, Vector<WebCore::Cookie>());
return;
}
WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
if (!frame || !frame->coreFrame() || !frame->coreFrame()->document()) {
String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
completionHandler(frameNotFoundErrorType, Vector<WebCore::Cookie>());
return;
}
auto& document = *frame->coreFrame()->document();
Vector<WebCore::Cookie> foundCookies;
if (!document.cookieURL().isEmpty())
page->corePage()->cookieJar().getRawCookies(document, document.cookieURL(), foundCookies);
completionHandler(WTF::nullopt, foundCookies);
}
void WebAutomationSessionProxy::deleteCookie(WebCore::PageIdentifier pageID, Optional<WebCore::FrameIdentifier> frameID, String cookieName, CompletionHandler<void(Optional<String>)>&& completionHandler)
{
WebPage* page = WebProcess::singleton().webPage(pageID);
if (!page) {
String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
completionHandler(windowNotFoundErrorType);
return;
}
WebFrame* frame = frameID ? WebProcess::singleton().webFrame(*frameID) : page->mainWebFrame();
if (!frame || !frame->coreFrame() || !frame->coreFrame()->document()) {
String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
completionHandler(frameNotFoundErrorType);
return;
}
auto& document = *frame->coreFrame()->document();
page->corePage()->cookieJar().deleteCookie(document, document.cookieURL(), cookieName);
completionHandler(WTF::nullopt);
}
}