summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPale Moon <git-repo@palemoon.org>2014-08-18 15:09:26 +0200
committerPale Moon <git-repo@palemoon.org>2014-08-18 15:09:26 +0200
commit7fc5e7c272599e9bfe9e5138cf8d842c4575d403 (patch)
tree1d007a7788bb9535283d1332b5a41dd645236699
parentd3f28b16be7d885ce4d85b620a2d97867b0d6468 (diff)
downloadpalemoon-gre-7fc5e7c272599e9bfe9e5138cf8d842c4575d403.tar.gz
Implement CSS basic filters, CSS hue-rotate, CSS image-orientation, and EXIF reader.
-rw-r--r--dom/locales/en-US/chrome/layout/css.properties5
-rw-r--r--gfx/thebes/gfxPlatform.cpp1
-rw-r--r--image/decoders/EXIF.cpp321
-rw-r--r--image/decoders/EXIF.h76
-rw-r--r--image/decoders/moz.build1
-rw-r--r--image/decoders/nsJPEGDecoder.cpp25
-rw-r--r--image/decoders/nsJPEGDecoder.h2
-rw-r--r--image/public/imgIContainer.idl15
-rw-r--r--image/src/ClippedImage.cpp9
-rw-r--r--image/src/ClippedImage.h1
-rw-r--r--image/src/Decoder.cpp13
-rw-r--r--image/src/Decoder.h5
-rw-r--r--image/src/ImageMetadata.h7
-rw-r--r--image/src/ImageOps.cpp16
-rw-r--r--image/src/ImageOps.h11
-rw-r--r--image/src/ImageWrapper.cpp7
-rw-r--r--image/src/Orientation.h62
-rw-r--r--image/src/OrientedImage.cpp254
-rw-r--r--image/src/OrientedImage.h67
-rw-r--r--image/src/RasterImage.cpp13
-rw-r--r--image/src/RasterImage.h12
-rw-r--r--image/src/VectorImage.cpp7
-rw-r--r--image/src/imgFrame.cpp2
-rw-r--r--image/src/moz.build2
-rw-r--r--layout/base/nsLayoutUtils.cpp41
-rw-r--r--layout/base/nsLayoutUtils.h17
-rw-r--r--layout/base/nsStyleConsts.h4
-rw-r--r--layout/generic/nsFrame.cpp2
-rw-r--r--layout/generic/nsImageFrame.cpp92
-rw-r--r--layout/generic/nsImageFrame.h1
-rw-r--r--layout/style/TopLevelImageDocument.css4
-rw-r--r--layout/style/nsCSSKeywordList.h11
-rw-r--r--layout/style/nsCSSParser.cpp197
-rw-r--r--layout/style/nsCSSPropList.h15
-rw-r--r--layout/style/nsCSSProps.cpp11
-rw-r--r--layout/style/nsCSSProps.h2
-rw-r--r--layout/style/nsComputedDOMStyle.cpp147
-rw-r--r--layout/style/nsComputedDOMStyle.h6
-rw-r--r--layout/style/nsROCSSPrimitiveValue.cpp66
-rw-r--r--layout/style/nsROCSSPrimitiveValue.h9
-rw-r--r--layout/style/nsRuleNode.cpp170
-rw-r--r--layout/style/nsStyleStruct.cpp53
-rw-r--r--layout/style/nsStyleStruct.h128
-rw-r--r--layout/style/nsStyleUtil.cpp18
-rw-r--r--layout/style/nsStyleUtil.h3
-rw-r--r--layout/style/test/property_database.js272
-rw-r--r--layout/svg/nsSVGEffects.cpp6
-rw-r--r--layout/svg/nsSVGIntegrationUtils.cpp2
-rw-r--r--layout/svg/nsSVGUtils.cpp2
-rw-r--r--modules/libpref/src/init/all.js6
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)