WebDriver: Implement page load strategy
[WebKit-https.git] / Source / WebDriver / Session.cpp
1 /*
2  * Copyright (C) 2017 Igalia S.L.
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 "Session.h"
28
29 #include "CommandResult.h"
30 #include "SessionHost.h"
31 #include "WebDriverAtoms.h"
32 #include <inspector/InspectorValues.h>
33 #include <wtf/CryptographicallyRandomNumber.h>
34 #include <wtf/HexNumber.h>
35 #include <wtf/UUID.h>
36
37 using namespace Inspector;
38
39 namespace WebDriver {
40
41 // The web element identifier is a constant defined by the spec in Section 11 Elements.
42 // https://www.w3.org/TR/webdriver/#elements
43 static const String webElementIdentifier = ASCIILiteral("element-6066-11e4-a52e-4f735466cecf");
44
45 Session::Session(std::unique_ptr<SessionHost>&& host)
46     : m_host(WTFMove(host))
47     , m_id(createCanonicalUUIDString())
48 {
49 }
50
51 Session::~Session()
52 {
53 }
54
55 const Capabilities& Session::capabilities() const
56 {
57     return m_host->capabilities();
58 }
59
60 void Session::close(Function<void (CommandResult&&)>&& completionHandler)
61 {
62     if (!m_toplevelBrowsingContext) {
63         completionHandler(CommandResult::success());
64         return;
65     }
66
67     RefPtr<InspectorObject> parameters = InspectorObject::create();
68     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
69     m_host->sendCommandToBackend(ASCIILiteral("closeBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
70         if (response.isError) {
71             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
72             return;
73         }
74         switchToTopLevelBrowsingContext(std::nullopt);
75         completionHandler(CommandResult::success());
76     });
77 }
78
79 void Session::setTimeouts(const Timeouts& timeouts, Function<void (CommandResult&&)>&& completionHandler)
80 {
81     if (timeouts.script)
82         m_timeouts.script = timeouts.script;
83     if (timeouts.pageLoad)
84         m_timeouts.pageLoad = timeouts.pageLoad;
85     if (timeouts.implicit)
86         m_timeouts.implicit = timeouts.implicit;
87     completionHandler(CommandResult::success());
88 }
89
90 void Session::switchToTopLevelBrowsingContext(std::optional<String> toplevelBrowsingContext)
91 {
92     m_toplevelBrowsingContext = toplevelBrowsingContext;
93     m_currentBrowsingContext = std::nullopt;
94 }
95
96 void Session::switchToBrowsingContext(std::optional<String> browsingContext)
97 {
98     // Automation sends empty strings for main frame.
99     if (!browsingContext || browsingContext.value().isEmpty())
100         m_currentBrowsingContext = std::nullopt;
101     else
102         m_currentBrowsingContext = browsingContext;
103 }
104
105 std::optional<String> Session::pageLoadStrategyString() const
106 {
107     if (!capabilities().pageLoadStrategy)
108         return std::nullopt;
109
110     switch (capabilities().pageLoadStrategy.value()) {
111     case PageLoadStrategy::None:
112         return String("None");
113     case PageLoadStrategy::Normal:
114         return String("Normal");
115     case PageLoadStrategy::Eager:
116         return String("Eager");
117     }
118
119     return std::nullopt;
120 }
121
122 void Session::createTopLevelBrowsingContext(Function<void (CommandResult&&)>&& completionHandler)
123 {
124     ASSERT(!m_toplevelBrowsingContext.value());
125     m_host->startAutomationSession(m_id, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)]() mutable {
126         m_host->sendCommandToBackend(ASCIILiteral("createBrowsingContext"), nullptr, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
127             if (response.isError || !response.responseObject) {
128                 completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
129                 return;
130             }
131             String handle;
132             if (!response.responseObject->getString(ASCIILiteral("handle"), handle)) {
133                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
134                 return;
135             }
136             switchToTopLevelBrowsingContext(handle);
137             completionHandler(CommandResult::success());
138         });
139     });
140 }
141
142 void Session::go(const String& url, Function<void (CommandResult&&)>&& completionHandler)
143 {
144     if (!m_toplevelBrowsingContext) {
145         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
146         return;
147     }
148
149     RefPtr<InspectorObject> parameters = InspectorObject::create();
150     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
151     parameters->setString(ASCIILiteral("url"), url);
152     if (m_timeouts.pageLoad)
153         parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
154     if (auto pageLoadStrategy = pageLoadStrategyString())
155         parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
156     m_host->sendCommandToBackend(ASCIILiteral("navigateBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
157         if (response.isError) {
158             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
159             return;
160         }
161         switchToBrowsingContext(std::nullopt);
162         completionHandler(CommandResult::success());
163     });
164 }
165
166 void Session::getCurrentURL(Function<void (CommandResult&&)>&& completionHandler)
167 {
168     if (!m_toplevelBrowsingContext) {
169         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
170         return;
171     }
172
173     RefPtr<InspectorObject> parameters = InspectorObject::create();
174     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
175     m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
176         if (response.isError || !response.responseObject) {
177             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
178             return;
179         }
180         RefPtr<InspectorObject> browsingContext;
181         if (!response.responseObject->getObject("context", browsingContext)) {
182             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
183             return;
184         }
185         String url;
186         if (!browsingContext->getString("url", url)) {
187             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
188             return;
189         }
190         completionHandler(CommandResult::success(InspectorValue::create(url)));
191     });
192 }
193
194 void Session::back(Function<void (CommandResult&&)>&& completionHandler)
195 {
196     if (!m_toplevelBrowsingContext) {
197         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
198         return;
199     }
200
201     RefPtr<InspectorObject> parameters = InspectorObject::create();
202     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
203     if (m_timeouts.pageLoad)
204         parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
205     if (auto pageLoadStrategy = pageLoadStrategyString())
206         parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
207     m_host->sendCommandToBackend(ASCIILiteral("goBackInBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
208         if (response.isError) {
209             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
210             return;
211         }
212         switchToBrowsingContext(std::nullopt);
213         completionHandler(CommandResult::success());
214     });
215 }
216
217 void Session::forward(Function<void (CommandResult&&)>&& completionHandler)
218 {
219     if (!m_toplevelBrowsingContext) {
220         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
221         return;
222     }
223
224     RefPtr<InspectorObject> parameters = InspectorObject::create();
225     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
226     if (m_timeouts.pageLoad)
227         parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
228     if (auto pageLoadStrategy = pageLoadStrategyString())
229         parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
230     m_host->sendCommandToBackend(ASCIILiteral("goForwardInBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
231         if (response.isError) {
232             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
233             return;
234         }
235         switchToBrowsingContext(std::nullopt);
236         completionHandler(CommandResult::success());
237     });
238 }
239
240 void Session::refresh(Function<void (CommandResult&&)>&& completionHandler)
241 {
242     if (!m_toplevelBrowsingContext) {
243         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
244         return;
245     }
246
247     RefPtr<InspectorObject> parameters = InspectorObject::create();
248     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
249     if (m_timeouts.pageLoad)
250         parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
251     if (auto pageLoadStrategy = pageLoadStrategyString())
252         parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
253     m_host->sendCommandToBackend(ASCIILiteral("reloadBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
254         if (response.isError) {
255             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
256             return;
257         }
258         switchToBrowsingContext(std::nullopt);
259         completionHandler(CommandResult::success());
260     });
261 }
262
263 void Session::getTitle(Function<void (CommandResult&&)>&& completionHandler)
264 {
265     if (!m_toplevelBrowsingContext) {
266         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
267         return;
268     }
269
270     RefPtr<InspectorObject> parameters = InspectorObject::create();
271     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
272     parameters->setString(ASCIILiteral("function"), ASCIILiteral("function() { return document.title; }"));
273     parameters->setArray(ASCIILiteral("arguments"), InspectorArray::create());
274     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
275         if (response.isError || !response.responseObject) {
276             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
277             return;
278         }
279         String valueString;
280         if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
281             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
282             return;
283         }
284         RefPtr<InspectorValue> resultValue;
285         if (!InspectorValue::parseJSON(valueString, resultValue)) {
286             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
287             return;
288         }
289         completionHandler(CommandResult::success(WTFMove(resultValue)));
290     });
291 }
292
293 void Session::getWindowHandle(Function<void (CommandResult&&)>&& completionHandler)
294 {
295     if (!m_toplevelBrowsingContext) {
296         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
297         return;
298     }
299
300     RefPtr<InspectorObject> parameters = InspectorObject::create();
301     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
302     m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
303         if (response.isError || !response.responseObject) {
304             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
305             return;
306         }
307         RefPtr<InspectorObject> browsingContext;
308         if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
309             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
310             return;
311         }
312         String handle;
313         if (!browsingContext->getString(ASCIILiteral("handle"), handle)) {
314             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
315             return;
316         }
317         completionHandler(CommandResult::success(InspectorValue::create(handle)));
318     });
319 }
320
321 void Session::closeWindow(Function<void (CommandResult&&)>&& completionHandler)
322 {
323     if (!m_toplevelBrowsingContext) {
324         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
325         return;
326     }
327
328     close(WTFMove(completionHandler));
329 }
330
331 void Session::switchToWindow(const String& windowHandle, Function<void (CommandResult&&)>&& completionHandler)
332 {
333     RefPtr<InspectorObject> parameters = InspectorObject::create();
334     parameters->setString(ASCIILiteral("browsingContextHandle"), windowHandle);
335     m_host->sendCommandToBackend(ASCIILiteral("switchToBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), windowHandle, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
336         if (response.isError) {
337             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
338             return;
339         }
340         switchToTopLevelBrowsingContext(windowHandle);
341         completionHandler(CommandResult::success());
342     });
343 }
344
345 void Session::getWindowHandles(Function<void (CommandResult&&)>&& completionHandler)
346 {
347     if (!m_toplevelBrowsingContext) {
348         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
349         return;
350     }
351
352     m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContexts"), InspectorObject::create(), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
353         if (response.isError || !response.responseObject) {
354             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
355             return;
356         }
357         RefPtr<InspectorArray> browsingContextArray;
358         if (!response.responseObject->getArray(ASCIILiteral("contexts"), browsingContextArray)) {
359             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
360             return;
361         }
362         RefPtr<InspectorArray> windowHandles = InspectorArray::create();
363         for (unsigned i = 0; i < browsingContextArray->length(); ++i) {
364             RefPtr<InspectorValue> browsingContextValue = browsingContextArray->get(i);
365             RefPtr<InspectorObject> browsingContext;
366             if (!browsingContextValue->asObject(browsingContext)) {
367                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
368                 return;
369             }
370
371             String handle;
372             if (!browsingContext->getString(ASCIILiteral("handle"), handle)) {
373                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
374                 return;
375             }
376
377             windowHandles->pushString(handle);
378         }
379         completionHandler(CommandResult::success(WTFMove(windowHandles)));
380     });
381 }
382
383 void Session::switchToFrame(RefPtr<InspectorValue>&& frameID, Function<void (CommandResult&&)>&& completionHandler)
384 {
385     if (!m_toplevelBrowsingContext) {
386         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
387         return;
388     }
389
390     if (frameID->isNull()) {
391         switchToBrowsingContext(std::nullopt);
392         completionHandler(CommandResult::success());
393         return;
394     }
395
396     RefPtr<InspectorObject> parameters = InspectorObject::create();
397     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
398     if (m_currentBrowsingContext)
399         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
400
401     int frameIndex;
402     if (frameID->asInteger(frameIndex)) {
403         if (frameIndex < 0 || frameIndex > USHRT_MAX) {
404             completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
405             return;
406         }
407         parameters->setInteger(ASCIILiteral("ordinal"), frameIndex);
408     } else {
409         String frameElementID = extractElementID(*frameID);
410         if (!frameElementID.isEmpty())
411             parameters->setString(ASCIILiteral("nodeHandle"), frameElementID);
412         else {
413             String frameName;
414             if (!frameID->asString(frameName)) {
415                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame));
416                 return;
417             }
418             parameters->setString(ASCIILiteral("name"), frameName);
419         }
420     }
421
422     m_host->sendCommandToBackend(ASCIILiteral("resolveChildFrameHandle"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
423         if (response.isError || !response.responseObject) {
424             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
425             return;
426         }
427         String frameHandle;
428         if (!response.responseObject->getString(ASCIILiteral("result"), frameHandle)) {
429             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
430             return;
431         }
432         switchToBrowsingContext(frameHandle);
433         completionHandler(CommandResult::success());
434     });
435 }
436
437 void Session::switchToParentFrame(Function<void (CommandResult&&)>&& completionHandler)
438 {
439     if (!m_toplevelBrowsingContext) {
440         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
441         return;
442     }
443
444     if (!m_currentBrowsingContext) {
445         completionHandler(CommandResult::success());
446         return;
447     }
448
449     RefPtr<InspectorObject> parameters = InspectorObject::create();
450     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
451     parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
452     m_host->sendCommandToBackend(ASCIILiteral("resolveParentFrameHandle"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
453         if (response.isError || !response.responseObject) {
454             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
455             return;
456         }
457         String frameHandle;
458         if (!response.responseObject->getString(ASCIILiteral("result"), frameHandle)) {
459             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
460             return;
461         }
462         switchToBrowsingContext(frameHandle);
463         completionHandler(CommandResult::success());
464     });
465 }
466
467 void Session::getWindowPosition(Function<void (CommandResult&&)>&& completionHandler)
468 {
469     if (!m_toplevelBrowsingContext) {
470         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
471         return;
472     }
473
474     RefPtr<InspectorObject> parameters = InspectorObject::create();
475     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
476     m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
477         if (response.isError || !response.responseObject) {
478             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
479             return;
480         }
481         RefPtr<InspectorObject> browsingContext;
482         if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
483             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
484             return;
485         }
486         RefPtr<InspectorObject> windowOrigin;
487         if (!browsingContext->getObject(ASCIILiteral("windowOrigin"), windowOrigin)) {
488             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
489             return;
490         }
491         completionHandler(CommandResult::success(WTFMove(windowOrigin)));
492     });
493 }
494
495 void Session::setWindowPosition(int windowX, int windowY, Function<void (CommandResult&&)>&& completionHandler)
496 {
497     if (!m_toplevelBrowsingContext) {
498         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
499         return;
500     }
501
502     RefPtr<InspectorObject> parameters = InspectorObject::create();
503     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
504     RefPtr<InspectorObject> windowOrigin = InspectorObject::create();
505     windowOrigin->setInteger("x", windowX);
506     windowOrigin->setInteger("y", windowY);
507     parameters->setObject(ASCIILiteral("origin"), WTFMove(windowOrigin));
508     m_host->sendCommandToBackend(ASCIILiteral("moveWindowOfBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
509         if (response.isError) {
510             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
511             return;
512         }
513         completionHandler(CommandResult::success());
514     });
515 }
516
517 void Session::getWindowSize(Function<void (CommandResult&&)>&& completionHandler)
518 {
519     if (!m_toplevelBrowsingContext) {
520         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
521         return;
522     }
523
524     RefPtr<InspectorObject> parameters = InspectorObject::create();
525     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
526     m_host->sendCommandToBackend(ASCIILiteral("getBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
527         if (response.isError || !response.responseObject) {
528             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
529             return;
530         }
531         RefPtr<InspectorObject> browsingContext;
532         if (!response.responseObject->getObject(ASCIILiteral("context"), browsingContext)) {
533             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
534             return;
535         }
536         RefPtr<InspectorObject> windowSize;
537         if (!browsingContext->getObject(ASCIILiteral("windowSize"), windowSize)) {
538             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
539             return;
540         }
541         completionHandler(CommandResult::success(WTFMove(windowSize)));
542     });
543 }
544
545 void Session::setWindowSize(int windowWidth, int windowHeight, Function<void (CommandResult&&)>&& completionHandler)
546 {
547     if (!m_toplevelBrowsingContext) {
548         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
549         return;
550     }
551
552     RefPtr<InspectorObject> parameters = InspectorObject::create();
553     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
554     RefPtr<InspectorObject> windowSize = InspectorObject::create();
555     windowSize->setInteger("width", windowWidth);
556     windowSize->setInteger("height", windowHeight);
557     parameters->setObject(ASCIILiteral("size"), WTFMove(windowSize));
558     m_host->sendCommandToBackend(ASCIILiteral("resizeWindowOfBrowsingContext"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
559         if (response.isError) {
560             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
561             return;
562         }
563         completionHandler(CommandResult::success());
564     });
565 }
566
567 RefPtr<InspectorObject> Session::createElement(RefPtr<InspectorValue>&& value)
568 {
569     RefPtr<InspectorObject> valueObject;
570     if (!value->asObject(valueObject))
571         return nullptr;
572
573     String elementID;
574     if (!valueObject->getString("session-node-" + m_id, elementID))
575         return nullptr;
576
577     RefPtr<InspectorObject> elementObject = InspectorObject::create();
578     elementObject->setString(webElementIdentifier, elementID);
579     return elementObject;
580 }
581
582 RefPtr<InspectorObject> Session::createElement(const String& elementID)
583 {
584     RefPtr<InspectorObject> elementObject = InspectorObject::create();
585     elementObject->setString("session-node-" + m_id, elementID);
586     return elementObject;
587 }
588
589 RefPtr<InspectorObject> Session::extractElement(InspectorValue& value)
590 {
591     String elementID = extractElementID(value);
592     return !elementID.isEmpty() ? createElement(elementID) : nullptr;
593 }
594
595 String Session::extractElementID(InspectorValue& value)
596 {
597     RefPtr<InspectorObject> valueObject;
598     if (!value.asObject(valueObject))
599         return emptyString();
600
601     String elementID;
602     if (!valueObject->getString(webElementIdentifier, elementID))
603         return emptyString();
604
605     return elementID;
606 }
607
608 void Session::computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption> options, Function<void (std::optional<Rect>&&, std::optional<Point>&&, bool, RefPtr<InspectorObject>&&)>&& completionHandler)
609 {
610     ASSERT(m_toplevelBrowsingContext.value());
611
612     RefPtr<InspectorObject> parameters = InspectorObject::create();
613     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
614     parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
615     parameters->setString(ASCIILiteral("nodeHandle"), elementID);
616     parameters->setBoolean(ASCIILiteral("scrollIntoViewIfNeeded"), options.contains(ElementLayoutOption::ScrollIntoViewIfNeeded));
617     parameters->setBoolean(ASCIILiteral("useViewportCoordinates"), options.contains(ElementLayoutOption::UseViewportCoordinates));
618     m_host->sendCommandToBackend(ASCIILiteral("computeElementLayout"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
619         if (response.isError || !response.responseObject) {
620             completionHandler(std::nullopt, std::nullopt, false, WTFMove(response.responseObject));
621             return;
622         }
623         RefPtr<InspectorObject> rectObject;
624         if (!response.responseObject->getObject(ASCIILiteral("rect"), rectObject)) {
625             completionHandler(std::nullopt, std::nullopt, false, nullptr);
626             return;
627         }
628         std::optional<int> elementX;
629         std::optional<int> elementY;
630         RefPtr<InspectorObject> elementPosition;
631         if (rectObject->getObject(ASCIILiteral("origin"), elementPosition)) {
632             int x, y;
633             if (elementPosition->getInteger(ASCIILiteral("x"), x) && elementPosition->getInteger(ASCIILiteral("y"), y)) {
634                 elementX = x;
635                 elementY = y;
636             }
637         }
638         if (!elementX || !elementY) {
639             completionHandler(std::nullopt, std::nullopt, false, nullptr);
640             return;
641         }
642         std::optional<int> elementWidth;
643         std::optional<int> elementHeight;
644         RefPtr<InspectorObject> elementSize;
645         if (rectObject->getObject(ASCIILiteral("size"), elementSize)) {
646             int width, height;
647             if (elementSize->getInteger(ASCIILiteral("width"), width) && elementSize->getInteger(ASCIILiteral("height"), height)) {
648                 elementWidth = width;
649                 elementHeight = height;
650             }
651         }
652         if (!elementWidth || !elementHeight) {
653             completionHandler(std::nullopt, std::nullopt, false, nullptr);
654             return;
655         }
656         Rect rect = { { elementX.value(), elementY.value() }, { elementWidth.value(), elementHeight.value() } };
657
658         bool isObscured;
659         if (!response.responseObject->getBoolean(ASCIILiteral("isObscured"), isObscured)) {
660             completionHandler(std::nullopt, std::nullopt, false, nullptr);
661             return;
662         }
663         RefPtr<InspectorObject> inViewCenterPointObject;
664         if (!response.responseObject->getObject(ASCIILiteral("inViewCenterPoint"), inViewCenterPointObject)) {
665             completionHandler(rect, std::nullopt, isObscured, nullptr);
666             return;
667         }
668         int inViewCenterPointX, inViewCenterPointY;
669         if (!inViewCenterPointObject->getInteger(ASCIILiteral("x"), inViewCenterPointX)
670             || !inViewCenterPointObject->getInteger(ASCIILiteral("y"), inViewCenterPointY)) {
671             completionHandler(std::nullopt, std::nullopt, isObscured, nullptr);
672             return;
673         }
674         Point inViewCenterPoint = { inViewCenterPointX, inViewCenterPointY };
675         completionHandler(rect, inViewCenterPoint, isObscured, nullptr);
676     });
677 }
678
679 void Session::findElements(const String& strategy, const String& selector, FindElementsMode mode, const String& rootElementID, Function<void (CommandResult&&)>&& completionHandler)
680 {
681     if (!m_toplevelBrowsingContext) {
682         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
683         return;
684     }
685
686     auto implicitWait = m_timeouts.implicit.value_or(0_s);
687     RefPtr<InspectorArray> arguments = InspectorArray::create();
688     arguments->pushString(InspectorValue::create(strategy)->toJSONString());
689     if (rootElementID.isEmpty())
690         arguments->pushString(InspectorValue::null()->toJSONString());
691     else
692         arguments->pushString(createElement(rootElementID)->toJSONString());
693     arguments->pushString(InspectorValue::create(selector)->toJSONString());
694     arguments->pushString(InspectorValue::create(mode == FindElementsMode::Single)->toJSONString());
695     arguments->pushString(InspectorValue::create(implicitWait.milliseconds())->toJSONString());
696
697     RefPtr<InspectorObject> parameters = InspectorObject::create();
698     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
699     if (m_currentBrowsingContext)
700         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
701     parameters->setString(ASCIILiteral("function"), FindNodesJavaScript);
702     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
703     parameters->setBoolean(ASCIILiteral("expectsImplicitCallbackArgument"), true);
704     // If there's an implicit wait, use one second more as callback timeout.
705     if (implicitWait)
706         parameters->setInteger(ASCIILiteral("callbackTimeout"), Seconds(implicitWait + 1_s).millisecondsAs<int>());
707
708     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), mode, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
709         if (response.isError || !response.responseObject) {
710             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
711             return;
712         }
713         String valueString;
714         if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
715             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
716             return;
717         }
718         RefPtr<InspectorValue> resultValue;
719         if (!InspectorValue::parseJSON(valueString, resultValue)) {
720             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
721             return;
722         }
723
724         switch (mode) {
725         case FindElementsMode::Single: {
726             RefPtr<InspectorObject> elementObject = createElement(WTFMove(resultValue));
727             if (!elementObject) {
728                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
729                 return;
730             }
731             completionHandler(CommandResult::success(WTFMove(elementObject)));
732             break;
733         }
734         case FindElementsMode::Multiple: {
735             RefPtr<InspectorArray> elementsArray;
736             if (!resultValue->asArray(elementsArray)) {
737                 completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement));
738                 return;
739             }
740             RefPtr<InspectorArray> elementObjectsArray = InspectorArray::create();
741             unsigned elementsArrayLength = elementsArray->length();
742             for (unsigned i = 0; i < elementsArrayLength; ++i) {
743                 if (auto elementObject = createElement(elementsArray->get(i)))
744                     elementObjectsArray->pushObject(WTFMove(elementObject));
745             }
746             completionHandler(CommandResult::success(WTFMove(elementObjectsArray)));
747             break;
748         }
749         }
750     });
751 }
752
753 void Session::isElementSelected(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
754 {
755     if (!m_toplevelBrowsingContext) {
756         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
757         return;
758     }
759
760     RefPtr<InspectorArray> arguments = InspectorArray::create();
761     arguments->pushString(createElement(elementID)->toJSONString());
762     arguments->pushString(InspectorValue::create("selected")->toJSONString());
763
764     RefPtr<InspectorObject> parameters = InspectorObject::create();
765     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
766     if (m_currentBrowsingContext)
767         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
768     parameters->setString(ASCIILiteral("function"), ElementAttributeJavaScript);
769     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
770     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
771         if (response.isError || !response.responseObject) {
772             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
773             return;
774         }
775         String valueString;
776         if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
777             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
778             return;
779         }
780         RefPtr<InspectorValue> resultValue;
781         if (!InspectorValue::parseJSON(valueString, resultValue)) {
782             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
783             return;
784         }
785         if (resultValue->isNull()) {
786             completionHandler(CommandResult::success(InspectorValue::create(false)));
787             return;
788         }
789         String booleanResult;
790         if (!resultValue->asString(booleanResult) || booleanResult != "true") {
791             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
792             return;
793         }
794         completionHandler(CommandResult::success(InspectorValue::create(true)));
795     });
796 }
797
798 void Session::getElementText(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
799 {
800     if (!m_toplevelBrowsingContext) {
801         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
802         return;
803     }
804
805     RefPtr<InspectorArray> arguments = InspectorArray::create();
806     arguments->pushString(createElement(elementID)->toJSONString());
807
808     RefPtr<InspectorObject> parameters = InspectorObject::create();
809     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
810     if (m_currentBrowsingContext)
811         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
812     // FIXME: Add an atom to properly implement this instead of just using innerText.
813     parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.innerText.replace(/^[^\\S\\xa0]+|[^\\S\\xa0]+$/g, '') }"));
814     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
815     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
816         if (response.isError || !response.responseObject) {
817             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
818             return;
819         }
820         String valueString;
821         if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
822             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
823             return;
824         }
825         RefPtr<InspectorValue> resultValue;
826         if (!InspectorValue::parseJSON(valueString, resultValue)) {
827             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
828             return;
829         }
830         completionHandler(CommandResult::success(WTFMove(resultValue)));
831     });
832 }
833
834 void Session::getElementTagName(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
835 {
836     if (!m_toplevelBrowsingContext) {
837         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
838         return;
839     }
840
841     RefPtr<InspectorArray> arguments = InspectorArray::create();
842     arguments->pushString(createElement(elementID)->toJSONString());
843
844     RefPtr<InspectorObject> parameters = InspectorObject::create();
845     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
846     if (m_currentBrowsingContext)
847         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
848     parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.tagName.toLowerCase() }"));
849     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
850     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
851         if (response.isError || !response.responseObject) {
852             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
853             return;
854         }
855         String valueString;
856         if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
857             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
858             return;
859         }
860         RefPtr<InspectorValue> resultValue;
861         if (!InspectorValue::parseJSON(valueString, resultValue)) {
862             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
863             return;
864         }
865         completionHandler(CommandResult::success(WTFMove(resultValue)));
866     });
867 }
868
869 void Session::getElementRect(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
870 {
871     if (!m_toplevelBrowsingContext) {
872         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
873         return;
874     }
875
876     computeElementLayout(elementID, { }, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&&, bool, RefPtr<InspectorObject>&& error) {
877         if (!rect || error) {
878             completionHandler(CommandResult::fail(WTFMove(error)));
879             return;
880         }
881         RefPtr<InspectorObject> rectObject = InspectorObject::create();
882         rectObject->setInteger(ASCIILiteral("x"), rect.value().origin.x);
883         rectObject->setInteger(ASCIILiteral("y"), rect.value().origin.y);
884         rectObject->setInteger(ASCIILiteral("width"), rect.value().size.width);
885         rectObject->setInteger(ASCIILiteral("height"), rect.value().size.height);
886         completionHandler(CommandResult::success(WTFMove(rectObject)));
887     });
888 }
889
890 void Session::isElementEnabled(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
891 {
892     if (!m_toplevelBrowsingContext) {
893         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
894         return;
895     }
896
897     RefPtr<InspectorArray> arguments = InspectorArray::create();
898     arguments->pushString(createElement(elementID)->toJSONString());
899
900     RefPtr<InspectorObject> parameters = InspectorObject::create();
901     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
902     if (m_currentBrowsingContext)
903         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
904     parameters->setString(ASCIILiteral("function"), ASCIILiteral("function(element) { return element.disabled === undefined ? true : !element.disabled }"));
905     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
906     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
907         if (response.isError || !response.responseObject) {
908             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
909             return;
910         }
911         String valueString;
912         if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
913             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
914             return;
915         }
916         RefPtr<InspectorValue> resultValue;
917         if (!InspectorValue::parseJSON(valueString, resultValue)) {
918             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
919             return;
920         }
921         completionHandler(CommandResult::success(WTFMove(resultValue)));
922     });
923 }
924
925 void Session::isElementDisplayed(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
926 {
927     if (!m_toplevelBrowsingContext) {
928         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
929         return;
930     }
931
932     RefPtr<InspectorArray> arguments = InspectorArray::create();
933     arguments->pushString(createElement(elementID)->toJSONString());
934
935     RefPtr<InspectorObject> parameters = InspectorObject::create();
936     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
937     if (m_currentBrowsingContext)
938         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
939     parameters->setString(ASCIILiteral("function"), ElementDisplayedJavaScript);
940     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
941     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
942         if (response.isError || !response.responseObject) {
943             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
944             return;
945         }
946         String valueString;
947         if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
948             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
949             return;
950         }
951         RefPtr<InspectorValue> resultValue;
952         if (!InspectorValue::parseJSON(valueString, resultValue)) {
953             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
954             return;
955         }
956         completionHandler(CommandResult::success(WTFMove(resultValue)));
957     });
958 }
959
960 void Session::getElementAttribute(const String& elementID, const String& attribute, Function<void (CommandResult&&)>&& completionHandler)
961 {
962     if (!m_toplevelBrowsingContext) {
963         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
964         return;
965     }
966
967     RefPtr<InspectorArray> arguments = InspectorArray::create();
968     arguments->pushString(createElement(elementID)->toJSONString());
969     arguments->pushString(InspectorValue::create(attribute)->toJSONString());
970
971     RefPtr<InspectorObject> parameters = InspectorObject::create();
972     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
973     if (m_currentBrowsingContext)
974         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
975     parameters->setString(ASCIILiteral("function"), ElementAttributeJavaScript);
976     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
977     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
978         if (response.isError || !response.responseObject) {
979             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
980             return;
981         }
982         String valueString;
983         if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
984             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
985             return;
986         }
987         RefPtr<InspectorValue> resultValue;
988         if (!InspectorValue::parseJSON(valueString, resultValue)) {
989             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
990             return;
991         }
992         completionHandler(CommandResult::success(WTFMove(resultValue)));
993     });
994 }
995
996 void Session::waitForNavigationToComplete(Function<void (CommandResult&&)>&& completionHandler)
997 {
998     if (!m_toplevelBrowsingContext) {
999         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1000         return;
1001     }
1002
1003     RefPtr<InspectorObject> parameters = InspectorObject::create();
1004     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1005     if (m_currentBrowsingContext)
1006         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1007     if (m_timeouts.pageLoad)
1008         parameters->setInteger(ASCIILiteral("pageLoadTimeout"), m_timeouts.pageLoad.value().millisecondsAs<int>());
1009     if (auto pageLoadStrategy = pageLoadStrategyString())
1010         parameters->setString(ASCIILiteral("pageLoadStrategy"), pageLoadStrategy.value());
1011     m_host->sendCommandToBackend(ASCIILiteral("waitForNavigationToComplete"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1012         if (response.isError) {
1013             auto result = CommandResult::fail(WTFMove(response.responseObject));
1014             if (result.errorCode() == CommandResult::ErrorCode::NoSuchFrame) {
1015                 // Navigation destroyed the current frame, switch to top level browsing context and ignore the error.
1016                 switchToBrowsingContext(std::nullopt);
1017             } else {
1018                 completionHandler(WTFMove(result));
1019                 return;
1020             }
1021         }
1022         completionHandler(CommandResult::success());
1023     });
1024 }
1025
1026 void Session::elementClick(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1027 {
1028     if (!m_toplevelBrowsingContext) {
1029         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1030         return;
1031     }
1032
1033     OptionSet<ElementLayoutOption> options = ElementLayoutOption::ScrollIntoViewIfNeeded;
1034     options |= ElementLayoutOption::UseViewportCoordinates;
1035     computeElementLayout(elementID, options, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](std::optional<Rect>&& rect, std::optional<Point>&& inViewCenter, bool isObscured, RefPtr<InspectorObject>&& error) mutable {
1036         if (!rect || error) {
1037             completionHandler(CommandResult::fail(WTFMove(error)));
1038             return;
1039         }
1040         if (isObscured) {
1041             completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted));
1042             return;
1043         }
1044         if (!inViewCenter) {
1045             completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable));
1046             return;
1047         }
1048
1049         performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(completionHandler));
1050
1051         waitForNavigationToComplete(WTFMove(completionHandler));
1052     });
1053 }
1054
1055 void Session::elementClear(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1056 {
1057     if (!m_toplevelBrowsingContext) {
1058         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1059         return;
1060     }
1061
1062     RefPtr<InspectorArray> arguments = InspectorArray::create();
1063     arguments->pushString(createElement(elementID)->toJSONString());
1064
1065     RefPtr<InspectorObject> parameters = InspectorObject::create();
1066     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1067     if (m_currentBrowsingContext)
1068         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1069     parameters->setString(ASCIILiteral("function"), FormElementClearJavaScript);
1070     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1071     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1072         if (response.isError) {
1073             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1074             return;
1075         }
1076         completionHandler(CommandResult::success());
1077     });
1078 }
1079
1080 String Session::virtualKeyForKeySequence(const String& keySequence, KeyModifier& modifier)
1081 {
1082     // ยง17.4.2 Keyboard Actions.
1083     // https://www.w3.org/TR/webdriver/#keyboard-actions
1084     modifier = KeyModifier::None;
1085     switch (keySequence[0]) {
1086     case 0xE001U:
1087         return ASCIILiteral("Cancel");
1088     case 0xE002U:
1089         return ASCIILiteral("Help");
1090     case 0xE003U:
1091         return ASCIILiteral("Backspace");
1092     case 0xE004U:
1093         return ASCIILiteral("Tab");
1094     case 0xE005U:
1095         return ASCIILiteral("Clear");
1096     case 0xE006U:
1097         return ASCIILiteral("Return");
1098     case 0xE007U:
1099         return ASCIILiteral("Enter");
1100     case 0xE008U:
1101         modifier = KeyModifier::Shift;
1102         return ASCIILiteral("Shift");
1103     case 0xE009U:
1104         modifier = KeyModifier::Control;
1105         return ASCIILiteral("Control");
1106     case 0xE00AU:
1107         modifier = KeyModifier::Alternate;
1108         return ASCIILiteral("Alternate");
1109     case 0xE00BU:
1110         return ASCIILiteral("Pause");
1111     case 0xE00CU:
1112         return ASCIILiteral("Escape");
1113     case 0xE00DU:
1114         return ASCIILiteral("Space");
1115     case 0xE00EU:
1116         return ASCIILiteral("PageUp");
1117     case 0xE00FU:
1118         return ASCIILiteral("PageDown");
1119     case 0xE010U:
1120         return ASCIILiteral("End");
1121     case 0xE011U:
1122         return ASCIILiteral("Home");
1123     case 0xE012U:
1124         return ASCIILiteral("LeftArrow");
1125     case 0xE013U:
1126         return ASCIILiteral("UpArrow");
1127     case 0xE014U:
1128         return ASCIILiteral("RightArrow");
1129     case 0xE015U:
1130         return ASCIILiteral("DownArrow");
1131     case 0xE016U:
1132         return ASCIILiteral("Insert");
1133     case 0xE017U:
1134         return ASCIILiteral("Delete");
1135     case 0xE018U:
1136         return ASCIILiteral("Semicolon");
1137     case 0xE019U:
1138         return ASCIILiteral("Equals");
1139     case 0xE01AU:
1140         return ASCIILiteral("NumberPad0");
1141     case 0xE01BU:
1142         return ASCIILiteral("NumberPad1");
1143     case 0xE01CU:
1144         return ASCIILiteral("NumberPad2");
1145     case 0xE01DU:
1146         return ASCIILiteral("NumberPad3");
1147     case 0xE01EU:
1148         return ASCIILiteral("NumberPad4");
1149     case 0xE01FU:
1150         return ASCIILiteral("NumberPad5");
1151     case 0xE020U:
1152         return ASCIILiteral("NumberPad6");
1153     case 0xE021U:
1154         return ASCIILiteral("NumberPad7");
1155     case 0xE022U:
1156         return ASCIILiteral("NumberPad8");
1157     case 0xE023U:
1158         return ASCIILiteral("NumberPad9");
1159     case 0xE024U:
1160         return ASCIILiteral("NumberPadMultiply");
1161     case 0xE025U:
1162         return ASCIILiteral("NumberPadAdd");
1163     case 0xE026U:
1164         return ASCIILiteral("NumberPadSeparator");
1165     case 0xE027U:
1166         return ASCIILiteral("NumberPadSubtract");
1167     case 0xE028U:
1168         return ASCIILiteral("NumberPadDecimal");
1169     case 0xE029U:
1170         return ASCIILiteral("NumberPadDivide");
1171     case 0xE031U:
1172         return ASCIILiteral("Function1");
1173     case 0xE032U:
1174         return ASCIILiteral("Function2");
1175     case 0xE033U:
1176         return ASCIILiteral("Function3");
1177     case 0xE034U:
1178         return ASCIILiteral("Function4");
1179     case 0xE035U:
1180         return ASCIILiteral("Function5");
1181     case 0xE036U:
1182         return ASCIILiteral("Function6");
1183     case 0xE037U:
1184         return ASCIILiteral("Function7");
1185     case 0xE038U:
1186         return ASCIILiteral("Function8");
1187     case 0xE039U:
1188         return ASCIILiteral("Function9");
1189     case 0xE03AU:
1190         return ASCIILiteral("Function10");
1191     case 0xE03BU:
1192         return ASCIILiteral("Function11");
1193     case 0xE03CU:
1194         return ASCIILiteral("Function12");
1195     case 0xE03DU:
1196         modifier = KeyModifier::Meta;
1197         return ASCIILiteral("Meta");
1198     default:
1199         break;
1200     }
1201     return String();
1202 }
1203
1204 void Session::elementSendKeys(const String& elementID, Vector<String>&& keys, Function<void (CommandResult&&)>&& completionHandler)
1205 {
1206     if (!m_toplevelBrowsingContext) {
1207         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1208         return;
1209     }
1210
1211     // FIXME: move this to an atom.
1212     static const char focusScript[] =
1213         "function focus(element) {"
1214         "    var doc = element.ownerDocument || element;"
1215         "    var prevActiveElement = doc.activeElement;"
1216         "    if (element != prevActiveElement && prevActiveElement)"
1217         "        prevActiveElement.blur();"
1218         "    element.focus();"
1219         "    if (element != prevActiveElement && element.value && element.value.length && element.setSelectionRange)"
1220         "        element.setSelectionRange(element.value.length, element.value.length);"
1221         "    if (element != doc.activeElement)"
1222         "        throw new Error('cannot focus element');"
1223         "}";
1224
1225     RefPtr<InspectorArray> arguments = InspectorArray::create();
1226     arguments->pushString(createElement(elementID)->toJSONString());
1227     RefPtr<InspectorObject> parameters = InspectorObject::create();
1228     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1229     if (m_currentBrowsingContext)
1230         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1231     parameters->setString(ASCIILiteral("function"), focusScript);
1232     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1233     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), keys = WTFMove(keys), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable {
1234         if (response.isError || !response.responseObject) {
1235             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1236             return;
1237         }
1238
1239         unsigned stickyModifiers = 0;
1240         Vector<KeyboardInteraction> interactions;
1241         interactions.reserveInitialCapacity(keys.size());
1242         for (const auto& key : keys) {
1243             KeyboardInteraction interaction;
1244             KeyModifier modifier;
1245             auto virtualKey = virtualKeyForKeySequence(key, modifier);
1246             if (!virtualKey.isNull()) {
1247                 interaction.key = virtualKey;
1248                 if (modifier != KeyModifier::None) {
1249                     stickyModifiers ^= modifier;
1250                     if (stickyModifiers & modifier)
1251                         interaction.type = KeyboardInteractionType::KeyPress;
1252                     else
1253                         interaction.type = KeyboardInteractionType::KeyRelease;
1254                 }
1255             } else
1256                 interaction.text = key;
1257             interactions.uncheckedAppend(WTFMove(interaction));
1258         }
1259
1260         // Reset sticky modifiers if needed.
1261         if (stickyModifiers) {
1262             if (stickyModifiers & KeyModifier::Shift)
1263                 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Shift")) });
1264             if (stickyModifiers & KeyModifier::Control)
1265                 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Control")) });
1266             if (stickyModifiers & KeyModifier::Alternate)
1267                 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Alternate")) });
1268             if (stickyModifiers & KeyModifier::Meta)
1269                 interactions.append({ KeyboardInteractionType::KeyRelease, std::nullopt, std::optional<String>(ASCIILiteral("Meta")) });
1270         }
1271
1272         performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler));
1273     });
1274 }
1275
1276 void Session::elementSubmit(const String& elementID, Function<void (CommandResult&&)>&& completionHandler)
1277 {
1278     if (!m_toplevelBrowsingContext) {
1279         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1280         return;
1281     }
1282
1283     RefPtr<InspectorArray> arguments = InspectorArray::create();
1284     arguments->pushString(createElement(elementID)->toJSONString());
1285
1286     RefPtr<InspectorObject> parameters = InspectorObject::create();
1287     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1288     if (m_currentBrowsingContext)
1289         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1290     parameters->setString(ASCIILiteral("function"), FormSubmitJavaScript);
1291     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1292     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1293         if (response.isError) {
1294             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1295             return;
1296         }
1297         completionHandler(CommandResult::success());
1298     });
1299 }
1300
1301 RefPtr<InspectorValue> Session::handleScriptResult(RefPtr<InspectorValue>&& resultValue)
1302 {
1303     RefPtr<InspectorArray> resultArray;
1304     if (resultValue->asArray(resultArray)) {
1305         RefPtr<InspectorArray> returnValueArray = InspectorArray::create();
1306         unsigned resultArrayLength = resultArray->length();
1307         for (unsigned i = 0; i < resultArrayLength; ++i)
1308             returnValueArray->pushValue(handleScriptResult(resultArray->get(i)));
1309         return returnValueArray;
1310     }
1311
1312     if (auto element = createElement(RefPtr<InspectorValue>(resultValue)))
1313         return element;
1314
1315     RefPtr<InspectorObject> resultObject;
1316     if (resultValue->asObject(resultObject)) {
1317         RefPtr<InspectorObject> returnValueObject = InspectorObject::create();
1318         auto end = resultObject->end();
1319         for (auto it = resultObject->begin(); it != end; ++it)
1320             returnValueObject->setValue(it->key, handleScriptResult(WTFMove(it->value)));
1321         return returnValueObject;
1322     }
1323
1324     return resultValue;
1325 }
1326
1327 void Session::executeScript(const String& script, RefPtr<InspectorArray>&& argumentsArray, ExecuteScriptMode mode, Function<void (CommandResult&&)>&& completionHandler)
1328 {
1329     if (!m_toplevelBrowsingContext) {
1330         completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow));
1331         return;
1332     }
1333
1334     RefPtr<InspectorArray> arguments = InspectorArray::create();
1335     unsigned argumentsLength = argumentsArray->length();
1336     for (unsigned i = 0; i < argumentsLength; ++i) {
1337         if (auto argument = argumentsArray->get(i)) {
1338             if (auto element = extractElement(*argument))
1339                 arguments->pushString(element->toJSONString());
1340             else
1341                 arguments->pushString(argument->toJSONString());
1342         }
1343     }
1344
1345     RefPtr<InspectorObject> parameters = InspectorObject::create();
1346     parameters->setString(ASCIILiteral("browsingContextHandle"), m_toplevelBrowsingContext.value());
1347     if (m_currentBrowsingContext)
1348         parameters->setString(ASCIILiteral("frameHandle"), m_currentBrowsingContext.value());
1349     parameters->setString(ASCIILiteral("function"), "function(){" + script + '}');
1350     parameters->setArray(ASCIILiteral("arguments"), WTFMove(arguments));
1351     if (mode == ExecuteScriptMode::Async) {
1352         parameters->setBoolean(ASCIILiteral("expectsImplicitCallbackArgument"), true);
1353         if (m_timeouts.script)
1354             parameters->setInteger(ASCIILiteral("callbackTimeout"), m_timeouts.script.value().millisecondsAs<int>());
1355     }
1356     m_host->sendCommandToBackend(ASCIILiteral("evaluateJavaScriptFunction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1357         if (response.isError || !response.responseObject) {
1358             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1359             return;
1360         }
1361         String valueString;
1362         if (!response.responseObject->getString(ASCIILiteral("result"), valueString)) {
1363             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1364             return;
1365         }
1366         if (valueString.isEmpty()) {
1367             completionHandler(CommandResult::success());
1368             return;
1369         }
1370         RefPtr<InspectorValue> resultValue;
1371         if (!InspectorValue::parseJSON(valueString, resultValue)) {
1372             completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError));
1373             return;
1374         }
1375         completionHandler(CommandResult::success(handleScriptResult(WTFMove(resultValue))));
1376     });
1377 }
1378
1379 void Session::performMouseInteraction(int x, int y, MouseButton button, MouseInteraction interaction, Function<void (CommandResult&&)>&& completionHandler)
1380 {
1381     RefPtr<InspectorObject> parameters = InspectorObject::create();
1382     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
1383     RefPtr<InspectorObject> position = InspectorObject::create();
1384     position->setInteger(ASCIILiteral("x"), x);
1385     position->setInteger(ASCIILiteral("y"), y);
1386     parameters->setObject(ASCIILiteral("position"), WTFMove(position));
1387     switch (button) {
1388     case MouseButton::None:
1389         parameters->setString(ASCIILiteral("button"), ASCIILiteral("None"));
1390         break;
1391     case MouseButton::Left:
1392         parameters->setString(ASCIILiteral("button"), ASCIILiteral("Left"));
1393         break;
1394     case MouseButton::Middle:
1395         parameters->setString(ASCIILiteral("button"), ASCIILiteral("Middle"));
1396         break;
1397     case MouseButton::Right:
1398         parameters->setString(ASCIILiteral("button"), ASCIILiteral("Right"));
1399         break;
1400     }
1401     switch (interaction) {
1402     case MouseInteraction::Move:
1403         parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Move"));
1404         break;
1405     case MouseInteraction::Down:
1406         parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Down"));
1407         break;
1408     case MouseInteraction::Up:
1409         parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("Up"));
1410         break;
1411     case MouseInteraction::SingleClick:
1412         parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("SingleClick"));
1413         break;
1414     case MouseInteraction::DoubleClick:
1415         parameters->setString(ASCIILiteral("interaction"), ASCIILiteral("DoubleClick"));
1416         break;
1417     }
1418     parameters->setArray(ASCIILiteral("modifiers"), InspectorArray::create());
1419     m_host->sendCommandToBackend(ASCIILiteral("performMouseInteraction"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1420         if (response.isError) {
1421             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1422             return;
1423         }
1424         completionHandler(CommandResult::success());
1425     });
1426 }
1427
1428 void Session::performKeyboardInteractions(Vector<KeyboardInteraction>&& interactions, Function<void (CommandResult&&)>&& completionHandler)
1429 {
1430     RefPtr<InspectorObject> parameters = InspectorObject::create();
1431     parameters->setString(ASCIILiteral("handle"), m_toplevelBrowsingContext.value());
1432     RefPtr<InspectorArray> interactionsArray = InspectorArray::create();
1433     for (const auto& interaction : interactions) {
1434         RefPtr<InspectorObject> interactionObject = InspectorObject::create();
1435         switch (interaction.type) {
1436         case KeyboardInteractionType::KeyPress:
1437             interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("KeyPress"));
1438             break;
1439         case KeyboardInteractionType::KeyRelease:
1440             interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("KeyRelease"));
1441             break;
1442         case KeyboardInteractionType::InsertByKey:
1443             interactionObject->setString(ASCIILiteral("type"), ASCIILiteral("InsertByKey"));
1444             break;
1445         }
1446         if (interaction.key)
1447             interactionObject->setString(ASCIILiteral("key"), interaction.key.value());
1448         if (interaction.text)
1449             interactionObject->setString(ASCIILiteral("text"), interaction.text.value());
1450         interactionsArray->pushObject(WTFMove(interactionObject));
1451     }
1452     parameters->setArray(ASCIILiteral("interactions"), WTFMove(interactionsArray));
1453     m_host->sendCommandToBackend(ASCIILiteral("performKeyboardInteractions"), WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) {
1454         if (response.isError) {
1455             completionHandler(CommandResult::fail(WTFMove(response.responseObject)));
1456             return;
1457         }
1458         completionHandler(CommandResult::success());
1459     });
1460 }
1461
1462 } // namespace WebDriver