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 <wtf/CryptographicallyRandomNumber.h> |
33 | #include <wtf/HashSet.h> |
34 | #include <wtf/HexNumber.h> |
35 | #include <wtf/NeverDestroyed.h> |
36 | |
37 | namespace WebDriver { |
38 | |
39 | // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-session-script-timeout |
40 | static const Seconds defaultScriptTimeout = 30_s; |
41 | // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-session-page-load-timeout |
42 | static const Seconds defaultPageLoadTimeout = 300_s; |
43 | // https://w3c.github.io/webdriver/webdriver-spec.html#dfn-session-implicit-wait-timeout |
44 | static const Seconds defaultImplicitWaitTimeout = 0_s; |
45 | |
46 | const String& Session::webElementIdentifier() |
47 | { |
48 | // The web element identifier is a constant defined by the spec in Section 11 Elements. |
49 | // https://www.w3.org/TR/webdriver/#elements |
50 | static NeverDestroyed<String> webElementID { "element-6066-11e4-a52e-4f735466cecf"_s }; |
51 | return webElementID; |
52 | } |
53 | |
54 | Session::Session(std::unique_ptr<SessionHost>&& host) |
55 | : m_host(WTFMove(host)) |
56 | , m_scriptTimeout(defaultScriptTimeout) |
57 | , m_pageLoadTimeout(defaultPageLoadTimeout) |
58 | , m_implicitWaitTimeout(defaultImplicitWaitTimeout) |
59 | { |
60 | if (capabilities().timeouts) |
61 | setTimeouts(capabilities().timeouts.value(), [](CommandResult&&) { }); |
62 | } |
63 | |
64 | Session::~Session() |
65 | { |
66 | } |
67 | |
68 | const String& Session::id() const |
69 | { |
70 | return m_host->sessionID(); |
71 | } |
72 | |
73 | const Capabilities& Session::capabilities() const |
74 | { |
75 | return m_host->capabilities(); |
76 | } |
77 | |
78 | bool Session::isConnected() const |
79 | { |
80 | return m_host->isConnected(); |
81 | } |
82 | |
83 | static Optional<String> firstWindowHandleInResult(JSON::Value& result) |
84 | { |
85 | RefPtr<JSON::Array> handles; |
86 | if (result.asArray(handles) && handles->length()) { |
87 | auto handleValue = handles->get(0); |
88 | String handle; |
89 | if (handleValue->asString(handle)) |
90 | return handle; |
91 | } |
92 | return WTF::nullopt; |
93 | } |
94 | |
95 | void Session::closeAllToplevelBrowsingContexts(const String& toplevelBrowsingContext, Function<void (CommandResult&&)>&& completionHandler) |
96 | { |
97 | closeTopLevelBrowsingContext(toplevelBrowsingContext, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
98 | if (result.isError()) { |
99 | completionHandler(WTFMove(result)); |
100 | return; |
101 | } |
102 | if (auto handle = firstWindowHandleInResult(*result.result())) { |
103 | closeAllToplevelBrowsingContexts(handle.value(), WTFMove(completionHandler)); |
104 | return; |
105 | } |
106 | completionHandler(CommandResult::success()); |
107 | }); |
108 | } |
109 | |
110 | void Session::close(Function<void (CommandResult&&)>&& completionHandler) |
111 | { |
112 | m_toplevelBrowsingContext = WTF::nullopt; |
113 | getWindowHandles([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
114 | if (result.isError()) { |
115 | completionHandler(WTFMove(result)); |
116 | return; |
117 | } |
118 | if (auto handle = firstWindowHandleInResult(*result.result())) { |
119 | closeAllToplevelBrowsingContexts(handle.value(), WTFMove(completionHandler)); |
120 | return; |
121 | } |
122 | completionHandler(CommandResult::success()); |
123 | }); |
124 | } |
125 | |
126 | void Session::getTimeouts(Function<void (CommandResult&&)>&& completionHandler) |
127 | { |
128 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
129 | parameters->setInteger("script"_s , m_scriptTimeout.millisecondsAs<int>()); |
130 | parameters->setInteger("pageLoad"_s , m_pageLoadTimeout.millisecondsAs<int>()); |
131 | parameters->setInteger("implicit"_s , m_implicitWaitTimeout.millisecondsAs<int>()); |
132 | completionHandler(CommandResult::success(WTFMove(parameters))); |
133 | } |
134 | |
135 | void Session::setTimeouts(const Timeouts& timeouts, Function<void (CommandResult&&)>&& completionHandler) |
136 | { |
137 | if (timeouts.script) |
138 | m_scriptTimeout = timeouts.script.value(); |
139 | if (timeouts.pageLoad) |
140 | m_pageLoadTimeout = timeouts.pageLoad.value(); |
141 | if (timeouts.implicit) |
142 | m_implicitWaitTimeout = timeouts.implicit.value(); |
143 | completionHandler(CommandResult::success()); |
144 | } |
145 | |
146 | void Session::switchToTopLevelBrowsingContext(Optional<String> toplevelBrowsingContext) |
147 | { |
148 | m_toplevelBrowsingContext = toplevelBrowsingContext; |
149 | m_currentBrowsingContext = WTF::nullopt; |
150 | } |
151 | |
152 | void Session::switchToBrowsingContext(Optional<String> browsingContext) |
153 | { |
154 | // Automation sends empty strings for main frame. |
155 | if (!browsingContext || browsingContext.value().isEmpty()) |
156 | m_currentBrowsingContext = WTF::nullopt; |
157 | else |
158 | m_currentBrowsingContext = browsingContext; |
159 | } |
160 | |
161 | Optional<String> Session::pageLoadStrategyString() const |
162 | { |
163 | if (!capabilities().pageLoadStrategy) |
164 | return WTF::nullopt; |
165 | |
166 | switch (capabilities().pageLoadStrategy.value()) { |
167 | case PageLoadStrategy::None: |
168 | return String("None" ); |
169 | case PageLoadStrategy::Normal: |
170 | return String("Normal" ); |
171 | case PageLoadStrategy::Eager: |
172 | return String("Eager" ); |
173 | } |
174 | |
175 | return WTF::nullopt; |
176 | } |
177 | |
178 | void Session::createTopLevelBrowsingContext(Function<void (CommandResult&&)>&& completionHandler) |
179 | { |
180 | ASSERT(!m_toplevelBrowsingContext); |
181 | m_host->sendCommandToBackend("createBrowsingContext"_s , nullptr, [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { |
182 | if (response.isError || !response.responseObject) { |
183 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
184 | return; |
185 | } |
186 | String handle; |
187 | if (!response.responseObject->getString("handle"_s , handle)) { |
188 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
189 | return; |
190 | } |
191 | switchToTopLevelBrowsingContext(handle); |
192 | completionHandler(CommandResult::success()); |
193 | }); |
194 | } |
195 | |
196 | void Session::handleUserPrompts(Function<void (CommandResult&&)>&& completionHandler) |
197 | { |
198 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
199 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
200 | m_host->sendCommandToBackend("isShowingJavaScriptDialog"_s , WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { |
201 | if (response.isError || !response.responseObject) { |
202 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
203 | return; |
204 | } |
205 | bool isShowingJavaScriptDialog; |
206 | if (!response.responseObject->getBoolean("result" , isShowingJavaScriptDialog)) { |
207 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
208 | return; |
209 | } |
210 | |
211 | if (!isShowingJavaScriptDialog) { |
212 | completionHandler(CommandResult::success()); |
213 | return; |
214 | } |
215 | |
216 | handleUnexpectedAlertOpen(WTFMove(completionHandler)); |
217 | }); |
218 | } |
219 | |
220 | void Session::handleUnexpectedAlertOpen(Function<void (CommandResult&&)>&& completionHandler) |
221 | { |
222 | switch (capabilities().unhandledPromptBehavior.valueOr(UnhandledPromptBehavior::DismissAndNotify)) { |
223 | case UnhandledPromptBehavior::Dismiss: |
224 | dismissAlert(WTFMove(completionHandler)); |
225 | break; |
226 | case UnhandledPromptBehavior::Accept: |
227 | acceptAlert(WTFMove(completionHandler)); |
228 | break; |
229 | case UnhandledPromptBehavior::DismissAndNotify: |
230 | dismissAndNotifyAlert(WTFMove(completionHandler)); |
231 | break; |
232 | case UnhandledPromptBehavior::AcceptAndNotify: |
233 | acceptAndNotifyAlert(WTFMove(completionHandler)); |
234 | break; |
235 | case UnhandledPromptBehavior::Ignore: |
236 | reportUnexpectedAlertOpen(WTFMove(completionHandler)); |
237 | break; |
238 | } |
239 | } |
240 | |
241 | void Session::dismissAndNotifyAlert(Function<void (CommandResult&&)>&& completionHandler) |
242 | { |
243 | reportUnexpectedAlertOpen([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
244 | dismissAlert([errorResult = WTFMove(result), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
245 | if (result.isError()) { |
246 | completionHandler(WTFMove(result)); |
247 | return; |
248 | } |
249 | completionHandler(WTFMove(errorResult)); |
250 | }); |
251 | }); |
252 | } |
253 | |
254 | void Session::acceptAndNotifyAlert(Function<void (CommandResult&&)>&& completionHandler) |
255 | { |
256 | reportUnexpectedAlertOpen([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
257 | acceptAlert([errorResult = WTFMove(result), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
258 | if (result.isError()) { |
259 | completionHandler(WTFMove(result)); |
260 | return; |
261 | } |
262 | completionHandler(WTFMove(errorResult)); |
263 | }); |
264 | }); |
265 | } |
266 | |
267 | void Session::reportUnexpectedAlertOpen(Function<void (CommandResult&&)>&& completionHandler) |
268 | { |
269 | getAlertText([completionHandler = WTFMove(completionHandler)](CommandResult&& result) { |
270 | Optional<String> alertText; |
271 | if (!result.isError()) { |
272 | String valueString; |
273 | if (result.result()->asString(valueString)) |
274 | alertText = valueString; |
275 | } |
276 | auto errorResult = CommandResult::fail(CommandResult::ErrorCode::UnexpectedAlertOpen); |
277 | if (alertText) { |
278 | RefPtr<JSON::Object> additonalData = JSON::Object::create(); |
279 | additonalData->setString("text"_s , alertText.value()); |
280 | errorResult.setAdditionalErrorData(WTFMove(additonalData)); |
281 | } |
282 | completionHandler(WTFMove(errorResult)); |
283 | }); |
284 | } |
285 | |
286 | void Session::go(const String& url, Function<void (CommandResult&&)>&& completionHandler) |
287 | { |
288 | if (!m_toplevelBrowsingContext) { |
289 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
290 | return; |
291 | } |
292 | |
293 | handleUserPrompts([this, protectedThis = makeRef(*this), url, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
294 | if (result.isError()) { |
295 | completionHandler(WTFMove(result)); |
296 | return; |
297 | } |
298 | |
299 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
300 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
301 | parameters->setString("url"_s , url); |
302 | parameters->setInteger("pageLoadTimeout"_s , m_pageLoadTimeout.millisecondsAs<int>()); |
303 | if (auto pageLoadStrategy = pageLoadStrategyString()) |
304 | parameters->setString("pageLoadStrategy"_s , pageLoadStrategy.value()); |
305 | m_host->sendCommandToBackend("navigateBrowsingContext"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
306 | if (response.isError) { |
307 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
308 | return; |
309 | } |
310 | switchToBrowsingContext(WTF::nullopt); |
311 | completionHandler(CommandResult::success()); |
312 | }); |
313 | }); |
314 | } |
315 | |
316 | void Session::getCurrentURL(Function<void (CommandResult&&)>&& completionHandler) |
317 | { |
318 | if (!m_toplevelBrowsingContext) { |
319 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
320 | return; |
321 | } |
322 | |
323 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
324 | if (result.isError()) { |
325 | completionHandler(WTFMove(result)); |
326 | return; |
327 | } |
328 | |
329 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
330 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
331 | m_host->sendCommandToBackend("getBrowsingContext"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
332 | if (response.isError || !response.responseObject) { |
333 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
334 | return; |
335 | } |
336 | RefPtr<JSON::Object> browsingContext; |
337 | if (!response.responseObject->getObject("context" , browsingContext)) { |
338 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
339 | return; |
340 | } |
341 | String url; |
342 | if (!browsingContext->getString("url" , url)) { |
343 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
344 | return; |
345 | } |
346 | completionHandler(CommandResult::success(JSON::Value::create(url))); |
347 | }); |
348 | }); |
349 | } |
350 | |
351 | void Session::back(Function<void (CommandResult&&)>&& completionHandler) |
352 | { |
353 | if (!m_toplevelBrowsingContext) { |
354 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
355 | return; |
356 | } |
357 | |
358 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
359 | if (result.isError()) { |
360 | completionHandler(WTFMove(result)); |
361 | return; |
362 | } |
363 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
364 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
365 | parameters->setInteger("pageLoadTimeout"_s , m_pageLoadTimeout.millisecondsAs<int>()); |
366 | if (auto pageLoadStrategy = pageLoadStrategyString()) |
367 | parameters->setString("pageLoadStrategy"_s , pageLoadStrategy.value()); |
368 | m_host->sendCommandToBackend("goBackInBrowsingContext"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
369 | if (response.isError) { |
370 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
371 | return; |
372 | } |
373 | switchToBrowsingContext(WTF::nullopt); |
374 | completionHandler(CommandResult::success()); |
375 | }); |
376 | }); |
377 | } |
378 | |
379 | void Session::forward(Function<void (CommandResult&&)>&& completionHandler) |
380 | { |
381 | if (!m_toplevelBrowsingContext) { |
382 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
383 | return; |
384 | } |
385 | |
386 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
387 | if (result.isError()) { |
388 | completionHandler(WTFMove(result)); |
389 | return; |
390 | } |
391 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
392 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
393 | parameters->setInteger("pageLoadTimeout"_s , m_pageLoadTimeout.millisecondsAs<int>()); |
394 | if (auto pageLoadStrategy = pageLoadStrategyString()) |
395 | parameters->setString("pageLoadStrategy"_s , pageLoadStrategy.value()); |
396 | m_host->sendCommandToBackend("goForwardInBrowsingContext"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
397 | if (response.isError) { |
398 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
399 | return; |
400 | } |
401 | switchToBrowsingContext(WTF::nullopt); |
402 | completionHandler(CommandResult::success()); |
403 | }); |
404 | }); |
405 | } |
406 | |
407 | void Session::refresh(Function<void (CommandResult&&)>&& completionHandler) |
408 | { |
409 | if (!m_toplevelBrowsingContext) { |
410 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
411 | return; |
412 | } |
413 | |
414 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
415 | if (result.isError()) { |
416 | completionHandler(WTFMove(result)); |
417 | return; |
418 | } |
419 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
420 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
421 | parameters->setInteger("pageLoadTimeout"_s , m_pageLoadTimeout.millisecondsAs<int>()); |
422 | if (auto pageLoadStrategy = pageLoadStrategyString()) |
423 | parameters->setString("pageLoadStrategy"_s , pageLoadStrategy.value()); |
424 | m_host->sendCommandToBackend("reloadBrowsingContext"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
425 | if (response.isError) { |
426 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
427 | return; |
428 | } |
429 | switchToBrowsingContext(WTF::nullopt); |
430 | completionHandler(CommandResult::success()); |
431 | }); |
432 | }); |
433 | } |
434 | |
435 | void Session::getTitle(Function<void (CommandResult&&)>&& completionHandler) |
436 | { |
437 | if (!m_toplevelBrowsingContext) { |
438 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
439 | return; |
440 | } |
441 | |
442 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
443 | if (result.isError()) { |
444 | completionHandler(WTFMove(result)); |
445 | return; |
446 | } |
447 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
448 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
449 | parameters->setString("function"_s , "function() { return document.title; }"_s ); |
450 | parameters->setArray("arguments"_s , JSON::Array::create()); |
451 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
452 | if (response.isError || !response.responseObject) { |
453 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
454 | return; |
455 | } |
456 | String valueString; |
457 | if (!response.responseObject->getString("result"_s , valueString)) { |
458 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
459 | return; |
460 | } |
461 | RefPtr<JSON::Value> resultValue; |
462 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
463 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
464 | return; |
465 | } |
466 | completionHandler(CommandResult::success(WTFMove(resultValue))); |
467 | }); |
468 | }); |
469 | } |
470 | |
471 | void Session::getWindowHandle(Function<void (CommandResult&&)>&& completionHandler) |
472 | { |
473 | if (!m_toplevelBrowsingContext) { |
474 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
475 | return; |
476 | } |
477 | |
478 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
479 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
480 | m_host->sendCommandToBackend("getBrowsingContext"_s , WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
481 | if (response.isError || !response.responseObject) { |
482 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
483 | return; |
484 | } |
485 | RefPtr<JSON::Object> browsingContext; |
486 | if (!response.responseObject->getObject("context"_s , browsingContext)) { |
487 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
488 | return; |
489 | } |
490 | String handle; |
491 | if (!browsingContext->getString("handle"_s , handle)) { |
492 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
493 | return; |
494 | } |
495 | completionHandler(CommandResult::success(JSON::Value::create(handle))); |
496 | }); |
497 | } |
498 | |
499 | void Session::closeTopLevelBrowsingContext(const String& toplevelBrowsingContext, Function<void (CommandResult&&)>&& completionHandler) |
500 | { |
501 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
502 | parameters->setString("handle"_s , toplevelBrowsingContext); |
503 | m_host->sendCommandToBackend("closeBrowsingContext"_s , WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { |
504 | if (!m_host->isConnected()) { |
505 | // Closing the browsing context made the browser quit. |
506 | completionHandler(CommandResult::success(JSON::Array::create())); |
507 | return; |
508 | } |
509 | if (response.isError) { |
510 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
511 | return; |
512 | } |
513 | |
514 | getWindowHandles([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) { |
515 | if (!m_host->isConnected()) { |
516 | // Closing the browsing context made the browser quit. |
517 | completionHandler(CommandResult::success(JSON::Array::create())); |
518 | return; |
519 | } |
520 | completionHandler(WTFMove(result)); |
521 | }); |
522 | }); |
523 | } |
524 | |
525 | void Session::closeWindow(Function<void (CommandResult&&)>&& completionHandler) |
526 | { |
527 | if (!m_toplevelBrowsingContext) { |
528 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
529 | return; |
530 | } |
531 | |
532 | handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
533 | if (result.isError()) { |
534 | completionHandler(WTFMove(result)); |
535 | return; |
536 | } |
537 | auto toplevelBrowsingContext = std::exchange(m_toplevelBrowsingContext, WTF::nullopt); |
538 | closeTopLevelBrowsingContext(toplevelBrowsingContext.value(), WTFMove(completionHandler)); |
539 | }); |
540 | } |
541 | |
542 | void Session::switchToWindow(const String& windowHandle, Function<void (CommandResult&&)>&& completionHandler) |
543 | { |
544 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
545 | parameters->setString("browsingContextHandle"_s , windowHandle); |
546 | m_host->sendCommandToBackend("switchToBrowsingContext"_s , WTFMove(parameters), [this, protectedThis = makeRef(*this), windowHandle, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
547 | if (response.isError) { |
548 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
549 | return; |
550 | } |
551 | switchToTopLevelBrowsingContext(windowHandle); |
552 | completionHandler(CommandResult::success()); |
553 | }); |
554 | } |
555 | |
556 | void Session::getWindowHandles(Function<void (CommandResult&&)>&& completionHandler) |
557 | { |
558 | m_host->sendCommandToBackend("getBrowsingContexts"_s , JSON::Object::create(), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
559 | if (response.isError || !response.responseObject) { |
560 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
561 | return; |
562 | } |
563 | RefPtr<JSON::Array> browsingContextArray; |
564 | if (!response.responseObject->getArray("contexts"_s , browsingContextArray)) { |
565 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
566 | return; |
567 | } |
568 | RefPtr<JSON::Array> windowHandles = JSON::Array::create(); |
569 | for (unsigned i = 0; i < browsingContextArray->length(); ++i) { |
570 | RefPtr<JSON::Value> browsingContextValue = browsingContextArray->get(i); |
571 | RefPtr<JSON::Object> browsingContext; |
572 | if (!browsingContextValue->asObject(browsingContext)) { |
573 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
574 | return; |
575 | } |
576 | |
577 | String handle; |
578 | if (!browsingContext->getString("handle"_s , handle)) { |
579 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
580 | return; |
581 | } |
582 | |
583 | windowHandles->pushString(handle); |
584 | } |
585 | completionHandler(CommandResult::success(WTFMove(windowHandles))); |
586 | }); |
587 | } |
588 | |
589 | void Session::switchToFrame(RefPtr<JSON::Value>&& frameID, Function<void (CommandResult&&)>&& completionHandler) |
590 | { |
591 | if (!m_toplevelBrowsingContext) { |
592 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
593 | return; |
594 | } |
595 | |
596 | if (frameID->isNull()) { |
597 | switchToBrowsingContext(WTF::nullopt); |
598 | completionHandler(CommandResult::success()); |
599 | return; |
600 | } |
601 | |
602 | handleUserPrompts([this, protectedThis = makeRef(*this), frameID = WTFMove(frameID), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
603 | if (result.isError()) { |
604 | completionHandler(WTFMove(result)); |
605 | return; |
606 | } |
607 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
608 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
609 | if (m_currentBrowsingContext) |
610 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
611 | |
612 | int frameIndex; |
613 | if (frameID->asInteger(frameIndex)) { |
614 | if (frameIndex < 0 || frameIndex > USHRT_MAX) { |
615 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame)); |
616 | return; |
617 | } |
618 | parameters->setInteger("ordinal"_s , frameIndex); |
619 | } else { |
620 | String frameElementID = extractElementID(*frameID); |
621 | if (!frameElementID.isEmpty()) |
622 | parameters->setString("nodeHandle"_s , frameElementID); |
623 | else { |
624 | String frameName; |
625 | if (!frameID->asString(frameName)) { |
626 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchFrame)); |
627 | return; |
628 | } |
629 | parameters->setString("name"_s , frameName); |
630 | } |
631 | } |
632 | |
633 | m_host->sendCommandToBackend("resolveChildFrameHandle"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
634 | if (response.isError || !response.responseObject) { |
635 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
636 | return; |
637 | } |
638 | String frameHandle; |
639 | if (!response.responseObject->getString("result"_s , frameHandle)) { |
640 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
641 | return; |
642 | } |
643 | switchToBrowsingContext(frameHandle); |
644 | completionHandler(CommandResult::success()); |
645 | }); |
646 | }); |
647 | } |
648 | |
649 | void Session::switchToParentFrame(Function<void (CommandResult&&)>&& completionHandler) |
650 | { |
651 | if (!m_toplevelBrowsingContext) { |
652 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
653 | return; |
654 | } |
655 | |
656 | if (!m_currentBrowsingContext) { |
657 | completionHandler(CommandResult::success()); |
658 | return; |
659 | } |
660 | |
661 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
662 | if (result.isError()) { |
663 | completionHandler(WTFMove(result)); |
664 | return; |
665 | } |
666 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
667 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
668 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
669 | m_host->sendCommandToBackend("resolveParentFrameHandle"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
670 | if (response.isError || !response.responseObject) { |
671 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
672 | return; |
673 | } |
674 | String frameHandle; |
675 | if (!response.responseObject->getString("result"_s , frameHandle)) { |
676 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
677 | return; |
678 | } |
679 | switchToBrowsingContext(frameHandle); |
680 | completionHandler(CommandResult::success()); |
681 | }); |
682 | }); |
683 | } |
684 | |
685 | void Session::getToplevelBrowsingContextRect(Function<void (CommandResult&&)>&& completionHandler) |
686 | { |
687 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
688 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
689 | m_host->sendCommandToBackend("getBrowsingContext"_s , WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
690 | if (response.isError || !response.responseObject) { |
691 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
692 | return; |
693 | } |
694 | RefPtr<JSON::Object> browsingContext; |
695 | if (!response.responseObject->getObject("context"_s , browsingContext)) { |
696 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
697 | return; |
698 | } |
699 | RefPtr<JSON::Object> windowOrigin; |
700 | double x, y; |
701 | if (!browsingContext->getObject("windowOrigin"_s , windowOrigin) |
702 | || !windowOrigin->getDouble("x"_s , x) |
703 | || !windowOrigin->getDouble("y"_s , y)) { |
704 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
705 | return; |
706 | } |
707 | RefPtr<JSON::Object> windowSize; |
708 | double width, height; |
709 | if (!browsingContext->getObject("windowSize"_s , windowSize) |
710 | || !windowSize->getDouble("width"_s , width) |
711 | || !windowSize->getDouble("height"_s , height)) { |
712 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
713 | return; |
714 | } |
715 | auto windowRect = JSON::Object::create(); |
716 | windowRect->setDouble("x"_s , x); |
717 | windowRect->setDouble("y"_s , y); |
718 | windowRect->setDouble("width"_s , width); |
719 | windowRect->setDouble("height"_s , height); |
720 | completionHandler(CommandResult::success(WTFMove(windowRect))); |
721 | }); |
722 | } |
723 | |
724 | void Session::getWindowRect(Function<void (CommandResult&&)>&& completionHandler) |
725 | { |
726 | if (!m_toplevelBrowsingContext) { |
727 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
728 | return; |
729 | } |
730 | |
731 | handleUserPrompts([this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
732 | if (result.isError()) { |
733 | completionHandler(WTFMove(result)); |
734 | return; |
735 | } |
736 | getToplevelBrowsingContextRect(WTFMove(completionHandler)); |
737 | }); |
738 | } |
739 | |
740 | void Session::setWindowRect(Optional<double> x, Optional<double> y, Optional<double> width, Optional<double> height, Function<void (CommandResult&&)>&& completionHandler) |
741 | { |
742 | if (!m_toplevelBrowsingContext) { |
743 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
744 | return; |
745 | } |
746 | |
747 | handleUserPrompts([this, protectedThis = makeRef(*this), x, y, width, height, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
748 | if (result.isError()) { |
749 | completionHandler(WTFMove(result)); |
750 | return; |
751 | } |
752 | |
753 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
754 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
755 | if (x && y) { |
756 | RefPtr<JSON::Object> windowOrigin = JSON::Object::create(); |
757 | windowOrigin->setDouble("x" , x.value()); |
758 | windowOrigin->setDouble("y" , y.value()); |
759 | parameters->setObject("origin"_s , WTFMove(windowOrigin)); |
760 | } |
761 | if (width && height) { |
762 | RefPtr<JSON::Object> windowSize = JSON::Object::create(); |
763 | windowSize->setDouble("width" , width.value()); |
764 | windowSize->setDouble("height" , height.value()); |
765 | parameters->setObject("size"_s , WTFMove(windowSize)); |
766 | } |
767 | m_host->sendCommandToBackend("setWindowFrameOfBrowsingContext"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) mutable { |
768 | if (response.isError) { |
769 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
770 | return; |
771 | } |
772 | getToplevelBrowsingContextRect(WTFMove(completionHandler)); |
773 | }); |
774 | }); |
775 | } |
776 | |
777 | void Session::maximizeWindow(Function<void (CommandResult&&)>&& completionHandler) |
778 | { |
779 | if (!m_toplevelBrowsingContext) { |
780 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
781 | return; |
782 | } |
783 | |
784 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
785 | if (result.isError()) { |
786 | completionHandler(WTFMove(result)); |
787 | return; |
788 | } |
789 | |
790 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
791 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
792 | m_host->sendCommandToBackend("maximizeWindowOfBrowsingContext"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) mutable { |
793 | if (response.isError) { |
794 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
795 | return; |
796 | } |
797 | getToplevelBrowsingContextRect(WTFMove(completionHandler)); |
798 | }); |
799 | }); |
800 | } |
801 | |
802 | void Session::minimizeWindow(Function<void (CommandResult&&)>&& completionHandler) |
803 | { |
804 | if (!m_toplevelBrowsingContext) { |
805 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
806 | return; |
807 | } |
808 | |
809 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
810 | if (result.isError()) { |
811 | completionHandler(WTFMove(result)); |
812 | return; |
813 | } |
814 | |
815 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
816 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
817 | m_host->sendCommandToBackend("hideWindowOfBrowsingContext"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) mutable { |
818 | if (response.isError) { |
819 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
820 | return; |
821 | } |
822 | getToplevelBrowsingContextRect(WTFMove(completionHandler)); |
823 | }); |
824 | }); |
825 | } |
826 | |
827 | void Session::fullscreenWindow(Function<void (CommandResult&&)>&& completionHandler) |
828 | { |
829 | if (!m_toplevelBrowsingContext) { |
830 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
831 | return; |
832 | } |
833 | |
834 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
835 | if (result.isError()) { |
836 | completionHandler(WTFMove(result)); |
837 | return; |
838 | } |
839 | |
840 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
841 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
842 | parameters->setString("function"_s , String(EnterFullscreenJavaScript, sizeof(EnterFullscreenJavaScript))); |
843 | parameters->setArray("arguments"_s , JSON::Array::create()); |
844 | parameters->setBoolean("expectsImplicitCallbackArgument"_s , true); |
845 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { |
846 | if (response.isError || !response.responseObject) { |
847 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
848 | return; |
849 | } |
850 | |
851 | String valueString; |
852 | if (!response.responseObject->getString("result"_s , valueString)) { |
853 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
854 | return; |
855 | } |
856 | RefPtr<JSON::Value> resultValue; |
857 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
858 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
859 | return; |
860 | } |
861 | |
862 | getToplevelBrowsingContextRect(WTFMove(completionHandler)); |
863 | }); |
864 | }); |
865 | } |
866 | |
867 | RefPtr<JSON::Object> Session::createElement(RefPtr<JSON::Value>&& value) |
868 | { |
869 | RefPtr<JSON::Object> valueObject; |
870 | if (!value->asObject(valueObject)) |
871 | return nullptr; |
872 | |
873 | String elementID; |
874 | if (!valueObject->getString("session-node-" + id(), elementID)) |
875 | return nullptr; |
876 | |
877 | RefPtr<JSON::Object> elementObject = JSON::Object::create(); |
878 | elementObject->setString(webElementIdentifier(), elementID); |
879 | return elementObject; |
880 | } |
881 | |
882 | RefPtr<JSON::Object> Session::createElement(const String& elementID) |
883 | { |
884 | RefPtr<JSON::Object> elementObject = JSON::Object::create(); |
885 | elementObject->setString("session-node-" + id(), elementID); |
886 | return elementObject; |
887 | } |
888 | |
889 | RefPtr<JSON::Object> Session::(JSON::Value& value) |
890 | { |
891 | String elementID = extractElementID(value); |
892 | return !elementID.isEmpty() ? createElement(elementID) : nullptr; |
893 | } |
894 | |
895 | String Session::(JSON::Value& value) |
896 | { |
897 | RefPtr<JSON::Object> valueObject; |
898 | if (!value.asObject(valueObject)) |
899 | return emptyString(); |
900 | |
901 | String elementID; |
902 | if (!valueObject->getString(webElementIdentifier(), elementID)) |
903 | return emptyString(); |
904 | |
905 | return elementID; |
906 | } |
907 | |
908 | void Session::computeElementLayout(const String& elementID, OptionSet<ElementLayoutOption> options, Function<void (Optional<Rect>&&, Optional<Point>&&, bool, RefPtr<JSON::Object>&&)>&& completionHandler) |
909 | { |
910 | ASSERT(m_toplevelBrowsingContext.value()); |
911 | |
912 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
913 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
914 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.valueOr(emptyString())); |
915 | parameters->setString("nodeHandle"_s , elementID); |
916 | parameters->setBoolean("scrollIntoViewIfNeeded"_s , options.contains(ElementLayoutOption::ScrollIntoViewIfNeeded)); |
917 | parameters->setString("coordinateSystem"_s , options.contains(ElementLayoutOption::UseViewportCoordinates) ? "LayoutViewport"_s : "Page"_s ); |
918 | m_host->sendCommandToBackend("computeElementLayout"_s , WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { |
919 | if (response.isError || !response.responseObject) { |
920 | completionHandler(WTF::nullopt, WTF::nullopt, false, WTFMove(response.responseObject)); |
921 | return; |
922 | } |
923 | RefPtr<JSON::Object> rectObject; |
924 | if (!response.responseObject->getObject("rect"_s , rectObject)) { |
925 | completionHandler(WTF::nullopt, WTF::nullopt, false, nullptr); |
926 | return; |
927 | } |
928 | Optional<int> elementX; |
929 | Optional<int> elementY; |
930 | RefPtr<JSON::Object> elementPosition; |
931 | if (rectObject->getObject("origin"_s , elementPosition)) { |
932 | int x, y; |
933 | if (elementPosition->getInteger("x"_s , x) && elementPosition->getInteger("y"_s , y)) { |
934 | elementX = x; |
935 | elementY = y; |
936 | } |
937 | } |
938 | if (!elementX || !elementY) { |
939 | completionHandler(WTF::nullopt, WTF::nullopt, false, nullptr); |
940 | return; |
941 | } |
942 | Optional<int> elementWidth; |
943 | Optional<int> elementHeight; |
944 | RefPtr<JSON::Object> elementSize; |
945 | if (rectObject->getObject("size"_s , elementSize)) { |
946 | int width, height; |
947 | if (elementSize->getInteger("width"_s , width) && elementSize->getInteger("height"_s , height)) { |
948 | elementWidth = width; |
949 | elementHeight = height; |
950 | } |
951 | } |
952 | if (!elementWidth || !elementHeight) { |
953 | completionHandler(WTF::nullopt, WTF::nullopt, false, nullptr); |
954 | return; |
955 | } |
956 | Rect rect = { { elementX.value(), elementY.value() }, { elementWidth.value(), elementHeight.value() } }; |
957 | |
958 | bool isObscured; |
959 | if (!response.responseObject->getBoolean("isObscured"_s , isObscured)) { |
960 | completionHandler(WTF::nullopt, WTF::nullopt, false, nullptr); |
961 | return; |
962 | } |
963 | RefPtr<JSON::Object> inViewCenterPointObject; |
964 | if (!response.responseObject->getObject("inViewCenterPoint"_s , inViewCenterPointObject)) { |
965 | completionHandler(rect, WTF::nullopt, isObscured, nullptr); |
966 | return; |
967 | } |
968 | int inViewCenterPointX, inViewCenterPointY; |
969 | if (!inViewCenterPointObject->getInteger("x"_s , inViewCenterPointX) |
970 | || !inViewCenterPointObject->getInteger("y"_s , inViewCenterPointY)) { |
971 | completionHandler(WTF::nullopt, WTF::nullopt, isObscured, nullptr); |
972 | return; |
973 | } |
974 | Point inViewCenterPoint = { inViewCenterPointX, inViewCenterPointY }; |
975 | completionHandler(rect, inViewCenterPoint, isObscured, nullptr); |
976 | }); |
977 | } |
978 | |
979 | void Session::findElements(const String& strategy, const String& selector, FindElementsMode mode, const String& rootElementID, Function<void (CommandResult&&)>&& completionHandler) |
980 | { |
981 | if (!m_toplevelBrowsingContext) { |
982 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
983 | return; |
984 | } |
985 | |
986 | handleUserPrompts([this, protectedThis = makeRef(*this), strategy, selector, mode, rootElementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
987 | if (result.isError()) { |
988 | completionHandler(WTFMove(result)); |
989 | return; |
990 | } |
991 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
992 | arguments->pushString(JSON::Value::create(strategy)->toJSONString()); |
993 | if (rootElementID.isEmpty()) |
994 | arguments->pushString(JSON::Value::null()->toJSONString()); |
995 | else |
996 | arguments->pushString(createElement(rootElementID)->toJSONString()); |
997 | arguments->pushString(JSON::Value::create(selector)->toJSONString()); |
998 | arguments->pushString(JSON::Value::create(mode == FindElementsMode::Single)->toJSONString()); |
999 | arguments->pushString(JSON::Value::create(m_implicitWaitTimeout.milliseconds())->toJSONString()); |
1000 | |
1001 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1002 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1003 | if (m_currentBrowsingContext) |
1004 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1005 | parameters->setString("function"_s , String(FindNodesJavaScript, sizeof(FindNodesJavaScript))); |
1006 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1007 | parameters->setBoolean("expectsImplicitCallbackArgument"_s , true); |
1008 | // If there's an implicit wait, use one second more as callback timeout. |
1009 | if (m_implicitWaitTimeout) |
1010 | parameters->setInteger("callbackTimeout"_s , Seconds(m_implicitWaitTimeout + 1_s).millisecondsAs<int>()); |
1011 | |
1012 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), mode, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1013 | if (response.isError || !response.responseObject) { |
1014 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1015 | return; |
1016 | } |
1017 | String valueString; |
1018 | if (!response.responseObject->getString("result"_s , valueString)) { |
1019 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1020 | return; |
1021 | } |
1022 | RefPtr<JSON::Value> resultValue; |
1023 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
1024 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1025 | return; |
1026 | } |
1027 | |
1028 | switch (mode) { |
1029 | case FindElementsMode::Single: { |
1030 | RefPtr<JSON::Object> elementObject = createElement(WTFMove(resultValue)); |
1031 | if (!elementObject) { |
1032 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement)); |
1033 | return; |
1034 | } |
1035 | completionHandler(CommandResult::success(WTFMove(elementObject))); |
1036 | break; |
1037 | } |
1038 | case FindElementsMode::Multiple: { |
1039 | RefPtr<JSON::Array> elementsArray; |
1040 | if (!resultValue->asArray(elementsArray)) { |
1041 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement)); |
1042 | return; |
1043 | } |
1044 | RefPtr<JSON::Array> elementObjectsArray = JSON::Array::create(); |
1045 | unsigned elementsArrayLength = elementsArray->length(); |
1046 | for (unsigned i = 0; i < elementsArrayLength; ++i) { |
1047 | if (auto elementObject = createElement(elementsArray->get(i))) |
1048 | elementObjectsArray->pushObject(WTFMove(elementObject)); |
1049 | } |
1050 | completionHandler(CommandResult::success(WTFMove(elementObjectsArray))); |
1051 | break; |
1052 | } |
1053 | } |
1054 | }); |
1055 | }); |
1056 | } |
1057 | |
1058 | void Session::getActiveElement(Function<void (CommandResult&&)>&& completionHandler) |
1059 | { |
1060 | if (!m_toplevelBrowsingContext) { |
1061 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1062 | return; |
1063 | } |
1064 | |
1065 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1066 | if (result.isError()) { |
1067 | completionHandler(WTFMove(result)); |
1068 | return; |
1069 | } |
1070 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1071 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1072 | parameters->setString("function"_s , "function() { return document.activeElement; }"_s ); |
1073 | parameters->setArray("arguments"_s , JSON::Array::create()); |
1074 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1075 | if (response.isError || !response.responseObject) { |
1076 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1077 | return; |
1078 | } |
1079 | String valueString; |
1080 | if (!response.responseObject->getString("result"_s , valueString)) { |
1081 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1082 | return; |
1083 | } |
1084 | RefPtr<JSON::Value> resultValue; |
1085 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
1086 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1087 | return; |
1088 | } |
1089 | RefPtr<JSON::Object> elementObject = createElement(WTFMove(resultValue)); |
1090 | if (!elementObject) { |
1091 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchElement)); |
1092 | return; |
1093 | } |
1094 | completionHandler(CommandResult::success(WTFMove(elementObject))); |
1095 | }); |
1096 | }); |
1097 | } |
1098 | |
1099 | void Session::isElementSelected(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) |
1100 | { |
1101 | if (!m_toplevelBrowsingContext) { |
1102 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1103 | return; |
1104 | } |
1105 | |
1106 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1107 | if (result.isError()) { |
1108 | completionHandler(WTFMove(result)); |
1109 | return; |
1110 | } |
1111 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
1112 | arguments->pushString(createElement(elementID)->toJSONString()); |
1113 | arguments->pushString(JSON::Value::create("selected" )->toJSONString()); |
1114 | |
1115 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1116 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1117 | if (m_currentBrowsingContext) |
1118 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1119 | parameters->setString("function"_s , String(ElementAttributeJavaScript, sizeof(ElementAttributeJavaScript))); |
1120 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1121 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1122 | if (response.isError || !response.responseObject) { |
1123 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1124 | return; |
1125 | } |
1126 | String valueString; |
1127 | if (!response.responseObject->getString("result"_s , valueString)) { |
1128 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1129 | return; |
1130 | } |
1131 | RefPtr<JSON::Value> resultValue; |
1132 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
1133 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1134 | return; |
1135 | } |
1136 | if (resultValue->isNull()) { |
1137 | completionHandler(CommandResult::success(JSON::Value::create(false))); |
1138 | return; |
1139 | } |
1140 | String booleanResult; |
1141 | if (!resultValue->asString(booleanResult) || booleanResult != "true" ) { |
1142 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1143 | return; |
1144 | } |
1145 | completionHandler(CommandResult::success(JSON::Value::create(true))); |
1146 | }); |
1147 | }); |
1148 | } |
1149 | |
1150 | void Session::getElementText(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) |
1151 | { |
1152 | if (!m_toplevelBrowsingContext) { |
1153 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1154 | return; |
1155 | } |
1156 | |
1157 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1158 | if (result.isError()) { |
1159 | completionHandler(WTFMove(result)); |
1160 | return; |
1161 | } |
1162 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
1163 | arguments->pushString(createElement(elementID)->toJSONString()); |
1164 | |
1165 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1166 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1167 | if (m_currentBrowsingContext) |
1168 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1169 | // FIXME: Add an atom to properly implement this instead of just using innerText. |
1170 | parameters->setString("function"_s , "function(element) { return element.innerText.replace(/^[^\\S\\xa0]+|[^\\S\\xa0]+$/g, '') }"_s ); |
1171 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1172 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1173 | if (response.isError || !response.responseObject) { |
1174 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1175 | return; |
1176 | } |
1177 | String valueString; |
1178 | if (!response.responseObject->getString("result"_s , valueString)) { |
1179 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1180 | return; |
1181 | } |
1182 | RefPtr<JSON::Value> resultValue; |
1183 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
1184 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1185 | return; |
1186 | } |
1187 | completionHandler(CommandResult::success(WTFMove(resultValue))); |
1188 | }); |
1189 | }); |
1190 | } |
1191 | |
1192 | void Session::getElementTagName(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) |
1193 | { |
1194 | if (!m_toplevelBrowsingContext) { |
1195 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1196 | return; |
1197 | } |
1198 | |
1199 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1200 | if (result.isError()) { |
1201 | completionHandler(WTFMove(result)); |
1202 | return; |
1203 | } |
1204 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
1205 | arguments->pushString(createElement(elementID)->toJSONString()); |
1206 | |
1207 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1208 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1209 | if (m_currentBrowsingContext) |
1210 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1211 | parameters->setString("function"_s , "function(element) { return element.tagName.toLowerCase() }"_s ); |
1212 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1213 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1214 | if (response.isError || !response.responseObject) { |
1215 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1216 | return; |
1217 | } |
1218 | String valueString; |
1219 | if (!response.responseObject->getString("result"_s , valueString)) { |
1220 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1221 | return; |
1222 | } |
1223 | RefPtr<JSON::Value> resultValue; |
1224 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
1225 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1226 | return; |
1227 | } |
1228 | completionHandler(CommandResult::success(WTFMove(resultValue))); |
1229 | }); |
1230 | }); |
1231 | } |
1232 | |
1233 | void Session::getElementRect(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) |
1234 | { |
1235 | if (!m_toplevelBrowsingContext) { |
1236 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1237 | return; |
1238 | } |
1239 | |
1240 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1241 | if (result.isError()) { |
1242 | completionHandler(WTFMove(result)); |
1243 | return; |
1244 | } |
1245 | computeElementLayout(elementID, { }, [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](Optional<Rect>&& rect, Optional<Point>&&, bool, RefPtr<JSON::Object>&& error) { |
1246 | if (!rect || error) { |
1247 | completionHandler(CommandResult::fail(WTFMove(error))); |
1248 | return; |
1249 | } |
1250 | RefPtr<JSON::Object> rectObject = JSON::Object::create(); |
1251 | rectObject->setInteger("x"_s , rect.value().origin.x); |
1252 | rectObject->setInteger("y"_s , rect.value().origin.y); |
1253 | rectObject->setInteger("width"_s , rect.value().size.width); |
1254 | rectObject->setInteger("height"_s , rect.value().size.height); |
1255 | completionHandler(CommandResult::success(WTFMove(rectObject))); |
1256 | }); |
1257 | }); |
1258 | } |
1259 | |
1260 | void Session::isElementEnabled(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) |
1261 | { |
1262 | if (!m_toplevelBrowsingContext) { |
1263 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1264 | return; |
1265 | } |
1266 | |
1267 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1268 | if (result.isError()) { |
1269 | completionHandler(WTFMove(result)); |
1270 | return; |
1271 | } |
1272 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
1273 | arguments->pushString(createElement(elementID)->toJSONString()); |
1274 | |
1275 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1276 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1277 | if (m_currentBrowsingContext) |
1278 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1279 | parameters->setString("function"_s , "function(element) { return element.disabled === undefined ? true : !element.disabled }"_s ); |
1280 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1281 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1282 | if (response.isError || !response.responseObject) { |
1283 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1284 | return; |
1285 | } |
1286 | String valueString; |
1287 | if (!response.responseObject->getString("result"_s , valueString)) { |
1288 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1289 | return; |
1290 | } |
1291 | RefPtr<JSON::Value> resultValue; |
1292 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
1293 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1294 | return; |
1295 | } |
1296 | completionHandler(CommandResult::success(WTFMove(resultValue))); |
1297 | }); |
1298 | }); |
1299 | } |
1300 | |
1301 | void Session::isElementDisplayed(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) |
1302 | { |
1303 | if (!m_toplevelBrowsingContext) { |
1304 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1305 | return; |
1306 | } |
1307 | |
1308 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1309 | if (result.isError()) { |
1310 | completionHandler(WTFMove(result)); |
1311 | return; |
1312 | } |
1313 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
1314 | arguments->pushString(createElement(elementID)->toJSONString()); |
1315 | |
1316 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1317 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1318 | if (m_currentBrowsingContext) |
1319 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1320 | parameters->setString("function"_s , String(ElementDisplayedJavaScript, sizeof(ElementDisplayedJavaScript))); |
1321 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1322 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1323 | if (response.isError || !response.responseObject) { |
1324 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1325 | return; |
1326 | } |
1327 | String valueString; |
1328 | if (!response.responseObject->getString("result"_s , valueString)) { |
1329 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1330 | return; |
1331 | } |
1332 | RefPtr<JSON::Value> resultValue; |
1333 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
1334 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1335 | return; |
1336 | } |
1337 | completionHandler(CommandResult::success(WTFMove(resultValue))); |
1338 | }); |
1339 | }); |
1340 | } |
1341 | |
1342 | void Session::getElementAttribute(const String& elementID, const String& attribute, Function<void (CommandResult&&)>&& completionHandler) |
1343 | { |
1344 | if (!m_toplevelBrowsingContext) { |
1345 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1346 | return; |
1347 | } |
1348 | |
1349 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, attribute, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1350 | if (result.isError()) { |
1351 | completionHandler(WTFMove(result)); |
1352 | return; |
1353 | } |
1354 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
1355 | arguments->pushString(createElement(elementID)->toJSONString()); |
1356 | arguments->pushString(JSON::Value::create(attribute)->toJSONString()); |
1357 | |
1358 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1359 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1360 | if (m_currentBrowsingContext) |
1361 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1362 | parameters->setString("function"_s , String(ElementAttributeJavaScript, sizeof(ElementAttributeJavaScript))); |
1363 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1364 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1365 | if (response.isError || !response.responseObject) { |
1366 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1367 | return; |
1368 | } |
1369 | String valueString; |
1370 | if (!response.responseObject->getString("result"_s , valueString)) { |
1371 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1372 | return; |
1373 | } |
1374 | RefPtr<JSON::Value> resultValue; |
1375 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
1376 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1377 | return; |
1378 | } |
1379 | completionHandler(CommandResult::success(WTFMove(resultValue))); |
1380 | }); |
1381 | }); |
1382 | } |
1383 | |
1384 | void Session::getElementProperty(const String& elementID, const String& property, Function<void (CommandResult&&)>&& completionHandler) |
1385 | { |
1386 | if (!m_toplevelBrowsingContext) { |
1387 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1388 | return; |
1389 | } |
1390 | |
1391 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, property, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1392 | if (result.isError()) { |
1393 | completionHandler(WTFMove(result)); |
1394 | return; |
1395 | } |
1396 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
1397 | arguments->pushString(createElement(elementID)->toJSONString()); |
1398 | |
1399 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1400 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1401 | if (m_currentBrowsingContext) |
1402 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1403 | parameters->setString("function"_s , makeString("function(element) { return element." , property, "; }" )); |
1404 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1405 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1406 | if (response.isError || !response.responseObject) { |
1407 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1408 | return; |
1409 | } |
1410 | String valueString; |
1411 | if (!response.responseObject->getString("result"_s , valueString)) { |
1412 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1413 | return; |
1414 | } |
1415 | RefPtr<JSON::Value> resultValue; |
1416 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
1417 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1418 | return; |
1419 | } |
1420 | completionHandler(CommandResult::success(WTFMove(resultValue))); |
1421 | }); |
1422 | }); |
1423 | } |
1424 | |
1425 | void Session::getElementCSSValue(const String& elementID, const String& cssProperty, Function<void (CommandResult&&)>&& completionHandler) |
1426 | { |
1427 | if (!m_toplevelBrowsingContext) { |
1428 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1429 | return; |
1430 | } |
1431 | |
1432 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, cssProperty, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1433 | if (result.isError()) { |
1434 | completionHandler(WTFMove(result)); |
1435 | return; |
1436 | } |
1437 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
1438 | arguments->pushString(createElement(elementID)->toJSONString()); |
1439 | |
1440 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1441 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1442 | if (m_currentBrowsingContext) |
1443 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1444 | parameters->setString("function"_s , makeString("function(element) { return document.defaultView.getComputedStyle(element).getPropertyValue('" , cssProperty, "'); }" )); |
1445 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1446 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1447 | if (response.isError || !response.responseObject) { |
1448 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1449 | return; |
1450 | } |
1451 | String valueString; |
1452 | if (!response.responseObject->getString("result"_s , valueString)) { |
1453 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1454 | return; |
1455 | } |
1456 | RefPtr<JSON::Value> resultValue; |
1457 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
1458 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1459 | return; |
1460 | } |
1461 | completionHandler(CommandResult::success(WTFMove(resultValue))); |
1462 | }); |
1463 | }); |
1464 | } |
1465 | |
1466 | void Session::waitForNavigationToComplete(Function<void (CommandResult&&)>&& completionHandler) |
1467 | { |
1468 | if (!m_toplevelBrowsingContext) { |
1469 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1470 | return; |
1471 | } |
1472 | |
1473 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1474 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1475 | if (m_currentBrowsingContext) |
1476 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1477 | parameters->setInteger("pageLoadTimeout"_s , m_pageLoadTimeout.millisecondsAs<int>()); |
1478 | if (auto pageLoadStrategy = pageLoadStrategyString()) |
1479 | parameters->setString("pageLoadStrategy"_s , pageLoadStrategy.value()); |
1480 | m_host->sendCommandToBackend("waitForNavigationToComplete"_s , WTFMove(parameters), [this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1481 | if (response.isError) { |
1482 | auto result = CommandResult::fail(WTFMove(response.responseObject)); |
1483 | switch (result.errorCode()) { |
1484 | case CommandResult::ErrorCode::NoSuchWindow: |
1485 | // Window was closed, reset the top level browsing context and ignore the error. |
1486 | m_toplevelBrowsingContext = WTF::nullopt; |
1487 | break; |
1488 | case CommandResult::ErrorCode::NoSuchFrame: |
1489 | // Navigation destroyed the current frame, switch to top level browsing context and ignore the error. |
1490 | switchToBrowsingContext(WTF::nullopt); |
1491 | break; |
1492 | default: |
1493 | completionHandler(WTFMove(result)); |
1494 | return; |
1495 | } |
1496 | } |
1497 | completionHandler(CommandResult::success()); |
1498 | }); |
1499 | } |
1500 | |
1501 | void Session::selectOptionElement(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) |
1502 | { |
1503 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1504 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1505 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.valueOr(emptyString())); |
1506 | parameters->setString("nodeHandle"_s , elementID); |
1507 | m_host->sendCommandToBackend("selectOptionElement"_s , WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1508 | if (response.isError) { |
1509 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1510 | return; |
1511 | } |
1512 | completionHandler(CommandResult::success()); |
1513 | }); |
1514 | } |
1515 | |
1516 | void Session::elementClick(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) |
1517 | { |
1518 | if (!m_toplevelBrowsingContext) { |
1519 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1520 | return; |
1521 | } |
1522 | |
1523 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1524 | if (result.isError()) { |
1525 | completionHandler(WTFMove(result)); |
1526 | return; |
1527 | } |
1528 | OptionSet<ElementLayoutOption> options = { ElementLayoutOption::ScrollIntoViewIfNeeded, ElementLayoutOption::UseViewportCoordinates }; |
1529 | computeElementLayout(elementID, options, [this, protectedThis = protectedThis.copyRef(), elementID, completionHandler = WTFMove(completionHandler)](Optional<Rect>&& rect, Optional<Point>&& inViewCenter, bool isObscured, RefPtr<JSON::Object>&& error) mutable { |
1530 | if (!rect || error) { |
1531 | completionHandler(CommandResult::fail(WTFMove(error))); |
1532 | return; |
1533 | } |
1534 | if (isObscured) { |
1535 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementClickIntercepted)); |
1536 | return; |
1537 | } |
1538 | if (!inViewCenter) { |
1539 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::ElementNotInteractable)); |
1540 | return; |
1541 | } |
1542 | |
1543 | getElementTagName(elementID, [this, elementID, inViewCenter = WTFMove(inViewCenter), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1544 | bool isOptionElement = false; |
1545 | if (!result.isError()) { |
1546 | String tagName; |
1547 | if (result.result()->asString(tagName)) |
1548 | isOptionElement = tagName == "option" ; |
1549 | } |
1550 | |
1551 | Function<void (CommandResult&&)> continueAfterClickFunction = [this, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1552 | if (result.isError()) { |
1553 | completionHandler(WTFMove(result)); |
1554 | return; |
1555 | } |
1556 | |
1557 | waitForNavigationToComplete(WTFMove(completionHandler)); |
1558 | }; |
1559 | if (isOptionElement) |
1560 | selectOptionElement(elementID, WTFMove(continueAfterClickFunction)); |
1561 | else |
1562 | performMouseInteraction(inViewCenter.value().x, inViewCenter.value().y, MouseButton::Left, MouseInteraction::SingleClick, WTFMove(continueAfterClickFunction)); |
1563 | }); |
1564 | }); |
1565 | }); |
1566 | } |
1567 | |
1568 | void Session::elementClear(const String& elementID, Function<void (CommandResult&&)>&& completionHandler) |
1569 | { |
1570 | if (!m_toplevelBrowsingContext) { |
1571 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1572 | return; |
1573 | } |
1574 | |
1575 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1576 | if (result.isError()) { |
1577 | completionHandler(WTFMove(result)); |
1578 | return; |
1579 | } |
1580 | |
1581 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
1582 | arguments->pushString(createElement(elementID)->toJSONString()); |
1583 | |
1584 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1585 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1586 | if (m_currentBrowsingContext) |
1587 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1588 | parameters->setString("function"_s , String(FormElementClearJavaScript, sizeof(FormElementClearJavaScript))); |
1589 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1590 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1591 | if (response.isError) { |
1592 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1593 | return; |
1594 | } |
1595 | completionHandler(CommandResult::success()); |
1596 | }); |
1597 | }); |
1598 | } |
1599 | |
1600 | String Session::virtualKeyForKey(UChar key, KeyModifier& modifier) |
1601 | { |
1602 | // §17.4.2 Keyboard Actions. |
1603 | // https://www.w3.org/TR/webdriver/#keyboard-actions |
1604 | modifier = KeyModifier::None; |
1605 | switch (key) { |
1606 | case 0xE001U: |
1607 | return "Cancel"_s ; |
1608 | case 0xE002U: |
1609 | return "Help"_s ; |
1610 | case 0xE003U: |
1611 | return "Backspace"_s ; |
1612 | case 0xE004U: |
1613 | return "Tab"_s ; |
1614 | case 0xE005U: |
1615 | return "Clear"_s ; |
1616 | case 0xE006U: |
1617 | return "Return"_s ; |
1618 | case 0xE007U: |
1619 | return "Enter"_s ; |
1620 | case 0xE008U: |
1621 | case 0xE050U: |
1622 | modifier = KeyModifier::Shift; |
1623 | return "Shift"_s ; |
1624 | case 0xE009U: |
1625 | case 0xE051U: |
1626 | modifier = KeyModifier::Control; |
1627 | return "Control"_s ; |
1628 | case 0xE00AU: |
1629 | case 0xE052U: |
1630 | modifier = KeyModifier::Alternate; |
1631 | return "Alternate"_s ; |
1632 | case 0xE00BU: |
1633 | return "Pause"_s ; |
1634 | case 0xE00CU: |
1635 | return "Escape"_s ; |
1636 | case 0xE00DU: |
1637 | return "Space"_s ; |
1638 | case 0xE00EU: |
1639 | case 0xE054U: |
1640 | return "PageUp"_s ; |
1641 | case 0xE00FU: |
1642 | case 0xE055U: |
1643 | return "PageDown"_s ; |
1644 | case 0xE010U: |
1645 | case 0xE056U: |
1646 | return "End"_s ; |
1647 | case 0xE011U: |
1648 | case 0xE057U: |
1649 | return "Home"_s ; |
1650 | case 0xE012U: |
1651 | case 0xE058U: |
1652 | return "LeftArrow"_s ; |
1653 | case 0xE013U: |
1654 | case 0xE059U: |
1655 | return "UpArrow"_s ; |
1656 | case 0xE014U: |
1657 | case 0xE05AU: |
1658 | return "RightArrow"_s ; |
1659 | case 0xE015U: |
1660 | case 0xE05BU: |
1661 | return "DownArrow"_s ; |
1662 | case 0xE016U: |
1663 | case 0xE05CU: |
1664 | return "Insert"_s ; |
1665 | case 0xE017U: |
1666 | case 0xE05DU: |
1667 | return "Delete"_s ; |
1668 | case 0xE018U: |
1669 | return "Semicolon"_s ; |
1670 | case 0xE019U: |
1671 | return "Equals"_s ; |
1672 | case 0xE01AU: |
1673 | return "NumberPad0"_s ; |
1674 | case 0xE01BU: |
1675 | return "NumberPad1"_s ; |
1676 | case 0xE01CU: |
1677 | return "NumberPad2"_s ; |
1678 | case 0xE01DU: |
1679 | return "NumberPad3"_s ; |
1680 | case 0xE01EU: |
1681 | return "NumberPad4"_s ; |
1682 | case 0xE01FU: |
1683 | return "NumberPad5"_s ; |
1684 | case 0xE020U: |
1685 | return "NumberPad6"_s ; |
1686 | case 0xE021U: |
1687 | return "NumberPad7"_s ; |
1688 | case 0xE022U: |
1689 | return "NumberPad8"_s ; |
1690 | case 0xE023U: |
1691 | return "NumberPad9"_s ; |
1692 | case 0xE024U: |
1693 | return "NumberPadMultiply"_s ; |
1694 | case 0xE025U: |
1695 | return "NumberPadAdd"_s ; |
1696 | case 0xE026U: |
1697 | return "NumberPadSeparator"_s ; |
1698 | case 0xE027U: |
1699 | return "NumberPadSubtract"_s ; |
1700 | case 0xE028U: |
1701 | return "NumberPadDecimal"_s ; |
1702 | case 0xE029U: |
1703 | return "NumberPadDivide"_s ; |
1704 | case 0xE031U: |
1705 | return "Function1"_s ; |
1706 | case 0xE032U: |
1707 | return "Function2"_s ; |
1708 | case 0xE033U: |
1709 | return "Function3"_s ; |
1710 | case 0xE034U: |
1711 | return "Function4"_s ; |
1712 | case 0xE035U: |
1713 | return "Function5"_s ; |
1714 | case 0xE036U: |
1715 | return "Function6"_s ; |
1716 | case 0xE037U: |
1717 | return "Function7"_s ; |
1718 | case 0xE038U: |
1719 | return "Function8"_s ; |
1720 | case 0xE039U: |
1721 | return "Function9"_s ; |
1722 | case 0xE03AU: |
1723 | return "Function10"_s ; |
1724 | case 0xE03BU: |
1725 | return "Function11"_s ; |
1726 | case 0xE03CU: |
1727 | return "Function12"_s ; |
1728 | case 0xE03DU: |
1729 | case 0xE053U: |
1730 | modifier = KeyModifier::Meta; |
1731 | return "Meta"_s ; |
1732 | default: |
1733 | break; |
1734 | } |
1735 | return String(); |
1736 | } |
1737 | |
1738 | void Session::elementSendKeys(const String& elementID, const String& text, Function<void (CommandResult&&)>&& completionHandler) |
1739 | { |
1740 | if (!m_toplevelBrowsingContext) { |
1741 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1742 | return; |
1743 | } |
1744 | |
1745 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, text, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1746 | if (result.isError()) { |
1747 | completionHandler(WTFMove(result)); |
1748 | return; |
1749 | } |
1750 | // FIXME: move this to an atom. |
1751 | static const char focusScript[] = |
1752 | "function focus(element) {" |
1753 | " let doc = element.ownerDocument || element;" |
1754 | " let prevActiveElement = doc.activeElement;" |
1755 | " if (element != prevActiveElement && prevActiveElement)" |
1756 | " prevActiveElement.blur();" |
1757 | " element.focus();" |
1758 | " let tagName = element.tagName.toUpperCase();" |
1759 | " let isTextElement = tagName === 'TEXTAREA' || (tagName === 'INPUT' && element.type === 'text');" |
1760 | " if (isTextElement && element.selectionEnd == 0)" |
1761 | " element.setSelectionRange(element.value.length, element.value.length);" |
1762 | " if (element != doc.activeElement)" |
1763 | " throw new Error('cannot focus element');" |
1764 | "}" ; |
1765 | |
1766 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
1767 | arguments->pushString(createElement(elementID)->toJSONString()); |
1768 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1769 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1770 | if (m_currentBrowsingContext) |
1771 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1772 | parameters->setString("function"_s , focusScript); |
1773 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1774 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), text, completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { |
1775 | if (response.isError || !response.responseObject) { |
1776 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1777 | return; |
1778 | } |
1779 | |
1780 | unsigned stickyModifiers = 0; |
1781 | auto textLength = text.length(); |
1782 | Vector<KeyboardInteraction> interactions; |
1783 | interactions.reserveInitialCapacity(textLength); |
1784 | for (unsigned i = 0; i < textLength; ++i) { |
1785 | auto key = text[i]; |
1786 | KeyboardInteraction interaction; |
1787 | KeyModifier modifier; |
1788 | auto virtualKey = virtualKeyForKey(key, modifier); |
1789 | if (!virtualKey.isNull()) { |
1790 | interaction.key = virtualKey; |
1791 | if (modifier != KeyModifier::None) { |
1792 | stickyModifiers ^= modifier; |
1793 | if (stickyModifiers & modifier) |
1794 | interaction.type = KeyboardInteractionType::KeyPress; |
1795 | else |
1796 | interaction.type = KeyboardInteractionType::KeyRelease; |
1797 | } |
1798 | } else |
1799 | interaction.text = String(&key, 1); |
1800 | interactions.uncheckedAppend(WTFMove(interaction)); |
1801 | } |
1802 | |
1803 | // Reset sticky modifiers if needed. |
1804 | if (stickyModifiers) { |
1805 | if (stickyModifiers & KeyModifier::Shift) |
1806 | interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Shift"_s ) }); |
1807 | if (stickyModifiers & KeyModifier::Control) |
1808 | interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Control"_s ) }); |
1809 | if (stickyModifiers & KeyModifier::Alternate) |
1810 | interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Alternate"_s ) }); |
1811 | if (stickyModifiers & KeyModifier::Meta) |
1812 | interactions.append({ KeyboardInteractionType::KeyRelease, WTF::nullopt, Optional<String>("Meta"_s ) }); |
1813 | } |
1814 | |
1815 | performKeyboardInteractions(WTFMove(interactions), WTFMove(completionHandler)); |
1816 | }); |
1817 | }); |
1818 | } |
1819 | |
1820 | RefPtr<JSON::Value> Session::handleScriptResult(RefPtr<JSON::Value>&& resultValue) |
1821 | { |
1822 | RefPtr<JSON::Array> resultArray; |
1823 | if (resultValue->asArray(resultArray)) { |
1824 | RefPtr<JSON::Array> returnValueArray = JSON::Array::create(); |
1825 | unsigned resultArrayLength = resultArray->length(); |
1826 | for (unsigned i = 0; i < resultArrayLength; ++i) |
1827 | returnValueArray->pushValue(handleScriptResult(resultArray->get(i))); |
1828 | return returnValueArray; |
1829 | } |
1830 | |
1831 | if (auto element = createElement(RefPtr<JSON::Value>(resultValue))) |
1832 | return element; |
1833 | |
1834 | RefPtr<JSON::Object> resultObject; |
1835 | if (resultValue->asObject(resultObject)) { |
1836 | RefPtr<JSON::Object> returnValueObject = JSON::Object::create(); |
1837 | auto end = resultObject->end(); |
1838 | for (auto it = resultObject->begin(); it != end; ++it) |
1839 | returnValueObject->setValue(it->key, handleScriptResult(WTFMove(it->value))); |
1840 | return returnValueObject; |
1841 | } |
1842 | |
1843 | return WTFMove(resultValue); |
1844 | } |
1845 | |
1846 | void Session::executeScript(const String& script, RefPtr<JSON::Array>&& argumentsArray, ExecuteScriptMode mode, Function<void (CommandResult&&)>&& completionHandler) |
1847 | { |
1848 | if (!m_toplevelBrowsingContext) { |
1849 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
1850 | return; |
1851 | } |
1852 | |
1853 | handleUserPrompts([this, protectedThis = makeRef(*this), script, argumentsArray = WTFMove(argumentsArray), mode, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
1854 | if (result.isError()) { |
1855 | completionHandler(WTFMove(result)); |
1856 | return; |
1857 | } |
1858 | RefPtr<JSON::Array> arguments = JSON::Array::create(); |
1859 | unsigned argumentsLength = argumentsArray->length(); |
1860 | for (unsigned i = 0; i < argumentsLength; ++i) { |
1861 | if (auto argument = argumentsArray->get(i)) { |
1862 | if (auto element = extractElement(*argument)) |
1863 | arguments->pushString(element->toJSONString()); |
1864 | else |
1865 | arguments->pushString(argument->toJSONString()); |
1866 | } |
1867 | } |
1868 | |
1869 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1870 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
1871 | if (m_currentBrowsingContext) |
1872 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
1873 | parameters->setString("function"_s , "function(){" + script + '}'); |
1874 | parameters->setArray("arguments"_s , WTFMove(arguments)); |
1875 | if (mode == ExecuteScriptMode::Async) { |
1876 | parameters->setBoolean("expectsImplicitCallbackArgument"_s , true); |
1877 | if (m_scriptTimeout) |
1878 | parameters->setInteger("callbackTimeout"_s , m_scriptTimeout.millisecondsAs<int>()); |
1879 | } |
1880 | m_host->sendCommandToBackend("evaluateJavaScriptFunction"_s , WTFMove(parameters), [this, protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { |
1881 | if (response.isError || !response.responseObject) { |
1882 | auto result = CommandResult::fail(WTFMove(response.responseObject)); |
1883 | if (result.errorCode() == CommandResult::ErrorCode::UnexpectedAlertOpen) |
1884 | completionHandler(CommandResult::success()); |
1885 | else |
1886 | completionHandler(WTFMove(result)); |
1887 | return; |
1888 | } |
1889 | String valueString; |
1890 | if (!response.responseObject->getString("result"_s , valueString)) { |
1891 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1892 | return; |
1893 | } |
1894 | RefPtr<JSON::Value> resultValue; |
1895 | if (!JSON::Value::parseJSON(valueString, resultValue)) { |
1896 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
1897 | return; |
1898 | } |
1899 | completionHandler(CommandResult::success(handleScriptResult(WTFMove(resultValue)))); |
1900 | }); |
1901 | }); |
1902 | } |
1903 | |
1904 | static String mouseButtonForAutomation(MouseButton button) |
1905 | { |
1906 | switch (button) { |
1907 | case MouseButton::None: |
1908 | return "None"_s ; |
1909 | case MouseButton::Left: |
1910 | return "Left"_s ; |
1911 | case MouseButton::Middle: |
1912 | return "Middle"_s ; |
1913 | case MouseButton::Right: |
1914 | return "Right"_s ; |
1915 | } |
1916 | |
1917 | RELEASE_ASSERT_NOT_REACHED(); |
1918 | } |
1919 | |
1920 | void Session::performMouseInteraction(int x, int y, MouseButton button, MouseInteraction interaction, Function<void (CommandResult&&)>&& completionHandler) |
1921 | { |
1922 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1923 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
1924 | RefPtr<JSON::Object> position = JSON::Object::create(); |
1925 | position->setInteger("x"_s , x); |
1926 | position->setInteger("y"_s , y); |
1927 | parameters->setObject("position"_s , WTFMove(position)); |
1928 | parameters->setString("button"_s , mouseButtonForAutomation(button)); |
1929 | switch (interaction) { |
1930 | case MouseInteraction::Move: |
1931 | parameters->setString("interaction"_s , "Move"_s ); |
1932 | break; |
1933 | case MouseInteraction::Down: |
1934 | parameters->setString("interaction"_s , "Down"_s ); |
1935 | break; |
1936 | case MouseInteraction::Up: |
1937 | parameters->setString("interaction"_s , "Up"_s ); |
1938 | break; |
1939 | case MouseInteraction::SingleClick: |
1940 | parameters->setString("interaction"_s , "SingleClick"_s ); |
1941 | break; |
1942 | case MouseInteraction::DoubleClick: |
1943 | parameters->setString("interaction"_s , "DoubleClick"_s ); |
1944 | break; |
1945 | } |
1946 | parameters->setArray("modifiers"_s , JSON::Array::create()); |
1947 | m_host->sendCommandToBackend("performMouseInteraction"_s , WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1948 | if (response.isError) { |
1949 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1950 | return; |
1951 | } |
1952 | completionHandler(CommandResult::success()); |
1953 | }); |
1954 | } |
1955 | |
1956 | void Session::performKeyboardInteractions(Vector<KeyboardInteraction>&& interactions, Function<void (CommandResult&&)>&& completionHandler) |
1957 | { |
1958 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
1959 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
1960 | RefPtr<JSON::Array> interactionsArray = JSON::Array::create(); |
1961 | for (const auto& interaction : interactions) { |
1962 | RefPtr<JSON::Object> interactionObject = JSON::Object::create(); |
1963 | switch (interaction.type) { |
1964 | case KeyboardInteractionType::KeyPress: |
1965 | interactionObject->setString("type"_s , "KeyPress"_s ); |
1966 | break; |
1967 | case KeyboardInteractionType::KeyRelease: |
1968 | interactionObject->setString("type"_s , "KeyRelease"_s ); |
1969 | break; |
1970 | case KeyboardInteractionType::InsertByKey: |
1971 | interactionObject->setString("type"_s , "InsertByKey"_s ); |
1972 | break; |
1973 | } |
1974 | if (interaction.key) |
1975 | interactionObject->setString("key"_s , interaction.key.value()); |
1976 | if (interaction.text) |
1977 | interactionObject->setString("text"_s , interaction.text.value()); |
1978 | interactionsArray->pushObject(WTFMove(interactionObject)); |
1979 | } |
1980 | parameters->setArray("interactions"_s , WTFMove(interactionsArray)); |
1981 | m_host->sendCommandToBackend("performKeyboardInteractions"_s , WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
1982 | if (response.isError) { |
1983 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
1984 | return; |
1985 | } |
1986 | completionHandler(CommandResult::success()); |
1987 | }); |
1988 | } |
1989 | |
1990 | static Optional<Session::Cookie> parseAutomationCookie(const JSON::Object& cookieObject) |
1991 | { |
1992 | Session::Cookie cookie; |
1993 | if (!cookieObject.getString("name"_s , cookie.name)) |
1994 | return WTF::nullopt; |
1995 | if (!cookieObject.getString("value"_s , cookie.value)) |
1996 | return WTF::nullopt; |
1997 | |
1998 | String path; |
1999 | if (cookieObject.getString("path"_s , path)) |
2000 | cookie.path = path; |
2001 | String domain; |
2002 | if (cookieObject.getString("domain"_s , domain)) |
2003 | cookie.domain = domain; |
2004 | bool secure; |
2005 | if (cookieObject.getBoolean("secure"_s , secure)) |
2006 | cookie.secure = secure; |
2007 | bool httpOnly; |
2008 | if (cookieObject.getBoolean("httpOnly"_s , httpOnly)) |
2009 | cookie.httpOnly = httpOnly; |
2010 | bool session = false; |
2011 | cookieObject.getBoolean("session"_s , session); |
2012 | if (!session) { |
2013 | double expiry; |
2014 | if (cookieObject.getDouble("expires"_s , expiry)) |
2015 | cookie.expiry = expiry; |
2016 | } |
2017 | |
2018 | return cookie; |
2019 | } |
2020 | |
2021 | static RefPtr<JSON::Object> builtAutomationCookie(const Session::Cookie& cookie) |
2022 | { |
2023 | RefPtr<JSON::Object> cookieObject = JSON::Object::create(); |
2024 | cookieObject->setString("name"_s , cookie.name); |
2025 | cookieObject->setString("value"_s , cookie.value); |
2026 | cookieObject->setString("path"_s , cookie.path.valueOr("/" )); |
2027 | cookieObject->setString("domain"_s , cookie.domain.valueOr(emptyString())); |
2028 | cookieObject->setBoolean("secure"_s , cookie.secure.valueOr(false)); |
2029 | cookieObject->setBoolean("httpOnly"_s , cookie.httpOnly.valueOr(false)); |
2030 | cookieObject->setBoolean("session"_s , !cookie.expiry); |
2031 | cookieObject->setDouble("expires"_s , cookie.expiry.valueOr(0)); |
2032 | return cookieObject; |
2033 | } |
2034 | |
2035 | static RefPtr<JSON::Object> serializeCookie(const Session::Cookie& cookie) |
2036 | { |
2037 | RefPtr<JSON::Object> cookieObject = JSON::Object::create(); |
2038 | cookieObject->setString("name"_s , cookie.name); |
2039 | cookieObject->setString("value"_s , cookie.value); |
2040 | if (cookie.path) |
2041 | cookieObject->setString("path"_s , cookie.path.value()); |
2042 | if (cookie.domain) |
2043 | cookieObject->setString("domain"_s , cookie.domain.value()); |
2044 | if (cookie.secure) |
2045 | cookieObject->setBoolean("secure"_s , cookie.secure.value()); |
2046 | if (cookie.httpOnly) |
2047 | cookieObject->setBoolean("httpOnly"_s , cookie.httpOnly.value()); |
2048 | if (cookie.expiry) |
2049 | cookieObject->setInteger("expiry"_s , cookie.expiry.value()); |
2050 | return cookieObject; |
2051 | } |
2052 | |
2053 | void Session::getAllCookies(Function<void (CommandResult&&)>&& completionHandler) |
2054 | { |
2055 | if (!m_toplevelBrowsingContext) { |
2056 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
2057 | return; |
2058 | } |
2059 | |
2060 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
2061 | if (result.isError()) { |
2062 | completionHandler(WTFMove(result)); |
2063 | return; |
2064 | } |
2065 | |
2066 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
2067 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
2068 | m_host->sendCommandToBackend("getAllCookies"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { |
2069 | if (response.isError || !response.responseObject) { |
2070 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
2071 | return; |
2072 | } |
2073 | RefPtr<JSON::Array> cookiesArray; |
2074 | if (!response.responseObject->getArray("cookies"_s , cookiesArray)) { |
2075 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
2076 | return; |
2077 | } |
2078 | RefPtr<JSON::Array> cookies = JSON::Array::create(); |
2079 | for (unsigned i = 0; i < cookiesArray->length(); ++i) { |
2080 | RefPtr<JSON::Value> cookieValue = cookiesArray->get(i); |
2081 | RefPtr<JSON::Object> cookieObject; |
2082 | if (!cookieValue->asObject(cookieObject)) { |
2083 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
2084 | return; |
2085 | } |
2086 | |
2087 | auto cookie = parseAutomationCookie(*cookieObject); |
2088 | if (!cookie) { |
2089 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
2090 | return; |
2091 | } |
2092 | cookies->pushObject(serializeCookie(cookie.value())); |
2093 | } |
2094 | completionHandler(CommandResult::success(WTFMove(cookies))); |
2095 | }); |
2096 | }); |
2097 | } |
2098 | |
2099 | void Session::getNamedCookie(const String& name, Function<void (CommandResult&&)>&& completionHandler) |
2100 | { |
2101 | getAllCookies([name, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
2102 | if (result.isError()) { |
2103 | completionHandler(WTFMove(result)); |
2104 | return; |
2105 | } |
2106 | RefPtr<JSON::Array> cookiesArray; |
2107 | result.result()->asArray(cookiesArray); |
2108 | for (unsigned i = 0; i < cookiesArray->length(); ++i) { |
2109 | RefPtr<JSON::Value> cookieValue = cookiesArray->get(i); |
2110 | RefPtr<JSON::Object> cookieObject; |
2111 | cookieValue->asObject(cookieObject); |
2112 | String cookieName; |
2113 | cookieObject->getString("name"_s , cookieName); |
2114 | if (cookieName == name) { |
2115 | completionHandler(CommandResult::success(WTFMove(cookieObject))); |
2116 | return; |
2117 | } |
2118 | } |
2119 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchCookie)); |
2120 | }); |
2121 | } |
2122 | |
2123 | void Session::addCookie(const Cookie& cookie, Function<void (CommandResult&&)>&& completionHandler) |
2124 | { |
2125 | if (!m_toplevelBrowsingContext) { |
2126 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
2127 | return; |
2128 | } |
2129 | |
2130 | handleUserPrompts([this, protectedThis = makeRef(*this), cookie = builtAutomationCookie(cookie), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
2131 | if (result.isError()) { |
2132 | completionHandler(WTFMove(result)); |
2133 | return; |
2134 | } |
2135 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
2136 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
2137 | parameters->setObject("cookie"_s , WTFMove(cookie)); |
2138 | m_host->sendCommandToBackend("addSingleCookie"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
2139 | if (response.isError) { |
2140 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
2141 | return; |
2142 | } |
2143 | completionHandler(CommandResult::success()); |
2144 | }); |
2145 | }); |
2146 | } |
2147 | |
2148 | void Session::deleteCookie(const String& name, Function<void (CommandResult&&)>&& completionHandler) |
2149 | { |
2150 | if (!m_toplevelBrowsingContext) { |
2151 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
2152 | return; |
2153 | } |
2154 | |
2155 | handleUserPrompts([this, protectedThis = makeRef(*this), name, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
2156 | if (result.isError()) { |
2157 | completionHandler(WTFMove(result)); |
2158 | return; |
2159 | } |
2160 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
2161 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
2162 | parameters->setString("cookieName"_s , name); |
2163 | m_host->sendCommandToBackend("deleteSingleCookie"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
2164 | if (response.isError) { |
2165 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
2166 | return; |
2167 | } |
2168 | completionHandler(CommandResult::success()); |
2169 | }); |
2170 | }); |
2171 | } |
2172 | |
2173 | void Session::deleteAllCookies(Function<void (CommandResult&&)>&& completionHandler) |
2174 | { |
2175 | if (!m_toplevelBrowsingContext) { |
2176 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
2177 | return; |
2178 | } |
2179 | |
2180 | handleUserPrompts([this, protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
2181 | if (result.isError()) { |
2182 | completionHandler(WTFMove(result)); |
2183 | return; |
2184 | } |
2185 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
2186 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
2187 | m_host->sendCommandToBackend("deleteAllCookies"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
2188 | if (response.isError) { |
2189 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
2190 | return; |
2191 | } |
2192 | completionHandler(CommandResult::success()); |
2193 | }); |
2194 | }); |
2195 | } |
2196 | |
2197 | InputSource& Session::getOrCreateInputSource(const String& id, InputSource::Type type, Optional<PointerType> pointerType) |
2198 | { |
2199 | auto addResult = m_activeInputSources.add(id, InputSource()); |
2200 | if (addResult.isNewEntry) |
2201 | addResult.iterator->value = { type, pointerType }; |
2202 | return addResult.iterator->value; |
2203 | } |
2204 | |
2205 | Session::InputSourceState& Session::inputSourceState(const String& id) |
2206 | { |
2207 | return m_inputStateTable.ensure(id, [] { return InputSourceState(); }).iterator->value; |
2208 | } |
2209 | |
2210 | static const char* automationSourceType(InputSource::Type type) |
2211 | { |
2212 | switch (type) { |
2213 | case InputSource::Type::None: |
2214 | return "Null" ; |
2215 | case InputSource::Type::Pointer: |
2216 | return "Mouse" ; |
2217 | case InputSource::Type::Key: |
2218 | return "Keyboard" ; |
2219 | } |
2220 | RELEASE_ASSERT_NOT_REACHED(); |
2221 | } |
2222 | |
2223 | static const char* automationOriginType(PointerOrigin::Type type) |
2224 | { |
2225 | switch (type) { |
2226 | case PointerOrigin::Type::Viewport: |
2227 | return "Viewport" ; |
2228 | case PointerOrigin::Type::Pointer: |
2229 | return "Pointer" ; |
2230 | case PointerOrigin::Type::Element: |
2231 | return "Element" ; |
2232 | } |
2233 | RELEASE_ASSERT_NOT_REACHED(); |
2234 | } |
2235 | |
2236 | void Session::performActions(Vector<Vector<Action>>&& actionsByTick, Function<void (CommandResult&&)>&& completionHandler) |
2237 | { |
2238 | if (!m_toplevelBrowsingContext) { |
2239 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
2240 | return; |
2241 | } |
2242 | |
2243 | handleUserPrompts([this, protectedThis = makeRef(*this), actionsByTick = WTFMove(actionsByTick), completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
2244 | if (result.isError()) { |
2245 | completionHandler(WTFMove(result)); |
2246 | return; |
2247 | } |
2248 | |
2249 | // First check if we have actions and whether we need to resolve any pointer move element origin. |
2250 | unsigned actionsCount = 0; |
2251 | for (const auto& tick : actionsByTick) |
2252 | actionsCount += tick.size(); |
2253 | if (!actionsCount) { |
2254 | completionHandler(CommandResult::success()); |
2255 | return; |
2256 | } |
2257 | |
2258 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
2259 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
2260 | if (m_currentBrowsingContext) |
2261 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
2262 | RefPtr<JSON::Array> inputSources = JSON::Array::create(); |
2263 | for (const auto& inputSource : m_activeInputSources) { |
2264 | RefPtr<JSON::Object> inputSourceObject = JSON::Object::create(); |
2265 | inputSourceObject->setString("sourceId"_s , inputSource.key); |
2266 | inputSourceObject->setString("sourceType"_s , automationSourceType(inputSource.value.type)); |
2267 | inputSources->pushObject(WTFMove(inputSourceObject)); |
2268 | } |
2269 | parameters->setArray("inputSources"_s , WTFMove(inputSources)); |
2270 | RefPtr<JSON::Array> steps = JSON::Array::create(); |
2271 | for (const auto& tick : actionsByTick) { |
2272 | RefPtr<JSON::Array> states = JSON::Array::create(); |
2273 | for (const auto& action : tick) { |
2274 | RefPtr<JSON::Object> state = JSON::Object::create(); |
2275 | auto& currentState = inputSourceState(action.id); |
2276 | state->setString("sourceId"_s , action.id); |
2277 | switch (action.type) { |
2278 | case Action::Type::None: |
2279 | state->setDouble("duration"_s , action.duration.value()); |
2280 | break; |
2281 | case Action::Type::Pointer: { |
2282 | switch (action.subtype) { |
2283 | case Action::Subtype::PointerUp: |
2284 | currentState.pressedButton = WTF::nullopt; |
2285 | break; |
2286 | case Action::Subtype::PointerDown: |
2287 | currentState.pressedButton = action.button.value(); |
2288 | break; |
2289 | case Action::Subtype::PointerMove: { |
2290 | state->setString("origin"_s , automationOriginType(action.origin->type)); |
2291 | RefPtr<JSON::Object> location = JSON::Object::create(); |
2292 | location->setInteger("x"_s , action.x.value()); |
2293 | location->setInteger("y"_s , action.y.value()); |
2294 | state->setObject("location"_s , WTFMove(location)); |
2295 | if (action.origin->type == PointerOrigin::Type::Element) |
2296 | state->setString("nodeHandle"_s , action.origin->elementID.value()); |
2297 | FALLTHROUGH; |
2298 | } |
2299 | case Action::Subtype::Pause: |
2300 | if (action.duration) |
2301 | state->setDouble("duration"_s , action.duration.value()); |
2302 | break; |
2303 | case Action::Subtype::PointerCancel: |
2304 | currentState.pressedButton = WTF::nullopt; |
2305 | break; |
2306 | case Action::Subtype::KeyUp: |
2307 | case Action::Subtype::KeyDown: |
2308 | ASSERT_NOT_REACHED(); |
2309 | } |
2310 | if (currentState.pressedButton) |
2311 | state->setString("pressedButton"_s , mouseButtonForAutomation(currentState.pressedButton.value())); |
2312 | break; |
2313 | } |
2314 | case Action::Type::Key: |
2315 | switch (action.subtype) { |
2316 | case Action::Subtype::KeyUp: |
2317 | if (currentState.pressedVirtualKey) |
2318 | currentState.pressedVirtualKey = WTF::nullopt; |
2319 | else |
2320 | currentState.pressedKey = WTF::nullopt; |
2321 | break; |
2322 | case Action::Subtype::KeyDown: { |
2323 | KeyModifier modifier; |
2324 | auto virtualKey = virtualKeyForKey(action.key.value()[0], modifier); |
2325 | if (!virtualKey.isNull()) |
2326 | currentState.pressedVirtualKey = virtualKey; |
2327 | else |
2328 | currentState.pressedKey = action.key.value(); |
2329 | break; |
2330 | } |
2331 | case Action::Subtype::Pause: |
2332 | if (action.duration) |
2333 | state->setDouble("duration"_s , action.duration.value()); |
2334 | break; |
2335 | case Action::Subtype::PointerUp: |
2336 | case Action::Subtype::PointerDown: |
2337 | case Action::Subtype::PointerMove: |
2338 | case Action::Subtype::PointerCancel: |
2339 | ASSERT_NOT_REACHED(); |
2340 | } |
2341 | if (currentState.pressedKey) |
2342 | state->setString("pressedCharKey"_s , currentState.pressedKey.value()); |
2343 | if (currentState.pressedVirtualKey) { |
2344 | // FIXME: support parsing and tracking multiple virtual keys. |
2345 | Ref<JSON::Array> virtualKeys = JSON::Array::create(); |
2346 | virtualKeys->pushString(currentState.pressedVirtualKey.value()); |
2347 | state->setArray("pressedVirtualKeys"_s , WTFMove(virtualKeys)); |
2348 | } |
2349 | break; |
2350 | } |
2351 | states->pushObject(WTFMove(state)); |
2352 | } |
2353 | RefPtr<JSON::Object> stepStates = JSON::Object::create(); |
2354 | stepStates->setArray("states"_s , WTFMove(states)); |
2355 | steps->pushObject(WTFMove(stepStates)); |
2356 | } |
2357 | |
2358 | parameters->setArray("steps"_s , WTFMove(steps)); |
2359 | m_host->sendCommandToBackend("performInteractionSequence"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)] (SessionHost::CommandResponse&& response) { |
2360 | if (response.isError) { |
2361 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
2362 | return; |
2363 | } |
2364 | completionHandler(CommandResult::success()); |
2365 | }); |
2366 | }); |
2367 | } |
2368 | |
2369 | void Session::releaseActions(Function<void (CommandResult&&)>&& completionHandler) |
2370 | { |
2371 | if (!m_toplevelBrowsingContext) { |
2372 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
2373 | return; |
2374 | } |
2375 | |
2376 | m_activeInputSources.clear(); |
2377 | m_inputStateTable.clear(); |
2378 | |
2379 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
2380 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
2381 | m_host->sendCommandToBackend("cancelInteractionSequence"_s , WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
2382 | if (response.isError) { |
2383 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
2384 | return; |
2385 | } |
2386 | completionHandler(CommandResult::success()); |
2387 | }); |
2388 | } |
2389 | |
2390 | void Session::dismissAlert(Function<void (CommandResult&&)>&& completionHandler) |
2391 | { |
2392 | if (!m_toplevelBrowsingContext) { |
2393 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
2394 | return; |
2395 | } |
2396 | |
2397 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
2398 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
2399 | m_host->sendCommandToBackend("dismissCurrentJavaScriptDialog"_s , WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
2400 | if (response.isError) { |
2401 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
2402 | return; |
2403 | } |
2404 | completionHandler(CommandResult::success()); |
2405 | }); |
2406 | } |
2407 | |
2408 | void Session::acceptAlert(Function<void (CommandResult&&)>&& completionHandler) |
2409 | { |
2410 | if (!m_toplevelBrowsingContext) { |
2411 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
2412 | return; |
2413 | } |
2414 | |
2415 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
2416 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
2417 | m_host->sendCommandToBackend("acceptCurrentJavaScriptDialog"_s , WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
2418 | if (response.isError) { |
2419 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
2420 | return; |
2421 | } |
2422 | completionHandler(CommandResult::success()); |
2423 | }); |
2424 | } |
2425 | |
2426 | void Session::getAlertText(Function<void (CommandResult&&)>&& completionHandler) |
2427 | { |
2428 | if (!m_toplevelBrowsingContext) { |
2429 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
2430 | return; |
2431 | } |
2432 | |
2433 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
2434 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
2435 | m_host->sendCommandToBackend("messageOfCurrentJavaScriptDialog"_s , WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
2436 | if (response.isError || !response.responseObject) { |
2437 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
2438 | return; |
2439 | } |
2440 | String valueString; |
2441 | if (!response.responseObject->getString("message"_s , valueString)) { |
2442 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
2443 | return; |
2444 | } |
2445 | completionHandler(CommandResult::success(JSON::Value::create(valueString))); |
2446 | }); |
2447 | } |
2448 | |
2449 | void Session::sendAlertText(const String& text, Function<void (CommandResult&&)>&& completionHandler) |
2450 | { |
2451 | if (!m_toplevelBrowsingContext) { |
2452 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
2453 | return; |
2454 | } |
2455 | |
2456 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
2457 | parameters->setString("browsingContextHandle"_s , m_toplevelBrowsingContext.value()); |
2458 | parameters->setString("userInput"_s , text); |
2459 | m_host->sendCommandToBackend("setUserInputForCurrentJavaScriptPrompt"_s , WTFMove(parameters), [protectedThis = makeRef(*this), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) { |
2460 | if (response.isError) { |
2461 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
2462 | return; |
2463 | } |
2464 | completionHandler(CommandResult::success()); |
2465 | }); |
2466 | } |
2467 | |
2468 | void Session::takeScreenshot(Optional<String> elementID, Optional<bool> scrollIntoView, Function<void (CommandResult&&)>&& completionHandler) |
2469 | { |
2470 | if (!m_toplevelBrowsingContext) { |
2471 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::NoSuchWindow)); |
2472 | return; |
2473 | } |
2474 | |
2475 | handleUserPrompts([this, protectedThis = makeRef(*this), elementID, scrollIntoView, completionHandler = WTFMove(completionHandler)](CommandResult&& result) mutable { |
2476 | if (result.isError()) { |
2477 | completionHandler(WTFMove(result)); |
2478 | return; |
2479 | } |
2480 | RefPtr<JSON::Object> parameters = JSON::Object::create(); |
2481 | parameters->setString("handle"_s , m_toplevelBrowsingContext.value()); |
2482 | if (m_currentBrowsingContext) |
2483 | parameters->setString("frameHandle"_s , m_currentBrowsingContext.value()); |
2484 | if (elementID) |
2485 | parameters->setString("nodeHandle"_s , elementID.value()); |
2486 | else |
2487 | parameters->setBoolean("clipToViewport"_s , true); |
2488 | if (scrollIntoView.valueOr(false)) |
2489 | parameters->setBoolean("scrollIntoViewIfNeeded"_s , true); |
2490 | m_host->sendCommandToBackend("takeScreenshot"_s , WTFMove(parameters), [protectedThis = protectedThis.copyRef(), completionHandler = WTFMove(completionHandler)](SessionHost::CommandResponse&& response) mutable { |
2491 | if (response.isError || !response.responseObject) { |
2492 | completionHandler(CommandResult::fail(WTFMove(response.responseObject))); |
2493 | return; |
2494 | } |
2495 | String data; |
2496 | if (!response.responseObject->getString("data"_s , data)) { |
2497 | completionHandler(CommandResult::fail(CommandResult::ErrorCode::UnknownError)); |
2498 | return; |
2499 | } |
2500 | completionHandler(CommandResult::success(JSON::Value::create(data))); |
2501 | }); |
2502 | }); |
2503 | } |
2504 | |
2505 | } // namespace WebDriver |
2506 | |