Commit 3b39a541 authored by Patrik Höglund's avatar Patrik Höglund Committed by Commit Bot

Revert "Don't make a LayerTreeFrameSink for a non-visible RenderWidget."

This reverts commit 68d8dbc0.

Reason for revert: Looks like it makes PrerenderBrowserTest.PrerenderInfiniteLoop according to FindIt. 

../../chrome/browser/prerender/prerender_browsertest.cc:1721: Failure
Expected equality of these values:
  1U
    Which is: 1
  GetLinkPrerenderCount()
    Which is: 2

Original change's description:
> Don't make a LayerTreeFrameSink for a non-visible RenderWidget.
> 
> Importantly, non-visible RenderWidgets include swapped out RenderWidgets
> which are zombies, without a frame, and which should not be used.
> 
> This can happen if the RenderWidget swap out races with the posted task
> from the compositor to make a LayerTreeFrameSink. When swapping out, the
> compositor would be marked as not-visible, which would stop it from
> making such requests, but one could already be in flight.
> 
> In the other case, honoring the request for a non-visible compositor is
> more benign, but we can delay it until the compositor is actually
> visible again. This means if a context is lost, backgrounded tabs would
> not all attempt to reconnect to the Gpu process at once.
> 
> This is a better followup for the hacky
> https://chromium-review.googlesource.com/c/chromium/src/+/1292711 which
> was meant only for merge to a release branch. It reverts that change as
> part of this one.
> 
> R=​piman@chromium.org
> 
> Change-Id: I058bdc37482d04bb86d65a7377b7520dad8573e7
> Bug: 896836, 419087
> Reviewed-on: https://chromium-review.googlesource.com/c/1292714
> Commit-Queue: danakj <danakj@chromium.org>
> Reviewed-by: Daniel Cheng <dcheng@chromium.org>
> Reviewed-by: Antoine Labour <piman@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#606655}

TBR=danakj@chromium.org,dcheng@chromium.org,piman@chromium.org

Change-Id: I65238aba0abdb6c9192e9f3cb3691f8dd89b3ea5
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 896836, 419087, 903696
Reviewed-on: https://chromium-review.googlesource.com/c/1329003Reviewed-by: default avatarPatrik Höglund <phoglund@chromium.org>
Commit-Queue: Patrik Höglund <phoglund@chromium.org>
Cr-Commit-Position: refs/heads/master@{#606807}
parent 24599fc6
...@@ -637,31 +637,11 @@ void LayerTreeView::RecordWheelAndTouchScrollingCount( ...@@ -637,31 +637,11 @@ void LayerTreeView::RecordWheelAndTouchScrollingCount(
} }
void LayerTreeView::RequestNewLayerTreeFrameSink() { void LayerTreeView::RequestNewLayerTreeFrameSink() {
// When the compositor is not visible it would not request a // If the host is closing, then no more compositing is possible. This
// LayerTreeFrameSink so this is a race where it requested one then became // prevents shutdown races between handling the close message and
// not-visible. In that case, we can wait for it to become visible again // the CreateLayerTreeFrameSink task.
// before replying. if (delegate_->IsClosing())
//
// This deals with an insidious race when the RenderWidget is swapped-out
// after this task was posted from the compositor. When swapped-out the
// compositor is stopped by making it not visible, and the RenderWidget (our
// delegate) is a zombie which can not be used (https://crbug.com/894899).
// Eventually the RenderWidget/LayerTreeView will not exist at all in this
// case (https://crbug.com/419087). So this handles the case for now since the
// compositor is not visible, and we can avoid using the RenderWidget until
// it marks the compositor visible again, indicating that it is valid to use
// the RenderWidget again.
//
// If there is no compositor thread, then this is a test-only path where
// composite is controlled directly by blink, and visibility is not
// considered. We don't expect blink to ever try composite on a swapped-out
// RenderWidget, which would be a bug, but the race condition can't happen
// in the single-thread case since this isn't a posted task.
if (!layer_tree_host_->IsVisible() && !!compositor_thread_) {
layer_tree_frame_sink_request_failed_while_invisible_ = true;
return; return;
}
delegate_->RequestNewLayerTreeFrameSink(base::BindOnce( delegate_->RequestNewLayerTreeFrameSink(base::BindOnce(
&LayerTreeView::SetLayerTreeFrameSink, weak_factory_.GetWeakPtr())); &LayerTreeView::SetLayerTreeFrameSink, weak_factory_.GetWeakPtr()));
} }
......
...@@ -63,6 +63,9 @@ class LayerTreeViewDelegate { ...@@ -63,6 +63,9 @@ class LayerTreeViewDelegate {
// or committing a frame (at the same time Tracing measurements are taken). // or committing a frame (at the same time Tracing measurements are taken).
virtual void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) = 0; virtual void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) = 0;
// Indicates whether the LayerTreeView is about to close.
virtual bool IsClosing() const = 0;
// Requests that the client schedule a composite now, and calculate // Requests that the client schedule a composite now, and calculate
// appropriate delay for potential future frame. // appropriate delay for potential future frame.
virtual void RequestScheduleAnimation() = 0; virtual void RequestScheduleAnimation() = 0;
......
...@@ -31,7 +31,7 @@ void RunClosureIfNotSwappedOut(base::WeakPtr<RenderWidget> render_widget, ...@@ -31,7 +31,7 @@ void RunClosureIfNotSwappedOut(base::WeakPtr<RenderWidget> render_widget,
// Input messages must not be processed if the RenderWidget is in swapped out // Input messages must not be processed if the RenderWidget is in swapped out
// or closing state. // or closing state.
if (!render_widget || render_widget->is_swapped_out() || if (!render_widget || render_widget->is_swapped_out() ||
render_widget->is_closing()) { render_widget->IsClosing()) {
return; return;
} }
std::move(closure).Run(); std::move(closure).Run();
......
...@@ -410,7 +410,7 @@ void WidgetInputHandlerManager::HandleInputEvent( ...@@ -410,7 +410,7 @@ void WidgetInputHandlerManager::HandleInputEvent(
const ui::LatencyInfo& latency, const ui::LatencyInfo& latency,
mojom::WidgetInputHandler::DispatchEventCallback callback) { mojom::WidgetInputHandler::DispatchEventCallback callback) {
if (!render_widget_ || render_widget_->is_swapped_out() || if (!render_widget_ || render_widget_->is_swapped_out() ||
render_widget_->is_closing()) { render_widget_->IsClosing()) {
if (callback) { if (callback) {
std::move(callback).Run(InputEventAckSource::MAIN_THREAD, latency, std::move(callback).Run(InputEventAckSource::MAIN_THREAD, latency,
INPUT_EVENT_ACK_STATE_NOT_CONSUMED, base::nullopt, INPUT_EVENT_ACK_STATE_NOT_CONSUMED, base::nullopt,
......
...@@ -2216,16 +2216,8 @@ void RenderFrameImpl::OnSwapOut( ...@@ -2216,16 +2216,8 @@ void RenderFrameImpl::OnSwapOut(
this, proxy_routing_id, replicated_frame_state.scope); this, proxy_routing_id, replicated_frame_state.scope);
// Swap out and stop sending any IPC messages that are not ACKs. // Swap out and stop sending any IPC messages that are not ACKs.
if (is_main_frame_) { if (is_main_frame_)
// The RenderWidget isn't actually closed here because we might need to use render_view_->SetSwappedOut(true);
// it again. It can't be destroyed and recreated later as it is part of
// the |render_view_|, which must be kept alive. So instead just stop the
// compositor.
// TODO(crbug.com/419087): The RenderWidget should be destroyed as a result
// of this (main) frame going away, then we won't have to do this.
render_view_->GetWidget()->StopCompositor();
render_view_->GetWidget()->SetSwappedOut(true);
}
RenderViewImpl* render_view = render_view_; RenderViewImpl* render_view = render_view_;
bool is_main_frame = is_main_frame_; bool is_main_frame = is_main_frame_;
...@@ -5896,15 +5888,8 @@ bool RenderFrameImpl::SwapIn() { ...@@ -5896,15 +5888,8 @@ bool RenderFrameImpl::SwapIn() {
if (is_main_frame_) { if (is_main_frame_) {
CHECK(!render_view_->main_render_frame_); CHECK(!render_view_->main_render_frame_);
render_view_->main_render_frame_ = this; render_view_->main_render_frame_ = this;
if (render_view_->GetWidget()->is_swapped_out()) { if (render_view_->is_swapped_out()) {
render_view_->GetWidget()->SetSwappedOut(false); render_view_->SetSwappedOut(false);
// In OnSwapOut() the RenderWidget's compositor was stopped instead of
// deleting the RenderWidget. So here we can start it again. If the
// |render_view_|'s RenderWidget started swapped in, it may already be
// swapped in here, so this does nothing.
// TODO(crbug.com/419087): The RenderWidget should be newly created here,
// then we won't have to do this.
render_view_->GetWidget()->StartCompositor();
} }
render_view_->UpdateWebViewWithDeviceScaleFactor(); render_view_->UpdateWebViewWithDeviceScaleFactor();
} }
......
...@@ -1518,10 +1518,22 @@ void RenderViewImpl::AttachWebFrameWidget(blink::WebFrameWidget* frame_widget) { ...@@ -1518,10 +1518,22 @@ void RenderViewImpl::AttachWebFrameWidget(blink::WebFrameWidget* frame_widget) {
// The previous WebFrameWidget must already be detached by CloseForFrame(). // The previous WebFrameWidget must already be detached by CloseForFrame().
DCHECK(!frame_widget_); DCHECK(!frame_widget_);
frame_widget_ = frame_widget; frame_widget_ = frame_widget;
// In CloseForFrame() the RenderWidget's compositor was stopped instead of
// deleting the RenderWidget. So here we can start it again. For the first
// main frame, it would already be started by default so this does nothing.
// TODO(crbug.com/419087): The RenderWidget should be newly created here along
// with the WebFrameWidget, then we won't have to do this.
GetWidget()->StartCompositor();
} }
void RenderViewImpl::DetachWebFrameWidget() { void RenderViewImpl::DetachWebFrameWidget() {
DCHECK(frame_widget_); DCHECK(frame_widget_);
// The RenderWidget isn't actually closed here because we might need to use it
// again since it can't be recreated as it is part of |this|. So instead just
// stop the compositor.
// TODO(crbug.com/419087): The RenderWidget should be destroyed here along
// with the WebFrameWidget, then we won't have to do this.
GetWidget()->StopCompositor();
frame_widget_->Close(); frame_widget_->Close();
frame_widget_ = nullptr; frame_widget_ = nullptr;
} }
......
...@@ -488,6 +488,12 @@ void RenderWidget::CloseForFrame() { ...@@ -488,6 +488,12 @@ void RenderWidget::CloseForFrame() {
OnClose(); OnClose();
} }
void RenderWidget::SetSwappedOut(bool is_swapped_out) {
// We should only toggle between states.
DCHECK(is_swapped_out_ != is_swapped_out);
is_swapped_out_ = is_swapped_out;
}
void RenderWidget::Init(ShowCallback show_callback, WebWidget* web_widget) { void RenderWidget::Init(ShowCallback show_callback, WebWidget* web_widget) {
DCHECK(!webwidget_internal_); DCHECK(!webwidget_internal_);
DCHECK_NE(routing_id_, MSG_ROUTING_NONE); DCHECK_NE(routing_id_, MSG_ROUTING_NONE);
...@@ -668,15 +674,9 @@ void RenderWidget::OnClose() { ...@@ -668,15 +674,9 @@ void RenderWidget::OnClose() {
if (routing_id_ != MSG_ROUTING_NONE) { if (routing_id_ != MSG_ROUTING_NONE) {
RenderThread::Get()->RemoveRoute(routing_id_); RenderThread::Get()->RemoveRoute(routing_id_);
g_routing_id_widget_map.Get().erase(routing_id_); g_routing_id_widget_map.Get().erase(routing_id_);
if (RenderThreadImpl::current()) { SetHidden(false);
// RenderWidgets may be hidden when they are closed. If we were previously if (RenderThreadImpl::current())
// 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(); RenderThreadImpl::current()->WidgetDestroyed();
}
} }
if (for_oopif_) { if (for_oopif_) {
...@@ -805,8 +805,6 @@ void RenderWidget::OnWasHidden() { ...@@ -805,8 +805,6 @@ void RenderWidget::OnWasHidden() {
void RenderWidget::OnWasShown(base::TimeTicks show_request_timestamp, void RenderWidget::OnWasShown(base::TimeTicks show_request_timestamp,
bool was_evicted) { bool was_evicted) {
TRACE_EVENT0("renderer", "RenderWidget::OnWasShown"); TRACE_EVENT0("renderer", "RenderWidget::OnWasShown");
DCHECK(!is_swapped_out_);
// During shutdown we can just ignore this message. // During shutdown we can just ignore this message.
if (!GetWebWidget()) if (!GetWebWidget())
return; return;
...@@ -968,17 +966,10 @@ void RenderWidget::BeginMainFrame(base::TimeTicks frame_time) { ...@@ -968,17 +966,10 @@ void RenderWidget::BeginMainFrame(base::TimeTicks frame_time) {
void RenderWidget::RequestNewLayerTreeFrameSink( void RenderWidget::RequestNewLayerTreeFrameSink(
LayerTreeFrameSinkCallback callback) { LayerTreeFrameSinkCallback callback) {
DCHECK(GetWebWidget());
// For widgets that are never visible, we don't start the compositor, so we // For widgets that are never visible, we don't start the compositor, so we
// never get a request for a cc::LayerTreeFrameSink. // never get a request for a cc::LayerTreeFrameSink.
DCHECK(!compositor_never_visible_); DCHECK(!compositor_never_visible_);
// Swapped out RenderWidgets should not be doing any compositing.
DCHECK(!is_swapped_out_);
if (is_closing()) {
// In this case, we drop the request which means the compositor waits
// forever, which is fine since we're going to destroy it.
return;
}
// TODO(jonross): have this generated by the LayerTreeFrameSink itself, which // TODO(jonross): have this generated by the LayerTreeFrameSink itself, which
// would then handle binding. // would then handle binding.
...@@ -1027,6 +1018,19 @@ void RenderWidget::DidCompletePageScaleAnimation() { ...@@ -1027,6 +1018,19 @@ void RenderWidget::DidCompletePageScaleAnimation() {
owner_delegate_->DidCompletePageScaleAnimationForWidget(); owner_delegate_->DidCompletePageScaleAnimationForWidget();
} }
bool RenderWidget::IsClosing() const {
// The |host_will_close_this_| is an optimization when we know that a
// WidgetMsg_Close IPC will be coming, though it has not arrived yet.
// The |closing_| state means we have received this IPC and are in the
// process of closing/destroying the RenderWidget.
// TODO(ajwong): Once RenderViewImpl and RenderWidget are split, attempt to
// combine two states so the shutdown logic is cleaner.
//
// http://crbug.com/545684
return host_will_close_this_ || closing_;
}
void RenderWidget::RequestScheduleAnimation() { void RenderWidget::RequestScheduleAnimation() {
if (owner_delegate_) { if (owner_delegate_) {
owner_delegate_->RequestScheduleAnimationForWidget(); owner_delegate_->RequestScheduleAnimationForWidget();
...@@ -1519,6 +1523,7 @@ void RenderWidget::Show(WebNavigationPolicy policy) { ...@@ -1519,6 +1523,7 @@ void RenderWidget::Show(WebNavigationPolicy policy) {
LayerTreeView* RenderWidget::InitializeLayerTreeView() { LayerTreeView* RenderWidget::InitializeLayerTreeView() {
TRACE_EVENT0("blink", "RenderWidget::InitializeLayerTreeView"); TRACE_EVENT0("blink", "RenderWidget::InitializeLayerTreeView");
DCHECK(!IsClosing());
layer_tree_view_ = std::make_unique<LayerTreeView>( layer_tree_view_ = std::make_unique<LayerTreeView>(
this, compositor_deps_->GetCompositorMainThreadTaskRunner(), this, compositor_deps_->GetCompositorMainThreadTaskRunner(),
......
...@@ -225,23 +225,10 @@ class CONTENT_EXPORT RenderWidget ...@@ -225,23 +225,10 @@ class CONTENT_EXPORT RenderWidget
// Sets whether this RenderWidget has been swapped out to be displayed by // Sets whether this RenderWidget has been swapped out to be displayed by
// a RenderWidget in a different process. If so, no new IPC messages will be // a RenderWidget in a different process. If so, no new IPC messages will be
// sent (only ACKs) and the process is free to exit when there are no other // sent (only ACKs) and the process is free to exit when there are no other
// active RenderWidgets. The RenderWidget is not used for compositing as there // active RenderWidgets.
// is no WebWidget that should display content when swapped out. void SetSwappedOut(bool is_swapped_out);
void SetSwappedOut(bool is_swapped_out) { is_swapped_out_ = is_swapped_out; }
bool is_swapped_out() const { return is_swapped_out_; } bool is_swapped_out() const { return is_swapped_out_; }
// This is true once a Close IPC has been received. The actual action of
// closing must be done on another stack frame, in case the IPC receipt
// is in a nested message loop and will unwind back up to javascript (from
// plugins). So this will be true between those two things, to avoid work
// when the RenderWidget will be closed.
// Additionally, as an optimization, this is true after we know the renderer
// has asked the browser to close this RenderWidget.
//
// TODO(crbug.com/545684): Once RenderViewImpl and RenderWidget are split,
// attempt to combine two states so the shutdown logic is cleaner.
bool is_closing() const { return host_will_close_this_ || closing_; }
// Manage edit commands to be used for the next keyboard event. // Manage edit commands to be used for the next keyboard event.
const EditCommands& edit_commands() const { return edit_commands_; } const EditCommands& edit_commands() const { return edit_commands_; }
void SetEditCommandForNextKeyEvent(const std::string& name, void SetEditCommandForNextKeyEvent(const std::string& name,
...@@ -279,6 +266,7 @@ class CONTENT_EXPORT RenderWidget ...@@ -279,6 +266,7 @@ class CONTENT_EXPORT RenderWidget
void DidCommitCompositorFrame() override; void DidCommitCompositorFrame() override;
void DidCompletePageScaleAnimation() override; void DidCompletePageScaleAnimation() override;
void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) override; void RecordEndOfFrameMetrics(base::TimeTicks frame_begin_time) override;
bool IsClosing() const override;
void RequestScheduleAnimation() override; void RequestScheduleAnimation() override;
void UpdateVisualState() override; void UpdateVisualState() override;
void WillBeginCompositorFrame() override; void WillBeginCompositorFrame() override;
......
...@@ -17,6 +17,10 @@ void StubLayerTreeViewDelegate::RequestNewLayerTreeFrameSink( ...@@ -17,6 +17,10 @@ void StubLayerTreeViewDelegate::RequestNewLayerTreeFrameSink(
std::move(callback).Run(nullptr); std::move(callback).Run(nullptr);
} }
bool StubLayerTreeViewDelegate::IsClosing() const {
return false;
}
std::unique_ptr<cc::SwapPromise> std::unique_ptr<cc::SwapPromise>
StubLayerTreeViewDelegate::RequestCopyOfOutputForLayoutTest( StubLayerTreeViewDelegate::RequestCopyOfOutputForLayoutTest(
std::unique_ptr<viz::CopyOutputRequest> request) { std::unique_ptr<viz::CopyOutputRequest> request) {
......
...@@ -26,6 +26,7 @@ class StubLayerTreeViewDelegate : public LayerTreeViewDelegate { ...@@ -26,6 +26,7 @@ class StubLayerTreeViewDelegate : public LayerTreeViewDelegate {
void DidCommitAndDrawCompositorFrame() override {} void DidCommitAndDrawCompositorFrame() override {}
void DidCommitCompositorFrame() override {} void DidCommitCompositorFrame() override {}
void DidCompletePageScaleAnimation() override {} void DidCompletePageScaleAnimation() override {}
bool IsClosing() const override;
void RequestScheduleAnimation() override {} void RequestScheduleAnimation() override {}
void UpdateVisualState() override {} void UpdateVisualState() override {}
void WillBeginCompositorFrame() override {} void WillBeginCompositorFrame() override {}
......
...@@ -2817,6 +2817,10 @@ void WebViewImpl::ShowContextMenu(WebMenuSourceType source_type) { ...@@ -2817,6 +2817,10 @@ void WebViewImpl::ShowContextMenu(WebMenuSourceType source_type) {
WebURL WebViewImpl::GetURLForDebugTrace() { WebURL WebViewImpl::GetURLForDebugTrace() {
WebFrame* main_frame = MainFrame(); WebFrame* main_frame = MainFrame();
// TODO(crbug.com/896836): Avoid a crash in minimal way for merge. But we'll
// avoid it properly in a followup.
if (!main_frame)
return {};
if (main_frame->IsWebLocalFrame()) if (main_frame->IsWebLocalFrame())
return main_frame->ToWebLocalFrame()->GetDocument().Url(); return main_frame->ToWebLocalFrame()->GetDocument().Url();
return {}; return {};
......
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