Bug 1541253 - Propagate CSS visivibility information to descendant documents. r=tnikkel
authorHiroyuki Ikezoe <hikezoe@mozilla.com>
Fri, 10 May 2019 11:15:43 +0000
changeset 532199 335b9a8f496819c5777c0f67eff5f88bf983288b
parent 532198 d3340f866c7df1f283153c1dec37d5b83933bf7f
child 532200 57c522abe28f6d78f646d90c720022057205666c
push id11265
push userffxbld-merge
push dateMon, 13 May 2019 10:53:39 +0000
treeherdermozilla-beta@77e0fe8dbdd3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstnikkel
bugs1541253
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1541253 - Propagate CSS visivibility information to descendant documents. r=tnikkel Depends on D26251 Differential Revision: https://phabricator.services.mozilla.com/D26252
docshell/base/nsDocShell.cpp
layout/base/PresShell.cpp
layout/base/PresShell.h
layout/generic/nsSubDocumentFrame.cpp
layout/generic/nsSubDocumentFrame.h
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7657,17 +7657,18 @@ nsresult nsDocShell::RestoreFromHistory(
   // Save off the root view's parent and sibling so that we can insert the
   // new content viewer's root view at the same position.  Also save the
   // bounds of the root view's widget.
 
   nsView* rootViewSibling = nullptr;
   nsView* rootViewParent = nullptr;
   nsIntRect newBounds(0, 0, 0, 0);
 
-  if (PresShell* oldPresShell = GetPresShell()) {
+  PresShell* oldPresShell = GetPresShell();
+  if (oldPresShell) {
     nsViewManager* vm = oldPresShell->GetViewManager();
     if (vm) {
       nsView* oldRootView = vm->GetRootView();
 
       if (oldRootView) {
         rootViewSibling = oldRootView->GetNextSibling();
         rootViewParent = oldRootView->GetParent();
 
@@ -7999,16 +8000,25 @@ nsresult nsDocShell::RestoreFromHistory(
     }
   }
 
   // The FinishRestore call below can kill these, null them out so we don't
   // have invalid pointer lying around.
   newRootView = rootViewSibling = rootViewParent = nullptr;
   newVM = nullptr;
 
+  // If the IsUnderHiddenEmbedderElement() state has been changed, we need to
+  // update it.
+  if (oldPresShell && presShell &&
+      presShell->IsUnderHiddenEmbedderElement() !=
+          oldPresShell->IsUnderHiddenEmbedderElement()) {
+    presShell->SetIsUnderHiddenEmbedderElement(
+        oldPresShell->IsUnderHiddenEmbedderElement());
+  }
+
   // Simulate the completion of the load.
   nsDocShell::FinishRestore();
 
   // Restart plugins, and paint the content.
   if (presShell) {
     presShell->Thaw();
   }
 
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -40,16 +40,17 @@
 
 #include "gfxContext.h"
 #include "gfxPrefs.h"
 #include "gfxUserFontSet.h"
 #include "nsContentList.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
 #include "mozilla/dom/BrowserBridgeChild.h"
+#include "mozilla/dom/BrowsingContext.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/PointerEventHandler.h"
 #include "mozilla/dom/PopupBlocker.h"
 #include "mozilla/dom/Document.h"
 #include "mozilla/dom/DocumentInlines.h"
 #include "nsAnimationManager.h"
 #include "nsNameSpaceManager.h"  // for Pref-related rule management (bugs 22963,20760,31816)
 #include "nsFrame.h"
@@ -816,16 +817,17 @@ PresShell::PresShell()
       mResizeEventPending(false),
       mFontSizeInflationForceEnabled(false),
       mFontSizeInflationDisabledInMasterProcess(false),
       mFontSizeInflationEnabled(false),
       mPaintingIsFrozen(false),
       mIsNeverPainting(false),
       mResolutionUpdated(false),
       mResolutionUpdatedByApz(false),
+      mUnderHiddenEmbedderElement(false),
       mDocumentLoading(false),
       mNoDelayedMouseEvents(false),
       mNoDelayedKeyEvents(false),
       mApproximateFrameVisibilityVisited(false),
       mNextPaintCompressed(false),
       mHasCSSBackgroundColor(true),
       mIsLastChromeOnlyEscapeKeyConsumed(false),
       mHasReceivedPaintMessage(false),
@@ -1050,16 +1052,36 @@ void PresShell::Init(Document* aDocument
 
   if (mPresContext->IsRootContentDocument()) {
     mZoomConstraintsClient = new ZoomConstraintsClient();
     mZoomConstraintsClient->Init(this, mDocument);
 
     // We call this to create mMobileViewportManager, if it is needed.
     UpdateViewportOverridden(false);
   }
+
+  if (nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell()) {
+    BrowsingContext* bc = nsDocShell::Cast(docShell)->GetBrowsingContext();
+    bool embedderFrameIsHidden = true;
+    if (Element* embedderElement = bc->GetEmbedderElement()) {
+      if (auto embedderFrame = embedderElement->GetPrimaryFrame()) {
+        embedderFrameIsHidden = !embedderFrame->StyleVisibility()->IsVisible();
+      }
+    }
+
+    if (BrowsingContext* parent = bc->GetParent()) {
+      if (nsCOMPtr<nsIDocShell> parentDocShell = parent->GetDocShell()) {
+        if (PresShell* parentPresShell = parentDocShell->GetPresShell()) {
+          mUnderHiddenEmbedderElement =
+              parentPresShell->IsUnderHiddenEmbedderElement() ||
+              embedderFrameIsHidden;
+        }
+      }
+    }
+  }
 }
 
 enum TextPerfLogType { eLog_reflow, eLog_loaddone, eLog_totals };
 
 static void LogTextPerfStats(gfxTextPerfMetrics* aTextPerf,
                              PresShell* aPresShell,
                              const gfxTextPerfMetrics::TextCounts& aCounts,
                              float aTime, TextPerfLogType aLogType,
@@ -10879,16 +10901,53 @@ void PresShell::NotifyStyleSheetServiceS
   }
 }
 
 void PresShell::NotifyStyleSheetServiceSheetRemoved(StyleSheet* aSheet,
                                                     uint32_t aSheetType) {
   RemoveSheet(ToOrigin(aSheetType), aSheet);
 }
 
+void PresShell::SetIsUnderHiddenEmbedderElement(
+    bool aUnderHiddenEmbedderElement) {
+  if (mUnderHiddenEmbedderElement == aUnderHiddenEmbedderElement) {
+    return;
+  }
+
+  mUnderHiddenEmbedderElement = aUnderHiddenEmbedderElement;
+
+  if (nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell()) {
+    BrowsingContext* bc = nsDocShell::Cast(docShell)->GetBrowsingContext();
+
+    // Propagate to children.
+    for (BrowsingContext* child : bc->GetChildren()) {
+      bool embedderFrameIsHidden = true;
+      if (Element* embedderElement = bc->GetEmbedderElement()) {
+        if (auto embedderFrame = embedderElement->GetPrimaryFrame()) {
+          embedderFrameIsHidden =
+              !embedderFrame->StyleVisibility()->IsVisible();
+        }
+      }
+
+      if (nsIDocShell* childDocShell = child->GetDocShell()) {
+        PresShell* presShell = childDocShell->GetPresShell();
+        if (!presShell) {
+          continue;
+        }
+
+        presShell->SetIsUnderHiddenEmbedderElement(
+            aUnderHiddenEmbedderElement || embedderFrameIsHidden);
+      }
+      // FIXME: Bug 1518919 - In the case where the BrowsingContext has no
+      // docshell which means it's out-of-process iframe, we need to propagate
+      // the info via an IPC call.
+    }
+  }
+}
+
 nsIContent* PresShell::EventHandler::GetOverrideClickTarget(
     WidgetGUIEvent* aGUIEvent, nsIFrame* aFrame) {
   if (aGUIEvent->mMessage != eMouseUp) {
     return nullptr;
   }
 
   MOZ_ASSERT(aGUIEvent->mClass == eMouseEventClass);
   WidgetMouseEvent* mouseEvent = aGUIEvent->AsMouseEvent();
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -1039,16 +1039,20 @@ class PresShell final : public nsStubDoc
   /**
    * Notify that we called Paint with PaintFlags::PaintComposite.
    * Fires on the presshell for the painted widget.
    * This is issued at a time when it's safe to modify widget geometry.
    */
   MOZ_CAN_RUN_SCRIPT void DidPaintWindow();
 
   bool IsVisible() const;
+  bool IsUnderHiddenEmbedderElement() const {
+    return mUnderHiddenEmbedderElement;
+  }
+  void SetIsUnderHiddenEmbedderElement(bool aUnderHiddenEmbedderElement);
   MOZ_CAN_RUN_SCRIPT
   void DispatchSynthMouseMove(WidgetGUIEvent* aEvent);
 
   /* Temporarily ignore the Displayport for better paint performance. We
    * trigger a repaint once suppression is disabled. Without that
    * the displayport may get left at the suppressed size for an extended
    * period of time and result in unnecessary checkerboarding (see bug
    * 1255054). */
@@ -3070,16 +3074,20 @@ class PresShell final : public nsStubDoc
 
   // Whether the most recent change to the pres shell resolution was
   // originated by the main thread.
   bool mResolutionUpdated : 1;
 
   // True if the resolution has been ever changed by APZ.
   bool mResolutionUpdatedByApz : 1;
 
+  // Whether this presshell is hidden by 'vibility:hidden' on an ancestor
+  // nsSubDocumentFrame.
+  bool mUnderHiddenEmbedderElement : 1;
+
   bool mDocumentLoading : 1;
   bool mNoDelayedMouseEvents : 1;
   bool mNoDelayedKeyEvents : 1;
 
   bool mApproximateFrameVisibilityVisited : 1;
 
   bool mNextPaintCompressed : 1;
 
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -146,19 +146,42 @@ void nsSubDocumentFrame::Init(nsIContent
         ::EndSwapDocShellsForViews(mInnerView->GetFirstChild());
       } else {
         // Presentation is for a different document, don't restore it.
         frameloader->Hide();
       }
     }
   }
 
+  PropagateIsUnderHiddenEmbedderElementToSubView(
+      PresShell()->IsUnderHiddenEmbedderElement() ||
+      !StyleVisibility()->IsVisible());
+
   nsContentUtils::AddScriptRunner(new AsyncFrameInit(this));
 }
 
+void nsSubDocumentFrame::PropagateIsUnderHiddenEmbedderElementToSubView(
+    bool aIsUnderHiddenEmbedderElement) {
+  // FIXME: Bug 1518919 - In the case where we have mFrameLoader and its
+  // IsRemoteFrame() is true, the iframe is out-of-process iframe, so we need
+  // to notify the change via nsFrameLoader.
+
+  if (!mInnerView) {
+    return;
+  }
+
+  nsView* subdocView = mInnerView->GetFirstChild();
+  while (subdocView) {
+    if (mozilla::PresShell* presShell = subdocView->GetPresShell()) {
+      presShell->SetIsUnderHiddenEmbedderElement(aIsUnderHiddenEmbedderElement);
+    }
+    subdocView = subdocView->GetNextSibling();
+  }
+}
+
 void nsSubDocumentFrame::ShowViewer() {
   if (mCallingShow) {
     return;
   }
 
   if (!PresContext()->IsDynamic()) {
     // We let the printing code take care of loading the document; just
     // create the inner view for it to use.
@@ -893,16 +916,34 @@ nsresult nsSubDocumentFrame::AttributeCh
     // Notify the frameloader
     RefPtr<nsFrameLoader> frameloader = FrameLoader();
     if (frameloader) frameloader->MarginsChanged(margins.width, margins.height);
   }
 
   return NS_OK;
 }
 
+void nsSubDocumentFrame::DidSetComputedStyle(ComputedStyle* aOldComputedStyle) {
+  nsAtomicContainerFrame::DidSetComputedStyle(aOldComputedStyle);
+
+  // If this presshell has invisible ancestors, we don't need to propagate the
+  // visibility style change to the subdocument since the subdocument should
+  // have already set the IsUnderHiddenEmbedderElement flag in
+  // nsSubDocumentFrame::Init.
+  if (PresShell()->IsUnderHiddenEmbedderElement()) {
+    return;
+  }
+
+  const bool isVisible = StyleVisibility()->IsVisible();
+  if (!aOldComputedStyle ||
+      isVisible != aOldComputedStyle->StyleVisibility()->IsVisible()) {
+    PropagateIsUnderHiddenEmbedderElementToSubView(!isVisible);
+  }
+}
+
 nsIFrame* NS_NewSubDocumentFrame(PresShell* aPresShell, ComputedStyle* aStyle) {
   return new (aPresShell)
       nsSubDocumentFrame(aStyle, aPresShell->GetPresContext());
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSubDocumentFrame)
 
 class nsHideViewer : public Runnable {
@@ -1196,21 +1237,27 @@ void nsSubDocumentFrame::EndSwapDocShell
   // Now make sure we reflow both frames, in case their contents
   // determine their size.
   // And repaint them, for good measure, in case there's nothing
   // interesting that happens during reflow.
   if (weakThis.IsAlive()) {
     PresShell()->FrameNeedsReflow(this, IntrinsicDirty::TreeChange,
                                   NS_FRAME_IS_DIRTY);
     InvalidateFrameSubtree();
+    PropagateIsUnderHiddenEmbedderElementToSubView(
+        PresShell()->IsUnderHiddenEmbedderElement() ||
+        !StyleVisibility()->IsVisible());
   }
   if (weakOther.IsAlive()) {
     other->PresShell()->FrameNeedsReflow(other, IntrinsicDirty::TreeChange,
                                          NS_FRAME_IS_DIRTY);
     other->InvalidateFrameSubtree();
+    other->PropagateIsUnderHiddenEmbedderElementToSubView(
+        other->PresShell()->IsUnderHiddenEmbedderElement() ||
+        !other->StyleVisibility()->IsVisible());
   }
 }
 
 void nsSubDocumentFrame::ClearDisplayItems() {
   DisplayItemArray* items = GetProperty(DisplayItems());
   if (!items) {
     return;
   }
--- a/layout/generic/nsSubDocumentFrame.h
+++ b/layout/generic/nsSubDocumentFrame.h
@@ -74,16 +74,18 @@ class nsSubDocumentFrame final : public 
               nsReflowStatus& aStatus) override;
 
   void BuildDisplayList(nsDisplayListBuilder* aBuilder,
                         const nsDisplayListSet& aLists) override;
 
   nsresult AttributeChanged(int32_t aNameSpaceID, nsAtom* aAttribute,
                             int32_t aModType) override;
 
+  void DidSetComputedStyle(ComputedStyle* aOldComputedStyle) override;
+
   // if the content is "visibility:hidden", then just hide the view
   // and all our contents. We don't extend "visibility:hidden" to
   // the child content ourselves, since it belongs to a different
   // document and CSS doesn't inherit in there.
   bool SupportsVisibilityHidden() override { return false; }
 
 #ifdef ACCESSIBILITY
   mozilla::a11y::AccType AccessibleType() override;
@@ -112,16 +114,19 @@ class nsSubDocumentFrame final : public 
     if (!mDidCreateDoc && !mCallingShow) {
       ShowViewer();
     }
   }
 
   nsFrameLoader* FrameLoader() const;
   void ResetFrameLoader();
 
+  void PropagateIsUnderHiddenEmbedderElementToSubView(
+      bool aIsUnderHiddenEmbedderElement);
+
  protected:
   friend class AsyncFrameInit;
 
   // Helper method to look up the HTML marginwidth & marginheight attributes.
   mozilla::CSSIntSize GetMarginAttributes();
 
   bool IsInline() { return mIsInline; }