LCOV - code coverage report
Current view: top level - layout/style - nsStyleTransformMatrix.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 73 532 13.7 %
Date: 2017-07-14 16:53:18 Functions: 9 57 15.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* This Source Code Form is subject to the terms of the Mozilla Public
       3             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       4             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       5             : 
       6             : /*
       7             :  * A class used for intermediate representations of the -moz-transform property.
       8             :  */
       9             : 
      10             : #include "nsStyleTransformMatrix.h"
      11             : #include "nsCSSValue.h"
      12             : #include "nsLayoutUtils.h"
      13             : #include "nsPresContext.h"
      14             : #include "nsRuleNode.h"
      15             : #include "nsSVGUtils.h"
      16             : #include "nsCSSKeywords.h"
      17             : #include "mozilla/ServoBindings.h"
      18             : #include "mozilla/StyleAnimationValue.h"
      19             : #include "gfxMatrix.h"
      20             : #include "gfxQuaternion.h"
      21             : 
      22             : using namespace mozilla;
      23             : using namespace mozilla::gfx;
      24             : 
      25             : namespace nsStyleTransformMatrix {
      26             : 
      27             : /* Note on floating point precision: The transform matrix is an array
      28             :  * of single precision 'float's, and so are most of the input values
      29             :  * we get from the style system, but intermediate calculations
      30             :  * involving angles need to be done in 'double'.
      31             :  */
      32             : 
      33             : 
      34             : // Define UNIFIED_CONTINUATIONS here and in nsDisplayList.cpp
      35             : // to have the transform property try
      36             : // to transform content with continuations as one unified block instead of
      37             : // several smaller ones.  This is currently disabled because it doesn't work
      38             : // correctly, since when the frames are initially being reflowed, their
      39             : // continuations all compute their bounding rects independently of each other
      40             : // and consequently get the wrong value.
      41             : //#define UNIFIED_CONTINUATIONS
      42             : 
      43             : void
      44         592 : TransformReferenceBox::EnsureDimensionsAreCached()
      45             : {
      46         592 :   if (mIsCached) {
      47         940 :     return;
      48             :   }
      49             : 
      50         194 :   MOZ_ASSERT(mFrame);
      51             : 
      52         194 :   mIsCached = true;
      53             : 
      54         194 :   if (mFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT) {
      55         144 :     if (!nsLayoutUtils::SVGTransformBoxEnabled()) {
      56           0 :       mX = -mFrame->GetPosition().x;
      57           0 :       mY = -mFrame->GetPosition().y;
      58           0 :       Size contextSize = nsSVGUtils::GetContextSize(mFrame);
      59           0 :       mWidth = nsPresContext::CSSPixelsToAppUnits(contextSize.width);
      60           0 :       mHeight = nsPresContext::CSSPixelsToAppUnits(contextSize.height);
      61             :     } else
      62         144 :     if (mFrame->StyleDisplay()->mTransformBox == StyleGeometryBox::FillBox) {
      63             :       // Percentages in transforms resolve against the SVG bbox, and the
      64             :       // transform is relative to the top-left of the SVG bbox.
      65             :       nsRect bboxInAppUnits =
      66           0 :         nsLayoutUtils::ComputeGeometryBox(const_cast<nsIFrame*>(mFrame),
      67           0 :                                           StyleGeometryBox::FillBox);
      68             :       // The mRect of an SVG nsIFrame is its user space bounds *including*
      69             :       // stroke and markers, whereas bboxInAppUnits is its user space bounds
      70             :       // including fill only.  We need to note the offset of the reference box
      71             :       // from the frame's mRect in mX/mY.
      72           0 :       mX = bboxInAppUnits.x - mFrame->GetPosition().x;
      73           0 :       mY = bboxInAppUnits.y - mFrame->GetPosition().y;
      74           0 :       mWidth = bboxInAppUnits.width;
      75           0 :       mHeight = bboxInAppUnits.height;
      76             :     } else {
      77             :       // The value 'border-box' is treated as 'view-box' for SVG content.
      78         144 :       MOZ_ASSERT(mFrame->StyleDisplay()->mTransformBox ==
      79             :                    StyleGeometryBox::ViewBox ||
      80             :                  mFrame->StyleDisplay()->mTransformBox ==
      81             :                    StyleGeometryBox::BorderBox,
      82             :                  "Unexpected value for 'transform-box'");
      83             :       // Percentages in transforms resolve against the width/height of the
      84             :       // nearest viewport (or its viewBox if one is applied), and the
      85             :       // transform is relative to {0,0} in current user space.
      86         144 :       mX = -mFrame->GetPosition().x;
      87         144 :       mY = -mFrame->GetPosition().y;
      88         144 :       Size contextSize = nsSVGUtils::GetContextSize(mFrame);
      89         144 :       mWidth = nsPresContext::CSSPixelsToAppUnits(contextSize.width);
      90         144 :       mHeight = nsPresContext::CSSPixelsToAppUnits(contextSize.height);
      91             :     }
      92         144 :     return;
      93             :   }
      94             : 
      95             :   // If UNIFIED_CONTINUATIONS is not defined, this is simply the frame's
      96             :   // bounding rectangle, translated to the origin.  Otherwise, it is the
      97             :   // smallest rectangle containing a frame and all of its continuations.  For
      98             :   // example, if there is a <span> element with several continuations split
      99             :   // over several lines, this function will return the rectangle containing all
     100             :   // of those continuations.
     101             : 
     102         100 :   nsRect rect;
     103             : 
     104             : #ifndef UNIFIED_CONTINUATIONS
     105          50 :   rect = mFrame->GetRect();
     106             : #else
     107             :   // Iterate the continuation list, unioning together the bounding rects:
     108             :   for (const nsIFrame *currFrame = mFrame->FirstContinuation();
     109             :        currFrame != nullptr;
     110             :        currFrame = currFrame->GetNextContinuation())
     111             :   {
     112             :     // Get the frame rect in local coordinates, then translate back to the
     113             :     // original coordinates:
     114             :     rect.UnionRect(result, nsRect(currFrame->GetOffsetTo(mFrame),
     115             :                                   currFrame->GetSize()));
     116             :   }
     117             : #endif
     118             : 
     119          50 :   mX = 0;
     120          50 :   mY = 0;
     121          50 :   mWidth = rect.Width();
     122          50 :   mHeight = rect.Height();
     123             : }
     124             : 
     125             : void
     126           0 : TransformReferenceBox::Init(const nsSize& aDimensions)
     127             : {
     128           0 :   MOZ_ASSERT(!mFrame && !mIsCached);
     129             : 
     130           0 :   mX = 0;
     131           0 :   mY = 0;
     132           0 :   mWidth = aDimensions.width;
     133           0 :   mHeight = aDimensions.height;
     134           0 :   mIsCached = true;
     135           0 : }
     136             : 
     137             : float
     138          36 : ProcessTranslatePart(const nsCSSValue& aValue,
     139             :                      nsStyleContext* aContext,
     140             :                      nsPresContext* aPresContext,
     141             :                      RuleNodeCacheConditions& aConditions,
     142             :                      TransformReferenceBox* aRefBox,
     143             :                      TransformReferenceBox::DimensionGetter aDimensionGetter)
     144             : {
     145          36 :   nscoord offset = 0;
     146          36 :   float percent = 0.0f;
     147             : 
     148          36 :   if (aValue.GetUnit() == eCSSUnit_Percent) {
     149           0 :     percent = aValue.GetPercentValue();
     150          36 :   } else if (aValue.GetUnit() == eCSSUnit_Pixel ||
     151           0 :              aValue.GetUnit() == eCSSUnit_Number) {
     152             :     // Handle this here (even though nsRuleNode::CalcLength handles it
     153             :     // fine) so that callers are allowed to pass a null style context
     154             :     // and pres context to SetToTransformFunction if they know (as
     155             :     // StyleAnimationValue does) that all lengths within the transform
     156             :     // function have already been computed to pixels and percents.
     157             :     //
     158             :     // Raw numbers are treated as being pixels.
     159             :     //
     160             :     // Don't convert to aValue to AppUnits here to avoid precision issues.
     161          36 :     return aValue.GetFloatValue();
     162           0 :   } else if (aValue.IsCalcUnit()) {
     163             :     nsRuleNode::ComputedCalc result =
     164             :       nsRuleNode::SpecifiedCalcToComputedCalc(aValue, aContext, aPresContext,
     165           0 :                                               aConditions);
     166           0 :     percent = result.mPercent;
     167           0 :     offset = result.mLength;
     168             :   } else {
     169             :     offset = nsRuleNode::CalcLength(aValue, aContext, aPresContext,
     170           0 :                                     aConditions);
     171             :   }
     172             : 
     173           0 :   float translation = NSAppUnitsToFloatPixels(offset,
     174           0 :                                               nsPresContext::AppUnitsPerCSSPixel());
     175             :   // We want to avoid calling aDimensionGetter if there's no percentage to be
     176             :   // resolved (for performance reasons - see TransformReferenceBox).
     177           0 :   if (percent != 0.0f && aRefBox && !aRefBox->IsEmpty()) {
     178           0 :     translation += percent *
     179           0 :                      NSAppUnitsToFloatPixels((aRefBox->*aDimensionGetter)(),
     180           0 :                                              nsPresContext::AppUnitsPerCSSPixel());
     181             :   }
     182           0 :   return translation;
     183             : }
     184             : 
     185             : /**
     186             :  * Helper functions to process all the transformation function types.
     187             :  *
     188             :  * These take a matrix parameter to accumulate the current matrix.
     189             :  */
     190             : 
     191             : /* Helper function to process a matrix entry. */
     192             : static void
     193           0 : ProcessMatrix(Matrix4x4& aMatrix,
     194             :               const nsCSSValue::Array* aData,
     195             :               nsStyleContext* aContext,
     196             :               nsPresContext* aPresContext,
     197             :               RuleNodeCacheConditions& aConditions,
     198             :               TransformReferenceBox& aRefBox)
     199             : {
     200           0 :   NS_PRECONDITION(aData->Count() == 7, "Invalid array!");
     201             : 
     202           0 :   gfxMatrix result;
     203             : 
     204             :   /* Take the first four elements out of the array as floats and store
     205             :    * them.
     206             :    */
     207           0 :   result._11 = aData->Item(1).GetFloatValue();
     208           0 :   result._12 = aData->Item(2).GetFloatValue();
     209           0 :   result._21 = aData->Item(3).GetFloatValue();
     210           0 :   result._22 = aData->Item(4).GetFloatValue();
     211             : 
     212             :   /* The last two elements have their length parts stored in aDelta
     213             :    * and their percent parts stored in aX[0] and aY[1].
     214             :    */
     215           0 :   result._31 = ProcessTranslatePart(aData->Item(5),
     216             :                                    aContext, aPresContext, aConditions,
     217             :                                    &aRefBox, &TransformReferenceBox::Width);
     218           0 :   result._32 = ProcessTranslatePart(aData->Item(6),
     219             :                                    aContext, aPresContext, aConditions,
     220             :                                    &aRefBox, &TransformReferenceBox::Height);
     221             : 
     222           0 :   aMatrix = result * aMatrix;
     223           0 : }
     224             : 
     225             : static void
     226           0 : ProcessMatrix3D(Matrix4x4& aMatrix,
     227             :                 const nsCSSValue::Array* aData,
     228             :                 nsStyleContext* aContext,
     229             :                 nsPresContext* aPresContext,
     230             :                 RuleNodeCacheConditions& aConditions,
     231             :                 TransformReferenceBox& aRefBox)
     232             : {
     233           0 :   NS_PRECONDITION(aData->Count() == 17, "Invalid array!");
     234             : 
     235           0 :   Matrix4x4 temp;
     236             : 
     237           0 :   temp._11 = aData->Item(1).GetFloatValue();
     238           0 :   temp._12 = aData->Item(2).GetFloatValue();
     239           0 :   temp._13 = aData->Item(3).GetFloatValue();
     240           0 :   temp._14 = aData->Item(4).GetFloatValue();
     241           0 :   temp._21 = aData->Item(5).GetFloatValue();
     242           0 :   temp._22 = aData->Item(6).GetFloatValue();
     243           0 :   temp._23 = aData->Item(7).GetFloatValue();
     244           0 :   temp._24 = aData->Item(8).GetFloatValue();
     245           0 :   temp._31 = aData->Item(9).GetFloatValue();
     246           0 :   temp._32 = aData->Item(10).GetFloatValue();
     247           0 :   temp._33 = aData->Item(11).GetFloatValue();
     248           0 :   temp._34 = aData->Item(12).GetFloatValue();
     249           0 :   temp._44 = aData->Item(16).GetFloatValue();
     250             : 
     251           0 :   temp._41 = ProcessTranslatePart(aData->Item(13),
     252             :                                   aContext, aPresContext, aConditions,
     253             :                                   &aRefBox, &TransformReferenceBox::Width);
     254           0 :   temp._42 = ProcessTranslatePart(aData->Item(14),
     255             :                                   aContext, aPresContext, aConditions,
     256             :                                   &aRefBox, &TransformReferenceBox::Height);
     257           0 :   temp._43 = ProcessTranslatePart(aData->Item(15),
     258             :                                   aContext, aPresContext, aConditions,
     259             :                                   nullptr);
     260             : 
     261           0 :   aMatrix = temp * aMatrix;
     262           0 : }
     263             : 
     264             : // For accumulation for transform functions, |aOne| corresponds to |aB| and
     265             : // |aTwo| corresponds to |aA| for StyleAnimationValue::Accumulate().
     266             : class Accumulate {
     267             : public:
     268             :   template<typename T>
     269           0 :   static T operate(const T& aOne, const T& aTwo, double aCoeff)
     270             :   {
     271           0 :     return aOne + aTwo * aCoeff;
     272             :   }
     273             : 
     274           0 :   static Point4D operateForPerspective(const Point4D& aOne,
     275             :                                        const Point4D& aTwo,
     276             :                                        double aCoeff)
     277             :   {
     278           0 :     return (aOne - Point4D(0, 0, 0, 1)) +
     279           0 :            (aTwo - Point4D(0, 0, 0, 1)) * aCoeff +
     280           0 :            Point4D(0, 0, 0, 1);
     281             :   }
     282           0 :   static Point3D operateForScale(const Point3D& aOne,
     283             :                                  const Point3D& aTwo,
     284             :                                  double aCoeff)
     285             :   {
     286             :     // For scale, the identify element is 1, see AddTransformScale in
     287             :     // StyleAnimationValue.cpp.
     288           0 :     return (aOne - Point3D(1, 1, 1)) +
     289           0 :            (aTwo - Point3D(1, 1, 1)) * aCoeff +
     290           0 :            Point3D(1, 1, 1);
     291             :   }
     292             : 
     293           0 :   static Matrix4x4 operateForRotate(const gfxQuaternion& aOne,
     294             :                                     const gfxQuaternion& aTwo,
     295             :                                     double aCoeff)
     296             :   {
     297           0 :     if (aCoeff == 0.0) {
     298           0 :       return aOne.ToMatrix();
     299             :     }
     300             : 
     301           0 :     double theta = acos(mozilla::clamped(aTwo.w, -1.0, 1.0));
     302           0 :     double scale = (theta != 0.0) ? 1.0 / sin(theta) : 0.0;
     303           0 :     theta *= aCoeff;
     304           0 :     scale *= sin(theta);
     305             : 
     306           0 :     gfxQuaternion result = gfxQuaternion(scale * aTwo.x,
     307           0 :                                          scale * aTwo.y,
     308           0 :                                          scale * aTwo.z,
     309           0 :                                          cos(theta)) * aOne;
     310           0 :     return result.ToMatrix();
     311             :   }
     312             : 
     313           0 :   static Matrix4x4 operateByServo(const Matrix4x4& aMatrix1,
     314             :                                   const Matrix4x4& aMatrix2,
     315             :                                   double aCount)
     316             :   {
     317           0 :     Matrix4x4 result;
     318           0 :     Servo_MatrixTransform_Operate(MatrixTransformOperator::Accumulate,
     319             :                                   &aMatrix1.components,
     320             :                                   &aMatrix2.components,
     321             :                                   aCount,
     322           0 :                                   &result.components);
     323           0 :     return result;
     324             :   }
     325             : };
     326             : 
     327             : class Interpolate {
     328             : public:
     329             :   template<typename T>
     330           0 :   static T operate(const T& aOne, const T& aTwo, double aCoeff)
     331             :   {
     332           0 :     return aOne + (aTwo - aOne) * aCoeff;
     333             :   }
     334             : 
     335           0 :   static Point4D operateForPerspective(const Point4D& aOne,
     336             :                                        const Point4D& aTwo,
     337             :                                        double aCoeff)
     338             :   {
     339           0 :     return aOne + (aTwo - aOne) * aCoeff;
     340             :   }
     341             : 
     342           0 :   static Point3D operateForScale(const Point3D& aOne,
     343             :                                  const Point3D& aTwo,
     344             :                                  double aCoeff)
     345             :   {
     346           0 :     return aOne + (aTwo - aOne) * aCoeff;
     347             :   }
     348             : 
     349           0 :   static Matrix4x4 operateForRotate(const gfxQuaternion& aOne,
     350             :                                     const gfxQuaternion& aTwo,
     351             :                                     double aCoeff)
     352             :   {
     353           0 :     return aOne.Slerp(aTwo, aCoeff).ToMatrix();
     354             :   }
     355             : 
     356           0 :   static Matrix4x4 operateByServo(const Matrix4x4& aMatrix1,
     357             :                                   const Matrix4x4& aMatrix2,
     358             :                                   double aProgress)
     359             :   {
     360           0 :     Matrix4x4 result;
     361           0 :     Servo_MatrixTransform_Operate(MatrixTransformOperator::Interpolate,
     362             :                                   &aMatrix1.components,
     363             :                                   &aMatrix2.components,
     364             :                                   aProgress,
     365           0 :                                   &result.components);
     366           0 :     return result;
     367             :   }
     368             : };
     369             : 
     370             : /**
     371             :  * Calculate 2 matrices by decomposing them with Operator.
     372             :  *
     373             :  * @param aMatrix1   First matrix, using CSS pixel units.
     374             :  * @param aMatrix2   Second matrix, using CSS pixel units.
     375             :  * @param aProgress  Coefficient for the Operator.
     376             :  */
     377             : template <typename Operator>
     378             : static Matrix4x4
     379           0 : OperateTransformMatrix(const Matrix4x4 &aMatrix1,
     380             :                        const Matrix4x4 &aMatrix2,
     381             :                        double aProgress)
     382             : {
     383             :   // Decompose both matrices
     384             : 
     385             :   // TODO: What do we do if one of these returns false (singular matrix)
     386           0 :   Point3D scale1(1, 1, 1), translate1;
     387           0 :   Point4D perspective1(0, 0, 0, 1);
     388           0 :   gfxQuaternion rotate1;
     389           0 :   nsStyleTransformMatrix::ShearArray shear1{0.0f, 0.0f, 0.0f};
     390             : 
     391           0 :   Point3D scale2(1, 1, 1), translate2;
     392           0 :   Point4D perspective2(0, 0, 0, 1);
     393           0 :   gfxQuaternion rotate2;
     394           0 :   nsStyleTransformMatrix::ShearArray shear2{0.0f, 0.0f, 0.0f};
     395             : 
     396           0 :   Matrix matrix2d1, matrix2d2;
     397           0 :   if (aMatrix1.Is2D(&matrix2d1) && aMatrix2.Is2D(&matrix2d2)) {
     398           0 :     Decompose2DMatrix(matrix2d1, scale1, shear1, rotate1, translate1);
     399           0 :     Decompose2DMatrix(matrix2d2, scale2, shear2, rotate2, translate2);
     400             :   } else {
     401           0 :     Decompose3DMatrix(aMatrix1, scale1, shear1,
     402             :                       rotate1, translate1, perspective1);
     403           0 :     Decompose3DMatrix(aMatrix2, scale2, shear2,
     404             :                       rotate2, translate2, perspective2);
     405             :   }
     406             : 
     407           0 :   Matrix4x4 result;
     408             : 
     409             :   // Operate each of the pieces in response to |Operator|.
     410             :   Point4D perspective =
     411           0 :     Operator::operateForPerspective(perspective1, perspective2, aProgress);
     412           0 :   result.SetTransposedVector(3, perspective);
     413             : 
     414             :   Point3D translate =
     415           0 :     Operator::operate(translate1, translate2, aProgress);
     416           0 :   result.PreTranslate(translate.x, translate.y, translate.z);
     417             : 
     418           0 :   Matrix4x4 rotate = Operator::operateForRotate(rotate1, rotate2, aProgress);
     419           0 :   if (!rotate.IsIdentity()) {
     420           0 :     result = rotate * result;
     421             :   }
     422             : 
     423             :   // TODO: Would it be better to operate these as angles?
     424             :   //       How do we convert back to angles?
     425             :   float yzshear =
     426           0 :     Operator::operate(shear1[ShearType::YZSHEAR],
     427             :                       shear2[ShearType::YZSHEAR],
     428           0 :                       aProgress);
     429           0 :   if (yzshear != 0.0) {
     430           0 :     result.SkewYZ(yzshear);
     431             :   }
     432             : 
     433             :   float xzshear =
     434           0 :     Operator::operate(shear1[ShearType::XZSHEAR],
     435             :                       shear2[ShearType::XZSHEAR],
     436           0 :                       aProgress);
     437           0 :   if (xzshear != 0.0) {
     438           0 :     result.SkewXZ(xzshear);
     439             :   }
     440             : 
     441             :   float xyshear =
     442           0 :     Operator::operate(shear1[ShearType::XYSHEAR],
     443             :                       shear2[ShearType::XYSHEAR],
     444           0 :                       aProgress);
     445           0 :   if (xyshear != 0.0) {
     446           0 :     result.SkewXY(xyshear);
     447             :   }
     448             : 
     449             :   Point3D scale =
     450           0 :     Operator::operateForScale(scale1, scale2, aProgress);
     451           0 :   if (scale != Point3D(1.0, 1.0, 1.0)) {
     452           0 :     result.PreScale(scale.x, scale.y, scale.z);
     453             :   }
     454             : 
     455           0 :   return result;
     456             : }
     457             : 
     458             : template <typename Operator>
     459             : static Matrix4x4
     460           0 : OperateTransformMatrixByServo(const Matrix4x4 &aMatrix1,
     461             :                               const Matrix4x4 &aMatrix2,
     462             :                               double aProgress)
     463             : {
     464           0 :   return Operator::operateByServo(aMatrix1, aMatrix2, aProgress);
     465             : }
     466             : 
     467             : template <typename Operator>
     468             : static void
     469           0 : ProcessMatrixOperator(Matrix4x4& aMatrix,
     470             :                       const nsCSSValue::Array* aData,
     471             :                       nsStyleContext* aContext,
     472             :                       nsPresContext* aPresContext,
     473             :                       RuleNodeCacheConditions& aConditions,
     474             :                       TransformReferenceBox& aRefBox,
     475             :                       bool* aContains3dTransform)
     476             : {
     477           0 :   NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
     478             : 
     479           0 :   auto readTransform = [&](const nsCSSValue& aValue) -> Matrix4x4 {
     480           0 :     const nsCSSValueList* list = nullptr;
     481           0 :     switch (aValue.GetUnit()) {
     482             :       case eCSSUnit_List:
     483             :         // For Gecko style backend.
     484           0 :         list = aValue.GetListValue();
     485           0 :         break;
     486             :       case eCSSUnit_SharedList:
     487             :         // For Servo style backend. The transform lists of interpolatematrix
     488             :         // are not created on the main thread (i.e. during parallel traversal),
     489             :         // and nsCSSValueList_heap is not thread safe. Therefore, we use
     490             :         // nsCSSValueSharedList as a workaround.
     491           0 :         list = aValue.GetSharedListValue()->mHead;
     492           0 :         break;
     493             :       default:
     494           0 :         list = nullptr;
     495             :     }
     496             : 
     497           0 :     Matrix4x4 matrix;
     498           0 :     if (!list) {
     499           0 :       return matrix;
     500             :     }
     501             : 
     502           0 :     float appUnitPerCSSPixel = nsPresContext::AppUnitsPerCSSPixel();
     503           0 :     matrix = nsStyleTransformMatrix::ReadTransforms(list,
     504           0 :                                                     aContext,
     505           0 :                                                     aPresContext,
     506           0 :                                                     aConditions,
     507           0 :                                                     aRefBox,
     508             :                                                     appUnitPerCSSPixel,
     509           0 :                                                     aContains3dTransform);
     510           0 :     return matrix;
     511           0 :   };
     512             : 
     513           0 :   Matrix4x4 matrix1 = readTransform(aData->Item(1));
     514           0 :   Matrix4x4 matrix2 = readTransform(aData->Item(2));
     515           0 :   double progress = aData->Item(3).GetPercentValue();
     516             : 
     517           0 :   if (aContext && aContext->IsServo()) {
     518           0 :     aMatrix =
     519             :       OperateTransformMatrixByServo<Operator>(matrix1, matrix2, progress)
     520             :         * aMatrix;
     521           0 :     return;
     522             :   }
     523             : 
     524           0 :   aMatrix =
     525             :     OperateTransformMatrix<Operator>(matrix1, matrix2, progress) * aMatrix;
     526             : }
     527             : 
     528             : /* Helper function to process two matrices that we need to interpolate between */
     529             : void
     530           0 : ProcessInterpolateMatrix(Matrix4x4& aMatrix,
     531             :                          const nsCSSValue::Array* aData,
     532             :                          nsStyleContext* aContext,
     533             :                          nsPresContext* aPresContext,
     534             :                          RuleNodeCacheConditions& aConditions,
     535             :                          TransformReferenceBox& aRefBox,
     536             :                          bool* aContains3dTransform)
     537             : {
     538             :   ProcessMatrixOperator<Interpolate>(aMatrix, aData, aContext, aPresContext,
     539             :                                      aConditions, aRefBox,
     540           0 :                                      aContains3dTransform);
     541           0 : }
     542             : 
     543             : void
     544           0 : ProcessAccumulateMatrix(Matrix4x4& aMatrix,
     545             :                         const nsCSSValue::Array* aData,
     546             :                         nsStyleContext* aContext,
     547             :                         nsPresContext* aPresContext,
     548             :                         RuleNodeCacheConditions& aConditions,
     549             :                         TransformReferenceBox& aRefBox,
     550             :                         bool* aContains3dTransform)
     551             : {
     552             :   ProcessMatrixOperator<Accumulate>(aMatrix, aData, aContext, aPresContext,
     553             :                                     aConditions, aRefBox,
     554           0 :                                     aContains3dTransform);
     555           0 : }
     556             : 
     557             : /* Helper function to process a translatex function. */
     558             : static void
     559          12 : ProcessTranslateX(Matrix4x4& aMatrix,
     560             :                   const nsCSSValue::Array* aData,
     561             :                   nsStyleContext* aContext,
     562             :                   nsPresContext* aPresContext,
     563             :                   RuleNodeCacheConditions& aConditions,
     564             :                   TransformReferenceBox& aRefBox)
     565             : {
     566          12 :   NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
     567             : 
     568          12 :   Point3D temp;
     569             : 
     570          12 :   temp.x = ProcessTranslatePart(aData->Item(1),
     571             :                                 aContext, aPresContext, aConditions,
     572             :                                 &aRefBox, &TransformReferenceBox::Width);
     573          12 :   aMatrix.PreTranslate(temp);
     574          12 : }
     575             : 
     576             : /* Helper function to process a translatey function. */
     577             : static void
     578           0 : ProcessTranslateY(Matrix4x4& aMatrix,
     579             :                   const nsCSSValue::Array* aData,
     580             :                   nsStyleContext* aContext,
     581             :                   nsPresContext* aPresContext,
     582             :                   RuleNodeCacheConditions& aConditions,
     583             :                   TransformReferenceBox& aRefBox)
     584             : {
     585           0 :   NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
     586             : 
     587           0 :   Point3D temp;
     588             : 
     589           0 :   temp.y = ProcessTranslatePart(aData->Item(1),
     590             :                                 aContext, aPresContext, aConditions,
     591             :                                 &aRefBox, &TransformReferenceBox::Height);
     592           0 :   aMatrix.PreTranslate(temp);
     593           0 : }
     594             : 
     595             : static void
     596           0 : ProcessTranslateZ(Matrix4x4& aMatrix,
     597             :                   const nsCSSValue::Array* aData,
     598             :                   nsStyleContext* aContext,
     599             :                   nsPresContext* aPresContext,
     600             :                   RuleNodeCacheConditions& aConditions)
     601             : {
     602           0 :   NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
     603             : 
     604           0 :   Point3D temp;
     605             : 
     606           0 :   temp.z = ProcessTranslatePart(aData->Item(1), aContext,
     607             :                                 aPresContext, aConditions,
     608             :                                 nullptr);
     609           0 :   aMatrix.PreTranslate(temp);
     610           0 : }
     611             : 
     612             : /* Helper function to process a translate function. */
     613             : static void
     614          24 : ProcessTranslate(Matrix4x4& aMatrix,
     615             :                  const nsCSSValue::Array* aData,
     616             :                  nsStyleContext* aContext,
     617             :                  nsPresContext* aPresContext,
     618             :                  RuleNodeCacheConditions& aConditions,
     619             :                  TransformReferenceBox& aRefBox)
     620             : {
     621          24 :   NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Invalid array!");
     622             : 
     623          24 :   Point3D temp;
     624             : 
     625          24 :   temp.x = ProcessTranslatePart(aData->Item(1),
     626             :                                 aContext, aPresContext, aConditions,
     627             :                                 &aRefBox, &TransformReferenceBox::Width);
     628             : 
     629             :   /* If we read in a Y component, set it appropriately */
     630          24 :   if (aData->Count() == 3) {
     631           0 :     temp.y = ProcessTranslatePart(aData->Item(2),
     632             :                                   aContext, aPresContext, aConditions,
     633             :                                   &aRefBox, &TransformReferenceBox::Height);
     634             :   }
     635          24 :   aMatrix.PreTranslate(temp);
     636          24 : }
     637             : 
     638             : static void
     639           0 : ProcessTranslate3D(Matrix4x4& aMatrix,
     640             :                    const nsCSSValue::Array* aData,
     641             :                    nsStyleContext* aContext,
     642             :                    nsPresContext* aPresContext,
     643             :                    RuleNodeCacheConditions& aConditions,
     644             :                    TransformReferenceBox& aRefBox)
     645             : {
     646           0 :   NS_PRECONDITION(aData->Count() == 4, "Invalid array!");
     647             : 
     648           0 :   Point3D temp;
     649             : 
     650           0 :   temp.x = ProcessTranslatePart(aData->Item(1),
     651             :                                 aContext, aPresContext, aConditions,
     652             :                                 &aRefBox, &TransformReferenceBox::Width);
     653             : 
     654           0 :   temp.y = ProcessTranslatePart(aData->Item(2),
     655             :                                 aContext, aPresContext, aConditions,
     656             :                                 &aRefBox, &TransformReferenceBox::Height);
     657             : 
     658           0 :   temp.z = ProcessTranslatePart(aData->Item(3),
     659             :                                 aContext, aPresContext, aConditions,
     660             :                                 nullptr);
     661             : 
     662           0 :   aMatrix.PreTranslate(temp);
     663           0 : }
     664             : 
     665             : /* Helper function to set up a scale matrix. */
     666             : static void
     667          14 : ProcessScaleHelper(Matrix4x4& aMatrix,
     668             :                    float aXScale,
     669             :                    float aYScale,
     670             :                    float aZScale)
     671             : {
     672          14 :   aMatrix.PreScale(aXScale, aYScale, aZScale);
     673          14 : }
     674             : 
     675             : /* Process a scalex function. */
     676             : static void
     677          14 : ProcessScaleX(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     678             : {
     679          14 :   NS_PRECONDITION(aData->Count() == 2, "Bad array!");
     680          14 :   ProcessScaleHelper(aMatrix, aData->Item(1).GetFloatValue(), 1.0f, 1.0f);
     681          14 : }
     682             : 
     683             : /* Process a scaley function. */
     684             : static void
     685           0 : ProcessScaleY(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     686             : {
     687           0 :   NS_PRECONDITION(aData->Count() == 2, "Bad array!");
     688           0 :   ProcessScaleHelper(aMatrix, 1.0f, aData->Item(1).GetFloatValue(), 1.0f);
     689           0 : }
     690             : 
     691             : static void
     692           0 : ProcessScaleZ(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     693             : {
     694           0 :   NS_PRECONDITION(aData->Count() == 2, "Bad array!");
     695           0 :   ProcessScaleHelper(aMatrix, 1.0f, 1.0f, aData->Item(1).GetFloatValue());
     696           0 : }
     697             : 
     698             : static void
     699           0 : ProcessScale3D(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     700             : {
     701           0 :   NS_PRECONDITION(aData->Count() == 4, "Bad array!");
     702           0 :   ProcessScaleHelper(aMatrix,
     703           0 :                      aData->Item(1).GetFloatValue(),
     704           0 :                      aData->Item(2).GetFloatValue(),
     705           0 :                      aData->Item(3).GetFloatValue());
     706           0 : }
     707             : 
     708             : /* Process a scale function. */
     709             : static void
     710           0 : ProcessScale(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     711             : {
     712           0 :   NS_PRECONDITION(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
     713             :   /* We either have one element or two.  If we have one, it's for both X and Y.
     714             :    * Otherwise it's one for each.
     715             :    */
     716           0 :   const nsCSSValue& scaleX = aData->Item(1);
     717           0 :   const nsCSSValue& scaleY = (aData->Count() == 2 ? scaleX :
     718           0 :                               aData->Item(2));
     719             : 
     720           0 :   ProcessScaleHelper(aMatrix,
     721             :                      scaleX.GetFloatValue(),
     722             :                      scaleY.GetFloatValue(),
     723           0 :                      1.0f);
     724           0 : }
     725             : 
     726             : /* Helper function that, given a set of angles, constructs the appropriate
     727             :  * skew matrix.
     728             :  */
     729             : static void
     730           0 : ProcessSkewHelper(Matrix4x4& aMatrix, double aXAngle, double aYAngle)
     731             : {
     732           0 :   aMatrix.SkewXY(aXAngle, aYAngle);
     733           0 : }
     734             : 
     735             : /* Function that converts a skewx transform into a matrix. */
     736             : static void
     737           0 : ProcessSkewX(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     738             : {
     739           0 :   NS_ASSERTION(aData->Count() == 2, "Bad array!");
     740           0 :   ProcessSkewHelper(aMatrix, aData->Item(1).GetAngleValueInRadians(), 0.0);
     741           0 : }
     742             : 
     743             : /* Function that converts a skewy transform into a matrix. */
     744             : static void
     745           0 : ProcessSkewY(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     746             : {
     747           0 :   NS_ASSERTION(aData->Count() == 2, "Bad array!");
     748           0 :   ProcessSkewHelper(aMatrix, 0.0, aData->Item(1).GetAngleValueInRadians());
     749           0 : }
     750             : 
     751             : /* Function that converts a skew transform into a matrix. */
     752             : static void
     753           0 : ProcessSkew(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     754             : {
     755           0 :   NS_ASSERTION(aData->Count() == 2 || aData->Count() == 3, "Bad array!");
     756             : 
     757           0 :   double xSkew = aData->Item(1).GetAngleValueInRadians();
     758           0 :   double ySkew = (aData->Count() == 2
     759           0 :                   ? 0.0 : aData->Item(2).GetAngleValueInRadians());
     760             : 
     761           0 :   ProcessSkewHelper(aMatrix, xSkew, ySkew);
     762           0 : }
     763             : 
     764             : /* Function that converts a rotate transform into a matrix. */
     765             : static void
     766           0 : ProcessRotateZ(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     767             : {
     768           0 :   NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
     769           0 :   double theta = aData->Item(1).GetAngleValueInRadians();
     770           0 :   aMatrix.RotateZ(theta);
     771           0 : }
     772             : 
     773             : static void
     774           0 : ProcessRotateX(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     775             : {
     776           0 :   NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
     777           0 :   double theta = aData->Item(1).GetAngleValueInRadians();
     778           0 :   aMatrix.RotateX(theta);
     779           0 : }
     780             : 
     781             : static void
     782           0 : ProcessRotateY(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     783             : {
     784           0 :   NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
     785           0 :   double theta = aData->Item(1).GetAngleValueInRadians();
     786           0 :   aMatrix.RotateY(theta);
     787           0 : }
     788             : 
     789             : static void
     790           0 : ProcessRotate3D(Matrix4x4& aMatrix, const nsCSSValue::Array* aData)
     791             : {
     792           0 :   NS_PRECONDITION(aData->Count() == 5, "Invalid array!");
     793             : 
     794           0 :   double theta = aData->Item(4).GetAngleValueInRadians();
     795           0 :   float x = aData->Item(1).GetFloatValue();
     796           0 :   float y = aData->Item(2).GetFloatValue();
     797           0 :   float z = aData->Item(3).GetFloatValue();
     798             : 
     799           0 :   Matrix4x4 temp;
     800           0 :   temp.SetRotateAxisAngle(x, y, z, theta);
     801             : 
     802           0 :   aMatrix = temp * aMatrix;
     803           0 : }
     804             : 
     805             : static void
     806           0 : ProcessPerspective(Matrix4x4& aMatrix,
     807             :                    const nsCSSValue::Array* aData,
     808             :                    nsStyleContext *aContext,
     809             :                    nsPresContext *aPresContext,
     810             :                    RuleNodeCacheConditions& aConditions)
     811             : {
     812           0 :   NS_PRECONDITION(aData->Count() == 2, "Invalid array!");
     813             : 
     814           0 :   float depth = ProcessTranslatePart(aData->Item(1), aContext,
     815           0 :                                      aPresContext, aConditions, nullptr);
     816           0 :   ApplyPerspectiveToMatrix(aMatrix, depth);
     817           0 : }
     818             : 
     819             : 
     820             : /**
     821             :  * SetToTransformFunction is essentially a giant switch statement that fans
     822             :  * out to many smaller helper functions.
     823             :  */
     824             : static void
     825          50 : MatrixForTransformFunction(Matrix4x4& aMatrix,
     826             :                            const nsCSSValue::Array * aData,
     827             :                            nsStyleContext* aContext,
     828             :                            nsPresContext* aPresContext,
     829             :                            RuleNodeCacheConditions& aConditions,
     830             :                            TransformReferenceBox& aRefBox,
     831             :                            bool* aContains3dTransform)
     832             : {
     833          50 :   MOZ_ASSERT(aContains3dTransform);
     834          50 :   NS_PRECONDITION(aData, "Why did you want to get data from a null array?");
     835             :   // It's OK if aContext and aPresContext are null if the caller already
     836             :   // knows that all length units have been converted to pixels (as
     837             :   // StyleAnimationValue does).
     838             : 
     839             : 
     840             :   /* Get the keyword for the transform. */
     841          50 :   switch (TransformFunctionOf(aData)) {
     842             :   case eCSSKeyword_translatex:
     843             :     ProcessTranslateX(aMatrix, aData, aContext, aPresContext,
     844          12 :                       aConditions, aRefBox);
     845          12 :     break;
     846             :   case eCSSKeyword_translatey:
     847             :     ProcessTranslateY(aMatrix, aData, aContext, aPresContext,
     848           0 :                       aConditions, aRefBox);
     849           0 :     break;
     850             :   case eCSSKeyword_translatez:
     851           0 :     *aContains3dTransform = true;
     852             :     ProcessTranslateZ(aMatrix, aData, aContext, aPresContext,
     853           0 :                       aConditions);
     854           0 :     break;
     855             :   case eCSSKeyword_translate:
     856             :     ProcessTranslate(aMatrix, aData, aContext, aPresContext,
     857          24 :                      aConditions, aRefBox);
     858          24 :     break;
     859             :   case eCSSKeyword_translate3d:
     860           0 :     *aContains3dTransform = true;
     861             :     ProcessTranslate3D(aMatrix, aData, aContext, aPresContext,
     862           0 :                        aConditions, aRefBox);
     863           0 :     break;
     864             :   case eCSSKeyword_scalex:
     865          14 :     ProcessScaleX(aMatrix, aData);
     866          14 :     break;
     867             :   case eCSSKeyword_scaley:
     868           0 :     ProcessScaleY(aMatrix, aData);
     869           0 :     break;
     870             :   case eCSSKeyword_scalez:
     871           0 :     *aContains3dTransform = true;
     872           0 :     ProcessScaleZ(aMatrix, aData);
     873           0 :     break;
     874             :   case eCSSKeyword_scale:
     875           0 :     ProcessScale(aMatrix, aData);
     876           0 :     break;
     877             :   case eCSSKeyword_scale3d:
     878           0 :     *aContains3dTransform = true;
     879           0 :     ProcessScale3D(aMatrix, aData);
     880           0 :     break;
     881             :   case eCSSKeyword_skewx:
     882           0 :     ProcessSkewX(aMatrix, aData);
     883           0 :     break;
     884             :   case eCSSKeyword_skewy:
     885           0 :     ProcessSkewY(aMatrix, aData);
     886           0 :     break;
     887             :   case eCSSKeyword_skew:
     888           0 :     ProcessSkew(aMatrix, aData);
     889           0 :     break;
     890             :   case eCSSKeyword_rotatex:
     891           0 :     *aContains3dTransform = true;
     892           0 :     ProcessRotateX(aMatrix, aData);
     893           0 :     break;
     894             :   case eCSSKeyword_rotatey:
     895           0 :     *aContains3dTransform = true;
     896           0 :     ProcessRotateY(aMatrix, aData);
     897           0 :     break;
     898             :   case eCSSKeyword_rotatez:
     899           0 :     *aContains3dTransform = true;
     900             :     MOZ_FALLTHROUGH;
     901             :   case eCSSKeyword_rotate:
     902           0 :     ProcessRotateZ(aMatrix, aData);
     903           0 :     break;
     904             :   case eCSSKeyword_rotate3d:
     905           0 :     *aContains3dTransform = true;
     906           0 :     ProcessRotate3D(aMatrix, aData);
     907           0 :     break;
     908             :   case eCSSKeyword_matrix:
     909             :     ProcessMatrix(aMatrix, aData, aContext, aPresContext,
     910           0 :                   aConditions, aRefBox);
     911           0 :     break;
     912             :   case eCSSKeyword_matrix3d:
     913           0 :     *aContains3dTransform = true;
     914             :     ProcessMatrix3D(aMatrix, aData, aContext, aPresContext,
     915           0 :                     aConditions, aRefBox);
     916           0 :     break;
     917             :   case eCSSKeyword_interpolatematrix:
     918             :     ProcessMatrixOperator<Interpolate>(aMatrix, aData, aContext, aPresContext,
     919             :                                        aConditions, aRefBox,
     920           0 :                                        aContains3dTransform);
     921           0 :     break;
     922             :   case eCSSKeyword_accumulatematrix:
     923             :     ProcessMatrixOperator<Accumulate>(aMatrix, aData, aContext, aPresContext,
     924             :                                       aConditions, aRefBox,
     925           0 :                                       aContains3dTransform);
     926           0 :     break;
     927             :   case eCSSKeyword_perspective:
     928           0 :     *aContains3dTransform = true;
     929             :     ProcessPerspective(aMatrix, aData, aContext, aPresContext,
     930           0 :                        aConditions);
     931           0 :     break;
     932             :   default:
     933           0 :     NS_NOTREACHED("Unknown transform function!");
     934             :   }
     935          50 : }
     936             : 
     937             : /**
     938             :  * Return the transform function, as an nsCSSKeyword, for the given
     939             :  * nsCSSValue::Array from a transform list.
     940             :  */
     941             : nsCSSKeyword
     942          50 : TransformFunctionOf(const nsCSSValue::Array* aData)
     943             : {
     944          50 :   MOZ_ASSERT(aData->Item(0).GetUnit() == eCSSUnit_Enumerated);
     945          50 :   return aData->Item(0).GetKeywordValue();
     946             : }
     947             : 
     948             : void
     949           0 : SetIdentityMatrix(nsCSSValue::Array* aMatrix)
     950             : {
     951           0 :   MOZ_ASSERT(aMatrix, "aMatrix should be non-null");
     952             : 
     953           0 :   nsCSSKeyword tfunc = TransformFunctionOf(aMatrix);
     954           0 :   MOZ_ASSERT(tfunc == eCSSKeyword_matrix ||
     955             :              tfunc == eCSSKeyword_matrix3d,
     956             :              "Only accept matrix and matrix3d");
     957             : 
     958           0 :   if (tfunc == eCSSKeyword_matrix) {
     959           0 :     MOZ_ASSERT(aMatrix->Count() == 7, "Invalid matrix");
     960           0 :     Matrix m;
     961           0 :     for (size_t i = 0; i < 6; ++i) {
     962           0 :       aMatrix->Item(i + 1).SetFloatValue(m.components[i], eCSSUnit_Number);
     963             :     }
     964           0 :     return;
     965             :   }
     966             : 
     967           0 :   MOZ_ASSERT(aMatrix->Count() == 17, "Invalid matrix3d");
     968           0 :   Matrix4x4 m;
     969           0 :   for (size_t i = 0; i < 16; ++i) {
     970           0 :     aMatrix->Item(i + 1).SetFloatValue(m.components[i], eCSSUnit_Number);
     971             :   }
     972             : }
     973             : 
     974             : Matrix4x4
     975          50 : ReadTransforms(const nsCSSValueList* aList,
     976             :                nsStyleContext* aContext,
     977             :                nsPresContext* aPresContext,
     978             :                RuleNodeCacheConditions& aConditions,
     979             :                TransformReferenceBox& aRefBox,
     980             :                float aAppUnitsPerMatrixUnit,
     981             :                bool* aContains3dTransform)
     982             : {
     983          50 :   Matrix4x4 result;
     984             : 
     985         100 :   for (const nsCSSValueList* curr = aList; curr != nullptr; curr = curr->mNext) {
     986          50 :     const nsCSSValue &currElem = curr->mValue;
     987          50 :     if (currElem.GetUnit() != eCSSUnit_Function) {
     988           0 :       NS_ASSERTION(currElem.GetUnit() == eCSSUnit_None &&
     989             :                    !aList->mNext,
     990             :                    "stream should either be a list of functions or a "
     991             :                    "lone None");
     992           0 :       continue;
     993             :     }
     994          50 :     NS_ASSERTION(currElem.GetArrayValue()->Count() >= 1,
     995             :                  "Incoming function is too short!");
     996             : 
     997             :     /* Read in a single transform matrix. */
     998          50 :     MatrixForTransformFunction(result, currElem.GetArrayValue(), aContext,
     999             :                                aPresContext, aConditions, aRefBox,
    1000          50 :                                aContains3dTransform);
    1001             :   }
    1002             : 
    1003          50 :   float scale = float(nsPresContext::AppUnitsPerCSSPixel()) / aAppUnitsPerMatrixUnit;
    1004          50 :   result.PreScale(1/scale, 1/scale, 1/scale);
    1005          50 :   result.PostScale(scale, scale, scale);
    1006             : 
    1007          50 :   return result;
    1008             : }
    1009             : 
    1010             : Point
    1011           0 : Convert2DPosition(nsStyleCoord const (&aValue)[2],
    1012             :                   TransformReferenceBox& aRefBox,
    1013             :                   int32_t aAppUnitsPerDevPixel)
    1014             : {
    1015             :   float position[2];
    1016             :   nsStyleTransformMatrix::TransformReferenceBox::DimensionGetter dimensionGetter[] =
    1017             :     { &nsStyleTransformMatrix::TransformReferenceBox::Width,
    1018           0 :       &nsStyleTransformMatrix::TransformReferenceBox::Height };
    1019           0 :   for (uint8_t index = 0; index < 2; ++index) {
    1020           0 :     const nsStyleCoord& value  = aValue[index];
    1021           0 :     if (value.GetUnit() == eStyleUnit_Calc) {
    1022           0 :       const nsStyleCoord::Calc *calc = value.GetCalcValue();
    1023           0 :       position[index] =
    1024           0 :         NSAppUnitsToFloatPixels((aRefBox.*dimensionGetter[index])(), aAppUnitsPerDevPixel) *
    1025           0 :           calc->mPercent +
    1026           0 :         NSAppUnitsToFloatPixels(calc->mLength, aAppUnitsPerDevPixel);
    1027           0 :     } else if (value.GetUnit() == eStyleUnit_Percent) {
    1028           0 :       position[index] =
    1029           0 :         NSAppUnitsToFloatPixels((aRefBox.*dimensionGetter[index])(), aAppUnitsPerDevPixel) *
    1030           0 :         value.GetPercentValue();
    1031             :     } else {
    1032           0 :       MOZ_ASSERT(value.GetUnit() == eStyleUnit_Coord,
    1033             :                 "unexpected unit");
    1034           0 :       position[index] =
    1035           0 :         NSAppUnitsToFloatPixels(value.GetCoordValue(),
    1036             :                                 aAppUnitsPerDevPixel);
    1037             :     }
    1038             :   }
    1039             : 
    1040           0 :   return Point(position[0], position[1]);
    1041             : }
    1042             : 
    1043             : /*
    1044             :  * The relevant section of the transitions specification:
    1045             :  * http://dev.w3.org/csswg/css3-transitions/#animation-of-property-types-
    1046             :  * defers all of the details to the 2-D and 3-D transforms specifications.
    1047             :  * For the 2-D transforms specification (all that's relevant for us, right
    1048             :  * now), the relevant section is:
    1049             :  * http://dev.w3.org/csswg/css3-2d-transforms/#animation
    1050             :  * This, in turn, refers to the unmatrix program in Graphics Gems,
    1051             :  * available from http://tog.acm.org/resources/GraphicsGems/ , and in
    1052             :  * particular as the file GraphicsGems/gemsii/unmatrix.c
    1053             :  * in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
    1054             :  *
    1055             :  * The unmatrix reference is for general 3-D transform matrices (any of the
    1056             :  * 16 components can have any value).
    1057             :  *
    1058             :  * For CSS 2-D transforms, we have a 2-D matrix with the bottom row constant:
    1059             :  *
    1060             :  * [ A C E ]
    1061             :  * [ B D F ]
    1062             :  * [ 0 0 1 ]
    1063             :  *
    1064             :  * For that case, I believe the algorithm in unmatrix reduces to:
    1065             :  *
    1066             :  *  (1) If A * D - B * C == 0, the matrix is singular.  Fail.
    1067             :  *
    1068             :  *  (2) Set translation components (Tx and Ty) to the translation parts of
    1069             :  *      the matrix (E and F) and then ignore them for the rest of the time.
    1070             :  *      (For us, E and F each actually consist of three constants:  a
    1071             :  *      length, a multiplier for the width, and a multiplier for the
    1072             :  *      height.  This actually requires its own decomposition, but I'll
    1073             :  *      keep that separate.)
    1074             :  *
    1075             :  *  (3) Let the X scale (Sx) be sqrt(A^2 + B^2).  Then divide both A and B
    1076             :  *      by it.
    1077             :  *
    1078             :  *  (4) Let the XY shear (K) be A * C + B * D.  From C, subtract A times
    1079             :  *      the XY shear.  From D, subtract B times the XY shear.
    1080             :  *
    1081             :  *  (5) Let the Y scale (Sy) be sqrt(C^2 + D^2).  Divide C, D, and the XY
    1082             :  *      shear (K) by it.
    1083             :  *
    1084             :  *  (6) At this point, A * D - B * C is either 1 or -1.  If it is -1,
    1085             :  *      negate the XY shear (K), the X scale (Sx), and A, B, C, and D.
    1086             :  *      (Alternatively, we could negate the XY shear (K) and the Y scale
    1087             :  *      (Sy).)
    1088             :  *
    1089             :  *  (7) Let the rotation be R = atan2(B, A).
    1090             :  *
    1091             :  * Then the resulting decomposed transformation is:
    1092             :  *
    1093             :  *   translate(Tx, Ty) rotate(R) skewX(atan(K)) scale(Sx, Sy)
    1094             :  *
    1095             :  * An interesting result of this is that all of the simple transform
    1096             :  * functions (i.e., all functions other than matrix()), in isolation,
    1097             :  * decompose back to themselves except for:
    1098             :  *   'skewY(φ)', which is 'matrix(1, tan(φ), 0, 1, 0, 0)', which decomposes
    1099             :  *   to 'rotate(φ) skewX(φ) scale(sec(φ), cos(φ))' since (ignoring the
    1100             :  *   alternate sign possibilities that would get fixed in step 6):
    1101             :  *     In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
    1102             :  *     Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
    1103             :  *     In step 4, the XY shear is sin(φ).
    1104             :  *     Thus, after step 4, C = -cos(φ)sin(φ) and D = 1 - sin²(φ) = cos²(φ).
    1105             :  *     Thus, in step 5, the Y scale is sqrt(cos²(φ)(sin²(φ) + cos²(φ)) = cos(φ).
    1106             :  *     Thus, after step 5, C = -sin(φ), D = cos(φ), and the XY shear is tan(φ).
    1107             :  *     Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
    1108             :  *     In step 7, the rotation is thus φ.
    1109             :  *
    1110             :  *   skew(θ, φ), which is matrix(1, tan(φ), tan(θ), 1, 0, 0), which decomposes
    1111             :  *   to 'rotate(φ) skewX(θ + φ) scale(sec(φ), cos(φ))' since (ignoring
    1112             :  *   the alternate sign possibilities that would get fixed in step 6):
    1113             :  *     In step 3, the X scale factor is sqrt(1+tan²(φ)) = sqrt(sec²(φ)) = sec(φ).
    1114             :  *     Thus, after step 3, A = 1/sec(φ) = cos(φ) and B = tan(φ) / sec(φ) = sin(φ).
    1115             :  *     In step 4, the XY shear is cos(φ)tan(θ) + sin(φ).
    1116             :  *     Thus, after step 4,
    1117             :  *     C = tan(θ) - cos(φ)(cos(φ)tan(θ) + sin(φ)) = tan(θ)sin²(φ) - cos(φ)sin(φ)
    1118             :  *     D = 1 - sin(φ)(cos(φ)tan(θ) + sin(φ)) = cos²(φ) - sin(φ)cos(φ)tan(θ)
    1119             :  *     Thus, in step 5, the Y scale is sqrt(C² + D²) =
    1120             :  *     sqrt(tan²(θ)(sin⁴(φ) + sin²(φ)cos²(φ)) -
    1121             :  *          2 tan(θ)(sin³(φ)cos(φ) + sin(φ)cos³(φ)) +
    1122             :  *          (sin²(φ)cos²(φ) + cos⁴(φ))) =
    1123             :  *     sqrt(tan²(θ)sin²(φ) - 2 tan(θ)sin(φ)cos(φ) + cos²(φ)) =
    1124             :  *     cos(φ) - tan(θ)sin(φ) (taking the negative of the obvious solution so
    1125             :  *     we avoid flipping in step 6).
    1126             :  *     After step 5, C = -sin(φ) and D = cos(φ), and the XY shear is
    1127             :  *     (cos(φ)tan(θ) + sin(φ)) / (cos(φ) - tan(θ)sin(φ)) =
    1128             :  *     (dividing both numerator and denominator by cos(φ))
    1129             :  *     (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)) = tan(θ + φ).
    1130             :  *     (See http://en.wikipedia.org/wiki/List_of_trigonometric_identities .)
    1131             :  *     Thus, in step 6, A * D - B * C = cos²(φ) + sin²(φ) = 1.
    1132             :  *     In step 7, the rotation is thus φ.
    1133             :  *
    1134             :  *     To check this result, we can multiply things back together:
    1135             :  *
    1136             :  *     [ cos(φ) -sin(φ) ] [ 1 tan(θ + φ) ] [ sec(φ)    0   ]
    1137             :  *     [ sin(φ)  cos(φ) ] [ 0      1     ] [   0    cos(φ) ]
    1138             :  *
    1139             :  *     [ cos(φ)      cos(φ)tan(θ + φ) - sin(φ) ] [ sec(φ)    0   ]
    1140             :  *     [ sin(φ)      sin(φ)tan(θ + φ) + cos(φ) ] [   0    cos(φ) ]
    1141             :  *
    1142             :  *     but since tan(θ + φ) = (tan(θ) + tan(φ)) / (1 - tan(θ)tan(φ)),
    1143             :  *     cos(φ)tan(θ + φ) - sin(φ)
    1144             :  *      = cos(φ)(tan(θ) + tan(φ)) - sin(φ) + sin(φ)tan(θ)tan(φ)
    1145             :  *      = cos(φ)tan(θ) + sin(φ) - sin(φ) + sin(φ)tan(θ)tan(φ)
    1146             :  *      = cos(φ)tan(θ) + sin(φ)tan(θ)tan(φ)
    1147             :  *      = tan(θ) (cos(φ) + sin(φ)tan(φ))
    1148             :  *      = tan(θ) sec(φ) (cos²(φ) + sin²(φ))
    1149             :  *      = tan(θ) sec(φ)
    1150             :  *     and
    1151             :  *     sin(φ)tan(θ + φ) + cos(φ)
    1152             :  *      = sin(φ)(tan(θ) + tan(φ)) + cos(φ) - cos(φ)tan(θ)tan(φ)
    1153             :  *      = tan(θ) (sin(φ) - sin(φ)) + sin(φ)tan(φ) + cos(φ)
    1154             :  *      = sec(φ) (sin²(φ) + cos²(φ))
    1155             :  *      = sec(φ)
    1156             :  *     so the above is:
    1157             :  *     [ cos(φ)  tan(θ) sec(φ) ] [ sec(φ)    0   ]
    1158             :  *     [ sin(φ)     sec(φ)     ] [   0    cos(φ) ]
    1159             :  *
    1160             :  *     [    1   tan(θ) ]
    1161             :  *     [ tan(φ)    1   ]
    1162             :  */
    1163             : 
    1164             : /*
    1165             :  * Decompose2DMatrix implements the above decomposition algorithm.
    1166             :  */
    1167             : 
    1168             : bool
    1169           0 : Decompose2DMatrix(const Matrix& aMatrix,
    1170             :                   Point3D& aScale,
    1171             :                   ShearArray& aShear,
    1172             :                   gfxQuaternion& aRotate,
    1173             :                   Point3D& aTranslate)
    1174             : {
    1175           0 :   float A = aMatrix._11,
    1176           0 :         B = aMatrix._12,
    1177           0 :         C = aMatrix._21,
    1178           0 :         D = aMatrix._22;
    1179           0 :   if (A * D == B * C) {
    1180             :     // singular matrix
    1181           0 :     return false;
    1182             :   }
    1183             : 
    1184           0 :   float scaleX = sqrt(A * A + B * B);
    1185           0 :   A /= scaleX;
    1186           0 :   B /= scaleX;
    1187             : 
    1188           0 :   float XYshear = A * C + B * D;
    1189           0 :   C -= A * XYshear;
    1190           0 :   D -= B * XYshear;
    1191             : 
    1192           0 :   float scaleY = sqrt(C * C + D * D);
    1193           0 :   C /= scaleY;
    1194           0 :   D /= scaleY;
    1195           0 :   XYshear /= scaleY;
    1196             : 
    1197             :   // A*D - B*C should now be 1 or -1
    1198           0 :   NS_ASSERTION(0.99 < Abs(A*D - B*C) && Abs(A*D - B*C) < 1.01,
    1199             :                "determinant should now be 1 or -1");
    1200           0 :   if (A * D < B * C) {
    1201           0 :     A = -A;
    1202           0 :     B = -B;
    1203           0 :     C = -C;
    1204           0 :     D = -D;
    1205           0 :     XYshear = -XYshear;
    1206           0 :     scaleX = -scaleX;
    1207             :   }
    1208             : 
    1209           0 :   float rotate = atan2f(B, A);
    1210           0 :   aRotate = gfxQuaternion(0, 0, sin(rotate/2), cos(rotate/2));
    1211           0 :   aShear[ShearType::XYSHEAR] = XYshear;
    1212           0 :   aScale.x = scaleX;
    1213           0 :   aScale.y = scaleY;
    1214           0 :   aTranslate.x = aMatrix._31;
    1215           0 :   aTranslate.y = aMatrix._32;
    1216           0 :   return true;
    1217             : }
    1218             : 
    1219             : /**
    1220             :  * Implementation of the unmatrix algorithm, specified by:
    1221             :  *
    1222             :  * http://dev.w3.org/csswg/css3-2d-transforms/#unmatrix
    1223             :  *
    1224             :  * This, in turn, refers to the unmatrix program in Graphics Gems,
    1225             :  * available from http://tog.acm.org/resources/GraphicsGems/ , and in
    1226             :  * particular as the file GraphicsGems/gemsii/unmatrix.c
    1227             :  * in http://tog.acm.org/resources/GraphicsGems/AllGems.tar.gz
    1228             :  */
    1229             : bool
    1230           0 : Decompose3DMatrix(const Matrix4x4& aMatrix,
    1231             :                   Point3D& aScale,
    1232             :                   ShearArray& aShear,
    1233             :                   gfxQuaternion& aRotate,
    1234             :                   Point3D& aTranslate,
    1235             :                   Point4D& aPerspective)
    1236             : {
    1237           0 :   Matrix4x4 local = aMatrix;
    1238             : 
    1239           0 :   if (local[3][3] == 0) {
    1240           0 :     return false;
    1241             :   }
    1242             :   /* Normalize the matrix */
    1243           0 :   local.Normalize();
    1244             : 
    1245             :   /**
    1246             :    * perspective is used to solve for perspective, but it also provides
    1247             :    * an easy way to test for singularity of the upper 3x3 component.
    1248             :    */
    1249           0 :   Matrix4x4 perspective = local;
    1250           0 :   Point4D empty(0, 0, 0, 1);
    1251           0 :   perspective.SetTransposedVector(3, empty);
    1252             : 
    1253           0 :   if (perspective.Determinant() == 0.0) {
    1254           0 :     return false;
    1255             :   }
    1256             : 
    1257             :   /* First, isolate perspective. */
    1258           0 :   if (local[0][3] != 0 || local[1][3] != 0 ||
    1259           0 :       local[2][3] != 0) {
    1260             :     /* aPerspective is the right hand side of the equation. */
    1261           0 :     aPerspective = local.TransposedVector(3);
    1262             : 
    1263             :     /**
    1264             :      * Solve the equation by inverting perspective and multiplying
    1265             :      * aPerspective by the inverse.
    1266             :      */
    1267           0 :     perspective.Invert();
    1268           0 :     aPerspective = perspective.TransposeTransform4D(aPerspective);
    1269             : 
    1270             :     /* Clear the perspective partition */
    1271           0 :     local.SetTransposedVector(3, empty);
    1272             :   } else {
    1273           0 :     aPerspective = Point4D(0, 0, 0, 1);
    1274             :   }
    1275             : 
    1276             :   /* Next take care of translation */
    1277           0 :   for (int i = 0; i < 3; i++) {
    1278           0 :     aTranslate[i] = local[3][i];
    1279           0 :     local[3][i] = 0;
    1280             :   }
    1281             : 
    1282             :   /* Now get scale and shear. */
    1283             : 
    1284             :   /* Compute X scale factor and normalize first row. */
    1285           0 :   aScale.x = local[0].Length();
    1286           0 :   local[0] /= aScale.x;
    1287             : 
    1288             :   /* Compute XY shear factor and make 2nd local orthogonal to 1st. */
    1289           0 :   aShear[ShearType::XYSHEAR] = local[0].DotProduct(local[1]);
    1290           0 :   local[1] -= local[0] * aShear[ShearType::XYSHEAR];
    1291             : 
    1292             :   /* Now, compute Y scale and normalize 2nd local. */
    1293           0 :   aScale.y = local[1].Length();
    1294           0 :   local[1] /= aScale.y;
    1295           0 :   aShear[ShearType::XYSHEAR] /= aScale.y;
    1296             : 
    1297             :   /* Compute XZ and YZ shears, make 3rd local orthogonal */
    1298           0 :   aShear[ShearType::XZSHEAR] = local[0].DotProduct(local[2]);
    1299           0 :   local[2] -= local[0] * aShear[ShearType::XZSHEAR];
    1300           0 :   aShear[ShearType::YZSHEAR] = local[1].DotProduct(local[2]);
    1301           0 :   local[2] -= local[1] * aShear[ShearType::YZSHEAR];
    1302             : 
    1303             :   /* Next, get Z scale and normalize 3rd local. */
    1304           0 :   aScale.z = local[2].Length();
    1305           0 :   local[2] /= aScale.z;
    1306             : 
    1307           0 :   aShear[ShearType::XZSHEAR] /= aScale.z;
    1308           0 :   aShear[ShearType::YZSHEAR] /= aScale.z;
    1309             : 
    1310             :   /**
    1311             :    * At this point, the matrix (in locals) is orthonormal.
    1312             :    * Check for a coordinate system flip.  If the determinant
    1313             :    * is -1, then negate the matrix and the scaling factors.
    1314             :    */
    1315           0 :   if (local[0].DotProduct(local[1].CrossProduct(local[2])) < 0) {
    1316           0 :     aScale *= -1;
    1317           0 :     for (int i = 0; i < 3; i++) {
    1318           0 :       local[i] *= -1;
    1319             :     }
    1320             :   }
    1321             : 
    1322             :   /* Now, get the rotations out */
    1323           0 :   aRotate = gfxQuaternion(local);
    1324             : 
    1325           0 :   return true;
    1326             : }
    1327             : 
    1328             : Matrix
    1329           0 : CSSValueArrayTo2DMatrix(nsCSSValue::Array* aArray)
    1330             : {
    1331           0 :   MOZ_ASSERT(aArray &&
    1332             :              TransformFunctionOf(aArray) == eCSSKeyword_matrix &&
    1333             :              aArray->Count() == 7);
    1334           0 :   Matrix m(aArray->Item(1).GetFloatValue(),
    1335           0 :            aArray->Item(2).GetFloatValue(),
    1336           0 :            aArray->Item(3).GetFloatValue(),
    1337           0 :            aArray->Item(4).GetFloatValue(),
    1338           0 :            aArray->Item(5).GetFloatValue(),
    1339           0 :            aArray->Item(6).GetFloatValue());
    1340           0 :   return m;
    1341             : }
    1342             : 
    1343             : Matrix4x4
    1344           0 : CSSValueArrayTo3DMatrix(nsCSSValue::Array* aArray)
    1345             : {
    1346           0 :   MOZ_ASSERT(aArray &&
    1347             :              TransformFunctionOf(aArray) == eCSSKeyword_matrix3d &&
    1348             :              aArray->Count() == 17);
    1349             :   gfx::Float array[16];
    1350           0 :   for (size_t i = 0; i < 16; ++i) {
    1351           0 :     array[i] = aArray->Item(i+1).GetFloatValue();
    1352             :   }
    1353           0 :   Matrix4x4 m(array);
    1354           0 :   return m;
    1355             : }
    1356             : 
    1357             : gfxSize
    1358           0 : GetScaleValue(const nsCSSValueSharedList* aList,
    1359             :               const nsIFrame* aForFrame)
    1360             : {
    1361           0 :   MOZ_ASSERT(aList && aList->mHead);
    1362           0 :   MOZ_ASSERT(aForFrame);
    1363             : 
    1364           0 :   RuleNodeCacheConditions dontCare;
    1365             :   bool dontCareBool;
    1366           0 :   TransformReferenceBox refBox(aForFrame);
    1367             :   Matrix4x4 transform = ReadTransforms(
    1368           0 :                           aList->mHead,
    1369             :                           aForFrame->StyleContext(),
    1370             :                           aForFrame->PresContext(), dontCare, refBox,
    1371           0 :                           aForFrame->PresContext()->AppUnitsPerDevPixel(),
    1372           0 :                           &dontCareBool);
    1373           0 :   Matrix transform2d;
    1374           0 :   bool canDraw2D = transform.CanDraw2D(&transform2d);
    1375           0 :   if (!canDraw2D) {
    1376           0 :     return gfxSize();
    1377             :   }
    1378             : 
    1379           0 :   return ThebesMatrix(transform2d).ScaleFactors(true);
    1380             : }
    1381             : 
    1382             : } // namespace nsStyleTransformMatrix

Generated by: LCOV version 1.13