<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[186550] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/186550">186550</a></dd>
<dt>Author</dt> <dd>benjamin@webkit.org</dd>
<dt>Date</dt> <dd>2015-07-08 17:52:53 -0700 (Wed, 08 Jul 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Content Extensions] Fuse undistinguishable actions as much as possible
https://bugs.webkit.org/show_bug.cgi?id=146762

Patch by Benjamin Poulain &lt;bpoulain@apple.com&gt; on 2015-07-08
Reviewed by Alex Christensen.

Source/WebCore:

Our previous code that fused actions was based on test lists that were
grouping similar actions in the input.

The input we get from developers is more distributed. It is very common to
have trigger flags all over the place, and &quot;css-display-none&quot; mixed with &quot;block&quot;.

This patch refines the merging code to merge those cases as much as possible.

The size taken by the actions is negligible, but having different actions make
nodes unkillable by the Minimizer. By merging many more actions, the minimizer
no longer see those subtrees as distinguishable and can do a better job.

On a large test list, this cuts the bytecode size by 2 megabytes.

Tests: http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html
       http/tests/contentextensions/single-css-display-none.html

* contentextensions/ContentExtensionCompiler.cpp:
(WebCore::ContentExtensions::resolvePendingDisplayNoneActions):
(WebCore::ContentExtensions::serializeActions):

Tools:

* TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
Test that combinations and flags still work as expected.

LayoutTests:

Make sure the last CSS rule is not ignored.

* http/tests/contentextensions/css-display-none-after-ignore-previous-rules-expected.txt: Added.
* http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html: Added.
* http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html.json: Added.
* http/tests/contentextensions/single-css-display-none-expected.txt: Added.
* http/tests/contentextensions/single-css-display-none.html: Added.
* http/tests/contentextensions/single-css-display-none.html.json: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsContentExtensionCompilercpp">trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebCoreContentExtensionscpp">trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestshttptestscontentextensionscssdisplaynoneafterignorepreviousrulesexpectedtxt">trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestscontentextensionscssdisplaynoneafterignorepreviousruleshtml">trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html</a></li>
<li><a href="#trunkLayoutTestshttptestscontentextensionscssdisplaynoneafterignorepreviousruleshtmljson">trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html.json</a></li>
<li><a href="#trunkLayoutTestshttptestscontentextensionssinglecssdisplaynoneexpectedtxt">trunk/LayoutTests/http/tests/contentextensions/single-css-display-none-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestscontentextensionssinglecssdisplaynonehtml">trunk/LayoutTests/http/tests/contentextensions/single-css-display-none.html</a></li>
<li><a href="#trunkLayoutTestshttptestscontentextensionssinglecssdisplaynonehtmljson">trunk/LayoutTests/http/tests/contentextensions/single-css-display-none.html.json</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (186549 => 186550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-07-09 00:41:49 UTC (rev 186549)
+++ trunk/LayoutTests/ChangeLog        2015-07-09 00:52:53 UTC (rev 186550)
</span><span class="lines">@@ -1,3 +1,19 @@
</span><ins>+2015-07-08  Benjamin Poulain  &lt;bpoulain@apple.com&gt;
+
+        [Content Extensions] Fuse undistinguishable actions as much as possible
+        https://bugs.webkit.org/show_bug.cgi?id=146762
+
+        Reviewed by Alex Christensen.
+
+        Make sure the last CSS rule is not ignored.
+
+        * http/tests/contentextensions/css-display-none-after-ignore-previous-rules-expected.txt: Added.
+        * http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html: Added.
+        * http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html.json: Added.
+        * http/tests/contentextensions/single-css-display-none-expected.txt: Added.
+        * http/tests/contentextensions/single-css-display-none.html: Added.
+        * http/tests/contentextensions/single-css-display-none.html.json: Added.
+
</ins><span class="cx"> 2015-07-08  Wenson Hsieh  &lt;whsieh@berkeley.edu&gt;
</span><span class="cx"> 
</span><span class="cx">         Fix asynchronous function calls for scroll snap animation tests
</span></span></pre></div>
<a id="trunkLayoutTestshttptestscontentextensionscssdisplaynoneafterignorepreviousrulesexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules-expected.txt (0 => 186550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules-expected.txt                                (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules-expected.txt        2015-07-09 00:52:53 UTC (rev 186550)
</span><span class="lines">@@ -0,0 +1,5 @@
</span><ins>+This text should be visible.
+
+This text should be visible.
+
+This text should be visible.
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestscontentextensionscssdisplaynoneafterignorepreviousruleshtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html (0 => 186550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html                                (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html        2015-07-09 00:52:53 UTC (rev 186550)
</span><span class="lines">@@ -0,0 +1,15 @@
</span><ins>+&lt;head&gt;
+&lt;meta charset=&quot;UTF-8&quot;&gt;&lt;/meta&gt;
+&lt;script&gt;
+    testRunner.dumpAsText();
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;p id=&quot;hidden&quot;&gt;This text should be visible.&lt;/p&gt;
+&lt;p id=&quot;hidden1&quot;&gt;This text should be visible.&lt;/p&gt;
+&lt;p id=&quot;hidden2&quot;&gt;This text should be visible.&lt;/p&gt;
+&lt;p class=&quot;hidden3&quot;&gt;This text should not be visible!!!&lt;/p&gt;
+&lt;p class=&quot;hidden4&quot;&gt;This text should not be visible!!!&lt;/p&gt;
+&lt;p class=&quot;hidden3 hidden1&quot;&gt;This text should not be visible!!!&lt;/p&gt;
+&lt;p class=&quot;hidden2 hidden4&quot;&gt;This text should not be visible!!!&lt;/p&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestscontentextensionscssdisplaynoneafterignorepreviousruleshtmljson"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html.json (0 => 186550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html.json                                (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html.json        2015-07-09 00:52:53 UTC (rev 186550)
</span><span class="lines">@@ -0,0 +1,46 @@
</span><ins>+[
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;css-display-none&quot;,
+            &quot;selector&quot;: &quot;.hidden1&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;css-display-none-after-ignore-&quot;
+        }
+    },
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;css-display-none&quot;,
+            &quot;selector&quot;: &quot;.hidden2&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;after-ignore-previous-rules.html&quot;
+        }
+    },
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;ignore-previous-rules&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;css-display-none-after-ignore-previous-rules\\.html$&quot;
+        }
+    },
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;css-display-none&quot;,
+            &quot;selector&quot;: &quot;.hidden3&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;css-display-none-after-ignore-&quot;
+        }
+    },
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;css-display-none&quot;,
+            &quot;selector&quot;: &quot;.hidden4&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;css-display-none-after-ignore-previous-rules\\.html$&quot;
+        }
+    }
+]
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestscontentextensionssinglecssdisplaynoneexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/contentextensions/single-css-display-none-expected.txt (0 => 186550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/contentextensions/single-css-display-none-expected.txt                                (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/single-css-display-none-expected.txt        2015-07-09 00:52:53 UTC (rev 186550)
</span><span class="lines">@@ -0,0 +1,5 @@
</span><ins>+This text should be visible.
+
+This text should be visible.
+
+This text should be visible.
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestscontentextensionssinglecssdisplaynonehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/contentextensions/single-css-display-none.html (0 => 186550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/contentextensions/single-css-display-none.html                                (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/single-css-display-none.html        2015-07-09 00:52:53 UTC (rev 186550)
</span><span class="lines">@@ -0,0 +1,12 @@
</span><ins>+&lt;head&gt;
+&lt;meta charset=&quot;UTF-8&quot;&gt;&lt;/meta&gt;
+&lt;script&gt;
+    testRunner.dumpAsText();
+&lt;/script&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;p id=&quot;hidden&quot;&gt;This text should be visible.&lt;/p&gt;
+&lt;p class=&quot;,hidden&quot;&gt;This text should be visible.&lt;/p&gt;
+&lt;p class=&quot;hidden&quot;&gt;This text should not be visible once the particular css selector is applied.&lt;/p&gt;
+&lt;p class=&quot;not_hidden&quot;&gt;This text should be visible.&lt;/p&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestscontentextensionssinglecssdisplaynonehtmljson"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/contentextensions/single-css-display-none.html.json (0 => 186550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/contentextensions/single-css-display-none.html.json                                (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/single-css-display-none.html.json        2015-07-09 00:52:53 UTC (rev 186550)
</span><span class="lines">@@ -0,0 +1,11 @@
</span><ins>+[
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;css-display-none&quot;,
+            &quot;selector&quot;: &quot;.hidden&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;single-css-display-none.html$&quot;
+        }
+    }
+]
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (186549 => 186550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-07-09 00:41:49 UTC (rev 186549)
+++ trunk/Source/WebCore/ChangeLog        2015-07-09 00:52:53 UTC (rev 186550)
</span><span class="lines">@@ -1,3 +1,31 @@
</span><ins>+2015-07-08  Benjamin Poulain  &lt;bpoulain@apple.com&gt;
+
+        [Content Extensions] Fuse undistinguishable actions as much as possible
+        https://bugs.webkit.org/show_bug.cgi?id=146762
+
+        Reviewed by Alex Christensen.
+
+        Our previous code that fused actions was based on test lists that were
+        grouping similar actions in the input.
+
+        The input we get from developers is more distributed. It is very common to
+        have trigger flags all over the place, and &quot;css-display-none&quot; mixed with &quot;block&quot;.
+
+        This patch refines the merging code to merge those cases as much as possible.
+
+        The size taken by the actions is negligible, but having different actions make
+        nodes unkillable by the Minimizer. By merging many more actions, the minimizer
+        no longer see those subtrees as distinguishable and can do a better job.
+
+        On a large test list, this cuts the bytecode size by 2 megabytes.
+
+        Tests: http/tests/contentextensions/css-display-none-after-ignore-previous-rules.html
+               http/tests/contentextensions/single-css-display-none.html
+
+        * contentextensions/ContentExtensionCompiler.cpp:
+        (WebCore::ContentExtensions::resolvePendingDisplayNoneActions):
+        (WebCore::ContentExtensions::serializeActions):
+
</ins><span class="cx"> 2015-07-08  Matthew Daiter  &lt;mdaiter@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Cleared contentMIMETypes for MediaStreams
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsContentExtensionCompilercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp (186549 => 186550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp        2015-07-09 00:41:49 UTC (rev 186549)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp        2015-07-09 00:52:53 UTC (rev 186550)
</span><span class="lines">@@ -69,56 +69,137 @@
</span><span class="cx">             actions.append(selector[i]);
</span><span class="cx">     }
</span><span class="cx"> }
</span><del>-    
</del><ins>+
+struct PendingDisplayNoneActions {
+    Vector&lt;String&gt; selectors;
+    Vector&lt;unsigned&gt; clientLocations;
+};
+typedef HashMap&lt;uint32_t, PendingDisplayNoneActions, DefaultHash&lt;uint32_t&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;uint32_t&gt;&gt; PendingDisplayNoneActionsMap;
+
+static void resolvePendingDisplayNoneActions(Vector&lt;SerializedActionByte&gt;&amp; actions, Vector&lt;unsigned&gt;&amp; actionLocations, PendingDisplayNoneActionsMap&amp; pendingDisplayNoneActionsMap)
+{
+    for (auto&amp; slot : pendingDisplayNoneActionsMap) {
+        PendingDisplayNoneActions&amp; pendingActions = slot.value;
+
+        StringBuilder combinedSelectors;
+        for (unsigned i = 0; i &lt; pendingActions.selectors.size(); ++i) {
+            if (i)
+                combinedSelectors.append(',');
+            combinedSelectors.append(pendingActions.selectors[i]);
+        }
+
+        unsigned actionLocation = actions.size();
+        serializeSelector(actions, combinedSelectors.toString());
+        for (unsigned clientLocation : pendingActions.clientLocations)
+            actionLocations[clientLocation] = actionLocation;
+    }
+    pendingDisplayNoneActionsMap.clear();
+}
+
</ins><span class="cx"> static Vector&lt;unsigned&gt; serializeActions(const Vector&lt;ContentExtensionRule&gt;&amp; ruleList, Vector&lt;SerializedActionByte&gt;&amp; actions)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!actions.size());
</span><del>-    
</del><ins>+
</ins><span class="cx">     Vector&lt;unsigned&gt; actionLocations;
</span><del>-        
</del><ins>+
+    // Block and BlockCookies do not need to be distinguishable. The order in which they are executed it irrelevant
+    // since Block is a strict superset of BlockCookies.
+    // Similarily, Block is a superset of CSSDisplayNone, and BlockCookies is independent from CSSDisplayNone.
+    //
+    // The only distinguisher is &quot;IgnorePreviousRules&quot;.
+    //
+    // The trigger's Flags do not need to be distinguishable either. The way we use them is filtering the actions
+    // based on the flag *after* matching.
+    //
+    // To reduce the number of unique actions, we keep track of the various action, indexed by their flag.
+    // We only need to create new ones when encountering a IgnorePreviousRules.
+    HashMap&lt;uint32_t, uint32_t, DefaultHash&lt;uint32_t&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;uint32_t&gt;&gt; blockActionsMap;
+    HashMap&lt;uint32_t, uint32_t, DefaultHash&lt;uint32_t&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;uint32_t&gt;&gt; blockCookiesActionsMap;
+    PendingDisplayNoneActionsMap cssDisplayNoneActionsMap;
+    HashMap&lt;uint32_t, uint32_t, DefaultHash&lt;uint32_t&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;uint32_t&gt;&gt; ignorePreviousRuleActionsMap;
+
</ins><span class="cx">     for (unsigned ruleIndex = 0; ruleIndex &lt; ruleList.size(); ++ruleIndex) {
</span><span class="cx">         const ContentExtensionRule&amp; rule = ruleList[ruleIndex];
</span><del>-        
-        // Consolidate css selectors with identical triggers.
-        if (rule.action().type() == ActionType::CSSDisplayNoneSelector) {
-            StringBuilder selector;
-            selector.append(rule.action().stringArgument());
</del><ins>+        ActionType actionType = rule.action().type();
+
+        RELEASE_ASSERT(actionType == ActionType::CSSDisplayNoneSelector
+            || actionType == ActionType::BlockLoad
+            || actionType == ActionType::BlockCookies
+            || actionType == ActionType::IgnorePreviousRules);
+
+        if (actionType == ActionType::IgnorePreviousRules) {
+            resolvePendingDisplayNoneActions(actions, actionLocations, cssDisplayNoneActionsMap);
+
+            blockActionsMap.clear();
+            blockCookiesActionsMap.clear();
+            cssDisplayNoneActionsMap.clear();
+        } else
+            ignorePreviousRuleActionsMap.clear();
+
+        // Anything with domain is just pushed.
+        // We could try to merge domains but that case is not common in practice.
+        if (!rule.trigger().domains.isEmpty()) {
</ins><span class="cx">             actionLocations.append(actions.size());
</span><del>-            for (unsigned i = ruleIndex + 1; i &lt; ruleList.size(); i++) {
-                if (rule.trigger() == ruleList[i].trigger() &amp;&amp; ruleList[i].action().type() == ActionType::CSSDisplayNoneSelector) {
-                    actionLocations.append(actions.size());
-                    ruleIndex++;
-                    selector.append(',');
-                    selector.append(ruleList[i].action().stringArgument());
-                } else
-                    break;
-            }
-            serializeSelector(actions, selector.toString());
</del><ins>+
+            if (actionType == ActionType::CSSDisplayNoneSelector)
+                serializeSelector(actions, rule.action().stringArgument());
+            else
+                actions.append(static_cast&lt;SerializedActionByte&gt;(actionType));
</ins><span class="cx">             continue;
</span><span class="cx">         }
</span><del>-        
-        // 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 &amp;&amp; rule.action() == ruleList[ruleIndex - 1].action() &amp;&amp; rule.trigger().domains.isEmpty()) {
-            actionLocations.append(actionLocations[ruleIndex - 1]);
-            continue;
-        }
</del><span class="cx"> 
</span><del>-        actionLocations.append(actions.size());
-        switch (rule.action().type()) {
-        case ActionType::CSSDisplayNoneSelector:
</del><ins>+        ResourceFlags flags = rule.trigger().flags;
+        unsigned actionLocation = std::numeric_limits&lt;unsigned&gt;::max();
+
+        switch (actionType) {
</ins><span class="cx">         case ActionType::CSSDisplayNoneStyleSheet:
</span><span class="cx">         case ActionType::InvalidAction:
</span><span class="cx">             RELEASE_ASSERT_NOT_REACHED();
</span><span class="cx"> 
</span><del>-        case ActionType::BlockLoad:
-        case ActionType::BlockCookies:
-        case ActionType::IgnorePreviousRules:
-            actions.append(static_cast&lt;SerializedActionByte&gt;(rule.action().type()));
</del><ins>+        case ActionType::IgnorePreviousRules: {
+            const auto existingAction = ignorePreviousRuleActionsMap.find(flags);
+            if (existingAction == ignorePreviousRuleActionsMap.end()) {
+                actionLocation = actions.size();
+                actions.append(static_cast&lt;SerializedActionByte&gt;(rule.action().type()));
+                ignorePreviousRuleActionsMap.set(flags, actionLocation);
+            } else
+                actionLocation = existingAction-&gt;value;
</ins><span class="cx">             break;
</span><span class="cx">         }
</span><ins>+        case ActionType::CSSDisplayNoneSelector: {
+            const auto addResult = cssDisplayNoneActionsMap.add(flags, PendingDisplayNoneActions());
+            PendingDisplayNoneActions&amp; pendingDisplayNoneActions = addResult.iterator-&gt;value;
+            pendingDisplayNoneActions.selectors.append(rule.action().stringArgument());
+            pendingDisplayNoneActions.clientLocations.append(actionLocations.size());
+
+            actionLocation = std::numeric_limits&lt;unsigned&gt;::max();
+            break;
+        }
+        case ActionType::BlockLoad: {
+            const auto existingAction = blockActionsMap.find(flags);
+            if (existingAction == blockActionsMap.end()) {
+                actionLocation = actions.size();
+                actions.append(static_cast&lt;SerializedActionByte&gt;(rule.action().type()));
+                blockActionsMap.set(flags, actionLocation);
+            } else
+                actionLocation = existingAction-&gt;value;
+            break;
+        }
+        case ActionType::BlockCookies: {
+            const auto existingAction = blockCookiesActionsMap.find(flags);
+            if (existingAction == blockCookiesActionsMap.end()) {
+                actionLocation = actions.size();
+                actions.append(static_cast&lt;SerializedActionByte&gt;(rule.action().type()));
+                blockCookiesActionsMap.set(flags, actionLocation);
+            } else
+                actionLocation = existingAction-&gt;value;
+            break;
+        }
+        }
+
+        actionLocations.append(actionLocation);
</ins><span class="cx">     }
</span><ins>+    resolvePendingDisplayNoneActions(actions, actionLocations, cssDisplayNoneActionsMap);
</ins><span class="cx">     return actionLocations;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (186549 => 186550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2015-07-09 00:41:49 UTC (rev 186549)
+++ trunk/Tools/ChangeLog        2015-07-09 00:52:53 UTC (rev 186550)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+2015-07-08  Benjamin Poulain  &lt;bpoulain@apple.com&gt;
+
+        [Content Extensions] Fuse undistinguishable actions as much as possible
+        https://bugs.webkit.org/show_bug.cgi?id=146762
+
+        Reviewed by Alex Christensen.
+
+        * TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
+        Test that combinations and flags still work as expected.
+
</ins><span class="cx"> 2015-07-08  Carlos Alberto Lopez Perez  &lt;clopez@igalia.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [GTK] [Wayland] Allow building and testing the Wayland target with the default JHBuild moduleset.
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebCoreContentExtensionscpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp (186549 => 186550)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp        2015-07-09 00:41:49 UTC (rev 186549)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp        2015-07-09 00:52:53 UTC (rev 186550)
</span><span class="lines">@@ -576,7 +576,53 @@
</span><span class="cx"> 
</span><span class="cx">     // FIXME: Add and test domain-specific popup-only blocking (with layout tests).
</span><span class="cx"> }
</span><del>-    
</del><ins>+
+TEST_F(ContentExtensionTest, DomainTriggersAlongMergedActions)
+{
+    auto backend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block-cookies\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;test\\\\.html\&quot;}},&quot;
+        &quot;{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;test\\\\.html\&quot;, \&quot;if-domain\&quot;:[\&quot;webkit.org\&quot;]}},&quot;
+        &quot;{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;css-display-none\&quot;, \&quot;selector\&quot;: \&quot;*\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;trigger-on-scripts\\\\.html\&quot;,\&quot;resource-type\&quot;:[\&quot;script\&quot;]}},&quot;
+        &quot;{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;ignore-previous-rules\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;ignore-previous\&quot;,\&quot;resource-type\&quot;:[\&quot;image\&quot;]}},&quot;
+        &quot;{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block-cookies\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;except-this\&quot;}}]&quot;);
+
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/test.htm&quot;), { });
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/test.html&quot;), { ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies });
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/test.html&quot;), { ContentExtensions::ActionType::BlockCookies });
+
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/trigger-on-scripts.html&quot;), { });
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/trigger-on-scripts.html&quot;), { });
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/trigger-on-scripts.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/trigger-on-scripts.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/trigger-on-scripts.html.test.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies });
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/trigger-on-scripts.html.test.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies });
+
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/ignore-previous-trigger-on-scripts.html&quot;), { });
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/ignore-previous-trigger-on-scripts.html&quot;), { });
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/ignore-previous-trigger-on-scripts.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/ignore-previous-trigger-on-scripts.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/ignore-previous-trigger-on-scripts.html.test.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies });
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/ignore-previous-trigger-on-scripts.html.test.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies });
+
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/ignore-previous-trigger-on-scripts.html&quot;, ResourceType::Image), { }, true);
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/ignore-previous-trigger-on-scripts.html&quot;, ResourceType::Image), { }, true);
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/ignore-previous-trigger-on-scripts.html.test.html&quot;, ResourceType::Image), { }, true);
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/ignore-previous-trigger-on-scripts.html.test.html&quot;, ResourceType::Image), { }, true);
+
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html&quot;), { ContentExtensions::ActionType::BlockCookies });
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html&quot;), { ContentExtensions::ActionType::BlockCookies });
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html&quot;), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockCookies });
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html&quot;), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies });
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html&quot;, ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true);
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html&quot;, ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true);
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html&quot;, ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true);
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html&quot;, ResourceType::Image), { ContentExtensions::ActionType::BlockCookies }, true);
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(backend, mainDocumentRequest(&quot;http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies });
+    testRequest(backend, mainDocumentRequest(&quot;http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html&quot;, ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies });
+
+}
+
</ins><span class="cx"> TEST_F(ContentExtensionTest, MultipleExtensions)
</span><span class="cx"> {
</span><span class="cx">     auto extension1 = InMemoryCompiledContentExtension::createFromFilter(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;block_load\&quot;}}]&quot;);
</span></span></pre>
</div>
</div>

</body>
</html>