<!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>[185230] 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/185230">185230</a></dd>
<dt>Author</dt> <dd>benjamin@webkit.org</dd>
<dt>Date</dt> <dd>2015-06-04 18:01:59 -0700 (Thu, 04 Jun 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Combine tiny DFAs into slightly larger ones
https://bugs.webkit.org/show_bug.cgi?id=145572

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

Source/WebCore:

This patch changes the ContentExtensions compiler to combine tiny DFA
until they reach a minimum size.

The main tool introduced here is DFAMerger. It combines 2 DFAs into
a single DFA that represent the union of the two machines.
That is done by a simple subset construction on the &quot;name&quot; of the nodes
in each DFAs.

Since we only merge 2 machines, and they are both deterministic, we know that
we can only be in one state of each machine, or a state in one machine without
equivalent in the other machine.
We exploit that to identify the mapping between nodes. To identify a node in
the new machine from nodes in the original machines, we just concatenate the node
IDs into a single 64 bits number. If there is no node in one of the machine, we
use a special tag.

The current algorithm does not have any subgraph pruning, machines grow very very
quickly. Because of that, we only merge very small DFAs at the moment.

Test: http/tests/contentextensions/filters-with-quantifiers-combined.html

* WebCore.xcodeproj/project.pbxproj:
* contentextensions/ContentExtensionCompiler.cpp:
(WebCore::ContentExtensions::compileRuleList):
* contentextensions/ContentExtensionsDebugging.h:
* contentextensions/DFA.cpp:
(WebCore::ContentExtensions::DFA::graphSize):
(WebCore::ContentExtensions::DFA::debugPrintDot):
* contentextensions/DFA.h:
* contentextensions/DFACombiner.cpp: Added.
(WebCore::ContentExtensions::DFAMerger::DFAMerger):
(WebCore::ContentExtensions::DFAMerger::merge):
(WebCore::ContentExtensions::DFAMerger::signatureForIndices):
(WebCore::ContentExtensions::DFAMerger::extractIndexA):
(WebCore::ContentExtensions::DFAMerger::extractIndexB):
(WebCore::ContentExtensions::DFAMerger::getOrCreateCombinedNode):
(WebCore::ContentExtensions::DFAMerger::setHalfSignature):
(WebCore::ContentExtensions::DFAMerger::populateTransitions):
(WebCore::ContentExtensions::DFAMerger::populateFromFallbackTransitions):
(WebCore::ContentExtensions::DFAMerger::createTransitions):
(WebCore::ContentExtensions::DFAMerger::createFallbackTransitionIfNeeded):
(WebCore::ContentExtensions::DFACombiner::combineDFAs):
* contentextensions/DFACombiner.h: Copied from Source/WebCore/contentextensions/DFA.h.
(WebCore::ContentExtensions::DFACombiner::addDFA):

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebCore/DFACombiner.cpp: Added.
(TestWebKitAPI::DFACombinerTest::SetUp):
(TestWebKitAPI::combine):
(TestWebKitAPI::TEST_F):
* TestWebKitAPI/Tests/WebCore/DFAHelpers.h: Copied from Source/WebCore/contentextensions/DFA.h.
(TestWebKitAPI::countLiveNodes):
(TestWebKitAPI::createNFAs):
(TestWebKitAPI::buildDFAFromPatterns):
* TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp:
(TestWebKitAPI::countLiveNodes): Deleted.
(TestWebKitAPI::createNFAs): Deleted.
(TestWebKitAPI::buildDFAFromPatterns): Deleted.

LayoutTests:

* http/tests/contentextensions/filters-with-quantifiers-combined-expected.txt: Added.
* http/tests/contentextensions/filters-with-quantifiers-combined.html: Added.
* http/tests/contentextensions/filters-with-quantifiers-combined.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="#trunkSourceWebCoreWebCorexcodeprojprojectpbxproj">trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsContentExtensionCompilercpp">trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsDFAcpp">trunk/Source/WebCore/contentextensions/DFA.cpp</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsDFAh">trunk/Source/WebCore/contentextensions/DFA.h</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsTestWebKitAPITestWebKitAPIxcodeprojprojectpbxproj">trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebCoreDFAMinimizercpp">trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestshttptestscontentextensionsfilterswithquantifierscombinedexpectedtxt">trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined-expected.txt</a></li>
<li><a href="#trunkLayoutTestshttptestscontentextensionsfilterswithquantifierscombinedhtml">trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined.html</a></li>
<li><a href="#trunkLayoutTestshttptestscontentextensionsfilterswithquantifierscombinedhtmljson">trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined.html.json</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsDFACombinercpp">trunk/Source/WebCore/contentextensions/DFACombiner.cpp</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsDFACombinerh">trunk/Source/WebCore/contentextensions/DFACombiner.h</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebCoreDFACombinercpp">trunk/Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp</a></li>
<li><a href="#trunkToolsTestWebKitAPITestsWebCoreDFAHelpersh">trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (185229 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-06-05 00:31:07 UTC (rev 185229)
+++ trunk/LayoutTests/ChangeLog        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2015-06-04  Benjamin Poulain  &lt;bpoulain@apple.com&gt;
+
+        Combine tiny DFAs into slightly larger ones
+        https://bugs.webkit.org/show_bug.cgi?id=145572
+
+        Reviewed by Alex Christensen.
+
+        * http/tests/contentextensions/filters-with-quantifiers-combined-expected.txt: Added.
+        * http/tests/contentextensions/filters-with-quantifiers-combined.html: Added.
+        * http/tests/contentextensions/filters-with-quantifiers-combined.html.json: Added.
+
</ins><span class="cx"> 2015-06-04  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Skip failed layout tests following &lt;http://trac.webkit.org/changeset/185207&gt;
</span></span></pre></div>
<a id="trunkLayoutTestshttptestscontentextensionsfilterswithquantifierscombinedexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined-expected.txt (0 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined-expected.txt                                (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined-expected.txt        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -0,0 +1,53 @@
</span><ins>+layer at (0,0) size 785x952
+  RenderView at (0,0) size 785x600
+layer at (0,0) size 785x952
+  RenderBlock {HTML} at (0,0) size 785x952
+    RenderBody {BODY} at (8,8) size 769x936
+      RenderText {#text} at (0,0) size 200x18
+        text run at (0,0) width 200: &quot;The images below should load.&quot;
+      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
+      RenderImage {IMG} at (0,218) size 100x100
+      RenderBR {BR} at (100,318) size 0x0
+      RenderImage {IMG} at (0,318) size 100x100
+      RenderBR {BR} at (100,418) size 0x0
+      RenderImage {IMG} at (0,418) size 100x100
+      RenderBR {BR} at (100,518) size 0x0
+      RenderImage {IMG} at (0,518) size 100x100
+      RenderBR {BR} at (100,618) size 0x0
+      RenderImage {IMG} at (0,618) size 100x100
+      RenderBR {BR} at (100,718) size 0x0
+      RenderImage {IMG} at (0,718) size 100x100
+      RenderBR {BR} at (100,818) size 0x0
+      RenderImage {IMG} at (0,818) size 100x100
+      RenderBR {BR} at (100,918) size 0x0
+      RenderText {#text} at (0,918) size 242x18
+        text run at (0,918) width 242: &quot;The images below should be blocked.&quot;
+      RenderBR {BR} at (241,932) size 1x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
+      RenderImage {IMG} at (0,936) size 0x0
+      RenderBR {BR} at (0,936) size 0x0
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestscontentextensionsfilterswithquantifierscombinedhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined.html (0 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined.html                                (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined.html        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -0,0 +1,26 @@
</span><ins>+&lt;body&gt;
+The images below should load.&lt;br&gt;
+&lt;img src=&quot;http://127.0.0.1:8000/resources/square100.png&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://127.0.0.1:8000/resources/square100.png?abcabcdwhitelist&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://127.0.0.1:8000/resources/square100.png?abccccccc&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://127.0.0.1:8000/resources/square100.png?defffffff&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://127.0.0.1:8000/resources/square100.png?jkljkljkl&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://127.0.0.1:8000/resources/square100.png?pqrabcdef&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://127.0.0.1:8000/resources/square100.png?wxybc&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://127.0.0.1:8000/resources/square100.png?acbxyxyxyxyxy&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://127.0.0.1:8000/resources/square100.png?abcdefjklpqrwxyacdacbbcghmosvabacbxy&quot;&gt;&lt;br&gt;
+
+The images below should be blocked.&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?abcabc&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?abccabc&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?defghi&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?defgghi&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?jklmno&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?jkljklmno&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?pqrstv&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?pqrabcstv&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?wxyabc&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?wxydefabc&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?acbwxy&quot;&gt;&lt;br&gt;
+&lt;img src=&quot;http://localhost:8000/resources/square100.png?abcacbdefabcdjklwhitelistwxy&quot;&gt;&lt;br&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestshttptestscontentextensionsfilterswithquantifierscombinedhtmljson"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined.html.json (0 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined.html.json                                (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/filters-with-quantifiers-combined.html.json        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -0,0 +1,58 @@
</span><ins>+[
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;block&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;abc.*abc&quot;
+        }
+    },
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;block&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;def.*ghi&quot;
+        }
+    },
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;block&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;jkl.*mno&quot;
+        }
+    },
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;block&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;pqr.*stv&quot;
+        }
+    },
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;block&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;wxy.*abc&quot;
+        }
+    },
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;ignore-previous-rules&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;abcd.*whitelist&quot;
+        }
+    },
+    {
+        &quot;action&quot;: {
+            &quot;type&quot;: &quot;block&quot;
+        },
+        &quot;trigger&quot;: {
+            &quot;url-filter&quot;: &quot;acb.*wxy&quot;
+        }
+    }
+]
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (185229 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-06-05 00:31:07 UTC (rev 185229)
+++ trunk/Source/WebCore/ChangeLog        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -1,3 +1,55 @@
</span><ins>+2015-06-04  Benjamin Poulain  &lt;bpoulain@apple.com&gt;
+
+        Combine tiny DFAs into slightly larger ones
+        https://bugs.webkit.org/show_bug.cgi?id=145572
+
+        Reviewed by Alex Christensen.
+
+        This patch changes the ContentExtensions compiler to combine tiny DFA
+        until they reach a minimum size.
+
+        The main tool introduced here is DFAMerger. It combines 2 DFAs into
+        a single DFA that represent the union of the two machines.
+        That is done by a simple subset construction on the &quot;name&quot; of the nodes
+        in each DFAs.
+
+        Since we only merge 2 machines, and they are both deterministic, we know that
+        we can only be in one state of each machine, or a state in one machine without
+        equivalent in the other machine.
+        We exploit that to identify the mapping between nodes. To identify a node in
+        the new machine from nodes in the original machines, we just concatenate the node
+        IDs into a single 64 bits number. If there is no node in one of the machine, we
+        use a special tag.
+
+        The current algorithm does not have any subgraph pruning, machines grow very very
+        quickly. Because of that, we only merge very small DFAs at the moment.
+
+        Test: http/tests/contentextensions/filters-with-quantifiers-combined.html
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * contentextensions/ContentExtensionCompiler.cpp:
+        (WebCore::ContentExtensions::compileRuleList):
+        * contentextensions/ContentExtensionsDebugging.h:
+        * contentextensions/DFA.cpp:
+        (WebCore::ContentExtensions::DFA::graphSize):
+        (WebCore::ContentExtensions::DFA::debugPrintDot):
+        * contentextensions/DFA.h:
+        * contentextensions/DFACombiner.cpp: Added.
+        (WebCore::ContentExtensions::DFAMerger::DFAMerger):
+        (WebCore::ContentExtensions::DFAMerger::merge):
+        (WebCore::ContentExtensions::DFAMerger::signatureForIndices):
+        (WebCore::ContentExtensions::DFAMerger::extractIndexA):
+        (WebCore::ContentExtensions::DFAMerger::extractIndexB):
+        (WebCore::ContentExtensions::DFAMerger::getOrCreateCombinedNode):
+        (WebCore::ContentExtensions::DFAMerger::setHalfSignature):
+        (WebCore::ContentExtensions::DFAMerger::populateTransitions):
+        (WebCore::ContentExtensions::DFAMerger::populateFromFallbackTransitions):
+        (WebCore::ContentExtensions::DFAMerger::createTransitions):
+        (WebCore::ContentExtensions::DFAMerger::createFallbackTransitionIfNeeded):
+        (WebCore::ContentExtensions::DFACombiner::combineDFAs):
+        * contentextensions/DFACombiner.h: Copied from Source/WebCore/contentextensions/DFA.h.
+        (WebCore::ContentExtensions::DFACombiner::addDFA):
+
</ins><span class="cx"> 2015-06-04  Matt Rajca  &lt;mrajca@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Rename MediaSessionManager to PlatformMediaSessionManager for consistency with PlatformMediaSession.
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (185229 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2015-06-05 00:31:07 UTC (rev 185229)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -1017,6 +1017,8 @@
</span><span class="cx">                 269397261A4A5FBD00E8349D /* NFA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 269397251A4A5FBD00E8349D /* NFA.cpp */; };
</span><span class="cx">                 26A517FD1AB92238006335DF /* DFAMinimizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A517FB1AB92238006335DF /* DFAMinimizer.cpp */; };
</span><span class="cx">                 26A517FE1AB92238006335DF /* DFAMinimizer.h in Headers */ = {isa = PBXBuildFile; fileRef = 26A517FC1AB92238006335DF /* DFAMinimizer.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><ins>+                26A807841B18F97700E219BE /* DFACombiner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26A807821B18F97700E219BE /* DFACombiner.cpp */; };
+                26A807851B18F97700E219BE /* DFACombiner.h in Headers */ = {isa = PBXBuildFile; fileRef = 26A807831B18F97700E219BE /* DFACombiner.h */; settings = {ATTRIBUTES = (Private, ); }; };
</ins><span class="cx">                 26AA0F9E18D2A18B00419381 /* SelectorPseudoElementTypeMap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26AA0F9D18D2A18B00419381 /* SelectorPseudoElementTypeMap.cpp */; };
</span><span class="cx">                 26B9998F1803AE7200D01121 /* RegisterAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 26B9998E1803AE7200D01121 /* RegisterAllocator.h */; };
</span><span class="cx">                 26B999911803B3C900D01121 /* StackAllocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 26B999901803B3C900D01121 /* StackAllocator.h */; };
</span><span class="lines">@@ -8141,6 +8143,8 @@
</span><span class="cx">                 269397251A4A5FBD00E8349D /* NFA.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NFA.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 26A517FB1AB92238006335DF /* DFAMinimizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DFAMinimizer.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 26A517FC1AB92238006335DF /* DFAMinimizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DFAMinimizer.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                26A807821B18F97700E219BE /* DFACombiner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DFACombiner.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
+                26A807831B18F97700E219BE /* DFACombiner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DFACombiner.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 26AA0F9918D2973D00419381 /* makeSelectorPseudoElementsMap.py */ = {isa = PBXFileReference; lastKnownFileType = text.script.python; path = makeSelectorPseudoElementsMap.py; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 26AA0F9A18D2973D00419381 /* SelectorPseudoElementTypeMap.in */ = {isa = PBXFileReference; lastKnownFileType = text; path = SelectorPseudoElementTypeMap.in; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 26AA0F9D18D2A18B00419381 /* SelectorPseudoElementTypeMap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SelectorPseudoElementTypeMap.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -15649,6 +15653,8 @@
</span><span class="cx">                                 5C39305F1AA0F6A90029C816 /* DFABytecodeCompiler.h */,
</span><span class="cx">                                 5C3930601AA0F6A90029C816 /* DFABytecodeInterpreter.cpp */,
</span><span class="cx">                                 5C3930611AA0F6A90029C816 /* DFABytecodeInterpreter.h */,
</span><ins>+                                26A807821B18F97700E219BE /* DFACombiner.cpp */,
+                                26A807831B18F97700E219BE /* DFACombiner.h */,
</ins><span class="cx">                                 26A517FB1AB92238006335DF /* DFAMinimizer.cpp */,
</span><span class="cx">                                 26A517FC1AB92238006335DF /* DFAMinimizer.h */,
</span><span class="cx">                                 267725F91A5B3AD9003C24DD /* DFANode.h */,
</span><span class="lines">@@ -24162,6 +24168,7 @@
</span><span class="cx">                                 F98FFF4511A2676200F548E8 /* CSSOMUtils.h in Headers */,
</span><span class="cx">                                 A80E6D000A1989CA007FB8C5 /* CSSPageRule.h in Headers */,
</span><span class="cx">                                 BC772B3E0C4EA91E0083285F /* CSSParser.h in Headers */,
</span><ins>+                                26A807851B18F97700E219BE /* DFACombiner.h in Headers */,
</ins><span class="cx">                                 FB78AD2E151BF5E600FE54D3 /* CSSParserMode.h in Headers */,
</span><span class="cx">                                 BC02A4B70E0997B9004B6D2B /* CSSParserValues.h in Headers */,
</span><span class="cx">                                 977B3863122883E900B81FF8 /* CSSPreloadScanner.h in Headers */,
</span><span class="lines">@@ -28283,6 +28290,7 @@
</span><span class="cx">                                 2EF1BFEA121C9F4200C27627 /* FileStream.cpp in Sources */,
</span><span class="cx">                                 C57FEDE11212EE9C0097BE65 /* FileSystem.cpp in Sources */,
</span><span class="cx">                                 5160306C0CC4362300C8AC25 /* FileSystemCF.cpp in Sources */,
</span><ins>+                                26A807841B18F97700E219BE /* DFACombiner.cpp in Sources */,
</ins><span class="cx">                                 26C17A3F1491D2D400D12BA2 /* FileSystemIOS.mm in Sources */,
</span><span class="cx">                                 514B3F760C722055000530DF /* FileSystemMac.mm in Sources */,
</span><span class="cx">                                 5160300B0CC4251200C8AC25 /* FileSystemPOSIX.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsContentExtensionCompilercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp (185229 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp        2015-06-05 00:31:07 UTC (rev 185229)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -36,6 +36,7 @@
</span><span class="cx"> #include &quot;ContentExtensionRule.h&quot;
</span><span class="cx"> #include &quot;ContentExtensionsDebugging.h&quot;
</span><span class="cx"> #include &quot;DFABytecodeCompiler.h&quot;
</span><ins>+#include &quot;DFACombiner.h&quot;
</ins><span class="cx"> #include &quot;NFA.h&quot;
</span><span class="cx"> #include &quot;NFAToDFA.h&quot;
</span><span class="cx"> #include &quot;URLFilterParser.h&quot;
</span><span class="lines">@@ -235,6 +236,10 @@
</span><span class="cx">     LOG_LARGE_STRUCTURES(domainFilters, domainFilters.memoryUsed());
</span><span class="cx"> 
</span><span class="cx"> #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
</span><ins>+    unsigned machinesWithoutDomainsCount = 0;
+    unsigned totalBytecodeSizeForMachinesWithoutDomains = 0;
+    unsigned machinesWithDomainsCount = 0;
+    unsigned totalBytecodeSizeForMachinesWithDomains = 0;
</ins><span class="cx">     double totalNFAToByteCodeBuildTimeStart = monotonicallyIncreasingTime();
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="lines">@@ -243,20 +248,9 @@
</span><span class="cx">     const unsigned maxNFASize = 30000;
</span><span class="cx">     
</span><span class="cx">     bool firstNFAWithoutDomainsSeen = false;
</span><del>-    // FIXME: Combine small NFAs to reduce the number of NFAs.
-    filtersWithoutDomains.processNFAs(maxNFASize, [&amp;](NFA&amp;&amp; nfa) {
-#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
-        dataLogF(&quot;filtersWithoutDomains NFA\n&quot;);
-        nfa.debugPrintDot();
-#endif
</del><span class="cx"> 
</span><del>-        LOG_LARGE_STRUCTURES(nfa, nfa.memoryUsed());
-
-        DFA dfa = NFAToDFA::convert(nfa);
-        LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
-
-        dfa.minimize();
-
</del><ins>+    auto lowerFiltersWithoutDomainsDFAToBytecode = [&amp;](DFA&amp;&amp; dfa)
+    {
</ins><span class="cx"> #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
</span><span class="cx">         dataLogF(&quot;filtersWithoutDomains DFA\n&quot;);
</span><span class="cx">         dfa.debugPrintDot();
</span><span class="lines">@@ -272,10 +266,41 @@
</span><span class="cx">         DFABytecodeCompiler compiler(dfa, bytecode);
</span><span class="cx">         compiler.compile();
</span><span class="cx">         LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
</span><ins>+#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
+        ++machinesWithoutDomainsCount;
+        totalBytecodeSizeForMachinesWithoutDomains += bytecode.size();
+#endif
</ins><span class="cx">         client.writeFiltersWithoutDomainsBytecode(WTF::move(bytecode));
</span><span class="cx"> 
</span><span class="cx">         firstNFAWithoutDomainsSeen = true;
</span><ins>+    };
+
+    const unsigned smallDFASize = 100;
+    DFACombiner smallFiltersWithoutDomainsDFACombiner;
+    filtersWithoutDomains.processNFAs(maxNFASize, [&amp;](NFA&amp;&amp; nfa) {
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+        dataLogF(&quot;filtersWithoutDomains NFA\n&quot;);
+        nfa.debugPrintDot();
+#endif
+
+        LOG_LARGE_STRUCTURES(nfa, nfa.memoryUsed());
+        DFA dfa = NFAToDFA::convert(nfa);
+        LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
+
+        if (dfa.graphSize() &lt; smallDFASize)
+            smallFiltersWithoutDomainsDFACombiner.addDFA(WTF::move(dfa));
+        else {
+            dfa.minimize();
+            lowerFiltersWithoutDomainsDFAToBytecode(WTF::move(dfa));
+        }
</ins><span class="cx">     });
</span><ins>+
+
+    smallFiltersWithoutDomainsDFACombiner.combineDFAs(smallDFASize, [&amp;](DFA&amp;&amp; dfa) {
+        LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
+        lowerFiltersWithoutDomainsDFAToBytecode(WTF::move(dfa));
+    });
+
</ins><span class="cx">     ASSERT(filtersWithoutDomains.isEmpty());
</span><span class="cx"> 
</span><span class="cx">     if (!firstNFAWithoutDomainsSeen) {
</span><span class="lines">@@ -297,6 +322,27 @@
</span><span class="cx">     universalActionsWithoutDomains.clear();
</span><span class="cx">     
</span><span class="cx">     bool firstNFAWithDomainsSeen = false;
</span><ins>+    auto lowerFiltersWithDomainsDFAToBytecode = [&amp;](DFA&amp;&amp; dfa)
+    {
+        if (!firstNFAWithDomainsSeen) {
+            // Put all the universal actions on the first DFA.
+            addUniversalActionsToDFA(dfa, universalActionsWithDomains);
+        }
+
+        Vector&lt;DFABytecode&gt; bytecode;
+        DFABytecodeCompiler compiler(dfa, bytecode);
+        compiler.compile();
+        LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
+#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
+        ++machinesWithDomainsCount;
+        totalBytecodeSizeForMachinesWithDomains += bytecode.size();
+#endif
+        client.writeFiltersWithDomainsBytecode(WTF::move(bytecode));
+
+        firstNFAWithDomainsSeen = true;
+    };
+
+    DFACombiner smallFiltersWithDomainsDFACombiner;
</ins><span class="cx">     filtersWithDomains.processNFAs(maxNFASize, [&amp;](NFA&amp;&amp; nfa) {
</span><span class="cx"> #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
</span><span class="cx">         dataLogF(&quot;filtersWithDomains NFA\n&quot;);
</span><span class="lines">@@ -309,26 +355,20 @@
</span><span class="cx">         dfa.debugPrintDot();
</span><span class="cx"> #endif
</span><span class="cx">         LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
</span><del>-        // Minimizing this DFA would not be effective because all actions with domains are unique.
-#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
-        dataLogF(&quot;filtersWithDomains POST MINIMIZING DFA\n&quot;);
-        dfa.debugPrintDot();
-#endif
</del><ins>+
</ins><span class="cx">         ASSERT_WITH_MESSAGE(!dfa.nodes[dfa.root].hasActions(), &quot;Filters with domains that match everything are not allowed right now.&quot;);
</span><del>-        
-        if (!firstNFAWithDomainsSeen) {
-            // Put all the universal actions on the first DFA.
-            addUniversalActionsToDFA(dfa, universalActionsWithDomains);
</del><ins>+
+        if (dfa.graphSize() &lt; smallDFASize)
+            smallFiltersWithDomainsDFACombiner.addDFA(WTF::move(dfa));
+        else {
+            dfa.minimize();
+            lowerFiltersWithDomainsDFAToBytecode(WTF::move(dfa));
</ins><span class="cx">         }
</span><del>-        
-        Vector&lt;DFABytecode&gt; bytecode;
-        DFABytecodeCompiler compiler(dfa, bytecode);
-        compiler.compile();
-        LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
-        client.writeFiltersWithDomainsBytecode(WTF::move(bytecode));
-        
-        firstNFAWithDomainsSeen = true;
</del><span class="cx">     });
</span><ins>+    smallFiltersWithDomainsDFACombiner.combineDFAs(smallDFASize, [&amp;](DFA&amp;&amp; dfa) {
+        LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
+        lowerFiltersWithDomainsDFAToBytecode(WTF::move(dfa));
+    });
</ins><span class="cx">     ASSERT(filtersWithDomains.isEmpty());
</span><span class="cx">     
</span><span class="cx">     if (!firstNFAWithDomainsSeen) {
</span><span class="lines">@@ -364,7 +404,7 @@
</span><span class="cx">         // Minimizing this DFA would not be effective because all actions are unique
</span><span class="cx">         // and because of the tree-like structure of this DFA.
</span><span class="cx">         ASSERT_WITH_MESSAGE(!dfa.nodes[dfa.root].hasActions(), &quot;There should not be any domains that match everything.&quot;);
</span><del>-        
</del><ins>+
</ins><span class="cx">         Vector&lt;DFABytecode&gt; bytecode;
</span><span class="cx">         DFABytecodeCompiler compiler(dfa, bytecode);
</span><span class="cx">         compiler.compile();
</span><span class="lines">@@ -376,6 +416,9 @@
</span><span class="cx"> #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
</span><span class="cx">     double totalNFAToByteCodeBuildTimeEnd = monotonicallyIncreasingTime();
</span><span class="cx">     dataLogF(&quot;    Time spent building and compiling the DFAs: %f\n&quot;, (totalNFAToByteCodeBuildTimeEnd - totalNFAToByteCodeBuildTimeStart));
</span><ins>+
+    dataLogF(&quot;    Number of machines without domain filters: %d (total bytecode size = %d)\n&quot;, machinesWithoutDomainsCount, totalBytecodeSizeForMachinesWithoutDomains);
+    dataLogF(&quot;    Number of machines with domain filters: %d (total bytecode size = %d)\n&quot;, machinesWithDomainsCount, totalBytecodeSizeForMachinesWithDomains);
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     client.finalize();
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsDFAcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/DFA.cpp (185229 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/DFA.cpp        2015-06-05 00:31:07 UTC (rev 185229)
+++ trunk/Source/WebCore/contentextensions/DFA.cpp        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -127,6 +127,16 @@
</span><span class="cx">     DFAMinimizer::minimize(*this);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+unsigned DFA::graphSize() const
+{
+    unsigned count = 0;
+    for (const DFANode&amp; node : nodes) {
+        if (!node.isKilled())
+            ++count;
+    }
+    return count;
+}
+
</ins><span class="cx"> #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
</span><span class="cx"> static void printRange(bool firstRange, char rangeStart, char rangeEnd)
</span><span class="cx"> {
</span><span class="lines">@@ -212,14 +222,14 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         Vector&lt;unsigned&gt; correspondingNFANodes = nodes[i].correspondingNFANodes;
</span><del>-        ASSERT(!correspondingNFANodes.isEmpty());
-        dataLogF(&quot;&lt;BR/&gt;NFA Nodes: &quot;);
-        for (unsigned correspondingDFANodeIndex = 0; correspondingDFANodeIndex &lt; correspondingNFANodes.size(); ++correspondingDFANodeIndex) {
-            if (correspondingDFANodeIndex)
-                dataLogF(&quot;, &quot;);
-            dataLogF(&quot;%d&quot;, correspondingNFANodes[correspondingDFANodeIndex]);
</del><ins>+        if (!correspondingNFANodes.isEmpty()) {
+            dataLogF(&quot;&lt;BR/&gt;NFA Nodes: &quot;);
+            for (unsigned correspondingDFANodeIndex = 0; correspondingDFANodeIndex &lt; correspondingNFANodes.size(); ++correspondingDFANodeIndex) {
+                if (correspondingDFANodeIndex)
+                    dataLogF(&quot;, &quot;);
+                dataLogF(&quot;%d&quot;, correspondingNFANodes[correspondingDFANodeIndex]);
+            }
</ins><span class="cx">         }
</span><del>-
</del><span class="cx">         dataLogF(&quot;&gt;]&quot;);
</span><span class="cx"> 
</span><span class="cx">         if (!actions.isEmpty())
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsDFAh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/DFA.h (185229 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/DFA.h        2015-06-05 00:31:07 UTC (rev 185229)
+++ trunk/Source/WebCore/contentextensions/DFA.h        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -39,6 +39,7 @@
</span><span class="cx"> // The DFA abstract a partial DFA graph in a compact form.
</span><span class="cx"> struct WEBCORE_EXPORT DFA {
</span><span class="cx">     void minimize();
</span><ins>+    unsigned graphSize() const;
</ins><span class="cx">     size_t memoryUsed() const;
</span><span class="cx"> 
</span><span class="cx"> #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsDFACombinercpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/contentextensions/DFACombiner.cpp (0 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/DFACombiner.cpp                                (rev 0)
+++ trunk/Source/WebCore/contentextensions/DFACombiner.cpp        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -0,0 +1,266 @@
</span><ins>+/*
+ * 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.
+ */
+
+#include &quot;config.h&quot;
+#include &quot;DFACombiner.h&quot;
+
+#include &lt;wtf/HashMap.h&gt;
+#include &lt;wtf/HashSet.h&gt;
+
+namespace WebCore {
+
+namespace ContentExtensions {
+
+class DFAMerger {
+public:
+    DFAMerger(const DFA&amp; a, const DFA&amp; b)
+        : m_dfaA(a)
+        , m_dfaB(b)
+    {
+    }
+
+    DFA merge()
+    {
+        if (!m_nodeMapping.isEmpty())
+            return m_output;
+
+        uint64_t rootSignature = signatureForIndices(m_dfaA.root, m_dfaB.root);
+        getOrCreateCombinedNode(rootSignature);
+
+        while (!m_unprocessedNodes.isEmpty()) {
+            memset(m_transitionTargets, 0xff, sizeof(m_transitionTargets));
+
+            uint64_t unprocessedNode = m_unprocessedNodes.takeLast();
+
+            // We cannot cache the source node itself since we modify the machine. We only manipulate the IDs.
+            uint32_t sourceNodeId = m_nodeMapping.get(unprocessedNode);
+
+            // Populate the unique transitions.
+            uint32_t indexA = extractIndexA(unprocessedNode);
+            if (indexA != invalidNodeIndex)
+                populateTransitions&lt;WhichDFA::A&gt;(indexA);
+
+            uint32_t indexB = extractIndexB(unprocessedNode);
+            if (indexB != invalidNodeIndex)
+                populateTransitions&lt;WhichDFA::B&gt;(indexB);
+
+            // Spread the fallback transitions over the unique transitions and keep a reference to the fallback transition.
+            uint64_t fallbackTransitionSignature = signatureForIndices(invalidNodeIndex, invalidNodeIndex);
+            if (indexA != invalidNodeIndex)
+                populateFromFallbackTransitions&lt;WhichDFA::A&gt;(indexA, fallbackTransitionSignature);
+            if (indexB != invalidNodeIndex)
+                populateFromFallbackTransitions&lt;WhichDFA::B&gt;(indexB, fallbackTransitionSignature);
+
+            createTransitions(sourceNodeId);
+            createFallbackTransitionIfNeeded(sourceNodeId, fallbackTransitionSignature);
+        }
+        return m_output;
+    }
+
+private:
+    uint32_t invalidNodeIndex = 0xffffffff;
+
+    enum class WhichDFA {
+        A,
+        B
+    };
+
+    static uint64_t signatureForIndices(uint32_t aIndex, uint32_t bIndex)
+    {
+        return static_cast&lt;uint64_t&gt;(aIndex) &lt;&lt; 32 | bIndex;
+    }
+
+    static uint32_t extractIndexA(uint64_t signature)
+    {
+        return static_cast&lt;uint32_t&gt;(signature &gt;&gt; 32);
+    }
+
+    static uint32_t extractIndexB(uint64_t signature)
+    {
+        return static_cast&lt;uint32_t&gt;(signature);
+    }
+
+    uint32_t getOrCreateCombinedNode(uint64_t newNodeSignature)
+    {
+        auto addResult = m_nodeMapping.add(newNodeSignature, invalidNodeIndex);
+        if (!addResult.isNewEntry)
+            return addResult.iterator-&gt;value;
+
+        m_output.nodes.append(DFANode());
+        uint32_t newNodeIndex = m_output.nodes.size() - 1;
+        addResult.iterator-&gt;value = newNodeIndex;
+        m_unprocessedNodes.append(newNodeSignature);
+
+        uint32_t indexA = extractIndexA(newNodeSignature);
+        uint32_t indexB = extractIndexB(newNodeSignature);
+
+        HashSet&lt;uint64_t, DefaultHash&lt;uint64_t&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;uint64_t&gt;&gt; actions;
+        if (indexA != invalidNodeIndex) {
+            const DFANode&amp; node = m_dfaA.nodes[indexA];
+            uint32_t actionsStart = node.actionsStart();
+            uint32_t actionsEnd = actionsStart + node.actionsLength();
+            for (uint32_t i = actionsStart; i &lt; actionsEnd; ++i)
+            actions.add(m_dfaA.actions[i]);
+        }
+        if (indexB != invalidNodeIndex) {
+            const DFANode&amp; node = m_dfaB.nodes[indexB];
+            uint32_t actionsStart = node.actionsStart();
+            uint32_t actionsEnd = actionsStart + node.actionsLength();
+            for (uint32_t i = actionsStart; i &lt; actionsEnd; ++i)
+            actions.add(m_dfaB.actions[i]);
+        }
+
+        uint32_t actionsStart = m_output.actions.size();
+        for (uint64_t action : actions)
+            m_output.actions.append(action);
+        uint32_t actionsEnd = m_output.actions.size();
+        uint16_t actionsLength = static_cast&lt;uint16_t&gt;(actionsEnd - actionsStart);
+
+        m_output.nodes.last().setActions(actionsStart, actionsLength);
+        return newNodeIndex;
+    }
+
+    template&lt;WhichDFA whichDFA&gt;
+    void setHalfSignature(uint64_t&amp; signature, uint32_t value)
+    {
+        unsigned shiftAmount = (whichDFA == WhichDFA::A) ? 32 : 0;
+        uint64_t mask = static_cast&lt;uint64_t&gt;(0xffffffff) &lt;&lt; (32 - shiftAmount);
+        signature = (signature &amp; mask) | static_cast&lt;uint64_t&gt;(value) &lt;&lt; shiftAmount;
+    }
+
+    template&lt;WhichDFA whichDFA&gt;
+    void populateTransitions(uint32_t nodeIndex)
+    {
+        const DFA&amp; dfa = (whichDFA == WhichDFA::A) ? m_dfaA : m_dfaB;
+        const DFANode&amp; node = dfa.nodes[nodeIndex];
+        uint32_t transitionsStart = node.transitionsStart();
+        uint32_t transitionsEnd = transitionsStart + node.transitionsLength();
+
+        // Extract transitions.
+        for (uint32_t transitionIndex = transitionsStart; transitionIndex &lt; transitionsEnd; ++transitionIndex) {
+            uint8_t transitionCharacter = dfa.transitionCharacters[transitionIndex];
+            RELEASE_ASSERT(transitionCharacter &lt; WTF_ARRAY_LENGTH(m_transitionTargets));
+
+            uint32_t transitionDestination = dfa.transitionDestinations[transitionIndex];
+            setHalfSignature&lt;whichDFA&gt;(m_transitionTargets[transitionCharacter], transitionDestination);
+        }
+    }
+
+    template&lt;WhichDFA whichDFA&gt;
+    void populateFromFallbackTransitions(uint32_t nodeIndex, uint64_t&amp; fallbackTransitionSignature)
+    {
+        const DFA&amp; dfa = (whichDFA == WhichDFA::A) ? m_dfaA : m_dfaB;
+        const DFANode&amp; node = dfa.nodes[nodeIndex];
+        if (!node.hasFallbackTransition())
+            return;
+
+        uint32_t fallbackTarget = node.fallbackTransitionDestination(dfa);
+        setHalfSignature&lt;whichDFA&gt;(fallbackTransitionSignature, fallbackTarget);
+
+        // Spread the fallback over transitions where the other has something and we don't.
+        for (unsigned i = 0; i &lt; WTF_ARRAY_LENGTH(m_transitionTargets); ++i) {
+            uint64_t&amp; targetSignature = m_transitionTargets[i];
+
+            uint32_t otherTarget = (whichDFA == WhichDFA::A) ? extractIndexB(targetSignature) : extractIndexA(targetSignature);
+            uint32_t selfTarget = (whichDFA == WhichDFA::A) ? extractIndexA(targetSignature) : extractIndexB(targetSignature);
+
+            if (otherTarget != invalidNodeIndex &amp;&amp; selfTarget == invalidNodeIndex)
+                setHalfSignature&lt;whichDFA&gt;(targetSignature, fallbackTarget);
+        }
+    }
+
+    void createTransitions(uint32_t sourceNodeId)
+    {
+        // Create transitions out of the source node, creating new nodes as needed.
+        uint32_t transitionsStart = m_output.transitionCharacters.size();
+        for (uint8_t i = 0; i &lt; WTF_ARRAY_LENGTH(m_transitionTargets); ++i) {
+            uint64_t signature = m_transitionTargets[i];
+            if (signature == signatureForIndices(invalidNodeIndex, invalidNodeIndex))
+                continue;
+            uint32_t nodeId = getOrCreateCombinedNode(signature);
+            m_output.transitionCharacters.append(i);
+            m_output.transitionDestinations.append(nodeId);
+        }
+        uint32_t transitionsEnd = m_output.transitionCharacters.size();
+        uint8_t transitionsLength = static_cast&lt;uint8_t&gt;(transitionsEnd - transitionsStart);
+
+        m_output.nodes[sourceNodeId].setTransitions(transitionsStart, transitionsLength);
+    }
+
+    void createFallbackTransitionIfNeeded(uint32_t sourceNodeId, uint64_t fallbackTransitionSignature)
+    {
+        if (fallbackTransitionSignature != signatureForIndices(invalidNodeIndex, invalidNodeIndex)) {
+            uint32_t nodeId = getOrCreateCombinedNode(fallbackTransitionSignature);
+            m_output.nodes[sourceNodeId].addFallbackTransition(m_output, nodeId);
+        }
+    }
+
+    const DFA&amp; m_dfaA;
+    const DFA&amp; m_dfaB;
+    DFA m_output;
+    HashMap&lt;uint64_t, uint32_t, DefaultHash&lt;uint64_t&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;uint64_t&gt;&gt; m_nodeMapping;
+    Vector&lt;uint64_t&gt; m_unprocessedNodes;
+    uint64_t m_transitionTargets[128];
+};
+
+void DFACombiner::combineDFAs(unsigned minimumSize, std::function&lt;void(DFA&amp;&amp;)&gt; handler)
+{
+    if (m_dfas.isEmpty())
+        return;
+
+    for (unsigned i = m_dfas.size(); i--;) {
+        if (m_dfas[i].graphSize() &gt; minimumSize) {
+            handler(WTF::move(m_dfas[i]));
+            m_dfas.remove(i);
+        }
+    }
+
+    while (!m_dfas.isEmpty()) {
+        if (m_dfas.size() == 1) {
+            handler(WTF::move(m_dfas.first()));
+            return;
+        }
+
+        DFA a = m_dfas.takeLast();
+        DFA b = m_dfas.takeLast();
+        DFAMerger dfaMerger(a, b);
+        DFA c = dfaMerger.merge();
+
+        if (c.graphSize() &gt; minimumSize) {
+            // Minimizing is somewhat expensive. We only do it in bulk when we reach the threshold
+            // to reduce the load.
+            c.minimize();
+        }
+
+        if (c.graphSize() &gt; minimumSize)
+            handler(WTF::move(c));
+        else
+            m_dfas.append(c);
+    }
+}
+
+}
+
+} // namespace WebCore
</ins></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsDFACombinerh"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/contentextensions/DFACombiner.h (0 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/DFACombiner.h                                (rev 0)
+++ trunk/Source/WebCore/contentextensions/DFACombiner.h        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -0,0 +1,55 @@
</span><ins>+/*
+ * 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 DFACombiner_h
+#define DFACombiner_h
+
+#include &quot;DFA.h&quot;
+#include &lt;wtf/Vector.h&gt;
+
+namespace WebCore {
+
+namespace ContentExtensions {
+
+class WEBCORE_EXPORT DFACombiner {
+public:
+    void addDFA(DFA&amp;&amp;);
+    void combineDFAs(unsigned minimumSize, std::function&lt;void(DFA&amp;&amp;)&gt; handler);
+
+private:
+    Vector&lt;DFA&gt; m_dfas;
+};
+
+inline void DFACombiner::addDFA(DFA&amp;&amp; dfa)
+{
+    dfa.minimize();
+    m_dfas.append(WTF::move(dfa));
+}
+
+}
+
+} // namespace WebCore
+
+#endif
</ins></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (185229 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2015-06-05 00:31:07 UTC (rev 185229)
+++ trunk/Tools/ChangeLog        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -1,3 +1,24 @@
</span><ins>+2015-06-04  Benjamin Poulain  &lt;bpoulain@apple.com&gt;
+
+        Combine tiny DFAs into slightly larger ones
+        https://bugs.webkit.org/show_bug.cgi?id=145572
+
+        Reviewed by Alex Christensen.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebCore/DFACombiner.cpp: Added.
+        (TestWebKitAPI::DFACombinerTest::SetUp):
+        (TestWebKitAPI::combine):
+        (TestWebKitAPI::TEST_F):
+        * TestWebKitAPI/Tests/WebCore/DFAHelpers.h: Copied from Source/WebCore/contentextensions/DFA.h.
+        (TestWebKitAPI::countLiveNodes):
+        (TestWebKitAPI::createNFAs):
+        (TestWebKitAPI::buildDFAFromPatterns):
+        * TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp:
+        (TestWebKitAPI::countLiveNodes): Deleted.
+        (TestWebKitAPI::createNFAs): Deleted.
+        (TestWebKitAPI::buildDFAFromPatterns): Deleted.
+
</ins><span class="cx"> 2015-06-04  Alexey Proskuryakov  &lt;ap@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         WebKitTestRunner leaks strings in generateWhitelist()
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestWebKitAPIxcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (185229 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj        2015-06-05 00:31:07 UTC (rev 185229)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -19,6 +19,7 @@
</span><span class="cx">                 1ADBEFE3130C6AA100D61D19 /* simple-accelerated-compositing.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 1ADBEFBC130C6A0100D61D19 /* simple-accelerated-compositing.html */; };
</span><span class="cx">                 1AEDE22613E5E7E700E62FE8 /* InjectedBundleControllerMac.mm in Sources */ = {isa = PBXBuildFile; fileRef = 1AEDE22413E5E7A000E62FE8 /* InjectedBundleControllerMac.mm */; };
</span><span class="cx">                 1CB9BC381A67482300FE5678 /* WeakPtr.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1CB9BC371A67482300FE5678 /* WeakPtr.cpp */; };
</span><ins>+                260BA5791B1D2E7B004FA07C /* DFACombiner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 260BA5781B1D2E7B004FA07C /* DFACombiner.cpp */; };
</ins><span class="cx">                 26DF5A6315A2A27E003689C2 /* CancelLoadFromResourceLoadDelegate.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 26DF5A6115A2A22B003689C2 /* CancelLoadFromResourceLoadDelegate.html */; };
</span><span class="cx">                 26F52EAD1828827B0023D412 /* geolocationGetCurrentPosition.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 26F52EAC1828820E0023D412 /* geolocationGetCurrentPosition.html */; };
</span><span class="cx">                 26F52EAF18288C230023D412 /* geolocationGetCurrentPositionWithHighAccuracy.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 26F52EAE18288C040023D412 /* geolocationGetCurrentPositionWithHighAccuracy.html */; };
</span><span class="lines">@@ -433,6 +434,8 @@
</span><span class="cx">                 1AEF994817A09F5300998EF0 /* GetPIDAfterAbortedProcessLaunch.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = GetPIDAfterAbortedProcessLaunch.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 1AFDE6541953B2C000C48FFA /* Optional.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Optional.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 1CB9BC371A67482300FE5678 /* WeakPtr.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WeakPtr.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                260BA5781B1D2E7B004FA07C /* DFACombiner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DFACombiner.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
+                260BA57A1B1D2EE2004FA07C /* DFAHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DFAHelpers.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 261516D515B0E60500A2C201 /* SetAndUpdateCacheModel.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = SetAndUpdateCacheModel.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 26300B1716755CD90066886D /* ListHashSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ListHashSet.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 265AF54F15D1E48A00B0CB4A /* WTFString.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WTFString.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -858,6 +861,8 @@
</span><span class="cx">                                 93A720E518F1A0E800A848E1 /* CalculationValue.cpp */,
</span><span class="cx">                                 7CB184C41AA3F2100066EDFD /* ContentExtensions.cpp */,
</span><span class="cx">                                 CD5451E919E41F9D0016936F /* CSSParser.cpp */,
</span><ins>+                                260BA5781B1D2E7B004FA07C /* DFACombiner.cpp */,
+                                260BA57A1B1D2EE2004FA07C /* DFAHelpers.h */,
</ins><span class="cx">                                 26F6E1EF1ADC749B00DE696B /* DFAMinimizer.cpp */,
</span><span class="cx">                                 41973B5A1AF2286A006C7B36 /* FileSystem.cpp */,
</span><span class="cx">                                 14464012167A8305000BD218 /* LayoutUnit.cpp */,
</span><span class="lines">@@ -1602,6 +1607,7 @@
</span><span class="cx">                                 41973B5B1AF2286A006C7B36 /* FileSystem.cpp in Sources */,
</span><span class="cx">                                 7A5623111AD5AF3E0096B920 /* MenuTypesForMouseEvents.cpp in Sources */,
</span><span class="cx">                                 26F6E1F01ADC749B00DE696B /* DFAMinimizer.cpp in Sources */,
</span><ins>+                                260BA5791B1D2E7B004FA07C /* DFACombiner.cpp in Sources */,
</ins><span class="cx">                                 7A99D9941AD4A29D00373141 /* MenuTypesForMouseEvents.mm in Sources */,
</span><span class="cx">                         );
</span><span class="cx">                         runOnlyForDeploymentPostprocessing = 0;
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebCoreDFACombinercpp"></a>
<div class="addfile"><h4>Added: trunk/Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp (0 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp                                (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/DFACombiner.cpp        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -0,0 +1,121 @@
</span><ins>+/*
+ * 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.
+ */
+
+#include &quot;config.h&quot;
+
+#include &quot;DFAHelpers.h&quot;
+#include &lt;WebCore/DFACombiner.h&gt;
+#include &lt;wtf/MainThread.h&gt;
+
+using namespace WebCore;
+using namespace ContentExtensions;
+
+namespace TestWebKitAPI {
+
+class DFACombinerTest : public testing::Test {
+public:
+    virtual void SetUp()
+    {
+        WTF::initializeMainThread();
+    }
+};
+
+Vector&lt;DFA&gt; combine(Vector&lt;DFA&gt; dfas, unsigned minimumSize)
+{
+    DFACombiner combiner;
+    for (DFA&amp; dfa : dfas)
+        combiner.addDFA(WTF::move(dfa));
+
+    Vector&lt;DFA&gt; output;
+    combiner.combineDFAs(minimumSize, [&amp;output](DFA&amp;&amp; dfa) {
+        output.append(dfa);
+    });
+    return output;
+}
+
+TEST_F(DFACombinerTest, Basic)
+{
+    Vector&lt;DFA&gt; dfas = { buildDFAFromPatterns({ &quot;foo&quot;}), buildDFAFromPatterns({ &quot;bar&quot;}) };
+    Vector&lt;DFA&gt; combinedDFAs = combine(dfas, 10000);
+    EXPECT_EQ(static_cast&lt;size_t&gt;(1), combinedDFAs.size());
+
+    DFA reference = buildDFAFromPatterns({ &quot;foo&quot;, &quot;bar&quot;});
+    reference.minimize();
+    EXPECT_EQ(countLiveNodes(reference), countLiveNodes(combinedDFAs.first()));
+}
+
+
+TEST_F(DFACombinerTest, IdenticalDFAs)
+{
+    Vector&lt;DFA&gt; dfas = { buildDFAFromPatterns({ &quot;foo&quot;}), buildDFAFromPatterns({ &quot;foo&quot;}) };
+    Vector&lt;DFA&gt; combinedDFAs = combine(dfas, 10000);
+    EXPECT_EQ(static_cast&lt;size_t&gt;(1), combinedDFAs.size());
+
+    // The result should have the exact same size as the minimized input.
+    DFA reference = buildDFAFromPatterns({ &quot;foo&quot;});
+    reference.minimize();
+    EXPECT_EQ(countLiveNodes(reference), countLiveNodes(combinedDFAs.first()));
+}
+
+TEST_F(DFACombinerTest, NoInput)
+{
+    DFACombiner combiner;
+    unsigned counter = 0;
+    combiner.combineDFAs(100000, [&amp;counter](DFA&amp;&amp; dfa) {
+        ++counter;
+    });
+    EXPECT_EQ(static_cast&lt;unsigned&gt;(0), counter);
+}
+
+TEST_F(DFACombinerTest, SingleInput)
+{
+    Vector&lt;DFA&gt; dfas = { buildDFAFromPatterns({ &quot;WebKit&quot;}) };
+    Vector&lt;DFA&gt; combinedDFAs = combine(dfas, 10000);
+    EXPECT_EQ(static_cast&lt;size_t&gt;(1), combinedDFAs.size());
+
+    DFA reference = buildDFAFromPatterns({ &quot;WebKit&quot;});
+    reference.minimize();
+    EXPECT_EQ(countLiveNodes(reference), countLiveNodes(combinedDFAs.first()));
+}
+
+TEST_F(DFACombinerTest, InputTooLargeForMinimumSize)
+{
+    Vector&lt;DFA&gt; dfas = { buildDFAFromPatterns({ &quot;foo&quot;}), buildDFAFromPatterns({ &quot;bar&quot;}) };
+    Vector&lt;DFA&gt; combinedDFAs = combine(dfas, 2);
+    EXPECT_EQ(static_cast&lt;size_t&gt;(2), combinedDFAs.size());
+    EXPECT_EQ(static_cast&lt;size_t&gt;(4), countLiveNodes(combinedDFAs[0]));
+    EXPECT_EQ(static_cast&lt;size_t&gt;(4), countLiveNodes(combinedDFAs[1]));
+}
+
+TEST_F(DFACombinerTest, CombinedInputReachesMinimumSize)
+{
+    Vector&lt;DFA&gt; dfas = { buildDFAFromPatterns({ &quot;foo&quot;}), buildDFAFromPatterns({ &quot;bar&quot;}), buildDFAFromPatterns({ &quot;WebKit&quot;}) };
+    Vector&lt;DFA&gt; combinedDFAs = combine(dfas, 5);
+    EXPECT_EQ(static_cast&lt;size_t&gt;(2), combinedDFAs.size());
+    EXPECT_EQ(static_cast&lt;size_t&gt;(7), countLiveNodes(combinedDFAs[0]));
+    EXPECT_EQ(static_cast&lt;size_t&gt;(6), countLiveNodes(combinedDFAs[1]));
+}
+
+} // namespace TestWebKitAPI
</ins></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebCoreDFAHelpersh"></a>
<div class="addfile"><h4>Added: trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h (0 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h                                (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAHelpers.h        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -0,0 +1,67 @@
</span><ins>+/*
+ * 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.
+ */
+
+#include &lt;WebCore/CombinedURLFilters.h&gt;
+#include &lt;WebCore/NFA.h&gt;
+#include &lt;WebCore/NFAToDFA.h&gt;
+#include &lt;WebCore/URLFilterParser.h&gt;
+
+using namespace WebCore;
+
+namespace TestWebKitAPI {
+
+static unsigned countLiveNodes(const ContentExtensions::DFA&amp; dfa)
+{
+    unsigned counter = 0;
+    for (const auto&amp; node : dfa.nodes) {
+        if (!node.isKilled())
+            ++counter;
+    }
+    return counter;
+}
+
+static Vector&lt;ContentExtensions::NFA&gt; createNFAs(ContentExtensions::CombinedURLFilters&amp; combinedURLFilters)
+{
+    Vector&lt;ContentExtensions::NFA&gt; nfas;
+
+    combinedURLFilters.processNFAs(std::numeric_limits&lt;size_t&gt;::max(), [&amp;](ContentExtensions::NFA&amp;&amp; nfa) {
+        nfas.append(WTF::move(nfa));
+    });
+
+    return nfas;
+}
+
+static ContentExtensions::DFA buildDFAFromPatterns(Vector&lt;const char*&gt; patterns)
+{
+    ContentExtensions::CombinedURLFilters combinedURLFilters;
+    ContentExtensions::URLFilterParser parser(combinedURLFilters);
+
+    for (const char* pattern : patterns)
+        parser.addPattern(pattern, false, 0);
+    Vector&lt;ContentExtensions::NFA&gt; nfas = createNFAs(combinedURLFilters);
+    return ContentExtensions::NFAToDFA::convert(nfas[0]);
+}
+
+} // namespace TestWebKitAPI
</ins></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebCoreDFAMinimizercpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp (185229 => 185230)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp        2015-06-05 00:31:07 UTC (rev 185229)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp        2015-06-05 01:01:59 UTC (rev 185230)
</span><span class="lines">@@ -25,14 +25,9 @@
</span><span class="cx"> 
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> 
</span><del>-#include &lt;WebCore/CombinedURLFilters.h&gt;
-#include &lt;WebCore/NFA.h&gt;
-#include &lt;WebCore/NFAToDFA.h&gt;
-#include &lt;WebCore/URLFilterParser.h&gt;
</del><ins>+#include &quot;DFAHelpers.h&quot;
</ins><span class="cx"> #include &lt;wtf/MainThread.h&gt;
</span><span class="cx"> 
</span><del>-using namespace WebCore;
-
</del><span class="cx"> namespace TestWebKitAPI {
</span><span class="cx"> 
</span><span class="cx"> class DFAMinimizerTest : public testing::Test {
</span><span class="lines">@@ -43,38 +38,6 @@
</span><span class="cx">     }
</span><span class="cx"> };
</span><span class="cx"> 
</span><del>-static unsigned countLiveNodes(const ContentExtensions::DFA&amp; dfa)
-{
-    unsigned counter = 0;
-    for (const auto&amp; node : dfa.nodes) {
-        if (!node.isKilled())
-            ++counter;
-    }
-    return counter;
-}
-
-static Vector&lt;ContentExtensions::NFA&gt; createNFAs(ContentExtensions::CombinedURLFilters&amp; combinedURLFilters)
-{
-    Vector&lt;ContentExtensions::NFA&gt; nfas;
-
-    combinedURLFilters.processNFAs(std::numeric_limits&lt;size_t&gt;::max(), [&amp;](ContentExtensions::NFA&amp;&amp; nfa) {
-        nfas.append(WTF::move(nfa));
-    });
-
-    return nfas;
-}
-
-ContentExtensions::DFA buildDFAFromPatterns(Vector&lt;const char*&gt; patterns)
-{
-    ContentExtensions::CombinedURLFilters combinedURLFilters;
-    ContentExtensions::URLFilterParser parser(combinedURLFilters);
-
-    for (const char* pattern : patterns)
-        parser.addPattern(pattern, false, 0);
-    Vector&lt;ContentExtensions::NFA&gt; nfas = createNFAs(combinedURLFilters);
-    return ContentExtensions::NFAToDFA::convert(nfas[0]);
-}
-
</del><span class="cx"> TEST_F(DFAMinimizerTest, BasicSearch)
</span><span class="cx"> {
</span><span class="cx">     ContentExtensions::DFA dfa = buildDFAFromPatterns({ &quot;.*foo&quot;, &quot;.*bar&quot;, &quot;.*bang&quot;});
</span></span></pre>
</div>
</div>

</body>
</html>