Bug 1126876 - Prevent subdocuments inside a pointer-events:none frame from building event regions. r=roc, r=tn, a=bajaj
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 03 Feb 2015 10:52:51 -0500
changeset 232780 34f99e78f6f43858b87f61bf260523938954cbf0
parent 232779 6e565575e031edd7e8bb18a77f1ded2586ca4add
child 232781 462eda583dbaa55a459d45940e80d28ea3755169
push id114
push userryanvm@gmail.com
push dateFri, 06 Feb 2015 21:54:33 +0000
treeherdermozilla-b2g37_v2_2@418673bfce1f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc, tn, bajaj
bugs1126876
milestone37.0a2
Bug 1126876 - Prevent subdocuments inside a pointer-events:none frame from building event regions. r=roc, r=tn, a=bajaj
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/generic/nsSubDocumentFrame.cpp
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -966,17 +966,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();
 
@@ -992,16 +993,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);
@@ -3081,16 +3088,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();
@@ -804,16 +811,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);
   }