Ignore beforeunload dialogs
According to WebDriver standard the alerts arising on beforeunload event
must never be shown in Classic session. This commit implements
autoacceptance of any beforeunload alerts.
Bug: chromedriver:4757
Change-Id: I5276db0a872699248f89cca06c697fb5aab64649
Validate-Test-Flakiness: skip
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5517510
Reviewed-by: Maksim Sadym <sadym@chromium.org>
Commit-Queue: Vladimir Nechaev <nechaev@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1297374}
diff --git a/chrome/test/chromedriver/capabilities.cc b/chrome/test/chromedriver/capabilities.cc
index 27d8baf3..103bd88b 100644
--- a/chrome/test/chromedriver/capabilities.cc
+++ b/chrome/test/chromedriver/capabilities.cc
@@ -1118,7 +1118,7 @@
parser_map["strictFileInteractability"] =
base::BindRepeating(&ParseBoolean, &strict_file_interactability);
parser_map["webSocketUrl"] =
- base::BindRepeating(&ParseBoolean, &webSocketUrl);
+ base::BindRepeating(&ParseBoolean, &web_socket_url);
if (!w3c_compliant) {
// TODO(https://crbug.com/chromedriver/2596): "unexpectedAlertBehaviour" is
// legacy name of "unhandledPromptBehavior", remove when we stop supporting
diff --git a/chrome/test/chromedriver/capabilities.h b/chrome/test/chromedriver/capabilities.h
index 12e4f92..6f52221 100644
--- a/chrome/test/chromedriver/capabilities.h
+++ b/chrome/test/chromedriver/capabilities.h
@@ -193,7 +193,7 @@
std::set<WebViewInfo::Type> window_types;
- bool webSocketUrl = false;
+ bool web_socket_url = false;
};
bool GetChromeOptionsDictionary(const base::Value::Dict& params,
diff --git a/chrome/test/chromedriver/chrome/chrome_android_impl.cc b/chrome/test/chromedriver/chrome/chrome_android_impl.cc
index 38b63bd..7426e6e 100644
--- a/chrome/test/chromedriver/chrome/chrome_android_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_android_impl.cc
@@ -22,13 +22,15 @@
devtools_event_listeners,
std::optional<MobileDevice> mobile_device,
std::string page_load_strategy,
- std::unique_ptr<Device> device)
+ std::unique_ptr<Device> device,
+ bool autoaccept_beforeunload)
: ChromeImpl(std::move(browser_info),
std::move(window_types),
std::move(websocket_client),
std::move(devtools_event_listeners),
std::move(mobile_device),
- page_load_strategy),
+ page_load_strategy,
+ autoaccept_beforeunload),
device_(std::move(device)) {}
ChromeAndroidImpl::~ChromeAndroidImpl() = default;
diff --git a/chrome/test/chromedriver/chrome/chrome_android_impl.h b/chrome/test/chromedriver/chrome/chrome_android_impl.h
index 28e469ef..b4562a4 100644
--- a/chrome/test/chromedriver/chrome/chrome_android_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_android_impl.h
@@ -23,7 +23,8 @@
devtools_event_listeners,
std::optional<MobileDevice> mobile_device,
std::string page_load_strategy,
- std::unique_ptr<Device> device);
+ std::unique_ptr<Device> device,
+ bool autoaccept_beforeunload);
~ChromeAndroidImpl() override;
// Overridden from Chrome:
diff --git a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
index 95136e2..09f6ca7d 100644
--- a/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_desktop_impl.cc
@@ -85,13 +85,15 @@
const base::CommandLine& command,
base::ScopedTempDir* user_data_dir,
base::ScopedTempDir* extension_dir,
- bool network_emulation_enabled)
+ bool network_emulation_enabled,
+ bool autoaccept_beforeunload)
: ChromeImpl(std::move(browser_info),
std::move(window_types),
std::move(websocket_client),
std::move(devtools_event_listeners),
std::move(mobile_device),
- page_load_strategy),
+ page_load_strategy,
+ autoaccept_beforeunload),
process_(std::move(process)),
command_(command),
network_connection_enabled_(network_emulation_enabled),
@@ -165,9 +167,9 @@
if (status.IsError())
return status;
std::unique_ptr<WebViewImpl> web_view_tmp =
- WebViewImpl::CreateTopLevelWebView(id, w3c_compliant, &browser_info_,
- std::move(client), mobile_device,
- page_load_strategy());
+ WebViewImpl::CreateTopLevelWebView(
+ id, w3c_compliant, &browser_info_, std::move(client), mobile_device,
+ page_load_strategy(), autoaccept_beforeunload_);
status = web_view_tmp->AttachTo(devtools_websocket_client_.get());
if (status.IsError()) {
return status;
diff --git a/chrome/test/chromedriver/chrome/chrome_desktop_impl.h b/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
index 84061d6..d549b82e 100644
--- a/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_desktop_impl.h
@@ -35,7 +35,8 @@
const base::CommandLine& command,
base::ScopedTempDir* user_data_dir,
base::ScopedTempDir* extension_dir,
- bool network_emulation_enabled);
+ bool network_emulation_enabled,
+ bool autoaccept_beforeunload);
~ChromeDesktopImpl() override;
// Waits for a page with the given URL to appear and finish loading.
diff --git a/chrome/test/chromedriver/chrome/chrome_impl.cc b/chrome/test/chromedriver/chrome/chrome_impl.cc
index 8d4c303..edf2737e8 100644
--- a/chrome/test/chromedriver/chrome/chrome_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_impl.cc
@@ -183,7 +183,7 @@
} else {
web_views_.push_back(WebViewImpl::CreateTopLevelWebView(
view.id, w3c_compliant, &browser_info_, std::move(client),
- mobile_device_, page_load_strategy_));
+ mobile_device_, page_load_strategy_, autoaccept_beforeunload_));
}
status = web_views_.back()->AttachTo(devtools_websocket_client_.get());
if (status.IsError()) {
@@ -689,11 +689,13 @@
std::vector<std::unique_ptr<DevToolsEventListener>>
devtools_event_listeners,
std::optional<MobileDevice> mobile_device,
- std::string page_load_strategy)
+ std::string page_load_strategy,
+ bool autoaccept_beforeunload)
: mobile_device_(std::move(mobile_device)),
browser_info_(std::move(browser_info)),
window_types_(std::move(window_types)),
devtools_websocket_client_(std::move(websocket_client)),
+ autoaccept_beforeunload_(autoaccept_beforeunload),
devtools_event_listeners_(std::move(devtools_event_listeners)),
page_load_strategy_(page_load_strategy) {
window_types_.insert(WebViewInfo::kPage);
diff --git a/chrome/test/chromedriver/chrome/chrome_impl.h b/chrome/test/chromedriver/chrome/chrome_impl.h
index 37a7bd7f..b806a07 100644
--- a/chrome/test/chromedriver/chrome/chrome_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_impl.h
@@ -96,7 +96,8 @@
std::vector<std::unique_ptr<DevToolsEventListener>>
devtools_event_listeners,
std::optional<MobileDevice> mobile_device,
- std::string page_load_strategy);
+ std::string page_load_strategy,
+ bool autoaccept_beforeunload);
virtual Status QuitImpl() = 0;
Status CloseTarget(const std::string& id);
@@ -118,6 +119,7 @@
BrowserInfo browser_info_;
std::set<WebViewInfo::Type> window_types_;
std::unique_ptr<DevToolsClient> devtools_websocket_client_;
+ bool autoaccept_beforeunload_ = false;
private:
static Status PermissionNameToChromePermissions(
diff --git a/chrome/test/chromedriver/chrome/chrome_remote_impl.cc b/chrome/test/chromedriver/chrome/chrome_remote_impl.cc
index ec6ce22..b3e4342 100644
--- a/chrome/test/chromedriver/chrome/chrome_remote_impl.cc
+++ b/chrome/test/chromedriver/chrome/chrome_remote_impl.cc
@@ -18,13 +18,15 @@
std::vector<std::unique_ptr<DevToolsEventListener>>
devtools_event_listeners,
std::optional<MobileDevice> mobile_device,
- std::string page_load_strategy)
+ std::string page_load_strategy,
+ bool autoaccept_beforeunload)
: ChromeImpl(std::move(browser_info),
std::move(window_types),
std::move(websocket_client),
std::move(devtools_event_listeners),
std::move(mobile_device),
- page_load_strategy) {}
+ page_load_strategy,
+ autoaccept_beforeunload) {}
ChromeRemoteImpl::~ChromeRemoteImpl() = default;
diff --git a/chrome/test/chromedriver/chrome/chrome_remote_impl.h b/chrome/test/chromedriver/chrome/chrome_remote_impl.h
index 4c21c80..4a56a355 100644
--- a/chrome/test/chromedriver/chrome/chrome_remote_impl.h
+++ b/chrome/test/chromedriver/chrome/chrome_remote_impl.h
@@ -21,7 +21,8 @@
std::vector<std::unique_ptr<DevToolsEventListener>>
devtools_event_listeners,
std::optional<MobileDevice> mobile_device,
- std::string page_load_strategy);
+ std::string page_load_strategy,
+ bool autoaccept_beforeunload);
~ChromeRemoteImpl() override;
// Overridden from Chrome.
diff --git a/chrome/test/chromedriver/chrome/devtools_client_impl.cc b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
index 18c04c2..fcc8707 100644
--- a/chrome/test/chromedriver/chrome/devtools_client_impl.cc
+++ b/chrome/test/chromedriver/chrome/devtools_client_impl.cc
@@ -47,6 +47,9 @@
const char kNoNodeForBackendNodeIdError[] =
"No node found for given backend id";
const char kNoNodeWithGivenIdFoundError[] = "No node with given id found";
+const char kExecutionContextWasDestroyed[] = "Execution context was destroyed.";
+const char kInspectedTargetNavigatedOrClosed[] =
+ "Inspected target navigated or closed";
static constexpr int kSessionNotFoundInspectorCode = -32001;
static constexpr int kCdpMethodNotFoundCode = -32601;
@@ -1061,7 +1064,8 @@
crashed_ = true;
return Status(kTabCrashed);
}
- if (event.method == "Page.javascriptDialogOpening") {
+ if ((owner_ && owner_->GetJavaScriptDialogManager()->IsDialogOpen()) ||
+ (!owner_ && event.method == "Page.javascriptDialogOpening")) {
// A command may have opened the dialog, which will block the response.
// To find out which one (if any), do a round trip with a simple command
// to the renderer and afterwards see if any of the commands still haven't
@@ -1388,6 +1392,11 @@
// The error message that arises during DOM.resolveNode code.
// This means that the node with given BackendNodeId is not found.
return Status{kNoSuchElement, error_message};
+ } else if (error_message == kExecutionContextWasDestroyed ||
+ error_message == kInspectedTargetNavigatedOrClosed) {
+ // The error messages that arise if navigation was started by the
+ // asynchronous script before the script execution was finished..
+ return Status{kNavigationDetectedByRemoteEnd, error_message};
}
std::optional<int> error_code = error_dict->FindInt("code");
if (error_code == kInvalidParamsInspectorCode) {
diff --git a/chrome/test/chromedriver/chrome/javascript_dialog_manager.cc b/chrome/test/chromedriver/chrome/javascript_dialog_manager.cc
index 600cd61..8a77e13 100644
--- a/chrome/test/chromedriver/chrome/javascript_dialog_manager.cc
+++ b/chrome/test/chromedriver/chrome/javascript_dialog_manager.cc
@@ -7,8 +7,9 @@
#include "chrome/test/chromedriver/chrome/devtools_client.h"
#include "chrome/test/chromedriver/chrome/status.h"
-JavaScriptDialogManager::JavaScriptDialogManager(DevToolsClient* client)
- : client_(client) {
+JavaScriptDialogManager::JavaScriptDialogManager(DevToolsClient* client,
+ bool autoaccept_beforeunload)
+ : client_(client), autoaccept_beforeunload_(autoaccept_beforeunload) {
client_->AddListener(this);
}
@@ -95,6 +96,10 @@
"dialog event missing or invalid 'defaultPrompt'");
}
prompt_text_ = *prompt_text;
+
+ if (*type == "beforeunload" && autoaccept_beforeunload_) {
+ return HandleDialog(true, nullptr);
+ }
} else if (method == "Page.javascriptDialogClosed") {
// Inspector only sends this event when all dialogs have been closed.
// Clear the unhandled queue in case the user closed a dialog manually.
diff --git a/chrome/test/chromedriver/chrome/javascript_dialog_manager.h b/chrome/test/chromedriver/chrome/javascript_dialog_manager.h
index 416d1cd..a0fcfba 100644
--- a/chrome/test/chromedriver/chrome/javascript_dialog_manager.h
+++ b/chrome/test/chromedriver/chrome/javascript_dialog_manager.h
@@ -18,7 +18,8 @@
// Tracks the opening and closing of JavaScript dialogs (e.g., alerts).
class JavaScriptDialogManager : public DevToolsEventListener {
public:
- explicit JavaScriptDialogManager(DevToolsClient* client);
+ explicit JavaScriptDialogManager(DevToolsClient* client,
+ bool autoaccept_beforeunload);
JavaScriptDialogManager(const JavaScriptDialogManager&) = delete;
JavaScriptDialogManager& operator=(const JavaScriptDialogManager&) = delete;
@@ -49,6 +50,8 @@
std::list<std::string> dialog_type_queue_;
std::string prompt_text_;
+
+ bool autoaccept_beforeunload_ = false;
};
#endif // CHROME_TEST_CHROMEDRIVER_CHROME_JAVASCRIPT_DIALOG_MANAGER_H_
diff --git a/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc b/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc
index 29385f6..1c374de 100644
--- a/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc
+++ b/chrome/test/chromedriver/chrome/javascript_dialog_manager_unittest.cc
@@ -17,7 +17,7 @@
TEST(JavaScriptDialogManager, NoDialog) {
StubDevToolsClient client;
- JavaScriptDialogManager manager(&client);
+ JavaScriptDialogManager manager(&client, true);
std::string message("HI");
ASSERT_EQ(kNoSuchAlert, manager.GetDialogMessage(&message).code());
ASSERT_FALSE(manager.IsDialogOpen());
@@ -27,7 +27,7 @@
TEST(JavaScriptDialogManager, HandleDialogPassesParams) {
RecorderDevToolsClient client;
- JavaScriptDialogManager manager(&client);
+ JavaScriptDialogManager manager(&client, true);
base::Value::Dict params;
params.Set("message", "hi");
params.Set("type", "prompt");
@@ -45,7 +45,7 @@
TEST(JavaScriptDialogManager, HandleDialogNullPrompt) {
RecorderDevToolsClient client;
- JavaScriptDialogManager manager(&client);
+ JavaScriptDialogManager manager(&client, true);
base::Value::Dict params;
params.Set("message", "hi");
params.Set("type", "prompt");
@@ -60,7 +60,7 @@
TEST(JavaScriptDialogManager, ReconnectClearsStateAndSendsEnable) {
RecorderDevToolsClient client;
- JavaScriptDialogManager manager(&client);
+ JavaScriptDialogManager manager(&client, true);
base::Value::Dict params;
params.Set("message", "hi");
params.Set("type", "alert");
@@ -122,7 +122,7 @@
TEST(JavaScriptDialogManager, OneDialog) {
FakeDevToolsClient client;
- JavaScriptDialogManager manager(&client);
+ JavaScriptDialogManager manager(&client, true);
base::Value::Dict params;
params.Set("message", "hi");
params.Set("type", "alert");
@@ -150,7 +150,7 @@
TEST(JavaScriptDialogManager, TwoDialogs) {
FakeDevToolsClient client;
- JavaScriptDialogManager manager(&client);
+ JavaScriptDialogManager manager(&client, true);
base::Value::Dict params;
params.Set("message", "1");
params.Set("type", "confirm");
@@ -189,7 +189,7 @@
TEST(JavaScriptDialogManager, OneDialogManualClose) {
StubDevToolsClient client;
BrowserInfo browser_info;
- JavaScriptDialogManager manager(&client);
+ JavaScriptDialogManager manager(&client, true);
base::Value::Dict params;
params.Set("message", "hi");
params.Set("type", "alert");
@@ -215,3 +215,50 @@
ASSERT_EQ(kNoSuchAlert, manager.GetDialogMessage(&message).code());
ASSERT_EQ(kNoSuchAlert, manager.HandleDialog(false, nullptr).code());
}
+
+TEST(JavaScriptDialogManager, BeforeunloadIsAutoAccepted) {
+ FakeDevToolsClient client;
+ JavaScriptDialogManager manager(&client, true);
+ base::Value::Dict params;
+ params.Set("message", "hi");
+ params.Set("type", "beforeunload");
+ params.Set("defaultPrompt", "");
+ ASSERT_FALSE(manager.IsDialogOpen());
+ std::string message;
+ ASSERT_EQ(kNoSuchAlert, manager.GetDialogMessage(&message).code());
+
+ client.set_closing_count(1);
+ ASSERT_EQ(
+ kOk,
+ manager.OnEvent(&client, "Page.javascriptDialogOpening", params).code());
+
+ ASSERT_FALSE(manager.IsDialogOpen());
+ ASSERT_EQ(kNoSuchAlert, manager.GetDialogMessage(&message).code());
+ ASSERT_EQ(kNoSuchAlert, manager.HandleDialog(false, nullptr).code());
+}
+
+TEST(JavaScriptDialogManager, AlertAndBeforeunloadAreAutoAccepted) {
+ FakeDevToolsClient client;
+ JavaScriptDialogManager manager(&client, true);
+ base::Value::Dict params;
+ params.Set("message", "1");
+ params.Set("type", "alert");
+ params.Set("defaultPrompt", "");
+ ASSERT_EQ(
+ kOk,
+ manager.OnEvent(&client, "Page.javascriptDialogOpening", params).code());
+ params.Set("message", "2");
+ params.Set("type", "beforeunload");
+ // If a beforeunload dialog is detected all the previous dialogs are
+ // auto-accepted because a navigation has started after after their
+ // appearance.
+ client.set_closing_count(1);
+ ASSERT_EQ(
+ kOk,
+ manager.OnEvent(&client, "Page.javascriptDialogOpening", params).code());
+
+ ASSERT_FALSE(manager.IsDialogOpen());
+ std::string message;
+ ASSERT_EQ(kNoSuchAlert, manager.GetDialogMessage(&message).code());
+ ASSERT_EQ(kNoSuchAlert, manager.HandleDialog(false, nullptr).code());
+}
diff --git a/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc b/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc
index 7b5377e..2f7965f9 100644
--- a/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc
+++ b/chrome/test/chromedriver/chrome/navigation_tracker_unittest.cc
@@ -131,8 +131,8 @@
DevToolsClient* client_ptr = client_uptr.get();
WebViewImpl web_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kNormal);
- JavaScriptDialogManager dialog_manager(client_ptr);
+ PageLoadStrategy::kNormal, true);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
NavigationTracker tracker(client_ptr, &web_view, &dialog_manager);
base::Value::Dict params;
@@ -159,8 +159,8 @@
DevToolsClient* client_ptr = client_uptr.get();
WebViewImpl web_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kNormal);
- JavaScriptDialogManager dialog_manager(client_ptr);
+ PageLoadStrategy::kNormal, true);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
NavigationTracker tracker(client_ptr, &web_view, &dialog_manager);
base::Value::Dict params;
@@ -188,8 +188,8 @@
DevToolsClient* client_ptr = client_uptr.get();
WebViewImpl web_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kNormal);
- JavaScriptDialogManager dialog_manager(client_ptr);
+ PageLoadStrategy::kNormal, true);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
NavigationTracker tracker(client_ptr, &web_view, &dialog_manager);
base::Value::Dict params;
@@ -241,8 +241,8 @@
DevToolsClient* client_ptr = client_uptr.get();
WebViewImpl web_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kNormal);
- JavaScriptDialogManager dialog_manager(client_ptr);
+ PageLoadStrategy::kNormal, true);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
NavigationTracker tracker(client_ptr, NavigationTracker::kNotLoading,
&web_view, &dialog_manager);
@@ -263,7 +263,7 @@
std::make_unique<DeterminingLoadStateDevToolsClient>(
false, false, std::string(), &dict);
DevToolsClient* client_ptr = client_uptr.get();
- JavaScriptDialogManager dialog_manager(client_ptr);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
EvaluateScriptWebView web_view(kOk);
NavigationTracker tracker(client_ptr, &web_view, &dialog_manager);
@@ -314,7 +314,7 @@
std::make_unique<DeterminingLoadStateDevToolsClient>(
false, false, std::string(), &dict);
DevToolsClient* client_ptr = client_uptr.get();
- JavaScriptDialogManager dialog_manager(client_ptr);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
EvaluateScriptWebView web_view(kOk);
NavigationTracker tracker(client_ptr, &web_view, &dialog_manager);
@@ -353,7 +353,7 @@
std::make_unique<DeterminingLoadStateDevToolsClient>(
false, false, std::string(), &dict);
DevToolsClient* client_ptr = client_uptr.get();
- JavaScriptDialogManager dialog_manager(client_ptr);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
EvaluateScriptWebView web_view(kOk);
NavigationTracker tracker(client_ptr, &web_view, &dialog_manager);
@@ -423,8 +423,8 @@
DevToolsClient* client_ptr = client_uptr.get();
WebViewImpl web_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kNormal);
- JavaScriptDialogManager dialog_manager(client_ptr);
+ PageLoadStrategy::kNormal, true);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
NavigationTracker tracker(client_ptr, &web_view, &dialog_manager);
bool is_pending;
@@ -441,8 +441,8 @@
DevToolsClient* client_ptr = client_uptr.get();
WebViewImpl web_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kNormal);
- JavaScriptDialogManager dialog_manager(client_ptr);
+ PageLoadStrategy::kNormal, true);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
NavigationTracker tracker(client_ptr, &web_view, &dialog_manager);
ASSERT_NO_FATAL_FAILURE(AssertPendingState(&tracker, true));
@@ -454,7 +454,7 @@
std::make_unique<DeterminingLoadStateDevToolsClient>(
false, true, std::string(), &dict);
DevToolsClient* client_ptr = client_uptr.get();
- JavaScriptDialogManager dialog_manager(client_ptr);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
EvaluateScriptWebView web_view(kOk);
NavigationTracker tracker(client_ptr, &web_view, &dialog_manager);
@@ -470,8 +470,8 @@
DevToolsClient* client_ptr = client_uptr.get();
WebViewImpl web_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kNormal);
- JavaScriptDialogManager dialog_manager(client_ptr);
+ PageLoadStrategy::kNormal, true);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
NavigationTracker tracker(client_ptr, &web_view, &dialog_manager);
base::Value::Dict params;
@@ -487,7 +487,7 @@
std::make_unique<DeterminingLoadStateDevToolsClient>(
false, true, std::string(), &dict);
DevToolsClient* client_ptr = client_uptr.get();
- JavaScriptDialogManager dialog_manager(client_ptr);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
EvaluateScriptWebView web_view(kOk);
NavigationTracker tracker(client_ptr, NavigationTracker::kNotLoading,
&web_view, &dialog_manager);
@@ -509,7 +509,7 @@
std::make_unique<DeterminingLoadStateDevToolsClient>(
false, true, std::string(), &dict);
DevToolsClient* client_ptr = client_uptr.get();
- JavaScriptDialogManager dialog_manager(client_ptr);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
EvaluateScriptWebView web_view(kOk);
NavigationTracker tracker(client_ptr, NavigationTracker::kNotLoading,
&web_view, &dialog_manager);
@@ -532,7 +532,7 @@
std::make_unique<DeterminingLoadStateDevToolsClient>(
false, true, std::string(), &dict);
DevToolsClient* client_ptr = client_uptr.get();
- JavaScriptDialogManager dialog_manager(client_ptr);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
EvaluateScriptWebView web_view(kOk);
NavigationTracker tracker(client_ptr, NavigationTracker::kNotLoading,
&web_view, &dialog_manager);
@@ -573,8 +573,8 @@
DevToolsClient* client_ptr = client_uptr.get();
WebViewImpl web_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kNormal);
- JavaScriptDialogManager dialog_manager(client_ptr);
+ PageLoadStrategy::kNormal, true);
+ JavaScriptDialogManager dialog_manager(client_ptr, true);
NavigationTracker tracker(client_ptr, &web_view, &dialog_manager);
bool is_pending;
diff --git a/chrome/test/chromedriver/chrome/status.cc b/chrome/test/chromedriver/chrome/status.cc
index 3ef992b..67556a9 100644
--- a/chrome/test/chromedriver/chrome/status.cc
+++ b/chrome/test/chromedriver/chrome/status.cc
@@ -76,6 +76,8 @@
return "no such shadow root";
case kDetachedShadowRoot:
return "detached shadow root";
+ case kNavigationDetectedByRemoteEnd:
+ return "navigation detected by remote end";
default:
return "<unknown>";
}
diff --git a/chrome/test/chromedriver/chrome/status.h b/chrome/test/chromedriver/chrome/status.h
index f881b7a..65e4157 100644
--- a/chrome/test/chromedriver/chrome/status.h
+++ b/chrome/test/chromedriver/chrome/status.h
@@ -44,6 +44,7 @@
kTabCrashed,
kTargetDetached,
kUnexpectedAlertOpen_Keep,
+ kNavigationDetectedByRemoteEnd,
};
// Represents a WebDriver status, which may be an error or ok.
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.cc b/chrome/test/chromedriver/chrome/web_view_impl.cc
index 3343f4c..d46ea59 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl.cc
@@ -373,10 +373,11 @@
const BrowserInfo* browser_info,
std::unique_ptr<DevToolsClient> client,
std::optional<MobileDevice> mobile_device,
- std::string page_load_strategy) {
+ std::string page_load_strategy,
+ bool autoaccept_beforeunload) {
return std::make_unique<WebViewImpl>(
id, w3c_compliant, nullptr, browser_info, std::move(client),
- std::move(mobile_device), page_load_strategy);
+ std::move(mobile_device), page_load_strategy, autoaccept_beforeunload);
}
WebViewImpl::WebViewImpl(const std::string& id,
@@ -407,7 +408,8 @@
const BrowserInfo* browser_info,
std::unique_ptr<DevToolsClient> client,
std::optional<MobileDevice> mobile_device,
- std::string page_load_strategy)
+ std::string page_load_strategy,
+ bool autoaccept_beforeunload)
: id_(id),
w3c_compliant_(w3c_compliant),
browser_info_(browser_info),
@@ -416,7 +418,8 @@
parent_(parent),
client_(std::move(client)),
frame_tracker_(new FrameTracker(client_.get(), this)),
- dialog_manager_(new JavaScriptDialogManager(client_.get())),
+ dialog_manager_(
+ new JavaScriptDialogManager(client_.get(), autoaccept_beforeunload)),
mobile_emulation_override_manager_(
new MobileEmulationOverrideManager(client_.get(),
std::move(mobile_device),
@@ -426,7 +429,8 @@
network_conditions_override_manager_(
new NetworkConditionsOverrideManager(client_.get())),
heap_snapshot_taker_(new HeapSnapshotTaker(client_.get())),
- is_service_worker_(false) {
+ is_service_worker_(false),
+ autoaccept_beforeunload_(autoaccept_beforeunload) {
// Downloading in headless mode requires the setting of
// Browser.setDownloadBehavior. This is handled by the
// DownloadDirectoryOverrideManager, which is only instantiated
@@ -481,7 +485,7 @@
std::make_unique<DevToolsClientImpl>(session_id, session_id);
std::unique_ptr<WebViewImpl> child = std::make_unique<WebViewImpl>(
target_id, w3c_compliant_, this, browser_info_, std::move(child_client),
- std::nullopt, "");
+ std::nullopt, "", autoaccept_beforeunload_);
const WebViewImpl* root_view = this;
while (root_view->parent_ != nullptr) {
root_view = root_view->parent_;
@@ -1378,7 +1382,8 @@
Status status{kOk};
while (keep_waiting) {
status = client_->HandleEventsUntil(not_pending_navigation, timeout);
- keep_waiting = status.code() == kNoSuchExecutionContext;
+ keep_waiting = status.code() == kNoSuchExecutionContext ||
+ status.code() == kNavigationDetectedByRemoteEnd;
}
if (status.code() == kTimeout && stop_load_on_timeout) {
VLOG(0) << "Timed out. Stopping navigation...";
@@ -1394,7 +1399,8 @@
new_status = client_->HandleEventsUntil(
not_pending_navigation,
Timeout(base::Seconds(kWaitForNavigationStopSeconds)));
- keep_waiting = new_status.code() == kNoSuchExecutionContext;
+ keep_waiting = new_status.code() == kNoSuchExecutionContext ||
+ new_status.code() == kNavigationDetectedByRemoteEnd;
}
navigation_tracker_->set_timed_out(false);
if (new_status.IsError())
@@ -1733,61 +1739,33 @@
async_args.Append(true);
std::unique_ptr<base::Value> tmp;
Timeout local_timeout(timeout);
+ std::unique_ptr<base::Value> query_value;
Status status = CallFunctionWithTimeout(frame, kExecuteAsyncScriptScript,
- async_args, timeout, &tmp);
- if (status.IsError())
+ async_args, timeout, &query_value);
+ if (status.IsError()) {
return status;
-
- const char kDocUnloadError[] = "document unloaded while waiting for result";
- std::string kQueryResult = base::StringPrintf(
- "function() {"
- " var info = document.$chrome_asyncScriptInfo;"
- " if (!info)"
- " return {status: %d, value: '%s'};"
- " var result = info.result;"
- " if (!result)"
- " return {status: 0};"
- " delete info.result;"
- " return result;"
- "}",
- kJavaScriptError,
- kDocUnloadError);
- const base::TimeDelta kOneHundredMs = base::Milliseconds(100);
-
- while (true) {
- base::Value::List no_args;
- std::unique_ptr<base::Value> query_value;
- status = CallFunction(frame, kQueryResult, no_args, &query_value);
- if (status.IsError()) {
- if (status.code() == kNoSuchFrame)
- return Status(kJavaScriptError, kDocUnloadError);
- return status;
- }
-
- base::Value::Dict* result_info = query_value->GetIfDict();
- if (!result_info)
- return Status(kUnknownError, "async result info is not a dictionary");
- std::optional<int> status_code = result_info->FindInt("status");
- if (!status_code)
- return Status(kUnknownError, "async result info has no int 'status'");
- if (*status_code != kOk) {
- const std::string* message = result_info->FindString("value");
- return Status(static_cast<StatusCode>(*status_code),
- message ? *message : "");
- }
-
- if (base::Value* value = result_info->Find("value")) {
- *result = base::Value::ToUniquePtrValue(value->Clone());
- return Status(kOk);
- }
-
- // Since async-scripts return immediately, need to time period here instead.
- if (local_timeout.IsExpired())
- return Status(kTimeout);
-
- base::PlatformThread::Sleep(
- std::min(kOneHundredMs, local_timeout.GetRemainingTime()));
}
+
+ base::Value::Dict* result_info = query_value->GetIfDict();
+ if (!result_info) {
+ return Status(kUnknownError, "async result info is not a dictionary");
+ }
+ std::optional<int> status_code = result_info->FindInt("status");
+ if (!status_code) {
+ return Status(kUnknownError, "async result info has no int 'status'");
+ }
+ if (*status_code != kOk) {
+ const std::string* message = result_info->FindString("value");
+ return Status(static_cast<StatusCode>(*status_code),
+ message ? *message : "");
+ }
+ base::Value* value = result_info->Find("value");
+ if (!value) {
+ return Status{kJavaScriptError,
+ "no value field in Reuntime.callFunctionOn result"};
+ }
+ *result = base::Value::ToUniquePtrValue(value->Clone());
+ return Status(kOk);
}
void WebViewImpl::SetFrame(const std::string& new_frame_id) {
diff --git a/chrome/test/chromedriver/chrome/web_view_impl.h b/chrome/test/chromedriver/chrome/web_view_impl.h
index c705bb80..9b53ae0 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl.h
+++ b/chrome/test/chromedriver/chrome/web_view_impl.h
@@ -43,14 +43,16 @@
const BrowserInfo* browser_info,
std::unique_ptr<DevToolsClient> client,
std::optional<MobileDevice> mobile_device,
- std::string page_load_strategy);
+ std::string page_load_strategy,
+ bool autoaccept_beforeunload);
WebViewImpl(const std::string& id,
const bool w3c_compliant,
const WebViewImpl* parent,
const BrowserInfo* browser_info,
std::unique_ptr<DevToolsClient> client,
std::optional<MobileDevice> mobile_device,
- std::string page_load_strategy);
+ std::string page_load_strategy,
+ bool autoaccept_beforeunload);
~WebViewImpl() override;
std::unique_ptr<WebViewImpl> CreateChild(const std::string& session_id,
const std::string& target_id) const;
@@ -270,6 +272,7 @@
std::unique_ptr<CastTracker> cast_tracker_;
std::unique_ptr<FedCmTracker> fedcm_tracker_;
bool is_service_worker_;
+ bool autoaccept_beforeunload_ = false;
};
// Responsible for locking a WebViewImpl and its associated data structure to
diff --git a/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc b/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
index 0d91820..5fcbfd2 100644
--- a/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
+++ b/chrome/test/chromedriver/chrome/web_view_impl_unittest.cc
@@ -467,7 +467,7 @@
BrowserInfo browser_info;
WebViewImpl level1(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kEager);
+ PageLoadStrategy::kEager, true);
EXPECT_TRUE(socket_holder.ConnectSocket());
EXPECT_TRUE(StatusOk(client_ptr->SetSocket(socket_holder.Wrapper())));
std::string sessionid = "2";
@@ -493,7 +493,7 @@
BrowserInfo browser_info;
WebViewImpl parent_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kEager);
+ PageLoadStrategy::kEager, true);
EXPECT_TRUE(socket_holder.ConnectSocket());
EXPECT_TRUE(StatusOk(client_ptr->SetSocket(socket_holder.Wrapper())));
ASSERT_FALSE(parent_view.IsNonBlocking());
@@ -515,7 +515,7 @@
BrowserInfo browser_info;
WebViewImpl parent_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kNone);
+ PageLoadStrategy::kNone, true);
EXPECT_TRUE(socket_holder.ConnectSocket());
EXPECT_TRUE(StatusOk(client_ptr->SetSocket(socket_holder.Wrapper())));
std::string sessionid = "2";
@@ -535,7 +535,7 @@
BrowserInfo browser_info;
WebViewImpl parent_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kNone);
+ PageLoadStrategy::kNone, true);
EXPECT_TRUE(socket_holder.ConnectSocket());
EXPECT_TRUE(StatusOk(client_ptr->SetSocket(socket_holder.Wrapper())));
std::string sessionid = "2";
@@ -558,7 +558,7 @@
BrowserInfo browser_info;
WebViewImpl parent_view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kNormal);
+ PageLoadStrategy::kNormal, true);
EXPECT_TRUE(socket_holder.ConnectSocket());
EXPECT_TRUE(StatusOk(client_ptr->SetSocket(socket_holder.Wrapper())));
std::string sessionid = "2";
@@ -578,7 +578,7 @@
BrowserInfo browser_info;
WebViewImpl view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kEager);
+ PageLoadStrategy::kEager, true);
std::string samesite = "Strict";
base::Value::Dict dict;
dict.Set("success", true);
@@ -595,7 +595,7 @@
BrowserInfo browser_info;
WebViewImpl view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kEager);
+ PageLoadStrategy::kEager, true);
{
// Good 1
base::Value::Dict node_ref;
@@ -647,7 +647,7 @@
BrowserInfo browser_info;
WebViewImpl view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kEager);
+ PageLoadStrategy::kEager, true);
{
// Good 1
base::Value::Dict node_ref;
@@ -696,7 +696,7 @@
BrowserInfo browser_info;
WebViewImpl view(client_ptr->GetId(), false, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kEager);
+ PageLoadStrategy::kEager, true);
{
base::Value::Dict node_ref;
node_ref.Set(kElementKey, ElementReference("root", "root_loader", 25));
@@ -723,7 +723,7 @@
std::make_unique<FakeDevToolsClient>("root");
client_uptr->SetResult(GenerateResponse(4321));
WebViewImpl view("root", true, nullptr, &browser_info, std::move(client_uptr),
- std::nullopt, PageLoadStrategy::kEager);
+ std::nullopt, PageLoadStrategy::kEager, true);
view.GetFrameTracker()->SetContextIdForFrame("root", "irrelevant");
view.GetFrameTracker()->SetContextIdForFrame("good", "irrelevant");
{
@@ -761,7 +761,7 @@
std::make_unique<FakeDevToolsClient>("good");
client_uptr->SetResult(GenerateResponse(4321));
WebViewImpl view("good", true, nullptr, &browser_info, std::move(client_uptr),
- std::nullopt, PageLoadStrategy::kEager);
+ std::nullopt, PageLoadStrategy::kEager, true);
view.GetFrameTracker()->SetContextIdForFrame("root", "irrelevant");
view.GetFrameTracker()->SetContextIdForFrame("good", "irrelevant");
{
@@ -799,7 +799,7 @@
std::make_unique<FakeDevToolsClient>("root");
FakeDevToolsClient* client_ptr = client_uptr.get();
WebViewImpl view("root", true, nullptr, &browser_info, std::move(client_uptr),
- std::nullopt, PageLoadStrategy::kEager);
+ std::nullopt, PageLoadStrategy::kEager, true);
view.GetFrameTracker()->SetContextIdForFrame("root", "irrelevant");
view.GetFrameTracker()->SetContextIdForFrame("good", "irrelevant");
view.GetFrameTracker()->SetContextIdForFrame("bad", "irrelevant");
@@ -891,7 +891,7 @@
BrowserInfo browser_info;
WebViewImpl view(client_ptr->GetId(), true, nullptr, &browser_info,
std::move(client_uptr), std::nullopt,
- PageLoadStrategy::kEager);
+ PageLoadStrategy::kEager, true);
FedCmTracker* tracker = nullptr;
Status status = view.GetFedCmTracker(&tracker);
EXPECT_TRUE(StatusOk(status));
@@ -907,7 +907,7 @@
client_ptr = client_uptr.get();
view = std::make_unique<WebViewImpl>(
"root", IsW3C(), nullptr, &browser_info, std::move(client_uptr),
- std::nullopt, PageLoadStrategy::kEager);
+ std::nullopt, PageLoadStrategy::kEager, true);
view->GetFrameTracker()->SetContextIdForFrame("root", "irrelevant");
view->GetFrameTracker()->SetContextIdForFrame("good", "irrelevant");
view->GetFrameTracker()->SetContextIdForFrame("bad", "irrelevant");
@@ -1104,7 +1104,7 @@
BrowserInfo browser_info;
WebViewImpl view("root", true, nullptr, &browser_info, std::move(client_uptr),
- std::nullopt, PageLoadStrategy::kEager);
+ std::nullopt, PageLoadStrategy::kEager, true);
view.GetFrameTracker()->SetContextIdForFrame("root", "irrelevant");
std::unique_ptr<base::Value> result;
@@ -1145,7 +1145,7 @@
BrowserInfo browser_info;
WebViewImpl view("root", true, nullptr, &browser_info, std::move(client_uptr),
- std::nullopt, PageLoadStrategy::kEager);
+ std::nullopt, PageLoadStrategy::kEager, true);
view.GetFrameTracker()->SetContextIdForFrame("root", "irrelevant");
std::unique_ptr<base::Value> result;
@@ -1183,7 +1183,7 @@
BrowserInfo browser_info;
WebViewImpl view("root", true, nullptr, &browser_info, std::move(client_uptr),
- std::nullopt, PageLoadStrategy::kEager);
+ std::nullopt, PageLoadStrategy::kEager, true);
view.GetFrameTracker()->SetContextIdForFrame("root", "irrelevant");
std::unique_ptr<base::Value> result;
diff --git a/chrome/test/chromedriver/chrome_launcher.cc b/chrome/test/chromedriver/chrome_launcher.cc
index a559362..7ee02f56 100644
--- a/chrome/test/chromedriver/chrome_launcher.cc
+++ b/chrome/test/chromedriver/chrome_launcher.cc
@@ -253,7 +253,7 @@
// disable throttling all together.
// TODO(crbug.com/chromedriver/4762): Remove after the Mapper is moved away
// from the tab.
- if (capabilities.webSocketUrl) {
+ if (capabilities.web_socket_url) {
switches.SetSwitch("disable-background-timer-throttling");
}
@@ -447,7 +447,8 @@
chrome = std::make_unique<ChromeRemoteImpl>(
browser_info, capabilities.window_types,
std::move(devtools_websocket_client), std::move(devtools_event_listeners),
- capabilities.mobile_device, capabilities.page_load_strategy);
+ capabilities.mobile_device, capabilities.page_load_strategy,
+ !capabilities.web_socket_url);
return Status(kOk);
}
@@ -759,20 +760,18 @@
std::move(devtools_event_listeners), capabilities.mobile_device,
capabilities.page_load_strategy, std::move(process), command,
&user_data_dir_temp_dir, &extension_dir,
- capabilities.network_emulation_enabled);
+ capabilities.network_emulation_enabled, !capabilities.web_socket_url);
if (!capabilities.extension_load_timeout.is_zero()) {
- for (size_t i = 0; i < extension_bg_pages.size(); ++i) {
- VLOG(0) << "Waiting for extension bg page load: "
- << extension_bg_pages[i];
+ for (const std::string& url : extension_bg_pages) {
+ VLOG(0) << "Waiting for extension bg page load: " << url;
std::unique_ptr<WebView> web_view;
status = chrome_desktop->WaitForPageToLoad(
- extension_bg_pages[i], capabilities.extension_load_timeout, &web_view,
- w3c_compliant);
+ url, capabilities.extension_load_timeout, &web_view, w3c_compliant);
if (status.IsError()) {
- return Status(kSessionNotCreated,
- "failed to wait for extension background page to load: " +
- extension_bg_pages[i],
- status);
+ return Status(
+ kSessionNotCreated,
+ "failed to wait for extension background page to load: " + url,
+ status);
}
}
}
@@ -847,7 +846,7 @@
browser_info, capabilities.window_types,
std::move(devtools_websocket_client), std::move(devtools_event_listeners),
capabilities.mobile_device, capabilities.page_load_strategy,
- std::move(device));
+ std::move(device), !capabilities.web_socket_url);
return Status(kOk);
}
@@ -907,21 +906,19 @@
std::move(devtools_event_listeners), capabilities.mobile_device,
capabilities.page_load_strategy, std::move(dummy_process), command,
&user_data_dir_temp_dir, &extension_dir,
- capabilities.network_emulation_enabled);
+ capabilities.network_emulation_enabled, !capabilities.web_socket_url);
if (!capabilities.extension_load_timeout.is_zero()) {
- for (size_t i = 0; i < extension_bg_pages.size(); ++i) {
- VLOG(0) << "Waiting for extension bg page load: "
- << extension_bg_pages[i];
+ for (const std::string& url : extension_bg_pages) {
+ VLOG(0) << "Waiting for extension bg page load: " << url;
std::unique_ptr<WebView> web_view;
status = chrome_impl->WaitForPageToLoad(
- extension_bg_pages[i], capabilities.extension_load_timeout, &web_view,
- w3c_compliant);
+ url, capabilities.extension_load_timeout, &web_view, w3c_compliant);
if (status.IsError()) {
- return Status(kSessionNotCreated,
- "failed to wait for extension background page to load: " +
- extension_bg_pages[i],
- status);
+ return Status(
+ kSessionNotCreated,
+ "failed to wait for extension background page to load: " + url,
+ status);
}
}
}
diff --git a/chrome/test/chromedriver/js/execute_async_script.js b/chrome/test/chromedriver/js/execute_async_script.js
index a0735e6..a210c48 100644
--- a/chrome/test/chromedriver/js/execute_async_script.js
+++ b/chrome/test/chromedriver/js/execute_async_script.js
@@ -14,23 +14,6 @@
};
/**
- * Dictionary key for asynchronous script info.
- * @const
- */
-var ASYNC_INFO_KEY = '$chrome_asyncScriptInfo';
-
-/**
-* Return the information of asynchronous script execution.
-*
-* @return {Object<?>} Information of asynchronous script execution.
-*/
-function getAsyncScriptInfo() {
- if (!(ASYNC_INFO_KEY in document))
- document[ASYNC_INFO_KEY] = {'id': 0};
- return document[ASYNC_INFO_KEY];
-}
-
-/**
* Execute the given script and save its asynchronous result.
*
* If script1 finishes after script2 is executed, then script1's result will be
@@ -46,61 +29,46 @@
* exception occurs during the script, and an additional error callback will
* be supplied to the script.
*/
-function executeAsyncScript(script, args, isUserSupplied) {
- let resolveHandle;
- let rejectHandle;
+async function executeAsyncScript(script, args, isUserSupplied) {
const Promise = window.cdc_adoQpoasnfa76pfcZLmcfl_Promise || window.Promise;
- var promise = new Promise((resolve, reject) => {
- resolveHandle = resolve;
- rejectHandle = reject;
- });
- const info = getAsyncScriptInfo();
- info.id++;
- delete info.result;
- const id = info.id;
-
function isThenable(value) {
return typeof value === 'object' && typeof value.then === 'function';
}
- function report(status, value) {
- if (id != info.id)
- return;
- info.id++;
- // Undefined value is skipped when the object is converted to JSON.
- // Replace it with null so we don't lose the value.
- if (value === undefined)
- value = null;
- info.result = {status: status, value: value};
- }
function reportValue(value) {
- report(StatusCode.OK, value);
+ return {status: StatusCode.OK, value: value};
}
- function reportScriptError(error) {
+ function reportError(error) {
var code = isUserSupplied ? StatusCode.JAVASCRIPT_ERROR :
(error.code || StatusCode.UNKNOWN_ERROR);
var message = error.message;
if (error.stack) {
message += "\nJavaScript stack:\n" + error.stack;
}
- report(code, message);
+ return {status: code, value: message};
}
- promise.then(reportValue).catch(reportScriptError);
- args.push(resolveHandle);
- if (!isUserSupplied)
- args.push(rejectHandle);
- try {
- const scriptResult = new Function(script).apply(null, args);
- // The return value is only considered if it is a promise.
- if (isThenable(scriptResult)) {
- const resolvedPromise = Promise.resolve(scriptResult);
- resolvedPromise.then((value) => {
- // Must be thenable if user-supplied.
- if (!isUserSupplied || isThenable(value))
- resolveHandle(value);
- })
- .catch(rejectHandle);
+ var promise = new Promise((resolve, reject) => {
+ args.push(resolve);
+ if (!isUserSupplied) {
+ args.push(reject);
}
- } catch (error) {
- rejectHandle(error);
- }
+ try {
+ let scriptResult = new Function(script).apply(null, args);
+ if (isThenable(scriptResult)) {
+ const resolvedPromise = Promise.resolve(scriptResult);
+ resolvedPromise.then((value) => {
+ // Must be thenable if user-supplied.
+ if (!isUserSupplied || isThenable(value))
+ resolve(value);
+ })
+ .catch(reject);
+ }
+ } catch (error) {
+ reject(error);
+ }
+ });
+ return await promise.then((result) => {
+ return reportValue(result);
+ }).catch((error) => {
+ return reportError(error);
+ });
}
diff --git a/chrome/test/chromedriver/js/execute_async_script_test.html b/chrome/test/chromedriver/js/execute_async_script_test.html
index 1c4ff5d..30aa597 100644
--- a/chrome/test/chromedriver/js/execute_async_script_test.html
+++ b/chrome/test/chromedriver/js/execute_async_script_test.html
@@ -4,117 +4,73 @@
<script src='execute_async_script.js'></script>
<script>
-function resetAsyncScriptInfo() {
- delete document[ASYNC_INFO_KEY];
-}
-
-function waitForResultToPopulate(callback) {
- const info = getAsyncScriptInfo();
- setTimeout(() => {
- if (info.result)
- callback(info);
- else
- waitForResultToPopulate(callback);
- }, 10);
-}
-
function testUserScriptThrows(runner) {
- resetAsyncScriptInfo();
- executeAsyncScript('f(123);', [], true);
- waitForResultToPopulate((info) => {
- assertEquals(StatusCode.JAVASCRIPT_ERROR, info.result.status);
+ executeAsyncScript('f(123);', [], true).then((result)=>{
+ assertEquals(StatusCode.JAVASCRIPT_ERROR, result.status);
runner.continueTesting();
- });
- runner.waitForAsync();
+ });
+ runner.waitForAsync("StatusCode.JAVASCRIPT_ERROR");
}
function testScriptThrows(runner) {
- resetAsyncScriptInfo();
- executeAsyncScript('f(123);', [], false);
- waitForResultToPopulate((info) => {
- assertEquals(StatusCode.UNKNOWN_ERROR, info.result.status);
+ executeAsyncScript('f(123);', [], false).then((result)=>{
+ assertEquals(StatusCode.UNKNOWN_ERROR, result.status);
runner.continueTesting();
- });
- runner.waitForAsync();
+ });
+ runner.waitForAsync("StatusCode.UNKNOWN_ERROR");
}
function testUserScriptWithArgs(runner) {
- resetAsyncScriptInfo();
-
- var injectedArgs = null;
+ let injectedArgs = null;
function captureArguments(args) {
injectedArgs = args;
}
// Pass function captureArguments as the first argument. It is used to capture
// the injected arguments to the following script.
- var script =
+ const script =
'var args = arguments; args[0](args); args[args.length - 1](args[1]);';
- var script_args = [captureArguments, 1];
- executeAsyncScript(script, script_args, true);
- waitForResultToPopulate((info) => {
- assertEquals(3, injectedArgs.length);
- assertEquals(captureArguments, injectedArgs[0]);
- assertEquals(1, injectedArgs[1]);
+ const script_args = [captureArguments, 1];
+ executeAsyncScript(script, script_args, true)
+ .then((result)=>{
+ assertEquals(3, injectedArgs.length);
+ assertEquals(captureArguments, injectedArgs[0]);
+ assertEquals(1, injectedArgs[1]);
- assertEquals(0, info.result.status);
- assertEquals(1, info.result.value);
- assertEquals(2, info.id);
- runner.continueTesting();
- });
- runner.waitForAsync();
+ assertEquals(0, result.status);
+ assertEquals(1, result.value);
+ runner.continueTesting();
+ });
+ runner.waitForAsync("arguments are captured");
}
function testNonUserScriptCallback(runner) {
- resetAsyncScriptInfo();
- executeAsyncScript('arguments[1](arguments[0])', [33], false);
- waitForResultToPopulate((info) => {
- assertEquals(0, info.result.status);
- assertEquals(33, info.result.value);
- runner.continueTesting();
- });
- runner.waitForAsync();
+ executeAsyncScript('arguments[1](arguments[0])', [33], false)
+ .then((result)=>{
+ assertEquals(0, result.status);
+ assertEquals(33, result.value);
+ runner.continueTesting();
+ });
+ runner.waitForAsync('user script callback');
}
function testNonUserScriptCustomError(runner) {
- resetAsyncScriptInfo();
- executeAsyncScript('arguments[2](new Error("ERR"))', [33], false);
- waitForResultToPopulate((info) => {
- assertEquals(StatusCode.UNKNOWN_ERROR, info.result.status);
- assertEquals(0, info.result.value.indexOf('ERR'));
- runner.continueTesting();
- });
- runner.waitForAsync();
+ executeAsyncScript('arguments[2](new Error("ERR"))', [33], false)
+ .then((result)=>{
+ assertEquals(StatusCode.UNKNOWN_ERROR, result.status);
+ assertEquals(0, result.value.indexOf('ERR'));
+ runner.continueTesting();
+ });
+ runner.waitForAsync("custom error");
}
function testNonUserScriptCustomErrorCode(runner) {
- resetAsyncScriptInfo();
executeAsyncScript('var e = new Error("ERR"); e.code = 111; arguments[1](e)',
- [], false);
- waitForResultToPopulate((info) => {
- assertEquals(111, info.result.status);
- assertEquals(0, info.result.value.indexOf('ERR'));
- runner.continueTesting();
+ [], false).then((result)=>{
+ assertEquals(111, result.status);
+ assertEquals(0, result.value.indexOf('ERR'));
+ runner.continueTesting();
});
- runner.waitForAsync();
-}
-
-function testFirstScriptFinishAfterSecondScriptExecute(runner) {
- resetAsyncScriptInfo();
-
- executeAsyncScript(
- 'var f = arguments[0]; setTimeout(function(){ f(1); }, 100000);', []);
- var info = getAsyncScriptInfo();
- assert(!info.hasOwnProperty('result'));
- assertEquals(1, info.id);
-
- executeAsyncScript('var fn = arguments[0]; fn(2);', []);
- waitForResultToPopulate((info) => {
- assertEquals(0, info.result.status);
- assertEquals(2, info.result.value);
- assertEquals(3, info.id);
- runner.continueTesting();
- });
- runner.waitForAsync();
+ runner.waitForAsync("custom error with custom error code");
}
</script>
diff --git a/chrome/test/chromedriver/log_replay/chrome_replay_impl.cc b/chrome/test/chromedriver/log_replay/chrome_replay_impl.cc
index 722bf4c..ed2676cf 100644
--- a/chrome/test/chromedriver/log_replay/chrome_replay_impl.cc
+++ b/chrome/test/chromedriver/log_replay/chrome_replay_impl.cc
@@ -20,7 +20,8 @@
const base::CommandLine& command,
base::ScopedTempDir* user_data_dir,
base::ScopedTempDir* extension_dir,
- bool network_emulation_enabled)
+ bool network_emulation_enabled,
+ bool autoaccept_beforeunload)
: ChromeDesktopImpl(std::move(browser_info),
std::move(window_types),
std::move(websocket_client),
@@ -31,7 +32,8 @@
command,
user_data_dir,
extension_dir,
- network_emulation_enabled) {}
+ network_emulation_enabled,
+ autoaccept_beforeunload) {}
ChromeReplayImpl::~ChromeReplayImpl() = default;
diff --git a/chrome/test/chromedriver/log_replay/chrome_replay_impl.h b/chrome/test/chromedriver/log_replay/chrome_replay_impl.h
index 43d743b..2848163 100644
--- a/chrome/test/chromedriver/log_replay/chrome_replay_impl.h
+++ b/chrome/test/chromedriver/log_replay/chrome_replay_impl.h
@@ -28,7 +28,8 @@
const base::CommandLine& command,
base::ScopedTempDir* user_data_dir,
base::ScopedTempDir* extension_dir,
- bool network_emulation_enabled);
+ bool network_emulation_enabled,
+ bool autoaccept_beforeunload);
~ChromeReplayImpl() override;
// A no-op: all this does in DesktopChromeImpl is kill the Chrome process.
diff --git a/chrome/test/chromedriver/navigation_tracker_integrationtest.cc b/chrome/test/chromedriver/navigation_tracker_integrationtest.cc
index ad6b94c4..ca50ea1 100644
--- a/chrome/test/chromedriver/navigation_tracker_integrationtest.cc
+++ b/chrome/test/chromedriver/navigation_tracker_integrationtest.cc
@@ -173,7 +173,7 @@
ASSERT_TRUE(StatusOk(status));
WebViewImpl web_view(view_info->id, true, nullptr, &browser_info_,
std::move(client), std::nullopt,
- PageLoadStrategy::kNormal);
+ PageLoadStrategy::kNormal, true);
web_view.AttachTo(browser_client_.get());
http_server_.SetDataForPath("test.html", "<span>DONE!</span>");
diff --git a/chrome/test/chromedriver/server/http_handler.cc b/chrome/test/chromedriver/server/http_handler.cc
index 8fd52ea..d6f13e17 100644
--- a/chrome/test/chromedriver/server/http_handler.cc
+++ b/chrome/test/chromedriver/server/http_handler.cc
@@ -1528,7 +1528,9 @@
default:
DCHECK(false);
// Examples of unexpected codes:
- // * kChromeNotReachable - kSessionNotCreated must be returned instead
+ // * kChromeNotReachable: kSessionNotCreated must be returned instead;
+ // * kNavigationDetectedByRemoteEnd: kUnknownError must be returned
+ // instead.
response = std::make_unique<net::HttpServerResponseInfo>(
net::HTTP_INTERNAL_SERVER_ERROR);
break;
diff --git a/chrome/test/chromedriver/session.h b/chrome/test/chromedriver/session.h
index c38f01f..d337d457 100644
--- a/chrome/test/chromedriver/session.h
+++ b/chrome/test/chromedriver/session.h
@@ -114,7 +114,7 @@
const std::string id;
bool w3c_compliant;
- bool webSocketUrl = false;
+ bool web_socket_url = false;
bool quit;
bool detach;
bool awaiting_bidi_response = false;
diff --git a/chrome/test/chromedriver/session_commands.cc b/chrome/test/chromedriver/session_commands.cc
index 4b36413f..7b38fa1b 100644
--- a/chrome/test/chromedriver/session_commands.cc
+++ b/chrome/test/chromedriver/session_commands.cc
@@ -259,7 +259,7 @@
caps.Set("hasTouchScreen", session->chrome->HasTouchScreen());
}
- if (session->webSocketUrl) {
+ if (session->web_socket_url) {
caps.Set("webSocketUrl",
"ws://" + session->host + "/session/" + session->id);
}
@@ -300,7 +300,7 @@
// |session| will own the |CommandListener|s.
session->command_listeners.swap(command_listeners);
- if (session->webSocketUrl) {
+ if (session->web_socket_url) {
// Suffixes used with the client channels.
std::string client_suffixes[] = {Session::kChannelSuffix,
Session::kNoChannelSuffix,
@@ -350,7 +350,7 @@
*value = std::make_unique<base::Value>(session->capabilities->Clone());
}
- if (session->webSocketUrl) {
+ if (session->web_socket_url) {
WebView* web_view = nullptr;
status = session->GetTargetWindow(&web_view);
if (status.IsError())
@@ -457,7 +457,7 @@
session->script_timeout = capabilities->script_timeout;
session->strict_file_interactability =
capabilities->strict_file_interactability;
- session->webSocketUrl = capabilities->webSocketUrl;
+ session->web_socket_url = capabilities->web_socket_url;
Log::Level driver_level = Log::kWarning;
if (capabilities->logging_prefs.count(WebDriverLog::kDriverType))
driver_level = capabilities->logging_prefs[WebDriverLog::kDriverType];
@@ -776,7 +776,7 @@
if (status.IsError())
return status;
bool is_last_web_view = web_view_ids.size() == 1u;
- if (session->webSocketUrl) {
+ if (session->web_socket_url) {
is_last_web_view = web_view_ids.size() <= 2u;
}
web_view_ids.clear();
@@ -850,7 +850,7 @@
if (status.IsError())
return status;
- if (session->webSocketUrl) {
+ if (session->web_socket_url) {
auto it =
base::ranges::find(web_view_ids, session->bidi_mapper_web_view_id);
if (it != web_view_ids.end()) {
diff --git a/chrome/test/chromedriver/test/run_py_tests.py b/chrome/test/chromedriver/test/run_py_tests.py
index 6ab046d..7dea4d3 100755
--- a/chrome/test/chromedriver/test/run_py_tests.py
+++ b/chrome/test/chromedriver/test/run_py_tests.py
@@ -1169,6 +1169,24 @@
with self.assertRaises(chromedriver.DetachedShadowRoot):
self._driver.ExecuteScript("return true;", shadow)
+ def testExecuteAsyncScriptWithResolve(self):
+ self.assertEqual(
+ 10,
+ self._driver.ExecuteAsyncScript(
+ 'arguments[0](10)'))
+ self.assertEqual(
+ 'one',
+ self._driver.ExecuteAsyncScript(
+ 'arguments[0]("one")'))
+ self.assertEqual(
+ 0.123,
+ self._driver.ExecuteAsyncScript(
+ 'arguments[0](0.123)'))
+ self.assertEqual(
+ [1, 2.2, 'three'],
+ self._driver.ExecuteAsyncScript(
+ 'arguments[0]([1, 2.2, "three"])'))
+
def testExecuteAsyncScript(self):
self._driver.SetTimeouts({'script': 3000})
self.assertRaises(
@@ -2452,8 +2470,32 @@
self._driver.ExecuteScript('window.onbeforeunload=function(){return true}')
self._driver.FindElement('tag name', 'body').Click()
self._driver.GoBack()
- self.assertTrue(self._driver.IsAlertOpen())
- self._driver.HandleAlert(True)
+ self.assertFalse(self._driver.IsAlertOpen())
+
+ def testAlertHandlingOnNavigation(self):
+ self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
+ self._driver.ExecuteScript('window.onbeforeunload=function(){return true}')
+ self._driver.FindElement('tag name', 'body').Click()
+ self._driver.ExecuteAsyncScript('''
+ const [url, resolve] = arguments;
+ window.location.href = url;
+ resolve(url);
+ ''', self.GetHttpUrlForFile('/chromedriver/empty.html'))
+ self.assertFalse(self._driver.IsAlertOpen())
+
+ def testAlertHandlingOnNavigationNoResolve(self):
+ self._driver.Load(self.GetHttpUrlForFile('/chromedriver/page_test.html'))
+ self._driver.ExecuteScript('window.onbeforeunload=function(){return true}')
+ self._driver.FindElement('tag name', 'body').Click()
+ self._driver.SetTimeouts({'script': 100})
+
+ # The following script never calls resolve. Therefore it times out.
+ with self.assertRaises(chromedriver.ScriptTimeout):
+ self._driver.ExecuteAsyncScript('''
+ const [url, resolve] = arguments;
+ window.location.href = url;
+ ''', self.GetHttpUrlForFile('/chromedriver/empty.html'))
+
self.assertFalse(self._driver.IsAlertOpen())
def testRefresh(self):
@@ -2485,10 +2527,7 @@
self._driver.ExecuteScript('window.onbeforeunload=function(){return true}')
self._driver.FindElement('tag name', 'body').Click()
self._driver.Refresh()
- self.assertTrue(self._driver.IsAlertOpen())
- self.assertRaises(chromedriver.UnsupportedOperation,
- self._driver.HandleAlert,
- True, 'textToOnBeforeUnload')
+ self.assertFalse(self._driver.IsAlertOpen())
def testAlertOnNewWindow(self):
self._driver.Load(self.GetHttpUrlForFile('/chromedriver/empty.html'))
@@ -5641,6 +5680,16 @@
self._CheckPageLoadTimeout(self._driver.GoForward)
self.assertEqual(self._initial_url, self._driver.GetCurrentUrl())
+ def testHistoryNavigationAndLoadWithPageLoadTimeout(self):
+ # Allow the page to load for the first time.
+ self._handler.send_response_event.set()
+ self._LoadHangingUrl()
+ self.assertTrue(self._handler.request_received_event.wait(1))
+
+ self._driver.GoBack()
+ self._CheckPageLoadTimeout(self._LoadHangingUrl)
+ self.assertEqual(self._initial_url, self._driver.GetCurrentUrl())
+
def testRefreshWithPageLoadTimeout(self):
# Allow the page to load for the first time.
self._handler.send_response_event.set()
diff --git a/chrome/test/chromedriver/window_commands.cc b/chrome/test/chromedriver/window_commands.cc
index ea5802062..3160862 100644
--- a/chrome/test/chromedriver/window_commands.cc
+++ b/chrome/test/chromedriver/window_commands.cc
@@ -816,9 +816,15 @@
Status status =
web_view->CallUserSyncScript(session->GetCurrentFrameId(), script, *args,
session->script_timeout, value);
- if (status.code() == kTimeout)
- return Status(kScriptTimeout);
- return status;
+ switch (status.code()) {
+ case kTimeout:
+ // Navigation has happened during script execution. Further wait would lead
+ // to timeout.
+ case kNavigationDetectedByRemoteEnd:
+ return Status(kScriptTimeout);
+ default:
+ return status;
+ }
}
Status ExecuteExecuteAsyncScript(Session* session,
@@ -842,9 +848,15 @@
Status status = web_view->CallUserAsyncFunction(
session->GetCurrentFrameId(), "async function(){" + script + "}", *args,
session->script_timeout, value);
- if (status.code() == kTimeout)
- return Status(kScriptTimeout);
- return status;
+ switch (status.code()) {
+ case kTimeout:
+ // Navigation has happened during script execution. Further wait would lead
+ // to timeout.
+ case kNavigationDetectedByRemoteEnd:
+ return Status(kScriptTimeout);
+ default:
+ return status;
+ }
}
Status ExecuteNewWindow(Session* session,