summaryrefslogtreecommitdiff
path: root/Tools/TestWebKitAPI/Tests/WebCore
diff options
context:
space:
mode:
Diffstat (limited to 'Tools/TestWebKitAPI/Tests/WebCore')
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/AffineTransform.cpp1024
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/CARingBuffer.cpp251
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/CSSParser.cpp84
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/CalculationValue.cpp283
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/Color.cpp190
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/ComplexTextController.cpp391
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp2795
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp121
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h67
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp121
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/ExtendedColor.cpp162
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/FileSystem.cpp122
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/FloatPoint.cpp598
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/FloatRect.cpp783
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/FloatSize.cpp328
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/GridPosition.cpp73
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/HTMLParserIdioms.cpp164
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/IntPoint.cpp283
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/IntRect.cpp615
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/IntSize.cpp332
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/LayoutUnit.cpp42
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/ParsedContentRange.cpp98
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/PublicSuffix.cpp78
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/SampleMap.cpp224
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/SecurityOrigin.cpp146
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/SharedBuffer.cpp163
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/SharedBufferTest.cpp56
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/SharedBufferTest.h46
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/TimeRanges.cpp291
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/TransformationMatrix.cpp1317
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/URL.cpp54
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/URLParser.cpp1305
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/UserAgentQuirks.cpp96
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/YouTubePluginReplacement.cpp86
-rw-r--r--Tools/TestWebKitAPI/Tests/WebCore/gtk/UserAgentQuirks.cpp63
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