52d347d37f0f02ed602963bccb81080e9fcf5783
[WebKit-https.git] / Source / WebKit / WebProcess / Automation / WebAutomationSessionProxy.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WebAutomationSessionProxy.h"
28
29 #include "AutomationProtocolObjects.h"
30 #include "WebAutomationSessionMessages.h"
31 #include "WebAutomationSessionProxyMessages.h"
32 #include "WebAutomationSessionProxyScriptSource.h"
33 #include "WebCoreArgumentCoders.h"
34 #include "WebFrame.h"
35 #include "WebImage.h"
36 #include "WebPage.h"
37 #include "WebProcess.h"
38 #include <JavaScriptCore/APICast.h>
39 #include <JavaScriptCore/JSObject.h>
40 #include <JavaScriptCore/JSRetainPtr.h>
41 #include <JavaScriptCore/JSStringRefPrivate.h>
42 #include <JavaScriptCore/OpaqueJSString.h>
43 #include <WebCore/CookieJar.h>
44 #include <WebCore/DOMRect.h>
45 #include <WebCore/DOMRectList.h>
46 #include <WebCore/DOMWindow.h>
47 #include <WebCore/Frame.h>
48 #include <WebCore/FrameTree.h>
49 #include <WebCore/FrameView.h>
50 #include <WebCore/HTMLFrameElementBase.h>
51 #include <WebCore/JSElement.h>
52 #include <WebCore/MainFrame.h>
53 #include <wtf/UUID.h>
54
55 namespace WebKit {
56
57 template <typename T>
58 static JSObjectRef toJSArray(JSContextRef context, const Vector<T>& data, JSValueRef (*converter)(JSContextRef, const T&), JSValueRef* exception)
59 {
60     ASSERT_ARG(converter, converter);
61
62     if (data.isEmpty())
63         return JSObjectMakeArray(context, 0, nullptr, exception);
64
65     Vector<JSValueRef, 8> convertedData;
66     convertedData.reserveCapacity(data.size());
67
68     for (auto& originalValue : data) {
69         JSValueRef convertedValue = converter(context, originalValue);
70         JSValueProtect(context, convertedValue);
71         convertedData.uncheckedAppend(convertedValue);
72     }
73
74     JSObjectRef array = JSObjectMakeArray(context, convertedData.size(), convertedData.data(), exception);
75
76     for (auto& convertedValue : convertedData)
77         JSValueUnprotect(context, convertedValue);
78
79     return array;
80 }
81
82 static inline JSRetainPtr<JSStringRef> toJSString(const String& string)
83 {
84     return JSRetainPtr<JSStringRef>(Adopt, OpaqueJSString::create(string).leakRef());
85 }
86
87 static inline JSValueRef toJSValue(JSContextRef context, const String& string)
88 {
89     return JSValueMakeString(context, toJSString(string).get());
90 }
91
92 static inline JSValueRef callPropertyFunction(JSContextRef context, JSObjectRef object, const String& propertyName, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
93 {
94     ASSERT_ARG(object, object);
95     ASSERT_ARG(object, JSValueIsObject(context, object));
96
97     JSObjectRef function = const_cast<JSObjectRef>(JSObjectGetProperty(context, object, toJSString(propertyName).get(), exception));
98     ASSERT(JSObjectIsFunction(context, function));
99
100     return JSObjectCallAsFunction(context, function, object, argumentCount, arguments, exception);
101 }
102
103 WebAutomationSessionProxy::WebAutomationSessionProxy(const String& sessionIdentifier)
104     : m_sessionIdentifier(sessionIdentifier)
105 {
106     WebProcess::singleton().addMessageReceiver(Messages::WebAutomationSessionProxy::messageReceiverName(), *this);
107 }
108
109 WebAutomationSessionProxy::~WebAutomationSessionProxy()
110 {
111     WebProcess::singleton().removeMessageReceiver(Messages::WebAutomationSessionProxy::messageReceiverName());
112 }
113
114 static JSValueRef evaluate(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
115 {
116     ASSERT_ARG(argumentCount, argumentCount == 1);
117     ASSERT_ARG(arguments, JSValueIsString(context, arguments[0]));
118
119     if (argumentCount != 1)
120         return JSValueMakeUndefined(context);
121
122     JSRetainPtr<JSStringRef> script(Adopt, JSValueToStringCopy(context, arguments[0], exception));
123     return JSEvaluateScript(context, script.get(), nullptr, nullptr, 0, exception);
124 }
125
126 static JSValueRef createUUID(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
127 {
128     return toJSValue(context, createCanonicalUUIDString().convertToASCIIUppercase());
129 }
130
131 static JSValueRef evaluateJavaScriptCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
132 {
133     ASSERT_ARG(argumentCount, argumentCount == 4);
134     ASSERT_ARG(arguments, JSValueIsNumber(context, arguments[0]));
135     ASSERT_ARG(arguments, JSValueIsNumber(context, arguments[1]));
136     ASSERT_ARG(arguments, JSValueIsString(context, arguments[2]));
137     ASSERT_ARG(arguments, JSValueIsBoolean(context, arguments[3]));
138
139     auto automationSessionProxy = WebProcess::singleton().automationSessionProxy();
140     if (!automationSessionProxy)
141         return JSValueMakeUndefined(context);
142
143     uint64_t frameID = JSValueToNumber(context, arguments[0], exception);
144     uint64_t callbackID = JSValueToNumber(context, arguments[1], exception);
145     JSRetainPtr<JSStringRef> result(Adopt, JSValueToStringCopy(context, arguments[2], exception));
146
147     bool resultIsErrorName = JSValueToBoolean(context, arguments[3]);
148
149     if (resultIsErrorName) {
150         if (result->string() == "JavaScriptTimeout") {
151             String errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::JavaScriptTimeout);
152             automationSessionProxy->didEvaluateJavaScriptFunction(frameID, callbackID, String(), errorType);
153         } else {
154             ASSERT_NOT_REACHED();
155             String errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InternalError);
156             automationSessionProxy->didEvaluateJavaScriptFunction(frameID, callbackID, String(), errorType);
157         }
158     } else
159         automationSessionProxy->didEvaluateJavaScriptFunction(frameID, callbackID, result->string(), String());
160
161     return JSValueMakeUndefined(context);
162 }
163
164 JSObjectRef WebAutomationSessionProxy::scriptObjectForFrame(WebFrame& frame)
165 {
166     if (JSObjectRef scriptObject = m_webFrameScriptObjectMap.get(frame.frameID()))
167         return scriptObject;
168
169     JSValueRef exception = nullptr;
170     JSGlobalContextRef context = frame.jsContext();
171
172     JSValueRef sessionIdentifier = toJSValue(context, m_sessionIdentifier);
173     JSObjectRef evaluateFunction = JSObjectMakeFunctionWithCallback(context, nullptr, evaluate);
174     JSObjectRef createUUIDFunction = JSObjectMakeFunctionWithCallback(context, nullptr, createUUID);
175
176     String script = StringImpl::createWithoutCopying(WebAutomationSessionProxyScriptSource, sizeof(WebAutomationSessionProxyScriptSource));
177
178     JSObjectRef scriptObjectFunction = const_cast<JSObjectRef>(JSEvaluateScript(context, toJSString(script).get(), nullptr, nullptr, 0, &exception));
179     ASSERT(JSValueIsObject(context, scriptObjectFunction));
180
181     JSValueRef arguments[] = { sessionIdentifier, evaluateFunction, createUUIDFunction };
182     JSObjectRef scriptObject = const_cast<JSObjectRef>(JSObjectCallAsFunction(context, scriptObjectFunction, nullptr, WTF_ARRAY_LENGTH(arguments), arguments, &exception));
183     ASSERT(JSValueIsObject(context, scriptObject));
184
185     JSValueProtect(context, scriptObject);
186     m_webFrameScriptObjectMap.add(frame.frameID(), scriptObject);
187
188     return scriptObject;
189 }
190
191 WebCore::Element* WebAutomationSessionProxy::elementForNodeHandle(WebFrame& frame, const String& nodeHandle)
192 {
193     // Don't use scriptObjectForFrame() since we can assume if the script object
194     // does not exist, there are no nodes mapped to handles. Using scriptObjectForFrame()
195     // will make a new script object if it can't find one, preventing us from returning fast.
196     JSObjectRef scriptObject = m_webFrameScriptObjectMap.get(frame.frameID());
197     if (!scriptObject)
198         return nullptr;
199
200     JSGlobalContextRef context = frame.jsContext();
201
202     JSValueRef functionArguments[] = {
203         toJSValue(context, nodeHandle)
204     };
205
206     JSValueRef result = callPropertyFunction(context, scriptObject, ASCIILiteral("nodeForIdentifier"), WTF_ARRAY_LENGTH(functionArguments), functionArguments, nullptr);
207     JSObjectRef element = JSValueToObject(context, result, nullptr);
208     if (!element)
209         return nullptr;
210
211     auto elementWrapper = WebCore::jsDynamicDowncast<WebCore::JSElement*>(toJS(context)->vm(), toJS(element));
212     if (!elementWrapper)
213         return nullptr;
214
215     return &elementWrapper->wrapped();
216 }
217
218 void WebAutomationSessionProxy::didClearWindowObjectForFrame(WebFrame& frame)
219 {
220     uint64_t frameID = frame.frameID();
221     if (JSObjectRef scriptObject = m_webFrameScriptObjectMap.take(frameID))
222         JSValueUnprotect(frame.jsContext(), scriptObject);
223
224     String errorMessage = ASCIILiteral("Callback was not called before the unload event.");
225     String errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::JavaScriptError);
226
227     auto pendingFrameCallbacks = m_webFramePendingEvaluateJavaScriptCallbacksMap.take(frameID);
228     for (uint64_t callbackID : pendingFrameCallbacks)
229         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidEvaluateJavaScriptFunction(callbackID, errorMessage, errorType), 0);
230 }
231
232 void WebAutomationSessionProxy::evaluateJavaScriptFunction(uint64_t pageID, uint64_t frameID, const String& function, Vector<String> arguments, bool expectsImplicitCallbackArgument, int callbackTimeout, uint64_t callbackID)
233 {
234     WebPage* page = WebProcess::singleton().webPage(pageID);
235     if (!page) {
236         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidEvaluateJavaScriptFunction(callbackID, { },
237             Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound)), 0);
238         return;
239     }
240     WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame();
241     if (!frame) {
242         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidEvaluateJavaScriptFunction(callbackID, { },
243             Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound)), 0);
244         return;
245     }
246
247     JSObjectRef scriptObject = scriptObjectForFrame(*frame);
248     ASSERT(scriptObject);
249
250     frameID = frame->frameID();
251     JSValueRef exception = nullptr;
252     JSGlobalContextRef context = frame->jsContext();
253
254     if (expectsImplicitCallbackArgument) {
255         auto result = m_webFramePendingEvaluateJavaScriptCallbacksMap.add(frameID, Vector<uint64_t>());
256         result.iterator->value.append(callbackID);
257     }
258
259     JSValueRef functionArguments[] = {
260         toJSValue(context, function),
261         toJSArray(context, arguments, toJSValue, &exception),
262         JSValueMakeBoolean(context, expectsImplicitCallbackArgument),
263         JSValueMakeNumber(context, frameID),
264         JSValueMakeNumber(context, callbackID),
265         JSObjectMakeFunctionWithCallback(context, nullptr, evaluateJavaScriptCallback),
266         JSValueMakeNumber(context, callbackTimeout)
267     };
268
269     callPropertyFunction(context, scriptObject, ASCIILiteral("evaluateJavaScriptFunction"), WTF_ARRAY_LENGTH(functionArguments), functionArguments, &exception);
270
271     if (!exception)
272         return;
273
274     String errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::JavaScriptError);
275
276     JSRetainPtr<JSStringRef> exceptionMessage;
277     if (JSValueIsObject(context, exception)) {
278         JSValueRef nameValue = JSObjectGetProperty(context, const_cast<JSObjectRef>(exception), toJSString(ASCIILiteral("name")).get(), nullptr);
279         JSRetainPtr<JSStringRef> exceptionName(Adopt, JSValueToStringCopy(context, nameValue, nullptr));
280         if (exceptionName->string() == "NodeNotFound")
281             errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);
282         else if (exceptionName->string() == "InvalidElementState")
283             errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InvalidElementState);
284         else if (exceptionName->string() == "InvalidParameter")
285             errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InvalidParameter);
286         else if (exceptionName->string() == "InvalidSelector")
287             errorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::InvalidSelector);
288
289         JSValueRef messageValue = JSObjectGetProperty(context, const_cast<JSObjectRef>(exception), toJSString(ASCIILiteral("message")).get(), nullptr);
290         exceptionMessage.adopt(JSValueToStringCopy(context, messageValue, nullptr));
291     } else
292         exceptionMessage.adopt(JSValueToStringCopy(context, exception, nullptr));
293
294     didEvaluateJavaScriptFunction(frameID, callbackID, exceptionMessage->string(), errorType);
295 }
296
297 void WebAutomationSessionProxy::didEvaluateJavaScriptFunction(uint64_t frameID, uint64_t callbackID, const String& result, const String& errorType)
298 {
299     auto findResult = m_webFramePendingEvaluateJavaScriptCallbacksMap.find(frameID);
300     if (findResult != m_webFramePendingEvaluateJavaScriptCallbacksMap.end()) {
301         findResult->value.removeFirst(callbackID);
302         ASSERT(!findResult->value.contains(callbackID));
303         if (findResult->value.isEmpty())
304             m_webFramePendingEvaluateJavaScriptCallbacksMap.remove(findResult);
305     }
306
307     WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidEvaluateJavaScriptFunction(callbackID, result, errorType), 0);
308 }
309
310 void WebAutomationSessionProxy::resolveChildFrameWithOrdinal(uint64_t pageID, uint64_t frameID, uint32_t ordinal, uint64_t callbackID)
311 {
312     WebPage* page = WebProcess::singleton().webPage(pageID);
313     if (!page) {
314         String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
315         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, windowNotFoundErrorType), 0);
316         return;
317     }
318
319     String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
320
321     WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame();
322     if (!frame) {
323         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
324         return;
325     }
326
327     WebCore::Frame* coreFrame = frame->coreFrame();
328     if (!coreFrame) {
329         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
330         return;
331     }
332
333     WebCore::Frame* coreChildFrame = coreFrame->tree().scopedChild(ordinal);
334     if (!coreChildFrame) {
335         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
336         return;
337     }
338
339     WebFrame* childFrame = WebFrame::fromCoreFrame(*coreChildFrame);
340     if (!childFrame) {
341         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
342         return;
343     }
344
345     WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, childFrame->frameID(), String()), 0);
346 }
347
348 void WebAutomationSessionProxy::resolveChildFrameWithNodeHandle(uint64_t pageID, uint64_t frameID, const String& nodeHandle, uint64_t callbackID)
349 {
350     WebPage* page = WebProcess::singleton().webPage(pageID);
351     if (!page) {
352         String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
353         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, windowNotFoundErrorType), 0);
354         return;
355     }
356
357     String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
358
359     WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame();
360     if (!frame) {
361         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
362         return;
363     }
364
365     WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle);
366     if (!coreElement || !coreElement->isFrameElementBase()) {
367         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
368         return;
369     }
370
371     WebCore::Frame* coreFrameFromElement = static_cast<WebCore::HTMLFrameElementBase*>(coreElement)->contentFrame();
372     if (!coreFrameFromElement) {
373         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
374         return;
375     }
376
377     WebFrame* frameFromElement = WebFrame::fromCoreFrame(*coreFrameFromElement);
378     if (!frameFromElement) {
379         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
380         return;
381     }
382
383     WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, frameFromElement->frameID(), String()), 0);
384 }
385
386 void WebAutomationSessionProxy::resolveChildFrameWithName(uint64_t pageID, uint64_t frameID, const String& name, uint64_t callbackID)
387 {
388     WebPage* page = WebProcess::singleton().webPage(pageID);
389     if (!page) {
390         String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
391         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, windowNotFoundErrorType), 0);
392         return;
393     }
394
395     String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
396
397     WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame();
398     if (!frame) {
399         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
400         return;
401     }
402
403     WebCore::Frame* coreFrame = frame->coreFrame();
404     if (!coreFrame) {
405         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
406         return;
407     }
408
409     WebCore::Frame* coreChildFrame = coreFrame->tree().scopedChild(name);
410     if (!coreChildFrame) {
411         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
412         return;
413     }
414
415     WebFrame* childFrame = WebFrame::fromCoreFrame(*coreChildFrame);
416     if (!childFrame) {
417         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, 0, frameNotFoundErrorType), 0);
418         return;
419     }
420
421     WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveChildFrame(callbackID, childFrame->frameID(), String()), 0);
422 }
423
424 void WebAutomationSessionProxy::resolveParentFrame(uint64_t pageID, uint64_t frameID, uint64_t callbackID)
425 {
426     WebPage* page = WebProcess::singleton().webPage(pageID);
427     if (!page) {
428         String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
429         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveParentFrame(callbackID, 0, windowNotFoundErrorType), 0);
430         return;
431     }
432
433     String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
434
435     WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame();
436     if (!frame) {
437         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveParentFrame(callbackID, 0, frameNotFoundErrorType), 0);
438         return;
439     }
440
441     WebFrame* parentFrame = frame->parentFrame();
442     if (!parentFrame) {
443         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveParentFrame(callbackID, 0, frameNotFoundErrorType), 0);
444         return;
445     }
446
447     WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidResolveParentFrame(callbackID, parentFrame->frameID(), String()), 0);
448 }
449
450 void WebAutomationSessionProxy::focusFrame(uint64_t pageID, uint64_t frameID)
451 {
452     WebPage* page = WebProcess::singleton().webPage(pageID);
453     if (!page)
454         return;
455
456     WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame();
457     if (!frame)
458         return;
459
460     WebCore::Frame* coreFrame = frame->coreFrame();
461     if (!coreFrame)
462         return;
463
464     WebCore::Document* coreDocument = coreFrame->document();
465     if (!coreDocument)
466         return;
467
468     WebCore::DOMWindow* coreDOMWindow = coreDocument->domWindow();
469     if (!coreDOMWindow)
470         return;
471
472     coreDOMWindow->focus(true);
473 }
474
475 static std::optional<WebCore::FloatPoint> elementInViewClientCenterPoint(WebCore::Element& element, bool& isObscured)
476 {
477     // ยง11.1 Element Interactability.
478     // https://www.w3.org/TR/webdriver/#dfn-in-view-center-point
479     auto* clientRect = element.getClientRects()->item(0);
480     if (!clientRect)
481         return std::nullopt;
482
483     auto clientCenterPoint = WebCore::FloatPoint::narrowPrecision(0.5 * (clientRect->left() + clientRect->right()), 0.5 * (clientRect->top() + clientRect->bottom()));
484     auto elementList = element.treeScope().elementsFromPoint(clientCenterPoint.x(), clientCenterPoint.y());
485     if (elementList.isEmpty()) {
486         // An element is obscured if the pointer-interactable paint tree at its center point is empty,
487         // or the first element in this tree is not an inclusive descendant of itself.
488         // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-obscured
489         isObscured = true;
490         return clientCenterPoint;
491     }
492
493     auto index = elementList.findMatching([&element] (auto& item) { return item.get() == &element; });
494     if (index == notFound)
495         return std::nullopt;
496
497     if (index) {
498         // Element is not the first one in the list.
499         auto firstElement = elementList[0];
500         isObscured = !firstElement->isDescendantOf(element);
501     }
502
503     return clientCenterPoint;
504 }
505
506 void WebAutomationSessionProxy::computeElementLayout(uint64_t pageID, uint64_t frameID, String nodeHandle, bool scrollIntoViewIfNeeded, bool useViewportCoordinates, uint64_t callbackID)
507 {
508     WebPage* page = WebProcess::singleton().webPage(pageID);
509     if (!page) {
510         String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
511         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, { }, std::nullopt, false, windowNotFoundErrorType), 0);
512         return;
513     }
514
515     String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
516     String nodeNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::NodeNotFound);
517
518     WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame();
519     if (!frame || !frame->coreFrame() || !frame->coreFrame()->view()) {
520         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, { }, std::nullopt, false, frameNotFoundErrorType), 0);
521         return;
522     }
523
524     WebCore::Element* coreElement = elementForNodeHandle(*frame, nodeHandle);
525     if (!coreElement) {
526         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, { }, std::nullopt, false, nodeNotFoundErrorType), 0);
527         return;
528     }
529
530     if (scrollIntoViewIfNeeded) {
531         // FIXME: Wait in an implementation-specific way up to the session implicit wait timeout for the element to become in view.
532         coreElement->scrollIntoViewIfNeeded(false);
533     }
534
535     WebCore::IntRect rect = coreElement->clientRect();
536
537     auto* coreFrameView = frame->coreFrame()->view();
538     if (useViewportCoordinates)
539         rect.moveBy(WebCore::IntPoint(0, -coreFrameView->topContentInset()));
540     else
541         rect = coreFrameView->rootViewToContents(rect);
542
543     bool isObscured = false;
544     std::optional<WebCore::IntPoint> inViewCenter;
545     if (auto clientCenterPoint = elementInViewClientCenterPoint(*coreElement, isObscured)) {
546         inViewCenter = WebCore::IntPoint(coreFrameView->clientToDocumentPoint(clientCenterPoint.value()));
547         if (useViewportCoordinates)
548             inViewCenter = coreFrameView->contentsToRootView(inViewCenter.value());
549     }
550
551     WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidComputeElementLayout(callbackID, rect, inViewCenter, isObscured, String()), 0);
552 }
553
554 void WebAutomationSessionProxy::takeScreenshot(uint64_t pageID, uint64_t callbackID)
555 {
556     ShareableBitmap::Handle handle;
557
558     WebPage* page = WebProcess::singleton().webPage(pageID);
559     if (!page) {
560         String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
561         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, windowNotFoundErrorType), 0);
562         return;
563     }
564
565     WebCore::FrameView* frameView = page->mainFrameView();
566     if (!frameView) {
567         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, String()), 0);
568         return;
569     }
570
571     WebCore::IntRect snapshotRect = WebCore::IntRect(WebCore::IntPoint(0, 0), frameView->contentsSize());
572     if (snapshotRect.isEmpty()) {
573         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, String()), 0);
574         return;
575     }
576
577     RefPtr<WebImage> image = page->scaledSnapshotWithOptions(snapshotRect, 1, SnapshotOptionsShareable);
578     if (image)
579         image->bitmap().createHandle(handle, SharedMemory::Protection::ReadOnly);
580
581     WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidTakeScreenshot(callbackID, handle, String()), 0);    
582 }
583
584 void WebAutomationSessionProxy::getCookiesForFrame(uint64_t pageID, uint64_t frameID, uint64_t callbackID)
585 {
586     WebPage* page = WebProcess::singleton().webPage(pageID);
587     if (!page) {
588         String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
589         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidGetCookiesForFrame(callbackID, Vector<WebCore::Cookie>(), windowNotFoundErrorType), 0);
590         return;
591     }
592
593     WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame();
594     if (!frame || !frame->coreFrame() || !frame->coreFrame()->document()) {
595         String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
596         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidGetCookiesForFrame(callbackID, Vector<WebCore::Cookie>(), frameNotFoundErrorType), 0);
597         return;
598     }
599
600     // This returns the same list of cookies as when evaluating `document.cookies` in JavaScript.
601     auto& document = *frame->coreFrame()->document();
602     Vector<WebCore::Cookie> foundCookies;
603     WebCore::getRawCookies(document, document.cookieURL(), foundCookies);
604
605     WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidGetCookiesForFrame(callbackID, foundCookies, String()), 0);
606 }
607
608 void WebAutomationSessionProxy::deleteCookie(uint64_t pageID, uint64_t frameID, String cookieName, uint64_t callbackID)
609 {
610     WebPage* page = WebProcess::singleton().webPage(pageID);
611     if (!page) {
612         String windowNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::WindowNotFound);
613         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidDeleteCookie(callbackID, windowNotFoundErrorType), 0);
614         return;
615     }
616
617     WebFrame* frame = frameID ? WebProcess::singleton().webFrame(frameID) : page->mainWebFrame();
618     if (!frame || !frame->coreFrame() || !frame->coreFrame()->document()) {
619         String frameNotFoundErrorType = Inspector::Protocol::AutomationHelpers::getEnumConstantValue(Inspector::Protocol::Automation::ErrorMessage::FrameNotFound);
620         WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidDeleteCookie(callbackID, frameNotFoundErrorType), 0);
621         return;
622     }
623
624     auto& document = *frame->coreFrame()->document();
625     WebCore::deleteCookie(document, document.cookieURL(), cookieName);
626
627     WebProcess::singleton().parentProcessConnection()->send(Messages::WebAutomationSession::DidDeleteCookie(callbackID, String()), 0);
628 }
629
630 } // namespace WebKit