3a36c82a162f13c3ddbd597fa58067cb9cb2c833
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebCore / ContentExtensions.cpp
1 /*
2  * Copyright (C) 2015 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27
28 #include "PlatformUtilities.h"
29 #include <JavaScriptCore/InitializeThreading.h>
30 #include <WebCore/CombinedURLFilters.h>
31 #include <WebCore/ContentExtensionCompiler.h>
32 #include <WebCore/ContentExtensionError.h>
33 #include <WebCore/ContentExtensionsBackend.h>
34 #include <WebCore/NFA.h>
35 #include <WebCore/ResourceLoadInfo.h>
36 #include <WebCore/URL.h>
37 #include <WebCore/URLFilterParser.h>
38 #include <wtf/MainThread.h>
39 #include <wtf/RunLoop.h>
40 #include <wtf/text/CString.h>
41 #include <wtf/text/StringBuilder.h>
42
43 namespace WebCore {
44 namespace ContentExtensions {
45 inline std::ostream& operator<<(std::ostream& os, const ActionType& action)
46 {
47     switch (action) {
48     case ActionType::BlockLoad:
49         return os << "ContentFilterAction::BlockLoad";
50     case ActionType::BlockCookies:
51         return os << "ContentFilterAction::BlockCookies";
52     case ActionType::CSSDisplayNoneSelector:
53         return os << "ContentFilterAction::CSSDisplayNone";
54     case ActionType::CSSDisplayNoneStyleSheet:
55         return os << "ContentFilterAction::CSSDisplayNoneStyleSheet";
56     case ActionType::IgnorePreviousRules:
57         return os << "ContentFilterAction::IgnorePreviousRules";
58     case ActionType::InvalidAction:
59         return os << "ContentFilterAction::InvalidAction";
60     }
61 }
62 }
63 }
64
65 using namespace WebCore;
66
67 namespace TestWebKitAPI {
68
69 class ContentExtensionTest : public testing::Test {
70 public:
71     virtual void SetUp()
72     {
73         WTF::initializeMainThread();
74         JSC::initializeThreading();
75         RunLoop::initializeMainRunLoop();
76     }
77 };
78
79 class InMemoryContentExtensionCompilationClient final : public WebCore::ContentExtensions::ContentExtensionCompilationClient {
80 public:
81     InMemoryContentExtensionCompilationClient(WebCore::ContentExtensions::CompiledContentExtensionData& data)
82         : m_data(data)
83     {
84     }
85
86     virtual void writeBytecode(Vector<WebCore::ContentExtensions::DFABytecode>&& bytecode) override
87     {
88         m_data.bytecode = WTF::move(bytecode);
89     }
90     
91     virtual void writeActions(Vector<WebCore::ContentExtensions::SerializedActionByte>&& actions) override
92     {
93         m_data.actions = WTF::move(actions);
94     }
95
96 private:
97     WebCore::ContentExtensions::CompiledContentExtensionData& m_data;
98 };
99
100 class InMemoryCompiledContentExtension : public ContentExtensions::CompiledContentExtension {
101 public:
102     static RefPtr<InMemoryCompiledContentExtension> createFromFilter(const String& filter)
103     {
104         WebCore::ContentExtensions::CompiledContentExtensionData extensionData;
105         InMemoryContentExtensionCompilationClient client(extensionData);
106         auto compilerError = ContentExtensions::compileRuleList(client, filter);
107         if (compilerError)
108             return nullptr;
109
110         return InMemoryCompiledContentExtension::create(WTF::move(extensionData));
111     }
112
113     static RefPtr<InMemoryCompiledContentExtension> create(ContentExtensions::CompiledContentExtensionData&& data)
114     {
115         return adoptRef(new InMemoryCompiledContentExtension(WTF::move(data)));
116     }
117
118     virtual ~InMemoryCompiledContentExtension()
119     {
120     }
121
122     virtual const ContentExtensions::DFABytecode* bytecode() const override { return m_data.bytecode.data(); }
123     virtual unsigned bytecodeLength() const override { return m_data.bytecode.size(); }
124     virtual const ContentExtensions::SerializedActionByte* actions() const override { return m_data.actions.data(); }
125     virtual unsigned actionsLength() const override { return m_data.actions.size(); }
126
127 private:
128     InMemoryCompiledContentExtension(ContentExtensions::CompiledContentExtensionData&& data)
129         : m_data(WTF::move(data))
130     {
131     }
132
133     ContentExtensions::CompiledContentExtensionData m_data;
134 };
135
136 void static testRequest(ContentExtensions::ContentExtensionsBackend contentExtensionsBackend, const ResourceLoadInfo& resourceLoadInfo, Vector<ContentExtensions::ActionType> expectedActions, bool ignorePreviousRules = false)
137 {
138     auto actions = contentExtensionsBackend.actionsForResourceLoad(resourceLoadInfo);
139     unsigned expectedSize = actions.size();
140     if (!ignorePreviousRules)
141         expectedSize--; // The last action is applying the compiled stylesheet.
142     
143     EXPECT_EQ(expectedActions.size(), expectedSize);
144     if (expectedActions.size() != expectedSize)
145         return;
146
147     for (unsigned i = 0; i < expectedActions.size(); ++i)
148         EXPECT_EQ(expectedActions[i], actions[i].type());
149     if (!ignorePreviousRules)
150         EXPECT_EQ(actions[actions.size() - 1].type(), ContentExtensions::ActionType::CSSDisplayNoneStyleSheet);
151 }
152
153 ResourceLoadInfo mainDocumentRequest(const char* url, ResourceType resourceType = ResourceType::Document)
154 {
155     return { URL(URL(), url), URL(URL(), url), resourceType };
156 }
157
158 ContentExtensions::ContentExtensionsBackend makeBackend(const char* json)
159 {
160     auto extension = InMemoryCompiledContentExtension::createFromFilter(json);
161     ContentExtensions::ContentExtensionsBackend backend;
162     backend.addContentExtension("testFilter", extension);
163     return backend;
164 }
165
166 TEST_F(ContentExtensionTest, Basic)
167 {
168     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]");
169
170     testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
171 }
172
173 TEST_F(ContentExtensionTest, RangeBasic)
174 {
175     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"w[0-9]c\", \"url-filter-is-case-sensitive\":true}},"
176         "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"[A-H][a-z]cko\", \"url-filter-is-case-sensitive\":true}}]");
177
178     testRequest(backend, mainDocumentRequest("http://w3c.org"), { ContentExtensions::ActionType::BlockLoad });
179     testRequest(backend, mainDocumentRequest("w2c://whatwg.org/"), { ContentExtensions::ActionType::BlockLoad });
180     testRequest(backend, mainDocumentRequest("http://webkit.org/w0c"), { ContentExtensions::ActionType::BlockLoad });
181     testRequest(backend, mainDocumentRequest("http://webkit.org/wac"), { });
182     testRequest(backend, mainDocumentRequest("http://webkit.org/wAc"), { });
183
184     // Note: URL parsing and canonicalization lowercase the scheme and hostname.
185     testRequest(backend, mainDocumentRequest("Aacko://webkit.org"), { });
186     testRequest(backend, mainDocumentRequest("aacko://webkit.org"), { });
187     testRequest(backend, mainDocumentRequest("http://gCcko.org/"), { });
188     testRequest(backend, mainDocumentRequest("http://gccko.org/"), { });
189
190     testRequest(backend, mainDocumentRequest("http://webkit.org/Gecko"), { ContentExtensions::ActionType::BlockCookies });
191     testRequest(backend, mainDocumentRequest("http://webkit.org/gecko"), { });
192     testRequest(backend, mainDocumentRequest("http://webkit.org/GEcko"), { });
193 }
194
195 TEST_F(ContentExtensionTest, RangeExclusionGeneratingUniversalTransition)
196 {
197     // Transition of the type ([^X]X) effictively transition on every input.
198     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[^a]+afoobar\"}}]");
199
200     testRequest(backend, mainDocumentRequest("http://w3c.org"), { });
201
202     testRequest(backend, mainDocumentRequest("http://w3c.org/foobafoobar"), { ContentExtensions::ActionType::BlockLoad });
203     testRequest(backend, mainDocumentRequest("http://w3c.org/foobarfoobar"), { });
204     testRequest(backend, mainDocumentRequest("http://w3c.org/FOOBAFOOBAR"), { ContentExtensions::ActionType::BlockLoad });
205     testRequest(backend, mainDocumentRequest("http://w3c.org/FOOBARFOOBAR"), { });
206
207     // The character before the "a" prefix cannot be another "a".
208     testRequest(backend, mainDocumentRequest("http://w3c.org/aafoobar"), { });
209     testRequest(backend, mainDocumentRequest("http://w3c.org/Aafoobar"), { });
210     testRequest(backend, mainDocumentRequest("http://w3c.org/aAfoobar"), { });
211     testRequest(backend, mainDocumentRequest("http://w3c.org/AAfoobar"), { });
212 }
213
214 TEST_F(ContentExtensionTest, PatternStartingWithGroup)
215 {
216     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(http://whatwg\\\\.org/)?webkit\134\134.org\"}}]");
217
218     testRequest(backend, mainDocumentRequest("http://whatwg.org/webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
219     testRequest(backend, mainDocumentRequest("http://whatwg.org/webkit.org"), { ContentExtensions::ActionType::BlockLoad });
220     testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
221     testRequest(backend, mainDocumentRequest("http://whatwg.org/"), { });
222     testRequest(backend, mainDocumentRequest("http://whatwg.org"), { });
223 }
224
225 TEST_F(ContentExtensionTest, PatternNestedGroups)
226 {
227     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/(foo(bar)*)+\"}}]");
228
229     testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad });
230     testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
231     testRequest(backend, mainDocumentRequest("http://webkit.org/foobarbar"), { ContentExtensions::ActionType::BlockLoad });
232     testRequest(backend, mainDocumentRequest("http://webkit.org/foofoobar"), { ContentExtensions::ActionType::BlockLoad });
233     testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoobar"), { ContentExtensions::ActionType::BlockLoad });
234     testRequest(backend, mainDocumentRequest("http://webkit.org/foob"), { ContentExtensions::ActionType::BlockLoad });
235     testRequest(backend, mainDocumentRequest("http://webkit.org/foor"), { ContentExtensions::ActionType::BlockLoad });
236
237     testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
238     testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { });
239     testRequest(backend, mainDocumentRequest("http://webkit.org/fobar"), { });
240 }
241
242 TEST_F(ContentExtensionTest, MatchPastEndOfString)
243 {
244     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".+\"}}]");
245
246     testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
247     testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad });
248     testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
249     testRequest(backend, mainDocumentRequest("http://webkit.org/foobarbar"), { ContentExtensions::ActionType::BlockLoad });
250     testRequest(backend, mainDocumentRequest("http://webkit.org/foofoobar"), { ContentExtensions::ActionType::BlockLoad });
251     testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoobar"), { ContentExtensions::ActionType::BlockLoad });
252     testRequest(backend, mainDocumentRequest("http://webkit.org/foob"), { ContentExtensions::ActionType::BlockLoad });
253     testRequest(backend, mainDocumentRequest("http://webkit.org/foor"), { ContentExtensions::ActionType::BlockLoad });
254 }
255
256 TEST_F(ContentExtensionTest, StartOfLineAssertion)
257 {
258     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^foobar\"}}]");
259
260     testRequest(backend, mainDocumentRequest("foobar://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
261     testRequest(backend, mainDocumentRequest("foobars:///foobar"), { ContentExtensions::ActionType::BlockLoad });
262     testRequest(backend, mainDocumentRequest("foobarfoobar:///foobarfoobarfoobar"), { ContentExtensions::ActionType::BlockLoad });
263
264     testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoo"), { });
265     testRequest(backend, mainDocumentRequest("http://webkit.org/foobarf"), { });
266     testRequest(backend, mainDocumentRequest("http://foobar.org/"), { });
267     testRequest(backend, mainDocumentRequest("http://foobar.org/"), { });
268 }
269
270 TEST_F(ContentExtensionTest, EndOfLineAssertion)
271 {
272     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foobar$\"}}]");
273
274     testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
275     testRequest(backend, mainDocumentRequest("file:///foobar"), { ContentExtensions::ActionType::BlockLoad });
276     testRequest(backend, mainDocumentRequest("file:///foobarfoobarfoobar"), { ContentExtensions::ActionType::BlockLoad });
277
278     testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoo"), { });
279     testRequest(backend, mainDocumentRequest("http://webkit.org/foobarf"), { });
280 }
281
282 TEST_F(ContentExtensionTest, EndOfLineAssertionWithInvertedCharacterSet)
283 {
284     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[^y]$\"}}]");
285
286     testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
287     testRequest(backend, mainDocumentRequest("http://webkit.org/a"), { ContentExtensions::ActionType::BlockLoad });
288     testRequest(backend, mainDocumentRequest("http://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
289     testRequest(backend, mainDocumentRequest("http://webkit.org/Ya"), { ContentExtensions::ActionType::BlockLoad });
290     testRequest(backend, mainDocumentRequest("http://webkit.org/yFoobar"), { ContentExtensions::ActionType::BlockLoad });
291     testRequest(backend, mainDocumentRequest("http://webkit.org/y"), { });
292     testRequest(backend, mainDocumentRequest("http://webkit.org/Y"), { });
293     testRequest(backend, mainDocumentRequest("http://webkit.org/foobary"), { });
294     testRequest(backend, mainDocumentRequest("http://webkit.org/foobarY"), { });
295 }
296
297 TEST_F(ContentExtensionTest, PrefixInfixSuffixExactMatch)
298 {
299     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"infix\"}},"
300         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^prefix\"}},"
301         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"suffix$\"}},"
302         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://exact\\\\.org/$\"}}]");
303
304     testRequest(backend, mainDocumentRequest("infix://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
305     testRequest(backend, mainDocumentRequest("http://infix.org/"), { ContentExtensions::ActionType::BlockLoad });
306     testRequest(backend, mainDocumentRequest("https://webkit.org/infix"), { ContentExtensions::ActionType::BlockLoad });
307
308     testRequest(backend, mainDocumentRequest("prefix://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
309     testRequest(backend, mainDocumentRequest("https://prefix.org/"), { });
310     testRequest(backend, mainDocumentRequest("https://webkit.org/prefix"), { });
311
312     testRequest(backend, mainDocumentRequest("https://webkit.org/suffix"), { ContentExtensions::ActionType::BlockLoad });
313     testRequest(backend, mainDocumentRequest("https://suffix.org/"), { });
314     testRequest(backend, mainDocumentRequest("suffix://webkit.org/"), { });
315
316     testRequest(backend, mainDocumentRequest("http://exact.org/"), { ContentExtensions::ActionType::BlockLoad });
317     testRequest(backend, mainDocumentRequest("http://exact.org/oops"), { });
318 }
319
320 TEST_F(ContentExtensionTest, DuplicatedMatchAllTermsInVariousFormat)
321 {
322     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*.*(.)*(.*)(.+)*(.?)*infix\"}},"
323         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"pre(.?)+(.+)?post\"}}]");
324
325     testRequest(backend, mainDocumentRequest("infix://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
326     testRequest(backend, mainDocumentRequest("http://infix.org/"), { ContentExtensions::ActionType::BlockLoad });
327     testRequest(backend, mainDocumentRequest("https://webkit.org/infix"), { ContentExtensions::ActionType::BlockLoad });
328
329     testRequest(backend, mainDocumentRequest("pre://webkit.org/post"), { ContentExtensions::ActionType::BlockLoad });
330     testRequest(backend, mainDocumentRequest("http://prepost.org/"), { ContentExtensions::ActionType::BlockLoad });
331     testRequest(backend, mainDocumentRequest("https://pre.org/posttail"), { ContentExtensions::ActionType::BlockLoad });
332     testRequest(backend, mainDocumentRequest("https://pre.pre/posttail"), { ContentExtensions::ActionType::BlockLoad });
333     testRequest(backend, mainDocumentRequest("https://pre.org/posttailpost"), { ContentExtensions::ActionType::BlockLoad });
334
335     testRequest(backend, mainDocumentRequest("https://post.org/pre"), { });
336     testRequest(backend, mainDocumentRequest("https://pre.org/pre"), { });
337     testRequest(backend, mainDocumentRequest("https://post.org/post"), { });
338 }
339
340 TEST_F(ContentExtensionTest, TermsKnownToMatchAnything)
341 {
342     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre1.*post1$\"}},"
343         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre2(.*)post2$\"}},"
344         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre3(.*)?post3$\"}},"
345         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre4(.*)+post4$\"}},"
346         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre5(.*)*post5$\"}},"
347         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre6(.)*post6$\"}},"
348         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre7(.+)*post7$\"}},"
349         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre8(.?)*post8$\"}},"
350         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre9(.+)?post9$\"}},"
351         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre0(.?)+post0$\"}}]");
352
353     testRequest(backend, mainDocumentRequest("pre1://webkit.org/post1"), { ContentExtensions::ActionType::BlockLoad });
354     testRequest(backend, mainDocumentRequest("pre2://webkit.org/post2"), { ContentExtensions::ActionType::BlockLoad });
355     testRequest(backend, mainDocumentRequest("pre3://webkit.org/post3"), { ContentExtensions::ActionType::BlockLoad });
356     testRequest(backend, mainDocumentRequest("pre4://webkit.org/post4"), { ContentExtensions::ActionType::BlockLoad });
357     testRequest(backend, mainDocumentRequest("pre5://webkit.org/post5"), { ContentExtensions::ActionType::BlockLoad });
358     testRequest(backend, mainDocumentRequest("pre6://webkit.org/post6"), { ContentExtensions::ActionType::BlockLoad });
359     testRequest(backend, mainDocumentRequest("pre7://webkit.org/post7"), { ContentExtensions::ActionType::BlockLoad });
360     testRequest(backend, mainDocumentRequest("pre8://webkit.org/post8"), { ContentExtensions::ActionType::BlockLoad });
361     testRequest(backend, mainDocumentRequest("pre9://webkit.org/post9"), { ContentExtensions::ActionType::BlockLoad });
362     testRequest(backend, mainDocumentRequest("pre0://webkit.org/post0"), { ContentExtensions::ActionType::BlockLoad });
363
364     testRequest(backend, mainDocumentRequest("pre1://webkit.org/post2"), { });
365     testRequest(backend, mainDocumentRequest("pre2://webkit.org/post3"), { });
366     testRequest(backend, mainDocumentRequest("pre3://webkit.org/post4"), { });
367     testRequest(backend, mainDocumentRequest("pre4://webkit.org/post5"), { });
368     testRequest(backend, mainDocumentRequest("pre5://webkit.org/post6"), { });
369     testRequest(backend, mainDocumentRequest("pre6://webkit.org/post7"), { });
370     testRequest(backend, mainDocumentRequest("pre7://webkit.org/post8"), { });
371     testRequest(backend, mainDocumentRequest("pre8://webkit.org/post9"), { });
372     testRequest(backend, mainDocumentRequest("pre9://webkit.org/post0"), { });
373     testRequest(backend, mainDocumentRequest("pre0://webkit.org/post1"), { });
374
375     testRequest(backend, mainDocumentRequest("pre0://webkit.org/post1"), { });
376     testRequest(backend, mainDocumentRequest("pre1://webkit.org/post2"), { });
377     testRequest(backend, mainDocumentRequest("pre2://webkit.org/post3"), { });
378     testRequest(backend, mainDocumentRequest("pre3://webkit.org/post4"), { });
379     testRequest(backend, mainDocumentRequest("pre4://webkit.org/post5"), { });
380     testRequest(backend, mainDocumentRequest("pre5://webkit.org/post6"), { });
381     testRequest(backend, mainDocumentRequest("pre6://webkit.org/post7"), { });
382     testRequest(backend, mainDocumentRequest("pre7://webkit.org/post8"), { });
383     testRequest(backend, mainDocumentRequest("pre8://webkit.org/post9"), { });
384     testRequest(backend, mainDocumentRequest("pre9://webkit.org/post0"), { });
385 }
386
387 TEST_F(ContentExtensionTest, TrailingDotStar)
388 {
389     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foo.*$\"}},"
390         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bar(.*)$\"}}]");
391
392     testRequest(backend, mainDocumentRequest("https://webkit.org/"), { });
393
394     testRequest(backend, mainDocumentRequest("foo://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
395     testRequest(backend, mainDocumentRequest("https://foo.org/"), { ContentExtensions::ActionType::BlockLoad });
396     testRequest(backend, mainDocumentRequest("https://webkit.foo/"), { ContentExtensions::ActionType::BlockLoad });
397     testRequest(backend, mainDocumentRequest("https://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad });
398
399     testRequest(backend, mainDocumentRequest("bar://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
400     testRequest(backend, mainDocumentRequest("https://bar.org/"), { ContentExtensions::ActionType::BlockLoad });
401     testRequest(backend, mainDocumentRequest("https://webkit.bar/"), { ContentExtensions::ActionType::BlockLoad });
402     testRequest(backend, mainDocumentRequest("https://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad });
403 }
404
405 TEST_F(ContentExtensionTest, TrailingTermsCarryingNoData)
406 {
407     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foob?a?r?\"}},"
408         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bazo(ok)?a?$\"}},"
409         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"cats*$\"}}]");
410
411     testRequest(backend, mainDocumentRequest("https://webkit.org/"), { });
412
413     // Anything is fine after foo.
414     testRequest(backend, mainDocumentRequest("https://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad });
415     testRequest(backend, mainDocumentRequest("https://webkit.org/foob"), { ContentExtensions::ActionType::BlockLoad });
416     testRequest(backend, mainDocumentRequest("https://webkit.org/fooc"), { ContentExtensions::ActionType::BlockLoad });
417     testRequest(backend, mainDocumentRequest("https://webkit.org/fooba"), { ContentExtensions::ActionType::BlockLoad });
418     testRequest(backend, mainDocumentRequest("https://webkit.org/foobar"), { ContentExtensions::ActionType::BlockLoad });
419     testRequest(backend, mainDocumentRequest("https://webkit.org/foobar-stuff"), { ContentExtensions::ActionType::BlockLoad });
420
421     // Bazooka has to be at the tail without any character not defined by the filter.
422     testRequest(backend, mainDocumentRequest("https://webkit.org/baz"), { });
423     testRequest(backend, mainDocumentRequest("https://webkit.org/bazo"), { ContentExtensions::ActionType::BlockLoad });
424     testRequest(backend, mainDocumentRequest("https://webkit.org/bazoa"), { ContentExtensions::ActionType::BlockLoad });
425     testRequest(backend, mainDocumentRequest("https://webkit.org/bazob"), { });
426     testRequest(backend, mainDocumentRequest("https://webkit.org/bazoo"), { });
427     testRequest(backend, mainDocumentRequest("https://webkit.org/bazook"), { ContentExtensions::ActionType::BlockLoad });
428     testRequest(backend, mainDocumentRequest("https://webkit.org/bazookb"), { });
429     testRequest(backend, mainDocumentRequest("https://webkit.org/bazooka"), { ContentExtensions::ActionType::BlockLoad });
430     testRequest(backend, mainDocumentRequest("https://webkit.org/bazookaa"), { });
431
432     // The pattern must finish with cat, with any number of 's' following it, but no other character.
433     testRequest(backend, mainDocumentRequest("https://cat.org/"), { });
434     testRequest(backend, mainDocumentRequest("https://cats.org/"), { });
435     testRequest(backend, mainDocumentRequest("https://webkit.org/cat"), { ContentExtensions::ActionType::BlockLoad });
436     testRequest(backend, mainDocumentRequest("https://webkit.org/cats"), { ContentExtensions::ActionType::BlockLoad });
437     testRequest(backend, mainDocumentRequest("https://webkit.org/catss"), { ContentExtensions::ActionType::BlockLoad });
438     testRequest(backend, mainDocumentRequest("https://webkit.org/catsss"), { ContentExtensions::ActionType::BlockLoad });
439     testRequest(backend, mainDocumentRequest("https://webkit.org/catso"), { });
440 }
441
442 TEST_F(ContentExtensionTest, LoadType)
443 {
444     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[\"third-party\"]}},"
445         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"whatwg.org\",\"load-type\":[\"first-party\"]}},"
446         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"alwaysblock.pdf\"}}]");
447     
448     testRequest(backend, mainDocumentRequest("http://webkit.org"), { });
449     testRequest(backend, {URL(URL(), "http://webkit.org"), URL(URL(), "http://not_webkit.org"), ResourceType::Document}, { ContentExtensions::ActionType::BlockLoad });
450         
451     testRequest(backend, mainDocumentRequest("http://whatwg.org"), { ContentExtensions::ActionType::BlockLoad });
452     testRequest(backend, {URL(URL(), "http://whatwg.org"), URL(URL(), "http://not_whatwg.org"), ResourceType::Document}, { });
453     
454     testRequest(backend, mainDocumentRequest("http://foobar.org/alwaysblock.pdf"), { ContentExtensions::ActionType::BlockLoad });
455     testRequest(backend, {URL(URL(), "http://foobar.org/alwaysblock.pdf"), URL(URL(), "http://not_foobar.org/alwaysblock.pdf"), ResourceType::Document}, { ContentExtensions::ActionType::BlockLoad });
456 }
457
458 TEST_F(ContentExtensionTest, ResourceType)
459 {
460     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\"]}},"
461         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"block_only_images\",\"resource-type\":[\"image\"]}}]");
462
463     testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Document), { ContentExtensions::ActionType::BlockLoad });
464     testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad });
465     testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::StyleSheet), { ContentExtensions::ActionType::BlockLoad });
466     testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Script), { ContentExtensions::ActionType::BlockLoad });
467     testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Font), { ContentExtensions::ActionType::BlockLoad });
468     testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Raw), { ContentExtensions::ActionType::BlockLoad });
469     testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::SVGDocument), { ContentExtensions::ActionType::BlockLoad });
470     testRequest(backend, mainDocumentRequest("http://block_all_types.org", ResourceType::Media), { ContentExtensions::ActionType::BlockLoad });
471     testRequest(backend, mainDocumentRequest("http://block_only_images.org", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad });
472     testRequest(backend, mainDocumentRequest("http://block_only_images.org", ResourceType::Document), { });
473 }
474
475 TEST_F(ContentExtensionTest, ResourceOrLoadTypeMatchingEverything)
476 {
477     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"resource-type\":[\"image\"]}},"
478         "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\".*\",\"load-type\":[\"third-party\"]}},"
479         "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\".*\",\"load-type\":[\"first-party\"]}}]");
480     
481     testRequest(backend, mainDocumentRequest("http://webkit.org"), { }, true);
482     testRequest(backend, {URL(URL(), "http://webkit.org"), URL(URL(), "http://not_webkit.org"), ResourceType::Document}, { ContentExtensions::ActionType::BlockCookies });
483     testRequest(backend, {URL(URL(), "http://webkit.org"), URL(URL(), "http://not_webkit.org"), ResourceType::Image}, { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad });
484 }
485     
486 TEST_F(ContentExtensionTest, MultiDFA)
487 {
488     // Make an NFA with about 1400 nodes.
489     StringBuilder ruleList;
490     ruleList.append('[');
491     for (char c1 = 'A'; c1 <= 'Z'; ++c1) {
492         for (char c2 = 'A'; c2 <= 'C'; ++c2) {
493             for (char c3 = 'A'; c3 <= 'C'; ++c3) {
494                 if (c1 != 'A' || c2 != 'A' || c3 != 'A')
495                     ruleList.append(',');
496                 ruleList.append("{\"action\":{\"type\":\"");
497                 
498                 // Put an ignore-previous-rules near the middle.
499                 if (c1 == 'L' && c2 == 'A' && c3 == 'A')
500                     ruleList.append("ignore-previous-rules");
501                 else
502                     ruleList.append("block");
503                 
504                 ruleList.append("\"},\"trigger\":{\"url-filter\":\".*");
505                 ruleList.append(c1);
506                 ruleList.append(c2);
507                 ruleList.append(c3);
508                 ruleList.append("\", \"url-filter-is-case-sensitive\":true}}");
509             }
510         }
511     }
512     ruleList.append(']');
513     
514     auto backend = makeBackend(ruleList.toString().utf8().data());
515
516     testRequest(backend, mainDocumentRequest("http://webkit.org/AAA"), { ContentExtensions::ActionType::BlockLoad });
517     testRequest(backend, mainDocumentRequest("http://webkit.org/ZAA"), { ContentExtensions::ActionType::BlockLoad });
518     testRequest(backend, mainDocumentRequest("http://webkit.org/LAA/AAA"), { }, true);
519     testRequest(backend, mainDocumentRequest("http://webkit.org/LAA/MAA"), { ContentExtensions::ActionType::BlockLoad }, true);
520     testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
521 }
522
523 void checkCompilerError(const char* json, ContentExtensions::ContentExtensionError expectedError)
524 {
525     WebCore::ContentExtensions::CompiledContentExtensionData extensionData;
526     InMemoryContentExtensionCompilationClient client(extensionData);
527     std::error_code compilerError = ContentExtensions::compileRuleList(client, json);
528     EXPECT_EQ(compilerError.value(), static_cast<int>(expectedError));
529 }
530
531 TEST_F(ContentExtensionTest, InvalidJSON)
532 {
533     checkCompilerError("[", ContentExtensions::ContentExtensionError::JSONInvalid);
534     checkCompilerError("123", ContentExtensions::ContentExtensionError::JSONTopLevelStructureNotAnObject);
535     checkCompilerError("{}", ContentExtensions::ContentExtensionError::JSONTopLevelStructureNotAnArray);
536     // FIXME: Add unit test for JSONInvalidRule if that is possible to hit.
537     checkCompilerError("[]", ContentExtensions::ContentExtensionError::JSONContainsNoRules);
538
539     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":5}]",
540         ContentExtensions::ContentExtensionError::JSONInvalidTrigger);
541     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"\"}}]",
542         ContentExtensions::ContentExtensionError::JSONInvalidURLFilterInTrigger);
543     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":{}}}]",
544         ContentExtensions::ContentExtensionError::JSONInvalidURLFilterInTrigger);
545
546     // FIXME: Add unit test for JSONInvalidObjectInTriggerFlagsArray if that is possible to hit.
547     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":{}}}]",
548         ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
549     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[\"invalid\"]}}]",
550         ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
551     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":{}}}]",
552         ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
553     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":[\"invalid\"]}}]",
554         ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
555
556     checkCompilerError("[{\"action\":5,\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
557         ContentExtensions::ContentExtensionError::JSONInvalidAction);
558     checkCompilerError("[{\"action\":{\"type\":\"invalid\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
559         ContentExtensions::ContentExtensionError::JSONInvalidActionType);
560     checkCompilerError("[{\"action\":{\"type\":\"css-display-none\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
561         ContentExtensions::ContentExtensionError::JSONInvalidCSSDisplayNoneActionType);
562
563     checkCompilerError("[{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"webkit.org\"}},"
564         "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]",
565         ContentExtensions::ContentExtensionError::RegexMatchesEverythingAfterIgnorePreviousRules);
566     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[\"}}]",
567         ContentExtensions::ContentExtensionError::JSONInvalidRegex);
568 }
569
570 TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines1)
571 {
572     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^.*foo\"}},"
573         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bar$\"}},"
574         "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[ab]+bang\"}}]");
575
576     testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad });
577     testRequest(backend, mainDocumentRequest("foo://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad });
578     testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad });
579     testRequest(backend, mainDocumentRequest("bar://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad });
580
581     testRequest(backend, mainDocumentRequest("abang://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
582     testRequest(backend, mainDocumentRequest("bbang://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
583     testRequest(backend, mainDocumentRequest("cbang://webkit.org/"), { });
584     testRequest(backend, mainDocumentRequest("http://webkit.org/bang"), { });
585     testRequest(backend, mainDocumentRequest("bang://webkit.org/"), { });
586 }
587
588 TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines1Partitioning)
589 {
590     ContentExtensions::CombinedURLFilters combinedURLFilters;
591     ContentExtensions::URLFilterParser parser(combinedURLFilters);
592
593     // Those two share a prefix.
594     EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^.*foo", false, 0));
595     EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("bar$", false, 1));
596
597     // Not this one.
598     EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^[ab]+bang", false, 0));
599
600     EXPECT_EQ(2ul, combinedURLFilters.createNFAs().size());
601 }
602
603 TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines2)
604 {
605     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^foo\"}},"
606     "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^.*[a-c]+bar\"}},"
607     "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^webkit:\"}},"
608     "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-c]+b+oom\"}}]");
609
610     testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
611     testRequest(backend, mainDocumentRequest("foo://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
612     testRequest(backend, mainDocumentRequest("webkit://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
613
614     testRequest(backend, mainDocumentRequest("http://bar.org/"), { });
615     testRequest(backend, mainDocumentRequest("http://abar.org/"), { ContentExtensions::ActionType::BlockLoad });
616     testRequest(backend, mainDocumentRequest("http://bbar.org/"), { ContentExtensions::ActionType::BlockLoad });
617     testRequest(backend, mainDocumentRequest("http://cbar.org/"), { ContentExtensions::ActionType::BlockLoad });
618     testRequest(backend, mainDocumentRequest("http://abcbar.org/"), { ContentExtensions::ActionType::BlockLoad });
619     testRequest(backend, mainDocumentRequest("http://dbar.org/"), { });
620 }
621
622 TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines2Partitioning)
623 {
624     ContentExtensions::CombinedURLFilters combinedURLFilters;
625     ContentExtensions::URLFilterParser parser(combinedURLFilters);
626
627     EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^foo", false, 0));
628     EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^.*[a-c]+bar", false, 1));
629     EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^webkit:", false, 2));
630     EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("[a-c]+b+oom", false, 3));
631
632     // "^foo" and "^webkit:" can be grouped, the other two have a variable prefix.
633     EXPECT_EQ(3ul, combinedURLFilters.createNFAs().size());
634 }
635
636 static void testPatternStatus(String pattern, ContentExtensions::URLFilterParser::ParseStatus status)
637 {
638     ContentExtensions::CombinedURLFilters combinedURLFilters;
639     ContentExtensions::URLFilterParser parser(combinedURLFilters);
640     EXPECT_EQ(status, parser.addPattern(pattern, false, 0));
641 }
642     
643 TEST_F(ContentExtensionTest, ParsingFailures)
644 {
645     testPatternStatus("a*b?.*.?[a-z]?[a-z]*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
646     testPatternStatus("a*b?.*.?[a-z]?[a-z]+", ContentExtensions::URLFilterParser::ParseStatus::Ok);
647     testPatternStatus("a*b?.*.?[a-z]?[a-z]", ContentExtensions::URLFilterParser::ParseStatus::Ok);
648     testPatternStatus(".*?a", ContentExtensions::URLFilterParser::ParseStatus::Ok);
649     testPatternStatus(".*a", ContentExtensions::URLFilterParser::ParseStatus::Ok);
650     
651     testPatternStatus("(?!)", ContentExtensions::URLFilterParser::ParseStatus::Group);
652     testPatternStatus("(?=)", ContentExtensions::URLFilterParser::ParseStatus::Group);
653     testPatternStatus("(?!a)", ContentExtensions::URLFilterParser::ParseStatus::Group);
654     testPatternStatus("(?=a)", ContentExtensions::URLFilterParser::ParseStatus::Group);
655     testPatternStatus("(regex)", ContentExtensions::URLFilterParser::ParseStatus::Ok);
656     testPatternStatus("(regex", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
657     testPatternStatus("((regex)", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
658     testPatternStatus("(?:regex)", ContentExtensions::URLFilterParser::ParseStatus::Ok);
659     testPatternStatus("(?:regex", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
660     testPatternStatus("[^.]+", ContentExtensions::URLFilterParser::ParseStatus::Ok);
661     
662     testPatternStatus("a++", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
663     testPatternStatus("[a]++", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
664     testPatternStatus("+", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
665     
666     testPatternStatus("[", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
667     testPatternStatus("[a}", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
668     
669     // FIXME: Look into why these do not cause YARR parsing errors.  They probably should.
670     testPatternStatus("a]", ContentExtensions::URLFilterParser::ParseStatus::Ok);
671     testPatternStatus("{", ContentExtensions::URLFilterParser::ParseStatus::Ok);
672     testPatternStatus("{[a]", ContentExtensions::URLFilterParser::ParseStatus::Ok);
673     testPatternStatus("{0", ContentExtensions::URLFilterParser::ParseStatus::Ok);
674     testPatternStatus("{0,", ContentExtensions::URLFilterParser::ParseStatus::Ok);
675     testPatternStatus("{0,1", ContentExtensions::URLFilterParser::ParseStatus::Ok);
676     testPatternStatus("a{0,1", ContentExtensions::URLFilterParser::ParseStatus::Ok);
677     testPatternStatus("a{a,b}", ContentExtensions::URLFilterParser::ParseStatus::Ok);
678
679     const char nonASCII[2] = {-1, '\0'};
680     testPatternStatus(nonASCII, ContentExtensions::URLFilterParser::ParseStatus::NonASCII);
681     testPatternStatus("\\xff", ContentExtensions::URLFilterParser::ParseStatus::NonASCII);
682     
683     testPatternStatus("\\x\\r\\n", ContentExtensions::URLFilterParser::ParseStatus::Ok);
684     testPatternStatus("\\b", ContentExtensions::URLFilterParser::ParseStatus::WordBoundary);
685     testPatternStatus("[\\d]", ContentExtensions::URLFilterParser::ParseStatus::AtomCharacter);
686     testPatternStatus("\\d\\D\\w\\s\\v\\h\\i\\c", ContentExtensions::URLFilterParser::ParseStatus::UnsupportedCharacterClass);
687     
688     testPatternStatus("this|that", ContentExtensions::URLFilterParser::ParseStatus::Disjunction);
689     testPatternStatus("a{0,1}b", ContentExtensions::URLFilterParser::ParseStatus::Ok);
690     testPatternStatus("a{0,2}b", ContentExtensions::URLFilterParser::ParseStatus::InvalidQuantifier);
691     testPatternStatus("", ContentExtensions::URLFilterParser::ParseStatus::EmptyPattern);
692     testPatternStatus("$$", ContentExtensions::URLFilterParser::ParseStatus::MisplacedEndOfLine);
693     testPatternStatus("a^", ContentExtensions::URLFilterParser::ParseStatus::MisplacedStartOfLine);
694     testPatternStatus("(^)", ContentExtensions::URLFilterParser::ParseStatus::MisplacedStartOfLine);
695     
696     testPatternStatus("(a)\\1", ContentExtensions::URLFilterParser::ParseStatus::Ok); // This should be BackReference, right?
697 }
698
699 TEST_F(ContentExtensionTest, PatternMatchingTheEmptyString)
700 {
701     // Simple atoms.
702     testPatternStatus(".*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
703     testPatternStatus("a*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
704     testPatternStatus(".?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
705     testPatternStatus("a?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
706
707     // Character sets.
708     testPatternStatus("[a-z]*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
709     testPatternStatus("[a-z]?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
710
711     // Groups.
712     testPatternStatus("(foobar)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
713     testPatternStatus("(foobar)?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
714     testPatternStatus("(.*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
715     testPatternStatus("(a*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
716     testPatternStatus("(.?)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
717     testPatternStatus("(a?)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
718     testPatternStatus("([a-z]*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
719     testPatternStatus("([a-z]?)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
720
721     testPatternStatus("(.)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
722     testPatternStatus("(.+)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
723     testPatternStatus("(.?)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
724     testPatternStatus("(.*)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
725     testPatternStatus("(.+)?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
726     testPatternStatus("(.?)+", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
727
728     // Nested groups.
729     testPatternStatus("((foo)?((.)*)(bar)*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
730 }
731
732 } // namespace TestWebKitAPI