Bug 1126876 - Prevent subdocuments inside a pointer-events:none frame from building event regions. r=roc,tn
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 03 Feb 2015 10:52:51 -0500
changeset 227234 93c0418bd4b267ffa2c9e4ba5251c5b9a94c08c7
parent 227233 5636007e3d1a110bbce81f46b61cee5602249cc1
child 227235 8b9d38b020e1bb372f8e6d7bcd9e0eac9801f9aa
push id55058
push userkgupta@mozilla.com
push dateTue, 03 Feb 2015 15:53:12 +0000
treeherdermozilla-inbound@93c0418bd4b2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, tn
bugs1126876
milestone38.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 1126876 - Prevent subdocuments inside a pointer-events:none frame from building event regions. r=roc,tn
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/generic/nsSubDocumentFrame.cpp
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -965,17 +965,18 @@ nsDisplayListBuilder::SubtractFromVisibl
 
 nsCaret *
 nsDisplayListBuilder::GetCaret() {
   nsRefPtr<nsCaret> caret = CurrentPresShellState()->mPresShell->GetCaret();
   return caret;
 }
 
 void
-nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame)
+nsDisplayListBuilder::EnterPresShell(nsIFrame* aReferenceFrame,
+                                     bool aPointerEventsNoneDoc)
 {
   PresShellState* state = mPresShellStates.AppendElement();
   state->mPresShell = aReferenceFrame->PresContext()->PresShell();
   state->mCaretFrame = nullptr;
   state->mFirstFrameMarkedForDisplay = mFramesMarkedForDisplay.Length();
 
   state->mPresShell->UpdateCanvasBackground();
 
@@ -991,16 +992,22 @@ nsDisplayListBuilder::EnterPresShell(nsI
       mHadToIgnoreSuppression = true;
     }
     state->mIsBackgroundOnly = false;
   } else {
     state->mIsBackgroundOnly = true;
     buildCaret = false;
   }
 
+  bool pointerEventsNone = aPointerEventsNoneDoc;
+  if (IsInSubdocument()) {
+    pointerEventsNone |= mPresShellStates[mPresShellStates.Length() - 2].mInsidePointerEventsNoneDoc;
+  }
+  state->mInsidePointerEventsNoneDoc = pointerEventsNone;
+
   if (!buildCaret)
     return;
 
   nsRefPtr<nsCaret> caret = state->mPresShell->GetCaret();
   state->mCaretFrame = caret->GetPaintGeometry(&state->mCaretRect);
   if (state->mCaretFrame) {
     mFramesMarkedForDisplay.AppendElement(state->mCaretFrame);
     MarkFrameForDisplay(state->mCaretFrame, nullptr);
@@ -3095,16 +3102,21 @@ nsDisplayEventReceiver::HitTest(nsDispla
 }
 
 void
 nsDisplayLayerEventRegions::AddFrame(nsDisplayListBuilder* aBuilder,
                                      nsIFrame* aFrame)
 {
   NS_ASSERTION(aBuilder->FindReferenceFrameFor(aFrame) == aBuilder->FindReferenceFrameFor(mFrame),
                "Reference frame mismatch");
+  if (aBuilder->IsInsidePointerEventsNoneDoc()) {
+    // Somewhere up the parent document chain is a subdocument with pointer-
+    // events:none set on it (and without a mozpasspointerevents).
+    return;
+  }
   if (!aFrame->GetParent()) {
     MOZ_ASSERT(aFrame->GetType() == nsGkAtoms::viewportFrame);
     nsSubDocumentFrame* subdoc = static_cast<nsSubDocumentFrame*>(
         nsLayoutUtils::GetCrossDocParentFrame(aFrame));
     if (subdoc && subdoc->PassPointerEventsToChildren()) {
       // If this viewport frame is for a subdocument with
       // mozpasspointerevents, then we don't want to add the viewport itself
       // to the event regions. Instead we want to add only subframes.
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -335,16 +335,20 @@ public:
   void SetLayerEventRegions(nsDisplayLayerEventRegions* aItem)
   {
     mLayerEventRegions = aItem;
   }
   bool IsBuildingLayerEventRegions()
   {
     return (gfxPrefs::LayoutEventRegionsEnabled() && mMode == PAINTING);
   }
+  bool IsInsidePointerEventsNoneDoc()
+  {
+    return CurrentPresShellState()->mInsidePointerEventsNoneDoc;
+  }
 
   bool GetAncestorHasTouchEventHandler() { return mAncestorHasTouchEventHandler; }
   void SetAncestorHasTouchEventHandler(bool aValue)
   {
     mAncestorHasTouchEventHandler = aValue;
   }
   bool GetAncestorHasScrollEventHandler() { return mAncestorHasScrollEventHandler; }
   void SetAncestorHasScrollEventHandler(bool aValue)
@@ -386,18 +390,21 @@ public:
   }
   /**
    * Get the caret associated with the current presshell.
    */
   nsCaret* GetCaret();
   /**
    * Notify the display list builder that we're entering a presshell.
    * aReferenceFrame should be a frame in the new presshell.
+   * aPointerEventsNoneDoc should be set to true if the frame generating this
+   * document is pointer-events:none without mozpasspointerevents.
    */
-  void EnterPresShell(nsIFrame* aReferenceFrame);
+  void EnterPresShell(nsIFrame* aReferenceFrame,
+                      bool aPointerEventsNoneDoc = false);
   /**
    * For print-preview documents, we sometimes need to build display items for
    * the same frames multiple times in the same presentation, with different
    * clipping. Between each such batch of items, call
    * ResetMarkedFramesForDisplayList to make sure that the results of
    * MarkFramesForDisplayList do not carry over between batches.
    */
   void ResetMarkedFramesForDisplayList();
@@ -810,16 +817,20 @@ private:
                                     const nsRect& aDirtyRect);
 
   struct PresShellState {
     nsIPresShell* mPresShell;
     nsIFrame*     mCaretFrame;
     nsRect        mCaretRect;
     uint32_t      mFirstFrameMarkedForDisplay;
     bool          mIsBackgroundOnly;
+    // This is a per-document flag turning off event handling for all content
+    // in the document, and is set when we enter a subdocument for a pointer-
+    // events:none frame that doesn't have mozpasspointerevents set.
+    bool          mInsidePointerEventsNoneDoc;
   };
 
   PresShellState* CurrentPresShellState() {
     NS_ASSERTION(mPresShellStates.Length() > 0,
                  "Someone forgot to enter a presshell");
     return &mPresShellStates[mPresShellStates.Length() - 1];
   }
 
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -358,31 +358,33 @@ nsSubDocumentFrame::BuildDisplayList(nsD
       // own layer so we generate a ColorLayer. This is helpful for optimizing
       // compositing; we can skip compositing the ColorLayer when the
       // remote content is opaque.
       WrapBackgroundColorInOwnLayer(aBuilder, this, decorations.BorderBackground());
     }
     decorations.MoveTo(aLists);
   }
 
-  bool passPointerEventsToChildren = false;
-  if (aBuilder->IsForEventDelivery()) {
-    passPointerEventsToChildren = PassPointerEventsToChildren();
-    // If mozpasspointerevents is set, then we should allow subdocument content
-    // to handle events even if we're pointer-events:none.
-    if (pointerEventsNone && !passPointerEventsToChildren) {
-      return;
-    }
+  // We only care about mozpasspointerevents if we're doing hit-testing
+  // related things.
+  bool passPointerEventsToChildren =
+    (aBuilder->IsForEventDelivery() || aBuilder->IsBuildingLayerEventRegions())
+    ? PassPointerEventsToChildren() : false;
+
+  // If mozpasspointerevents is set, then we should allow subdocument content
+  // to handle events even if we're pointer-events:none.
+  if (aBuilder->IsForEventDelivery() && pointerEventsNone && !passPointerEventsToChildren) {
+    return;
   }
 
   // If we're passing pointer events to children then we have to descend into
   // subdocuments no matter what, to determine which parts are transparent for
-  // elementFromPoint.
-  if (!mInnerView ||
-      (!aBuilder->GetDescendIntoSubdocuments() && !passPointerEventsToChildren)) {
+  // hit-testing or event regions.
+  bool needToDescend = aBuilder->GetDescendIntoSubdocuments() || passPointerEventsToChildren;
+  if (!mInnerView || !needToDescend) {
     return;
   }
 
   if (rfp) {
     rfp->BuildDisplayList(aBuilder, this, aDirtyRect, aLists);
     return;
   }
 
@@ -435,17 +437,18 @@ nsSubDocumentFrame::BuildDisplayList(nsD
           // The ExpandRectToNearlyVisible that the root scroll frame would do gets short
           // circuited due to us ignoring the root scroll frame, so we do it here.
           nsIScrollableFrame* rootScrollableFrame = do_QueryFrame(rootScrollFrame);
           dirty = rootScrollableFrame->ExpandRectToNearlyVisible(dirty);
         }
       }
     }
 
-    aBuilder->EnterPresShell(subdocRootFrame);
+    aBuilder->EnterPresShell(subdocRootFrame,
+                             pointerEventsNone && !passPointerEventsToChildren);
   } else {
     dirty = aDirtyRect;
   }
 
   DisplayListClipState::AutoSaveRestore clipState(aBuilder);
   if (ShouldClipSubdocument()) {
     clipState.ClipContainingBlockDescendantsToContentBox(aBuilder, this);
   }