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