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:: |
---|