Title: [184116] trunk
Revision
184116
Author
[email protected]
Date
2015-05-11 12:54:15 -0700 (Mon, 11 May 2015)

Log Message

[Content Extensions] Support domain-specific rules and exceptions.
https://bugs.webkit.org/show_bug.cgi?id=144833

Patch by Alex Christensen <[email protected]> on 2015-05-11
Reviewed by Darin Adler.

Source/WebCore:

Test: http/tests/contentextensions/domain-rules.html
(And lots of API tests)

This patch adds if-domain and unless-domain to the trigger in the JSON format.
if-domain makes the rule apply only to domains in the list.
unless-domain makes the rule apply to domains that are not in the list.

All rules without if-domain or unless-domain are compiled into a set of DFAs.  This behavior is unchanged.
All rules with if-domain or unless-domain are compiled into a separate set of DFAs.
The domains are also compiled into another set of DFAs.  This makes 3 arrays of DFA bytecode instead of 1.

If there are no domain specific rules, there is no change in behavior.
If there are domain specific rules, the URL will be checked for matches in both
filtersWithoutDomainsBytecode and filtersWithDomainsBytecode.  If there are matches from
filtersWithDomainsBytecode then the domain of the main document will be checked with
domainFiltersBytecode to see which of the matches applies to this domain.

* contentextensions/CombinedURLFilters.cpp:
(WebCore::ContentExtensions::CombinedURLFilters::isEmpty):
(WebCore::ContentExtensions::CombinedURLFilters::addDomain):
* contentextensions/CombinedURLFilters.h:
Added addDomain, which adds characters from a domain to the prefix tree directly without using YARR.
* contentextensions/CompiledContentExtension.cpp:
(WebCore::ContentExtensions::CompiledContentExtension::globalDisplayNoneSelectors):
* contentextensions/CompiledContentExtension.h:
* contentextensions/ContentExtensionCompiler.cpp:
(WebCore::ContentExtensions::serializeActions):
(WebCore::ContentExtensions::compileRuleList):
Separate the rules into rules with domains and rules without domains and compile to bytecode.
* contentextensions/ContentExtensionCompiler.h:
Updated compiler interface to reflect the slightly more complicated structure.
* contentextensions/ContentExtensionError.cpp:
(WebCore::ContentExtensions::contentExtensionErrorCategory):
* contentextensions/ContentExtensionError.h:
Added new parsing errors.
* contentextensions/ContentExtensionParser.cpp:
(WebCore::ContentExtensions::getDomainList):
(WebCore::ContentExtensions::loadTrigger):
(WebCore::ContentExtensions::loadEncodedRules):
* contentextensions/ContentExtensionRule.h:
(WebCore::ContentExtensions::Trigger::operator==):
Parse the new domain structures from JSON into the Trigger structure.
* contentextensions/ContentExtensionsBackend.cpp:
(WebCore::ContentExtensions::ContentExtensionsBackend::actionsForResourceLoad):
Interpret as much bytecode as necessary to determine which rules apply to this URL and domain.
* contentextensions/DFABytecode.h:
(WebCore::ContentExtensions::instructionSizeWithArguments):
* contentextensions/DFABytecodeCompiler.cpp:
(WebCore::ContentExtensions::DFABytecodeCompiler::emitAppendAction):
(WebCore::ContentExtensions::DFABytecodeCompiler::emitTestFlagsAndAppendAction):
(WebCore::ContentExtensions::DFABytecodeCompiler::compileNode):
* contentextensions/DFABytecodeCompiler.h:
* contentextensions/DFABytecodeInterpreter.cpp:
(WebCore::ContentExtensions::DFABytecodeInterpreter::interpretAppendAction):
(WebCore::ContentExtensions::DFABytecodeInterpreter::interpretTestFlagsAndAppendAction):
(WebCore::ContentExtensions::DFABytecodeInterpreter::actionsFromDFARoot):
(WebCore::ContentExtensions::DFABytecodeInterpreter::interpret):
* contentextensions/DFABytecodeInterpreter.h:
* loader/ResourceLoadInfo.h:
Keep track of which actions have an if-domain trigger.  If an action is in the
filtersWithDomainsBytecode, then it either has an if-domain or an unless-domain in its trigger.
This requires additional information in the bytecode, so there are two new bytecode types.

Source/WebKit2:

* Shared/WebCompiledContentExtension.cpp:
(WebKit::WebCompiledContentExtension::filtersWithoutDomainsBytecode):
(WebKit::WebCompiledContentExtension::filtersWithoutDomainsBytecodeLength):
(WebKit::WebCompiledContentExtension::filtersWithDomainsBytecode):
(WebKit::WebCompiledContentExtension::filtersWithDomainsBytecodeLength):
(WebKit::WebCompiledContentExtension::domainFiltersBytecode):
(WebKit::WebCompiledContentExtension::domainFiltersBytecodeLength):
(WebKit::WebCompiledContentExtension::bytecode): Deleted.
(WebKit::WebCompiledContentExtension::bytecodeLength): Deleted.
* Shared/WebCompiledContentExtension.h:
* Shared/WebCompiledContentExtensionData.cpp:
(WebKit::WebCompiledContentExtensionData::encode):
(WebKit::WebCompiledContentExtensionData::decode):
* Shared/WebCompiledContentExtensionData.h:
(WebKit::WebCompiledContentExtensionData::WebCompiledContentExtensionData):
* UIProcess/API/APIUserContentExtensionStore.cpp:
(API::ContentExtensionMetaData::fileSize):
(API::encodeContentExtensionMetaData):
(API::decodeContentExtensionMetaData):
(API::compiledToFile):
(API::createExtension):
Keep track of 3 different types of bytecode to be able to handle domain-specific rules.

Tools:

* TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
(TestWebKitAPI::InMemoryCompiledContentExtension::createFromFilter):
(TestWebKitAPI::InMemoryCompiledContentExtension::create):
(TestWebKitAPI::InMemoryCompiledContentExtension::InMemoryCompiledContentExtension):
Moved CompiledContentExtensionData from ContentExtensionCompiler.h because it is only used for testing.
(TestWebKitAPI::mainDocumentRequest):
(TestWebKitAPI::subResourceRequest):
(TestWebKitAPI::TEST_F):
(TestWebKitAPI::checkCompilerError):
Added tests for parsing and functionality of if-domain and unless-domain.

LayoutTests:

* http/tests/contentextensions/domain-rules-expected.txt: Added.
* http/tests/contentextensions/domain-rules.html: Added.
* http/tests/contentextensions/domain-rules.html.json: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (184115 => 184116)


--- trunk/LayoutTests/ChangeLog	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/LayoutTests/ChangeLog	2015-05-11 19:54:15 UTC (rev 184116)
@@ -1,3 +1,14 @@
+2015-05-11  Alex Christensen  <[email protected]>
+
+        [Content Extensions] Support domain-specific rules and exceptions.
+        https://bugs.webkit.org/show_bug.cgi?id=144833
+
+        Reviewed by Darin Adler.
+
+        * http/tests/contentextensions/domain-rules-expected.txt: Added.
+        * http/tests/contentextensions/domain-rules.html: Added.
+        * http/tests/contentextensions/domain-rules.html.json: Added.
+
 2015-05-10  Filip Pizlo  <[email protected]>
 
         We shouldn't promote LoadVarargs to a sequence of GetStacks and PutStacks if doing so would exceed the LoadVarargs' limit

Added: trunk/LayoutTests/http/tests/contentextensions/domain-rules-expected.txt (0 => 184116)


--- trunk/LayoutTests/http/tests/contentextensions/domain-rules-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/domain-rules-expected.txt	2015-05-11 19:54:15 UTC (rev 184116)
@@ -0,0 +1,21 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderBlock {HTML} at (0,0) size 800x600
+    RenderBody {BODY} at (8,8) size 784x584
+      RenderText {#text} at (0,0) size 200x18
+        text run at (0,0) width 200: "The images below should load."
+      RenderBR {BR} at (199,14) size 1x0
+      RenderImage {IMG} at (0,18) size 100x100
+      RenderBR {BR} at (100,118) size 0x0
+      RenderImage {IMG} at (0,118) size 100x100
+      RenderBR {BR} at (100,218) size 0x0
+      RenderText {#text} at (0,218) size 242x18
+        text run at (0,218) width 242: "The images below should be blocked."
+      RenderBR {BR} at (241,232) size 1x0
+      RenderImage {IMG} at (0,236) size 0x0
+      RenderBR {BR} at (0,236) size 0x0
+      RenderImage {IMG} at (0,236) size 0x0
+      RenderBR {BR} at (0,236) size 0x0
+      RenderImage {IMG} at (0,236) size 0x0
+      RenderBR {BR} at (0,236) size 0x0

Added: trunk/LayoutTests/http/tests/contentextensions/domain-rules.html (0 => 184116)


--- trunk/LayoutTests/http/tests/contentextensions/domain-rules.html	                        (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/domain-rules.html	2015-05-11 19:54:15 UTC (rev 184116)
@@ -0,0 +1,9 @@
+<body>
+The images below should load.<br>
+<img src=""
+<img src=""
+The images below should be blocked.<br>
+<img src=""
+<img src=""
+<img src=""
+</body>

Added: trunk/LayoutTests/http/tests/contentextensions/domain-rules.html.json (0 => 184116)


--- trunk/LayoutTests/http/tests/contentextensions/domain-rules.html.json	                        (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/domain-rules.html.json	2015-05-11 19:54:15 UTC (rev 184116)
@@ -0,0 +1,46 @@
+[
+    {
+        "action": {
+            "type": "block"
+        },
+        "trigger": {
+            "url-filter": "should_be_blocked_1"
+        }
+    },
+    {
+        "action": {
+            "type": "block"
+        },
+        "trigger": {
+            "url-filter": "should_be_blocked_2",
+            "if-domain": ["127.0.0.1"]
+        }
+    },
+    {
+        "action": {
+            "type": "block"
+        },
+        "trigger": {
+            "url-filter": "should_be_blocked_3",
+            "unless-domain": ["this domain should not match"]
+        }
+    },
+    {
+        "action": {
+            "type": "block"
+        },
+        "trigger": {
+            "url-filter": "should_not_be_blocked_1",
+            "if-domain": ["this domain should not match either"]
+        }
+    },
+    {
+        "action": {
+            "type": "block"
+        },
+        "trigger": {
+            "url-filter": "should_not_be_blocked_2",
+            "unless-domain": ["127.0.0.1"]
+        }
+    }
+]

Modified: trunk/Source/WebCore/ChangeLog (184115 => 184116)


--- trunk/Source/WebCore/ChangeLog	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/ChangeLog	2015-05-11 19:54:15 UTC (rev 184116)
@@ -1,3 +1,73 @@
+2015-05-11  Alex Christensen  <[email protected]>
+
+        [Content Extensions] Support domain-specific rules and exceptions.
+        https://bugs.webkit.org/show_bug.cgi?id=144833
+
+        Reviewed by Darin Adler.
+
+        Test: http/tests/contentextensions/domain-rules.html
+        (And lots of API tests)
+
+        This patch adds if-domain and unless-domain to the trigger in the JSON format.
+        if-domain makes the rule apply only to domains in the list.
+        unless-domain makes the rule apply to domains that are not in the list.
+        
+        All rules without if-domain or unless-domain are compiled into a set of DFAs.  This behavior is unchanged.
+        All rules with if-domain or unless-domain are compiled into a separate set of DFAs.
+        The domains are also compiled into another set of DFAs.  This makes 3 arrays of DFA bytecode instead of 1.
+        
+        If there are no domain specific rules, there is no change in behavior.
+        If there are domain specific rules, the URL will be checked for matches in both
+        filtersWithoutDomainsBytecode and filtersWithDomainsBytecode.  If there are matches from 
+        filtersWithDomainsBytecode then the domain of the main document will be checked with 
+        domainFiltersBytecode to see which of the matches applies to this domain.
+
+        * contentextensions/CombinedURLFilters.cpp:
+        (WebCore::ContentExtensions::CombinedURLFilters::isEmpty):
+        (WebCore::ContentExtensions::CombinedURLFilters::addDomain):
+        * contentextensions/CombinedURLFilters.h:
+        Added addDomain, which adds characters from a domain to the prefix tree directly without using YARR.
+        * contentextensions/CompiledContentExtension.cpp:
+        (WebCore::ContentExtensions::CompiledContentExtension::globalDisplayNoneSelectors):
+        * contentextensions/CompiledContentExtension.h:
+        * contentextensions/ContentExtensionCompiler.cpp:
+        (WebCore::ContentExtensions::serializeActions):
+        (WebCore::ContentExtensions::compileRuleList):
+        Separate the rules into rules with domains and rules without domains and compile to bytecode.
+        * contentextensions/ContentExtensionCompiler.h:
+        Updated compiler interface to reflect the slightly more complicated structure.
+        * contentextensions/ContentExtensionError.cpp:
+        (WebCore::ContentExtensions::contentExtensionErrorCategory):
+        * contentextensions/ContentExtensionError.h:
+        Added new parsing errors.
+        * contentextensions/ContentExtensionParser.cpp:
+        (WebCore::ContentExtensions::getDomainList):
+        (WebCore::ContentExtensions::loadTrigger):
+        (WebCore::ContentExtensions::loadEncodedRules):
+        * contentextensions/ContentExtensionRule.h:
+        (WebCore::ContentExtensions::Trigger::operator==):
+        Parse the new domain structures from JSON into the Trigger structure.
+        * contentextensions/ContentExtensionsBackend.cpp:
+        (WebCore::ContentExtensions::ContentExtensionsBackend::actionsForResourceLoad):
+        Interpret as much bytecode as necessary to determine which rules apply to this URL and domain.
+        * contentextensions/DFABytecode.h:
+        (WebCore::ContentExtensions::instructionSizeWithArguments):
+        * contentextensions/DFABytecodeCompiler.cpp:
+        (WebCore::ContentExtensions::DFABytecodeCompiler::emitAppendAction):
+        (WebCore::ContentExtensions::DFABytecodeCompiler::emitTestFlagsAndAppendAction):
+        (WebCore::ContentExtensions::DFABytecodeCompiler::compileNode):
+        * contentextensions/DFABytecodeCompiler.h:
+        * contentextensions/DFABytecodeInterpreter.cpp:
+        (WebCore::ContentExtensions::DFABytecodeInterpreter::interpretAppendAction):
+        (WebCore::ContentExtensions::DFABytecodeInterpreter::interpretTestFlagsAndAppendAction):
+        (WebCore::ContentExtensions::DFABytecodeInterpreter::actionsFromDFARoot):
+        (WebCore::ContentExtensions::DFABytecodeInterpreter::interpret):
+        * contentextensions/DFABytecodeInterpreter.h:
+        * loader/ResourceLoadInfo.h:
+        Keep track of which actions have an if-domain trigger.  If an action is in the 
+        filtersWithDomainsBytecode, then it either has an if-domain or an unless-domain in its trigger.
+        This requires additional information in the bytecode, so there are two new bytecode types.
+
 2015-05-11  Yoav Weiss  <[email protected]>
 
         Add srcset, sizes and picture to the features json

Modified: trunk/Source/WebCore/contentextensions/CombinedURLFilters.cpp (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/CombinedURLFilters.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/CombinedURLFilters.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -115,11 +115,40 @@
 {
 }
 
-bool CombinedURLFilters::isEmpty()
+bool CombinedURLFilters::isEmpty() const
 {
     return m_prefixTreeRoot->edges.isEmpty();
 }
 
+void CombinedURLFilters::addDomain(uint64_t actionId, const String& domain)
+{
+    // This is like adding (.|^)domain$ by adding two Vector<Term>'s,
+    // but interpreting domain as a series of characters, not a regular _expression_.
+    // This way a domain of "webkit.org" will match "bugs.webkit.org" and "webkit.org".
+    // FIXME: Add support for matching only subdomains or no subdomains.
+    Vector<Term> prependDot;
+    Vector<Term> prependBeginningOfLine;
+    prependDot.reserveInitialCapacity(domain.length() + 3);
+    prependBeginningOfLine.reserveInitialCapacity(domain.length() + 1); // This is just no .* at the beginning.
+    
+    Term canonicalDotStar(Term::UniversalTransition);
+    canonicalDotStar.quantify(AtomQuantifier::ZeroOrMore);
+    prependDot.uncheckedAppend(canonicalDotStar);
+    prependDot.uncheckedAppend(Term('.', true));
+    
+    for (unsigned i = 0; i < domain.length(); i++) {
+        ASSERT(isASCII(domain[i]));
+        ASSERT(!isASCIIUpper(domain[i]));
+        prependDot.uncheckedAppend(Term(domain[i], true));
+        prependBeginningOfLine.uncheckedAppend(Term(domain[i], true));
+    }
+    prependDot.uncheckedAppend(Term::EndOfLineAssertionTerm);
+    prependBeginningOfLine.uncheckedAppend(Term::EndOfLineAssertionTerm);
+    
+    addPattern(actionId, prependDot);
+    addPattern(actionId, prependBeginningOfLine);
+}
+
 void CombinedURLFilters::addPattern(uint64_t actionId, const Vector<Term>& pattern)
 {
     ASSERT_WITH_MESSAGE(!pattern.isEmpty(), "The parser should have excluded empty patterns before reaching CombinedURLFilters.");

Modified: trunk/Source/WebCore/contentextensions/CombinedURLFilters.h (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/CombinedURLFilters.h	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/CombinedURLFilters.h	2015-05-11 19:54:15 UTC (rev 184116)
@@ -30,6 +30,7 @@
 
 #include "ContentExtensionsDebugging.h"
 #include "NFA.h"
+#include <wtf/Forward.h>
 #include <wtf/Vector.h>
 
 namespace WebCore {
@@ -44,10 +45,11 @@
     CombinedURLFilters();
     ~CombinedURLFilters();
 
-    void addPattern(uint64_t patternId, const Vector<Term>& pattern);
+    void addPattern(uint64_t actionId, const Vector<Term>& pattern);
+    void addDomain(uint64_t actionId, const String& domain);
 
     void processNFAs(size_t maxNFASize, std::function<void(NFA&&)> handler);
-    bool isEmpty();
+    bool isEmpty() const;
 
 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
     size_t memoryUsed() const;

Modified: trunk/Source/WebCore/contentextensions/CompiledContentExtension.cpp (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/CompiledContentExtension.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/CompiledContentExtension.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -38,7 +38,7 @@
 
 Vector<String> CompiledContentExtension::globalDisplayNoneSelectors(Vector<bool>& pagesUsed)
 {
-    DFABytecodeInterpreter interpreter(bytecode(), bytecodeLength(), pagesUsed);
+    DFABytecodeInterpreter interpreter(filtersWithoutDomainsBytecode(), filtersWithoutDomainsBytecodeLength(), pagesUsed);
     DFABytecodeInterpreter::Actions actionLocations = interpreter.actionsFromDFARoot();
     
     Vector<Action> globalActions;

Modified: trunk/Source/WebCore/contentextensions/CompiledContentExtension.h (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/CompiledContentExtension.h	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/CompiledContentExtension.h	2015-05-11 19:54:15 UTC (rev 184116)
@@ -40,8 +40,12 @@
 public:
     virtual ~CompiledContentExtension();
 
-    virtual const DFABytecode* bytecode() const = 0;
-    virtual unsigned bytecodeLength() const = 0;
+    virtual const DFABytecode* filtersWithoutDomainsBytecode() const = 0;
+    virtual unsigned filtersWithoutDomainsBytecodeLength() const = 0;
+    virtual const DFABytecode* filtersWithDomainsBytecode() const = 0;
+    virtual unsigned filtersWithDomainsBytecodeLength() const = 0;
+    virtual const DFABytecode* domainFiltersBytecode() const = 0;
+    virtual unsigned domainFiltersBytecodeLength() const = 0;
     virtual const SerializedActionByte* actions() const = 0;
     virtual unsigned actionsLength() const = 0;
     Vector<String> globalDisplayNoneSelectors(Vector<bool>& pagesUsed);

Modified: trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -96,8 +96,10 @@
             continue;
         }
         
-        // Identical sequential actions should not be rewritten.
-        if (ruleIndex && rule.action() == ruleList[ruleIndex - 1].action()) {
+        // Identical sequential actions should not be rewritten unless there are domains in the trigger.
+        // If there are domains in the trigger, we need to distinguish the actions by index to tell if we need to apply it
+        // by comparing the output of the filters with domains and the domain filters.
+        if (ruleIndex && rule.action() == ruleList[ruleIndex - 1].action() && rule.trigger().domains.isEmpty()) {
             actionLocations.append(actionLocations[ruleIndex - 1]);
             continue;
         }
@@ -156,8 +158,13 @@
 
     UniversalActionLocationsSet universalActionLocations;
 
-    CombinedURLFilters combinedURLFilters;
-    URLFilterParser urlFilterParser(combinedURLFilters);
+    // FIXME: These don't all need to be in memory at the same time.
+    CombinedURLFilters filtersWithoutDomains;
+    CombinedURLFilters filtersWithDomains;
+    CombinedURLFilters domainFilters;
+    URLFilterParser filtersWithoutDomainParser(filtersWithoutDomains);
+    URLFilterParser filtersWithDomainParser(filtersWithDomains);
+    
     bool ignorePreviousRulesSeen = false;
     for (unsigned ruleIndex = 0; ruleIndex < parsedRuleList.size(); ++ruleIndex) {
         const ContentExtensionRule& contentExtensionRule = parsedRuleList[ruleIndex];
@@ -166,18 +173,39 @@
 
         // High bits are used for flags. This should match how they are used in DFABytecodeCompiler::compileNode.
         uint64_t actionLocationAndFlags = (static_cast<uint64_t>(trigger.flags) << 32) | static_cast<uint64_t>(actionLocations[ruleIndex]);
-        URLFilterParser::ParseStatus status = urlFilterParser.addPattern(trigger.urlFilter, trigger.urlFilterIsCaseSensitive, actionLocationAndFlags);
-
-        if (status == URLFilterParser::MatchesEverything) {
-            if (ignorePreviousRulesSeen)
-                return ContentExtensionError::RegexMatchesEverythingAfterIgnorePreviousRules;
-            universalActionLocations.add(actionLocationAndFlags);
+        URLFilterParser::ParseStatus status = URLFilterParser::Ok;
+        if (trigger.domains.isEmpty()) {
+            ASSERT(trigger.domainCondition == Trigger::DomainCondition::None);
+            status = filtersWithoutDomainParser.addPattern(trigger.urlFilter, trigger.urlFilterIsCaseSensitive, actionLocationAndFlags);
+            if (status == URLFilterParser::MatchesEverything) {
+                if (ignorePreviousRulesSeen)
+                    return ContentExtensionError::RegexMatchesEverythingAfterIgnorePreviousRules;
+                universalActionLocations.add(actionLocationAndFlags);
+                status = URLFilterParser::Ok;
+            }
+            if (status != URLFilterParser::Ok) {
+                dataLogF("Error while parsing %s: %s\n", trigger.urlFilter.utf8().data(), URLFilterParser::statusString(status).utf8().data());
+                return ContentExtensionError::JSONInvalidRegex;
+            }
+        } else {
+            if (trigger.domainCondition == Trigger::DomainCondition::IfDomain)
+                actionLocationAndFlags |= IfDomainFlag;
+            else {
+                ASSERT(trigger.domainCondition == Trigger::DomainCondition::UnlessDomain);
+                ASSERT(!(actionLocationAndFlags & IfDomainFlag));
+            }
+            
+            status = filtersWithDomainParser.addPattern(trigger.urlFilter, trigger.urlFilterIsCaseSensitive, actionLocationAndFlags);
+            if (status == URLFilterParser::MatchesEverything)
+                return ContentExtensionError::RegexMatchesEverythingWithDomains;
+            if (status != URLFilterParser::Ok) {
+                dataLogF("Error while parsing %s: %s\n", trigger.urlFilter.utf8().data(), URLFilterParser::statusString(status).utf8().data());
+                return ContentExtensionError::JSONInvalidRegex;
+            }
+            for (const String& domain : trigger.domains)
+                domainFilters.addDomain(actionLocationAndFlags, domain);
         }
-
-        if (status != URLFilterParser::Ok && status != URLFilterParser::MatchesEverything) {
-            dataLogF("Error while parsing %s: %s\n", trigger.urlFilter.utf8().data(), URLFilterParser::statusString(status).utf8().data());
-            return ContentExtensionError::JSONInvalidRegex;
-        }
+        ASSERT(status == URLFilterParser::Ok);
         
         if (contentExtensionRule.action().type() == ActionType::IgnorePreviousRules)
             ignorePreviousRulesSeen = true;
@@ -192,7 +220,9 @@
     dataLogF("    Time spent partitioning the rules into groups: %f\n", (patternPartitioningEnd - patternPartitioningStart));
 #endif
 
-    LOG_LARGE_STRUCTURES(combinedURLFilters, combinedURLFilters.memoryUsed());
+    LOG_LARGE_STRUCTURES(filtersWithoutDomains, filtersWithoutDomains.memoryUsed());
+    LOG_LARGE_STRUCTURES(filtersWithDomains, filtersWithDomains.memoryUsed());
+    LOG_LARGE_STRUCTURES(domainFilters, domainFilters.memoryUsed());
 
 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
     double totalNFAToByteCodeBuildTimeStart = monotonicallyIncreasingTime();
@@ -203,8 +233,9 @@
     
     bool firstNFASeen = false;
     // FIXME: Combine small NFAs to reduce the number of NFAs.
-    combinedURLFilters.processNFAs(maxNFASize, [&](NFA&& nfa) {
+    filtersWithoutDomains.processNFAs(maxNFASize, [&](NFA&& nfa) {
 #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+        dataLogF("filtersWithoutDomains NFA\n");
         nfa.debugPrintDot();
 #endif
 
@@ -216,7 +247,7 @@
         dfa.minimize();
 
 #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
-        WTFLogAlways("DFA");
+        dataLogF("filtersWithoutDomains DFA\n");
         dfa.debugPrintDot();
 #endif
         ASSERT_WITH_MESSAGE(!dfa.nodes[dfa.root].hasActions(), "All actions on the DFA root should come from regular expressions that match everything.");
@@ -230,11 +261,11 @@
         DFABytecodeCompiler compiler(dfa, bytecode);
         compiler.compile();
         LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
-        client.writeBytecode(WTF::move(bytecode));
+        client.writeFiltersWithoutDomainsBytecode(WTF::move(bytecode));
 
         firstNFASeen = true;
     });
-    ASSERT(combinedURLFilters.isEmpty());
+    ASSERT(filtersWithoutDomains.isEmpty());
 
     if (!firstNFASeen) {
         // Our bytecode interpreter expects to have at least one DFA, so if we haven't seen any
@@ -249,12 +280,62 @@
         DFABytecodeCompiler compiler(dummyDFA, bytecode);
         compiler.compile();
         LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
-        client.writeBytecode(WTF::move(bytecode));
+        client.writeFiltersWithoutDomainsBytecode(WTF::move(bytecode));
     }
-
     LOG_LARGE_STRUCTURES(universalActionLocations, universalActionLocations.capacity() * sizeof(unsigned));
     universalActionLocations.clear();
+    
+    filtersWithDomains.processNFAs(maxNFASize, [&](NFA&& nfa) {
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+        dataLogF("filtersWithDomains NFA\n");
+        nfa.debugPrintDot();
+#endif
+        LOG_LARGE_STRUCTURES(nfa, nfa.memoryUsed());
+        DFA dfa = NFAToDFA::convert(nfa);
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+        dataLogF("filtersWithDomains PRE MINIMIZING DFA\n");
+        dfa.debugPrintDot();
+#endif
+        LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
+        // Minimizing this DFA would not be effective because all actions with domains are unique.
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+        dataLogF("filtersWithDomains POST MINIMIZING DFA\n");
+        dfa.debugPrintDot();
+#endif
+        ASSERT_WITH_MESSAGE(!dfa.nodes[dfa.root].hasActions(), "Filters with domains that match everything are not allowed right now.");
+        
+        Vector<DFABytecode> bytecode;
+        DFABytecodeCompiler compiler(dfa, bytecode);
+        compiler.compile();
+        LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
+        client.writeFiltersWithDomainsBytecode(WTF::move(bytecode));
+    });
+    ASSERT(filtersWithDomains.isEmpty());
 
+    domainFilters.processNFAs(maxNFASize, [&](NFA&& nfa) {
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+        dataLogF("domainFilters NFA\n");
+        nfa.debugPrintDot();
+#endif
+        LOG_LARGE_STRUCTURES(nfa, nfa.memoryUsed());
+        DFA dfa = NFAToDFA::convert(nfa);
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+        dataLogF("domainFilters DFA\n");
+        dfa.debugPrintDot();
+#endif
+        LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
+        // Minimizing this DFA would not be effective because all actions are unique
+        // and because of the tree-like structure of this DFA.
+        ASSERT_WITH_MESSAGE(!dfa.nodes[dfa.root].hasActions(), "There should not be any domains that match everything.");
+        
+        Vector<DFABytecode> bytecode;
+        DFABytecodeCompiler compiler(dfa, bytecode);
+        compiler.compile();
+        LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
+        client.writeDomainFiltersBytecode(WTF::move(bytecode));
+    });
+    ASSERT(domainFilters.isEmpty());    
+    
 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
     double totalNFAToByteCodeBuildTimeEnd = monotonicallyIncreasingTime();
     dataLogF("    Time spent building and compiling the DFAs: %f\n", (totalNFAToByteCodeBuildTimeEnd - totalNFAToByteCodeBuildTimeStart));

Modified: trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h	2015-05-11 19:54:15 UTC (rev 184116)
@@ -36,17 +36,15 @@
 namespace WebCore {
 namespace ContentExtensions {
 
-struct CompiledContentExtensionData {
-    Vector<DFABytecode> bytecode;
-    Vector<SerializedActionByte> actions;
-};
-
 class ContentExtensionCompilationClient {
 public:
     virtual ~ContentExtensionCompilationClient() { }
     
-    virtual void writeBytecode(Vector<DFABytecode>&&) = 0;
+    // Functions should be called in this order. All except writeActions and finalize can be called multiple times, though.
     virtual void writeActions(Vector<SerializedActionByte>&&) = 0;
+    virtual void writeFiltersWithoutDomainsBytecode(Vector<DFABytecode>&&) = 0;
+    virtual void writeFiltersWithDomainsBytecode(Vector<DFABytecode>&&) = 0;
+    virtual void writeDomainFiltersBytecode(Vector<DFABytecode>&&) = 0;
     virtual void finalize() = 0;
 };
 

Modified: trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -77,6 +77,14 @@
                 return "Invalid or unsupported regular _expression_.";
             case ContentExtensionError::RegexMatchesEverythingAfterIgnorePreviousRules:
                 return "Regular expressions that match everything are only allowed before the first ignore-previous-rules.";
+            case ContentExtensionError::RegexMatchesEverythingWithDomains:
+                return "Regular expressions that match everything are not allowed with unless- or if-domain.";
+            case ContentExtensionError::JSONInvalidDomainList:
+                return "Invalid domain list.";
+            case ContentExtensionError::JSONDomainNotLowerCaseASCII:
+                return "Domains must be lower case ASCII.  Use punycode to encode non-ASCII characters.";
+            case ContentExtensionError::JSONUnlessAndIfDomain:
+                return "A trigger cannot have both unless- and if-domain.";
             }
 
             return std::string();

Modified: trunk/Source/WebCore/contentextensions/ContentExtensionError.h (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/ContentExtensionError.h	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionError.h	2015-05-11 19:54:15 UTC (rev 184116)
@@ -49,6 +49,9 @@
     JSONInvalidTriggerFlagsArray,
     JSONInvalidObjectInTriggerFlagsArray,
     JSONInvalidStringInTriggerFlagsArray,
+    JSONInvalidDomainList,
+    JSONDomainNotLowerCaseASCII,
+    JSONUnlessAndIfDomain,
     
     JSONInvalidAction,
     JSONInvalidActionType,
@@ -56,9 +59,10 @@
     JSONInvalidRegex,
     
     RegexMatchesEverythingAfterIgnorePreviousRules,
+    RegexMatchesEverythingWithDomains,
 };
 
-const std::error_category& contentExtensionErrorCategory();
+WEBCORE_EXPORT const std::error_category& contentExtensionErrorCategory();
 
 inline std::error_code make_error_code(ContentExtensionError error)
 {

Modified: trunk/Source/WebCore/contentextensions/ContentExtensionParser.cpp (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/ContentExtensionParser.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionParser.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -46,6 +46,41 @@
 namespace WebCore {
 
 namespace ContentExtensions {
+    
+static bool containsOnlyASCIIWithNoUppercase(const String& domain)
+{
+    for (unsigned i = 0; i < domain.length(); ++i) {
+        UChar c = domain.at(i);
+        if (!isASCII(c) || isASCIIUpper(c))
+            return false;
+    }
+    return true;
+}
+    
+static std::error_code getDomainList(ExecState& exec, JSObject* arrayObject, Vector<String>& vector)
+{
+    ASSERT(vector.isEmpty());
+    if (!arrayObject || !isJSArray(arrayObject))
+        return ContentExtensionError::JSONInvalidDomainList;
+    JSArray* array = jsCast<JSArray*>(arrayObject);
+    
+    unsigned length = array->length();
+    for (unsigned i = 0; i < length; ++i) {
+        // FIXME: JSObject::getIndex should be marked as const.
+        JSValue value = array->getIndex(&exec, i);
+        if (exec.hadException() || !value.isString())
+            return ContentExtensionError::JSONInvalidDomainList;
+        
+        // Domains should be punycode encoded lower case.
+        const String& domain = jsCast<JSString*>(value)->value(&exec);
+        if (domain.isEmpty())
+            return ContentExtensionError::JSONInvalidDomainList;
+        if (!containsOnlyASCIIWithNoUppercase(domain))
+            return ContentExtensionError::JSONDomainNotLowerCaseASCII;
+        vector.append(domain);
+    }
+    return { };
+}
 
 static std::error_code getTypeFlags(ExecState& exec, const JSValue& typeValue, ResourceFlags& flags, uint16_t (*stringToType)(const String&))
 {
@@ -109,6 +144,29 @@
             return typeFlagsError;
     }
 
+    JSValue ifDomain = triggerObject.get(&exec, Identifier::fromString(&exec, "if-domain"));
+    if (!exec.hadException() && ifDomain.isObject()) {
+        auto ifDomainError = getDomainList(exec, asObject(ifDomain), trigger.domains);
+        if (ifDomainError)
+            return ifDomainError;
+        if (trigger.domains.isEmpty())
+            return ContentExtensionError::JSONInvalidDomainList;
+        ASSERT(trigger.domainCondition == Trigger::DomainCondition::None);
+        trigger.domainCondition = Trigger::DomainCondition::IfDomain;
+    }
+    
+    JSValue unlessDomain = triggerObject.get(&exec, Identifier::fromString(&exec, "unless-domain"));
+    if (!exec.hadException() && unlessDomain.isObject()) {
+        if (trigger.domainCondition != Trigger::DomainCondition::None)
+            return ContentExtensionError::JSONUnlessAndIfDomain;
+        auto unlessDomainError = getDomainList(exec, asObject(unlessDomain), trigger.domains);
+        if (unlessDomainError)
+            return unlessDomainError;
+        if (trigger.domains.isEmpty())
+            return ContentExtensionError::JSONInvalidDomainList;
+        trigger.domainCondition = Trigger::DomainCondition::UnlessDomain;
+    }
+
     return { };
 }
 
@@ -160,6 +218,7 @@
 
 static std::error_code loadEncodedRules(ExecState& exec, const String& rules, Vector<ContentExtensionRule>& ruleList)
 {
+    // FIXME: JSONParse should require callbacks instead of an ExecState.
     JSValue decodedRules = JSONParse(&exec, rules);
 
     if (exec.hadException() || !decodedRules)

Modified: trunk/Source/WebCore/contentextensions/ContentExtensionRule.h (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/ContentExtensionRule.h	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionRule.h	2015-05-11 19:54:15 UTC (rev 184116)
@@ -45,11 +45,25 @@
     String urlFilter;
     bool urlFilterIsCaseSensitive { false };
     ResourceFlags flags { 0 };
+    Vector<String> domains;
+    enum class DomainCondition {
+        None,
+        IfDomain,
+        UnlessDomain,
+    } domainCondition { DomainCondition::None };
+    
+    ~Trigger()
+    {
+        ASSERT(domains.isEmpty() == (domainCondition == DomainCondition::None));
+    }
+    
     bool operator==(const Trigger& other) const
     {
         return urlFilter == other.urlFilter
             && urlFilterIsCaseSensitive == other.urlFilterIsCaseSensitive
-            && flags == other.flags;
+            && flags == other.flags
+            && domains == other.domains
+            && domainCondition == other.domainCondition;
     }
 };
     

Modified: trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -88,9 +88,41 @@
     for (auto& contentExtension : m_contentExtensions.values()) {
         RELEASE_ASSERT(contentExtension);
         const CompiledContentExtension& compiledExtension = contentExtension->compiledExtension();
-        DFABytecodeInterpreter interpreter(compiledExtension.bytecode(), compiledExtension.bytecodeLength(), contentExtension->m_pagesUsed);
-        DFABytecodeInterpreter::Actions triggeredActions = interpreter.interpret(urlCString, flags);
         
+        // FIXME: These should use a different Vector<bool> to keep track of which memory pages are used when doing memory reporting. Or just remove the memory reporting completely.
+        DFABytecodeInterpreter withoutDomainsInterpreter(compiledExtension.filtersWithoutDomainsBytecode(), compiledExtension.filtersWithoutDomainsBytecodeLength(), contentExtension->m_pagesUsed);
+        DFABytecodeInterpreter::Actions triggeredActions = withoutDomainsInterpreter.interpret(urlCString, flags);
+        
+        // Check to see if there are any actions triggered with if- or unless-domain and check the domain if there are.
+        DFABytecodeInterpreter withDomainsInterpreter(compiledExtension.filtersWithDomainsBytecode(), compiledExtension.filtersWithDomainsBytecodeLength(), contentExtension->m_pagesUsed);
+        
+        DFABytecodeInterpreter::Actions withDomainsPossibleActions = withDomainsInterpreter.interpret(urlCString, flags);
+        if (!withDomainsPossibleActions.isEmpty()) {
+            DFABytecodeInterpreter domainsInterpreter(compiledExtension.domainFiltersBytecode(), compiledExtension.domainFiltersBytecodeLength(), contentExtension->m_pagesUsed);
+            DFABytecodeInterpreter::Actions domainsActions = domainsInterpreter.interpret(resourceLoadInfo.mainDocumentURL.host().utf8(), flags);
+            
+            DFABytecodeInterpreter::Actions ifDomainActions;
+            DFABytecodeInterpreter::Actions unlessDomainActions;
+            for (uint64_t action : domainsActions) {
+                if (action & IfDomainFlag)
+                    ifDomainActions.add(action);
+                else
+                    unlessDomainActions.add(action);
+            }
+            
+            for (uint64_t action : withDomainsPossibleActions) {
+                if (ifDomainActions.contains(action)) {
+                    // If an if-domain trigger matches, add the action.
+                    ASSERT(action & IfDomainFlag);
+                    triggeredActions.add(action & ~IfDomainFlag);
+                } else if (!(action & IfDomainFlag) && !unlessDomainActions.contains(action)) {
+                    // If this action did not need an if-domain, it must have been an unless-domain rule.
+                    // Add the action unless it matched an unless-domain trigger.
+                    triggeredActions.add(action);
+                }
+            }
+        }
+        
         const SerializedActionByte* actions = compiledExtension.actions();
         const unsigned actionsLength = compiledExtension.actionsLength();
         
@@ -114,8 +146,9 @@
             }
         }
         if (!sawIgnorePreviousRules) {
-            DFABytecodeInterpreter::Actions universalActions = interpreter.actionsFromDFARoot();
-            for (auto actionLocation : universalActions) {
+            DFABytecodeInterpreter::Actions universalActions = withoutDomainsInterpreter.actionsFromDFARoot();
+            for (uint64_t actionLocation : universalActions) {
+                // FIXME: We shouldn't deserialize an action all the way if it is a css-display-none selector.
                 Action action = "" actionsLength, static_cast<unsigned>(actionLocation));
                 action.setExtensionIdentifier(contentExtension->identifier());
 

Modified: trunk/Source/WebCore/contentextensions/DFABytecode.h (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/DFABytecode.h	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/DFABytecode.h	2015-05-11 19:54:15 UTC (rev 184116)
@@ -52,11 +52,13 @@
     // AppendAction has one argument:
     // The action to append (4 bytes).
     AppendAction,
+    AppendActionWithIfDomain,
     
     // TestFlagsAndAppendAction has two arguments:
-    // The flags to check before appending (2 bytes),
-    // The action to append (4bytes).
+    // The flags to check before appending (2 bytes).
+    // The action to append (4 bytes).
     TestFlagsAndAppendAction,
+    TestFlagsAndAppendActionWithIfDomain,
 
     // Terminate has no arguments.
     Terminate,
@@ -76,8 +78,10 @@
     case DFABytecodeInstruction::CheckValueRangeCaseInsensitive:
         return sizeof(DFABytecodeInstruction) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(unsigned);
     case DFABytecodeInstruction::AppendAction:
+    case DFABytecodeInstruction::AppendActionWithIfDomain:
         return sizeof(DFABytecodeInstruction) + sizeof(unsigned);
     case DFABytecodeInstruction::TestFlagsAndAppendAction:
+    case DFABytecodeInstruction::TestFlagsAndAppendActionWithIfDomain:
         return sizeof(DFABytecodeInstruction) + sizeof(uint16_t) + sizeof(unsigned);
     case DFABytecodeInstruction::Terminate:
         return sizeof(DFABytecodeInstruction);

Modified: trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -48,16 +48,22 @@
     *reinterpret_cast<unsigned*>(&bytecode[index]) = value;
 }
 
-void DFABytecodeCompiler::emitAppendAction(unsigned action)
+void DFABytecodeCompiler::emitAppendAction(unsigned action, bool ifDomain)
 {
-    append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::AppendAction);
+    if (ifDomain)
+        append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::AppendActionWithIfDomain);
+    else
+        append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::AppendAction);
     append<unsigned>(m_bytecode, action);
 }
 
-void DFABytecodeCompiler::emitTestFlagsAndAppendAction(uint16_t flags, unsigned action)
+void DFABytecodeCompiler::emitTestFlagsAndAppendAction(uint16_t flags, unsigned action, bool ifDomain)
 {
     ASSERT(flags);
-    append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::TestFlagsAndAppendAction);
+    if (ifDomain)
+        append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::TestFlagsAndAppendActionWithIfDomain);
+    else
+        append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::TestFlagsAndAppendAction);
     append<uint16_t>(m_bytecode, flags);
     append<unsigned>(m_bytecode, action);
 }
@@ -106,11 +112,11 @@
         m_nodeStartOffsets[index] = m_bytecode.size();
 
     for (uint64_t action : node.actions(m_dfa)) {
-        // High bits are used to store flags. See compileRuleList.
+        // High bits are used to store flags. A boolean is stored in the 48th bit. See compileRuleList.
         if (action & 0xFFFF00000000)
-            emitTestFlagsAndAppendAction(static_cast<uint16_t>(action >> 32), static_cast<unsigned>(action));
+            emitTestFlagsAndAppendAction(static_cast<uint16_t>(action >> 32), static_cast<unsigned>(action), action & IfDomainFlag);
         else
-            emitAppendAction(static_cast<unsigned>(action));
+            emitAppendAction(static_cast<unsigned>(action), action & IfDomainFlag);
     }
     
     // If we jump to the root, we don't want to re-add its actions to a HashSet.

Modified: trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.h (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.h	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.h	2015-05-11 19:54:15 UTC (rev 184116)
@@ -66,8 +66,8 @@
     void compileNodeTransitions(const DFANode&);
     void compileCheckForRange(const Range&);
 
-    void emitAppendAction(unsigned);
-    void emitTestFlagsAndAppendAction(uint16_t flags, unsigned);
+    void emitAppendAction(unsigned, bool ifDomain);
+    void emitTestFlagsAndAppendAction(uint16_t flags, unsigned, bool ifDomain);
     void emitJump(unsigned destinationNodeIndex);
     void emitCheckValue(uint8_t value, unsigned destinationNodeIndex, bool caseSensitive);
     void emitCheckValueRange(uint8_t lowValue, uint8_t highValue, unsigned destinationNodeIndex, bool caseSensitive);

Modified: trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -50,18 +50,20 @@
     return *reinterpret_cast<const IntType*>(&bytecode[index]);
 }
     
-void DFABytecodeInterpreter::interpretAppendAction(unsigned& programCounter, Actions& actions)
+void DFABytecodeInterpreter::interpretAppendAction(unsigned& programCounter, Actions& actions, bool ifDomain)
 {
-    ASSERT(getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::AppendAction);
-    actions.add(static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode), m_pagesUsed)));
+    ASSERT(getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::AppendAction
+        || getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::AppendActionWithIfDomain);
+    actions.add((ifDomain ? IfDomainFlag : 0) | static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode), m_pagesUsed)));
     programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction);
 }
 
-void DFABytecodeInterpreter::interpretTestFlagsAndAppendAction(unsigned& programCounter, uint16_t flags, Actions& actions)
+void DFABytecodeInterpreter::interpretTestFlagsAndAppendAction(unsigned& programCounter, uint16_t flags, Actions& actions, bool ifDomain)
 {
-    ASSERT(getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::TestFlagsAndAppendAction);
+    ASSERT(getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::TestFlagsAndAppendAction
+        || getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::TestFlagsAndAppendActionWithIfDomain);
     if (flags & getBits<uint16_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode), m_pagesUsed))
-        actions.add(static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint16_t), m_pagesUsed)));
+        actions.add((ifDomain ? IfDomainFlag : 0) | static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint16_t), m_pagesUsed)));
     programCounter += instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendAction);
 }
 
@@ -76,7 +78,7 @@
     while (programCounter < dfaBytecodeLength) {
         DFABytecodeInstruction instruction = static_cast<DFABytecodeInstruction>(m_bytecode[programCounter]);
         if (instruction == DFABytecodeInstruction::AppendAction)
-            interpretAppendAction(programCounter, actions);
+            interpretAppendAction(programCounter, actions, false);
         else if (instruction == DFABytecodeInstruction::TestFlagsAndAppendAction)
             programCounter += instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendAction);
         else
@@ -107,7 +109,7 @@
                 if (instruction == DFABytecodeInstruction::AppendAction)
                     programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction);
                 else if (instruction == DFABytecodeInstruction::TestFlagsAndAppendAction)
-                    interpretTestFlagsAndAppendAction(programCounter, flags, actions);
+                    interpretTestFlagsAndAppendAction(programCounter, flags, actions, false);
                 else
                     break;
             }
@@ -203,12 +205,20 @@
                 break;
                     
             case DFABytecodeInstruction::AppendAction:
-                interpretAppendAction(programCounter, actions);
+                interpretAppendAction(programCounter, actions, false);
                 break;
                     
+            case DFABytecodeInstruction::AppendActionWithIfDomain:
+                interpretAppendAction(programCounter, actions, true);
+                break;
+                    
             case DFABytecodeInstruction::TestFlagsAndAppendAction:
-                interpretTestFlagsAndAppendAction(programCounter, flags, actions);
+                interpretTestFlagsAndAppendAction(programCounter, flags, actions, false);
                 break;
+            
+            case DFABytecodeInstruction::TestFlagsAndAppendActionWithIfDomain:
+                interpretTestFlagsAndAppendAction(programCounter, flags, actions, true);
+                break;
                     
             default:
                 RELEASE_ASSERT_NOT_REACHED(); // Invalid bytecode.

Modified: trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.h (184115 => 184116)


--- trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.h	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.h	2015-05-11 19:54:15 UTC (rev 184116)
@@ -63,8 +63,8 @@
     Actions actionsFromDFARoot();
 
 private:
-    void interpretAppendAction(unsigned& programCounter, Actions&);
-    void interpretTestFlagsAndAppendAction(unsigned& programCounter, uint16_t flags, Actions&);
+    void interpretAppendAction(unsigned& programCounter, Actions&, bool ifDomain);
+    void interpretTestFlagsAndAppendAction(unsigned& programCounter, uint16_t flags, Actions&, bool ifDomain);
     const DFABytecode* m_bytecode;
     const unsigned m_bytecodeLength;
     Vector<bool>& m_pagesUsed;

Modified: trunk/Source/WebCore/loader/ResourceLoadInfo.h (184115 => 184116)


--- trunk/Source/WebCore/loader/ResourceLoadInfo.h	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/loader/ResourceLoadInfo.h	2015-05-11 19:54:15 UTC (rev 184116)
@@ -53,6 +53,12 @@
 
 typedef uint16_t ResourceFlags;
 
+// The first 32 bits of a uint64_t action are used for the action location.
+// The next 16 bits are used for the flags (ResourceType and LoadType).
+// The next bit is used to mark actions that are from a rule with an if-domain condition.
+// The values -1 and -2 are used for removed and empty values in HashTables.
+const uint64_t IfDomainFlag = 0x0001000000000000;
+
 ResourceType toResourceType(CachedResource::Type);
 uint16_t readResourceType(const String&);
 uint16_t readLoadType(const String&);

Modified: trunk/Source/WebKit2/ChangeLog (184115 => 184116)


--- trunk/Source/WebKit2/ChangeLog	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/ChangeLog	2015-05-11 19:54:15 UTC (rev 184116)
@@ -1,3 +1,33 @@
+2015-05-11  Alex Christensen  <[email protected]>
+
+        [Content Extensions] Support domain-specific rules and exceptions.
+        https://bugs.webkit.org/show_bug.cgi?id=144833
+
+        Reviewed by Darin Adler.
+
+        * Shared/WebCompiledContentExtension.cpp:
+        (WebKit::WebCompiledContentExtension::filtersWithoutDomainsBytecode):
+        (WebKit::WebCompiledContentExtension::filtersWithoutDomainsBytecodeLength):
+        (WebKit::WebCompiledContentExtension::filtersWithDomainsBytecode):
+        (WebKit::WebCompiledContentExtension::filtersWithDomainsBytecodeLength):
+        (WebKit::WebCompiledContentExtension::domainFiltersBytecode):
+        (WebKit::WebCompiledContentExtension::domainFiltersBytecodeLength):
+        (WebKit::WebCompiledContentExtension::bytecode): Deleted.
+        (WebKit::WebCompiledContentExtension::bytecodeLength): Deleted.
+        * Shared/WebCompiledContentExtension.h:
+        * Shared/WebCompiledContentExtensionData.cpp:
+        (WebKit::WebCompiledContentExtensionData::encode):
+        (WebKit::WebCompiledContentExtensionData::decode):
+        * Shared/WebCompiledContentExtensionData.h:
+        (WebKit::WebCompiledContentExtensionData::WebCompiledContentExtensionData):
+        * UIProcess/API/APIUserContentExtensionStore.cpp:
+        (API::ContentExtensionMetaData::fileSize):
+        (API::encodeContentExtensionMetaData):
+        (API::decodeContentExtensionMetaData):
+        (API::compiledToFile):
+        (API::createExtension):
+        Keep track of 3 different types of bytecode to be able to handle domain-specific rules.
+
 2015-05-11  Tim Horton  <[email protected]>
 
         Page overlay action context override should indicate the source of the request

Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp (184115 => 184116)


--- trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -44,16 +44,36 @@
 {
 }
 
-const WebCore::ContentExtensions::DFABytecode* WebCompiledContentExtension::bytecode() const
+const WebCore::ContentExtensions::DFABytecode* WebCompiledContentExtension::filtersWithoutDomainsBytecode() const
 {
-    return static_cast<const WebCore::ContentExtensions::DFABytecode*>(m_data.data->data()) + m_data.bytecodeOffset;
+    return static_cast<const WebCore::ContentExtensions::DFABytecode*>(m_data.data->data()) + m_data.filtersWithoutDomainsBytecodeOffset;
 }
 
-unsigned WebCompiledContentExtension::bytecodeLength() const
+unsigned WebCompiledContentExtension::filtersWithoutDomainsBytecodeLength() const
 {
-    return m_data.bytecodeSize;
+    return m_data.filtersWithoutDomainsBytecodeSize;
 }
 
+const WebCore::ContentExtensions::DFABytecode* WebCompiledContentExtension::filtersWithDomainsBytecode() const
+{
+    return static_cast<const WebCore::ContentExtensions::DFABytecode*>(m_data.data->data()) + m_data.filtersWithDomainsBytecodeOffset;
+}
+
+unsigned WebCompiledContentExtension::filtersWithDomainsBytecodeLength() const
+{
+    return m_data.filtersWithDomainsBytecodeSize;
+}
+
+const WebCore::ContentExtensions::DFABytecode* WebCompiledContentExtension::domainFiltersBytecode() const
+{
+    return static_cast<const WebCore::ContentExtensions::DFABytecode*>(m_data.data->data()) + m_data.domainFiltersBytecodeOffset;
+}
+
+unsigned WebCompiledContentExtension::domainFiltersBytecodeLength() const
+{
+    return m_data.domainFiltersBytecodeSize;
+}
+
 const WebCore::ContentExtensions::SerializedActionByte* WebCompiledContentExtension::actions() const
 {
     return static_cast<const WebCore::ContentExtensions::SerializedActionByte*>(m_data.data->data()) + m_data.actionsOffset;

Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtension.h (184115 => 184116)


--- trunk/Source/WebKit2/Shared/WebCompiledContentExtension.h	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtension.h	2015-05-11 19:54:15 UTC (rev 184116)
@@ -44,8 +44,13 @@
 private:
     WebCompiledContentExtension(WebCompiledContentExtensionData&&);
 
-    virtual const WebCore::ContentExtensions::DFABytecode* bytecode() const override;
-    virtual unsigned bytecodeLength() const override;
+    virtual const WebCore::ContentExtensions::DFABytecode* filtersWithoutDomainsBytecode() const override;
+    virtual unsigned filtersWithoutDomainsBytecodeLength() const override;
+    virtual const WebCore::ContentExtensions::DFABytecode* filtersWithDomainsBytecode() const override;
+    virtual unsigned filtersWithDomainsBytecodeLength() const override;
+    virtual const WebCore::ContentExtensions::DFABytecode* domainFiltersBytecode() const override;
+    virtual unsigned domainFiltersBytecodeLength() const override;
+    
     virtual const WebCore::ContentExtensions::SerializedActionByte* actions() const override;
     virtual unsigned actionsLength() const override;
     

Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp (184115 => 184116)


--- trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -40,8 +40,12 @@
 
     encoder << actionsOffset;
     encoder << actionsSize;
-    encoder << bytecodeOffset;
-    encoder << bytecodeSize;
+    encoder << filtersWithoutDomainsBytecodeOffset;
+    encoder << filtersWithoutDomainsBytecodeSize;
+    encoder << filtersWithDomainsBytecodeOffset;
+    encoder << filtersWithDomainsBytecodeSize;
+    encoder << domainFiltersBytecodeOffset;
+    encoder << domainFiltersBytecodeSize;
 }
 
 bool WebCompiledContentExtensionData::decode(IPC::ArgumentDecoder& decoder, WebCompiledContentExtensionData& compiledContentExtensionData)
@@ -55,10 +59,18 @@
         return false;
     if (!decoder.decode(compiledContentExtensionData.actionsSize))
         return false;
-    if (!decoder.decode(compiledContentExtensionData.bytecodeOffset))
+    if (!decoder.decode(compiledContentExtensionData.filtersWithoutDomainsBytecodeOffset))
         return false;
-    if (!decoder.decode(compiledContentExtensionData.bytecodeSize))
+    if (!decoder.decode(compiledContentExtensionData.filtersWithoutDomainsBytecodeSize))
         return false;
+    if (!decoder.decode(compiledContentExtensionData.filtersWithDomainsBytecodeOffset))
+        return false;
+    if (!decoder.decode(compiledContentExtensionData.filtersWithDomainsBytecodeSize))
+        return false;
+    if (!decoder.decode(compiledContentExtensionData.domainFiltersBytecodeOffset))
+        return false;
+    if (!decoder.decode(compiledContentExtensionData.domainFiltersBytecodeSize))
+        return false;
 
     return true;
 }

Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h (184115 => 184116)


--- trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h	2015-05-11 19:54:15 UTC (rev 184116)
@@ -45,13 +45,17 @@
     {
     }
     
-    WebCompiledContentExtensionData(RefPtr<SharedMemory>&& data, NetworkCache::Data fileData, unsigned actionsOffset, unsigned actionsSize, unsigned bytecodeOffset, unsigned bytecodeSize)
+    WebCompiledContentExtensionData(RefPtr<SharedMemory>&& data, NetworkCache::Data fileData, unsigned actionsOffset, unsigned actionsSize, unsigned filtersWithoutDomainsBytecodeOffset, unsigned filtersWithoutDomainsBytecodeSize, unsigned filtersWithDomainsBytecodeOffset, unsigned filtersWithDomainsBytecodeSize, unsigned domainFiltersBytecodeOffset, unsigned domainFiltersBytecodeSize)
         : data(WTF::move(data))
         , fileData(fileData)
         , actionsOffset(actionsOffset)
         , actionsSize(actionsSize)
-        , bytecodeOffset(bytecodeOffset)
-        , bytecodeSize(bytecodeSize)
+        , filtersWithoutDomainsBytecodeOffset(filtersWithoutDomainsBytecodeOffset)
+        , filtersWithoutDomainsBytecodeSize(filtersWithoutDomainsBytecodeSize)
+        , filtersWithDomainsBytecodeOffset(filtersWithDomainsBytecodeOffset)
+        , filtersWithDomainsBytecodeSize(filtersWithDomainsBytecodeSize)
+        , domainFiltersBytecodeOffset(domainFiltersBytecodeOffset)
+        , domainFiltersBytecodeSize(domainFiltersBytecodeSize)
     {
     }
 
@@ -62,8 +66,12 @@
     NetworkCache::Data fileData;
     unsigned actionsOffset { 0 };
     unsigned actionsSize { 0 };
-    unsigned bytecodeOffset { 0 };
-    unsigned bytecodeSize { 0 };
+    unsigned filtersWithoutDomainsBytecodeOffset { 0 };
+    unsigned filtersWithoutDomainsBytecodeSize { 0 };
+    unsigned filtersWithDomainsBytecodeOffset { 0 };
+    unsigned filtersWithDomainsBytecodeSize { 0 };
+    unsigned domainFiltersBytecodeOffset { 0 };
+    unsigned domainFiltersBytecodeSize { 0 };
 };
 
 }

Modified: trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp (184115 => 184116)


--- trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -74,11 +74,22 @@
     return WebCore::pathByAppendingComponent(base, "ContentExtension-" + WebCore::encodeForFileName(identifier));
 }
 
-const size_t ContentExtensionFileHeaderSize = sizeof(uint32_t) + 2 * sizeof(uint64_t);
+const size_t ContentExtensionFileHeaderSize = sizeof(uint32_t) + 4 * sizeof(uint64_t);
 struct ContentExtensionMetaData {
-    uint32_t version { 1 };
+    uint32_t version { 2 };
     uint64_t actionsSize { 0 };
-    uint64_t bytecodeSize { 0 };
+    uint64_t filtersWithoutDomainsBytecodeSize { 0 };
+    uint64_t filtersWithDomainBytecodeSize { 0 };
+    uint64_t domainFiltersBytecodeSize { 0 };
+    
+    size_t fileSize() const
+    {
+        return ContentExtensionFileHeaderSize
+            + actionsSize
+            + filtersWithoutDomainsBytecodeSize
+            + filtersWithDomainBytecodeSize
+            + domainFiltersBytecodeSize;
+    }
 };
 
 static Data encodeContentExtensionMetaData(const ContentExtensionMetaData& metaData)
@@ -87,7 +98,9 @@
 
     encoder << metaData.version;
     encoder << metaData.actionsSize;
-    encoder << metaData.bytecodeSize;
+    encoder << metaData.filtersWithoutDomainsBytecodeSize;
+    encoder << metaData.filtersWithDomainBytecodeSize;
+    encoder << metaData.domainFiltersBytecodeSize;
 
     ASSERT(encoder.bufferSize() == ContentExtensionFileHeaderSize);
     return Data(encoder.buffer(), encoder.bufferSize());
@@ -107,8 +120,12 @@
             return false;
         if (!decoder.decode(metaData.actionsSize))
             return false;
-        if (!decoder.decode(metaData.bytecodeSize))
+        if (!decoder.decode(metaData.filtersWithoutDomainsBytecodeSize))
             return false;
+        if (!decoder.decode(metaData.filtersWithDomainBytecodeSize))
+            return false;
+        if (!decoder.decode(metaData.domainFiltersBytecodeSize))
+            return false;
         success = true;
         return false;
     });
@@ -163,39 +180,62 @@
             : m_fileHandle(fileHandle)
             , m_metaData(metaData)
         {
+            ASSERT(!metaData.actionsSize);
+            ASSERT(!metaData.filtersWithoutDomainsBytecodeSize);
+            ASSERT(!metaData.filtersWithDomainBytecodeSize);
+            ASSERT(!metaData.domainFiltersBytecodeSize);
         }
-
-        virtual void writeBytecode(Vector<DFABytecode>&& bytecode) override
+        
+        virtual void writeFiltersWithoutDomainsBytecode(Vector<DFABytecode>&& bytecode)
         {
-            m_bytecodeWritten += bytecode.size();
-            write(Data(bytecode.data(), bytecode.size()));
+            ASSERT(!m_filtersWithDomainBytecodeWritten);
+            ASSERT(!m_domainFiltersBytecodeWritten);
+            m_filtersWithoutDomainsBytecodeWritten += bytecode.size();
+            writeToFile(Data(bytecode.data(), bytecode.size()));
         }
+        
+        virtual void writeFiltersWithDomainsBytecode(Vector<DFABytecode>&& bytecode)
+        {
+            ASSERT(!m_domainFiltersBytecodeWritten);
+            m_filtersWithDomainBytecodeWritten += bytecode.size();
+            writeToFile(Data(bytecode.data(), bytecode.size()));
+        }
+        
+        virtual void writeDomainFiltersBytecode(Vector<DFABytecode>&& bytecode)
+        {
+            m_domainFiltersBytecodeWritten += bytecode.size();
+            writeToFile(Data(bytecode.data(), bytecode.size()));
+        }
 
         virtual void writeActions(Vector<SerializedActionByte>&& actions) override
         {
-            ASSERT(!m_bytecodeWritten);
+            ASSERT(!m_filtersWithoutDomainsBytecodeWritten);
+            ASSERT(!m_filtersWithDomainBytecodeWritten);
+            ASSERT(!m_domainFiltersBytecodeWritten);
             ASSERT(!m_actionsWritten);
             m_actionsWritten += actions.size();
-            write(Data(actions.data(), actions.size()));
+            writeToFile(Data(actions.data(), actions.size()));
         }
         
         virtual void finalize() override
         {
             m_metaData.actionsSize = m_actionsWritten;
-            m_metaData.bytecodeSize = m_bytecodeWritten;
+            m_metaData.filtersWithoutDomainsBytecodeSize = m_filtersWithoutDomainsBytecodeWritten;
+            m_metaData.filtersWithDomainBytecodeSize = m_filtersWithDomainBytecodeWritten;
+            m_metaData.domainFiltersBytecodeSize = m_domainFiltersBytecodeWritten;
             
             Data header = encodeContentExtensionMetaData(m_metaData);
             if (!m_fileError && WebCore::seekFile(m_fileHandle, 0ll, WebCore::FileSeekOrigin::SeekFromBeginning) == -1) {
                 WebCore::closeFile(m_fileHandle);
                 m_fileError = true;
             }
-            write(header);
+            writeToFile(header);
         }
         
         bool hadErrorWhileWritingToFile() { return m_fileError; }
 
     private:
-        void write(const Data& data)
+        void writeToFile(const Data& data)
         {
             if (!m_fileError && !writeDataToFile(data, m_fileHandle)) {
                 WebCore::closeFile(m_fileHandle);
@@ -205,7 +245,9 @@
         
         WebCore::PlatformFileHandle m_fileHandle;
         ContentExtensionMetaData& m_metaData;
-        size_t m_bytecodeWritten { 0 };
+        size_t m_filtersWithoutDomainsBytecodeWritten { 0 };
+        size_t m_filtersWithDomainBytecodeWritten { 0 };
+        size_t m_domainFiltersBytecodeWritten { 0 };
         size_t m_actionsWritten { 0 };
         bool m_fileError { false };
     };
@@ -228,7 +270,7 @@
     if (compilationClient.hadErrorWhileWritingToFile())
         return UserContentExtensionStore::Error::CompileFailed;
     
-    mappedData = mapFile(temporaryFileHandle, 0, ContentExtensionFileHeaderSize + metaData.actionsSize + metaData.bytecodeSize);
+    mappedData = mapFile(temporaryFileHandle, 0, metaData.fileSize());
     WebCore::closeFile(temporaryFileHandle);
 
     if (mappedData.isNull())
@@ -248,8 +290,18 @@
         fileData,
         ContentExtensionFileHeaderSize,
         metaData.actionsSize,
-        ContentExtensionFileHeaderSize + metaData.actionsSize,
-        metaData.bytecodeSize
+        ContentExtensionFileHeaderSize
+            + metaData.actionsSize,
+        metaData.filtersWithoutDomainsBytecodeSize,
+        ContentExtensionFileHeaderSize
+            + metaData.actionsSize
+            + metaData.filtersWithoutDomainsBytecodeSize,
+        metaData.filtersWithDomainBytecodeSize,
+        ContentExtensionFileHeaderSize
+            + metaData.actionsSize
+            + metaData.filtersWithoutDomainsBytecodeSize
+            + metaData.filtersWithDomainBytecodeSize,
+        metaData.domainFiltersBytecodeSize
     );
     auto compiledContentExtension = WebKit::WebCompiledContentExtension::create(WTF::move(compiledContentExtensionData));
     return API::UserContentExtension::create(identifier, WTF::move(compiledContentExtension));

Modified: trunk/Tools/ChangeLog (184115 => 184116)


--- trunk/Tools/ChangeLog	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Tools/ChangeLog	2015-05-11 19:54:15 UTC (rev 184116)
@@ -1,3 +1,21 @@
+2015-05-11  Alex Christensen  <[email protected]>
+
+        [Content Extensions] Support domain-specific rules and exceptions.
+        https://bugs.webkit.org/show_bug.cgi?id=144833
+
+        Reviewed by Darin Adler.
+
+        * TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
+        (TestWebKitAPI::InMemoryCompiledContentExtension::createFromFilter):
+        (TestWebKitAPI::InMemoryCompiledContentExtension::create):
+        (TestWebKitAPI::InMemoryCompiledContentExtension::InMemoryCompiledContentExtension):
+        Moved CompiledContentExtensionData from ContentExtensionCompiler.h because it is only used for testing.
+        (TestWebKitAPI::mainDocumentRequest):
+        (TestWebKitAPI::subResourceRequest):
+        (TestWebKitAPI::TEST_F):
+        (TestWebKitAPI::checkCompilerError):
+        Added tests for parsing and functionality of if-domain and unless-domain.
+
 2015-05-11  Ryosuke Niwa  <[email protected]>
 
         run-benchmark should support Chrome Canary and Firefox Nightly

Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp (184115 => 184116)


--- trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp	2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp	2015-05-11 19:54:15 UTC (rev 184116)
@@ -80,34 +80,62 @@
     }
 };
 
-class InMemoryContentExtensionCompilationClient final : public WebCore::ContentExtensions::ContentExtensionCompilationClient {
+struct CompiledContentExtensionData {
+    Vector<ContentExtensions::SerializedActionByte> actions;
+    Vector<ContentExtensions::DFABytecode> filtersWithoutDomains;
+    Vector<ContentExtensions::DFABytecode> filtersWithDomains;
+    Vector<ContentExtensions::DFABytecode> domainFilters;
+};
+
+class InMemoryContentExtensionCompilationClient final : public ContentExtensions::ContentExtensionCompilationClient {
 public:
-    InMemoryContentExtensionCompilationClient(WebCore::ContentExtensions::CompiledContentExtensionData& data)
+    InMemoryContentExtensionCompilationClient(CompiledContentExtensionData& data)
         : m_data(data)
     {
-        EXPECT_EQ(data.bytecode.size(), 0ull);
         EXPECT_EQ(data.actions.size(), 0ull);
+        EXPECT_EQ(data.filtersWithoutDomains.size(), 0ull);
+        EXPECT_EQ(data.filtersWithDomains.size(), 0ull);
+        EXPECT_EQ(data.domainFilters.size(), 0ull);
     }
 
-    virtual void writeBytecode(Vector<WebCore::ContentExtensions::DFABytecode>&& bytecode) override
+    virtual void writeActions(Vector<ContentExtensions::SerializedActionByte>&& actions) override
     {
         EXPECT_FALSE(finalized);
-        m_data.bytecode.appendVector(bytecode);
+        EXPECT_EQ(m_data.actions.size(), 0ull);
+        EXPECT_EQ(m_data.filtersWithoutDomains.size(), 0ull);
+        EXPECT_EQ(m_data.filtersWithDomains.size(), 0ull);
+        EXPECT_EQ(m_data.domainFilters.size(), 0ull);
+        m_data.actions.appendVector(actions);
     }
     
-    virtual void writeActions(Vector<WebCore::ContentExtensions::SerializedActionByte>&& actions) override
+    virtual void writeFiltersWithoutDomainsBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) override
     {
         EXPECT_FALSE(finalized);
-        m_data.actions.appendVector(actions);
+        EXPECT_EQ(m_data.filtersWithDomains.size(), 0ull);
+        EXPECT_EQ(m_data.domainFilters.size(), 0ull);
+        m_data.filtersWithoutDomains.appendVector(bytecode);
     }
     
+    virtual void writeFiltersWithDomainsBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) override
+    {
+        EXPECT_FALSE(finalized);
+        EXPECT_EQ(m_data.domainFilters.size(), 0ull);
+        m_data.filtersWithDomains.appendVector(bytecode);
+    }
+    
+    virtual void writeDomainFiltersBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) override
+    {
+        EXPECT_FALSE(finalized);
+        m_data.domainFilters.appendVector(bytecode);
+    }
+    
     virtual void finalize() override
     {
         finalized = true;
     }
 
 private:
-    WebCore::ContentExtensions::CompiledContentExtensionData& m_data;
+    CompiledContentExtensionData& m_data;
     bool finalized { false };
 };
 
@@ -115,16 +143,19 @@
 public:
     static RefPtr<InMemoryCompiledContentExtension> createFromFilter(String&& filter)
     {
-        WebCore::ContentExtensions::CompiledContentExtensionData extensionData;
+        CompiledContentExtensionData extensionData;
         InMemoryContentExtensionCompilationClient client(extensionData);
         auto compilerError = ContentExtensions::compileRuleList(client, WTF::move(filter));
-        if (compilerError)
+        if (compilerError) {
+            // Compiling should always succeed here. We have other tests for compile failures.
+            EXPECT_TRUE(false);
             return nullptr;
+        }
 
         return InMemoryCompiledContentExtension::create(WTF::move(extensionData));
     }
 
-    static RefPtr<InMemoryCompiledContentExtension> create(ContentExtensions::CompiledContentExtensionData&& data)
+    static RefPtr<InMemoryCompiledContentExtension> create(CompiledContentExtensionData&& data)
     {
         return adoptRef(new InMemoryCompiledContentExtension(WTF::move(data)));
     }
@@ -133,18 +164,22 @@
     {
     }
 
-    virtual const ContentExtensions::DFABytecode* bytecode() const override { return m_data.bytecode.data(); }
-    virtual unsigned bytecodeLength() const override { return m_data.bytecode.size(); }
     virtual const ContentExtensions::SerializedActionByte* actions() const override { return m_data.actions.data(); }
     virtual unsigned actionsLength() const override { return m_data.actions.size(); }
+    virtual const ContentExtensions::DFABytecode* filtersWithoutDomainsBytecode() const override { return m_data.filtersWithoutDomains.data(); }
+    virtual unsigned filtersWithoutDomainsBytecodeLength() const override { return m_data.filtersWithoutDomains.size(); }
+    virtual const ContentExtensions::DFABytecode* filtersWithDomainsBytecode() const override { return m_data.filtersWithDomains.data(); }
+    virtual unsigned filtersWithDomainsBytecodeLength() const override { return m_data.filtersWithDomains.size(); }
+    virtual const ContentExtensions::DFABytecode* domainFiltersBytecode() const override { return m_data.domainFilters.data(); }
+    virtual unsigned domainFiltersBytecodeLength() const override { return m_data.domainFilters.size(); }
 
 private:
-    InMemoryCompiledContentExtension(ContentExtensions::CompiledContentExtensionData&& data)
+    InMemoryCompiledContentExtension(CompiledContentExtensionData&& data)
         : m_data(WTF::move(data))
     {
     }
 
-    ContentExtensions::CompiledContentExtensionData m_data;
+    CompiledContentExtensionData m_data;
 };
 
 void static testRequest(ContentExtensions::ContentExtensionsBackend contentExtensionsBackend, const ResourceLoadInfo& resourceLoadInfo, Vector<ContentExtensions::ActionType> expectedActions, bool ignorePreviousRules = false)
@@ -164,11 +199,16 @@
         EXPECT_EQ(actions[actions.size() - 1].type(), ContentExtensions::ActionType::CSSDisplayNoneStyleSheet);
 }
 
-ResourceLoadInfo mainDocumentRequest(const char* url, ResourceType resourceType = ResourceType::Document)
+static ResourceLoadInfo mainDocumentRequest(const char* url, ResourceType resourceType = ResourceType::Document)
 {
     return { URL(URL(), url), URL(URL(), url), resourceType };
 }
 
+static ResourceLoadInfo subResourceRequest(const char* url, const char* mainDocumentURL, ResourceType resourceType = ResourceType::Document)
+{
+    return { URL(URL(), url), URL(URL(), mainDocumentURL), resourceType };
+}
+
 ContentExtensions::ContentExtensionsBackend makeBackend(const char* json)
 {
     auto extension = InMemoryCompiledContentExtension::createFromFilter(json);
@@ -362,6 +402,79 @@
     testRequest(backend, mainDocumentRequest("https://post.org/post"), { });
 }
 
+TEST_F(ContentExtensionTest, DomainTriggers)
+{
+    auto ifDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"webkit.org\"]}}]");
+    testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.org/test.htm"), { });
+    testRequest(ifDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { });
+    testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(ifDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(ifDomainBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { });
+    testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { });
+    testRequest(ifDomainBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+    testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.organization/test.html"), { });
+    
+    auto unlessDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"webkit.org\"]}}]");
+    testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.org/test.htm"), { });
+    testRequest(unlessDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { });
+    testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+    testRequest(unlessDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { });
+    testRequest(unlessDomainBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { });
+    testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { });
+    testRequest(unlessDomainBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.organization/test.html"), { ContentExtensions::ActionType::BlockLoad });
+
+    auto combinedBackend1 = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test_block_load\", \"if-domain\":[\"webkit.org\"]}},"
+        "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"test_block_cookies\", \"unless-domain\":[\"webkit.org\"]}}]");
+    testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org"), { });
+    testRequest(combinedBackend1, mainDocumentRequest("http://not_webkit.org"), { });
+    testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org/test_block_load.html"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_load.html", "http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://webkit.org/"), { });
+    testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_load.html", "http://not_webkit.org/"), { });
+    testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://not_webkit.org/"), { });
+    testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org/test_block_cookies.html"), { });
+    testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_cookies.html", "http://webkit.org/"), { });
+    testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://webkit.org/"), { });
+    testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_cookies.html", "http://not_webkit.org/path/to/main/document.html"), { ContentExtensions::ActionType::BlockCookies });
+    testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://not_webkit.org/"), { });
+    
+    auto combinedBackend2 = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test_block_load\\\\.html\", \"if-domain\":[\"webkit.org\"]}},"
+        "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"test_block_cookies\\\\.html\", \"unless-domain\":[\"w3c.org\"]}},"
+        "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"test_css\\\\.html\"}}]");
+    testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_css.htm"), { });
+    testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_block_load.html"), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(combinedBackend2, mainDocumentRequest("http://not_webkit.org/test_block_load.html"), { });
+    testRequest(combinedBackend2, mainDocumentRequest("http://not_webkit.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/TEST_CSS.hTmL/test_block_load.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad});
+    testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_block_load.html"), { });
+    testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_block_cookies.html"), { });
+    testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_css.html/test_block_cookies.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(combinedBackend2, mainDocumentRequest("http://not_w3c.org/test_block_cookies.html"), { ContentExtensions::ActionType::BlockCookies });
+    testRequest(combinedBackend2, mainDocumentRequest("http://not_w3c.org/test_css.html/test_block_cookies.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies });
+
+    auto ifDomainWithFlagsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\", \"if-domain\":[\"webkit.org\"],\"resource-type\":[\"image\"]}}]");
+    testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+    testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.png", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+    testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.png", ResourceType::Image), { });
+
+    auto unlessDomainWithFlagsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\", \"unless-domain\":[\"webkit.org\"],\"resource-type\":[\"image\"]}}]");
+    testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+    testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.png", ResourceType::Image), { });
+    testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+    testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.png", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad });
+
+    // Domains should not be interepted as regular expressions.
+    auto domainRegexBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"we?bkit.org\"]}}]");
+    testRequest(domainRegexBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+    testRequest(domainRegexBackend, mainDocumentRequest("http://wbkit.org/test.html"), { });
+    
+    // FIXME: Add and test domain-specific popup-only blocking (with layout tests).
+}
+    
 TEST_F(ContentExtensionTest, TermsKnownToMatchAnything)
 {
     auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre1.*post1$\"}},"
@@ -577,12 +690,14 @@
     EXPECT_EQ(1ul, createNFAs(combinedURLFilters).size());
 }
 
-void checkCompilerError(const char* json, ContentExtensions::ContentExtensionError expectedError)
+void checkCompilerError(const char* json, std::error_code expectedError)
 {
-    WebCore::ContentExtensions::CompiledContentExtensionData extensionData;
+    CompiledContentExtensionData extensionData;
     InMemoryContentExtensionCompilationClient client(extensionData);
     std::error_code compilerError = ContentExtensions::compileRuleList(client, json);
-    EXPECT_EQ(compilerError.value(), static_cast<int>(expectedError));
+    EXPECT_EQ(compilerError.value(), expectedError.value());
+    if (compilerError.value())
+        EXPECT_STREQ(compilerError.category().name(), expectedError.category().name());
 }
 
 TEST_F(ContentExtensionTest, InvalidJSON)
@@ -605,11 +720,38 @@
         ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[\"invalid\"]}}]",
         ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[5]}}]",
+        ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":{}}}]",
         ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":[\"invalid\"]}}]",
         ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":[5]}}]",
+        ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
+    
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":{}}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[5]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"]}}]", { });
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":{}}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[5]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"\"]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"A\"]}}]", ContentExtensions::ContentExtensionError::JSONDomainNotLowerCaseASCII);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"\\u00DC\"]}}]", ContentExtensions::ContentExtensionError::JSONDomainNotLowerCaseASCII);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"0\"]}}]", { });
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"a\"]}}]", { });
 
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[],\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":5}}]", { });
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":5}}]", { });
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":5,\"unless-domain\":5}}]", { });
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+    
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"],\"unless-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONUnlessAndIfDomain);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"],\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONUnlessAndIfDomain);
+
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\", \"unexpected-identifier-should-be-ignored\":5}}]", { });
+
     checkCompilerError("[{\"action\":5,\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
         ContentExtensions::ContentExtensionError::JSONInvalidAction);
     checkCompilerError("[{\"action\":{\"type\":\"invalid\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
@@ -620,6 +762,8 @@
     checkCompilerError("[{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"webkit.org\"}},"
         "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]",
         ContentExtensions::ContentExtensionError::RegexMatchesEverythingAfterIgnorePreviousRules);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"if-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::RegexMatchesEverythingWithDomains);
+    checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::RegexMatchesEverythingWithDomains);
     checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[\"}}]",
         ContentExtensions::ContentExtensionError::JSONInvalidRegex);
 }
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to