Bug 1717983 - Improve PresShell active flag handling. r=nika

This moves the logic of whether a pres shell should be active to a
single place to make it sane to reason about, and fixes the
subdocument propagation when a BrowserChild becomes visible.

Differential Revision: https://phabricator.services.mozilla.com/D118703
This commit is contained in:
Emilio Cobos Álvarez
2021-07-05 17:31:48 +00:00
parent 76e9f0ae2f
commit 1af5e8922e
4 changed files with 78 additions and 71 deletions

View File

@@ -4721,9 +4721,9 @@ nsDocShell::GetVisibility(bool* aVisibility) {
} }
void nsDocShell::ActivenessMaybeChanged() { void nsDocShell::ActivenessMaybeChanged() {
bool isActive = mBrowsingContext->IsActive(); const bool isActive = mBrowsingContext->IsActive();
if (RefPtr<PresShell> presShell = GetPresShell()) { if (RefPtr<PresShell> presShell = GetPresShell()) {
presShell->SetIsActive(isActive); presShell->ActivenessMaybeChanged();
} }
// Tell the window about it // Tell the window about it
@@ -7990,7 +7990,6 @@ nsresult nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer,
} }
nscolor bgcolor = NS_RGBA(0, 0, 0, 0); nscolor bgcolor = NS_RGBA(0, 0, 0, 0);
bool isActive = false;
// Ensure that the content viewer is destroyed *after* the GC - bug 71515 // Ensure that the content viewer is destroyed *after* the GC - bug 71515
nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer; nsCOMPtr<nsIContentViewer> contentViewer = mContentViewer;
if (contentViewer) { if (contentViewer) {
@@ -8002,7 +8001,6 @@ nsresult nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer,
// presentation shell, so we can use it for the next document. // presentation shell, so we can use it for the next document.
if (PresShell* presShell = contentViewer->GetPresShell()) { if (PresShell* presShell = contentViewer->GetPresShell()) {
bgcolor = presShell->GetCanvasBackground(); bgcolor = presShell->GetCanvasBackground();
isActive = presShell->IsActive();
} }
contentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr); contentViewer->Close(mSavingOldViewer ? mOSHE.get() : nullptr);
@@ -8047,9 +8045,7 @@ nsresult nsDocShell::SetupNewViewer(nsIContentViewer* aNewViewer,
// pres shell. This improves page load continuity. // pres shell. This improves page load continuity.
if (RefPtr<PresShell> presShell = mContentViewer->GetPresShell()) { if (RefPtr<PresShell> presShell = mContentViewer->GetPresShell()) {
presShell->SetCanvasBackground(bgcolor); presShell->SetCanvasBackground(bgcolor);
if (isActive) { presShell->ActivenessMaybeChanged();
presShell->SetIsActive(isActive);
}
} }
// XXX: It looks like the LayoutState gets restored again in Embed() // XXX: It looks like the LayoutState gets restored again in Embed()

View File

@@ -2960,23 +2960,20 @@ void BrowserChild::MakeVisible() {
mPuppetWidget->Show(true); mPuppetWidget->Show(true);
} }
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation()); if (nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation())) {
if (!docShell) { // The browser / tab-switcher is responsible of fixing the browsingContext
return; // state up explicitly via SetDocShellIsActive, which propagates to children
} // automatically.
//
// The browser / tab-switcher is responsible of fixing the browsingContext // We need it not to be observable, as this used via RecvRenderLayers and co.,
// state up explicitly via SetDocShellIsActive, which propagates to children // for stuff like async tab warming.
// automatically. //
// // We don't want to go through the docshell because we don't want to change
// We need it not to be observable, as this used via RecvRenderLayers and co., // the visibility state of the document, which has side effects like firing
// for stuff like async tab warming. // events to content, unblocking media playback, unthrottling timeouts...
// if (RefPtr<PresShell> presShell = docShell->GetPresShell()) {
// We don't want to go through the docshell because we don't want to change presShell->ActivenessMaybeChanged();
// the visibility state of the document, which has side effects like firing }
// events to content, unblocking media playback, unthrottling timeouts...
if (RefPtr<PresShell> presShell = docShell->GetPresShell()) {
presShell->SetIsActive(true);
} }
} }
@@ -2990,24 +2987,22 @@ void BrowserChild::MakeHidden() {
// setting up a layer manager. We should skip clearing cached layers // setting up a layer manager. We should skip clearing cached layers
// in that case, since doing so might accidentally put is into // in that case, since doing so might accidentally put is into
// BasicLayers mode. // BasicLayers mode.
if (mPuppetWidget && mPuppetWidget->HasLayerManager()) { if (mPuppetWidget) {
ClearCachedResources(); if (mPuppetWidget->HasLayerManager()) {
ClearCachedResources();
}
mPuppetWidget->Show(false);
} }
if (nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation())) { if (nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation())) {
// We don't use // We don't use BrowserChildBase::GetPresShell() here because that would
// BrowserChildBase::GetPresShell() here because that would create a content // create a content viewer if one doesn't exist yet. Creating a content
// viewer if one doesn't exist yet. Creating a content viewer can cause JS // viewer can cause JS to run, which we want to avoid.
// to run, which we want to avoid. nsIDocShell::GetPresShell returns null if // nsIDocShell::GetPresShell returns null if no content viewer exists yet.
// no content viewer exists yet.
if (RefPtr<PresShell> presShell = docShell->GetPresShell()) { if (RefPtr<PresShell> presShell = docShell->GetPresShell()) {
presShell->SetIsActive(false); presShell->ActivenessMaybeChanged();
} }
} }
if (mPuppetWidget) {
mPuppetWidget->Show(false);
}
} }
NS_IMETHODIMP NS_IMETHODIMP

View File

@@ -1032,7 +1032,7 @@ void PresShell::Init(nsPresContext* aPresContext, nsViewManager* aViewManager) {
} }
// Get our activeness from the docShell. // Get our activeness from the docShell.
QueryIsActive(); ActivenessMaybeChanged();
// Setup our font inflation preferences. // Setup our font inflation preferences.
mFontSizeInflationEmPerLine = StaticPrefs::font_size_inflation_emPerLine(); mFontSizeInflationEmPerLine = StaticPrefs::font_size_inflation_emPerLine();
@@ -9309,7 +9309,7 @@ void PresShell::Thaw(bool aIncludeSubDocuments) {
// Get the activeness of our presshell, as this might have changed // Get the activeness of our presshell, as this might have changed
// while we were in the bfcache // while we were in the bfcache
QueryIsActive(); ActivenessMaybeChanged();
// We're now unfrozen // We're now unfrozen
mFrozen = false; mFrozen = false;
@@ -10763,12 +10763,15 @@ nsAccessibilityService* PresShell::GetAccessibilityService() {
#endif // #ifdef ACCESSIBILITY #endif // #ifdef ACCESSIBILITY
// Asks our docshell whether we're active. void PresShell::ActivenessMaybeChanged() {
void PresShell::QueryIsActive() { if (!mDocument) {
Document* doc = mDocument;
if (!doc) {
return; return;
} }
SetIsActive(ShouldBeActive());
}
bool PresShell::ShouldBeActive() const {
Document* doc = mDocument;
if (Document* displayDoc = doc->GetDisplayDocument()) { if (Document* displayDoc = doc->GetDisplayDocument()) {
// Ok, we're an external resource document -- we need to use our display // Ok, we're an external resource document -- we need to use our display
// document's docshell to determine "IsActive" status, since we lack // document's docshell to determine "IsActive" status, since we lack
@@ -10778,25 +10781,33 @@ void PresShell::QueryIsActive() {
doc = displayDoc; doc = displayDoc;
} }
if (BrowsingContext* bc = doc->GetBrowsingContext()) { Document* root = nsContentUtils::GetInProcessSubtreeRootDocument(doc);
// Even though in theory the docshell here could be "Inactive and if (auto* browserChild = BrowserChild::GetFrom(root->GetDocShell())) {
// Foreground", thus implying aIsHidden=false for SetIsActive(), this is a // We might want to activate a tab even though the browsing-context is not
// newly created PresShell so we'd like to invalidate anyway upon being made // active if the BrowserChild is considered visible. This serves two
// active to ensure that the contents get painted. // purposes:
auto* browserChild = BrowserChild::GetFrom(doc->GetDocShell()); //
const bool hiddenInRemoteFrame = browserChild && // * For top-level tabs, we use this for tab warming. The browsing-context
!browserChild->IsTopLevel() && // might still be inactive, but we want to activate the pres shell and
!browserChild->IsVisible(); // the refresh driver.
SetIsActive(bc->IsActive() && !hiddenInRemoteFrame); //
// * For oop iframes, we do want to throttle them if they're not visible.
//
// TODO(emilio): Consider unifying the in-process vs. fission iframe
// throttling code (in-process throttling for non-visible iframes lives
// right now in Document::ShouldThrottleFrameRequests(), but that only
// throttles rAF).
return browserChild->IsVisible();
} }
BrowsingContext* bc = doc->GetBrowsingContext();
return bc && bc->IsActive();
} }
nsresult PresShell::SetIsActive(bool aIsActive) { void PresShell::SetIsActive(bool aIsActive) {
MOZ_ASSERT(mDocument, "should only be called with a document"); MOZ_ASSERT(mDocument, "should only be called with a document");
#if defined(MOZ_WIDGET_ANDROID)
const bool changed = mIsActive != aIsActive; const bool changed = mIsActive != aIsActive;
#endif
mIsActive = aIsActive; mIsActive = aIsActive;
@@ -10806,17 +10817,24 @@ nsresult PresShell::SetIsActive(bool aIsActive) {
presContext->RefreshDriver()->SetThrottled(!mIsActive); presContext->RefreshDriver()->SetThrottled(!mIsActive);
} }
{ if (changed) {
// Propagate state-change to my resource documents' PresShells // Propagate state-change to my resource documents' PresShells and other
auto recurse = [aIsActive](Document& aResourceDoc) { // subdocuments.
if (PresShell* presShell = aResourceDoc.GetPresShell()) { //
// Note that it is fine to not propagate to fission iframes. Those will
// become active / inactive as needed as a result of they getting painted /
// not painted eventually.
auto recurse = [aIsActive](Document& aSubDoc) {
if (PresShell* presShell = aSubDoc.GetPresShell()) {
presShell->SetIsActive(aIsActive); presShell->SetIsActive(aIsActive);
} }
return CallState::Continue; return CallState::Continue;
}; };
mDocument->EnumerateExternalResources(recurse); mDocument->EnumerateExternalResources(recurse);
mDocument->EnumerateSubDocuments(recurse);
} }
nsresult rv = UpdateImageLockingState();
UpdateImageLockingState();
#ifdef ACCESSIBILITY #ifdef ACCESSIBILITY
if (aIsActive) { if (aIsActive) {
if (nsAccessibilityService* accService = if (nsAccessibilityService* accService =
@@ -10841,8 +10859,6 @@ nsresult PresShell::SetIsActive(bool aIsActive) {
rootFrame->SchedulePaint(); rootFrame->SchedulePaint();
} }
} }
return rv;
} }
RefPtr<MobileViewportManager> PresShell::GetMobileViewportManager() const { RefPtr<MobileViewportManager> PresShell::GetMobileViewportManager() const {
@@ -10952,11 +10968,11 @@ bool PresShell::UsesMobileViewportSizing() const {
* Determines the current image locking state. Called when one of the * Determines the current image locking state. Called when one of the
* dependent factors changes. * dependent factors changes.
*/ */
nsresult PresShell::UpdateImageLockingState() { void PresShell::UpdateImageLockingState() {
// We're locked if we're both thawed and active. // We're locked if we're both thawed and active.
bool locked = !mFrozen && mIsActive; bool locked = !mFrozen && mIsActive;
nsresult rv = mDocument->ImageTracker()->SetLockingState(locked); mDocument->ImageTracker()->SetLockingState(locked);
if (locked) { if (locked) {
// Request decodes for visible image frames; we want to start decoding as // Request decodes for visible image frames; we want to start decoding as
@@ -10967,8 +10983,6 @@ nsresult PresShell::UpdateImageLockingState() {
} }
} }
} }
return rv;
} }
PresShell* PresShell::GetRootPresShell() const { PresShell* PresShell::GetRootPresShell() const {

View File

@@ -881,9 +881,8 @@ class PresShell final : public nsStubDocumentObserver,
return mObservesMutationsForPrint; return mObservesMutationsForPrint;
} }
nsresult SetIsActive(bool aIsActive); void ActivenessMaybeChanged();
bool IsActive() const { return mIsActive; }
bool IsActive() { return mIsActive; }
/** /**
* Keep track of how many times this presshell has been rendered to * Keep track of how many times this presshell has been rendered to
@@ -1692,6 +1691,10 @@ class PresShell final : public nsStubDocumentObserver,
private: private:
~PresShell(); ~PresShell();
void SetIsActive(bool aIsActive);
bool ShouldBeActive() const;
/** /**
* Refresh observer management. * Refresh observer management.
*/ */
@@ -1936,8 +1939,7 @@ class PresShell final : public nsStubDocumentObserver,
}; };
MOZ_CAN_RUN_SCRIPT void ProcessSynthMouseMoveEvent(bool aFromScroll); MOZ_CAN_RUN_SCRIPT void ProcessSynthMouseMoveEvent(bool aFromScroll);
void QueryIsActive(); void UpdateImageLockingState();
nsresult UpdateImageLockingState();
already_AddRefed<PresShell> GetParentPresShellForEventHandling(); already_AddRefed<PresShell> GetParentPresShellForEventHandling();