diff options
Diffstat (limited to 'Tools/TestWebKitAPI/Tests/WebCore')
35 files changed, 12762 insertions, 90 deletions
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 <WebCore/AffineTransform.h> +#include <WebCore/FloatPoint.h> +#include <WebCore/FloatQuad.h> +#include <WebCore/FloatRect.h> +#include <WebCore/FloatSize.h> +#include <WebCore/IntPoint.h> +#include <WebCore/IntRect.h> +#include <WebCore/IntSize.h> +#include <WebCore/TransformationMatrix.h> + +#if USE(CG) +#include <CoreGraphics/CoreGraphics.h> +#endif + +#if PLATFORM(WIN) +#include <d2d1.h> +#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 <WebCore/CARingBuffer.h> +#include <wtf/MainThread.h> + +using namespace WebCore; + +namespace TestWebKitAPI { + +class CARingBufferTest : public testing::Test { +public: + + virtual void SetUp() + { + WTF::initializeMainThread(); + m_ringBuffer = std::make_unique<CARingBuffer>(); + } + + // 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<uint32_t>(1, m_description.numberOfChannelStreams())); + m_bufferList = std::unique_ptr<AudioBufferList>(static_cast<AudioBufferList*>(::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<uint32_t>(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<AudioBufferList> m_bufferList; + std::unique_ptr<CARingBuffer> 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<uint8_t*>(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<uint8_t*>(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 <typename type> +class MixingTest { +public: + static void run(CARingBufferTest& test) + { + const int sampleCount = 64; + + CAAudioStreamDescription::PCMFormat format; + if (std::is_same<type, float>::value) + format = CAAudioStreamDescription::PCMFormat::Float32; + else if (std::is_same<type, double>::value) + format = CAAudioStreamDescription::PCMFormat::Float64; + else if (std::is_same<type, int32_t>::value) + format = CAAudioStreamDescription::PCMFormat::Int32; + else if (std::is_same<type, int16_t>::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<uint8_t*>(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<uint8_t*>(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<float>::run(*this); +} + +TEST_F(CARingBufferTest, DoubleMixing) +{ + MixingTest<double>::run(*this); +} + +TEST_F(CARingBufferTest, Int32Mixing) +{ + MixingTest<int32_t>::run(*this); +} + +TEST_F(CARingBufferTest, Int16Mixing) +{ + MixingTest<int16_t>::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 <WebCore/CSSParser.h> +#include <WebCore/CSSValueList.h> +#include <WebCore/StyleProperties.h> + +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<CSSValue> value = properties->getPropertyCSSValue(testCase.propertyID); + + ASSERT_TRUE(value->isValueList()); + EXPECT_EQ(computeNumberOfTracks(*downcast<CSSValueList>(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 <WebCore/CalculationValue.h> + +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<WebCore::CalculationValue> createTestValue() +{ + auto node = std::make_unique<CalculationDeletionTestNode>(); + return WebCore::CalculationValue::create(WTFMove(node), WebCore::ValueRangeAll); +} + +TEST(CalculationValue, LengthConstruction) +{ + RefPtr<WebCore::CalculationValue> 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<WebCore::CalculationValue> 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<WebCore::CalculationValue> 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<WebCore::CalculationValue> 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<WebCore::CalculationValue> 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<WebCore::CalculationValue> 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 <WebCore/Color.h> + +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 <JavaScriptCore/InitializeThreading.h> +#include <WebCore/ComplexTextController.h> +#include <WebCore/FontCascade.h> +#include <wtf/MainThread.h> +#include <wtf/RunLoop.h> + +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<FloatSize> advances = { FloatSize(), FloatSize(21.640625, 0.0), FloatSize(42.3046875, 0.0), FloatSize(55.8984375, 0.0), FloatSize(22.34375, 0.0) }; + Vector<FloatPoint> origins = { FloatPoint(-15.15625, 18.046875), FloatPoint(), FloatPoint(), FloatPoint(), FloatPoint() }; +#else + Vector<FloatSize> 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<FloatPoint> 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<Ref<ComplexTextController::ComplexTextRun>> 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<FloatSize> advances = { FloatSize(), FloatSize(21.640625, 0.0), FloatSize(42.3046875, 0.0), FloatSize(55.8984375, 0.0), FloatSize(22.34375, 0.0) }; + Vector<FloatPoint> origins = { FloatPoint(-15.15625, 18.046875), FloatPoint(), FloatPoint(), FloatPoint(), FloatPoint() }; +#else + Vector<FloatSize> 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<FloatPoint> 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<Ref<ComplexTextController::ComplexTextRun>> 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<FloatSize> advances = { FloatSize(76.347656, 0.000000), FloatSize(0.000000, 0.000000) }; + Vector<FloatPoint> origins = { FloatPoint(), FloatPoint(-23.281250, -8.398438) }; +#else + Vector<FloatSize> advances = { FloatSize(53.066406, -8.398438), FloatSize(23.281250, 8.398438) }; + Vector<FloatPoint> 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<Ref<ComplexTextController::ComplexTextRun>> 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<FloatSize> advances = { FloatSize(76.347656, 0.000000), FloatSize(0.000000, 0.000000) }; + Vector<FloatPoint> origins = { FloatPoint(), FloatPoint(-23.281250, -8.398438) }; +#else + Vector<FloatSize> advances = { FloatSize(53.066406, -8.398438), FloatSize(23.281250, 8.398438) }; + Vector<FloatPoint> 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<Ref<ComplexTextController::ComplexTextRun>> 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<Ref<ComplexTextController::ComplexTextRun>> 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<Ref<ComplexTextController::ComplexTextRun>> 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<Ref<ComplexTextController::ComplexTextRun>> 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<FloatSize> advances = { FloatSize(1, 0), FloatSize(2, 0), FloatSize(4, 0), FloatSize(8, 0), FloatSize(16, 0) }; +#if USE_LAYOUT_SPECIFIC_ADVANCES + Vector<FloatPoint> origins = { FloatPoint(), FloatPoint(), FloatPoint(), FloatPoint(), FloatPoint() }; +#else + Vector<FloatPoint> 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<Ref<ComplexTextController::ComplexTextRun>> 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 <JavaScriptCore/InitializeThreading.h> +#include <WebCore/CombinedURLFilters.h> +#include <WebCore/ContentExtensionCompiler.h> +#include <WebCore/ContentExtensionError.h> +#include <WebCore/ContentExtensionsBackend.h> +#include <WebCore/DFA.h> +#include <WebCore/DFABytecodeCompiler.h> +#include <WebCore/DFABytecodeInterpreter.h> +#include <WebCore/NFA.h> +#include <WebCore/NFAToDFA.h> +#include <WebCore/ResourceLoadInfo.h> +#include <WebCore/URL.h> +#include <WebCore/URLFilterParser.h> +#include <wtf/MainThread.h> +#include <wtf/RunLoop.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringBuilder.h> + +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<ContentExtensions::SerializedActionByte> actions; + Vector<ContentExtensions::DFABytecode> filtersWithoutDomains; + Vector<ContentExtensions::DFABytecode> filtersWithDomains; + Vector<ContentExtensions::DFABytecode> 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<ContentExtensions::SerializedActionByte>&& 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<ContentExtensions::DFABytecode>&& 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<ContentExtensions::DFABytecode>&& bytecode) override + { + EXPECT_FALSE(finalized); + EXPECT_EQ(m_data.domainFilters.size(), 0ull); + m_data.filtersWithDomains.appendVector(bytecode); + } + + void writeDomainFiltersBytecode(Vector<ContentExtensions::DFABytecode>&& 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<InMemoryCompiledContentExtension> 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<InMemoryCompiledContentExtension> 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<ContentExtensions::ActionType> 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<ContentExtensions::NFA> createNFAs(ContentExtensions::CombinedURLFilters& combinedURLFilters) +{ + Vector<ContentExtensions::NFA> nfas; + + combinedURLFilters.processNFAs(std::numeric_limits<size_t>::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<uint64_t>& 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<ContentExtensions::NFA> nfas = createNFAs(combinedURLFilters); + EXPECT_EQ(1ul, nfas.size()); + EXPECT_EQ(12ul, nfas.first().nodes.size()); + + ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first()); + Vector<ContentExtensions::DFABytecode> 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<ContentExtensions::NFA> nfas = createNFAs(combinedURLFilters); + + EXPECT_EQ(1ul, nfas.size()); + EXPECT_EQ(17ul, nfas.first().nodes.size()); + + ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first()); + Vector<ContentExtensions::DFABytecode> 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<ContentExtensions::NFA> nfas = createNFAs(combinedURLFilters); + EXPECT_EQ(1ul, nfas.size()); + EXPECT_EQ(7ul, nfas.first().nodes.size()); + + ContentExtensions::DFA dfa = ContentExtensions::NFAToDFA::convert(nfas.first()); + Vector<ContentExtensions::DFABytecode> 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<ContentExtensions::NFA> nfas; + combinedURLFilters.processNFAs(std::numeric_limits<size_t>::max(), [&](ContentExtensions::NFA&& nfa) { + nfas.append(WTFMove(nfa)); + }); + EXPECT_EQ(nfas.size(), 1ull); + + Vector<ContentExtensions::DFA> dfas; + for (auto& nfa : nfas) + dfas.append(ContentExtensions::NFAToDFA::convert(nfa)); + EXPECT_EQ(dfas.size(), 1ull); + + Vector<ContentExtensions::DFABytecode> combinedBytecode; + for (const auto& dfa : dfas) { + Vector<ContentExtensions::DFABytecode> 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<ContentExtensions::NFA> nfas; + combinedURLFilters.processNFAs(i, [&](ContentExtensions::NFA&& nfa) { + nfas.append(WTFMove(nfa)); + }); + EXPECT_EQ(nfas.size(), expectedNFACounts[i]); + + Vector<ContentExtensions::DFA> dfas; + for (auto& nfa : nfas) + dfas.append(ContentExtensions::NFAToDFA::convert(nfa)); + + Vector<ContentExtensions::DFABytecode> combinedBytecode; + for (const auto& dfa : dfas) { + Vector<ContentExtensions::DFABytecode> 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 <WebCore/DFACombiner.h> +#include <wtf/MainThread.h> + +using namespace WebCore; +using namespace ContentExtensions; + +namespace TestWebKitAPI { + +class DFACombinerTest : public testing::Test { +public: + virtual void SetUp() + { + WTF::initializeMainThread(); + } +}; + +Vector<DFA> combine(Vector<DFA> dfas, unsigned minimumSize) +{ + DFACombiner combiner; + for (DFA& dfa : dfas) + combiner.addDFA(WTFMove(dfa)); + + Vector<DFA> output; + combiner.combineDFAs(minimumSize, [&output](DFA&& dfa) { + output.append(dfa); + }); + return output; +} + +TEST_F(DFACombinerTest, Basic) +{ + Vector<DFA> dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "bar"}) }; + Vector<DFA> combinedDFAs = combine(dfas, 10000); + EXPECT_EQ(static_cast<size_t>(1), combinedDFAs.size()); + + DFA reference = buildDFAFromPatterns({ "foo", "bar"}); + reference.minimize(); + EXPECT_EQ(countLiveNodes(reference), countLiveNodes(combinedDFAs.first())); +} + + +TEST_F(DFACombinerTest, IdenticalDFAs) +{ + Vector<DFA> dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "foo"}) }; + Vector<DFA> combinedDFAs = combine(dfas, 10000); + EXPECT_EQ(static_cast<size_t>(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<unsigned>(0), counter); +} + +TEST_F(DFACombinerTest, SingleInput) +{ + Vector<DFA> dfas = { buildDFAFromPatterns({ "WebKit"}) }; + Vector<DFA> combinedDFAs = combine(dfas, 10000); + EXPECT_EQ(static_cast<size_t>(1), combinedDFAs.size()); + + DFA reference = buildDFAFromPatterns({ "WebKit"}); + reference.minimize(); + EXPECT_EQ(countLiveNodes(reference), countLiveNodes(combinedDFAs.first())); +} + +TEST_F(DFACombinerTest, InputTooLargeForMinimumSize) +{ + Vector<DFA> dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "bar"}) }; + Vector<DFA> combinedDFAs = combine(dfas, 2); + EXPECT_EQ(static_cast<size_t>(2), combinedDFAs.size()); + EXPECT_EQ(static_cast<size_t>(4), countLiveNodes(combinedDFAs[0])); + EXPECT_EQ(static_cast<size_t>(4), countLiveNodes(combinedDFAs[1])); +} + +TEST_F(DFACombinerTest, CombinedInputReachesMinimumSize) +{ + Vector<DFA> dfas = { buildDFAFromPatterns({ "foo"}), buildDFAFromPatterns({ "bar"}), buildDFAFromPatterns({ "WebKit"}) }; + Vector<DFA> combinedDFAs = combine(dfas, 5); + EXPECT_EQ(static_cast<size_t>(2), combinedDFAs.size()); + EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(combinedDFAs[0])); + EXPECT_EQ(static_cast<size_t>(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 <WebCore/CombinedURLFilters.h> +#include <WebCore/NFA.h> +#include <WebCore/NFAToDFA.h> +#include <WebCore/URLFilterParser.h> + +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<ContentExtensions::NFA> createNFAs(ContentExtensions::CombinedURLFilters& combinedURLFilters) +{ + Vector<ContentExtensions::NFA> nfas; + + combinedURLFilters.processNFAs(std::numeric_limits<size_t>::max(), [&](ContentExtensions::NFA&& nfa) { + nfas.append(WTFMove(nfa)); + }); + + return nfas; +} + +static ContentExtensions::DFA buildDFAFromPatterns(Vector<const char*> patterns) +{ + ContentExtensions::CombinedURLFilters combinedURLFilters; + ContentExtensions::URLFilterParser parser(combinedURLFilters); + + for (const char* pattern : patterns) + parser.addPattern(pattern, false, 0); + Vector<ContentExtensions::NFA> 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 <wtf/MainThread.h> + +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<size_t>(8), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, MergeSuffixes) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ ".*aaa", ".*aab", ".*aba", ".*abb", ".*baa", ".*bab", ".*bba", ".*bbb"}); + EXPECT_EQ(static_cast<size_t>(12), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast<size_t>(4), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, MergeInfixes) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ ".*aaakit", ".*aabkit", ".*abakit", ".*abbkit", ".*baakit", ".*babkit", ".*bbakit", ".*bbbkit"}); + EXPECT_EQ(static_cast<size_t>(15), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge1) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.a", "^b.a", "^bac", "^bbc", "^BCC"}); + EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge2) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^bbc", "^BCC", "^a.a", "^b.a"}); + EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge3) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.c", "^b.c", "^baa", "^bba", "^BCA"}); + EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, FallbackTransitionsWithDifferentiatorDoNotMerge4) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^baa", "^bba", "^BCA", "^a.c", "^b.c"}); + EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast<size_t>(6), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, FallbackTransitionsToOtherNodeInSameGroupDoesNotDifferentiateGroup) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^aac", "^a.c", "^b.c"}); + EXPECT_EQ(static_cast<size_t>(5), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast<size_t>(4), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, SimpleFallBackTransitionDifferentiator1) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^a.bc.de", "^a.bd.ef"}); + EXPECT_EQ(static_cast<size_t>(11), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast<size_t>(11), countLiveNodes(dfa)); +} + +TEST_F(DFAMinimizerTest, SimpleFallBackTransitionDifferentiator2) +{ + ContentExtensions::DFA dfa = buildDFAFromPatterns({ "^cb.", "^db.b"}); + EXPECT_EQ(static_cast<size_t>(7), countLiveNodes(dfa)); + dfa.minimize(); + EXPECT_EQ(static_cast<size_t>(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 <WebCore/Color.h> + +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 <WebCore/FileSystem.h> +#include <wtf/MainThread.h> +#include <wtf/StringExtras.h> + +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<const char*>(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 <WebCore/AffineTransform.h> +#include <WebCore/FloatPoint.h> +#include <WebCore/FloatSize.h> +#include <WebCore/IntPoint.h> +#include <WebCore/IntSize.h> +#include <WebCore/TransformationMatrix.h> + +#if USE(CG) +#include <CoreGraphics/CoreGraphics.h> +#endif + +#if PLATFORM(WIN) +#include <d2d1.h> +#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 <WebCore/FloatPoint.h> +#include <WebCore/FloatRect.h> +#include <WebCore/FloatSize.h> +#include <WebCore/IntRect.h> + +#if USE(CG) +#include <CoreGraphics/CoreGraphics.h> +#endif + +#if PLATFORM(WIN) +#include <d2d1.h> +#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<float>::max() / 2, infinite.x()); + EXPECT_FLOAT_EQ(-std::numeric_limits<float>::max() / 2, infinite.y()); + EXPECT_FLOAT_EQ(std::numeric_limits<float>::max(), infinite.width()); + EXPECT_FLOAT_EQ(std::numeric_limits<float>::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<float>::max() / 2, cgInfiniteRect.origin.x); + EXPECT_FLOAT_EQ(-std::numeric_limits<float>::max() / 2, cgInfiniteRect.origin.y); + EXPECT_FLOAT_EQ(std::numeric_limits<float>::max(), cgInfiniteRect.size.width); + EXPECT_FLOAT_EQ(std::numeric_limits<float>::max(), cgInfiniteRect.size.height); +#else + EXPECT_FLOAT_EQ(-std::numeric_limits<float>::max(), cgInfiniteRect.origin.x); + EXPECT_FLOAT_EQ(-std::numeric_limits<float>::max(), cgInfiniteRect.origin.y); + EXPECT_FLOAT_EQ(std::numeric_limits<float>::max(), cgInfiniteRect.origin.x + cgInfiniteRect.size.width); + EXPECT_FLOAT_EQ(std::numeric_limits<float>::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<float>::max(), d2dInfiniteRect.left); + EXPECT_FLOAT_EQ(-std::numeric_limits<float>::max(), d2dInfiniteRect.top); + EXPECT_FLOAT_EQ(std::numeric_limits<float>::max(), d2dInfiniteRect.right); + EXPECT_FLOAT_EQ(std::numeric_limits<float>::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 <WebCore/FloatSize.h> +#include <WebCore/IntPoint.h> +#include <WebCore/IntSize.h> + +#if USE(CG) +#include <CoreGraphics/CoreGraphics.h> +#endif + +#if PLATFORM(WIN) +#include <d2d1.h> +#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 <WebCore/GridPosition.h> + +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 <WebCore/HTMLParserIdioms.h> +#include <wtf/text/WTFString.h> + +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 <WebCore/FloatPoint.h> +#include <WebCore/IntPoint.h> +#include <WebCore/IntSize.h> + +#if USE(CG) +#include <CoreGraphics/CoreGraphics.h> +#endif + +#if PLATFORM(WIN) +#include <d2d1.h> +#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 <WebCore/FloatRect.h> +#include <WebCore/IntPoint.h> +#include <WebCore/IntRect.h> +#include <WebCore/IntSize.h> + +#if USE(CG) +#include <CoreGraphics/CoreGraphics.h> +#endif + +#if PLATFORM(WIN) +#include <d2d1.h> +#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 <WebCore/FloatSize.h> +#include <WebCore/IntSize.h> + +#if USE(CG) +#include <CoreGraphics/CoreGraphics.h> +#endif + +#if PLATFORM(WIN) +#include <d2d1.h> +#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 <WebCore/LayoutUnit.h> @@ -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 <WebCore/ParsedContentRange.h> +#include <wtf/text/WTFString.h> + +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 <WebCore/PublicSuffix.h> +#include <wtf/MainThread.h> + +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 <WebCore/MediaSample.h> +#include <WebCore/SampleMap.h> + +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<TestSample> 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<MediaSample>, RefPtr<MediaSample>> divide(const MediaTime& presentationTime) final { return { }; } + Ref<MediaSample> createNonDisplayingCopy() const final { + return create(m_presentationTime, m_decodeTime, m_duration, static_cast<SampleFlags>(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 <WebCore/FileSystem.h> +#include <WebCore/SecurityOrigin.h> +#include <WebCore/URL.h> +#include <wtf/MainThread.h> + +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<SecurityOrigin> o1 = SecurityOrigin::create("http", "example.com", std::optional<uint16_t>(80)); + Ref<SecurityOrigin> o2 = SecurityOrigin::create("http", "example.com", std::optional<uint16_t>()); + Ref<SecurityOrigin> o3 = SecurityOrigin::createFromString("http://example.com"); + Ref<SecurityOrigin> o4 = SecurityOrigin::createFromString("http://example.com:80"); + Ref<SecurityOrigin> o5 = SecurityOrigin::create(URL(URL(), "http://example.com")); + Ref<SecurityOrigin> 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 <WebCore/SharedBuffer.h> +#include <wtf/MainThread.h> +#include <wtf/StringExtras.h> + +using namespace WebCore; + +namespace TestWebKitAPI { + +TEST_F(SharedBufferTest, createWithContentsOfMissingFile) +{ + RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(String("not_existing_file")); + ASSERT_NULL(buffer); +} + +TEST_F(SharedBufferTest, createWithContentsOfExistingFile) +{ + RefPtr<SharedBuffer> 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<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(tempEmptyFilePath()); + ASSERT_NOT_NULL(buffer); + EXPECT_TRUE(buffer->isEmpty()); +} + +TEST_F(SharedBufferTest, copyBufferCreatedWithContentsOfExistingFile) +{ + RefPtr<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(tempFilePath()); + ASSERT_NOT_NULL(buffer); + RefPtr<SharedBuffer> 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<SharedBuffer> buffer = SharedBuffer::createWithContentsOfFile(tempFilePath()); + ASSERT_NOT_NULL(buffer); + buffer->clear(); + EXPECT_TRUE(!buffer->size()); + EXPECT_TRUE(!buffer->data()); +} + +TEST_F(SharedBufferTest, appendBufferCreatedWithContentsOfExistingFile) +{ + RefPtr<SharedBuffer> 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 = SharedBuffer::create(testData0, strlen(testData0)); + sharedBuffer->append(testData1, strlen(testData1)); + sharedBuffer->append(testData2, strlen(testData2)); + RefPtr<ArrayBuffer> 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<char> vector0(0x4000, 'a'); + Vector<char> vector1(0x4000, 'b'); + Vector<char> vector2(0x4000, 'c'); + + RefPtr<SharedBuffer> sharedBuffer = SharedBuffer::adoptVector(vector0); + sharedBuffer->append(vector1); + sharedBuffer->append(vector2); + RefPtr<ArrayBuffer> 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<char*>(arrayBuffer->data())[position]); + ++position; + } + for (int i = 0; i < 0x4000; ++i) { + EXPECT_EQ('b', static_cast<char*>(arrayBuffer->data())[position]); + ++position; + } + for (int i = 0; i < 0x4000; ++i) { + EXPECT_EQ('c', static_cast<char*>(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 = 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<SharedBuffer> 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 <WebCore/FileSystem.h> +#include <wtf/MainThread.h> + +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 <wtf/text/WTFString.h> + +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 <WebCore/TimeRanges.h> + +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<TimeRanges> rangeA = TimeRanges::create(); + RefPtr<TimeRanges> 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<TimeRanges> 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<TimeRanges> ranges = TimeRanges::create(0, 2); + + ASSERT_RANGE("{ [0,2) }", ranges); + + ranges->intersectWith(*ranges.get()); + + ASSERT_RANGE("{ [0,2) }", ranges); +} + +TEST(TimeRanges, IntersectWith_IdenticalRange) +{ + RefPtr<TimeRanges> rangesA = TimeRanges::create(0, 2); + RefPtr<TimeRanges> 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<TimeRanges> rangesA = TimeRanges::create(0, 2); + RefPtr<TimeRanges> 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<TimeRanges> rangesA = TimeRanges::create(); + RefPtr<TimeRanges> 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<TimeRanges> rangesA = TimeRanges::create(); + RefPtr<TimeRanges> 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<TimeRanges> rangesA = TimeRanges::create(); + RefPtr<TimeRanges> 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<TimeRanges> rangesA = TimeRanges::create(); + RefPtr<TimeRanges> 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<TimeRanges> rangesA = TimeRanges::create(); + RefPtr<TimeRanges> 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<TimeRanges> rangesA = TimeRanges::create(); + RefPtr<TimeRanges> 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<TimeRanges> rangesA = TimeRanges::create(); + RefPtr<TimeRanges> 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 <WebCore/AffineTransform.h> +#include <WebCore/FloatPoint.h> +#include <WebCore/FloatQuad.h> +#include <WebCore/FloatRect.h> +#include <WebCore/IntPoint.h> +#include <WebCore/IntRect.h> +#include <WebCore/TransformationMatrix.h> + +#if USE(CG) +#include <CoreGraphics/CoreGraphics.h> +#endif + +#if USE(CA) +#include <QuartzCore/QuartzCore.h> +#endif + +#if PLATFORM(WIN) +#include <d2d1.h> +#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 <WebCore/URL.h> +#include <WebCore/URLParser.h> #include <wtf/MainThread.h> 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 <WebCore/URLParser.h> +#include <wtf/MainThread.h> +#include <wtf/text/StringBuilder.h> + +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</"), {"foo", "", "", "host", 0, "/", "", "%D0%9F%D0%9F%07 a</", "foo://host/#%D0%9F%D0%9F%07 a</"}); + checkURL(utf16String(u"foo://host/#\u0007 a</"), {"foo", "", "", "host", 0, "/", "", "%07 a</", "foo://host/#%07 a</"}); + checkURL(utf16String(u"http://host?ß😍#ß😍"), {"http", "", "", "host", 0, "/", "%C3%9F%F0%9F%98%8D", "%C3%9F%F0%9F%98%8D", "http://host/?%C3%9F%F0%9F%98%8D#%C3%9F%F0%9F%98%8D"}, testTabsValueForSurrogatePairs); + checkURL(utf16String(u"http://host/path#💩\t💩"), {"http", "", "", "host", 0, "/path", "", "%F0%9F%92%A9%F0%9F%92%A9", "http://host/path#%F0%9F%92%A9%F0%9F%92%A9"}, testTabsValueForSurrogatePairs); + checkURL(utf16String(u"http://host/#ПП\u0007 a</"), {"http", "", "", "host", 0, "/", "", "%D0%9F%D0%9F%07 a</", "http://host/#%D0%9F%D0%9F%07 a</"}); + checkURL(utf16String(u"http://host/#\u0007 a</"), {"http", "", "", "host", 0, "/", "", "%07 a</", "http://host/#%07 a</"}); + + // This disagrees with the web platform test for http://:@www.example.com but agrees with Chrome and URL::parse, + // and Firefox fails the web platform test differently. Maybe the web platform test ought to be changed. + checkURL("http://:@host", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); +} + +TEST_F(URLParserTest, ParseRelative) +{ + checkRelativeURL("/index.html", "http://webkit.org/path1/path2/", {"http", "", "", "webkit.org", 0, "/index.html", "", "", "http://webkit.org/index.html"}); + checkRelativeURL("http://whatwg.org/index.html", "http://webkit.org/path1/path2/", {"http", "", "", "whatwg.org", 0, "/index.html", "", "", "http://whatwg.org/index.html"}); + checkRelativeURL("index.html", "http://webkit.org/path1/path2/page.html?query#fragment", {"http", "", "", "webkit.org", 0, "/path1/path2/index.html", "", "", "http://webkit.org/path1/path2/index.html"}); + checkRelativeURL("//whatwg.org/index.html", "https://www.webkit.org/path", {"https", "", "", "whatwg.org", 0, "/index.html", "", "", "https://whatwg.org/index.html"}); + checkRelativeURL("http://example\t.\norg", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/", "", "", "http://example.org/"}); + checkRelativeURL("test", "file:///path1/path2", {"file", "", "", "", 0, "/path1/test", "", "", "file:///path1/test"}); + checkRelativeURL(utf16String(u"http://www.foo。bar.com"), "http://other.com/", {"http", "", "", "www.foo.bar.com", 0, "/", "", "", "http://www.foo.bar.com/"}); + checkRelativeURLDifferences(utf16String(u"sc://ñ.test/"), "about:blank", + {"sc", "", "", "%C3%B1.test", 0, "/", "", "", "sc://%C3%B1.test/"}, + {"sc", "", "", "xn--ida.test", 0, "/", "", "", "sc://xn--ida.test/"}); + checkRelativeURL("#fragment", "http://host/path", {"http", "", "", "host", 0, "/path", "", "fragment", "http://host/path#fragment"}); + checkRelativeURL("#fragment", "file:///path", {"file", "", "", "", 0, "/path", "", "fragment", "file:///path#fragment"}); + checkRelativeURL("#fragment", "file:///path#old", {"file", "", "", "", 0, "/path", "", "fragment", "file:///path#fragment"}); + checkRelativeURL("#", "file:///path#old", {"file", "", "", "", 0, "/path", "", "", "file:///path#"}); + checkRelativeURL(" ", "file:///path#old", {"file", "", "", "", 0, "/path", "", "", "file:///path"}); + checkRelativeURL("#", "file:///path", {"file", "", "", "", 0, "/path", "", "", "file:///path#"}); + checkRelativeURL("#", "file:///path?query", {"file", "", "", "", 0, "/path", "query", "", "file:///path?query#"}); + checkRelativeURL("#", "file:///path?query#old", {"file", "", "", "", 0, "/path", "query", "", "file:///path?query#"}); + checkRelativeURL("?query", "http://host/path", {"http", "", "", "host", 0, "/path", "query", "", "http://host/path?query"}); + checkRelativeURL("?query#fragment", "http://host/path", {"http", "", "", "host", 0, "/path", "query", "fragment", "http://host/path?query#fragment"}); + checkRelativeURL("?new", "file:///path?old#fragment", {"file", "", "", "", 0, "/path", "new", "", "file:///path?new"}); + checkRelativeURL("?", "file:///path?old#fragment", {"file", "", "", "", 0, "/path", "", "", "file:///path?"}); + checkRelativeURL("?", "file:///path", {"file", "", "", "", 0, "/path", "", "", "file:///path?"}); + checkRelativeURL("?query", "file:///path", {"file", "", "", "", 0, "/path", "query", "", "file:///path?query"}); + checkRelativeURL(utf16String(u"?β"), "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "%CE%B2", "", "http://example.org/foo/bar?%CE%B2"}); + checkRelativeURL("?", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar?"}); + checkRelativeURL("#", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar#"}); + checkRelativeURL("?#", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar?#"}); + checkRelativeURL("#?", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "?", "http://example.org/foo/bar#?"}); + checkRelativeURL("/", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/", "", "", "http://example.org/"}); + checkRelativeURL("http://@host", "about:blank", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkRelativeURL("http://:@host", "about:blank", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkRelativeURL("http://foo.com/\\@", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "//@", "", "", "http://foo.com//@"}); + checkRelativeURL("\\@", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/@", "", "", "http://example.org/@"}); + checkRelativeURL("/path3", "http://user@example.org/path1/path2", {"http", "user", "", "example.org", 0, "/path3", "", "", "http://user@example.org/path3"}); + checkRelativeURL("", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"}); + checkRelativeURL("\t", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"}); + checkRelativeURL(" ", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"}); + checkRelativeURL(" \a \t\n", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"}); + checkRelativeURL(":foo.com\\", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/:foo.com/", "", "", "http://example.org/foo/:foo.com/"}); + checkRelativeURL("http:/example.com/", "about:blank", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); + checkRelativeURL("http:example.com/", "about:blank", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); + checkRelativeURL("http:\\\\foo.com\\", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"}); + checkRelativeURL("http:\\\\foo.com/", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"}); + checkRelativeURL("http:\\\\foo.com", "http://example.org/foo/bar", {"http", "", "", "foo.com", 0, "/", "", "", "http://foo.com/"}); + checkRelativeURL("http://ExAmPlE.CoM", "http://other.com", {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); + checkRelativeURL("http:", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "", "http://example.org/foo/bar"}); + checkRelativeURL("#x", "data:,", {"data", "", "", "", 0, ",", "", "x", "data:,#x"}); + checkRelativeURL("#x", "about:blank", {"about", "", "", "", 0, "blank", "", "x", "about:blank#x"}); + checkRelativeURL(" foo.com ", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/foo.com", "", "", "http://example.org/foo/foo.com"}); + checkRelativeURL(" \a baz", "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/baz", "", "", "http://example.org/foo/baz"}); + checkRelativeURL("~", "http://example.org", {"http", "", "", "example.org", 0, "/~", "", "", "http://example.org/~"}); + checkRelativeURL("notspecial:", "about:blank", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"}); + checkRelativeURL("notspecial:", "http://host", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"}); + checkRelativeURL("http:", "http://host", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkRelativeURL("i", "sc:/pa/po", {"sc", "", "", "", 0, "/pa/i", "", "", "sc:/pa/i"}); + checkRelativeURL("i ", "sc:/pa/po", {"sc", "", "", "", 0, "/pa/i", "", "", "sc:/pa/i"}); + checkRelativeURL("i\t\n ", "sc:/pa/po", {"sc", "", "", "", 0, "/pa/i", "", "", "sc:/pa/i"}); + checkRelativeURL("i", "sc://ho/pa", {"sc", "", "", "ho", 0, "/i", "", "", "sc://ho/i"}); + checkRelativeURL("!", "sc://ho/pa", {"sc", "", "", "ho", 0, "/!", "", "", "sc://ho/!"}); + checkRelativeURL("!", "sc:/ho/pa", {"sc", "", "", "", 0, "/ho/!", "", "", "sc:/ho/!"}); + checkRelativeURL("notspecial:/", "about:blank", {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"}); + checkRelativeURL("notspecial:/", "http://host", {"notspecial", "", "", "", 0, "/", "", "", "notspecial:/"}); + checkRelativeURL("foo:/", "http://example.org/foo/bar", {"foo", "", "", "", 0, "/", "", "", "foo:/"}); + checkRelativeURL("://:0/", "http://webkit.org/", {"http", "", "", "webkit.org", 0, "/://:0/", "", "", "http://webkit.org/://:0/"}); + checkRelativeURL(String(), "http://webkit.org/", {"http", "", "", "webkit.org", 0, "/", "", "", "http://webkit.org/"}); + checkRelativeURL("https://@test@test@example:800\\path@end", "http://doesnotmatter/", {"", "", "", "", 0, "", "", "", "https://@test@test@example:800\\path@end"}); + checkRelativeURL("http://f:0/c", "http://example.org/foo/bar", {"http", "", "", "f", 0, "/c", "", "", "http://f:0/c"}); + checkRelativeURL(String(), "http://host/#fragment", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkRelativeURL("", "http://host/#fragment", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkRelativeURL(" ", "http://host/#fragment", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkRelativeURL(" ", "http://host/path?query#fra#gment", {"http", "", "", "host", 0, "/path", "query", "", "http://host/path?query"}); + checkRelativeURL(" \a ", "http://host/#fragment", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkRelativeURLDifferences("foo://", "http://example.org/foo/bar", + {"foo", "", "", "", 0, "", "", "", "foo://"}, + {"foo", "", "", "", 0, "//", "", "", "foo://"}); + checkRelativeURL(utf16String(u"#β"), "http://example.org/foo/bar", {"http", "", "", "example.org", 0, "/foo/bar", "", "%CE%B2", "http://example.org/foo/bar#%CE%B2"}); + checkRelativeURL("index.html", "applewebdata://Host/", {"applewebdata", "", "", "Host", 0, "/index.html", "", "", "applewebdata://Host/index.html"}); + checkRelativeURL("index.html", "applewebdata://Host", {"applewebdata", "", "", "Host", 0, "/index.html", "", "", "applewebdata://Host/index.html"}); + checkRelativeURL("", "applewebdata://Host", {"applewebdata", "", "", "Host", 0, "", "", "", "applewebdata://Host"}); + checkRelativeURL("?query", "applewebdata://Host", {"applewebdata", "", "", "Host", 0, "", "query", "", "applewebdata://Host?query"}); + checkRelativeURL("#fragment", "applewebdata://Host", {"applewebdata", "", "", "Host", 0, "", "", "fragment", "applewebdata://Host#fragment"}); + checkRelativeURL("notspecial://something?", "file:////var//containers//stuff/", {"notspecial", "", "", "something", 0, "", "", "", "notspecial://something?"}, TestTabs::No); + checkRelativeURL("notspecial://something#", "file:////var//containers//stuff/", {"notspecial", "", "", "something", 0, "", "", "", "notspecial://something#"}, TestTabs::No); + checkRelativeURL("http://something?", "file:////var//containers//stuff/", {"http", "", "", "something", 0, "/", "", "", "http://something/?"}, TestTabs::No); + checkRelativeURL("http://something#", "file:////var//containers//stuff/", {"http", "", "", "something", 0, "/", "", "", "http://something/#"}, TestTabs::No); + checkRelativeURL("file:", "file:///path?query#fragment", {"file", "", "", "", 0, "/path", "query", "", "file:///path?query"}); + checkRelativeURL("/", "file:///C:/a/b", {"file", "", "", "", 0, "/C:/", "", "", "file:///C:/"}); + checkRelativeURL("/abc", "file:///C:/a/b", {"file", "", "", "", 0, "/C:/abc", "", "", "file:///C:/abc"}); + checkRelativeURL("/abc", "file:///C:", {"file", "", "", "", 0, "/C:/abc", "", "", "file:///C:/abc"}); + checkRelativeURL("/abc", "file:///", {"file", "", "", "", 0, "/abc", "", "", "file:///abc"}); + checkRelativeURL("//d:", "file:///C:/a/b", {"file", "", "", "", 0, "/d:", "", "", "file:///d:"}, TestTabs::No); + checkRelativeURL("//d|", "file:///C:/a/b", {"file", "", "", "", 0, "/d:", "", "", "file:///d:"}, TestTabs::No); + checkRelativeURL("//A|", "file:///C:/a/b", {"file", "", "", "", 0, "/A:", "", "", "file:///A:"}, TestTabs::No); + + // The checking of slashes in SpecialAuthoritySlashes needed to get this to pass contradicts what is in the spec, + // but it is included in the web platform tests. + checkRelativeURL("http:\\\\host\\foo", "about:blank", {"http", "", "", "host", 0, "/foo", "", "", "http://host/foo"}); +} + +// These are differences between the new URLParser and the old URL::parse which make URLParser more standards compliant. +TEST_F(URLParserTest, ParserDifferences) +{ + checkURLDifferences("http://127.0.1", + {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}, + {"http", "", "", "127.0.1", 0, "/", "", "", "http://127.0.1/"}); + checkURLDifferences("http://011.11.0X11.0x011", + {"http", "", "", "9.11.17.17", 0, "/", "", "", "http://9.11.17.17/"}, + {"http", "", "", "011.11.0x11.0x011", 0, "/", "", "", "http://011.11.0x11.0x011/"}); + checkURLDifferences("http://[1234:0078:90AB:CdEf:0123:0007:89AB:0000]", + {"http", "", "", "[1234:78:90ab:cdef:123:7:89ab:0]", 0, "/", "", "", "http://[1234:78:90ab:cdef:123:7:89ab:0]/"}, + {"http", "", "", "[1234:0078:90ab:cdef:0123:0007:89ab:0000]", 0, "/", "", "", "http://[1234:0078:90ab:cdef:0123:0007:89ab:0000]/"}); + checkURLDifferences("http://[0:f:0:0:f:f:0:0]", + {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]/"}, + {"http", "", "", "[0:f:0:0:f:f:0:0]", 0, "/", "", "", "http://[0:f:0:0:f:f:0:0]/"}); + checkURLDifferences("http://[0:f:0:0:f:0:0:0]", + {"http", "", "", "[0:f:0:0:f::]", 0, "/", "", "", "http://[0:f:0:0:f::]/"}, + {"http", "", "", "[0:f:0:0:f:0:0:0]", 0, "/", "", "", "http://[0:f:0:0:f:0:0:0]/"}); + checkURLDifferences("http://[0:0:f:0:0:f:0:0]", + {"http", "", "", "[::f:0:0:f:0:0]", 0, "/", "", "", "http://[::f:0:0:f:0:0]/"}, + {"http", "", "", "[0:0:f:0:0:f:0:0]", 0, "/", "", "", "http://[0:0:f:0:0:f:0:0]/"}); + checkURLDifferences("http://[a:0:0:0:b:c::d]", + {"http", "", "", "[a::b:c:0:d]", 0, "/", "", "", "http://[a::b:c:0:d]/"}, + {"http", "", "", "[a:0:0:0:b:c::d]", 0, "/", "", "", "http://[a:0:0:0:b:c::d]/"}); + checkURLDifferences("http://[::7f00:0001]/", + {"http", "", "", "[::7f00:1]", 0, "/", "", "", "http://[::7f00:1]/"}, + {"http", "", "", "[::7f00:0001]", 0, "/", "", "", "http://[::7f00:0001]/"}); + checkURLDifferences("http://[::7f00:00]/", + {"http", "", "", "[::7f00:0]", 0, "/", "", "", "http://[::7f00:0]/"}, + {"http", "", "", "[::7f00:00]", 0, "/", "", "", "http://[::7f00:00]/"}); + checkURLDifferences("http://[::0:7f00:0001]/", + {"http", "", "", "[::7f00:1]", 0, "/", "", "", "http://[::7f00:1]/"}, + {"http", "", "", "[::0:7f00:0001]", 0, "/", "", "", "http://[::0:7f00:0001]/"}); + checkURLDifferences("http://127.00.0.1/", + {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}, + {"http", "", "", "127.00.0.1", 0, "/", "", "", "http://127.00.0.1/"}); + checkURLDifferences("http://127.0.0.01/", + {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}, + {"http", "", "", "127.0.0.01", 0, "/", "", "", "http://127.0.0.01/"}); + checkURLDifferences("http://example.com/path1/.%2e", + {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}, + {"http", "", "", "example.com", 0, "/path1/.%2e", "", "", "http://example.com/path1/.%2e"}); + checkURLDifferences("http://example.com/path1/.%2E", + {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}, + {"http", "", "", "example.com", 0, "/path1/.%2E", "", "", "http://example.com/path1/.%2E"}); + checkURLDifferences("http://example.com/path1/.%2E/", + {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}, + {"http", "", "", "example.com", 0, "/path1/.%2E/", "", "", "http://example.com/path1/.%2E/"}); + checkURLDifferences("http://example.com/path1/%2e.", + {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}, + {"http", "", "", "example.com", 0, "/path1/%2e.", "", "", "http://example.com/path1/%2e."}); + checkURLDifferences("http://example.com/path1/%2E%2e", + {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}, + {"http", "", "", "example.com", 0, "/path1/%2E%2e", "", "", "http://example.com/path1/%2E%2e"}); + checkURLDifferences("http://example.com/path1/%2e", + {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"}, + {"http", "", "", "example.com", 0, "/path1/%2e", "", "", "http://example.com/path1/%2e"}); + checkURLDifferences("http://example.com/path1/%2E", + {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"}, + {"http", "", "", "example.com", 0, "/path1/%2E", "", "", "http://example.com/path1/%2E"}); + checkURLDifferences("http://example.com/path1/%2E/", + {"http", "", "", "example.com", 0, "/path1/", "", "", "http://example.com/path1/"}, + {"http", "", "", "example.com", 0, "/path1/%2E/", "", "", "http://example.com/path1/%2E/"}); + checkURLDifferences("http://example.com/path1/path2/%2e?query", + {"http", "", "", "example.com", 0, "/path1/path2/", "query", "", "http://example.com/path1/path2/?query"}, + {"http", "", "", "example.com", 0, "/path1/path2/%2e", "query", "", "http://example.com/path1/path2/%2e?query"}); + checkURLDifferences("http://example.com/path1/path2/%2e%2e?query", + {"http", "", "", "example.com", 0, "/path1/", "query", "", "http://example.com/path1/?query"}, + {"http", "", "", "example.com", 0, "/path1/path2/%2e%2e", "query", "", "http://example.com/path1/path2/%2e%2e?query"}); + checkURLDifferences("http://example.com/path1/path2/%2e#fragment", + {"http", "", "", "example.com", 0, "/path1/path2/", "", "fragment", "http://example.com/path1/path2/#fragment"}, + {"http", "", "", "example.com", 0, "/path1/path2/%2e", "", "fragment", "http://example.com/path1/path2/%2e#fragment"}); + checkURLDifferences("http://example.com/path1/path2/%2e%2e#fragment", + {"http", "", "", "example.com", 0, "/path1/", "", "fragment", "http://example.com/path1/#fragment"}, + {"http", "", "", "example.com", 0, "/path1/path2/%2e%2e", "", "fragment", "http://example.com/path1/path2/%2e%2e#fragment"}); + checkURL("http://example.com/path1/path2/A%2e%2e#fragment", {"http", "", "", "example.com", 0, "/path1/path2/A%2e%2e", "", "fragment", "http://example.com/path1/path2/A%2e%2e#fragment"}); + checkURLDifferences("file://[0:a:0:0:b:c:0:0]/path", + {"file", "", "", "[0:a::b:c:0:0]", 0, "/path", "", "", "file://[0:a::b:c:0:0]/path"}, + {"file", "", "", "[0:a:0:0:b:c:0:0]", 0, "/path", "", "", "file://[0:a:0:0:b:c:0:0]/path"}); + checkURLDifferences("http://", + {"", "", "", "", 0, "", "", "", "http://"}, + {"http", "", "", "", 0, "/", "", "", "http:/"}); + checkRelativeURLDifferences("//", "https://www.webkit.org/path", + {"", "", "", "", 0, "", "", "", "//"}, + {"https", "", "", "", 0, "/", "", "", "https:/"}); + checkURLDifferences("http://127.0.0.1:65536/path", + {"", "", "", "", 0, "", "", "", "http://127.0.0.1:65536/path"}, + {"http", "", "", "127.0.0.1", 0, "/path", "", "", "http://127.0.0.1:65536/path"}); + checkURLDifferences("http://host:65536", + {"", "", "", "", 0, "", "", "", "http://host:65536"}, + {"http", "", "", "host", 0, "/", "", "", "http://host:65536/"}); + checkURLDifferences("http://127.0.0.1:65536", + {"", "", "", "", 0, "", "", "", "http://127.0.0.1:65536"}, + {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1:65536/"}); + checkURLDifferences("http://[0:f::f:f:0:0]:65536", + {"", "", "", "", 0, "", "", "", "http://[0:f::f:f:0:0]:65536"}, + {"http", "", "", "[0:f::f:f:0:0]", 0, "/", "", "", "http://[0:f::f:f:0:0]:65536/"}); + checkRelativeURLDifferences(":foo.com\\", "notspecial://example.org/foo/bar", + {"notspecial", "", "", "example.org", 0, "/foo/:foo.com\\", "", "", "notspecial://example.org/foo/:foo.com\\"}, + {"notspecial", "", "", "example.org", 0, "/foo/:foo.com/", "", "", "notspecial://example.org/foo/:foo.com/"}); + checkURL("sc://pa", {"sc", "", "", "pa", 0, "", "", "", "sc://pa"}); + checkRelativeURLDifferences("notspecial:\\\\foo.com\\", "http://example.org/foo/bar", + {"notspecial", "", "", "", 0, "\\\\foo.com\\", "", "", "notspecial:\\\\foo.com\\"}, + {"notspecial", "", "", "foo.com", 0, "/", "", "", "notspecial://foo.com/"}); + checkRelativeURLDifferences("notspecial:\\\\foo.com/", "http://example.org/foo/bar", + {"notspecial", "", "", "", 0, "\\\\foo.com/", "", "", "notspecial:\\\\foo.com/"}, + {"notspecial", "", "", "foo.com", 0, "/", "", "", "notspecial://foo.com/"}); + checkRelativeURLDifferences("notspecial:\\\\foo.com", "http://example.org/foo/bar", + {"notspecial", "", "", "", 0, "\\\\foo.com", "", "", "notspecial:\\\\foo.com"}, + {"notspecial", "", "", "foo.com", 0, "", "", "", "notspecial://foo.com"}); + checkURLDifferences("file://notuser:notpassword@test", + {"", "", "", "", 0, "", "", "", "file://notuser:notpassword@test"}, + {"file", "notuser", "notpassword", "test", 0, "/", "", "", "file://notuser:notpassword@test/"}); + checkURLDifferences("file://notuser:notpassword@test/", + {"", "", "", "", 0, "", "", "", "file://notuser:notpassword@test/"}, + {"file", "notuser", "notpassword", "test", 0, "/", "", "", "file://notuser:notpassword@test/"}); + checkRelativeURLDifferences("http:/", "about:blank", + {"", "", "", "", 0, "", "", "", "http:/"}, + {"http", "", "", "", 0, "/", "", "", "http:/"}); + checkRelativeURLDifferences("http:", "about:blank", + {"http", "", "", "", 0, "", "", "", "http:"}, + {"http", "", "", "", 0, "/", "", "", "http:/"}); + checkRelativeURLDifferences("http:/", "http://host", + {"", "", "", "", 0, "", "", "", "http:/"}, + {"http", "", "", "", 0, "/", "", "", "http:/"}); + checkURLDifferences("http:/", + {"", "", "", "", 0, "", "", "", "http:/"}, + {"http", "", "", "", 0, "/", "", "", "http:/"}); + checkURLDifferences("http:", + {"http", "", "", "", 0, "", "", "", "http:"}, + {"http", "", "", "", 0, "/", "", "", "http:/"}); + checkRelativeURLDifferences("http:/example.com/", "http://example.org/foo/bar", + {"http", "", "", "example.org", 0, "/example.com/", "", "", "http://example.org/example.com/"}, + {"http", "", "", "example.com", 0, "/", "", "", "http://example.com/"}); + + // This behavior matches Chrome and Firefox, but not WebKit using URL::parse. + // The behavior of URL::parse is clearly wrong because reparsing file://path would make path the host. + // The spec is unclear. + checkURLDifferences("file:path", + {"file", "", "", "", 0, "/path", "", "", "file:///path"}, + {"file", "", "", "", 0, "path", "", "", "file://path"}); + checkURLDifferences("file:pAtH", + {"file", "", "", "", 0, "/pAtH", "", "", "file:///pAtH"}, + {"file", "", "", "", 0, "pAtH", "", "", "file://pAtH"}); + checkURLDifferences("file:pAtH/", + {"file", "", "", "", 0, "/pAtH/", "", "", "file:///pAtH/"}, + {"file", "", "", "", 0, "pAtH/", "", "", "file://pAtH/"}); + checkURLDifferences("http://example.com%A0", + {"", "", "", "", 0, "", "", "", "http://example.com%A0"}, + {"http", "", "", "example.com%a0", 0, "/", "", "", "http://example.com%a0/"}); + checkURLDifferences("http://%E2%98%83", + {"http", "", "", "xn--n3h", 0, "/", "", "", "http://xn--n3h/"}, + {"http", "", "", "%e2%98%83", 0, "/", "", "", "http://%e2%98%83/"}); + checkURLDifferences("http://host%73", + {"http", "", "", "hosts", 0, "/", "", "", "http://hosts/"}, + {"http", "", "", "host%73", 0, "/", "", "", "http://host%73/"}); + checkURLDifferences("http://host%53", + {"http", "", "", "hosts", 0, "/", "", "", "http://hosts/"}, + {"http", "", "", "host%53", 0, "/", "", "", "http://host%53/"}); + checkURLDifferences("http://%", + {"", "", "", "", 0, "", "", "", "http://%"}, + {"http", "", "", "%", 0, "/", "", "", "http://%/"}); + checkURLDifferences("http://%7", + {"", "", "", "", 0, "", "", "", "http://%7"}, + {"http", "", "", "%7", 0, "/", "", "", "http://%7/"}); + checkURLDifferences("http://%7s", + {"", "", "", "", 0, "", "", "", "http://%7s"}, + {"http", "", "", "%7s", 0, "/", "", "", "http://%7s/"}); + checkURLDifferences("http://%73", + {"http", "", "", "s", 0, "/", "", "", "http://s/"}, + {"http", "", "", "%73", 0, "/", "", "", "http://%73/"}); + checkURLDifferences("http://abcdefg%", + {"", "", "", "", 0, "", "", "", "http://abcdefg%"}, + {"http", "", "", "abcdefg%", 0, "/", "", "", "http://abcdefg%/"}); + checkURLDifferences("http://abcd%7Xefg", + {"", "", "", "", 0, "", "", "", "http://abcd%7Xefg"}, + {"http", "", "", "abcd%7xefg", 0, "/", "", "", "http://abcd%7xefg/"}); + + + // URLParser matches Chrome and the spec, but not URL::parse or Firefox. + checkURLDifferences(utf16String(u"http://0Xc0.0250.01"), + {"http", "", "", "192.168.0.1", 0, "/", "", "", "http://192.168.0.1/"}, + {"http", "", "", "0xc0.0250.01", 0, "/", "", "", "http://0xc0.0250.01/"}); + + checkURL("http://host/path%2e.%2E", {"http", "", "", "host", 0, "/path%2e.%2E", "", "", "http://host/path%2e.%2E"}); + + checkRelativeURLDifferences(utf16String(u"http://foo:💩@example.com/bar"), "http://other.com/", + {"http", "foo", utf16String(u"💩"), "example.com", 0, "/bar", "", "", "http://foo:%F0%9F%92%A9@example.com/bar"}, + {"", "", "", "", 0, "", "", "", utf16String(u"http://foo:💩@example.com/bar")}, testTabsValueForSurrogatePairs); + checkRelativeURLDifferences("http://&a:foo(b]c@d:2/", "http://example.org/foo/bar", + {"http", "&a", "foo(b]c", "d", 2, "/", "", "", "http://&a:foo(b%5Dc@d:2/"}, + {"", "", "", "", 0, "", "", "", "http://&a:foo(b]c@d:2/"}); + checkRelativeURLDifferences("http://`{}:`{}@h/`{}?`{}", "http://doesnotmatter/", + {"http", "`{}", "`{}", "h", 0, "/%60%7B%7D", "`{}", "", "http://%60%7B%7D:%60%7B%7D@h/%60%7B%7D?`{}"}, + {"", "", "", "", 0, "", "", "", "http://`{}:`{}@h/`{}?`{}"}); + checkURLDifferences("http://[0:f::f::f]", + {"", "", "", "", 0, "" , "", "", "http://[0:f::f::f]"}, + {"http", "", "", "[0:f::f::f]", 0, "/" , "", "", "http://[0:f::f::f]/"}); + checkURLDifferences("http://123", + {"http", "", "", "0.0.0.123", 0, "/", "", "", "http://0.0.0.123/"}, + {"http", "", "", "123", 0, "/", "", "", "http://123/"}); + checkURLDifferences("http://123.234/", + {"http", "", "", "123.0.0.234", 0, "/", "", "", "http://123.0.0.234/"}, + {"http", "", "", "123.234", 0, "/", "", "", "http://123.234/"}); + checkURLDifferences("http://123.234.012", + {"http", "", "", "123.234.0.10", 0, "/", "", "", "http://123.234.0.10/"}, + {"http", "", "", "123.234.012", 0, "/", "", "", "http://123.234.012/"}); + checkURLDifferences("http://123.234.12", + {"http", "", "", "123.234.0.12", 0, "/", "", "", "http://123.234.0.12/"}, + {"http", "", "", "123.234.12", 0, "/", "", "", "http://123.234.12/"}); + checkRelativeURLDifferences("file:c:\\foo\\bar.html", "file:///tmp/mock/path", + {"file", "", "", "", 0, "/c:/foo/bar.html", "", "", "file:///c:/foo/bar.html"}, + {"file", "", "", "", 0, "/tmp/mock/c:/foo/bar.html", "", "", "file:///tmp/mock/c:/foo/bar.html"}); + checkRelativeURLDifferences(" File:c|////foo\\bar.html", "file:///tmp/mock/path", + {"file", "", "", "", 0, "/c:////foo/bar.html", "", "", "file:///c:////foo/bar.html"}, + {"file", "", "", "", 0, "/tmp/mock/c|////foo/bar.html", "", "", "file:///tmp/mock/c|////foo/bar.html"}); + checkRelativeURLDifferences(" Fil\t\n\te\n\t\n:\t\n\tc\t\n\t|\n\t\n/\t\n\t/\n\t\n//foo\\bar.html", "file:///tmp/mock/path", + {"file", "", "", "", 0, "/c:////foo/bar.html", "", "", "file:///c:////foo/bar.html"}, + {"file", "", "", "", 0, "/tmp/mock/c|////foo/bar.html", "", "", "file:///tmp/mock/c|////foo/bar.html"}); + checkRelativeURLDifferences("C|/foo/bar", "file:///tmp/mock/path", + {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"}, + {"file", "", "", "", 0, "/tmp/mock/C|/foo/bar", "", "", "file:///tmp/mock/C|/foo/bar"}); + checkRelativeURLDifferences("/C|/foo/bar", "file:///tmp/mock/path", + {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"}, + {"file", "", "", "", 0, "/C|/foo/bar", "", "", "file:///C|/foo/bar"}); + checkRelativeURLDifferences("https://@test@test@example:800/", "http://doesnotmatter/", + {"https", "@test@test", "", "example", 800, "/", "", "", "https://%40test%40test@example:800/"}, + {"", "", "", "", 0, "", "", "", "https://@test@test@example:800/"}); + checkRelativeURLDifferences("https://@test@test@example:800/path@end", "http://doesnotmatter/", + {"https", "@test@test", "", "example", 800, "/path@end", "", "", "https://%40test%40test@example:800/path@end"}, + {"", "", "", "", 0, "", "", "", "https://@test@test@example:800/path@end"}); + checkURLDifferences("notspecial://@test@test@example:800/path@end", + {"notspecial", "@test@test", "", "example", 800, "/path@end", "", "", "notspecial://%40test%40test@example:800/path@end"}, + {"", "", "", "", 0, "", "", "", "notspecial://@test@test@example:800/path@end"}); + checkURLDifferences("notspecial://@test@test@example:800\\path@end", + {"notspecial", "@test@test@example", "800\\path", "end", 0, "", "", "", "notspecial://%40test%40test%40example:800%5Cpath@end"}, + {"", "", "", "", 0, "", "", "", "notspecial://@test@test@example:800\\path@end"}); + checkURLDifferences("http://%48OsT", + {"http", "", "", "host", 0, "/", "", "", "http://host/"}, + {"http", "", "", "%48ost", 0, "/", "", "", "http://%48ost/"}); + checkURLDifferences("http://h%4FsT", + {"http", "", "", "host", 0, "/", "", "", "http://host/"}, + {"http", "", "", "h%4fst", 0, "/", "", "", "http://h%4fst/"}); + checkURLDifferences("http://h%4fsT", + {"http", "", "", "host", 0, "/", "", "", "http://host/"}, + {"http", "", "", "h%4fst", 0, "/", "", "", "http://h%4fst/"}); + checkURLDifferences("http://h%6fsT", + {"http", "", "", "host", 0, "/", "", "", "http://host/"}, + {"http", "", "", "h%6fst", 0, "/", "", "", "http://h%6fst/"}); + checkURLDifferences("http://host/`", + {"http", "", "", "host", 0, "/%60", "", "", "http://host/%60"}, + {"http", "", "", "host", 0, "/`", "", "", "http://host/`"}); + checkURLDifferences("http://://", + {"", "", "", "", 0, "", "", "", "http://://"}, + {"http", "", "", "", 0, "//", "", "", "http://://"}); + checkURLDifferences("http://:123?", + {"", "", "", "", 0, "", "", "", "http://:123?"}, + {"http", "", "", "", 123, "/", "", "", "http://:123/?"}); + checkURLDifferences("http:/:", + {"", "", "", "", 0, "", "", "", "http:/:"}, + {"http", "", "", "", 0, "/", "", "", "http://:/"}); + checkURLDifferences("asdf://:", + {"", "", "", "", 0, "", "", "", "asdf://:"}, + {"asdf", "", "", "", 0, "", "", "", "asdf://:"}); + checkURLDifferences("http://:", + {"", "", "", "", 0, "", "", "", "http://:"}, + {"http", "", "", "", 0, "/", "", "", "http://:/"}); + checkURLDifferences("http:##foo", + {"", "", "", "", 0, "", "", "", "http:##foo"}, + {"http", "", "", "", 0, "/", "", "#foo", "http:/##foo"}); + checkURLDifferences("http:??bar", + {"", "", "", "", 0, "", "", "", "http:??bar"}, + {"http", "", "", "", 0, "/", "?bar", "", "http:/??bar"}); + checkURL("asdf:##foo", {"asdf", "", "", "", 0, "", "", "#foo", "asdf:##foo"}); + checkURL("asdf:??bar", {"asdf", "", "", "", 0, "", "?bar", "", "asdf:??bar"}); + checkRelativeURLDifferences("//C|/foo/bar", "file:///tmp/mock/path", + {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"}, + {"", "", "", "", 0, "", "", "", "//C|/foo/bar"}); + checkRelativeURLDifferences("//C:/foo/bar", "file:///tmp/mock/path", + {"file", "", "", "", 0, "/C:/foo/bar", "", "", "file:///C:/foo/bar"}, + {"file", "", "", "c", 0, "/foo/bar", "", "", "file://c/foo/bar"}); + checkRelativeURLDifferences("//C|?foo/bar", "file:///tmp/mock/path", + {"file", "", "", "", 0, "/C:/", "foo/bar", "", "file:///C:/?foo/bar"}, + {"", "", "", "", 0, "", "", "", "//C|?foo/bar"}); + checkRelativeURLDifferences("//C|#foo/bar", "file:///tmp/mock/path", + {"file", "", "", "", 0, "/C:/", "", "foo/bar", "file:///C:/#foo/bar"}, + {"", "", "", "", 0, "", "", "", "//C|#foo/bar"}); + checkURLDifferences("http://0xFFFFFfFF/", + {"http", "", "", "255.255.255.255", 0, "/", "", "", "http://255.255.255.255/"}, + {"http", "", "", "0xffffffff", 0, "/", "", "", "http://0xffffffff/"}); + checkURLDifferences("http://0000000000000000037777777777/", + {"http", "", "", "255.255.255.255", 0, "/", "", "", "http://255.255.255.255/"}, + {"http", "", "", "0000000000000000037777777777", 0, "/", "", "", "http://0000000000000000037777777777/"}); + checkURLDifferences("http://4294967295/", + {"http", "", "", "255.255.255.255", 0, "/", "", "", "http://255.255.255.255/"}, + {"http", "", "", "4294967295", 0, "/", "", "", "http://4294967295/"}); + checkURLDifferences("http://256/", + {"http", "", "", "0.0.1.0", 0, "/", "", "", "http://0.0.1.0/"}, + {"http", "", "", "256", 0, "/", "", "", "http://256/"}); + checkURLDifferences("http://256./", + {"http", "", "", "0.0.1.0", 0, "/", "", "", "http://0.0.1.0/"}, + {"http", "", "", "256.", 0, "/", "", "", "http://256./"}); + checkURLDifferences("http://123.256/", + {"http", "", "", "123.0.1.0", 0, "/", "", "", "http://123.0.1.0/"}, + {"http", "", "", "123.256", 0, "/", "", "", "http://123.256/"}); + checkURLDifferences("http://127.%.0.1/", + {"", "", "", "", 0, "", "", "", "http://127.%.0.1/"}, + {"http", "", "", "127.%.0.1", 0, "/", "", "", "http://127.%.0.1/"}); + checkURLDifferences("http://[1:2:3:4:5:6:7:8:]/", + {"", "", "", "", 0, "", "", "", "http://[1:2:3:4:5:6:7:8:]/"}, + {"http", "", "", "[1:2:3:4:5:6:7:8:]", 0, "/", "", "", "http://[1:2:3:4:5:6:7:8:]/"}); + checkURLDifferences("http://[:2:3:4:5:6:7:8:]/", + {"", "", "", "", 0, "", "", "", "http://[:2:3:4:5:6:7:8:]/"}, + {"http", "", "", "[:2:3:4:5:6:7:8:]", 0, "/", "", "", "http://[:2:3:4:5:6:7:8:]/"}); + checkURLDifferences("http://[1:2:3:4:5:6:7::]/", + {"http", "", "", "[1:2:3:4:5:6:7:0]", 0, "/", "", "", "http://[1:2:3:4:5:6:7:0]/"}, + {"http", "", "", "[1:2:3:4:5:6:7::]", 0, "/", "", "", "http://[1:2:3:4:5:6:7::]/"}); + checkURLDifferences("http://[1:2:3:4:5:6:7:::]/", + {"", "", "", "", 0, "", "", "", "http://[1:2:3:4:5:6:7:::]/"}, + {"http", "", "", "[1:2:3:4:5:6:7:::]", 0, "/", "", "", "http://[1:2:3:4:5:6:7:::]/"}); + checkURLDifferences("http://127.0.0.1~/", + {"http", "", "", "127.0.0.1~", 0, "/", "", "", "http://127.0.0.1~/"}, + {"", "", "", "", 0, "", "", "", "http://127.0.0.1~/"}); + checkURLDifferences("http://127.0.1~/", + {"http", "", "", "127.0.1~", 0, "/", "", "", "http://127.0.1~/"}, + {"", "", "", "", 0, "", "", "", "http://127.0.1~/"}); + checkURLDifferences("http://127.0.1./", + {"http", "", "", "127.0.0.1", 0, "/", "", "", "http://127.0.0.1/"}, + {"http", "", "", "127.0.1.", 0, "/", "", "", "http://127.0.1./"}); + checkURLDifferences("http://127.0.1.~/", + {"http", "", "", "127.0.1.~", 0, "/", "", "", "http://127.0.1.~/"}, + {"", "", "", "", 0, "", "", "", "http://127.0.1.~/"}); + checkURLDifferences("http://127.0.1.~", + {"http", "", "", "127.0.1.~", 0, "/", "", "", "http://127.0.1.~/"}, + {"", "", "", "", 0, "", "", "", "http://127.0.1.~"}); + checkRelativeURLDifferences("http://f:000/c", "http://example.org/foo/bar", + {"http", "", "", "f", 0, "/c", "", "", "http://f:0/c"}, + {"http", "", "", "f", 0, "/c", "", "", "http://f:000/c"}); + checkRelativeURLDifferences("http://f:010/c", "http://example.org/foo/bar", + {"http", "", "", "f", 10, "/c", "", "", "http://f:10/c"}, + {"http", "", "", "f", 10, "/c", "", "", "http://f:010/c"}); + checkURL("notspecial://HoSt", {"notspecial", "", "", "HoSt", 0, "", "", "", "notspecial://HoSt"}); + checkURL("notspecial://H%6FSt", {"notspecial", "", "", "H%6FSt", 0, "", "", "", "notspecial://H%6FSt"}); + checkURL("notspecial://H%4fSt", {"notspecial", "", "", "H%4fSt", 0, "", "", "", "notspecial://H%4fSt"}); + checkURLDifferences(utf16String(u"notspecial://H😍ßt"), + {"notspecial", "", "", "H%F0%9F%98%8D%C3%9Ft", 0, "", "", "", "notspecial://H%F0%9F%98%8D%C3%9Ft"}, + {"notspecial", "", "", "xn--hsst-qc83c", 0, "", "", "", "notspecial://xn--hsst-qc83c"}, testTabsValueForSurrogatePairs); + checkURLDifferences("http://[ffff:aaaa:cccc:eeee:bbbb:dddd:255.255.255.255]/", + {"http", "", "", "[ffff:aaaa:cccc:eeee:bbbb:dddd:ffff:ffff]", 0, "/", "", "", "http://[ffff:aaaa:cccc:eeee:bbbb:dddd:ffff:ffff]/"}, + {"http", "", "", "[ffff:aaaa:cccc:eeee:bbbb:dddd:255.255.255.255]", 0, "/", "", "", "http://[ffff:aaaa:cccc:eeee:bbbb:dddd:255.255.255.255]/"}, TestTabs::No); + checkURLDifferences("http://[::123.234.12.210]/", + {"http", "", "", "[::7bea:cd2]", 0, "/", "", "", "http://[::7bea:cd2]/"}, + {"http", "", "", "[::123.234.12.210]", 0, "/", "", "", "http://[::123.234.12.210]/"}); + checkURLDifferences("http://[::a:255.255.255.255]/", + {"http", "", "", "[::a:ffff:ffff]", 0, "/", "", "", "http://[::a:ffff:ffff]/"}, + {"http", "", "", "[::a:255.255.255.255]", 0, "/", "", "", "http://[::a:255.255.255.255]/"}); + checkURLDifferences("http://[::0.00.255.255]/", + {"", "", "", "", 0, "", "", "", "http://[::0.00.255.255]/"}, + {"http", "", "", "[::0.00.255.255]", 0, "/", "", "", "http://[::0.00.255.255]/"}); + checkURLDifferences("http://[::0.0.255.255]/", + {"http", "", "", "[::ffff]", 0, "/", "", "", "http://[::ffff]/"}, + {"http", "", "", "[::0.0.255.255]", 0, "/", "", "", "http://[::0.0.255.255]/"}); + checkURLDifferences("http://[::0:1.0.255.255]/", + {"http", "", "", "[::100:ffff]", 0, "/", "", "", "http://[::100:ffff]/"}, + {"http", "", "", "[::0:1.0.255.255]", 0, "/", "", "", "http://[::0:1.0.255.255]/"}); + checkURLDifferences("http://[::A:1.0.255.255]/", + {"http", "", "", "[::a:100:ffff]", 0, "/", "", "", "http://[::a:100:ffff]/"}, + {"http", "", "", "[::a:1.0.255.255]", 0, "/", "", "", "http://[::a:1.0.255.255]/"}); + checkURLDifferences("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]", + {"", "", "", "", 0, "", "", "", "http://[127.0.0.1]"}, + {"http", "", "", "[127.0.0.1]", 0, "/", "", "", "http://[127.0.0.1]/"}); + checkURLDifferences("http://[a:b:c:d:e:f:127.0.0.1]", + {"http", "", "", "[a:b:c:d:e:f:7f00:1]", 0, "/", "", "", "http://[a:b:c:d:e:f:7f00:1]/"}, + {"http", "", "", "[a:b:c:d:e:f:127.0.0.1]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.0.1]/"}); + checkURLDifferences("http://[a:b:c:d:e:f:127.0.0.101]", + {"http", "", "", "[a:b:c:d:e:f:7f00:65]", 0, "/", "", "", "http://[a:b:c:d:e:f:7f00:65]/"}, + {"http", "", "", "[a:b:c:d:e:f:127.0.0.101]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.0.101]/"}); + checkURLDifferences("http://[::a:b:c:d:e:f:127.0.0.1]", + {"", "", "", "", 0, "", "", "", "http://[::a:b:c:d:e:f:127.0.0.1]"}, + {"http", "", "", "[::a:b:c:d:e:f:127.0.0.1]", 0, "/", "", "", "http://[::a:b:c:d:e:f:127.0.0.1]/"}); + checkURLDifferences("http://[a:b::c:d:e:f:127.0.0.1]", + {"", "", "", "", 0, "", "", "", "http://[a:b::c:d:e:f:127.0.0.1]"}, + {"http", "", "", "[a:b::c:d:e:f:127.0.0.1]", 0, "/", "", "", "http://[a:b::c:d:e:f:127.0.0.1]/"}); + checkURLDifferences("http://[a:b:c:d:e:127.0.0.1]", + {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:127.0.0.1]"}, + {"http", "", "", "[a:b:c:d:e:127.0.0.1]", 0, "/", "", "", "http://[a:b:c:d:e:127.0.0.1]/"}); + checkURLDifferences("http://[a:b:c:d:e:f:127.0.0.0.1]", + {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0.0.0.1]"}, + {"http", "", "", "[a:b:c:d:e:f:127.0.0.0.1]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.0.0.1]/"}); + checkURLDifferences("http://[a:b:c:d:e:f:127.0.1]", + {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0.1]"}, + {"http", "", "", "[a:b:c:d:e:f:127.0.1]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.1]/"}); + checkURLDifferences("http://[a:b:c:d:e:f:127.0.0.011]", // Chrome treats this as octal, Firefox and the spec fail + {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0.0.011]"}, + {"http", "", "", "[a:b:c:d:e:f:127.0.0.011]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.0.011]/"}); + checkURLDifferences("http://[a:b:c:d:e:f:127.0.00.1]", + {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0.00.1]"}, + {"http", "", "", "[a:b:c:d:e:f:127.0.00.1]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.00.1]/"}); + checkURLDifferences("http://[a:b:c:d:e:f:127.0.0.1.]", + {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0.0.1.]"}, + {"http", "", "", "[a:b:c:d:e:f:127.0.0.1.]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0.0.1.]/"}); + checkURLDifferences("http://[a:b:c:d:e:f:127.0..0.1]", + {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f:127.0..0.1]"}, + {"http", "", "", "[a:b:c:d:e:f:127.0..0.1]", 0, "/", "", "", "http://[a:b:c:d:e:f:127.0..0.1]/"}); + checkURLDifferences("http://[a:b:c:d:e:f::127.0.0.1]", + {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f::127.0.0.1]"}, + {"http", "", "", "[a:b:c:d:e:f::127.0.0.1]", 0, "/", "", "", "http://[a:b:c:d:e:f::127.0.0.1]/"}); + checkURLDifferences("http://[a:b:c:d:e::127.0.0.1]", + {"http", "", "", "[a:b:c:d:e:0:7f00:1]", 0, "/", "", "", "http://[a:b:c:d:e:0:7f00:1]/"}, + {"http", "", "", "[a:b:c:d:e::127.0.0.1]", 0, "/", "", "", "http://[a:b:c:d:e::127.0.0.1]/"}); + checkURLDifferences("http://[a:b:c:d::e:127.0.0.1]", + {"http", "", "", "[a:b:c:d:0:e:7f00:1]", 0, "/", "", "", "http://[a:b:c:d:0:e:7f00:1]/"}, + {"http", "", "", "[a:b:c:d::e:127.0.0.1]", 0, "/", "", "", "http://[a:b:c:d::e:127.0.0.1]/"}); + checkURLDifferences("http://[a:b:c:d:e:f::127.0.0.]", + {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f::127.0.0.]"}, + {"http", "", "", "[a:b:c:d:e:f::127.0.0.]", 0, "/", "", "", "http://[a:b:c:d:e:f::127.0.0.]/"}); + checkURLDifferences("http://[a:b:c:d:e:f::127.0.0.256]", + {"", "", "", "", 0, "", "", "", "http://[a:b:c:d:e:f::127.0.0.256]"}, + {"http", "", "", "[a:b:c:d:e:f::127.0.0.256]", 0, "/", "", "", "http://[a:b:c:d:e:f::127.0.0.256]/"}); + checkURLDifferences("http://123456", {"http", "", "", "0.1.226.64", 0, "/", "", "", "http://0.1.226.64/"}, {"http", "", "", "123456", 0, "/", "", "", "http://123456/"}); + checkURL("asdf://123456", {"asdf", "", "", "123456", 0, "", "", "", "asdf://123456"}); + checkURLDifferences("http://[0:0:0:0:a:b:c:d]", + {"http", "", "", "[::a:b:c:d]", 0, "/", "", "", "http://[::a:b:c:d]/"}, + {"http", "", "", "[0:0:0:0:a:b:c:d]", 0, "/", "", "", "http://[0:0:0:0:a:b:c:d]/"}); + checkURLDifferences("asdf://[0:0:0:0:a:b:c:d]", + {"asdf", "", "", "[::a:b:c:d]", 0, "", "", "", "asdf://[::a:b:c:d]"}, + {"asdf", "", "", "[0:0:0:0:a:b:c:d]", 0, "", "", "", "asdf://[0:0:0:0:a:b:c:d]"}, TestTabs::No); + shouldFail("a://%:a"); + checkURL("a://%:/", {"a", "", "", "%", 0, "/", "", "", "a://%/"}); + checkURL("a://%:", {"a", "", "", "%", 0, "", "", "", "a://%"}); + checkURL("a://%:1/", {"a", "", "", "%", 1, "/", "", "", "a://%:1/"}); + checkURLDifferences("http://%:", + {"", "", "", "", 0, "", "", "", "http://%:"}, + {"http", "", "", "%", 0, "/", "", "", "http://%/"}); + checkURL("a://123456", {"a", "", "", "123456", 0, "", "", "", "a://123456"}); + checkURL("a://123456:7", {"a", "", "", "123456", 7, "", "", "", "a://123456:7"}); + checkURL("a://123456:7/", {"a", "", "", "123456", 7, "/", "", "", "a://123456:7/"}); + checkURL("a://A", {"a", "", "", "A", 0, "", "", "", "a://A"}); + checkURL("a://A:2", {"a", "", "", "A", 2, "", "", "", "a://A:2"}); + checkURL("a://A:2/", {"a", "", "", "A", 2, "/", "", "", "a://A:2/"}); + checkURLDifferences(u8"asd://ß", + {"asd", "", "", "%C3%83%C2%9F", 0, "", "", "", "asd://%C3%83%C2%9F"}, + {"", "", "", "", 0, "", "", "", "about:blank"}, TestTabs::No); + checkURLDifferences(u8"asd://ß:4", + {"asd", "", "", "%C3%83%C2%9F", 4, "", "", "", "asd://%C3%83%C2%9F:4"}, + {"", "", "", "", 0, "", "", "", "about:blank"}, TestTabs::No); + checkURLDifferences(u8"asd://ß:4/", + {"asd", "", "", "%C3%83%C2%9F", 4, "/", "", "", "asd://%C3%83%C2%9F:4/"}, + {"", "", "", "", 0, "", "", "", "about:blank"}, TestTabs::No); + checkURLDifferences("a://[A::b]:4", + {"a", "", "", "[a::b]", 4, "", "", "", "a://[a::b]:4"}, + {"a", "", "", "[A::b]", 4, "", "", "", "a://[A::b]:4"}); + shouldFail("http://[~]"); + shouldFail("a://[~]"); + checkRelativeURLDifferences("a://b", "//[aBc]", + {"a", "", "", "b", 0, "", "", "", "a://b"}, + {"", "", "", "", 0, "", "", "", "a://b"}); + checkURL(utf16String(u"http://öbb.at"), {"http", "", "", "xn--bb-eka.at", 0, "/", "", "", "http://xn--bb-eka.at/"}); + checkURL(utf16String(u"http://ÖBB.at"), {"http", "", "", "xn--bb-eka.at", 0, "/", "", "", "http://xn--bb-eka.at/"}); + checkURL(utf16String(u"http://√.com"), {"http", "", "", "xn--19g.com", 0, "/", "", "", "http://xn--19g.com/"}); + checkURLDifferences(utf16String(u"http://faß.de"), + {"http", "", "", "xn--fa-hia.de", 0, "/", "", "", "http://xn--fa-hia.de/"}, + {"http", "", "", "fass.de", 0, "/", "", "", "http://fass.de/"}); + checkURL(utf16String(u"http://ԛәлп.com"), {"http", "", "", "xn--k1ai47bhi.com", 0, "/", "", "", "http://xn--k1ai47bhi.com/"}); + checkURLDifferences(utf16String(u"http://Ⱥbby.com"), + {"http", "", "", "xn--bby-iy0b.com", 0, "/", "", "", "http://xn--bby-iy0b.com/"}, + {"http", "", "", "xn--bby-spb.com", 0, "/", "", "", "http://xn--bby-spb.com/"}); + checkURLDifferences(utf16String(u"http://\u2132"), + {"", "", "", "", 0, "", "", "", utf16String(u"http://Ⅎ")}, + {"http", "", "", "xn--f3g", 0, "/", "", "", "http://xn--f3g/"}); + checkURLDifferences(utf16String(u"http://\u05D9\u05B4\u05D5\u05D0\u05B8/"), + {"http", "", "", "xn--cdbi5etas", 0, "/", "", "", "http://xn--cdbi5etas/"}, + {"", "", "", "", 0, "", "", "", "about:blank"}, TestTabs::No); + checkURLDifferences(utf16String(u"http://bidirectional\u0786\u07AE\u0782\u07B0\u0795\u07A9\u0793\u07A6\u0783\u07AA/"), + {"", "", "", "", 0, "", "", "", utf16String(u"http://bidirectionalކޮންޕީޓަރު/")}, + {"", "", "", "", 0, "", "", "", "about:blank"}, TestTabs::No); + checkURLDifferences(utf16String(u"http://contextj\u200D"), + {"", "", "", "", 0, "", "", "", utf16String(u"http://contextj\u200D")}, + {"http", "", "", "contextj", 0, "/", "", "", "http://contextj/"}); + checkURL(utf16String(u"http://contexto\u30FB"), {"http", "", "", "xn--contexto-wg5g", 0, "/", "", "", "http://xn--contexto-wg5g/"}); + checkURLDifferences(utf16String(u"http://\u321D\u321E/"), + {"http", "", "", "xn--()()-bs0sc174agx4b", 0, "/", "", "", "http://xn--()()-bs0sc174agx4b/"}, + {"http", "", "", "xn--5mkc", 0, "/", "", "", "http://xn--5mkc/"}); +} + +TEST_F(URLParserTest, DefaultPort) +{ + checkURL("FtP://host:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}); + checkURL("ftp://host:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}); + checkURL("f\ttp://host:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}); + checkURL("f\ttp://host\t:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}); + checkURL("f\ttp://host:\t21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}); + checkURL("f\ttp://host:2\t1/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}); + checkURL("f\ttp://host:21\t/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}); + checkURL("ftp://host\t:21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}); + checkURL("ftp://host:\t21/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}); + checkURL("ftp://host:2\t1/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}); + checkURL("ftp://host:21\t/", {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}); + checkURL("ftp://host:22/", {"ftp", "", "", "host", 22, "/", "", "", "ftp://host:22/"}); + checkURLDifferences("ftp://host:21", + {"ftp", "", "", "host", 0, "/", "", "", "ftp://host/"}, + {"ftp", "", "", "host", 0, "", "", "", "ftp://host"}); + checkURLDifferences("ftp://host:22", + {"ftp", "", "", "host", 22, "/", "", "", "ftp://host:22/"}, + {"ftp", "", "", "host", 22, "", "", "", "ftp://host:22"}); + + checkURL("gOpHeR://host:70/", {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"}); + checkURL("gopher://host:70/", {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"}); + checkURL("gopher://host:71/", {"gopher", "", "", "host", 71, "/", "", "", "gopher://host:71/"}); + // Spec, Chrome, Firefox, and URLParser have "/", URL::parse does not. + // Spec, Chrome, URLParser, URL::parse recognize gopher default port, Firefox does not. + checkURLDifferences("gopher://host:70", + {"gopher", "", "", "host", 0, "/", "", "", "gopher://host/"}, + {"gopher", "", "", "host", 0, "", "", "", "gopher://host"}); + checkURLDifferences("gopher://host:71", + {"gopher", "", "", "host", 71, "/", "", "", "gopher://host:71/"}, + {"gopher", "", "", "host", 71, "", "", "", "gopher://host:71"}); + + checkURL("hTtP://host:80", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkURL("http://host:80", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkURL("http://host:80/", {"http", "", "", "host", 0, "/", "", "", "http://host/"}); + checkURL("http://host:81", {"http", "", "", "host", 81, "/", "", "", "http://host:81/"}); + checkURL("http://host:81/", {"http", "", "", "host", 81, "/", "", "", "http://host:81/"}); + + checkURL("hTtPs://host:443", {"https", "", "", "host", 0, "/", "", "", "https://host/"}); + checkURL("https://host:443", {"https", "", "", "host", 0, "/", "", "", "https://host/"}); + checkURL("https://host:443/", {"https", "", "", "host", 0, "/", "", "", "https://host/"}); + checkURL("https://host:444", {"https", "", "", "host", 444, "/", "", "", "https://host:444/"}); + checkURL("https://host:444/", {"https", "", "", "host", 444, "/", "", "", "https://host:444/"}); + + checkURL("wS://host:80/", {"ws", "", "", "host", 0, "/", "", "", "ws://host/"}); + checkURL("ws://host:80/", {"ws", "", "", "host", 0, "/", "", "", "ws://host/"}); + checkURL("ws://host:81/", {"ws", "", "", "host", 81, "/", "", "", "ws://host:81/"}); + // URLParser matches Chrome and Firefox, but not URL::parse + checkURLDifferences("ws://host:80", + {"ws", "", "", "host", 0, "/", "", "", "ws://host/"}, + {"ws", "", "", "host", 0, "", "", "", "ws://host"}); + checkURLDifferences("ws://host:81", + {"ws", "", "", "host", 81, "/", "", "", "ws://host:81/"}, + {"ws", "", "", "host", 81, "", "", "", "ws://host:81"}); + + checkURL("WsS://host:443/", {"wss", "", "", "host", 0, "/", "", "", "wss://host/"}); + checkURL("wss://host:443/", {"wss", "", "", "host", 0, "/", "", "", "wss://host/"}); + checkURL("wss://host:444/", {"wss", "", "", "host", 444, "/", "", "", "wss://host:444/"}); + // URLParser matches Chrome and Firefox, but not URL::parse + checkURLDifferences("wss://host:443", + {"wss", "", "", "host", 0, "/", "", "", "wss://host/"}, + {"wss", "", "", "host", 0, "", "", "", "wss://host"}); + checkURLDifferences("wss://host:444", + {"wss", "", "", "host", 444, "/", "", "", "wss://host:444/"}, + {"wss", "", "", "host", 444, "", "", "", "wss://host:444"}); + + checkURL("fTpS://host:990/", {"ftps", "", "", "host", 990, "/", "", "", "ftps://host:990/"}); + checkURL("ftps://host:990/", {"ftps", "", "", "host", 990, "/", "", "", "ftps://host:990/"}); + checkURL("ftps://host:991/", {"ftps", "", "", "host", 991, "/", "", "", "ftps://host:991/"}); + checkURL("ftps://host:990", {"ftps", "", "", "host", 990, "", "", "", "ftps://host:990"}); + checkURL("ftps://host:991", {"ftps", "", "", "host", 991, "", "", "", "ftps://host:991"}); + + checkURL("uNkNoWn://host:80/", {"unknown", "", "", "host", 80, "/", "", "", "unknown://host:80/"}); + checkURL("unknown://host:80/", {"unknown", "", "", "host", 80, "/", "", "", "unknown://host:80/"}); + checkURL("unknown://host:81/", {"unknown", "", "", "host", 81, "/", "", "", "unknown://host:81/"}); + checkURL("unknown://host:80", {"unknown", "", "", "host", 80, "", "", "", "unknown://host:80"}); + checkURL("unknown://host:81", {"unknown", "", "", "host", 81, "", "", "", "unknown://host:81"}); + + checkURL("file://host:0", {"file", "", "", "host", 0, "/", "", "", "file://host:0/"}); + checkURL("file://host:80", {"file", "", "", "host", 80, "/", "", "", "file://host:80/"}); + checkURL("file://host:80/path", {"file", "", "", "host", 80, "/path", "", "", "file://host:80/path"}); + checkURLDifferences("file://:80/path", + {"", "", "", "", 0, "", "", "", "file://:80/path"}, + {"file", "", "", "", 80, "/path", "", "", "file://:80/path"}); + checkURLDifferences("file://:0/path", + {"", "", "", "", 0, "", "", "", "file://:0/path"}, + {"file", "", "", "", 0, "/path", "", "", "file://:0/path"}); +} + +TEST_F(URLParserTest, ParserFailures) +{ + shouldFail(" "); + shouldFail(" \a "); + shouldFail(""); + shouldFail(String()); + shouldFail("", "about:blank"); + shouldFail(String(), "about:blank"); + shouldFail("http://127.0.0.1:abc"); + shouldFail("http://host:abc"); + shouldFail("http://:abc"); + shouldFail("http://a:@", "about:blank"); + shouldFail("http://:b@", "about:blank"); + shouldFail("http://:@", "about:blank"); + shouldFail("http://a:@"); + shouldFail("http://:b@"); + shouldFail("http://@"); + shouldFail("http://[0:f::f:f:0:0]:abc"); + shouldFail("../i", "sc:sd"); + shouldFail("../i", "sc:sd/sd"); + shouldFail("/i", "sc:sd"); + shouldFail("/i", "sc:sd/sd"); + shouldFail("?i", "sc:sd"); + shouldFail("?i", "sc:sd/sd"); + shouldFail("http://example example.com", "http://other.com/"); + shouldFail("http://[www.example.com]/", "about:blank"); + shouldFail("http://192.168.0.1 hello", "http://other.com/"); + shouldFail("http://[example.com]", "http://other.com/"); + shouldFail("i", "sc:sd"); + shouldFail("i", "sc:sd/sd"); + shouldFail("i"); + shouldFail("asdf"); + shouldFail("~"); + shouldFail("%"); + shouldFail("//%"); + shouldFail("~", "about:blank"); + shouldFail("~~~"); + shouldFail("://:0/"); + shouldFail("://:0/", ""); + shouldFail("://:0/", "about:blank"); + shouldFail("about~"); + shouldFail("//C:asdf/foo/bar", "file:///tmp/mock/path"); + shouldFail("http://[1234::ab#]"); + shouldFail("http://[1234::ab/]"); + shouldFail("http://[1234::ab?]"); + shouldFail("http://[1234::ab@]"); + shouldFail("http://[1234::ab~]"); + shouldFail("http://[2001::1"); + shouldFail("http://4:b\xE1"); + shouldFail("http://[1:2:3:4:5:6:7:8~]/"); + shouldFail("http://[a:b:c:d:e:f:g:127.0.0.1]"); + shouldFail("http://[a:b:c:d:e:f:g:h:127.0.0.1]"); + shouldFail("http://[a:b:c:d:e:f:127.0.0.0x11]"); // Chrome treats this as hex, Firefox and the spec fail + shouldFail("http://[a:b:c:d:e:f:127.0.-0.1]"); + shouldFail("asdf://space In\aHost"); + shouldFail("asdf://[0:0:0:0:a:b:c:d"); +} + +// These are in the spec but not in the web platform tests. +TEST_F(URLParserTest, AdditionalTests) +{ + checkURL("about:\a\aabc", {"about", "", "", "", 0, "%07%07abc", "", "", "about:%07%07abc"}); + checkURL("notspecial:\t\t\n\t", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"}); + checkURL("notspecial\t\t\n\t:\t\t\n\t/\t\t\n\t/\t\t\n\thost", {"notspecial", "", "", "host", 0, "", "", "", "notspecial://host"}); + checkRelativeURL("http:", "http://example.org/foo/bar?query#fragment", {"http", "", "", "example.org", 0, "/foo/bar", "query", "", "http://example.org/foo/bar?query"}); + checkRelativeURLDifferences("ws:", "http://example.org/foo/bar", + {"ws", "", "", "", 0, "", "", "", "ws:"}, + {"ws", "", "", "", 0, "s:", "", "", "ws:s:"}); + checkRelativeURL("notspecial:", "http://example.org/foo/bar", {"notspecial", "", "", "", 0, "", "", "", "notspecial:"}); + + const wchar_t surrogateBegin = 0xD800; + const wchar_t validSurrogateEnd = 0xDD55; + const wchar_t invalidSurrogateEnd = 'A'; + checkURL(utf16String<12>({'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 <WebCore/URL.h> +#include <WebCore/UserAgent.h> + +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 <WebCore/URL.h> +#include <WebCore/YouTubePluginReplacement.h> +#include <wtf/MainThread.h> + +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 <WebCore/URL.h> -#include <WebCore/UserAgentGtk.h> - -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 |