Commit f18f865d authored by Ryan Hunt's avatar Ryan Hunt
Browse files

Bug 1519546, part 6 - Centralize the visibility logic in BrowserChild and use...

Bug 1519546, part 6 - Centralize the visibility logic in BrowserChild and use EffectsInfo. r=mattwoodrow

Currently, BrowserChild rendering is enabled and disabled by `RecvRenderLayers`, and this
method is called only by the tab switching code.

This commit does several things.
1. It factors out the code to enable/disable rendering to MakeVisible/MakeHidden so it can
   be used outside of `RecvRenderLayers`
2. We track the current value of RenderLayers and use it in conjunction with EffectsInfo to
   determine if we need to be rendering at any given moment
3. We only apply RenderLayers to the root OOP browser (not OOP-iframes)

These changes together make it so that BrowserChild will render IFF 'visible' || 'renderLayers',
and will only apply 'renderLayers' to the root browser.

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

--HG--
extra : rebase_source : 0ce9db458f7fd2aa920adf99c5fa8c2634e7ab88
extra : source : e2197dd98aaeeb3d80b65c9892a82d41c4adc80d
parent 31f16852
Loading
Loading
Loading
Loading
+82 −55
Original line number Diff line number Diff line
@@ -405,6 +405,7 @@ BrowserChild::BrowserChild(ContentChild* aManager, const TabId& aTabId,
      mTopLevelDocAccessibleChild(nullptr),
#endif
      mShouldSendWebProgressEventsToParent(false),
      mRenderLayers(true),
      mPendingDocShellIsActive(false),
      mPendingDocShellReceivedMessage(false),
      mPendingRenderLayers(false),
@@ -1211,6 +1212,8 @@ mozilla::ipc::IPCResult BrowserChild::RecvShow(const ScreenIntSize& aSize,
    recordreplay::child::CreateCheckpoint();
  }

  UpdateVisibility(false);

  return IPC_OK();
}

@@ -2486,6 +2489,8 @@ mozilla::ipc::IPCResult BrowserChild::RecvRenderLayers(
    lm->SetLayersObserverEpoch(mLayersObserverEpoch);
  }

  mRenderLayers = aEnabled;

  if (aEnabled) {
    if (!aForceRepaint && IsVisible()) {
      // This request is a no-op. In this case, we still want a
@@ -2497,57 +2502,9 @@ mozilla::ipc::IPCResult BrowserChild::RecvRenderLayers(
        return IPC_OK();
      }
    }

    if (!sVisibleTabs) {
      sVisibleTabs = new nsTHashtable<nsPtrHashKey<BrowserChild>>();
    }
    sVisibleTabs->PutEntry(this);

    MakeVisible();

    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.
    if (RefPtr<PresShell> presShell = docShell->GetPresShell()) {
      presShell->SetIsActive(true);

      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);
    }
  } else {
    if (sVisibleTabs) {
      sVisibleTabs->RemoveEntry(this);
      // We don't delete sVisibleTabs here when it's empty since that
      // could cause a lot of churn. Instead, we wait until ~BrowserChild.
  }

    MakeHidden();
  }
  UpdateVisibility(true);

  return IPC_OK();
}
@@ -2801,20 +2758,94 @@ void BrowserChild::NotifyPainted() {

IPCResult BrowserChild::RecvUpdateEffects(const EffectsInfo& aEffects) {
  mEffectsInfo = aEffects;
  UpdateVisibility(false);
  return IPC_OK();
}

void BrowserChild::MakeVisible() {
bool BrowserChild::IsVisible() {
  return mPuppetWidget && mPuppetWidget->IsVisible();
}

void BrowserChild::UpdateVisibility(bool aForceRepaint) {
  bool shouldBeVisible = mIsTopLevel ? mRenderLayers : mEffectsInfo.mVisible;
  bool isVisible = IsVisible();

  if (shouldBeVisible != isVisible) {
    if (shouldBeVisible) {
      MakeVisible(aForceRepaint);
    } else {
      MakeHidden();
    }
  }
}

void BrowserChild::MakeVisible(bool aForceRepaint) {
  if (IsVisible()) {
    return;
  }

  if (!sVisibleTabs) {
    sVisibleTabs = new nsTHashtable<nsPtrHashKey<BrowserChild>>();
  }
  sVisibleTabs->PutEntry(this);

  if (mPuppetWidget) {
    mPuppetWidget->Show(true);
  }

  nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
  if (!docShell) {
    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();
    }

    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);
  }
}

void BrowserChild::MakeHidden() {
  if (sVisibleTabs) {
    sVisibleTabs->RemoveEntry(this);
    // We don't delete sVisibleTabs here when it's empty since that
    // could cause a lot of churn. Instead, we wait until ~BrowserChild.
  }

  if (!IsVisible()) {
    return;
  }
@@ -2852,10 +2883,6 @@ void BrowserChild::MakeHidden() {
  }
}

bool BrowserChild::IsVisible() {
  return mPuppetWidget && mPuppetWidget->IsVisible();
}

NS_IMETHODIMP
BrowserChild::GetMessageManager(ContentFrameMessageManager** aResult) {
  RefPtr<ContentFrameMessageManager> mm(mBrowserChildMessageManager);
+9 −3
Original line number Diff line number Diff line
@@ -307,6 +307,7 @@ class BrowserChild final : public BrowserChildBase,
  mozilla::ipc::IPCResult RecvResumeLoad(const uint64_t& aPendingSwitchID,
                                         const ShowInfo& aInfo);

  MOZ_CAN_RUN_SCRIPT_BOUNDARY
  mozilla::ipc::IPCResult RecvShow(const ScreenIntSize& aSize,
                                   const ShowInfo& aInfo,
                                   const bool& aParentIsActive,
@@ -474,21 +475,23 @@ class BrowserChild final : public BrowserChildBase,

  void NotifyPainted();

  virtual mozilla::ipc::IPCResult RecvUpdateEffects(
  MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual mozilla::ipc::IPCResult RecvUpdateEffects(
      const EffectsInfo& aEffects);

  void RequestEditCommands(nsIWidget::NativeKeyBindingsType aType,
                           const WidgetKeyboardEvent& aEvent,
                           nsTArray<CommandInt>& aCommands);

  bool IsVisible();

  /**
   * Signal to this BrowserChild that it should be made visible:
   * activated widget, retained layer tree, etc.  (Respectively,
   * made not visible.)
   */
  void MakeVisible();
  MOZ_CAN_RUN_SCRIPT void UpdateVisibility(bool aForceRepaint);
  MOZ_CAN_RUN_SCRIPT void MakeVisible(bool aForceRepaint);
  void MakeHidden();
  bool IsVisible();

  ContentChild* Manager() const { return mManager; }

@@ -926,6 +929,9 @@ class BrowserChild final : public BrowserChildBase,

  bool mShouldSendWebProgressEventsToParent;

  // Whether we are rendering to the compositor or not.
  bool mRenderLayers;

  // In some circumstances, a DocShell might be in a state where it is
  // "blocked", and we should not attempt to change its active state or
  // the underlying PresShell state until the DocShell becomes unblocked.
+1 −3
Original line number Diff line number Diff line
@@ -124,9 +124,7 @@ BrowserHost::SetRenderLayers(bool aRenderLayers) {
  if (!mRoot) {
    return NS_OK;
  }
  VisitAll([&](BrowserParent* aBrowserParent) {
    aBrowserParent->SetRenderLayers(aRenderLayers);
  });
  mRoot->SetRenderLayers(aRenderLayers);
  return NS_OK;
}