Commit 25a31d58 authored by Hitoshi Yoshida's avatar Hitoshi Yoshida Committed by Commit Bot

TestRunner: Migrate work queue from TestRunner to WebTestControlHost

Currently, the work queue of a test is managed in a TestRunner,
i.e. a render thread.
But if we swap RenderFrameHost with pro-active same-site BI swap or
Back-Forward cache, the TestRunner that controls the test can
migrate processes and can lose some information.

This CL migrates the work queue to WebTestControlHost, i.e. the
browser process, and makes the TestRunner that refers the main window
work together.


Bug: 1136383, 1132180
Change-Id: Ie95fd4c694be120b9611e4ac156fc6417bb96d87
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2469351
Commit-Queue: Hitoshi Yoshida <peria@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Reviewed-by: default avatardanakj <danakj@chromium.org>
Auto-Submit: Hitoshi Yoshida <peria@chromium.org>
Cr-Commit-Position: refs/heads/master@{#822067}
parent b5d21b74
...@@ -42,6 +42,7 @@ mojom("web_test_common_mojom") { ...@@ -42,6 +42,7 @@ mojom("web_test_common_mojom") {
static_library("web_test_common") { static_library("web_test_common") {
testonly = true testonly = true
sources = [ sources = [
"common/web_test_constants.cc",
"common/web_test_constants.h", "common/web_test_constants.h",
"common/web_test_string_util.cc", "common/web_test_string_util.cc",
"common/web_test_string_util.h", "common/web_test_string_util.h",
......
...@@ -1217,6 +1217,8 @@ void WebTestControlHost::HandleNewRenderFrameHost(RenderFrameHost* frame) { ...@@ -1217,6 +1217,8 @@ void WebTestControlHost::HandleNewRenderFrameHost(RenderFrameHost* frame) {
GetWebTestRenderThreadRemote(process_host) GetWebTestRenderThreadRemote(process_host)
->ReplicateWebTestRuntimeFlagsChanges( ->ReplicateWebTestRuntimeFlagsChanges(
accumulated_web_test_runtime_flags_changes_.Clone()); accumulated_web_test_runtime_flags_changes_.Clone());
GetWebTestRenderThreadRemote(process_host)
->ReplicateWorkQueueStates(work_queue_states_.Clone());
} }
} }
...@@ -1229,6 +1231,7 @@ void WebTestControlHost::OnTestFinished() { ...@@ -1229,6 +1231,7 @@ void WebTestControlHost::OnTestFinished() {
devtools_bindings_.reset(); devtools_bindings_.reset();
devtools_protocol_test_bindings_.reset(); devtools_protocol_test_bindings_.reset();
accumulated_web_test_runtime_flags_changes_.Clear(); accumulated_web_test_runtime_flags_changes_.Clear();
work_queue_states_.Clear();
ShellBrowserContext* browser_context = ShellBrowserContext* browser_context =
ShellContentBrowserClient::Get()->browser_context(); ShellContentBrowserClient::Get()->browser_context();
...@@ -1711,6 +1714,32 @@ void WebTestControlHost::AllowPointerLock() { ...@@ -1711,6 +1714,32 @@ void WebTestControlHost::AllowPointerLock() {
next_pointer_lock_action_ = NextPointerLockAction::kWillSucceed; next_pointer_lock_action_ = NextPointerLockAction::kWillSucceed;
} }
void WebTestControlHost::WorkItemAdded(mojom::WorkItemPtr work_item) {
// TODO(peria): Check if |work_item| comes from the main window's main frame.
// TODO(peria): Reject the item if the work queue is frozen.
work_queue_.push_back(std::move(work_item));
}
void WebTestControlHost::RequestWorkItem() {
DCHECK(main_window_);
RenderProcessHost* main_frame_process =
main_window_->web_contents()->GetRenderViewHost()->GetProcess();
if (work_queue_.empty()) {
work_queue_states_.SetBoolPath(kDictKeyWorkQueueHasItems, false);
GetWebTestRenderThreadRemote(main_frame_process)
->ReplicateWorkQueueStates(work_queue_states_.Clone());
} else {
GetWebTestRenderThreadRemote(main_frame_process)
->ProcessWorkItem(work_queue_.front()->Clone());
work_queue_.pop_front();
}
}
void WebTestControlHost::WorkQueueStatesChanged(
base::Value changed_work_queue_states) {
work_queue_states_.MergeDictionary(&changed_work_queue_states);
}
void WebTestControlHost::GoToOffset(int offset) { void WebTestControlHost::GoToOffset(int offset) {
main_window_->GoBackOrForward(offset); main_window_->GoBackOrForward(offset);
} }
......
...@@ -246,6 +246,9 @@ class WebTestControlHost : public WebContentsObserver, ...@@ -246,6 +246,9 @@ class WebTestControlHost : public WebContentsObserver,
void SetPointerLockWillFail() override; void SetPointerLockWillFail() override;
void SetPointerLockWillRespondAsynchronously() override; void SetPointerLockWillRespondAsynchronously() override;
void AllowPointerLock() override; void AllowPointerLock() override;
void WorkItemAdded(mojom::WorkItemPtr work_item) override;
void RequestWorkItem() override;
void WorkQueueStatesChanged(base::Value changed_work_queue_states) override;
void DiscardMainWindow(); void DiscardMainWindow();
void CloseTestOpenedWindows(); void CloseTestOpenedWindows();
...@@ -354,6 +357,13 @@ class WebTestControlHost : public WebContentsObserver, ...@@ -354,6 +357,13 @@ class WebTestControlHost : public WebContentsObserver,
// renderer created while test is in progress). // renderer created while test is in progress).
base::DictionaryValue accumulated_web_test_runtime_flags_changes_; base::DictionaryValue accumulated_web_test_runtime_flags_changes_;
// Work items to be processed in the TestRunner on the renderer process
// that hosts the main window's main frame.
base::circular_deque<mojom::WorkItemPtr> work_queue_;
// Properties of the work queue.
base::DictionaryValue work_queue_states_;
mojom::WebTestRendererDumpResultPtr renderer_dump_result_; mojom::WebTestRendererDumpResultPtr renderer_dump_result_;
std::string navigation_history_dump_; std::string navigation_history_dump_;
base::Optional<SkBitmap> pixel_dump_; base::Optional<SkBitmap> pixel_dump_;
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
module content.mojom; module content.mojom;
import "mojo/public/mojom/base/file_path.mojom"; import "mojo/public/mojom/base/file_path.mojom";
import "mojo/public/mojom/base/values.mojom";
import "mojo/public/mojom/base/string16.mojom"; import "mojo/public/mojom/base/string16.mojom";
import "mojo/public/mojom/base/values.mojom";
import "skia/public/mojom/bitmap.mojom"; import "skia/public/mojom/bitmap.mojom";
import "third_party/blink/public/mojom/permissions/permission_status.mojom"; import "third_party/blink/public/mojom/permissions/permission_status.mojom";
import "third_party/blink/public/mojom/webpreferences/web_preferences.mojom"; import "third_party/blink/public/mojom/webpreferences/web_preferences.mojom";
...@@ -50,6 +50,48 @@ struct WebTestRendererDumpResult { ...@@ -50,6 +50,48 @@ struct WebTestRendererDumpResult {
gfx.mojom.Rect selection_rect; gfx.mojom.Rect selection_rect;
}; };
// Tasks in the work queue. TestRunner on the main window's process manages
// the queue, but TestRunner can migrate processes on navigations, so the queue
// is stored on the browser side.
// The work queue can be populated in the first page's loading on the main
// window's main frame, and gets frozen at the end of the loading.
// Work queues on non-main windows are frozen from the beginning, and can't
// add work items.
// Navigates backward/forward in history
struct WorkItemBackForward {
// Navigation distance. If |distance| is negative, navigates to backward.
int32 distance;
};
// Navigates a frame named |target| to |url|
struct WorkItemLoad {
string url;
// If |target| is empty, it means the main frame.
string target;
};
// Reloads
struct WorkItemReload {};
// Runs JavaScript code |script| which may trigger a navigation
struct WorkItemLoadingScript {
string script;
};
// Runs JavaScript code |script| which does not trigger navigations
struct WorkItemNonLoadingScript {
string script;
};
union WorkItem {
WorkItemBackForward back_forward;
WorkItemLoad load;
WorkItemReload reload;
WorkItemLoadingScript loading_script;
WorkItemNonLoadingScript non_loading_script;
};
// Web test messages sent from the browser process to the renderer. This // Web test messages sent from the browser process to the renderer. This
// interface is associated with RenderFrameHost. // interface is associated with RenderFrameHost.
interface WebTestRenderFrame { interface WebTestRenderFrame {
...@@ -95,6 +137,13 @@ interface WebTestRenderThread { ...@@ -95,6 +137,13 @@ interface WebTestRenderThread {
// leak detection in order for the test harness to drop anything that tests // leak detection in order for the test harness to drop anything that tests
// passed in and which could cause a leak otherwise. // passed in and which could cause a leak otherwise.
ResetRendererAfterWebTest() => (); ResetRendererAfterWebTest() => ();
// Processes a work item. This is called for the process with the main
// window's main frame.
ProcessWorkItem(WorkItem work_item);
// Syncs the work queue states to the new renderer.
ReplicateWorkQueueStates(mojo_base.mojom.DictionaryValue work_queue_states);
}; };
// Web test messages sent from the renderer process to the browser. This // Web test messages sent from the renderer process to the browser. This
...@@ -244,4 +293,18 @@ interface WebTestControlHost { ...@@ -244,4 +293,18 @@ interface WebTestControlHost {
// Allow a pending pointer lock request. // Allow a pending pointer lock request.
AllowPointerLock(); AllowPointerLock();
// Notifies the browser that a work item is enqueued.
WorkItemAdded(WorkItem work_item);
// Requests the browser to send a work item. If the work queue has N items,
// there will be N+1 calls to RequestWorkItem and N replies via
// WebTestRenderThread.ProcessWorkItem. The last RequestWorkItem sets
// |has_items| bit false, and is replied via
// WebTestRenderThread.ReplicateWorkQueueStates.
RequestWorkItem();
// Notifies the browser that the work queue states changed.
WorkQueueStatesChanged(
mojo_base.mojom.DictionaryValue changed_work_queue_states);
}; };
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/web_test/common/web_test_constants.h"
namespace content {
// DictValue key to show if the work queue has something to do.
const char kDictKeyWorkQueueHasItems[] = "has_items";
} // namespace content
...@@ -9,6 +9,8 @@ namespace content { ...@@ -9,6 +9,8 @@ namespace content {
constexpr int kDefaultDatabaseQuota = -1; constexpr int kDefaultDatabaseQuota = -1;
extern const char kDictKeyWorkQueueHasItems[];
} // namespace content } // namespace content
#endif // CONTENT_WEB_TEST_COMMON_WEB_TEST_CONSTANTS_H_ #endif // CONTENT_WEB_TEST_COMMON_WEB_TEST_CONSTANTS_H_
This diff is collapsed.
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "content/web_test/common/web_test.mojom.h" #include "content/web_test/common/web_test.mojom.h"
#include "content/web_test/common/web_test_bluetooth_fake_adapter_setter.mojom.h" #include "content/web_test/common/web_test_bluetooth_fake_adapter_setter.mojom.h"
#include "content/web_test/common/web_test_constants.h"
#include "content/web_test/renderer/fake_screen_orientation_impl.h" #include "content/web_test/renderer/fake_screen_orientation_impl.h"
#include "content/web_test/renderer/gamepad_controller.h" #include "content/web_test/renderer/gamepad_controller.h"
#include "content/web_test/renderer/layout_dump.h" #include "content/web_test/renderer/layout_dump.h"
...@@ -239,19 +240,13 @@ class TestRunner { ...@@ -239,19 +240,13 @@ class TestRunner {
blink::WebString RegisterIsolatedFileSystem( blink::WebString RegisterIsolatedFileSystem(
const std::vector<base::FilePath>& file_paths); const std::vector<base::FilePath>& file_paths);
void ProcessWorkItem(mojom::WorkItemPtr work_item);
void ReplicateWorkQueueStates(const base::DictionaryValue& changed_values);
blink::WebEffectiveConnectionType effective_connection_type() const { blink::WebEffectiveConnectionType effective_connection_type() const {
return effective_connection_type_; return effective_connection_type_;
} }
// A single item in the work queue.
class WorkItem {
public:
virtual ~WorkItem() {}
// Returns true if this started a load.
virtual bool Run(TestRunner*, WebFrameTestProxy*) = 0;
};
private: private:
friend class TestRunnerBindings; friend class TestRunnerBindings;
friend class WorkQueue; friend class WorkQueue;
...@@ -259,30 +254,47 @@ class TestRunner { ...@@ -259,30 +254,47 @@ class TestRunner {
// Helper class for managing events queued by methods like QueueLoad or // Helper class for managing events queued by methods like QueueLoad or
// QueueScript. // QueueScript.
class WorkQueue { class WorkQueue {
static constexpr const char* kKeyFrozen = "frozen";
public: public:
explicit WorkQueue(TestRunner* controller); explicit WorkQueue(TestRunner* controller);
virtual ~WorkQueue(); ~WorkQueue() = default;
void ProcessWorkSoon();
// Reset the state of the class between tests. // Reset the state of the class between tests.
void Reset(); void Reset();
void AddWork(WorkItem*); void AddWork(mojom::WorkItemPtr work_item);
void RequestWork();
void ProcessWorkItem(mojom::WorkItemPtr work_item);
void ReplicateStates(const base::DictionaryValue& values);
void set_frozen(bool frozen) { frozen_ = frozen; } // Takes care of notifying the browser after a change to the state.
bool is_empty() const { return queue_.empty(); } void OnStatesChanged();
void set_finished_loading() { finished_loading_ = true; } void set_loading(bool value) { loading_ = value; }
void set_frozen(bool value) { states_.SetBoolean(kKeyFrozen, value); }
void set_has_items(bool value) {
states_.SetBoolean(kDictKeyWorkQueueHasItems, value);
}
bool has_items() const { return GetStateValue(kDictKeyWorkQueueHasItems); }
private: private:
void ProcessWork(); bool ProcessWorkItemInternal(mojom::WorkItemPtr work_item);
base::circular_deque<WorkItem*> queue_; bool is_frozen() const { return GetStateValue(kKeyFrozen); }
bool frozen_ = false;
bool finished_loading_ = false; bool GetStateValue(const char* key) const {
TestRunner* controller_; base::Optional<bool> value = states_.current_values().FindBoolPath(key);
DCHECK(value.has_value());
return value.value();
}
bool loading_ = true;
// Collection of flags to be synced with the browser process.
TrackedDictionary states_;
base::WeakPtrFactory<WorkQueue> weak_factory_{this}; TestRunner* controller_;
}; };
// If the main test window's main frame is hosted in this renderer process, // If the main test window's main frame is hosted in this renderer process,
......
...@@ -80,4 +80,17 @@ void WebTestRenderThreadObserver::ResetRendererAfterWebTest( ...@@ -80,4 +80,17 @@ void WebTestRenderThreadObserver::ResetRendererAfterWebTest(
test_runner_->ResetRendererAfterWebTest(std::move(done_callback)); test_runner_->ResetRendererAfterWebTest(std::move(done_callback));
} }
void WebTestRenderThreadObserver::ProcessWorkItem(
mojom::WorkItemPtr work_item) {
test_runner_->ProcessWorkItem(std::move(work_item));
}
void WebTestRenderThreadObserver::ReplicateWorkQueueStates(
base::Value work_queue_states) {
base::DictionaryValue* work_queue_states_dict = nullptr;
bool ok = work_queue_states.GetAsDictionary(&work_queue_states_dict);
DCHECK(ok);
test_runner_->ReplicateWorkQueueStates(*work_queue_states_dict);
}
} // namespace content } // namespace content
...@@ -39,6 +39,8 @@ class WebTestRenderThreadObserver : public RenderThreadObserver, ...@@ -39,6 +39,8 @@ class WebTestRenderThreadObserver : public RenderThreadObserver,
base::Value changed_layout_test_runtime_flags) override; base::Value changed_layout_test_runtime_flags) override;
void TestFinishedFromSecondaryRenderer() override; void TestFinishedFromSecondaryRenderer() override;
void ResetRendererAfterWebTest(base::OnceClosure done_callback) override; void ResetRendererAfterWebTest(base::OnceClosure done_callback) override;
void ProcessWorkItem(mojom::WorkItemPtr work_item) override;
void ReplicateWorkQueueStates(base::Value work_queue_states) override;
// Helper to bind this class as the mojom::WebTestRenderThread. // Helper to bind this class as the mojom::WebTestRenderThread.
void OnWebTestRenderThreadAssociatedRequest( void OnWebTestRenderThreadAssociatedRequest(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment