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