Bug 505115 - Part 10 - Implement the backface-visibility CSS property. r=dbaron
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 03 Aug 2011 15:04:22 +1200
changeset 73739 85cc7836c552230773782842fe5be67cc70a6298
parent 73738 96941ed69aeb5d33faddea7d13d338f9ac87c49a
child 73740 ea882f18d8ac5020db9af2bca4e7d252de2dc766
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewersdbaron
bugs505115
milestone8.0a1
Bug 505115 - Part 10 - Implement the backface-visibility CSS property. r=dbaron
dom/interfaces/css/nsIDOMCSS2Properties.idl
gfx/thebes/gfx3DMatrix.cpp
gfx/thebes/gfx3DMatrix.h
layout/base/nsDisplayList.cpp
layout/base/nsStyleConsts.h
layout/style/nsCSSPropList.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
--- a/dom/interfaces/css/nsIDOMCSS2Properties.idl
+++ b/dom/interfaces/css/nsIDOMCSS2Properties.idl
@@ -46,17 +46,17 @@
  * The nsIDOMCSS2Properties interface is a datatype for additional
  * reflection of data already provided in nsIDOMCSSStyleDeclaration in
  * the Document Object Model.
  *
  * For more information on this interface please see
  * http://www.w3.org/TR/DOM-Level-2-Style
  */
 
-[scriptable, uuid(1ca298f0-4eaf-4483-8aa2-587f392f84bb)]
+[scriptable, uuid(63a0afd0-b322-11e0-aff2-0800200c9a66)]
 interface nsIDOMCSS2Properties : nsISupports
 {
            attribute DOMString        background;
                                         // raises(DOMException) on setting
 
            attribute DOMString        backgroundAttachment;
                                         // raises(DOMException) on setting
 
@@ -679,16 +679,19 @@ interface nsIDOMCSS2Properties : nsISupp
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTransformOrigin;
                                         // raises(DOMException) on setting 
 
            attribute DOMString        MozPerspective;
                                         // raises(DOMException) on setting
 
+           attribute DOMString        MozBackfaceVisibility;
+                                        // raises(DOMException) on setting 
+
            attribute DOMString        MozWindowShadow;
                                         // raises(DOMException) on setting
 
            attribute DOMString        backgroundSize;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozTextBlink;
                                         // raises(DOMException) on setting
--- a/gfx/thebes/gfx3DMatrix.cpp
+++ b/gfx/thebes/gfx3DMatrix.cpp
@@ -387,8 +387,22 @@ gfxRect gfx3DMatrix::ProjectRectBounds(c
     max_x = max(points[i].x, max_x);
     min_y = min(points[i].y, min_y);
     max_y = max(points[i].y, max_y);
   }
 
   return gfxRect(min_x, min_y, max_x - min_x, max_y - min_y);
 }
 
+gfxPoint3D gfx3DMatrix::GetNormalVector() const
+{
+    // Define a plane in transformed space as the transformations
+    // of 3 points on the z=0 screen plane.
+    gfxPoint3D a = Transform3D(gfxPoint3D(0, 0, 0));
+    gfxPoint3D b = Transform3D(gfxPoint3D(0, 1, 0));
+    gfxPoint3D c = Transform3D(gfxPoint3D(1, 0, 0));
+
+    // Convert to two vectors on the surface of the plane.
+    gfxPoint3D ab = b - a;
+    gfxPoint3D ac = c - a;
+
+    return ac.CrossProduct(ab);
+}
--- a/gfx/thebes/gfx3DMatrix.h
+++ b/gfx/thebes/gfx3DMatrix.h
@@ -130,16 +130,22 @@ public:
   inline gfx3DMatrix Inverse() const
   {
     gfx3DMatrix temp = *this;
     temp.Invert();
     return temp;
   }
 
   /**
+   * Returns a unit vector that is perpendicular to the plane formed
+   * by transform the screen plane (z=0) by this matrix.
+   */
+  gfxPoint3D GetNormalVector() const;
+
+  /**
    * Check if matrix is singular (no inverse exists).
    */
   PRBool IsSingular() const;
 
   /**
    * Create a translation matrix.
    *
    * \param aX Translation on X-axis.
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -2413,18 +2413,22 @@ nsDisplayTransform::GetTransform(float a
 }
 
 already_AddRefed<Layer> nsDisplayTransform::BuildLayer(nsDisplayListBuilder *aBuilder,
                                                        LayerManager *aManager,
                                                        const ContainerParameters& aContainerParameters)
 {
   const gfx3DMatrix& newTransformMatrix = 
     GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel());
-  if (newTransformMatrix.IsSingular())
+
+  if (newTransformMatrix.IsSingular() ||
+      (mFrame->GetStyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
+       newTransformMatrix.GetNormalVector().z <= 0.0)) {
     return nsnull;
+  }
 
   return aBuilder->LayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetList(),
                            aContainerParameters, &newTransformMatrix);
 }
 
 nsDisplayItem::LayerState
 nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder,
@@ -2482,18 +2486,22 @@ void nsDisplayTransform::HitTest(nsDispl
    *    anything).
    * 2. Invert the matrix.
    * 3. Use it to transform the rect into the correct space.
    * 4. Pass that rect down through to the list's version of HitTest.
    */
   float factor = nsPresContext::AppUnitsPerCSSPixel();
   gfx3DMatrix matrix = GetTransform(factor);
 
-  if (matrix.IsSingular())
-    return;
+  /* If the matrix is singular, or a hidden backface is shown, we didn't hit anything. */
+  if (matrix.IsSingular() ||
+      (mFrame->GetStyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
+       matrix.GetNormalVector().z <= 0.0)) {
+          return;
+  }
 
   /* We want to go from transformed-space to regular space.
    * Thus we have to invert the matrix, which normally does
    * the reverse operation (e.g. regular->transformed)
    */
 
   /* Now, apply the transform and pass it down the channel. */
   nsRect resultingRect;
--- a/layout/base/nsStyleConsts.h
+++ b/layout/base/nsStyleConsts.h
@@ -853,16 +853,20 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_TEXT_RENDERING_OPTIMIZELEGIBILITY 2
 #define NS_STYLE_TEXT_RENDERING_GEOMETRICPRECISION 3
 
 // color-interpolation and color-interpolation-filters
 #define NS_STYLE_COLOR_INTERPOLATION_AUTO           0
 #define NS_STYLE_COLOR_INTERPOLATION_SRGB           1
 #define NS_STYLE_COLOR_INTERPOLATION_LINEARRGB      2
 
+// 3d Transforms - Backface visibility
+#define NS_STYLE_BACKFACE_VISIBILITY_VISIBLE        1
+#define NS_STYLE_BACKFACE_VISIBILITY_HIDDEN         0
+
 /*****************************************************************************
  * Constants for media features.                                             *
  *****************************************************************************/
 
 // orientation
 #define NS_STYLE_ORIENTATION_PORTRAIT           0
 #define NS_STYLE_ORIENTATION_LANDSCAPE          1
 
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2262,16 +2262,25 @@ CSS_PROP_DISPLAY(
     -moz-perspective,
     perspective,
     CSS_PROP_DOMPROP_PREFIXED(Perspective),
     CSS_PROPERTY_PARSE_VALUE,
     VARIANT_NONE | VARIANT_INHERIT | VARIANT_LENGTH,
     nsnull,
     offsetof(nsStyleDisplay, mChildPerspective),
     eStyleAnimType_Coord)
+CSS_PROP_DISPLAY(
+    -moz-backface-visibility,
+    backface_visibility,
+    CSS_PROP_DOMPROP_PREFIXED(BackfaceVisibility),
+    CSS_PROPERTY_PARSE_VALUE,
+    VARIANT_HK,
+    kBackfaceVisibilityKTable,
+    offsetof(nsStyleDisplay, mBackfaceVisibility),
+    eStyleAnimType_None)
 CSS_PROP_POSITION(
     top,
     top,
     Top,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_STORES_CALC,
     VARIANT_AHLP | VARIANT_CALC,
     nsnull,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -576,16 +576,21 @@ const PRInt32 nsCSSProps::kAppearanceKTa
   eCSSKeyword__moz_window_button_maximize,    NS_THEME_WINDOW_BUTTON_MAXIMIZE,
   eCSSKeyword__moz_window_button_restore,     NS_THEME_WINDOW_BUTTON_RESTORE,
   eCSSKeyword__moz_window_button_box,         NS_THEME_WINDOW_BUTTON_BOX,
   eCSSKeyword__moz_window_button_box_maximized, NS_THEME_WINDOW_BUTTON_BOX_MAXIMIZED,
   eCSSKeyword__moz_win_exclude_glass,         NS_THEME_WIN_EXCLUDE_GLASS,
   eCSSKeyword_UNKNOWN,-1
 };
 
+const PRInt32 nsCSSProps::kBackfaceVisibilityKTable[] = {
+  eCSSKeyword_visible, NS_STYLE_BACKFACE_VISIBILITY_VISIBLE,
+  eCSSKeyword_hidden, NS_STYLE_BACKFACE_VISIBILITY_HIDDEN
+};
+
 const PRInt32 nsCSSProps::kBackgroundAttachmentKTable[] = {
   eCSSKeyword_fixed, NS_STYLE_BG_ATTACHMENT_FIXED,
   eCSSKeyword_scroll, NS_STYLE_BG_ATTACHMENT_SCROLL,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const PRInt32 nsCSSProps::kBackgroundInlinePolicyKTable[] = {
   eCSSKeyword_each_box,     NS_STYLE_BG_INLINE_POLICY_EACH_BOX,
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -328,16 +328,17 @@ public:
   // Keyword/Enum value tables
   static const PRInt32 kAnimationDirectionKTable[];
   static const PRInt32 kAnimationFillModeKTable[];
   static const PRInt32 kAnimationIterationCountKTable[];
   static const PRInt32 kAnimationPlayStateKTable[];
   static const PRInt32 kAnimationTimingFunctionKTable[];
   static const PRInt32 kAppearanceKTable[];
   static const PRInt32 kAzimuthKTable[];
+  static const PRInt32 kBackfaceVisibilityKTable[];
   static const PRInt32 kBackgroundAttachmentKTable[];
   static const PRInt32 kBackgroundInlinePolicyKTable[];
   static const PRInt32 kBackgroundOriginKTable[];
   static const PRInt32 kBackgroundPositionKTable[];
   static const PRInt32 kBackgroundRepeatKTable[];
   static const PRInt32 kBackgroundSizeKTable[];
   static const PRInt32 kBorderCollapseKTable[];
   static const PRInt32 kBorderColorKTable[];
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -949,16 +949,26 @@ nsComputedDOMStyle::DoGetMozPerspective(
         GetStyleDisplay()->mChildPerspective.GetCoordValue() == 0.0) {
         val->SetIdent(eCSSKeyword_none);
     } else {
         SetValueToCoord(val, GetStyleDisplay()->mChildPerspective, PR_FALSE);
     }
     return val;
 }
 
+nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetMozBackfaceVisibility()
+{
+    nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
+    val->SetIdent(
+        nsCSSProps::ValueToKeywordEnum(GetStyleDisplay()->mBackfaceVisibility,
+                                       nsCSSProps::kBackfaceVisibilityKTable));
+    return val;
+}
+
 /* If the property is "none", hand back "none" wrapped in a value.
  * Otherwise, compute the aggregate transform matrix and hands it back in a
  * "matrix" wrapper.
  */
 nsIDOMCSSValue*
 nsComputedDOMStyle::DoGetMozTransform()
 {
   /* First, get the display data.  We'll need it. */
@@ -4373,16 +4383,17 @@ nsComputedDOMStyle::GetQueryableProperty
     COMPUTED_STYLE_MAP_ENTRY(animation_direction,           AnimationDirection),
     COMPUTED_STYLE_MAP_ENTRY(animation_duration,            AnimationDuration),
     COMPUTED_STYLE_MAP_ENTRY(animation_fill_mode,           AnimationFillMode),
     COMPUTED_STYLE_MAP_ENTRY(animation_iteration_count,     AnimationIterationCount),
     COMPUTED_STYLE_MAP_ENTRY(animation_name,                AnimationName),
     COMPUTED_STYLE_MAP_ENTRY(animation_play_state,          AnimationPlayState),
     COMPUTED_STYLE_MAP_ENTRY(animation_timing_function,     AnimationTimingFunction),
     COMPUTED_STYLE_MAP_ENTRY(appearance,                    Appearance),
+    COMPUTED_STYLE_MAP_ENTRY(backface_visibility,           MozBackfaceVisibility),
     COMPUTED_STYLE_MAP_ENTRY(_moz_background_inline_policy, BackgroundInlinePolicy),
     COMPUTED_STYLE_MAP_ENTRY(binding,                       Binding),
     COMPUTED_STYLE_MAP_ENTRY(border_bottom_colors,          BorderBottomColors),
     COMPUTED_STYLE_MAP_ENTRY(border_image,                  BorderImage),
     COMPUTED_STYLE_MAP_ENTRY(border_left_colors,            BorderLeftColors),
     COMPUTED_STYLE_MAP_ENTRY(border_right_colors,           BorderRightColors),
     COMPUTED_STYLE_MAP_ENTRY(border_top_colors,             BorderTopColors),
     COMPUTED_STYLE_MAP_ENTRY(box_align,                     BoxAlign),
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -343,16 +343,17 @@ private:
   nsIDOMCSSValue* DoGetOverflowX();
   nsIDOMCSSValue* DoGetOverflowY();
   nsIDOMCSSValue* DoGetResize();
   nsIDOMCSSValue* DoGetPageBreakAfter();
   nsIDOMCSSValue* DoGetPageBreakBefore();
   nsIDOMCSSValue* DoGetMozTransform();
   nsIDOMCSSValue* DoGetMozTransformOrigin();
   nsIDOMCSSValue* DoGetMozPerspective();
+  nsIDOMCSSValue* DoGetMozBackfaceVisibility();
   nsIDOMCSSValue* DoGetOrient();
 
   /* User interface properties */
   nsIDOMCSSValue* DoGetCursor();
   nsIDOMCSSValue* DoGetForceBrokenImageIcon();
   nsIDOMCSSValue* DoGetIMEMode();
   nsIDOMCSSValue* DoGetUserFocus();
   nsIDOMCSSValue* DoGetUserInput();
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -4484,16 +4484,21 @@ nsRuleNode::ComputeDisplayData(void* aSt
     NS_ASSERTION(result, "Malformed -moz-transform-origin parse!");
   }
 
   SetCoord(*aRuleData->ValueForPerspective(), 
            display->mChildPerspective, parentDisplay->mChildPerspective,
            SETCOORD_LAH | SETCOORD_INITIAL_ZERO | SETCOORD_NONE,
            aContext, mPresContext, canStoreInRuleTree);
 
+  SetDiscrete(*aRuleData->ValueForBackfaceVisibility(),
+              display->mBackfaceVisibility, canStoreInRuleTree,
+              SETDSC_ENUMERATED, parentDisplay->mBackfaceVisibility,
+              NS_STYLE_BACKFACE_VISIBILITY_VISIBLE, 0, 0, 0, 0);
+
   // orient: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForOrient(),
               display->mOrient, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentDisplay->mOrient,
               NS_STYLE_ORIENT_HORIZONTAL, 0, 0, 0, 0);
 
   COMPUTE_END_RESET(Display, display)
 }
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2029,16 +2029,17 @@ nsStyleDisplay::nsStyleDisplay()
   mResize = NS_STYLE_RESIZE_NONE;
   mClipFlags = NS_STYLE_CLIP_AUTO;
   mClip.SetRect(0,0,0,0);
   mOpacity = 1.0f;
   mSpecifiedTransform = nsnull;
   mTransformOrigin[0].SetPercentValue(0.5f); // Transform is centered on origin
   mTransformOrigin[1].SetPercentValue(0.5f); 
   mChildPerspective.SetCoordValue(0);
+  mBackfaceVisibility = NS_STYLE_BACKFACE_VISIBILITY_VISIBLE;
   mOrient = NS_STYLE_ORIENT_HORIZONTAL;
 
   mTransitions.AppendElement();
   NS_ABORT_IF_FALSE(mTransitions.Length() == 1,
                     "appending within auto buffer should never fail");
   mTransitions[0].SetInitialValues();
   mTransitionTimingFunctionCount = 1;
   mTransitionDurationCount = 1;
@@ -2095,16 +2096,17 @@ nsStyleDisplay::nsStyleDisplay(const nsS
 
   /* Copy over the transformation information. */
   mSpecifiedTransform = aSource.mSpecifiedTransform;
   
   /* Copy over transform origin. */
   mTransformOrigin[0] = aSource.mTransformOrigin[0];
   mTransformOrigin[1] = aSource.mTransformOrigin[1];
   mChildPerspective = aSource.mChildPerspective;
+  mBackfaceVisibility = aSource.mBackfaceVisibility;
 }
 
 nsChangeHint nsStyleDisplay::CalcDifference(const nsStyleDisplay& aOther) const
 {
   nsChangeHint hint = nsChangeHint(0);
 
   if (!EqualURIs(mBinding, aOther.mBinding)
       || mPosition != aOther.mPosition
@@ -2160,16 +2162,19 @@ nsChangeHint nsStyleDisplay::CalcDiffere
         NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
                                            nsChangeHint_RepaintFrame));
         break;
       }
     
     if (mChildPerspective != aOther.mChildPerspective)
       NS_UpdateHint(hint, NS_CombineHint(nsChangeHint_ReflowFrame,
                                          nsChangeHint_RepaintFrame));
+
+    if (mBackfaceVisibility != aOther.mBackfaceVisibility)
+      NS_UpdateHint(hint, nsChangeHint_RepaintFrame);
   }
 
   // Note:  Our current behavior for handling changes to the
   // transition-duration, transition-delay, and transition-timing-function
   // properties is to do nothing.  In other words, the transition
   // property that matters is what it is when the transition begins, and
   // we don't stop a transition later because the transition property
   // changed.
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1511,16 +1511,17 @@ struct nsStyleDisplay {
 
   // mSpecifiedTransform is the list of transform functions as
   // specified, or null to indicate there is no transform.  (inherit or
   // initial are replaced by an actual list of transform functions, or
   // null, as appropriate.) (owned by the style rule)
   const nsCSSValueList *mSpecifiedTransform; // [reset]
   nsStyleCoord mTransformOrigin[2]; // [reset] percent, coord, calc
   nsStyleCoord mChildPerspective; // [reset] coord
+  PRUint8 mBackfaceVisibility;
 
   nsAutoTArray<nsTransition, 1> mTransitions; // [reset]
   // The number of elements in mTransitions that are not from repeating
   // a list due to another property being longer.
   PRUint32 mTransitionTimingFunctionCount,
            mTransitionDurationCount,
            mTransitionDelayCount,
            mTransitionPropertyCount;
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -970,16 +970,24 @@ var gCSSProperties = {
 	"-moz-perspective": {
 		domProp: "MozPerspective",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "none", "0" ],
 		other_values: [ "1000px", "500.2px", "-100px", "-27.2em" ],
 		invalid_values: [ "pants", "200" ]
 	},
+    "-moz-backface-visibility": {
+        domProp: "MozBackfaceVisibility",
+        inherited: false,
+        type: CSS_TYPE_LONGHAND,
+        initial_values: [ "visible" ],
+        other_values: [ "hidden" ],
+        invalid_values: [ "collapse" ]
+    },
 	"-moz-user-focus": {
 		domProp: "MozUserFocus",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "none" ],
 		other_values: [ "normal", "ignore", "select-all", "select-before", "select-after", "select-same", "select-menu" ],
 		invalid_values: []
 	},