<!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>[186374] 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/186374">186374</a></dd>
<dt>Author</dt> <dd>achristensen@apple.com</dd>
<dt>Date</dt> <dd>2015-07-06 14:06:30 -0700 (Mon, 06 Jul 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Content Extensions] Make the DFA transitions ranges instead of characters
https://bugs.webkit.org/show_bug.cgi?id=146575

Patch by Benjamin Poulain &lt;benjamin@webkit.org&gt; on 2015-07-06
Reviewed by Alex Christensen.

Source/WebCore:

This patch changes the DFA and code using the DFA to use ranges
to represent the transitions between any two nodes.

This patch builds on top of the tools introduced in <a href="http://trac.webkit.org/projects/webkit/changeset/186079">r186079</a>.

The DFA structure is basically the same as ImmutableNFA but without
any epsilon transitions.

This patch introduces a transition iterator to make the DFA
compatible with the existing algorithms.

---

The DFA combiner is rebuilt on top of MutableRangeList. Combining the transitions
of two nodes is one by merging the range list of each not into a common
MutableRangeList.
The data converter takes care of creating the signature of the combination.

The code got simpler since MutableRangeList does most of the work now. It is also
much faster.

---

The minimizer is more intersting.

With the current algorithm, we cannot resolve overlaps between ranges. On the other
hand, the minimizer does not care about the symbol of the transitions if we are careful
to partition transitions of the same symbol together.

What I did was to turn the minimizer into a pure transition based one, BUT each
&quot;symbol&quot; is actually an unbreakable range.

The first step is to go over all the transitions of all the nodes and find the largest
ranges such that the alphabet of interest is covered but there is not a single intersection
between any two nodes (what I called &quot;singular transitions&quot; in the code). 

This can be done efficiently with MutableRangeList.
A little trick there is that I also used the converter to count how many real transition
overlaps any singular transition.

Those singular transitions become the alphabet of our minimizer. The &quot;symbol&quot; of our alphabet
is simply the position of the singular transition in the list.

The partition of transition is created by populating each set with all the transition that
overlaps the symbols.
Note that since the partition is created on the fly, the Transition structure used for
repartitioning only contains the source of the transitions.

Once our transition parition has been carefuly created, we can completely forget about
the symbols and only work with subsets.

Since the singular transitions have no overlap (unlike fallback transitions), this new minimizer
will find the minimial solution for well formed input.

* WebCore.xcodeproj/project.pbxproj:
* contentextensions/DFA.cpp:
(WebCore::ContentExtensions::DFA::memoryUsed):
(WebCore::ContentExtensions::printTransitions):
(WebCore::ContentExtensions::DFANode::actions): Deleted.
(WebCore::ContentExtensions::DFANode::transitions): Deleted.
(WebCore::ContentExtensions::DFANode::fallbackTransitionDestination): Deleted.
(WebCore::ContentExtensions::DFANode::changeFallbackTransition): Deleted.
(WebCore::ContentExtensions::DFANode::addFallbackTransition): Deleted.
(WebCore::ContentExtensions::DFANode::containsTransition): Deleted.
(WebCore::ContentExtensions::DFANode::kill): Deleted.
(WebCore::ContentExtensions::DFA::debugPrintDot): Deleted.
* contentextensions/DFA.h:
(WebCore::ContentExtensions::DFANode::ConstRangeIterator::range):
(WebCore::ContentExtensions::DFANode::ConstRangeIterator::target):
(WebCore::ContentExtensions::DFANode::RangeIterator::range):
(WebCore::ContentExtensions::DFANode::RangeIterator::target):
(WebCore::ContentExtensions::DFANode::RangeIterator::resetTarget):
* contentextensions/DFABytecodeCompiler.cpp:
(WebCore::ContentExtensions::DFABytecodeCompiler::ranges):
(WebCore::ContentExtensions::DFABytecodeCompiler::nodeTransitionsMaxBytecodeSize):
(WebCore::ContentExtensions::DFABytecodeCompiler::compileNodeTransitions):
* contentextensions/DFACombiner.cpp:
(WebCore::ContentExtensions::DFAMerger::TargetConverter::convert):
(WebCore::ContentExtensions::DFAMerger::TargetConverter::extend):
(WebCore::ContentExtensions::DFAMerger::TargetConverter::setHalfSignature):
(WebCore::ContentExtensions::DFAMerger::merge):
(WebCore::ContentExtensions::DFAMerger::getOrCreateCombinedNode):
(WebCore::ContentExtensions::DFAMerger::setHalfSignature): Deleted.
(WebCore::ContentExtensions::DFAMerger::populateTransitions): Deleted.
(WebCore::ContentExtensions::DFAMerger::populateFromFallbackTransitions): Deleted.
(WebCore::ContentExtensions::DFAMerger::createTransitions): Deleted.
(WebCore::ContentExtensions::DFAMerger::createFallbackTransitionIfNeeded): Deleted.
* contentextensions/DFAMinimizer.cpp:
(WebCore::ContentExtensions::DFAMinimizer::minimize):
* contentextensions/DFANode.cpp: Added.
(WebCore::ContentExtensions::DFANode::actions):
(WebCore::ContentExtensions::DFANode::containsTransition):
(WebCore::ContentExtensions::DFANode::kill):
(WebCore::ContentExtensions::DFANode::canUseFallbackTransition):
(WebCore::ContentExtensions::DFANode::bestFallbackTarget):
* contentextensions/DFANode.h:
(WebCore::ContentExtensions::CharRange::size):
(WebCore::ContentExtensions::DFANode::ConstRangeIterator::operator*):
(WebCore::ContentExtensions::DFANode::ConstRangeIterator::operator==):
(WebCore::ContentExtensions::DFANode::ConstRangeIterator::operator!=):
(WebCore::ContentExtensions::DFANode::ConstRangeIterator::operator++):
(WebCore::ContentExtensions::DFANode::ConstRangeIterator::first):
(WebCore::ContentExtensions::DFANode::ConstRangeIterator::last):
(WebCore::ContentExtensions::DFANode::ConstRangeIterator::data):
(WebCore::ContentExtensions::DFANode::IterableConstRange::begin):
(WebCore::ContentExtensions::DFANode::IterableConstRange::end):
(WebCore::ContentExtensions::DFANode::transitions):
(WebCore::ContentExtensions::DFANode::RangeIterator::operator*):
(WebCore::ContentExtensions::DFANode::RangeIterator::operator==):
(WebCore::ContentExtensions::DFANode::RangeIterator::operator!=):
(WebCore::ContentExtensions::DFANode::RangeIterator::operator++):
(WebCore::ContentExtensions::DFANode::RangeIterator::first):
(WebCore::ContentExtensions::DFANode::RangeIterator::last):
(WebCore::ContentExtensions::DFANode::RangeIterator::data):
(WebCore::ContentExtensions::DFANode::IterableRange::begin):
(WebCore::ContentExtensions::DFANode::IterableRange::end):
(WebCore::ContentExtensions::DFANode::hasFallbackTransition): Deleted.
(WebCore::ContentExtensions::DFANode::transitionsLength): Deleted.
(WebCore::ContentExtensions::DFANode::transitionsStart): Deleted.
(WebCore::ContentExtensions::DFANode::resetTransitions): Deleted.
(WebCore::ContentExtensions::DFANode::setHasFallbackTransitionWithoutChangingDFA): Deleted.
* contentextensions/ImmutableNFA.h:
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::first):
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::last):
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::data):
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::range):
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::operator*): Deleted.
(WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::operator-&gt;): Deleted.
* contentextensions/ImmutableNFANodeBuilder.h:
(WebCore::ContentExtensions::ImmutableNFANodeBuilder::FakeRangeIterator::first):
(WebCore::ContentExtensions::ImmutableNFANodeBuilder::FakeRangeIterator::last):
(WebCore::ContentExtensions::ImmutableNFANodeBuilder::FakeRangeIterator::operator*): Deleted.
(WebCore::ContentExtensions::ImmutableNFANodeBuilder::FakeRangeIterator::operator-&gt;): Deleted.
* contentextensions/MutableRange.h:
(WebCore::ContentExtensions::MutableRange::size): Deleted.
* contentextensions/MutableRangeList.h:
(WebCore::ContentExtensions::MutableRangeList::ConstIterator::first):
(WebCore::ContentExtensions::MutableRangeList::ConstIterator::last):
(WebCore::ContentExtensions::MutableRangeList::ConstIterator::data):
(WebCore::ContentExtensions::MutableRangeList::extend):
(WebCore::ContentExtensions::MutableRangeList::size):
(WebCore::ContentExtensions::MutableRangeList::initializeFrom):
* contentextensions/NFAToDFA.cpp:
(WebCore::ContentExtensions::NFAToDFA::convert):
(WebCore::ContentExtensions::canUseFallbackTransition): Deleted.
(WebCore::ContentExtensions::findBestFallbackTarget): Deleted.

Tools:

* TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
* TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp:
Since the minimizer is perfect, we get the minimal solution now,
which is really cool!</pre>

<h3>Modified Paths</h3>
<ul>
<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="#trunkSourceWebCorecontentextensionsDFAcpp">trunk/Source/WebCore/contentextensions/DFA.cpp</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsDFAh">trunk/Source/WebCore/contentextensions/DFA.h</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsDFABytecodeCompilercpp">trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsDFACombinercpp">trunk/Source/WebCore/contentextensions/DFACombiner.cpp</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsDFAMinimizercpp">trunk/Source/WebCore/contentextensions/DFAMinimizer.cpp</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsDFANodeh">trunk/Source/WebCore/contentextensions/DFANode.h</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsImmutableNFAh">trunk/Source/WebCore/contentextensions/ImmutableNFA.h</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsImmutableNFANodeBuilderh">trunk/Source/WebCore/contentextensions/ImmutableNFANodeBuilder.h</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsMutableRangeh">trunk/Source/WebCore/contentextensions/MutableRange.h</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsMutableRangeListh">trunk/Source/WebCore/contentextensions/MutableRangeList.h</a></li>
<li><a href="#trunkSourceWebCorecontentextensionsNFAToDFAcpp">trunk/Source/WebCore/contentextensions/NFAToDFA.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>
<li><a href="#trunkToolsTestWebKitAPITestsWebCoreDFAMinimizercpp">trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceWebCorecontentextensionsDFANodecpp">trunk/Source/WebCore/contentextensions/DFANode.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/ChangeLog        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -1,3 +1,157 @@
</span><ins>+2015-07-06  Benjamin Poulain  &lt;benjamin@webkit.org&gt;
+
+        [Content Extensions] Make the DFA transitions ranges instead of characters
+        https://bugs.webkit.org/show_bug.cgi?id=146575
+
+        Reviewed by Alex Christensen.
+
+        This patch changes the DFA and code using the DFA to use ranges
+        to represent the transitions between any two nodes.
+
+        This patch builds on top of the tools introduced in r186079.
+
+        The DFA structure is basically the same as ImmutableNFA but without
+        any epsilon transitions.
+
+        This patch introduces a transition iterator to make the DFA
+        compatible with the existing algorithms.
+
+        ---
+
+        The DFA combiner is rebuilt on top of MutableRangeList. Combining the transitions
+        of two nodes is one by merging the range list of each not into a common
+        MutableRangeList.
+        The data converter takes care of creating the signature of the combination.
+
+        The code got simpler since MutableRangeList does most of the work now. It is also
+        much faster.
+
+        ---
+
+        The minimizer is more intersting.
+
+        With the current algorithm, we cannot resolve overlaps between ranges. On the other
+        hand, the minimizer does not care about the symbol of the transitions if we are careful
+        to partition transitions of the same symbol together.
+
+        What I did was to turn the minimizer into a pure transition based one, BUT each
+        &quot;symbol&quot; is actually an unbreakable range.
+
+        The first step is to go over all the transitions of all the nodes and find the largest
+        ranges such that the alphabet of interest is covered but there is not a single intersection
+        between any two nodes (what I called &quot;singular transitions&quot; in the code). 
+
+        This can be done efficiently with MutableRangeList.
+        A little trick there is that I also used the converter to count how many real transition
+        overlaps any singular transition.
+
+        Those singular transitions become the alphabet of our minimizer. The &quot;symbol&quot; of our alphabet
+        is simply the position of the singular transition in the list.
+
+        The partition of transition is created by populating each set with all the transition that
+        overlaps the symbols.
+        Note that since the partition is created on the fly, the Transition structure used for
+        repartitioning only contains the source of the transitions.
+
+        Once our transition parition has been carefuly created, we can completely forget about
+        the symbols and only work with subsets.
+
+        Since the singular transitions have no overlap (unlike fallback transitions), this new minimizer
+        will find the minimial solution for well formed input.
+
+        * WebCore.xcodeproj/project.pbxproj:
+        * contentextensions/DFA.cpp:
+        (WebCore::ContentExtensions::DFA::memoryUsed):
+        (WebCore::ContentExtensions::printTransitions):
+        (WebCore::ContentExtensions::DFANode::actions): Deleted.
+        (WebCore::ContentExtensions::DFANode::transitions): Deleted.
+        (WebCore::ContentExtensions::DFANode::fallbackTransitionDestination): Deleted.
+        (WebCore::ContentExtensions::DFANode::changeFallbackTransition): Deleted.
+        (WebCore::ContentExtensions::DFANode::addFallbackTransition): Deleted.
+        (WebCore::ContentExtensions::DFANode::containsTransition): Deleted.
+        (WebCore::ContentExtensions::DFANode::kill): Deleted.
+        (WebCore::ContentExtensions::DFA::debugPrintDot): Deleted.
+        * contentextensions/DFA.h:
+        (WebCore::ContentExtensions::DFANode::ConstRangeIterator::range):
+        (WebCore::ContentExtensions::DFANode::ConstRangeIterator::target):
+        (WebCore::ContentExtensions::DFANode::RangeIterator::range):
+        (WebCore::ContentExtensions::DFANode::RangeIterator::target):
+        (WebCore::ContentExtensions::DFANode::RangeIterator::resetTarget):
+        * contentextensions/DFABytecodeCompiler.cpp:
+        (WebCore::ContentExtensions::DFABytecodeCompiler::ranges):
+        (WebCore::ContentExtensions::DFABytecodeCompiler::nodeTransitionsMaxBytecodeSize):
+        (WebCore::ContentExtensions::DFABytecodeCompiler::compileNodeTransitions):
+        * contentextensions/DFACombiner.cpp:
+        (WebCore::ContentExtensions::DFAMerger::TargetConverter::convert):
+        (WebCore::ContentExtensions::DFAMerger::TargetConverter::extend):
+        (WebCore::ContentExtensions::DFAMerger::TargetConverter::setHalfSignature):
+        (WebCore::ContentExtensions::DFAMerger::merge):
+        (WebCore::ContentExtensions::DFAMerger::getOrCreateCombinedNode):
+        (WebCore::ContentExtensions::DFAMerger::setHalfSignature): Deleted.
+        (WebCore::ContentExtensions::DFAMerger::populateTransitions): Deleted.
+        (WebCore::ContentExtensions::DFAMerger::populateFromFallbackTransitions): Deleted.
+        (WebCore::ContentExtensions::DFAMerger::createTransitions): Deleted.
+        (WebCore::ContentExtensions::DFAMerger::createFallbackTransitionIfNeeded): Deleted.
+        * contentextensions/DFAMinimizer.cpp:
+        (WebCore::ContentExtensions::DFAMinimizer::minimize):
+        * contentextensions/DFANode.cpp: Added.
+        (WebCore::ContentExtensions::DFANode::actions):
+        (WebCore::ContentExtensions::DFANode::containsTransition):
+        (WebCore::ContentExtensions::DFANode::kill):
+        (WebCore::ContentExtensions::DFANode::canUseFallbackTransition):
+        (WebCore::ContentExtensions::DFANode::bestFallbackTarget):
+        * contentextensions/DFANode.h:
+        (WebCore::ContentExtensions::CharRange::size):
+        (WebCore::ContentExtensions::DFANode::ConstRangeIterator::operator*):
+        (WebCore::ContentExtensions::DFANode::ConstRangeIterator::operator==):
+        (WebCore::ContentExtensions::DFANode::ConstRangeIterator::operator!=):
+        (WebCore::ContentExtensions::DFANode::ConstRangeIterator::operator++):
+        (WebCore::ContentExtensions::DFANode::ConstRangeIterator::first):
+        (WebCore::ContentExtensions::DFANode::ConstRangeIterator::last):
+        (WebCore::ContentExtensions::DFANode::ConstRangeIterator::data):
+        (WebCore::ContentExtensions::DFANode::IterableConstRange::begin):
+        (WebCore::ContentExtensions::DFANode::IterableConstRange::end):
+        (WebCore::ContentExtensions::DFANode::transitions):
+        (WebCore::ContentExtensions::DFANode::RangeIterator::operator*):
+        (WebCore::ContentExtensions::DFANode::RangeIterator::operator==):
+        (WebCore::ContentExtensions::DFANode::RangeIterator::operator!=):
+        (WebCore::ContentExtensions::DFANode::RangeIterator::operator++):
+        (WebCore::ContentExtensions::DFANode::RangeIterator::first):
+        (WebCore::ContentExtensions::DFANode::RangeIterator::last):
+        (WebCore::ContentExtensions::DFANode::RangeIterator::data):
+        (WebCore::ContentExtensions::DFANode::IterableRange::begin):
+        (WebCore::ContentExtensions::DFANode::IterableRange::end):
+        (WebCore::ContentExtensions::DFANode::hasFallbackTransition): Deleted.
+        (WebCore::ContentExtensions::DFANode::transitionsLength): Deleted.
+        (WebCore::ContentExtensions::DFANode::transitionsStart): Deleted.
+        (WebCore::ContentExtensions::DFANode::resetTransitions): Deleted.
+        (WebCore::ContentExtensions::DFANode::setHasFallbackTransitionWithoutChangingDFA): Deleted.
+        * contentextensions/ImmutableNFA.h:
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::first):
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::last):
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::data):
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::range):
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::operator*): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFA::ConstRangeIterator::operator-&gt;): Deleted.
+        * contentextensions/ImmutableNFANodeBuilder.h:
+        (WebCore::ContentExtensions::ImmutableNFANodeBuilder::FakeRangeIterator::first):
+        (WebCore::ContentExtensions::ImmutableNFANodeBuilder::FakeRangeIterator::last):
+        (WebCore::ContentExtensions::ImmutableNFANodeBuilder::FakeRangeIterator::operator*): Deleted.
+        (WebCore::ContentExtensions::ImmutableNFANodeBuilder::FakeRangeIterator::operator-&gt;): Deleted.
+        * contentextensions/MutableRange.h:
+        (WebCore::ContentExtensions::MutableRange::size): Deleted.
+        * contentextensions/MutableRangeList.h:
+        (WebCore::ContentExtensions::MutableRangeList::ConstIterator::first):
+        (WebCore::ContentExtensions::MutableRangeList::ConstIterator::last):
+        (WebCore::ContentExtensions::MutableRangeList::ConstIterator::data):
+        (WebCore::ContentExtensions::MutableRangeList::extend):
+        (WebCore::ContentExtensions::MutableRangeList::size):
+        (WebCore::ContentExtensions::MutableRangeList::initializeFrom):
+        * contentextensions/NFAToDFA.cpp:
+        (WebCore::ContentExtensions::NFAToDFA::convert):
+        (WebCore::ContentExtensions::canUseFallbackTransition): Deleted.
+        (WebCore::ContentExtensions::findBestFallbackTarget): Deleted.
+
</ins><span class="cx"> 2015-07-06  Timothy Hatcher  &lt;timothy@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Fix ASSERT causing crashes in Inspector tests on the bots.
</span></span></pre></div>
<a id="trunkSourceWebCoreWebCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/WebCore.xcodeproj/project.pbxproj        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -1031,6 +1031,7 @@
</span><span class="cx">                 26C15CF71857E15E00F15C03 /* ResourceHandleCFURLConnectionDelegateWithOperationQueue.h in Headers */ = {isa = PBXBuildFile; fileRef = 26C15CF51857E15D00F15C03 /* ResourceHandleCFURLConnectionDelegateWithOperationQueue.h */; };
</span><span class="cx">                 26C17A3E1491D2D400D12BA2 /* FileSystemIOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 26C17A3C1491D2D400D12BA2 /* FileSystemIOS.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 26C17A3F1491D2D400D12BA2 /* FileSystemIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 26C17A3D1491D2D400D12BA2 /* FileSystemIOS.mm */; };
</span><ins>+                26D4E8461B42539D00E033A2 /* DFANode.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26D4E8451B42539D00E033A2 /* DFANode.cpp */; };
</ins><span class="cx">                 26E944D81AC4B2DD007B85B5 /* CombinedURLFilters.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 26E944D41AC4B2DD007B85B5 /* CombinedURLFilters.cpp */; };
</span><span class="cx">                 26E944D91AC4B2DD007B85B5 /* CombinedURLFilters.h in Headers */ = {isa = PBXBuildFile; fileRef = 26E944D51AC4B2DD007B85B5 /* CombinedURLFilters.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="cx">                 26E944DD1AC4B4EA007B85B5 /* Term.h in Headers */ = {isa = PBXBuildFile; fileRef = 26E944DC1AC4B4EA007B85B5 /* Term.h */; };
</span><span class="lines">@@ -8180,6 +8181,7 @@
</span><span class="cx">                 26C15CF51857E15D00F15C03 /* ResourceHandleCFURLConnectionDelegateWithOperationQueue.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ResourceHandleCFURLConnectionDelegateWithOperationQueue.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 26C17A3C1491D2D400D12BA2 /* FileSystemIOS.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FileSystemIOS.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 26C17A3D1491D2D400D12BA2 /* FileSystemIOS.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = FileSystemIOS.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                26D4E8451B42539D00E033A2 /* DFANode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DFANode.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 26E944D41AC4B2DD007B85B5 /* CombinedURLFilters.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CombinedURLFilters.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 26E944D51AC4B2DD007B85B5 /* CombinedURLFilters.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CombinedURLFilters.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 26E944DC1AC4B4EA007B85B5 /* Term.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Term.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="lines">@@ -15706,6 +15708,7 @@
</span><span class="cx">                                 26A807831B18F97700E219BE /* DFACombiner.h */,
</span><span class="cx">                                 26A517FB1AB92238006335DF /* DFAMinimizer.cpp */,
</span><span class="cx">                                 26A517FC1AB92238006335DF /* DFAMinimizer.h */,
</span><ins>+                                26D4E8451B42539D00E033A2 /* DFANode.cpp */,
</ins><span class="cx">                                 267725F91A5B3AD9003C24DD /* DFANode.h */,
</span><span class="cx">                                 26F756B21B3B66F70005DD79 /* ImmutableNFA.h */,
</span><span class="cx">                                 26F756B41B3B68F20005DD79 /* ImmutableNFANodeBuilder.h */,
</span><span class="lines">@@ -30250,6 +30253,7 @@
</span><span class="cx">                                 B2A1F2AD0CEF0ABF00442F6A /* SVGGlyphElement.cpp in Sources */,
</span><span class="cx">                                 24D912BD13CA9A9700D21915 /* SVGGlyphRefElement.cpp in Sources */,
</span><span class="cx">                                 B2227A290D00BF220071B782 /* SVGGradientElement.cpp in Sources */,
</span><ins>+                                26D4E8461B42539D00E033A2 /* DFANode.cpp in Sources */,
</ins><span class="cx">                                 B2227AB50D00BF220071B782 /* SVGGraphicsElement.cpp in Sources */,
</span><span class="cx">                                 650FBF2A0D9AF047008FC292 /* SVGHKernElement.cpp in Sources */,
</span><span class="cx">                                 B25599A30D00D8BA00BB825C /* SVGImage.cpp in Sources */,
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsDFAcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/DFA.cpp (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/DFA.cpp        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/contentextensions/DFA.cpp        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -46,89 +46,11 @@
</span><span class="cx"> {
</span><span class="cx">     return sizeof(DFA)
</span><span class="cx">         + actions.capacity() * sizeof(uint64_t)
</span><del>-        + transitionCharacters.capacity() * sizeof(uint8_t)
</del><ins>+        + transitionRanges.capacity() * sizeof(CharRange)
</ins><span class="cx">         + transitionDestinations.capacity() * sizeof(uint32_t)
</span><span class="cx">         + nodes.capacity() * sizeof(DFANode);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-// FIXME: Make DFANode.cpp.
-Vector&lt;uint64_t&gt; DFANode::actions(const DFA&amp; dfa) const
-{
-    // FIXME: Use iterators instead of copying the Vector elements.
-    Vector&lt;uint64_t&gt; vector;
-    vector.reserveInitialCapacity(m_actionsLength);
-    for (uint32_t i = m_actionsStart; i &lt; m_actionsStart + m_actionsLength; ++i)
-        vector.uncheckedAppend(dfa.actions[i]);
-    return vector;
-}
-    
-Vector&lt;std::pair&lt;uint8_t, uint32_t&gt;&gt; DFANode::transitions(const DFA&amp; dfa) const
-{
-    // FIXME: Use iterators instead of copying the Vector elements.
-    Vector&lt;std::pair&lt;uint8_t, uint32_t&gt;&gt; vector;
-    vector.reserveInitialCapacity(transitionsLength());
-    for (uint32_t i = m_transitionsStart; i &lt; m_transitionsStart + m_transitionsLength; ++i)
-        vector.uncheckedAppend(std::make_pair(dfa.transitionCharacters[i], dfa.transitionDestinations[i]));
-    return vector;
-}
-
-uint32_t DFANode::fallbackTransitionDestination(const DFA&amp; dfa) const
-{
-    RELEASE_ASSERT(hasFallbackTransition());
-
-    // If there is a fallback transition, it is just after the other transitions and has an invalid ASCII character to mark it as a fallback transition.
-    ASSERT(dfa.transitionCharacters[m_transitionsStart + m_transitionsLength] == std::numeric_limits&lt;uint8_t&gt;::max());
-    return dfa.transitionDestinations[m_transitionsStart + m_transitionsLength];
-}
-
-void DFANode::changeFallbackTransition(DFA&amp; dfa, uint32_t newDestination)
-{
-    RELEASE_ASSERT(hasFallbackTransition());
-    ASSERT_WITH_MESSAGE(dfa.transitionCharacters[m_transitionsStart + m_transitionsLength] == std::numeric_limits&lt;uint8_t&gt;::max(), &quot;When changing a fallback transition, the fallback transition should already be marked as such&quot;);
-    dfa.transitionCharacters[m_transitionsStart + m_transitionsLength] = std::numeric_limits&lt;uint8_t&gt;::max();
-    dfa.transitionDestinations[m_transitionsStart + m_transitionsLength] = newDestination;
-}
-
-void DFANode::addFallbackTransition(DFA&amp; dfa, uint32_t destination)
-{
-    RELEASE_ASSERT(dfa.transitionCharacters.size() == dfa.transitionDestinations.size());
-    RELEASE_ASSERT_WITH_MESSAGE(dfa.transitionCharacters.size() == m_transitionsStart + m_transitionsLength, &quot;Adding a fallback transition should only happen if the node is at the end&quot;);
-    dfa.transitionCharacters.append(std::numeric_limits&lt;uint8_t&gt;::max());
-    dfa.transitionDestinations.append(destination);
-    ASSERT(!(m_flags &amp; HasFallbackTransition));
-    m_flags |= HasFallbackTransition;
-}
-
-bool DFANode::containsTransition(uint8_t transition, DFA&amp; dfa)
-{
-    // Called from DFAMinimizer, this loops though a maximum of 128 transitions, so it's not too slow.
-    ASSERT(m_transitionsLength &lt;= 128);
-    for (unsigned i = m_transitionsStart; i &lt; m_transitionsStart + m_transitionsLength; ++i) {
-        if (dfa.transitionCharacters[i] == transition)
-            return true;
-    }
-    return false;
-}
-    
-void DFANode::kill(DFA&amp; dfa)
-{
-    ASSERT(m_flags != IsKilled);
-    m_flags = IsKilled; // Killed nodes don't have any other flags.
-    
-    // Invalidate the now-unused memory in the DFA to make finding bugs easier.
-    for (unsigned i = m_transitionsStart; i &lt; m_transitionsStart + m_transitionsLength; ++i) {
-        dfa.transitionCharacters[i] = std::numeric_limits&lt;uint8_t&gt;::max();
-        dfa.transitionDestinations[i] = std::numeric_limits&lt;uint32_t&gt;::max();
-    }
-    for (unsigned i = m_actionsStart; i &lt; m_actionsStart + m_actionsLength; ++i)
-        dfa.actions[i] = std::numeric_limits&lt;uint64_t&gt;::max();
-
-    m_actionsStart = 0;
-    m_actionsLength = 0;
-    m_transitionsStart = 0;
-    m_transitionsLength = 0;
-};
-
</del><span class="cx"> void DFA::minimize()
</span><span class="cx"> {
</span><span class="cx">     DFAMinimizer::minimize(*this);
</span><span class="lines">@@ -165,17 +87,18 @@
</span><span class="cx">     const DFANode&amp; sourceNode = dfa.nodes[sourceNodeId];
</span><span class="cx">     auto transitions = sourceNode.transitions(dfa);
</span><span class="cx"> 
</span><del>-    if (transitions.isEmpty() &amp;&amp; !sourceNode.hasFallbackTransition())
</del><ins>+    if (transitions.begin() == transitions.end())
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><span class="cx">     HashMap&lt;unsigned, Vector&lt;uint16_t&gt;, DefaultHash&lt;unsigned&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;unsigned&gt;&gt; transitionsPerTarget;
</span><span class="cx"> 
</span><span class="cx">     // First, we build the list of transitions coming to each target node.
</span><span class="cx">     for (const auto&amp; transition : transitions) {
</span><del>-        unsigned target = transition.second;
</del><ins>+        unsigned target = transition.target();
</ins><span class="cx">         transitionsPerTarget.add(target, Vector&lt;uint16_t&gt;());
</span><span class="cx"> 
</span><del>-        transitionsPerTarget.find(target)-&gt;value.append(transition.first);
</del><ins>+        for (unsigned offset = 0; offset &lt; transition.range().size(); ++offset)
+            transitionsPerTarget.find(target)-&gt;value.append(transition.first() + offset);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Then we go over each one an display the ranges one by one.
</span><span class="lines">@@ -202,9 +125,6 @@
</span><span class="cx"> 
</span><span class="cx">         dataLogF(&quot;\&quot;];\n&quot;);
</span><span class="cx">     }
</span><del>-
-    if (sourceNode.hasFallbackTransition())
-        dataLogF(&quot;        %d -&gt; %d [label=\&quot;[fallback]\&quot;];\n&quot;, sourceNodeId, sourceNode.fallbackTransitionDestination(dfa));
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void DFA::debugPrintDot() const
</span><span class="lines">@@ -228,17 +148,6 @@
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        Vector&lt;unsigned&gt; correspondingNFANodes = nodes[i].correspondingNFANodes;
-        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]);
-            }
-        }
-        dataLogF(&quot;&gt;]&quot;);
-
</del><span class="cx">         if (!actions.isEmpty())
</span><span class="cx">             dataLogF(&quot; [shape=doublecircle]&quot;);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsDFAh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/DFA.h (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/DFA.h        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/contentextensions/DFA.h        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -47,16 +47,41 @@
</span><span class="cx"> #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
</span><span class="cx">     void debugPrintDot() const;
</span><span class="cx"> #endif
</span><del>-    
</del><ins>+
+    Vector&lt;DFANode&gt; nodes;
</ins><span class="cx">     Vector&lt;uint64_t&gt; actions;
</span><del>-    Vector&lt;uint8_t&gt; transitionCharacters;
</del><ins>+    Vector&lt;CharRange&gt; transitionRanges;
</ins><span class="cx">     Vector&lt;uint32_t&gt; transitionDestinations;
</span><del>-    Vector&lt;DFANode&gt; nodes;
</del><span class="cx">     unsigned root { 0 };
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+inline const CharRange&amp; DFANode::ConstRangeIterator::range() const
+{
+    return dfa.transitionRanges[position];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+inline uint32_t DFANode::ConstRangeIterator::target() const
+{
+    return dfa.transitionDestinations[position];
+}
+
+inline const CharRange&amp; DFANode::RangeIterator::range() const
+{
+    return dfa.transitionRanges[position];
+}
+
+inline uint32_t DFANode::RangeIterator::target() const
+{
+    return dfa.transitionDestinations[position];
+}
+
+inline void DFANode::RangeIterator::resetTarget(uint32_t newTarget)
+{
+    dfa.transitionDestinations[position] = newTarget;
+}
+
+}
+
</ins><span class="cx"> } // namespace WebCore
</span><span class="cx"> 
</span><span class="cx"> #endif // ENABLE(CONTENT_EXTENSIONS)
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsDFABytecodeCompilercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -204,10 +204,18 @@
</span><span class="cx">     memset(destinations, 0xff, sizeof(destinations));
</span><span class="cx">     const uint32_t noDestination = std::numeric_limits&lt;uint32_t&gt;::max();
</span><span class="cx"> 
</span><del>-    for (const auto&amp; pair : node.transitions(m_dfa)) {
-        RELEASE_ASSERT(pair.first &lt; WTF_ARRAY_LENGTH(destinations));
-        ASSERT_WITH_MESSAGE(destinations[pair.first] == noDestination, &quot;Transitions should be unique&quot;);
-        destinations[pair.first] = pair.second;
</del><ins>+    bool canUseFallbackTransition = node.canUseFallbackTransition(m_dfa);
+    uint32_t fallbackTransitionTarget = std::numeric_limits&lt;uint32_t&gt;::max();
+    if (canUseFallbackTransition)
+        fallbackTransitionTarget = node.bestFallbackTarget(m_dfa);
+
+    for (const auto&amp; transition : node.transitions(m_dfa)) {
+        uint32_t targetNodeIndex = transition.target();
+        if (canUseFallbackTransition &amp;&amp; fallbackTransitionTarget == targetNodeIndex)
+            continue;
+
+        for (uint16_t i = transition.range().first; i &lt;= transition.range().last; ++i)
+            destinations[i] = targetNodeIndex;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     Vector&lt;Range&gt; ranges;
</span><span class="lines">@@ -215,7 +223,6 @@
</span><span class="cx">     bool hasRangeMin = false;
</span><span class="cx">     for (uint8_t i = 0; i &lt; 128; i++) {
</span><span class="cx">         if (hasRangeMin) {
</span><del>-            ASSERT_WITH_MESSAGE(!(node.hasFallbackTransition() &amp;&amp; node.fallbackTransitionDestination(m_dfa) == destinations[rangeMin]), &quot;Individual transitions to the fallback transitions should have been eliminated by the optimizer.&quot;);
</del><span class="cx">             if (destinations[i] != destinations[rangeMin]) {
</span><span class="cx"> 
</span><span class="cx">                 // This is the end of a range. Check if it can be case insensitive.
</span><span class="lines">@@ -260,6 +267,7 @@
</span><span class="cx">         // If a range goes to 127, there will never be values higher than it, so checking for case-insensitive ranges would always fail.
</span><span class="cx">         ranges.append(Range(rangeMin, 127, destinations[rangeMin], true));
</span><span class="cx">     }
</span><ins>+
</ins><span class="cx">     return ranges;
</span><span class="cx"> }
</span><span class="cx">     
</span><span class="lines">@@ -289,7 +297,7 @@
</span><span class="cx">     unsigned size = 0;
</span><span class="cx">     for (const auto&amp; range : ranges(node))
</span><span class="cx">         size += checkForRangeMaxBytecodeSize(range);
</span><del>-    if (node.hasFallbackTransition())
</del><ins>+    if (node.canUseFallbackTransition(m_dfa))
</ins><span class="cx">         size += sizeof(DFABytecodeInstruction::Jump) + sizeof(uint32_t);
</span><span class="cx">     else
</span><span class="cx">         size += instructionSizeWithArguments(DFABytecodeInstruction::Terminate);
</span><span class="lines">@@ -303,8 +311,8 @@
</span><span class="cx">     
</span><span class="cx">     for (const auto&amp; range : ranges(node))
</span><span class="cx">         compileCheckForRange(nodeIndex, range);
</span><del>-    if (node.hasFallbackTransition())
-        emitJump(nodeIndex, node.fallbackTransitionDestination(m_dfa));
</del><ins>+    if (node.canUseFallbackTransition(m_dfa))
+        emitJump(nodeIndex, node.bestFallbackTarget(m_dfa));
</ins><span class="cx">     else
</span><span class="cx">         emitTerminate();
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsDFACombinercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/DFACombiner.cpp (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/DFACombiner.cpp        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/contentextensions/DFACombiner.cpp        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> #include &quot;DFACombiner.h&quot;
</span><span class="cx"> 
</span><ins>+#include &quot;MutableRangeList.h&quot;
</ins><span class="cx"> #include &lt;wtf/HashMap.h&gt;
</span><span class="cx"> #include &lt;wtf/HashSet.h&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -34,6 +35,35 @@
</span><span class="cx"> namespace ContentExtensions {
</span><span class="cx"> 
</span><span class="cx"> class DFAMerger {
</span><ins>+    typedef MutableRangeList&lt;char, uint64_t, 128&gt; CombinedTransitionsMutableRangeList;
+
+    enum class WhichDFA {
+        A,
+        B
+    };
+
+    template&lt;WhichDFA whichDFA&gt;
+    struct TargetConverter {
+        uint64_t convert(uint32_t target)
+        {
+            uint64_t value = 0xffffffffffffffff;
+            extend(value, target);
+            return value;
+        }
+
+        void extend(uint64_t&amp; destination, uint32_t target)
+        {
+            setHalfSignature(destination, target);
+        }
+    private:
+        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;
+        }
+    };
+
</ins><span class="cx"> public:
</span><span class="cx">     DFAMerger(const DFA&amp; a, const DFA&amp; b)
</span><span class="cx">         : m_dfaA(a)
</span><span class="lines">@@ -49,32 +79,40 @@
</span><span class="cx">         uint64_t rootSignature = signatureForIndices(m_dfaA.root, m_dfaB.root);
</span><span class="cx">         getOrCreateCombinedNode(rootSignature);
</span><span class="cx"> 
</span><ins>+        CombinedTransitionsMutableRangeList combinedTransitions;
</ins><span class="cx">         while (!m_unprocessedNodes.isEmpty()) {
</span><del>-            memset(m_transitionTargets, 0xff, sizeof(m_transitionTargets));
</del><ins>+            combinedTransitions.clear();
</ins><span class="cx"> 
</span><span class="cx">             uint64_t unprocessedNode = m_unprocessedNodes.takeLast();
</span><span class="cx"> 
</span><del>-            // 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.
</del><span class="cx">             uint32_t indexA = extractIndexA(unprocessedNode);
</span><del>-            if (indexA != invalidNodeIndex)
-                populateTransitions&lt;WhichDFA::A&gt;(indexA);
</del><ins>+            if (indexA != invalidNodeIndex) {
+                const DFANode&amp; nodeA = m_dfaA.nodes[indexA];
+                auto transitionsA = nodeA.transitions(m_dfaA);
+                TargetConverter&lt;WhichDFA::A&gt; converterA;
+                combinedTransitions.extend(transitionsA.begin(), transitionsA.end(), converterA);
+            }
</ins><span class="cx"> 
</span><span class="cx">             uint32_t indexB = extractIndexB(unprocessedNode);
</span><del>-            if (indexB != invalidNodeIndex)
-                populateTransitions&lt;WhichDFA::B&gt;(indexB);
</del><ins>+            if (indexB != invalidNodeIndex) {
+                const DFANode&amp; nodeB = m_dfaB.nodes[indexB];
+                auto transitionsB = nodeB.transitions(m_dfaB);
+                TargetConverter&lt;WhichDFA::B&gt; converterB;
+                combinedTransitions.extend(transitionsB.begin(), transitionsB.end(), converterB);
+            }
</ins><span class="cx"> 
</span><del>-            // 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);
</del><ins>+            unsigned transitionsStart = m_output.transitionRanges.size();
+            for (const auto&amp; range : combinedTransitions) {
+                unsigned targetNodeId = getOrCreateCombinedNode(range.data);
+                m_output.transitionRanges.append({ range.first, range.last });
+                m_output.transitionDestinations.append(targetNodeId);
+            }
+            unsigned transitionsEnd = m_output.transitionRanges.size();
+            unsigned transitionsLength = transitionsEnd - transitionsStart;
</ins><span class="cx"> 
</span><del>-            createTransitions(sourceNodeId);
-            createFallbackTransitionIfNeeded(sourceNodeId, fallbackTransitionSignature);
</del><ins>+            uint32_t sourceNodeId = m_nodeMapping.get(unprocessedNode);
+            DFANode&amp; dfaSourceNode = m_output.nodes[sourceNodeId];
+            dfaSourceNode.setTransitions(transitionsStart, static_cast&lt;uint8_t&gt;(transitionsLength));
</ins><span class="cx">         }
</span><span class="cx">         return m_output;
</span><span class="cx">     }
</span><span class="lines">@@ -82,11 +120,6 @@
</span><span class="cx"> private:
</span><span class="cx">     uint32_t invalidNodeIndex = 0xffffffff;
</span><span class="cx"> 
</span><del>-    enum class WhichDFA {
-        A,
-        B
-    };
-
</del><span class="cx">     static uint64_t signatureForIndices(uint32_t aIndex, uint32_t bIndex)
</span><span class="cx">     {
</span><span class="cx">         return static_cast&lt;uint64_t&gt;(aIndex) &lt;&lt; 32 | bIndex;
</span><span class="lines">@@ -122,14 +155,14 @@
</span><span class="cx">             uint32_t actionsStart = node.actionsStart();
</span><span class="cx">             uint32_t actionsEnd = actionsStart + node.actionsLength();
</span><span class="cx">             for (uint32_t i = actionsStart; i &lt; actionsEnd; ++i)
</span><del>-            actions.add(m_dfaA.actions[i]);
</del><ins>+                actions.add(m_dfaA.actions[i]);
</ins><span class="cx">         }
</span><span class="cx">         if (indexB != invalidNodeIndex) {
</span><span class="cx">             const DFANode&amp; node = m_dfaB.nodes[indexB];
</span><span class="cx">             uint32_t actionsStart = node.actionsStart();
</span><span class="cx">             uint32_t actionsEnd = actionsStart + node.actionsLength();
</span><span class="cx">             for (uint32_t i = actionsStart; i &lt; actionsEnd; ++i)
</span><del>-            actions.add(m_dfaB.actions[i]);
</del><ins>+                actions.add(m_dfaB.actions[i]);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         uint32_t actionsStart = m_output.actions.size();
</span><span class="lines">@@ -142,87 +175,11 @@
</span><span class="cx">         return newNodeIndex;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    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);
-        }
-    }
-
</del><span class="cx">     const DFA&amp; m_dfaA;
</span><span class="cx">     const DFA&amp; m_dfaB;
</span><span class="cx">     DFA m_output;
</span><span class="cx">     HashMap&lt;uint64_t, uint32_t, DefaultHash&lt;uint64_t&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;uint64_t&gt;&gt; m_nodeMapping;
</span><span class="cx">     Vector&lt;uint64_t&gt; m_unprocessedNodes;
</span><del>-    uint64_t m_transitionTargets[128];
</del><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> void DFACombiner::combineDFAs(unsigned minimumSize, std::function&lt;void(DFA&amp;&amp;)&gt; handler)
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsDFAMinimizercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/DFAMinimizer.cpp (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/DFAMinimizer.cpp        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/contentextensions/DFAMinimizer.cpp        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -30,6 +30,7 @@
</span><span class="cx"> 
</span><span class="cx"> #include &quot;DFA.h&quot;
</span><span class="cx"> #include &quot;DFANode.h&quot;
</span><ins>+#include &quot;MutableRangeList.h&quot;
</ins><span class="cx"> #include &lt;wtf/HashMap.h&gt;
</span><span class="cx"> #include &lt;wtf/HashSet.h&gt;
</span><span class="cx"> #include &lt;wtf/StringHasher.h&gt;
</span><span class="lines">@@ -40,79 +41,32 @@
</span><span class="cx"> 
</span><span class="cx"> namespace {
</span><span class="cx"> 
</span><del>-// simplifyTransitions() tries to collapse individual transitions into fallback transitions.
-// After simplifyTransitions(), you can also make the assumption that a fallback transition target will never be
-// also the target of an individual transition.
-static void simplifyTransitions(DFA&amp; dfa)
</del><ins>+template&lt;typename VectorType, typename Iterable, typename Function&gt;
+static inline void iterateIntersections(const VectorType&amp; singularTransitionsFirsts, const Iterable&amp; iterableTransitionList, const Function&amp; intersectionHandler)
</ins><span class="cx"> {
</span><del>-    for (DFANode&amp; dfaNode : dfa.nodes) {
-        bool addingFallback = false;
-        uint32_t newFallbackDestination = std::numeric_limits&lt;uint32_t&gt;::max();
-        if (!dfaNode.hasFallbackTransition()
-            &amp;&amp; ((dfaNode.transitionsLength() == 126 &amp;&amp; !dfaNode.containsTransition(0, dfa))
-                || (dfaNode.transitionsLength() == 127 &amp;&amp; dfaNode.containsTransition(0, dfa)))) {
-            unsigned bestTarget = std::numeric_limits&lt;unsigned&gt;::max();
-            unsigned bestTargetScore = 0;
-            HashMap&lt;unsigned, unsigned, DefaultHash&lt;unsigned&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;unsigned&gt;&gt; targetHistogram;
-            for (const auto&amp; transition : dfaNode.transitions(dfa)) {
-                if (!transition.first)
-                    continue;
</del><ins>+    ASSERT(!singularTransitionsFirsts.isEmpty());
+    auto otherIterator = iterableTransitionList.begin();
+    auto otherEnd = iterableTransitionList.end();
</ins><span class="cx"> 
</span><del>-                unsigned transitionTarget = transition.second;
-                auto addResult = targetHistogram.add(transitionTarget, 1);
-                if (!addResult.isNewEntry)
-                    addResult.iterator-&gt;value++;
</del><ins>+    if (otherIterator == otherEnd)
+        return;
</ins><span class="cx"> 
</span><del>-                if (addResult.iterator-&gt;value &gt; bestTargetScore) {
-                    bestTargetScore = addResult.iterator-&gt;value;
-                    bestTarget = transitionTarget;
-                }
-            }
-            ASSERT_WITH_MESSAGE(bestTargetScore, &quot;There should be at least one valid target since having transitions is a precondition to enter this path.&quot;);
</del><ins>+    unsigned singularTransitionsLength = singularTransitionsFirsts.size();
+    unsigned singularTransitionsFirstsIndex = 0;
+    for (; otherIterator != otherEnd; ++otherIterator) {
+        auto firstCharacter = otherIterator.first();
+        while (singularTransitionsFirstsIndex &lt; singularTransitionsLength
+            &amp;&amp; singularTransitionsFirsts[singularTransitionsFirstsIndex] != firstCharacter)
+            ++singularTransitionsFirstsIndex;
</ins><span class="cx"> 
</span><del>-            newFallbackDestination = bestTarget;
-            addingFallback = true;
-        }
-        
-        // Use the same location to write new transitions possibly followed by unused memory.
-        // We can do this because we are always decreasing the amount of memory used.
-        // We will probably need something like setHasFallbackTransitionWithoutChangingDFA to do that.
</del><ins>+        intersectionHandler(singularTransitionsFirstsIndex, otherIterator);
+        ++singularTransitionsFirstsIndex;
</ins><span class="cx"> 
</span><del>-        unsigned oldFallbackTransition = std::numeric_limits&lt;uint32_t&gt;::max();
-        bool hadFallbackTransition = dfaNode.hasFallbackTransition();
-        if (hadFallbackTransition)
-            oldFallbackTransition = dfaNode.fallbackTransitionDestination(dfa);
-        
-        newFallbackDestination = (newFallbackDestination == std::numeric_limits&lt;uint32_t&gt;::max() ? oldFallbackTransition : newFallbackDestination);
-        ASSERT(!addingFallback || (newFallbackDestination != std::numeric_limits&lt;uint32_t&gt;::max() &amp;&amp; oldFallbackTransition == std::numeric_limits&lt;uint32_t&gt;::max()));
-        bool willHaveFallback = newFallbackDestination != std::numeric_limits&lt;uint32_t&gt;::max();
-        dfaNode.setHasFallbackTransitionWithoutChangingDFA(willHaveFallback);
-        
-        if (willHaveFallback) {
-            Vector&lt;std::pair&lt;uint8_t, uint32_t&gt;&gt; transitions = dfaNode.transitions(dfa);
-            unsigned availableSlotCount = transitions.size() + hadFallbackTransition;
-            for (unsigned i = 0; i &lt; transitions.size(); ++i) {
-                if (transitions[i].second == newFallbackDestination)
-                    transitions.remove(i--);
-            }
-        
-            RELEASE_ASSERT(transitions.size() + willHaveFallback &lt;= availableSlotCount);
-        
-            unsigned firstSlot = dfaNode.transitionsStart();
-            dfaNode.resetTransitions(firstSlot, transitions.size());
-            for (unsigned i = 0; i &lt; transitions.size(); ++i) {
-                dfa.transitionCharacters[firstSlot + i] = transitions[i].first;
-                dfa.transitionDestinations[firstSlot + i] = transitions[i].second;
-            }
-            for (unsigned i = transitions.size(); i &lt; availableSlotCount; ++i) {
-                // Invalidate now-unused memory to make finding bugs easier.
-                dfa.transitionCharacters[firstSlot + i] = std::numeric_limits&lt;uint8_t&gt;::max();
-                dfa.transitionDestinations[firstSlot + i] = std::numeric_limits&lt;uint32_t&gt;::max();
-            }
-            if (willHaveFallback) {
-                dfa.transitionCharacters[firstSlot + transitions.size()] = std::numeric_limits&lt;uint8_t&gt;::max();
-                dfa.transitionDestinations[firstSlot + transitions.size()] = newFallbackDestination;
-            }
</del><ins>+        auto lastCharacter = otherIterator.last();
+        while (singularTransitionsFirstsIndex &lt; singularTransitionsLength
+            &amp;&amp; singularTransitionsFirsts[singularTransitionsFirstsIndex] &lt;= lastCharacter) {
+            intersectionHandler(singularTransitionsFirstsIndex, otherIterator);
+            ++singularTransitionsFirstsIndex;
</ins><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="lines">@@ -137,8 +91,33 @@
</span><span class="cx">         m_sets.append(SetDescriptor({ 0, size, 0 }));
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void markElementInCurrentGeneration(unsigned elementIndex)
</del><ins>+    void reserveUninitializedCapacity(unsigned elementCount)
</ins><span class="cx">     {
</span><ins>+        m_partitionedElements.resize(elementCount);
+        m_elementPositionInPartitionedNodes.resize(elementCount);
+        m_elementToSetMap.resize(elementCount);
+    }
+
+    void addSetUnchecked(unsigned start, unsigned size)
+    {
+        m_sets.append(SetDescriptor { start, size, 0 });
+    }
+
+    void setElementUnchecked(unsigned elementIndex, unsigned positionInPartition, unsigned setIndex)
+    {
+        ASSERT(setIndex &lt; m_sets.size());
+        m_partitionedElements[positionInPartition] = elementIndex;
+        m_elementPositionInPartitionedNodes[elementIndex] = positionInPartition;
+        m_elementToSetMap[elementIndex] = setIndex;
+    }
+
+    unsigned startOffsetOfSet(unsigned setIndex) const
+    {
+        return m_sets[setIndex].start;
+    }
+
+    ALWAYS_INLINE void markElementInCurrentGeneration(unsigned elementIndex)
+    {
</ins><span class="cx">         // Swap the node with the first unmarked node.
</span><span class="cx">         unsigned setIndex = m_elementToSetMap[elementIndex];
</span><span class="cx">         SetDescriptor&amp; setDescriptor = m_sets[setIndex];
</span><span class="lines">@@ -238,45 +217,66 @@
</span><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     // List of sets.
</span><del>-    Vector&lt;SetDescriptor&gt; m_sets;
</del><ins>+    Vector&lt;SetDescriptor, 0, UnsafeVectorOverflow&gt; m_sets;
</ins><span class="cx"> 
</span><span class="cx">     // All the element indices such that two elements of the same set never have a element of a different set between them.
</span><del>-    Vector&lt;unsigned&gt; m_partitionedElements;
</del><ins>+    Vector&lt;unsigned, 0, UnsafeVectorOverflow&gt; m_partitionedElements;
</ins><span class="cx"> 
</span><span class="cx">     // Map elementIndex-&gt;position in the partitionedElements.
</span><del>-    Vector&lt;unsigned&gt; m_elementPositionInPartitionedNodes;
</del><ins>+    Vector&lt;unsigned, 0, UnsafeVectorOverflow&gt; m_elementPositionInPartitionedNodes;
</ins><span class="cx"> 
</span><span class="cx">     // Map elementIndex-&gt;SetIndex.
</span><del>-    Vector&lt;unsigned&gt; m_elementToSetMap;
</del><ins>+    Vector&lt;unsigned, 0, UnsafeVectorOverflow&gt; m_elementToSetMap;
</ins><span class="cx"> 
</span><span class="cx">     // List of sets with any marked node. Each set can appear at most once.
</span><span class="cx">     // FIXME: find a good inline size for this.
</span><del>-    Vector&lt;unsigned, 128&gt; m_setsMarkedInCurrentGeneration;
</del><ins>+    Vector&lt;unsigned, 128, UnsafeVectorOverflow&gt; m_setsMarkedInCurrentGeneration;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> class FullGraphPartition {
</span><ins>+    typedef MutableRangeList&lt;char, uint32_t, 128&gt; SingularTransitionsMutableRangeList;
</ins><span class="cx"> public:
</span><span class="cx">     FullGraphPartition(const DFA&amp; dfa)
</span><span class="cx">     {
</span><span class="cx">         m_nodePartition.initialize(dfa.nodes.size());
</span><span class="cx"> 
</span><del>-        m_flattenedTransitionsStartOffsetPerNode.resize(dfa.nodes.size());
-        for (unsigned&amp; counter : m_flattenedTransitionsStartOffsetPerNode)
-            counter = 0;
</del><ins>+        SingularTransitionsMutableRangeList singularTransitions;
+        CounterConverter counterConverter;
+        for (const DFANode&amp; node : dfa.nodes) {
+            if (node.isKilled())
+                continue;
+            auto transitions = node.transitions(dfa);
+            singularTransitions.extend(transitions.begin(), transitions.end(), counterConverter);
+        }
</ins><span class="cx"> 
</span><del>-        m_flattenedFallbackTransitionsStartOffsetPerNode.resize(dfa.nodes.size());
-        for (unsigned&amp; counter : m_flattenedFallbackTransitionsStartOffsetPerNode)
-            counter = 0;
</del><ins>+        // Count the number of transition for each singular range. This will give us the bucket size
+        // for the transition partition, where transitions are partitioned by &quot;symbol&quot;.
+        unsigned rangeIndexAccumulator = 0;
+        for (const auto&amp; transition : singularTransitions) {
+            m_transitionPartition.addSetUnchecked(rangeIndexAccumulator, transition.data);
+            rangeIndexAccumulator += transition.data;
+        }
</ins><span class="cx"> 
</span><span class="cx">         // Count the number of incoming transitions per node.
</span><del>-        for (const DFANode&amp; dfaNode : dfa.nodes) {
-            for (const auto&amp; transition : dfaNode.transitions(dfa))
-                ++m_flattenedTransitionsStartOffsetPerNode[transition.second];
-            if (dfaNode.hasFallbackTransition())
-                ++m_flattenedFallbackTransitionsStartOffsetPerNode[dfaNode.fallbackTransitionDestination(dfa)];
</del><ins>+        m_flattenedTransitionsStartOffsetPerNode.resize(dfa.nodes.size());
+        memset(m_flattenedTransitionsStartOffsetPerNode.data(), 0, m_flattenedTransitionsStartOffsetPerNode.size() * sizeof(unsigned));
+
+        Vector&lt;char, 0, UnsafeVectorOverflow&gt; singularTransitionsFirsts;
+        singularTransitionsFirsts.reserveInitialCapacity(singularTransitions.m_ranges.size());
+        for (const auto&amp; transition : singularTransitions)
+            singularTransitionsFirsts.uncheckedAppend(transition.first);
+
+        for (const DFANode&amp; node : dfa.nodes) {
+            if (node.isKilled())
+                continue;
+            auto transitions = node.transitions(dfa);
+            iterateIntersections(singularTransitionsFirsts, transitions, [&amp;](unsigned, const DFANode::ConstRangeIterator&amp; origin) {
+                uint32_t targetNodeIndex = origin.target();
+                ++m_flattenedTransitionsStartOffsetPerNode[targetNodeIndex];
+            });
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        // Accumulate the offsets.
</del><ins>+        // Accumulate the offsets. This gives us the start position of each bucket.
</ins><span class="cx">         unsigned transitionAccumulator = 0;
</span><span class="cx">         for (unsigned i = 0; i &lt; m_flattenedTransitionsStartOffsetPerNode.size(); ++i) {
</span><span class="cx">             unsigned transitionsCountForNode = m_flattenedTransitionsStartOffsetPerNode[i];
</span><span class="lines">@@ -284,65 +284,42 @@
</span><span class="cx">             transitionAccumulator += transitionsCountForNode;
</span><span class="cx">         }
</span><span class="cx">         unsigned flattenedTransitionsSize = transitionAccumulator;
</span><ins>+        ASSERT_WITH_MESSAGE(flattenedTransitionsSize == rangeIndexAccumulator, &quot;The number of transitions should be the same, regardless of how they are arranged in buckets.&quot;);
</ins><span class="cx"> 
</span><del>-        unsigned fallbackTransitionAccumulator = 0;
-        for (unsigned i = 0; i &lt; m_flattenedFallbackTransitionsStartOffsetPerNode.size(); ++i) {
-            unsigned fallbackTransitionsCountForNode = m_flattenedFallbackTransitionsStartOffsetPerNode[i];
-            m_flattenedFallbackTransitionsStartOffsetPerNode[i] = fallbackTransitionAccumulator;
-            fallbackTransitionAccumulator += fallbackTransitionsCountForNode;
-        }
-        unsigned flattenedFallbackTransitionsSize = fallbackTransitionAccumulator;
</del><ins>+        m_transitionPartition.reserveUninitializedCapacity(flattenedTransitionsSize);
</ins><span class="cx"> 
</span><span class="cx">         // Next, let's fill the transition table and set up the size of each group at the same time.
</span><span class="cx">         m_flattenedTransitionsSizePerNode.resize(dfa.nodes.size());
</span><span class="cx">         for (unsigned&amp; counter : m_flattenedTransitionsSizePerNode)
</span><span class="cx">             counter = 0;
</span><del>-
-        m_flattenedFallbackTransitionsSizePerNode.resize(dfa.nodes.size());
-        for (unsigned&amp; counter : m_flattenedFallbackTransitionsSizePerNode)
-            counter = 0;
-
</del><span class="cx">         m_flattenedTransitions.resize(flattenedTransitionsSize);
</span><span class="cx"> 
</span><del>-        m_flattenedFallbackTransitions.resize(flattenedFallbackTransitionsSize);
</del><ins>+        Vector&lt;uint32_t&gt; transitionPerRangeOffset(m_transitionPartition.size());
+        memset(transitionPerRangeOffset.data(), 0, transitionPerRangeOffset.size() * sizeof(uint32_t));
</ins><span class="cx"> 
</span><span class="cx">         for (unsigned i = 0; i &lt; dfa.nodes.size(); ++i) {
</span><del>-            const DFANode&amp; dfaNode = dfa.nodes[i];
-            for (const auto&amp; transition : dfaNode.transitions(dfa)) {
-                unsigned targetNodeIndex = transition.second;
</del><ins>+            const DFANode&amp; node = dfa.nodes[i];
+            if (node.isKilled())
+                continue;
</ins><span class="cx"> 
</span><ins>+            auto transitions = node.transitions(dfa);
+            iterateIntersections(singularTransitionsFirsts, transitions, [&amp;](unsigned singularTransitonIndex, const DFANode::ConstRangeIterator&amp; origin) {
+                uint32_t targetNodeIndex = origin.target();
+
</ins><span class="cx">                 unsigned start = m_flattenedTransitionsStartOffsetPerNode[targetNodeIndex];
</span><span class="cx">                 unsigned offset = m_flattenedTransitionsSizePerNode[targetNodeIndex];
</span><ins>+                unsigned positionInFlattenedTransitions = start + offset;
+                m_flattenedTransitions[positionInFlattenedTransitions] = Transition({ i });
</ins><span class="cx"> 
</span><del>-                m_flattenedTransitions[start + offset] = Transition({ i, targetNodeIndex, transition.first });
</del><ins>+                uint32_t&amp; inRangeOffset = transitionPerRangeOffset[singularTransitonIndex];
+                unsigned positionInTransitionPartition = m_transitionPartition.startOffsetOfSet(singularTransitonIndex) + inRangeOffset;
+                ++inRangeOffset;
</ins><span class="cx"> 
</span><del>-                ++m_flattenedTransitionsSizePerNode[targetNodeIndex];
-            }
-            if (dfaNode.hasFallbackTransition()) {
-                unsigned targetNodeIndex = dfaNode.fallbackTransitionDestination(dfa);
</del><ins>+                m_transitionPartition.setElementUnchecked(positionInFlattenedTransitions, positionInTransitionPartition, singularTransitonIndex);
</ins><span class="cx"> 
</span><del>-                unsigned start = m_flattenedFallbackTransitionsStartOffsetPerNode[targetNodeIndex];
-                unsigned offset = m_flattenedFallbackTransitionsSizePerNode[targetNodeIndex];
-
-                m_flattenedFallbackTransitions[start + offset] = i;
-                ++m_flattenedFallbackTransitionsSizePerNode[targetNodeIndex];
-            }
</del><ins>+                ++m_flattenedTransitionsSizePerNode[targetNodeIndex];
+            });
</ins><span class="cx">         }
</span><del>-
-        // Create the initial partition of transition. Each character differentiating a transiton
-        // from an other gets its own set in the partition.
-        m_transitionPartition.initialize(m_flattenedTransitions.size());
-        for (uint16_t i = 0; i &lt; 128; ++i) {
-            for (unsigned transitionIndex = 0; transitionIndex &lt; m_flattenedTransitions.size(); ++transitionIndex) {
-                const Transition&amp; transition = m_flattenedTransitions[transitionIndex];
-                if (transition.character == i)
-                    m_transitionPartition.markElementInCurrentGeneration(transitionIndex);
-            }
-            m_transitionPartition.refineGeneration([](unsigned) { });
-        }
-
-        // Fallback partitions are considered as a special type of differentiator, we don't split them initially.
-        m_fallbackTransitionPartition.initialize(m_flattenedFallbackTransitions.size());
</del><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     void markNode(unsigned nodeIndex)
</span><span class="lines">@@ -359,17 +336,10 @@
</span><span class="cx"> 
</span><span class="cx">                 for (unsigned i = 0; i &lt; incomingTransitionsSizeForNode; ++i)
</span><span class="cx">                     m_transitionPartition.markElementInCurrentGeneration(incomingTransitionsStartForNode + i);
</span><del>-
-                unsigned incomingFallbackTransitionsStartForNode = m_flattenedFallbackTransitionsStartOffsetPerNode[nodeIndex];
-                unsigned incomingFallbackTransitionsSizeForNode = m_flattenedFallbackTransitionsSizePerNode[nodeIndex];
-
-                for (unsigned i = 0; i &lt; incomingFallbackTransitionsSizeForNode; ++i)
-                    m_fallbackTransitionPartition.markElementInCurrentGeneration(incomingFallbackTransitionsStartForNode + i);
</del><span class="cx">             });
</span><span class="cx"> 
</span><span class="cx">             // We only need to split the transitions, we handle the new sets through the main loop.
</span><span class="cx">             m_transitionPartition.refineGeneration([](unsigned) { });
</span><del>-            m_fallbackTransitionPartition.refineGeneration([](unsigned) { });
</del><span class="cx">         });
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -386,23 +356,6 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    void splitByFallbackTransitions()
-    {
-        ASSERT_WITH_MESSAGE(m_nextTransitionSetToProcess || !m_transitionPartition.size(), &quot;We can only distinguish nodes by fallback transition *after* all other transitions are covered. Doing otherwise would be incorrect since the unique transitions from 2 nodes could cover all possible transitions.&quot;);
-
-        for (unsigned fallbackTransitionSetIndex = 0; fallbackTransitionSetIndex &lt; m_fallbackTransitionPartition.size(); ++fallbackTransitionSetIndex) {
-
-            m_fallbackTransitionPartition.iterateSet(fallbackTransitionSetIndex, [&amp;](unsigned transitionIndex) {
-                unsigned sourceNodeIndex = m_flattenedFallbackTransitions[transitionIndex];
-                m_nodePartition.markElementInCurrentGeneration(sourceNodeIndex);
-            });
-            refinePartitions();
-
-            // Any new split need to spread to all the unique transition that can reach the two new sets.
-            splitByUniqueTransitions();
-        }
-    }
-
</del><span class="cx">     unsigned nodeReplacement(unsigned nodeIndex)
</span><span class="cx">     {
</span><span class="cx">         unsigned setIndex = m_nodePartition.setIndex(nodeIndex);
</span><span class="lines">@@ -412,21 +365,26 @@
</span><span class="cx"> private:
</span><span class="cx">     struct Transition {
</span><span class="cx">         unsigned source;
</span><del>-        unsigned target;
-        uint16_t character;
</del><span class="cx">     };
</span><span class="cx"> 
</span><del>-    Vector&lt;unsigned&gt; m_flattenedTransitionsStartOffsetPerNode;
-    Vector&lt;unsigned&gt; m_flattenedTransitionsSizePerNode;
-    Vector&lt;unsigned&gt; m_flattenedFallbackTransitionsStartOffsetPerNode;
-    Vector&lt;unsigned&gt; m_flattenedFallbackTransitionsSizePerNode;
</del><ins>+    struct CounterConverter {
+        uint32_t convert(uint32_t)
+        {
+            return 1;
+        }
</ins><span class="cx"> 
</span><del>-    Vector&lt;Transition&gt; m_flattenedTransitions;
-    Vector&lt;unsigned&gt; m_flattenedFallbackTransitions;
</del><ins>+        void extend(uint32_t&amp; destination, uint32_t)
+        {
+            ++destination;
+        }
+    };
</ins><span class="cx"> 
</span><ins>+    Vector&lt;unsigned, 0, UnsafeVectorOverflow&gt; m_flattenedTransitionsStartOffsetPerNode;
+    Vector&lt;unsigned, 0, UnsafeVectorOverflow&gt; m_flattenedTransitionsSizePerNode;
+    Vector&lt;Transition, 0, UnsafeVectorOverflow&gt; m_flattenedTransitions;
+
</ins><span class="cx">     Partition m_nodePartition;
</span><span class="cx">     Partition m_transitionPartition;
</span><del>-    Partition m_fallbackTransitionPartition;
</del><span class="cx"> 
</span><span class="cx">     unsigned m_nextTransitionSetToProcess { 0 };
</span><span class="cx"> };
</span><span class="lines">@@ -494,8 +452,6 @@
</span><span class="cx"> 
</span><span class="cx"> void DFAMinimizer::minimize(DFA&amp; dfa)
</span><span class="cx"> {
</span><del>-    simplifyTransitions(dfa);
-
</del><span class="cx">     FullGraphPartition fullGraphPartition(dfa);
</span><span class="cx"> 
</span><span class="cx">     // Unlike traditional minimization final states can be differentiated by their action.
</span><span class="lines">@@ -520,21 +476,12 @@
</span><span class="cx">     // Use every splitter to refine the node partitions.
</span><span class="cx">     fullGraphPartition.splitByUniqueTransitions();
</span><span class="cx"> 
</span><del>-    // At this stage, we know that no pair of state can be distinguished by the individual transitions. They can still
-    // be distinguished by their fallback transitions.
-    //
-    // For example, two states [1, 2] can both have a transition on 'x' to a state [3], but each have fallback transition
-    // to different states [4] and [5].
-    //
-    // Here, we distinguish such cases and at each stage refine the new discovered sets by their individual transitions.
-    fullGraphPartition.splitByFallbackTransitions();
-
</del><span class="cx">     Vector&lt;unsigned&gt; relocationVector;
</span><span class="cx">     relocationVector.reserveInitialCapacity(dfa.nodes.size());
</span><span class="cx">     for (unsigned i = 0; i &lt; dfa.nodes.size(); ++i)
</span><span class="cx">         relocationVector.uncheckedAppend(i);
</span><span class="cx"> 
</span><del>-    // Kill the useless nodes and keep track of the new node transitions should point to.
</del><ins>+    // Update all the transitions.
</ins><span class="cx">     for (unsigned i = 0; i &lt; dfa.nodes.size(); ++i) {
</span><span class="cx">         unsigned replacement = fullGraphPartition.nodeReplacement(i);
</span><span class="cx">         if (i != replacement) {
</span><span class="lines">@@ -543,19 +490,18 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    dfa.root = relocationVector[dfa.root];
</ins><span class="cx">     for (DFANode&amp; node : dfa.nodes) {
</span><del>-        auto nodeTransitions = node.transitions(dfa);
-        for (unsigned i = 0; i &lt; node.transitionsLength(); ++i)
-            dfa.transitionDestinations[node.transitionsStart() + i] = relocationVector[nodeTransitions[i].second];
-        if (node.hasFallbackTransition())
-            node.changeFallbackTransition(dfa, relocationVector[node.fallbackTransitionDestination(dfa)]);
-    }
</del><ins>+        if (node.isKilled())
+            continue;
</ins><span class="cx"> 
</span><del>-    // After minimizing, there is no guarantee individual transition are still poiting to different states.
-    // The state pointed by one individual transition and the fallback states may have been merged.
-    simplifyTransitions(dfa);
-
-    dfa.root = relocationVector[dfa.root];
</del><ins>+        for (auto&amp; transition : node.transitions(dfa)) {
+            uint32_t target = transition.target();
+            uint32_t relocatedTarget = relocationVector[target];
+            if (target != relocatedTarget)
+                transition.resetTarget(relocatedTarget);
+        }
+    }
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> } // namespace ContentExtensions
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsDFANodecpp"></a>
<div class="addfile"><h4>Added: trunk/Source/WebCore/contentextensions/DFANode.cpp (0 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/DFANode.cpp                                (rev 0)
+++ trunk/Source/WebCore/contentextensions/DFANode.cpp        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -0,0 +1,147 @@
</span><ins>+/*
+ * Copyright (C) 2014, 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;DFANode.h&quot;
+
+#include &quot;DFA.h&quot;
+
+#if ENABLE(CONTENT_EXTENSIONS)
+
+namespace WebCore {
+
+namespace ContentExtensions {
+
+Vector&lt;uint64_t&gt; DFANode::actions(const DFA&amp; dfa) const
+{
+    // FIXME: Use iterators instead of copying the Vector elements.
+    Vector&lt;uint64_t&gt; vector;
+    vector.reserveInitialCapacity(m_actionsLength);
+    for (uint32_t i = m_actionsStart; i &lt; m_actionsStart + m_actionsLength; ++i)
+        vector.uncheckedAppend(dfa.actions[i]);
+    return vector;
+}
+
+bool DFANode::containsTransition(char transition, const DFA&amp; dfa) const
+{
+    // Called from DFAMinimizer, this loops though a maximum of 128 transitions, so it's not too slow.
+    ASSERT(m_transitionsLength &lt;= 128);
+    for (unsigned i = m_transitionsStart; i &lt; m_transitionsStart + m_transitionsLength; ++i) {
+        if (dfa.transitionRanges[i].first &lt;= transition
+            &amp;&amp; dfa.transitionRanges[i].last &gt;= transition)
+            return true;
+    }
+    return false;
+}
+
+void DFANode::kill(DFA&amp; dfa)
+{
+    ASSERT(m_flags != IsKilled);
+    m_flags = IsKilled; // Killed nodes don't have any other flags.
+
+    // Invalidate the now-unused memory in the DFA to make finding bugs easier.
+    for (unsigned i = m_transitionsStart; i &lt; m_transitionsStart + m_transitionsLength; ++i) {
+        dfa.transitionRanges[i] = { -1, -1 };
+        dfa.transitionDestinations[i] = std::numeric_limits&lt;uint32_t&gt;::max();
+    }
+    for (unsigned i = m_actionsStart; i &lt; m_actionsStart + m_actionsLength; ++i)
+        dfa.actions[i] = std::numeric_limits&lt;uint64_t&gt;::max();
+
+    m_actionsStart = 0;
+    m_actionsLength = 0;
+    m_transitionsStart = 0;
+    m_transitionsLength = 0;
+};
+
+bool DFANode::canUseFallbackTransition(const DFA&amp; dfa) const
+{
+    // Transitions can contain '\0' if the expression has a end-of-line marker.
+    // Fallback transitions cover 1-127. We have to be careful with the first.
+
+    IterableConstRange iterableTransitions = transitions(dfa);
+    auto iterator = iterableTransitions.begin();
+    auto end = iterableTransitions.end();
+    if (iterator == end)
+        return false;
+
+    char lastSeenCharacter = 0;
+    if (!iterator.first()) {
+        lastSeenCharacter = iterator.last();
+        if (lastSeenCharacter == 127)
+            return true;
+        ++iterator;
+    }
+
+    for (;iterator != end; ++iterator) {
+        ASSERT(iterator.first() &gt; lastSeenCharacter);
+        if (iterator.first() != lastSeenCharacter + 1)
+            return false;
+
+        if (iterator.range().last == 127)
+            return true;
+        lastSeenCharacter = iterator.last();
+    }
+    return false;
+}
+
+uint32_t DFANode::bestFallbackTarget(const DFA&amp; dfa) const
+{
+    ASSERT(canUseFallbackTransition(dfa));
+
+    HashMap&lt;uint32_t, unsigned, DefaultHash&lt;uint32_t&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;uint32_t&gt;&gt; histogram;
+
+    IterableConstRange iterableTransitions = transitions(dfa);
+    auto iterator = iterableTransitions.begin();
+    auto end = iterableTransitions.end();
+    ASSERT_WITH_MESSAGE(iterator != end, &quot;An empty range list cannot use a fallback transition.&quot;);
+
+    if (!iterator.first() &amp;&amp; !iterator.last())
+        ++iterator;
+    ASSERT_WITH_MESSAGE(iterator != end, &quot;An empty range list matching only zero cannot use a fallback transition.&quot;);
+
+    uint32_t bestTarget = iterator.target();
+    unsigned bestTargetScore = !iterator.range().first ? iterator.range().size() - 1 : iterator.range().size();
+    histogram.add(bestTarget, bestTargetScore);
+    ++iterator;
+
+    for (;iterator != end; ++iterator) {
+        unsigned rangeSize = iterator.range().size();
+        uint32_t target = iterator.target();
+        auto addResult = histogram.add(target, rangeSize);
+        if (!addResult.isNewEntry)
+            addResult.iterator-&gt;value += rangeSize;
+        if (addResult.iterator-&gt;value &gt; bestTargetScore) {
+            bestTargetScore = addResult.iterator-&gt;value;
+            bestTarget = target;
+        }
+    }
+    return bestTarget;
+}
+
+} // namespace ContentExtensions
+
+} // namespace WebCore
+
+#endif // ENABLE(CONTENT_EXTENSIONS)
</ins></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsDFANodeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/DFANode.h (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/DFANode.h        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/contentextensions/DFANode.h        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -38,27 +38,112 @@
</span><span class="cx"> 
</span><span class="cx"> struct DFA;
</span><span class="cx"> 
</span><ins>+struct CharRange {
+    char first;
+    char last;
+    unsigned size() const { return last - first + 1; }
+};
+
</ins><span class="cx"> // A DFANode abstract the transition table out of a DFA state. If a state is accepting, the DFANode also have
</span><span class="cx"> // the actions for that state.
</span><span class="cx"> class DFANode {
</span><span class="cx"> public:
</span><ins>+    struct ConstRangeIterator {
+        const DFA&amp; dfa;
+        uint32_t position;
+
+        const ConstRangeIterator&amp; operator*() const { return *this; }
+
+        bool operator==(const ConstRangeIterator&amp; other) const
+        {
+            ASSERT(&amp;dfa  == &amp;other.dfa);
+            return position == other.position;
+        }
+        bool operator!=(const ConstRangeIterator&amp; other) const { return !(*this == other); }
+
+        ConstRangeIterator&amp; operator++()
+        {
+            ++position;
+            return *this;
+        }
+
+        const CharRange&amp; range() const;
+        uint32_t target() const;
+
+        char first() const { return range().first; }
+        char last() const { return range().last; }
+        uint32_t data() const { return target(); }
+    };
+
+    struct IterableConstRange {
+        const DFA&amp; dfa;
+        uint32_t rangesStart;
+        uint32_t rangesEnd;
+
+        ConstRangeIterator begin() const { return { dfa, rangesStart }; }
+        ConstRangeIterator end() const { return { dfa, rangesEnd }; }
+    };
+
+    IterableConstRange transitions(const DFA&amp; dfa) const
+    {
+        return IterableConstRange { dfa, m_transitionsStart, m_transitionsStart + m_transitionsLength };
+    }
+
+    struct RangeIterator {
+        DFA&amp; dfa;
+        uint32_t position;
+
+        RangeIterator&amp; operator*() { return *this; }
+
+        bool operator==(const RangeIterator&amp; other) const
+        {
+            ASSERT(&amp;dfa  == &amp;other.dfa);
+            return position == other.position;
+        }
+        bool operator!=(const RangeIterator&amp; other) const { return !(*this == other); }
+
+        RangeIterator&amp; operator++()
+        {
+            ++position;
+            return *this;
+        }
+
+        const CharRange&amp; range() const;
+        uint32_t target() const;
+        void resetTarget(uint32_t);
+
+        char first() const { return range().first; }
+        char last() const { return range().last; }
+        uint32_t data() const { return target(); }
+    };
+
+    struct IterableRange {
+        DFA&amp; dfa;
+        uint32_t rangesStart;
+        uint32_t rangesEnd;
+
+        RangeIterator begin() const { return { dfa, rangesStart }; }
+        RangeIterator end() const { return { dfa, rangesEnd }; }
+    };
+
+    IterableRange transitions(DFA&amp; dfa)
+    {
+        return IterableRange { dfa, m_transitionsStart, m_transitionsStart + m_transitionsLength };
+    }
+
</ins><span class="cx">     // FIXME: Stop minimizing killed nodes and add ASSERT(!isKilled()) in many functions here.
</span><span class="cx">     void kill(DFA&amp;);
</span><span class="cx">     Vector&lt;uint64_t&gt; actions(const DFA&amp;) const;
</span><del>-    Vector&lt;std::pair&lt;uint8_t, uint32_t&gt;&gt; transitions(const DFA&amp;) const;
-    uint32_t fallbackTransitionDestination(const DFA&amp;) const;
-    void addFallbackTransition(DFA&amp;, uint32_t destination);
-    bool containsTransition(uint8_t, DFA&amp;);
-    void changeFallbackTransition(DFA&amp;, uint32_t newDestination);
</del><ins>+    bool containsTransition(char, const DFA&amp;) const;
</ins><span class="cx">     
</span><span class="cx">     bool isKilled() const { return m_flags &amp; IsKilled; }
</span><del>-    bool hasFallbackTransition() const { return m_flags &amp; HasFallbackTransition; }
</del><span class="cx">     bool hasActions() const { return !!m_actionsLength; }
</span><del>-    uint8_t transitionsLength() const { return m_transitionsLength; }
</del><span class="cx">     uint16_t actionsLength() const { return m_actionsLength; }
</span><span class="cx">     uint32_t actionsStart() const { return m_actionsStart; }
</span><del>-    uint32_t transitionsStart() const { return m_transitionsStart; }
-    
</del><ins>+
+    bool canUseFallbackTransition(const DFA&amp;) const;
+    uint32_t bestFallbackTarget(const DFA&amp;) const;
+
</ins><span class="cx">     void setActions(uint32_t start, uint16_t length)
</span><span class="cx">     {
</span><span class="cx">         ASSERT(!m_actionsStart);
</span><span class="lines">@@ -73,22 +158,7 @@
</span><span class="cx">         m_transitionsStart = start;
</span><span class="cx">         m_transitionsLength = length;
</span><span class="cx">     }
</span><del>-    void resetTransitions(uint32_t start, uint16_t length)
-    {
-        m_transitionsStart = start;
-        m_transitionsLength = length;
-    }
-    void setHasFallbackTransitionWithoutChangingDFA(bool shouldHaveFallbackTransition)
-    {
-        if (shouldHaveFallbackTransition)
-            m_flags |= HasFallbackTransition;
-        else
-            m_flags &amp;= ~HasFallbackTransition;
-    }
-    
-#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
-    Vector&lt;unsigned&gt; correspondingNFANodes;
-#endif
</del><ins>+
</ins><span class="cx"> private:
</span><span class="cx">     uint32_t m_actionsStart { 0 };
</span><span class="cx">     uint32_t m_transitionsStart { 0 };
</span><span class="lines">@@ -96,17 +166,10 @@
</span><span class="cx">     uint8_t m_transitionsLength { 0 };
</span><span class="cx">     
</span><span class="cx">     uint8_t m_flags { 0 };
</span><del>-    const uint8_t HasFallbackTransition = 0x01;
-    const uint8_t IsKilled = 0x02;
</del><ins>+    const uint8_t IsKilled = 0x01;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><del>-// FIXME: Pack this down to 12.
-// It's probably already 12 on ARMv7.
-#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
-COMPILE_ASSERT(sizeof(DFANode) &lt;= 16 + sizeof(Vector&lt;unsigned&gt;), Keep the DFANodes small);
-#else
</del><span class="cx"> COMPILE_ASSERT(sizeof(DFANode) &lt;= 16, Keep the DFANodes small);
</span><del>-#endif
</del><span class="cx"> 
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsImmutableNFAh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/ImmutableNFA.h (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/ImmutableNFA.h        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/contentextensions/ImmutableNFA.h        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -93,9 +93,6 @@
</span><span class="cx">         const ImmutableNFA&amp; immutableNFA;
</span><span class="cx">         uint32_t position;
</span><span class="cx"> 
</span><del>-        const ImmutableRange&lt;CharacterType&gt;&amp; operator*() const { return immutableNFA.transitions[position]; }
-        const ImmutableRange&lt;CharacterType&gt;* operator-&gt;() const { return &amp;immutableNFA.transitions[position]; }
-
</del><span class="cx">         bool operator==(const ConstRangeIterator&amp; other) const
</span><span class="cx">         {
</span><span class="cx">             ASSERT(&amp;immutableNFA == &amp;other.immutableNFA);
</span><span class="lines">@@ -109,11 +106,27 @@
</span><span class="cx">             return *this;
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        CharacterType first() const
+        {
+            return range().first;
+        }
+
+        CharacterType last() const
+        {
+            return range().last;
+        }
+
</ins><span class="cx">         IterableConstTargets data() const
</span><span class="cx">         {
</span><del>-            const ImmutableRange&lt;CharacterType&gt;&amp; range = immutableNFA.transitions[position];
</del><ins>+            const ImmutableRange&lt;CharacterType&gt;&amp; range = this-&gt;range();
</ins><span class="cx">             return { immutableNFA, range.targetStart, range.targetEnd };
</span><span class="cx">         };
</span><ins>+
+    private:
+        const ImmutableRange&lt;CharacterType&gt;&amp; range() const
+        {
+            return immutableNFA.transitions[position];
+        }
</ins><span class="cx">     };
</span><span class="cx"> 
</span><span class="cx">     struct IterableConstRange {
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsImmutableNFANodeBuilderh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/ImmutableNFANodeBuilder.h (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/ImmutableNFANodeBuilder.h        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/contentextensions/ImmutableNFANodeBuilder.h        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -89,8 +89,8 @@
</span><span class="cx">     };
</span><span class="cx">     
</span><span class="cx">     struct FakeRangeIterator {
</span><del>-        const TrivialRange&amp; operator*() const { return range; }
-        const TrivialRange* operator-&gt;() const { return &amp;range; }
</del><ins>+        CharacterType first() const { return range.first; }
+        CharacterType last() const { return range.last; }
</ins><span class="cx">         uint32_t data() const { return targetId; }
</span><span class="cx">         bool operator==(const FakeRangeIterator&amp; other)
</span><span class="cx">         {
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsMutableRangeh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/MutableRange.h (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/MutableRange.h        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/contentextensions/MutableRange.h        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -89,8 +89,6 @@
</span><span class="cx">     uint32_t nextRangeIndex;
</span><span class="cx">     CharacterType first;
</span><span class="cx">     CharacterType last;
</span><del>-
-    CharacterType size() const { return last - first; }
</del><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsMutableRangeListh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/MutableRangeList.h (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/MutableRangeList.h        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/contentextensions/MutableRangeList.h        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -48,6 +48,10 @@
</span><span class="cx">         const TypedMutableRange&amp; operator*() const { return rangeList.m_ranges[index]; }
</span><span class="cx">         const TypedMutableRange* operator-&gt;() const { return &amp;rangeList.m_ranges[index]; }
</span><span class="cx"> 
</span><ins>+        CharacterType first() const { return rangeList.m_ranges[index].first; }
+        CharacterType last() const { return rangeList.m_ranges[index].last; }
+        CharacterType data() const { return rangeList.m_ranges[index].data; }
+
</ins><span class="cx">         bool operator==(const ConstIterator&amp; other) const
</span><span class="cx">         {
</span><span class="cx">             ASSERT(&amp;rangeList == &amp;other.rangeList);
</span><span class="lines">@@ -99,21 +103,21 @@
</span><span class="cx">         uint32_t lastSelfRangeIndex = 0;
</span><span class="cx">         uint32_t selfRangeIndex = 0;
</span><span class="cx"> 
</span><del>-        auto otherRangeOffset = otherIterator-&gt;first; // To get the right type :)
</del><ins>+        auto otherRangeOffset = otherIterator.first(); // To get the right type :)
</ins><span class="cx">         otherRangeOffset = 0;
</span><span class="cx"> 
</span><span class="cx">         do {
</span><span class="cx">             TypedMutableRange* activeSelfRange = &amp;m_ranges[selfRangeIndex];
</span><span class="cx"> 
</span><span class="cx">             // First, we move forward until we find something interesting.
</span><del>-            if (activeSelfRange-&gt;last &lt; otherIterator-&gt;first + otherRangeOffset) {
</del><ins>+            if (activeSelfRange-&gt;last &lt; otherIterator.first() + otherRangeOffset) {
</ins><span class="cx">                 lastSelfRangeIndex = selfRangeIndex;
</span><span class="cx">                 selfRangeIndex = activeSelfRange-&gt;nextRangeIndex;
</span><span class="cx">                 reachedSelfEnd = !selfRangeIndex;
</span><span class="cx">                 continue;
</span><span class="cx">             }
</span><del>-            if (otherIterator-&gt;last &lt; activeSelfRange-&gt;first) {
-                insertBetween(lastSelfRangeIndex, selfRangeIndex, otherIterator-&gt;first + otherRangeOffset, otherIterator-&gt;last, dataConverter.convert(otherIterator.data()));
</del><ins>+            if (otherIterator.last() &lt; activeSelfRange-&gt;first) {
+                insertBetween(lastSelfRangeIndex, selfRangeIndex, otherIterator.first() + otherRangeOffset, otherIterator.last(), dataConverter.convert(otherIterator.data()));
</ins><span class="cx"> 
</span><span class="cx">                 ++otherIterator;
</span><span class="cx">                 otherRangeOffset = 0;
</span><span class="lines">@@ -126,24 +130,24 @@
</span><span class="cx">             // But we don't know how they collide.
</span><span class="cx"> 
</span><span class="cx">             // Do we have a part on the left? Create a new range for it.
</span><del>-            if (activeSelfRange-&gt;first &lt; otherIterator-&gt;first + otherRangeOffset) {
</del><ins>+            if (activeSelfRange-&gt;first &lt; otherIterator.first() + otherRangeOffset) {
</ins><span class="cx">                 DataType copiedData = activeSelfRange-&gt;data;
</span><span class="cx">                 CharacterType newRangeFirstCharacter = activeSelfRange-&gt;first;
</span><del>-                activeSelfRange-&gt;first = otherIterator-&gt;first + otherRangeOffset;
-                insertBetween(lastSelfRangeIndex, selfRangeIndex, newRangeFirstCharacter, otherIterator-&gt;first + otherRangeOffset - 1, WTF::move(copiedData));
</del><ins>+                activeSelfRange-&gt;first = otherIterator.first() + otherRangeOffset;
+                insertBetween(lastSelfRangeIndex, selfRangeIndex, newRangeFirstCharacter, otherIterator.first() + otherRangeOffset - 1, WTF::move(copiedData));
</ins><span class="cx">                 activeSelfRange = &amp;m_ranges[selfRangeIndex];
</span><del>-            } else if (otherIterator-&gt;first + otherRangeOffset &lt; activeSelfRange-&gt;first) {
-                insertBetween(lastSelfRangeIndex, selfRangeIndex, otherIterator-&gt;first + otherRangeOffset, activeSelfRange-&gt;first - 1, dataConverter.convert(otherIterator.data()));
</del><ins>+            } else if (otherIterator.first() + otherRangeOffset &lt; activeSelfRange-&gt;first) {
+                insertBetween(lastSelfRangeIndex, selfRangeIndex, otherIterator.first() + otherRangeOffset, activeSelfRange-&gt;first - 1, dataConverter.convert(otherIterator.data()));
</ins><span class="cx"> 
</span><span class="cx">                 activeSelfRange = &amp;m_ranges[selfRangeIndex];
</span><del>-                ASSERT_WITH_MESSAGE(otherRangeOffset &lt; activeSelfRange-&gt;first - otherIterator-&gt;first, &quot;The offset must move forward or we could get stuck on this operation.&quot;);
-                otherRangeOffset = activeSelfRange-&gt;first - otherIterator-&gt;first;
</del><ins>+                ASSERT_WITH_MESSAGE(otherRangeOffset &lt; activeSelfRange-&gt;first - otherIterator.first(), &quot;The offset must move forward or we could get stuck on this operation.&quot;);
+                otherRangeOffset = activeSelfRange-&gt;first - otherIterator.first();
</ins><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             // Here, we know both ranges start at the same point, we need to create the part that intersect
</span><span class="cx">             // and merge the data.
</span><span class="cx"> 
</span><del>-            if (activeSelfRange-&gt;last == otherIterator-&gt;last) {
</del><ins>+            if (activeSelfRange-&gt;last == otherIterator.last()) {
</ins><span class="cx">                 // If they finish together, things are really easy: we just add B to A.
</span><span class="cx">                 dataConverter.extend(activeSelfRange-&gt;data, otherIterator.data());
</span><span class="cx"> 
</span><span class="lines">@@ -156,15 +160,15 @@
</span><span class="cx">                 continue;
</span><span class="cx">             }
</span><span class="cx"> 
</span><del>-            if (activeSelfRange-&gt;last &gt; otherIterator-&gt;last) {
</del><ins>+            if (activeSelfRange-&gt;last &gt; otherIterator.last()) {
</ins><span class="cx">                 // If A is bigger than B, we add a merged version and move A to the right.
</span><span class="cx"> 
</span><span class="cx">                 CharacterType combinedPartStart = activeSelfRange-&gt;first;
</span><del>-                activeSelfRange-&gt;first = otherIterator-&gt;last + 1;
</del><ins>+                activeSelfRange-&gt;first = otherIterator.last() + 1;
</ins><span class="cx"> 
</span><span class="cx">                 DataType combinedData = activeSelfRange-&gt;data;
</span><span class="cx">                 dataConverter.extend(combinedData, otherIterator.data());
</span><del>-                insertBetween(lastSelfRangeIndex, selfRangeIndex, combinedPartStart, otherIterator-&gt;last, WTF::move(combinedData));
</del><ins>+                insertBetween(lastSelfRangeIndex, selfRangeIndex, combinedPartStart, otherIterator.last(), WTF::move(combinedData));
</ins><span class="cx"> 
</span><span class="cx">                 ++otherIterator;
</span><span class="cx">                 otherRangeOffset = 0;
</span><span class="lines">@@ -172,22 +176,27 @@
</span><span class="cx">             }
</span><span class="cx"> 
</span><span class="cx">             // If we reached here, B ends after A. We merge the intersection and move on.
</span><del>-            ASSERT(otherIterator-&gt;last &gt; activeSelfRange-&gt;last);
</del><ins>+            ASSERT(otherIterator.last() &gt; activeSelfRange-&gt;last);
</ins><span class="cx">             dataConverter.extend(activeSelfRange-&gt;data, otherIterator.data());
</span><span class="cx"> 
</span><del>-            otherRangeOffset = activeSelfRange-&gt;last - otherIterator-&gt;first + 1;
</del><ins>+            otherRangeOffset = activeSelfRange-&gt;last - otherIterator.first() + 1;
</ins><span class="cx">             lastSelfRangeIndex = selfRangeIndex;
</span><span class="cx">             selfRangeIndex = activeSelfRange-&gt;nextRangeIndex;
</span><span class="cx">             reachedSelfEnd = !selfRangeIndex;
</span><span class="cx">         } while (!reachedSelfEnd &amp;&amp; otherIterator != otherEnd);
</span><span class="cx"> 
</span><span class="cx">         while (otherIterator != otherEnd) {
</span><del>-            lastSelfRangeIndex = appendRange(lastSelfRangeIndex, otherIterator-&gt;first + otherRangeOffset, otherIterator-&gt;last, dataConverter.convert(otherIterator.data()));
</del><ins>+            lastSelfRangeIndex = appendRange(lastSelfRangeIndex, otherIterator.first() + otherRangeOffset, otherIterator.last(), dataConverter.convert(otherIterator.data()));
</ins><span class="cx">             otherRangeOffset = 0;
</span><span class="cx">             ++otherIterator;
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    unsigned size() const
+    {
+        return m_ranges.size();
+    }
+
</ins><span class="cx">     bool isEmpty() const
</span><span class="cx">     {
</span><span class="cx">         return m_ranges.isEmpty();
</span><span class="lines">@@ -241,8 +250,8 @@
</span><span class="cx">         do {
</span><span class="cx">             m_ranges.append(TypedMutableRange(dataConverter.convert(otherIterator.data()),
</span><span class="cx">                 loopCounter + 1,
</span><del>-                otherIterator-&gt;first,
-                otherIterator-&gt;last));
</del><ins>+                otherIterator.first(),
+                otherIterator.last()));
</ins><span class="cx">             ++loopCounter;
</span><span class="cx">             ++otherIterator;
</span><span class="cx">         } while (otherIterator != otherEnd);
</span></span></pre></div>
<a id="trunkSourceWebCorecontentextensionsNFAToDFAcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/contentextensions/NFAToDFA.cpp (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/contentextensions/NFAToDFA.cpp        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Source/WebCore/contentextensions/NFAToDFA.cpp        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -336,73 +336,6 @@
</span><span class="cx">     }
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static inline bool canUseFallbackTransition(const PreallocatedNFANodeRangeList&amp; rangeList)
-{
-    // Transitions can contains &quot;0&quot; if the expression has a end-of-line marker.
-    // Fallback transitions cover 1-127. We have to be careful with the first.
-
-    auto iterator = rangeList.begin();
-    auto end = rangeList.end();
-    if (iterator == end)
-        return false;
-
-    char lastSeenCharacter = 0;
-    if (!iterator-&gt;first) {
-        lastSeenCharacter = iterator-&gt;last;
-        if (lastSeenCharacter == 127)
-            return true;
-        ++iterator;
-    }
-
-    for (;iterator != end; ++iterator) {
-        ASSERT(iterator-&gt;first &gt; lastSeenCharacter);
-        if (iterator-&gt;first != lastSeenCharacter + 1)
-            return false;
-
-        if (iterator-&gt;last == 127)
-            return true;
-        lastSeenCharacter = iterator-&gt;last;
-    }
-    return false;
-}
-
-static inline uint32_t findBestFallbackTarget(const PreallocatedNFANodeRangeList&amp; rangeList, const Vector&lt;unsigned, 128&gt;&amp; rangeToTarget)
-{
-    ASSERT(canUseFallbackTransition(rangeList));
-
-    HashMap&lt;uint32_t, unsigned, DefaultHash&lt;uint32_t&gt;::Hash, WTF::UnsignedWithZeroKeyHashTraits&lt;uint32_t&gt;&gt; histogram;
-
-    unsigned rangeToTargetIndex = 0;
-    auto iterator = rangeList.begin();
-    auto end = rangeList.end();
-    ASSERT_WITH_MESSAGE(iterator != end, &quot;An empty range list cannot use a fallback transition.&quot;);
-
-    if (!iterator-&gt;first &amp;&amp; !iterator-&gt;last) {
-        ++iterator;
-        ++rangeToTargetIndex;
-    }
-    ASSERT_WITH_MESSAGE(iterator != end, &quot;An empty range list matching only zero cannot use a fallback transition.&quot;);
-
-    uint32_t bestTarget = rangeToTarget[rangeToTargetIndex];
-    unsigned bestTargetScore = !iterator-&gt;first ? iterator-&gt;size() - 1 : iterator-&gt;size();
-    histogram.add(bestTarget, bestTargetScore);
-    ++iterator;
-    ++rangeToTargetIndex;
-
-    for (;iterator != end; ++iterator, ++rangeToTargetIndex) {
-        unsigned rangeSize = iterator-&gt;size();
-        uint32_t target = rangeToTarget[rangeToTargetIndex];
-        auto addResult = histogram.add(target, rangeSize);
-        if (!addResult.isNewEntry)
-            addResult.iterator-&gt;value += rangeSize;
-        if (addResult.iterator-&gt;value &gt; bestTargetScore) {
-            bestTargetScore = addResult.iterator-&gt;value;
-            bestTarget = target;
-        }
-    }
-    return bestTarget;
-}
-
</del><span class="cx"> static ALWAYS_INLINE unsigned getOrCreateDFANode(const NodeIdSet&amp; nfaNodeSet, const NFA&amp; nfa, DFA&amp; dfa, UniqueNodeIdSetTable&amp; uniqueNodeIdSetTable, Vector&lt;UniqueNodeIdSetImpl*&gt;&amp; unprocessedNodes)
</span><span class="cx"> {
</span><span class="cx">     NodeIdSetToUniqueNodeIdSetSource nodeIdSetToUniqueNodeIdSetSource(dfa, nfa, nfaNodeSet);
</span><span class="lines">@@ -432,46 +365,24 @@
</span><span class="cx">     unprocessedNodes.append(addResult.iterator-&gt;impl());
</span><span class="cx"> 
</span><span class="cx">     PreallocatedNFANodeRangeList combinedRangeList;
</span><del>-    Vector&lt;unsigned, 128&gt; rangeToTarget;
</del><span class="cx">     do {
</span><span class="cx">         UniqueNodeIdSetImpl* uniqueNodeIdSetImpl = unprocessedNodes.takeLast();
</span><span class="cx">         createCombinedTransition(combinedRangeList, *uniqueNodeIdSetImpl, nfa, nfaNodeClosures);
</span><span class="cx"> 
</span><del>-        rangeToTarget.clear();
</del><ins>+        unsigned transitionsStart = dfa.transitionRanges.size();
</ins><span class="cx">         for (const NFANodeRange&amp; range : combinedRangeList) {
</span><span class="cx">             unsigned targetNodeId = getOrCreateDFANode(range.data, nfa, dfa, uniqueNodeIdSetTable, unprocessedNodes);
</span><del>-            rangeToTarget.append(targetNodeId);
</del><ins>+            dfa.transitionRanges.append({ range.first, range.last });
+            dfa.transitionDestinations.append(targetNodeId);
</ins><span class="cx">         }
</span><del>-
-        bool useFallbackTransition = canUseFallbackTransition(combinedRangeList);
-        uint32_t preferedFallbackTarget = 0;
-        if (useFallbackTransition)
-            preferedFallbackTarget = findBestFallbackTarget(combinedRangeList, rangeToTarget);
-
-        unsigned transitionsStart = dfa.transitionCharacters.size();
-        unsigned rangeToTargetIndex = 0;
-
-        for (const NFANodeRange&amp; range : combinedRangeList) {
-            unsigned targetNodeId = rangeToTarget[rangeToTargetIndex];
-
-            if (!(useFallbackTransition &amp;&amp; targetNodeId == preferedFallbackTarget)) {
-                for (unsigned i = range.first; i &lt;= static_cast&lt;unsigned&gt;(range.last); ++i) {
-                    dfa.transitionCharacters.append(static_cast&lt;uint8_t&gt;(i));
-                    dfa.transitionDestinations.append(targetNodeId);
-                }
-            }
-            ++rangeToTargetIndex;
-        }
-        unsigned transitionsEnd = dfa.transitionCharacters.size();
</del><ins>+        unsigned transitionsEnd = dfa.transitionRanges.size();
</ins><span class="cx">         unsigned transitionsLength = transitionsEnd - transitionsStart;
</span><ins>+
</ins><span class="cx">         unsigned dfaNodeId = uniqueNodeIdSetImpl-&gt;m_dfaNodeId;
</span><span class="cx">         DFANode&amp; dfaSourceNode = dfa.nodes[dfaNodeId];
</span><del>-        ASSERT_WITH_MESSAGE(transitionsLength &lt; 127, &quot;Transitions covering the entire alphabet should use a fallback transition&quot;);
</del><span class="cx">         dfaSourceNode.setTransitions(transitionsStart, static_cast&lt;uint8_t&gt;(transitionsLength));
</span><del>-
-        if (useFallbackTransition)
-            dfaSourceNode.addFallbackTransition(dfa, preferedFallbackTarget);
</del><span class="cx">     } while (!unprocessedNodes.isEmpty());
</span><ins>+
</ins><span class="cx">     return dfa;
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Tools/ChangeLog        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2015-07-06  Benjamin Poulain  &lt;benjamin@webkit.org&gt;
+
+        [Content Extensions] Make the DFA transitions ranges instead of characters
+        https://bugs.webkit.org/show_bug.cgi?id=146575
+
+        Reviewed by Alex Christensen.
+
+        * TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
+        * TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp:
+        Since the minimizer is perfect, we get the minimal solution now,
+        which is really cool!
+
</ins><span class="cx"> 2015-07-06  Brady Eidson  &lt;beidson@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         ShouldOpenExternalURLsPolicy should default to &quot;Allow&quot; for WK2 API loads.
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebCoreContentExtensionscpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -1930,4 +1930,470 @@
</span><span class="cx">     testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ddjjyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase1)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[a-m]\&quot;}},&quot;
+        &quot;{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;ignore-previous-rules\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(bar)*[c-e]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;b://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;c://www.webkit.org/&quot;), { }, true);
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { }, true);
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { }, true);
+    testRequest(matchBackend, mainDocumentRequest(&quot;f://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;m://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;n://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[a-m]\&quot;}},&quot;
+        &quot;{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;ignore-previous-rules\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(bar)*[c-e]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.b.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.c.xxx/&quot;), { }, true);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { }, true);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { }, true);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.f.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.m.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.n.xxx/&quot;), { });
+}
+
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase2)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[a-m]\&quot;}},&quot;
+        &quot;{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;ignore-previous-rules\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(bar)*b\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;b://www.webkit.org/&quot;), { }, true);
+    testRequest(matchBackend, mainDocumentRequest(&quot;c://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;m://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;n://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[a-m]\&quot;}},&quot;
+        &quot;{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;ignore-previous-rules\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(bar)*l\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.k.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.l.xxx/&quot;), { }, true);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.m.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.n.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase3)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[b-d]\&quot;}},&quot;
+        &quot;{\&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;^(bar)*[a-m]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;b://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;m://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;n://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[b-d]\&quot;}},&quot;
+        &quot;{\&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;(bar)*[a-m]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.b.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.m.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.n.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase4)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*l\&quot;}},&quot;
+        &quot;{\&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;^(bar)*[a-m]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;k://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;l://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;m://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;n://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*l\&quot;}},&quot;
+        &quot;{\&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;(bar)*[a-m]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.k.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.l.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.m.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.n.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase5)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[a-e]\&quot;}},&quot;
+        &quot;{\&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;^(bar)*[e-h]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;f://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;h://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;i://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[a-e]\&quot;}},&quot;
+        &quot;{\&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;(bar)*[e-h]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.f.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.h.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.i.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase6)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*e\&quot;}},&quot;
+        &quot;{\&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;^(bar)*[e-h]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;f://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;h://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;i://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*e\&quot;}},&quot;
+        &quot;{\&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;(bar)*[e-h]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.f.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.h.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.i.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase7)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[a-e]\&quot;}},&quot;
+        &quot;{\&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;^(bar)*e\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;f://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[a-e]\&quot;}},&quot;
+        &quot;{\&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;(bar)*e\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.f.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase8)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[e-h]\&quot;}},&quot;
+        &quot;{\&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;^(bar)*[a-e]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;f://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;h://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;i://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[e-h]\&quot;}},&quot;
+        &quot;{\&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;(bar)*[a-e]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.f.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.h.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.i.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase9)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*e\&quot;}},&quot;
+        &quot;{\&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;^(bar)*[a-e]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;f://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*e\&quot;}},&quot;
+        &quot;{\&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;(bar)*[a-e]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.f.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase10)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[e-h]\&quot;}},&quot;
+        &quot;{\&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;^(bar)*e\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;f://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;h://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;i://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[e-h]\&quot;}},&quot;
+        &quot;{\&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;(bar)*e\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.f.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.h.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.i.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase11)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[e-g]\&quot;}},&quot;
+        &quot;{\&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;^(bar)*[d-f]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;c://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;f://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;g://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;h://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[e-g]\&quot;}},&quot;
+        &quot;{\&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;(bar)*[d-f]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.c.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.f.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.g.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.h.xxx/&quot;), { });
+}
+
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase12)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[e-g]\&quot;}},&quot;
+        &quot;{\&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;^(bar)*[d-g]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;c://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;f://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;g://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;h://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[e-g]\&quot;}},&quot;
+        &quot;{\&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;(bar)*[d-g]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.c.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.f.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.g.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.h.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase13)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[b-d]\&quot;}},&quot;
+        &quot;{\&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;^(bar)*[c-e]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;b://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;c://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(matchBackend, mainDocumentRequest(&quot;h://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[b-d]\&quot;}},&quot;
+        &quot;{\&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;(bar)*[c-e]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.b.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.c.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.h.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase14)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[b-e]\&quot;}},&quot;
+        &quot;{\&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;^(bar)*[c-e]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;b://www.webkit.org/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;c://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;h://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[b-e]\&quot;}},&quot;
+        &quot;{\&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;(bar)*[c-e]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.b.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.c.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.h.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase15)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*[c-f]\&quot;}},&quot;
+        &quot;{\&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;^(bar)*[c-f]\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;b://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;c://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;f://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;g://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[c-f]\&quot;}},&quot;
+        &quot;{\&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;(bar)*[c-f]\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.b.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.c.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.f.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.g.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedRangeOverlapCase16)
+{
+    auto matchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;^(foo)*c\&quot;}},&quot;
+        &quot;{\&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;^(bar)*c\&quot;}}]&quot;);
+
+    testRequest(matchBackend, mainDocumentRequest(&quot;a://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;b://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;c://www.webkit.org/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(matchBackend, mainDocumentRequest(&quot;d://www.webkit.org/&quot;), { });
+    testRequest(matchBackend, mainDocumentRequest(&quot;e://www.webkit.org/&quot;), { });
+
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*c\&quot;}},&quot;
+        &quot;{\&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;(bar)*c\&quot;}}]&quot;);
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.a.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.b.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.c.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.d.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.e.xxx/&quot;), { });
+}
+
+TEST_F(ContentExtensionTest, CombinedQuantifiedOneOrMoreRangesCase11And13)
+{
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*[c-e]+[g-i]+YYY\&quot;}},&quot;
+        &quot;{\&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;(bar)*[b-d]+[h-j]+YYY\&quot;}}]&quot;);
+
+    // The interesting ranges only match between 'b' and 'k', plus a gap in 'f'.
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.aayyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.abyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.acyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.adyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.aeyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.afyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.agyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ahyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.aiyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ajyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.akyyy.xxx/&quot;), { });
+
+    // 'b' is the first character of the second rule.
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.byyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bayyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bbyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bcyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bdyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.beyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bfyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bgyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bhyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.biyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bjyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bkyyy.xxx/&quot;), { });
+
+    // 'c' is the first character of the first rule, and it overlaps the first character of the second rule.
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cayyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cbyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ccyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cdyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ceyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cfyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cgyyy.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.chyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ciyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cjyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ckyyy.xxx/&quot;), { });
+
+    // 'd' is in the first range of both rule. This series cover overlaps between the two rules.
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.dgyyy.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ddgyyy.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ddhyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ddhhyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.degyyy.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.dehyyy.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.dfgyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.dfhyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.djyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ddjyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ddjjyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+}
+
+TEST_F(ContentExtensionTest, CombinedQuantifiedOneOrMoreRangesCase11And13InGroups)
+{
+    auto searchBackend = makeBackend(&quot;[{\&quot;action\&quot;:{\&quot;type\&quot;:\&quot;block\&quot;},\&quot;trigger\&quot;:{\&quot;url-filter\&quot;:\&quot;(foo)*([c-e])+([g-i]+YYY)\&quot;}},&quot;
+        &quot;{\&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;(bar)*[b-d]+[h-j]+YYY\&quot;}}]&quot;);
+
+    // The interesting ranges only match between 'b' and 'k', plus a gap in 'f'.
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.aayyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.abyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.acyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.adyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.aeyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.afyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.agyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ahyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.aiyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ajyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.akyyy.xxx/&quot;), { });
+
+    // 'b' is the first character of the second rule.
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.byyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bayyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bbyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bcyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bdyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.beyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bfyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bgyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bhyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.biyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bjyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.bkyyy.xxx/&quot;), { });
+
+    // 'c' is the first character of the first rule, and it overlaps the first character of the second rule.
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cayyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cbyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ccyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cdyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ceyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cfyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cgyyy.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.chyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ciyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.cjyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ckyyy.xxx/&quot;), { });
+
+    // 'd' is in the first range of both rule. This series cover overlaps between the two rules.
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.dgyyy.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ddgyyy.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ddhyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ddhhyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.degyyy.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.dehyyy.xxx/&quot;), { ContentExtensions::ActionType::BlockLoad });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.dfgyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.dfhyyy.xxx/&quot;), { });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.djyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ddjyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+    testRequest(searchBackend, mainDocumentRequest(&quot;zzz://www.ddjjyyy.xxx/&quot;), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+}
+
</ins><span class="cx"> } // namespace TestWebKitAPI
</span></span></pre></div>
<a id="trunkToolsTestWebKitAPITestsWebCoreDFAMinimizercpp"></a>
<div class="modfile"><h4>Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp (186373 => 186374)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp        2015-07-06 20:51:04 UTC (rev 186373)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/DFAMinimizer.cpp        2015-07-06 21:06:30 UTC (rev 186374)
</span><span class="lines">@@ -83,7 +83,7 @@
</span><span class="cx">     ContentExtensions::DFA dfa = buildDFAFromPatterns({ &quot;^aac&quot;, &quot;^a.c&quot;, &quot;^b.c&quot;});
</span><span class="cx">     EXPECT_EQ(static_cast&lt;size_t&gt;(9), countLiveNodes(dfa));
</span><span class="cx">     dfa.minimize();
</span><del>-    EXPECT_EQ(static_cast&lt;size_t&gt;(5), countLiveNodes(dfa));
</del><ins>+    EXPECT_EQ(static_cast&lt;size_t&gt;(4), countLiveNodes(dfa));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> TEST_F(DFAMinimizerTest, SimpleFallBackTransitionDifferentiator1)
</span></span></pre>
</div>
</div>

</body>
</html>