Handle WebContents occlusion on aura.
With this CL, when a WebContents is occluded on aura:
- WebContents::GetVisibility returns OCCLUDED
- RenderWidgetHostImpl::WasHidden is called on all
RenderWidgetHostImpls that belong to the WebContents.
That allows:
- Changing the priority of renderer processes to background.
- Not rendering frames unnecessarily.
Bug: 668690
Change-Id: Ie1c86d271276576a6b5c7adfb11bd0e42d5d491e
Reviewed-on: https://chromium-review.googlesource.com/937820
Reviewed-by: Peter Beverloo <[email protected]>
Reviewed-by: Scott Violet <[email protected]>
Reviewed-by: Dominick Ng <[email protected]>
Reviewed-by: Avi Drissman <[email protected]>
Commit-Queue: François Doray <[email protected]>
Cr-Commit-Position: refs/heads/master@{#540149}
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 0185340..ca66c49 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -531,9 +531,6 @@
last_sent_theme_color_(SK_ColorTRANSPARENT),
did_first_visually_non_empty_paint_(false),
capturer_count_(0),
- should_normally_be_visible_(true),
- should_normally_be_occluded_(false),
- did_first_set_visible_(false),
is_being_destroyed_(false),
is_notifying_observers_(false),
notify_disconnection_(false),
@@ -1317,6 +1314,7 @@
void WebContentsImpl::IncrementCapturerCount(const gfx::Size& capture_size) {
DCHECK(!is_being_destroyed_);
+ const bool was_captured = IsBeingCaptured();
++capturer_count_;
DVLOG(1) << "There are now " << capturer_count_
<< " capturing(s) of WebContentsImpl@" << this;
@@ -1328,8 +1326,14 @@
OnPreferredSizeChanged(preferred_size_);
}
- // Ensure that all views are un-occluded before capture begins.
- DoWasUnOccluded();
+ if (GetVisibility() != Visibility::VISIBLE && !was_captured) {
+ // Ensure that all views act as if they were visible before capture begins.
+ // TODO(fdoray): Replace RenderWidgetHostView::WasUnOccluded() with a method
+ // to explicitly notify the RenderWidgetHostView that capture began.
+ // https://crbug.com/668690
+ for (RenderWidgetHostView* view : GetRenderWidgetHostViewsInTree())
+ view->WasUnOccluded();
+ }
}
void WebContentsImpl::DecrementCapturerCount() {
@@ -1346,13 +1350,12 @@
preferred_size_for_capture_ = gfx::Size();
OnPreferredSizeChanged(old_size);
- if (IsHidden()) {
+ if (visibility_ == Visibility::HIDDEN) {
DVLOG(1) << "Executing delayed WasHidden().";
WasHidden();
- }
-
- if (should_normally_be_occluded_)
+ } else if (visibility_ == Visibility::OCCLUDED) {
WasOccluded();
+ }
}
}
@@ -1463,8 +1466,6 @@
}
void WebContentsImpl::WasShown() {
- const Visibility previous_visibility = GetVisibility();
-
controller_.SetActive(true);
if (auto* view = GetRenderWidgetHostView()) {
@@ -1480,13 +1481,10 @@
SendPageMessage(new PageMsg_WasShown(MSG_ROUTING_NONE));
last_active_time_ = base::TimeTicks::Now();
- should_normally_be_visible_ = true;
- NotifyVisibilityChanged(previous_visibility);
+ SetVisibility(Visibility::VISIBLE);
}
void WebContentsImpl::WasHidden() {
- const Visibility previous_visibility = GetVisibility();
-
// If there are entities capturing screenshots or video (e.g., mirroring),
// don't activate the "disable rendering" optimization.
if (!IsBeingCaptured()) {
@@ -1505,8 +1503,7 @@
SendPageMessage(new PageMsg_WasHidden(MSG_ROUTING_NONE));
}
- should_normally_be_visible_ = false;
- NotifyVisibilityChanged(previous_visibility);
+ SetVisibility(Visibility::HIDDEN);
}
#if defined(OS_ANDROID)
@@ -1539,39 +1536,15 @@
#endif
void WebContentsImpl::WasOccluded() {
- const Visibility previous_visibility = GetVisibility();
-
if (!IsBeingCaptured()) {
for (RenderWidgetHostView* view : GetRenderWidgetHostViewsInTree())
view->WasOccluded();
}
-
- should_normally_be_occluded_ = true;
- NotifyVisibilityChanged(previous_visibility);
-}
-
-void WebContentsImpl::WasUnOccluded() {
- const Visibility previous_visibility = GetVisibility();
-
- if (!IsBeingCaptured())
- DoWasUnOccluded();
-
- should_normally_be_occluded_ = false;
- NotifyVisibilityChanged(previous_visibility);
-}
-
-void WebContentsImpl::DoWasUnOccluded() {
- // TODO(fdoray): Only call WasUnOccluded on frames in the active viewport.
- for (RenderWidgetHostView* view : GetRenderWidgetHostViewsInTree())
- view->WasUnOccluded();
+ SetVisibility(Visibility::OCCLUDED);
}
Visibility WebContentsImpl::GetVisibility() const {
- if (!should_normally_be_visible_)
- return Visibility::HIDDEN;
- if (should_normally_be_occluded_)
- return Visibility::OCCLUDED;
- return Visibility::VISIBLE;
+ return visibility_;
}
bool WebContentsImpl::NeedToFireBeforeUnload() {
@@ -1724,7 +1697,8 @@
// This is set before initializing the render manager since
// RenderFrameHostManager::Init calls back into us via its delegate to ask if
// it should be hidden.
- should_normally_be_visible_ = !params.initially_hidden;
+ visibility_ =
+ params.initially_hidden ? Visibility::HIDDEN : Visibility::VISIBLE;
// The routing ids must either all be set or all be unset.
DCHECK((params.routing_id == MSG_ROUTING_NONE &&
@@ -3441,10 +3415,12 @@
}
}
-void WebContentsImpl::NotifyVisibilityChanged(Visibility previous_visibility) {
+void WebContentsImpl::SetVisibility(Visibility visibility) {
+ const Visibility previous_visibility = visibility_;
+ visibility_ = visibility;
+
// Notify observers if the visibility changed or if WasShown() is being called
// for the first time.
- const Visibility visibility = GetVisibility();
if (visibility != previous_visibility ||
(visibility == Visibility::VISIBLE && !did_first_set_visible_)) {
for (auto& observer : observers_)
@@ -5786,7 +5762,7 @@
}
bool WebContentsImpl::IsHidden() {
- return !IsBeingCaptured() && !should_normally_be_visible_;
+ return !IsBeingCaptured() && visibility_ != Visibility::VISIBLE;
}
int WebContentsImpl::GetOuterDelegateFrameTreeNodeId() {
@@ -6037,25 +6013,40 @@
return currently_playing_video_count_;
}
-void WebContentsImpl::UpdateWebContentsVisibility(bool visible) {
+void WebContentsImpl::UpdateWebContentsVisibility(Visibility visibility) {
+ // Occlusion can cause flakiness in browser tests.
+ static const bool occlusion_is_disabled =
+ base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kDisableBackgroundingOccludedWindowsForTesting);
+ if (occlusion_is_disabled && visibility == Visibility::OCCLUDED)
+ visibility = Visibility::VISIBLE;
+
if (!did_first_set_visible_) {
- // If this WebContents has not yet been set to be visible for the first
- // time, ignore any requests to make it hidden, since resources would
- // immediately be destroyed and only re-created after content loaded. In
- // this state the window content is undefined and can show garbage.
- // However, the page load mechanism requires an activation call through a
- // visibility call to (re)load.
- if (visible) {
+ if (visibility == Visibility::VISIBLE) {
+ // A WebContents created with CreateParams::initially_hidden = false
+ // starts with GetVisibility() == Visibility::VISIBLE even though it is
+ // not really visible. Call WasShown() when it becomes visible for real as
+ // the page load mechanism and some WebContentsObserver rely on that.
WasShown();
did_first_set_visible_ = true;
}
+
+ // Trust the initial visibility of the WebContents and do not switch it to
+ // HIDDEN or OCCLUDED before it becomes VISIBLE for real. Doing so would
+ // result in destroying resources that would immediately be recreated (e.g.
+ // UpdateWebContents(HIDDEN) can be called when a WebContents is added to a
+ // hidden window that is about to be shown).
+
return;
}
- if (visible == should_normally_be_visible_)
+
+ if (visibility == visibility_)
return;
- if (visible)
+ if (visibility == Visibility::VISIBLE)
WasShown();
+ else if (visibility == Visibility::OCCLUDED)
+ WasOccluded();
else
WasHidden();
}