Bug 1582042 - Use DocShell::SetIsActive from BrowserChild::MakeVisible for non top-level browsers. r=mconley,rhunt

That is, for fission iframes. For top level stuff we rely on the tab switcher
going through SetDocShellIsActive and such.

See the expanded comment as for why. Ideally we could simplify this further by
not making RecvRenderLayers update the visibility (which spins the refresh
driver and such).

It's a bit suspect because it's very easy to get to an inconsistent state if the
browser chrome does something wrong.

I'll try to do that, but for now this should improve the fission situation
anyway.

Differential Revision: https://phabricator.services.mozilla.com/D46706

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Emilio Cobos Álvarez 2019-09-27 13:11:51 +00:00
Родитель 4353735c92
Коммит 35f576f36b
2 изменённых файлов: 89 добавлений и 58 удалений

Просмотреть файл

@ -1219,7 +1219,7 @@ mozilla::ipc::IPCResult BrowserChild::RecvShow(const ScreenIntSize& aSize,
recordreplay::child::CreateCheckpoint();
}
UpdateVisibility(false);
UpdateVisibility();
return IPC_OK();
}
@ -2445,9 +2445,7 @@ void BrowserChild::RemovePendingDocShellBlocker() {
}
void BrowserChild::InternalSetDocShellIsActive(bool aIsActive) {
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
if (docShell) {
if (nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation())) {
docShell->SetIsActive(aIsActive);
}
}
@ -2514,21 +2512,73 @@ mozilla::ipc::IPCResult BrowserChild::RecvRenderLayers(
mRenderLayers = aEnabled;
if (aEnabled) {
if (!aForceRepaint && IsVisible()) {
// This request is a no-op. In this case, we still want a
// MozLayerTreeReady notification to fire in the parent (so that it knows
// that the child has updated its epoch). PaintWhileInterruptingJSNoOp
// does that.
if (aEnabled && IsVisible()) {
// This request is a no-op.
if (!aForceRepaint) {
// In this case, we still want a MozLayerTreeReady notification to fire
// in the parent (so that it knows that the child has updated its epoch).
// PaintWhileInterruptingJSNoOp does that.
//
// FIXME(emilio): Why only when aForceRepaint is false? It seems it's
// just what the tab switcher uses, but we could remove the argument
// if we do this unconditionally.
if (IPCOpen()) {
Unused << SendPaintWhileInterruptingJSNoOp(mLayersObserverEpoch);
return IPC_OK();
}
}
return IPC_OK();
}
UpdateVisibility(true);
// FIXME(emilio): Probably / maybe this shouldn't be needed? See the comment
// in MakeVisible(), having the two separate states is not great.
UpdateVisibility();
if (!aEnabled) {
return IPC_OK();
}
// FIXME(emilio): We force a repaint if visible even if aForceRepaint, why?
//
// This is artifact of a refacting, but respecting aForceRepaint breaks
// tab-switching in WR+Windows, because well, we don't repaint and thus never
// send the MozLayerTreeReady notification to the parent.
//
// At this point aForceRepaint seems pretty useless.
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
if (!docShell) {
return IPC_OK();
}
// We don't use BrowserChildBase::GetPresShell() here because that would
// create a content viewer if one doesn't exist yet. Creating a content
// viewer can cause JS to run, which we want to avoid.
// nsIDocShell::GetPresShell returns null if no content viewer exists yet.
RefPtr<PresShell> presShell = docShell->GetPresShell();
if (!presShell) {
return IPC_OK();
}
if (nsIFrame* root = presShell->GetRootFrame()) {
FrameLayerBuilder::InvalidateAllLayersForFrame(
nsLayoutUtils::GetDisplayRootFrame(root));
root->SchedulePaint();
}
Telemetry::AutoTimer<Telemetry::TABCHILD_PAINT_TIME> timer;
// If we need to repaint, let's do that right away. No sense waiting until
// we get back to the event loop again. We suppress the display port so
// that we only paint what's visible. This ensures that the tab we're
// switching to paints as quickly as possible.
presShell->SuppressDisplayport(true);
if (nsContentUtils::IsSafeToRunScript()) {
WebWidget()->PaintNowIfNeeded();
} else {
RefPtr<nsViewManager> vm = presShell->GetViewManager();
if (nsView* view = vm->GetRootView()) {
presShell->Paint(view, view->GetBounds(), PaintFlags::PaintLayers);
}
}
presShell->SuppressDisplayport(false);
return IPC_OK();
}
@ -2753,7 +2803,7 @@ void BrowserChild::NotifyPainted() {
IPCResult BrowserChild::RecvUpdateEffects(const EffectsInfo& aEffects) {
mEffectsInfo = aEffects;
UpdateVisibility(false);
UpdateVisibility();
return IPC_OK();
}
@ -2761,20 +2811,20 @@ bool BrowserChild::IsVisible() {
return mPuppetWidget && mPuppetWidget->IsVisible();
}
void BrowserChild::UpdateVisibility(bool aForceRepaint) {
void BrowserChild::UpdateVisibility() {
bool shouldBeVisible = mIsTopLevel ? mRenderLayers : mEffectsInfo.IsVisible();
bool isVisible = IsVisible();
if (shouldBeVisible != isVisible) {
if (shouldBeVisible) {
MakeVisible(aForceRepaint);
MakeVisible();
} else {
MakeHidden();
}
}
}
void BrowserChild::MakeVisible(bool aForceRepaint) {
void BrowserChild::MakeVisible() {
if (IsVisible()) {
return;
}
@ -2793,44 +2843,26 @@ void BrowserChild::MakeVisible(bool aForceRepaint) {
return;
}
// We don't use BrowserChildBase::GetPresShell() here because that would
// create a content viewer if one doesn't exist yet. Creating a content
// viewer can cause JS to run, which we want to avoid.
// nsIDocShell::GetPresShell returns null if no content viewer exists yet.
if (RefPtr<PresShell> presShell = docShell->GetPresShell()) {
presShell->SetIsActive(true);
}
if (!aForceRepaint) {
return;
}
// We don't use BrowserChildBase::GetPresShell() here because that would
// create a content viewer if one doesn't exist yet. Creating a content
// viewer can cause JS to run, which we want to avoid.
// nsIDocShell::GetPresShell returns null if no content viewer exists yet.
if (RefPtr<PresShell> presShell = docShell->GetPresShell()) {
if (nsIFrame* root = presShell->GetRootFrame()) {
FrameLayerBuilder::InvalidateAllLayersForFrame(
nsLayoutUtils::GetDisplayRootFrame(root));
root->SchedulePaint();
// For top level stuff, the browser / tab-switcher is responsible of fixing
// the docshell state up explicitly via SetDocShellIsActive.
//
// We need it not to be observable, as this used via RecvRenderLayers and co.,
// for stuff like async tab warming.
//
// We don't want to go through the docshell because we don't want to change
// the visibility state of the document, which has side effects like firing
// events to content and unblocking media playback.
//
// FIXME(emilio): This feels a bit sketchy. Ideally we'd be able to just not
// update visibility of stuff in the tab warming case (we just want to paint
// once so that stuff is there already, really...), and use the docshell here
// all the time, but that makes some of the devtools tests fail (??).
if (mIsTopLevel) {
if (RefPtr<PresShell> presShell = docShell->GetPresShell()) {
presShell->SetIsActive(true);
}
Telemetry::AutoTimer<Telemetry::TABCHILD_PAINT_TIME> timer;
// If we need to repaint, let's do that right away. No sense waiting until
// we get back to the event loop again. We suppress the display port so
// that we only paint what's visible. This ensures that the tab we're
// switching to paints as quickly as possible.
presShell->SuppressDisplayport(true);
if (nsContentUtils::IsSafeToRunScript()) {
WebWidget()->PaintNowIfNeeded();
} else {
RefPtr<nsViewManager> vm = presShell->GetViewManager();
if (nsView* view = vm->GetRootView()) {
presShell->Paint(view, view->GetBounds(), PaintFlags::PaintLayers);
}
}
presShell->SuppressDisplayport(false);
} else {
docShell->SetIsActive(true);
}
}
@ -2854,8 +2886,7 @@ void BrowserChild::MakeHidden() {
ClearCachedResources();
}
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
if (docShell) {
if (nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation())) {
// Hide all plugins in this tab. We don't use
// BrowserChildBase::GetPresShell() here because that would create a content
// viewer if one doesn't exist yet. Creating a content viewer can cause JS
@ -2869,8 +2900,8 @@ void BrowserChild::MakeHidden() {
nullptr);
rootPresContext->ApplyPluginGeometryUpdates();
}
presShell->SetIsActive(false);
}
docShell->SetIsActive(false);
}
if (mPuppetWidget) {

Просмотреть файл

@ -447,8 +447,8 @@ class BrowserChild final : public nsMessageManagerScriptExecutor,
* activated widget, retained layer tree, etc. (Respectively,
* made not visible.)
*/
MOZ_CAN_RUN_SCRIPT void UpdateVisibility(bool aForceRepaint);
MOZ_CAN_RUN_SCRIPT void MakeVisible(bool aForceRepaint);
MOZ_CAN_RUN_SCRIPT void UpdateVisibility();
MOZ_CAN_RUN_SCRIPT void MakeVisible();
void MakeHidden();
ContentChild* Manager() const { return mManager; }