diff options
author | Pale Moon <git-repo@palemoon.org> | 2014-08-18 15:09:26 +0200 |
---|---|---|
committer | Pale Moon <git-repo@palemoon.org> | 2014-08-18 15:09:26 +0200 |
commit | 7fc5e7c272599e9bfe9e5138cf8d842c4575d403 (patch) | |
tree | 1d007a7788bb9535283d1332b5a41dd645236699 | |
parent | d3f28b16be7d885ce4d85b620a2d97867b0d6468 (diff) | |
download | palemoon-gre-7fc5e7c272599e9bfe9e5138cf8d842c4575d403.tar.gz |
Implement CSS basic filters, CSS hue-rotate, CSS image-orientation, and EXIF reader.
50 files changed, 2094 insertions, 125 deletions
diff --git a/dom/locales/en-US/chrome/layout/css.properties b/dom/locales/en-US/chrome/layout/css.properties index 8a476f87d..8deeea9d3 100644 --- a/dom/locales/en-US/chrome/layout/css.properties +++ b/dom/locales/en-US/chrome/layout/css.properties @@ -137,3 +137,8 @@ PESupportsConditionExpectedCloseParen=Expected ')' while parsing supports condit PESupportsConditionExpectedStart2=Expected 'not', '(', or function while parsing supports condition but found '%1$S'. PESupportsConditionExpectedNot=Expected 'not' while parsing supports condition but found '%1$S'. PESupportsGroupRuleStart=Expected '{' to begin @supports rule but found '%1$S'. +PEFilterEOF=filter +PEExpectedNoneOrURL=Expected 'none' or URL but found '%1$S'. +PEExpectedNoneOrURLOrFilterFunction=Expected 'none', URL, or filter function but found '%1$S'. +PEExpectedNonnegativeNP=Expected non-negative number or percentage. +PEFilterFunctionArgumentsParsingError=Error in parsing arguments for filter function. diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index 727a2efdf..43e9d2560 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -817,7 +817,6 @@ gfxPlatform::CreateOffscreenDrawTarget(const IntSize& aSize, SurfaceFormat aForm return CreateDrawTargetForBackend(mFallbackCanvasBackend, aSize, aFormat); } - RefPtr<DrawTarget> gfxPlatform::CreateDrawTargetForData(unsigned char* aData, const IntSize& aSize, int32_t aStride, SurfaceFormat aFormat) { diff --git a/image/decoders/EXIF.cpp b/image/decoders/EXIF.cpp new file mode 100644 index 000000000..1c7abd237 --- /dev/null +++ b/image/decoders/EXIF.cpp @@ -0,0 +1,321 @@ +/* -*- Mode: C++; tab-width: 2; 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 "EXIF.h" + +namespace mozilla { +namespace image { + +// Section references in this file refer to the EXIF v2.3 standard, also known +// as CIPA DC-008-Translation-2010. + +// See Section 4.6.4, Table 4. +// Typesafe enums are intentionally not used here since we're comparing to raw +// integers produced by parsing. +enum EXIFTag +{ + OrientationTag = 0x112, +}; + +// See Section 4.6.2. +enum EXIFType +{ + ByteType = 1, + ASCIIType = 2, + ShortType = 3, + LongType = 4, + RationalType = 5, + UndefinedType = 7, + SignedLongType = 9, + SignedRational = 10, +}; + +static const char* EXIFHeader = "Exif\0\0"; +static const uint32_t EXIFHeaderLength = 6; + +///////////////////////////////////////////////////////////// +// Parse EXIF data, typically found in a JPEG's APP1 segment. +///////////////////////////////////////////////////////////// +EXIFData +EXIFParser::ParseEXIF(const uint8_t* aData, const uint32_t aLength) +{ + if (!Initialize(aData, aLength)) + return EXIFData(); + + if (!ParseEXIFHeader()) + return EXIFData(); + + uint32_t offsetIFD; + if (!ParseTIFFHeader(offsetIFD)) + return EXIFData(); + + JumpTo(offsetIFD); + + Orientation orientation; + if (!ParseIFD0(orientation)) + return EXIFData(); + + // We only care about orientation at this point, so we don't bother with the + // other IFDs. If we got this far we're done. + return EXIFData(orientation); +} + +///////////////////////////////////////////////////////// +// Parse the EXIF header. (Section 4.7.2, Figure 30) +///////////////////////////////////////////////////////// +bool +EXIFParser::ParseEXIFHeader() +{ + return MatchString(EXIFHeader, EXIFHeaderLength); +} + +///////////////////////////////////////////////////////// +// Parse the TIFF header. (Section 4.5.2, Table 1) +///////////////////////////////////////////////////////// +bool +EXIFParser::ParseTIFFHeader(uint32_t& aIFD0OffsetOut) +{ + // Determine byte order. + if (MatchString("MM", 2)) + mByteOrder = ByteOrder::BigEndian; + else if (MatchString("II", 2)) + mByteOrder = ByteOrder::LittleEndian; + else + return false; + + if (!MatchString("\0*", 2)) + return false; + + // Determine offset of the 0th IFD. (It shouldn't be greater than 64k, which + // is the maximum size of the entry APP1 segment.) + uint32_t ifd0Offset; + if (!ReadUInt32(ifd0Offset) || ifd0Offset > 64 * 1024) + return false; + + // The IFD offset is relative to the beginning of the TIFF header, which + // begins after the EXIF header, so we need to increase the offset + // appropriately. + aIFD0OffsetOut = ifd0Offset + EXIFHeaderLength; + return true; +} + +///////////////////////////////////////////////////////// +// Parse the entries in IFD0. (Section 4.6.2) +///////////////////////////////////////////////////////// +bool +EXIFParser::ParseIFD0(Orientation& aOrientationOut) +{ + uint16_t entryCount; + if (!ReadUInt16(entryCount)) + return false; + + for (uint16_t entry = 0 ; entry < entryCount ; ++entry) { + // Read the fields of the entry. + uint16_t tag; + if (!ReadUInt16(tag)) + return false; + + // Right now, we only care about orientation, so we immediately skip to the + // next entry if we find anything else. + if (tag != OrientationTag) { + Advance(10); + continue; + } + + uint16_t type; + if (!ReadUInt16(type)) + return false; + + uint32_t count; + if (!ReadUInt32(count)) + return false; + + // We should have an orientation value here; go ahead and parse it. + Orientation orientation; + if (!ParseOrientation(type, count, aOrientationOut)) + return false; + + // Since the orientation is all we care about, we're done. + return true; + } + + // We didn't find an orientation field in the IFD. That's OK; we assume the + // default orientation in that case. + aOrientationOut = Orientation(); + return true; +} + +bool +EXIFParser::ParseOrientation(uint16_t aType, uint32_t aCount, Orientation& aOut) +{ + // Sanity check the type and count. + if (aType != ShortType || aCount != 1) + return false; + + uint16_t value; + if (!ReadUInt16(value)) + return false; + + switch (value) { + case 1: aOut = Orientation(Angle::D0, Flip::Unflipped); break; + case 2: aOut = Orientation(Angle::D0, Flip::Horizontal); break; + case 3: aOut = Orientation(Angle::D180, Flip::Unflipped); break; + case 4: aOut = Orientation(Angle::D180, Flip::Horizontal); break; + case 5: aOut = Orientation(Angle::D90, Flip::Horizontal); break; + case 6: aOut = Orientation(Angle::D90, Flip::Unflipped); break; + case 7: aOut = Orientation(Angle::D270, Flip::Horizontal); break; + case 8: aOut = Orientation(Angle::D270, Flip::Unflipped); break; + default: return false; + } + + // This is a 32-bit field, but the orientation value only occupies the first + // 16 bits. We need to advance another 16 bits to consume the entire field. + Advance(2); + return true; +} + +bool +EXIFParser::Initialize(const uint8_t* aData, const uint32_t aLength) +{ + if (aData == nullptr) + return false; + + // An APP1 segment larger than 64k violates the JPEG standard. + if (aLength > 64 * 1024) + return false; + + mStart = mCurrent = aData; + mLength = mRemainingLength = aLength; + mByteOrder = ByteOrder::Unknown; + return true; +} + +void +EXIFParser::Advance(const uint32_t aDistance) +{ + if (mRemainingLength >= aDistance) { + mCurrent += aDistance; + mRemainingLength -= aDistance; + } else { + mCurrent = mStart; + mRemainingLength = 0; + } +} + +void +EXIFParser::JumpTo(const uint32_t aOffset) +{ + if (mLength >= aOffset) { + mCurrent = mStart + aOffset; + mRemainingLength = mLength - aOffset; + } else { + mCurrent = mStart; + mRemainingLength = 0; + } +} + +bool +EXIFParser::MatchString(const char* aString, const uint32_t aLength) +{ + if (mRemainingLength < aLength) + return false; + + for (uint32_t i = 0 ; i < aLength ; ++i) { + if (mCurrent[i] != aString[i]) + return false; + } + + Advance(aLength); + return true; +} + +bool +EXIFParser::MatchUInt16(const uint16_t aValue) +{ + if (mRemainingLength < 2) + return false; + + const uint8_t low = aValue & 0xFF; + const uint8_t high = aValue >> 8; + + bool matched; + switch (mByteOrder) { + case ByteOrder::LittleEndian: + matched = mCurrent[0] == low && mCurrent[1] == high; + break; + case ByteOrder::BigEndian: + matched = mCurrent[0] == high && mCurrent[1] == low; + break; + default: + NS_NOTREACHED("Should know the byte order by now"); + matched = false; + } + + if (matched) + Advance(2); + + return matched; +} + +bool +EXIFParser::ReadUInt16(uint16_t& aValue) +{ + if (mRemainingLength < 2) + return false; + + bool matched = true; + switch (mByteOrder) { + case ByteOrder::LittleEndian: + aValue = (uint32_t(mCurrent[1]) << 8) | + (uint32_t(mCurrent[0])); + break; + case ByteOrder::BigEndian: + aValue = (uint32_t(mCurrent[0]) << 8) | + (uint32_t(mCurrent[1])); + break; + default: + NS_NOTREACHED("Should know the byte order by now"); + matched = false; + } + + if (matched) + Advance(2); + + return matched; +} + +bool +EXIFParser::ReadUInt32(uint32_t& aValue) +{ + if (mRemainingLength < 4) + return false; + + bool matched = true; + switch (mByteOrder) { + case ByteOrder::LittleEndian: + aValue = (uint32_t(mCurrent[3]) << 24) | + (uint32_t(mCurrent[2]) << 16) | + (uint32_t(mCurrent[1]) << 8) | + (uint32_t(mCurrent[0])); + break; + case ByteOrder::BigEndian: + aValue = (uint32_t(mCurrent[0]) << 24) | + (uint32_t(mCurrent[1]) << 16) | + (uint32_t(mCurrent[2]) << 8) | + (uint32_t(mCurrent[3])); + break; + default: + NS_NOTREACHED("Should know the byte order by now"); + matched = false; + } + + if (matched) + Advance(4); + + return matched; +} + +} // namespace image +} // namespace mozilla diff --git a/image/decoders/EXIF.h b/image/decoders/EXIF.h new file mode 100644 index 000000000..62a83cedb --- /dev/null +++ b/image/decoders/EXIF.h @@ -0,0 +1,76 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#ifndef MOZILLA_IMAGELIB_EXIF_H_ +#define MOZILLA_IMAGELIB_EXIF_H_ + +#include <stdint.h> +#include "mozilla/TypedEnum.h" +#include "nsDebug.h" + +#include "Orientation.h" + +namespace mozilla { +namespace image { + +MOZ_BEGIN_ENUM_CLASS(ByteOrder, uint8_t) + Unknown, + LittleEndian, + BigEndian +MOZ_END_ENUM_CLASS(ByteOrder) + +struct EXIFData +{ + EXIFData() { } + explicit EXIFData(Orientation aOrientation) : orientation(aOrientation) { } + + const Orientation orientation; +}; + +class EXIFParser +{ +public: + static EXIFData + Parse(const uint8_t* aData, const uint32_t aLength) + { + EXIFParser parser; + return parser.ParseEXIF(aData, aLength); + } + +private: + EXIFParser() + : mStart(nullptr) + , mCurrent(nullptr) + , mLength(0) + , mRemainingLength(0) + , mByteOrder(ByteOrder::Unknown) + { } + + EXIFData ParseEXIF(const uint8_t* aData, const uint32_t aLength); + bool ParseEXIFHeader(); + bool ParseTIFFHeader(uint32_t& aIFD0OffsetOut); + bool ParseIFD0(Orientation& aOrientationOut); + bool ParseOrientation(uint16_t aType, uint32_t aCount, Orientation& aOut); + + bool Initialize(const uint8_t* aData, const uint32_t aLength); + void Advance(const uint32_t aDistance); + void JumpTo(const uint32_t aOffset); + + bool MatchString(const char* aString, const uint32_t aLength); + bool MatchUInt16(const uint16_t aValue); + bool ReadUInt16(uint16_t& aOut); + bool ReadUInt32(uint32_t& aOut); + + const uint8_t* mStart; + const uint8_t* mCurrent; + uint32_t mLength; + uint32_t mRemainingLength; + ByteOrder mByteOrder; +}; + +} +} + +#endif diff --git a/image/decoders/moz.build b/image/decoders/moz.build index 8cd502563..0aa6482d5 100644 --- a/image/decoders/moz.build +++ b/image/decoders/moz.build @@ -26,6 +26,7 @@ elif toolkit == 'android': MODULE = 'imgdecoders' CPP_SOURCES += [ + 'EXIF.cpp', 'nsBMPDecoder.cpp', 'nsGIFDecoder2.cpp', 'nsICODecoder.cpp', diff --git a/image/decoders/nsJPEGDecoder.cpp b/image/decoders/nsJPEGDecoder.cpp index 24b9fd347..9ffed3799 100644 --- a/image/decoders/nsJPEGDecoder.cpp +++ b/image/decoders/nsJPEGDecoder.cpp @@ -6,6 +6,8 @@ #include "ImageLogging.h" #include "nsJPEGDecoder.h" +#include "Orientation.h" +#include "EXIF.h" #include "nsIInputStream.h" @@ -240,7 +242,7 @@ nsJPEGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrateg } // Post our size to the superclass - PostSize(mInfo.image_width, mInfo.image_height); + PostSize(mInfo.image_width, mInfo.image_height, ReadOrientationFromEXIF()); if (HasError()) { // Setting the size led to an error. mState = JPEG_ERROR; @@ -538,6 +540,27 @@ nsJPEGDecoder::WriteInternal(const char *aBuffer, uint32_t aCount, DecodeStrateg return; } +Orientation +nsJPEGDecoder::ReadOrientationFromEXIF() +{ + jpeg_saved_marker_ptr marker; + + // Locate the APP1 marker, where EXIF data is stored, in the marker list. + for (marker = mInfo.marker_list ; marker != nullptr ; marker = marker->next) { + if (marker->marker == JPEG_APP0 + 1) + break; + } + + // If we're at the end of the list, there's no EXIF data. + if (!marker) + return Orientation(); + + // Extract the orientation information. + EXIFData exif = EXIFParser::Parse(marker->data, + static_cast<uint32_t>(marker->data_length)); + return exif.orientation; +} + void nsJPEGDecoder::NotifyDone() { diff --git a/image/decoders/nsJPEGDecoder.h b/image/decoders/nsJPEGDecoder.h index 82ec3bbe0..e706f0b9d 100644 --- a/image/decoders/nsJPEGDecoder.h +++ b/image/decoders/nsJPEGDecoder.h @@ -47,6 +47,7 @@ typedef enum { } jstate; class RasterImage; +class Orientation; class nsJPEGDecoder : public Decoder { @@ -62,6 +63,7 @@ public: void NotifyDone(); protected: + Orientation ReadOrientationFromEXIF(); void OutputScanlines(bool* suspend); public: diff --git a/image/public/imgIContainer.idl b/image/public/imgIContainer.idl index 4d20df288..85175d5a0 100644 --- a/image/public/imgIContainer.idl +++ b/image/public/imgIContainer.idl @@ -31,6 +31,12 @@ class TimeStamp; class SVGImageContext; } +namespace mozilla { +namespace image { +class Orientation; +} +} + %} [ptr] native gfxImageSurface(gfxImageSurface); @@ -46,6 +52,7 @@ native nsSize(nsSize); [ptr] native nsIFrame(nsIFrame); [ptr] native ImageContainer(mozilla::layers::ImageContainer); [ptr] native LayerManager(mozilla::layers::LayerManager); +native Orientation(mozilla::image::Orientation); [ref] native TimeStamp(mozilla::TimeStamp); [ptr] native SVGImageContext(mozilla::SVGImageContext); @@ -57,7 +64,7 @@ native nsSize(nsSize); * * Internally, imgIContainer also manages animation of images. */ -[scriptable, builtinclass, uuid(f9029a03-758c-4047-a1f3-4b2e51e47363)] +[scriptable, builtinclass, uuid(73340b79-e3ae-4f02-97d0-822db78017e5)] interface imgIContainer : nsISupports { /** @@ -303,6 +310,12 @@ interface imgIContainer : nsISupports [notxpcom] float getFrameIndex(in uint32_t aWhichFrame); /* + * Returns the inherent orientation of the image, as described in the image's + * metadata (e.g. EXIF). + */ + [notxpcom] Orientation getOrientation(); + + /* * Returns the delay, in ms, between the first and second frame. If this * returns 0, there is no delay between first and second frame (i.e., this * image could render differently whenever it draws). diff --git a/image/src/ClippedImage.cpp b/image/src/ClippedImage.cpp index f42a2e030..1b788a1dd 100644 --- a/image/src/ClippedImage.cpp +++ b/image/src/ClippedImage.cpp @@ -9,6 +9,7 @@ #include "mozilla/dom/SVGSVGElement.h" #include "ClippedImage.h" +#include "Orientation.h" using mozilla::layers::LayerManager; using mozilla::layers::ImageContainer; @@ -411,5 +412,13 @@ ClippedImage::RequestDiscard() return InnerImage()->RequestDiscard(); } +NS_IMETHODIMP_(Orientation) +ClippedImage::GetOrientation() +{ + // XXX(seth): This should not actually be here; this is just to work around a + // what appears to be a bug in MSVC's linker. + return InnerImage()->GetOrientation(); +} + } // namespace image } // namespace mozilla diff --git a/image/src/ClippedImage.h b/image/src/ClippedImage.h index 74fb12e5f..f6862bf46 100644 --- a/image/src/ClippedImage.h +++ b/image/src/ClippedImage.h @@ -49,6 +49,7 @@ public: uint32_t aWhichFrame, uint32_t aFlags) MOZ_OVERRIDE; NS_IMETHOD RequestDiscard() MOZ_OVERRIDE; + NS_IMETHOD_(Orientation) GetOrientation() MOZ_OVERRIDE; protected: ClippedImage(Image* aImage, nsIntRect aClip); diff --git a/image/src/Decoder.cpp b/image/src/Decoder.cpp index 2e3b36f28..c836b1fb9 100644 --- a/image/src/Decoder.cpp +++ b/image/src/Decoder.cpp @@ -270,7 +270,12 @@ Decoder::FlushInvalidations() void Decoder::SetSizeOnImage() { - mImage.SetSize(mImageMetadata.GetWidth(), mImageMetadata.GetHeight()); + MOZ_ASSERT(mImageMetadata.HasSize(), "Should have size"); + MOZ_ASSERT(mImageMetadata.HasOrientation(), "Should have orientation"); + + mImage.SetSize(mImageMetadata.GetWidth(), + mImageMetadata.GetHeight(), + mImageMetadata.GetOrientation()); } /* @@ -286,14 +291,16 @@ void Decoder::FinishInternal() { } */ void -Decoder::PostSize(int32_t aWidth, int32_t aHeight) +Decoder::PostSize(int32_t aWidth, + int32_t aHeight, + Orientation aOrientation /* = Orientation()*/) { // Validate NS_ABORT_IF_FALSE(aWidth >= 0, "Width can't be negative!"); NS_ABORT_IF_FALSE(aHeight >= 0, "Height can't be negative!"); // Tell the image - mImageMetadata.SetSize(aWidth, aHeight); + mImageMetadata.SetSize(aWidth, aHeight, aOrientation); // Notify the observer if (mObserver) diff --git a/image/src/Decoder.h b/image/src/Decoder.h index 285cf59b2..57198f1c6 100644 --- a/image/src/Decoder.h +++ b/image/src/Decoder.h @@ -11,6 +11,7 @@ #include "mozilla/RefPtr.h" #include "DecodeStrategy.h" #include "ImageMetadata.h" +#include "Orientation.h" namespace mozilla { namespace image { @@ -180,7 +181,9 @@ protected: // Called by decoders when they determine the size of the image. Informs // the image of its size and sends notifications. - void PostSize(int32_t aWidth, int32_t aHeight); + void PostSize(int32_t aWidth, + int32_t aHeight, + Orientation aOrientation = Orientation()); // Called by decoders when they begin a frame. Informs the image, sends // notifications, and does internal book-keeping. diff --git a/image/src/ImageMetadata.h b/image/src/ImageMetadata.h index c5c5e4c06..b37e646d1 100644 --- a/image/src/ImageMetadata.h +++ b/image/src/ImageMetadata.h @@ -7,6 +7,7 @@ #include "mozilla/StandardInteger.h" #include "mozilla/Util.h" #include "nsSize.h" +#include "Orientation.h" namespace mozilla { namespace image { @@ -42,15 +43,18 @@ public: mIsNonPremultiplied = nonPremult; } - void SetSize(int32_t width, int32_t height) + void SetSize(int32_t width, int32_t height, Orientation orientation) { mSize.construct(nsIntSize(width, height)); + mOrientation.construct(orientation); } bool HasSize() const { return !mSize.empty(); } + bool HasOrientation() const { return !mOrientation.empty(); } int32_t GetWidth() const { return mSize.ref().width; } int32_t GetHeight() const { return mSize.ref().height; } + Orientation GetOrientation() const { return mOrientation.ref(); } private: // The hotspot found on cursors, or -1 if none was found. @@ -61,6 +65,7 @@ private: int32_t mLoopCount; Maybe<nsIntSize> mSize; + Maybe<Orientation> mOrientation; bool mIsNonPremultiplied; }; diff --git a/image/src/ImageOps.cpp b/image/src/ImageOps.cpp index add83a89f..e7107ac1a 100644 --- a/image/src/ImageOps.cpp +++ b/image/src/ImageOps.cpp @@ -7,6 +7,7 @@ #include "imgIContainer.h" #include "ClippedImage.h" #include "FrozenImage.h" +#include "OrientedImage.h" #include "Image.h" #include "ImageOps.h" @@ -44,5 +45,20 @@ ImageOps::Clip(imgIContainer* aImage, nsIntRect aClip) return clippedImage.forget(); } +/* static */ already_AddRefed<Image> +ImageOps::Orient(Image* aImage, Orientation aOrientation) +{ + nsRefPtr<Image> orientedImage = new OrientedImage(aImage, aOrientation); + return orientedImage.forget(); +} + +/* static */ already_AddRefed<imgIContainer> +ImageOps::Orient(imgIContainer* aImage, Orientation aOrientation) +{ + nsCOMPtr<imgIContainer> orientedImage = + new OrientedImage(static_cast<Image*>(aImage), aOrientation); + return orientedImage.forget(); +} + } // namespace image } // namespace mozilla diff --git a/image/src/ImageOps.h b/image/src/ImageOps.h index e68571f19..93b99e1a0 100644 --- a/image/src/ImageOps.h +++ b/image/src/ImageOps.h @@ -9,6 +9,7 @@ #include "nsCOMPtr.h" #include "nsRect.h" +#include "Orientation.h" class imgIContainer; @@ -38,6 +39,16 @@ public: static already_AddRefed<Image> Clip(Image* aImage, nsIntRect aClip); static already_AddRefed<imgIContainer> Clip(imgIContainer* aImage, nsIntRect aClip); + /** + * Creates a version of an existing image which is rotated and/or flipped to + * the specified orientation. + * + * @param aImage The existing image. + * @param aOrientation The desired orientation. + */ + static already_AddRefed<Image> Orient(Image* aImage, Orientation aOrientation); + static already_AddRefed<imgIContainer> Orient(imgIContainer* aImage, Orientation aOrientation); + private: // This is a static utility class, so disallow instantiation. virtual ~ImageOps() = 0; diff --git a/image/src/ImageWrapper.cpp b/image/src/ImageWrapper.cpp index 60e5f79a6..0d17bee77 100644 --- a/image/src/ImageWrapper.cpp +++ b/image/src/ImageWrapper.cpp @@ -4,6 +4,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ImageWrapper.h" +#include "Orientation.h" using mozilla::layers::LayerManager; using mozilla::layers::ImageContainer; @@ -165,6 +166,12 @@ ImageWrapper::GetIntrinsicRatio(nsSize* aSize) return mInnerImage->GetIntrinsicRatio(aSize); } +NS_IMETHODIMP_(Orientation) +ImageWrapper::GetOrientation() +{ + return mInnerImage->GetOrientation(); +} + NS_IMETHODIMP ImageWrapper::GetType(uint16_t* aType) { diff --git a/image/src/Orientation.h b/image/src/Orientation.h new file mode 100644 index 000000000..c2fa82e6a --- /dev/null +++ b/image/src/Orientation.h @@ -0,0 +1,62 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#ifndef MOZILLA_IMAGELIB_ORIENTATION_H_ +#define MOZILLA_IMAGELIB_ORIENTATION_H_ + +#include <stdint.h> +#include "mozilla/TypedEnum.h" + +namespace mozilla { +namespace image { + +MOZ_BEGIN_ENUM_CLASS(Angle, uint8_t) + D0, + D90, + D180, + D270 +MOZ_END_ENUM_CLASS(Angle) + +MOZ_BEGIN_ENUM_CLASS(Flip, uint8_t) + Unflipped, + Horizontal +MOZ_END_ENUM_CLASS(Flip) + +/** + * A struct that describes an image's orientation as a rotation optionally + * followed by a reflection. This may be used to be indicate an image's inherent + * orientation or a desired orientation for the image. + */ +struct Orientation +{ + Orientation(Angle aRotation = Angle::D0, Flip mFlip = Flip::Unflipped) + : rotation(aRotation) + , flip(mFlip) + { } + + bool IsIdentity() const { + return (rotation == Angle::D0) && (flip == Flip::Unflipped); + } + + bool SwapsWidthAndHeight() const { + return (rotation == Angle::D90) || (rotation == Angle::D270); + } + + bool operator==(const Orientation& aOther) const { + return (rotation == aOther.rotation) && (flip == aOther.flip); + } + + bool operator!=(const Orientation& aOther) const { + return !(*this == aOther); + } + + Angle rotation; + Flip flip; +}; + +} +} + +#endif diff --git a/image/src/OrientedImage.cpp b/image/src/OrientedImage.cpp new file mode 100644 index 000000000..6fe26a7a0 --- /dev/null +++ b/image/src/OrientedImage.cpp @@ -0,0 +1,254 @@ +/* -*- Mode: C++; tab-width: 2; 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 <algorithm> + +#include "gfxDrawable.h" +#include "gfxPlatform.h" +#include "gfxUtils.h" + +#include "OrientedImage.h" + +using std::swap; +using mozilla::layers::LayerManager; +using mozilla::layers::ImageContainer; + +namespace mozilla { +namespace image { + +NS_IMPL_ISUPPORTS1(OrientedImage, imgIContainer) + +nsIntRect +OrientedImage::FrameRect(uint32_t aWhichFrame) +{ + if (mOrientation.SwapsWidthAndHeight()) { + nsIntRect innerRect = InnerImage()->FrameRect(aWhichFrame); + return nsIntRect(innerRect.x, innerRect.y, innerRect.height, innerRect.width); + } else { + return InnerImage()->FrameRect(aWhichFrame); + } +} + +NS_IMETHODIMP +OrientedImage::GetWidth(int32_t* aWidth) +{ + if (mOrientation.SwapsWidthAndHeight()) { + return InnerImage()->GetHeight(aWidth); + } else { + return InnerImage()->GetWidth(aWidth); + } +} + +NS_IMETHODIMP +OrientedImage::GetHeight(int32_t* aHeight) +{ + if (mOrientation.SwapsWidthAndHeight()) { + return InnerImage()->GetWidth(aHeight); + } else { + return InnerImage()->GetHeight(aHeight); + } +} + +NS_IMETHODIMP +OrientedImage::GetIntrinsicSize(nsSize* aSize) +{ + nsresult rv = InnerImage()->GetIntrinsicSize(aSize); + + if (mOrientation.SwapsWidthAndHeight()) { + swap(aSize->width, aSize->height); + } + + return rv; +} + +NS_IMETHODIMP +OrientedImage::GetIntrinsicRatio(nsSize* aRatio) +{ + nsresult rv = InnerImage()->GetIntrinsicRatio(aRatio); + + if (mOrientation.SwapsWidthAndHeight()) { + swap(aRatio->width, aRatio->height); + } + + return rv; +} + +NS_IMETHODIMP +OrientedImage::GetFrame(uint32_t aWhichFrame, + uint32_t aFlags, + gfxASurface** _retval) +{ + nsresult rv; + + if (mOrientation.IsIdentity()) { + return InnerImage()->GetFrame(aWhichFrame, aFlags, _retval); + } + + // Get the underlying dimensions. + int32_t width, height; + if (mOrientation.SwapsWidthAndHeight()) { + rv = InnerImage()->GetWidth(&height); + rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&width); + } else { + rv = InnerImage()->GetWidth(&width); + rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&height); + } + NS_ENSURE_SUCCESS(rv, rv); + + // Determine an appropriate format for the surface. + gfx::SurfaceFormat surfaceFormat; + gfxImageFormat imageFormat; + if (InnerImage()->FrameIsOpaque(aWhichFrame)) { + surfaceFormat = gfx::FORMAT_B8G8R8X8; + imageFormat = gfxASurface::ImageFormatARGB32; + } else { + surfaceFormat = gfx::FORMAT_B8G8R8A8; + imageFormat = gfxASurface::ImageFormatARGB32; + } + + // Create a surface to draw into. + mozilla::RefPtr<mozilla::gfx::DrawTarget> target; + target = gfxPlatform::GetPlatform()-> + CreateOffscreenDrawTarget(gfx::IntSize(width, height), surfaceFormat); + nsRefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()-> + GetThebesSurfaceForDrawTarget(target); + + // Create our drawable. + nsRefPtr<gfxASurface> innerSurface; + rv = InnerImage()->GetFrame(aWhichFrame, aFlags, getter_AddRefs(innerSurface)); + NS_ENSURE_SUCCESS(rv, rv); + nsRefPtr<gfxDrawable> drawable = + new gfxSurfaceDrawable(innerSurface, gfxIntSize(width, height)); + + // Draw. + nsRefPtr<gfxContext> ctx = new gfxContext(surface); + gfxRect imageRect(0, 0, width, height); + gfxUtils::DrawPixelSnapped(ctx, drawable, OrientationMatrix(nsIntSize(width, height)), + imageRect, imageRect, imageRect, imageRect, + imageFormat, gfxPattern::FILTER_FAST); + + surface.forget(_retval); + return NS_OK; +} + +NS_IMETHODIMP +OrientedImage::GetImageContainer(LayerManager* aManager, ImageContainer** _retval) +{ + // XXX(seth): We currently don't have a way of orienting the result of + // GetImageContainer. We work around this by always returning null, but if it + // ever turns out that OrientedImage is widely used on codepaths that can + // actually benefit from GetImageContainer, it would be a good idea to fix + // that method for performance reasons. + + if (mOrientation.IsIdentity()) { + return InnerImage()->GetImageContainer(aManager, _retval); + } + + *_retval = nullptr; + return NS_OK; +} + +gfxMatrix +OrientedImage::OrientationMatrix(const nsIntSize& aViewportSize) +{ + gfxMatrix matrix; + + int32_t width, height; + if (InnerImage()->GetType() == imgIContainer::TYPE_VECTOR) { + width = mOrientation.SwapsWidthAndHeight() ? aViewportSize.height : aViewportSize.width; + height = mOrientation.SwapsWidthAndHeight() ? aViewportSize.width : aViewportSize.height; + } else { + nsresult rv = InnerImage()->GetWidth(&width); + rv = NS_FAILED(rv) ? rv : InnerImage()->GetHeight(&height); + if (NS_FAILED(rv)) { + // Fall back to identity if the width and height aren't available. + return matrix; + } + } + + // Apply reflection, if present. (This logically happens second, but we + // apply it first because these transformations are all premultiplied.) A + // translation is necessary to place the image back in the first quadrant. + switch (mOrientation.flip) { + case Flip::Unflipped: + break; + case Flip::Horizontal: + if (mOrientation.SwapsWidthAndHeight()) { + // In the coordinate system after the rotation the reflection we want + // is across the x-axis rather than the y-axis. + matrix.Translate(gfxPoint(0, height)); + matrix.Scale(1.0, -1.0); + } else { + matrix.Translate(gfxPoint(width, 0)); + matrix.Scale(-1.0, 1.0); + } + break; + default: + MOZ_ASSERT(false, "Invalid flip value"); + } + + // Apply rotation, if present. Again, a translation is used to place the + // image back in the first quadrant. + switch (mOrientation.rotation) { + case Angle::D0: + break; + case Angle::D90: + matrix.Translate(gfxPoint(0, height)); + matrix.Rotate(-0.5 * M_PI); + break; + case Angle::D180: + matrix.Translate(gfxPoint(width, height)); + matrix.Rotate(-1.0 * M_PI); + break; + case Angle::D270: + matrix.Translate(gfxPoint(width, 0)); + matrix.Rotate(-1.5 * M_PI); + break; + default: + MOZ_ASSERT(false, "Invalid rotation value"); + } + + return matrix; +} + +NS_IMETHODIMP +OrientedImage::Draw(gfxContext* aContext, + gfxPattern::GraphicsFilter aFilter, + const gfxMatrix& aUserSpaceToImageSpace, + const gfxRect& aFill, + const nsIntRect& aSubimage, + const nsIntSize& aViewportSize, + const SVGImageContext* aSVGContext, + uint32_t aWhichFrame, + uint32_t aFlags) +{ + if (mOrientation.IsIdentity()) { + return InnerImage()->Draw(aContext, aFilter, aUserSpaceToImageSpace, + aFill, aSubimage, aViewportSize, aSVGContext, + aWhichFrame, aFlags); + } + + // Update the matrix to reorient the image. + gfxMatrix matrix(OrientationMatrix(aViewportSize)); + gfxMatrix userSpaceToImageSpace(aUserSpaceToImageSpace * matrix); + + // Update the subimage. + gfxRect gfxSubimage(matrix.TransformBounds(gfxRect(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height))); + nsIntRect subimage(gfxSubimage.x, gfxSubimage.y, gfxSubimage.width, gfxSubimage.height); + + // Update the viewport size. (This could be done using TransformBounds but + // since it's only a size a swap is enough.) + nsIntSize viewportSize(aViewportSize); + if (mOrientation.SwapsWidthAndHeight()) { + swap(viewportSize.width, viewportSize.height); + } + + return InnerImage()->Draw(aContext, aFilter, userSpaceToImageSpace, + aFill, subimage, viewportSize, aSVGContext, + aWhichFrame, aFlags); +} + +} // namespace image +} // namespace mozilla diff --git a/image/src/OrientedImage.h b/image/src/OrientedImage.h new file mode 100644 index 000000000..3bb2cee72 --- /dev/null +++ b/image/src/OrientedImage.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#ifndef MOZILLA_IMAGELIB_ORIENTEDIMAGE_H_ +#define MOZILLA_IMAGELIB_ORIENTEDIMAGE_H_ + +#include "ImageWrapper.h" +#include "Orientation.h" + +namespace mozilla { +namespace image { + +/** + * An Image wrapper that rotates and/or flips an image according to a specified + * Orientation. + * + * XXX(seth): There a known (performance, not correctness) issue with + * GetImageContainer. See the comments for that method for more information. + */ +class OrientedImage : public ImageWrapper +{ +public: + NS_DECL_ISUPPORTS + + virtual ~OrientedImage() { } + + virtual nsIntRect FrameRect(uint32_t aWhichFrame) MOZ_OVERRIDE; + + NS_IMETHOD GetWidth(int32_t* aWidth) MOZ_OVERRIDE; + NS_IMETHOD GetHeight(int32_t* aHeight) MOZ_OVERRIDE; + NS_IMETHOD GetIntrinsicSize(nsSize* aSize) MOZ_OVERRIDE; + NS_IMETHOD GetIntrinsicRatio(nsSize* aRatio) MOZ_OVERRIDE; + NS_IMETHOD GetFrame(uint32_t aWhichFrame, + uint32_t aFlags, + gfxASurface** _retval) MOZ_OVERRIDE; + NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager, + mozilla::layers::ImageContainer** _retval) MOZ_OVERRIDE; + NS_IMETHOD Draw(gfxContext* aContext, + gfxPattern::GraphicsFilter aFilter, + const gfxMatrix& aUserSpaceToImageSpace, + const gfxRect& aFill, + const nsIntRect& aSubimage, + const nsIntSize& aViewportSize, + const SVGImageContext* aSVGContext, + uint32_t aWhichFrame, + uint32_t aFlags) MOZ_OVERRIDE; + +protected: + OrientedImage(Image* aImage, Orientation aOrientation) + : ImageWrapper(aImage) + , mOrientation(aOrientation) + { } + + gfxMatrix OrientationMatrix(const nsIntSize& aViewportSize); + +private: + Orientation mOrientation; + + friend class ImageOps; +}; + +} // namespace image +} // namespace mozilla + +#endif // MOZILLA_IMAGELIB_ORIENTEDIMAGE_H_ diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index fdd965479..94da72e1b 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -773,6 +773,12 @@ RasterImage::GetIntrinsicRatio(nsSize* aRatio) return NS_OK; } +NS_IMETHODIMP_(Orientation) +RasterImage::GetOrientation() +{ + return mOrientation; +} + //****************************************************************************** /* unsigned short GetType(); */ NS_IMETHODIMP @@ -1352,7 +1358,7 @@ RasterImage::ApplyDecodeFlags(uint32_t aNewFlags) } nsresult -RasterImage::SetSize(int32_t aWidth, int32_t aHeight) +RasterImage::SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation) { MOZ_ASSERT(NS_IsMainThread()); @@ -1366,7 +1372,9 @@ RasterImage::SetSize(int32_t aWidth, int32_t aHeight) // if we already have a size, check the new size against the old one if (!mMultipart && mHasSize && - ((aWidth != mSize.width) || (aHeight != mSize.height))) { + ((aWidth != mSize.width) || + (aHeight != mSize.height) || + (aOrientation != mOrientation))) { NS_WARNING("Image changed size on redecode! This should not happen!"); // Make the decoder aware of the error so that it doesn't try to call @@ -1380,6 +1388,7 @@ RasterImage::SetSize(int32_t aWidth, int32_t aHeight) // Set the size and flag that we have it mSize.SizeTo(aWidth, aHeight); + mOrientation = aOrientation; mHasSize = true; mFrameBlender.SetSize(mSize); diff --git a/image/src/RasterImage.h b/image/src/RasterImage.h index 87d7286b5..52859657f 100644 --- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -29,6 +29,7 @@ #include "nsThreadUtils.h" #include "DecodeStrategy.h" #include "DiscardTracker.h" +#include "Orientation.h" #include "nsISupportsImpl.h" #include "mozilla/LinkedList.h" #include "mozilla/Mutex.h" @@ -192,13 +193,11 @@ public: /* Callbacks for decoders */ nsresult SetFrameAsNonPremult(uint32_t aFrameNum, bool aIsNonPremult); - /** - * Sets the size of the container. This should only be called by the - * decoder. This function may be called multiple times, but will throw an - * error if subsequent calls do not match the first. + /** Sets the size and inherent orientation of the container. This should only + * be called by the decoder. This function may be called multiple times, but + * will throw an error if subsequent calls do not match the first. */ - nsresult SetSize(int32_t aWidth, int32_t aHeight); - + nsresult SetSize(int32_t aWidth, int32_t aHeight, Orientation aOrientation); /** * Ensures that a given frame number exists with the given parameters, and @@ -655,6 +654,7 @@ private: private: // data nsIntSize mSize; + Orientation mOrientation; // Whether our frames were decoded using any special flags. // Some flags (e.g. unpremultiplied data) may not be compatible diff --git a/image/src/VectorImage.cpp b/image/src/VectorImage.cpp index 1d66364ed..8b0db9701 100644 --- a/image/src/VectorImage.cpp +++ b/image/src/VectorImage.cpp @@ -25,6 +25,7 @@ #include "nsStubDocumentObserver.h" #include "nsSVGEffects.h" // for nsSVGRenderingObserver #include "nsSVGUtils.h" // for nsSVGUtils::ConvertToSurfaceSize +#include "Orientation.h" #include "SVGDocumentWrapper.h" namespace mozilla { @@ -538,6 +539,12 @@ VectorImage::GetIntrinsicRatio(nsSize* aRatio) return NS_OK; } +NS_IMETHODIMP_(Orientation) +VectorImage::GetOrientation() +{ + return Orientation(); +} + //****************************************************************************** /* readonly attribute unsigned short type; */ NS_IMETHODIMP diff --git a/image/src/imgFrame.cpp b/image/src/imgFrame.cpp index c1b402239..ff6bedec2 100644 --- a/image/src/imgFrame.cpp +++ b/image/src/imgFrame.cpp @@ -464,7 +464,7 @@ void imgFrame::Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter, } gfxMatrix userSpaceToImageSpace = aUserSpaceToImageSpace; - gfxRect sourceRect = userSpaceToImageSpace.Transform(aFill); + gfxRect sourceRect = userSpaceToImageSpace.TransformBounds(aFill); gfxRect imageRect(0, 0, mSize.width + aPadding.LeftRight(), mSize.height + aPadding.TopBottom()); gfxRect subimage(aSubimage.x, aSubimage.y, aSubimage.width, aSubimage.height); diff --git a/image/src/moz.build b/image/src/moz.build index 77bb66ac6..e9f011aca 100644 --- a/image/src/moz.build +++ b/image/src/moz.build @@ -8,6 +8,7 @@ MODULE = 'imglib2' EXPORTS += [ 'ImageOps.h', + 'Orientation.h', 'imgLoader.h', 'imgRequest.h', 'imgRequestProxy.h', @@ -24,6 +25,7 @@ CPP_SOURCES += [ 'ImageMetadata.cpp', 'ImageOps.cpp', 'ImageWrapper.cpp', + 'OrientedImage.cpp', 'RasterImage.cpp', 'SVGDocumentWrapper.cpp', 'ScriptedNotificationObserver.cpp', diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 49438a99b..fcb4a2d1e 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -31,6 +31,7 @@ #include "nsBlockFrame.h" #include "nsBidiPresUtils.h" #include "imgIContainer.h" +#include "ImageOps.h" #include "gfxRect.h" #include "gfxContext.h" #include "gfxFont.h" @@ -98,6 +99,11 @@ using namespace mozilla::dom; using namespace mozilla::layers; using namespace mozilla::layout; +using mozilla::image::Angle; +using mozilla::image::Flip; +using mozilla::image::ImageOps; +using mozilla::image::Orientation; + #define FLEXBOX_ENABLED_PREF_NAME "layout.css.flexbox.enabled" #ifdef DEBUG @@ -383,6 +389,22 @@ nsLayoutUtils::AnimatedImageLayersEnabled() return sAnimatedImageLayersEnabled; } +bool +nsLayoutUtils::CSSFiltersEnabled() +{ + static bool sCSSFiltersEnabled; + static bool sCSSFiltersPrefCached = false; + + if (!sCSSFiltersPrefCached) { + sCSSFiltersPrefCached = true; + Preferences::AddBoolVarCache(&sCSSFiltersEnabled, + "layout.css.filters.enabled", + false); + } + + return sCSSFiltersEnabled; +} + void nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame, nsOverflowAreas& aOverflowAreas) @@ -4219,6 +4241,25 @@ nsLayoutUtils::GetWholeImageDestination(const nsIntSize& aWholeImageSize, nsSize(wholeSizeX, wholeSizeY)); } +/* static */ already_AddRefed<imgIContainer> +nsLayoutUtils::OrientImage(imgIContainer* aContainer, + const nsStyleImageOrientation& aOrientation) +{ + MOZ_ASSERT(aContainer, "Should have an image container"); + nsCOMPtr<imgIContainer> img(aContainer); + + if (aOrientation.IsFromImage()) { + img = ImageOps::Orient(img, img->GetOrientation()); + } else if (!aOrientation.IsDefault()) { + Angle angle = aOrientation.Angle(); + Flip flip = aOrientation.IsFlipped() ? Flip::Horizontal + : Flip::Unflipped; + img = ImageOps::Orient(img, Orientation(angle, flip)); + } + + return img.forget(); +} + static bool NonZeroStyleCoord(const nsStyleCoord& aCoord) { if (aCoord.IsCoordPercentCalcUnit()) { diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 454d3b991..ef3fa6238 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -1334,6 +1334,18 @@ public: const nsRect& aDestArea); /** + * Given an image container and an orientation, returns an image container + * that contains the same image, reoriented appropriately. May return the + * original image container if no changes are needed. + * + * @param aContainer The image container to apply the orientation to. + * @param aOrientation The desired orientation. + */ + static already_AddRefed<imgIContainer> + OrientImage(imgIContainer* aContainer, + const nsStyleImageOrientation& aOrientation); + + /** * Determine if any corner radius is of nonzero size * @param aCorners the |nsStyleCorners| object to check * @return true unless all the coordinates are 0%, 0 or null. @@ -1614,6 +1626,11 @@ public: static bool AnimatedImageLayersEnabled(); /** + * Checks if we should enable parsing for CSS Filters. + */ + static bool CSSFiltersEnabled(); + + /** * Unions the overflow areas of all non-popup children of aFrame with * aOverflowAreas. */ diff --git a/layout/base/nsStyleConsts.h b/layout/base/nsStyleConsts.h index aa6f2eef9..af36dd996 100644 --- a/layout/base/nsStyleConsts.h +++ b/layout/base/nsStyleConsts.h @@ -621,6 +621,10 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) { #define NS_STYLE_POINTER_EVENTS_ALL 8 #define NS_STYLE_POINTER_EVENTS_AUTO 9 +// See nsStyleVisibility.mImageOrientationType +#define NS_STYLE_IMAGE_ORIENTATION_FLIP 0 +#define NS_STYLE_IMAGE_ORIENTATION_FROM_IMAGE 1 + // See nsStyleDisplay #define NS_STYLE_RESIZE_NONE 0 #define NS_STYLE_RESIZE_BOTH 1 diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 922689742..d435f4bd3 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -4953,7 +4953,7 @@ ComputeOutlineAndEffectsRect(nsIFrame* aFrame, // For SVG frames, we only need to account for filters. // TODO: We could also take account of clipPath and mask to reduce the // visual overflow, but that's not essential. - if (aFrame->StyleSVGReset()->mFilter) { + if (aFrame->StyleSVGReset()->SingleFilter()) { if (aStoreRectProperties) { aFrame->Properties(). Set(nsIFrame::PreEffectsBBoxProperty(), new nsRect(r)); diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 40cf8c1a4..87a8154ce 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -254,13 +254,10 @@ nsImageFrame::Init(nsIContent* aContent, p->AdjustPriority(-1); // If we already have an image container, OnStartContainer won't be called - // Set the animation mode here if (currentRequest) { nsCOMPtr<imgIContainer> image; currentRequest->GetImage(getter_AddRefs(image)); - if (image) { - image->SetAnimationMode(aPresContext->ImageAnimationMode()); - } + OnStartContainer(currentRequest, image); } } @@ -542,9 +539,12 @@ nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage) // We don't care return NS_OK; } + + // This is for the current request, so update our stored image container. + mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation); - bool intrinsicSizeChanged = UpdateIntrinsicSize(aImage); - intrinsicSizeChanged = UpdateIntrinsicRatio(aImage) || intrinsicSizeChanged; + bool intrinsicSizeChanged = UpdateIntrinsicSize(mImage); + intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged; if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) { // Now we need to reflow if we have an unconstrained size and have @@ -629,21 +629,38 @@ nsImageFrame::OnStopRequest(imgIRequest *aRequest, return NS_OK; } +static bool +SizeIsAvailable(imgIRequest* aRequest) +{ + if (!aRequest) + return false; + + uint32_t imageStatus = 0; + aRequest->GetImageStatus(&imageStatus); + + return (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE); +} + void nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest, nsresult aStatus) { + nsCOMPtr<imgIContainer> image; + aRequest->GetImage(getter_AddRefs(image)); + NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?"); + // May have to switch sizes here! bool intrinsicSizeChanged = true; - if (NS_SUCCEEDED(aStatus)) { - nsCOMPtr<imgIContainer> imageContainer; - aRequest->GetImage(getter_AddRefs(imageContainer)); - NS_ASSERTION(imageContainer, "Successful load with no container?"); - intrinsicSizeChanged = UpdateIntrinsicSize(imageContainer); - intrinsicSizeChanged = UpdateIntrinsicRatio(imageContainer) || - intrinsicSizeChanged; - } - else { + if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) { + // Update our stored image container, orienting according to our style. + mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation); + + intrinsicSizeChanged = UpdateIntrinsicSize(mImage); + intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged; + } else { + // We no longer have a valid image, so release our stored image container. + mImage = nullptr; + // Have to size to 0,0 so that GetDesiredSize recalculates the size mIntrinsicSize.width.SetCoordValue(0); mIntrinsicSize.height.SetCoordValue(0); @@ -683,30 +700,16 @@ nsImageFrame::FrameChanged(imgIRequest *aRequest, void nsImageFrame::EnsureIntrinsicSizeAndRatio(nsPresContext* aPresContext) { - // if mIntrinsicSize.width and height are 0, then we should - // check to see if the size is already known by the image container. + // If mIntrinsicSize.width and height are 0, then we need to update from the + // image container. if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord && mIntrinsicSize.width.GetCoordValue() == 0 && mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord && mIntrinsicSize.height.GetCoordValue() == 0) { - // Jump through all the hoops to get the status of the request - nsCOMPtr<imgIRequest> currentRequest; - nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); - if (imageLoader) - imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, - getter_AddRefs(currentRequest)); - uint32_t status = 0; - if (currentRequest) - currentRequest->GetImageStatus(&status); - - // If we know the size, we can grab it and use it for an update - if (status & imgIRequest::STATUS_SIZE_AVAILABLE) { - nsCOMPtr<imgIContainer> imgCon; - currentRequest->GetImage(getter_AddRefs(imgCon)); - NS_ABORT_IF_FALSE(imgCon, "SIZE_AVAILABLE, but no imgContainer?"); - UpdateIntrinsicSize(imgCon); - UpdateIntrinsicRatio(imgCon); + if (mImage) { + UpdateIntrinsicSize(mImage); + UpdateIntrinsicRatio(mImage); } else { // image request is null or image size not known, probably an // invalid image specified @@ -1390,31 +1393,16 @@ nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsEventStates contentState = mContent->AsElement()->State(); bool imageOK = IMAGE_OK(contentState, true); - nsCOMPtr<imgIContainer> imgCon; - if (currentRequest) { - currentRequest->GetImage(getter_AddRefs(imgCon)); - } - - // Determine if the size is available - bool haveSize = false; - uint32_t imageStatus = 0; - if (currentRequest) - currentRequest->GetImageStatus(&imageStatus); - if (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE) - haveSize = true; - - // We should never have the size and not have an image container - NS_ABORT_IF_FALSE(!haveSize || imgCon, "Have size but not container?"); - - if (!imageOK || !haveSize) { + if (!imageOK || !mImage) { // No image yet, or image load failed. Draw the alt-text and an icon // indicating the status aLists.Content()->AppendNewToTop(new (aBuilder) nsDisplayAltFeedback(aBuilder, this)); } else { + NS_ASSERTION(SizeIsAvailable(currentRequest), "Should have a size"); aLists.Content()->AppendNewToTop(new (aBuilder) - nsDisplayImage(aBuilder, this, imgCon)); + nsDisplayImage(aBuilder, this, mImage)); // If we were previously displaying an icon, we're not anymore if (mDisplayingIcon) { diff --git a/layout/generic/nsImageFrame.h b/layout/generic/nsImageFrame.h index 01307c992..f3a54c477 100644 --- a/layout/generic/nsImageFrame.h +++ b/layout/generic/nsImageFrame.h @@ -280,6 +280,7 @@ private: nsCOMPtr<imgINotificationObserver> mListener; + nsCOMPtr<imgIContainer> mImage; nsSize mComputedSize; nsIFrame::IntrinsicSize mIntrinsicSize; nsSize mIntrinsicRatio; diff --git a/layout/style/TopLevelImageDocument.css b/layout/style/TopLevelImageDocument.css index e19d9e23b..df1c224dd 100644 --- a/layout/style/TopLevelImageDocument.css +++ b/layout/style/TopLevelImageDocument.css @@ -27,3 +27,7 @@ transition: transform 0.3s ease 0s; } } + +img { + image-orientation: from-image; +}
\ No newline at end of file diff --git a/layout/style/nsCSSKeywordList.h b/layout/style/nsCSSKeywordList.h index 3dab88849..68ef47255 100644 --- a/layout/style/nsCSSKeywordList.h +++ b/layout/style/nsCSSKeywordList.h @@ -182,6 +182,7 @@ CSS_KEY(bidi-override, bidi_override) CSS_KEY(blink, blink) CSS_KEY(block, block) CSS_KEY(block-axis, block_axis) +CSS_KEY(blur, blur) CSS_KEY(bold, bold) CSS_KEY(bolder, bolder) CSS_KEY(border-box, border_box) @@ -191,6 +192,7 @@ CSS_KEY(bottom-outside, bottom_outside) CSS_KEY(bounding-box, bounding_box) CSS_KEY(break-all, break_all) CSS_KEY(break-word, break_word) +CSS_KEY(brightness, brightness) CSS_KEY(button, button) CSS_KEY(buttonface, buttonface) CSS_KEY(buttonhighlight, buttonhighlight) @@ -220,6 +222,7 @@ CSS_KEY(contain, contain) CSS_KEY(content-box, content_box) CSS_KEY(context-menu, context_menu) CSS_KEY(continuous, continuous) +CSS_KEY(contrast, contrast) CSS_KEY(copy, copy) CSS_KEY(contextual, contextual) CSS_KEY(cover, cover) @@ -266,10 +269,13 @@ CSS_KEY(flat, flat) CSS_KEY(flex, flex) CSS_KEY(flex-end, flex_end) CSS_KEY(flex-start, flex_start) +CSS_KEY(flip, flip) CSS_KEY(forwards, forwards) +CSS_KEY(from-image, from_image) CSS_KEY(full-width, full_width) CSS_KEY(georgian, georgian) CSS_KEY(grad, grad) +CSS_KEY(grayscale, grayscale) CSS_KEY(graytext, graytext) CSS_KEY(groove, groove) CSS_KEY(hebrew, hebrew) @@ -284,6 +290,7 @@ CSS_KEY(historical-forms, historical_forms) CSS_KEY(historical-ligatures, historical_ligatures) CSS_KEY(horizontal, horizontal) CSS_KEY(horizontal-tb, horizontal_tb) +CSS_KEY(hue-rotate, hue_rotate) CSS_KEY(hz, hz) CSS_KEY(icon, icon) CSS_KEY(ignore, ignore) @@ -306,6 +313,7 @@ CSS_KEY(inline-table, inline_table) CSS_KEY(inset, inset) CSS_KEY(inside, inside) CSS_KEY(interpolatematrix, interpolatematrix) +CSS_KEY(invert, invert) CSS_KEY(italic, italic) CSS_KEY(jis78, jis78) CSS_KEY(jis83, jis83) @@ -369,6 +377,7 @@ CSS_KEY(nw-resize, nw_resize) CSS_KEY(nwse-resize, nwse_resize) CSS_KEY(oblique, oblique) CSS_KEY(oldstyle-nums, oldstyle_nums) +CSS_KEY(opacity, opacity) CSS_KEY(open-quote, open_quote) CSS_KEY(ordinal, ordinal) CSS_KEY(ornaments, ornaments) @@ -418,6 +427,7 @@ CSS_KEY(ruby, ruby) CSS_KEY(running, running) CSS_KEY(s, s) CSS_KEY(s-resize, s_resize) +CSS_KEY(saturate, saturate) CSS_KEY(scale, scale) CSS_KEY(scale3d, scale3d) CSS_KEY(scalex, scalex) @@ -435,6 +445,7 @@ CSS_KEY(select-same, select_same) CSS_KEY(semi-condensed, semi_condensed) CSS_KEY(semi-expanded, semi_expanded) CSS_KEY(separate, separate) +CSS_KEY(sepia, sepia) CSS_KEY(show, show) CSS_KEY(simplified, simplified) CSS_KEY(skew, skew) diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index 188072c0a..f23a18e85 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -118,8 +118,9 @@ using namespace mozilla; #define VARIANT_UK (VARIANT_URL | VARIANT_KEYWORD) #define VARIANT_UO (VARIANT_URL | VARIANT_NONE) #define VARIANT_ANGLE_OR_ZERO (VARIANT_ANGLE | VARIANT_ZERO_ANGLE) -#define VARIANT_LPCALC (VARIANT_LENGTH | VARIANT_CALC | VARIANT_PERCENT) -#define VARIANT_LNCALC (VARIANT_LENGTH | VARIANT_CALC | VARIANT_NUMBER) +#define VARIANT_LCALC (VARIANT_LENGTH | VARIANT_CALC) +#define VARIANT_LPCALC (VARIANT_LCALC | VARIANT_PERCENT) +#define VARIANT_LNCALC (VARIANT_LCALC | VARIANT_NUMBER) #define VARIANT_LPNCALC (VARIANT_LNCALC | VARIANT_PERCENT) #define VARIANT_IMAGE (VARIANT_URL | VARIANT_NONE | VARIANT_GRADIENT | \ VARIANT_IMAGE_RECT | VARIANT_ELEMENT) @@ -653,6 +654,7 @@ protected: bool SetValueToURL(nsCSSValue& aValue, const nsString& aURL); bool TranslateDimension(nsCSSValue& aValue, int32_t aVariantMask, float aNumber, const nsString& aUnit); + bool ParseImageOrientation(nsCSSValue& aAngle); bool ParseImageRect(nsCSSValue& aImage); bool ParseElement(nsCSSValue& aValue); bool ParseColorStop(nsCSSValueGradient* aGradient); @@ -686,6 +688,10 @@ protected: /* Functions for transform-origin/perspective-origin Parsing */ bool ParseTransformOrigin(bool aPerspective); + /* Functions for filter parsing */ + bool ParseFilter(); + bool ParseSingleFilter(nsCSSValue* aValue); + /* Find and return the namespace ID associated with aPrefix. If aPrefix has not been declared in an @namespace rule, returns kNameSpaceID_Unknown. */ @@ -5533,6 +5539,47 @@ CSSParserImpl::SetValueToURL(nsCSSValue& aValue, const nsString& aURL) } /** + * Parse the image-orientation property, which has the grammar: + * <angle> flip? | flip | from-image + */ +bool +CSSParserImpl::ParseImageOrientation(nsCSSValue& aValue) +{ + if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) { + // 'inherit' and 'initial' must be alone + return true; + } + + // Check for an angle with optional 'flip'. + nsCSSValue angle; + if (ParseVariant(angle, VARIANT_ANGLE, nullptr)) { + nsCSSValue flip; + + if (ParseVariant(flip, VARIANT_KEYWORD, nsCSSProps::kImageOrientationFlipKTable)) { + nsRefPtr<nsCSSValue::Array> array = nsCSSValue::Array::Create(2); + array->Item(0) = angle; + array->Item(1) = flip; + aValue.SetArrayValue(array, eCSSUnit_Array); + } else { + aValue = angle; + } + + return true; + } + + // The remaining possibilities (bare 'flip' and 'from-image') are both + // keywords, so we can handle them at the same time. + nsCSSValue keyword; + if (ParseVariant(keyword, VARIANT_KEYWORD, nsCSSProps::kImageOrientationKTable)) { + aValue = keyword; + return true; + } + + // All possibilities failed. + return false; +} + +/** * Parse the arguments of -moz-image-rect() function. * -moz-image-rect(<uri>, <top>, <right>, <bottom>, <left>) */ @@ -6550,6 +6597,8 @@ CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID) return ParseCounterData(aPropID); case eCSSProperty_cursor: return ParseCursor(); + case eCSSProperty_filter: + return ParseFilter(); case eCSSProperty_flex: return ParseFlex(); case eCSSProperty_font: @@ -6671,6 +6720,8 @@ CSSParserImpl::ParseSingleValueProperty(nsCSSValue& aValue, return ParseFontFeatureSettings(aValue); case eCSSProperty_font_weight: return ParseFontWeight(aValue); + case eCSSProperty_image_orientation: + return ParseImageOrientation(aValue); case eCSSProperty_marks: return ParseMarks(aValue); case eCSSProperty_text_decoration_line: @@ -10120,6 +10171,148 @@ bool CSSParserImpl::ParseTransformOrigin(bool aPerspective) return true; } +/** + * Reads a single url or filter function from the tokenizer stream, reporting an + * error if something goes wrong. + */ +bool +CSSParserImpl::ParseSingleFilter(nsCSSValue* aValue) +{ + if (ParseVariant(*aValue, VARIANT_URL, nullptr)) { + return true; + } + + if (!nsLayoutUtils::CSSFiltersEnabled()) { + // With CSS Filters disabled, we should only accept an SVG reference filter. + REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURL); + return false; + } + + if (!GetToken(true)) { + REPORT_UNEXPECTED_EOF(PEFilterEOF); + return false; + } + + if (mToken.mType != eCSSToken_Function) { + REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction); + return false; + } + + // Set up the parsing rules based on the filter function. + int32_t variantMask = VARIANT_PN; + bool rejectNegativeArgument = true; + bool clampArgumentToOne = false; + nsCSSKeyword functionName = nsCSSKeywords::LookupKeyword(mToken.mIdent); + switch (functionName) { + case eCSSKeyword_blur: + variantMask = VARIANT_LCALC | VARIANT_NONNEGATIVE_DIMENSION; + // VARIANT_NONNEGATIVE_DIMENSION will already reject negative lengths. + rejectNegativeArgument = false; + break; + case eCSSKeyword_brightness: + case eCSSKeyword_contrast: + case eCSSKeyword_saturate: + break; + case eCSSKeyword_grayscale: + case eCSSKeyword_invert: + case eCSSKeyword_sepia: + case eCSSKeyword_opacity: + clampArgumentToOne = true; + break; + case eCSSKeyword_hue_rotate: + variantMask = VARIANT_ANGLE; + rejectNegativeArgument = false; + break; + default: + // Unrecognized filter function. + REPORT_UNEXPECTED_TOKEN(PEExpectedNoneOrURLOrFilterFunction); + SkipUntil(')'); + return false; + } + + // Parse the function. + uint16_t minElems = 1U; + uint16_t maxElems = 1U; + uint32_t allVariants = 0; + if (!ParseFunction(functionName, &variantMask, allVariants, + minElems, maxElems, *aValue)) { + REPORT_UNEXPECTED(PEFilterFunctionArgumentsParsingError); + return false; + } + + // Get the first and only argument to the filter function. + NS_ABORT_IF_FALSE(aValue->GetUnit() == eCSSUnit_Function, + "expected a filter function"); + NS_ABORT_IF_FALSE(aValue->UnitHasArrayValue(), + "filter function should be an array"); + NS_ABORT_IF_FALSE(aValue->GetArrayValue()->Count() == 2, + "filter function should have exactly one argument"); + nsCSSValue& arg = aValue->GetArrayValue()->Item(1); + + if (rejectNegativeArgument && + ((arg.GetUnit() == eCSSUnit_Percent && arg.GetPercentValue() < 0.0f) || + (arg.GetUnit() == eCSSUnit_Number && arg.GetFloatValue() < 0.0f))) { + REPORT_UNEXPECTED(PEExpectedNonnegativeNP); + return false; + } + + if (clampArgumentToOne) { + if (arg.GetUnit() == eCSSUnit_Number && + arg.GetFloatValue() > 1.0f) { + arg.SetFloatValue(1.0f, arg.GetUnit()); + } else if (arg.GetUnit() == eCSSUnit_Percent && + arg.GetPercentValue() > 1.0f) { + arg.SetPercentValue(1.0f); + } + } + + return true; +} + +/** + * Parses a filter property value by continuously reading in urls and/or filter + * functions and constructing a list. + * + * When CSS Filters are enabled, the filter property accepts one or more SVG + * reference filters and/or CSS filter functions. + * e.g. filter: url(#my-filter-1) blur(3px) url(#my-filter-2) grayscale(50%); + * + * When CSS Filters are disabled, the filter property only accepts one SVG + * reference filter. + * e.g. filter: url(#my-filter); + */ +bool +CSSParserImpl::ParseFilter() +{ + nsCSSValue value; + if (ParseVariant(value, VARIANT_INHERIT | VARIANT_NONE, nullptr)) { + // 'inherit', 'initial', and 'none' must be alone + if (!ExpectEndProperty()) { + return false; + } + } else { + nsCSSValueList* cur = value.SetListValue(); + while (cur) { + if (!ParseSingleFilter(&cur->mValue)) { + return false; + } + if (CheckEndProperty()) { + break; + } + if (!nsLayoutUtils::CSSFiltersEnabled()) { + // With CSS Filters disabled, we should only accept one SVG reference + // filter. + REPORT_UNEXPECTED_TOKEN(PEExpectEndValue); + return false; + } + cur->mNext = new nsCSSValueList; + cur = cur->mNext; + } + } + AppendValue(eCSSProperty_filter, value); + return true; +} + bool CSSParserImpl::ParseTransitionProperty() { diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 5bd8222a9..1acc3cb0f 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -1923,6 +1923,17 @@ CSS_PROP_POSITION( nullptr, offsetof(nsStylePosition, mHeight), eStyleAnimType_Coord) +CSS_PROP_VISIBILITY( + image-orientation, + image_orientation, + ImageOrientation, + CSS_PROPERTY_PARSE_VALUE | + CSS_PROPERTY_VALUE_PARSER_FUNCTION, + "layout.css.image-orientation.enabled", + 0, + kImageOrientationKTable, + offsetof(nsStyleVisibility, mImageOrientation), + eStyleAnimType_None) CSS_PROP_LIST( -moz-image-region, image_region, @@ -3327,9 +3338,9 @@ CSS_PROP_SVGRESET( filter, filter, Filter, - CSS_PROPERTY_PARSE_VALUE, + CSS_PROPERTY_PARSE_FUNCTION, "", - VARIANT_HUO, + 0, nullptr, CSS_PROP_NO_OFFSET, eStyleAnimType_None) diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 4af5c25b1..daf852c86 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -1151,6 +1151,17 @@ const int32_t nsCSSProps::kFontWeightKTable[] = { eCSSKeyword_UNKNOWN,-1 }; +const int32_t nsCSSProps::kImageOrientationKTable[] = { + eCSSKeyword_flip, NS_STYLE_IMAGE_ORIENTATION_FLIP, + eCSSKeyword_from_image, NS_STYLE_IMAGE_ORIENTATION_FROM_IMAGE, + eCSSKeyword_UNKNOWN,-1 +}; + +const int32_t nsCSSProps::kImageOrientationFlipKTable[] = { + eCSSKeyword_flip, NS_STYLE_IMAGE_ORIENTATION_FLIP, + eCSSKeyword_UNKNOWN,-1 +}; + const int32_t nsCSSProps::kIMEModeKTable[] = { eCSSKeyword_normal, NS_STYLE_IME_MODE_NORMAL, eCSSKeyword_auto, NS_STYLE_IME_MODE_AUTO, diff --git a/layout/style/nsCSSProps.h b/layout/style/nsCSSProps.h index aef6655fc..d9ec19e72 100644 --- a/layout/style/nsCSSProps.h +++ b/layout/style/nsCSSProps.h @@ -415,6 +415,8 @@ public: static const int32_t kFontVariantNumericKTable[]; static const int32_t kFontVariantPositionKTable[]; static const int32_t kFontWeightKTable[]; + static const int32_t kImageOrientationKTable[]; + static const int32_t kImageOrientationFlipKTable[]; static const int32_t kIMEModeKTable[]; static const int32_t kLineHeightKTable[]; static const int32_t kListStylePositionKTable[]; diff --git a/layout/style/nsComputedDOMStyle.cpp b/layout/style/nsComputedDOMStyle.cpp index 011c98831..963b278fa 100644 --- a/layout/style/nsComputedDOMStyle.cpp +++ b/layout/style/nsComputedDOMStyle.cpp @@ -42,6 +42,7 @@ #include "nsStyleTransformMatrix.h" #include "mozilla/dom/Element.h" #include "nsWrapperCacheInlines.h" +#include "nsUTF8Utils.h" #include <algorithm> using namespace mozilla; @@ -1711,16 +1712,7 @@ nsComputedDOMStyle::GetCSSGradientString(const nsStyleGradient* aGradient, if (needSep) { aString.AppendLiteral(" "); } - tmpVal->SetNumber(aGradient->mAngle.GetAngleValue()); - tmpVal->GetCssText(tokenString); - aString.Append(tokenString); - switch (aGradient->mAngle.GetUnit()) { - case eStyleUnit_Degree: aString.AppendLiteral("deg"); break; - case eStyleUnit_Grad: aString.AppendLiteral("grad"); break; - case eStyleUnit_Radian: aString.AppendLiteral("rad"); break; - case eStyleUnit_Turn: aString.AppendLiteral("turn"); break; - default: NS_NOTREACHED("unrecognized angle unit"); - } + nsStyleUtil::AppendAngleValue(aGradient->mAngle, aString); needSep = true; } @@ -3301,6 +3293,27 @@ nsComputedDOMStyle::DoGetForceBrokenImageIcon() } CSSValue* +nsComputedDOMStyle::DoGetImageOrientation() +{ + nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue; + nsAutoString string; + nsStyleImageOrientation orientation = StyleVisibility()->mImageOrientation; + + if (orientation.IsFromImage()) { + string.AppendLiteral("from-image"); + } else { + nsStyleUtil::AppendAngleValue(orientation.AngleAsCoord(), string); + + if (orientation.IsFlipped()) { + string.AppendLiteral(" flip"); + } + } + + val->SetString(string); + return val; +} + +CSSValue* nsComputedDOMStyle::DoGetIMEMode() { nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue; @@ -4005,6 +4018,23 @@ nsComputedDOMStyle::SetValueToCoord(nsROCSSPrimitiveValue* aValue, SetValueToCalc(calc, aValue); } break; + + case eStyleUnit_Degree: + aValue->SetDegree(aCoord.GetAngleValue()); + break; + + case eStyleUnit_Grad: + aValue->SetGrad(aCoord.GetAngleValue()); + break; + + case eStyleUnit_Radian: + aValue->SetRadian(aCoord.GetAngleValue()); + break; + + case eStyleUnit_Turn: + aValue->SetTurn(aCoord.GetAngleValue()); + break; + default: NS_ERROR("Can't handle this unit"); break; @@ -4466,19 +4496,99 @@ nsComputedDOMStyle::DoGetClipPath() return val; } +void +nsComputedDOMStyle::SetCssTextToCoord(nsAString& aCssText, + const nsStyleCoord& aCoord) +{ + nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue; + bool clampNegativeCalc = true; + SetValueToCoord(value, aCoord, clampNegativeCalc); + value->GetCssText(aCssText); + delete value; +} + +static void +GetFilterFunctionName(nsAString& aString, nsStyleFilter::Type mType) +{ + switch (mType) { + case nsStyleFilter::Type::eBlur: + aString.AssignLiteral("blur("); + break; + case nsStyleFilter::Type::eBrightness: + aString.AssignLiteral("brightness("); + break; + case nsStyleFilter::Type::eContrast: + aString.AssignLiteral("contrast("); + break; + case nsStyleFilter::Type::eGrayscale: + aString.AssignLiteral("grayscale("); + break; + case nsStyleFilter::Type::eHueRotate: + aString.AssignLiteral("hue-rotate("); + break; + case nsStyleFilter::Type::eInvert: + aString.AssignLiteral("invert("); + break; + case nsStyleFilter::Type::eOpacity: + aString.AssignLiteral("opacity("); + break; + case nsStyleFilter::Type::eSaturate: + aString.AssignLiteral("saturate("); + break; + case nsStyleFilter::Type::eSepia: + aString.AssignLiteral("sepia("); + break; + default: + NS_NOTREACHED("unrecognized filter type"); + } +} + +nsROCSSPrimitiveValue* +nsComputedDOMStyle::CreatePrimitiveValueForStyleFilter( + const nsStyleFilter& aStyleFilter) +{ + nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue; + + // Handle url(). + if (nsStyleFilter::Type::eURL == aStyleFilter.mType) { + value->SetURI(aStyleFilter.mURL); + return value; + } + + // Filter function name and opening parenthesis. + nsAutoString filterFunctionString; + GetFilterFunctionName(filterFunctionString, aStyleFilter.mType); + + // Filter function argument. + nsAutoString argumentString; + SetCssTextToCoord(argumentString, aStyleFilter.mFilterParameter); + filterFunctionString.Append(argumentString); + + // Filter function closing parenthesis. + filterFunctionString.AppendLiteral(")"); + + value->SetString(filterFunctionString); + return value; +} + CSSValue* nsComputedDOMStyle::DoGetFilter() { - nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue; - - const nsStyleSVGReset* svg = StyleSVGReset(); + const nsTArray<nsStyleFilter>& filters = StyleSVGReset()->mFilters; - if (svg->mFilter) - val->SetURI(svg->mFilter); - else - val->SetIdent(eCSSKeyword_none); + if (filters.IsEmpty()) { + nsROCSSPrimitiveValue* value = new nsROCSSPrimitiveValue; + value->SetIdent(eCSSKeyword_none); + return value; + } - return val; + nsDOMCSSValueList* valueList = GetROCSSValueList(false); + for(uint32_t i = 0; i < filters.Length(); i++) { + nsROCSSPrimitiveValue* value = + CreatePrimitiveValueForStyleFilter(filters[i]); + valueList->AppendCSSValue(value); + } + return valueList; } CSSValue* @@ -4938,6 +5048,7 @@ nsComputedDOMStyle::GetQueryablePropertyMap(uint32_t* aLength) COMPUTED_STYLE_MAP_ENTRY(font_variant_position, FontVariantPosition), COMPUTED_STYLE_MAP_ENTRY(font_weight, FontWeight), COMPUTED_STYLE_MAP_ENTRY_LAYOUT(height, Height), + COMPUTED_STYLE_MAP_ENTRY(image_orientation, ImageOrientation), COMPUTED_STYLE_MAP_ENTRY(ime_mode, IMEMode), COMPUTED_STYLE_MAP_ENTRY(justify_content, JustifyContent), COMPUTED_STYLE_MAP_ENTRY_LAYOUT(left, Left), diff --git a/layout/style/nsComputedDOMStyle.h b/layout/style/nsComputedDOMStyle.h index 4ce1d7d09..ca90c9dda 100644 --- a/layout/style/nsComputedDOMStyle.h +++ b/layout/style/nsComputedDOMStyle.h @@ -340,6 +340,7 @@ private: mozilla::dom::CSSValue* DoGetDisplay(); mozilla::dom::CSSValue* DoGetPosition(); mozilla::dom::CSSValue* DoGetClip(); + mozilla::dom::CSSValue* DoGetImageOrientation(); mozilla::dom::CSSValue* DoGetOverflow(); mozilla::dom::CSSValue* DoGetOverflowX(); mozilla::dom::CSSValue* DoGetOverflowY(); @@ -491,6 +492,11 @@ private: bool GetFrameBorderRectWidth(nscoord& aWidth); bool GetFrameBorderRectHeight(nscoord& aHeight); + /* Helper functions for computing the filter property style. */ + void SetCssTextToCoord(nsAString& aCssText, const nsStyleCoord& aCoord); + nsROCSSPrimitiveValue* CreatePrimitiveValueForStyleFilter( + const nsStyleFilter& aStyleFilter); + struct ComputedStyleMapEntry { // Create a pointer-to-member-function type. diff --git a/layout/style/nsROCSSPrimitiveValue.cpp b/layout/style/nsROCSSPrimitiveValue.cpp index e092f311a..c651db106 100644 --- a/layout/style/nsROCSSPrimitiveValue.cpp +++ b/layout/style/nsROCSSPrimitiveValue.cpp @@ -18,6 +18,13 @@ using namespace mozilla; +// There is no CSS_TURN constant on the CSSPrimitiveValue interface, +// since that unit is newer than DOM Level 2 Style, and CSS OM will +// probably expose CSS values in some other way in the future. We +// use this value in mType for "turn"-unit angles, but we define it +// here to avoid exposing it to content. +#define CSS_TURN 30U + nsROCSSPrimitiveValue::nsROCSSPrimitiveValue() : CSSValue(), mType(CSS_PX) { @@ -134,6 +141,30 @@ nsROCSSPrimitiveValue::GetCssText(nsAString& aCssText) nsStyleUtil::AppendCSSNumber(mValue.mFloat, tmpStr); break; } + case CSS_DEG : + { + nsStyleUtil::AppendCSSNumber(mValue.mFloat, tmpStr); + tmpStr.AppendLiteral("deg"); + break; + } + case CSS_GRAD : + { + nsStyleUtil::AppendCSSNumber(mValue.mFloat, tmpStr); + tmpStr.AppendLiteral("grad"); + break; + } + case CSS_RAD : + { + nsStyleUtil::AppendCSSNumber(mValue.mFloat, tmpStr); + tmpStr.AppendLiteral("rad"); + break; + } + case CSS_TURN : + { + nsStyleUtil::AppendCSSNumber(mValue.mFloat, tmpStr); + tmpStr.AppendLiteral("turn"); + break; + } case CSS_RECT : { NS_ASSERTION(mValue.mRect, "mValue.mRect should never be null"); @@ -230,9 +261,6 @@ nsROCSSPrimitiveValue::GetCssText(nsAString& aCssText) case CSS_UNKNOWN : case CSS_EMS : case CSS_EXS : - case CSS_DEG : - case CSS_RAD : - case CSS_GRAD : case CSS_MS : case CSS_HZ : case CSS_KHZ : @@ -521,6 +549,38 @@ nsROCSSPrimitiveValue::SetPercent(float aValue) } void +nsROCSSPrimitiveValue::SetDegree(float aValue) +{ + Reset(); + mValue.mFloat = aValue; + mType = CSS_DEG; +} + +void +nsROCSSPrimitiveValue::SetGrad(float aValue) +{ + Reset(); + mValue.mFloat = aValue; + mType = CSS_GRAD; +} + +void +nsROCSSPrimitiveValue::SetRadian(float aValue) +{ + Reset(); + mValue.mFloat = aValue; + mType = CSS_RAD; +} + +void +nsROCSSPrimitiveValue::SetTurn(float aValue) +{ + Reset(); + mValue.mFloat = aValue; + mType = CSS_TURN; +} + +void nsROCSSPrimitiveValue::SetAppUnits(nscoord aValue) { Reset(); diff --git a/layout/style/nsROCSSPrimitiveValue.h b/layout/style/nsROCSSPrimitiveValue.h index dc444f1e7..d6bd3db4a 100644 --- a/layout/style/nsROCSSPrimitiveValue.h +++ b/layout/style/nsROCSSPrimitiveValue.h @@ -44,6 +44,11 @@ public: // CSSPrimitiveValue uint16_t PrimitiveType() { + // New value types were introduced but not added to CSS OM. + // Return CSS_UNKNOWN to avoid exposing CSS_TURN to content. + if (mType > CSS_RGBCOLOR) { + return CSS_UNKNOWN; + } return mType; } void SetFloatValue(uint16_t aUnitType, float aValue, @@ -64,6 +69,10 @@ public: void SetNumber(int32_t aValue); void SetNumber(uint32_t aValue); void SetPercent(float aValue); + void SetDegree(float aValue); + void SetGrad(float aValue); + void SetRadian(float aValue); + void SetTurn(float aValue); void SetAppUnits(nscoord aValue); void SetAppUnits(float aValue); void SetIdent(nsCSSKeyword aKeyword); diff --git a/layout/style/nsRuleNode.cpp b/layout/style/nsRuleNode.cpp index f01b6a2e4..e53af8efc 100644 --- a/layout/style/nsRuleNode.cpp +++ b/layout/style/nsRuleNode.cpp @@ -653,6 +653,7 @@ GetFloatFromBoxPosition(int32_t aEnumValue) #define SETCOORD_CALC_CLAMP_NONNEGATIVE 0x00040000 // modifier for CALC_LENGTH_ONLY #define SETCOORD_STORE_CALC 0x00080000 #define SETCOORD_BOX_POSITION 0x00100000 // exclusive with _ENUMERATED +#define SETCOORD_ANGLE 0x00200000 #define SETCOORD_LP (SETCOORD_LENGTH | SETCOORD_PERCENT) #define SETCOORD_LH (SETCOORD_LENGTH | SETCOORD_INHERIT) @@ -765,6 +766,19 @@ static bool SetCoord(const nsCSSValue& aValue, nsStyleCoord& aCoord, result = false; // didn't set anything } } + else if ((aMask & SETCOORD_ANGLE) != 0 && + (aValue.IsAngularUnit())) { + nsStyleUnit unit; + switch (aValue.GetUnit()) { + case eCSSUnit_Degree: unit = eStyleUnit_Degree; break; + case eCSSUnit_Grad: unit = eStyleUnit_Grad; break; + case eCSSUnit_Radian: unit = eStyleUnit_Radian; break; + case eCSSUnit_Turn: unit = eStyleUnit_Turn; break; + default: NS_NOTREACHED("unrecognized angular unit"); + unit = eStyleUnit_Degree; + } + aCoord.SetAngleValue(aValue.GetAngleValue(), unit); + } else { result = false; // didn't set anything } @@ -987,18 +1001,9 @@ static void SetGradient(const nsCSSValue& aValue, nsPresContext* aPresContext, aResult.mRepeating = gradient->mIsRepeating; // angle - if (gradient->mAngle.IsAngularUnit()) { - nsStyleUnit unit; - switch (gradient->mAngle.GetUnit()) { - case eCSSUnit_Degree: unit = eStyleUnit_Degree; break; - case eCSSUnit_Grad: unit = eStyleUnit_Grad; break; - case eCSSUnit_Radian: unit = eStyleUnit_Radian; break; - case eCSSUnit_Turn: unit = eStyleUnit_Turn; break; - default: NS_NOTREACHED("unrecognized angular unit"); - unit = eStyleUnit_Degree; - } - aResult.mAngle.SetAngleValue(gradient->mAngle.GetAngleValue(), unit); - } else { + const nsStyleCoord dummyParentCoord; + if (!SetCoord(gradient->mAngle, aResult.mAngle, dummyParentCoord, SETCOORD_ANGLE, + aContext, aPresContext, aCanStoreInRuleTree)) { NS_ASSERTION(gradient->mAngle.GetUnit() == eCSSUnit_None, "bad unit for gradient angle"); aResult.mAngle.SetNoneValue(); @@ -5249,6 +5254,43 @@ nsRuleNode::ComputeVisibilityData(void* aStartStruct, parentVisibility->mWritingMode, NS_STYLE_WRITING_MODE_HORIZONTAL_TB, 0, 0, 0, 0); + // image-orientation: enum, inherit, initial + const nsCSSValue* orientation = aRuleData->ValueForImageOrientation(); + if (orientation->GetUnit() == eCSSUnit_Inherit) { + canStoreInRuleTree = false; + visibility->mImageOrientation = parentVisibility->mImageOrientation; + } else if (orientation->GetUnit() == eCSSUnit_Initial) { + visibility->mImageOrientation = nsStyleImageOrientation(); + } else if (orientation->IsAngularUnit()) { + double angle = orientation->GetAngleValueInRadians(); + visibility->mImageOrientation = + nsStyleImageOrientation::CreateAsAngleAndFlip(angle, false); + } else if (orientation->GetUnit() == eCSSUnit_Array) { + const nsCSSValue::Array* array = orientation->GetArrayValue(); + MOZ_ASSERT(array->Item(0).IsAngularUnit(), + "First image-orientation value is not an angle"); + MOZ_ASSERT(array->Item(1).GetUnit() == eCSSUnit_Enumerated && + array->Item(1).GetIntValue() == NS_STYLE_IMAGE_ORIENTATION_FLIP, + "Second image-orientation value is not 'flip'"); + double angle = array->Item(0).GetAngleValueInRadians(); + visibility->mImageOrientation = + nsStyleImageOrientation::CreateAsAngleAndFlip(angle, true); + + } else if (orientation->GetUnit() == eCSSUnit_Enumerated) { + switch (orientation->GetIntValue()) { + case NS_STYLE_IMAGE_ORIENTATION_FLIP: + visibility->mImageOrientation = nsStyleImageOrientation::CreateAsFlip(); + break; + case NS_STYLE_IMAGE_ORIENTATION_FROM_IMAGE: + visibility->mImageOrientation = nsStyleImageOrientation::CreateAsFromImage(); + break; + default: + NS_NOTREACHED("Invalid image-orientation enumerated value"); + } + } else { + MOZ_ASSERT(orientation->GetUnit() == eCSSUnit_Null, "Should be null unit"); + } + COMPUTE_END_INHERITED(Visibility, visibility) } @@ -7679,6 +7721,74 @@ nsRuleNode::ComputeSVGData(void* aStartStruct, COMPUTE_END_INHERITED(SVG, svg) } +static nsStyleFilter::Type +StyleFilterTypeForFunctionName(nsCSSKeyword functionName) +{ + switch (functionName) { + case eCSSKeyword_blur: + return nsStyleFilter::Type::eBlur; + case eCSSKeyword_brightness: + return nsStyleFilter::Type::eBrightness; + case eCSSKeyword_contrast: + return nsStyleFilter::Type::eContrast; + case eCSSKeyword_grayscale: + return nsStyleFilter::Type::eGrayscale; + case eCSSKeyword_hue_rotate: + return nsStyleFilter::Type::eHueRotate; + case eCSSKeyword_invert: + return nsStyleFilter::Type::eInvert; + case eCSSKeyword_opacity: + return nsStyleFilter::Type::eOpacity; + case eCSSKeyword_saturate: + return nsStyleFilter::Type::eSaturate; + case eCSSKeyword_sepia: + return nsStyleFilter::Type::eSepia; + default: + NS_NOTREACHED("Unknown filter type."); + return nsStyleFilter::Type::eNull; + } +} + +static void +SetStyleFilterToCSSValue(nsStyleFilter* aStyleFilter, + const nsCSSValue& aValue, + nsStyleContext* aStyleContext, + nsPresContext* aPresContext, + bool& aCanStoreInRuleTree) +{ + nsCSSUnit unit = aValue.GetUnit(); + if (unit == eCSSUnit_URL) { + aStyleFilter->mType = nsStyleFilter::Type::eURL; + aStyleFilter->mURL = aValue.GetURLValue(); + return; + } + + NS_ABORT_IF_FALSE(unit == eCSSUnit_Function, "expected a filter function"); + + nsCSSValue::Array* filterFunction = aValue.GetArrayValue(); + nsCSSKeyword functionName = + (nsCSSKeyword)filterFunction->Item(0).GetIntValue(); + aStyleFilter->mType = StyleFilterTypeForFunctionName(functionName); + + int32_t mask = SETCOORD_PERCENT | SETCOORD_FACTOR; + if (aStyleFilter->mType == nsStyleFilter::Type::eBlur) { + mask = SETCOORD_LENGTH | SETCOORD_STORE_CALC; + } else if (aStyleFilter->mType == nsStyleFilter::Type::eHueRotate) { + mask = SETCOORD_ANGLE; + } + + NS_ABORT_IF_FALSE(filterFunction->Count() == 2, + "all filter functions except drop-shadow should have " + "exactly one argument"); + + nsCSSValue& arg = filterFunction->Item(1); + DebugOnly<bool> success = SetCoord(arg, aStyleFilter->mFilterParameter, + nsStyleCoord(), mask, + aStyleContext, aPresContext, + aCanStoreInRuleTree); + NS_ABORT_IF_FALSE(success, "unexpected unit"); +} + const void* nsRuleNode::ComputeSVGResetData(void* aStartStruct, const nsRuleData* aRuleData, @@ -7755,14 +7865,34 @@ nsRuleNode::ComputeSVGResetData(void* aStartStruct, // filter: url, none, inherit const nsCSSValue* filterValue = aRuleData->ValueForFilter(); - if (eCSSUnit_URL == filterValue->GetUnit()) { - svgReset->mFilter = filterValue->GetURLValue(); - } else if (eCSSUnit_None == filterValue->GetUnit() || - eCSSUnit_Initial == filterValue->GetUnit()) { - svgReset->mFilter = nullptr; - } else if (eCSSUnit_Inherit == filterValue->GetUnit()) { - canStoreInRuleTree = false; - svgReset->mFilter = parentSVGReset->mFilter; + switch (filterValue->GetUnit()) { + case eCSSUnit_Null: + break; + case eCSSUnit_None: + case eCSSUnit_Initial: + svgReset->mFilters.Clear(); + break; + case eCSSUnit_Inherit: + canStoreInRuleTree = false; + svgReset->mFilters = parentSVGReset->mFilters; + break; + case eCSSUnit_List: + case eCSSUnit_ListDep: { + svgReset->mFilters.Clear(); + const nsCSSValueList* cur = filterValue->GetListValue(); + while(cur) { + nsStyleFilter styleFilter; + SetStyleFilterToCSSValue(&styleFilter, cur->mValue, aContext, + mPresContext, canStoreInRuleTree); + NS_ABORT_IF_FALSE(styleFilter.mType != nsStyleFilter::Type::eNull, + "filter should be set"); + svgReset->mFilters.AppendElement(styleFilter); + cur = cur->mNext; + } + break; + } + default: + NS_NOTREACHED("unexpected unit"); } // mask: url, none, inherit diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 7ee647240..2d3e70942 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -977,6 +977,48 @@ nsChangeHint nsStyleSVG::CalcDifference(const nsStyleSVG& aOther) const } // -------------------- +// nsStyleFilter +// +nsStyleFilter::nsStyleFilter() + : mType(eNull) +{ + MOZ_COUNT_CTOR(nsStyleFilter); +} + +nsStyleFilter::nsStyleFilter(const nsStyleFilter& aSource) + : mType(aSource.mType) +{ + MOZ_COUNT_CTOR(nsStyleFilter); + + if (mType == eURL) { + mURL = aSource.mURL; + } else if (mType != eNull) { + mFilterParameter = aSource.mFilterParameter; + } +} + +nsStyleFilter::~nsStyleFilter() +{ + MOZ_COUNT_DTOR(nsStyleFilter); +} + +bool +nsStyleFilter::operator==(const nsStyleFilter& aOther) const +{ + if (mType != aOther.mType) { + return false; + } + + if (mType == eURL) { + return EqualURIs(mURL, aOther.mURL); + } else if (mType != eNull) { + return mFilterParameter == aOther.mFilterParameter; + } + + return true; +} + +// -------------------- // nsStyleSVGReset // nsStyleSVGReset::nsStyleSVGReset() @@ -986,7 +1028,6 @@ nsStyleSVGReset::nsStyleSVGReset() mFloodColor = NS_RGB(0,0,0); mLightingColor = NS_RGB(255,255,255); mClipPath = nullptr; - mFilter = nullptr; mMask = nullptr; mStopOpacity = 1.0f; mFloodOpacity = 1.0f; @@ -1007,7 +1048,7 @@ nsStyleSVGReset::nsStyleSVGReset(const nsStyleSVGReset& aSource) mFloodColor = aSource.mFloodColor; mLightingColor = aSource.mLightingColor; mClipPath = aSource.mClipPath; - mFilter = aSource.mFilter; + mFilters = aSource.mFilters; mMask = aSource.mMask; mStopOpacity = aSource.mStopOpacity; mFloodOpacity = aSource.mFloodOpacity; @@ -1021,8 +1062,8 @@ nsChangeHint nsStyleSVGReset::CalcDifference(const nsStyleSVGReset& aOther) cons nsChangeHint hint = nsChangeHint(0); if (!EqualURIs(mClipPath, aOther.mClipPath) || - !EqualURIs(mFilter, aOther.mFilter) || - !EqualURIs(mMask, aOther.mMask)) { + !EqualURIs(mMask, aOther.mMask) || + mFilters != aOther.mFilters) { NS_UpdateHint(hint, nsChangeHint_UpdateEffects); NS_UpdateHint(hint, nsChangeHint_RepaintFrame); } @@ -2311,6 +2352,7 @@ nsStyleVisibility::nsStyleVisibility(nsPresContext* aPresContext) nsStyleVisibility::nsStyleVisibility(const nsStyleVisibility& aSource) { MOZ_COUNT_CTOR(nsStyleVisibility); + mImageOrientation = aSource.mImageOrientation; mDirection = aSource.mDirection; mVisible = aSource.mVisible; mPointerEvents = aSource.mPointerEvents; @@ -2324,6 +2366,9 @@ nsChangeHint nsStyleVisibility::CalcDifference(const nsStyleVisibility& aOther) if (mDirection != aOther.mDirection || mWritingMode != aOther.mWritingMode) { NS_UpdateHint(hint, nsChangeHint_ReconstructFrame); } else { + if ((mImageOrientation != aOther.mImageOrientation)) { + NS_UpdateHint(hint, nsChangeHint_AllReflowHints); + } if (mVisible != aOther.mVisible) { if ((NS_STYLE_VISIBILITY_COLLAPSE == mVisible) || (NS_STYLE_VISIBILITY_COLLAPSE == aOther.mVisible)) { diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index d3d1fe349..95abf8096 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -28,6 +28,7 @@ #include "nsIAtom.h" #include "nsCSSValue.h" #include "imgRequestProxy.h" +#include "Orientation.h" #include <algorithm> class nsIFrame; @@ -1347,6 +1348,94 @@ struct nsStyleText { inline bool WordCanWrap(const nsIFrame* aContextFrame) const; }; +struct nsStyleImageOrientation { + static nsStyleImageOrientation CreateAsAngleAndFlip(double aRadians, + bool aFlip) { + uint8_t orientation(0); + + // Compute the final angle value, rounding to the closest quarter turn. + double roundedAngle = fmod(aRadians, 2 * M_PI); + if (roundedAngle < 0.25 * M_PI) orientation = ANGLE_0; + else if (roundedAngle < 0.75 * M_PI) orientation = ANGLE_90; + else if (roundedAngle < 1.25 * M_PI) orientation = ANGLE_180; + else if (roundedAngle < 1.75 * M_PI) orientation = ANGLE_270; + else orientation = ANGLE_0; + + // Add a bit for 'flip' if needed. + if (aFlip) + orientation |= FLIP_MASK; + + return nsStyleImageOrientation(orientation); + } + + static nsStyleImageOrientation CreateAsFlip() { + return nsStyleImageOrientation(FLIP_MASK); + } + + static nsStyleImageOrientation CreateAsFromImage() { + return nsStyleImageOrientation(FROM_IMAGE_MASK); + } + + // The default constructor yields 0 degrees of rotation and no flip. + nsStyleImageOrientation() : mOrientation(0) { } + + bool IsDefault() const { return mOrientation == 0; } + bool IsFlipped() const { return mOrientation & FLIP_MASK; } + bool IsFromImage() const { return mOrientation & FROM_IMAGE_MASK; } + + mozilla::image::Angle Angle() const { + switch (mOrientation & ORIENTATION_MASK) { + case ANGLE_0: return mozilla::image::Angle::D0; + case ANGLE_90: return mozilla::image::Angle::D90; + case ANGLE_180: return mozilla::image::Angle::D180; + case ANGLE_270: return mozilla::image::Angle::D270; + default: + NS_NOTREACHED("Unexpected angle"); + return mozilla::image::Angle::D0; + } + } + + nsStyleCoord AngleAsCoord() const { + switch (mOrientation & ORIENTATION_MASK) { + case ANGLE_0: return nsStyleCoord(0.0f, eStyleUnit_Degree); + case ANGLE_90: return nsStyleCoord(90.0f, eStyleUnit_Degree); + case ANGLE_180: return nsStyleCoord(180.0f, eStyleUnit_Degree); + case ANGLE_270: return nsStyleCoord(270.0f, eStyleUnit_Degree); + default: + NS_NOTREACHED("Unexpected angle"); + return nsStyleCoord(); + } + } + + bool operator==(const nsStyleImageOrientation& aOther) const { + return aOther.mOrientation == mOrientation; + } + + bool operator!=(const nsStyleImageOrientation& aOther) const { + return !(*this == aOther); + } + +protected: + enum Bits { + ORIENTATION_MASK = 0x1 | 0x2, // The bottom two bits are the angle. + FLIP_MASK = 0x4, // Whether the image should be flipped. + FROM_IMAGE_MASK = 0x8, // Whether the image's inherent orientation + }; // should be used. + + enum Angles { + ANGLE_0 = 0, + ANGLE_90 = 1, + ANGLE_180 = 2, + ANGLE_270 = 3, + }; + + explicit nsStyleImageOrientation(uint8_t aOrientation) + : mOrientation(aOrientation) + { } + + uint8_t mOrientation; +}; + struct nsStyleVisibility { nsStyleVisibility(nsPresContext* aPresContext); nsStyleVisibility(const nsStyleVisibility& aVisibility); @@ -1367,6 +1456,7 @@ struct nsStyleVisibility { return NS_STYLE_HINT_FRAMECHANGE; } + nsStyleImageOrientation mImageOrientation; // [inherited] uint8_t mDirection; // [inherited] see nsStyleConsts.h NS_STYLE_DIRECTION_* uint8_t mVisible; // [inherited] uint8_t mPointerEvents; // [inherited] see nsStyleConsts.h @@ -2252,6 +2342,33 @@ struct nsStyleSVG { bool mStrokeWidthFromObject : 1; }; +struct nsStyleFilter { + nsStyleFilter(); + nsStyleFilter(const nsStyleFilter& aSource); + ~nsStyleFilter(); + + bool operator==(const nsStyleFilter& aOther) const; + + enum Type { + eNull, + eURL, + eBlur, + eBrightness, + eContrast, + eHueRotate, + eInvert, + eOpacity, + eGrayscale, + eSaturate, + eSepia, + }; + + Type mType; + nsIURI* mURL; + nsStyleCoord mFilterParameter; // coord, percent, factor, angle + // FIXME: Add a nsCSSShadowItem when we implement drop shadow. +}; + struct nsStyleSVGReset { nsStyleSVGReset(); nsStyleSVGReset(const nsStyleSVGReset& aSource); @@ -2270,8 +2387,17 @@ struct nsStyleSVGReset { return NS_CombineHint(nsChangeHint_UpdateEffects, NS_STYLE_HINT_REFLOW); } + // The backend only supports one SVG reference right now. + // Eventually, it will support multiple chained SVG reference filters and CSS + // filter functions. + nsIURI* SingleFilter() const { + return (mFilters.Length() == 1 && + mFilters[0].mType == nsStyleFilter::Type::eURL) ? + mFilters[0].mURL : nullptr; + } + nsCOMPtr<nsIURI> mClipPath; // [reset] - nsCOMPtr<nsIURI> mFilter; // [reset] + nsTArray<nsStyleFilter> mFilters; // [reset] nsCOMPtr<nsIURI> mMask; // [reset] nscolor mStopColor; // [reset] nscolor mFloodColor; // [reset] diff --git a/layout/style/nsStyleUtil.cpp b/layout/style/nsStyleUtil.cpp index d2e5bd18d..e9b1ca39e 100644 --- a/layout/style/nsStyleUtil.cpp +++ b/layout/style/nsStyleUtil.cpp @@ -10,6 +10,7 @@ #include "nsReadableUtils.h" #include "nsCSSProps.h" #include "nsRuleNode.h" +#include "nsROCSSPrimitiveValue.h" #include "nsIContentSecurityPolicy.h" using namespace mozilla; @@ -157,6 +158,23 @@ nsStyleUtil::AppendBitmaskCSSValue(nsCSSProperty aProperty, } /* static */ void +nsStyleUtil::AppendAngleValue(const nsStyleCoord& aAngle, nsAString& aResult) +{ + MOZ_ASSERT(aAngle.IsAngleValue(), "Should have angle value"); + + AppendCSSNumber(aAngle.GetAngleValue(), aResult); + + // Append unit. + switch (aAngle.GetUnit()) { + case eStyleUnit_Degree: aResult.AppendLiteral("deg"); break; + case eStyleUnit_Grad: aResult.AppendLiteral("grad"); break; + case eStyleUnit_Radian: aResult.AppendLiteral("rad"); break; + case eStyleUnit_Turn: aResult.AppendLiteral("turn"); break; + default: NS_NOTREACHED("unrecognized angle unit"); + } +} + +/* static */ void nsStyleUtil::AppendPaintOrderValue(uint8_t aValue, nsAString& aResult) { diff --git a/layout/style/nsStyleUtil.h b/layout/style/nsStyleUtil.h index 895b28fe1..5a10e0872 100644 --- a/layout/style/nsStyleUtil.h +++ b/layout/style/nsStyleUtil.h @@ -13,6 +13,7 @@ class nsCSSValue; class nsStringComparator; +class nsStyleCoord; class nsIContent; struct gfxFontFeature; class nsCSSValueList; @@ -45,6 +46,8 @@ public: int32_t aLastMask, nsAString& aResult); + static void AppendAngleValue(const nsStyleCoord& aValue, nsAString& aResult); + static void AppendPaintOrderValue(uint8_t aValue, nsAString& aResult); static void AppendFontFeatureSettings(const nsTArray<gfxFontFeature>& aFeatures, diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 69363a711..fd700a502 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -4421,3 +4421,275 @@ if (SpecialPowers.getBoolPref("svg.paint-order.enabled")) { invalid_values: [ "fill stroke markers fill", "fill normal" ] }; } + +if (SpecialPowers.getBoolPref("layout.css.filters.enabled")) { + gCSSProperties["filter"] = { + domProp: "filter", + inherited: false, + type: CSS_TYPE_LONGHAND, + initial_values: [ "none" ], + other_values: [ + // SVG reference filters + "url(#my-filter)", + "url(#my-filter-1) url(#my-filter-2)", + + // Filter functions + "opacity(50%) saturate(1.0)", + "invert(50%) sepia(0.1) brightness(90%)", + + // Mixed SVG reference filters and filter functions + "grayscale(1) url(#my-filter-1)", + "url(#my-filter-1) brightness(50%) contrast(0.9)", + + "blur(0)", + "blur(0px)", + "blur(0.5px)", + "blur(3px)", + "blur(100px)", + "blur(0.1em)", + "blur(calc(-1px))", // Parses and becomes blur(0px). + "blur(calc(0px))", + "blur(calc(5px))", + "blur(calc(2 * 5px))", + + "brightness(0)", + "brightness(50%)", + "brightness(1)", + "brightness(1.0)", + "brightness(2)", + "brightness(350%)", + "brightness(4.567)", + + "contrast(0)", + "contrast(50%)", + "contrast(1)", + "contrast(1.0)", + "contrast(2)", + "contrast(350%)", + "contrast(4.567)", + + "grayscale(0)", + "grayscale(50%)", + "grayscale(1)", + "grayscale(1.0)", + "grayscale(2)", + "grayscale(350%)", + "grayscale(4.567)", + + "hue-rotate(0deg)", + "hue-rotate(90deg)", + "hue-rotate(540deg)", + "hue-rotate(-90deg)", + "hue-rotate(10grad)", + "hue-rotate(1.6rad)", + "hue-rotate(-1.6rad)", + "hue-rotate(0.5turn)", + "hue-rotate(-2turn)", + + "invert(0)", + "invert(50%)", + "invert(1)", + "invert(1.0)", + "invert(2)", + "invert(350%)", + "invert(4.567)", + + "opacity(0)", + "opacity(50%)", + "opacity(1)", + "opacity(1.0)", + "opacity(2)", + "opacity(350%)", + "opacity(4.567)", + + "saturate(0)", + "saturate(50%)", + "saturate(1)", + "saturate(1.0)", + "saturate(2)", + "saturate(350%)", + "saturate(4.567)", + + "sepia(0)", + "sepia(50%)", + "sepia(1)", + "sepia(1.0)", + "sepia(2)", + "sepia(350%)", + "sepia(4.567)", + ], + invalid_values: [ + // none + "none none", + "url(#my-filter) none", + "none url(#my-filter)", + "blur(2px) none url(#my-filter)", + + // Nested filters + "grayscale(invert(1.0))", + + // Comma delimited filters + "url(#my-filter),", + "invert(50%), url(#my-filter), brightness(90%)", + + // Test the following situations for each filter function: + // - Invalid number of arguments + // - Comma delimited arguments + // - Wrong argument type + // - Argument value out of range + "blur()", + "blur(3px 5px)", + "blur(3px,)", + "blur(3px, 5px)", + "blur(#my-filter)", + "blur(0.5)", + "blur(50%)", + "blur(calc(0))", // Unitless zero in calc is not a valid length. + "blur(calc(0.1))", + "blur(calc(10%))", + "blur(calc(20px - 5%))", + "blur(-3px)", + + "brightness()", + "brightness(0.5 0.5)", + "brightness(0.5,)", + "brightness(0.5, 0.5)", + "brightness(#my-filter)", + "brightness(10px)", + "brightness(-1)", + + "contrast()", + "contrast(0.5 0.5)", + "contrast(0.5,)", + "contrast(0.5, 0.5)", + "contrast(#my-filter)", + "contrast(10px)", + "contrast(-1)", + + "grayscale()", + "grayscale(0.5 0.5)", + "grayscale(0.5,)", + "grayscale(0.5, 0.5)", + "grayscale(#my-filter)", + "grayscale(10px)", + "grayscale(-1)", + + "hue-rotate()", + "hue-rotate(0)", + "hue-rotate(0.5 0.5)", + "hue-rotate(0.5,)", + "hue-rotate(0.5, 0.5)", + "hue-rotate(#my-filter)", + "hue-rotate(10px)", + "hue-rotate(-1)", + "hue-rotate(45deg,)", + + "invert()", + "invert(0.5 0.5)", + "invert(0.5,)", + "invert(0.5, 0.5)", + "invert(#my-filter)", + "invert(10px)", + "invert(-1)", + + "opacity()", + "opacity(0.5 0.5)", + "opacity(0.5,)", + "opacity(0.5, 0.5)", + "opacity(#my-filter)", + "opacity(10px)", + "opacity(-1)", + + "saturate()", + "saturate(0.5 0.5)", + "saturate(0.5,)", + "saturate(0.5, 0.5)", + "saturate(#my-filter)", + "saturate(10px)", + "saturate(-1)", + + "sepia()", + "sepia(0.5 0.5)", + "sepia(0.5,)", + "sepia(0.5, 0.5)", + "sepia(#my-filter)", + "sepia(10px)", + "sepia(-1)", + ] + }; +} + +if (SpecialPowers.getBoolPref("layout.css.image-orientation.enabled")) { + gCSSProperties["image-orientation"] = { + domProp: "imageOrientation", + inherited: true, + type: CSS_TYPE_LONGHAND, + initial_values: [ + "0deg", + "0grad", + "0rad", + "0turn", + + // Rounded initial values. + "-90deg", + "15deg", + "360deg", + ], + other_values: [ + "0deg flip", + "90deg", + "90deg flip", + "180deg", + "180deg flip", + "270deg", + "270deg flip", + "flip", + "from-image", + + // Grad units. + "0grad flip", + "100grad", + "100grad flip", + "200grad", + "200grad flip", + "300grad", + "300grad flip", + + // Radian units. + "0rad flip", + "1.57079633rad", + "1.57079633rad flip", + "3.14159265rad", + "3.14159265rad flip", + "4.71238898rad", + "4.71238898rad flip", + + // Turn units. + "0turn flip", + "0.25turn", + "0.25turn flip", + "0.5turn", + "0.5turn flip", + "0.75turn", + "0.75turn flip", + + // Rounded values. + "-45deg flip", + "65deg flip", + "400deg flip", + ], + invalid_values: [ + "none", + "0deg none", + "flip 0deg", + "flip 0deg", + "0", + "0 flip", + "flip 0", + "0deg from-image", + "from-image 0deg", + "flip from-image", + "from-image flip", + ] + }; +} diff --git a/layout/svg/nsSVGEffects.cpp b/layout/svg/nsSVGEffects.cpp index 97b75db48..e0f740a1f 100644 --- a/layout/svg/nsSVGEffects.cpp +++ b/layout/svg/nsSVGEffects.cpp @@ -450,7 +450,7 @@ nsSVGEffects::GetEffectProperties(nsIFrame *aFrame) EffectProperties result; const nsStyleSVGReset *style = aFrame->StyleSVGReset(); result.mFilter = static_cast<nsSVGFilterProperty*> - (GetEffectProperty(style->mFilter, aFrame, FilterProperty(), + (GetEffectProperty(style->SingleFilter(), aFrame, FilterProperty(), CreateFilterProperty)); result.mClipPath = GetPaintingProperty(style->mClipPath, aFrame, ClipPathProperty()); @@ -526,7 +526,7 @@ nsSVGEffects::UpdateEffects(nsIFrame *aFrame) // Ensure that the filter is repainted correctly // We can't do that in DoUpdate as the referenced frame may not be valid - GetEffectProperty(aFrame->StyleSVGReset()->mFilter, + GetEffectProperty(aFrame->StyleSVGReset()->SingleFilter(), aFrame, FilterProperty(), CreateFilterProperty); if (aFrame->GetType() == nsGkAtoms::svgPathGeometryFrame && @@ -547,7 +547,7 @@ nsSVGEffects::GetFilterProperty(nsIFrame *aFrame) { NS_ASSERTION(!aFrame->GetPrevContinuation(), "aFrame should be first continuation"); - if (!aFrame->StyleSVGReset()->mFilter) + if (!aFrame->StyleSVGReset()->SingleFilter()) return nullptr; return static_cast<nsSVGFilterProperty *> diff --git a/layout/svg/nsSVGIntegrationUtils.cpp b/layout/svg/nsSVGIntegrationUtils.cpp index 8ad7527f1..8334038ad 100644 --- a/layout/svg/nsSVGIntegrationUtils.cpp +++ b/layout/svg/nsSVGIntegrationUtils.cpp @@ -150,7 +150,7 @@ nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame) // checking the SDL prefs here, since we don't know if we're being called for // painting or hit-testing anyway. const nsStyleSVGReset *style = aFrame->StyleSVGReset(); - return (style->mFilter || style->mClipPath || style->mMask); + return (style->SingleFilter() || style->mClipPath || style->mMask); } /* static */ nsPoint diff --git a/layout/svg/nsSVGUtils.cpp b/layout/svg/nsSVGUtils.cpp index ea4c0df8a..96d1e6668 100644 --- a/layout/svg/nsSVGUtils.cpp +++ b/layout/svg/nsSVGUtils.cpp @@ -1269,7 +1269,7 @@ nsSVGUtils::CanOptimizeOpacity(nsIFrame *aFrame) type != nsGkAtoms::svgPathGeometryFrame) { return false; } - if (aFrame->StyleSVGReset()->mFilter) { + if (aFrame->StyleSVGReset()->SingleFilter()) { return false; } // XXX The SVG WG is intending to allow fill, stroke and markers on <image> diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 3cc6328fe..be7e74515 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -1764,9 +1764,15 @@ pref("layout.css.masking.enabled", true); // Is support for the the @supports rule enabled? pref("layout.css.supports-rule.enabled", true); +// Is support for CSS Filters enabled? +pref("layout.css.filters.enabled", false); + // Is support for CSS Flexbox enabled? pref("layout.css.flexbox.enabled", true); +// Is support for the CSS4 image-orientation property enabled? +pref("layout.css.image-orientation.enabled", true); + // Is support for CSS3 Fonts features enabled? // (includes font-variant-*, font-kerning, font-synthesis // and the @font-feature-values rule) |