/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/dom/WebKitCSSMatrix.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/WebKitCSSMatrixBinding.h" #include "mozilla/Preferences.h" #include "nsCSSParser.h" #include "nsStyleTransformMatrix.h" #include "RuleNodeCacheConditions.h" namespace mozilla { namespace dom { static const double sRadPerDegree = 2.0 * M_PI / 360.0; bool WebKitCSSMatrix::FeatureEnabled(JSContext* aCx, JSObject* aObj) { return Preferences::GetBool("layout.css.DOMMatrix.enabled", false) && Preferences::GetBool("layout.css.prefixes.webkit", false); } already_AddRefed WebKitCSSMatrix::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv) { RefPtr obj = new WebKitCSSMatrix(aGlobal.GetAsSupports()); return obj.forget(); } already_AddRefed WebKitCSSMatrix::Constructor(const GlobalObject& aGlobal, const nsAString& aTransformList, ErrorResult& aRv) { RefPtr obj = new WebKitCSSMatrix(aGlobal.GetAsSupports()); obj = obj->SetMatrixValue(aTransformList, aRv); return obj.forget(); } already_AddRefed WebKitCSSMatrix::Constructor(const GlobalObject& aGlobal, const DOMMatrixReadOnly& aOther, ErrorResult& aRv) { RefPtr obj = new WebKitCSSMatrix(aGlobal.GetAsSupports(), aOther); return obj.forget(); } JSObject* WebKitCSSMatrix::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { return WebKitCSSMatrixBinding::Wrap(aCx, this, aGivenProto); } WebKitCSSMatrix* WebKitCSSMatrix::SetMatrixValue(const nsAString& aTransformList, ErrorResult& aRv) { // An empty string is a no-op. if (aTransformList.IsEmpty()) { return this; } nsCSSValue value; nsCSSParser parser; bool parseSuccess = parser.ParseTransformProperty(aTransformList, true, value); if (!parseSuccess) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return nullptr; } // A value of "none" results in a 2D identity matrix. if (value.GetUnit() == eCSSUnit_None) { mMatrix3D = nullptr; mMatrix2D = new gfx::Matrix(); return this; } // A value other than a transform-list is a syntax error. if (value.GetUnit() != eCSSUnit_SharedList) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return nullptr; } RuleNodeCacheConditions dummy; nsStyleTransformMatrix::TransformReferenceBox dummyBox; bool contains3dTransform = false; gfx::Matrix4x4 transform = nsStyleTransformMatrix::ReadTransforms( value.GetSharedListValue()->mHead, nullptr, nullptr, dummy, dummyBox, nsPresContext::AppUnitsPerCSSPixel(), &contains3dTransform); if (!contains3dTransform) { mMatrix3D = nullptr; mMatrix2D = new gfx::Matrix(); SetA(transform._11); SetB(transform._12); SetC(transform._21); SetD(transform._22); SetE(transform._41); SetF(transform._42); } else { mMatrix3D = new gfx::Matrix4x4(transform); mMatrix2D = nullptr; } return this; } already_AddRefed WebKitCSSMatrix::Multiply(const WebKitCSSMatrix& other) const { RefPtr retval = new WebKitCSSMatrix(mParent, *this); retval->MultiplySelf(other); return retval.forget(); } already_AddRefed WebKitCSSMatrix::Inverse(ErrorResult& aRv) const { RefPtr retval = new WebKitCSSMatrix(mParent, *this); retval->InvertSelfThrow(aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } return retval.forget(); } WebKitCSSMatrix* WebKitCSSMatrix::InvertSelfThrow(ErrorResult& aRv) { if (mMatrix3D) { if (!mMatrix3D->Invert()) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; } } else if (!mMatrix2D->Invert()) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; } return this; } already_AddRefed WebKitCSSMatrix::Translate(double aTx, double aTy, double aTz) const { RefPtr retval = new WebKitCSSMatrix(mParent, *this); retval->TranslateSelf(aTx, aTy, aTz); return retval.forget(); } already_AddRefed WebKitCSSMatrix::Scale(double aScaleX, const Optional& aScaleY, double aScaleZ) const { double scaleX = aScaleX; double scaleY = aScaleY.WasPassed() ? aScaleY.Value() : scaleX; double scaleZ = aScaleZ; RefPtr retval = new WebKitCSSMatrix(mParent, *this); retval->ScaleNonUniformSelf(scaleX, scaleY, scaleZ); return retval.forget(); } already_AddRefed WebKitCSSMatrix::Rotate(double aRotX, const Optional& aRotY, const Optional& aRotZ) const { double rotX = aRotX; double rotY; double rotZ; if (!aRotY.WasPassed() && !aRotZ.WasPassed()) { rotZ = rotX; rotX = 0; rotY = 0; } else { rotY = aRotY.WasPassed() ? aRotY.Value() : 0; rotZ = aRotZ.WasPassed() ? aRotZ.Value() : 0; } RefPtr retval = new WebKitCSSMatrix(mParent, *this); retval->Rotate3dSelf(rotX, rotY, rotZ); return retval.forget(); } WebKitCSSMatrix* WebKitCSSMatrix::Rotate3dSelf(double aRotX, double aRotY, double aRotZ) { if (aRotX != 0 || aRotY != 0) { Ensure3DMatrix(); } if (mMatrix3D) { if (fmod(aRotZ, 360) != 0) { mMatrix3D->RotateZ(aRotZ * sRadPerDegree); } if (fmod(aRotY, 360) != 0) { mMatrix3D->RotateY(aRotY * sRadPerDegree); } if (fmod(aRotX, 360) != 0) { mMatrix3D->RotateX(aRotX * sRadPerDegree); } } else if (fmod(aRotZ, 360) != 0) { mMatrix2D->PreRotate(aRotZ * sRadPerDegree); } return this; } already_AddRefed WebKitCSSMatrix::RotateAxisAngle(double aX, double aY, double aZ, double aAngle) const { RefPtr retval = new WebKitCSSMatrix(mParent, *this); retval->RotateAxisAngleSelf(aX, aY, aZ, aAngle); return retval.forget(); } already_AddRefed WebKitCSSMatrix::SkewX(double aSx) const { RefPtr retval = new WebKitCSSMatrix(mParent, *this); retval->SkewXSelf(aSx); return retval.forget(); } already_AddRefed WebKitCSSMatrix::SkewY(double aSy) const { RefPtr retval = new WebKitCSSMatrix(mParent, *this); retval->SkewYSelf(aSy); return retval.forget(); } } // namespace dom } // namespace mozilla