Bug 1462412 - Correctly ignore the perspective property for frames that aren't transformable. r=dbaron, a=RyanVM
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 23 May 2018 13:28:17 +1200
changeset 470903 27790f51b104ec2718a6a83b89fb6f6255dc6564
parent 470902 100647b2c9dc36fc3f7de101c9113c3487cf7ef9
child 470904 0f95c023462e4de617ebdd77cbe90ec523efad10
push id9260
push userryanvm@gmail.com
push dateThu, 24 May 2018 18:12:34 +0000
treeherdermozilla-beta@0f95c023462e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron, RyanVM
bugs1462412
milestone61.0
Bug 1462412 - Correctly ignore the perspective property for frames that aren't transformable. r=dbaron, a=RyanVM
layout/base/crashtests/1462412.html
layout/base/crashtests/crashtests.list
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsIFrame.h
layout/reftests/w3c-css/submitted/transforms/perspective-untransformable-no-stacking-context-ref.html
layout/reftests/w3c-css/submitted/transforms/perspective-untransformable-no-stacking-context.html
layout/reftests/w3c-css/submitted/transforms/reftest.list
layout/style/nsStyleStruct.h
layout/style/nsStyleStructInlines.h
new file mode 100644
--- /dev/null
+++ b/layout/base/crashtests/1462412.html
@@ -0,0 +1,9 @@
+<style>
+* {
+-webkit-perspective: 1px;
+will-change: transform;
+</style>
+<span>
+<p>*</p>
+<keygen>
+<!-- a -->
\ No newline at end of file
--- a/layout/base/crashtests/crashtests.list
+++ b/layout/base/crashtests/crashtests.list
@@ -526,8 +526,9 @@ load 1442506.html
 load 1437155.html
 load 1443027-1.html
 load 1448841-1.html
 load 1452839.html
 load 1453702.html
 load 1453342.html
 load 1453196.html
 load 1461812.html
+load 1462412.html
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -1539,16 +1539,23 @@ nsIFrame::HasAnimationOfTransform() cons
 {
 
   return IsPrimaryFrame() &&
     nsLayoutUtils::HasAnimationOfProperty(this, eCSSProperty_transform) &&
     IsFrameOfType(eSupportsCSSTransforms);
 }
 
 bool
+nsIFrame::ChildrenHavePerspective(const nsStyleDisplay* aStyleDisplay) const
+{
+  MOZ_ASSERT(aStyleDisplay == StyleDisplay());
+  return aStyleDisplay->HasPerspective(this);
+}
+
+bool
 nsIFrame::HasOpacityInternal(float aThreshold,
                              EffectSet* aEffectSet) const
 {
   MOZ_ASSERT(0.0 <= aThreshold && aThreshold <= 1.0, "Invalid argument");
   if (StyleEffects()->mOpacity < aThreshold ||
       (StyleDisplay()->mWillChangeBitField & NS_STYLE_WILL_CHANGE_OPACITY)) {
     return true;
   }
@@ -10989,17 +10996,17 @@ nsIFrame::IsSelected() const
 bool
 nsIFrame::IsVisuallyAtomic(EffectSet* aEffectSet,
                            const nsStyleDisplay* aStyleDisplay,
                            const nsStyleEffects* aStyleEffects) {
   return HasOpacity(aEffectSet) ||
          IsTransformed(aStyleDisplay) ||
          // strictly speaking, 'perspective' doesn't require visual atomicity,
          // but the spec says it acts like the rest of these
-         aStyleDisplay->mChildPerspective.GetUnit() == eStyleUnit_Coord ||
+         ChildrenHavePerspective(aStyleDisplay) ||
          aStyleEffects->mMixBlendMode != NS_STYLE_BLEND_NORMAL ||
          nsSVGIntegrationUtils::UsingEffectsForFrame(this);
 }
 
 bool
 nsIFrame::IsStackingContext(const nsStyleDisplay* aStyleDisplay,
                             const nsStylePosition* aStylePosition,
                             bool aIsPositioned,
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2212,23 +2212,16 @@ ScrollFrameHelper::HasPluginFrames()
       return true;
     }
   }
 #endif
   return false;
 }
 
 bool
-ScrollFrameHelper::HasPerspective() const
-{
-  const nsStyleDisplay* disp = mOuter->StyleDisplay();
-  return disp->mChildPerspective.GetUnit() != eStyleUnit_None;
-}
-
-bool
 ScrollFrameHelper::HasBgAttachmentLocal() const
 {
   const nsStyleBackground* bg = mOuter->StyleBackground();
   return bg->HasLocalBackground();
 }
 
 void
 ScrollFrameHelper::ScrollToCSSPixels(const CSSIntPoint& aScrollPosition,
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -676,17 +676,19 @@ protected:
                           nsAtom *aOrigin, // nullptr indicates "other" origin
                           const nsRect* aRange,
                           nsIScrollbarMediator::ScrollSnapMode aSnap
                             = nsIScrollbarMediator::DISABLE_SNAP);
 
   void CompleteAsyncScroll(const nsRect &aRange, nsAtom* aOrigin = nullptr);
 
   bool HasPluginFrames();
-  bool HasPerspective() const;
+  bool HasPerspective() const {
+    return mOuter->ChildrenHavePerspective();
+  }
   bool HasBgAttachmentLocal() const;
   uint8_t GetScrolledFrameDir() const;
 
   static void EnsureFrameVisPrefsCached();
   static bool sFrameVisPrefsCached;
   // The number of scrollports wide/high to expand when tracking frame visibility.
   static uint32_t sHorzExpandScrollPort;
   static uint32_t sVertExpandScrollPort;
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -1848,20 +1848,17 @@ public:
     return IsPreserve3DLeaf(StyleDisplay(), aEffectSet);
   }
 
   bool HasPerspective(const nsStyleDisplay* aStyleDisplay) const;
   bool HasPerspective() const {
     return HasPerspective(StyleDisplay());
   }
 
-  bool ChildrenHavePerspective(const nsStyleDisplay* aStyleDisplay) const {
-    MOZ_ASSERT(aStyleDisplay == StyleDisplay());
-    return aStyleDisplay->HasPerspectiveStyle();
-  }
+  bool ChildrenHavePerspective(const nsStyleDisplay* aStyleDisplay) const;
   bool ChildrenHavePerspective() const {
     return ChildrenHavePerspective(StyleDisplay());
   }
 
   /**
    * Includes the overflow area of all descendants that participate in the current
    * 3d context into aOverflowAreas.
    */
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/transforms/perspective-untransformable-no-stacking-context-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS transforms: 'perspective' on a non-transformable element doesn't create a stacking context</title>
+<link rel="author" title="Matt Woodrow" href="mailto:mwoodrow@mozilla.com">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<style>
+  html, body { margin: 0; padding: 0 }
+  #fixedmoves {
+    position: absolute;
+    background: green;
+    height: 100px;
+    width: 100px;
+  }
+</style>
+<body>
+  <div id="fixedmoves"></div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/transforms/perspective-untransformable-no-stacking-context.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS transforms: 'perspective' on a non-transformable element doesn't create a stacking context</title>
+<link rel="author" title="Matt Woodrow" href="mailto:mwoodrow@mozilla.com">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#perspective-property">
+<link rel="match" href="perspective-untransformable-no-stacking-context-ref.html">
+<meta name="assert" content="Perspective on a non-transformable element shouldn't create a stacking context.">
+<style>
+* { margin: 0; padding: 0; }
+div, span { width: 100px; height: 100px }
+#perspective { background: green; padding-top: 100px; perspective: 100px; }
+#child { display:inline-block; z-index: -1; position:absolute; background: red; }
+#spacer { display:inline-block; }
+#wrapper { overflow:hidden }
+</style>
+<body>
+  <div id="wrapper">
+    <span id="perspective">
+      <div id="child">
+      </div>
+      <span id="spacer"></span>
+    </span>
+  </div>
+</body>
--- a/layout/reftests/w3c-css/submitted/transforms/reftest.list
+++ b/layout/reftests/w3c-css/submitted/transforms/reftest.list
@@ -1,14 +1,15 @@
 == transform-containing-block-dynamic-1a.html containing-block-dynamic-1-ref.html
 == transform-containing-block-dynamic-1b.html containing-block-dynamic-1-ref.html
 == perspective-containing-block-dynamic-1a.html containing-block-dynamic-1-ref.html
 == perspective-containing-block-dynamic-1b.html containing-block-dynamic-1-ref.html
 == perspective-zero.html reference/green.html
 == perspective-zero-2.html perspective-zero-2-ref.html
+== perspective-untransformable-no-stacking-context.html perspective-untransformable-no-stacking-context-ref.html
 
 default-preferences pref(layout.css.individual-transform.enabled,true)
 == individual-transform-1.html individual-transform-1-ref.html
 == individual-transform-2a.html individual-transform-2-ref.html
 == individual-transform-2b.html individual-transform-2-ref.html
 == individual-transform-2c.html individual-transform-2-ref.html
 == individual-transform-2d.html individual-transform-2-ref.html
 == individual-transform-2e.html individual-transform-2-ref.html
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2520,16 +2520,23 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsSt
   /**
    * Returns true when the element has the transform property
    * or a related property, and supports CSS transforms.
    * aContextFrame is the frame for which this is the nsStyleDisplay.
    */
   inline bool HasTransform(const nsIFrame* aContextFrame) const;
 
   /**
+   * Returns true when the element has the perspective property,
+   * and supports CSS transforms. aContextFrame is the frame for
+   * which this is the nsStyleDisplay.
+   */
+  inline bool HasPerspective(const nsIFrame* aContextFrame) const;
+
+  /**
    * Returns true when the element is a containing block for its fixed-pos
    * descendants.
    * aContextFrame is the frame for which this is the nsStyleDisplay.
    */
   inline bool IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const;
 
   /**
    * The same as IsFixedPosContainingBlock, except skipping the tests that
--- a/layout/style/nsStyleStructInlines.h
+++ b/layout/style/nsStyleStructInlines.h
@@ -141,27 +141,34 @@ nsStyleDisplay::IsFloating(const nsIFram
 // this function in comments.
 bool
 nsStyleDisplay::HasTransform(const nsIFrame* aContextFrame) const
 {
   NS_ASSERTION(aContextFrame->StyleDisplay() == this, "unexpected aContextFrame");
   return HasTransformStyle() && aContextFrame->IsFrameOfType(nsIFrame::eSupportsCSSTransforms);
 }
 
+bool
+nsStyleDisplay::HasPerspective(const nsIFrame* aContextFrame) const
+{
+  MOZ_ASSERT(aContextFrame->StyleDisplay() == this, "unexpected aContextFrame");
+  return HasPerspectiveStyle() && aContextFrame->IsFrameOfType(nsIFrame::eSupportsCSSTransforms);
+}
+
 template<class ComputedStyleLike>
 bool
 nsStyleDisplay::HasFixedPosContainingBlockStyleInternal(
                   ComputedStyleLike* aComputedStyle) const
 {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the FIXPOS_CB flag set on them.
   NS_ASSERTION(aComputedStyle->ThreadsafeStyleDisplay() == this,
                "unexpected aComputedStyle");
 
-  if (IsContainPaint() || HasPerspectiveStyle()) {
+  if (IsContainPaint()) {
     return true;
   }
 
   if (mWillChangeBitField & NS_STYLE_WILL_CHANGE_FIXPOS_CB) {
     return true;
   }
 
   return aComputedStyle->ThreadsafeStyleEffects()->HasFilters();
@@ -170,26 +177,26 @@ nsStyleDisplay::HasFixedPosContainingBlo
 template<class ComputedStyleLike>
 bool
 nsStyleDisplay::IsFixedPosContainingBlockForAppropriateFrame(
                   ComputedStyleLike* aComputedStyle) const
 {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the FIXPOS_CB flag set on them.
   return HasFixedPosContainingBlockStyleInternal(aComputedStyle) ||
-         HasTransformStyle();
+         HasTransformStyle() || HasPerspectiveStyle();
 }
 
 bool
 nsStyleDisplay::IsFixedPosContainingBlock(const nsIFrame* aContextFrame) const
 {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the FIXPOS_CB flag set on them.
   if (!HasFixedPosContainingBlockStyleInternal(aContextFrame->Style()) &&
-      !HasTransform(aContextFrame)) {
+      !HasTransform(aContextFrame) && !HasPerspective(aContextFrame)) {
     return false;
   }
   return !nsSVGUtils::IsInSVGTextSubtree(aContextFrame);
 }
 
 template<class ComputedStyleLike>
 bool
 nsStyleDisplay::HasAbsPosContainingBlockStyleInternal(
@@ -217,17 +224,18 @@ nsStyleDisplay::IsAbsPosContainingBlockF
 bool
 nsStyleDisplay::IsAbsPosContainingBlock(const nsIFrame* aContextFrame) const
 {
   // NOTE: Any CSS properties that influence the output of this function
   // should have the ABSPOS_CB set on them.
   mozilla::ComputedStyle* sc = aContextFrame->Style();
   if (!HasAbsPosContainingBlockStyleInternal(sc) &&
       !HasFixedPosContainingBlockStyleInternal(sc) &&
-      !HasTransform(aContextFrame)) {
+      !HasTransform(aContextFrame) &&
+      !HasPerspective(aContextFrame)) {
     return false;
   }
   return !nsSVGUtils::IsInSVGTextSubtree(aContextFrame);
 }
 
 bool
 nsStyleDisplay::IsRelativelyPositioned(const nsIFrame* aContextFrame) const
 {