2 * Copyright (C) 2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
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>
44 namespace ContentExtensions {
45 inline std::ostream& operator<<(std::ostream& os, const ActionType& 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";
65 using namespace WebCore;
67 namespace TestWebKitAPI {
69 class ContentExtensionTest : public testing::Test {
73 WTF::initializeMainThread();
74 JSC::initializeThreading();
75 RunLoop::initializeMainRunLoop();
79 class InMemoryContentExtensionCompilationClient final : public WebCore::ContentExtensions::ContentExtensionCompilationClient {
81 InMemoryContentExtensionCompilationClient(WebCore::ContentExtensions::CompiledContentExtensionData& data)
86 virtual void writeBytecode(Vector<WebCore::ContentExtensions::DFABytecode>&& bytecode) override
88 m_data.bytecode = WTF::move(bytecode);
91 virtual void writeActions(Vector<WebCore::ContentExtensions::SerializedActionByte>&& actions) override
93 m_data.actions = WTF::move(actions);
97 WebCore::ContentExtensions::CompiledContentExtensionData& m_data;
100 class InMemoryCompiledContentExtension : public ContentExtensions::CompiledContentExtension {
102 static RefPtr<InMemoryCompiledContentExtension> createFromFilter(const String& filter)
104 WebCore::ContentExtensions::CompiledContentExtensionData extensionData;
105 InMemoryContentExtensionCompilationClient client(extensionData);
106 auto compilerError = ContentExtensions::compileRuleList(client, filter);
110 return InMemoryCompiledContentExtension::create(WTF::move(extensionData));
113 static RefPtr<InMemoryCompiledContentExtension> create(ContentExtensions::CompiledContentExtensionData&& data)
115 return adoptRef(new InMemoryCompiledContentExtension(WTF::move(data)));
118 virtual ~InMemoryCompiledContentExtension()
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(); }
128 InMemoryCompiledContentExtension(ContentExtensions::CompiledContentExtensionData&& data)
129 : m_data(WTF::move(data))
133 ContentExtensions::CompiledContentExtensionData m_data;
136 void static testRequest(ContentExtensions::ContentExtensionsBackend contentExtensionsBackend, const ResourceLoadInfo& resourceLoadInfo, Vector<ContentExtensions::ActionType> expectedActions, bool ignorePreviousRules = false)
138 auto actions = contentExtensionsBackend.actionsForResourceLoad(resourceLoadInfo);
139 unsigned expectedSize = actions.size();
140 if (!ignorePreviousRules)
141 expectedSize--; // The last action is applying the compiled stylesheet.
143 EXPECT_EQ(expectedActions.size(), expectedSize);
144 if (expectedActions.size() != expectedSize)
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);
153 ResourceLoadInfo mainDocumentRequest(const char* url, ResourceType resourceType = ResourceType::Document)
155 return { URL(URL(), url), URL(URL(), url), resourceType };
158 ContentExtensions::ContentExtensionsBackend makeBackend(const char* json)
160 auto extension = InMemoryCompiledContentExtension::createFromFilter(json);
161 ContentExtensions::ContentExtensionsBackend backend;
162 backend.addContentExtension("testFilter", extension);
166 TEST_F(ContentExtensionTest, Basic)
168 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]");
170 testRequest(backend, mainDocumentRequest("http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
173 TEST_F(ContentExtensionTest, RangeBasic)
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}}]");
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"), { });
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/"), { });
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"), { });
195 TEST_F(ContentExtensionTest, RangeExclusionGeneratingUniversalTransition)
197 // Transition of the type ([^X]X) effictively transition on every input.
198 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[^a]+afoobar\"}}]");
200 testRequest(backend, mainDocumentRequest("http://w3c.org"), { });
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"), { });
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"), { });
214 TEST_F(ContentExtensionTest, PatternStartingWithGroup)
216 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^(http://whatwg\\\\.org/)?webkit\134\134.org\"}}]");
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"), { });
225 TEST_F(ContentExtensionTest, PatternNestedGroups)
227 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^http://webkit\\\\.org/(foo(bar)*)+\"}}]");
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 });
237 testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
238 testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { });
239 testRequest(backend, mainDocumentRequest("http://webkit.org/fobar"), { });
242 TEST_F(ContentExtensionTest, MatchPastEndOfString)
244 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".+\"}}]");
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 });
256 TEST_F(ContentExtensionTest, StartOfLineAssertion)
258 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^foobar\"}}]");
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 });
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/"), { });
270 TEST_F(ContentExtensionTest, EndOfLineAssertion)
272 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foobar$\"}}]");
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 });
278 testRequest(backend, mainDocumentRequest("http://webkit.org/foobarfoo"), { });
279 testRequest(backend, mainDocumentRequest("http://webkit.org/foobarf"), { });
282 TEST_F(ContentExtensionTest, EndOfLineAssertionWithInvertedCharacterSet)
284 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[^y]$\"}}]");
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"), { });
297 TEST_F(ContentExtensionTest, PrefixInfixSuffixExactMatch)
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/$\"}}]");
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 });
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"), { });
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/"), { });
316 testRequest(backend, mainDocumentRequest("http://exact.org/"), { ContentExtensions::ActionType::BlockLoad });
317 testRequest(backend, mainDocumentRequest("http://exact.org/oops"), { });
320 TEST_F(ContentExtensionTest, DuplicatedMatchAllTermsInVariousFormat)
322 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*.*(.)*(.*)(.+)*(.?)*infix\"}},"
323 "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"pre(.?)+(.+)?post\"}}]");
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 });
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 });
335 testRequest(backend, mainDocumentRequest("https://post.org/pre"), { });
336 testRequest(backend, mainDocumentRequest("https://pre.org/pre"), { });
337 testRequest(backend, mainDocumentRequest("https://post.org/post"), { });
340 TEST_F(ContentExtensionTest, TermsKnownToMatchAnything)
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$\"}}]");
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 });
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"), { });
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"), { });
387 TEST_F(ContentExtensionTest, TrailingDotStar)
389 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"foo.*$\"}},"
390 "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bar(.*)$\"}}]");
392 testRequest(backend, mainDocumentRequest("https://webkit.org/"), { });
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 });
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 });
405 TEST_F(ContentExtensionTest, TrailingTermsCarryingNoData)
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*$\"}}]");
411 testRequest(backend, mainDocumentRequest("https://webkit.org/"), { });
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 });
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"), { });
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"), { });
442 TEST_F(ContentExtensionTest, LoadType)
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\"}}]");
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 });
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}, { });
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 });
458 TEST_F(ContentExtensionTest, ResourceType)
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\",\"popup\"]}},"
461 "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"block_only_images\",\"resource-type\":[\"image\"]}}]");
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_all_types.org", ResourceType::Popup), { ContentExtensions::ActionType::BlockLoad });
472 testRequest(backend, mainDocumentRequest("http://block_only_images.org", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad });
473 testRequest(backend, mainDocumentRequest("http://block_only_images.org", ResourceType::Document), { });
476 TEST_F(ContentExtensionTest, ResourceOrLoadTypeMatchingEverything)
478 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"resource-type\":[\"image\"]}},"
479 "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\".*\",\"load-type\":[\"third-party\"]}},"
480 "{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\".*\",\"load-type\":[\"first-party\"]}}]");
482 testRequest(backend, mainDocumentRequest("http://webkit.org"), { }, true);
483 testRequest(backend, {URL(URL(), "http://webkit.org"), URL(URL(), "http://not_webkit.org"), ResourceType::Document}, { ContentExtensions::ActionType::BlockCookies });
484 testRequest(backend, {URL(URL(), "http://webkit.org"), URL(URL(), "http://not_webkit.org"), ResourceType::Image}, { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad });
487 TEST_F(ContentExtensionTest, MultiDFA)
489 // Make an NFA with about 1400 nodes.
490 StringBuilder ruleList;
491 ruleList.append('[');
492 for (char c1 = 'A'; c1 <= 'Z'; ++c1) {
493 for (char c2 = 'A'; c2 <= 'C'; ++c2) {
494 for (char c3 = 'A'; c3 <= 'C'; ++c3) {
495 if (c1 != 'A' || c2 != 'A' || c3 != 'A')
496 ruleList.append(',');
497 ruleList.append("{\"action\":{\"type\":\"");
499 // Put an ignore-previous-rules near the middle.
500 if (c1 == 'L' && c2 == 'A' && c3 == 'A')
501 ruleList.append("ignore-previous-rules");
503 ruleList.append("block");
505 ruleList.append("\"},\"trigger\":{\"url-filter\":\".*");
509 ruleList.append("\", \"url-filter-is-case-sensitive\":true}}");
513 ruleList.append(']');
515 auto backend = makeBackend(ruleList.toString().utf8().data());
517 testRequest(backend, mainDocumentRequest("http://webkit.org/AAA"), { ContentExtensions::ActionType::BlockLoad });
518 testRequest(backend, mainDocumentRequest("http://webkit.org/ZAA"), { ContentExtensions::ActionType::BlockLoad });
519 testRequest(backend, mainDocumentRequest("http://webkit.org/LAA/AAA"), { }, true);
520 testRequest(backend, mainDocumentRequest("http://webkit.org/LAA/MAA"), { ContentExtensions::ActionType::BlockLoad }, true);
521 testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
524 void checkCompilerError(const char* json, ContentExtensions::ContentExtensionError expectedError)
526 WebCore::ContentExtensions::CompiledContentExtensionData extensionData;
527 InMemoryContentExtensionCompilationClient client(extensionData);
528 std::error_code compilerError = ContentExtensions::compileRuleList(client, json);
529 EXPECT_EQ(compilerError.value(), static_cast<int>(expectedError));
532 TEST_F(ContentExtensionTest, InvalidJSON)
534 checkCompilerError("[", ContentExtensions::ContentExtensionError::JSONInvalid);
535 checkCompilerError("123", ContentExtensions::ContentExtensionError::JSONTopLevelStructureNotAnObject);
536 checkCompilerError("{}", ContentExtensions::ContentExtensionError::JSONTopLevelStructureNotAnArray);
537 // FIXME: Add unit test for JSONInvalidRule if that is possible to hit.
538 checkCompilerError("[]", ContentExtensions::ContentExtensionError::JSONContainsNoRules);
540 checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":5}]",
541 ContentExtensions::ContentExtensionError::JSONInvalidTrigger);
542 checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"\"}}]",
543 ContentExtensions::ContentExtensionError::JSONInvalidURLFilterInTrigger);
544 checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":{}}}]",
545 ContentExtensions::ContentExtensionError::JSONInvalidURLFilterInTrigger);
547 // FIXME: Add unit test for JSONInvalidObjectInTriggerFlagsArray if that is possible to hit.
548 checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":{}}}]",
549 ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
550 checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[\"invalid\"]}}]",
551 ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
552 checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":{}}}]",
553 ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
554 checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":[\"invalid\"]}}]",
555 ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
557 checkCompilerError("[{\"action\":5,\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
558 ContentExtensions::ContentExtensionError::JSONInvalidAction);
559 checkCompilerError("[{\"action\":{\"type\":\"invalid\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
560 ContentExtensions::ContentExtensionError::JSONInvalidActionType);
561 checkCompilerError("[{\"action\":{\"type\":\"css-display-none\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
562 ContentExtensions::ContentExtensionError::JSONInvalidCSSDisplayNoneActionType);
564 checkCompilerError("[{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"webkit.org\"}},"
565 "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]",
566 ContentExtensions::ContentExtensionError::RegexMatchesEverythingAfterIgnorePreviousRules);
567 checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[\"}}]",
568 ContentExtensions::ContentExtensionError::JSONInvalidRegex);
571 TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines1)
573 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^.*foo\"}},"
574 "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"bar$\"}},"
575 "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^[ab]+bang\"}}]");
577 testRequest(backend, mainDocumentRequest("http://webkit.org/foo"), { ContentExtensions::ActionType::BlockLoad });
578 testRequest(backend, mainDocumentRequest("foo://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad });
579 testRequest(backend, mainDocumentRequest("http://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad });
580 testRequest(backend, mainDocumentRequest("bar://webkit.org/bar"), { ContentExtensions::ActionType::BlockLoad });
582 testRequest(backend, mainDocumentRequest("abang://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
583 testRequest(backend, mainDocumentRequest("bbang://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
584 testRequest(backend, mainDocumentRequest("cbang://webkit.org/"), { });
585 testRequest(backend, mainDocumentRequest("http://webkit.org/bang"), { });
586 testRequest(backend, mainDocumentRequest("bang://webkit.org/"), { });
589 TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines1Partitioning)
591 ContentExtensions::CombinedURLFilters combinedURLFilters;
592 ContentExtensions::URLFilterParser parser(combinedURLFilters);
594 // Those two share a prefix.
595 EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^.*foo", false, 0));
596 EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("bar$", false, 1));
599 EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^[ab]+bang", false, 0));
601 EXPECT_EQ(2ul, combinedURLFilters.createNFAs().size());
604 TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines2)
606 auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^foo\"}},"
607 "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^.*[a-c]+bar\"}},"
608 "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^webkit:\"}},"
609 "{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[a-c]+b+oom\"}}]");
611 testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
612 testRequest(backend, mainDocumentRequest("foo://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
613 testRequest(backend, mainDocumentRequest("webkit://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
615 testRequest(backend, mainDocumentRequest("http://bar.org/"), { });
616 testRequest(backend, mainDocumentRequest("http://abar.org/"), { ContentExtensions::ActionType::BlockLoad });
617 testRequest(backend, mainDocumentRequest("http://bbar.org/"), { ContentExtensions::ActionType::BlockLoad });
618 testRequest(backend, mainDocumentRequest("http://cbar.org/"), { ContentExtensions::ActionType::BlockLoad });
619 testRequest(backend, mainDocumentRequest("http://abcbar.org/"), { ContentExtensions::ActionType::BlockLoad });
620 testRequest(backend, mainDocumentRequest("http://dbar.org/"), { });
623 TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines2Partitioning)
625 ContentExtensions::CombinedURLFilters combinedURLFilters;
626 ContentExtensions::URLFilterParser parser(combinedURLFilters);
628 EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^foo", false, 0));
629 EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^.*[a-c]+bar", false, 1));
630 EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("^webkit:", false, 2));
631 EXPECT_EQ(ContentExtensions::URLFilterParser::ParseStatus::Ok, parser.addPattern("[a-c]+b+oom", false, 3));
633 // "^foo" and "^webkit:" can be grouped, the other two have a variable prefix.
634 EXPECT_EQ(3ul, combinedURLFilters.createNFAs().size());
637 static void testPatternStatus(String pattern, ContentExtensions::URLFilterParser::ParseStatus status)
639 ContentExtensions::CombinedURLFilters combinedURLFilters;
640 ContentExtensions::URLFilterParser parser(combinedURLFilters);
641 EXPECT_EQ(status, parser.addPattern(pattern, false, 0));
644 TEST_F(ContentExtensionTest, ParsingFailures)
646 testPatternStatus("a*b?.*.?[a-z]?[a-z]*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
647 testPatternStatus("a*b?.*.?[a-z]?[a-z]+", ContentExtensions::URLFilterParser::ParseStatus::Ok);
648 testPatternStatus("a*b?.*.?[a-z]?[a-z]", ContentExtensions::URLFilterParser::ParseStatus::Ok);
649 testPatternStatus(".*?a", ContentExtensions::URLFilterParser::ParseStatus::Ok);
650 testPatternStatus(".*a", ContentExtensions::URLFilterParser::ParseStatus::Ok);
652 testPatternStatus("(?!)", ContentExtensions::URLFilterParser::ParseStatus::Group);
653 testPatternStatus("(?=)", ContentExtensions::URLFilterParser::ParseStatus::Group);
654 testPatternStatus("(?!a)", ContentExtensions::URLFilterParser::ParseStatus::Group);
655 testPatternStatus("(?=a)", ContentExtensions::URLFilterParser::ParseStatus::Group);
656 testPatternStatus("(regex)", ContentExtensions::URLFilterParser::ParseStatus::Ok);
657 testPatternStatus("(regex", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
658 testPatternStatus("((regex)", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
659 testPatternStatus("(?:regex)", ContentExtensions::URLFilterParser::ParseStatus::Ok);
660 testPatternStatus("(?:regex", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
661 testPatternStatus("[^.]+", ContentExtensions::URLFilterParser::ParseStatus::Ok);
663 testPatternStatus("a++", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
664 testPatternStatus("[a]++", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
665 testPatternStatus("+", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
667 testPatternStatus("[", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
668 testPatternStatus("[a}", ContentExtensions::URLFilterParser::ParseStatus::YarrError);
670 // FIXME: Look into why these do not cause YARR parsing errors. They probably should.
671 testPatternStatus("a]", ContentExtensions::URLFilterParser::ParseStatus::Ok);
672 testPatternStatus("{", ContentExtensions::URLFilterParser::ParseStatus::Ok);
673 testPatternStatus("{[a]", ContentExtensions::URLFilterParser::ParseStatus::Ok);
674 testPatternStatus("{0", ContentExtensions::URLFilterParser::ParseStatus::Ok);
675 testPatternStatus("{0,", ContentExtensions::URLFilterParser::ParseStatus::Ok);
676 testPatternStatus("{0,1", ContentExtensions::URLFilterParser::ParseStatus::Ok);
677 testPatternStatus("a{0,1", ContentExtensions::URLFilterParser::ParseStatus::Ok);
678 testPatternStatus("a{a,b}", ContentExtensions::URLFilterParser::ParseStatus::Ok);
680 const char nonASCII[2] = {-1, '\0'};
681 testPatternStatus(nonASCII, ContentExtensions::URLFilterParser::ParseStatus::NonASCII);
682 testPatternStatus("\\xff", ContentExtensions::URLFilterParser::ParseStatus::NonASCII);
684 testPatternStatus("\\x\\r\\n", ContentExtensions::URLFilterParser::ParseStatus::Ok);
685 testPatternStatus("\\b", ContentExtensions::URLFilterParser::ParseStatus::WordBoundary);
686 testPatternStatus("[\\d]", ContentExtensions::URLFilterParser::ParseStatus::AtomCharacter);
687 testPatternStatus("\\d\\D\\w\\s\\v\\h\\i\\c", ContentExtensions::URLFilterParser::ParseStatus::UnsupportedCharacterClass);
689 testPatternStatus("this|that", ContentExtensions::URLFilterParser::ParseStatus::Disjunction);
690 testPatternStatus("a{0,1}b", ContentExtensions::URLFilterParser::ParseStatus::Ok);
691 testPatternStatus("a{0,2}b", ContentExtensions::URLFilterParser::ParseStatus::InvalidQuantifier);
692 testPatternStatus("", ContentExtensions::URLFilterParser::ParseStatus::EmptyPattern);
693 testPatternStatus("$$", ContentExtensions::URLFilterParser::ParseStatus::MisplacedEndOfLine);
694 testPatternStatus("a^", ContentExtensions::URLFilterParser::ParseStatus::MisplacedStartOfLine);
695 testPatternStatus("(^)", ContentExtensions::URLFilterParser::ParseStatus::MisplacedStartOfLine);
697 testPatternStatus("(a)\\1", ContentExtensions::URLFilterParser::ParseStatus::Ok); // This should be BackReference, right?
700 TEST_F(ContentExtensionTest, PatternMatchingTheEmptyString)
703 testPatternStatus(".*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
704 testPatternStatus("a*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
705 testPatternStatus(".?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
706 testPatternStatus("a?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
709 testPatternStatus("[a-z]*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
710 testPatternStatus("[a-z]?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
713 testPatternStatus("(foobar)*", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
714 testPatternStatus("(foobar)?", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
715 testPatternStatus("(.*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
716 testPatternStatus("(a*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
717 testPatternStatus("(.?)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
718 testPatternStatus("(a?)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
719 testPatternStatus("([a-z]*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
720 testPatternStatus("([a-z]?)", 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 testPatternStatus("(.?)+", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
730 testPatternStatus("((foo)?((.)*)(bar)*)", ContentExtensions::URLFilterParser::ParseStatus::MatchesEverything);
733 } // namespace TestWebKitAPI