Prepare to use CSS selectors in content extensions.
authorachristensen@apple.com <achristensen@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Mar 2015 01:57:19 +0000 (01:57 +0000)
committerachristensen@apple.com <achristensen@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Mar 2015 01:57:19 +0000 (01:57 +0000)
https://bugs.webkit.org/show_bug.cgi?id=142227

Reviewed by Benjamin Poulain.

Source/WebCore:

Test: http/tests/usercontentfilter/css-display-none.html

* CMakeLists.txt:
* WebCore.xcodeproj/project.pbxproj:
* contentextensions/ContentExtensionActions.h: Added.
* contentextensions/ContentExtensionRule.cpp:
(WebCore::ContentExtensions::Action::deserialize):
* contentextensions/ContentExtensionRule.h:
(WebCore::ContentExtensions::Action::Action):
(WebCore::ContentExtensions::Action::type):
(WebCore::ContentExtensions::Action::cssSelector):
* contentextensions/ContentExtensionsBackend.cpp:
(WebCore::ContentExtensions::ContentExtensionsBackend::serializeActions):
Put action descriptions into a compact format in a Vector
to be able to be put into one block of shared read-only memory.
(WebCore::ContentExtensions::ContentExtensionsBackend::setRuleList):
Put an index of the beginning of the description into the NFA instead of the index of the rule
because we will be sharing the descriptions of the actions and not the rules.
(WebCore::ContentExtensions::ContentExtensionsBackend::actionsForURL):
(WebCore::ContentExtensions::ContentExtensionsBackend::actionForURL): Deleted.
Return a vector of actions to be able to do multiple actions for one URL.
* contentextensions/ContentExtensionsBackend.h:
* contentextensions/ContentExtensionsManager.cpp:
(WebCore::ContentExtensions::ExtensionsManager::loadTrigger):
(WebCore::ContentExtensions::ExtensionsManager::loadAction):
Added the css-display-none action type, which requires a selector.
(WebCore::ContentExtensions::ExtensionsManager::loadRule):
* loader/cache/CachedResourceLoader.cpp:
(WebCore::CachedResourceLoader::requestResource):
* page/UserContentController.cpp:
(WebCore::UserContentController::actionsForURL):
(WebCore::UserContentController::actionForURL): Deleted.
* page/UserContentController.h:

LayoutTests:

* http/tests/usercontentfilter/css-display-none-expected.txt: Added.
* http/tests/usercontentfilter/css-display-none.html: Added.
* http/tests/usercontentfilter/css-display-none.html.json: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@180978 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/usercontentfilter/css-display-none-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/usercontentfilter/css-display-none.html [new file with mode: 0644]
LayoutTests/http/tests/usercontentfilter/css-display-none.html.json [new file with mode: 0644]
Source/WebCore/CMakeLists.txt
Source/WebCore/ChangeLog
Source/WebCore/WebCore.xcodeproj/project.pbxproj
Source/WebCore/contentextensions/ContentExtensionActions.h [new file with mode: 0644]
Source/WebCore/contentextensions/ContentExtensionRule.cpp
Source/WebCore/contentextensions/ContentExtensionRule.h
Source/WebCore/contentextensions/ContentExtensionsBackend.cpp
Source/WebCore/contentextensions/ContentExtensionsBackend.h
Source/WebCore/contentextensions/ContentExtensionsManager.cpp
Source/WebCore/loader/cache/CachedResourceLoader.cpp
Source/WebCore/page/UserContentController.cpp
Source/WebCore/page/UserContentController.h

index bf97444..ac95949 100644 (file)
@@ -1,3 +1,14 @@
+2015-03-03  Alex Christensen  <achristensen@webkit.org>
+
+        Prepare to use CSS selectors in content extensions.
+        https://bugs.webkit.org/show_bug.cgi?id=142227
+
+        Reviewed by Benjamin Poulain.
+
+        * http/tests/usercontentfilter/css-display-none-expected.txt: Added.
+        * http/tests/usercontentfilter/css-display-none.html: Added.
+        * http/tests/usercontentfilter/css-display-none.html.json: Added.
+
 2015-03-03  Brent Fulgham  <bfulgham@apple.com>
 
         [Win] Add baseline for new legacy-event-handler test.
diff --git a/LayoutTests/http/tests/usercontentfilter/css-display-none-expected.txt b/LayoutTests/http/tests/usercontentfilter/css-display-none-expected.txt
new file mode 100644 (file)
index 0000000..989ebd2
--- /dev/null
@@ -0,0 +1,14 @@
+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 784x576
+      RenderBlock {P} at (0,0) size 784x18
+        RenderText {#text} at (0,0) size 492x18
+          text run at (0,0) width 492: "This text should not be visible once the css selector is applied (not done yet)."
+      RenderBlock {P} at (0,34) size 784x18
+        RenderText {#text} at (0,0) size 522x18
+          text run at (0,0) width 522: "This text should also not be visible once the css selector is applied (not done yet)."
+      RenderBlock {P} at (0,68) size 784x18
+        RenderText {#text} at (0,0) size 173x18
+          text run at (0,0) width 173: "This text should be visible."
diff --git a/LayoutTests/http/tests/usercontentfilter/css-display-none.html b/LayoutTests/http/tests/usercontentfilter/css-display-none.html
new file mode 100644 (file)
index 0000000..c435af2
--- /dev/null
@@ -0,0 +1,5 @@
+<body>
+<p class="hidden">This text should not be visible once the css selector is applied (not done yet).</p>
+<p class="hidden_Ž">This text should also not be visible once the css selector is applied (not done yet).</p>
+<p class="not_hidden">This text should be visible.</p>
+</body>
diff --git a/LayoutTests/http/tests/usercontentfilter/css-display-none.html.json b/LayoutTests/http/tests/usercontentfilter/css-display-none.html.json
new file mode 100644 (file)
index 0000000..9f3cb60
--- /dev/null
@@ -0,0 +1,20 @@
+[
+    {
+        "action": {
+            "type": "css-display-none",
+            "selector": ".hidden"
+        },
+        "trigger": {
+            "url-filter": ".*css-display-none.html"
+        }
+    },
+    {
+        "action": {
+            "type": "css-display-none",
+            "selector": ".hidden_\u017D"
+        },
+        "trigger": {
+            "url-filter": ".*css-display-none.html"
+        }
+    }
+]
index 32898c7..ca34629 100644 (file)
@@ -29,6 +29,7 @@ set(WebCore_INCLUDE_DIRECTORIES
     "${WEBCORE_DIR}/bridge"
     "${WEBCORE_DIR}/bridge/c"
     "${WEBCORE_DIR}/bridge/jsc"
+    "${WEBCORE_DIR}/contentextensions"
     "${WEBCORE_DIR}/crypto"
     "${WEBCORE_DIR}/crypto/algorithms"
     "${WEBCORE_DIR}/crypto/keys"
index f83e792..8e2ed77 100644 (file)
@@ -1,3 +1,44 @@
+2015-03-03  Alex Christensen  <achristensen@webkit.org>
+
+        Prepare to use CSS selectors in content extensions.
+        https://bugs.webkit.org/show_bug.cgi?id=142227
+
+        Reviewed by Benjamin Poulain.
+
+        Test: http/tests/usercontentfilter/css-display-none.html
+
+        * CMakeLists.txt:
+        * WebCore.xcodeproj/project.pbxproj:
+        * contentextensions/ContentExtensionActions.h: Added.
+        * contentextensions/ContentExtensionRule.cpp:
+        (WebCore::ContentExtensions::Action::deserialize):
+        * contentextensions/ContentExtensionRule.h:
+        (WebCore::ContentExtensions::Action::Action):
+        (WebCore::ContentExtensions::Action::type):
+        (WebCore::ContentExtensions::Action::cssSelector):
+        * contentextensions/ContentExtensionsBackend.cpp:
+        (WebCore::ContentExtensions::ContentExtensionsBackend::serializeActions):
+        Put action descriptions into a compact format in a Vector 
+        to be able to be put into one block of shared read-only memory.
+        (WebCore::ContentExtensions::ContentExtensionsBackend::setRuleList):
+        Put an index of the beginning of the description into the NFA instead of the index of the rule
+        because we will be sharing the descriptions of the actions and not the rules.
+        (WebCore::ContentExtensions::ContentExtensionsBackend::actionsForURL):
+        (WebCore::ContentExtensions::ContentExtensionsBackend::actionForURL): Deleted.
+        Return a vector of actions to be able to do multiple actions for one URL.
+        * contentextensions/ContentExtensionsBackend.h:
+        * contentextensions/ContentExtensionsManager.cpp:
+        (WebCore::ContentExtensions::ExtensionsManager::loadTrigger):
+        (WebCore::ContentExtensions::ExtensionsManager::loadAction):
+        Added the css-display-none action type, which requires a selector.
+        (WebCore::ContentExtensions::ExtensionsManager::loadRule):
+        * loader/cache/CachedResourceLoader.cpp:
+        (WebCore::CachedResourceLoader::requestResource):
+        * page/UserContentController.cpp:
+        (WebCore::UserContentController::actionsForURL):
+        (WebCore::UserContentController::actionForURL): Deleted.
+        * page/UserContentController.h:
+
 2015-03-03  Brent Fulgham  <bfulgham@apple.com>
 
         Move scroll animating functions from ScrollAnimator to ScrollController
index 9c4e9d4..72a7b7e 100644 (file)
                5CD9F5661AA0F73C00DA45FF /* DFABytecode.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C39305D1AA0F6A90029C816 /* DFABytecode.h */; };
                5CD9F5671AA0F74200DA45FF /* DFABytecodeCompiler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C39305F1AA0F6A90029C816 /* DFABytecodeCompiler.h */; };
                5CD9F5681AA0F74600DA45FF /* DFABytecodeInterpreter.h in Headers */ = {isa = PBXBuildFile; fileRef = 5C3930611AA0F6A90029C816 /* DFABytecodeInterpreter.h */; };
+               5CDFA6C81AA4F2DA00EA8746 /* ContentExtensionActions.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CDFA6C71AA4F2DA00EA8746 /* ContentExtensionActions.h */; };
                5CFC4350192409E300A0D3B5 /* PointerLockController.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5CFC434E192406A900A0D3B5 /* PointerLockController.cpp */; };
                5D21A80213ECE5DF00BB7064 /* WebVTTParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5D21A80013ECE5DF00BB7064 /* WebVTTParser.cpp */; };
                5D21A80313ECE5DF00BB7064 /* WebVTTParser.h in Headers */ = {isa = PBXBuildFile; fileRef = 5D21A80113ECE5DF00BB7064 /* WebVTTParser.h */; };
                5C4304AF191AC908000E2BC0 /* EXTShaderTextureLOD.idl */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = EXTShaderTextureLOD.idl; path = canvas/EXTShaderTextureLOD.idl; sourceTree = "<group>"; };
                5C4304B3191AEF46000E2BC0 /* JSEXTShaderTextureLOD.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSEXTShaderTextureLOD.cpp; sourceTree = "<group>"; };
                5C4304B4191AEF46000E2BC0 /* JSEXTShaderTextureLOD.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSEXTShaderTextureLOD.h; sourceTree = "<group>"; };
+               5CDFA6C71AA4F2DA00EA8746 /* ContentExtensionActions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ContentExtensionActions.h; sourceTree = "<group>"; };
                5CFC434E192406A900A0D3B5 /* PointerLockController.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PointerLockController.cpp; sourceTree = "<group>"; };
                5CFC434F192406A900A0D3B5 /* PointerLockController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PointerLockController.h; sourceTree = "<group>"; };
                5D21A80013ECE5DF00BB7064 /* WebVTTParser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WebVTTParser.cpp; sourceTree = "<group>"; };
                26F0C8901A2D79CB002794F8 /* contentextensions */ = {
                        isa = PBXGroup;
                        children = (
+                               5CDFA6C71AA4F2DA00EA8746 /* ContentExtensionActions.h */,
                                26F0C8991A2EC110002794F8 /* ContentExtensionRule.cpp */,
                                26F0C89A1A2EC110002794F8 /* ContentExtensionRule.h */,
                                26F0C89D1A2EC3BE002794F8 /* ContentExtensionsBackend.cpp */,
                                FD31602912B0267600C1A359 /* ConvolverNode.h in Headers */,
                                D8B6152F1032495100C8554A /* Cookie.h in Headers */,
                                E1424C94164B52C800F32D40 /* CookieJar.h in Headers */,
+                               5CDFA6C81AA4F2DA00EA8746 /* ContentExtensionActions.h in Headers */,
                                339B5B63131DAA3200F48D02 /* CookiesStrategy.h in Headers */,
                                33D0212D131DB37B004091A8 /* CookieStorage.h in Headers */,
                                CE1252431A16C01A00864480 /* CoreUISPI.h in Headers */,
diff --git a/Source/WebCore/contentextensions/ContentExtensionActions.h b/Source/WebCore/contentextensions/ContentExtensionActions.h
new file mode 100644 (file)
index 0000000..e0b3b9d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ContentExtensionActions_h
+#define ContentExtensionActions_h
+
+#if ENABLE(CONTENT_EXTENSIONS)
+
+namespace WebCore {
+    
+namespace ContentExtensions {
+
+typedef uint8_t SerializedActionByte;
+    
+enum class ActionType : uint8_t {
+    BlockLoad,
+    BlockCookies,
+    CSSDisplayNone,
+    IgnorePreviousRules,
+    InvalidAction,
+};
+
+} // namespace ContentExtensions
+    
+} // namespace WebCore
+
+#endif // ENABLE(CONTENT_EXTENSIONS)
+
+#endif // ContentExtensionActions_h
index 59b8e0b..d46e795 100644 (file)
@@ -39,6 +39,33 @@ ContentExtensionRule::ContentExtensionRule(const Trigger& trigger, const Action&
     ASSERT(!m_trigger.urlFilter.isEmpty());
 }
 
+Action Action::deserialize(const Vector<SerializedActionByte>& actions, unsigned location)
+{
+    switch (static_cast<ActionType>(actions[location])) {
+    case ActionType::BlockCookies:
+        return ActionType::BlockCookies;
+    case ActionType::BlockLoad:
+        return ActionType::BlockLoad;
+    case ActionType::IgnorePreviousRules:
+        return ActionType::IgnorePreviousRules;
+    case ActionType::InvalidAction:
+        RELEASE_ASSERT_NOT_REACHED();
+    case ActionType::CSSDisplayNone: {
+        unsigned stringStartIndex = location + sizeof(ActionType) + sizeof(unsigned) + sizeof(bool);
+        RELEASE_ASSERT(actions.size() >= stringStartIndex);
+        unsigned selectorLength = *reinterpret_cast<const unsigned*>(&actions[location + sizeof(ActionType)]);
+        bool wideCharacters = actions[location + sizeof(ActionType) + sizeof(unsigned)];
+        
+        if (wideCharacters) {
+            RELEASE_ASSERT(actions.size() >= stringStartIndex + selectorLength * sizeof(UChar));
+            return Action(ActionType::CSSDisplayNone, String(reinterpret_cast<const UChar*>(&actions[stringStartIndex]), selectorLength));
+        }
+        RELEASE_ASSERT(actions.size() >= stringStartIndex + selectorLength * sizeof(LChar));
+        return Action(ActionType::CSSDisplayNone, String(reinterpret_cast<const LChar*>(&actions[stringStartIndex]), selectorLength));
+    }
+    }
+}
+
 }
 
 } // namespace WebCore
index ef2113b..4d2fd3f 100644 (file)
 
 #if ENABLE(CONTENT_EXTENSIONS)
 
+#include "ContentExtensionActions.h"
 #include <wtf/text/WTFString.h>
 
 namespace WebCore {
 
 namespace ContentExtensions {
 
-enum class ExtensionActionType {
-    BlockLoad,
-    BlockCookies,
-    IgnorePreviousRules
-};
-
 // A ContentExtensionRule is the smallest unit in a ContentExtension.
 //
 // It is composed of a trigger and an action. The trigger defines on what kind of content this extension should apply.
 // The action defines what to perform on that content.
-class ContentExtensionRule {
-public:
-    struct Trigger {
-        String urlFilter;
-        bool urlFilterIsCaseSensitive = false;
-    };
 
-    struct Action {
-        ExtensionActionType type;
-    };
+struct Trigger {
+    String urlFilter;
+    bool urlFilterIsCaseSensitive { false };
+};
+    
+struct Action {
+    Action()
+        : m_type(ActionType::InvalidAction)
+    {
+    }
+    Action(ActionType type, const String& cssSelector)
+        : m_type(type)
+        , m_cssSelector(cssSelector)
+    {
+        ASSERT(type == ActionType::CSSDisplayNone);
+    }
+    Action(ActionType type)
+        : m_type(type)
+    {
+        ASSERT(type != ActionType::CSSDisplayNone);
+    }
+    static Action deserialize(const Vector<SerializedActionByte>&, unsigned location);
 
+    ActionType type() const { return m_type; }
+    const String& cssSelector() const { return m_cssSelector; }
+        
+private:
+    ActionType m_type;
+    String m_cssSelector;
+};
+    
+class ContentExtensionRule {
+public:
     ContentExtensionRule(const Trigger&, const Action&);
 
     const Trigger& trigger() const { return m_trigger; }
index fe78fc3..565809a 100644 (file)
 namespace WebCore {
 
 namespace ContentExtensions {
+    
+Vector<unsigned> ContentExtensionsBackend::serializeActions(const Vector<ContentExtensionRule>& ruleList, Vector<SerializedActionByte>& actions)
+{
+    ASSERT(!actions.size());
+    
+    Vector<unsigned> actionLocations;
+        
+    for (unsigned ruleIndex = 0; ruleIndex < ruleList.size(); ++ruleIndex) {
+        const ContentExtensionRule& rule = ruleList[ruleIndex];
+        actionLocations.append(actions.size());
+        
+        switch (rule.action().type()) {
+        case ActionType::InvalidAction:
+            RELEASE_ASSERT_NOT_REACHED();
+
+        case ActionType::BlockLoad:
+        case ActionType::BlockCookies:
+        case ActionType::IgnorePreviousRules:
+            actions.append(static_cast<SerializedActionByte>(rule.action().type()));
+            break;
+
+        case ActionType::CSSDisplayNone: {
+            const String& selector = rule.action().cssSelector();
+            // Append action type (1 byte).
+            actions.append(static_cast<SerializedActionByte>(ActionType::CSSDisplayNone));
+            // Append Selector length (4 bytes).
+            unsigned selectorLength = selector.length();
+            actions.resize(actions.size() + sizeof(unsigned));
+            *reinterpret_cast<unsigned*>(&actions[actions.size() - sizeof(unsigned)]) = selectorLength;
+            bool wideCharacters = !selector.is8Bit();
+            actions.append(wideCharacters);
+            // Append Selector.
+            if (wideCharacters) {
+                for (unsigned i = 0; i < selectorLength; i++) {
+                    actions.resize(actions.size() + sizeof(UChar));
+                    *reinterpret_cast<UChar*>(&actions[actions.size() - sizeof(UChar)]) = selector[i];
+                }
+            } else {
+                for (unsigned i = 0; i < selectorLength; i++)
+                    actions.append(selector[i]);
+            }
+            break;
+        }
+        }
+    }
+    return actionLocations;
+}
 
 void ContentExtensionsBackend::setRuleList(const String& identifier, const Vector<ContentExtensionRule>& ruleList)
 {
@@ -59,14 +106,17 @@ void ContentExtensionsBackend::setRuleList(const String& identifier, const Vecto
     double nfaBuildTimeStart = monotonicallyIncreasingTime();
 #endif
 
+    Vector<SerializedActionByte> actions;
+    Vector<unsigned> actionLocations = serializeActions(ruleList, actions);
+
     NFA nfa;
     URLFilterParser urlFilterParser(nfa);
     for (unsigned ruleIndex = 0; ruleIndex < ruleList.size(); ++ruleIndex) {
         const ContentExtensionRule& contentExtensionRule = ruleList[ruleIndex];
-        const ContentExtensionRule::Trigger& trigger = contentExtensionRule.trigger();
+        const Trigger& trigger = contentExtensionRule.trigger();
         ASSERT(trigger.urlFilter.length());
 
-        String error = urlFilterParser.addPattern(trigger.urlFilter, trigger.urlFilterIsCaseSensitive, ruleIndex);
+        String error = urlFilterParser.addPattern(trigger.urlFilter, trigger.urlFilterIsCaseSensitive, actionLocations[ruleIndex]);
 
         if (!error.isNull()) {
             dataLogF("Error while parsing %s: %s\n", trigger.urlFilter.utf8().data(), error.utf8().data());
@@ -103,7 +153,7 @@ void ContentExtensionsBackend::setRuleList(const String& identifier, const Vecto
     Vector<DFABytecode> bytecode;
     DFABytecodeCompiler compiler(dfa, bytecode);
     compiler.compile();
-    CompiledContentExtension compiledContentExtension = { bytecode, ruleList };
+    CompiledContentExtension compiledContentExtension = { bytecode, actions };
     m_ruleLists.set(identifier, compiledContentExtension);
 }
 
@@ -117,32 +167,37 @@ void ContentExtensionsBackend::removeAllRuleLists()
     m_ruleLists.clear();
 }
 
-ContentFilterAction ContentExtensionsBackend::actionForURL(const URL& url)
+Vector<Action> ContentExtensionsBackend::actionsForURL(const URL& url)
 {
     const String& urlString = url.string();
     ASSERT_WITH_MESSAGE(urlString.containsOnlyASCII(), "A decoded URL should only contain ASCII characters. The matching algorithm assumes the input is ASCII.");
     const CString& urlCString = urlString.utf8();
 
+    Vector<Action> actions;
     for (auto& ruleListSlot : m_ruleLists) {
         const CompiledContentExtension& compiledContentExtension = ruleListSlot.value;
         DFABytecodeInterpreter interpreter(compiledContentExtension.bytecode);
         DFABytecodeInterpreter::Actions triggeredActions = interpreter.interpret(urlCString);
-        // FIXME: We should eventually do something with each action rather than just returning a bool.
+        
         if (!triggeredActions.isEmpty()) {
-            Vector<uint64_t> sortedActions;
-            copyToVector(triggeredActions, sortedActions);
-            std::sort(sortedActions.begin(), sortedActions.end());
-            size_t lastAction = static_cast<size_t>(sortedActions.last());
-            ExtensionActionType type = compiledContentExtension.ruleList[lastAction].action().type;
-
-            if (type == ExtensionActionType::BlockLoad)
-                return ContentFilterAction::Block;
-            if (type == ExtensionActionType::BlockCookies)
-                return ContentFilterAction::BlockCookies;
+            Vector<unsigned> actionLocations;
+            actionLocations.reserveInitialCapacity(triggeredActions.size());
+            for (auto actionLocation : triggeredActions)
+                actionLocations.append(static_cast<unsigned>(actionLocation));
+            std::sort(actionLocations.begin(), actionLocations.end());
+            
+            // Add actions in reverse order to properly deal with IgnorePreviousRules.
+            for (unsigned i = actionLocations.size(); i; i--) {
+                Action action = Action::deserialize(ruleListSlot.value.actions, actionLocations[i - 1]);
+                if (action.type() == ActionType::IgnorePreviousRules)
+                    break;
+                actions.append(action);
+                if (action.type() == ActionType::BlockLoad)
+                    break;
+            }
         }
     }
-
-    return ContentFilterAction::Load;
+    return actions;
 }
 
 } // namespace ContentExtensions
index e97d9a4..a667330 100644 (file)
@@ -58,14 +58,16 @@ public:
     void removeAllRuleLists();
 
     // - Internal WebCore Interface.
-    ContentFilterAction actionForURL(const URL&);
+    Vector<Action> actionsForURL(const URL&);
 
 private:
     struct CompiledContentExtension {
         Vector<DFABytecode> bytecode;
-        Vector<ContentExtensionRule> ruleList;
+        Vector<SerializedActionByte> actions;
     };
 
+    Vector<unsigned> serializeActions(const Vector<ContentExtensionRule>& ruleList, Vector<SerializedActionByte>& actions);
+
     HashMap<String, CompiledContentExtension> m_ruleLists;
 };
 
index f962138..a2115fb 100644 (file)
@@ -48,7 +48,7 @@ namespace ContentExtensions {
 
 namespace ExtensionsManager {
 
-static bool loadTrigger(ExecState& exec, JSObject& ruleObject, ContentExtensionRule::Trigger& trigger)
+static bool loadTrigger(ExecState& exec, JSObject& ruleObject, Trigger& trigger)
 {
     JSValue triggerObject = ruleObject.get(&exec, Identifier(&exec, "trigger"));
     if (!triggerObject || exec.hadException() || !triggerObject.isObject()) {
@@ -76,7 +76,7 @@ static bool loadTrigger(ExecState& exec, JSObject& ruleObject, ContentExtensionR
     return true;
 }
 
-static bool loadAction(ExecState& exec, JSObject& ruleObject, ContentExtensionRule::Action& action)
+static bool loadAction(ExecState& exec, JSObject& ruleObject, Action& action)
 {
     JSValue actionObject = ruleObject.get(&exec, Identifier(&exec, "action"));
     if (!actionObject || exec.hadException() || !actionObject.isObject()) {
@@ -93,12 +93,19 @@ static bool loadAction(ExecState& exec, JSObject& ruleObject, ContentExtensionRu
     String actionType = typeObject.toWTFString(&exec);
 
     if (actionType == "block")
-        action.type = ExtensionActionType::BlockLoad;
+        action = ActionType::BlockLoad;
     else if (actionType == "ignore-previous-rules")
-        action.type = ExtensionActionType::IgnorePreviousRules;
+        action = ActionType::IgnorePreviousRules;
     else if (actionType == "block-cookies")
-        action.type = ExtensionActionType::BlockCookies;
-    else if (actionType != "block" && actionType != "") {
+        action = ActionType::BlockCookies;
+    else if (actionType == "css-display-none") {
+        JSValue selector = actionObject.get(&exec, Identifier(&exec, "selector"));
+        if (!selector || exec.hadException() || !selector.isString()) {
+            WTFLogAlways("css-display-none action type requires a selector");
+            return false;
+        }
+        action = Action(ActionType::CSSDisplayNone, selector.toWTFString(&exec));
+    } else {
         WTFLogAlways("Unrecognized action: \"%s\"", actionType.utf8().data());
         return false;
     }
@@ -108,11 +115,11 @@ static bool loadAction(ExecState& exec, JSObject& ruleObject, ContentExtensionRu
 
 static void loadRule(ExecState& exec, JSObject& ruleObject, Vector<ContentExtensionRule>& ruleList)
 {
-    ContentExtensionRule::Trigger trigger;
+    Trigger trigger;
     if (!loadTrigger(exec, ruleObject, trigger))
         return;
 
-    ContentExtensionRule::Action action;
+    Action action;
     if (!loadAction(exec, ruleObject, action))
         return;
 
index b68f708..5afba31 100644 (file)
@@ -38,6 +38,7 @@
 #include "CachedXSLStyleSheet.h"
 #include "Chrome.h"
 #include "ChromeClient.h"
+#include "ContentExtensionRule.h"
 #include "ContentSecurityPolicy.h"
 #include "DOMWindow.h"
 #include "DiagnosticLoggingClient.h"
@@ -464,16 +465,22 @@ CachedResourceHandle<CachedResource> CachedResourceLoader::requestResource(Cache
         return nullptr;
 
 #if ENABLE(CONTENT_EXTENSIONS)
-    ContentFilterAction action = ContentFilterAction::Load;
+    Vector<ContentExtensions::Action> actions;
 
-    if (frame() && frame()->page() && frame()->page()->userContentController()) {
-        action = frame()->page()->userContentController()->actionForURL(url);
-        if (action == ContentFilterAction::Block)
+    if (frame() && frame()->page() && frame()->page()->userContentController())
+        actions = frame()->page()->userContentController()->actionsForURL(url);
+
+    for (const auto& action : actions) {
+        if (action.type() == ContentExtensions::ActionType::BlockLoad)
             return nullptr;
+        if (action.type() == ContentExtensions::ActionType::BlockCookies)
+            request.mutableResourceRequest().setAllowCookies(false);
+        else if (action.type() == ContentExtensions::ActionType::CSSDisplayNone) {
+            // action.cssSelector() is the css to use here.
+            // FIXME: That css selector should be used to apply display:none.
+        } else
+            RELEASE_ASSERT_NOT_REACHED();
     }
-
-    if (action == ContentFilterAction::BlockCookies)
-        request.mutableResourceRequest().setAllowCookies(false);
 #endif
 
     auto& memoryCache = MemoryCache::singleton();
index 32cfdd7..08ec596 100644 (file)
@@ -203,12 +203,12 @@ void UserContentController::removeAllUserContentFilters()
     m_contentExtensionBackend->removeAllRuleLists();
 }
 
-ContentFilterAction UserContentController::actionForURL(const URL& url)
+Vector<ContentExtensions::Action> UserContentController::actionsForURL(const URL& url)
 {
     if (!m_contentExtensionBackend)
-        return ContentFilterAction::Load;
+        return Vector<ContentExtensions::Action>();
 
-    return m_contentExtensionBackend->actionForURL(url);
+    return m_contentExtensionBackend->actionsForURL(url);
 }
 
 #endif
index 451929f..bbbd566 100644 (file)
@@ -47,14 +47,9 @@ class UserMessageHandlerDescriptor;
 
 namespace ContentExtensions {
 class ContentExtensionsBackend;
+struct Action;
 }
 
-enum class ContentFilterAction {
-    Load,
-    Block,
-    BlockCookies
-};
-
 class UserContentController : public RefCounted<UserContentController> {
 public:
     WEBCORE_EXPORT static RefPtr<UserContentController> create();
@@ -91,7 +86,7 @@ public:
     WEBCORE_EXPORT void removeAllUserContentFilters();
     
     // FIXME: Consider putting this (and other future content filter predicates) in its own class.
-    ContentFilterAction actionForURL(const URL&);
+    Vector<ContentExtensions::Action> actionsForURL(const URL&);
 #endif
 
 private: