From 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Tue, 27 Jun 2017 06:07:23 +0000 Subject: webkitgtk-2.16.5 --- .../Tests/WebCore/AffineTransform.cpp | 1024 +++++++ Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp | 251 ++ Tools/TestWebKitAPI/Tests/WebCore/CSSParser.cpp | 84 + .../Tests/WebCore/CalculationValue.cpp | 283 ++ Tools/TestWebKitAPI/Tests/WebCore/Color.cpp | 190 ++ .../Tests/WebCore/ComplexTextController.cpp | 391 +++ .../Tests/WebCore/ContentExtensions.cpp | 2795 ++++++++++++++++++++ Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp | 121 + Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h | 67 + Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp | 121 + .../TestWebKitAPI/Tests/WebCore/ExtendedColor.cpp | 162 ++ Tools/TestWebKitAPI/Tests/WebCore/FileSystem.cpp | 122 + Tools/TestWebKitAPI/Tests/WebCore/FloatPoint.cpp | 598 +++++ Tools/TestWebKitAPI/Tests/WebCore/FloatRect.cpp | 783 ++++++ Tools/TestWebKitAPI/Tests/WebCore/FloatSize.cpp | 328 +++ Tools/TestWebKitAPI/Tests/WebCore/GridPosition.cpp | 73 + .../Tests/WebCore/HTMLParserIdioms.cpp | 164 ++ Tools/TestWebKitAPI/Tests/WebCore/IntPoint.cpp | 283 ++ Tools/TestWebKitAPI/Tests/WebCore/IntRect.cpp | 615 +++++ Tools/TestWebKitAPI/Tests/WebCore/IntSize.cpp | 332 +++ Tools/TestWebKitAPI/Tests/WebCore/LayoutUnit.cpp | 42 +- .../Tests/WebCore/ParsedContentRange.cpp | 98 + Tools/TestWebKitAPI/Tests/WebCore/PublicSuffix.cpp | 78 + Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp | 224 ++ .../TestWebKitAPI/Tests/WebCore/SecurityOrigin.cpp | 146 + Tools/TestWebKitAPI/Tests/WebCore/SharedBuffer.cpp | 163 ++ .../Tests/WebCore/SharedBufferTest.cpp | 56 + .../TestWebKitAPI/Tests/WebCore/SharedBufferTest.h | 46 + Tools/TestWebKitAPI/Tests/WebCore/TimeRanges.cpp | 291 ++ .../Tests/WebCore/TransformationMatrix.cpp | 1317 +++++++++ Tools/TestWebKitAPI/Tests/WebCore/URL.cpp | 54 +- Tools/TestWebKitAPI/Tests/WebCore/URLParser.cpp | 1305 +++++++++ .../Tests/WebCore/UserAgentQuirks.cpp | 96 + .../Tests/WebCore/YouTubePluginReplacement.cpp | 86 + .../Tests/WebCore/gtk/UserAgentQuirks.cpp | 63 - 35 files changed, 12762 insertions(+), 90 deletions(-) create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/AffineTransform.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/CSSParser.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/CalculationValue.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/Color.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/ComplexTextController.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/ExtendedColor.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/FileSystem.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/FloatPoint.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/FloatRect.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/FloatSize.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/GridPosition.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/HTMLParserIdioms.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/IntPoint.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/IntRect.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/IntSize.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/PublicSuffix.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/SecurityOrigin.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/SharedBuffer.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/SharedBufferTest.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/SharedBufferTest.h create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/TimeRanges.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/TransformationMatrix.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/URLParser.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/UserAgentQuirks.cpp create mode 100644 Tools/TestWebKitAPI/Tests/WebCore/YouTubePluginReplacement.cpp delete mode 100644 Tools/TestWebKitAPI/Tests/WebCore/gtk/UserAgentQuirks.cpp (limited to 'Tools/TestWebKitAPI/Tests/WebCore') diff --git a/Tools/TestWebKitAPI/Tests/WebCore/AffineTransform.cpp b/Tools/TestWebKitAPI/Tests/WebCore/AffineTransform.cpp new file mode 100644 index 000000000..9bcd1a4de --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/AffineTransform.cpp @@ -0,0 +1,1024 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if USE(CG) +#include +#endif + +#if PLATFORM(WIN) +#include +#endif + +namespace TestWebKitAPI { + +static void testGetAndSet(WebCore::AffineTransform& affineTransform) +{ + affineTransform.setA(1.1); + EXPECT_DOUBLE_EQ(1.1, affineTransform.a()); + affineTransform.setB(2.2); + EXPECT_DOUBLE_EQ(2.2, affineTransform.b()); + affineTransform.setC(3.3); + EXPECT_DOUBLE_EQ(3.3, affineTransform.c()); + affineTransform.setD(4.4); + EXPECT_DOUBLE_EQ(4.4, affineTransform.d()); + affineTransform.setE(5.5); + EXPECT_DOUBLE_EQ(5.5, affineTransform.e()); + affineTransform.setF(6.6); + EXPECT_DOUBLE_EQ(6.6, affineTransform.f()); +} + +static void testIdentity(const WebCore::AffineTransform& transform) +{ + EXPECT_DOUBLE_EQ(1.0, transform.a()); + EXPECT_DOUBLE_EQ(0.0, transform.b()); + EXPECT_DOUBLE_EQ(0.0, transform.c()); + EXPECT_DOUBLE_EQ(1.0, transform.d()); + EXPECT_DOUBLE_EQ(0.0, transform.e()); + EXPECT_DOUBLE_EQ(0.0, transform.f()); +} + +TEST(AffineTransform, DefaultConstruction) +{ + WebCore::AffineTransform test; + + testIdentity(test); + testGetAndSet(test); + + ASSERT_FALSE(test.isIdentity()); +} + +static void testValueConstruction(const WebCore::AffineTransform& transform) +{ + EXPECT_DOUBLE_EQ(6.0, transform.a()); + EXPECT_DOUBLE_EQ(5.0, transform.b()); + EXPECT_DOUBLE_EQ(4.0, transform.c()); + EXPECT_DOUBLE_EQ(3.0, transform.d()); + EXPECT_DOUBLE_EQ(2.0, transform.e()); + EXPECT_DOUBLE_EQ(1.0, transform.f()); +} + +static void testDoubled(const WebCore::AffineTransform& transform) +{ + EXPECT_DOUBLE_EQ(12.0, transform.a()); + EXPECT_DOUBLE_EQ(10.0, transform.b()); + EXPECT_DOUBLE_EQ(8.0, transform.c()); + EXPECT_DOUBLE_EQ(6.0, transform.d()); + EXPECT_DOUBLE_EQ(2.0, transform.e()); + EXPECT_DOUBLE_EQ(1.0, transform.f()); +} + +static void testHalved(const WebCore::AffineTransform& transform) +{ + EXPECT_DOUBLE_EQ(3.0, transform.a()); + EXPECT_DOUBLE_EQ(2.5, transform.b()); + EXPECT_DOUBLE_EQ(2.0, transform.c()); + EXPECT_DOUBLE_EQ(1.5, transform.d()); + EXPECT_DOUBLE_EQ(2.0, transform.e()); + EXPECT_DOUBLE_EQ(1.0, transform.f()); +} + +TEST(AffineTransform, ValueConstruction) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test); + testGetAndSet(test); + + ASSERT_FALSE(test.isIdentity()); + ASSERT_FALSE(test.preservesAxisAlignment()); +} + +#if USE(CG) +TEST(AffineTransform, CGAffineTransformConstruction) +{ + CGAffineTransform cgTransform = CGAffineTransformMake(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::AffineTransform test(cgTransform); + + testValueConstruction(test); + testGetAndSet(test); + + ASSERT_FALSE(test.isIdentity()); +} +#endif + +#if PLATFORM(WIN) +TEST(AffineTransform, D2D1MatrixConstruction) +{ + D2D1_MATRIX_3X2_F d2dTransform = D2D1::Matrix3x2F(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::AffineTransform test(d2dTransform); + + testValueConstruction(test); + testGetAndSet(test); + + ASSERT_FALSE(test.isIdentity()); +} +#endif + +TEST(AffineTransform, Identity) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + ASSERT_FALSE(test.isIdentity()); + ASSERT_FALSE(test.isIdentityOrTranslation()); + ASSERT_FALSE(test.isIdentityOrTranslationOrFlipped()); + ASSERT_FALSE(test.preservesAxisAlignment()); + + test.makeIdentity(); + + ASSERT_TRUE(test.isIdentity()); + ASSERT_TRUE(test.isIdentityOrTranslation()); + ASSERT_TRUE(test.isIdentityOrTranslationOrFlipped()); + ASSERT_TRUE(test.preservesAxisAlignment()); + + testIdentity(test); +} + +TEST(AffineTransform, MapFloatPoint) +{ + WebCore::AffineTransform test; + WebCore::FloatPoint point(100.0f, 50.0f); + + auto mappedPoint = test.mapPoint(point); + + ASSERT_FLOAT_EQ(100.0f, mappedPoint.x()); + ASSERT_FLOAT_EQ(50.0f, mappedPoint.y()); + + test.setD(2.0); + + auto mappedPoint2 = test.mapPoint(point); + + ASSERT_FLOAT_EQ(100.0f, mappedPoint2.x()); + ASSERT_FLOAT_EQ(100.0f, mappedPoint2.y()); + + test.setA(0.5); + + auto mappedPoint3 = test.mapPoint(point); + + ASSERT_FLOAT_EQ(50.0f, mappedPoint3.x()); + ASSERT_FLOAT_EQ(100.0f, mappedPoint3.y()); +} + +TEST(AffineTransform, MapIntPoint) +{ + WebCore::AffineTransform test; + WebCore::IntPoint point(100, 50); + + auto mappedPoint = test.mapPoint(point); + + ASSERT_EQ(100, mappedPoint.x()); + ASSERT_EQ(50, mappedPoint.y()); + + test.setD(2.0); + + auto mappedPoint2 = test.mapPoint(point); + + ASSERT_EQ(100, mappedPoint2.x()); + ASSERT_EQ(100, mappedPoint2.y()); + + test.setA(0.5); + + auto mappedPoint3 = test.mapPoint(point); + + ASSERT_EQ(50, mappedPoint3.x()); + ASSERT_EQ(100, mappedPoint3.y()); +} + +TEST(AffineTransform, MapIntSize) +{ + WebCore::AffineTransform test; + WebCore::IntSize size(200, 300); + + auto mappedSize = test.mapSize(size); + + ASSERT_EQ(200, mappedSize.width()); + ASSERT_EQ(300, mappedSize.height()); + + test.setD(2.0); + + auto mappedSize2 = test.mapSize(size); + + ASSERT_EQ(200, mappedSize2.width()); + ASSERT_EQ(600, mappedSize2.height()); + + test.setA(0.5); + + auto mappedSize3 = test.mapSize(size); + + ASSERT_EQ(100, mappedSize3.width()); + ASSERT_EQ(600, mappedSize3.height()); +} + +TEST(AffineTransform, MapFloatSize) +{ + WebCore::AffineTransform test; + WebCore::FloatSize size(200.0f, 300.0f); + + auto mappedSize = test.mapSize(size); + + ASSERT_EQ(200.0f, mappedSize.width()); + ASSERT_EQ(300.0f, mappedSize.height()); + + test.setD(2.0); + + auto mappedSize2 = test.mapSize(size); + + ASSERT_EQ(200.0f, mappedSize2.width()); + ASSERT_EQ(600.0f, mappedSize2.height()); + + test.setA(0.5); + + auto mappedSize3 = test.mapSize(size); + + ASSERT_EQ(100.0f, mappedSize3.width()); + ASSERT_EQ(600.0f, mappedSize3.height()); +} + +TEST(AffineTransform, MapIntRect) +{ + WebCore::AffineTransform test; + WebCore::IntRect rect(10, 20, 200, 300); + + auto mappedRect = test.mapRect(rect); + + ASSERT_EQ(10, mappedRect.x()); + ASSERT_EQ(20, mappedRect.y()); + ASSERT_EQ(200, mappedRect.width()); + ASSERT_EQ(300, mappedRect.height()); + + test.setD(2.0); + + auto mappedRect2 = test.mapRect(rect); + + ASSERT_EQ(10, mappedRect2.x()); + ASSERT_EQ(40, mappedRect2.y()); + ASSERT_EQ(200, mappedRect2.width()); + ASSERT_EQ(600, mappedRect2.height()); + + test.setA(0.5); + + auto mappedRect3 = test.mapRect(rect); + + ASSERT_EQ(5, mappedRect3.x()); + ASSERT_EQ(40, mappedRect3.y()); + ASSERT_EQ(100, mappedRect3.width()); + ASSERT_EQ(600, mappedRect3.height()); +} + +TEST(AffineTransform, MapFloatRect) +{ + WebCore::AffineTransform test; + WebCore::FloatRect rect(10.f, 20.0f, 200.0f, 300.0f); + + auto mappedRect = test.mapRect(rect); + + ASSERT_FLOAT_EQ(10.0f, mappedRect.x()); + ASSERT_FLOAT_EQ(20.0f, mappedRect.y()); + ASSERT_FLOAT_EQ(200.0f, mappedRect.width()); + ASSERT_FLOAT_EQ(300.0f, mappedRect.height()); + + test.setD(2.0); + + auto mappedRect2 = test.mapRect(rect); + + ASSERT_FLOAT_EQ(10.0f, mappedRect2.x()); + ASSERT_FLOAT_EQ(40.0f, mappedRect2.y()); + ASSERT_FLOAT_EQ(200.0f, mappedRect2.width()); + ASSERT_FLOAT_EQ(600.0f, mappedRect2.height()); + + test.setA(0.5); + + auto mappedRect3 = test.mapRect(rect); + + ASSERT_FLOAT_EQ(5.0f, mappedRect3.x()); + ASSERT_FLOAT_EQ(40.0f, mappedRect3.y()); + ASSERT_FLOAT_EQ(100.0f, mappedRect3.width()); + ASSERT_FLOAT_EQ(600.0f, mappedRect3.height()); +} + +TEST(AffineTransform, MapFloatQuad) +{ + WebCore::FloatRect rect(100.0f, 100.0f, 100.0f, 50.0f); + WebCore::FloatQuad quad(rect); + + ASSERT_FLOAT_EQ(100.0f, quad.p1().x()); + ASSERT_FLOAT_EQ(100.0f, quad.p1().y()); + ASSERT_FLOAT_EQ(200.0f, quad.p2().x()); + ASSERT_FLOAT_EQ(100.0f, quad.p2().y()); + ASSERT_FLOAT_EQ(200.0f, quad.p3().x()); + ASSERT_FLOAT_EQ(150.0f, quad.p3().y()); + ASSERT_FLOAT_EQ(100.0f, quad.p4().x()); + ASSERT_FLOAT_EQ(150.0f, quad.p4().y()); + + WebCore::AffineTransform test; + auto mappedQuad = test.mapQuad(quad); + + ASSERT_FLOAT_EQ(100.0f, mappedQuad.p1().x()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad.p1().y()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad.p2().x()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad.p2().y()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad.p3().x()); + ASSERT_FLOAT_EQ(150.0f, mappedQuad.p3().y()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad.p4().x()); + ASSERT_FLOAT_EQ(150.0f, mappedQuad.p4().y()); + + test.setD(2.0); + + auto mappedQuad2 = test.mapQuad(quad); + + ASSERT_FLOAT_EQ(100.0f, mappedQuad2.p1().x()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad2.p1().y()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad2.p2().x()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad2.p2().y()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad2.p3().x()); + ASSERT_FLOAT_EQ(300.0f, mappedQuad2.p3().y()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad2.p4().x()); + ASSERT_FLOAT_EQ(300.0f, mappedQuad2.p4().y()); + + test.setA(0.5); + + auto mappedQuad3 = test.mapQuad(quad); + + ASSERT_FLOAT_EQ(50.0f, mappedQuad3.p1().x()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad3.p1().y()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad3.p2().x()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad3.p2().y()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad3.p3().x()); + ASSERT_FLOAT_EQ(300.0f, mappedQuad3.p3().y()); + ASSERT_FLOAT_EQ(50.0f, mappedQuad3.p4().x()); + ASSERT_FLOAT_EQ(300.0f, mappedQuad3.p4().y()); +} + +TEST(AffineTransform, Multiply) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::AffineTransform identity; + + testValueConstruction(test); + + test.multiply(identity); + + testValueConstruction(test); + + WebCore::AffineTransform doubler(2.0, 0.0, 0.0, 2.0, 0.0, 0.0); + + test.multiply(doubler); + + testDoubled(test); + + WebCore::AffineTransform halver(0.5, 0.0, 0.0, 0.5, 0.0, 0.0); + + test.multiply(halver); + + testValueConstruction(test); + + test.multiply(halver); + + testHalved(test); +} + +TEST(AffineTransform, Scale) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test); + + test.scale(1.0); + + testValueConstruction(test); + + test.scale(2.0); + + testDoubled(test); + + test.scale(0.5); + + testValueConstruction(test); + + test.scale(0.5); + + testHalved(test); +} + +TEST(AffineTransform, ScaleUniformNonUniform) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test); + + test.scaleNonUniform(1.0, 1.0); + + testValueConstruction(test); + + test.scaleNonUniform(2.0, 2.0); + + testDoubled(test); + + test.scaleNonUniform(0.5, 0.5); + + testValueConstruction(test); + + test.scaleNonUniform(0.5, 0.5); + + testHalved(test); +} + +TEST(AffineTransform, ScaleNonUniform) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test); + + test.scaleNonUniform(1.0, 2.0); + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(8.0, test.c()); + EXPECT_DOUBLE_EQ(6.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.scaleNonUniform(1.0, 0.5); + + testValueConstruction(test); + + test.scaleNonUniform(2.0, 1.0); + + EXPECT_DOUBLE_EQ(12.0, test.a()); + EXPECT_DOUBLE_EQ(10.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.scaleNonUniform(0.5, 1.0); + + testValueConstruction(test); + + test.scaleNonUniform(0.5, 2.0); + + EXPECT_DOUBLE_EQ(3.0, test.a()); + EXPECT_DOUBLE_EQ(2.5, test.b()); + EXPECT_DOUBLE_EQ(8.0, test.c()); + EXPECT_DOUBLE_EQ(6.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); +} + +TEST(AffineTransform, ScaleFloatSize) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test); + + WebCore::FloatSize first(1.0f, 2.0f); + + test.scale(first); + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(8.0, test.c()); + EXPECT_DOUBLE_EQ(6.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + WebCore::FloatSize second(1.0f, 0.5f); + + test.scale(second); + + testValueConstruction(test); + + WebCore::FloatSize third(2.0f, 1.0f); + + test.scale(third); + + EXPECT_DOUBLE_EQ(12.0, test.a()); + EXPECT_DOUBLE_EQ(10.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + WebCore::FloatSize fourth(0.5f, 1.0f); + + test.scale(fourth); + + testValueConstruction(test); + + WebCore::FloatSize fifth(0.5f, 2.0f); + + test.scale(fifth); + + EXPECT_DOUBLE_EQ(3.0, test.a()); + EXPECT_DOUBLE_EQ(2.5, test.b()); + EXPECT_DOUBLE_EQ(8.0, test.c()); + EXPECT_DOUBLE_EQ(6.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); +} + +TEST(AffineTransform, Rotate) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + test.rotate(360.0); + + testValueConstruction(test); + + test.rotate(180.0); + + static double epsilon = 0.0001; + + EXPECT_NEAR(-6.0, test.a(), epsilon); + EXPECT_NEAR(-5.0, test.b(), epsilon); + EXPECT_NEAR(-4.0, test.c(), epsilon); + EXPECT_NEAR(-3.0, test.d(), epsilon); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.rotate(-180.0); + + testValueConstruction(test); + + test.rotate(90.0); + + EXPECT_NEAR(4.0, test.a(), epsilon); + EXPECT_NEAR(3.0, test.b(), epsilon); + EXPECT_NEAR(-6.0, test.c(), epsilon); + EXPECT_NEAR(-5.0, test.d(), epsilon); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.rotate(-90.0); + + testValueConstruction(test); +} + +TEST(AffineTransform, TranslateXY) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + test.translate(0.0, 0.0); + + testValueConstruction(test); + + test.translate(5.0, 0.0); + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(32.0, test.e()); + EXPECT_DOUBLE_EQ(26.0, test.f()); + + test.translate(0.0, -1.2); + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(27.2, test.e()); + EXPECT_DOUBLE_EQ(22.4, test.f()); +} + +TEST(AffineTransform, TranslateFloatPoint) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + WebCore::FloatPoint none; + test.translate(none); + + testValueConstruction(test); + + WebCore::FloatPoint first(5.0f, 0.0f); + + test.translate(first); + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(32.0, test.e()); + EXPECT_DOUBLE_EQ(26.0, test.f()); + + WebCore::FloatPoint second(0.0f, -1.2f); + + test.translate(second); + + static double epsilon = 0.0001; + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_NEAR(27.2, test.e(), epsilon); + EXPECT_NEAR(22.4, test.f(), epsilon); + + WebCore::AffineTransform test2; + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); + ASSERT_TRUE(test2.isIdentityOrTranslationOrFlipped()); + + test2.translate(second); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); + ASSERT_TRUE(test2.isIdentityOrTranslationOrFlipped()); +} + +TEST(AffineTransform, Shear) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + test.shear(0.0, 0.0); + + testValueConstruction(test); + + test.shear(2.0, 2.0); + + EXPECT_DOUBLE_EQ(14.0, test.a()); + EXPECT_DOUBLE_EQ(11.0, test.b()); + EXPECT_DOUBLE_EQ(16.0, test.c()); + EXPECT_DOUBLE_EQ(13.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.shear(-1.0, 2.0); + + EXPECT_DOUBLE_EQ(46.0, test.a()); + EXPECT_DOUBLE_EQ(37.0, test.b()); + EXPECT_DOUBLE_EQ(2.0, test.c()); + EXPECT_DOUBLE_EQ(2.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); +} + +TEST(AffineTransform, FlipX) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test); + + test.flipX(); + + EXPECT_DOUBLE_EQ(-6.0, test.a()); + EXPECT_DOUBLE_EQ(-5.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.flipX(); + + testValueConstruction(test); + + WebCore::AffineTransform test2; + + testIdentity(test2); + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); + ASSERT_TRUE(test2.isIdentityOrTranslationOrFlipped()); + + test2.flipX(); + + EXPECT_DOUBLE_EQ(-1.0, test2.a()); + EXPECT_DOUBLE_EQ(0.0, test2.b()); + EXPECT_DOUBLE_EQ(0.0, test2.c()); + EXPECT_DOUBLE_EQ(1.0, test2.d()); + EXPECT_DOUBLE_EQ(0.0, test2.e()); + EXPECT_DOUBLE_EQ(0.0, test2.f()); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_FALSE(test2.isIdentityOrTranslation()); + // 'Flipped' just means in the Y direction + ASSERT_FALSE(test2.isIdentityOrTranslationOrFlipped()); + + test2.flipX(); + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); + ASSERT_TRUE(test2.isIdentityOrTranslationOrFlipped()); +} + +TEST(AffineTransform, FlipY) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test); + + test.flipY(); + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(-4.0, test.c()); + EXPECT_DOUBLE_EQ(-3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.flipY(); + + testValueConstruction(test); + + WebCore::AffineTransform test2; + + testIdentity(test2); + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); + ASSERT_TRUE(test2.isIdentityOrTranslationOrFlipped()); + + test2.flipY(); + + EXPECT_DOUBLE_EQ(1.0, test2.a()); + EXPECT_DOUBLE_EQ(0.0, test2.b()); + EXPECT_DOUBLE_EQ(0.0, test2.c()); + EXPECT_DOUBLE_EQ(-1.0, test2.d()); + EXPECT_DOUBLE_EQ(0.0, test2.e()); + EXPECT_DOUBLE_EQ(0.0, test2.f()); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_FALSE(test2.isIdentityOrTranslation()); + ASSERT_TRUE(test2.isIdentityOrTranslationOrFlipped()); + + test2.flipY(); + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); + ASSERT_TRUE(test2.isIdentityOrTranslationOrFlipped()); +} + +TEST(AffineTransform, FlipXandFlipY) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test); + + test.flipX(); + + EXPECT_DOUBLE_EQ(-6.0, test.a()); + EXPECT_DOUBLE_EQ(-5.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.flipY(); + + EXPECT_DOUBLE_EQ(-6.0, test.a()); + EXPECT_DOUBLE_EQ(-5.0, test.b()); + EXPECT_DOUBLE_EQ(-4.0, test.c()); + EXPECT_DOUBLE_EQ(-3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.flipX(); + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(-4.0, test.c()); + EXPECT_DOUBLE_EQ(-3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.flipY(); + + testValueConstruction(test); + + WebCore::AffineTransform test2; + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); + ASSERT_TRUE(test2.isIdentityOrTranslationOrFlipped()); + + test2.flipX(); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_FALSE(test2.isIdentityOrTranslation()); + ASSERT_FALSE(test2.isIdentityOrTranslationOrFlipped()); + + test2.flipY(); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_FALSE(test2.isIdentityOrTranslation()); + // False here because X is also flipped. + ASSERT_FALSE(test2.isIdentityOrTranslationOrFlipped()); + + test2.flipX(); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_FALSE(test2.isIdentityOrTranslation()); + ASSERT_TRUE(test2.isIdentityOrTranslationOrFlipped()); + + test2.flipY(); + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); + ASSERT_TRUE(test2.isIdentityOrTranslationOrFlipped()); +} + +TEST(AffineTransform, Skew) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test); + + test.skew(360.0, 360.0); + + testValueConstruction(test); + + test.skew(0.0, 0.0); + + testValueConstruction(test); + + test.skew(180.0, 180.0); + + static double epsilon = 0.0001; + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_NEAR(4.0, test.c(), epsilon); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.skew(-180.0, -180.0); + + testValueConstruction(test); +} + +TEST(AffineTransform, XandYScale) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + EXPECT_NEAR(7.8102, test.xScale(), 0.0001); + EXPECT_NEAR(5.0, test.yScale(), 0.0001); +} + +TEST(AffineTransform, Equality) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::AffineTransform test2; + + ASSERT_FALSE(test == test2); + ASSERT_TRUE(test != test2); + + test.makeIdentity(); + + ASSERT_TRUE(test == test2); + ASSERT_FALSE(test != test2); +} + +TEST(AffineTransform, Inverse) +{ + WebCore::AffineTransform test; + + auto inverse = test.inverse(); + + ASSERT(inverse); + + EXPECT_DOUBLE_EQ(1.0, inverse->a()); + EXPECT_DOUBLE_EQ(0.0, inverse->b()); + EXPECT_DOUBLE_EQ(0.0, inverse->c()); + EXPECT_DOUBLE_EQ(1.0, inverse->d()); + EXPECT_DOUBLE_EQ(0.0, inverse->e()); + EXPECT_DOUBLE_EQ(0.0, inverse->f()); + + auto test2 = test * inverse.value(); + + testIdentity(test2); +} + +TEST(AffineTransform, Blend) +{ + WebCore::AffineTransform test; + + WebCore::AffineTransform test2; + test2.scale(2.0); + + test.blend(test2, 50); + + EXPECT_DOUBLE_EQ(-48.0, test.a()); + EXPECT_DOUBLE_EQ(0.0, test.b()); + EXPECT_DOUBLE_EQ(0.0, test.c()); + EXPECT_DOUBLE_EQ(-48.0, test.d()); + EXPECT_DOUBLE_EQ(0.0, test.e()); + EXPECT_DOUBLE_EQ(0.0, test.f()); +} + +TEST(AffineTransform, Translation) +{ + auto test = WebCore::AffineTransform::translation(-5.0, -7.0); + EXPECT_DOUBLE_EQ(1.0, test.a()); + EXPECT_DOUBLE_EQ(0.0, test.b()); + EXPECT_DOUBLE_EQ(0.0, test.c()); + EXPECT_DOUBLE_EQ(1.0, test.d()); + EXPECT_DOUBLE_EQ(-5.0, test.e()); + EXPECT_DOUBLE_EQ(-7.0, test.f()); +} + +TEST(AffineTransform, ToTransformationMatrix) +{ + WebCore::AffineTransform transform; + WebCore::TransformationMatrix matrix = transform.toTransformationMatrix(); + + EXPECT_DOUBLE_EQ(1.0, matrix.m11()); + EXPECT_DOUBLE_EQ(0.0, matrix.m12()); + EXPECT_DOUBLE_EQ(0.0, matrix.m13()); + EXPECT_DOUBLE_EQ(0.0, matrix.m14()); + EXPECT_DOUBLE_EQ(0.0, matrix.m21()); + EXPECT_DOUBLE_EQ(1.0, matrix.m22()); + EXPECT_DOUBLE_EQ(0.0, matrix.m23()); + EXPECT_DOUBLE_EQ(0.0, matrix.m24()); + EXPECT_DOUBLE_EQ(0.0, matrix.m31()); + EXPECT_DOUBLE_EQ(0.0, matrix.m32()); + EXPECT_DOUBLE_EQ(1.0, matrix.m33()); + EXPECT_DOUBLE_EQ(0.0, matrix.m34()); +} + +TEST(AffineTransform, MakeMapBetweenRects) +{ + WebCore::AffineTransform transform; + + WebCore::FloatRect fromRect(10.0f, 10.0f, 100.0f, 100.0f); + WebCore::FloatRect toRect(70.0f, 70.0f, 200.0f, 50.0f); + + auto mapBetween = WebCore::makeMapBetweenRects(fromRect, toRect); + + EXPECT_DOUBLE_EQ(2.0, mapBetween.a()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.b()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.c()); + EXPECT_DOUBLE_EQ(0.5, mapBetween.d()); + EXPECT_DOUBLE_EQ(60.0, mapBetween.e()); + EXPECT_DOUBLE_EQ(60.0, mapBetween.f()); +} + +#if USE(CG) +TEST(AffineTransform, CoreGraphicsCasting) +{ + WebCore::AffineTransform test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + CGAffineTransform test2 = CGAffineTransformMake(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + ASSERT_TRUE(CGAffineTransformEqualToTransform(test, test2)); + + WebCore::AffineTransform test3; + + ASSERT_FALSE(CGAffineTransformEqualToTransform(test, test3)); +} +#endif + +#if PLATFORM(WIN) +TEST(AffineTransform, Direct2DCasting) +{ + WebCore::AffineTransform transform(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + D2D1_MATRIX_3X2_F test = transform; + D2D1_MATRIX_3X2_F test2 = D2D1::Matrix3x2F(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + static const double epsilon = 0.0000001; + + EXPECT_NEAR(test._11, test2._11, epsilon); + EXPECT_NEAR(test._12, test2._12, epsilon); + EXPECT_NEAR(test._21, test2._21, epsilon); + EXPECT_NEAR(test._22, test2._22, epsilon); + EXPECT_NEAR(test._31, test2._31, epsilon); + EXPECT_NEAR(test._32, test2._32, epsilon); +} +#endif + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp b/Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp new file mode 100644 index 000000000..2aab65dd8 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if PLATFORM(MAC) + +#include "Test.h" +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +class CARingBufferTest : public testing::Test { +public: + + virtual void SetUp() + { + WTF::initializeMainThread(); + m_ringBuffer = std::make_unique(); + } + + // CAAudioStreamDescription(double sampleRate, UInt32 numChannels, PCMFormat format, bool isInterleaved, size_t capacity) + void setup(double sampleRate, UInt32 numChannels, CAAudioStreamDescription::PCMFormat format, bool isInterleaved, size_t capacity) + { + m_description = CAAudioStreamDescription(sampleRate, numChannels, format, isInterleaved); + m_capacity = capacity; + size_t listSize = offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * std::max(1, m_description.numberOfChannelStreams())); + m_bufferList = std::unique_ptr(static_cast(::operator new (listSize))); + m_ringBuffer->allocate(m_description, capacity); + } + + void setListDataBuffer(uint8_t* bufferData, size_t sampleCount) + { + size_t bufferCount = m_description.numberOfChannelStreams(); + size_t channelCount = m_description.numberOfInterleavedChannels(); + size_t bytesPerChannel = sampleCount * m_description.bytesPerFrame(); + + m_bufferList->mNumberBuffers = bufferCount; + for (unsigned i = 0; i < bufferCount; ++i) { + m_bufferList->mBuffers[i].mNumberChannels = channelCount; + m_bufferList->mBuffers[i].mDataByteSize = bytesPerChannel; + m_bufferList->mBuffers[i].mData = bufferData; + if (bufferData) + bufferData = bufferData + bytesPerChannel; + } + } + + const CAAudioStreamDescription& description() const { return m_description; } + AudioBufferList& bufferList() const { return *m_bufferList.get(); } + CARingBuffer& ringBuffer() const { return *m_ringBuffer.get(); } + size_t capacity() const { return m_capacity; } + +private: + size_t audioBufferListSizeForStream(const CAAudioStreamDescription& format) + { + return offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * std::max(1, format.numberOfChannelStreams())); + } + + void configureBufferListForStream(AudioBufferList& bufferList, const CAAudioStreamDescription& format, uint8_t* bufferData, size_t sampleCount) + { + size_t bufferCount = format.numberOfChannelStreams(); + size_t channelCount = format.numberOfInterleavedChannels(); + size_t bytesPerChannel = sampleCount * format.bytesPerFrame(); + + bufferList.mNumberBuffers = bufferCount; + for (unsigned i = 0; i < bufferCount; ++i) { + bufferList.mBuffers[i].mNumberChannels = channelCount; + bufferList.mBuffers[i].mDataByteSize = bytesPerChannel; + bufferList.mBuffers[i].mData = bufferData; + if (bufferData) + bufferData = bufferData + bytesPerChannel; + } + } + + std::unique_ptr m_bufferList; + std::unique_ptr m_ringBuffer; + CAAudioStreamDescription m_description = { }; + size_t m_capacity = { 0 }; +}; + +TEST_F(CARingBufferTest, Basics) +{ + const int capacity = 32; + + setup(44100, 1, CAAudioStreamDescription::PCMFormat::Float32, true, capacity); + + float sourceBuffer[capacity]; + for (int i = 0; i < capacity; i++) + sourceBuffer[i] = i + 0.5; + + setListDataBuffer(reinterpret_cast(sourceBuffer), capacity); + + // Fill the first half of the buffer ... + int sampleCount = capacity / 2; + CARingBuffer::Error err = ringBuffer().store(&bufferList(), sampleCount, 0); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + uint64_t startTime; + uint64_t endTime; + ringBuffer().getCurrentFrameBounds(startTime, endTime); + EXPECT_EQ(0, (int)startTime); + EXPECT_EQ((int)sampleCount, (int)endTime); + + float scratchBuffer[capacity]; + setListDataBuffer(reinterpret_cast(scratchBuffer), capacity); + + err = ringBuffer().fetch(&bufferList(), sampleCount, 0); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + EXPECT_TRUE(!memcmp(sourceBuffer, scratchBuffer, sampleCount * description().sampleWordSize())); + + // ... and the second half. + err = ringBuffer().store(&bufferList(), capacity / 2, capacity / 2); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + ringBuffer().getCurrentFrameBounds(startTime, endTime); + EXPECT_EQ(0, (int)startTime); + EXPECT_EQ(capacity, (int)endTime); + + memset(scratchBuffer, 0, sampleCount * description().sampleWordSize()); + err = ringBuffer().fetch(&bufferList(), sampleCount, 0); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + EXPECT_TRUE(!memcmp(sourceBuffer, scratchBuffer, sampleCount * description().sampleWordSize())); + + // Force the buffer to wrap around + err = ringBuffer().store(&bufferList(), capacity, capacity - 1); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + ringBuffer().getCurrentFrameBounds(startTime, endTime); + EXPECT_EQ((int)capacity - 1, (int)startTime); + EXPECT_EQ(capacity - 1 + capacity, (int)endTime); + + // Make sure it returns an error when asked to store too much ... + err = ringBuffer().store(&bufferList(), capacity * 3, capacity / 2); + EXPECT_EQ(err, CARingBuffer::Error::TooMuch); + + // ... and doesn't modify the buffer + ringBuffer().getCurrentFrameBounds(startTime, endTime); + EXPECT_EQ((int)capacity - 1, (int)startTime); + EXPECT_EQ(capacity - 1 + capacity, (int)endTime); + + ringBuffer().flush(); + ringBuffer().getCurrentFrameBounds(startTime, endTime); + EXPECT_EQ(0, (int)startTime); + EXPECT_EQ(0, (int)endTime); +} + +template +class MixingTest { +public: + static void run(CARingBufferTest& test) + { + const int sampleCount = 64; + + CAAudioStreamDescription::PCMFormat format; + if (std::is_same::value) + format = CAAudioStreamDescription::PCMFormat::Float32; + else if (std::is_same::value) + format = CAAudioStreamDescription::PCMFormat::Float64; + else if (std::is_same::value) + format = CAAudioStreamDescription::PCMFormat::Int32; + else if (std::is_same::value) + format = CAAudioStreamDescription::PCMFormat::Int16; + else + ASSERT_NOT_REACHED(); + + test.setup(44100, 1, format, true, sampleCount); + + type referenceBuffer[sampleCount]; + type sourceBuffer[sampleCount]; + type readBuffer[sampleCount]; + + for (int i = 0; i < sampleCount; i++) { + sourceBuffer[i] = i * 0.5; + referenceBuffer[i] = sourceBuffer[i]; + } + + test.setListDataBuffer(reinterpret_cast(sourceBuffer), sampleCount); + CARingBuffer::Error err = test.ringBuffer().store(&test.bufferList(), sampleCount, 0); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + memset(readBuffer, 0, sampleCount * test.description().sampleWordSize()); + test.setListDataBuffer(reinterpret_cast(readBuffer), sampleCount); + err = test.ringBuffer().fetch(&test.bufferList(), sampleCount, 0, CARingBuffer::FetchMode::Mix); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + for (int i = 0; i < sampleCount; i++) + EXPECT_EQ(readBuffer[i], referenceBuffer[i]) << "Ring buffer value differs at index " << i; + + err = test.ringBuffer().fetch(&test.bufferList(), sampleCount, 0, CARingBuffer::FetchMode::Mix); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + err = test.ringBuffer().fetch(&test.bufferList(), sampleCount, 0, CARingBuffer::FetchMode::Mix); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + err = test.ringBuffer().fetch(&test.bufferList(), sampleCount, 0, CARingBuffer::FetchMode::Mix); + EXPECT_EQ(err, CARingBuffer::Error::Ok); + + for (int i = 0; i < sampleCount; i++) + referenceBuffer[i] += sourceBuffer[i] * 3; + + for (int i = 0; i < sampleCount; i++) + EXPECT_EQ(readBuffer[i], referenceBuffer[i]) << "Ring buffer value differs at index " << i; + } +}; + +TEST_F(CARingBufferTest, FloatMixing) +{ + MixingTest::run(*this); +} + +TEST_F(CARingBufferTest, DoubleMixing) +{ + MixingTest::run(*this); +} + +TEST_F(CARingBufferTest, Int32Mixing) +{ + MixingTest::run(*this); +} + +TEST_F(CARingBufferTest, Int16Mixing) +{ + MixingTest::run(*this); +} + +} + +#endif diff --git a/Tools/TestWebKitAPI/Tests/WebCore/CSSParser.cpp b/Tools/TestWebKitAPI/Tests/WebCore/CSSParser.cpp new file mode 100644 index 000000000..edc7dcd91 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/CSSParser.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2014 Igalia, S.L. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include + +namespace TestWebKitAPI { + +using namespace WebCore; + +static unsigned computeNumberOfTracks(CSSValueList& valueList) +{ + unsigned numberOfTracks = 0; + for (const auto& value : valueList) { + if (value->isGridLineNamesValue()) + continue; + ++numberOfTracks; + } + return numberOfTracks; +} + +TEST(CSSPropertyParserTest, GridTrackLimits) +{ + struct { + const CSSPropertyID propertyID; + const char* input; + const size_t output; + } testCases[] = { + {CSSPropertyGridTemplateColumns, "grid-template-columns: repeat(999999, 20px);", 999999}, + {CSSPropertyGridTemplateRows, "grid-template-rows: repeat(999999, 20px);", 999999}, + {CSSPropertyGridTemplateColumns, "grid-template-columns: repeat(1000000, 10%);", 1000000}, + {CSSPropertyGridTemplateRows, "grid-template-rows: repeat(1000000, 10%);", 1000000}, + {CSSPropertyGridTemplateColumns, "grid-template-columns: repeat(1000000, [first] -webkit-min-content [last]);", 1000000}, + {CSSPropertyGridTemplateRows, "grid-template-rows: repeat(1000000, [first] -webkit-min-content [last]);", 1000000}, + {CSSPropertyGridTemplateColumns, "grid-template-columns: repeat(1000001, auto);", 1000000}, + {CSSPropertyGridTemplateRows, "grid-template-rows: repeat(1000001, auto);", 1000000}, + {CSSPropertyGridTemplateColumns, "grid-template-columns: repeat(400000, 2em minmax(10px, -webkit-max-content) 0.5fr);", 999999}, + {CSSPropertyGridTemplateRows, "grid-template-rows: repeat(400000, 2em minmax(10px, -webkit-max-content) 0.5fr);", 999999}, + {CSSPropertyGridTemplateColumns, "grid-template-columns: repeat(600000, [first] 3vh 10% 2fr [nav] 10px auto 1fr 6em [last]);", 999999}, + {CSSPropertyGridTemplateRows, "grid-template-rows: repeat(600000, [first] 3vh 10% 2fr [nav] 10px auto 1fr 6em [last]);", 999999}, + {CSSPropertyGridTemplateColumns, "grid-template-columns: repeat(100000000000000000000, 10% 1fr);", 1000000}, + {CSSPropertyGridTemplateRows, "grid-template-rows: repeat(100000000000000000000, 10% 1fr);", 1000000}, + {CSSPropertyGridTemplateColumns, "grid-template-columns: repeat(100000000000000000000, 10% 5em 1fr auto auto 15px -webkit-min-content);", 999999}, + {CSSPropertyGridTemplateRows, "grid-template-rows: repeat(100000000000000000000, 10% 5em 1fr auto auto 15px -webkit-min-content);", 999999}, + }; + + CSSParser parser(strictCSSParserContext()); + auto properties = MutableStyleProperties::create(); + + for (auto& testCase : testCases) { + ASSERT_TRUE(parser.parseDeclaration(properties, testCase.input)); + RefPtr value = properties->getPropertyCSSValue(testCase.propertyID); + + ASSERT_TRUE(value->isValueList()); + EXPECT_EQ(computeNumberOfTracks(*downcast(value.get())), testCase.output); + } +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/CalculationValue.cpp b/Tools/TestWebKitAPI/Tests/WebCore/CalculationValue.cpp new file mode 100644 index 000000000..07fbdce00 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/CalculationValue.cpp @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2014 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include + +namespace TestWebKitAPI { + +static unsigned deletionCount; + +class CalculationDeletionTestNode : public WebCore::CalcExpressionNode { +public: + virtual ~CalculationDeletionTestNode() + { + ++deletionCount; + } + + float evaluate(float) const override { return 0; } + bool operator==(const CalcExpressionNode&) const override { ASSERT_NOT_REACHED(); return false; } +}; + +static Ref createTestValue() +{ + auto node = std::make_unique(); + return WebCore::CalculationValue::create(WTFMove(node), WebCore::ValueRangeAll); +} + +TEST(CalculationValue, LengthConstruction) +{ + RefPtr value = createTestValue(); + + EXPECT_EQ(1U, value->refCount()); + + { + WebCore::Length length(*value); + EXPECT_EQ(2U, value->refCount()); + } + + EXPECT_EQ(1U, value->refCount()); + + { + WebCore::Length lengthA(*value); + EXPECT_EQ(2U, value->refCount()); + WebCore::Length lengthB(lengthA); + EXPECT_EQ(2U, value->refCount()); + } + + EXPECT_EQ(1U, value->refCount()); + + { + WebCore::Length lengthC(*value); + EXPECT_EQ(2U, value->refCount()); + WebCore::Length lengthD(WTFMove(lengthC)); + EXPECT_EQ(2U, value->refCount()); + } + + EXPECT_EQ(1U, value->refCount()); + + EXPECT_EQ(0U, deletionCount); + value = nullptr; + EXPECT_EQ(1U, deletionCount); + deletionCount = 0; +} + +TEST(CalculationValue, LengthConstructionReleasedValue) +{ + RefPtr value = createTestValue(); + + EXPECT_EQ(1U, value->refCount()); + + { + auto* rawValue = value.get(); + WebCore::Length length(value.releaseNonNull()); + EXPECT_EQ(1U, rawValue->refCount()); + + EXPECT_EQ(0U, deletionCount); + } + + EXPECT_EQ(1U, deletionCount); + deletionCount = 0; + + value = createTestValue(); + + { + auto* rawValue = value.get(); + WebCore::Length lengthA(value.releaseNonNull()); + EXPECT_EQ(1U, rawValue->refCount()); + WebCore::Length lengthB(lengthA); + EXPECT_EQ(1U, rawValue->refCount()); + + EXPECT_EQ(0U, deletionCount); + } + + EXPECT_EQ(1U, deletionCount); + deletionCount = 0; + + value = createTestValue(); + + { + auto* rawValue = value.get(); + WebCore::Length lengthC(value.releaseNonNull()); + EXPECT_EQ(1U, rawValue->refCount()); + WebCore::Length lengthD(WTFMove(lengthC)); + EXPECT_EQ(1U, rawValue->refCount()); + + EXPECT_EQ(0U, deletionCount); + } + + EXPECT_EQ(1U, deletionCount); + deletionCount = 0; +} + +TEST(CalculationValue, LengthAssignment) +{ + RefPtr value = createTestValue(); + + EXPECT_EQ(1U, value->refCount()); + + { + WebCore::Length lengthA(*value); + EXPECT_EQ(2U, value->refCount()); + WebCore::Length lengthB; + lengthB = lengthA; + EXPECT_EQ(2U, value->refCount()); + } + + EXPECT_EQ(1U, value->refCount()); + + { + WebCore::Length lengthC(*value); + EXPECT_EQ(2U, value->refCount()); + WebCore::Length lengthD; + lengthD = WTFMove(lengthC); + EXPECT_EQ(2U, value->refCount()); + } + + EXPECT_EQ(1U, value->refCount()); + + EXPECT_EQ(0U, deletionCount); + value = nullptr; + EXPECT_EQ(1U, deletionCount); + deletionCount = 0; + + value = createTestValue(); + RefPtr value2 = createTestValue(); + + EXPECT_EQ(1U, value->refCount()); + EXPECT_EQ(1U, value2->refCount()); + + { + WebCore::Length lengthE(*value); + EXPECT_EQ(2U, value->refCount()); + WebCore::Length lengthF(*value2); + EXPECT_EQ(2U, value2->refCount()); + lengthE = lengthF; + EXPECT_EQ(1U, value->refCount()); + EXPECT_EQ(2U, value2->refCount()); + } + + EXPECT_EQ(1U, value->refCount()); + EXPECT_EQ(1U, value2->refCount()); + + { + WebCore::Length lengthG(*value); + EXPECT_EQ(2U, value->refCount()); + WebCore::Length lengthH(*value2); + EXPECT_EQ(2U, value2->refCount()); + lengthG = WTFMove(lengthH); + EXPECT_EQ(1U, value->refCount()); + EXPECT_EQ(2U, value2->refCount()); + } + + EXPECT_EQ(0U, deletionCount); + value = nullptr; + EXPECT_EQ(1U, deletionCount); + value2 = nullptr; + EXPECT_EQ(2U, deletionCount); + deletionCount = 0; +} + +TEST(CalculationValue, LengthAssignmentReleasedValue) +{ + RefPtr value = createTestValue(); + + { + auto* rawValue = value.get(); + WebCore::Length lengthA(value.releaseNonNull()); + EXPECT_EQ(1U, rawValue->refCount()); + WebCore::Length lengthB; + lengthB = lengthA; + EXPECT_EQ(1U, rawValue->refCount()); + + EXPECT_EQ(0U, deletionCount); + } + + EXPECT_EQ(1U, deletionCount); + deletionCount = 0; + + value = createTestValue(); + + { + auto* rawValue = value.get(); + WebCore::Length lengthC(value.releaseNonNull()); + EXPECT_EQ(1U, rawValue->refCount()); + WebCore::Length lengthD; + lengthD = WTFMove(lengthC); + EXPECT_EQ(1U, rawValue->refCount()); + + EXPECT_EQ(0U, deletionCount); + } + + EXPECT_EQ(1U, deletionCount); + deletionCount = 0; + + value = createTestValue(); + RefPtr value2 = createTestValue(); + + EXPECT_EQ(1U, value->refCount()); + EXPECT_EQ(1U, value2->refCount()); + + { + auto* rawValue = value.get(); + WebCore::Length lengthE(value.releaseNonNull()); + EXPECT_EQ(1U, rawValue->refCount()); + auto* rawValue2 = value2.get(); + WebCore::Length lengthF(value2.releaseNonNull()); + EXPECT_EQ(1U, rawValue2->refCount()); + + lengthE = lengthF; + EXPECT_EQ(1U, deletionCount); + EXPECT_EQ(1U, rawValue2->refCount()); + } + + EXPECT_EQ(2U, deletionCount); + deletionCount = 0; + + value = createTestValue(); + value2 = createTestValue(); + + EXPECT_EQ(1U, value->refCount()); + EXPECT_EQ(1U, value2->refCount()); + + { + auto* rawValue = value.get(); + WebCore::Length lengthG(value.releaseNonNull()); + EXPECT_EQ(1U, rawValue->refCount()); + auto* rawValue2 = value2.get(); + WebCore::Length lengthH(value2.releaseNonNull()); + EXPECT_EQ(1U, rawValue2->refCount()); + + lengthG = WTFMove(lengthH); + EXPECT_EQ(1U, deletionCount); + EXPECT_EQ(1U, rawValue2->refCount()); + } + + EXPECT_EQ(2U, deletionCount); + deletionCount = 0; +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/Color.cpp b/Tools/TestWebKitAPI/Tests/WebCore/Color.cpp new file mode 100644 index 000000000..61af1c660 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/Color.cpp @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "Test.h" +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +TEST(Color, RGBToHSV_White) +{ + Color color = Color::white; + + double h = 0; + double s = 0; + double v = 0; + color.getHSV(h, s, v); + + EXPECT_DOUBLE_EQ(0, h); + EXPECT_DOUBLE_EQ(0, s); + EXPECT_DOUBLE_EQ(1, v); +} + +TEST(Color, RGBToHSV_Black) +{ + Color color = Color::black; + + double h = 0; + double s = 0; + double v = 0; + color.getHSV(h, s, v); + + EXPECT_DOUBLE_EQ(0, h); + EXPECT_DOUBLE_EQ(0, s); + EXPECT_DOUBLE_EQ(0, v); +} + +TEST(Color, RGBToHSV_Red) +{ + Color color(255, 0, 0); + + double h = 0; + double s = 0; + double v = 0; + color.getHSV(h, s, v); + + EXPECT_DOUBLE_EQ(0, h); + EXPECT_DOUBLE_EQ(1, s); + EXPECT_DOUBLE_EQ(1, v); +} + +TEST(Color, RGBToHSV_Green) +{ + Color color(0, 255, 0); + + double h = 0; + double s = 0; + double v = 0; + color.getHSV(h, s, v); + + EXPECT_DOUBLE_EQ(0.33333333333333331, h); + EXPECT_DOUBLE_EQ(1, s); + EXPECT_DOUBLE_EQ(1, v); +} + +TEST(Color, RGBToHSV_Blue) +{ + Color color(0, 0, 255); + + double h = 0; + double s = 0; + double v = 0; + color.getHSV(h, s, v); + + EXPECT_DOUBLE_EQ(0.66666666666666663, h); + EXPECT_DOUBLE_EQ(1, s); + EXPECT_DOUBLE_EQ(1, v); +} + +TEST(Color, RGBToHSV_DarkGray) +{ + Color color = Color::darkGray; + + double h = 0; + double s = 0; + double v = 0; + color.getHSV(h, s, v); + + EXPECT_DOUBLE_EQ(0, h); + EXPECT_DOUBLE_EQ(0, s); + EXPECT_DOUBLE_EQ(0.50196078431372548, v); +} + +TEST(Color, RGBToHSV_Gray) +{ + Color color = Color::gray; + + double h = 0; + double s = 0; + double v = 0; + color.getHSV(h, s, v); + + EXPECT_DOUBLE_EQ(0, h); + EXPECT_DOUBLE_EQ(0, s); + EXPECT_DOUBLE_EQ(0.62745098039215685, v); +} + +TEST(Color, RGBToHSV_LightGray) +{ + Color color = Color::lightGray; + + double h = 0; + double s = 0; + double v = 0; + color.getHSV(h, s, v); + + EXPECT_DOUBLE_EQ(0, h); + EXPECT_DOUBLE_EQ(0, s); + EXPECT_DOUBLE_EQ(0.75294117647058822, v); +} + +TEST(Color, Validity) +{ + Color invalidColor; + EXPECT_FALSE(invalidColor.isValid()); + EXPECT_FALSE(invalidColor.isExtended()); + + Color otherInvalidColor = invalidColor; + EXPECT_FALSE(otherInvalidColor.isValid()); + EXPECT_FALSE(otherInvalidColor.isExtended()); + + Color validColor(255, 0, 0); + EXPECT_TRUE(validColor.isValid()); + EXPECT_FALSE(validColor.isExtended()); + + Color otherValidColor = validColor; + EXPECT_TRUE(otherValidColor.isValid()); + EXPECT_FALSE(otherValidColor.isExtended()); + + validColor = Color(1, 2, 3, 4); + EXPECT_TRUE(validColor.isValid()); + EXPECT_FALSE(validColor.isExtended()); + EXPECT_EQ(validColor.red(), 1); + EXPECT_EQ(validColor.green(), 2); + EXPECT_EQ(validColor.blue(), 3); + EXPECT_EQ(validColor.alpha(), 4); + + Color yetAnotherValidColor(WTFMove(validColor)); + EXPECT_TRUE(yetAnotherValidColor.isValid()); + EXPECT_FALSE(yetAnotherValidColor.isExtended()); + EXPECT_EQ(yetAnotherValidColor.red(), 1); + EXPECT_EQ(yetAnotherValidColor.green(), 2); + EXPECT_EQ(yetAnotherValidColor.blue(), 3); + EXPECT_EQ(yetAnotherValidColor.alpha(), 4); + + otherValidColor = WTFMove(yetAnotherValidColor); + EXPECT_TRUE(otherValidColor.isValid()); + EXPECT_FALSE(otherValidColor.isExtended()); + EXPECT_EQ(otherValidColor.red(), 1); + EXPECT_EQ(otherValidColor.green(), 2); + EXPECT_EQ(otherValidColor.blue(), 3); + EXPECT_EQ(otherValidColor.alpha(), 4); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/ComplexTextController.cpp b/Tools/TestWebKitAPI/Tests/WebCore/ComplexTextController.cpp new file mode 100644 index 000000000..680dac0be --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/ComplexTextController.cpp @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +class ComplexTextControllerTest : public testing::Test { +public: + virtual void SetUp() + { + JSC::initializeThreading(); + RunLoop::initializeMainRunLoop(); + } +}; + +TEST_F(ComplexTextControllerTest, InitialAdvanceWithLeftRunInRTL) +{ + FontCascadeDescription description; + description.setOneFamily("Times"); + description.setComputedSize(80); + FontCascade font(description); + font.update(); + auto spaceWidth = font.primaryFont().spaceWidth(); + +#if USE_LAYOUT_SPECIFIC_ADVANCES + Vector advances = { FloatSize(), FloatSize(21.640625, 0.0), FloatSize(42.3046875, 0.0), FloatSize(55.8984375, 0.0), FloatSize(22.34375, 0.0) }; + Vector origins = { FloatPoint(-15.15625, 18.046875), FloatPoint(), FloatPoint(), FloatPoint(), FloatPoint() }; +#else + Vector advances = { FloatSize(15.15625, -18.046875), FloatSize(21.640625, 0.0), FloatSize(42.3046875, 0.0), FloatSize(55.8984375, 0.0), FloatSize(22.34375, 0.0) }; + Vector origins = { }; +#endif + + FloatSize initialAdvance = FloatSize(-15.15625, 18.046875); + + UChar characters[] = { 0x644, 0x637, 0x641, 0x627, 0x64b, 0x20 }; + size_t charactersLength = WTF_ARRAY_LENGTH(characters); + TextRun textRun(StringView(characters, charactersLength)); + auto run1 = ComplexTextController::ComplexTextRun::create({ FloatSize(21.875, 0) }, { FloatPoint() }, { 5 }, { 5 }, FloatSize(), font.primaryFont(), characters, 0, charactersLength, 5, 6, false); + auto run2 = ComplexTextController::ComplexTextRun::create(advances, origins, { 193, 377, 447, 431, 458 }, { 4, 3, 2, 1, 0 }, initialAdvance, font.primaryFont(), characters, 0, charactersLength, 0, 5, false); + Vector> runs; + runs.append(WTFMove(run1)); + runs.append(WTFMove(run2)); + ComplexTextController controller(font, textRun, runs); + + float totalWidth = 0; + for (size_t i = 1; i < advances.size(); ++i) + totalWidth += advances[i].width(); + EXPECT_NEAR(controller.totalWidth(), spaceWidth + totalWidth, 0.0001); + GlyphBuffer glyphBuffer; + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(0, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(1, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), advances[4].width(), 0.0001); + controller.advance(6, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), spaceWidth + totalWidth, 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().width(), 0, 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().height(), 0, 0.0001); + EXPECT_EQ(glyphBuffer.size(), 6U); + EXPECT_NEAR(glyphBuffer.advanceAt(0).width(), advances[4].width(), 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(1).width(), advances[3].width(), 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(2).width(), advances[2].width(), 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(3).width(), advances[1].width(), 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(4).width(), -initialAdvance.width(), 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(5).width(), spaceWidth + initialAdvance.width(), 0.0001); +} + +TEST_F(ComplexTextControllerTest, InitialAdvanceInRTL) +{ + FontCascadeDescription description; + description.setOneFamily("Times"); + description.setComputedSize(80); + FontCascade font(description); + font.update(); + +#if USE_LAYOUT_SPECIFIC_ADVANCES + Vector advances = { FloatSize(), FloatSize(21.640625, 0.0), FloatSize(42.3046875, 0.0), FloatSize(55.8984375, 0.0), FloatSize(22.34375, 0.0) }; + Vector origins = { FloatPoint(-15.15625, 18.046875), FloatPoint(), FloatPoint(), FloatPoint(), FloatPoint() }; +#else + Vector advances = { FloatSize(15.15625, -18.046875), FloatSize(21.640625, 0.0), FloatSize(42.3046875, 0.0), FloatSize(55.8984375, 0.0), FloatSize(22.34375, 0.0) }; + Vector origins = { }; +#endif + + FloatSize initialAdvance = FloatSize(-15.15625, 18.046875); + + UChar characters[] = { 0x644, 0x637, 0x641, 0x627, 0x64b }; + size_t charactersLength = WTF_ARRAY_LENGTH(characters); + TextRun textRun(StringView(characters, charactersLength)); + auto run = ComplexTextController::ComplexTextRun::create(advances, origins, { 193, 377, 447, 431, 458 }, { 4, 3, 2, 1, 0 }, initialAdvance, font.primaryFont(), characters, 0, charactersLength, 0, 5, false); + Vector> runs; + runs.append(WTFMove(run)); + ComplexTextController controller(font, textRun, runs); + + float totalWidth = 0; + for (size_t i = 1; i < advances.size(); ++i) + totalWidth += advances[i].width(); + EXPECT_NEAR(controller.totalWidth(), totalWidth, 0.0001); + GlyphBuffer glyphBuffer; + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(0, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(1, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), advances[4].width(), 0.0001); + controller.advance(5, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), totalWidth, 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().width(), initialAdvance.width(), 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().height(), initialAdvance.height(), 0.0001); + EXPECT_EQ(glyphBuffer.size(), 5U); + EXPECT_NEAR(glyphBuffer.advanceAt(0).width(), advances[4].width(), 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(1).width(), advances[3].width(), 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(2).width(), advances[2].width(), 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(3).width(), advances[1].width(), 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(4).width(), -initialAdvance.width(), 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(4).height(), initialAdvance.height(), 0.0001); +} + +TEST_F(ComplexTextControllerTest, InitialAdvanceWithLeftRunInLTR) +{ + FontCascadeDescription description; + description.setOneFamily("LucidaGrande"); + description.setComputedSize(80); + FontCascade font(description); + font.update(); + auto spaceWidth = font.primaryFont().spaceWidth(); + +#if USE_LAYOUT_SPECIFIC_ADVANCES + Vector advances = { FloatSize(76.347656, 0.000000), FloatSize(0.000000, 0.000000) }; + Vector origins = { FloatPoint(), FloatPoint(-23.281250, -8.398438) }; +#else + Vector advances = { FloatSize(53.066406, -8.398438), FloatSize(23.281250, 8.398438) }; + Vector origins = { }; +#endif + + FloatSize initialAdvance = FloatSize(28.144531, 0); + + UChar characters[] = { 0x20, 0x61, 0x20e3 }; + size_t charactersLength = WTF_ARRAY_LENGTH(characters); + TextRun textRun(StringView(characters, charactersLength)); + auto run1 = ComplexTextController::ComplexTextRun::create({ FloatSize(spaceWidth, 0) }, { FloatPoint() }, { 5 }, { 0 }, FloatSize(), font.primaryFont(), characters, 0, charactersLength, 0, 1, true); + auto run2 = ComplexTextController::ComplexTextRun::create(advances, origins, { 68, 1471 }, { 1, 2 }, initialAdvance, font.primaryFont(), characters, 0, charactersLength, 1, 3, true); + Vector> runs; + runs.append(WTFMove(run1)); + runs.append(WTFMove(run2)); + ComplexTextController controller(font, textRun, runs); + + EXPECT_NEAR(controller.totalWidth(), spaceWidth + 76.347656 + initialAdvance.width(), 0.0001); + GlyphBuffer glyphBuffer; + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(0, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(1, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), spaceWidth, 0.0001); + controller.advance(2, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), spaceWidth + advances[0].width() + initialAdvance.width(), 0.0001); + controller.advance(3, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), spaceWidth + 76.347656 + initialAdvance.width(), 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().width(), 0, 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().height(), 0, 0.0001); + EXPECT_EQ(glyphBuffer.size(), 3U); + EXPECT_NEAR(glyphBuffer.advanceAt(0).width(), spaceWidth + initialAdvance.width(), 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(1).width(), 53.066406, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(2).width(), 23.281250, 0.0001); +} + +TEST_F(ComplexTextControllerTest, InitialAdvanceInLTR) +{ + FontCascadeDescription description; + description.setOneFamily("LucidaGrande"); + description.setComputedSize(80); + FontCascade font(description); + font.update(); + +#if USE_LAYOUT_SPECIFIC_ADVANCES + Vector advances = { FloatSize(76.347656, 0.000000), FloatSize(0.000000, 0.000000) }; + Vector origins = { FloatPoint(), FloatPoint(-23.281250, -8.398438) }; +#else + Vector advances = { FloatSize(53.066406, -8.398438), FloatSize(23.281250, 8.398438) }; + Vector origins = { }; +#endif + + FloatSize initialAdvance = FloatSize(28.144531, 0); + + UChar characters[] = { 0x61, 0x20e3 }; + size_t charactersLength = WTF_ARRAY_LENGTH(characters); + TextRun textRun(StringView(characters, charactersLength)); + auto run = ComplexTextController::ComplexTextRun::create(advances, origins, { 68, 1471 }, { 0, 1 }, initialAdvance, font.primaryFont(), characters, 0, charactersLength, 0, 2, true); + Vector> runs; + runs.append(WTFMove(run)); + ComplexTextController controller(font, textRun, runs); + + EXPECT_NEAR(controller.totalWidth(), 76.347656 + initialAdvance.width(), 0.0001); + GlyphBuffer glyphBuffer; + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(0, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(1, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), advances[0].width() + initialAdvance.width(), 0.0001); + controller.advance(2, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 76.347656 + initialAdvance.width(), 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().width(), initialAdvance.width(), 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().height(), initialAdvance.height(), 0.0001); + EXPECT_EQ(glyphBuffer.size(), 2U); + EXPECT_NEAR(glyphBuffer.advanceAt(0).width(), 53.066406, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(1).width(), 23.281250, 0.0001); +} + +TEST_F(ComplexTextControllerTest, InitialAdvanceInRTLNoOrigins) +{ + FontCascadeDescription description; + description.setOneFamily("Times"); + description.setComputedSize(48); + FontCascade font(description); + font.update(); + + FloatSize initialAdvance = FloatSize(4.33996383363472, 12.368896925859); + + UChar characters[] = { 0x633, 0x20, 0x627, 0x650 }; + size_t charactersLength = WTF_ARRAY_LENGTH(characters); + TextRun textRun(StringView(characters, charactersLength)); + auto run1 = ComplexTextController::ComplexTextRun::create({ FloatSize(-4.33996383363472, -12.368896925859), FloatSize(14.0397830018083, 0) }, { }, { 884, 240 }, { 3, 2 }, initialAdvance, font.primaryFont(), characters, 0, charactersLength, 2, 4, false); + auto run2 = ComplexTextController::ComplexTextRun::create({ FloatSize(12.0, 0) }, { }, { 3 }, { 1 }, FloatSize(), font.primaryFont(), characters, 0, charactersLength, 1, 2, false); + auto run3 = ComplexTextController::ComplexTextRun::create({ FloatSize(43.8119349005425, 0) }, { }, { 276 }, { 0 }, FloatSize(), font.primaryFont(), characters, 0, charactersLength, 0, 1, false); + Vector> runs; + runs.append(WTFMove(run1)); + runs.append(WTFMove(run2)); + runs.append(WTFMove(run3)); + ComplexTextController controller(font, textRun, runs); + + float totalWidth = 14.0397830018083 + 12.0 + 43.8119349005425; + EXPECT_NEAR(controller.totalWidth(), totalWidth, 0.0001); + GlyphBuffer glyphBuffer; + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(0, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(1, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 43.8119349005425, 0.0001); + controller.advance(2, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 43.8119349005425 + 12.0, 0.0001); + controller.advance(3, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), totalWidth, 0.0001); + controller.advance(4, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), totalWidth, 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().width(), initialAdvance.width(), 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().height(), initialAdvance.height(), 0.0001); + EXPECT_EQ(glyphBuffer.size(), 4U); + EXPECT_NEAR(glyphBuffer.advanceAt(0).width(), 43.8119349005425, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(1).width(), 12.0, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(2).width(), 14.0397830018083, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(3).width(), -4.33996383363472, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(3).height(), 12.368896925859, 0.0001); +} + +TEST_F(ComplexTextControllerTest, LeadingExpansion) +{ + FontCascadeDescription description; + description.setOneFamily("Times"); + description.setComputedSize(48); + FontCascade font(description); + font.update(); + + UChar characters[] = { 'a' }; + size_t charactersLength = WTF_ARRAY_LENGTH(characters); + TextRun textRun(StringView(characters, charactersLength), 0, 100, ForceLeadingExpansion); + auto run = ComplexTextController::ComplexTextRun::create({ FloatSize(24, 0) }, { }, { 16 }, { 0 }, FloatSize(), font.primaryFont(), characters, 0, charactersLength, 0, 1, true); + Vector> runs; + runs.append(WTFMove(run)); + ComplexTextController controller(font, textRun, runs); + + float totalWidth = 100 + 24; + EXPECT_NEAR(controller.totalWidth(), totalWidth, 0.0001); + GlyphBuffer glyphBuffer; + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(0, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(1, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), totalWidth, 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().width(), 100, 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().height(), 0, 0.0001); + EXPECT_EQ(glyphBuffer.size(), 1U); + EXPECT_NEAR(glyphBuffer.advanceAt(0).width(), 24, 0.0001); +} + +TEST_F(ComplexTextControllerTest, VerticalAdvances) +{ + FontCascadeDescription description; + description.setOneFamily("Times"); + description.setComputedSize(48); + FontCascade font(description); + font.update(); + + UChar characters[] = { 'a', 'b', 'c', 'd' }; + size_t charactersLength = WTF_ARRAY_LENGTH(characters); + TextRun textRun(StringView(characters, charactersLength)); + auto run1 = ComplexTextController::ComplexTextRun::create({ FloatSize(0, 1), FloatSize(0, 2) }, { FloatPoint(0, 4), FloatPoint(0, 8) }, { 16, 17 }, { 0, 1 }, FloatSize(0, 16), font.primaryFont(), characters, 0, charactersLength, 0, 2, true); + auto run2 = ComplexTextController::ComplexTextRun::create({ FloatSize(0, 32), FloatSize(0, 64) }, { FloatPoint(0, 128), FloatPoint(0, 256) }, { 18, 19 }, { 2, 3 }, FloatSize(0, 512), font.primaryFont(), characters, 0, charactersLength, 2, 4, true); + Vector> runs; + runs.append(WTFMove(run1)); + runs.append(WTFMove(run2)); + ComplexTextController controller(font, textRun, runs); + + EXPECT_NEAR(controller.totalWidth(), 0, 0.0001); + GlyphBuffer glyphBuffer; + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(0, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(1, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(2, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(3, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(4, &glyphBuffer); + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().width(), 0, 0.0001); + EXPECT_NEAR(glyphBuffer.initialAdvance().height(), 16, 0.0001); + EXPECT_EQ(glyphBuffer.size(), 4U); + EXPECT_NEAR(glyphBuffer.advanceAt(0).width(), 0, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(0).height(), 4 - 1 -8, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(1).width(), 0, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(1).height(), 8 - 2 - 512, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(2).width(), 0, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(2).height(), 128 - 32 - 256, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(3).width(), 0, 0.0001); + EXPECT_NEAR(glyphBuffer.advanceAt(3).height(), 256 - 64, 0.0001); +} + +TEST_F(ComplexTextControllerTest, TotalWidthWithJustification) +{ + FontCascadeDescription description; + description.setOneFamily("Times"); + description.setComputedSize(80); + FontCascade font(description); + font.update(); + + Vector advances = { FloatSize(1, 0), FloatSize(2, 0), FloatSize(4, 0), FloatSize(8, 0), FloatSize(16, 0) }; +#if USE_LAYOUT_SPECIFIC_ADVANCES + Vector origins = { FloatPoint(), FloatPoint(), FloatPoint(), FloatPoint(), FloatPoint() }; +#else + Vector origins = { }; +#endif + + FloatSize initialAdvance = FloatSize(); + + UChar characters[] = { 0x644, ' ', 0x644, ' ', 0x644 }; + size_t charactersLength = WTF_ARRAY_LENGTH(characters); + TextRun textRun(StringView(characters, charactersLength), 0, 14, DefaultExpansion, RTL); + auto run = ComplexTextController::ComplexTextRun::create(advances, origins, { 5, 6, 7, 8, 9 }, { 4, 3, 2, 1, 0 }, initialAdvance, font.primaryFont(), characters, 0, charactersLength, 0, 5, false); + Vector> runs; + runs.append(WTFMove(run)); + ComplexTextController controller(font, textRun, runs); + + EXPECT_NEAR(controller.totalWidth(), 1 + 20 + 7 + 4 + 20 + 7 + 16, 0.0001); + GlyphBuffer glyphBuffer; + EXPECT_NEAR(controller.runWidthSoFar(), 0, 0.0001); + controller.advance(5, &glyphBuffer); + EXPECT_EQ(glyphBuffer.size(), 5U); + EXPECT_NEAR(glyphBuffer.advanceAt(0).width() + glyphBuffer.advanceAt(1).width() + glyphBuffer.advanceAt(2).width() + glyphBuffer.advanceAt(3).width() + glyphBuffer.advanceAt(4).width(), controller.totalWidth(), 0.0001); +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp b/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp new file mode 100644 index 000000000..40950c273 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp @@ -0,0 +1,2795 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "PlatformUtilities.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace WebCore { +namespace ContentExtensions { +inline std::ostream& operator<<(std::ostream& os, const ActionType& action) +{ + switch (action) { + case ActionType::BlockLoad: + return os << "ActionType::BlockLoad"; + case ActionType::BlockCookies: + return os << "ActionType::BlockCookies"; + case ActionType::CSSDisplayNoneSelector: + return os << "ActionType::CSSDisplayNone"; + case ActionType::CSSDisplayNoneStyleSheet: + return os << "ActionType::CSSDisplayNoneStyleSheet"; + case ActionType::IgnorePreviousRules: + return os << "ActionType::IgnorePreviousRules"; + case ActionType::MakeHTTPS: + return os << "ActionType::MakeHTTPS"; + case ActionType::InvalidAction: + return os << "ActionType::InvalidAction"; + } +} +} +} + +using namespace WebCore; + +namespace TestWebKitAPI { + +class ContentExtensionTest : public testing::Test { +public: + virtual void SetUp() + { + JSC::initializeThreading(); + RunLoop::initializeMainRunLoop(); + } +}; + +struct CompiledContentExtensionData { + Vector actions; + Vector filtersWithoutDomains; + Vector filtersWithDomains; + Vector domainFilters; +}; + +class InMemoryContentExtensionCompilationClient final : public ContentExtensions::ContentExtensionCompilationClient { +public: + InMemoryContentExtensionCompilationClient(CompiledContentExtensionData& data) + : m_data(data) + { + EXPECT_EQ(data.actions.size(), 0ull); + EXPECT_EQ(data.filtersWithoutDomains.size(), 0ull); + EXPECT_EQ(data.filtersWithDomains.size(), 0ull); + EXPECT_EQ(data.domainFilters.size(), 0ull); + } + + void writeActions(Vector&& actions) override + { + EXPECT_FALSE(finalized); + EXPECT_EQ(m_data.actions.size(), 0ull); + EXPECT_EQ(m_data.filtersWithoutDomains.size(), 0ull); + EXPECT_EQ(m_data.filtersWithDomains.size(), 0ull); + EXPECT_EQ(m_data.domainFilters.size(), 0ull); + m_data.actions.appendVector(actions); + } + + void writeFiltersWithoutDomainsBytecode(Vector&& bytecode) override + { + EXPECT_FALSE(finalized); + EXPECT_EQ(m_data.filtersWithDomains.size(), 0ull); + EXPECT_EQ(m_data.domainFilters.size(), 0ull); + m_data.filtersWithoutDomains.appendVector(bytecode); + } + + void writeFiltersWithDomainsBytecode(Vector&& bytecode) override + { + EXPECT_FALSE(finalized); + EXPECT_EQ(m_data.domainFilters.size(), 0ull); + m_data.filtersWithDomains.appendVector(bytecode); + } + + void writeDomainFiltersBytecode(Vector&& bytecode) override + { + EXPECT_FALSE(finalized); + m_data.domainFilters.appendVector(bytecode); + } + + void finalize() override + { + finalized = true; + } + +private: + CompiledContentExtensionData& m_data; + bool finalized { false }; +}; + +class InMemoryCompiledContentExtension : public ContentExtensions::CompiledContentExtension { +public: + static RefPtr createFromFilter(String&& filter) + { + CompiledContentExtensionData extensionData; + InMemoryContentExtensionCompilationClient client(extensionData); + auto compilerError = ContentExtensions::compileRuleList(client, WTFMove(filter)); + if (compilerError) { + // Compiling should always succeed here. We have other tests for compile failures. + EXPECT_TRUE(false); + return nullptr; + } + + return InMemoryCompiledContentExtension::create(WTFMove(extensionData)); + } + + static RefPtr create(CompiledContentExtensionData&& data) + { + return adoptRef(new InMemoryCompiledContentExtension(WTFMove(data))); + } + + virtual ~InMemoryCompiledContentExtension() + { + } + + const ContentExtensions::SerializedActionByte* actions() const override { return m_data.actions.data(); } + unsigned actionsLength() const override { return m_data.actions.size(); } + const ContentExtensions::DFABytecode* filtersWithoutDomainsBytecode() const override { return m_data.filtersWithoutDomains.data(); } + unsigned filtersWithoutDomainsBytecodeLength() const override { return m_data.filtersWithoutDomains.size(); } + const ContentExtensions::DFABytecode* filtersWithDomainsBytecode() const override { return m_data.filtersWithDomains.data(); } + unsigned filtersWithDomainsBytecodeLength() const override { return m_data.filtersWithDomains.size(); } + const ContentExtensions::DFABytecode* domainFiltersBytecode() const override { return m_data.domainFilters.data(); } + unsigned domainFiltersBytecodeLength() const override { return m_data.domainFilters.size(); } + +private: + InMemoryCompiledContentExtension(CompiledContentExtensionData&& data) + : m_data(WTFMove(data)) + { + } + + CompiledContentExtensionData m_data; +}; + +void static testRequest(ContentExtensions::ContentExtensionsBackend contentExtensionsBackend, const ResourceLoadInfo& resourceLoadInfo, Vector expectedActions, bool ignorePreviousRules = false) +{ + auto actions = contentExtensionsBackend.actionsForResourceLoad(resourceLoadInfo); + unsigned expectedSize = actions.size(); + if (!ignorePreviousRules) + expectedSize--; // The last action is applying the compiled stylesheet. + + EXPECT_EQ(expectedActions.size(), expectedSize); + if (expectedActions.size() != expectedSize) + return; + + for (unsigned i = 0; i < expectedActions.size(); ++i) + EXPECT_EQ(expectedActions[i], actions[i].type()); + if (!ignorePreviousRules) + EXPECT_EQ(actions[actions.size() - 1].type(), ContentExtensions::ActionType::CSSDisplayNoneStyleSheet); +} + +static ResourceLoadInfo mainDocumentRequest(const char* url, ResourceType resourceType = ResourceType::Document) +{ + return { URL(URL(), url), URL(URL(), url), resourceType }; +} + +static ResourceLoadInfo subResourceRequest(const char* url, const char* mainDocumentURL, ResourceType resourceType = ResourceType::Document) +{ + return { URL(URL(), url), URL(URL(), mainDocumentURL), resourceType }; +} + +ContentExtensions::ContentExtensionsBackend makeBackend(const char* json) +{ + AtomicString::init(); + auto extension = InMemoryCompiledContentExtension::createFromFilter(json); + ContentExtensions::ContentExtensionsBackend backend; + backend.addContentExtension("testFilter", extension); + return backend; +} + +static Vector createNFAs(ContentExtensions::CombinedURLFilters& combinedURLFilters) +{ + Vector nfas; + + combinedURLFilters.processNFAs(std::numeric_limits::max(), [&](ContentExtensions::NFA&& nfa) { + nfas.append(WTFMove(nfa)); + }); + + return nfas; +} + +TEST_F(ContentExtensionTest, Basic) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); +} + +TEST_F(ContentExtensionTest, SingleCharacter) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^z\"}}]"); + testRequest(matchBackend, mainDocumentRequest("http://webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("zttp://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"y\"}}]"); + testRequest(searchBackend, mainDocumentRequest("http://webkit.org/"), { }); + testRequest(searchBackend, mainDocumentRequest("http://webkit.org/ywebkit"), { ContentExtensions::ActionType::BlockLoad }); +} + +TEST_F(ContentExtensionTest, SingleCharacterDisjunction) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^z\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^c\"}}]"); + testRequest(matchBackend, mainDocumentRequest("http://webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("bttp://webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("cttp://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("dttp://webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("zttp://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"x\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"y\"}}]"); + testRequest(searchBackend, mainDocumentRequest("http://webkit.org/"), { }); + testRequest(searchBackend, mainDocumentRequest("http://webkit.org/dwebkit"), { }); + testRequest(searchBackend, mainDocumentRequest("http://webkit.org/xwebkit"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("http://webkit.org/ywebkit"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("http://webkit.org/zwebkit"), { }); +} + +TEST_F(ContentExtensionTest, RangeBasic) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"w[0-9]c\", \"url-filter-is-case-sensitive\":true}}," + "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"[A-H][a-z]cko\", \"url-filter-is-case-sensitive\":true}}]"); + + testRequest(backend, mainDocumentRequest("http://w3c.org"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("w2c://whatwg.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/w0c"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/wac"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/wAc"), { }); + + // Note: URL parsing and canonicalization lowercase the scheme and hostname. + testRequest(backend, mainDocumentRequest("Aacko://webkit.org"), { }); + testRequest(backend, mainDocumentRequest("aacko://webkit.org"), { }); + testRequest(backend, mainDocumentRequest("http://gCcko.org/"), { }); + testRequest(backend, mainDocumentRequest("http://gccko.org/"), { }); + + testRequest(backend, mainDocumentRequest("http://webkit.org/Gecko"), { ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("http://webkit.org/gecko"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/GEcko"), { }); +} + +TEST_F(ContentExtensionTest, RangeExclusionGeneratingUniversalTransition) +{ + // Transition of the type ([^X]X) effictively transition on every input. + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[^a]+afoobar\"}}]"); + + testRequest(backend, mainDocumentRequest("http://w3c.org"), { }); + + testRequest(backend, mainDocumentRequest("http://w3c.org/foobafoobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://w3c.org/foobarfoobar"), { }); + testRequest(backend, mainDocumentRequest("http://w3c.org/FOOBAFOOBAR"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://w3c.org/FOOBARFOOBAR"), { }); + + // The character before the "a" prefix cannot be another "a". + testRequest(backend, mainDocumentRequest("http://w3c.org/aafoobar"), { }); + testRequest(backend, mainDocumentRequest("http://w3c.org/Aafoobar"), { }); + testRequest(backend, mainDocumentRequest("http://w3c.org/aAfoobar"), { }); + testRequest(backend, mainDocumentRequest("http://w3c.org/AAfoobar"), { }); +} + +TEST_F(ContentExtensionTest, PatternStartingWithGroup) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(http://whatwg\\\\.org/)?webkit\134\134.org\"}}]"); + + testRequest(backend, mainDocumentRequest("http://whatwg.org/webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://whatwg.org/webkit.org"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("http://whatwg.org/"), { }); + testRequest(backend, mainDocumentRequest("http://whatwg.org"), { }); +} + +TEST_F(ContentExtensionTest, PatternNestedGroups) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/(foo(bar)*)+\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobarbar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foofoobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foob"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foor"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("http://webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/fobar"), { }); +} + +TEST_F(ContentExtensionTest, EmptyGroups) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/foo()bar\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/((me)()(too))\"}}]"); + testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/me"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/too"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/metoo"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foome"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foomebar"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/mefoo"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/mefootoo"), { }); +} + +TEST_F(ContentExtensionTest, QuantifiedEmptyGroups) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/foo()+bar\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/(()*()?(target)()+)\"}}]"); + testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/me"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/too"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/target"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foome"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foomebar"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/mefoo"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/mefootoo"), { }); +} + +TEST_F(ContentExtensionTest, MatchPastEndOfString) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".+\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobarbar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foofoobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foob"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foor"), { ContentExtensions::ActionType::BlockLoad }); +} + +TEST_F(ContentExtensionTest, StartOfLineAssertion) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^foobar\"}}]"); + + testRequest(backend, mainDocumentRequest("foobar://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("foobars:///foobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("foobarfoobar:///foobarfoobarfoobar"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoo"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobarf"), { }); + testRequest(backend, mainDocumentRequest("http://foobar.org/"), { }); + testRequest(backend, mainDocumentRequest("http://foobar.org/"), { }); +} + +TEST_F(ContentExtensionTest, EndOfLineAssertion) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foobar$\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("file:///foobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("file:///foobarfoobarfoobar"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoo"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobarf"), { }); +} + +TEST_F(ContentExtensionTest, EndOfLineAssertionWithInvertedCharacterSet) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[^y]$\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/a"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/Ya"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/yFoobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/y"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/Y"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobary"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/foobarY"), { }); +} + +TEST_F(ContentExtensionTest, DotDoesNotIncludeEndOfLine) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"https://webkit\\\\.org/.\"}}]"); + + testRequest(backend, mainDocumentRequest("https://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("https://webkit.org/A"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/z"), { ContentExtensions::ActionType::BlockLoad }); +} + +TEST_F(ContentExtensionTest, PrefixInfixSuffixExactMatch) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"infix\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^prefix\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"suffix$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://exact\\\\.org/$\"}}]"); + + testRequest(backend, mainDocumentRequest("infix://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://infix.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/infix"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("prefix://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://prefix.org/"), { }); + testRequest(backend, mainDocumentRequest("https://webkit.org/prefix"), { }); + + testRequest(backend, mainDocumentRequest("https://webkit.org/suffix"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://suffix.org/"), { }); + testRequest(backend, mainDocumentRequest("suffix://webkit.org/"), { }); + + testRequest(backend, mainDocumentRequest("http://exact.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://exact.org/oops"), { }); +} + +TEST_F(ContentExtensionTest, DuplicatedMatchAllTermsInVariousFormat) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*.*(.)*(.*)(.+)*(.?)*infix\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"pre(.?)+(.+)?post\"}}]"); + + testRequest(backend, mainDocumentRequest("infix://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://infix.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/infix"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("pre://webkit.org/post"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://prepost.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://pre.org/posttail"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://pre.pre/posttail"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://pre.org/posttailpost"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("https://post.org/pre"), { }); + testRequest(backend, mainDocumentRequest("https://pre.org/pre"), { }); + testRequest(backend, mainDocumentRequest("https://post.org/post"), { }); +} + +TEST_F(ContentExtensionTest, UndistinguishableActionInsidePrefixTree) +{ + // In this case, the two actions are undistinguishable. The actions of "prefix" appear inside the prefixtree + // ending at "suffix". + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"prefix\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"prefixsuffix\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("http://prefix.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/prefix"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/aaaprefixaaa"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://prefixsuffix.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/prefixsuffix"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/bbbprefixsuffixbbb"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("http://suffix.org/"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/suffix"), { }); +} + +TEST_F(ContentExtensionTest, DistinguishableActionInsidePrefixTree) +{ + // In this case, the two actions are distinguishable. + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"prefix\"}}," + "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"prefixsuffix\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("http://prefix.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/prefix"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/aaaprefixaaa"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://prefixsuffix.org/"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/prefixsuffix"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/bbbprefixsuffixbbb"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("http://suffix.org/"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/suffix"), { }); +} + +TEST_F(ContentExtensionTest, DistinguishablePrefixAreNotMerged) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foo\\\\.org\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bar\\\\.org\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("http://foo.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://bar.org/"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("http://foor.org/"), { }); + testRequest(backend, mainDocumentRequest("http://fooar.org/"), { }); + testRequest(backend, mainDocumentRequest("http://fooba.org/"), { }); + testRequest(backend, mainDocumentRequest("http://foob.org/"), { }); + testRequest(backend, mainDocumentRequest("http://foor.org/"), { }); + testRequest(backend, mainDocumentRequest("http://foar.org/"), { }); + testRequest(backend, mainDocumentRequest("http://foba.org/"), { }); + testRequest(backend, mainDocumentRequest("http://fob.org/"), { }); + testRequest(backend, mainDocumentRequest("http://barf.org/"), { }); + testRequest(backend, mainDocumentRequest("http://barfo.org/"), { }); + testRequest(backend, mainDocumentRequest("http://baroo.org/"), { }); + testRequest(backend, mainDocumentRequest("http://baro.org/"), { }); + testRequest(backend, mainDocumentRequest("http://baf.org/"), { }); + testRequest(backend, mainDocumentRequest("http://bafo.org/"), { }); + testRequest(backend, mainDocumentRequest("http://baoo.org/"), { }); + testRequest(backend, mainDocumentRequest("http://bao.org/"), { }); + + testRequest(backend, mainDocumentRequest("http://foo.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://oo.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://o.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://.orgbar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://rgbar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://gbar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://foo.orgar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://foo.orgr.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://foo.org.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://foo.orgorg/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://foo.orgrg/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://foo.orgg/"), { ContentExtensions::ActionType::BlockLoad }); +} + +static void compareContents(const ContentExtensions::DFABytecodeInterpreter::Actions& a, const Vector& b) +{ + EXPECT_EQ(a.size(), b.size()); + for (unsigned i = 0; i < b.size(); ++i) + EXPECT_TRUE(a.contains(b[i])); +} + +TEST_F(ContentExtensionTest, SearchSuffixesWithIdenticalActionAreMerged) +{ + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("foo\\.org", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("ba\\.org", false, 0)); + + Vector nfas = createNFAs(combinedURLFilters); + EXPECT_EQ(1ul, nfas.size()); + EXPECT_EQ(12ul, nfas.first().nodes.size()); + + ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first()); + Vector bytecode; + ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode); + compiler.compile(); + ContentExtensions::DFABytecodeInterpreter interpreter(bytecode.data(), bytecode.size()); + compareContents(interpreter.interpret("foo.org", 0), { 0 }); + compareContents(interpreter.interpret("ba.org", 0), { 0 }); + compareContents(interpreter.interpret("bar.org", 0), { }); + + compareContents(interpreter.interpret("paddingfoo.org", 0), { 0 }); + compareContents(interpreter.interpret("paddingba.org", 0), { 0 }); + compareContents(interpreter.interpret("paddingbar.org", 0), { }); +} + +TEST_F(ContentExtensionTest, SearchSuffixesWithDistinguishableActionAreNotMerged) +{ + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("foo\\.org", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("ba\\.org", false, 1)); + + Vector nfas = createNFAs(combinedURLFilters); + + EXPECT_EQ(1ul, nfas.size()); + EXPECT_EQ(17ul, nfas.first().nodes.size()); + + ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first()); + Vector bytecode; + ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode); + compiler.compile(); + ContentExtensions::DFABytecodeInterpreter interpreter(bytecode.data(), bytecode.size()); + compareContents(interpreter.interpret("foo.org", 0), { 0 }); + compareContents(interpreter.interpret("ba.org", 0), { 1 }); + compareContents(interpreter.interpret("bar.org", 0), { }); + + compareContents(interpreter.interpret("paddingfoo.org", 0), { 0 }); + compareContents(interpreter.interpret("paddingba.org", 0), { 1 }); + compareContents(interpreter.interpret("paddingba.orgfoo.org", 0), { 1, 0 }); + compareContents(interpreter.interpret("paddingbar.org", 0), { }); +} + +TEST_F(ContentExtensionTest, DomainTriggers) +{ + auto ifDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"webkit.org\"]}}]"); + testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.org/test.htm"), { }); + testRequest(ifDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { }); + testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(ifDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { }); + testRequest(ifDomainBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { }); + testRequest(ifDomainBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { }); + testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { }); + testRequest(ifDomainBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { }); + testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.organization/test.html"), { }); + + auto unlessDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"webkit.org\"]}}]"); + testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.org/test.htm"), { }); + testRequest(unlessDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { }); + testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { }); + testRequest(unlessDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(unlessDomainBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(unlessDomainBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { }); + testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { }); + testRequest(unlessDomainBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.organization/test.html"), { ContentExtensions::ActionType::BlockLoad }); + + auto ifDomainStarBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"*webkit.org\"]}}]"); + testRequest(ifDomainStarBackend, mainDocumentRequest("http://webkit.org/test.htm"), { }); + testRequest(ifDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { }); + testRequest(ifDomainStarBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(ifDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(ifDomainStarBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(ifDomainStarBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { }); + testRequest(ifDomainStarBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { }); + testRequest(ifDomainStarBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { }); + testRequest(ifDomainStarBackend, mainDocumentRequest("http://webkit.organization/test.html"), { }); + + auto unlessDomainStarBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"*webkit.org\"]}}]"); + testRequest(unlessDomainStarBackend, mainDocumentRequest("http://webkit.org/test.htm"), { }); + testRequest(unlessDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { }); + testRequest(unlessDomainStarBackend, mainDocumentRequest("http://webkit.org/test.html"), { }); + testRequest(unlessDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { }); + testRequest(unlessDomainStarBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { }); + testRequest(unlessDomainStarBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { }); + testRequest(unlessDomainStarBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { }); + testRequest(unlessDomainStarBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(unlessDomainStarBackend, mainDocumentRequest("http://webkit.organization/test.html"), { ContentExtensions::ActionType::BlockLoad }); + + auto ifSubDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"sub1.webkit.org\"]}}]"); + testRequest(ifSubDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { }); + testRequest(ifSubDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { }); + testRequest(ifSubDomainBackend, mainDocumentRequest("http://sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(ifSubDomainBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { }); + + auto ifSubDomainStarBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"*sub1.webkit.org\"]}}]"); + testRequest(ifSubDomainStarBackend, mainDocumentRequest("http://webkit.org/test.html"), { }); + testRequest(ifSubDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { }); + testRequest(ifSubDomainStarBackend, mainDocumentRequest("http://sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(ifSubDomainStarBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + + auto unlessSubDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"sub1.webkit.org\"]}}]"); + testRequest(unlessSubDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(unlessSubDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(unlessSubDomainBackend, mainDocumentRequest("http://sub1.webkit.org/test.html"), { }); + testRequest(unlessSubDomainBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + + auto unlessSubDomainStarBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"*sub1.webkit.org\"]}}]"); + testRequest(unlessSubDomainStarBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(unlessSubDomainStarBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(unlessSubDomainStarBackend, mainDocumentRequest("http://sub1.webkit.org/test.html"), { }); + testRequest(unlessSubDomainStarBackend, mainDocumentRequest("http://sub2.sub1.webkit.org/test.html"), { }); + + auto combinedBackend1 = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test_block_load\", \"if-domain\":[\"webkit.org\"]}}," + "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"test_block_cookies\", \"unless-domain\":[\"webkit.org\"]}}]"); + testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org"), { }); + testRequest(combinedBackend1, mainDocumentRequest("http://not_webkit.org"), { }); + testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org/test_block_load.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_load.html", "http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://webkit.org/"), { }); + testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_load.html", "http://not_webkit.org/"), { }); + testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://not_webkit.org/"), { }); + testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org/test_block_cookies.html"), { }); + testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_cookies.html", "http://webkit.org/"), { }); + testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://webkit.org/"), { }); + testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_cookies.html", "http://not_webkit.org/path/to/main/document.html"), { ContentExtensions::ActionType::BlockCookies }); + testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://not_webkit.org/"), { }); + + auto combinedBackend2 = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test_block_load\\\\.html\", \"if-domain\":[\"webkit.org\"]}}," + "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"test_block_cookies\\\\.html\", \"unless-domain\":[\"w3c.org\"]}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"test_css\\\\.html\"}}]"); + testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_css.htm"), { }); + testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_block_load.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(combinedBackend2, mainDocumentRequest("http://not_webkit.org/test_block_load.html"), { }); + testRequest(combinedBackend2, mainDocumentRequest("http://not_webkit.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/TEST_CSS.hTmL/test_block_load.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad}); + testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_block_load.html"), { }); + testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_block_cookies.html"), { }); + testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_css.html/test_block_cookies.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(combinedBackend2, mainDocumentRequest("http://not_w3c.org/test_block_cookies.html"), { ContentExtensions::ActionType::BlockCookies }); + testRequest(combinedBackend2, mainDocumentRequest("http://not_w3c.org/test_css.html/test_block_cookies.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies }); + + auto ifDomainWithFlagsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\", \"if-domain\":[\"webkit.org\"],\"resource-type\":[\"image\"]}}]"); + testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.html"), { }); + testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.png", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad }); + testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { }); + testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.png", ResourceType::Image), { }); + + auto unlessDomainWithFlagsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\", \"unless-domain\":[\"webkit.org\"],\"resource-type\":[\"image\"]}}]"); + testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.html"), { }); + testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.png", ResourceType::Image), { }); + testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { }); + testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.png", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad }); + + // Domains should not be interepted as regular expressions. + auto domainRegexBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"we?bkit.org\"]}}]"); + testRequest(domainRegexBackend, mainDocumentRequest("http://webkit.org/test.html"), { }); + testRequest(domainRegexBackend, mainDocumentRequest("http://wbkit.org/test.html"), { }); + + auto multipleIfDomainsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"webkit.org\", \"w3c.org\"]}}]"); + testRequest(multipleIfDomainsBackend, mainDocumentRequest("http://webkit.org/test.htm"), { }); + testRequest(multipleIfDomainsBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(multipleIfDomainsBackend, mainDocumentRequest("http://w3c.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(multipleIfDomainsBackend, mainDocumentRequest("http://whatwg.org/test.html"), { }); + + auto multipleUnlessDomainsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"webkit.org\", \"w3c.org\"]}}]"); + testRequest(multipleUnlessDomainsBackend, mainDocumentRequest("http://webkit.org/test.htm"), { }); + testRequest(multipleUnlessDomainsBackend, mainDocumentRequest("http://webkit.org/test.html"), { }); + testRequest(multipleUnlessDomainsBackend, mainDocumentRequest("http://w3c.org/test.html"), { }); + testRequest(multipleUnlessDomainsBackend, mainDocumentRequest("http://whatwg.org/test.html"), { ContentExtensions::ActionType::BlockLoad }); + + // FIXME: Add and test domain-specific popup-only blocking (with layout tests). +} + +TEST_F(ContentExtensionTest, DomainTriggersAlongMergedActions) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"test\\\\.html\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"webkit.org\"]}}," + "{\"action\":{\"type\":\"css-display-none\", \"selector\": \"*\"},\"trigger\":{\"url-filter\":\"trigger-on-scripts\\\\.html\",\"resource-type\":[\"script\"]}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"ignore-previous\",\"resource-type\":[\"image\"]}}," + "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"except-this\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/test.htm"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("http://notwebkit.org/test.html"), { ContentExtensions::ActionType::BlockCookies }); + + testRequest(backend, mainDocumentRequest("http://notwebkit.org/trigger-on-scripts.html"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/trigger-on-scripts.html"), { }); + testRequest(backend, mainDocumentRequest("http://notwebkit.org/trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(backend, mainDocumentRequest("http://webkit.org/trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(backend, mainDocumentRequest("http://notwebkit.org/trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("http://webkit.org/trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies }); + + testRequest(backend, mainDocumentRequest("http://notwebkit.org/ignore-previous-trigger-on-scripts.html"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/ignore-previous-trigger-on-scripts.html"), { }); + testRequest(backend, mainDocumentRequest("http://notwebkit.org/ignore-previous-trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(backend, mainDocumentRequest("http://webkit.org/ignore-previous-trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(backend, mainDocumentRequest("http://notwebkit.org/ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("http://webkit.org/ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies }); + + testRequest(backend, mainDocumentRequest("http://notwebkit.org/ignore-previous-trigger-on-scripts.html", ResourceType::Image), { }, true); + testRequest(backend, mainDocumentRequest("http://webkit.org/ignore-previous-trigger-on-scripts.html", ResourceType::Image), { }, true); + testRequest(backend, mainDocumentRequest("http://notwebkit.org/ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Image), { }, true); + testRequest(backend, mainDocumentRequest("http://webkit.org/ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Image), { }, true); + + testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html"), { ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html"), { ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true); + testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true); + testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true); + testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true); + testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies }); + +} + +TEST_F(ContentExtensionTest, MultipleExtensions) +{ + auto extension1 = InMemoryCompiledContentExtension::createFromFilter("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"block_load\"}}]"); + auto extension2 = InMemoryCompiledContentExtension::createFromFilter("[{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"block_cookies\"}}]"); + ContentExtensions::ContentExtensionsBackend backend; + backend.addContentExtension("testFilter1", extension1); + backend.addContentExtension("testFilter2", extension2); + + // These each have two display:none stylesheets. The second one is implied by using the default parameter ignorePreviousRules = false. + testRequest(backend, mainDocumentRequest("http://webkit.org"), { ContentExtensions::ActionType::CSSDisplayNoneStyleSheet }); + testRequest(backend, mainDocumentRequest("http://webkit.org/block_load.html"), { ContentExtensions::ActionType::CSSDisplayNoneStyleSheet, ContentExtensions::ActionType::BlockLoad}); + testRequest(backend, mainDocumentRequest("http://webkit.org/block_cookies.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet}); + testRequest(backend, mainDocumentRequest("http://webkit.org/block_load/block_cookies.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet, ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/block_cookies/block_load.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet, ContentExtensions::ActionType::BlockLoad }); + + auto ignoreExtension1 = InMemoryCompiledContentExtension::createFromFilter("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"block_load\"}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"ignore1\"}}]"); + auto ignoreExtension2 = InMemoryCompiledContentExtension::createFromFilter("[{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"block_cookies\"}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"ignore2\"}}]"); + ContentExtensions::ContentExtensionsBackend backendWithIgnore; + backendWithIgnore.addContentExtension("testFilter1", ignoreExtension1); + backendWithIgnore.addContentExtension("testFilter2", ignoreExtension2); + + testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org"), { ContentExtensions::ActionType::CSSDisplayNoneStyleSheet, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet }, true); + testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org/block_load/ignore1.html"), { ContentExtensions::ActionType::CSSDisplayNoneStyleSheet }, true); + testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org/block_cookies/ignore1.html"), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet}, true); + testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org/block_load/ignore2.html"), { ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::CSSDisplayNoneStyleSheet }, true); + testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org/block_cookies/ignore2.html"), { ContentExtensions::ActionType::CSSDisplayNoneStyleSheet}, true); + testRequest(backendWithIgnore, mainDocumentRequest("http://webkit.org/block_load/block_cookies/ignore1/ignore2.html"), { }, true); +} + +TEST_F(ContentExtensionTest, TermsKnownToMatchAnything) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre1.*post1$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre2(.*)post2$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre3(.*)?post3$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre4(.*)+post4$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre5(.*)*post5$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre6(.)*post6$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre7(.+)*post7$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre8(.?)*post8$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre9(.+)?post9$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre0(.?)+post0$\"}}]"); + + testRequest(backend, mainDocumentRequest("pre1://webkit.org/post1"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("pre2://webkit.org/post2"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("pre3://webkit.org/post3"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("pre4://webkit.org/post4"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("pre5://webkit.org/post5"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("pre6://webkit.org/post6"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("pre7://webkit.org/post7"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("pre8://webkit.org/post8"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("pre9://webkit.org/post9"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("pre0://webkit.org/post0"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("pre1://webkit.org/post2"), { }); + testRequest(backend, mainDocumentRequest("pre2://webkit.org/post3"), { }); + testRequest(backend, mainDocumentRequest("pre3://webkit.org/post4"), { }); + testRequest(backend, mainDocumentRequest("pre4://webkit.org/post5"), { }); + testRequest(backend, mainDocumentRequest("pre5://webkit.org/post6"), { }); + testRequest(backend, mainDocumentRequest("pre6://webkit.org/post7"), { }); + testRequest(backend, mainDocumentRequest("pre7://webkit.org/post8"), { }); + testRequest(backend, mainDocumentRequest("pre8://webkit.org/post9"), { }); + testRequest(backend, mainDocumentRequest("pre9://webkit.org/post0"), { }); + testRequest(backend, mainDocumentRequest("pre0://webkit.org/post1"), { }); + + testRequest(backend, mainDocumentRequest("pre0://webkit.org/post1"), { }); + testRequest(backend, mainDocumentRequest("pre1://webkit.org/post2"), { }); + testRequest(backend, mainDocumentRequest("pre2://webkit.org/post3"), { }); + testRequest(backend, mainDocumentRequest("pre3://webkit.org/post4"), { }); + testRequest(backend, mainDocumentRequest("pre4://webkit.org/post5"), { }); + testRequest(backend, mainDocumentRequest("pre5://webkit.org/post6"), { }); + testRequest(backend, mainDocumentRequest("pre6://webkit.org/post7"), { }); + testRequest(backend, mainDocumentRequest("pre7://webkit.org/post8"), { }); + testRequest(backend, mainDocumentRequest("pre8://webkit.org/post9"), { }); + testRequest(backend, mainDocumentRequest("pre9://webkit.org/post0"), { }); +} + +TEST_F(ContentExtensionTest, TrailingDotStar) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foo.*$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bar(.*)$\"}}]"); + + testRequest(backend, mainDocumentRequest("https://webkit.org/"), { }); + + testRequest(backend, mainDocumentRequest("foo://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://foo.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.foo/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("bar://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://bar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.bar/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad }); +} + +TEST_F(ContentExtensionTest, TrailingTermsCarryingNoData) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foob?a?r?\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bazo(ok)?a?$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"cats*$\"}}]"); + + testRequest(backend, mainDocumentRequest("https://webkit.org/"), { }); + + // Anything is fine after foo. + testRequest(backend, mainDocumentRequest("https://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/foob"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/fooc"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/fooba"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/foobar-stuff"), { ContentExtensions::ActionType::BlockLoad }); + + // Bazooka has to be at the tail without any character not defined by the filter. + testRequest(backend, mainDocumentRequest("https://webkit.org/baz"), { }); + testRequest(backend, mainDocumentRequest("https://webkit.org/bazo"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/bazoa"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/bazob"), { }); + testRequest(backend, mainDocumentRequest("https://webkit.org/bazoo"), { }); + testRequest(backend, mainDocumentRequest("https://webkit.org/bazook"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/bazookb"), { }); + testRequest(backend, mainDocumentRequest("https://webkit.org/bazooka"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/bazookaa"), { }); + + // The pattern must finish with cat, with any number of 's' following it, but no other character. + testRequest(backend, mainDocumentRequest("https://cat.org/"), { }); + testRequest(backend, mainDocumentRequest("https://cats.org/"), { }); + testRequest(backend, mainDocumentRequest("https://webkit.org/cat"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/cats"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/catss"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/catsss"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/catso"), { }); +} + +TEST_F(ContentExtensionTest, UselessTermsMatchingEverythingAreEliminated) +{ + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern(".*web", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.*)web", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.)*web", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.+)*web", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.?)*web", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.+)?web", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(.?)+web", false, 0)); + + Vector nfas = createNFAs(combinedURLFilters); + EXPECT_EQ(1ul, nfas.size()); + EXPECT_EQ(7ul, nfas.first().nodes.size()); + + ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first()); + Vector bytecode; + ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode); + compiler.compile(); + ContentExtensions::DFABytecodeInterpreter interpreter(bytecode.data(), bytecode.size()); + compareContents(interpreter.interpret("eb", 0), { }); + compareContents(interpreter.interpret("we", 0), { }); + compareContents(interpreter.interpret("weeb", 0), { }); + compareContents(interpreter.interpret("web", 0), { 0 }); + compareContents(interpreter.interpret("wweb", 0), { 0 }); + compareContents(interpreter.interpret("wwebb", 0), { 0 }); + compareContents(interpreter.interpret("http://theweb.com/", 0), { 0 }); +} + +TEST_F(ContentExtensionTest, LoadType) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[\"third-party\"]}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"whatwg.org\",\"load-type\":[\"first-party\"]}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"alwaysblock.pdf\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org"), { }); + testRequest(backend, {URL(URL(), "http://webkit.org"), URL(URL(), "http://not_webkit.org"), ResourceType::Document}, { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("http://whatwg.org"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, {URL(URL(), "http://whatwg.org"), URL(URL(), "http://not_whatwg.org"), ResourceType::Document}, { }); + + testRequest(backend, mainDocumentRequest("http://foobar.org/alwaysblock.pdf"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, {URL(URL(), "http://foobar.org/alwaysblock.pdf"), URL(URL(), "http://not_foobar.org/alwaysblock.pdf"), ResourceType::Document}, { ContentExtensions::ActionType::BlockLoad }); +} + +TEST_F(ContentExtensionTest, ResourceType) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"block_all_types.org\",\"resource-type\":[\"document\",\"image\",\"style-sheet\",\"script\",\"font\",\"raw\",\"svg-document\",\"media\",\"popup\"]}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"block_only_images\",\"resource-type\":[\"image\"]}}]"); + + testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Document), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::StyleSheet), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Script), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Font), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Raw), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::SVGDocument), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Media), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Popup), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://block_only_images.org", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://block_only_images.org", ResourceType::Document), { }); +} + +TEST_F(ContentExtensionTest, ResourceAndLoadType) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"BlockOnlyIfThirdPartyAndScript\",\"resource-type\":[\"script\"],\"load-type\":[\"third-party\"]}}]"); + + testRequest(backend, subResourceRequest("http://webkit.org/BlockOnlyIfThirdPartyAndScript.js", "http://webkit.org", ResourceType::Script), { }); + testRequest(backend, subResourceRequest("http://webkit.org/BlockOnlyIfThirdPartyAndScript.png", "http://not_webkit.org", ResourceType::Image), { }); + testRequest(backend, subResourceRequest("http://webkit.org/BlockOnlyIfThirdPartyAndScript.js", "http://not_webkit.org", ResourceType::Script), { ContentExtensions::ActionType::BlockLoad }); +} + +TEST_F(ContentExtensionTest, ResourceOrLoadTypeMatchingEverything) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"resource-type\":[\"image\"]}}," + "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\".*\",\"load-type\":[\"third-party\"]}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\".*\",\"load-type\":[\"first-party\"]}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org"), { }, true); + testRequest(backend, {URL(URL(), "http://webkit.org"), URL(URL(), "http://not_webkit.org"), ResourceType::Document}, { ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, {URL(URL(), "http://webkit.org"), URL(URL(), "http://not_webkit.org"), ResourceType::Image}, { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad }); +} + +TEST_F(ContentExtensionTest, WideNFA) +{ + // Make an NFA with about 1400 nodes that won't be combined. + StringBuilder ruleList; + ruleList.append('['); + for (char c1 = 'A'; c1 <= 'Z'; ++c1) { + for (char c2 = 'A'; c2 <= 'C'; ++c2) { + for (char c3 = 'A'; c3 <= 'C'; ++c3) { + if (c1 != 'A' || c2 != 'A' || c3 != 'A') + ruleList.append(','); + ruleList.append("{\"action\":{\"type\":\""); + + // Make every other rule ignore-previous-rules to not combine actions. + if (!((c1 + c2 + c3) % 2)) + ruleList.append("ignore-previous-rules"); + else { + ruleList.append("css-display-none"); + ruleList.append("\",\"selector\":\""); + ruleList.append(c1); + ruleList.append(c2); + ruleList.append(c3); + } + ruleList.append("\"},\"trigger\":{\"url-filter\":\".*"); + ruleList.append(c1); + ruleList.append(c2); + ruleList.append(c3); + ruleList.append("\", \"url-filter-is-case-sensitive\":true}}"); + } + } + } + ruleList.append(']'); + + auto backend = makeBackend(ruleList.toString().utf8().data()); + + testRequest(backend, mainDocumentRequest("http://webkit.org/AAA"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(backend, mainDocumentRequest("http://webkit.org/YAA"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(backend, mainDocumentRequest("http://webkit.org/ZAA"), { }, true); + testRequest(backend, mainDocumentRequest("http://webkit.org/LAA/AAA"), { }, true); + testRequest(backend, mainDocumentRequest("http://webkit.org/LAA/MAA"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }, true); + testRequest(backend, mainDocumentRequest("http://webkit.org/"), { }); +} + +#ifdef NDEBUG +static uint64_t expectedIndex(char c, unsigned position) +{ + uint64_t index = c - 'A'; + for (unsigned i = 1; i < position; ++i) + index *= (i == 1) ? ('C' - 'A' + 1) : ('Z' - 'A' + 1); + return index; +} +#endif + +TEST_F(ContentExtensionTest, LargeJumps) +{ +// A large test like this is necessary to test 24 and 32 bit jumps, but it's so large it times out in debug builds. +#ifdef NDEBUG + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + + uint64_t patternId = 0; + for (char c1 = 'A'; c1 <= 'Z'; ++c1) { + for (char c2 = 'A'; c2 <= 'Z'; ++c2) { + for (char c3 = 'A'; c3 <= 'Z'; ++c3) { + for (char c4 = 'A'; c4 <= 'C'; ++c4) { + StringBuilder pattern; + pattern.append(c1); + pattern.append(c2); + pattern.append(c3); + pattern.append(c4); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern(pattern.toString(), true, patternId++)); + } + } + } + } + + Vector nfas; + combinedURLFilters.processNFAs(std::numeric_limits::max(), [&](ContentExtensions::NFA&& nfa) { + nfas.append(WTFMove(nfa)); + }); + EXPECT_EQ(nfas.size(), 1ull); + + Vector dfas; + for (auto& nfa : nfas) + dfas.append(ContentExtensions::NFAToDFA::convert(nfa)); + EXPECT_EQ(dfas.size(), 1ull); + + Vector combinedBytecode; + for (const auto& dfa : dfas) { + Vector bytecode; + ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode); + compiler.compile(); + combinedBytecode.appendVector(bytecode); + } + + ContentExtensions::DFABytecodeInterpreter interpreter(&combinedBytecode[0], combinedBytecode.size()); + + patternId = 0; + for (char c1 = 'A'; c1 <= 'Z'; ++c1) { + for (char c2 = 'A'; c2 <= 'Z'; ++c2) { + for (char c3 = 'A'; c3 <= 'Z'; ++c3) { + for (char c4 = 'A'; c4 <= 'C'; ++c4) { + StringBuilder pattern; + pattern.append(c1); + pattern.append(c2); + pattern.append(c3); + // Test different jumping patterns distributed throughout the DFA: + switch ((c1 + c2 + c3 + c4) % 4) { + case 0: + // This should not match. + pattern.append('x'); + pattern.append(c4); + break; + case 1: + // This should jump back to the root, then match. + pattern.append('x'); + pattern.append(c1); + pattern.append(c2); + pattern.append(c3); + pattern.append(c4); + break; + case 2: + // This should match at the end of the string. + pattern.append(c4); + break; + case 3: + // This should match then jump back to the root. + pattern.append(c4); + pattern.append('x'); + break; + } + auto matches = interpreter.interpret(pattern.toString().utf8(), 0); + switch ((c1 + c2 + c3 + c4) % 4) { + case 0: + compareContents(matches, { }); + break; + case 1: + case 2: + case 3: + compareContents(matches, {patternId}); + break; + } + patternId++; + } + } + } + } + + compareContents(interpreter.interpret("CAAAAx", 0), {expectedIndex('C', 4), expectedIndex('A', 1)}); + compareContents(interpreter.interpret("KAAAAx", 0), {expectedIndex('K', 4), expectedIndex('A', 1)}); + compareContents(interpreter.interpret("AKAAAx", 0), {expectedIndex('K', 3), expectedIndex('K', 4)}); + compareContents(interpreter.interpret("AKxAAAAx", 0), {expectedIndex('A', 1)}); + compareContents(interpreter.interpret("AKAxAAAAx", 0), {expectedIndex('A', 1)}); + compareContents(interpreter.interpret("AKAxZKAxZKZxAAAAx", 0), {expectedIndex('A', 1)}); + compareContents(interpreter.interpret("ZAAAA", 0), {expectedIndex('Z', 4), expectedIndex('A', 1)}); + compareContents(interpreter.interpret("ZZxZAAAB", 0), {expectedIndex('Z', 4), expectedIndex('B', 1)}); +#endif +} + +TEST_F(ContentExtensionTest, DeepNFA) +{ + const unsigned size = 100000; + + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + + // FIXME: DFAToNFA::convert takes way too long on these deep NFAs. We should optimize for that case. + + StringBuilder lotsOfAs; + for (unsigned i = 0; i < size; ++i) + lotsOfAs.append('A'); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern(lotsOfAs.toString().utf8().data(), false, 0)); + + // FIXME: Yarr ought to be able to handle 2MB regular expressions. + StringBuilder tooManyAs; + for (unsigned i = 0; i < size * 20; ++i) + tooManyAs.append('A'); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::YarrError, parser.addPattern(tooManyAs.toString().utf8().data(), false, 0)); + + StringBuilder nestedGroups; + for (unsigned i = 0; i < size; ++i) + nestedGroups.append('('); + for (unsigned i = 0; i < size; ++i) + nestedGroups.append("B)"); + // FIXME: Add nestedGroups. Right now it also takes too long. It should be optimized. + + // This should not crash and not timeout. + EXPECT_EQ(1ul, createNFAs(combinedURLFilters).size()); +} + +void checkCompilerError(const char* json, std::error_code expectedError) +{ + CompiledContentExtensionData extensionData; + InMemoryContentExtensionCompilationClient client(extensionData); + std::error_code compilerError = ContentExtensions::compileRuleList(client, json); + EXPECT_EQ(compilerError.value(), expectedError.value()); + if (compilerError.value()) + EXPECT_STREQ(compilerError.category().name(), expectedError.category().name()); +} + +TEST_F(ContentExtensionTest, MatchesEverything) +{ + // Only css-display-none rules with triggers that match everything, no domain rules, and no flags + // should go in the global display:none stylesheet. css-display-none rules with domain rules or flags + // are applied separately on pages where they apply. + auto backend1 = makeBackend("[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]"); + EXPECT_TRUE(nullptr != backend1.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter"))); + testRequest(backend1, mainDocumentRequest("http://webkit.org"), { }); // Selector is in global stylesheet. + + auto backend2 = makeBackend("[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\",\"if-domain\":[\"webkit.org\"]}}]"); + EXPECT_EQ(nullptr, backend2.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter"))); + testRequest(backend2, mainDocumentRequest("http://webkit.org"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(backend2, mainDocumentRequest("http://w3c.org"), { }); + + auto backend3 = makeBackend("[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\",\"unless-domain\":[\"webkit.org\"]}}]"); + EXPECT_EQ(nullptr, backend3.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter"))); + testRequest(backend3, mainDocumentRequest("http://webkit.org"), { }); + testRequest(backend3, mainDocumentRequest("http://w3c.org"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + + auto backend4 = makeBackend("[{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\",\"load-type\":[\"third-party\"]}}]"); + EXPECT_EQ(nullptr, backend4.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter"))); + testRequest(backend4, mainDocumentRequest("http://webkit.org"), { }); + testRequest(backend4, subResourceRequest("http://not_webkit.org", "http://webkit.org"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + + // css-display-none rules after ignore-previous-rules should not be put in the default stylesheet. + auto backend5 = makeBackend("[{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\".*\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]"); + EXPECT_EQ(nullptr, backend5.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter"))); + testRequest(backend5, mainDocumentRequest("http://webkit.org"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }, true); + + auto backend6 = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"if-domain\":[\"webkit.org\",\"*w3c.org\"],\"resource-type\":[\"document\",\"script\"]}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"ignore\",\"if-domain\":[\"*webkit.org\",\"w3c.org\"]}}," + "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\".*\",\"unless-domain\":[\"webkit.org\",\"whatwg.org\"],\"resource-type\":[\"script\",\"image\"],\"load-type\":[\"third-party\"]}}]"); + EXPECT_EQ(nullptr, backend6.globalDisplayNoneStyleSheet(ASCIILiteral("testFilter"))); + testRequest(backend6, mainDocumentRequest("http://webkit.org"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend6, mainDocumentRequest("http://w3c.org"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend6, mainDocumentRequest("http://whatwg.org"), { }); + testRequest(backend6, mainDocumentRequest("http://sub.webkit.org"), { }); + testRequest(backend6, mainDocumentRequest("http://sub.w3c.org"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend6, mainDocumentRequest("http://sub.whatwg.org"), { }); + testRequest(backend6, mainDocumentRequest("http://webkit.org/ignore"), { }, true); + testRequest(backend6, mainDocumentRequest("http://w3c.org/ignore"), { }, true); + testRequest(backend6, mainDocumentRequest("http://whatwg.org/ignore"), { }); + testRequest(backend6, mainDocumentRequest("http://sub.webkit.org/ignore"), { }, true); + testRequest(backend6, mainDocumentRequest("http://sub.w3c.org/ignore"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend6, mainDocumentRequest("http://sub.whatwg.org/ignore"), { }); + testRequest(backend6, subResourceRequest("http://example.com/image.png", "http://webkit.org/", ResourceType::Image), { }); + testRequest(backend6, subResourceRequest("http://example.com/image.png", "http://w3c.org/", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }); + testRequest(backend6, subResourceRequest("http://example.com/doc.html", "http://webkit.org/", ResourceType::Document), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend6, subResourceRequest("http://example.com/script.js", "http://webkit.org/", ResourceType::Script), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend6, subResourceRequest("http://example.com/script.js", "http://w3c.org/", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad }); + testRequest(backend6, subResourceRequest("http://example.com/script.js", "http://example.com/", ResourceType::Script), { }); + testRequest(backend6, subResourceRequest("http://example.com/ignore/image.png", "http://webkit.org/", ResourceType::Image), { }, true); + testRequest(backend6, subResourceRequest("http://example.com/ignore/image.png", "http://example.com/", ResourceType::Image), { }); + testRequest(backend6, subResourceRequest("http://example.com/ignore/image.png", "http://example.org/", ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }); + testRequest(backend6, subResourceRequest("http://example.com/doc.html", "http://example.org/", ResourceType::Document), { }); + testRequest(backend6, subResourceRequest("http://example.com/", "http://example.com/", ResourceType::Font), { }); + testRequest(backend6, subResourceRequest("http://example.com/ignore", "http://webkit.org/", ResourceType::Image), { }, true); + testRequest(backend6, subResourceRequest("http://example.com/ignore", "http://webkit.org/", ResourceType::Font), { }, true); + testRequest(backend6, subResourceRequest("http://example.com/", "http://example.com/", ResourceType::Script), { }); + testRequest(backend6, subResourceRequest("http://example.com/ignore", "http://example.com/", ResourceType::Script), { }); +} + +TEST_F(ContentExtensionTest, InvalidJSON) +{ + checkCompilerError("[", ContentExtensions::ContentExtensionError::JSONInvalid); + checkCompilerError("123", ContentExtensions::ContentExtensionError::JSONTopLevelStructureNotAnObject); + checkCompilerError("{}", ContentExtensions::ContentExtensionError::JSONTopLevelStructureNotAnArray); + // FIXME: Add unit test for JSONInvalidRule if that is possible to hit. + checkCompilerError("[]", ContentExtensions::ContentExtensionError::JSONContainsNoRules); + + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":5}]", + ContentExtensions::ContentExtensionError::JSONInvalidTrigger); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"\"}}]", + ContentExtensions::ContentExtensionError::JSONInvalidURLFilterInTrigger); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":{}}}]", + ContentExtensions::ContentExtensionError::JSONInvalidURLFilterInTrigger); + + // FIXME: Add unit test for JSONInvalidObjectInTriggerFlagsArray if that is possible to hit. + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":{}}}]", + ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[\"invalid\"]}}]", + ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[5]}}]", + ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":5}}]", + ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":\"first-party\"}}]", + ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":null}}]", + ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":false}}]", + ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":{}}}]", + ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":[\"invalid\"]}}]", + ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":[5]}}]", + ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":5}}]", + ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":\"document\"}}]", + ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":null}}]", + ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":false}}]", + ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray); + + StringBuilder rules; + rules.append("["); + for (unsigned i = 0; i < 49999; ++i) + rules.append("{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"a\"}},"); + String rules50000 = rules.toString(); + String rules50001 = rules.toString(); + rules50000.append("{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"a\"}}]"); + rules50001.append("{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"a\"}},{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"a\"}}]"); + checkCompilerError(rules50000.utf8().data(), { }); + checkCompilerError(rules50001.utf8().data(), ContentExtensions::ContentExtensionError::JSONTooManyRules); + + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":{}}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[5]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"]}}]", { }); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":\"a\"}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":false}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":null}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":{}}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[5]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"\"]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":\"a\"}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":null}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":false}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"A\"]}}]", ContentExtensions::ContentExtensionError::JSONDomainNotLowerCaseASCII); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"\\u00DC\"]}}]", ContentExtensions::ContentExtensionError::JSONDomainNotLowerCaseASCII); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"0\"]}}]", { }); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"a\"]}}]", { }); + + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[],\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":5}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":5}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":5,\"unless-domain\":5}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList); + + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"],\"unless-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONUnlessAndIfDomain); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"],\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONUnlessAndIfDomain); + + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\", \"unexpected-identifier-should-be-ignored\":5}}]", { }); + + checkCompilerError("[{\"action\":5,\"trigger\":{\"url-filter\":\"webkit.org\"}}]", + ContentExtensions::ContentExtensionError::JSONInvalidAction); + checkCompilerError("[{\"action\":{\"type\":\"invalid\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]", + ContentExtensions::ContentExtensionError::JSONInvalidActionType); + checkCompilerError("[{\"action\":{\"type\":\"css-display-none\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]", + ContentExtensions::ContentExtensionError::JSONInvalidCSSDisplayNoneActionType); + + checkCompilerError("[{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]", { }); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"if-domain\":[\"a\"]}}]", { }); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"unless-domain\":[\"a\"]}}]", { }); + checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[\"}}]", + ContentExtensions::ContentExtensionError::JSONInvalidRegex); +} + +TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines1) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^.*foo\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bar$\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[ab]+bang\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("foo://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bar://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("abang://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bbang://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("cbang://webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/bang"), { }); + testRequest(backend, mainDocumentRequest("bang://webkit.org/"), { }); +} + +TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines1Partitioning) +{ + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + + // Those two share a prefix. + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^.*foo", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("bar$", false, 1)); + + // Not this one. + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^[ab]+bang", false, 0)); + + EXPECT_EQ(2ul, createNFAs(combinedURLFilters).size()); +} + +TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines2) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^foo\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^.*[a-c]+bar\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^webkit:\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-c]+b+oom\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("foo://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("webkit://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("http://bar.org/"), { }); + testRequest(backend, mainDocumentRequest("http://abar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://bbar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://cbar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://abcbar.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://dbar.org/"), { }); +} + +TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines2Partitioning) +{ + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^foo", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^.*[a-c]+bar", false, 1)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^webkit:", false, 2)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("[a-c]+b+oom", false, 3)); + + // "^foo" and "^webkit:" can be grouped, the other two have a variable prefix. + EXPECT_EQ(3ul, createNFAs(combinedURLFilters).size()); +} + +TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines3) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"A*D\"}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"A*BA+\"}}," + "{\"action\":{\"type\":\"make-https\"},\"trigger\":{\"url-filter\":\"A*BC\"}}]"); + + testRequest(backend, mainDocumentRequest("http://webkit.org/D"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/AAD"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://webkit.org/AB"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/ABA"), { }, true); + testRequest(backend, mainDocumentRequest("http://webkit.org/ABAD"), { }, true); + testRequest(backend, mainDocumentRequest("http://webkit.org/BC"), { ContentExtensions::ActionType::MakeHTTPS }); + testRequest(backend, mainDocumentRequest("http://webkit.org/ABC"), { ContentExtensions::ActionType::MakeHTTPS }); + testRequest(backend, mainDocumentRequest("http://webkit.org/ABABC"), { ContentExtensions::ActionType::MakeHTTPS }, true); + testRequest(backend, mainDocumentRequest("http://webkit.org/ABABCAD"), { ContentExtensions::ActionType::MakeHTTPS }, true); + testRequest(backend, mainDocumentRequest("http://webkit.org/ABCAD"), { ContentExtensions::ActionType::MakeHTTPS, ContentExtensions::ActionType::BlockLoad }); +} + +TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines3Partitioning) +{ + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A*D", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A*BA+", false, 1)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A*BC", false, 2)); + + // "A*A" and "A*BC" can be grouped, "A*BA+" should not. + EXPECT_EQ(2ul, createNFAs(combinedURLFilters).size()); +} + +TEST_F(ContentExtensionTest, SplittingLargeNFAs) +{ + const size_t expectedNFACounts[16] = {3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 1, 1, 1}; + + for (size_t i = 0; i < 16; i++) { + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A+BBB", false, 1)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A+CCC", false, 2)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("A+DDD", false, 2)); + + Vector nfas; + combinedURLFilters.processNFAs(i, [&](ContentExtensions::NFA&& nfa) { + nfas.append(WTFMove(nfa)); + }); + EXPECT_EQ(nfas.size(), expectedNFACounts[i]); + + Vector dfas; + for (auto& nfa : nfas) + dfas.append(ContentExtensions::NFAToDFA::convert(nfa)); + + Vector combinedBytecode; + for (const auto& dfa : dfas) { + Vector bytecode; + ContentExtensions::DFABytecodeCompiler compiler(dfa, bytecode); + compiler.compile(); + combinedBytecode.appendVector(bytecode); + } + + ContentExtensions::DFABytecodeInterpreter interpreter(&combinedBytecode[0], combinedBytecode.size()); + + EXPECT_EQ(interpreter.interpret("ABBBX", 0).size(), 1ull); + EXPECT_EQ(interpreter.interpret("ACCCX", 0).size(), 1ull); + EXPECT_EQ(interpreter.interpret("ADDDX", 0).size(), 1ull); + EXPECT_EQ(interpreter.interpret("XBBBX", 0).size(), 0ull); + EXPECT_EQ(interpreter.interpret("ABBX", 0).size(), 0ull); + EXPECT_EQ(interpreter.interpret("ACCX", 0).size(), 0ull); + EXPECT_EQ(interpreter.interpret("ADDX", 0).size(), 0ull); + } +} + +TEST_F(ContentExtensionTest, QuantifierInGroup) +{ + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(((A+)B)C)", false, 0)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(((A)B+)C)", false, 1)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(((A)B+)C)D", false, 2)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(((A)B)C+)", false, 3)); + EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("(((A)B)C)", false, 4)); + + // (((A)B+)C) and (((A)B+)C)D should be in the same NFA. + EXPECT_EQ(4ul, createNFAs(combinedURLFilters).size()); +} + +static void testPatternStatus(String pattern, ContentExtensions::URLFilterParser::ParseStatus status) +{ + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + EXPECT_EQ(status, parser.addPattern(pattern, false, 0)); +} + +TEST_F(ContentExtensionTest, ParsingFailures) +{ + testPatternStatus("a*b?.*.?[a-z]?[a-z]*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("a*b?.*.?[a-z]?[a-z]+", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("a*b?.*.?[a-z]?[a-z]", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus(".*?a", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus(".*a", ContentExtensions::URLFilterParser::ParseStatus::Ok); + + testPatternStatus("(?!)", ContentExtensions::URLFilterParser::ParseStatus::Group); + testPatternStatus("(?=)", ContentExtensions::URLFilterParser::ParseStatus::Group); + testPatternStatus("(?!a)", ContentExtensions::URLFilterParser::ParseStatus::Group); + testPatternStatus("(?=a)", ContentExtensions::URLFilterParser::ParseStatus::Group); + testPatternStatus("(regex)", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("(regex", ContentExtensions::URLFilterParser::ParseStatus::YarrError); + testPatternStatus("((regex)", ContentExtensions::URLFilterParser::ParseStatus::YarrError); + testPatternStatus("(?:regex)", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("(?:regex", ContentExtensions::URLFilterParser::ParseStatus::YarrError); + testPatternStatus("[^.]+", ContentExtensions::URLFilterParser::ParseStatus::Ok); + + testPatternStatus("a++", ContentExtensions::URLFilterParser::ParseStatus::YarrError); + testPatternStatus("[a]++", ContentExtensions::URLFilterParser::ParseStatus::YarrError); + testPatternStatus("+", ContentExtensions::URLFilterParser::ParseStatus::YarrError); + + testPatternStatus("[", ContentExtensions::URLFilterParser::ParseStatus::YarrError); + testPatternStatus("[a}", ContentExtensions::URLFilterParser::ParseStatus::YarrError); + + // FIXME: Look into why these do not cause YARR parsing errors. They probably should. + testPatternStatus("a]", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("{", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("{[a]", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("{0", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("{0,", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("{0,1", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("a{0,1", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("a{a,b}", ContentExtensions::URLFilterParser::ParseStatus::Ok); + + const char nonASCII[2] = {-1, '\0'}; + testPatternStatus(nonASCII, ContentExtensions::URLFilterParser::ParseStatus::NonASCII); + testPatternStatus("\\xff", ContentExtensions::URLFilterParser::ParseStatus::NonASCII); + + testPatternStatus("\\x\\r\\n", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("\\b", ContentExtensions::URLFilterParser::ParseStatus::WordBoundary); + testPatternStatus("[\\d]", ContentExtensions::URLFilterParser::ParseStatus::AtomCharacter); + testPatternStatus("\\d\\D\\w\\s\\v\\h\\i\\c", ContentExtensions::URLFilterParser::ParseStatus::UnsupportedCharacterClass); + + testPatternStatus("this|that", ContentExtensions::URLFilterParser::ParseStatus::Disjunction); + testPatternStatus("a{0,1}b", ContentExtensions::URLFilterParser::ParseStatus::Ok); + testPatternStatus("a{0,2}b", ContentExtensions::URLFilterParser::ParseStatus::InvalidQuantifier); + testPatternStatus("", ContentExtensions::URLFilterParser::ParseStatus::EmptyPattern); + testPatternStatus("$$", ContentExtensions::URLFilterParser::ParseStatus::MisplacedEndOfLine); + testPatternStatus("a^", ContentExtensions::URLFilterParser::ParseStatus::MisplacedStartOfLine); + testPatternStatus("(^)", ContentExtensions::URLFilterParser::ParseStatus::MisplacedStartOfLine); + + testPatternStatus("(a)\\1", ContentExtensions::URLFilterParser::ParseStatus::Ok); // This should be BackReference, right? +} + +TEST_F(ContentExtensionTest, PatternMatchingTheEmptyString) +{ + // Simple atoms. + testPatternStatus(".*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("a*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus(".?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("a?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + + // Character sets. + testPatternStatus("[a-z]*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("[a-z]?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + + // Groups. + testPatternStatus("(foobar)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("(foobar)?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("(.*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("(a*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("(.?)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("(a?)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("([a-z]*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("([a-z]?)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + + testPatternStatus("(.)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("(.+)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("(.?)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("(.*)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("(.+)?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + testPatternStatus("(.?)+", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); + + // Nested groups. + testPatternStatus("((foo)?((.)*)(bar)*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything); +} + +TEST_F(ContentExtensionTest, MinimizingWithMoreFinalStatesThanNonFinalStates) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^h[a-z://]+\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://foo.com/\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://bar.com/\"}}]"); + + testRequest(backend, mainDocumentRequest("http://foo.com/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("http://bar.com/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("attp://foo.com/"), { }); + testRequest(backend, mainDocumentRequest("attp://bar.com/"), { }); + + testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bttp://webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("bttps://webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("http://webkit.org/b"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://webkit.org/b"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("cttp://webkit.org/B"), { }); + testRequest(backend, mainDocumentRequest("cttps://webkit.org/B"), { }); +} + +TEST_F(ContentExtensionTest, StatesWithDifferentActionsAreNotUnified1) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://www.webkit.org/\"}}," + "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"^https://www.webkit.org/\"}}," + "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"^attps://www.webkit.org/\"}}]"); + + testRequest(backend, mainDocumentRequest("http://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://www.webkit.org/"), { ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("attps://www.webkit.org/"), { ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("http://www.webkit.org/a"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://www.webkit.org/B"), { ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("attps://www.webkit.org/c"), { ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("http://www.whatwg.org/"), { }); + testRequest(backend, mainDocumentRequest("https://www.whatwg.org/"), { }); + testRequest(backend, mainDocumentRequest("attps://www.whatwg.org/"), { }); +} + +TEST_F(ContentExtensionTest, StatesWithDifferentActionsAreNotUnified2) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://www.webkit.org/\"}}," + "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"^https://www.webkit.org/\"}}," + "{\"action\":{\"type\":\"css-display-none\", \"selector\":\"#foo\"},\"trigger\":{\"url-filter\":\"^https://www.webkit.org/\"}}]"); + + testRequest(backend, mainDocumentRequest("http://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("https://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies }); + testRequest(backend, mainDocumentRequest("https://www.whatwg.org/"), { }); + testRequest(backend, mainDocumentRequest("attps://www.whatwg.org/"), { }); +} + +// The order in which transitions from the root will be processed is unpredictable. +// To exercises the various options, this test exists in various version exchanging the transition to the final state. +TEST_F(ContentExtensionTest, FallbackTransitionsWithDifferentiatorDoNotMerge1) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.a\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^b.a\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bac\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bbc\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^BCC\"}}]"); + + testRequest(backend, mainDocumentRequest("aza://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bac://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bbc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bcc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("aac://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("abc://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("acc://www.webkit.org/"), { }); + + testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { }); +} +TEST_F(ContentExtensionTest, FallbackTransitionsWithDifferentiatorDoNotMerge2) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bac\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bbc\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^BCC\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.a\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^b.a\"}}]"); + + testRequest(backend, mainDocumentRequest("aza://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bac://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bbc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bcc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("aac://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("abc://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("acc://www.webkit.org/"), { }); + + testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { }); +} +TEST_F(ContentExtensionTest, FallbackTransitionsWithDifferentiatorDoNotMerge3) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.c\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^b.c\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^baa\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bba\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^BCA\"}}]"); + + testRequest(backend, mainDocumentRequest("azc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("baa://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bba://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bca://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("aaa://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("aba://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("aca://www.webkit.org/"), { }); + + testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { }); +} +TEST_F(ContentExtensionTest, FallbackTransitionsWithDifferentiatorDoNotMerge4) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^baa\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^bba\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^BCA\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.c\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^b.c\"}}]"); + + testRequest(backend, mainDocumentRequest("azc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bzc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("baa://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bba://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bca://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("aaa://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("aba://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("aca://www.webkit.org/"), { }); + + testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("bza://www.webkit.org/"), { }); +} + +TEST_F(ContentExtensionTest, FallbackTransitionsToOtherNodeInSameGroupDoesNotDifferentiateGroup) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^aac\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.c\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^b.c\"}}]"); + + testRequest(backend, mainDocumentRequest("aac://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("abc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("bac://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("abc://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("aaa://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("aca://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("baa://www.webkit.org/"), { }); +} + +TEST_F(ContentExtensionTest, SimpleFallBackTransitionDifferentiator1) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.bc.de\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^a.bd.ef\"}}]"); + + testRequest(backend, mainDocumentRequest("abbccde://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("aabcdde://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("aabddef://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("aabddef://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("abcde://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("abdef://www.webkit.org/"), { }); +} + +TEST_F(ContentExtensionTest, SimpleFallBackTransitionDifferentiator2) +{ + auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^cb.\"}}," + "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^db.b\"}}]"); + + testRequest(backend, mainDocumentRequest("cba://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("cbb://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("dbab://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(backend, mainDocumentRequest("dbxb://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + + testRequest(backend, mainDocumentRequest("cca://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("dddd://www.webkit.org/"), { }); + testRequest(backend, mainDocumentRequest("bbbb://www.webkit.org/"), { }); +} + +// *** We have the following ranges intersections: *** +// Full overlap. +// 1) +// A: |-----| +// B: |---| +// 2) +// A: |-----| +// B: | +// 3) +// A: |---| +// B: |-----| +// 4) +// A: | +// B: |-----| +// One edge in common +// 5) +// A: |-| +// B: |-| +// 6) +// A: | +// B: |-| +// 7) +// A: |-| +// B: | +// 8) +// A: |-| +// B: |-| +// 9) +// A: | +// B: |-| +// 10) +// A: |-| +// B: | +// B overlap on the left side of A. +// 11) +// A: |---| +// B: |---| +// 12) +// A: |---| +// B: |-----| +// A overlap on the left side of B +// 13) +// A: |---| +// B: |---| +// 14) +// A: |-----| +// B: |---| +// Equal ranges +// 15) +// A: |---| +// B: |---| +// 16) +// A: | +// B: | + +TEST_F(ContentExtensionTest, RangeOverlapCase1) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[a-m]\"}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"^[c-e]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { }, true); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { }, true); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { }, true); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-m]\"}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"[c-e]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { }, true); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { }, true); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { }, true); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase2) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[a-m]\"}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"^b\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { }, true); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-m]\"}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"l\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.k.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.l.xxx/"), { }, true); + testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase3) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[b-d]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[a-m]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[b-d]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[a-m]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase4) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^l\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[a-m]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("k://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("l://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"l\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[a-m]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.k.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.l.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase5) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[a-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[e-h]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[e-h]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase6) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^e\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[e-h]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"e\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[e-h]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase7) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[a-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^e\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"e\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase8) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[e-h]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[a-e]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[e-h]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[a-e]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase9) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^e\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[a-e]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"e\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[a-e]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase10) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[e-h]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^e\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[e-h]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"e\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase11) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[e-g]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[d-f]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[e-g]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[d-f]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { }); +} + + +TEST_F(ContentExtensionTest, RangeOverlapCase12) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[e-g]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[d-g]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[e-g]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[d-g]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase13) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[b-d]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[c-e]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[b-d]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[c-e]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase14) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[b-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[c-e]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[b-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[c-e]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase15) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[c-f]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^[c-f]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[c-f]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[c-f]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, RangeOverlapCase16) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^c\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^c\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"c\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"c\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, QuantifiedOneOrMoreRangesCase11And13) +{ + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[c-e]+[g-i]+YYY\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[b-d]+[h-j]+YYY\"}}]"); + + // The interesting ranges only match between 'b' and 'k', plus a gap in 'f'. + testRequest(searchBackend, mainDocumentRequest("zzz://www.aayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.abyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.acyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.adyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.aeyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.afyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.agyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ahyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.aiyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ajyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.akyyy.xxx/"), { }); + + // 'b' is the first character of the second rule. + testRequest(searchBackend, mainDocumentRequest("zzz://www.byyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bbyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bcyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bdyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.beyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bfyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bgyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.biyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bkyyy.xxx/"), { }); + + // 'c' is the first character of the first rule, and it overlaps the first character of the second rule. + testRequest(searchBackend, mainDocumentRequest("zzz://www.cyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cbyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ccyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cdyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ceyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cfyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.chyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ciyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ckyyy.xxx/"), { }); + + // 'd' is in the first range of both rule. This series cover overlaps between the two rules. + testRequest(searchBackend, mainDocumentRequest("zzz://www.dgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.degyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dehyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dfgyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dfhyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.djyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); +} + +TEST_F(ContentExtensionTest, QuantifiedOneOrMoreRangesCase11And13InGroups) +{ + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"([c-e])+([g-i]+YYY)\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"[b-d]+[h-j]+YYY\"}}]"); + + // The interesting ranges only match between 'b' and 'k', plus a gap in 'f'. + testRequest(searchBackend, mainDocumentRequest("zzz://www.aayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.abyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.acyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.adyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.aeyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.afyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.agyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ahyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.aiyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ajyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.akyyy.xxx/"), { }); + + // 'b' is the first character of the second rule. + testRequest(searchBackend, mainDocumentRequest("zzz://www.byyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bbyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bcyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bdyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.beyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bfyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bgyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.biyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bkyyy.xxx/"), { }); + + // 'c' is the first character of the first rule, and it overlaps the first character of the second rule. + testRequest(searchBackend, mainDocumentRequest("zzz://www.cyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cbyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ccyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cdyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ceyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cfyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.chyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ciyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ckyyy.xxx/"), { }); + + // 'd' is in the first range of both rule. This series cover overlaps between the two rules. + testRequest(searchBackend, mainDocumentRequest("zzz://www.dgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.degyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dehyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dfgyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dfhyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.djyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase1) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[a-m]\"}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"^(bar)*[c-e]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { }, true); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { }, true); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { }, true); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[a-m]\"}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"(bar)*[c-e]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { }, true); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { }, true); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { }, true); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { }); +} + + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase2) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[a-m]\"}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"^(bar)*b\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { }, true); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[a-m]\"}}," + "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"(bar)*l\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.k.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.l.xxx/"), { }, true); + testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase3) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[b-d]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[a-m]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[b-d]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[a-m]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase4) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*l\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[a-m]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("k://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("l://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("m://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("n://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*l\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[a-m]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.k.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.l.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.m.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.n.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase5) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[a-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[e-h]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[a-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[e-h]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase6) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*e\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[e-h]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*e\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[e-h]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase7) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[a-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*e\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[a-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*e\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase8) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[e-h]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[a-e]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[e-h]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[a-e]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase9) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*e\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[a-e]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*e\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[a-e]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase10) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[e-h]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*e\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("i://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[e-h]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*e\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.i.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase11) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[e-g]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[d-f]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[e-g]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[d-f]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { }); +} + + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase12) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[e-g]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[d-g]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[e-g]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[d-g]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase13) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[b-d]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[c-e]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[b-d]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[c-e]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase14) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[b-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[c-e]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("h://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[b-e]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[c-e]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.h.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase15) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*[c-f]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*[c-f]\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("f://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("g://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[c-f]\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[c-f]\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.f.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.g.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedRangeOverlapCase16) +{ + auto matchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(foo)*c\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"^(bar)*c\"}}]"); + + testRequest(matchBackend, mainDocumentRequest("a://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("b://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("c://www.webkit.org/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(matchBackend, mainDocumentRequest("d://www.webkit.org/"), { }); + testRequest(matchBackend, mainDocumentRequest("e://www.webkit.org/"), { }); + + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*c\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*c\"}}]"); + testRequest(searchBackend, mainDocumentRequest("zzz://www.a.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.b.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.c.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.d.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.e.xxx/"), { }); +} + +TEST_F(ContentExtensionTest, CombinedQuantifiedOneOrMoreRangesCase11And13) +{ + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*[c-e]+[g-i]+YYY\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[b-d]+[h-j]+YYY\"}}]"); + + // The interesting ranges only match between 'b' and 'k', plus a gap in 'f'. + testRequest(searchBackend, mainDocumentRequest("zzz://www.aayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.abyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.acyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.adyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.aeyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.afyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.agyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ahyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.aiyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ajyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.akyyy.xxx/"), { }); + + // 'b' is the first character of the second rule. + testRequest(searchBackend, mainDocumentRequest("zzz://www.byyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bbyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bcyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bdyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.beyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bfyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bgyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.biyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bkyyy.xxx/"), { }); + + // 'c' is the first character of the first rule, and it overlaps the first character of the second rule. + testRequest(searchBackend, mainDocumentRequest("zzz://www.cyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cbyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ccyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cdyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ceyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cfyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.chyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ciyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ckyyy.xxx/"), { }); + + // 'd' is in the first range of both rule. This series cover overlaps between the two rules. + testRequest(searchBackend, mainDocumentRequest("zzz://www.dgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.degyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dehyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dfgyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dfhyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.djyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); +} + +TEST_F(ContentExtensionTest, CombinedQuantifiedOneOrMoreRangesCase11And13InGroups) +{ + auto searchBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"(foo)*([c-e])+([g-i]+YYY)\"}}," + "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"(bar)*[b-d]+[h-j]+YYY\"}}]"); + + // The interesting ranges only match between 'b' and 'k', plus a gap in 'f'. + testRequest(searchBackend, mainDocumentRequest("zzz://www.aayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.abyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.acyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.adyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.aeyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.afyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.agyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ahyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.aiyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ajyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.akyyy.xxx/"), { }); + + // 'b' is the first character of the second rule. + testRequest(searchBackend, mainDocumentRequest("zzz://www.byyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bbyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bcyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bdyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.beyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bfyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bgyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.biyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.bkyyy.xxx/"), { }); + + // 'c' is the first character of the first rule, and it overlaps the first character of the second rule. + testRequest(searchBackend, mainDocumentRequest("zzz://www.cyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cayyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cbyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ccyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cdyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ceyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cfyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.chyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ciyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.cjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ckyyy.xxx/"), { }); + + // 'd' is in the first range of both rule. This series cover overlaps between the two rules. + testRequest(searchBackend, mainDocumentRequest("zzz://www.dgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddgyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddhhyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.degyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dehyyy.xxx/"), { ContentExtensions::ActionType::BlockLoad }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dfgyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.dfhyyy.xxx/"), { }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.djyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); + testRequest(searchBackend, mainDocumentRequest("zzz://www.ddjjyyy.xxx/"), { ContentExtensions::ActionType::CSSDisplayNoneSelector }); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp b/Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp new file mode 100644 index 000000000..d53ebb75a --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "DFAHelpers.h" +#include +#include + +using namespace WebCore; +using namespace ContentExtensions; + +namespace TestWebKitAPI { + +class DFACombinerTest : public testing::Test { +public: + virtual void SetUp() + { + WTF::initializeMainThread(); + } +}; + +Vector combine(Vector dfas, unsigned minimumSize) +{ + DFACombiner combiner; + for (DFA& dfa : dfas) + combiner.addDFA(WTFMove(dfa)); + + Vector output; + combiner.combineDFAs(minimumSize, [&output](DFA&& dfa) { + output.append(dfa); + }); + return output; +} + +TEST_F(DFACombinerTest, Basic) +{ + Vector dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "bar"}) }; + Vector combinedDFAs = combine(dfas, 10000); + EXPECT_EQ(static_cast(1), combinedDFAs.size()); + + DFA reference = buildDFAFromPatterns({ "foo", "bar"}); + reference.minimize(); + EXPECT_EQ(countLiveNodes(reference), countLiveNodes(combinedDFAs.first())); +} + + +TEST_F(DFACombinerTest, IdenticalDFAs) +{ + Vector dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "foo"}) }; + Vector combinedDFAs = combine(dfas, 10000); + EXPECT_EQ(static_cast(1), combinedDFAs.size()); + + // The result should have the exact same size as the minimized input. + DFA reference = buildDFAFromPatterns({ "foo"}); + reference.minimize(); + EXPECT_EQ(countLiveNodes(reference), countLiveNodes(combinedDFAs.first())); +} + +TEST_F(DFACombinerTest, NoInput) +{ + DFACombiner combiner; + unsigned counter = 0; + combiner.combineDFAs(100000, [&counter](DFA&& dfa) { + ++counter; + }); + EXPECT_EQ(static_cast(0), counter); +} + +TEST_F(DFACombinerTest, SingleInput) +{ + Vector dfas = { buildDFAFromPatterns({ "WebKit"}) }; + Vector combinedDFAs = combine(dfas, 10000); + EXPECT_EQ(static_cast(1), combinedDFAs.size()); + + DFA reference = buildDFAFromPatterns({ "WebKit"}); + reference.minimize(); + EXPECT_EQ(countLiveNodes(reference), countLiveNodes(combinedDFAs.first())); +} + +TEST_F(DFACombinerTest, InputTooLargeForMinimumSize) +{ + Vector dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "bar"}) }; + Vector combinedDFAs = combine(dfas, 2); + EXPECT_EQ(static_cast(2), combinedDFAs.size()); + EXPECT_EQ(static_cast(4), countLiveNodes(combinedDFAs[0])); + EXPECT_EQ(static_cast(4), countLiveNodes(combinedDFAs[1])); +} + +TEST_F(DFACombinerTest, CombinedInputReachesMinimumSize) +{ + Vector dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "bar"}), buildDFAFromPatterns({ "WebKit"}) }; + Vector combinedDFAs = combine(dfas, 5); + EXPECT_EQ(static_cast(2), combinedDFAs.size()); + EXPECT_EQ(static_cast(7), countLiveNodes(combinedDFAs[0])); + EXPECT_EQ(static_cast(6), countLiveNodes(combinedDFAs[1])); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h b/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h new file mode 100644 index 000000000..8c6402760 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +static unsigned countLiveNodes(const ContentExtensions::DFA& dfa) +{ + unsigned counter = 0; + for (const auto& node : dfa.nodes) { + if (!node.isKilled()) + ++counter; + } + return counter; +} + +static Vector createNFAs(ContentExtensions::CombinedURLFilters& combinedURLFilters) +{ + Vector nfas; + + combinedURLFilters.processNFAs(std::numeric_limits::max(), [&](ContentExtensions::NFA&& nfa) { + nfas.append(WTFMove(nfa)); + }); + + return nfas; +} + +static ContentExtensions::DFA buildDFAFromPatterns(Vector patterns) +{ + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + + for (const char* pattern : patterns) + parser.addPattern(pattern, false, 0); + Vector nfas = createNFAs(combinedURLFilters); + return ContentExtensions::NFAToDFA::convert(nfas[0]); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp b/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp new file mode 100644 index 000000000..bb3ac2e2a --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2015 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "DFAHelpers.h" +#include + +namespace TestWebKitAPI { + +class DFAMinimizerTest : public testing::Test { +public: + virtual void SetUp() + { + WTF::initializeMainThread(); + } +}; + +TEST_F(DFAMinimizerTest, BasicSearch) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ ".*foo", ".*bar", ".*bang"}); + EXPECT_EQ(static_cast(8), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast(7), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, MergeSuffixes) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ ".*aaa", ".*aab", ".*aba", ".*abb", ".*baa", ".*bab", ".*bba", ".*bbb"}); + EXPECT_EQ(static_cast(12), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast(4), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, MergeInfixes) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ ".*aaakit", ".*aabkit", ".*abakit", ".*abbkit", ".*baakit", ".*babkit", ".*bbakit", ".*bbbkit"}); + EXPECT_EQ(static_cast(15), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast(7), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge1) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.a", "^b.a", "^bac", "^bbc", "^BCC"}); + EXPECT_EQ(static_cast(6), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast(6), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge2) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^bbc", "^BCC", "^a.a", "^b.a"}); + EXPECT_EQ(static_cast(6), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast(6), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge3) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.c", "^b.c", "^baa", "^bba", "^BCA"}); + EXPECT_EQ(static_cast(6), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast(6), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge4) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^baa", "^bba", "^BCA", "^a.c", "^b.c"}); + EXPECT_EQ(static_cast(6), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast(6), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, FallbackTransitionsToOtherNodeInSameGroupDoesNotDifferentiateGroup) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^aac", "^a.c", "^b.c"}); + EXPECT_EQ(static_cast(5), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast(4), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, SimpleFallBackTransitionDifferentiator1) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.bc.de", "^a.bd.ef"}); + EXPECT_EQ(static_cast(11), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast(11), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, SimpleFallBackTransitionDifferentiator2) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^cb.", "^db.b"}); + EXPECT_EQ(static_cast(7), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast(7), countLiveNodes(dfa)); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/ExtendedColor.cpp b/Tools/TestWebKitAPI/Tests/WebCore/ExtendedColor.cpp new file mode 100644 index 000000000..607777dc8 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/ExtendedColor.cpp @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "Test.h" +#include "WTFStringUtilities.h" +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +TEST(ExtendedColor, Constructor) +{ + Color c1(1.0, 0.5, 0.25, 1.0, ColorSpaceDisplayP3); + EXPECT_TRUE(c1.isExtended()); + EXPECT_FLOAT_EQ(1.0, c1.asExtended().red()); + EXPECT_FLOAT_EQ(0.5, c1.asExtended().green()); + EXPECT_FLOAT_EQ(0.25, c1.asExtended().blue()); + EXPECT_FLOAT_EQ(1.0, c1.asExtended().alpha()); + EXPECT_EQ(1u, c1.asExtended().refCount()); + EXPECT_EQ(c1.cssText(), "color(display-p3 1 0.5 0.25)"); +} + +TEST(ExtendedColor, CopyConstructor) +{ + Color c1(1.0, 0.5, 0.25, 1.0, ColorSpaceDisplayP3); + EXPECT_TRUE(c1.isExtended()); + + Color c2(c1); + + EXPECT_FLOAT_EQ(1.0, c2.asExtended().red()); + EXPECT_FLOAT_EQ(0.5, c2.asExtended().green()); + EXPECT_FLOAT_EQ(0.25, c2.asExtended().blue()); + EXPECT_FLOAT_EQ(1.0, c2.asExtended().alpha()); + EXPECT_EQ(2u, c1.asExtended().refCount()); + EXPECT_EQ(2u, c2.asExtended().refCount()); + EXPECT_EQ(c2.cssText(), "color(display-p3 1 0.5 0.25)"); +} + +TEST(ExtendedColor, Assignment) +{ + Color c1(1.0, 0.5, 0.25, 1.0, ColorSpaceDisplayP3); + EXPECT_TRUE(c1.isExtended()); + + Color c2 = c1; + + EXPECT_FLOAT_EQ(1.0, c2.asExtended().red()); + EXPECT_FLOAT_EQ(0.5, c2.asExtended().green()); + EXPECT_FLOAT_EQ(0.25, c2.asExtended().blue()); + EXPECT_FLOAT_EQ(1.0, c2.asExtended().alpha()); + EXPECT_EQ(2u, c1.asExtended().refCount()); + EXPECT_EQ(2u, c2.asExtended().refCount()); + EXPECT_EQ(c2.cssText(), "color(display-p3 1 0.5 0.25)"); +} + +TEST(ExtendedColor, MoveConstructor) +{ + Color c1(1.0, 0.5, 0.25, 1.0, ColorSpaceDisplayP3); + EXPECT_TRUE(c1.isExtended()); + + Color c2(WTFMove(c1)); + // We should have moved the extended color pointer into c2, + // and set c1 to invalid so that it doesn't cause deletion. + EXPECT_FALSE(c1.isExtended()); + EXPECT_FALSE(c1.isValid()); + + EXPECT_FLOAT_EQ(1.0, c2.asExtended().red()); + EXPECT_FLOAT_EQ(0.5, c2.asExtended().green()); + EXPECT_FLOAT_EQ(0.25, c2.asExtended().blue()); + EXPECT_FLOAT_EQ(1.0, c2.asExtended().alpha()); + EXPECT_EQ(1u, c2.asExtended().refCount()); + EXPECT_EQ(c2.cssText(), "color(display-p3 1 0.5 0.25)"); +} + +TEST(ExtendedColor, MoveAssignment) +{ + Color c1(1.0, 0.5, 0.25, 1.0, ColorSpaceDisplayP3); + EXPECT_TRUE(c1.isExtended()); + + Color c2 = WTFMove(c1); + + // We should have moved the extended color pointer into c2, + // and set c1 to invalid so that it doesn't cause deletion. + EXPECT_FALSE(c1.isExtended()); + EXPECT_FALSE(c1.isValid()); + + EXPECT_FLOAT_EQ(1.0, c2.asExtended().red()); + EXPECT_FLOAT_EQ(0.5, c2.asExtended().green()); + EXPECT_FLOAT_EQ(0.25, c2.asExtended().blue()); + EXPECT_FLOAT_EQ(1.0, c2.asExtended().alpha()); + EXPECT_EQ(1u, c2.asExtended().refCount()); + EXPECT_EQ(c2.cssText(), "color(display-p3 1 0.5 0.25)"); +} + +TEST(ExtendedColor, BasicReferenceCounting) +{ + Color* c1 = new Color(1.0, 0.5, 0.25, 1.0, ColorSpaceDisplayP3); + EXPECT_TRUE(c1->isExtended()); + + Color* c2 = new Color(*c1); + Color* c3 = new Color(*c2); + + EXPECT_FLOAT_EQ(1.0, c2->asExtended().red()); + EXPECT_FLOAT_EQ(0.5, c2->asExtended().green()); + EXPECT_FLOAT_EQ(0.25, c2->asExtended().blue()); + EXPECT_FLOAT_EQ(1.0, c2->asExtended().alpha()); + EXPECT_EQ(3u, c2->asExtended().refCount()); + EXPECT_EQ(c2->cssText(), "color(display-p3 1 0.5 0.25)"); + + delete c1; + EXPECT_EQ(2u, c2->asExtended().refCount()); + + delete c2; + EXPECT_EQ(1u, c3->asExtended().refCount()); + + delete c3; +} + +Color makeColor() +{ + Color c1(1.0, 0.5, 0.25, 1.0, ColorSpaceDisplayP3); + EXPECT_TRUE(c1.isExtended()); + EXPECT_EQ(1u, c1.asExtended().refCount()); + + return c1; +} + +TEST(ExtendedColor, ReturnValues) +{ + Color c2 = makeColor(); + EXPECT_TRUE(c2.isExtended()); + + EXPECT_EQ(1u, c2.asExtended().refCount()); + EXPECT_EQ(c2.cssText(), "color(display-p3 1 0.5 0.25)"); +} + + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/FileSystem.cpp b/Tools/TestWebKitAPI/Tests/WebCore/FileSystem.cpp new file mode 100644 index 000000000..8a4a78c65 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/FileSystem.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2015 Canon Inc. All rights reserved. + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "Test.h" +#include +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +const char* FileSystemTestData = "This is a test"; + +// FIXME: Refactor FileSystemTest and SharedBufferTest as a single class. +class FileSystemTest : public testing::Test { +public: + void SetUp() override + { + WTF::initializeMainThread(); + + // create temp file + PlatformFileHandle handle; + m_tempFilePath = openTemporaryFile("tempTestFile", handle); + writeToFile(handle, FileSystemTestData, strlen(FileSystemTestData)); + closeFile(handle); + + m_tempEmptyFilePath = openTemporaryFile("tempEmptyTestFile", handle); + closeFile(handle); + + m_spaceContainingFilePath = openTemporaryFile("temp Empty Test File", handle); + closeFile(handle); + + m_bangContainingFilePath = openTemporaryFile("temp!Empty!Test!File", handle); + closeFile(handle); + + m_quoteContainingFilePath = openTemporaryFile("temp\"Empty\"TestFile", handle); + closeFile(handle); + } + + void TearDown() override + { + deleteFile(m_tempFilePath); + deleteFile(m_tempEmptyFilePath); + deleteFile(m_spaceContainingFilePath); + deleteFile(m_bangContainingFilePath); + deleteFile(m_quoteContainingFilePath); + } + + const String& tempFilePath() { return m_tempFilePath; } + const String& tempEmptyFilePath() { return m_tempEmptyFilePath; } + const String& spaceContainingFilePath() { return m_spaceContainingFilePath; } + const String& bangContainingFilePath() { return m_bangContainingFilePath; } + const String& quoteContainingFilePath() { return m_quoteContainingFilePath; } + +private: + String m_tempFilePath; + String m_tempEmptyFilePath; + String m_spaceContainingFilePath; + String m_bangContainingFilePath; + String m_quoteContainingFilePath; +}; + +TEST_F(FileSystemTest, MappingMissingFile) +{ + bool success; + MappedFileData mappedFileData(String("not_existing_file"), success); + EXPECT_FALSE(success); + EXPECT_TRUE(!mappedFileData); +} + +TEST_F(FileSystemTest, MappingExistingFile) +{ + bool success; + MappedFileData mappedFileData(tempFilePath(), success); + EXPECT_TRUE(success); + EXPECT_TRUE(!!mappedFileData); + EXPECT_TRUE(mappedFileData.size() == strlen(FileSystemTestData)); + EXPECT_TRUE(strnstr(FileSystemTestData, static_cast(mappedFileData.data()), mappedFileData.size())); +} + +TEST_F(FileSystemTest, MappingExistingEmptyFile) +{ + bool success; + MappedFileData mappedFileData(tempEmptyFilePath(), success); + EXPECT_TRUE(success); + EXPECT_TRUE(!mappedFileData); +} + +TEST_F(FileSystemTest, FilesHaveSameVolume) +{ + EXPECT_TRUE(filesHaveSameVolume(tempFilePath(), spaceContainingFilePath())); + EXPECT_TRUE(filesHaveSameVolume(spaceContainingFilePath(), bangContainingFilePath())); + EXPECT_TRUE(filesHaveSameVolume(bangContainingFilePath(), quoteContainingFilePath())); +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/FloatPoint.cpp b/Tools/TestWebKitAPI/Tests/WebCore/FloatPoint.cpp new file mode 100644 index 000000000..24c0ff9e3 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/FloatPoint.cpp @@ -0,0 +1,598 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#if USE(CG) +#include +#endif + +#if PLATFORM(WIN) +#include +#endif + +namespace TestWebKitAPI { + +static void testGetAndSet(WebCore::FloatPoint point) +{ + point.setX(1.1f); + EXPECT_FLOAT_EQ(1.1f, point.x()); + point.setY(2.2f); + EXPECT_FLOAT_EQ(2.2f, point.y()); + + point.set(9.9f, 8.8f); + EXPECT_FLOAT_EQ(9.9f, point.x()); + EXPECT_FLOAT_EQ(8.8f, point.y()); +} + +TEST(FloatPoint, DefaultConstruction) +{ + WebCore::FloatPoint test; + + EXPECT_FLOAT_EQ(0, test.x()); + EXPECT_FLOAT_EQ(0, test.y()); + + testGetAndSet(test); +} + +TEST(FloatPoint, ValueConstruction) +{ + WebCore::FloatPoint test(9.9f, 8.8f); + + EXPECT_FLOAT_EQ(9.9f, test.x()); + EXPECT_FLOAT_EQ(8.8f, test.y()); + + testGetAndSet(test); +} + +TEST(FloatPoint, ZeroConstruction) +{ + WebCore::FloatPoint test = WebCore::FloatPoint::zero(); + + EXPECT_FLOAT_EQ(0, test.x()); + EXPECT_FLOAT_EQ(0, test.y()); +} + +TEST(FloatPoint, IntPointConstruction) +{ + WebCore::IntPoint testInput(2003, 1997); + WebCore::FloatPoint test = WebCore::FloatPoint(testInput); + + EXPECT_FLOAT_EQ(2003.0f, test.x()); + EXPECT_FLOAT_EQ(1997.0f, test.y()); +} + +TEST(FloatPoint, FloatSizeConstruction) +{ + WebCore::FloatSize testInput(500.7f, 300.2f); + WebCore::FloatPoint test = WebCore::FloatPoint(testInput); + + EXPECT_FLOAT_EQ(500.7, test.x()); + EXPECT_FLOAT_EQ(300.2, test.y()); +} + +TEST(FloatPoint, MoveByFloat) +{ + WebCore::FloatPoint test(100.0f, 200.0f); + + EXPECT_FLOAT_EQ(100.0f, test.x()); + EXPECT_FLOAT_EQ(200.0f, test.y()); + + test.move(20.2f, 30.3f); + + EXPECT_FLOAT_EQ(120.2f, test.x()); + EXPECT_FLOAT_EQ(230.3f, test.y()); + + test.move(-81.3f, 10.0f); + + EXPECT_FLOAT_EQ(38.9f, test.x()); + EXPECT_FLOAT_EQ(240.3f, test.y()); + + test.move(11.1f, -33.2f); + + EXPECT_FLOAT_EQ(50.0f, test.x()); + EXPECT_FLOAT_EQ(207.1f, test.y()); + + test.move(-5.6f, -9.8f); + + EXPECT_FLOAT_EQ(44.4f, test.x()); + EXPECT_FLOAT_EQ(197.3f, test.y()); +} + +TEST(FloatPoint, MoveByIntSize) +{ + WebCore::FloatPoint test(100.0f, 200.0f); + + EXPECT_FLOAT_EQ(100.0f, test.x()); + EXPECT_FLOAT_EQ(200.0f, test.y()); + + WebCore::IntSize first(20, 30); + + test.move(first); + + EXPECT_FLOAT_EQ(120.0f, test.x()); + EXPECT_FLOAT_EQ(230.0f, test.y()); + + WebCore::IntSize second(-81, 10); + + test.move(second); + + EXPECT_FLOAT_EQ(39.0f, test.x()); + EXPECT_FLOAT_EQ(240.0f, test.y()); + + WebCore::IntSize third(11, -33); + + test.move(third); + + EXPECT_FLOAT_EQ(50.0f, test.x()); + EXPECT_FLOAT_EQ(207.0f, test.y()); + + WebCore::IntSize fourth(-6, -10); + + test.move(fourth); + + EXPECT_FLOAT_EQ(44.0f, test.x()); + EXPECT_FLOAT_EQ(197.0f, test.y()); +} + +TEST(FloatPoint, MoveByFloatSize) +{ + WebCore::FloatPoint test(100.0f, 200.0f); + + EXPECT_FLOAT_EQ(100.0f, test.x()); + EXPECT_FLOAT_EQ(200.0f, test.y()); + + WebCore::FloatSize first(20.2f, 30.3f); + + test.move(first); + + EXPECT_FLOAT_EQ(120.2f, test.x()); + EXPECT_FLOAT_EQ(230.3f, test.y()); + + WebCore::FloatSize second(-81.3f, 10.0f); + + test.move(second); + + EXPECT_FLOAT_EQ(38.9f, test.x()); + EXPECT_FLOAT_EQ(240.3f, test.y()); + + WebCore::FloatSize third(11.1f, -33.2f); + + test.move(third); + + EXPECT_FLOAT_EQ(50.0f, test.x()); + EXPECT_FLOAT_EQ(207.1f, test.y()); + + WebCore::FloatSize fourth(-5.6f, -9.8f); + + test.move(fourth); + + EXPECT_FLOAT_EQ(44.4f, test.x()); + EXPECT_FLOAT_EQ(197.3f, test.y()); +} + +TEST(FloatPoint, MoveByIntPoint) +{ + WebCore::FloatPoint test(100.0f, 200.0f); + + EXPECT_FLOAT_EQ(100.0f, test.x()); + EXPECT_FLOAT_EQ(200.0f, test.y()); + + WebCore::IntPoint first(20, 30); + + test.moveBy(first); + + EXPECT_FLOAT_EQ(120.0f, test.x()); + EXPECT_FLOAT_EQ(230.0f, test.y()); + + WebCore::IntPoint second(-81, 10); + + test.moveBy(second); + + EXPECT_FLOAT_EQ(39.0f, test.x()); + EXPECT_FLOAT_EQ(240.0f, test.y()); + + WebCore::IntPoint third(11, -33); + + test.moveBy(third); + + EXPECT_FLOAT_EQ(50.0f, test.x()); + EXPECT_FLOAT_EQ(207.0f, test.y()); + + WebCore::IntPoint fourth(-6, -10); + + test.moveBy(fourth); + + EXPECT_FLOAT_EQ(44.0f, test.x()); + EXPECT_FLOAT_EQ(197.0f, test.y()); +} + +TEST(FloatPoint, MoveByFloatPoint) +{ + WebCore::FloatPoint test(100.0f, 200.0f); + + EXPECT_FLOAT_EQ(100.0f, test.x()); + EXPECT_FLOAT_EQ(200.0f, test.y()); + + WebCore::FloatPoint first(20.2f, 30.3f); + + test.moveBy(first); + + EXPECT_FLOAT_EQ(120.2f, test.x()); + EXPECT_FLOAT_EQ(230.3f, test.y()); + + WebCore::FloatPoint second(-81.3f, 10.0f); + + test.moveBy(second); + + EXPECT_FLOAT_EQ(38.9f, test.x()); + EXPECT_FLOAT_EQ(240.3f, test.y()); + + WebCore::FloatPoint third(11.1f, -33.2f); + + test.moveBy(third); + + EXPECT_FLOAT_EQ(50.0f, test.x()); + EXPECT_FLOAT_EQ(207.1f, test.y()); + + WebCore::FloatPoint fourth(-5.6f, -9.8f); + + test.moveBy(fourth); + + EXPECT_FLOAT_EQ(44.4f, test.x()); + EXPECT_FLOAT_EQ(197.3f, test.y()); +} + +TEST(FloatPoint, Scale) +{ + WebCore::FloatPoint test(100.0f, 200.0f); + + EXPECT_FLOAT_EQ(100.0f, test.x()); + EXPECT_FLOAT_EQ(200.0f, test.y()); + + test.scale(2.0f, 2.0f); + + EXPECT_FLOAT_EQ(200.0f, test.x()); + EXPECT_FLOAT_EQ(400.0f, test.y()); + + test.scale(0.5f, 0.5f); + + EXPECT_FLOAT_EQ(100.0f, test.x()); + EXPECT_FLOAT_EQ(200.0f, test.y()); + + test.scale(2); + + EXPECT_FLOAT_EQ(200.0f, test.x()); + EXPECT_FLOAT_EQ(400.0f, test.y()); + + test.scale(1.0f, 0.5f); + + EXPECT_FLOAT_EQ(200.0f, test.x()); + EXPECT_FLOAT_EQ(200.0f, test.y()); +} + +TEST(FloatPoint, Normalize) +{ + WebCore::FloatPoint a(30.0f, 40.0f); + + a.normalize(); + + EXPECT_FLOAT_EQ(0.6, a.x()); + EXPECT_FLOAT_EQ(0.8, a.y()); +} + +TEST(FloatPoint, Dot) +{ + WebCore::FloatPoint a(100.0f, 200.0f); + WebCore::FloatPoint b(2.0f, 4.0f); + WebCore::FloatPoint c(1.0f, 0.5f); + + EXPECT_NEAR(1000.0f, a.dot(b), 0.0001f); + EXPECT_NEAR(200.0f, a.dot(c), 0.0001f); +} + +TEST(FloatPoint, LengthSquared) +{ + WebCore::FloatPoint test(100.0f, 200.0f); + + EXPECT_FLOAT_EQ(100.0f, test.x()); + EXPECT_FLOAT_EQ(200.0f, test.y()); + + EXPECT_FLOAT_EQ(50000.0f, test.lengthSquared()); +} + +TEST(FloatPoint, ConstrainedBetween) +{ + WebCore::FloatPoint left(10.0f, 20.0f); + WebCore::FloatPoint right(100.0f, 200.0f); + + WebCore::FloatPoint test1(50.0f, 80.0f); + WebCore::FloatPoint test2(8.0f, 80.0f); + WebCore::FloatPoint test3(50.0f, 220.0f); + + auto constrained1 = test1.constrainedBetween(left, right); + EXPECT_FLOAT_EQ(50.0f, constrained1.x()); + EXPECT_FLOAT_EQ(80.0f, constrained1.y()); + + auto constrained2 = test2.constrainedBetween(left, right); + EXPECT_FLOAT_EQ(10.0f, constrained2.x()); + EXPECT_FLOAT_EQ(80.0f, constrained2.y()); + + auto constrained3 = test3.constrainedBetween(left, right); + EXPECT_FLOAT_EQ(50.0f, constrained3.x()); + EXPECT_FLOAT_EQ(200.0f, constrained3.y()); +} + +TEST(FloatPoint, ShrunkTo) +{ + WebCore::FloatPoint big(100.0f, 200.0f); + WebCore::FloatPoint smaller(10.0f, 80.0f); + + auto shrunkTo = big.shrunkTo(smaller); + + EXPECT_FLOAT_EQ(10.0f, shrunkTo.x()); + EXPECT_FLOAT_EQ(80.0f, shrunkTo.y()); + + WebCore::FloatPoint bigish(8.0f, 200.0f); + + auto shrunkTo2 = bigish.shrunkTo(smaller); + + EXPECT_FLOAT_EQ(8.0f, shrunkTo2.x()); + EXPECT_FLOAT_EQ(80.0f, shrunkTo2.y()); +} + +TEST(FloatPoint, ExpandedTo) +{ + WebCore::FloatPoint big(100.0f, 200.0f); + WebCore::FloatPoint smaller(10.0f, 80.0f); + + auto expandedTo = smaller.expandedTo(big); + + EXPECT_FLOAT_EQ(100.0f, expandedTo.x()); + EXPECT_FLOAT_EQ(200.0f, expandedTo.y()); + + WebCore::FloatPoint bigish(8.0f, 300.0f); + + auto expandedTo2 = bigish.expandedTo(big); + + EXPECT_FLOAT_EQ(100.0f, expandedTo2.x()); + EXPECT_FLOAT_EQ(300.0f, expandedTo2.y()); +} + +TEST(FloatPoint, Transpose) +{ + WebCore::FloatPoint test(100.0f, 200.0f); + + EXPECT_FLOAT_EQ(100.0f, test.x()); + EXPECT_FLOAT_EQ(200.0f, test.y()); + + auto transposed = test.transposedPoint(); + + EXPECT_FLOAT_EQ(200.0f, transposed.x()); + EXPECT_FLOAT_EQ(100.0f, transposed.y()); +} + +TEST(FloatPoint, Transforms) +{ + WebCore::FloatPoint test(100.0f, 200.0f); + + WebCore::AffineTransform affine; + + auto transformed1 = test.matrixTransform(affine); + + EXPECT_FLOAT_EQ(100.0f, transformed1.x()); + EXPECT_FLOAT_EQ(200.0f, transformed1.y()); + + WebCore::AffineTransform affine2(2.0, 0.0, 0.0, 2.0, 0.0, 0.0); + + auto transformed2 = test.matrixTransform(affine2); + + EXPECT_FLOAT_EQ(200.0f, transformed2.x()); + EXPECT_FLOAT_EQ(400.0f, transformed2.y()); + + WebCore::TransformationMatrix matrix; + + auto transformed3 = test.matrixTransform(matrix); + + EXPECT_FLOAT_EQ(100.0f, transformed3.x()); + EXPECT_FLOAT_EQ(200.0f, transformed3.y()); + + auto transformed4 = test.matrixTransform(affine2.toTransformationMatrix()); + + EXPECT_FLOAT_EQ(200.0f, transformed4.x()); + EXPECT_FLOAT_EQ(400.0f, transformed4.y()); +} + +TEST(FloatPoint, Math) +{ + WebCore::FloatPoint a(100.0f, 200.0f); + WebCore::FloatPoint b(100.0f, 200.0f); + + a += b; + + EXPECT_FLOAT_EQ(200.0f, a.x()); + EXPECT_FLOAT_EQ(400.0f, a.y()); + + WebCore::FloatSize c(50.0f, 50.0f); + + a += c; + + EXPECT_FLOAT_EQ(250.0f, a.x()); + EXPECT_FLOAT_EQ(450.0f, a.y()); + + WebCore::FloatSize d(10.0f, 200.0f); + + a -= d; + + EXPECT_FLOAT_EQ(240.0f, a.x()); + EXPECT_FLOAT_EQ(250.0f, a.y()); + + WebCore::FloatSize e(100.0f, 200.0f); + + auto f = b + e; + + EXPECT_FLOAT_EQ(200.0f, f.x()); + EXPECT_FLOAT_EQ(400.0f, f.y()); + + WebCore::FloatPoint g(10.0f, 20.0f); + + auto h = b + g; + + EXPECT_FLOAT_EQ(110.0f, h.x()); + EXPECT_FLOAT_EQ(220.0f, h.y()); + + WebCore::FloatSize i = b - g; + + EXPECT_FLOAT_EQ(90.0f, i.width()); + EXPECT_FLOAT_EQ(180.0f, i.height()); + + WebCore::FloatPoint j = b - e; + + EXPECT_FLOAT_EQ(0.0f, j.x()); + EXPECT_FLOAT_EQ(0.0f, j.y()); + + WebCore::FloatPoint negated = -b; + + EXPECT_FLOAT_EQ(-100.0f, negated.x()); + EXPECT_FLOAT_EQ(-200.0f, negated.y()); + + float value = b * g; + + EXPECT_FLOAT_EQ(5000.0f, value); +} + +TEST(FloatPoint, Equality) +{ + WebCore::FloatPoint a(100.0f, 200.0f); + WebCore::FloatPoint b(100.0f, 200.0f); + WebCore::FloatPoint c(10.0f, 20.0f); + + ASSERT_TRUE(a == b); + ASSERT_FALSE(a == c); + ASSERT_FALSE(a != b); + ASSERT_TRUE(a != c); + + ASSERT_TRUE(WebCore::areEssentiallyEqual(a, b)); + ASSERT_FALSE(WebCore::areEssentiallyEqual(a, c)); +} + +TEST(FloatPoint, Floors) +{ + WebCore::FloatPoint a(100.6f, 199.9f); + + WebCore::IntSize flooredSize = WebCore::flooredIntSize(a); + EXPECT_FLOAT_EQ(100, flooredSize.width()); + EXPECT_FLOAT_EQ(199, flooredSize.height()); + + WebCore::IntPoint flooredPoint = WebCore::flooredIntPoint(a); + EXPECT_FLOAT_EQ(100, flooredPoint.x()); + EXPECT_FLOAT_EQ(199, flooredPoint.y()); + + WebCore::FloatPoint flooredPoint1x = WebCore::floorPointToDevicePixels(a, 1.0); + EXPECT_FLOAT_EQ(100.0f, flooredPoint1x.x()); + EXPECT_FLOAT_EQ(199.0f, flooredPoint1x.y()); + + WebCore::FloatPoint flooredPoint2x = WebCore::floorPointToDevicePixels(a, 2.0); + EXPECT_FLOAT_EQ(100.5f, flooredPoint2x.x()); + EXPECT_FLOAT_EQ(199.5f, flooredPoint2x.y()); +} + +TEST(FloatPoint, Rounding) +{ + WebCore::FloatPoint a(100.4f, 199.9f); + + WebCore::IntPoint roundedPoint = WebCore::roundedIntPoint(a); + EXPECT_FLOAT_EQ(100, roundedPoint.x()); + EXPECT_FLOAT_EQ(200, roundedPoint.y()); +} + +TEST(FloatPoint, Ceiling) +{ + WebCore::FloatPoint a(100.4f, 199.9f); + + WebCore::IntPoint ceilingPoint = WebCore::ceiledIntPoint(a); + EXPECT_FLOAT_EQ(101, ceilingPoint.x()); + EXPECT_FLOAT_EQ(200, ceilingPoint.y()); + + WebCore::FloatPoint ceilingPoint1x = WebCore::ceilPointToDevicePixels(a, 1.0); + EXPECT_FLOAT_EQ(101.0f, ceilingPoint1x.x()); + EXPECT_FLOAT_EQ(200.0f, ceilingPoint1x.y()); + + WebCore::FloatPoint ceilingPoint2x = WebCore::ceilPointToDevicePixels(a, 2.0); + EXPECT_FLOAT_EQ(100.5f, ceilingPoint2x.x()); + EXPECT_FLOAT_EQ(200.0f, ceilingPoint2x.y()); +} + +TEST(FloatPoint, Casting) +{ + WebCore::FloatPoint a(100.4f, 199.9f); + + WebCore::FloatSize floatSize = WebCore::toFloatSize(a); + EXPECT_FLOAT_EQ(100.4f, floatSize.width()); + EXPECT_FLOAT_EQ(199.9f, floatSize.height()); + + WebCore::FloatSize b(99.6f, 299.1f); + + WebCore::FloatPoint floatPoint = WebCore::toFloatPoint(b); + EXPECT_FLOAT_EQ(99.6f, floatPoint.x()); + EXPECT_FLOAT_EQ(299.1f, floatPoint.y()); + +#if USE(CG) + CGPoint cgPoint = a; + + EXPECT_FLOAT_EQ(100.4f, cgPoint.x); + EXPECT_FLOAT_EQ(199.9f, cgPoint.y); + + CGPoint cgPoint2 = CGPointMake(-22.3f, 14.2f); + + WebCore::FloatPoint testCG(cgPoint2); + + EXPECT_FLOAT_EQ(-22.3f, testCG.x()); + EXPECT_FLOAT_EQ(14.2f, testCG.y()); +#endif + +#if PLATFORM(WIN) + D2D_POINT_2F d2dPoint = a; + + EXPECT_FLOAT_EQ(100.4f, d2dPoint.x); + EXPECT_FLOAT_EQ(199.9f, d2dPoint.y); + + D2D_POINT_2F d2dPoint2 = D2D1::Point2F(-22.3f, 14.2f); + + WebCore::FloatPoint testD2D(d2dPoint2); + + EXPECT_FLOAT_EQ(-22.3f, testD2D.x()); + EXPECT_FLOAT_EQ(14.2f, testD2D.y()); +#endif +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/FloatRect.cpp b/Tools/TestWebKitAPI/Tests/WebCore/FloatRect.cpp new file mode 100644 index 000000000..b7e100e06 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/FloatRect.cpp @@ -0,0 +1,783 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include + +#if USE(CG) +#include +#endif + +#if PLATFORM(WIN) +#include +#endif + +namespace TestWebKitAPI { + +static void testGetAndSet(WebCore::FloatRect rect) +{ + rect.setX(1.1f); + EXPECT_FLOAT_EQ(1.1f, rect.x()); + rect.setY(2.2f); + EXPECT_FLOAT_EQ(2.2f, rect.y()); + rect.setWidth(203.2f); + EXPECT_FLOAT_EQ(203.2f, rect.width()); + rect.setHeight(72.9f); + EXPECT_FLOAT_EQ(72.9f, rect.height()); +} + +static void testEmptyRect(const WebCore::FloatRect& rect) +{ + EXPECT_FLOAT_EQ(0, rect.x()); + EXPECT_FLOAT_EQ(0, rect.y()); + EXPECT_FLOAT_EQ(0, rect.width()); + EXPECT_FLOAT_EQ(0, rect.height()); + EXPECT_FLOAT_EQ(0, rect.maxX()); + EXPECT_FLOAT_EQ(0, rect.maxY()); + EXPECT_TRUE(rect.isEmpty()); + EXPECT_TRUE(rect.isZero()); + EXPECT_FALSE(rect.isInfinite()); +} + +TEST(FloatRect, DefaultConstruction) +{ + WebCore::FloatRect test; + + testEmptyRect(test); + + testGetAndSet(test); + + auto location = test.location(); + EXPECT_FLOAT_EQ(0, location.x()); + EXPECT_FLOAT_EQ(0, location.y()); + + auto size = test.size(); + EXPECT_FLOAT_EQ(0, size.width()); + EXPECT_FLOAT_EQ(0, size.height()); +} + +TEST(FloatRect, ValueConstruction) +{ + WebCore::FloatRect test(10.0f, 20.0f, 100.0f, 50.0f); + + EXPECT_FLOAT_EQ(10.0f, test.x()); + EXPECT_FLOAT_EQ(20.0f, test.y()); + EXPECT_FLOAT_EQ(100.0f, test.width()); + EXPECT_FLOAT_EQ(50.0f, test.height()); + EXPECT_FLOAT_EQ(110.0f, test.maxX()); + EXPECT_FLOAT_EQ(70.0f, test.maxY()); + EXPECT_FALSE(test.isEmpty()); + EXPECT_FALSE(test.isZero()); + EXPECT_FALSE(test.isInfinite()); + + auto location = test.location(); + EXPECT_FLOAT_EQ(10.0f, location.x()); + EXPECT_FLOAT_EQ(20.0f, location.y()); + + auto size = test.size(); + EXPECT_FLOAT_EQ(100.0f, size.width()); + EXPECT_FLOAT_EQ(50.0f, size.height()); +} + +TEST(FloatRect, PointSizeConstruction) +{ + WebCore::FloatPoint location(20.0f, 30.0f); + WebCore::FloatSize size(100.0f, 50.0f); + + WebCore::FloatRect test(location, size); + + EXPECT_FLOAT_EQ(20.0f, test.x()); + EXPECT_FLOAT_EQ(30.0f, test.y()); + EXPECT_FLOAT_EQ(100.0f, test.width()); + EXPECT_FLOAT_EQ(50.0f, test.height()); + EXPECT_FLOAT_EQ(120.0f, test.maxX()); + EXPECT_FLOAT_EQ(80.0f, test.maxY()); + EXPECT_FALSE(test.isEmpty()); + EXPECT_FALSE(test.isZero()); + EXPECT_FALSE(test.isInfinite()); + + auto location2 = test.location(); + EXPECT_FLOAT_EQ(20.0f, location2.x()); + EXPECT_FLOAT_EQ(30.0f, location2.y()); + + auto size2 = test.size(); + EXPECT_FLOAT_EQ(100.0f, size2.width()); + EXPECT_FLOAT_EQ(50.0f, size2.height()); +} + +TEST(FloatRect, TwoPointConstruction) +{ + WebCore::FloatPoint point1(20.0f, 30.0f); + WebCore::FloatPoint point2(150.0f, 250.0f); + + WebCore::FloatRect test(point1, point2); + + EXPECT_FLOAT_EQ(20.0f, test.x()); + EXPECT_FLOAT_EQ(30.0f, test.y()); + EXPECT_FLOAT_EQ(130.0f, test.width()); + EXPECT_FLOAT_EQ(220.0f, test.height()); + EXPECT_FLOAT_EQ(150.0f, test.maxX()); + EXPECT_FLOAT_EQ(250.0f, test.maxY()); + EXPECT_FALSE(test.isEmpty()); + EXPECT_FALSE(test.isZero()); + EXPECT_FALSE(test.isInfinite()); + + auto location = test.location(); + EXPECT_FLOAT_EQ(20.0f, location.x()); + EXPECT_FLOAT_EQ(30.0f, location.y()); + + auto size = test.size(); + EXPECT_FLOAT_EQ(130.0f, size.width()); + EXPECT_FLOAT_EQ(220.0f, size.height()); +} + +TEST(FloatRect, IntRectConstruction) +{ + WebCore::IntRect rect(20, 30, 150, 300); + + WebCore::FloatRect test(rect); + + EXPECT_FLOAT_EQ(20.0f, test.x()); + EXPECT_FLOAT_EQ(30.0f, test.y()); + EXPECT_FLOAT_EQ(150.0f, test.width()); + EXPECT_FLOAT_EQ(300.0f, test.height()); + EXPECT_FLOAT_EQ(170.0f, test.maxX()); + EXPECT_FLOAT_EQ(330.0f, test.maxY()); + EXPECT_FALSE(test.isEmpty()); + EXPECT_FALSE(test.isZero()); + EXPECT_FALSE(test.isInfinite()); + + auto location = test.location(); + EXPECT_FLOAT_EQ(20.0f, location.x()); + EXPECT_FLOAT_EQ(30.0f, location.y()); + + auto size = test.size(); + EXPECT_FLOAT_EQ(150.0f, size.width()); + EXPECT_FLOAT_EQ(300.0f, size.height()); +} + +TEST(FloatRect, SetLocationAndSize) +{ + WebCore::FloatRect rect; + + testEmptyRect(rect); + + WebCore::FloatPoint location(10.0f, 20.0f); + + rect.setLocation(location); + + EXPECT_FLOAT_EQ(10.0f, rect.x()); + EXPECT_FLOAT_EQ(20.0f, rect.y()); + EXPECT_FLOAT_EQ(0.0f, rect.width()); + EXPECT_FLOAT_EQ(0.0f, rect.height()); + EXPECT_FLOAT_EQ(10.0f, rect.maxX()); + EXPECT_FLOAT_EQ(20.0f, rect.maxY()); + EXPECT_TRUE(rect.isEmpty()); + EXPECT_TRUE(rect.isZero()); + EXPECT_FALSE(rect.isInfinite()); + + WebCore::FloatSize size(100.0f, 200.0f); + + rect.setSize(size); + + EXPECT_FLOAT_EQ(10.0f, rect.x()); + EXPECT_FLOAT_EQ(20.0f, rect.y()); + EXPECT_FLOAT_EQ(100.0f, rect.width()); + EXPECT_FLOAT_EQ(200.0f, rect.height()); + EXPECT_FLOAT_EQ(110.0f, rect.maxX()); + EXPECT_FLOAT_EQ(220.0f, rect.maxY()); + EXPECT_FALSE(rect.isEmpty()); + EXPECT_FALSE(rect.isZero()); + EXPECT_FALSE(rect.isInfinite()); +} + +TEST(FloatRect, Center) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + auto center = rect.center(); + + EXPECT_FLOAT_EQ(70.0f, center.x()); + EXPECT_FLOAT_EQ(130.0f, center.y()); +} + +TEST(FloatRect, Move) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + WebCore::FloatSize delta(10.0f, 20.0f); + + rect.move(delta); + + EXPECT_FLOAT_EQ(30.0f, rect.x()); + EXPECT_FLOAT_EQ(50.0f, rect.y()); + + WebCore::FloatPoint deltaPoint(-20.0f, -40.0f); + + rect.moveBy(deltaPoint); + + EXPECT_FLOAT_EQ(10.0f, rect.x()); + EXPECT_FLOAT_EQ(10.0f, rect.y()); + + rect.move(-10.0f, 22.0f); + + EXPECT_FLOAT_EQ(0.0f, rect.x()); + EXPECT_FLOAT_EQ(32.0f, rect.y()); +} + +TEST(FloatRect, Expand) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + WebCore::FloatSize size(100.0f, 100.0f); + + rect.expand(size); + + EXPECT_FLOAT_EQ(200.0f, rect.width()); + EXPECT_FLOAT_EQ(300.0f, rect.height()); + + rect.expand(55.0f, 22.0f); + + EXPECT_FLOAT_EQ(255.0f, rect.width()); + EXPECT_FLOAT_EQ(322.0f, rect.height()); + + WebCore::FloatSize size2(-10.0f, -20.0f); + + rect.expand(size2); + + EXPECT_FLOAT_EQ(245.0f, rect.width()); + EXPECT_FLOAT_EQ(302.0f, rect.height()); + + rect.expand(-5.0f, -2.0f); + + EXPECT_FLOAT_EQ(240.0f, rect.width()); + EXPECT_FLOAT_EQ(300.0f, rect.height()); + + EXPECT_FALSE(rect.isInfinite()); +} + +TEST(FloatRect, Contract) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + WebCore::FloatSize size(50.0f, 100.0f); + + rect.contract(size); + + EXPECT_FLOAT_EQ(50.0f, rect.width()); + EXPECT_FLOAT_EQ(100.0f, rect.height()); + + rect.contract(25.0f, 22.0f); + + EXPECT_FLOAT_EQ(25.0f, rect.width()); + EXPECT_FLOAT_EQ(78.0f, rect.height()); + + WebCore::FloatSize size2(-10.0f, -20.0f); + + rect.contract(size2); + + EXPECT_FLOAT_EQ(35.0f, rect.width()); + EXPECT_FLOAT_EQ(98.0f, rect.height()); + + rect.contract(-5.0f, -2.0f); + + EXPECT_FLOAT_EQ(40.0f, rect.width()); + EXPECT_FLOAT_EQ(100.0f, rect.height()); + + EXPECT_FALSE(rect.isInfinite()); +} + +TEST(FloatRect, ShiftXEdge) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + rect.shiftXEdgeTo(77.0f); + + EXPECT_FLOAT_EQ(77.0f, rect.x()); + EXPECT_FLOAT_EQ(120.0f, rect.maxX()); + EXPECT_FLOAT_EQ(30.0f, rect.y()); + EXPECT_FLOAT_EQ(230.0f, rect.maxY()); + EXPECT_FLOAT_EQ(43.0f, rect.width()); + EXPECT_FLOAT_EQ(200.0f, rect.height()); + + rect.shiftMaxXEdgeTo(200.0f); + + EXPECT_FLOAT_EQ(77.0f, rect.x()); + EXPECT_FLOAT_EQ(200.0f, rect.maxX()); + EXPECT_FLOAT_EQ(30.0f, rect.y()); + EXPECT_FLOAT_EQ(230.0f, rect.maxY()); + EXPECT_FLOAT_EQ(123.0f, rect.width()); + EXPECT_FLOAT_EQ(200.0f, rect.height()); +} + +TEST(FloatRect, ShiftYEdge) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + rect.shiftYEdgeTo(59.0f); + + EXPECT_FLOAT_EQ(20.0f, rect.x()); + EXPECT_FLOAT_EQ(120.0f, rect.maxX()); + EXPECT_FLOAT_EQ(59.0f, rect.y()); + EXPECT_FLOAT_EQ(230.0f, rect.maxY()); + EXPECT_FLOAT_EQ(100.0f, rect.width()); + EXPECT_FLOAT_EQ(171.0f, rect.height()); + + rect.shiftMaxYEdgeTo(270.0f); + + EXPECT_FLOAT_EQ(20.0f, rect.x()); + EXPECT_FLOAT_EQ(120.0f, rect.maxX()); + EXPECT_FLOAT_EQ(59.0f, rect.y()); + EXPECT_FLOAT_EQ(270.0f, rect.maxY()); + EXPECT_FLOAT_EQ(100.0f, rect.width()); + EXPECT_FLOAT_EQ(211.0f, rect.height()); +} + +TEST(FloatRect, Inflate) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + rect.inflateX(5.0f); + + EXPECT_FLOAT_EQ(15.0f, rect.x()); + EXPECT_FLOAT_EQ(125.0f, rect.maxX()); + + rect.inflateY(4.0f); + + EXPECT_FLOAT_EQ(26.0f, rect.y()); + EXPECT_FLOAT_EQ(234.0f, rect.maxY()); + + rect.inflate(10.0f); + + EXPECT_FLOAT_EQ(5.0f, rect.x()); + EXPECT_FLOAT_EQ(135.0f, rect.maxX()); + EXPECT_FLOAT_EQ(16.0f, rect.y()); + EXPECT_FLOAT_EQ(244.0f, rect.maxY()); + + EXPECT_FALSE(rect.isInfinite()); +} + +TEST(FloatRect, Corners) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + WebCore::FloatPoint topLeft = rect.minXMinYCorner(); + EXPECT_FLOAT_EQ(20.0f, topLeft.x()); + EXPECT_FLOAT_EQ(30.0f, topLeft.y()); + + WebCore::FloatPoint topRight = rect.maxXMinYCorner(); + EXPECT_FLOAT_EQ(120.0f, topRight.x()); + EXPECT_FLOAT_EQ(30.0f, topRight.y()); + + WebCore::FloatPoint bottomLeft = rect.minXMaxYCorner(); + EXPECT_FLOAT_EQ(20.0f, bottomLeft.x()); + EXPECT_FLOAT_EQ(230.0f, bottomLeft.y()); + + WebCore::FloatPoint bottomRight = rect.maxXMaxYCorner(); + EXPECT_FLOAT_EQ(120.0f, bottomRight.x()); + EXPECT_FLOAT_EQ(230.0f, bottomRight.y()); +} + +TEST(FloatRect, Contains) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + WebCore::FloatRect contained(30.0f, 40.0f, 50.0f, 100.0f); + + ASSERT_TRUE(rect.contains(contained)); + + WebCore::FloatRect outside(120.0f, 230.0f, 50.0f, 100.0f); + + ASSERT_FALSE(rect.contains(outside)); + + WebCore::FloatRect intersects(10.0f, 20.0f, 90.0f, 180.0f); + + ASSERT_FALSE(rect.contains(intersects)); + + WebCore::FloatPoint pointInside(60.0f, 70.0f); + + ASSERT_TRUE(rect.contains(pointInside)); + ASSERT_TRUE(rect.contains(pointInside, WebCore::FloatRect::InsideButNotOnStroke)); + + WebCore::FloatPoint pointOutside(160.0f, 270.0f); + + ASSERT_FALSE(rect.contains(pointOutside)); + ASSERT_FALSE(rect.contains(pointOutside, WebCore::FloatRect::InsideButNotOnStroke)); + + WebCore::FloatPoint pointOnLine(20.0f, 30.0f); + + ASSERT_TRUE(rect.contains(pointOnLine)); + ASSERT_FALSE(rect.contains(pointOutside, WebCore::FloatRect::InsideButNotOnStroke)); + + ASSERT_TRUE(rect.contains(60.0f, 70.0f)); + ASSERT_FALSE(rect.contains(160.0f, 270.0f)); +} + +TEST(FloatRect, Intersects) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + WebCore::FloatRect contained(30.0f, 40.0f, 50.0f, 100.0f); + + ASSERT_TRUE(rect.intersects(contained)); + + WebCore::FloatRect outside(120.0f, 230.0f, 50.0f, 100.0f); + + ASSERT_FALSE(rect.intersects(outside)); + + WebCore::FloatRect intersects(10.0f, 20.0f, 90.0f, 180.0f); + + ASSERT_TRUE(rect.intersects(intersects)); +} + +static void testIntersectResult(const WebCore::FloatRect& rect) +{ + EXPECT_FLOAT_EQ(70.0f, rect.x()); + EXPECT_FLOAT_EQ(120.0f, rect.maxX()); + EXPECT_FLOAT_EQ(80.0f, rect.y()); + EXPECT_FLOAT_EQ(230.0f, rect.maxY()); +} + +TEST(FloatRect, Intersect) +{ + WebCore::FloatRect rectA(20.0f, 30.0f, 100.0f, 200.0f); + WebCore::FloatRect rectB(70.0f, 80.0f, 100.0f, 200.0f); + + rectA.intersect(rectB); + + testIntersectResult(rectA); + + WebCore::FloatRect rectC(20.0f, 30.0f, 100.0f, 200.0f); + + auto intersected = WebCore::intersection(rectC, rectB); + + testIntersectResult(intersected); +} + +static void testUnitedRects(const WebCore::FloatRect& united) +{ + EXPECT_FLOAT_EQ(20.0f, united.x()); + EXPECT_FLOAT_EQ(170.0f, united.maxX()); + EXPECT_FLOAT_EQ(30.0f, united.y()); + EXPECT_FLOAT_EQ(280.0f, united.maxY()); +} + +TEST(FloatRect, Unite) +{ + WebCore::FloatRect rectA(20.0f, 30.0f, 100.0f, 200.0f); + WebCore::FloatRect rectB(70.0f, 80.0f, 100.0f, 200.0f); + + rectA.unite(rectB); + + testUnitedRects(rectA); + + WebCore::FloatRect rectC(20.0f, 30.0f, 100.0f, 200.0f); + + auto united = WebCore::unionRect(rectC, rectB); + + testUnitedRects(united); +} + +TEST(FloatRect, Extend) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + WebCore::FloatPoint point(170.0f, 280.0f); + + rect.extend(point); + + EXPECT_FLOAT_EQ(20.0f, rect.x()); + EXPECT_FLOAT_EQ(170.0f, rect.maxX()); + EXPECT_FLOAT_EQ(30.0f, rect.y()); + EXPECT_FLOAT_EQ(280.0f, rect.maxY()); +} + +TEST(FloatRect, Overlaps) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + ASSERT_FALSE(rect.overlapsXRange(0.0f, 10.0f)); + ASSERT_TRUE(rect.overlapsXRange(10.0f, 30.0f)); + ASSERT_TRUE(rect.overlapsXRange(40.0f, 70.0f)); + ASSERT_TRUE(rect.overlapsXRange(110.0f, 130.0f)); + ASSERT_FALSE(rect.overlapsXRange(140.0f, 600.0f)); + + ASSERT_FALSE(rect.overlapsYRange(0.0f, 10.0f)); + ASSERT_TRUE(rect.overlapsYRange(10.0f, 40.0f)); + ASSERT_TRUE(rect.overlapsYRange(40.0f, 70.0f)); + ASSERT_TRUE(rect.overlapsYRange(220.0f, 250.0f)); + ASSERT_FALSE(rect.overlapsYRange(250.0f, 600.0f)); +} + +TEST(FloatRect, Scale) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + rect.scale(2.0f); + + EXPECT_FLOAT_EQ(40.0f, rect.x()); + EXPECT_FLOAT_EQ(240.0f, rect.maxX()); + EXPECT_FLOAT_EQ(60.0f, rect.y()); + EXPECT_FLOAT_EQ(460.0f, rect.maxY()); + + rect.scale(0.5f, 2.0f); + + EXPECT_FLOAT_EQ(20.0f, rect.x()); + EXPECT_FLOAT_EQ(120.0f, rect.maxX()); + EXPECT_FLOAT_EQ(120.0f, rect.y()); + EXPECT_FLOAT_EQ(920.0f, rect.maxY()); + + rect.scale(1.0f, 0.25f); + + EXPECT_FLOAT_EQ(20.0f, rect.x()); + EXPECT_FLOAT_EQ(120.0f, rect.maxX()); + EXPECT_FLOAT_EQ(30.0f, rect.y()); + EXPECT_FLOAT_EQ(230.0f, rect.maxY()); +} + +TEST(FloatRect, Transpose) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 100.0f, 200.0f); + + auto transposed = rect.transposedRect(); + + EXPECT_FLOAT_EQ(30.0f, transposed.x()); + EXPECT_FLOAT_EQ(230.0f, transposed.maxX()); + EXPECT_FLOAT_EQ(20.0f, transposed.y()); + EXPECT_FLOAT_EQ(120.0f, transposed.maxY()); +} + +TEST(FloatRect, FitToPoints) +{ + WebCore::FloatRect rect(10.0f, 20.0f, 30.0f, 40.0f); + + WebCore::FloatPoint p0(20.0f, 30.0f); + WebCore::FloatPoint p1(70.0f, 130.0f); + WebCore::FloatPoint p2(50.0f, 20.0f); + WebCore::FloatPoint p3(90.0f, 190.0f); + + rect.fitToPoints(p0, p1); + + EXPECT_FLOAT_EQ(20.0f, rect.x()); + EXPECT_FLOAT_EQ(70.0f, rect.maxX()); + EXPECT_FLOAT_EQ(30.0f, rect.y()); + EXPECT_FLOAT_EQ(130.0f, rect.maxY()); + + rect.fitToPoints(p0, p1, p2); + + EXPECT_FLOAT_EQ(20.0f, rect.x()); + EXPECT_FLOAT_EQ(70.0f, rect.maxX()); + EXPECT_FLOAT_EQ(20.0f, rect.y()); + EXPECT_FLOAT_EQ(130.0f, rect.maxY()); + + rect.fitToPoints(p0, p1, p2, p3); + + EXPECT_FLOAT_EQ(20.0f, rect.x()); + EXPECT_FLOAT_EQ(90.0f, rect.maxX()); + EXPECT_FLOAT_EQ(20.0f, rect.y()); + EXPECT_FLOAT_EQ(190.0f, rect.maxY()); +} + +static void checkCastRect(const WebCore::FloatRect& rect) +{ + EXPECT_FLOAT_EQ(10.0f, rect.x()); + EXPECT_FLOAT_EQ(40.0f, rect.maxX()); + EXPECT_FLOAT_EQ(20.0f, rect.y()); + EXPECT_FLOAT_EQ(60.0f, rect.maxY()); + EXPECT_FLOAT_EQ(30.0f, rect.width()); + EXPECT_FLOAT_EQ(40.0f, rect.height()); +} + +TEST(FloatRect, Casting) +{ + WebCore::FloatRect rect(10.0f, 20.0f, 30.0f, 40.0f); + +#if USE(CG) + CGRect cgRect = rect; + + EXPECT_FLOAT_EQ(10.0f, cgRect.origin.x); + EXPECT_FLOAT_EQ(20.0f, cgRect.origin.y); + EXPECT_FLOAT_EQ(30.0f, cgRect.size.width); + EXPECT_FLOAT_EQ(40.0f, cgRect.size.height); + + WebCore::FloatRect rectFromCGRect(cgRect); + + checkCastRect(rectFromCGRect); +#endif + +#if PLATFORM(WIN) + D2D1_RECT_F d2dRect = rect; + + EXPECT_FLOAT_EQ(10.0f, d2dRect.left); + EXPECT_FLOAT_EQ(20.0f, d2dRect.top); + EXPECT_FLOAT_EQ(40.0f, d2dRect.right); + EXPECT_FLOAT_EQ(60.0f, d2dRect.bottom); + + WebCore::FloatRect rectFromD2DRect(d2dRect); + + checkCastRect(rectFromD2DRect); +#endif +} + +static void checkAdditionResult1(const WebCore::FloatRect& rect) +{ + EXPECT_FLOAT_EQ(10.0f, rect.x()); + EXPECT_FLOAT_EQ(130.0f, rect.maxX()); + EXPECT_FLOAT_EQ(20.0f, rect.y()); + EXPECT_FLOAT_EQ(220.0f, rect.maxY()); + EXPECT_FLOAT_EQ(120.0f, rect.width()); + EXPECT_FLOAT_EQ(200.0f, rect.height()); +} + +static void checkAdditionResult2(const WebCore::FloatRect& rect) +{ + EXPECT_FLOAT_EQ(10.0f, rect.x()); + EXPECT_FLOAT_EQ(250.0f, rect.maxX()); + EXPECT_FLOAT_EQ(20.0f, rect.y()); + EXPECT_FLOAT_EQ(240.0f, rect.maxY()); + EXPECT_FLOAT_EQ(240.0f, rect.width()); + EXPECT_FLOAT_EQ(220.0f, rect.height()); +} + +TEST(FloatRect, Addition) +{ + WebCore::FloatRect rect(10.0f, 20.0f, 100.0f, 100.0f); + WebCore::FloatRect rightSide(0.0f, 0.0f, 20.0f, 100.0f); + WebCore::FloatRect bottom(0.0f, 0.0f, 120.0f, 20.0f); + + auto combined = rect + rightSide; + + checkAdditionResult1(combined); + + auto combined2 = combined + bottom; + + checkAdditionResult2(combined2); + + rect += rightSide; + rect += bottom; + + checkAdditionResult2(rect); +} + +TEST(FloatRect, Equality) +{ + WebCore::FloatRect rect(10.0f, 20.0f, 100.0f, 100.0f); + WebCore::FloatRect rect2(10.0f, 20.0f, 100.0f, 100.0f); + WebCore::FloatRect rightSide(110.0f, 20.0f, 20.0f, 100.0f); + + ASSERT_TRUE(rect == rect2); + ASSERT_FALSE(rect != rect2); + ASSERT_TRUE(rect != rightSide); + ASSERT_FALSE(rect == rightSide); +} + +TEST(FloatRect, InfiniteRect) +{ + WebCore::FloatRect infinite = WebCore::FloatRect::infiniteRect(); + + EXPECT_FLOAT_EQ(-std::numeric_limits::max() / 2, infinite.x()); + EXPECT_FLOAT_EQ(-std::numeric_limits::max() / 2, infinite.y()); + EXPECT_FLOAT_EQ(std::numeric_limits::max(), infinite.width()); + EXPECT_FLOAT_EQ(std::numeric_limits::max(), infinite.height()); + + // FIXME: We have an unusual representation for our infinite rect. + // WebCore::Float::infiniteRect is (negative infinity)/2 for the upper left corner, + // while CoreGraphics and D2D use (negative infinity). + +#if USE(CG) + CGRect cgInfiniteRect = CGRectInfinite; + +#if PLATFORM(WIN) + EXPECT_FLOAT_EQ(-std::numeric_limits::max() / 2, cgInfiniteRect.origin.x); + EXPECT_FLOAT_EQ(-std::numeric_limits::max() / 2, cgInfiniteRect.origin.y); + EXPECT_FLOAT_EQ(std::numeric_limits::max(), cgInfiniteRect.size.width); + EXPECT_FLOAT_EQ(std::numeric_limits::max(), cgInfiniteRect.size.height); +#else + EXPECT_FLOAT_EQ(-std::numeric_limits::max(), cgInfiniteRect.origin.x); + EXPECT_FLOAT_EQ(-std::numeric_limits::max(), cgInfiniteRect.origin.y); + EXPECT_FLOAT_EQ(std::numeric_limits::max(), cgInfiniteRect.origin.x + cgInfiniteRect.size.width); + EXPECT_FLOAT_EQ(std::numeric_limits::max(), cgInfiniteRect.origin.y + cgInfiniteRect.size.height); +#endif + // ASSERT_TRUE(infinite == cgInfiniteRect); +#endif + +#if PLATFORM(WIN) + D2D1_RECT_F d2dInfiniteRect = D2D1::InfiniteRect(); + + EXPECT_FLOAT_EQ(-std::numeric_limits::max(), d2dInfiniteRect.left); + EXPECT_FLOAT_EQ(-std::numeric_limits::max(), d2dInfiniteRect.top); + EXPECT_FLOAT_EQ(std::numeric_limits::max(), d2dInfiniteRect.right); + EXPECT_FLOAT_EQ(std::numeric_limits::max(), d2dInfiniteRect.bottom); + // ASSERT_TRUE(infinite == d2dInfiniteRect); +#endif +} + +TEST(FloatRect, EnclosingAndRounding) +{ + WebCore::FloatRect rect(10.0f, 20.0f, 1024.3f, 768.3f); + + auto enclosed = WebCore::encloseRectToDevicePixels(rect, 1.0f); + + EXPECT_FLOAT_EQ(10.0f, enclosed.x()); + EXPECT_FLOAT_EQ(20.0f, enclosed.y()); + EXPECT_FLOAT_EQ(1035.0f, enclosed.maxX()); + EXPECT_FLOAT_EQ(789.0f, enclosed.maxY()); + + auto enclosed2 = WebCore::encloseRectToDevicePixels(rect, 2.0f); + + EXPECT_FLOAT_EQ(10.0f, enclosed2.x()); + EXPECT_FLOAT_EQ(20.0f, enclosed2.y()); + EXPECT_FLOAT_EQ(1034.5f, enclosed2.maxX()); + EXPECT_FLOAT_EQ(788.5f, enclosed2.maxY()); +} + +TEST(FloatRect, EnclosingIntRect) +{ + WebCore::FloatRect rect(10.0f, 20.0f, 1024.3f, 768.6f); + + auto enclosed = WebCore::enclosingIntRect(rect); + + EXPECT_FLOAT_EQ(10, enclosed.x()); + EXPECT_FLOAT_EQ(20, enclosed.y()); + EXPECT_FLOAT_EQ(1035, enclosed.maxX()); + EXPECT_FLOAT_EQ(789, enclosed.maxY()); +} + +TEST(FloatRect, RoundedIntRect) +{ + WebCore::FloatRect rect(10.0f, 20.0f, 1024.3f, 768.6f); + + auto enclosed = WebCore::roundedIntRect(rect); + + EXPECT_FLOAT_EQ(10, enclosed.x()); + EXPECT_FLOAT_EQ(20, enclosed.y()); + EXPECT_FLOAT_EQ(1034, enclosed.maxX()); + EXPECT_FLOAT_EQ(789, enclosed.maxY()); +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/FloatSize.cpp b/Tools/TestWebKitAPI/Tests/WebCore/FloatSize.cpp new file mode 100644 index 000000000..756e76f1d --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/FloatSize.cpp @@ -0,0 +1,328 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include + +#if USE(CG) +#include +#endif + +#if PLATFORM(WIN) +#include +#endif + +namespace TestWebKitAPI { + +static void testGetAndSet(WebCore::FloatSize size) +{ + size.setWidth(101.1f); + EXPECT_FLOAT_EQ(101.1f, size.width()); + size.setHeight(218.2f); + EXPECT_FLOAT_EQ(218.2f, size.height()); +} + +TEST(FloatSize, DefaultConstruction) +{ + WebCore::FloatSize test; + + EXPECT_FLOAT_EQ(0, test.width()); + EXPECT_FLOAT_EQ(0, test.height()); + + ASSERT_TRUE(test.isEmpty()); + ASSERT_TRUE(test.isZero()); + + testGetAndSet(test); +} + +TEST(FloatSize, ValueConstruction) +{ + WebCore::FloatSize test(1024.0f, 768.0f); + + EXPECT_FLOAT_EQ(1024.0f, test.width()); + EXPECT_FLOAT_EQ(768.0f, test.height()); + + ASSERT_FALSE(test.isEmpty()); + ASSERT_FALSE(test.isZero()); + + static const float epsilon = 0.0001f; + EXPECT_NEAR(1.3333f, test.aspectRatio(), epsilon); + + testGetAndSet(test); +} + +TEST(FloatSize, IntSizeConstruction) +{ + WebCore::IntSize size(1024, 768); + WebCore::FloatSize test(size); + + EXPECT_FLOAT_EQ(1024.0f, test.width()); + EXPECT_FLOAT_EQ(768.0f, test.height()); + + ASSERT_FALSE(test.isEmpty()); + ASSERT_FALSE(test.isZero()); + + static const double epsilon = 0.0001; + EXPECT_NEAR(1.3333f, test.aspectRatio(), epsilon); + + testGetAndSet(test); +} + +TEST(FloatSize, Scale) +{ + WebCore::FloatSize test(1024.0f, 768.0f); + + test.scale(2.0f); + + EXPECT_FLOAT_EQ(2048.0f, test.width()); + EXPECT_FLOAT_EQ(1536.0f, test.height()); + + test.scale(0.5f); + + EXPECT_FLOAT_EQ(1024.0f, test.width()); + EXPECT_FLOAT_EQ(768.0f, test.height()); + + test.scale(2.0f, 0.5f); + + EXPECT_FLOAT_EQ(2048.0f, test.width()); + EXPECT_FLOAT_EQ(384.0f, test.height()); +} + +TEST(FloatSize, Expand) +{ + WebCore::FloatSize test(1024.0f, 768.0f); + + EXPECT_FLOAT_EQ(1024.0f, test.width()); + EXPECT_FLOAT_EQ(768.0f, test.height()); + + test.expand(100.0f, 50.0f); + + EXPECT_FLOAT_EQ(1124.0f, test.width()); + EXPECT_FLOAT_EQ(818.0f, test.height()); + + WebCore::FloatSize other(2048.0f, 700.0f); + + auto expanded = test.expandedTo(other); + + EXPECT_FLOAT_EQ(2048.0f, expanded.width()); + EXPECT_FLOAT_EQ(818.0f, expanded.height()); +} + +TEST(FloatSize, Shrink) +{ + WebCore::FloatSize test(1024.0f, 768.0f); + WebCore::FloatSize other(1000.0f, 700.0f); + + auto shrunken = test.shrunkTo(other); + + EXPECT_FLOAT_EQ(1000.0f, shrunken.width()); + EXPECT_FLOAT_EQ(700.0f, shrunken.height()); + + WebCore::FloatSize other2(2000.0f, 700.0f); + + auto shrunken2 = test.shrunkTo(other2); + + EXPECT_FLOAT_EQ(1024.0f, shrunken2.width()); + EXPECT_FLOAT_EQ(700.0f, shrunken2.height()); +} + +TEST(FloatSize, DiagonalLengthAndArea) +{ + WebCore::FloatSize test(1024.0f, 768.0f); + + EXPECT_FLOAT_EQ(1280.0f, test.diagonalLength()); + EXPECT_FLOAT_EQ(1638400.0f, test.diagonalLengthSquared()); + EXPECT_FLOAT_EQ(786432.0f, test.area()); +} + +TEST(FloatSize, TransposedSize) +{ + WebCore::FloatSize test(1024.0f, 768.0f); + + auto transposedSize = test.transposedSize(); + + EXPECT_FLOAT_EQ(768.0f, transposedSize.width()); + EXPECT_FLOAT_EQ(1024.0f, transposedSize.height()); +} + +TEST(FloatSize, Casting) +{ + WebCore::FloatSize test(1024.0f, 768.0f); + +#if USE(CG) + CGSize cgSize = test; + + EXPECT_FLOAT_EQ(1024.0f, cgSize.width); + EXPECT_FLOAT_EQ(768.0f, cgSize.height); + + CGSize cgSize2 = CGSizeMake(-22.3f, 14.2f); + + WebCore::FloatSize testCG(cgSize2); + + EXPECT_FLOAT_EQ(-22.3f, testCG.width()); + EXPECT_FLOAT_EQ(14.2f, testCG.height()); +#endif + +#if PLATFORM(WIN) + D2D1_SIZE_F d2dSize = test; + + EXPECT_FLOAT_EQ(1024.0f, d2dSize.width); + EXPECT_FLOAT_EQ(768.0f, d2dSize.height); + + D2D1_SIZE_F d2dSize2 = D2D1::SizeF(-22.3f, 14.2f); + + WebCore::FloatSize testD2D(d2dSize2); + + EXPECT_FLOAT_EQ(-22.3f, testD2D.width()); + EXPECT_FLOAT_EQ(14.2f, testD2D.height()); +#endif +} + +TEST(FloatSize, AddSubtract) +{ + WebCore::FloatSize a(512.0f, 384.0f); + WebCore::FloatSize b(100.0f, 100.0f); + + WebCore::FloatSize c = a + b; + + EXPECT_FLOAT_EQ(612.0f, c.width()); + EXPECT_FLOAT_EQ(484.0f, c.height()); + + a += b; + + EXPECT_FLOAT_EQ(612.0f, a.width()); + EXPECT_FLOAT_EQ(484.0f, a.height()); + + WebCore::FloatSize a2(512.0f, 384.0f); + + WebCore::FloatSize d = a2 - b; + + EXPECT_FLOAT_EQ(412.0f, d.width()); + EXPECT_FLOAT_EQ(284.0f, d.height()); + + a2 -= b; + + EXPECT_FLOAT_EQ(412.0f, a2.width()); + EXPECT_FLOAT_EQ(284.0f, a2.height()); +} + +TEST(FloatSize, Negation) +{ + WebCore::FloatSize a(512.0f, 384.0f); + + WebCore::FloatSize negated = -a; + + EXPECT_FLOAT_EQ(-512.0f, negated.width()); + EXPECT_FLOAT_EQ(-384.0f, negated.height()); +} + +TEST(FloatSize, Multiply) +{ + WebCore::FloatSize a(512.0f, 384.0f); + + WebCore::FloatSize multiplied = a * 2.0f; + + EXPECT_FLOAT_EQ(1024.0f, multiplied.width()); + EXPECT_FLOAT_EQ(768.0f, multiplied.height()); + + WebCore::FloatSize multiplied2 = 3.0f * a; + + EXPECT_FLOAT_EQ(1536.0f, multiplied2.width()); + EXPECT_FLOAT_EQ(1152.0f, multiplied2.height()); + + WebCore::FloatSize b(1024.0f, 768.0f); + + WebCore::FloatSize multiplied3 = a * b; + + EXPECT_FLOAT_EQ(524288.0f, multiplied3.width()); + EXPECT_FLOAT_EQ(294912.0f, multiplied3.height()); +} + +TEST(FloatSize, Divide) +{ + WebCore::FloatSize a(1024.0f, 768.0f); + + WebCore::FloatSize divided = a / 2.0f; + + EXPECT_FLOAT_EQ(512.0f, divided.width()); + EXPECT_FLOAT_EQ(384.0f, divided.height()); + + WebCore::FloatSize b(512.0f, 256.0f); + + WebCore::FloatSize divided2 = 1024.0f / b; + + EXPECT_FLOAT_EQ(2.0f, divided2.width()); + EXPECT_FLOAT_EQ(4.0f, divided2.height()); +} + +TEST(FloatSize, Equality) +{ + WebCore::FloatSize a(1024.0f, 768.0f); + WebCore::FloatSize b(1024.0f, 768.0f); + WebCore::FloatSize c(768.0f, 534.0F); + + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + ASSERT_FALSE(a == c); + ASSERT_TRUE(a != c); + + ASSERT_TRUE(WebCore::areEssentiallyEqual(a, b)); + ASSERT_FALSE(WebCore::areEssentiallyEqual(a, c)); +} + +TEST(FloatSize, Floors) +{ + WebCore::FloatSize a(1024.4f, 768.8f); + + WebCore::IntSize floorSize = WebCore::flooredIntSize(a); + + EXPECT_EQ(1024, floorSize.width()); + EXPECT_EQ(768, floorSize.height()); + + WebCore::IntPoint floorPoint = WebCore::flooredIntPoint(a); + + EXPECT_EQ(1024, floorPoint.x()); + EXPECT_EQ(768, floorPoint.y()); +} + +TEST(FloatSize, Rounded) +{ + WebCore::FloatSize a(1024.4f, 768.8f); + + WebCore::IntSize roundedSize = WebCore::roundedIntSize(a); + + EXPECT_EQ(1024, roundedSize.width()); + EXPECT_EQ(769, roundedSize.height()); + + WebCore::IntSize expandedSize = WebCore::expandedIntSize(a); + + EXPECT_EQ(1025, expandedSize.width()); + EXPECT_EQ(769, expandedSize.height()); +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/GridPosition.cpp b/Tools/TestWebKitAPI/Tests/WebCore/GridPosition.cpp new file mode 100644 index 000000000..416ebd1f7 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/GridPosition.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 Igalia, S.L. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include + +namespace TestWebKitAPI { + +TEST(GridPositionTest, GridPositionLimits) +{ + + WebCore::GridPosition gridPosition; + + gridPosition.setExplicitPosition(999999, ""); + EXPECT_EQ(gridPosition.integerPosition(), 999999); + gridPosition.setExplicitPosition(1000000, ""); + EXPECT_EQ(gridPosition.integerPosition(), 1000000); + gridPosition.setExplicitPosition(1000001, ""); + EXPECT_EQ(gridPosition.integerPosition(), 1000000); + gridPosition.setExplicitPosition(INT_MAX, ""); + EXPECT_EQ(gridPosition.integerPosition(), 1000000); + gridPosition.setExplicitPosition(-999999, ""); + EXPECT_EQ(gridPosition.integerPosition(), -999999); + gridPosition.setExplicitPosition(-1000000, ""); + EXPECT_EQ(gridPosition.integerPosition(), -1000000); + gridPosition.setExplicitPosition(-1000001, ""); + EXPECT_EQ(gridPosition.integerPosition(), -1000000); + gridPosition.setExplicitPosition(INT_MIN, ""); + EXPECT_EQ(gridPosition.integerPosition(), -1000000); + + gridPosition.setSpanPosition(999999, ""); + EXPECT_EQ(gridPosition.spanPosition(), 999999); + gridPosition.setSpanPosition(1000000, ""); + EXPECT_EQ(gridPosition.spanPosition(), 1000000); + gridPosition.setSpanPosition(1000001, ""); + EXPECT_EQ(gridPosition.spanPosition(), 1000000); + gridPosition.setSpanPosition(INT_MAX, ""); + EXPECT_EQ(gridPosition.spanPosition(), 1000000); + gridPosition.setSpanPosition(-999999, ""); + EXPECT_EQ(gridPosition.spanPosition(), -999999); + gridPosition.setSpanPosition(-1000000, ""); + EXPECT_EQ(gridPosition.spanPosition(), -1000000); + gridPosition.setSpanPosition(-1000001, ""); + EXPECT_EQ(gridPosition.spanPosition(), -1000000); + gridPosition.setSpanPosition(INT_MIN, ""); + EXPECT_EQ(gridPosition.spanPosition(), -1000000); + +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/HTMLParserIdioms.cpp b/Tools/TestWebKitAPI/Tests/WebCore/HTMLParserIdioms.cpp new file mode 100644 index 000000000..6e9e738ab --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/HTMLParserIdioms.cpp @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "Test.h" +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +static int testParseHTMLInteger(const String& input) +{ + auto optionalResult = parseHTMLInteger(input); + EXPECT_TRUE(!!optionalResult); + return optionalResult.value_or(0); +} + +static bool parseHTMLIntegerFails(const String& input) +{ + return !parseHTMLInteger(input); +} + +TEST(WebCoreHTMLParserIdioms, parseHTMLInteger) +{ + EXPECT_EQ(0, testParseHTMLInteger("0")); + EXPECT_EQ(0, testParseHTMLInteger("-0")); + EXPECT_EQ(0, testParseHTMLInteger("+0")); + EXPECT_EQ(123, testParseHTMLInteger("123")); + EXPECT_EQ(123, testParseHTMLInteger("+123")); + EXPECT_EQ(-123, testParseHTMLInteger("-123")); + EXPECT_EQ(123, testParseHTMLInteger(" 123")); + EXPECT_EQ(123, testParseHTMLInteger("123 ")); + EXPECT_EQ(123, testParseHTMLInteger(" 123 ")); + EXPECT_EQ(123, testParseHTMLInteger("123abc")); + EXPECT_EQ(-123, testParseHTMLInteger("-123abc")); + EXPECT_EQ(123, testParseHTMLInteger(" +123")); + EXPECT_EQ(-123, testParseHTMLInteger(" -123")); + EXPECT_EQ(12, testParseHTMLInteger(" 12 3")); + EXPECT_EQ(1, testParseHTMLInteger("1.0")); + EXPECT_EQ(1, testParseHTMLInteger("1.")); + EXPECT_EQ(1, testParseHTMLInteger("1e1")); + + // All HTML whitespaces. + EXPECT_EQ(123, testParseHTMLInteger(" \t\r\n\f123")); + + // Boundaries. + EXPECT_EQ(-2147483648, testParseHTMLInteger("-2147483648")); + EXPECT_EQ(2147483647, testParseHTMLInteger("2147483647")); + + // Failure cases. + EXPECT_TRUE(parseHTMLIntegerFails("-2147483649")); + EXPECT_TRUE(parseHTMLIntegerFails("2147483648")); + EXPECT_TRUE(parseHTMLIntegerFails("111111111111111111")); + EXPECT_TRUE(parseHTMLIntegerFails("")); + EXPECT_TRUE(parseHTMLIntegerFails(" ")); + EXPECT_TRUE(parseHTMLIntegerFails(" ")); + EXPECT_TRUE(parseHTMLIntegerFails("+")); + EXPECT_TRUE(parseHTMLIntegerFails("+ 123")); + EXPECT_TRUE(parseHTMLIntegerFails("-")); + EXPECT_TRUE(parseHTMLIntegerFails("- 123")); + EXPECT_TRUE(parseHTMLIntegerFails("a")); + EXPECT_TRUE(parseHTMLIntegerFails("-a")); + EXPECT_TRUE(parseHTMLIntegerFails("+-123")); + EXPECT_TRUE(parseHTMLIntegerFails("-+123")); + EXPECT_TRUE(parseHTMLIntegerFails("++123")); + EXPECT_TRUE(parseHTMLIntegerFails("--123")); + EXPECT_TRUE(parseHTMLIntegerFails("\v123")); // '\v' is an ASCII space but not an HTML whitespace. + EXPECT_TRUE(parseHTMLIntegerFails("a123")); + EXPECT_TRUE(parseHTMLIntegerFails("+a123")); + EXPECT_TRUE(parseHTMLIntegerFails("-a123")); + EXPECT_TRUE(parseHTMLIntegerFails(".1")); + EXPECT_TRUE(parseHTMLIntegerFails("infinity")); +} + +static unsigned testParseHTMLNonNegativeInteger(const String& input) +{ + auto optionalResult = parseHTMLNonNegativeInteger(input); + EXPECT_TRUE(!!optionalResult); + return optionalResult.value_or(0); +} + +static bool parseHTMLNonNegativeIntegerFails(const String& input) +{ + return !parseHTMLNonNegativeInteger(input); +} + +TEST(WebCoreHTMLParserIdioms, parseHTMLNonNegativeInteger) +{ + EXPECT_EQ(123u, testParseHTMLNonNegativeInteger("123")); + EXPECT_EQ(123u, testParseHTMLNonNegativeInteger("+123")); + EXPECT_EQ(123u, testParseHTMLNonNegativeInteger(" 123")); + EXPECT_EQ(123u, testParseHTMLNonNegativeInteger("123 ")); + EXPECT_EQ(123u, testParseHTMLNonNegativeInteger(" 123 ")); + EXPECT_EQ(123u, testParseHTMLNonNegativeInteger("123abc")); + EXPECT_EQ(123u, testParseHTMLNonNegativeInteger(" +123")); + EXPECT_EQ(12u, testParseHTMLNonNegativeInteger(" 12 3")); + EXPECT_EQ(1u, testParseHTMLNonNegativeInteger("1.0")); + EXPECT_EQ(1u, testParseHTMLNonNegativeInteger("1.")); + EXPECT_EQ(1u, testParseHTMLNonNegativeInteger("1e1")); + + // All HTML whitespaces. + EXPECT_EQ(123u, testParseHTMLNonNegativeInteger(" \t\r\n\f123")); + + // Boundaries. + EXPECT_EQ(0u, testParseHTMLNonNegativeInteger("+0")); + EXPECT_EQ(0u, testParseHTMLNonNegativeInteger("0")); + EXPECT_EQ(0u, testParseHTMLNonNegativeInteger("-0")); + EXPECT_EQ(2147483647u, testParseHTMLNonNegativeInteger("2147483647")); + + // Failure cases. + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("-1")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("2147483648")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("2147483649")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("111111111111111111")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails(" -123")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("-123")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("-123abc")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails(" ")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails(" ")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("+")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("+ 123")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("-")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("- 123")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("a")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("-a")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("+-123")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("-+123")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("++123")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("--123")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("\v123")); // '\v' is an ASCII space but not an HTML whitespace. + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("a123")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("+a123")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("-a123")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails(".1")); + EXPECT_TRUE(parseHTMLNonNegativeIntegerFails("infinity")); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/IntPoint.cpp b/Tools/TestWebKitAPI/Tests/WebCore/IntPoint.cpp new file mode 100644 index 000000000..aa332e5c3 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/IntPoint.cpp @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include + +#if USE(CG) +#include +#endif + +#if PLATFORM(WIN) +#include +#endif + +namespace TestWebKitAPI { + +static void testGetAndSet(WebCore::IntPoint point) +{ + point.setX(1); + EXPECT_EQ(1, point.x()); + point.setY(2); + EXPECT_EQ(2, point.y()); +} + +TEST(IntPoint, DefaultConstruction) +{ + WebCore::IntPoint test; + + EXPECT_EQ(0, test.x()); + EXPECT_EQ(0, test.y()); + ASSERT_TRUE(test.isZero()); + + testGetAndSet(test); +} + +TEST(IntPoint, ValueConstruction) +{ + WebCore::IntPoint test(10, 9); + + EXPECT_EQ(10, test.x()); + EXPECT_EQ(9, test.y()); + + testGetAndSet(test); +} + +TEST(IntPoint, ZeroConstruction) +{ + WebCore::IntPoint test = WebCore::IntPoint::zero(); + + EXPECT_EQ(0, test.x()); + EXPECT_EQ(0, test.y()); + ASSERT_TRUE(test.isZero()); +} + +TEST(IntPoint, IntSizeConstruction) +{ + WebCore::IntSize testInput(2003, 1997); + WebCore::IntPoint test(testInput); + + EXPECT_EQ(2003, test.x()); + EXPECT_EQ(1997, test.y()); + ASSERT_FALSE(test.isZero()); +} + +TEST(IntPoint, FloatPointConstruction) +{ + WebCore::FloatPoint testInput(2003.2f, 1997.3f); + WebCore::IntPoint test(testInput); + + EXPECT_EQ(2003, test.x()); + EXPECT_EQ(1997, test.y()); + ASSERT_FALSE(test.isZero()); +} + +TEST(IntPoint, Move) +{ + WebCore::IntPoint test(10, 20); + WebCore::IntSize size(30, 50); + + test.move(size); + + EXPECT_EQ(40, test.x()); + EXPECT_EQ(70, test.y()); + + test.move(-2, 8); + + EXPECT_EQ(38, test.x()); + EXPECT_EQ(78, test.y()); + + WebCore::IntPoint offset(100, 100); + + test.moveBy(offset); + + EXPECT_EQ(138, test.x()); + EXPECT_EQ(178, test.y()); +} + +TEST(IntPoint, Scale) +{ + WebCore::IntPoint test(10, 20); + + test.scale(2.0); + + EXPECT_EQ(20, test.x()); + EXPECT_EQ(40, test.y()); + + test.scale(3.0, 1.5); + + EXPECT_EQ(60, test.x()); + EXPECT_EQ(60, test.y()); +} + +TEST(IntPoint, Expand) +{ + WebCore::IntPoint a(10, 20); + WebCore::IntPoint b(20, 40); + + auto c = a.expandedTo(b); + + EXPECT_EQ(20, c.x()); + EXPECT_EQ(40, c.y()); +} + +TEST(IntPoint, Shrink) +{ + WebCore::IntPoint a(10, 20); + WebCore::IntPoint b(20, 40); + + auto c = b.shrunkTo(a); + + EXPECT_EQ(10, c.x()); + EXPECT_EQ(20, c.y()); +} + +TEST(IntPoint, Transpose) +{ + WebCore::IntPoint a(10, 20); + + auto b = a.transposedPoint(); + + EXPECT_EQ(20, b.x()); + EXPECT_EQ(10, b.y()); +} + +TEST(IntPoint, Cast) +{ + WebCore::IntPoint a(10, 20); + + WebCore::IntSize as = WebCore::toIntSize(a); + EXPECT_EQ(10, as.width()); + EXPECT_EQ(20, as.height()); + +#if USE(CG) + CGPoint cgPoint = a; + + ASSERT_FLOAT_EQ(10.0f, cgPoint.x); + ASSERT_FLOAT_EQ(20.0f, cgPoint.y); + + WebCore::IntPoint fromCGPoint(cgPoint); + EXPECT_EQ(10, fromCGPoint.x()); + EXPECT_EQ(20, fromCGPoint.y()); + ASSERT_TRUE(a == fromCGPoint); +#endif + +#if PLATFORM(WIN) + POINT gdiPoint = a; + + ASSERT_FLOAT_EQ(10.0f, gdiPoint.x); + ASSERT_FLOAT_EQ(20.0f, gdiPoint.y); + + WebCore::IntPoint fromGDIPoint(gdiPoint); + EXPECT_EQ(10, fromGDIPoint.x()); + EXPECT_EQ(20, fromGDIPoint.y()); + ASSERT_TRUE(a == fromGDIPoint); + + D2D1_POINT_2F d2dPointF = a; + + ASSERT_FLOAT_EQ(10.0f, d2dPointF.x); + ASSERT_FLOAT_EQ(20.0f, d2dPointF.y); + + WebCore::IntPoint fromD2DPointF(d2dPointF); + EXPECT_EQ(10, fromD2DPointF.x()); + EXPECT_EQ(20, fromD2DPointF.y()); + ASSERT_TRUE(a == fromD2DPointF); + + D2D1_POINT_2U d2dPointU = a; + + ASSERT_FLOAT_EQ(10.0f, d2dPointU.x); + ASSERT_FLOAT_EQ(20.0f, d2dPointU.y); + + WebCore::IntPoint fromD2DPointU(d2dPointU); + EXPECT_EQ(10, fromD2DPointU.x()); + EXPECT_EQ(20, fromD2DPointU.y()); + ASSERT_TRUE(a == fromD2DPointU); +#endif +} + +TEST(IntPoint, Addition) +{ + WebCore::IntPoint a(10, 20); + WebCore::IntPoint b(50, 60); + WebCore::IntSize bs(50, 60); + + auto c = a + b; + + EXPECT_EQ(60, c.x()); + EXPECT_EQ(80, c.y()); + + a += bs; + + EXPECT_EQ(60, a.x()); + EXPECT_EQ(80, a.y()); +} + +TEST(IntPoint, Subtraction) +{ + WebCore::IntPoint a(100, 80); + WebCore::IntPoint b(50, 60); + WebCore::IntSize bs(50, 60); + + WebCore::IntSize c = a - b; + + EXPECT_EQ(50, c.width()); + EXPECT_EQ(20, c.height()); + + WebCore::IntPoint d = a - bs; + + EXPECT_EQ(50, d.x()); + EXPECT_EQ(20, d.y()); + + a -= bs; + + EXPECT_EQ(50, a.x()); + EXPECT_EQ(20, a.y()); +} + +TEST(IntPoint, Negation) +{ + WebCore::IntPoint a(100, 80); + auto b = -a; + + EXPECT_EQ(-100, b.x()); + EXPECT_EQ(-80, b.y()); +} + +TEST(IntPoint, Equality) +{ + WebCore::IntPoint a(100, 80); + WebCore::IntPoint b(70, 50); + WebCore::IntPoint c(100, 80); + + ASSERT_TRUE(a == c); + ASSERT_FALSE(a == b); + ASSERT_FALSE(a != c); + ASSERT_TRUE(a != b); +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/IntRect.cpp b/Tools/TestWebKitAPI/Tests/WebCore/IntRect.cpp new file mode 100644 index 000000000..7392f06a7 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/IntRect.cpp @@ -0,0 +1,615 @@ +/* + * Copyright (C) 2014-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include + +#if USE(CG) +#include +#endif + +#if PLATFORM(WIN) +#include +#endif + +namespace TestWebKitAPI { + +static void testGetAndSet(WebCore::IntRect rect) +{ + rect.setX(1); + EXPECT_EQ(1, rect.x()); + rect.setY(2); + EXPECT_EQ(2, rect.y()); + rect.setWidth(203); + EXPECT_EQ(203, rect.width()); + rect.setHeight(73); + EXPECT_EQ(73, rect.height()); +} + +static void testEmptyRect(const WebCore::IntRect& rect) +{ + EXPECT_EQ(0, rect.x()); + EXPECT_EQ(0, rect.y()); + EXPECT_EQ(0, rect.width()); + EXPECT_EQ(0, rect.height()); + EXPECT_EQ(0, rect.maxX()); + EXPECT_EQ(0, rect.maxY()); + EXPECT_TRUE(rect.isEmpty()); +} + +TEST(IntRect, DefaultConstruction) +{ + WebCore::IntRect test; + + testEmptyRect(test); + + testGetAndSet(test); + + auto location = test.location(); + EXPECT_EQ(0, location.x()); + EXPECT_EQ(0, location.y()); + + auto size = test.size(); + EXPECT_EQ(0, size.width()); + EXPECT_EQ(0, size.height()); +} + +TEST(IntRect, ValueConstruction) +{ + WebCore::IntRect test(10, 20, 100, 50); + + EXPECT_EQ(10, test.x()); + EXPECT_EQ(20, test.y()); + EXPECT_EQ(100, test.width()); + EXPECT_EQ(50, test.height()); + EXPECT_EQ(110, test.maxX()); + EXPECT_EQ(70, test.maxY()); + EXPECT_FALSE(test.isEmpty()); + + auto location = test.location(); + EXPECT_EQ(10, location.x()); + EXPECT_EQ(20, location.y()); + + auto size = test.size(); + EXPECT_EQ(100, size.width()); + EXPECT_EQ(50, size.height()); +} + +TEST(IntRect, PointSizeConstruction) +{ + WebCore::IntPoint location(20, 30); + WebCore::IntSize size(100, 50); + + WebCore::IntRect test(location, size); + + EXPECT_EQ(20, test.x()); + EXPECT_EQ(30, test.y()); + EXPECT_EQ(100, test.width()); + EXPECT_EQ(50, test.height()); + EXPECT_EQ(120, test.maxX()); + EXPECT_EQ(80, test.maxY()); + EXPECT_FALSE(test.isEmpty()); + + auto location2 = test.location(); + EXPECT_EQ(20, location2.x()); + EXPECT_EQ(30, location2.y()); + + auto size2 = test.size(); + EXPECT_EQ(100, size2.width()); + EXPECT_EQ(50, size2.height()); +} + +TEST(IntRect, FloatRectConstruction) +{ + WebCore::FloatRect rect(20.0f, 30.0f, 150.0f, 300.0f); + + WebCore::IntRect test(rect); + + EXPECT_EQ(20, test.x()); + EXPECT_EQ(30, test.y()); + EXPECT_EQ(150, test.width()); + EXPECT_EQ(300, test.height()); + EXPECT_EQ(170, test.maxX()); + EXPECT_EQ(330, test.maxY()); + EXPECT_FALSE(test.isEmpty()); + + auto location = test.location(); + EXPECT_EQ(20, location.x()); + EXPECT_EQ(30, location.y()); + + auto size = test.size(); + EXPECT_EQ(150, size.width()); + EXPECT_EQ(300, size.height()); +} + +TEST(IntRect, SetLocationAndSize) +{ + WebCore::IntRect rect; + + testEmptyRect(rect); + + WebCore::IntPoint location(10, 20); + + rect.setLocation(location); + + EXPECT_EQ(10, rect.x()); + EXPECT_EQ(20, rect.y()); + EXPECT_EQ(0, rect.width()); + EXPECT_EQ(0, rect.height()); + EXPECT_EQ(10, rect.maxX()); + EXPECT_EQ(20, rect.maxY()); + EXPECT_TRUE(rect.isEmpty()); + + WebCore::IntSize size(100, 200); + + rect.setSize(size); + + EXPECT_EQ(10, rect.x()); + EXPECT_EQ(20, rect.y()); + EXPECT_EQ(100, rect.width()); + EXPECT_EQ(200, rect.height()); + EXPECT_EQ(110, rect.maxX()); + EXPECT_EQ(220, rect.maxY()); + EXPECT_FALSE(rect.isEmpty()); +} + +TEST(IntRect, Center) +{ + WebCore::IntRect rect(20, 40, 100, 200); + + auto center = rect.center(); + + EXPECT_EQ(70, center.x()); + EXPECT_EQ(140, center.y()); +} + +TEST(IntRect, Move) +{ + WebCore::IntRect rect(20, 30, 100, 200); + + WebCore::IntSize delta(10, 20); + + rect.move(delta); + + EXPECT_EQ(30, rect.x()); + EXPECT_EQ(50, rect.y()); + + WebCore::IntPoint deltaPoint(-20, -40); + + rect.moveBy(deltaPoint); + + EXPECT_EQ(10, rect.x()); + EXPECT_EQ(10, rect.y()); + + rect.move(-10, 22); + + EXPECT_EQ(0, rect.x()); + EXPECT_EQ(32, rect.y()); +} + +TEST(IntRect, Expand) +{ + WebCore::IntRect rect(20, 30, 100, 200); + + WebCore::IntSize size(100, 100); + + rect.expand(size); + + EXPECT_EQ(200, rect.width()); + EXPECT_EQ(300, rect.height()); + + rect.expand(55, 22); + + EXPECT_EQ(255, rect.width()); + EXPECT_EQ(322, rect.height()); + + WebCore::IntSize size2(-10, -20); + + rect.expand(size2); + + EXPECT_EQ(245, rect.width()); + EXPECT_EQ(302, rect.height()); + + rect.expand(-5, -2); + + EXPECT_EQ(240, rect.width()); + EXPECT_EQ(300, rect.height()); +} + +TEST(IntRect, Contract) +{ + WebCore::IntRect rect(20, 30, 100, 200); + + WebCore::IntSize size(50, 100); + + rect.contract(size); + + EXPECT_EQ(50, rect.width()); + EXPECT_EQ(100, rect.height()); + + rect.contract(25, 22); + + EXPECT_EQ(25, rect.width()); + EXPECT_EQ(78, rect.height()); + + WebCore::IntSize size2(-10, -20); + + rect.contract(size2); + + EXPECT_EQ(35, rect.width()); + EXPECT_EQ(98, rect.height()); + + rect.contract(-5, -2); + + EXPECT_EQ(40, rect.width()); + EXPECT_EQ(100, rect.height()); +} + +TEST(IntRect, ShiftXEdge) +{ + WebCore::IntRect rect(20, 30, 100, 200); + + rect.shiftXEdgeTo(77); + + EXPECT_EQ(77, rect.x()); + EXPECT_EQ(120, rect.maxX()); + EXPECT_EQ(30, rect.y()); + EXPECT_EQ(230, rect.maxY()); + EXPECT_EQ(43, rect.width()); + EXPECT_EQ(200, rect.height()); + + rect.shiftMaxXEdgeTo(200); + + EXPECT_EQ(77, rect.x()); + EXPECT_EQ(200, rect.maxX()); + EXPECT_EQ(30, rect.y()); + EXPECT_EQ(230, rect.maxY()); + EXPECT_EQ(123, rect.width()); + EXPECT_EQ(200, rect.height()); +} + +TEST(IntRect, ShiftYEdge) +{ + WebCore::IntRect rect(20, 30, 100, 200); + + rect.shiftYEdgeTo(59.0f); + + EXPECT_EQ(20, rect.x()); + EXPECT_EQ(120, rect.maxX()); + EXPECT_EQ(59, rect.y()); + EXPECT_EQ(230, rect.maxY()); + EXPECT_EQ(100, rect.width()); + EXPECT_EQ(171, rect.height()); + + rect.shiftMaxYEdgeTo(270.0f); + + EXPECT_EQ(20, rect.x()); + EXPECT_EQ(120, rect.maxX()); + EXPECT_EQ(59, rect.y()); + EXPECT_EQ(270, rect.maxY()); + EXPECT_EQ(100, rect.width()); + EXPECT_EQ(211, rect.height()); +} + +TEST(IntRect, Inflate) +{ + WebCore::IntRect rect(20, 30, 100, 200); + + rect.inflateX(5); + + EXPECT_EQ(15, rect.x()); + EXPECT_EQ(125, rect.maxX()); + + rect.inflateY(4); + + EXPECT_EQ(26, rect.y()); + EXPECT_EQ(234, rect.maxY()); + + rect.inflate(10); + + EXPECT_EQ(5, rect.x()); + EXPECT_EQ(135, rect.maxX()); + EXPECT_EQ(16, rect.y()); + EXPECT_EQ(244, rect.maxY()); +} + +TEST(IntRect, Corners) +{ + WebCore::IntRect rect(20, 30, 100, 200); + + WebCore::FloatPoint topLeft = rect.minXMinYCorner(); + EXPECT_EQ(20, topLeft.x()); + EXPECT_EQ(30, topLeft.y()); + + WebCore::FloatPoint topRight = rect.maxXMinYCorner(); + EXPECT_EQ(120, topRight.x()); + EXPECT_EQ(30, topRight.y()); + + WebCore::FloatPoint bottomLeft = rect.minXMaxYCorner(); + EXPECT_EQ(20, bottomLeft.x()); + EXPECT_EQ(230, bottomLeft.y()); + + WebCore::FloatPoint bottomRight = rect.maxXMaxYCorner(); + EXPECT_EQ(120, bottomRight.x()); + EXPECT_EQ(230, bottomRight.y()); +} + +TEST(IntRect, Contains) +{ + WebCore::IntRect rect(20, 30, 100, 200); + + WebCore::IntRect contained(30, 40, 50, 100); + + ASSERT_TRUE(rect.contains(contained)); + + WebCore::IntRect outside(120, 230, 50, 100); + + ASSERT_FALSE(rect.contains(outside)); + + WebCore::IntRect intersects(10, 20, 90, 180); + + ASSERT_FALSE(rect.contains(intersects)); + + WebCore::IntPoint pointInside(60, 70); + + ASSERT_TRUE(rect.contains(pointInside)); + + WebCore::IntPoint pointOutside(160, 270); + + ASSERT_FALSE(rect.contains(pointOutside)); + + WebCore::IntPoint pointOnLine(20, 30); + + ASSERT_TRUE(rect.contains(pointOnLine)); + + ASSERT_TRUE(rect.contains(60, 70)); + ASSERT_FALSE(rect.contains(160, 270)); +} + +TEST(IntRect, Intersects) +{ + WebCore::IntRect rect(20, 30, 100, 200); + + WebCore::IntRect contained(30, 40, 50, 100); + + ASSERT_TRUE(rect.intersects(contained)); + + WebCore::IntRect outside(120, 230, 50, 100); + + ASSERT_FALSE(rect.intersects(outside)); + + WebCore::IntRect intersects(10, 20, 90, 180); + + ASSERT_TRUE(rect.intersects(intersects)); +} + +static void testIntersectResult(const WebCore::IntRect& rect) +{ + EXPECT_EQ(70, rect.x()); + EXPECT_EQ(120, rect.maxX()); + EXPECT_EQ(80, rect.y()); + EXPECT_EQ(230, rect.maxY()); +} + +TEST(IntRect, Intersect) +{ + WebCore::IntRect rectA(20, 30, 100, 200); + WebCore::IntRect rectB(70, 80, 100, 200); + + rectA.intersect(rectB); + + testIntersectResult(rectA); + + WebCore::IntRect rectC(20, 30, 100, 200); + + auto intersected = WebCore::intersection(rectC, rectB); + + testIntersectResult(intersected); +} + +static void testUnitedRects(const WebCore::IntRect& united) +{ + EXPECT_EQ(20, united.x()); + EXPECT_EQ(170, united.maxX()); + EXPECT_EQ(30, united.y()); + EXPECT_EQ(280, united.maxY()); +} + +TEST(IntRect, Unite) +{ + WebCore::IntRect rectA(20, 30, 100, 200); + WebCore::IntRect rectB(70, 80, 100, 200); + + rectA.unite(rectB); + + testUnitedRects(rectA); + + WebCore::IntRect rectC(20, 30, 100, 200); + + auto united = WebCore::unionRect(rectC, rectB); + + testUnitedRects(united); +} + +TEST(IntRect, Scale) +{ + WebCore::IntRect rect(20, 30, 100, 200); + + rect.scale(2.0f); + + EXPECT_EQ(40, rect.x()); + EXPECT_EQ(240, rect.maxX()); + EXPECT_EQ(60, rect.y()); + EXPECT_EQ(460, rect.maxY()); +} + +TEST(IntRect, Transpose) +{ + WebCore::IntRect rect(20, 30, 100, 200); + + auto transposed = rect.transposedRect(); + + EXPECT_EQ(30, transposed.x()); + EXPECT_EQ(230, transposed.maxX()); + EXPECT_EQ(20, transposed.y()); + EXPECT_EQ(120, transposed.maxY()); +} + +static void checkCastRect(const WebCore::IntRect& rect) +{ + EXPECT_EQ(10, rect.x()); + EXPECT_EQ(40, rect.maxX()); + EXPECT_EQ(20, rect.y()); + EXPECT_EQ(60, rect.maxY()); + EXPECT_EQ(30, rect.width()); + EXPECT_EQ(40, rect.height()); +} + +TEST(IntRect, Casting) +{ + WebCore::IntRect rect(10, 20, 30, 40); + +#if USE(CG) + CGRect cgRect = CGRectMake(10.0, 20.0, 30.0, 40.0); + + WebCore::IntRect rectFromCGRect(cgRect); + + checkCastRect(rectFromCGRect); +#endif + +#if PLATFORM(WIN) + RECT gdiRect = rect; + + EXPECT_EQ(10, gdiRect.left); + EXPECT_EQ(20, gdiRect.top); + EXPECT_EQ(40, gdiRect.right); + EXPECT_EQ(60, gdiRect.bottom); + + WebCore::IntRect rectFromGDIRect(gdiRect); + + checkCastRect(rectFromGDIRect); + + D2D1_RECT_U d2dRectU = rect; + + EXPECT_EQ(10, d2dRectU.left); + EXPECT_EQ(20, d2dRectU.top); + EXPECT_EQ(40, d2dRectU.right); + EXPECT_EQ(60, d2dRectU.bottom); + + WebCore::IntRect rectFromD2DRectU(d2dRectU); + + checkCastRect(rectFromD2DRectU); + + D2D1_RECT_F d2dRectF = rect; + + EXPECT_FLOAT_EQ(10.0f, d2dRectF.left); + EXPECT_FLOAT_EQ(20.0f, d2dRectF.top); + EXPECT_FLOAT_EQ(40.0f, d2dRectF.right); + EXPECT_FLOAT_EQ(60.0f, d2dRectF.bottom); + + WebCore::IntRect rectFromD2DRectF(d2dRectF); + + checkCastRect(rectFromD2DRectF); +#endif +} + +static void checkSubtractionResult1(const WebCore::IntRect& rect) +{ + EXPECT_EQ(-10, rect.x()); + EXPECT_EQ(90, rect.maxX()); + EXPECT_EQ(-10, rect.y()); + EXPECT_EQ(90, rect.maxY()); + EXPECT_EQ(100, rect.width()); + EXPECT_EQ(100, rect.height()); +} + +static void checkSubtractionResult2(const WebCore::IntRect& rect) +{ + EXPECT_EQ(-40, rect.x()); + EXPECT_EQ(60, rect.maxX()); + EXPECT_EQ(-50, rect.y()); + EXPECT_EQ(50, rect.maxY()); + EXPECT_EQ(100, rect.width()); + EXPECT_EQ(100, rect.height()); +} + +TEST(IntRect, Subtraction) +{ + WebCore::IntRect rect(10, 20, 100, 100); + WebCore::IntPoint rightSide(20, 30); + + rect -= rightSide; + + checkSubtractionResult1(rect); + + auto rect2 = rect - WebCore::IntPoint(30, 40); + checkSubtractionResult2(rect2); +} + +TEST(IntRect, Equality) +{ + WebCore::IntRect rect(10, 20, 100, 100); + WebCore::IntRect rect2(10, 20, 100, 100); + WebCore::IntRect rightSide(110, 20, 20, 100); + + ASSERT_TRUE(rect == rect2); + ASSERT_FALSE(rect != rect2); + ASSERT_TRUE(rect != rightSide); + ASSERT_FALSE(rect == rightSide); +} + +static void checkEnclosingIntRect(const WebCore::IntRect& rect) +{ + EXPECT_EQ(10, rect.x()); + EXPECT_EQ(41, rect.maxX()); + EXPECT_EQ(21, rect.y()); + EXPECT_EQ(62, rect.maxY()); + EXPECT_EQ(31, rect.width()); + EXPECT_EQ(41, rect.height()); +} + +TEST(IntRect, EnclosingIntRect) +{ +#if USE(CG) + CGRect cgRect = CGRectMake(10.5, 21.3, 30.1, 40.0); + + WebCore::IntRect enclosingCG = WebCore::enclosingIntRect(cgRect); + + checkEnclosingIntRect(enclosingCG); +#endif +} + +TEST(IntRect, AreaAndDistances) +{ + WebCore::IntRect rect(10, 20, 100, 100); + + EXPECT_EQ(10000U, rect.area().unsafeGet()); +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/IntSize.cpp b/Tools/TestWebKitAPI/Tests/WebCore/IntSize.cpp new file mode 100644 index 000000000..a55942f74 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/IntSize.cpp @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2014-2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include + +#if USE(CG) +#include +#endif + +#if PLATFORM(WIN) +#include +#endif + +namespace TestWebKitAPI { + +static void testGetAndSet(WebCore::IntSize rect) +{ + rect.setWidth(203); + EXPECT_EQ(203, rect.width()); + rect.setHeight(73); + EXPECT_EQ(73, rect.height()); +} + +static void testEmptySize(const WebCore::IntSize& rect) +{ + EXPECT_EQ(0, rect.width()); + EXPECT_EQ(0, rect.height()); + EXPECT_TRUE(rect.isEmpty()); + EXPECT_TRUE(rect.isZero()); +} + +TEST(IntSize, DefaultConstruction) +{ + WebCore::IntSize test; + + testEmptySize(test); + + testGetAndSet(test); +} + +TEST(IntSize, ValueConstruction) +{ + WebCore::IntSize test(100, 200); + + EXPECT_EQ(100, test.width()); + EXPECT_EQ(200, test.height()); + EXPECT_FALSE(test.isEmpty()); + EXPECT_FALSE(test.isZero()); + + static const float epsilon = 0.0001f; + EXPECT_NEAR(0.5f, test.aspectRatio(), epsilon); +} + +TEST(IntSize, FloatSizeConstruction) +{ + WebCore::FloatSize size(1024.2, 767.8); + WebCore::IntSize test(size); + + EXPECT_EQ(1024, test.width()); + EXPECT_EQ(767, test.height()); + + ASSERT_FALSE(test.isEmpty()); + ASSERT_FALSE(test.isZero()); + + static const double epsilon = 0.001; + EXPECT_NEAR(1.335f, test.aspectRatio(), epsilon); + + testGetAndSet(test); +} + +TEST(IntSize, DiagonalLengthAndArea) +{ + WebCore::IntSize test(1024, 768); + + EXPECT_EQ(1638400, test.diagonalLengthSquared()); + EXPECT_EQ(786432U, test.area().unsafeGet()); +} + +TEST(IntSize, Scale) +{ + WebCore::IntSize test(1024, 768); + + test.scale(2.0f); + + EXPECT_EQ(2048, test.width()); + EXPECT_EQ(1536, test.height()); + + test.scale(0.5f); + + EXPECT_EQ(1024, test.width()); + EXPECT_EQ(768, test.height()); + + test.scale(2.0f, 0.5f); + + EXPECT_EQ(2048, test.width()); + EXPECT_EQ(384, test.height()); +} + +TEST(IntSize, Expand) +{ + WebCore::IntSize test(1024, 768); + + EXPECT_EQ(1024, test.width()); + EXPECT_EQ(768, test.height()); + + test.expand(100, 50); + + EXPECT_EQ(1124, test.width()); + EXPECT_EQ(818, test.height()); + + WebCore::IntSize other(2048, 700); + + auto expanded = test.expandedTo(other); + + EXPECT_EQ(2048, expanded.width()); + EXPECT_EQ(818, expanded.height()); +} + +TEST(IntSize, Shrink) +{ + WebCore::IntSize test(1024, 768); + WebCore::IntSize other(1000, 700); + + auto shrunken = test.shrunkTo(other); + + EXPECT_EQ(1000, shrunken.width()); + EXPECT_EQ(700, shrunken.height()); + + WebCore::IntSize other2(2000.0f, 700.0f); + + auto shrunken2 = test.shrunkTo(other2); + + EXPECT_EQ(1024, shrunken2.width()); + EXPECT_EQ(700, shrunken2.height()); +} + +TEST(IntSize, TransposedSize) +{ + WebCore::IntSize test(1024, 768); + + auto transposedSize = test.transposedSize(); + + EXPECT_EQ(768, transposedSize.width()); + EXPECT_EQ(1024, transposedSize.height()); +} + +TEST(IntSize, Casting) +{ + WebCore::IntSize test(1024, 768); + +#if USE(CG) + CGSize cgSize = test; + + EXPECT_FLOAT_EQ(1024.0f, cgSize.width); + EXPECT_FLOAT_EQ(768.0f, cgSize.height); + + CGSize cgSize2 = CGSizeMake(-22.3f, 14.6f); + + WebCore::IntSize testCG(cgSize2); + + EXPECT_EQ(-22, testCG.width()); + EXPECT_EQ(14, testCG.height()); +#endif + +#if PLATFORM(WIN) + D2D1_SIZE_U d2dSizeU = test; + EXPECT_EQ(1024, d2dSizeU.width); + EXPECT_EQ(768, d2dSizeU.height); + + D2D1_SIZE_F d2dSizeF = test; + EXPECT_FLOAT_EQ(1024.0f, d2dSizeF.width); + EXPECT_FLOAT_EQ(768.0f, d2dSizeF.height); + + D2D1_SIZE_F d2dSizeF2 = D2D1::SizeF(-22.3f, 14.6f); + + WebCore::IntSize testD2D(d2dSizeF2); + + EXPECT_EQ(-22, testD2D.width()); + EXPECT_EQ(14, testD2D.height()); + + D2D1_SIZE_F d2dSizeU2 = D2D1::SizeF(-23, 16); + + WebCore::IntSize testD2D2(d2dSizeU2); + + EXPECT_EQ(-23, testD2D2.width()); + EXPECT_EQ(16, testD2D2.height()); +#endif +} + +TEST(IntSize, AddSubtract) +{ + WebCore::IntSize a(512, 384); + WebCore::IntSize b(100, 100); + + WebCore::IntSize c = a + b; + + EXPECT_EQ(612, c.width()); + EXPECT_EQ(484, c.height()); + + a += b; + + EXPECT_EQ(612, a.width()); + EXPECT_EQ(484, a.height()); + + WebCore::IntSize a2(512, 384); + + WebCore::IntSize d = a2 - b; + + EXPECT_EQ(412, d.width()); + EXPECT_EQ(284, d.height()); + + a2 -= b; + + EXPECT_EQ(412, a2.width()); + EXPECT_EQ(284, a2.height()); +} + +TEST(IntSize, Negation) +{ + WebCore::IntSize a(512, 384); + + WebCore::IntSize negated = -a; + + EXPECT_EQ(-512, negated.width()); + EXPECT_EQ(-384, negated.height()); +} + +TEST(IntSize, Equality) +{ + WebCore::IntSize a(1024, 768); + WebCore::IntSize b(1024, 768); + WebCore::IntSize c(768, 534); + + ASSERT_TRUE(a == b); + ASSERT_FALSE(a != b); + ASSERT_FALSE(a == c); + ASSERT_TRUE(a != c); +} + +TEST(IntSize, Contract) +{ + WebCore::IntSize a(1024, 768); + + a.contract(100, 50); + + EXPECT_EQ(924, a.width()); + EXPECT_EQ(718, a.height()); +} + +TEST(IntSize, Clamp) +{ + WebCore::IntSize a(1024, 768); + + a.clampNegativeToZero(); + + EXPECT_EQ(1024, a.width()); + EXPECT_EQ(768, a.height()); + + WebCore::IntSize b(-1024, -768); + + b.clampNegativeToZero(); + + EXPECT_EQ(0, b.width()); + EXPECT_EQ(0, b.height()); + + WebCore::IntSize minimumSize(1024, 1000); + + a.clampToMinimumSize(minimumSize); + + EXPECT_EQ(1024, a.width()); + EXPECT_EQ(1000, a.height()); +} + +TEST(IntSize, ConstrainedBetween) +{ + WebCore::IntSize minimumSize(384, 256); + WebCore::IntSize maximumSize(2048, 1536); + + WebCore::IntSize a(1024, 768); + + auto constrained1 = a.constrainedBetween(minimumSize, maximumSize); + + EXPECT_EQ(1024, constrained1.width()); + EXPECT_EQ(768, constrained1.height()); + + WebCore::IntSize b(200, 100); + + auto constrained2 = b.constrainedBetween(minimumSize, maximumSize); + + EXPECT_EQ(384, constrained2.width()); + EXPECT_EQ(256, constrained2.height()); + + WebCore::IntSize c(5000, 2000); + + auto constrained3 = c.constrainedBetween(minimumSize, maximumSize); + + EXPECT_EQ(2048, constrained3.width()); + EXPECT_EQ(1536, constrained3.height()); +} + +/* + +IntSize constrainedBetween(const IntSize& min, const IntSize& max) const; + +*/ + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/LayoutUnit.cpp b/Tools/TestWebKitAPI/Tests/WebCore/LayoutUnit.cpp index ceafb039c..f904c96a1 100644 --- a/Tools/TestWebKitAPI/Tests/WebCore/LayoutUnit.cpp +++ b/Tools/TestWebKitAPI/Tests/WebCore/LayoutUnit.cpp @@ -28,8 +28,6 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define ENABLE_SUBPIXEL_LAYOUT 1 -#define ENABLE_SATURATED_LAYOUT_ARITHMETIC 1 #include "config.h" #include @@ -77,6 +75,10 @@ TEST(WebCoreLayoutUnit, LayoutUnitFloat) ASSERT_NEAR(LayoutUnit(345634.12335f).toFloat(), 345634.12335f, tolerance); ASSERT_NEAR(LayoutUnit(-345634.12335f).toFloat(), -345634.12335f, tolerance); ASSERT_NEAR(LayoutUnit(-345634).toFloat(), -345634.0f, tolerance); + ASSERT_NEAR(LayoutUnit(33554432.f).toFloat(), 33554432.f, tolerance); + ASSERT_NEAR(LayoutUnit(-33554432.f).toFloat(), -33554432.f, tolerance); + ASSERT_NEAR(LayoutUnit(33554432.f).toDouble(), 33554432.f, tolerance); + ASSERT_NEAR(LayoutUnit(-33554432.f).toDouble(), -33554432.f, tolerance); } TEST(WebCoreLayoutUnit, LayoutUnitRounding) @@ -104,28 +106,6 @@ TEST(WebCoreLayoutUnit, LayoutUnitRounding) ASSERT_EQ(LayoutUnit::fromFloatRound(1.51f).round(), 2); } -TEST(WebCoreLayoutUnit, LayoutUnitSnapSizeToPixel) -{ - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1), LayoutUnit(0)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1), LayoutUnit(0.5)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0)), 2); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.49)), 2); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.5)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.75)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(0.99)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1)), 2); - - ASSERT_EQ(snapSizeToPixel(LayoutUnit(0.5), LayoutUnit(1.5)), 0); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(0.99), LayoutUnit(1.5)), 0); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.0), LayoutUnit(1.5)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.49), LayoutUnit(1.5)), 1); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(1.5), LayoutUnit(1.5)), 1); - - ASSERT_EQ(snapSizeToPixel(LayoutUnit(100.5), LayoutUnit(100)), 101); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(intMaxForLayoutUnit), LayoutUnit(0.3)), intMaxForLayoutUnit); - ASSERT_EQ(snapSizeToPixel(LayoutUnit(intMinForLayoutUnit), LayoutUnit(-0.3)), intMinForLayoutUnit); -} - TEST(WebCoreLayoutUnit, LayoutUnitMultiplication) { ASSERT_EQ((LayoutUnit(1) * LayoutUnit(1)).toInt(), 1); @@ -250,5 +230,19 @@ TEST(WebCoreLayoutUnit, LayoutUnitFloor) ASSERT_EQ((LayoutUnit(intMinForLayoutUnit) + LayoutUnit(1)).floor(), intMinForLayoutUnit + 1); } +TEST(WebCoreLayoutUnit, LayoutUnitPixelSnapping) +{ + for (int i = -100000; i <= 100000; ++i) { + ASSERT_EQ(roundToDevicePixel(LayoutUnit(i), 1), i); + ASSERT_EQ(roundToDevicePixel(LayoutUnit(i), 2), i); + ASSERT_EQ(roundToDevicePixel(LayoutUnit(i), 3), i); + } + + for (float i = -10000; i < 0; i = i + 0.5) + ASSERT_FLOAT_EQ(roundToDevicePixel(LayoutUnit(i), 2), i); + + for (float i = -10000.25; i < 0; i = i + 0.5) + ASSERT_FLOAT_EQ(roundToDevicePixel(LayoutUnit(i), 2), i + 0.25); +} } // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp b/Tools/TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp new file mode 100644 index 000000000..986388302 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +TEST(WebCore, ParsedContentRangeFromString) +{ + // Basic parsing + ASSERT_TRUE(ParsedContentRange("bytes 0-1/2").isValid()); + ASSERT_TRUE(ParsedContentRange("bytes 0-1/*").isValid()); + ASSERT_EQ(0, ParsedContentRange("bytes 0-1/2").firstBytePosition()); + ASSERT_EQ(1, ParsedContentRange("bytes 0-1/2").lastBytePosition()); + ASSERT_EQ(2, ParsedContentRange("bytes 0-1/2").instanceLength()); + ASSERT_EQ(ParsedContentRange::UnknownLength, ParsedContentRange("bytes 0-1/*").instanceLength()); + + // Whitespace errors + ASSERT_FALSE(ParsedContentRange("bytes 0-1/*").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0 -1/*").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0- 1/*").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0-1 /*").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0-1/ *").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0-1/* ").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0-1/ 2").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0-1/2 ").isValid()); + + // Non-digit errors + ASSERT_FALSE(ParsedContentRange("bytes abcd-1/2").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0-abcd/2").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0-1/abcd").isValid()); + + // Range requirement errors + ASSERT_FALSE(ParsedContentRange("bytes 1-0/2").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0-2/1").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 2/0-1").isValid()); + ASSERT_FALSE(ParsedContentRange("abcd 0/1-2").isValid()); + + // Negative value errors + ASSERT_FALSE(ParsedContentRange("bytes -0-1/*").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes -1/*").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0--0/2").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 0-1/-2").isValid()); + + // Edge cases + ASSERT_TRUE(ParsedContentRange("bytes 9223372036854775805-9223372036854775806/9223372036854775807").isValid()); + ASSERT_FALSE(ParsedContentRange("bytes 9223372036854775808-9223372036854775809/9223372036854775810").isValid()); +} + +TEST(WebCore, ParsedContentRangeFromValues) +{ + ASSERT_TRUE(ParsedContentRange(0, 1, 2).isValid()); + ASSERT_TRUE(ParsedContentRange(0, 1, ParsedContentRange::UnknownLength).isValid()); + ASSERT_FALSE(ParsedContentRange().isValid()); + ASSERT_FALSE(ParsedContentRange(1, 0, 2).isValid()); + ASSERT_FALSE(ParsedContentRange(0, 2, 1).isValid()); + ASSERT_FALSE(ParsedContentRange(0, 0, 0).isValid()); + ASSERT_FALSE(ParsedContentRange(-1, 1, 2).isValid()); + ASSERT_FALSE(ParsedContentRange(0, -1, 2).isValid()); + ASSERT_FALSE(ParsedContentRange(0, 1, -2).isValid()); + ASSERT_FALSE(ParsedContentRange(-2, -1, 2).isValid()); +} + +TEST(WebCore, ParsedContentRangeToString) +{ + ASSERT_STREQ("bytes 0-1/2", ParsedContentRange(0, 1, 2).headerValue().utf8().data()); + ASSERT_STREQ("bytes 0-1/*", ParsedContentRange(0, 1, ParsedContentRange::UnknownLength).headerValue().utf8().data()); + ASSERT_STREQ("", ParsedContentRange().headerValue().utf8().data()); +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/PublicSuffix.cpp b/Tools/TestWebKitAPI/Tests/WebCore/PublicSuffix.cpp new file mode 100644 index 000000000..f87ba63eb --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/PublicSuffix.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(PUBLIC_SUFFIX_LIST) + +#include "WTFStringUtilities.h" +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +class PublicSuffix: public testing::Test { +public: + virtual void SetUp() + { + WTF::initializeMainThread(); + } +}; + +TEST_F(PublicSuffix, IsPublicSuffix) +{ + EXPECT_TRUE(isPublicSuffix("com")); + EXPECT_FALSE(isPublicSuffix("test.com")); + EXPECT_FALSE(isPublicSuffix("com.com")); + EXPECT_TRUE(isPublicSuffix("net")); + EXPECT_TRUE(isPublicSuffix("org")); + EXPECT_TRUE(isPublicSuffix("co.uk")); + EXPECT_FALSE(isPublicSuffix("bl.uk")); + EXPECT_FALSE(isPublicSuffix("test.co.uk")); + EXPECT_TRUE(isPublicSuffix("xn--zf0ao64a.tw")); +} + +TEST_F(PublicSuffix, TopPrivatelyControlledDomain) +{ + EXPECT_EQ(String("test.com"), topPrivatelyControlledDomain("test.com")); + EXPECT_EQ(String("test.com"), topPrivatelyControlledDomain("com.test.com")); + EXPECT_EQ(String("test.com"), topPrivatelyControlledDomain("subdomain.test.com")); + EXPECT_EQ(String("com.com"), topPrivatelyControlledDomain("www.com.com")); + EXPECT_EQ(String("test.co.uk"), topPrivatelyControlledDomain("test.co.uk")); + EXPECT_EQ(String("test.co.uk"), topPrivatelyControlledDomain("subdomain.test.co.uk")); + EXPECT_EQ(String("bl.uk"), topPrivatelyControlledDomain("bl.uk")); + EXPECT_EQ(String("bl.uk"), topPrivatelyControlledDomain("subdomain.bl.uk")); + EXPECT_EQ(String("test.xn--zf0ao64a.tw"), topPrivatelyControlledDomain("test.xn--zf0ao64a.tw")); + EXPECT_EQ(String("test.xn--zf0ao64a.tw"), topPrivatelyControlledDomain("www.test.xn--zf0ao64a.tw")); + EXPECT_EQ(String("127.0.0.1"), topPrivatelyControlledDomain("127.0.0.1")); + EXPECT_EQ(String(), topPrivatelyControlledDomain("1")); + EXPECT_EQ(String(), topPrivatelyControlledDomain("com")); +} + +} + +#endif // ENABLE(PUBLIC_SUFFIX_LIST) diff --git a/Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp b/Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp new file mode 100644 index 000000000..f7e91c6fd --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(MEDIA_SOURCE) + +#include "Test.h" +#include +#include + +namespace WTF { +inline std::ostream& operator<<(std::ostream& os, const MediaTime& time) +{ + if (time.hasDoubleValue()) + os << "{ " << time.toDouble() << " }"; + else + os << "{ " << time.timeValue() << " / " << time.timeScale() << ", " << time.toDouble() << " }"; + return os; +} +} + +using namespace WebCore; + +namespace TestWebKitAPI { + +class TestSample : public MediaSample { +public: + static Ref create(const MediaTime& presentationTime, const MediaTime& decodeTime, const MediaTime& duration, SampleFlags flags) + { + return adoptRef(*new TestSample(presentationTime, decodeTime, duration, flags)); + } + + MediaTime presentationTime() const final { return m_presentationTime; } + MediaTime decodeTime() const final { return m_decodeTime; } + MediaTime duration() const final { return m_duration; } + AtomicString trackID() const final { return m_trackID; } + void setTrackID(const String& trackID) final { m_trackID = trackID; } + size_t sizeInBytes() const final { return m_sizeInBytes; } + FloatSize presentationSize() const final { return m_presentationSize; } + void offsetTimestampsBy(const MediaTime& offset) final { m_presentationTime += offset; m_decodeTime += offset; } + void setTimestamps(const MediaTime& presentationTime, const MediaTime& decodeTime) final { + m_presentationTime = presentationTime; + m_decodeTime = decodeTime; + }; + bool isDivisable() const final { return false; } + std::pair, RefPtr> divide(const MediaTime& presentationTime) final { return { }; } + Ref createNonDisplayingCopy() const final { + return create(m_presentationTime, m_decodeTime, m_duration, static_cast(m_flags | IsNonDisplaying)); + } + SampleFlags flags() const final { return m_flags; } + PlatformSample platformSample() final { return { PlatformSample::None, {nullptr}}; } + void dump(PrintStream&) const final { } + +private: + TestSample(const MediaTime& presentationTime, const MediaTime& decodeTime, const MediaTime& duration, SampleFlags flags) + : m_presentationTime(presentationTime) + , m_decodeTime(decodeTime) + , m_duration(duration) + , m_flags(flags) + { + } + + MediaTime m_presentationTime; + MediaTime m_decodeTime; + MediaTime m_duration; + FloatSize m_presentationSize; + AtomicString m_trackID; + size_t m_sizeInBytes { 0 }; + SampleFlags m_flags { None }; +}; + +class SampleMapTest : public testing::Test { +public: + void SetUp() final { + map.addSample(TestSample::create(MediaTime(0, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::IsSync)); + map.addSample(TestSample::create(MediaTime(1, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(2, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(3, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(4, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(5, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::IsSync)); + map.addSample(TestSample::create(MediaTime(6, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(7, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(8, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(9, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + // Gap at MediaTime(10, 1) -> MediaTime(11, 1); + map.addSample(TestSample::create(MediaTime(11, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::IsSync)); + map.addSample(TestSample::create(MediaTime(12, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(13, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(14, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(15, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::IsSync)); + map.addSample(TestSample::create(MediaTime(16, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(17, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(18, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + map.addSample(TestSample::create(MediaTime(19, 1), MediaTime(0, 1), MediaTime(1, 1), MediaSample::None)); + } + + SampleMap map; +}; + +TEST_F(SampleMapTest, findSampleWithPresentationTime) +{ + auto& presentationMap = map.presentationOrder(); + EXPECT_EQ(MediaTime(0, 1), presentationMap.findSampleWithPresentationTime(MediaTime(0, 1))->second->presentationTime()); + EXPECT_EQ(MediaTime(19, 1), presentationMap.findSampleWithPresentationTime(MediaTime(19, 1))->second->presentationTime()); + EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleWithPresentationTime(MediaTime(-1, 1))); + EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleWithPresentationTime(MediaTime(10, 1))); + EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleWithPresentationTime(MediaTime(20, 1))); + EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleWithPresentationTime(MediaTime(1, 2))); +} + +TEST_F(SampleMapTest, findSampleContainingPresentationTime) +{ + auto& presentationMap = map.presentationOrder(); + EXPECT_EQ(MediaTime(0, 1), presentationMap.findSampleContainingPresentationTime(MediaTime(0, 1))->second->presentationTime()); + EXPECT_EQ(MediaTime(19, 1), presentationMap.findSampleContainingPresentationTime(MediaTime(19, 1))->second->presentationTime()); + EXPECT_EQ(MediaTime(0, 1), presentationMap.findSampleContainingPresentationTime(MediaTime(1, 2))->second->presentationTime()); + EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleContainingPresentationTime(MediaTime(-1, 1))); + EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleContainingPresentationTime(MediaTime(10, 1))); + EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleContainingPresentationTime(MediaTime(20, 1))); +} + +TEST_F(SampleMapTest, findSampleStartingOnOrAfterPresentationTime) +{ + auto& presentationMap = map.presentationOrder(); + EXPECT_EQ(MediaTime(0, 1), presentationMap.findSampleStartingOnOrAfterPresentationTime(MediaTime(0, 1))->second->presentationTime()); + EXPECT_EQ(MediaTime(19, 1), presentationMap.findSampleStartingOnOrAfterPresentationTime(MediaTime(19, 1))->second->presentationTime()); + EXPECT_EQ(MediaTime(1, 1), presentationMap.findSampleStartingOnOrAfterPresentationTime(MediaTime(1, 2))->second->presentationTime()); + EXPECT_EQ(MediaTime(0, 1), presentationMap.findSampleStartingOnOrAfterPresentationTime(MediaTime(-1, 1))->second->presentationTime()); + EXPECT_EQ(MediaTime(11, 1), presentationMap.findSampleStartingOnOrAfterPresentationTime(MediaTime(10, 1))->second->presentationTime()); + EXPECT_TRUE(presentationMap.end() == presentationMap.findSampleContainingPresentationTime(MediaTime(20, 1))); +} + +TEST_F(SampleMapTest, findSamplesBetweenPresentationTimes) +{ + auto& presentationMap = map.presentationOrder(); + auto iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(0, 1), MediaTime(1, 1)); + EXPECT_EQ(MediaTime(0, 1), iterator_range.first->second->presentationTime()); + EXPECT_EQ(MediaTime(1, 1), iterator_range.second->second->presentationTime()); + + iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(1, 2), MediaTime(3, 2)); + EXPECT_EQ(MediaTime(1, 1), iterator_range.first->second->presentationTime()); + EXPECT_EQ(MediaTime(2, 1), iterator_range.second->second->presentationTime()); + + iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(9, 1), MediaTime(21, 1)); + EXPECT_EQ(MediaTime(9, 1), iterator_range.first->second->presentationTime()); + EXPECT_TRUE(presentationMap.end() == iterator_range.second); + + iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(-1, 1), MediaTime(0, 1)); + EXPECT_TRUE(presentationMap.end() == iterator_range.first); + EXPECT_TRUE(presentationMap.end() == iterator_range.second); + + iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(19, 2), MediaTime(10, 1)); + EXPECT_TRUE(presentationMap.end() == iterator_range.first); + EXPECT_TRUE(presentationMap.end() == iterator_range.second); + + iterator_range = presentationMap.findSamplesBetweenPresentationTimes(MediaTime(20, 1), MediaTime(21, 1)); + EXPECT_TRUE(presentationMap.end() == iterator_range.first); + EXPECT_TRUE(presentationMap.end() == iterator_range.second); +} + +TEST_F(SampleMapTest, findSamplesWithinPresentationRange) +{ + auto& presentationMap = map.presentationOrder(); + auto iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(0, 1), MediaTime(1, 1)); + EXPECT_EQ(MediaTime(1, 1), iterator_range.first->second->presentationTime()); + EXPECT_EQ(MediaTime(2, 1), iterator_range.second->second->presentationTime()); + + iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(1, 2), MediaTime(3, 2)); + EXPECT_EQ(MediaTime(1, 1), iterator_range.first->second->presentationTime()); + EXPECT_EQ(MediaTime(2, 1), iterator_range.second->second->presentationTime()); + + iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(9, 1), MediaTime(21, 1)); + EXPECT_EQ(MediaTime(11, 1), iterator_range.first->second->presentationTime()); + EXPECT_TRUE(presentationMap.end() == iterator_range.second); + + iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(-1, 1), MediaTime(0, 1)); + EXPECT_EQ(MediaTime(0, 1), iterator_range.first->second->presentationTime()); + EXPECT_EQ(MediaTime(1, 1), iterator_range.second->second->presentationTime()); + + iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(10, 1), MediaTime(21, 2)); + EXPECT_TRUE(presentationMap.end() == iterator_range.first); + EXPECT_TRUE(presentationMap.end() == iterator_range.second); + + iterator_range = presentationMap.findSamplesWithinPresentationRange(MediaTime(20, 1), MediaTime(21, 1)); + EXPECT_TRUE(presentationMap.end() == iterator_range.first); + EXPECT_TRUE(presentationMap.end() == iterator_range.second); +} + +TEST_F(SampleMapTest, reverseFindSampleBeforePresentationTime) +{ + auto& presentationMap = map.presentationOrder(); + EXPECT_EQ(MediaTime(0, 1), presentationMap.reverseFindSampleBeforePresentationTime(MediaTime(0, 1))->second->presentationTime()); + EXPECT_EQ(MediaTime(9, 1), presentationMap.reverseFindSampleBeforePresentationTime(MediaTime(10, 1))->second->presentationTime()); + EXPECT_EQ(MediaTime(19, 1), presentationMap.reverseFindSampleBeforePresentationTime(MediaTime(19, 1))->second->presentationTime()); + EXPECT_EQ(MediaTime(19, 1), presentationMap.reverseFindSampleBeforePresentationTime(MediaTime(21, 1))->second->presentationTime()); + EXPECT_TRUE(presentationMap.rend() == presentationMap.reverseFindSampleBeforePresentationTime(MediaTime(-1, 1))); +} + +} + +#endif // ENABLE(MEDIA_SOURCE) diff --git a/Tools/TestWebKitAPI/Tests/WebCore/SecurityOrigin.cpp b/Tools/TestWebKitAPI/Tests/WebCore/SecurityOrigin.cpp new file mode 100644 index 000000000..9f9cc9cca --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/SecurityOrigin.cpp @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2016 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WTFStringUtilities.h" +#include +#include +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +class SecurityOriginTest : public testing::Test { +public: + void SetUp() final { + WTF::initializeMainThread(); + + // create temp file + PlatformFileHandle handle; + m_tempFilePath = openTemporaryFile("tempTestFile", handle); + closeFile(handle); + + m_spaceContainingFilePath = openTemporaryFile("temp Empty Test File", handle); + closeFile(handle); + + m_bangContainingFilePath = openTemporaryFile("temp!Empty!Test!File", handle); + closeFile(handle); + + m_quoteContainingFilePath = openTemporaryFile("temp\"Empty\"TestFile", handle); + closeFile(handle); + } + + void TearDown() override + { + deleteFile(m_tempFilePath); + deleteFile(m_spaceContainingFilePath); + deleteFile(m_bangContainingFilePath); + deleteFile(m_quoteContainingFilePath); + } + + const String& tempFilePath() { return m_tempFilePath; } + const String& spaceContainingFilePath() { return m_spaceContainingFilePath; } + const String& bangContainingFilePath() { return m_bangContainingFilePath; } + const String& quoteContainingFilePath() { return m_quoteContainingFilePath; } + +private: + String m_tempFilePath; + String m_spaceContainingFilePath; + String m_bangContainingFilePath; + String m_quoteContainingFilePath; +}; + +TEST_F(SecurityOriginTest, SecurityOriginConstructors) +{ + Ref o1 = SecurityOrigin::create("http", "example.com", std::optional(80)); + Ref o2 = SecurityOrigin::create("http", "example.com", std::optional()); + Ref o3 = SecurityOrigin::createFromString("http://example.com"); + Ref o4 = SecurityOrigin::createFromString("http://example.com:80"); + Ref o5 = SecurityOrigin::create(URL(URL(), "http://example.com")); + Ref o6 = SecurityOrigin::create(URL(URL(), "http://example.com:80")); + + EXPECT_EQ(String("http"), o1->protocol()); + EXPECT_EQ(String("http"), o2->protocol()); + EXPECT_EQ(String("http"), o3->protocol()); + EXPECT_EQ(String("http"), o4->protocol()); + EXPECT_EQ(String("http"), o5->protocol()); + EXPECT_EQ(String("http"), o6->protocol()); + + EXPECT_EQ(String("example.com"), o1->host()); + EXPECT_EQ(String("example.com"), o2->host()); + EXPECT_EQ(String("example.com"), o3->host()); + EXPECT_EQ(String("example.com"), o4->host()); + EXPECT_EQ(String("example.com"), o5->host()); + EXPECT_EQ(String("example.com"), o6->host()); + + EXPECT_FALSE(o1->port()); + EXPECT_FALSE(o2->port()); + EXPECT_FALSE(o3->port()); + EXPECT_FALSE(o4->port()); + EXPECT_FALSE(o5->port()); + EXPECT_FALSE(o6->port()); + + EXPECT_EQ("http://example.com", o1->toString()); + EXPECT_EQ("http://example.com", o2->toString()); + EXPECT_EQ("http://example.com", o3->toString()); + EXPECT_EQ("http://example.com", o4->toString()); + EXPECT_EQ("http://example.com", o5->toString()); + EXPECT_EQ("http://example.com", o6->toString()); + + EXPECT_TRUE(o1->isSameOriginAs(o2.get())); + EXPECT_TRUE(o1->isSameOriginAs(o3.get())); + EXPECT_TRUE(o1->isSameOriginAs(o4.get())); + EXPECT_TRUE(o1->isSameOriginAs(o5.get())); + EXPECT_TRUE(o1->isSameOriginAs(o6.get())); +} + +TEST_F(SecurityOriginTest, SecurityOriginFileBasedConstructors) +{ + auto tempFileOrigin = SecurityOrigin::create(URL(URL(), "file:///" + tempFilePath())); + auto spaceContainingOrigin = SecurityOrigin::create(URL(URL(), "file:///" + spaceContainingFilePath())); + auto bangContainingOrigin = SecurityOrigin::create(URL(URL(), "file:///" + bangContainingFilePath())); + auto quoteContainingOrigin = SecurityOrigin::create(URL(URL(), "file:///" + quoteContainingFilePath())); + + EXPECT_EQ(String("file"), tempFileOrigin->protocol()); + EXPECT_EQ(String("file"), spaceContainingOrigin->protocol()); + EXPECT_EQ(String("file"), bangContainingOrigin->protocol()); + EXPECT_EQ(String("file"), quoteContainingOrigin->protocol()); + + EXPECT_TRUE(tempFileOrigin->isSameOriginAs(spaceContainingOrigin.get())); + EXPECT_TRUE(tempFileOrigin->isSameOriginAs(bangContainingOrigin.get())); + EXPECT_TRUE(tempFileOrigin->isSameOriginAs(quoteContainingOrigin.get())); + + EXPECT_TRUE(tempFileOrigin->isSameSchemeHostPort(spaceContainingOrigin.get())); + EXPECT_TRUE(tempFileOrigin->isSameSchemeHostPort(bangContainingOrigin.get())); + EXPECT_TRUE(tempFileOrigin->isSameSchemeHostPort(quoteContainingOrigin.get())); + + EXPECT_TRUE(tempFileOrigin->canAccess(spaceContainingOrigin.get())); + EXPECT_TRUE(tempFileOrigin->canAccess(bangContainingOrigin.get())); + EXPECT_TRUE(tempFileOrigin->canAccess(quoteContainingOrigin.get())); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/SharedBuffer.cpp b/Tools/TestWebKitAPI/Tests/WebCore/SharedBuffer.cpp new file mode 100644 index 000000000..543ef2316 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/SharedBuffer.cpp @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2016-2017 Apple Inc. All rights reserved. + * Copyright (C) 2015 Canon Inc. All rights reserved. + * Copyright (C) 2013 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "SharedBufferTest.h" +#include "Test.h" +#include +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +TEST_F(SharedBufferTest, createWithContentsOfMissingFile) +{ + RefPtr buffer = SharedBuffer::createWithContentsOfFile(String("not_existing_file")); + ASSERT_NULL(buffer); +} + +TEST_F(SharedBufferTest, createWithContentsOfExistingFile) +{ + RefPtr buffer = SharedBuffer::createWithContentsOfFile(tempFilePath()); + ASSERT_NOT_NULL(buffer); + EXPECT_TRUE(buffer->size() == strlen(SharedBufferTest::testData())); + EXPECT_TRUE(String(SharedBufferTest::testData()) == String(buffer->data(), buffer->size())); +} + +TEST_F(SharedBufferTest, createWithContentsOfExistingEmptyFile) +{ + RefPtr buffer = SharedBuffer::createWithContentsOfFile(tempEmptyFilePath()); + ASSERT_NOT_NULL(buffer); + EXPECT_TRUE(buffer->isEmpty()); +} + +TEST_F(SharedBufferTest, copyBufferCreatedWithContentsOfExistingFile) +{ + RefPtr buffer = SharedBuffer::createWithContentsOfFile(tempFilePath()); + ASSERT_NOT_NULL(buffer); + RefPtr copy = buffer->copy(); + EXPECT_GT(buffer->size(), 0U); + EXPECT_TRUE(buffer->size() == copy->size()); + EXPECT_TRUE(!memcmp(buffer->data(), copy->data(), buffer->size())); +} + +TEST_F(SharedBufferTest, clearBufferCreatedWithContentsOfExistingFile) +{ + RefPtr buffer = SharedBuffer::createWithContentsOfFile(tempFilePath()); + ASSERT_NOT_NULL(buffer); + buffer->clear(); + EXPECT_TRUE(!buffer->size()); + EXPECT_TRUE(!buffer->data()); +} + +TEST_F(SharedBufferTest, appendBufferCreatedWithContentsOfExistingFile) +{ + RefPtr buffer = SharedBuffer::createWithContentsOfFile(tempFilePath()); + ASSERT_NOT_NULL(buffer); + buffer->append("a", 1); + EXPECT_TRUE(buffer->size() == (strlen(SharedBufferTest::testData()) + 1)); + EXPECT_TRUE(!memcmp(buffer->data(), SharedBufferTest::testData(), strlen(SharedBufferTest::testData()))); + EXPECT_EQ('a', buffer->data()[strlen(SharedBufferTest::testData())]); +} + +TEST_F(SharedBufferTest, createArrayBuffer) +{ + char testData0[] = "Hello"; + char testData1[] = "World"; + char testData2[] = "Goodbye"; + RefPtr sharedBuffer = SharedBuffer::create(testData0, strlen(testData0)); + sharedBuffer->append(testData1, strlen(testData1)); + sharedBuffer->append(testData2, strlen(testData2)); + RefPtr arrayBuffer = sharedBuffer->createArrayBuffer(); + char expectedConcatenation[] = "HelloWorldGoodbye"; + ASSERT_EQ(strlen(expectedConcatenation), arrayBuffer->byteLength()); + EXPECT_EQ(0, memcmp(expectedConcatenation, arrayBuffer->data(), strlen(expectedConcatenation))); +} + +TEST_F(SharedBufferTest, createArrayBufferLargeSegments) +{ + Vector vector0(0x4000, 'a'); + Vector vector1(0x4000, 'b'); + Vector vector2(0x4000, 'c'); + + RefPtr sharedBuffer = SharedBuffer::adoptVector(vector0); + sharedBuffer->append(vector1); + sharedBuffer->append(vector2); + RefPtr arrayBuffer = sharedBuffer->createArrayBuffer(); + ASSERT_EQ(0x4000U + 0x4000U + 0x4000U, arrayBuffer->byteLength()); + int position = 0; + for (int i = 0; i < 0x4000; ++i) { + EXPECT_EQ('a', static_cast(arrayBuffer->data())[position]); + ++position; + } + for (int i = 0; i < 0x4000; ++i) { + EXPECT_EQ('b', static_cast(arrayBuffer->data())[position]); + ++position; + } + for (int i = 0; i < 0x4000; ++i) { + EXPECT_EQ('c', static_cast(arrayBuffer->data())[position]); + ++position; + } +} + +TEST_F(SharedBufferTest, copy) +{ + char testData[] = "Habitasse integer eros tincidunt a scelerisque! Enim elit? Scelerisque magnis," + "et montes ultrices tristique a! Pid. Velit turpis, dapibus integer rhoncus sociis amet facilisis," + "adipiscing pulvinar nascetur magnis tempor sit pulvinar, massa urna enim porttitor sociis sociis proin enim?" + "Lectus, platea dolor, integer a. A habitasse hac nunc, nunc, nec placerat vut in sit nunc nec, sed. Sociis," + "vut! Hac, velit rhoncus facilisis. Rhoncus et, enim, sed et in tristique nunc montes," + "natoque nunc sagittis elementum parturient placerat dolor integer? Pulvinar," + "magnis dignissim porttitor ac pulvinar mid tempor. A risus sed mid! Magnis elit duis urna," + "cras massa, magna duis. Vut magnis pid a! Penatibus aliquet porttitor nunc, adipiscing massa odio lundium," + "risus elementum ac turpis massa pellentesque parturient augue. Purus amet turpis pid aliquam?" + "Dolor est tincidunt? Dolor? Dignissim porttitor sit in aliquam! Tincidunt, non nunc, rhoncus dictumst!" + "Porta augue etiam. Cursus augue nunc lacus scelerisque. Rhoncus lectus, integer hac, nec pulvinar augue massa," + "integer amet nisi facilisis? A! A, enim velit pulvinar elit in non scelerisque in et ultricies amet est!" + "in porttitor montes lorem et, hac aliquet pellentesque a sed? Augue mid purus ridiculus vel dapibus," + "sagittis sed, tortor auctor nascetur rhoncus nec, rhoncus, magna integer. Sit eu massa vut?" + "Porta augue porttitor elementum, enim, rhoncus pulvinar duis integer scelerisque rhoncus natoque," + "mattis dignissim massa ac pulvinar urna, nunc ut. Sagittis, aliquet penatibus proin lorem, pulvinar lectus," + "augue proin! Ac, arcu quis. Placerat habitasse, ridiculus ridiculus."; + unsigned length = strlen(testData); + RefPtr sharedBuffer = SharedBuffer::create(testData, length); + sharedBuffer->append(testData, length); + sharedBuffer->append(testData, length); + sharedBuffer->append(testData, length); + // sharedBuffer must contain data more than segmentSize (= 0x1000) to check copy(). + ASSERT_EQ(length * 4, sharedBuffer->size()); + RefPtr clone = sharedBuffer->copy(); + ASSERT_EQ(length * 4, clone->size()); + ASSERT_EQ(0, memcmp(clone->data(), sharedBuffer->data(), clone->size())); + clone->append(testData, length); + ASSERT_EQ(length * 5, clone->size()); +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/SharedBufferTest.cpp b/Tools/TestWebKitAPI/Tests/WebCore/SharedBufferTest.cpp new file mode 100644 index 000000000..a297a55b8 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/SharedBufferTest.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SharedBufferTest.h" + +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +void SharedBufferTest::SetUp() +{ + WTF::initializeMainThread(); + + // create temp file + PlatformFileHandle handle; + m_tempFilePath = openTemporaryFile("tempTestFile", handle); + writeToFile(handle, testData(), strlen(testData())); + closeFile(handle); + + m_tempEmptyFilePath = openTemporaryFile("tempEmptyTestFile", handle); + closeFile(handle); +} + +void SharedBufferTest::TearDown() +{ + deleteFile(m_tempFilePath); + deleteFile(m_tempEmptyFilePath); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/SharedBufferTest.h b/Tools/TestWebKitAPI/Tests/WebCore/SharedBufferTest.h new file mode 100644 index 000000000..e5d90935d --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/SharedBufferTest.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2017 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace TestWebKitAPI { + +class SharedBufferTest : public testing::Test { +public: + void SetUp() override; + void TearDown() override; + + static const char* testData() { return "This is a test"; } + const String& tempFilePath() { return m_tempFilePath; } + const String& tempEmptyFilePath() { return m_tempEmptyFilePath; } + +private: + String m_tempFilePath; + String m_tempEmptyFilePath; +}; + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/TimeRanges.cpp b/Tools/TestWebKitAPI/Tests/WebCore/TimeRanges.cpp new file mode 100644 index 000000000..c6dca0115 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/TimeRanges.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (c) 2013, Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +static std::string ToString(const TimeRanges& ranges) +{ + std::stringstream ss; + ss << "{"; + for (unsigned i = 0; i < ranges.length(); ++i) + ss << " [" << ranges.start(i).releaseReturnValue() << "," << ranges.end(i).releaseReturnValue() << ")"; + ss << " }"; + + return ss.str(); +} + +#define ASSERT_RANGE(expected, range) EXPECT_EQ(expected, ToString(*range)) + +TEST(TimeRanges, Empty) +{ + ASSERT_RANGE("{ }", TimeRanges::create().ptr()); +} + +TEST(TimeRanges, SingleRange) +{ + ASSERT_RANGE("{ [1,2) }", TimeRanges::create(1, 2).ptr()); +} + +TEST(TimeRanges, AddOrder) +{ + RefPtr rangeA = TimeRanges::create(); + RefPtr rangeB = TimeRanges::create(); + + rangeA->add(0, 2); + rangeA->add(3, 4); + rangeA->add(5, 100); + + std::string expected = "{ [0,2) [3,4) [5,100) }"; + ASSERT_RANGE(expected, rangeA); + + // Add the values in rangeA to rangeB in reverse order. + for (int i = rangeA->length() - 1; i >= 0; --i) + rangeB->add(rangeA->start(i).releaseReturnValue(), rangeA->end(i).releaseReturnValue()); + + ASSERT_RANGE(expected, rangeB); +} + +TEST(TimeRanges, OverlappingAdds) +{ + RefPtr ranges = TimeRanges::create(); + + ranges->add(0, 2); + ranges->add(10, 11); + ASSERT_RANGE("{ [0,2) [10,11) }", ranges); + + ranges->add(0, 2); + ASSERT_RANGE("{ [0,2) [10,11) }", ranges); + + ranges->add(2, 3); + ASSERT_RANGE("{ [0,3) [10,11) }", ranges); + + ranges->add(2, 6); + ASSERT_RANGE("{ [0,6) [10,11) }", ranges); + + ranges->add(9, 10); + ASSERT_RANGE("{ [0,6) [9,11) }", ranges); + + ranges->add(8, 10); + ASSERT_RANGE("{ [0,6) [8,11) }", ranges); + + ranges->add(-1, 7); + ASSERT_RANGE("{ [-1,7) [8,11) }", ranges); + + ranges->add(6, 9); + ASSERT_RANGE("{ [-1,11) }", ranges); +} + +TEST(TimeRanges, IntersectWith_Self) +{ + RefPtr ranges = TimeRanges::create(0, 2); + + ASSERT_RANGE("{ [0,2) }", ranges); + + ranges->intersectWith(*ranges.get()); + + ASSERT_RANGE("{ [0,2) }", ranges); +} + +TEST(TimeRanges, IntersectWith_IdenticalRange) +{ + RefPtr rangesA = TimeRanges::create(0, 2); + RefPtr rangesB = rangesA->copy(); + + ASSERT_RANGE("{ [0,2) }", rangesA); + ASSERT_RANGE("{ [0,2) }", rangesB); + + rangesA->intersectWith(*rangesB.get()); + + ASSERT_RANGE("{ [0,2) }", rangesA); + ASSERT_RANGE("{ [0,2) }", rangesB); +} + +TEST(TimeRanges, IntersectWith_Empty) +{ + RefPtr rangesA = TimeRanges::create(0, 2); + RefPtr rangesB = TimeRanges::create(); + + ASSERT_RANGE("{ [0,2) }", rangesA); + ASSERT_RANGE("{ }", rangesB); + + rangesA->intersectWith(*rangesB.get()); + + ASSERT_RANGE("{ }", rangesA); + ASSERT_RANGE("{ }", rangesB); +} + +TEST(TimeRanges, IntersectWith_DisjointRanges1) +{ + + RefPtr rangesA = TimeRanges::create(); + RefPtr rangesB = TimeRanges::create(); + + rangesA->add(0, 1); + rangesA->add(4, 5); + + rangesB->add(2, 3); + rangesB->add(6, 7); + + ASSERT_RANGE("{ [0,1) [4,5) }", rangesA); + ASSERT_RANGE("{ [2,3) [6,7) }", rangesB); + + rangesA->intersectWith(*rangesB.get()); + + ASSERT_RANGE("{ }", rangesA); + ASSERT_RANGE("{ [2,3) [6,7) }", rangesB); +} + +TEST(TimeRanges, IntersectWith_DisjointRanges2) +{ + RefPtr rangesA = TimeRanges::create(); + RefPtr rangesB = TimeRanges::create(); + + rangesA->add(0, 1); + rangesA->add(4, 5); + + rangesB->add(1, 4); + rangesB->add(5, 7); + + ASSERT_RANGE("{ [0,1) [4,5) }", rangesA); + ASSERT_RANGE("{ [1,4) [5,7) }", rangesB); + + rangesA->intersectWith(*rangesB.get()); + + ASSERT_RANGE("{ }", rangesA); + ASSERT_RANGE("{ [1,4) [5,7) }", rangesB); +} + +TEST(TimeRanges, IntersectWith_CompleteOverlap1) +{ + RefPtr rangesA = TimeRanges::create(); + RefPtr rangesB = TimeRanges::create(); + + rangesA->add(1, 3); + rangesA->add(4, 5); + rangesA->add(6, 9); + + rangesB->add(0, 10); + + ASSERT_RANGE("{ [1,3) [4,5) [6,9) }", rangesA); + ASSERT_RANGE("{ [0,10) }", rangesB); + + rangesA->intersectWith(*rangesB.get()); + + ASSERT_RANGE("{ [1,3) [4,5) [6,9) }", rangesA); + ASSERT_RANGE("{ [0,10) }", rangesB); +} + +TEST(TimeRanges, IntersectWith_CompleteOverlap2) +{ + RefPtr rangesA = TimeRanges::create(); + RefPtr rangesB = TimeRanges::create(); + + rangesA->add(1, 3); + rangesA->add(4, 5); + rangesA->add(6, 9); + + rangesB->add(1, 9); + + ASSERT_RANGE("{ [1,3) [4,5) [6,9) }", rangesA); + ASSERT_RANGE("{ [1,9) }", rangesB); + + rangesA->intersectWith(*rangesB.get()); + + ASSERT_RANGE("{ [1,3) [4,5) [6,9) }", rangesA); + ASSERT_RANGE("{ [1,9) }", rangesB); +} + +TEST(TimeRanges, IntersectWith_Gaps1) +{ + RefPtr rangesA = TimeRanges::create(); + RefPtr rangesB = TimeRanges::create(); + + rangesA->add(0, 2); + rangesA->add(4, 6); + + rangesB->add(1, 5); + + ASSERT_RANGE("{ [0,2) [4,6) }", rangesA); + ASSERT_RANGE("{ [1,5) }", rangesB); + + rangesA->intersectWith(*rangesB.get()); + + ASSERT_RANGE("{ [1,2) [4,5) }", rangesA); + ASSERT_RANGE("{ [1,5) }", rangesB); +} + +TEST(TimeRanges, IntersectWith_Gaps2) +{ + RefPtr rangesA = TimeRanges::create(); + RefPtr rangesB = TimeRanges::create(); + + rangesA->add(0, 2); + rangesA->add(4, 6); + rangesA->add(8, 10); + + rangesB->add(1, 9); + + ASSERT_RANGE("{ [0,2) [4,6) [8,10) }", rangesA); + ASSERT_RANGE("{ [1,9) }", rangesB); + + rangesA->intersectWith(*rangesB.get()); + + ASSERT_RANGE("{ [1,2) [4,6) [8,9) }", rangesA); + ASSERT_RANGE("{ [1,9) }", rangesB); +} + +TEST(TimeRanges, IntersectWith_Gaps3) +{ + RefPtr rangesA = TimeRanges::create(); + RefPtr rangesB = TimeRanges::create(); + + rangesA->add(0, 2); + rangesA->add(4, 7); + rangesA->add(8, 10); + + rangesB->add(1, 5); + rangesB->add(6, 9); + + ASSERT_RANGE("{ [0,2) [4,7) [8,10) }", rangesA); + ASSERT_RANGE("{ [1,5) [6,9) }", rangesB); + + rangesA->intersectWith(*rangesB.get()); + + ASSERT_RANGE("{ [1,2) [4,5) [6,7) [8,9) }", rangesA); + ASSERT_RANGE("{ [1,5) [6,9) }", rangesB); +} + +} + diff --git a/Tools/TestWebKitAPI/Tests/WebCore/TransformationMatrix.cpp b/Tools/TestWebKitAPI/Tests/WebCore/TransformationMatrix.cpp new file mode 100644 index 000000000..9315b75a5 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/TransformationMatrix.cpp @@ -0,0 +1,1317 @@ +/* + * Copyright (c) 2013, Google Inc. All rights reserved. + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include + +#if USE(CG) +#include +#endif + +#if USE(CA) +#include +#endif + +#if PLATFORM(WIN) +#include +#endif + +namespace TestWebKitAPI { + +static void testIdentity(const WebCore::TransformationMatrix& transform) +{ + EXPECT_DOUBLE_EQ(1.0, transform.m11()); + EXPECT_DOUBLE_EQ(0.0, transform.m12()); + EXPECT_DOUBLE_EQ(0.0, transform.m13()); + EXPECT_DOUBLE_EQ(0.0, transform.m14()); + EXPECT_DOUBLE_EQ(0.0, transform.m21()); + EXPECT_DOUBLE_EQ(1.0, transform.m22()); + EXPECT_DOUBLE_EQ(0.0, transform.m23()); + EXPECT_DOUBLE_EQ(0.0, transform.m24()); + EXPECT_DOUBLE_EQ(0.0, transform.m31()); + EXPECT_DOUBLE_EQ(0.0, transform.m32()); + EXPECT_DOUBLE_EQ(1.0, transform.m33()); + EXPECT_DOUBLE_EQ(0.0, transform.m34()); + EXPECT_DOUBLE_EQ(0.0, transform.m41()); + EXPECT_DOUBLE_EQ(0.0, transform.m42()); + EXPECT_DOUBLE_EQ(0.0, transform.m43()); + EXPECT_DOUBLE_EQ(1.0, transform.m44()); +} + +static void testGetAndSet(WebCore::TransformationMatrix& transform) +{ + transform.setA(1.1); + EXPECT_DOUBLE_EQ(1.1, transform.a()); + transform.setB(2.2); + EXPECT_DOUBLE_EQ(2.2, transform.b()); + transform.setC(3.3); + EXPECT_DOUBLE_EQ(3.3, transform.c()); + transform.setD(4.4); + EXPECT_DOUBLE_EQ(4.4, transform.d()); + transform.setE(5.5); + EXPECT_DOUBLE_EQ(5.5, transform.e()); + transform.setF(6.6); + EXPECT_DOUBLE_EQ(6.6, transform.f()); + + transform.setM11(1.1); + EXPECT_DOUBLE_EQ(1.1, transform.m11()); + transform.setM12(2.2); + EXPECT_DOUBLE_EQ(2.2, transform.m12()); + transform.setM13(3.3); + EXPECT_DOUBLE_EQ(3.3, transform.m13()); + transform.setM14(4.4); + EXPECT_DOUBLE_EQ(4.4, transform.m14()); + transform.setM21(5.5); + EXPECT_DOUBLE_EQ(5.5, transform.m21()); + transform.setM22(6.6); + EXPECT_DOUBLE_EQ(6.6, transform.m22()); + transform.setM23(7.7); + EXPECT_DOUBLE_EQ(7.7, transform.m23()); + transform.setM24(8.8); + EXPECT_DOUBLE_EQ(8.8, transform.m24()); + transform.setM31(9.9); + EXPECT_DOUBLE_EQ(9.9, transform.m31()); + transform.setM32(10.10); + EXPECT_DOUBLE_EQ(10.10, transform.m32()); + transform.setM33(11.11); + EXPECT_DOUBLE_EQ(11.11, transform.m33()); + transform.setM34(12.12); + EXPECT_DOUBLE_EQ(12.12, transform.m34()); + transform.setM41(13.13); + EXPECT_DOUBLE_EQ(13.13, transform.m41()); + transform.setM42(14.14); + EXPECT_DOUBLE_EQ(14.14, transform.m42()); + transform.setM43(15.15); + EXPECT_DOUBLE_EQ(15.15, transform.m43()); + transform.setM44(16.16); + EXPECT_DOUBLE_EQ(16.16, transform.m44()); +} + +TEST(TransformationMatrix, DefaultConstruction) +{ + WebCore::TransformationMatrix test; + + testIdentity(test); + + testGetAndSet(test); + + ASSERT_FALSE(test.isIdentity()); +} + +static void testAffineLikeConstruction(const WebCore::TransformationMatrix& transform) +{ + EXPECT_DOUBLE_EQ(6.0, transform.a()); + EXPECT_DOUBLE_EQ(5.0, transform.b()); + EXPECT_DOUBLE_EQ(4.0, transform.c()); + EXPECT_DOUBLE_EQ(3.0, transform.d()); + EXPECT_DOUBLE_EQ(2.0, transform.e()); + EXPECT_DOUBLE_EQ(1.0, transform.f()); +} + +static void testValueConstruction(const WebCore::TransformationMatrix& transform) +{ + EXPECT_DOUBLE_EQ(16.0, transform.m11()); + EXPECT_DOUBLE_EQ(15.0, transform.m12()); + EXPECT_DOUBLE_EQ(14.0, transform.m13()); + EXPECT_DOUBLE_EQ(13.0, transform.m14()); + EXPECT_DOUBLE_EQ(12.0, transform.m21()); + EXPECT_DOUBLE_EQ(11.0, transform.m22()); + EXPECT_DOUBLE_EQ(10.0, transform.m23()); + EXPECT_DOUBLE_EQ(9.0, transform.m24()); + EXPECT_DOUBLE_EQ(8.0, transform.m31()); + EXPECT_DOUBLE_EQ(7.0, transform.m32()); + EXPECT_DOUBLE_EQ(6.0, transform.m33()); + EXPECT_DOUBLE_EQ(5.0, transform.m34()); + EXPECT_DOUBLE_EQ(4.0, transform.m41()); + EXPECT_DOUBLE_EQ(3.0, transform.m42()); + EXPECT_DOUBLE_EQ(2.0, transform.m43()); + EXPECT_DOUBLE_EQ(1.0, transform.m44()); + ASSERT_FALSE(transform.isAffine()); +} + +TEST(TransformationMatrix, ValueConstruction) +{ + WebCore::TransformationMatrix test1(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testAffineLikeConstruction(test1); + + ASSERT_FALSE(test1.isIdentity()); + ASSERT_TRUE(test1.isAffine()); + + WebCore::TransformationMatrix test2(16.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0, + 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test2); + + testGetAndSet(test2); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_FALSE(test2.isAffine()); + + test2.makeIdentity(); + + testIdentity(test2); + + ASSERT_TRUE(test2.isIdentity()); +} + +#if USE(CG) +TEST(TransformationMatrix, CGAffineTransformConstruction) +{ + CGAffineTransform cgTransform = CGAffineTransformMake(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::TransformationMatrix test(cgTransform); + + testAffineLikeConstruction(test); + testGetAndSet(test); + + ASSERT_FALSE(test.isIdentity()); +} +#endif + +#if PLATFORM(WIN) +TEST(TransformationMatrix, D2D1MatrixConstruction) +{ + D2D1_MATRIX_3X2_F d2dTransform = D2D1::Matrix3x2F(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::TransformationMatrix test(d2dTransform); + + testAffineLikeConstruction(test); + testGetAndSet(test); + + ASSERT_FALSE(test.isIdentity()); +} +#endif + +TEST(TransformationMatrix, AffineTransformConstruction) +{ + WebCore::AffineTransform affineTransform(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::TransformationMatrix test(affineTransform); + + testAffineLikeConstruction(test); + testGetAndSet(test); + + ASSERT_FALSE(test.isIdentity()); +} + +TEST(TransformationMatrix, TransformMatrixConstruction) +{ + WebCore::TransformationMatrix matrix(16.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0, + 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::TransformationMatrix test(matrix); + + testValueConstruction(test); +} + +TEST(TransformationMatrix, Assignment) +{ + WebCore::TransformationMatrix test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::TransformationMatrix matrix(16.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0, + 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testAffineLikeConstruction(test); + + test = matrix; + + testValueConstruction(test); +} + +TEST(TransformationMatrix, Identity) +{ + WebCore::TransformationMatrix test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + ASSERT_FALSE(test.isIdentity()); + ASSERT_FALSE(test.isIdentityOrTranslation()); + + test.makeIdentity(); + + ASSERT_TRUE(test.isIdentity()); + ASSERT_TRUE(test.isIdentityOrTranslation()); + + testIdentity(test); +} + +static void testAffineVersion(WebCore::TransformationMatrix affineTransformMatrix) +{ + ASSERT_FALSE(affineTransformMatrix.isIdentity()); + ASSERT_FALSE(affineTransformMatrix.isIdentityOrTranslation()); + ASSERT_TRUE(affineTransformMatrix.isAffine()); + + EXPECT_DOUBLE_EQ(16.0, affineTransformMatrix.m11()); + EXPECT_DOUBLE_EQ(15.0, affineTransformMatrix.m12()); + EXPECT_DOUBLE_EQ(0.0, affineTransformMatrix.m13()); + EXPECT_DOUBLE_EQ(0.0, affineTransformMatrix.m14()); + EXPECT_DOUBLE_EQ(12.0, affineTransformMatrix.m21()); + EXPECT_DOUBLE_EQ(11.0, affineTransformMatrix.m22()); + EXPECT_DOUBLE_EQ(0.0, affineTransformMatrix.m23()); + EXPECT_DOUBLE_EQ(0.0, affineTransformMatrix.m24()); + EXPECT_DOUBLE_EQ(0.0, affineTransformMatrix.m31()); + EXPECT_DOUBLE_EQ(0.0, affineTransformMatrix.m32()); + EXPECT_DOUBLE_EQ(1.0, affineTransformMatrix.m33()); + EXPECT_DOUBLE_EQ(0.0, affineTransformMatrix.m34()); + EXPECT_DOUBLE_EQ(4.0, affineTransformMatrix.m41()); + EXPECT_DOUBLE_EQ(3.0, affineTransformMatrix.m42()); + EXPECT_DOUBLE_EQ(0.0, affineTransformMatrix.m43()); + EXPECT_DOUBLE_EQ(1.0, affineTransformMatrix.m44()); +} + +TEST(TransformationMatrix, AffineVersion) +{ + WebCore::TransformationMatrix test(16.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0, + 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + ASSERT_FALSE(test.isIdentity()); + ASSERT_FALSE(test.isIdentityOrTranslation()); + ASSERT_FALSE(test.isAffine()); + + auto affineCopy = test.toAffineTransform(); + + testAffineVersion(affineCopy); + + test.makeAffine(); + + testAffineVersion(test); +} + +TEST(TransformationMatrix, MapFloatPoint) +{ + WebCore::TransformationMatrix test; + WebCore::FloatPoint point(100.0f, 50.0f); + + auto mappedPoint = test.mapPoint(point); + + ASSERT_FLOAT_EQ(100.0f, mappedPoint.x()); + ASSERT_FLOAT_EQ(50.0f, mappedPoint.y()); + + test.setD(2.0); + + auto mappedPoint2 = test.mapPoint(point); + + ASSERT_FLOAT_EQ(100.0f, mappedPoint2.x()); + ASSERT_FLOAT_EQ(100.0f, mappedPoint2.y()); + + test.setA(0.5); + + auto mappedPoint3 = test.mapPoint(point); + + ASSERT_FLOAT_EQ(50.0f, mappedPoint3.x()); + ASSERT_FLOAT_EQ(100.0f, mappedPoint3.y()); + + test.setM22(0.5); + + auto mappedPoint4 = test.mapPoint(point); + + ASSERT_FLOAT_EQ(50.0f, mappedPoint4.x()); + ASSERT_FLOAT_EQ(25.0f, mappedPoint4.y()); + + test.setM11(2.0); + + auto mappedPoint5 = test.mapPoint(point); + + ASSERT_FLOAT_EQ(200.0f, mappedPoint5.x()); + ASSERT_FLOAT_EQ(25.0f, mappedPoint5.y()); +} + +TEST(TransformationMatrix, MapIntPoint) +{ + WebCore::TransformationMatrix test; + WebCore::IntPoint point(100, 50); + + auto mappedPoint = test.mapPoint(point); + + ASSERT_EQ(100, mappedPoint.x()); + ASSERT_EQ(50, mappedPoint.y()); + + test.setD(2.0); + + auto mappedPoint2 = test.mapPoint(point); + + ASSERT_EQ(100, mappedPoint2.x()); + ASSERT_EQ(100, mappedPoint2.y()); + + test.setA(0.5); + + auto mappedPoint3 = test.mapPoint(point); + + ASSERT_EQ(50, mappedPoint3.x()); + ASSERT_EQ(100, mappedPoint3.y()); + + test.setM22(0.5); + + auto mappedPoint4 = test.mapPoint(point); + + ASSERT_EQ(50, mappedPoint4.x()); + ASSERT_EQ(25, mappedPoint4.y()); + + test.setM11(2.0); + + auto mappedPoint5 = test.mapPoint(point); + + ASSERT_EQ(200, mappedPoint5.x()); + ASSERT_EQ(25, mappedPoint5.y()); +} + +TEST(TransformationMatrix, MapIntRect) +{ + WebCore::TransformationMatrix test; + WebCore::IntRect rect(10, 20, 200, 300); + + auto mappedRect = test.mapRect(rect); + + ASSERT_EQ(10, mappedRect.x()); + ASSERT_EQ(20, mappedRect.y()); + ASSERT_EQ(200, mappedRect.width()); + ASSERT_EQ(300, mappedRect.height()); + + test.setD(2.0); + + auto mappedRect2 = test.mapRect(rect); + + ASSERT_EQ(10, mappedRect2.x()); + ASSERT_EQ(40, mappedRect2.y()); + ASSERT_EQ(200, mappedRect2.width()); + ASSERT_EQ(600, mappedRect2.height()); + + test.setA(0.5); + + auto mappedRect3 = test.mapRect(rect); + + ASSERT_EQ(5, mappedRect3.x()); + ASSERT_EQ(40, mappedRect3.y()); + ASSERT_EQ(100, mappedRect3.width()); + ASSERT_EQ(600, mappedRect3.height()); + + test.setM22(0.5); + + auto mappedRect4 = test.mapRect(rect); + + ASSERT_EQ(5, mappedRect4.x()); + ASSERT_EQ(10, mappedRect4.y()); + ASSERT_EQ(100, mappedRect4.width()); + ASSERT_EQ(150, mappedRect4.height()); + + test.setM11(2.0); + + auto mappedRect5 = test.mapRect(rect); + + ASSERT_EQ(20, mappedRect5.x()); + ASSERT_EQ(10, mappedRect5.y()); + ASSERT_EQ(100, mappedRect4.width()); + ASSERT_EQ(150, mappedRect4.height()); +} + +TEST(TransformationMatrix, MapFloatRect) +{ + WebCore::TransformationMatrix test; + WebCore::FloatRect rect(10.f, 20.0f, 200.0f, 300.0f); + + auto mappedRect = test.mapRect(rect); + + ASSERT_FLOAT_EQ(10.0f, mappedRect.x()); + ASSERT_FLOAT_EQ(20.0f, mappedRect.y()); + ASSERT_FLOAT_EQ(200.0f, mappedRect.width()); + ASSERT_FLOAT_EQ(300.0f, mappedRect.height()); + + test.setD(2.0); + + auto mappedRect2 = test.mapRect(rect); + + ASSERT_FLOAT_EQ(10.0f, mappedRect2.x()); + ASSERT_FLOAT_EQ(40.0f, mappedRect2.y()); + ASSERT_FLOAT_EQ(200.0f, mappedRect2.width()); + ASSERT_FLOAT_EQ(600.0f, mappedRect2.height()); + + test.setA(0.5); + + auto mappedRect3 = test.mapRect(rect); + + ASSERT_FLOAT_EQ(5.0f, mappedRect3.x()); + ASSERT_FLOAT_EQ(40.0f, mappedRect3.y()); + ASSERT_FLOAT_EQ(100.0f, mappedRect3.width()); + ASSERT_FLOAT_EQ(600.0f, mappedRect3.height()); + + test.setM22(0.5); + + auto mappedRect4 = test.mapRect(rect); + + ASSERT_FLOAT_EQ(5.0f, mappedRect4.x()); + ASSERT_FLOAT_EQ(10.0f, mappedRect4.y()); + ASSERT_FLOAT_EQ(100.0f, mappedRect4.width()); + ASSERT_FLOAT_EQ(150.0f, mappedRect4.height()); + + test.setM11(2.0); + + auto mappedRect5 = test.mapRect(rect); + + ASSERT_FLOAT_EQ(20.0f, mappedRect5.x()); + ASSERT_FLOAT_EQ(10.0f, mappedRect5.y()); + ASSERT_FLOAT_EQ(100.0f, mappedRect4.width()); + ASSERT_FLOAT_EQ(150.0f, mappedRect4.height()); +} + +TEST(TransformationMatrix, MapFloatQuad) +{ + WebCore::FloatRect rect(100.0f, 100.0f, 100.0f, 50.0f); + WebCore::FloatQuad quad(rect); + + ASSERT_FLOAT_EQ(100.0f, quad.p1().x()); + ASSERT_FLOAT_EQ(100.0f, quad.p1().y()); + ASSERT_FLOAT_EQ(200.0f, quad.p2().x()); + ASSERT_FLOAT_EQ(100.0f, quad.p2().y()); + ASSERT_FLOAT_EQ(200.0f, quad.p3().x()); + ASSERT_FLOAT_EQ(150.0f, quad.p3().y()); + ASSERT_FLOAT_EQ(100.0f, quad.p4().x()); + ASSERT_FLOAT_EQ(150.0f, quad.p4().y()); + + WebCore::TransformationMatrix test; + auto mappedQuad = test.mapQuad(quad); + + ASSERT_FLOAT_EQ(100.0f, mappedQuad.p1().x()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad.p1().y()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad.p2().x()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad.p2().y()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad.p3().x()); + ASSERT_FLOAT_EQ(150.0f, mappedQuad.p3().y()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad.p4().x()); + ASSERT_FLOAT_EQ(150.0f, mappedQuad.p4().y()); + + test.setD(2.0); + + auto mappedQuad2 = test.mapQuad(quad); + + ASSERT_FLOAT_EQ(100.0f, mappedQuad2.p1().x()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad2.p1().y()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad2.p2().x()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad2.p2().y()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad2.p3().x()); + ASSERT_FLOAT_EQ(300.0f, mappedQuad2.p3().y()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad2.p4().x()); + ASSERT_FLOAT_EQ(300.0f, mappedQuad2.p4().y()); + + test.setA(0.5); + + auto mappedQuad3 = test.mapQuad(quad); + + ASSERT_FLOAT_EQ(50.0f, mappedQuad3.p1().x()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad3.p1().y()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad3.p2().x()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad3.p2().y()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad3.p3().x()); + ASSERT_FLOAT_EQ(300.0f, mappedQuad3.p3().y()); + ASSERT_FLOAT_EQ(50.0f, mappedQuad3.p4().x()); + ASSERT_FLOAT_EQ(300.0f, mappedQuad3.p4().y()); + + test.setM22(0.5); + + auto mappedQuad4 = test.mapQuad(quad); + + ASSERT_FLOAT_EQ(50.0f, mappedQuad4.p1().x()); + ASSERT_FLOAT_EQ(50.0f, mappedQuad4.p1().y()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad4.p2().x()); + ASSERT_FLOAT_EQ(50.0f, mappedQuad4.p2().y()); + ASSERT_FLOAT_EQ(100.0f, mappedQuad4.p3().x()); + ASSERT_FLOAT_EQ(75.0f, mappedQuad4.p3().y()); + ASSERT_FLOAT_EQ(50.0f, mappedQuad4.p4().x()); + ASSERT_FLOAT_EQ(75.0f, mappedQuad4.p4().y()); + + test.setM11(2.0); + + auto mappedQuad5 = test.mapQuad(quad); + + ASSERT_FLOAT_EQ(200.0f, mappedQuad5.p1().x()); + ASSERT_FLOAT_EQ(50.0f, mappedQuad5.p1().y()); + ASSERT_FLOAT_EQ(400.0f, mappedQuad5.p2().x()); + ASSERT_FLOAT_EQ(50.0f, mappedQuad5.p2().y()); + ASSERT_FLOAT_EQ(400.0f, mappedQuad5.p3().x()); + ASSERT_FLOAT_EQ(75.0f, mappedQuad5.p3().y()); + ASSERT_FLOAT_EQ(200.0f, mappedQuad5.p4().x()); + ASSERT_FLOAT_EQ(75.0f, mappedQuad5.p4().y()); +} + +static void testDoubled(const WebCore::TransformationMatrix& transform) +{ + EXPECT_DOUBLE_EQ(12.0, transform.a()); + EXPECT_DOUBLE_EQ(10.0, transform.b()); + EXPECT_DOUBLE_EQ(8.0, transform.c()); + EXPECT_DOUBLE_EQ(6.0, transform.d()); + EXPECT_DOUBLE_EQ(2.0, transform.e()); + EXPECT_DOUBLE_EQ(1.0, transform.f()); +} + +static void testHalved(const WebCore::TransformationMatrix& transform) +{ + EXPECT_DOUBLE_EQ(3.0, transform.a()); + EXPECT_DOUBLE_EQ(2.5, transform.b()); + EXPECT_DOUBLE_EQ(2.0, transform.c()); + EXPECT_DOUBLE_EQ(1.5, transform.d()); + EXPECT_DOUBLE_EQ(2.0, transform.e()); + EXPECT_DOUBLE_EQ(1.0, transform.f()); +} + +static void testDoubled2(const WebCore::TransformationMatrix& transform) +{ + EXPECT_DOUBLE_EQ(32.0, transform.m11()); + EXPECT_DOUBLE_EQ(30.0, transform.m12()); + EXPECT_DOUBLE_EQ(28.0, transform.m13()); + EXPECT_DOUBLE_EQ(26.0, transform.m14()); + EXPECT_DOUBLE_EQ(24.0, transform.m21()); + EXPECT_DOUBLE_EQ(22.0, transform.m22()); + EXPECT_DOUBLE_EQ(20.0, transform.m23()); + EXPECT_DOUBLE_EQ(18.0, transform.m24()); + EXPECT_DOUBLE_EQ(16.0, transform.m31()); + EXPECT_DOUBLE_EQ(14.0, transform.m32()); + EXPECT_DOUBLE_EQ(12.0, transform.m33()); + EXPECT_DOUBLE_EQ(10.0, transform.m34()); + EXPECT_DOUBLE_EQ(8.0, transform.m41()); + EXPECT_DOUBLE_EQ(6.0, transform.m42()); + EXPECT_DOUBLE_EQ(4.0, transform.m43()); + EXPECT_DOUBLE_EQ(2.0, transform.m44()); +} + +static void testHalved2(const WebCore::TransformationMatrix& transform) +{ + EXPECT_DOUBLE_EQ(8.0, transform.m11()); + EXPECT_DOUBLE_EQ(7.5, transform.m12()); + EXPECT_DOUBLE_EQ(7.0, transform.m13()); + EXPECT_DOUBLE_EQ(6.5, transform.m14()); + EXPECT_DOUBLE_EQ(6.0, transform.m21()); + EXPECT_DOUBLE_EQ(5.5, transform.m22()); + EXPECT_DOUBLE_EQ(5.0, transform.m23()); + EXPECT_DOUBLE_EQ(4.5, transform.m24()); + EXPECT_DOUBLE_EQ(4.0, transform.m31()); + EXPECT_DOUBLE_EQ(3.5, transform.m32()); + EXPECT_DOUBLE_EQ(3.0, transform.m33()); + EXPECT_DOUBLE_EQ(2.5, transform.m34()); + EXPECT_DOUBLE_EQ(2.0, transform.m41()); + EXPECT_DOUBLE_EQ(1.5, transform.m42()); + EXPECT_DOUBLE_EQ(1.0, transform.m43()); + EXPECT_DOUBLE_EQ(0.5, transform.m44()); +} + +TEST(TransformationMatrix, Multiply) +{ + WebCore::TransformationMatrix test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::TransformationMatrix identity; + + testAffineLikeConstruction(test); + + test.multiply(identity); + + testAffineLikeConstruction(test); + + WebCore::TransformationMatrix doubler(2.0, 0.0, 0.0, 2.0, 0.0, 0.0); + + test.multiply(doubler); + + testDoubled(test); + + WebCore::TransformationMatrix halver(0.5, 0.0, 0.0, 0.5, 0.0, 0.0); + + test.multiply(halver); + + testAffineLikeConstruction(test); + + test.multiply(halver); + + testHalved(test); + + WebCore::TransformationMatrix test2(16.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0, + 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test2); + + test2.multiply(identity); + + testValueConstruction(test2); + + WebCore::TransformationMatrix doubler2(2.0, 0.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, + 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 0.0, 2.0); + + test2.multiply(doubler2); + + testDoubled2(test2); + + WebCore::TransformationMatrix halver2(0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, + 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5); + + test2.multiply(halver2); + + testValueConstruction(test2); + + test2.multiply(halver2); + + testHalved2(test2); + + WebCore::TransformationMatrix test3(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + test3 *= identity; + + testAffineLikeConstruction(test3); + + test3 *= doubler; + + testDoubled(test3); + + const WebCore::TransformationMatrix test4(16.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0, + 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + auto result1 = test4 * identity; + + testValueConstruction(result1); + + auto result2 = test4 * doubler2; + + testDoubled2(result2); +} + +static void testScaledByTwo(const WebCore::TransformationMatrix& transform) +{ + EXPECT_DOUBLE_EQ(32.0, transform.m11()); + EXPECT_DOUBLE_EQ(30.0, transform.m12()); + EXPECT_DOUBLE_EQ(28.0, transform.m13()); + EXPECT_DOUBLE_EQ(26.0, transform.m14()); + EXPECT_DOUBLE_EQ(24.0, transform.m21()); + EXPECT_DOUBLE_EQ(22.0, transform.m22()); + EXPECT_DOUBLE_EQ(20.0, transform.m23()); + EXPECT_DOUBLE_EQ(18.0, transform.m24()); + EXPECT_DOUBLE_EQ(8.0, transform.m31()); + EXPECT_DOUBLE_EQ(7.0, transform.m32()); + EXPECT_DOUBLE_EQ(6.0, transform.m33()); + EXPECT_DOUBLE_EQ(5.0, transform.m34()); + EXPECT_DOUBLE_EQ(4.0, transform.m41()); + EXPECT_DOUBLE_EQ(3.0, transform.m42()); + EXPECT_DOUBLE_EQ(2.0, transform.m43()); + EXPECT_DOUBLE_EQ(1.0, transform.m44()); +} + +static void testScaledByHalf(const WebCore::TransformationMatrix& transform) +{ + EXPECT_DOUBLE_EQ(8.0, transform.m11()); + EXPECT_DOUBLE_EQ(7.5, transform.m12()); + EXPECT_DOUBLE_EQ(7.0, transform.m13()); + EXPECT_DOUBLE_EQ(6.5, transform.m14()); + EXPECT_DOUBLE_EQ(6.0, transform.m21()); + EXPECT_DOUBLE_EQ(5.5, transform.m22()); + EXPECT_DOUBLE_EQ(5.0, transform.m23()); + EXPECT_DOUBLE_EQ(4.5, transform.m24()); + EXPECT_DOUBLE_EQ(8.0, transform.m31()); + EXPECT_DOUBLE_EQ(7.0, transform.m32()); + EXPECT_DOUBLE_EQ(6.0, transform.m33()); + EXPECT_DOUBLE_EQ(5.0, transform.m34()); + EXPECT_DOUBLE_EQ(4.0, transform.m41()); + EXPECT_DOUBLE_EQ(3.0, transform.m42()); + EXPECT_DOUBLE_EQ(2.0, transform.m43()); + EXPECT_DOUBLE_EQ(1.0, transform.m44()); +} + + +TEST(TransformationMatrix, Scale) +{ + WebCore::TransformationMatrix test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testAffineLikeConstruction(test); + + test.scale(1.0); + + testAffineLikeConstruction(test); + + test.scale(2.0); + + testDoubled(test); + + test.scale(0.5); + + testAffineLikeConstruction(test); + + test.scale(0.5); + + testHalved(test); + + WebCore::TransformationMatrix test2(16.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0, + 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test2); + + test2.scale(2.0); + + testScaledByTwo(test2); + + test2.scale(0.5); + + testValueConstruction(test2); + + test2.scale(0.5); + + testScaledByHalf(test2); +} + +TEST(TransformationMatrix, ScaleUniformNonUniform) +{ + WebCore::TransformationMatrix test(16.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0, + 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testValueConstruction(test); + + test.scaleNonUniform(1.0, 1.0); + + testValueConstruction(test); + + test.scaleNonUniform(2.0, 2.0); + + testScaledByTwo(test); + + test.scaleNonUniform(0.5, 0.5); + + testValueConstruction(test); + + test.scaleNonUniform(0.5, 0.5); + + testScaledByHalf(test); +} + +TEST(TransformationMatrix, Rotate) +{ + WebCore::TransformationMatrix test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + test.rotate(360.0); + + testAffineLikeConstruction(test); + + test.rotate(180.0); + + static double epsilon = 0.0001; + + EXPECT_NEAR(-6.0, test.a(), epsilon); + EXPECT_NEAR(-5.0, test.b(), epsilon); + EXPECT_NEAR(-4.0, test.c(), epsilon); + EXPECT_NEAR(-3.0, test.d(), epsilon); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.rotate(-180.0); + + testAffineLikeConstruction(test); + + test.rotate(90.0); + + EXPECT_NEAR(4.0, test.a(), epsilon); + EXPECT_NEAR(3.0, test.b(), epsilon); + EXPECT_NEAR(-6.0, test.c(), epsilon); + EXPECT_NEAR(-5.0, test.d(), epsilon); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.rotate(-90.0); + + testAffineLikeConstruction(test); +} + +TEST(TransformationMatrix, TranslateXY) +{ + WebCore::TransformationMatrix test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + test.translate(0.0, 0.0); + + testAffineLikeConstruction(test); + + test.translate(5.0, 0.0); + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(32.0, test.e()); + EXPECT_DOUBLE_EQ(26.0, test.f()); + + test.translate(0.0, -1.2); + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(27.2, test.e()); + EXPECT_DOUBLE_EQ(22.4, test.f()); +} + +TEST(TransformationMatrix, FlipX) +{ + WebCore::TransformationMatrix test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testAffineLikeConstruction(test); + + test.flipX(); + + EXPECT_DOUBLE_EQ(-6.0, test.a()); + EXPECT_DOUBLE_EQ(-5.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.flipX(); + + testAffineLikeConstruction(test); + + WebCore::TransformationMatrix test2; + + testIdentity(test2); + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); + + test2.flipX(); + + EXPECT_DOUBLE_EQ(-1.0, test2.a()); + EXPECT_DOUBLE_EQ(0.0, test2.b()); + EXPECT_DOUBLE_EQ(0.0, test2.c()); + EXPECT_DOUBLE_EQ(1.0, test2.d()); + EXPECT_DOUBLE_EQ(0.0, test2.e()); + EXPECT_DOUBLE_EQ(0.0, test2.f()); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_FALSE(test2.isIdentityOrTranslation()); + + test2.flipX(); + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); +} + +TEST(TransformationMatrix, FlipY) +{ + WebCore::TransformationMatrix test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testAffineLikeConstruction(test); + + test.flipY(); + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(-4.0, test.c()); + EXPECT_DOUBLE_EQ(-3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.flipY(); + + testAffineLikeConstruction(test); + + WebCore::TransformationMatrix test2; + + testIdentity(test2); + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); + + test2.flipY(); + + EXPECT_DOUBLE_EQ(1.0, test2.a()); + EXPECT_DOUBLE_EQ(0.0, test2.b()); + EXPECT_DOUBLE_EQ(0.0, test2.c()); + EXPECT_DOUBLE_EQ(-1.0, test2.d()); + EXPECT_DOUBLE_EQ(0.0, test2.e()); + EXPECT_DOUBLE_EQ(0.0, test2.f()); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_FALSE(test2.isIdentityOrTranslation()); + + test2.flipY(); + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); +} + +TEST(TransformationMatrix, FlipXandFlipY) +{ + WebCore::TransformationMatrix test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testAffineLikeConstruction(test); + + test.flipX(); + + EXPECT_DOUBLE_EQ(-6.0, test.a()); + EXPECT_DOUBLE_EQ(-5.0, test.b()); + EXPECT_DOUBLE_EQ(4.0, test.c()); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.flipY(); + + EXPECT_DOUBLE_EQ(-6.0, test.a()); + EXPECT_DOUBLE_EQ(-5.0, test.b()); + EXPECT_DOUBLE_EQ(-4.0, test.c()); + EXPECT_DOUBLE_EQ(-3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.flipX(); + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_DOUBLE_EQ(-4.0, test.c()); + EXPECT_DOUBLE_EQ(-3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.flipY(); + + testAffineLikeConstruction(test); + + WebCore::TransformationMatrix test2; + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); + + test2.flipX(); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_FALSE(test2.isIdentityOrTranslation()); + + test2.flipY(); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_FALSE(test2.isIdentityOrTranslation()); + + test2.flipX(); + + ASSERT_FALSE(test2.isIdentity()); + ASSERT_FALSE(test2.isIdentityOrTranslation()); + + test2.flipY(); + + ASSERT_TRUE(test2.isIdentity()); + ASSERT_TRUE(test2.isIdentityOrTranslation()); +} + +TEST(TransformationMatrix, Skew) +{ + WebCore::TransformationMatrix test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + + testAffineLikeConstruction(test); + + test.skew(360.0, 360.0); + + testAffineLikeConstruction(test); + + test.skew(0.0, 0.0); + + testAffineLikeConstruction(test); + + test.skew(180.0, 180.0); + + static double epsilon = 0.0001; + + EXPECT_DOUBLE_EQ(6.0, test.a()); + EXPECT_DOUBLE_EQ(5.0, test.b()); + EXPECT_NEAR(4.0, test.c(), epsilon); + EXPECT_DOUBLE_EQ(3.0, test.d()); + EXPECT_DOUBLE_EQ(2.0, test.e()); + EXPECT_DOUBLE_EQ(1.0, test.f()); + + test.skew(-180.0, -180.0); + + testAffineLikeConstruction(test); +} + +TEST(TransformationMatrix, Inverse) +{ + WebCore::TransformationMatrix test; + + auto inverse = test.inverse(); + + ASSERT(inverse); + + EXPECT_DOUBLE_EQ(1.0, inverse->a()); + EXPECT_DOUBLE_EQ(0.0, inverse->b()); + EXPECT_DOUBLE_EQ(0.0, inverse->c()); + EXPECT_DOUBLE_EQ(1.0, inverse->d()); + EXPECT_DOUBLE_EQ(0.0, inverse->e()); + EXPECT_DOUBLE_EQ(0.0, inverse->f()); + + auto test2 = test * inverse.value(); + + testIdentity(test2); +} + +TEST(TransformationMatrix, NonInvertableBlend) +{ + WebCore::TransformationMatrix from; + WebCore::TransformationMatrix to(2.7133590938, 0.0, 0.0, 0.0, 0.0, 2.4645137761, 0.0, 0.0, 0.0, 0.0, 0.00, 0.01, 0.02, 0.03, 0.04, 0.05); + WebCore::TransformationMatrix result; + + result = to; + result.blend(from, 0.25); + EXPECT_TRUE(result == from); + + result = to; + result.blend(from, 0.75); + EXPECT_TRUE(result == to); +} + +TEST(TransformationMatrix, Blend) +{ + WebCore::TransformationMatrix transform; + + WebCore::TransformationMatrix scaled; + scaled.scale(2.0); + + transform.blend(scaled, 50); + + static const double epsilon = 0.0001; + EXPECT_NEAR(-48.0, transform.m11(), epsilon); + EXPECT_NEAR(0.0, transform.m12(), epsilon); + EXPECT_NEAR(0.0, transform.m13(), epsilon); + EXPECT_NEAR(0.0, transform.m14(), epsilon); + EXPECT_NEAR(0.0, transform.m21(), epsilon); + EXPECT_NEAR(-48.0, transform.m22(), epsilon); + EXPECT_NEAR(0.0, transform.m23(), epsilon); + EXPECT_NEAR(0.0, transform.m24(), epsilon); + EXPECT_NEAR(0.0, transform.m31(), epsilon); + EXPECT_NEAR(0.0, transform.m32(), epsilon); + EXPECT_NEAR(1.0, transform.m33(), epsilon); + EXPECT_NEAR(0.0, transform.m34(), epsilon); + EXPECT_NEAR(0.0, transform.m41(), epsilon); + EXPECT_NEAR(0.0, transform.m42(), epsilon); + EXPECT_NEAR(0.0, transform.m43(), epsilon); + EXPECT_NEAR(1.0, transform.m44(), epsilon); +} + +TEST(TransformationMatrix, Blend2) +{ + WebCore::TransformationMatrix transform; + + WebCore::TransformationMatrix scaled; + scaled.scale(2.0); + + transform.blend2(scaled, 20); + + static const double epsilon = 0.0001; + EXPECT_NEAR(-18.0, transform.m11(), epsilon); + EXPECT_NEAR(0.0, transform.m12(), epsilon); + EXPECT_NEAR(0.0, transform.m13(), epsilon); + EXPECT_NEAR(0.0, transform.m14(), epsilon); + EXPECT_NEAR(0.0, transform.m21(), epsilon); + EXPECT_NEAR(-18.0, transform.m22(), epsilon); + EXPECT_NEAR(0.0, transform.m23(), epsilon); + EXPECT_NEAR(0.0, transform.m24(), epsilon); + EXPECT_NEAR(0.0, transform.m31(), epsilon); + EXPECT_NEAR(0.0, transform.m32(), epsilon); + EXPECT_NEAR(1.0, transform.m33(), epsilon); + EXPECT_NEAR(0.0, transform.m34(), epsilon); + EXPECT_NEAR(0.0, transform.m41(), epsilon); + EXPECT_NEAR(0.0, transform.m42(), epsilon); + EXPECT_NEAR(0.0, transform.m43(), epsilon); + EXPECT_NEAR(1.0, transform.m44(), epsilon); +} + +TEST(TransformationMatrix, Blend4) +{ + WebCore::TransformationMatrix transform; + + WebCore::TransformationMatrix scaled; + scaled.scale(2.0); + + transform.blend4(scaled, 30); + + static const double epsilon = 0.0001; + EXPECT_NEAR(-28.0, transform.m11(), epsilon); + EXPECT_NEAR(0.0, transform.m12(), epsilon); + EXPECT_NEAR(0.0, transform.m13(), epsilon); + EXPECT_NEAR(0.0, transform.m14(), epsilon); + EXPECT_NEAR(0.0, transform.m21(), epsilon); + EXPECT_NEAR(-28.0, transform.m22(), epsilon); + EXPECT_NEAR(0.0, transform.m23(), epsilon); + EXPECT_NEAR(0.0, transform.m24(), epsilon); + EXPECT_NEAR(0.0, transform.m31(), epsilon); + EXPECT_NEAR(0.0, transform.m32(), epsilon); + EXPECT_NEAR(1.0, transform.m33(), epsilon); + EXPECT_NEAR(0.0, transform.m34(), epsilon); + EXPECT_NEAR(0.0, transform.m41(), epsilon); + EXPECT_NEAR(0.0, transform.m42(), epsilon); + EXPECT_NEAR(0.0, transform.m43(), epsilon); + EXPECT_NEAR(1.0, transform.m44(), epsilon); +} + +TEST(TransformationMatrix, Equality) +{ + WebCore::TransformationMatrix test(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::TransformationMatrix test2; + + ASSERT_FALSE(test == test2); + ASSERT_TRUE(test != test2); + + test.makeIdentity(); + + ASSERT_TRUE(test == test2); + ASSERT_FALSE(test != test2); + + WebCore::TransformationMatrix test3(16.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0, + 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::TransformationMatrix test4(test3); + + ASSERT_TRUE(test3 == test4); + ASSERT_FALSE(test3 != test4); + + test4.setM43(1002.22); + + ASSERT_FALSE(test3 == test4); + ASSERT_TRUE(test3 != test4); +} + +static void testTranslationMatrix(const WebCore::TransformationMatrix& matrix) +{ + EXPECT_DOUBLE_EQ(1.0, matrix.m11()); + EXPECT_DOUBLE_EQ(0.0, matrix.m12()); + EXPECT_DOUBLE_EQ(0.0, matrix.m13()); + EXPECT_DOUBLE_EQ(0.0, matrix.m14()); + EXPECT_DOUBLE_EQ(0.0, matrix.m21()); + EXPECT_DOUBLE_EQ(1.0, matrix.m22()); + EXPECT_DOUBLE_EQ(0.0, matrix.m23()); + EXPECT_DOUBLE_EQ(0.0, matrix.m24()); + EXPECT_DOUBLE_EQ(0.0, matrix.m31()); + EXPECT_DOUBLE_EQ(0.0, matrix.m32()); + EXPECT_DOUBLE_EQ(1.0, matrix.m33()); + EXPECT_DOUBLE_EQ(0.0, matrix.m34()); + EXPECT_DOUBLE_EQ(10.0, matrix.m41()); + EXPECT_DOUBLE_EQ(15.0, matrix.m42()); + EXPECT_DOUBLE_EQ(30.0, matrix.m43()); + EXPECT_DOUBLE_EQ(1.0, matrix.m44()); +} + +TEST(TransformationMatrix, Casting) +{ + WebCore::TransformationMatrix test(16.0, 15.0, 14.0, 13.0, 12.0, 11.0, 10.0, + 9.0, 8.0, 7.0, 6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + +#if USE(CA) + CATransform3D caTransform = CATransform3DMakeTranslation(10.0f, 15.0f, 30.0f); + + WebCore::TransformationMatrix fromCATransform(caTransform); + + testTranslationMatrix(fromCATransform); + + CATransform3D caFromWK = test; + + EXPECT_DOUBLE_EQ(16.0, caFromWK.m11); + EXPECT_DOUBLE_EQ(15.0, caFromWK.m12); + EXPECT_DOUBLE_EQ(14.0, caFromWK.m13); + EXPECT_DOUBLE_EQ(13.0, caFromWK.m14); + EXPECT_DOUBLE_EQ(12.0, caFromWK.m21); + EXPECT_DOUBLE_EQ(11.0, caFromWK.m22); + EXPECT_DOUBLE_EQ(10.0, caFromWK.m23); + EXPECT_DOUBLE_EQ(9.0, caFromWK.m24); + EXPECT_DOUBLE_EQ(8.0, caFromWK.m31); + EXPECT_DOUBLE_EQ(7.0, caFromWK.m32); + EXPECT_DOUBLE_EQ(6.0, caFromWK.m33); + EXPECT_DOUBLE_EQ(5.0, caFromWK.m34); + EXPECT_DOUBLE_EQ(4.0, caFromWK.m41); + EXPECT_DOUBLE_EQ(3.0, caFromWK.m42); + EXPECT_DOUBLE_EQ(2.0, caFromWK.m43); + EXPECT_DOUBLE_EQ(1.0, caFromWK.m44); +#endif + +#if USE(CG) + CGAffineTransform cgTransform = CGAffineTransformMake(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::TransformationMatrix fromCGTransform(cgTransform); + + testAffineLikeConstruction(fromCGTransform); + + CGAffineTransform cgFromWK = test; + EXPECT_DOUBLE_EQ(16.0, cgFromWK.a); + EXPECT_DOUBLE_EQ(15.0, cgFromWK.b); + EXPECT_DOUBLE_EQ(12.0, cgFromWK.c); + EXPECT_DOUBLE_EQ(11.0, cgFromWK.d); + EXPECT_DOUBLE_EQ(4.0, cgFromWK.tx); + EXPECT_DOUBLE_EQ(3.0, cgFromWK.ty); +#endif + +#if PLATFORM(WIN) || (PLATFORM(GTK) && OS(WINDOWS)) + XFORM gdiFromWK = test; + EXPECT_DOUBLE_EQ(16.0, gdiFromWK.eM11); + EXPECT_DOUBLE_EQ(15.0, gdiFromWK.eM12); + EXPECT_DOUBLE_EQ(12.0, gdiFromWK.eM21); + EXPECT_DOUBLE_EQ(11.0, gdiFromWK.eM22); + EXPECT_DOUBLE_EQ(4.0, gdiFromWK.eDx); + EXPECT_DOUBLE_EQ(3.0, gdiFromWK.eDy); +#endif + +#if PLATFORM(WIN) + D2D1_MATRIX_3X2_F d2dTransform = D2D1::Matrix3x2F(6.0, 5.0, 4.0, 3.0, 2.0, 1.0); + WebCore::TransformationMatrix fromD2DTransform(d2dTransform); + + testAffineLikeConstruction(fromD2DTransform); + + D2D1_MATRIX_3X2_F d2dFromWK = test; + EXPECT_DOUBLE_EQ(16.0, d2dFromWK._11); + EXPECT_DOUBLE_EQ(15.0, d2dFromWK._12); + EXPECT_DOUBLE_EQ(12.0, d2dFromWK._21); + EXPECT_DOUBLE_EQ(11.0, d2dFromWK._22); + EXPECT_DOUBLE_EQ(4.0, d2dFromWK._31); + EXPECT_DOUBLE_EQ(3.0, d2dFromWK._32); +#endif +} + +TEST(TransformationMatrix, MakeMapBetweenRects) +{ + WebCore::TransformationMatrix transform; + + WebCore::FloatRect fromRect(10.0f, 10.0f, 100.0f, 100.0f); + WebCore::FloatRect toRect(70.0f, 70.0f, 200.0f, 50.0f); + + auto mapBetween = WebCore::TransformationMatrix::rectToRect(fromRect, toRect); + + EXPECT_DOUBLE_EQ(2.0, mapBetween.a()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.b()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.c()); + EXPECT_DOUBLE_EQ(0.5, mapBetween.d()); + EXPECT_DOUBLE_EQ(60.0, mapBetween.e()); + EXPECT_DOUBLE_EQ(60.0, mapBetween.f()); + + EXPECT_DOUBLE_EQ(2.0, mapBetween.m11()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.m12()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.m13()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.m14()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.m21()); + EXPECT_DOUBLE_EQ(0.5, mapBetween.m22()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.m23()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.m24()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.m31()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.m32()); + EXPECT_DOUBLE_EQ(1.0, mapBetween.m33()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.m34()); + EXPECT_DOUBLE_EQ(60.0, mapBetween.m41()); + EXPECT_DOUBLE_EQ(60.0, mapBetween.m42()); + EXPECT_DOUBLE_EQ(0.0, mapBetween.m43()); + EXPECT_DOUBLE_EQ(1.0, mapBetween.m44()); +} + +} diff --git a/Tools/TestWebKitAPI/Tests/WebCore/URL.cpp b/Tools/TestWebKitAPI/Tests/WebCore/URL.cpp index 4a7f53977..30a7e301e 100644 --- a/Tools/TestWebKitAPI/Tests/WebCore/URL.cpp +++ b/Tools/TestWebKitAPI/Tests/WebCore/URL.cpp @@ -26,6 +26,7 @@ #include "config.h" #include "WTFStringUtilities.h" #include +#include #include using namespace WebCore; @@ -57,10 +58,10 @@ TEST_F(URLTest, URLConstructorConstChar) EXPECT_FALSE(kurl.isNull()); EXPECT_TRUE(kurl.isValid()); - EXPECT_EQ(String("http"), kurl.protocol()); + EXPECT_EQ(kurl.protocol() == "http", true); EXPECT_EQ(String("www.example.com"), kurl.host()); - EXPECT_TRUE(kurl.hasPort()); - EXPECT_EQ(8080, kurl.port()); + EXPECT_TRUE(!!kurl.port()); + EXPECT_EQ(8080, kurl.port().value()); EXPECT_EQ(String("username"), kurl.user()); EXPECT_EQ(String("password"), kurl.pass()); EXPECT_EQ(String("/index.html"), kurl.path()); @@ -70,6 +71,53 @@ TEST_F(URLTest, URLConstructorConstChar) EXPECT_EQ(String("fragment"), kurl.fragmentIdentifier()); } +TEST_F(URLTest, URLProtocolHostAndPort) +{ + auto createURL = [](const char* urlAsString) { + URLParser parser(urlAsString); + return parser.result(); + }; + + auto url = createURL("http://username:password@www.example.com:8080/index.html?var=val#fragment"); + EXPECT_EQ(String("http://www.example.com:8080"), url.protocolHostAndPort()); + + url = createURL("http://username:@www.example.com:8080/index.html?var=val#fragment"); + EXPECT_EQ(String("http://www.example.com:8080"), url.protocolHostAndPort()); + + url = createURL("http://:password@www.example.com:8080/index.html?var=val#fragment"); + EXPECT_EQ(String("http://www.example.com:8080"), url.protocolHostAndPort()); + + url = createURL("http://username@www.example.com:8080/index.html?var=val#fragment"); + EXPECT_EQ(String("http://www.example.com:8080"), url.protocolHostAndPort()); + + url = createURL("http://www.example.com:8080/index.html?var=val#fragment"); + EXPECT_EQ(String("http://www.example.com:8080"), url.protocolHostAndPort()); + + url = createURL("http://www.example.com:/index.html?var=val#fragment"); + EXPECT_EQ(String("http://www.example.com"), url.protocolHostAndPort()); + + url = createURL("http://www.example.com/index.html?var=val#fragment"); + EXPECT_EQ(String("http://www.example.com"), url.protocolHostAndPort()); + + url = createURL("file:///a/b/c"); + EXPECT_EQ(String("file://"), url.protocolHostAndPort()); + + url = createURL("file:///a/b"); + EXPECT_EQ(String("file://"), url.protocolHostAndPort()); + + url = createURL("file:///a"); + EXPECT_EQ(String("file://"), url.protocolHostAndPort()); + + url = createURL("file:///a"); + EXPECT_EQ(String("file://"), url.protocolHostAndPort()); + + url = createURL("asdf://username:password@www.example.com:8080/index.html?var=val#fragment"); + EXPECT_EQ(String("asdf://www.example.com:8080"), url.protocolHostAndPort()); + + url = createURL("asdf:///a/b/c"); + EXPECT_EQ(String("asdf://"), url.protocolHostAndPort()); +} + TEST_F(URLTest, URLDataURIStringSharing) { URL baseURL(ParsedURLString, "http://www.webkit.org/"); diff --git a/Tools/TestWebKitAPI/Tests/WebCore/URLParser.cpp b/Tools/TestWebKitAPI/Tests/WebCore/URLParser.cpp new file mode 100644 index 000000000..043e9c21d --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/URLParser.cpp @@ -0,0 +1,1305 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "WTFStringUtilities.h" +#include +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +class URLParserTest : public testing::Test { +public: + void SetUp() final { + WTF::initializeMainThread(); + } +}; + +struct ExpectedParts { + String protocol; + String user; + String password; + String host; + unsigned short port; + String path; + String query; + String fragment; + String string; + + bool isInvalid() const + { + return protocol.isEmpty() + && user.isEmpty() + && password.isEmpty() + && host.isEmpty() + && !port + && path.isEmpty() + && query.isEmpty() + && fragment.isEmpty(); + } +}; + +static bool eq(const String& s1, const String& s2) +{ + EXPECT_STREQ(s1.utf8().data(), s2.utf8().data()); + return s1.utf8() == s2.utf8(); +} + +static String insertTabAtLocation(const String& string, size_t location) +{ + ASSERT(location <= string.length()); + return makeString(string.substring(0, location), "\t", string.substring(location)); +} + +static ExpectedParts invalidParts(const String& urlStringWithTab) +{ + return {"", "", "", "", 0, "" , "", "", urlStringWithTab}; +} + +enum class TestTabs { No, Yes }; + +// Inserting tabs between surrogate pairs changes the encoded value instead of being skipped by the URLParser. +const TestTabs testTabsValueForSurrogatePairs = TestTabs::No; + +static void checkURL(const String& urlString, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes) +{ + auto url = URL(URL(), urlString); + + EXPECT_TRUE(eq(parts.protocol, url.protocol().toString())); + EXPECT_TRUE(eq(parts.user, url.user())); + EXPECT_TRUE(eq(parts.password, url.pass())); + EXPECT_TRUE(eq(parts.host, url.host())); + EXPECT_EQ(parts.port, url.port().value_or(0)); + EXPECT_TRUE(eq(parts.path, url.path())); + EXPECT_TRUE(eq(parts.query, url.query())); + EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier())); + EXPECT_TRUE(eq(parts.string, url.string())); + + EXPECT_TRUE(URLParser::internalValuesConsistent(url)); + + if (testTabs == TestTabs::No) + return; + + for (size_t i = 0; i < urlString.length(); ++i) { + String urlStringWithTab = insertTabAtLocation(urlString, i); + checkURL(urlStringWithTab, + parts.isInvalid() ? invalidParts(urlStringWithTab) : parts, + TestTabs::No); + } +} + +static void checkRelativeURL(const String& urlString, const String& baseURLString, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes) +{ + auto url = URL(URL(URL(), baseURLString), urlString); + + EXPECT_TRUE(eq(parts.protocol, url.protocol().toString())); + EXPECT_TRUE(eq(parts.user, url.user())); + EXPECT_TRUE(eq(parts.password, url.pass())); + EXPECT_TRUE(eq(parts.host, url.host())); + EXPECT_EQ(parts.port, url.port().value_or(0)); + EXPECT_TRUE(eq(parts.path, url.path())); + EXPECT_TRUE(eq(parts.query, url.query())); + EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier())); + EXPECT_TRUE(eq(parts.string, url.string())); + + EXPECT_TRUE(URLParser::internalValuesConsistent(url)); + + if (testTabs == TestTabs::No) + return; + + for (size_t i = 0; i < urlString.length(); ++i) { + String urlStringWithTab = insertTabAtLocation(urlString, i); + checkRelativeURL(urlStringWithTab, + baseURLString, + parts.isInvalid() ? invalidParts(urlStringWithTab) : parts, + TestTabs::No); + } +} + +static void checkURLDifferences(const String& urlString, const ExpectedParts& partsNew, const ExpectedParts& partsOld, TestTabs testTabs = TestTabs::Yes) +{ + UNUSED_PARAM(partsOld); // FIXME: Remove all the old expected parts. + auto url = URL(URL(), urlString); + + EXPECT_TRUE(eq(partsNew.protocol, url.protocol().toString())); + EXPECT_TRUE(eq(partsNew.user, url.user())); + EXPECT_TRUE(eq(partsNew.password, url.pass())); + EXPECT_TRUE(eq(partsNew.host, url.host())); + EXPECT_EQ(partsNew.port, url.port().value_or(0)); + EXPECT_TRUE(eq(partsNew.path, url.path())); + EXPECT_TRUE(eq(partsNew.query, url.query())); + EXPECT_TRUE(eq(partsNew.fragment, url.fragmentIdentifier())); + EXPECT_TRUE(eq(partsNew.string, url.string())); + + EXPECT_TRUE(URLParser::internalValuesConsistent(url)); + + if (testTabs == TestTabs::No) + return; + + for (size_t i = 0; i < urlString.length(); ++i) { + String urlStringWithTab = insertTabAtLocation(urlString, i); + checkURLDifferences(urlStringWithTab, + partsNew.isInvalid() ? invalidParts(urlStringWithTab) : partsNew, + partsOld.isInvalid() ? invalidParts(urlStringWithTab) : partsOld, + TestTabs::No); + } +} + +static void checkRelativeURLDifferences(const String& urlString, const String& baseURLString, const ExpectedParts& partsNew, const ExpectedParts& partsOld, TestTabs testTabs = TestTabs::Yes) +{ + UNUSED_PARAM(partsOld); // FIXME: Remove all the old expected parts. + auto url = URL(URL(URL(), baseURLString), urlString); + + EXPECT_TRUE(eq(partsNew.protocol, url.protocol().toString())); + EXPECT_TRUE(eq(partsNew.user, url.user())); + EXPECT_TRUE(eq(partsNew.password, url.pass())); + EXPECT_TRUE(eq(partsNew.host, url.host())); + EXPECT_EQ(partsNew.port, url.port().value_or(0)); + EXPECT_TRUE(eq(partsNew.path, url.path())); + EXPECT_TRUE(eq(partsNew.query, url.query())); + EXPECT_TRUE(eq(partsNew.fragment, url.fragmentIdentifier())); + EXPECT_TRUE(eq(partsNew.string, url.string())); + + EXPECT_TRUE(URLParser::internalValuesConsistent(url)); + + if (testTabs == TestTabs::No) + return; + + for (size_t i = 0; i < urlString.length(); ++i) { + String urlStringWithTab = insertTabAtLocation(urlString, i); + checkRelativeURLDifferences(urlStringWithTab, baseURLString, + partsNew.isInvalid() ? invalidParts(urlStringWithTab) : partsNew, + partsOld.isInvalid() ? invalidParts(urlStringWithTab) : partsOld, + TestTabs::No); + } +} + +static void shouldFail(const String& urlString) +{ + checkURL(urlString, {"", "", "", "", 0, "", "", "", urlString}); +} + +static void shouldFail(const String& urlString, const String& baseString) +{ + checkRelativeURL(urlString, baseString, {"", "", "", "", 0, "", "", "", urlString}); +} + +static void checkURL(const String& urlString, const TextEncoding& encoding, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes) +{ + URLParser parser(urlString, { }, encoding); + auto url = parser.result(); + EXPECT_TRUE(eq(parts.protocol, url.protocol().toString())); + EXPECT_TRUE(eq(parts.user, url.user())); + EXPECT_TRUE(eq(parts.password, url.pass())); + EXPECT_TRUE(eq(parts.host, url.host())); + EXPECT_EQ(parts.port, url.port().value_or(0)); + EXPECT_TRUE(eq(parts.path, url.path())); + EXPECT_TRUE(eq(parts.query, url.query())); + EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier())); + EXPECT_TRUE(eq(parts.string, url.string())); + + if (testTabs == TestTabs::No) + return; + + for (size_t i = 0; i < urlString.length(); ++i) { + String urlStringWithTab = insertTabAtLocation(urlString, i); + checkURL(urlStringWithTab, encoding, + parts.isInvalid() ? invalidParts(urlStringWithTab) : parts, + TestTabs::No); + } +} + +static void checkURL(const String& urlString, const String& baseURLString, const TextEncoding& encoding, const ExpectedParts& parts, TestTabs testTabs = TestTabs::Yes) +{ + URLParser baseParser(baseURLString, { }, encoding); + URLParser parser(urlString, baseParser.result(), encoding); + auto url = parser.result(); + EXPECT_TRUE(eq(parts.protocol, url.protocol().toString())); + EXPECT_TRUE(eq(parts.user, url.user())); + EXPECT_TRUE(eq(parts.password, url.pass())); + EXPECT_TRUE(eq(parts.host, url.host())); + EXPECT_EQ(parts.port, url.port().value_or(0)); + EXPECT_TRUE(eq(parts.path, url.path())); + EXPECT_TRUE(eq(parts.query, url.query())); + EXPECT_TRUE(eq(parts.fragment, url.fragmentIdentifier())); + EXPECT_TRUE(eq(parts.string, url.string())); + + if (testTabs == TestTabs::No) + return; + + for (size_t i = 0; i < urlString.length(); ++i) { + String urlStringWithTab = insertTabAtLocation(urlString, i); + checkURL(urlStringWithTab, baseURLString, encoding, + parts.isInvalid() ? invalidParts(urlStringWithTab) : parts, + TestTabs::No); + } +} + +TEST_F(URLParserTest, Basic) +{ + checkURL("http://user:pass@webkit.org:123/path?query#fragment", {"http", "user", "pass", "webkit.org", 123, "/path", "query", "fragment", "http://user:pass@webkit.org:123/path?query#fragment"}); + checkURL("http://user:pass@webkit.org:123/path?query", {"http", "user", "pass", "webkit.org", 123, "/path", "query", "", "http://user:pass@webkit.org:123/path?query"}); + checkURL("http://user:pass@webkit.org:123/path", {"http", "user", "pass", "webkit.org", 123, "/path", "", "", "http://user:pass@webkit.org:123/path"}); + checkURL("http://user:pass@webkit.org:123/", {"http", "user", "pass", "webkit.org", 123, "/", "", "", "http://user:pass@webkit.org:123/"}); + checkURL("http://user:pass@webkit.org:123", {"http", "user", "pass", "webkit.org", 123, "/", "", "", "http://user:pass@webkit.org:123/"}); + checkURL("http://user:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); + checkURL("http://user:\t\t\tpass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); + checkURL("http://us\ter:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); + checkURL("http://user:pa\tss@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); + checkURL("http://user:pass\t@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); + checkURL("http://\tuser:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); + checkURL("http://user\t:pass@webkit.org", {"http", "user", "pass", "webkit.org", 0, "/", "", "", "http://user:pass@webkit.org/"}); + checkURL("http://webkit.org", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"}); + checkURL("http://127.0.0.1", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); + checkURL("http://webkit.org/", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"}); + checkURL("http://webkit.org/path1/path2/index.html", {"http", "", "", "webkit.org", 0, "/path1/path2/index.html", "", "", "http://webkit.org/path1/path2/index.html"}); + checkURL("about:blank", {"about", "", "", "", 0, "blank", "", "", "about:blank"}); + checkURL("about:blank?query", {"about", "", "", "", 0, "blank", "query", "", "about:blank?query"}); + checkURL("about:blank#fragment", {"about", "", "", "", 0, "blank", "", "fragment", "about:blank#fragment"}); + checkURL("http://[0:f::f:f:0:0]", {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"}); + checkURL("http://[0:f:0:0:f::]", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"}); + checkURL("http://[::f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); + checkURL("http://[0:f:0:0:f::]:", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"}); + checkURL("http://[0:f:0:0:f::]:\t", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"}); + checkURL("http://[0:f:0:0:f::]\t:", {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"}); + checkURL("http://\t[::f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); + checkURL("http://[\t::f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); + checkURL("http://[:\t:f:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); + checkURL("http://[::\tf:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); + checkURL("http://[::f\t:0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); + checkURL("http://[::f:\t0:0:f:0:0]", {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}); + checkURL("http://example.com/path1/path2/.", {"http", "", "", "example.com", 0, "/path1/path2/", "", "", "http://example.com/path1/path2/"}); + checkURL("http://example.com/path1/path2/..", {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"}); + checkURL("http://example.com/path1/path2/./path3", {"http", "", "", "example.com", 0, "/path1/path2/path3", "", "", "http://example.com/path1/path2/path3"}); + checkURL("http://example.com/path1/path2/.\\path3", {"http", "", "", "example.com", 0, "/path1/path2/path3", "", "", "http://example.com/path1/path2/path3"}); + checkURL("http://example.com/path1/path2/../path3", {"http", "", "", "example.com", 0, "/path1/path3", "", "", "http://example.com/path1/path3"}); + checkURL("http://example.com/path1/path2/..\\path3", {"http", "", "", "example.com", 0, "/path1/path3", "", "", "http://example.com/path1/path3"}); + checkURL("http://example.com/.", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); + checkURL("http://example.com/..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); + checkURL("http://example.com/./path1", {"http", "", "", "example.com", 0, "/path1", "", "", "http://example.com/path1"}); + checkURL("http://example.com/../path1", {"http", "", "", "example.com", 0, "/path1", "", "", "http://example.com/path1"}); + checkURL("http://example.com/../path1/../../path2/path3/../path4", {"http", "", "", "example.com", 0, "/path2/path4", "", "", "http://example.com/path2/path4"}); + checkURL("http://example.com/path1/.%2", {"http", "", "", "example.com", 0, "/path1/.%2", "", "", "http://example.com/path1/.%2"}); + checkURL("http://example.com/path1/%2", {"http", "", "", "example.com", 0, "/path1/%2", "", "", "http://example.com/path1/%2"}); + checkURL("http://example.com/path1/%", {"http", "", "", "example.com", 0, "/path1/%", "", "", "http://example.com/path1/%"}); + checkURL("http://example.com/path1/.%", {"http", "", "", "example.com", 0, "/path1/.%", "", "", "http://example.com/path1/.%"}); + checkURL("http://example.com//.", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"}); + checkURL("http://example.com//./", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"}); + checkURL("http://example.com//.//", {"http", "", "", "example.com", 0, "///", "", "", "http://example.com///"}); + checkURL("http://example.com//..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); + checkURL("http://example.com//../", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); + checkURL("http://example.com//..//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"}); + checkURL("http://example.com//..", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); + checkURL("http://example.com/.//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"}); + checkURL("http://example.com/..//", {"http", "", "", "example.com", 0, "//", "", "", "http://example.com//"}); + checkURL("http://example.com/./", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); + checkURL("http://example.com/../", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); + checkURL("http://example.com/path1/.../path3", {"http", "", "", "example.com", 0, "/path1/.../path3", "", "", "http://example.com/path1/.../path3"}); + checkURL("http://example.com/path1/...", {"http", "", "", "example.com", 0, "/path1/...", "", "", "http://example.com/path1/..."}); + checkURL("http://example.com/path1/.../", {"http", "", "", "example.com", 0, "/path1/.../", "", "", "http://example.com/path1/.../"}); + checkURL("http://example.com/.path1/", {"http", "", "", "example.com", 0, "/.path1/", "", "", "http://example.com/.path1/"}); + checkURL("http://example.com/..path1/", {"http", "", "", "example.com", 0, "/..path1/", "", "", "http://example.com/..path1/"}); + checkURL("http://example.com/path1/.path2", {"http", "", "", "example.com", 0, "/path1/.path2", "", "", "http://example.com/path1/.path2"}); + checkURL("http://example.com/path1/..path2", {"http", "", "", "example.com", 0, "/path1/..path2", "", "", "http://example.com/path1/..path2"}); + checkURL("http://example.com/path1/path2/.?query", {"http", "", "", "example.com", 0, "/path1/path2/", "query", "", "http://example.com/path1/path2/?query"}); + checkURL("http://example.com/path1/path2/..?query", {"http", "", "", "example.com", 0, "/path1/", "query", "", "http://example.com/path1/?query"}); + checkURL("http://example.com/path1/path2/.#fragment", {"http", "", "", "example.com", 0, "/path1/path2/", "", "fragment", "http://example.com/path1/path2/#fragment"}); + checkURL("http://example.com/path1/path2/..#fragment", {"http", "", "", "example.com", 0, "/path1/", "", "fragment", "http://example.com/path1/#fragment"}); + + checkURL("file:", {"file", "", "", "", 0, "/", "", "", "file:///"}); + checkURL("file:/", {"file", "", "", "", 0, "/", "", "", "file:///"}); + checkURL("file://", {"file", "", "", "", 0, "/", "", "", "file:///"}); + checkURL("file:///", {"file", "", "", "", 0, "/", "", "", "file:///"}); + checkURL("file:////", {"file", "", "", "", 0, "//", "", "", "file:////"}); // This matches Firefox and URL::parse which I believe are correct, but not Chrome. + checkURL("file:/path", {"file", "", "", "", 0, "/path", "", "", "file:///path"}); + checkURL("file://host/path", {"file", "", "", "host", 0, "/path", "", "", "file://host/path"}); + checkURL("file://host", {"file", "", "", "host", 0, "/", "", "", "file://host/"}); + checkURL("file://host/", {"file", "", "", "host", 0, "/", "", "", "file://host/"}); + checkURL("file:///path", {"file", "", "", "", 0, "/path", "", "", "file:///path"}); + checkURL("file:////path", {"file", "", "", "", 0, "//path", "", "", "file:////path"}); + checkURL("file://localhost/path", {"file", "", "", "", 0, "/path", "", "", "file:///path"}); + checkURL("file://localhost/", {"file", "", "", "", 0, "/", "", "", "file:///"}); + checkURL("file://localhost", {"file", "", "", "", 0, "/", "", "", "file:///"}); + checkURL("file://lOcAlHoSt", {"file", "", "", "", 0, "/", "", "", "file:///"}); + checkURL("file://lOcAlHoSt/", {"file", "", "", "", 0, "/", "", "", "file:///"}); + checkURL("file:/pAtH/", {"file", "", "", "", 0, "/pAtH/", "", "", "file:///pAtH/"}); + checkURL("file:/pAtH", {"file", "", "", "", 0, "/pAtH", "", "", "file:///pAtH"}); + checkURL("file:?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"}); + checkURL("file:#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"}); + checkURL("file:?query#fragment", {"file", "", "", "", 0, "/", "query", "fragment", "file:///?query#fragment"}); + checkURL("file:#fragment?notquery", {"file", "", "", "", 0, "/", "", "fragment?notquery", "file:///#fragment?notquery"}); + checkURL("file:/?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"}); + checkURL("file:/#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"}); + checkURL("file://?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"}); + checkURL("file://#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"}); + checkURL("file:///?query", {"file", "", "", "", 0, "/", "query", "", "file:///?query"}); + checkURL("file:///#fragment", {"file", "", "", "", 0, "/", "", "fragment", "file:///#fragment"}); + checkURL("file:////?query", {"file", "", "", "", 0, "//", "query", "", "file:////?query"}); + checkURL("file:////#fragment", {"file", "", "", "", 0, "//", "", "fragment", "file:////#fragment"}); + checkURL("http://host/A b", {"http", "", "", "host", 0, "/A%20b", "", "", "http://host/A%20b"}); + checkURL("http://host/a%20B", {"http", "", "", "host", 0, "/a%20B", "", "", "http://host/a%20B"}); + checkURL("http://host?q=@ <>!#fragment", {"http", "", "", "host", 0, "/", "q=@%20%3C%3E!", "fragment", "http://host/?q=@%20%3C%3E!#fragment"}); + checkURL("http://user:@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"}); + checkURL("http://user:@\thost", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"}); + checkURL("http://user:\t@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"}); + checkURL("http://user\t:@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"}); + checkURL("http://use\tr:@host", {"http", "user", "", "host", 0, "/", "", "", "http://user@host/"}); + checkURL("http://127.0.0.1:10100/path", {"http", "", "", "127.0.0.1", 10100, "/path", "", "", "http://127.0.0.1:10100/path"}); + checkURL("http://127.0.0.1:/path", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"}); + checkURL("http://127.0.0.1\t:/path", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"}); + checkURL("http://127.0.0.1:\t/path", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"}); + checkURL("http://127.0.0.1:/\tpath", {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1/path"}); + checkURL("http://127.0.0.1:123", {"http", "", "", "127.0.0.1", 123, "/", "", "", "http://127.0.0.1:123/"}); + checkURL("http://127.0.0.1:", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); + checkURL("http://[0:f::f:f:0:0]:123/path", {"http", "", "", "[0:f::f:f:0:0]", 123, "/path", "", "", "http://[0:f::f:f:0:0]:123/path"}); + checkURL("http://[0:f::f:f:0:0]:123", {"http", "", "", "[0:f::f:f:0:0]", 123, "/", "", "", "http://[0:f::f:f:0:0]:123/"}); + checkURL("http://[0:f:0:0:f:\t:]:123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"}); + checkURL("http://[0:f:0:0:f::\t]:123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"}); + checkURL("http://[0:f:0:0:f::]\t:123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"}); + checkURL("http://[0:f:0:0:f::]:\t123", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"}); + checkURL("http://[0:f:0:0:f::]:1\t23", {"http", "", "", "[0:f:0:0:f::]", 123, "/", "", "", "http://[0:f:0:0:f::]:123/"}); + checkURL("http://[0:f::f:f:0:0]:/path", {"http", "", "", "[0:f::f:f:0:0]", 0, "/path", "", "", "http://[0:f::f:f:0:0]/path"}); + checkURL("http://[0:f::f:f:0:0]:", {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"}); + checkURL("http://host:10100/path", {"http", "", "", "host", 10100, "/path", "", "", "http://host:10100/path"}); + checkURL("http://host:/path", {"http", "", "", "host", 0, "/path", "", "", "http://host/path"}); + checkURL("http://host:123", {"http", "", "", "host", 123, "/", "", "", "http://host:123/"}); + checkURL("http://host:", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkURL("http://hos\tt\n:\t1\n2\t3\t/\npath", {"http", "", "", "host", 123, "/path", "", "", "http://host:123/path"}); + checkURL("http://user@example.org/path3", {"http", "user", "", "example.org", 0, "/path3", "", "", "http://user@example.org/path3"}); + checkURL("sc:/pa/pa", {"sc", "", "", "", 0, "/pa/pa", "", "", "sc:/pa/pa"}); + checkURL("sc:/pa", {"sc", "", "", "", 0, "/pa", "", "", "sc:/pa"}); + checkURL("sc:/pa/", {"sc", "", "", "", 0, "/pa/", "", "", "sc:/pa/"}); + checkURL("notspecial:/notuser:notpassword@nothost", {"notspecial", "", "", "", 0, "/notuser:notpassword@nothost", "", "", "notspecial:/notuser:notpassword@nothost"}); + checkURL("sc://pa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"}); + checkURL("sc://\tpa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"}); + checkURL("sc:/\t/pa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"}); + checkURL("sc:\t//pa/", {"sc", "", "", "pa", 0, "/", "", "", "sc://pa/"}); + checkURL("http://host \a ", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkURL("notspecial:/a", {"notspecial", "", "", "", 0, "/a", "", "", "notspecial:/a"}); + checkURL("notspecial:", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"}); + checkURL("http:/a", {"http", "", "", "a", 0, "/", "", "", "http://a/"}); + checkURL("http://256../", {"http", "", "", "256..", 0, "/", "", "", "http://256../"}); + checkURL("http://256..", {"http", "", "", "256..", 0, "/", "", "", "http://256../"}); + checkURL("http://127..1/", {"http", "", "", "127..1", 0, "/", "", "", "http://127..1/"}); + checkURL("http://127.a.0.1/", {"http", "", "", "127.a.0.1", 0, "/", "", "", "http://127.a.0.1/"}); + checkURL("http://127.0.0.1/", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); + checkURL("http://12\t7.0.0.1/", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); + checkURL("http://127.\t0.0.1/", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); + checkURL("http://./", {"http", "", "", ".", 0, "/", "", "", "http://./"}); + checkURL("http://.", {"http", "", "", ".", 0, "/", "", "", "http://./"}); + checkURL("notspecial:/a", {"notspecial", "", "", "", 0, "/a", "", "", "notspecial:/a"}); + checkURL("notspecial:", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"}); + checkURL("notspecial:/", {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"}); + checkURL("data:image/png;base64,encoded-data-follows-here", {"data", "", "", "", 0, "image/png;base64,encoded-data-follows-here", "", "", "data:image/png;base64,encoded-data-follows-here"}); + checkURL("data:image/png;base64,encoded/data-with-slash", {"data", "", "", "", 0, "image/png;base64,encoded/data-with-slash", "", "", "data:image/png;base64,encoded/data-with-slash"}); + checkURL("about:~", {"about", "", "", "", 0, "~", "", "", "about:~"}); + checkURL("https://@test@test@example:800\\path@end", {"", "", "", "", 0, "", "", "", "https://@test@test@example:800\\path@end"}); + checkURL("http://www.example.com/#a\nb\rc\td", {"http", "", "", "www.example.com", 0, "/", "", "abcd", "http://www.example.com/#abcd"}); + checkURL("http://[A:b:c:DE:fF:0:1:aC]/", {"http", "", "", "[a:b:c:de:ff:0:1:ac]", 0, "/", "", "", "http://[a:b:c:de:ff:0:1:ac]/"}); + checkURL("http:////////user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"}); + checkURL("http:////////user:@webkit.org:99#foo", {"http", "user", "", "webkit.org", 99, "/", "", "foo", "http://user@webkit.org:99/#foo"}); + checkURL("http:////\t////user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"}); + checkURL("http://\t//\\///user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"}); + checkURL("http:/\\user:@webkit.org:99?foo", {"http", "user", "", "webkit.org", 99, "/", "foo", "", "http://user@webkit.org:99/?foo"}); + checkURL("http://127.0.0.1", {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}); + checkURLDifferences("http://127.0.0.1.", + {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}, + {"http", "", "", "127.0.0.1.", 0, "/", "", "", "http://127.0.0.1./"}); + checkURLDifferences("http://127.0.0.1./", + {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}, + {"http", "", "", "127.0.0.1.", 0, "/", "", "", "http://127.0.0.1./"}); + checkURL("http://127.0.0.1../", {"http", "", "", "127.0.0.1..", 0, "/", "", "", "http://127.0.0.1../"}); + checkURLDifferences("http://0x100.0/", + {"", "", "", "", 0, "", "", "", "http://0x100.0/"}, + {"http", "", "", "0x100.0", 0, "/", "", "", "http://0x100.0/"}); + checkURLDifferences("http://0.0.0x100.0/", + {"", "", "", "", 0, "", "", "", "http://0.0.0x100.0/"}, + {"http", "", "", "0.0.0x100.0", 0, "/", "", "", "http://0.0.0x100.0/"}); + checkURLDifferences("http://0.0.0.0x100/", + {"", "", "", "", 0, "", "", "", "http://0.0.0.0x100/"}, + {"http", "", "", "0.0.0.0x100", 0, "/", "", "", "http://0.0.0.0x100/"}); + checkURL("http://host:123?", {"http", "", "", "host", 123, "/", "", "", "http://host:123/?"}); + checkURL("http://host:123?query", {"http", "", "", "host", 123, "/", "query", "", "http://host:123/?query"}); + checkURL("http://host:123#", {"http", "", "", "host", 123, "/", "", "", "http://host:123/#"}); + checkURL("http://host:123#fragment", {"http", "", "", "host", 123, "/", "", "fragment", "http://host:123/#fragment"}); + checkURLDifferences("foo:////", + {"foo", "", "", "", 0, "//", "", "", "foo:////"}, + {"foo", "", "", "", 0, "////", "", "", "foo:////"}); + checkURLDifferences("foo:///?", + {"foo", "", "", "", 0, "/", "", "", "foo:///?"}, + {"foo", "", "", "", 0, "///", "", "", "foo:///?"}); + checkURLDifferences("foo:///#", + {"foo", "", "", "", 0, "/", "", "", "foo:///#"}, + {"foo", "", "", "", 0, "///", "", "", "foo:///#"}); + checkURLDifferences("foo:///", + {"foo", "", "", "", 0, "/", "", "", "foo:///"}, + {"foo", "", "", "", 0, "///", "", "", "foo:///"}); + checkURLDifferences("foo://?", + {"foo", "", "", "", 0, "", "", "", "foo://?"}, + {"foo", "", "", "", 0, "//", "", "", "foo://?"}); + checkURLDifferences("foo://#", + {"foo", "", "", "", 0, "", "", "", "foo://#"}, + {"foo", "", "", "", 0, "//", "", "", "foo://#"}); + checkURLDifferences("foo://", + {"foo", "", "", "", 0, "", "", "", "foo://"}, + {"foo", "", "", "", 0, "//", "", "", "foo://"}); + checkURL("foo:/?", {"foo", "", "", "", 0, "/", "", "", "foo:/?"}); + checkURL("foo:/#", {"foo", "", "", "", 0, "/", "", "", "foo:/#"}); + checkURL("foo:/", {"foo", "", "", "", 0, "/", "", "", "foo:/"}); + checkURL("foo:?", {"foo", "", "", "", 0, "", "", "", "foo:?"}); + checkURL("foo:#", {"foo", "", "", "", 0, "", "", "", "foo:#"}); + checkURLDifferences("A://", + {"a", "", "", "", 0, "", "", "", "a://"}, + {"a", "", "", "", 0, "//", "", "", "a://"}); + checkURLDifferences("aA://", + {"aa", "", "", "", 0, "", "", "", "aa://"}, + {"aa", "", "", "", 0, "//", "", "", "aa://"}); + checkURL(utf16String(u"foo://host/#ПП\u0007 a({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, validSurrogateEnd, '\0'}), + {"http", "", "", "w", 0, "/%F0%90%85%95", "", "", "http://w/%F0%90%85%95"}, testTabsValueForSurrogatePairs); + + // URLParser matches Chrome and Firefox but not URL::parse. + checkURLDifferences(utf16String<12>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, invalidSurrogateEnd}), + {"http", "", "", "w", 0, "/%EF%BF%BDA", "", "", "http://w/%EF%BF%BDA"}, + {"http", "", "", "w", 0, "/%ED%A0%80A", "", "", "http://w/%ED%A0%80A"}); + checkURLDifferences(utf16String<13>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, invalidSurrogateEnd, '\0'}), + {"http", "", "", "w", 0, "/", "%EF%BF%BDA", "", "http://w/?%EF%BF%BDA"}, + {"http", "", "", "w", 0, "/", "%ED%A0%80A", "", "http://w/?%ED%A0%80A"}); + checkURLDifferences(utf16String<11>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', surrogateBegin, '\0'}), + {"http", "", "", "w", 0, "/%EF%BF%BD", "", "", "http://w/%EF%BF%BD"}, + {"http", "", "", "w", 0, "/%ED%A0%80", "", "", "http://w/%ED%A0%80"}); + checkURLDifferences(utf16String<12>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, '\0'}), + {"http", "", "", "w", 0, "/", "%EF%BF%BD", "", "http://w/?%EF%BF%BD"}, + {"http", "", "", "w", 0, "/", "%ED%A0%80", "", "http://w/?%ED%A0%80"}); + checkURLDifferences(utf16String<13>({'h', 't', 't', 'p', ':', '/', '/', 'w', '/', '?', surrogateBegin, ' ', '\0'}), + {"http", "", "", "w", 0, "/", "%EF%BF%BD", "", "http://w/?%EF%BF%BD"}, + {"http", "", "", "w", 0, "/", "%ED%A0%80", "", "http://w/?%ED%A0%80"}); + + // FIXME: Write more invalid surrogate pair tests based on feedback from https://bugs.webkit.org/show_bug.cgi?id=162105 +} + +TEST_F(URLParserTest, QueryEncoding) +{ + checkURL(utf16String(u"http://host?ß😍#ß😍"), UTF8Encoding(), {"http", "", "", "host", 0, "/", "%C3%9F%F0%9F%98%8D", "%C3%9F%F0%9F%98%8D", utf16String(u"http://host/?%C3%9F%F0%9F%98%8D#%C3%9F%F0%9F%98%8D")}, testTabsValueForSurrogatePairs); + + TextEncoding latin1(String("latin1")); + checkURL("http://host/?query with%20spaces", latin1, {"http", "", "", "host", 0, "/", "query%20with%20spaces", "", "http://host/?query%20with%20spaces"}); + checkURL("http://host/?query", latin1, {"http", "", "", "host", 0, "/", "query", "", "http://host/?query"}); + checkURL("http://host/?\tquery", latin1, {"http", "", "", "host", 0, "/", "query", "", "http://host/?query"}); + checkURL("http://host/?q\tuery", latin1, {"http", "", "", "host", 0, "/", "query", "", "http://host/?query"}); + checkURL("http://host/?query with SpAcEs#fragment", latin1, {"http", "", "", "host", 0, "/", "query%20with%20SpAcEs", "fragment", "http://host/?query%20with%20SpAcEs#fragment"}); + checkURL("http://host/?que\rry\t\r\n#fragment", latin1, {"http", "", "", "host", 0, "/", "query", "fragment", "http://host/?query#fragment"}); + + TextEncoding unrecognized(String("unrecognized invalid encoding name")); + checkURL("http://host/?query", unrecognized, {"http", "", "", "host", 0, "/", "", "", "http://host/?"}); + checkURL("http://host/?", unrecognized, {"http", "", "", "host", 0, "/", "", "", "http://host/?"}); + + TextEncoding iso88591(String("ISO-8859-1")); + String withUmlauts = utf16String<4>({0xDC, 0x430, 0x451, '\0'}); + checkURL(makeString("ws://host/path?", withUmlauts), iso88591, {"ws", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "", "ws://host/path?%C3%9C%D0%B0%D1%91"}); + checkURL(makeString("wss://host/path?", withUmlauts), iso88591, {"wss", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "", "wss://host/path?%C3%9C%D0%B0%D1%91"}); + checkURL(makeString("asdf://host/path?", withUmlauts), iso88591, {"asdf", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "", "asdf://host/path?%C3%9C%D0%B0%D1%91"}); + checkURL(makeString("https://host/path?", withUmlauts), iso88591, {"https", "", "", "host", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "", "https://host/path?%DC%26%231072%3B%26%231105%3B"}); + checkURL(makeString("gopher://host/path?", withUmlauts), iso88591, {"gopher", "", "", "host", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "", "gopher://host/path?%DC%26%231072%3B%26%231105%3B"}); + checkURL(makeString("/path?", withUmlauts, "#fragment"), "ws://example.com/", iso88591, {"ws", "", "", "example.com", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "ws://example.com/path?%C3%9C%D0%B0%D1%91#fragment"}); + checkURL(makeString("/path?", withUmlauts, "#fragment"), "wss://example.com/", iso88591, {"wss", "", "", "example.com", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "wss://example.com/path?%C3%9C%D0%B0%D1%91#fragment"}); + checkURL(makeString("/path?", withUmlauts, "#fragment"), "asdf://example.com/", iso88591, {"asdf", "", "", "example.com", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "asdf://example.com/path?%C3%9C%D0%B0%D1%91#fragment"}); + checkURL(makeString("/path?", withUmlauts, "#fragment"), "https://example.com/", iso88591, {"https", "", "", "example.com", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "fragment", "https://example.com/path?%DC%26%231072%3B%26%231105%3B#fragment"}); + checkURL(makeString("/path?", withUmlauts, "#fragment"), "gopher://example.com/", iso88591, {"gopher", "", "", "example.com", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "fragment", "gopher://example.com/path?%DC%26%231072%3B%26%231105%3B#fragment"}); + checkURL(makeString("gopher://host/path?", withUmlauts, "#fragment"), "asdf://example.com/?doesntmatter", iso88591, {"gopher", "", "", "host", 0, "/path", "%DC%26%231072%3B%26%231105%3B", "fragment", "gopher://host/path?%DC%26%231072%3B%26%231105%3B#fragment"}); + checkURL(makeString("asdf://host/path?", withUmlauts, "#fragment"), "http://example.com/?doesntmatter", iso88591, {"asdf", "", "", "host", 0, "/path", "%C3%9C%D0%B0%D1%91", "fragment", "asdf://host/path?%C3%9C%D0%B0%D1%91#fragment"}); + + checkURL("http://host/?query=foo'bar", UTF8Encoding(), {"http", "", "", "host", 0, "/", "query=foo%27bar", "", "http://host/?query=foo%27bar"}); + // FIXME: Add more tests with other encodings and things like non-ascii characters, emoji and unmatched surrogate pairs. +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/UserAgentQuirks.cpp b/Tools/TestWebKitAPI/Tests/WebCore/UserAgentQuirks.cpp new file mode 100644 index 000000000..7fff8e361 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/UserAgentQuirks.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2014 Igalia S.L. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +static void assertUserAgentForURLHasChromeBrowserQuirk(const char* url) +{ + String uaString = standardUserAgentForURL(URL(ParsedURLString, url)); + + EXPECT_TRUE(uaString.contains("Chrome")); + EXPECT_TRUE(uaString.contains("Safari")); + EXPECT_FALSE(uaString.contains("Chromium")); + EXPECT_FALSE(uaString.contains("Firefox")); +} + +static void assertUserAgentForURLHasLinuxPlatformQuirk(const char* url) +{ + String uaString = standardUserAgentForURL(URL(ParsedURLString, url)); + + EXPECT_TRUE(uaString.contains("Linux")); + EXPECT_FALSE(uaString.contains("Macintosh")); + EXPECT_FALSE(uaString.contains("Mac OS X")); + EXPECT_FALSE(uaString.contains("Windows")); + EXPECT_FALSE(uaString.contains("Chrome")); + EXPECT_FALSE(uaString.contains("FreeBSD")); +} + +static void assertUserAgentForURLHasMacPlatformQuirk(const char* url) +{ + String uaString = standardUserAgentForURL(URL(ParsedURLString, url)); + + EXPECT_TRUE(uaString.contains("Macintosh")); + EXPECT_TRUE(uaString.contains("Mac OS X")); + EXPECT_FALSE(uaString.contains("Linux")); + EXPECT_FALSE(uaString.contains("Windows")); + EXPECT_FALSE(uaString.contains("Chrome")); + EXPECT_FALSE(uaString.contains("FreeBSD")); +} + +TEST(UserAgentTest, Quirks) +{ + // A site with not quirks should return a null String. + String uaString = standardUserAgentForURL(URL(ParsedURLString, "http://www.webkit.org/")); + EXPECT_TRUE(uaString.isNull()); + +#if !OS(LINUX) || !CPU(X86_64) + // Google quirk should not affect sites with similar domains. + uaString = standardUserAgentForURL(URL(ParsedURLString, "http://www.googleblog.com/")); + EXPECT_FALSE(uaString.contains("Linux x86_64")); +#endif + + assertUserAgentForURLHasChromeBrowserQuirk("http://typekit.com/"); + assertUserAgentForURLHasChromeBrowserQuirk("http://typekit.net/"); + + assertUserAgentForURLHasLinuxPlatformQuirk("http://www.google.com/"); + assertUserAgentForURLHasLinuxPlatformQuirk("http://www.google.es/"); + assertUserAgentForURLHasLinuxPlatformQuirk("http://calendar.google.com/"); + assertUserAgentForURLHasLinuxPlatformQuirk("http://plus.google.com/"); + + assertUserAgentForURLHasMacPlatformQuirk("http://www.yahoo.com/"); + assertUserAgentForURLHasMacPlatformQuirk("http://finance.yahoo.com/"); + assertUserAgentForURLHasMacPlatformQuirk("http://intl.taobao.com/"); + assertUserAgentForURLHasMacPlatformQuirk("http://www.whatsapp.com/"); + assertUserAgentForURLHasMacPlatformQuirk("http://web.whatsapp.com/"); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/YouTubePluginReplacement.cpp b/Tools/TestWebKitAPI/Tests/WebCore/YouTubePluginReplacement.cpp new file mode 100644 index 000000000..76b103575 --- /dev/null +++ b/Tools/TestWebKitAPI/Tests/WebCore/YouTubePluginReplacement.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include +#include +#include + +using namespace WebCore; + +namespace TestWebKitAPI { + +class YouTubePluginReplacementTest : public testing::Test { +public: + void SetUp() final { + WTF::initializeMainThread(); + } +}; + +static bool test(const String& inputURLString, const String& expectedURLString) +{ + URL inputURL(URL(), inputURLString); + String actualURLString = YouTubePluginReplacement::youTubeURLFromAbsoluteURL(inputURL, inputURLString); + return actualURLString.utf8() == expectedURLString.utf8(); +} + +TEST_F(YouTubePluginReplacementTest, YouTubeURLFromAbsoluteURL) +{ + // YouTube non-video URL, not expected to be transformed. + EXPECT_TRUE(test("https://www.youtube.com", "https://www.youtube.com")); + + // Basic YouTube video links, expected to be transformed. + EXPECT_TRUE(test("https://www.youtube.com/v/dQw4w9WgXcQ", "https://www.youtube.com/embed/dQw4w9WgXcQ")); + EXPECT_TRUE(test("http://www.youtube.com/v/dQw4w9WgXcQ", "http://www.youtube.com/embed/dQw4w9WgXcQ")); + EXPECT_TRUE(test("https://youtube.com/v/dQw4w9WgXcQ", "https://youtube.com/embed/dQw4w9WgXcQ")); + EXPECT_TRUE(test("http://youtube.com/v/dQw4w9WgXcQ", "http://youtube.com/embed/dQw4w9WgXcQ")); + + // With start time, preserved. + EXPECT_TRUE(test("http://www.youtube.com/v/dQw4w9WgXcQ?start=4", "http://www.youtube.com/embed/dQw4w9WgXcQ?start=4")); + EXPECT_TRUE(test("http://www.youtube.com/v/dQw4w9WgXcQ?start=4&fs=1", "http://www.youtube.com/embed/dQw4w9WgXcQ?start=4&fs=1")); + + // With an invalid query (see & instead of ?), we preserve and fix the query. + EXPECT_TRUE(test("http://www.youtube.com/v/dQw4w9WgXcQ&start=4", "http://www.youtube.com/embed/dQw4w9WgXcQ?start=4")); + EXPECT_TRUE(test("http://www.youtube.com/v/dQw4w9WgXcQ&start=4&fs=1", "http://www.youtube.com/embed/dQw4w9WgXcQ?start=4&fs=1")); + + // Non-Flash URL is untouched. + EXPECT_TRUE(test("https://www.youtube.com/embed/dQw4w9WgXcQ", "https://www.youtube.com/embed/dQw4w9WgXcQ")); + EXPECT_TRUE(test("http://www.youtube.com/embed/dQw4w9WgXcQ", "http://www.youtube.com/embed/dQw4w9WgXcQ")); + EXPECT_TRUE(test("https://youtube.com/embed/dQw4w9WgXcQ", "https://youtube.com/embed/dQw4w9WgXcQ")); + EXPECT_TRUE(test("http://youtube.com/embed/dQw4w9WgXcQ", "http://youtube.com/embed/dQw4w9WgXcQ")); + // Even with extra parameters. + EXPECT_TRUE(test("https://www.youtube.com/embed/dQw4w9WgXcQ?start=4", "https://www.youtube.com/embed/dQw4w9WgXcQ?start=4")); + EXPECT_TRUE(test("http://www.youtube.com/embed/dQw4w9WgXcQ?enablejsapi=1", "http://www.youtube.com/embed/dQw4w9WgXcQ?enablejsapi=1")); + // Even with an invalid "query". + EXPECT_TRUE(test("https://www.youtube.com/embed/dQw4w9WgXcQ&start=4", "https://www.youtube.com/embed/dQw4w9WgXcQ&start=4")); + + // Don't transform anything with a non "/v/" path component immediately following the domain. + EXPECT_TRUE(test("https://www.youtube.com/something/v/dQw4w9WgXcQ", "https://www.youtube.com/something/v/dQw4w9WgXcQ")); + + // Non-YouTube domain whose path looks like a Flash video shouldn't be transformed. + EXPECT_TRUE(test("https://www.notyoutube.com/v/dQw4w9WgXcQ", "https://www.notyoutube.com/v/dQw4w9WgXcQ")); +} + +} // namespace TestWebKitAPI diff --git a/Tools/TestWebKitAPI/Tests/WebCore/gtk/UserAgentQuirks.cpp b/Tools/TestWebKitAPI/Tests/WebCore/gtk/UserAgentQuirks.cpp deleted file mode 100644 index 40305e801..000000000 --- a/Tools/TestWebKitAPI/Tests/WebCore/gtk/UserAgentQuirks.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2014 Igalia S.L. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, - * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS - * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - * THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include - -using namespace WebCore; - -namespace TestWebKitAPI { - -TEST(WebCore, UserAgentQuirksTest) -{ - // A site with not quirks should return a null String. - String uaString = standardUserAgentForURL(URL(ParsedURLString, "http://www.webkit.org/")); - EXPECT_TRUE(uaString.isNull()); - - // www.yahoo.com requires MAC OS platform in the UA. - uaString = standardUserAgentForURL(URL(ParsedURLString, "http://www.yahoo.com/")); - EXPECT_TRUE(uaString.contains("Macintosh")); - EXPECT_TRUE(uaString.contains("Mac OS X")); - EXPECT_FALSE(uaString.contains("Linux")); - - // For Google domains we always return the standard UA. - uaString = standardUserAgent(); - EXPECT_FALSE(uaString.isNull()); - EXPECT_TRUE(uaString == standardUserAgentForURL(URL(ParsedURLString, "http://www.google.com/"))); - EXPECT_TRUE(uaString == standardUserAgentForURL(URL(ParsedURLString, "http://calendar.google.com/"))); - EXPECT_TRUE(uaString == standardUserAgentForURL(URL(ParsedURLString, "http://gmail.com/"))); - EXPECT_TRUE(uaString == standardUserAgentForURL(URL(ParsedURLString, "http://www.google.com.br/"))); - EXPECT_TRUE(uaString == standardUserAgentForURL(URL(ParsedURLString, "http://www.youtube.com/"))); - EXPECT_TRUE(uaString != standardUserAgentForURL(URL(ParsedURLString, "http://www.google.uk.not.com.br/"))); - - // For Google Maps we remove the Version/8.0 string. - uaString = standardUserAgentForURL(URL(ParsedURLString, "http://maps.google.com/")); - EXPECT_TRUE(uaString == standardUserAgentForURL(URL(ParsedURLString, "http://www.google.com/maps/"))); - EXPECT_FALSE(uaString.contains("Version/8.0 Safari/")); -} - -} // namespace TestWebKitAPI -- cgit v1.2.1