Home | History | Annotate | Download | only in resolver
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 2004-2005 Allan Sandfeld Jensen (kde (at) carewolf.com)
      4  * Copyright (C) 2006, 2007 Nicholas Shanks (webkit (at) nickshanks.com)
      5  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
      6  * Copyright (C) 2007 Alexey Proskuryakov <ap (at) webkit.org>
      7  * Copyright (C) 2007, 2008 Eric Seidel <eric (at) webkit.org>
      8  * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
      9  * Copyright (c) 2011, Code Aurora Forum. All rights reserved.
     10  * Copyright (C) Research In Motion Limited 2011. All rights reserved.
     11  * Copyright (C) 2012 Google Inc. All rights reserved.
     12  *
     13  * This library is free software; you can redistribute it and/or
     14  * modify it under the terms of the GNU Library General Public
     15  * License as published by the Free Software Foundation; either
     16  * version 2 of the License, or (at your option) any later version.
     17  *
     18  * This library is distributed in the hope that it will be useful,
     19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     21  * Library General Public License for more details.
     22  *
     23  * You should have received a copy of the GNU Library General Public License
     24  * along with this library; see the file COPYING.LIB.  If not, write to
     25  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     26  * Boston, MA 02110-1301, USA.
     27  */
     28 
     29 #include "config.h"
     30 #include "core/css/resolver/TransformBuilder.h"
     31 
     32 #include "core/css/CSSPrimitiveValueMappings.h"
     33 #include "core/css/CSSTransformValue.h"
     34 #include "core/rendering/style/RenderStyle.h"
     35 #include "platform/heap/Handle.h"
     36 #include "platform/transforms/Matrix3DTransformOperation.h"
     37 #include "platform/transforms/MatrixTransformOperation.h"
     38 #include "platform/transforms/PerspectiveTransformOperation.h"
     39 #include "platform/transforms/RotateTransformOperation.h"
     40 #include "platform/transforms/ScaleTransformOperation.h"
     41 #include "platform/transforms/SkewTransformOperation.h"
     42 #include "platform/transforms/TransformationMatrix.h"
     43 #include "platform/transforms/TranslateTransformOperation.h"
     44 
     45 namespace WebCore {
     46 
     47 static Length convertToFloatLength(CSSPrimitiveValue* primitiveValue, const CSSToLengthConversionData& conversionData)
     48 {
     49     ASSERT(primitiveValue);
     50     return primitiveValue->convertToLength<FixedConversion | PercentConversion>(conversionData);
     51 }
     52 
     53 static TransformOperation::OperationType getTransformOperationType(CSSTransformValue::TransformOperationType type)
     54 {
     55     switch (type) {
     56     case CSSTransformValue::ScaleTransformOperation: return TransformOperation::Scale;
     57     case CSSTransformValue::ScaleXTransformOperation: return TransformOperation::ScaleX;
     58     case CSSTransformValue::ScaleYTransformOperation: return TransformOperation::ScaleY;
     59     case CSSTransformValue::ScaleZTransformOperation: return TransformOperation::ScaleZ;
     60     case CSSTransformValue::Scale3DTransformOperation: return TransformOperation::Scale3D;
     61     case CSSTransformValue::TranslateTransformOperation: return TransformOperation::Translate;
     62     case CSSTransformValue::TranslateXTransformOperation: return TransformOperation::TranslateX;
     63     case CSSTransformValue::TranslateYTransformOperation: return TransformOperation::TranslateY;
     64     case CSSTransformValue::TranslateZTransformOperation: return TransformOperation::TranslateZ;
     65     case CSSTransformValue::Translate3DTransformOperation: return TransformOperation::Translate3D;
     66     case CSSTransformValue::RotateTransformOperation: return TransformOperation::Rotate;
     67     case CSSTransformValue::RotateXTransformOperation: return TransformOperation::RotateX;
     68     case CSSTransformValue::RotateYTransformOperation: return TransformOperation::RotateY;
     69     case CSSTransformValue::RotateZTransformOperation: return TransformOperation::RotateZ;
     70     case CSSTransformValue::Rotate3DTransformOperation: return TransformOperation::Rotate3D;
     71     case CSSTransformValue::SkewTransformOperation: return TransformOperation::Skew;
     72     case CSSTransformValue::SkewXTransformOperation: return TransformOperation::SkewX;
     73     case CSSTransformValue::SkewYTransformOperation: return TransformOperation::SkewY;
     74     case CSSTransformValue::MatrixTransformOperation: return TransformOperation::Matrix;
     75     case CSSTransformValue::Matrix3DTransformOperation: return TransformOperation::Matrix3D;
     76     case CSSTransformValue::PerspectiveTransformOperation: return TransformOperation::Perspective;
     77     case CSSTransformValue::UnknownTransformOperation: return TransformOperation::None;
     78     }
     79     return TransformOperation::None;
     80 }
     81 
     82 bool TransformBuilder::createTransformOperations(CSSValue* inValue, const CSSToLengthConversionData& conversionData, TransformOperations& outOperations)
     83 {
     84     if (!inValue || !inValue->isValueList()) {
     85         outOperations.clear();
     86         return false;
     87     }
     88 
     89     float zoomFactor = conversionData.zoom();
     90     TransformOperations operations;
     91     for (CSSValueListIterator i = inValue; i.hasMore(); i.advance()) {
     92         CSSValue* currValue = i.value();
     93 
     94         if (!currValue->isTransformValue())
     95             continue;
     96 
     97         CSSTransformValue* transformValue = toCSSTransformValue(i.value());
     98         if (!transformValue->length())
     99             continue;
    100 
    101         bool haveNonPrimitiveValue = false;
    102         for (unsigned j = 0; j < transformValue->length(); ++j) {
    103             if (!transformValue->itemWithoutBoundsCheck(j)->isPrimitiveValue()) {
    104                 haveNonPrimitiveValue = true;
    105                 break;
    106             }
    107         }
    108         if (haveNonPrimitiveValue)
    109             continue;
    110 
    111         CSSPrimitiveValue* firstValue = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(0));
    112 
    113         switch (transformValue->operationType()) {
    114         case CSSTransformValue::ScaleTransformOperation:
    115         case CSSTransformValue::ScaleXTransformOperation:
    116         case CSSTransformValue::ScaleYTransformOperation: {
    117             double sx = 1.0;
    118             double sy = 1.0;
    119             if (transformValue->operationType() == CSSTransformValue::ScaleYTransformOperation)
    120                 sy = firstValue->getDoubleValue();
    121             else {
    122                 sx = firstValue->getDoubleValue();
    123                 if (transformValue->operationType() != CSSTransformValue::ScaleXTransformOperation) {
    124                     if (transformValue->length() > 1) {
    125                         CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(1));
    126                         sy = secondValue->getDoubleValue();
    127                     } else
    128                         sy = sx;
    129                 }
    130             }
    131             operations.operations().append(ScaleTransformOperation::create(sx, sy, 1.0, getTransformOperationType(transformValue->operationType())));
    132             break;
    133         }
    134         case CSSTransformValue::ScaleZTransformOperation:
    135         case CSSTransformValue::Scale3DTransformOperation: {
    136             double sx = 1.0;
    137             double sy = 1.0;
    138             double sz = 1.0;
    139             if (transformValue->operationType() == CSSTransformValue::ScaleZTransformOperation)
    140                 sz = firstValue->getDoubleValue();
    141             else if (transformValue->operationType() == CSSTransformValue::ScaleYTransformOperation)
    142                 sy = firstValue->getDoubleValue();
    143             else {
    144                 sx = firstValue->getDoubleValue();
    145                 if (transformValue->operationType() != CSSTransformValue::ScaleXTransformOperation) {
    146                     if (transformValue->length() > 2) {
    147                         CSSPrimitiveValue* thirdValue = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(2));
    148                         sz = thirdValue->getDoubleValue();
    149                     }
    150                     if (transformValue->length() > 1) {
    151                         CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(1));
    152                         sy = secondValue->getDoubleValue();
    153                     } else
    154                         sy = sx;
    155                 }
    156             }
    157             operations.operations().append(ScaleTransformOperation::create(sx, sy, sz, getTransformOperationType(transformValue->operationType())));
    158             break;
    159         }
    160         case CSSTransformValue::TranslateTransformOperation:
    161         case CSSTransformValue::TranslateXTransformOperation:
    162         case CSSTransformValue::TranslateYTransformOperation: {
    163             Length tx = Length(0, Fixed);
    164             Length ty = Length(0, Fixed);
    165             if (transformValue->operationType() == CSSTransformValue::TranslateYTransformOperation)
    166                 ty = convertToFloatLength(firstValue, conversionData);
    167             else {
    168                 tx = convertToFloatLength(firstValue, conversionData);
    169                 if (transformValue->operationType() != CSSTransformValue::TranslateXTransformOperation) {
    170                     if (transformValue->length() > 1) {
    171                         CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(1));
    172                         ty = convertToFloatLength(secondValue, conversionData);
    173                     }
    174                 }
    175             }
    176 
    177             operations.operations().append(TranslateTransformOperation::create(tx, ty, 0, getTransformOperationType(transformValue->operationType())));
    178             break;
    179         }
    180         case CSSTransformValue::TranslateZTransformOperation:
    181         case CSSTransformValue::Translate3DTransformOperation: {
    182             Length tx = Length(0, Fixed);
    183             Length ty = Length(0, Fixed);
    184             double tz = 0;
    185             if (transformValue->operationType() == CSSTransformValue::TranslateZTransformOperation)
    186                 tz = firstValue->computeLength<double>(conversionData);
    187             else if (transformValue->operationType() == CSSTransformValue::TranslateYTransformOperation)
    188                 ty = convertToFloatLength(firstValue, conversionData);
    189             else {
    190                 tx = convertToFloatLength(firstValue, conversionData);
    191                 if (transformValue->operationType() != CSSTransformValue::TranslateXTransformOperation) {
    192                     if (transformValue->length() > 2) {
    193                         CSSPrimitiveValue* thirdValue = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(2));
    194                         tz = thirdValue->computeLength<double>(conversionData);
    195                     }
    196                     if (transformValue->length() > 1) {
    197                         CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(1));
    198                         ty = convertToFloatLength(secondValue, conversionData);
    199                     }
    200                 }
    201             }
    202 
    203             operations.operations().append(TranslateTransformOperation::create(tx, ty, tz, getTransformOperationType(transformValue->operationType())));
    204             break;
    205         }
    206         case CSSTransformValue::RotateTransformOperation: {
    207             double angle = firstValue->computeDegrees();
    208             operations.operations().append(RotateTransformOperation::create(0, 0, 1, angle, getTransformOperationType(transformValue->operationType())));
    209             break;
    210         }
    211         case CSSTransformValue::RotateXTransformOperation:
    212         case CSSTransformValue::RotateYTransformOperation:
    213         case CSSTransformValue::RotateZTransformOperation: {
    214             double x = 0;
    215             double y = 0;
    216             double z = 0;
    217             double angle = firstValue->computeDegrees();
    218 
    219             if (transformValue->operationType() == CSSTransformValue::RotateXTransformOperation)
    220                 x = 1;
    221             else if (transformValue->operationType() == CSSTransformValue::RotateYTransformOperation)
    222                 y = 1;
    223             else
    224                 z = 1;
    225             operations.operations().append(RotateTransformOperation::create(x, y, z, angle, getTransformOperationType(transformValue->operationType())));
    226             break;
    227         }
    228         case CSSTransformValue::Rotate3DTransformOperation: {
    229             if (transformValue->length() < 4)
    230                 break;
    231             CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(1));
    232             CSSPrimitiveValue* thirdValue = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(2));
    233             CSSPrimitiveValue* fourthValue = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(3));
    234             double x = firstValue->getDoubleValue();
    235             double y = secondValue->getDoubleValue();
    236             double z = thirdValue->getDoubleValue();
    237             double angle = fourthValue->computeDegrees();
    238             operations.operations().append(RotateTransformOperation::create(x, y, z, angle, getTransformOperationType(transformValue->operationType())));
    239             break;
    240         }
    241         case CSSTransformValue::SkewTransformOperation:
    242         case CSSTransformValue::SkewXTransformOperation:
    243         case CSSTransformValue::SkewYTransformOperation: {
    244             double angleX = 0;
    245             double angleY = 0;
    246             double angle = firstValue->computeDegrees();
    247             if (transformValue->operationType() == CSSTransformValue::SkewYTransformOperation)
    248                 angleY = angle;
    249             else {
    250                 angleX = angle;
    251                 if (transformValue->operationType() == CSSTransformValue::SkewTransformOperation) {
    252                     if (transformValue->length() > 1) {
    253                         CSSPrimitiveValue* secondValue = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(1));
    254                         angleY = secondValue->computeDegrees();
    255                     }
    256                 }
    257             }
    258             operations.operations().append(SkewTransformOperation::create(angleX, angleY, getTransformOperationType(transformValue->operationType())));
    259             break;
    260         }
    261         case CSSTransformValue::MatrixTransformOperation: {
    262             if (transformValue->length() < 6)
    263                 break;
    264             double a = firstValue->getDoubleValue();
    265             double b = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(1))->getDoubleValue();
    266             double c = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(2))->getDoubleValue();
    267             double d = toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(3))->getDoubleValue();
    268             double e = zoomFactor * toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(4))->getDoubleValue();
    269             double f = zoomFactor * toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(5))->getDoubleValue();
    270             operations.operations().append(MatrixTransformOperation::create(a, b, c, d, e, f));
    271             break;
    272         }
    273         case CSSTransformValue::Matrix3DTransformOperation: {
    274             if (transformValue->length() < 16)
    275                 break;
    276             TransformationMatrix matrix(toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(0))->getDoubleValue(),
    277                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(1))->getDoubleValue(),
    278                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(2))->getDoubleValue(),
    279                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(3))->getDoubleValue(),
    280                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(4))->getDoubleValue(),
    281                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(5))->getDoubleValue(),
    282                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(6))->getDoubleValue(),
    283                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(7))->getDoubleValue(),
    284                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(8))->getDoubleValue(),
    285                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(9))->getDoubleValue(),
    286                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(10))->getDoubleValue(),
    287                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(11))->getDoubleValue(),
    288                 zoomFactor * toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(12))->getDoubleValue(),
    289                 zoomFactor * toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(13))->getDoubleValue(),
    290                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(14))->getDoubleValue(),
    291                 toCSSPrimitiveValue(transformValue->itemWithoutBoundsCheck(15))->getDoubleValue());
    292             operations.operations().append(Matrix3DTransformOperation::create(matrix));
    293             break;
    294         }
    295         case CSSTransformValue::PerspectiveTransformOperation: {
    296             double p;
    297             if (firstValue->isLength())
    298                 p = firstValue->computeLength<double>(conversionData);
    299             else {
    300                 // This is a quirk that should go away when 3d transforms are finalized.
    301                 double val = firstValue->getDoubleValue();
    302                 if (val < 0)
    303                     return false;
    304                 p = clampToPositiveInteger(val);
    305             }
    306 
    307             operations.operations().append(PerspectiveTransformOperation::create(p));
    308             break;
    309         }
    310         case CSSTransformValue::UnknownTransformOperation:
    311             ASSERT_NOT_REACHED();
    312             break;
    313         }
    314     }
    315 
    316     outOperations = operations;
    317     return true;
    318 }
    319 
    320 } // namespace WebCore
    321