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_
...@@ -2140,71 +2140,102 @@ void TestRunnerBindings::NotImplemented(const gin::Arguments& args) {} ...@@ -2140,71 +2140,102 @@ void TestRunnerBindings::NotImplemented(const gin::Arguments& args) {}
TestRunner::WorkQueue::WorkQueue(TestRunner* controller) TestRunner::WorkQueue::WorkQueue(TestRunner* controller)
: controller_(controller) {} : controller_(controller) {}
TestRunner::WorkQueue::~WorkQueue() {
Reset();
}
void TestRunner::WorkQueue::ProcessWorkSoon() {
// We delay processing queued work to avoid recursion problems, and to avoid
// running tasks in the middle of a navigation call stack, where blink and
// content may have inconsistent states halfway through being updated.
blink::scheduler::GetSingleThreadTaskRunnerForTesting()->PostTask(
FROM_HERE, base::BindOnce(&TestRunner::WorkQueue::ProcessWork,
weak_factory_.GetWeakPtr()));
}
void TestRunner::WorkQueue::Reset() { void TestRunner::WorkQueue::Reset() {
frozen_ = false; // Set values in a TrackedDictionary |states_| to avoid accessing missing
finished_loading_ = false; // values.
while (!queue_.empty()) { set_frozen(false);
delete queue_.front(); set_has_items(false);
queue_.pop_front(); states_.ResetChangeTracking();
} set_loading(true);
} }
void TestRunner::WorkQueue::AddWork(WorkItem* work) { void TestRunner::WorkQueue::AddWork(mojom::WorkItemPtr work_item) {
if (frozen_) { if (is_frozen())
delete work;
return; return;
} controller_->GetWebTestControlHostRemote()->WorkItemAdded(
queue_.push_back(work); std::move(work_item));
set_has_items(true);
OnStatesChanged();
} }
void TestRunner::WorkQueue::ProcessWork() { void TestRunner::WorkQueue::RequestWork() {
WebFrameTestProxy* in_process_main_frame = controller_->GetWebTestControlHostRemote()->RequestWorkItem();
controller_->FindInProcessMainWindowMainFrame(); }
if (!in_process_main_frame)
return;
while (!queue_.empty()) {
finished_loading_ = false; // Watch for loading finishing inside Run().
bool started_load = queue_.front()->Run(controller_, in_process_main_frame);
delete queue_.front();
queue_.pop_front();
void TestRunner::WorkQueue::ProcessWorkItem(mojom::WorkItemPtr work_item) {
// Watch for loading finishing inside ProcessWorkItemInternal().
set_loading(true);
bool started_load = ProcessWorkItemInternal(std::move(work_item));
if (started_load) { if (started_load) {
// If a load started, and didn't complete inside of Run(), then mark // If a load started, and didn't complete inside of
// the load as running. // ProcessWorkItemInternal(), then mark the load as running.
if (!finished_loading_) if (loading_)
controller_->frame_will_start_load_ = true; controller_->frame_will_start_load_ = true;
// Quit doing work once a load is in progress. // Wait for an ongoing load to complete before requesting the next WorkItem.
//
// TODO(danakj): We could avoid the post-task of ProcessWork() by not
// early-outting here if |finished_loading_|. Since load finished we
// could keep running work. And in RemoveLoadingFrame() instead of
// calling ProcessWorkSoon() unconditionally, only call it if we're not
// already inside ProcessWork().
return; return;
} }
RequestWork();
}
bool TestRunner::WorkQueue::ProcessWorkItemInternal(
mojom::WorkItemPtr work_item) {
switch (work_item->which()) {
case mojom::WorkItem::Tag::BACK_FORWARD: {
mojom::WorkItemBackForwardPtr& item_back_forward =
work_item->get_back_forward();
controller_->GoToOffset(item_back_forward->distance);
return true; // TODO(danakj): Did it really start a navigation?
}
case mojom::WorkItem::Tag::LOADING_SCRIPT: {
mojom::WorkItemLoadingScriptPtr& item_loading_script =
work_item->get_loading_script();
WebFrameTestProxy* main_frame =
controller_->FindInProcessMainWindowMainFrame();
DCHECK(main_frame);
main_frame->GetWebFrame()->ExecuteScript(blink::WebScriptSource(
blink::WebString::FromUTF8(item_loading_script->script)));
return true; // TODO(danakj): Did it really start a navigation?
}
case mojom::WorkItem::Tag::NON_LOADING_SCRIPT: {
mojom::WorkItemNonLoadingScriptPtr& item_non_loading_script =
work_item->get_non_loading_script();
WebFrameTestProxy* main_frame =
controller_->FindInProcessMainWindowMainFrame();
DCHECK(main_frame);
main_frame->GetWebFrame()->ExecuteScript(blink::WebScriptSource(
blink::WebString::FromUTF8(item_non_loading_script->script)));
return false;
} }
case mojom::WorkItem::Tag::LOAD: {
mojom::WorkItemLoadPtr& item_load = work_item->get_load();
controller_->LoadURLForFrame(GURL(item_load->url), item_load->target);
return true; // TODO(danakj): Did it really start a navigation?
}
case mojom::WorkItem::Tag::RELOAD:
controller_->Reload();
return true;
}
NOTREACHED();
return false;
}
// If there was no navigation stated, there may be no more tasks in the void TestRunner::WorkQueue::ReplicateStates(
// system. We can safely finish the test here as we're not in the middle const base::DictionaryValue& values) {
// of a navigation call stack, and ProcessWork() was a posted task. states_.ApplyUntrackedChanges(values);
if (!has_items())
controller_->FinishTestIfReady(); controller_->FinishTestIfReady();
} }
void TestRunner::WorkQueue::OnStatesChanged() {
if (states_.changed_values().empty())
return;
controller_->GetWebTestControlHostRemote()->WorkQueueStatesChanged(
states_.changed_values().Clone());
states_.ResetChangeTracking();
}
TestRunner::TestRunner() TestRunner::TestRunner()
: work_queue_(this), : work_queue_(this),
test_content_settings_client_(this, &web_test_runtime_flags_) { test_content_settings_client_(this, &web_test_runtime_flags_) {
...@@ -2520,15 +2551,17 @@ void TestRunner::RemoveLoadingFrame(blink::WebFrame* frame) { ...@@ -2520,15 +2551,17 @@ void TestRunner::RemoveLoadingFrame(blink::WebFrame* frame) {
// No more new work after the first complete load. // No more new work after the first complete load.
work_queue_.set_frozen(true); work_queue_.set_frozen(true);
work_queue_.OnStatesChanged();
// Inform the work queue that any load it started is done, in case it is // Inform the work queue that any load it started is done, in case it is
// still inside ProcessWork(). // still inside ProcessWorkItem().
work_queue_.set_finished_loading(); work_queue_.set_loading(false);
// testRunner.waitUntilDone() will pause the work queue if it is being used by // testRunner.waitUntilDone() will pause the work queue if it is being used by
// the test, until testRunner.notifyDone() is called. However this can only be // the test, until testRunner.notifyDone() is called. However this can only be
// done once. // done once.
if (!web_test_runtime_flags_.wait_until_done() || did_notify_done_) if (!web_test_runtime_flags_.wait_until_done() || did_notify_done_)
work_queue_.ProcessWorkSoon(); work_queue_.RequestWork();
} }
void TestRunner::FinishTestIfReady() { void TestRunner::FinishTestIfReady() {
...@@ -2552,7 +2585,7 @@ void TestRunner::FinishTestIfReady() { ...@@ -2552,7 +2585,7 @@ void TestRunner::FinishTestIfReady() {
// If there are tasks in the queue still, we must wait for them before // If there are tasks in the queue still, we must wait for them before
// finishing the test. // finishing the test.
if (!work_queue_.is_empty()) if (work_queue_.has_items())
return; return;
// If waiting for testRunner.notifyDone() then we can not end the test. // If waiting for testRunner.notifyDone() then we can not end the test.
...@@ -2650,19 +2683,6 @@ bool TestRunner::ShouldDumpNavigationPolicy() const { ...@@ -2650,19 +2683,6 @@ bool TestRunner::ShouldDumpNavigationPolicy() const {
return web_test_runtime_flags_.dump_navigation_policy(); return web_test_runtime_flags_.dump_navigation_policy();
} }
class WorkItemBackForward : public TestRunner::WorkItem {
public:
explicit WorkItemBackForward(int distance) : distance_(distance) {}
bool Run(TestRunner* test_runner, WebFrameTestProxy*) override {
test_runner->GoToOffset(distance_);
return true; // FIXME: Did it really start a navigation?
}
private:
int distance_;
};
WebFrameTestProxy* TestRunner::FindInProcessMainWindowMainFrame() { WebFrameTestProxy* TestRunner::FindInProcessMainWindowMainFrame() {
for (WebFrameTestProxy* main_frame : main_frames_) { for (WebFrameTestProxy* main_frame : main_frames_) {
WebViewTestProxy* view = main_frame->GetWebViewTestProxy(); WebViewTestProxy* view = main_frame->GetWebViewTestProxy();
...@@ -2691,82 +2711,45 @@ void TestRunner::NotifyDone() { ...@@ -2691,82 +2711,45 @@ void TestRunner::NotifyDone() {
} }
void TestRunner::QueueBackNavigation(int how_far_back) { void TestRunner::QueueBackNavigation(int how_far_back) {
work_queue_.AddWork(new WorkItemBackForward(-how_far_back)); work_queue_.AddWork(mojom::WorkItem::NewBackForward(
mojom::WorkItemBackForward::New(-how_far_back)));
} }
void TestRunner::QueueForwardNavigation(int how_far_forward) { void TestRunner::QueueForwardNavigation(int how_far_forward) {
work_queue_.AddWork(new WorkItemBackForward(how_far_forward)); work_queue_.AddWork(mojom::WorkItem::NewBackForward(
mojom::WorkItemBackForward::New(how_far_forward)));
} }
class WorkItemReload : public TestRunner::WorkItem {
public:
bool Run(TestRunner* test_runner, WebFrameTestProxy*) override {
test_runner->Reload();
return true;
}
};
void TestRunner::QueueReload() { void TestRunner::QueueReload() {
work_queue_.AddWork(new WorkItemReload()); work_queue_.AddWork(mojom::WorkItem::NewReload(mojom::WorkItemReload::New()));
} }
class WorkItemLoadingScript : public TestRunner::WorkItem {
public:
explicit WorkItemLoadingScript(const std::string& script) : script_(script) {}
bool Run(TestRunner*, WebFrameTestProxy* main_frame) override {
main_frame->GetWebFrame()->ExecuteScript(
blink::WebScriptSource(blink::WebString::FromUTF8(script_)));
return true; // FIXME: Did it really start a navigation?
}
private:
std::string script_;
};
void TestRunner::QueueLoadingScript(const std::string& script) { void TestRunner::QueueLoadingScript(const std::string& script) {
work_queue_.AddWork(new WorkItemLoadingScript(script)); work_queue_.AddWork(mojom::WorkItem::NewLoadingScript(
mojom::WorkItemLoadingScript::New(script)));
} }
class WorkItemNonLoadingScript : public TestRunner::WorkItem {
public:
explicit WorkItemNonLoadingScript(const std::string& script)
: script_(script) {}
bool Run(TestRunner*, WebFrameTestProxy* main_frame) override {
main_frame->GetWebFrame()->ExecuteScript(
blink::WebScriptSource(blink::WebString::FromUTF8(script_)));
return false;
}
private:
std::string script_;
};
void TestRunner::QueueNonLoadingScript(const std::string& script) { void TestRunner::QueueNonLoadingScript(const std::string& script) {
work_queue_.AddWork(new WorkItemNonLoadingScript(script)); work_queue_.AddWork(mojom::WorkItem::NewNonLoadingScript(
mojom::WorkItemNonLoadingScript::New(script)));
} }
class WorkItemLoad : public TestRunner::WorkItem {
public:
WorkItemLoad(const GURL& url, const std::string& target)
: url_(url), target_(target) {}
bool Run(TestRunner* test_runner, WebFrameTestProxy*) override {
test_runner->LoadURLForFrame(url_, target_);
return true; // FIXME: Did it really start a navigation?
}
private:
GURL url_;
std::string target_;
};
void TestRunner::QueueLoad(const GURL& current_url, void TestRunner::QueueLoad(const GURL& current_url,
const std::string& relative_url, const std::string& relative_url,
const std::string& target) { const std::string& target) {
GURL full_url = current_url.Resolve(relative_url); GURL full_url = current_url.Resolve(relative_url);
work_queue_.AddWork(new WorkItemLoad(full_url, target)); work_queue_.AddWork(mojom::WorkItem::NewLoad(
mojom::WorkItemLoad::New(full_url.spec(), target)));
}
void TestRunner::ProcessWorkItem(mojom::WorkItemPtr work_item) {
work_queue_.ProcessWorkItem(std::move(work_item));
}
void TestRunner::ReplicateWorkQueueStates(const base::DictionaryValue& values) {
if (!test_is_running_)
return;
work_queue_.ReplicateStates(values);
} }
void TestRunner::OnTestPreferencesChanged(const TestPreferences& test_prefs, void TestRunner::OnTestPreferencesChanged(const TestPreferences& test_prefs,
......
...@@ -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