Update renderer visibility state using IPC from the browser process
Currently, RenderThreadImpl::RendererIsHidden() is implemented by
keeping track of the total number and the number of hidden RenderWidgets
in a renderer. If there is a non-zero amount of RenderWidgets and the
two numbers are equal, RendererIsHidden() will return true.
However, the current implementation is broken, as it does not account
for the existence of inactive RenderWidgets. Inactive widgets refer to
widgets that are created to support out-of-process iframes, but that do
not represent actual widgets. These widgets exist with the sole purpose
of maintaining state. Consequently, this widgets are never explicitly
marked as hidden or visible, they are simply considered "frozen". This
behaviour effects the implementation of RendererIsHidden(), as each time
an inactive RenderWidget is created, the total number of RenderWidgets
is incremented. However, those widgets will never be marked as hidden,
and there will always be a difference between the total number and the
number of hidden widgets. As a result, renderers that contain inactive
widgets are incorrectly considered visible.
Note that RenderProcessHostImpl already has information regarding a
renderer's backgrounded and visibility state, using
ChildProcessLauncherPriority. Rather than computing this information
twice, we can forward the information from the browser process to the
renderer process. The existing SetProcessBackgrounded IPC was modified
to SetProcessState, now taking a RenderProcessState enum as a parameter.
The process can be marked visible, hidden or backgrounded. Depending on
the state transition, RenderThreadImpl calls OnRendererHidden,
OnRendererVisible, OnRendererBackgrounded and OnRendererForegrounded at
the appropriately.
The new approach resolves a flaw in the current approach to determine
renderer visibility and ensures that we have a single source of truth
regarding a renderer's backgrounded and visibility state.
Bug: 927440
Change-Id: Ib5aa60cd515443e17a0be691c211721caaf573ac
Reviewed-on: https://chromium-review.googlesource.com/c/1448742
Commit-Queue: Aditya Keerthi <[email protected]>
Reviewed-by: Daniel Cheng <[email protected]>
Reviewed-by: Alexander Timin <[email protected]>
Reviewed-by: François Doray <[email protected]>
Reviewed-by: Alex Moshchuk <[email protected]>
Reviewed-by: Gabriel Charette <[email protected]>
Cr-Commit-Position: refs/heads/master@{#635182}
diff --git a/content/browser/renderer_host/render_process_host_impl.cc b/content/browser/renderer_host/render_process_host_impl.cc
index 581830b..1c602b8 100644
--- a/content/browser/renderer_host/render_process_host_impl.cc
+++ b/content/browser/renderer_host/render_process_host_impl.cc
@@ -4357,8 +4357,9 @@
#endif
);
- const bool should_background_changed =
+ const bool background_state_changed =
priority_.is_background() != priority.is_background();
+ const bool visibility_state_changed = priority_.visible != priority.visible;
if (priority_ == priority)
return;
@@ -4367,6 +4368,7 @@
"has_pending_views", priority.boost_for_pending_views);
priority_ = priority;
+ bool allow_background_change = true;
#if defined(OS_WIN)
// The cbstext.dll loads as a global GetMessage hook in the browser process
// and intercepts/unintercepts the kernel32 API SetPriorityClass in a
@@ -4375,7 +4377,7 @@
// which causes random crashes in the browser process. Our hack for now
// is to not invoke the SetPriorityClass API if the dll is loaded.
if (GetModuleHandle(L"cbstext.dll"))
- return;
+ allow_background_change = false;
#endif // OS_WIN
// Control the background state from the browser process, otherwise the task
@@ -4383,7 +4385,7 @@
// tasks executing at lowered priority ahead of it or simply by not being
// swiftly scheduled by the OS per the low process priority
// (http://crbug.com/398103).
- if (!run_renderer_in_process()) {
+ if (!run_renderer_in_process() && allow_background_change) {
DCHECK(child_process_launcher_.get());
DCHECK(!child_process_launcher_->IsStarting());
// Make sure to keep the pid in the trace so we can tell which process is
@@ -4396,12 +4398,24 @@
child_process_launcher_->SetProcessPriority(priority_);
}
- // Notify the child process of background state.
- if (should_background_changed) {
- GetRendererInterface()->SetProcessBackgrounded(priority_.is_background());
+ // Notify the child process of the change in state.
+ if ((background_state_changed && allow_background_change) ||
+ visibility_state_changed) {
+ SendProcessStateToRenderer();
}
}
+void RenderProcessHostImpl::SendProcessStateToRenderer() {
+ mojom::RenderProcessState process_state;
+ if (priority_.is_background())
+ process_state = mojom::RenderProcessState::kBackgrounded;
+ else if (priority_.visible)
+ process_state = mojom::RenderProcessState::kVisible;
+ else
+ process_state = mojom::RenderProcessState::kHidden;
+ GetRendererInterface()->SetProcessState(process_state);
+}
+
void RenderProcessHostImpl::OnProcessLaunched() {
// No point doing anything, since this object will be destructed soon. We
// especially don't want to send the RENDERER_PROCESS_CREATED notification,
diff --git a/content/browser/renderer_host/render_process_host_impl.h b/content/browser/renderer_host/render_process_host_impl.h
index 7434784..74af076a 100644
--- a/content/browser/renderer_host/render_process_host_impl.h
+++ b/content/browser/renderer_host/render_process_host_impl.h
@@ -588,6 +588,9 @@
// change.
void UpdateProcessPriority();
+ // Called if the backgrounded or visibility state of the process changes.
+ void SendProcessStateToRenderer();
+
// Creates a PersistentMemoryAllocator and shares it with the renderer
// process for it to store histograms from that process. The allocator is
// available for extraction by a SubprocesMetricsProvider in order to
diff --git a/content/browser/service_worker/embedded_worker_test_helper.cc b/content/browser/service_worker/embedded_worker_test_helper.cc
index f1471eb8..63122463 100644
--- a/content/browser/service_worker/embedded_worker_test_helper.cc
+++ b/content/browser/service_worker/embedded_worker_test_helper.cc
@@ -160,7 +160,9 @@
NOTREACHED();
}
void PurgePluginListCache(bool reload_pages) override { NOTREACHED(); }
- void SetProcessBackgrounded(bool backgrounded) override { NOTREACHED(); }
+ void SetProcessState(mojom::RenderProcessState process_state) override {
+ NOTREACHED();
+ }
void SetSchedulerKeepActive(bool keep_active) override { NOTREACHED(); }
void ProcessPurgeAndSuspend() override { NOTREACHED(); }
void SetIsLockedToSite() override { NOTREACHED(); }
diff --git a/content/common/renderer.mojom b/content/common/renderer.mojom
index a9f2e5c..3760989 100644
--- a/content/common/renderer.mojom
+++ b/content/common/renderer.mojom
@@ -159,6 +159,16 @@
bool scroll_view_rubber_banding;
};
+enum RenderProcessState {
+ kVisible,
+ // Hidden render processes can still be foregrounded. For example, a hidden
+ // renderer playing audio would be foregrounded.
+ kHidden,
+ // Refers to a renderer that is hidden and running at background process
+ // priority.
+ kBackgrounded,
+};
+
// The primordial Channel-associated interface implemented by a render process.
// This should be used for implementing browser-to-renderer control messages
// which need to retain FIFO with respect to legacy IPC messages.
@@ -242,8 +252,8 @@
PurgePluginListCache(bool reload_pages);
- // Tells the renderer process to enter or leave background mode.
- SetProcessBackgrounded(bool background);
+ // Tells the renderer process of a change in visibility or background state.
+ SetProcessState(RenderProcessState process_state);
// Tells the scheduler about "keep-alive" state which can be due to:
// service workers, shared workers, or fetch keep-alive.
diff --git a/content/renderer/render_thread_impl.cc b/content/renderer/render_thread_impl.cc
index 0d69486..44d96379 100644
--- a/content/renderer/render_thread_impl.cc
+++ b/content/renderer/render_thread_impl.cc
@@ -145,7 +145,6 @@
#include "services/ws/public/cpp/gpu/gpu.h"
#include "services/ws/public/mojom/constants.mojom.h"
#include "skia/ext/skia_memory_dump_provider.h"
-#include "third_party/blink/public/common/page/launching_process_state.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "third_party/blink/public/platform/web_cache.h"
#include "third_party/blink/public/platform/web_image_generator.h"
@@ -753,12 +752,6 @@
auto registry = std::make_unique<service_manager::BinderRegistry>();
InitializeWebKit(registry.get());
- is_backgrounded_ = blink::kLaunchingProcessIsBackgrounded;
-
- // In single process the single process is all there is.
- widget_count_ = 0;
- hidden_widget_count_ = 0;
-
dom_storage_dispatcher_.reset(new DomStorageDispatcher());
vc_manager_.reset(new VideoCaptureImplManager());
@@ -1653,33 +1646,27 @@
main_thread_scheduler_->SetSchedulerKeepActive(keep_active);
}
-void RenderThreadImpl::SetProcessBackgrounded(bool backgrounded) {
- main_thread_scheduler_->SetRendererBackgrounded(backgrounded);
- if (backgrounded) {
- needs_to_record_first_active_paint_ = false;
- GetWebMainThreadScheduler()->DefaultTaskRunner()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&RenderThreadImpl::RecordMemoryUsageAfterBackgrounded,
- base::Unretained(this), "5min",
- process_foregrounded_count_),
- base::TimeDelta::FromMinutes(5));
- GetWebMainThreadScheduler()->DefaultTaskRunner()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&RenderThreadImpl::RecordMemoryUsageAfterBackgrounded,
- base::Unretained(this), "10min",
- process_foregrounded_count_),
- base::TimeDelta::FromMinutes(10));
- GetWebMainThreadScheduler()->DefaultTaskRunner()->PostDelayedTask(
- FROM_HERE,
- base::BindOnce(&RenderThreadImpl::RecordMemoryUsageAfterBackgrounded,
- base::Unretained(this), "15min",
- process_foregrounded_count_),
- base::TimeDelta::FromMinutes(15));
- was_backgrounded_time_ = base::TimeTicks::Now();
- } else {
- process_foregrounded_count_++;
+void RenderThreadImpl::SetProcessState(
+ mojom::RenderProcessState process_state) {
+ DCHECK(process_state_ != process_state);
+
+ if (process_state_ == mojom::RenderProcessState::kBackgrounded ||
+ (!process_state_.has_value() &&
+ process_state != mojom::RenderProcessState::kBackgrounded)) {
+ OnRendererForegrounded();
}
- is_backgrounded_ = backgrounded;
+
+ if (process_state == mojom::RenderProcessState::kVisible) {
+ OnRendererVisible();
+ } else if (process_state_ == mojom::RenderProcessState::kVisible ||
+ !process_state_.has_value()) {
+ OnRendererHidden();
+ }
+
+ if (process_state == mojom::RenderProcessState::kBackgrounded)
+ OnRendererBackgrounded();
+
+ process_state_ = process_state;
}
void RenderThreadImpl::ProcessPurgeAndSuspend() {
@@ -1701,6 +1688,9 @@
return;
purge_and_suspend_memory_metrics_ = memory_metrics;
+
+ // Use of Unretained(this) is safe because |this| has the same lifetime as a
+ // renderer process.
GetWebMainThreadScheduler()->DefaultTaskRunner()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
@@ -2383,39 +2373,8 @@
}
bool RenderThreadImpl::RendererIsHidden() const {
- return widget_count_ > 0 && hidden_widget_count_ == widget_count_;
-}
-
-void RenderThreadImpl::WidgetCreated() {
- bool renderer_was_hidden = RendererIsHidden();
- widget_count_++;
- if (renderer_was_hidden)
- OnRendererVisible();
-}
-
-void RenderThreadImpl::WidgetDestroyed() {
- // TODO(rmcilroy): Remove the restriction that destroyed widgets must be
- // unhidden before WidgetDestroyed is called.
- DCHECK_GT(widget_count_, 0);
- DCHECK_GT(widget_count_, hidden_widget_count_);
- widget_count_--;
- if (RendererIsHidden())
- OnRendererHidden();
-}
-
-void RenderThreadImpl::WidgetHidden() {
- DCHECK_LT(hidden_widget_count_, widget_count_);
- hidden_widget_count_++;
- if (RendererIsHidden())
- OnRendererHidden();
-}
-
-void RenderThreadImpl::WidgetRestored() {
- bool renderer_was_hidden = RendererIsHidden();
- DCHECK_GT(hidden_widget_count_, 0);
- hidden_widget_count_--;
- if (renderer_was_hidden)
- OnRendererVisible();
+ return process_state_ == mojom::RenderProcessState::kHidden ||
+ process_state_ == mojom::RenderProcessState::kBackgrounded;
}
void RenderThreadImpl::OnRendererHidden() {
@@ -2434,6 +2393,39 @@
main_thread_scheduler_->SetRendererHidden(false);
}
+bool RenderThreadImpl::RendererIsBackgrounded() const {
+ return process_state_ == mojom::RenderProcessState::kBackgrounded;
+}
+
+void RenderThreadImpl::OnRendererBackgrounded() {
+ main_thread_scheduler_->SetRendererBackgrounded(true);
+ needs_to_record_first_active_paint_ = false;
+ GetWebMainThreadScheduler()->DefaultTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RenderThreadImpl::RecordMemoryUsageAfterBackgrounded,
+ base::Unretained(this), "5min",
+ process_foregrounded_count_),
+ base::TimeDelta::FromMinutes(5));
+ GetWebMainThreadScheduler()->DefaultTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RenderThreadImpl::RecordMemoryUsageAfterBackgrounded,
+ base::Unretained(this), "10min",
+ process_foregrounded_count_),
+ base::TimeDelta::FromMinutes(10));
+ GetWebMainThreadScheduler()->DefaultTaskRunner()->PostDelayedTask(
+ FROM_HERE,
+ base::BindOnce(&RenderThreadImpl::RecordMemoryUsageAfterBackgrounded,
+ base::Unretained(this), "15min",
+ process_foregrounded_count_),
+ base::TimeDelta::FromMinutes(15));
+ was_backgrounded_time_ = base::TimeTicks::Now();
+}
+
+void RenderThreadImpl::OnRendererForegrounded() {
+ main_thread_scheduler_->SetRendererBackgrounded(false);
+ process_foregrounded_count_++;
+}
+
void RenderThreadImpl::ReleaseFreeMemory() {
base::allocator::ReleaseFreeMemory();
discardable_shared_memory_manager_->ReleaseFreeMemory();
diff --git a/content/renderer/render_thread_impl.h b/content/renderer/render_thread_impl.h
index b3f3ffbc..afa96183 100644
--- a/content/renderer/render_thread_impl.h
+++ b/content/renderer/render_thread_impl.h
@@ -458,15 +458,6 @@
return &histogram_customizer_;
}
- // Called by a RenderWidget when it is created or destroyed. This
- // allows the process to know when there are no visible widgets.
- void WidgetCreated();
- // Note: A widget must not be hidden when it is destroyed - ensure that
- // WidgetRestored is called before WidgetDestroyed for any hidden widget.
- void WidgetDestroyed();
- void WidgetHidden();
- void WidgetRestored();
-
void RegisterPendingFrameCreate(
const service_manager::BindSourceInfo& source_info,
int routing_id,
@@ -504,6 +495,8 @@
}
private:
+ friend class RenderThreadImplBrowserTest;
+
void OnProcessFinalRelease() override;
// IPC::Listener
void OnChannelError() override;
@@ -555,7 +548,7 @@
const std::string& highlight_text_color,
const std::string& highlight_color) override;
void PurgePluginListCache(bool reload_pages) override;
- void SetProcessBackgrounded(bool backgrounded) override;
+ void SetProcessState(mojom::RenderProcessState process_state) override;
void SetSchedulerKeepActive(bool keep_active) override;
void ProcessPurgeAndSuspend() override;
void SetIsLockedToSite() override;
@@ -568,6 +561,10 @@
void OnRendererHidden();
void OnRendererVisible();
+ bool RendererIsBackgrounded() const;
+ void OnRendererBackgrounded();
+ void OnRendererForegrounded();
+
void RecordMemoryUsageAfterBackgrounded(const char* suffix,
int foregrounded_count);
void RecordPurgeAndSuspendMemoryGrowthMetrics(
@@ -621,14 +618,10 @@
// Used on the render thread.
std::unique_ptr<VideoCaptureImplManager> vc_manager_;
- // Used to keep track of the renderer's backgrounded state.
- bool is_backgrounded_;
-
- // The count of RenderWidgets running through this thread.
- int widget_count_;
-
- // The count of hidden RenderWidgets running through this thread.
- int hidden_widget_count_;
+ // Used to keep track of the renderer's backgrounded and visibility state.
+ // Updated via an IPC from the browser process. If nullopt, the browser
+ // process has yet to send an update and the state is unknown.
+ base::Optional<mojom::RenderProcessState> process_state_;
blink::WebString user_agent_;
blink::UserAgentMetadata user_agent_metadata_;
diff --git a/content/renderer/render_thread_impl_browsertest.cc b/content/renderer/render_thread_impl_browsertest.cc
index 1683632..7a3a0eea 100644
--- a/content/renderer/render_thread_impl_browsertest.cc
+++ b/content/renderer/render_thread_impl_browsertest.cc
@@ -58,6 +58,7 @@
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h"
+#include "third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h"
#include "third_party/blink/public/platform/scheduler/web_thread_scheduler.h"
#include "ui/base/ui_base_switches.h"
#include "ui/gfx/buffer_format_util.h"
@@ -87,7 +88,6 @@
#endif
namespace content {
-namespace {
// FIXME: It would be great if there was a reusable mock SingleThreadTaskRunner
class TestTaskCounter : public base::SingleThreadTaskRunner {
@@ -203,9 +203,8 @@
// in RenderThreadImpl::Init().
cmd->AppendSwitch(switches::kIgnoreGpuBlacklist);
- std::unique_ptr<blink::scheduler::WebThreadScheduler>
- main_thread_scheduler =
- blink::scheduler::WebThreadScheduler::CreateMainThreadScheduler();
+ auto main_thread_scheduler =
+ blink::scheduler::CreateMockWebMainThreadSchedulerForTests();
scoped_refptr<base::SingleThreadTaskRunner> test_task_counter(
test_task_counter_.get());
@@ -221,6 +220,10 @@
test_msg_filter_ = base::MakeRefCounted<QuitOnTestMsgFilter>(
run_loop_->QuitWhenIdleClosure());
thread_->AddFilter(test_msg_filter_.get());
+
+ main_thread_scheduler_ =
+ static_cast<blink::scheduler::WebMockThreadScheduler*>(
+ thread_->GetWebMainThreadScheduler());
}
void TearDown() override {
@@ -237,6 +240,14 @@
protected:
IPC::Sender* sender() { return channel_.get(); }
+ void SetProcessState(mojom::RenderProcessState process_state) {
+ mojom::Renderer* renderer_interface = thread_;
+ renderer_interface->SetProcessState(process_state);
+ }
+
+ bool RendererIsBackgrounded() { return thread_->RendererIsBackgrounded(); }
+ bool RendererIsHidden() { return thread_->RendererIsHidden(); }
+
scoped_refptr<TestTaskCounter> test_task_counter_;
TestContentClientInitializer content_client_initializer_;
std::unique_ptr<ContentRendererClient> content_renderer_client_;
@@ -250,6 +261,8 @@
std::unique_ptr<MockRenderProcess> mock_process_;
scoped_refptr<QuitOnTestMsgFilter> test_msg_filter_;
+ blink::scheduler::WebMockThreadScheduler* main_thread_scheduler_;
+
// RenderThreadImpl doesn't currently support a proper shutdown sequence
// and it's okay when we're running in multi-process mode because renderers
// get killed by the OS. Memory leaks aren't nice but it's test-only.
@@ -290,6 +303,127 @@
EXPECT_EQ(0, test_task_counter_->NumTasksPosted());
}
+TEST_F(RenderThreadImplBrowserTest, RendererIsBackgrounded) {
+ SetProcessState(mojom::RenderProcessState::kBackgrounded);
+ EXPECT_TRUE(RendererIsBackgrounded());
+
+ SetProcessState(mojom::RenderProcessState::kHidden);
+ EXPECT_FALSE(RendererIsBackgrounded());
+
+ SetProcessState(mojom::RenderProcessState::kVisible);
+ EXPECT_FALSE(RendererIsBackgrounded());
+}
+
+TEST_F(RenderThreadImplBrowserTest, RendererIsHidden) {
+ SetProcessState(mojom::RenderProcessState::kBackgrounded);
+ EXPECT_TRUE(RendererIsHidden());
+
+ SetProcessState(mojom::RenderProcessState::kHidden);
+ EXPECT_TRUE(RendererIsHidden());
+
+ SetProcessState(mojom::RenderProcessState::kVisible);
+ EXPECT_FALSE(RendererIsHidden());
+}
+
+TEST_F(RenderThreadImplBrowserTest, RendererStateTransitionVisible) {
+ // Going from an unknown to a visible state should mark the renderer as
+ // foregrounded and visible.
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
+ SetProcessState(mojom::RenderProcessState::kVisible);
+ testing::Mock::VerifyAndClear(main_thread_scheduler_);
+
+ // Going from a hidden to a visible state should mark the renderer as visible.
+ // A hidden renderer is already foregrounded.
+ SetProcessState(mojom::RenderProcessState::kHidden);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
+ SetProcessState(mojom::RenderProcessState::kVisible);
+ testing::Mock::VerifyAndClear(main_thread_scheduler_);
+
+ // Going from a backgrounded to a visible state should mark the renderer as
+ // foregrounded and visible.
+ SetProcessState(mojom::RenderProcessState::kBackgrounded);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
+ SetProcessState(mojom::RenderProcessState::kVisible);
+ testing::Mock::VerifyAndClear(main_thread_scheduler_);
+
+ testing::Mock::AllowLeak(main_thread_scheduler_);
+}
+
+TEST_F(RenderThreadImplBrowserTest, RendererStateTransitionHidden) {
+ // Going from an unknown to a visible state should mark the renderer as
+ // foregrounded and hidden.
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
+ SetProcessState(mojom::RenderProcessState::kHidden);
+ testing::Mock::VerifyAndClear(main_thread_scheduler_);
+
+ // Going from a visible to a hidden state should mark the renderer as hidden.
+ // A visible renderer is already foregrounded.
+ SetProcessState(mojom::RenderProcessState::kVisible);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
+ SetProcessState(mojom::RenderProcessState::kHidden);
+ testing::Mock::VerifyAndClear(main_thread_scheduler_);
+
+ // Going from a backgrounded to a hidden state should mark the renderer as
+ // foregrounded and hidden.
+ SetProcessState(mojom::RenderProcessState::kBackgrounded);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
+ SetProcessState(mojom::RenderProcessState::kHidden);
+ testing::Mock::VerifyAndClear(main_thread_scheduler_);
+
+ testing::Mock::AllowLeak(main_thread_scheduler_);
+}
+
+TEST_F(RenderThreadImplBrowserTest, RendererStateTransitionBackgrounded) {
+ // Going from an unknown to a backgrounded state should mark the renderer as
+ // hidden and backgrounded.
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
+ SetProcessState(mojom::RenderProcessState::kBackgrounded);
+ testing::Mock::VerifyAndClear(main_thread_scheduler_);
+
+ // Going from a visible to a backgrounded state should mark the renderer as
+ // hidden and backgrounded.
+ SetProcessState(mojom::RenderProcessState::kVisible);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
+ SetProcessState(mojom::RenderProcessState::kBackgrounded);
+ testing::Mock::VerifyAndClear(main_thread_scheduler_);
+
+ // Going from a hidden state to a backgrounded state should mark the renderer
+ // backgrounded. A hidden renderer is already marked as hidden.
+ SetProcessState(mojom::RenderProcessState::kHidden);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(true));
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(true)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererHidden(false)).Times(0);
+ EXPECT_CALL(*main_thread_scheduler_, SetRendererBackgrounded(false)).Times(0);
+ SetProcessState(mojom::RenderProcessState::kBackgrounded);
+ testing::Mock::VerifyAndClear(main_thread_scheduler_);
+
+ testing::Mock::AllowLeak(main_thread_scheduler_);
+}
+
enum NativeBufferFlag { kDisableNativeBuffers, kEnableNativeBuffers };
class RenderThreadImplGpuMemoryBufferBrowserTest
@@ -391,5 +525,5 @@
gfx::BufferFormat::RGBA_8888,
gfx::BufferFormat::BGRA_8888,
gfx::BufferFormat::YVU_420)));
-} // namespace
+
} // namespace content
diff --git a/content/renderer/render_widget.cc b/content/renderer/render_widget.cc
index 846a66c..82907ab 100644
--- a/content/renderer/render_widget.cc
+++ b/content/renderer/render_widget.cc
@@ -581,11 +581,6 @@
// Take a reference on behalf of the RenderThread. This will be balanced
// when we receive WidgetMsg_Close.
AddRef();
- if (RenderThreadImpl::current()) {
- RenderThreadImpl::current()->WidgetCreated();
- if (is_hidden_)
- RenderThreadImpl::current()->WidgetHidden();
- }
}
void RenderWidget::ApplyEmulatedScreenMetricsForPopupWidget(
@@ -721,15 +716,6 @@
if (routing_id_ != MSG_ROUTING_NONE) {
RenderThread::Get()->RemoveRoute(routing_id_);
g_routing_id_widget_map.Get().erase(routing_id_);
- if (RenderThreadImpl::current()) {
- // RenderWidgets may be hidden when they are closed. If we were previously
- // hidden, we are being counted as such in RenderThreadImpl. Thus we
- // remove that count here by calling WidgetRestored() even though we're
- // clearly not becoming visible here.
- if (is_hidden_)
- RenderThreadImpl::current()->WidgetRestored();
- RenderThreadImpl::current()->WidgetDestroyed();
- }
}
// Stop handling main thread input events immediately so we don't have them
@@ -2486,14 +2472,8 @@
RendererWindowTreeClient::Get(routing_id_)->SetVisible(!hidden);
#endif
- // RenderThreadImpl::current() could be null in tests.
- if (RenderThreadImpl::current()) {
- if (is_hidden_) {
- RenderThreadImpl::current()->WidgetHidden();
- first_update_visual_state_after_hidden_ = true;
- } else {
- RenderThreadImpl::current()->WidgetRestored();
- }
+ if (is_hidden_) {
+ first_update_visual_state_after_hidden_ = true;
}
if (render_widget_scheduling_state_)
diff --git a/third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h b/third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h
index 8eaaf0c..b5b1e80 100644
--- a/third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h
+++ b/third_party/blink/public/platform/scheduler/test/renderer_scheduler_test_support.h
@@ -5,6 +5,8 @@
#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_SCHEDULER_TEST_RENDERER_SCHEDULER_TEST_SUPPORT_H_
#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_SCHEDULER_TEST_RENDERER_SCHEDULER_TEST_SUPPORT_H_
+#include <memory>
+
#include "base/bind.h"
namespace base {
@@ -17,6 +19,7 @@
namespace scheduler {
class WebThreadScheduler;
+class WebMockThreadScheduler;
// Creates simple scheduling infrastructure for unit tests.
// It allows creation of FrameSchedulers and PageSchedulers, but doesn't provide
@@ -25,6 +28,11 @@
// create base::debug::ScopedTaskEnvironment.
std::unique_ptr<WebThreadScheduler> CreateWebMainThreadSchedulerForTests();
+// Simple scheduling infrastructure for unit tests, with the addition of mocked
+// methods.
+std::unique_ptr<WebMockThreadScheduler>
+CreateMockWebMainThreadSchedulerForTests();
+
void RunIdleTasksForTesting(WebThreadScheduler* scheduler,
base::OnceClosure callback);
diff --git a/third_party/blink/renderer/platform/scheduler/BUILD.gn b/third_party/blink/renderer/platform/scheduler/BUILD.gn
index c831f8a..9dc0d1d 100644
--- a/third_party/blink/renderer/platform/scheduler/BUILD.gn
+++ b/third_party/blink/renderer/platform/scheduler/BUILD.gn
@@ -172,6 +172,7 @@
"//base",
"//base/test:test_support",
"//mojo/public/cpp/bindings",
+ "//testing/gmock",
"//third_party/blink/public/mojom:mojom_platform_blink_headers",
]
diff --git a/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc b/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc
index e7ffead..4af2a79 100644
--- a/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc
+++ b/third_party/blink/renderer/platform/scheduler/test/renderer_scheduler_test_support.cc
@@ -10,6 +10,7 @@
#include "base/task/sequence_manager/test/sequence_manager_for_test.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/threading/thread_task_runner_handle.h"
+#include "third_party/blink/public/platform/scheduler/test/web_mock_thread_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/main_thread/main_thread_scheduler_impl.h"
#include "third_party/blink/renderer/platform/scheduler/public/frame_scheduler.h"
#include "third_party/blink/renderer/platform/scheduler/public/page_scheduler.h"
@@ -130,8 +131,58 @@
DISALLOW_COPY_AND_ASSIGN(SimplePageScheduler);
};
+class SimpleThreadScheduler : public ThreadScheduler {
+ public:
+ SimpleThreadScheduler() {}
+ ~SimpleThreadScheduler() override {}
+
+ void Shutdown() override {}
+
+ scoped_refptr<base::SingleThreadTaskRunner> V8TaskRunner() override {
+ return base::ThreadTaskRunnerHandle::Get();
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> DeprecatedDefaultTaskRunner()
+ override {
+ return base::ThreadTaskRunnerHandle::Get();
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> CompositorTaskRunner() override {
+ return base::ThreadTaskRunnerHandle::Get();
+ }
+
+ scoped_refptr<base::SingleThreadTaskRunner> IPCTaskRunner() override {
+ return base::ThreadTaskRunnerHandle::Get();
+ }
+
+ std::unique_ptr<PageScheduler> CreatePageScheduler(
+ PageScheduler::Delegate*) override {
+ return std::make_unique<SimplePageScheduler>();
+ }
+
+ // ThreadScheduler implementation:
+ bool ShouldYieldForHighPriorityWork() override { return false; }
+ bool CanExceedIdleDeadlineIfRequired() const override { return false; }
+ void PostIdleTask(const base::Location&, Thread::IdleTask) override {}
+ void PostNonNestableIdleTask(const base::Location&,
+ Thread::IdleTask) override {}
+ void AddRAILModeObserver(WebRAILModeObserver*) override {}
+ std::unique_ptr<WebThreadScheduler::RendererPauseHandle> PauseScheduler()
+ override {
+ return nullptr;
+ }
+ base::TimeTicks MonotonicallyIncreasingVirtualTime() override {
+ return base::TimeTicks::Now();
+ }
+ void AddTaskObserver(base::MessageLoop::TaskObserver*) override {}
+ void RemoveTaskObserver(base::MessageLoop::TaskObserver*) override {}
+ NonMainThreadSchedulerImpl* AsNonMainThreadScheduler() override {
+ return nullptr;
+ }
+};
+
class SimpleMainThreadScheduler : public WebThreadScheduler,
- public ThreadScheduler {
+ public SimpleThreadScheduler {
public:
SimpleMainThreadScheduler() {}
~SimpleMainThreadScheduler() override {}
@@ -183,30 +234,31 @@
PageScheduler::Delegate*) override {
return std::make_unique<SimplePageScheduler>();
}
+};
- // ThreadScheduler implementation:
- bool ShouldYieldForHighPriorityWork() override { return false; }
- bool CanExceedIdleDeadlineIfRequired() const override { return false; }
- void PostIdleTask(const base::Location&, Thread::IdleTask) override {
- // NOTREACHED();
+class SimpleMockMainThreadScheduler : public WebMockThreadScheduler {
+ public:
+ SimpleMockMainThreadScheduler() {}
+ ~SimpleMockMainThreadScheduler() override {}
+
+ scoped_refptr<base::SingleThreadTaskRunner> DefaultTaskRunner() override {
+ return base::ThreadTaskRunnerHandle::Get();
}
- void PostNonNestableIdleTask(const base::Location&,
- Thread::IdleTask) override {
- // NOTREACHED();
+
+ scoped_refptr<base::SingleThreadTaskRunner> InputTaskRunner() override {
+ return base::ThreadTaskRunnerHandle::Get();
}
- void AddRAILModeObserver(WebRAILModeObserver*) override {}
- std::unique_ptr<WebThreadScheduler::RendererPauseHandle> PauseScheduler()
- override {
- return nullptr;
+
+ scoped_refptr<base::SingleThreadTaskRunner> CleanupTaskRunner() override {
+ return base::ThreadTaskRunnerHandle::Get();
}
- base::TimeTicks MonotonicallyIncreasingVirtualTime() override {
- return base::TimeTicks::Now();
+
+ std::unique_ptr<Thread> CreateMainThread() override {
+ return std::make_unique<SimpleThread>(&simple_thread_scheduler_);
}
- void AddTaskObserver(base::MessageLoop::TaskObserver*) override {}
- void RemoveTaskObserver(base::MessageLoop::TaskObserver*) override {}
- NonMainThreadSchedulerImpl* AsNonMainThreadScheduler() override {
- return nullptr;
- }
+
+ private:
+ SimpleThreadScheduler simple_thread_scheduler_;
};
} // namespace
@@ -215,6 +267,11 @@
return std::make_unique<SimpleMainThreadScheduler>();
}
+std::unique_ptr<WebMockThreadScheduler>
+CreateMockWebMainThreadSchedulerForTests() {
+ return std::make_unique<SimpleMockMainThreadScheduler>();
+}
+
void RunIdleTasksForTesting(WebThreadScheduler* scheduler,
base::OnceClosure callback) {
MainThreadSchedulerImpl* scheduler_impl =