<!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>[182356] 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/182356">182356</a></dd>
<dt>Author</dt> <dd>aestes@apple.com</dd>
<dt>Date</dt> <dd>2015-04-05 00:52:14 -0700 (Sun, 05 Apr 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>[Content Filtering] Blocked page is not always displayed when it should be
https://bugs.webkit.org/show_bug.cgi?id=143410
Source/WebCore:

rdar://problem/20211099

Reviewed by Andreas Kling.

These tests now pass: contentfiltering/block-after-add-data.html
                      contentfiltering/block-after-response.html

There were several problems with how ContentFilter loaded replacement data:
(1) Replacement data was delivered to DocumentLoader as if it were the original document's data. This assumes
    that the original data was a UTF-8 encoded HTML document, which is not always true. We had a way to reset
    the encoding, but not the content type.
(2) Replacement data was never delivered when the filter blocks in DocumentLoader::responseReceived().
(3) The main resource load was cancelled before the replacement data could be rendered when the filter blocks
    in DocumentLoader::dataReceived().
The result was that only when the load was blocked after DocumentLoader::notifyFinished() would the replacement
data be shown properly, and only when problem (1) wasn't occurring.

This patch addresses these issues by using the substitute data mechanism to deliver replacement data. By using
substitute data, we can ensure that the original load is cancelled at the earliest opportunity and that the
replacement data is loaded with the proper content type and encoding.

Accomplishing this required changing the way ContentFilter interacts with DocumentLoader. Instead of placing
ContentFilter hooks throughout DocumentLoader, this patch makes ContentFilter itself the client of the
CachedRawResource for the duration of the filtering. If the filter decides to allow the load, DocumentLoader
adds itself as a client causing CachedRawResource to deliver to it the response and buffered data. If the
filter decides to block the load, DocumentLoader schedules a substitute data load. An added benefit of this
approach is that ContentFilter can reuse CachedRawResource's original data buffer instead of keeping its own.

* loader/ContentFilter.cpp:
(WebCore::ContentFilter::createIfNeeded): Changed to take a DecisionFunction rather than a ResourceResponse and DocumentLoader.
(WebCore::ContentFilter::ContentFilter): Ditto.
(WebCore::ContentFilter::~ContentFilter): Removed ourself as a CachedRawResource client if needed.
(WebCore::ContentFilter::startFilteringMainResource): Became the client of the CachedRawResource in order to start the filtering process.
(WebCore::ContentFilter::unblockHandler): Returned the unblock handler.
(WebCore::ContentFilter::replacementData): Returned the replacement data.
(WebCore::ContentFilter::unblockRequestDeniedScript): Returned the unblock request denied script.
(WebCore::ContentFilter::responseReceived): Called responseReceived() on each filter using forEachContentFilterUntilBlocked().
(WebCore::ContentFilter::dataReceived): Ditto for dataReceived().
(WebCore::ContentFilter::notifyFinished): Ditto for finishedLoading().
(WebCore::ContentFilter::forEachContentFilterUntilBlocked): For each filter that needs more data, called the function.
If the filter blocked the load, called didDecide() with State::Blocked.
If all filters allowed the load, called didDecide() with State::Allowed.
(WebCore::ContentFilter::didDecide): Set m_state and called m_decisionFunction().
(WebCore::ContentFilter::addData): Deleted.
(WebCore::ContentFilter::finishedAddingData): Deleted.
(WebCore::ContentFilter::needsMoreData): Deleted.
(WebCore::ContentFilter::didBlockData): Deleted.
(WebCore::ContentFilter::getReplacementData): Deleted.
* loader/ContentFilter.h:
(WebCore::ContentFilter::type):
* loader/DocumentLoader.cpp:
(WebCore::DocumentLoader::DocumentLoader): Called ContentFilter::createIfNeeded() if not loading substitute data.
(WebCore::DocumentLoader::finishedLoading): Removed old ContentFilter code.
(WebCore::DocumentLoader::responseReceived): Ditto.
(WebCore::DocumentLoader::commitData): Ditto.
(WebCore::DocumentLoader::dataReceived): Ditto.
(WebCore::DocumentLoader::detachFromFrame): Set m_contentFilter to nullptr.
(WebCore::DocumentLoader::startLoadingMainResource): Called becomeMainResourceClientIfFilterAllows() instead of
becoming m_mainResource's client.
(WebCore::DocumentLoader::clearMainResource): Set m_contentFilter to nullptr.
(WebCore::DocumentLoader::becomeMainResourceClientIfFilterAllows): If ContentFilter is initialized, called
ContentFilter::startFilteringMainResource(). Otherwise added ourself as a client of m_mainResource.
(WebCore::DocumentLoader::installContentFilterUnblockHandler): Added a helper for creating and notifying
FrameLoaderClient of the unblock handler.
(WebCore::DocumentLoader::contentFilterDidDecide): Set m_contentFilter to nullptr. If the content filter
allowed the load, then added ourself as the CachedRawResource's client. Otherwise, installed the unblock handler
and scheduled a substitute data load with the replacement data.
* loader/DocumentLoader.h:
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::prepareForLoadStart): Removed call to PolicyChecker::prepareForLoadStart().
* loader/NavigationScheduler.cpp:
(WebCore::ScheduledSubstituteDataLoad::ScheduledSubstituteDataLoad): Added a ScheduledNavigation subclass that
calls FrameLoader::load() with a FrameLoadRequest containing substitute data.
(WebCore::NavigationScheduler::scheduleSubstituteDataLoad): Scheduled a substitute data load.
* loader/NavigationScheduler.h:
* loader/PolicyChecker.cpp:
(WebCore::PolicyChecker::checkNavigationPolicy): Reset m_contentFilterUnblockHandler if it couldn't handle the request.
(WebCore::PolicyChecker::prepareForLoadStart): Deleted.
* loader/PolicyChecker.h:
* platform/ContentFilterUnblockHandler.h:
(WebCore::ContentFilterUnblockHandler::unreachableURL):
(WebCore::ContentFilterUnblockHandler::setUnreachableURL):
(WebCore::ContentFilterUnblockHandler::unblockURLScheme): Renamed to ContentFilter::urlScheme().
* platform/PlatformContentFilter.h:
* platform/cocoa/ContentFilterUnblockHandlerCocoa.mm:
(WebCore::ContentFilterUnblockHandler::wrapWithDecisionHandler): Added a helper to wrap the unblock handler with an outer DecisionHandlerFunction.
(WebCore::ContentFilterUnblockHandler::encode): Added m_unreachableURL to the encoding.
(WebCore::ContentFilterUnblockHandler::decode): Ditto for the decoding.
(WebCore::ContentFilterUnblockHandler::canHandleRequest): Changed to call ContentFilter::urlScheme().
* platform/cocoa/NetworkExtensionContentFilter.h:
* platform/cocoa/NetworkExtensionContentFilter.mm:
(replacementDataFromDecisionInfo): Added a helper to extract replacement data from the decisionInfo dictionary.
(WebCore::NetworkExtensionContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
(WebCore::NetworkExtensionContentFilter::create): Created a new object.
(WebCore::NetworkExtensionContentFilter::NetworkExtensionContentFilter): Created a NEFilterSource immediately when using the modern API.
(WebCore::NetworkExtensionContentFilter::responseReceived): Created a NEFilterSource when using the legacy API.
Called -[NEFilterSource receivedResponse:decisionHandler:] when using the modern API.
(WebCore::NetworkExtensionContentFilter::addData): Stopped buffering the original data.
(WebCore::NetworkExtensionContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&amp;.
(WebCore::NetworkExtensionContentFilter::canHandleResponse): Deleted.
(WebCore::createNEFilterSource): Deleted.
(WebCore::NetworkExtensionContentFilter::getReplacementData): Deleted.
* platform/cocoa/ParentalControlsContentFilter.h:
* platform/cocoa/ParentalControlsContentFilter.mm:
(WebCore::ParentalControlsContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
(WebCore::ParentalControlsContentFilter::create): Created a new object.
(WebCore::ParentalControlsContentFilter::ParentalControlsContentFilter): Initialized m_filterState to kWFEStateBuffering.
(WebCore::canHandleResponse): Added a helper to check if the response can be filtered.
(WebCore::ParentalControlsContentFilter::responseReceived): If !canHandleResponse(), set m_filterState to kWFEStateAllowed and return.
Otherwise created a new WebFilterEvaluator with the response.
(WebCore::ParentalControlsContentFilter::addData): Called updateFilterState().
(WebCore::ParentalControlsContentFilter::finishedAddingData): Ditto.
(WebCore::ParentalControlsContentFilter::needsMoreData): Changed to check m_filterState.
(WebCore::ParentalControlsContentFilter::didBlockData): Ditto.
(WebCore::ParentalControlsContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&amp;.
(WebCore::ParentalControlsContentFilter::updateFilterState): Updated m_filterState by calling -[WebFilterEvaluator filterState].
(WebCore::ParentalControlsContentFilter::canHandleResponse): Deleted.
(WebCore::ParentalControlsContentFilter::getReplacementData): Deleted.
* platform/spi/cocoa/NEFilterSourceSPI.h:
* platform/spi/cocoa/WebFilterEvaluatorSPI.h:
* testing/MockContentFilter.cpp:
(WebCore::MockContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
(WebCore::MockContentFilter::create): Created a new object.
(WebCore::MockContentFilter::responseReceived): Called maybeDetermineStatus().
(WebCore::MockContentFilter::addData): Stopped buffering the original data.
(WebCore::MockContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&amp;.
(WebCore::MockContentFilter::unblockHandler): Asserted that we blocked data.
(WebCore::MockContentFilter::canHandleResponse): Deleted.
(WebCore::MockContentFilter::MockContentFilter): Deleted.
(WebCore::MockContentFilter::getReplacementData): Deleted.
* testing/MockContentFilter.h:
* testing/MockContentFilterSettings.cpp:
(WebCore::MockContentFilterSettings::unblockRequestURL): Changed to use a StringBuilder.

Source/WebKit2:

Reviewed by Andreas Kling.

* UIProcess/WebFrameProxy.cpp:
(WebKit::WebFrameProxy::didStartProvisionalLoad): Stopped clearing m_contentFilterUnblockHandler here.
(WebKit::WebFrameProxy::didHandleContentFilterUnblockNavigation): Started doing it here instead.

LayoutTests:

Reviewed by Andreas Kling.

* TestExpectations: Unskipped block-after-add-data.html.
* contentfiltering/block-after-add-data-expected.html: Added a passing expectation.
* contentfiltering/block-after-response-expected.html: Ditto.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsTestExpectations">trunk/LayoutTests/TestExpectations</a></li>
<li><a href="#trunkLayoutTestscontentfilteringblockafteradddataexpectedhtml">trunk/LayoutTests/contentfiltering/block-after-add-data-expected.html</a></li>
<li><a href="#trunkLayoutTestscontentfilteringblockafterresponseexpectedhtml">trunk/LayoutTests/contentfiltering/block-after-response-expected.html</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreloaderContentFiltercpp">trunk/Source/WebCore/loader/ContentFilter.cpp</a></li>
<li><a href="#trunkSourceWebCoreloaderContentFilterh">trunk/Source/WebCore/loader/ContentFilter.h</a></li>
<li><a href="#trunkSourceWebCoreloaderDocumentLoadercpp">trunk/Source/WebCore/loader/DocumentLoader.cpp</a></li>
<li><a href="#trunkSourceWebCoreloaderDocumentLoaderh">trunk/Source/WebCore/loader/DocumentLoader.h</a></li>
<li><a href="#trunkSourceWebCoreloaderFrameLoadercpp">trunk/Source/WebCore/loader/FrameLoader.cpp</a></li>
<li><a href="#trunkSourceWebCoreloaderNavigationSchedulercpp">trunk/Source/WebCore/loader/NavigationScheduler.cpp</a></li>
<li><a href="#trunkSourceWebCoreloaderNavigationSchedulerh">trunk/Source/WebCore/loader/NavigationScheduler.h</a></li>
<li><a href="#trunkSourceWebCoreloaderPolicyCheckercpp">trunk/Source/WebCore/loader/PolicyChecker.cpp</a></li>
<li><a href="#trunkSourceWebCoreloaderPolicyCheckerh">trunk/Source/WebCore/loader/PolicyChecker.h</a></li>
<li><a href="#trunkSourceWebCoreplatformContentFilterUnblockHandlerh">trunk/Source/WebCore/platform/ContentFilterUnblockHandler.h</a></li>
<li><a href="#trunkSourceWebCoreplatformPlatformContentFilterh">trunk/Source/WebCore/platform/PlatformContentFilter.h</a></li>
<li><a href="#trunkSourceWebCoreplatformcocoaContentFilterUnblockHandlerCocoamm">trunk/Source/WebCore/platform/cocoa/ContentFilterUnblockHandlerCocoa.mm</a></li>
<li><a href="#trunkSourceWebCoreplatformcocoaNetworkExtensionContentFilterh">trunk/Source/WebCore/platform/cocoa/NetworkExtensionContentFilter.h</a></li>
<li><a href="#trunkSourceWebCoreplatformcocoaNetworkExtensionContentFiltermm">trunk/Source/WebCore/platform/cocoa/NetworkExtensionContentFilter.mm</a></li>
<li><a href="#trunkSourceWebCoreplatformcocoaParentalControlsContentFilterh">trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.h</a></li>
<li><a href="#trunkSourceWebCoreplatformcocoaParentalControlsContentFiltermm">trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.mm</a></li>
<li><a href="#trunkSourceWebCoreplatformspicocoaNEFilterSourceSPIh">trunk/Source/WebCore/platform/spi/cocoa/NEFilterSourceSPI.h</a></li>
<li><a href="#trunkSourceWebCoreplatformspicocoaWebFilterEvaluatorSPIh">trunk/Source/WebCore/platform/spi/cocoa/WebFilterEvaluatorSPI.h</a></li>
<li><a href="#trunkSourceWebCoretestingMockContentFiltercpp">trunk/Source/WebCore/testing/MockContentFilter.cpp</a></li>
<li><a href="#trunkSourceWebCoretestingMockContentFilterh">trunk/Source/WebCore/testing/MockContentFilter.h</a></li>
<li><a href="#trunkSourceWebCoretestingMockContentFilterSettingscpp">trunk/Source/WebCore/testing/MockContentFilterSettings.cpp</a></li>
<li><a href="#trunkSourceWebKit2ChangeLog">trunk/Source/WebKit2/ChangeLog</a></li>
<li><a href="#trunkSourceWebKit2UIProcessWebFrameProxycpp">trunk/Source/WebKit2/UIProcess/WebFrameProxy.cpp</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/LayoutTests/ChangeLog        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2015-04-04  Andy Estes  &lt;aestes@apple.com&gt;
+
+        [Content Filtering] Blocked page is not always displayed when it should be
+        https://bugs.webkit.org/show_bug.cgi?id=143410
+
+        Reviewed by Andreas Kling.
+
+        * TestExpectations: Unskipped block-after-add-data.html.
+        * contentfiltering/block-after-add-data-expected.html: Added a passing expectation.
+        * contentfiltering/block-after-response-expected.html: Ditto.
+
</ins><span class="cx"> 2015-04-04  Chris Fleizach  &lt;cfleizach@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         AX: Heuristic: Avoid exposing an element as clickable if mouse event delegation is handled on an AXElement with more than one descendant AXElement
</span></span></pre></div>
<a id="trunkLayoutTestsTestExpectations"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/TestExpectations (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/TestExpectations        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/LayoutTests/TestExpectations        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -497,9 +497,6 @@
</span><span class="cx"> webkit.org/b/114280 svg/animations/smil-leak-element-instances.svg [ Pass Failure ]
</span><span class="cx"> webkit.org/b/114280 svg/animations/smil-leak-elements.svg [ Pass Failure ]
</span><span class="cx"> 
</span><del>-# contentfiltering/block-after-add-data.html times out unexpectedly
-webkit.org/b/142894 contentfiltering/block-after-add-data.html [ Skip ]
-
</del><span class="cx"> webkit.org/b/143085 media/track/track-mode.html [ Pass Timeout ]
</span><span class="cx"> 
</span><span class="cx"> # In ES6, Object type restrictions on a first parameter of several Object.* functions are relaxed.
</span></span></pre></div>
<a id="trunkLayoutTestscontentfilteringblockafteradddataexpectedhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/contentfiltering/block-after-add-data-expected.html (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/contentfiltering/block-after-add-data-expected.html        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/LayoutTests/contentfiltering/block-after-add-data-expected.html        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -1,2 +1,2 @@
</span><span class="cx"> &lt;!DOCTYPE html&gt;
</span><del>-&lt;iframe src=&quot;data:text/html,&lt;!DOCTYPE html&gt;&lt;body&gt;&quot;&gt;&lt;/iframe&gt;
</del><ins>+&lt;iframe src=&quot;data:text/html,&lt;!DOCTYPE html&gt;&lt;body&gt;PASS&quot;&gt;&lt;/iframe&gt;
</ins></span></pre></div>
<a id="trunkLayoutTestscontentfilteringblockafterresponseexpectedhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/contentfiltering/block-after-response-expected.html (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/contentfiltering/block-after-response-expected.html        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/LayoutTests/contentfiltering/block-after-response-expected.html        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -1,2 +1,2 @@
</span><span class="cx"> &lt;!DOCTYPE html&gt;
</span><del>-&lt;iframe src=&quot;data:text/html,&lt;!DOCTYPE html&gt;&lt;body&gt;FAIL&quot;&gt;&lt;/iframe&gt;
</del><ins>+&lt;iframe src=&quot;data:text/html,&lt;!DOCTYPE html&gt;&lt;body&gt;PASS&quot;&gt;&lt;/iframe&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/ChangeLog        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -1,3 +1,141 @@
</span><ins>+2015-04-04  Andy Estes  &lt;aestes@apple.com&gt;
+
+        [Content Filtering] Blocked page is not always displayed when it should be
+        https://bugs.webkit.org/show_bug.cgi?id=143410
+        rdar://problem/20211099
+
+        Reviewed by Andreas Kling.
+
+        These tests now pass: contentfiltering/block-after-add-data.html
+                              contentfiltering/block-after-response.html
+
+        There were several problems with how ContentFilter loaded replacement data:
+        (1) Replacement data was delivered to DocumentLoader as if it were the original document's data. This assumes
+            that the original data was a UTF-8 encoded HTML document, which is not always true. We had a way to reset
+            the encoding, but not the content type.
+        (2) Replacement data was never delivered when the filter blocks in DocumentLoader::responseReceived().
+        (3) The main resource load was cancelled before the replacement data could be rendered when the filter blocks
+            in DocumentLoader::dataReceived().
+        The result was that only when the load was blocked after DocumentLoader::notifyFinished() would the replacement
+        data be shown properly, and only when problem (1) wasn't occurring.
+
+        This patch addresses these issues by using the substitute data mechanism to deliver replacement data. By using
+        substitute data, we can ensure that the original load is cancelled at the earliest opportunity and that the
+        replacement data is loaded with the proper content type and encoding.
+
+        Accomplishing this required changing the way ContentFilter interacts with DocumentLoader. Instead of placing
+        ContentFilter hooks throughout DocumentLoader, this patch makes ContentFilter itself the client of the
+        CachedRawResource for the duration of the filtering. If the filter decides to allow the load, DocumentLoader
+        adds itself as a client causing CachedRawResource to deliver to it the response and buffered data. If the
+        filter decides to block the load, DocumentLoader schedules a substitute data load. An added benefit of this
+        approach is that ContentFilter can reuse CachedRawResource's original data buffer instead of keeping its own.
+
+        * loader/ContentFilter.cpp:
+        (WebCore::ContentFilter::createIfNeeded): Changed to take a DecisionFunction rather than a ResourceResponse and DocumentLoader.
+        (WebCore::ContentFilter::ContentFilter): Ditto.
+        (WebCore::ContentFilter::~ContentFilter): Removed ourself as a CachedRawResource client if needed.
+        (WebCore::ContentFilter::startFilteringMainResource): Became the client of the CachedRawResource in order to start the filtering process.
+        (WebCore::ContentFilter::unblockHandler): Returned the unblock handler.
+        (WebCore::ContentFilter::replacementData): Returned the replacement data.
+        (WebCore::ContentFilter::unblockRequestDeniedScript): Returned the unblock request denied script.
+        (WebCore::ContentFilter::responseReceived): Called responseReceived() on each filter using forEachContentFilterUntilBlocked().
+        (WebCore::ContentFilter::dataReceived): Ditto for dataReceived().
+        (WebCore::ContentFilter::notifyFinished): Ditto for finishedLoading().
+        (WebCore::ContentFilter::forEachContentFilterUntilBlocked): For each filter that needs more data, called the function.
+        If the filter blocked the load, called didDecide() with State::Blocked.
+        If all filters allowed the load, called didDecide() with State::Allowed.
+        (WebCore::ContentFilter::didDecide): Set m_state and called m_decisionFunction().
+        (WebCore::ContentFilter::addData): Deleted.
+        (WebCore::ContentFilter::finishedAddingData): Deleted.
+        (WebCore::ContentFilter::needsMoreData): Deleted.
+        (WebCore::ContentFilter::didBlockData): Deleted.
+        (WebCore::ContentFilter::getReplacementData): Deleted.
+        * loader/ContentFilter.h:
+        (WebCore::ContentFilter::type):
+        * loader/DocumentLoader.cpp:
+        (WebCore::DocumentLoader::DocumentLoader): Called ContentFilter::createIfNeeded() if not loading substitute data.
+        (WebCore::DocumentLoader::finishedLoading): Removed old ContentFilter code.
+        (WebCore::DocumentLoader::responseReceived): Ditto.
+        (WebCore::DocumentLoader::commitData): Ditto.
+        (WebCore::DocumentLoader::dataReceived): Ditto.
+        (WebCore::DocumentLoader::detachFromFrame): Set m_contentFilter to nullptr.
+        (WebCore::DocumentLoader::startLoadingMainResource): Called becomeMainResourceClientIfFilterAllows() instead of
+        becoming m_mainResource's client.
+        (WebCore::DocumentLoader::clearMainResource): Set m_contentFilter to nullptr.
+        (WebCore::DocumentLoader::becomeMainResourceClientIfFilterAllows): If ContentFilter is initialized, called
+        ContentFilter::startFilteringMainResource(). Otherwise added ourself as a client of m_mainResource.
+        (WebCore::DocumentLoader::installContentFilterUnblockHandler): Added a helper for creating and notifying
+        FrameLoaderClient of the unblock handler.
+        (WebCore::DocumentLoader::contentFilterDidDecide): Set m_contentFilter to nullptr. If the content filter
+        allowed the load, then added ourself as the CachedRawResource's client. Otherwise, installed the unblock handler
+        and scheduled a substitute data load with the replacement data.
+        * loader/DocumentLoader.h:
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::prepareForLoadStart): Removed call to PolicyChecker::prepareForLoadStart().
+        * loader/NavigationScheduler.cpp:
+        (WebCore::ScheduledSubstituteDataLoad::ScheduledSubstituteDataLoad): Added a ScheduledNavigation subclass that
+        calls FrameLoader::load() with a FrameLoadRequest containing substitute data.
+        (WebCore::NavigationScheduler::scheduleSubstituteDataLoad): Scheduled a substitute data load.
+        * loader/NavigationScheduler.h:
+        * loader/PolicyChecker.cpp:
+        (WebCore::PolicyChecker::checkNavigationPolicy): Reset m_contentFilterUnblockHandler if it couldn't handle the request.
+        (WebCore::PolicyChecker::prepareForLoadStart): Deleted.
+        * loader/PolicyChecker.h:
+        * platform/ContentFilterUnblockHandler.h:
+        (WebCore::ContentFilterUnblockHandler::unreachableURL):
+        (WebCore::ContentFilterUnblockHandler::setUnreachableURL):
+        (WebCore::ContentFilterUnblockHandler::unblockURLScheme): Renamed to ContentFilter::urlScheme().
+        * platform/PlatformContentFilter.h:
+        * platform/cocoa/ContentFilterUnblockHandlerCocoa.mm:
+        (WebCore::ContentFilterUnblockHandler::wrapWithDecisionHandler): Added a helper to wrap the unblock handler with an outer DecisionHandlerFunction.
+        (WebCore::ContentFilterUnblockHandler::encode): Added m_unreachableURL to the encoding.
+        (WebCore::ContentFilterUnblockHandler::decode): Ditto for the decoding.
+        (WebCore::ContentFilterUnblockHandler::canHandleRequest): Changed to call ContentFilter::urlScheme().
+        * platform/cocoa/NetworkExtensionContentFilter.h:
+        * platform/cocoa/NetworkExtensionContentFilter.mm:
+        (replacementDataFromDecisionInfo): Added a helper to extract replacement data from the decisionInfo dictionary.
+        (WebCore::NetworkExtensionContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
+        (WebCore::NetworkExtensionContentFilter::create): Created a new object.
+        (WebCore::NetworkExtensionContentFilter::NetworkExtensionContentFilter): Created a NEFilterSource immediately when using the modern API.
+        (WebCore::NetworkExtensionContentFilter::responseReceived): Created a NEFilterSource when using the legacy API.
+        Called -[NEFilterSource receivedResponse:decisionHandler:] when using the modern API.
+        (WebCore::NetworkExtensionContentFilter::addData): Stopped buffering the original data.
+        (WebCore::NetworkExtensionContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&amp;.
+        (WebCore::NetworkExtensionContentFilter::canHandleResponse): Deleted.
+        (WebCore::createNEFilterSource): Deleted.
+        (WebCore::NetworkExtensionContentFilter::getReplacementData): Deleted.
+        * platform/cocoa/ParentalControlsContentFilter.h:
+        * platform/cocoa/ParentalControlsContentFilter.mm:
+        (WebCore::ParentalControlsContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
+        (WebCore::ParentalControlsContentFilter::create): Created a new object.
+        (WebCore::ParentalControlsContentFilter::ParentalControlsContentFilter): Initialized m_filterState to kWFEStateBuffering.
+        (WebCore::canHandleResponse): Added a helper to check if the response can be filtered.
+        (WebCore::ParentalControlsContentFilter::responseReceived): If !canHandleResponse(), set m_filterState to kWFEStateAllowed and return.
+        Otherwise created a new WebFilterEvaluator with the response.
+        (WebCore::ParentalControlsContentFilter::addData): Called updateFilterState().
+        (WebCore::ParentalControlsContentFilter::finishedAddingData): Ditto.
+        (WebCore::ParentalControlsContentFilter::needsMoreData): Changed to check m_filterState.
+        (WebCore::ParentalControlsContentFilter::didBlockData): Ditto.
+        (WebCore::ParentalControlsContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&amp;.
+        (WebCore::ParentalControlsContentFilter::updateFilterState): Updated m_filterState by calling -[WebFilterEvaluator filterState].
+        (WebCore::ParentalControlsContentFilter::canHandleResponse): Deleted.
+        (WebCore::ParentalControlsContentFilter::getReplacementData): Deleted.
+        * platform/spi/cocoa/NEFilterSourceSPI.h:
+        * platform/spi/cocoa/WebFilterEvaluatorSPI.h:
+        * testing/MockContentFilter.cpp:
+        (WebCore::MockContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
+        (WebCore::MockContentFilter::create): Created a new object.
+        (WebCore::MockContentFilter::responseReceived): Called maybeDetermineStatus().
+        (WebCore::MockContentFilter::addData): Stopped buffering the original data.
+        (WebCore::MockContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&amp;.
+        (WebCore::MockContentFilter::unblockHandler): Asserted that we blocked data.
+        (WebCore::MockContentFilter::canHandleResponse): Deleted.
+        (WebCore::MockContentFilter::MockContentFilter): Deleted.
+        (WebCore::MockContentFilter::getReplacementData): Deleted.
+        * testing/MockContentFilter.h:
+        * testing/MockContentFilterSettings.cpp:
+        (WebCore::MockContentFilterSettings::unblockRequestURL): Changed to use a StringBuilder.
+
</ins><span class="cx"> 2015-04-04  Chris Fleizach  &lt;cfleizach@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         AX: Heuristic: Avoid exposing an element as clickable if mouse event delegation is handled on an AXElement with more than one descendant AXElement
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderContentFiltercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/ContentFilter.cpp (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/ContentFilter.cpp        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/loader/ContentFilter.cpp        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -28,12 +28,11 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CONTENT_FILTERING)
</span><span class="cx"> 
</span><del>-#include &quot;DocumentLoader.h&quot;
-#include &quot;Frame.h&quot;
</del><ins>+#include &quot;CachedRawResource.h&quot;
+#include &quot;ContentFilterUnblockHandler.h&quot;
</ins><span class="cx"> #include &quot;NetworkExtensionContentFilter.h&quot;
</span><span class="cx"> #include &quot;ParentalControlsContentFilter.h&quot;
</span><del>-#include &quot;ScriptController.h&quot;
-#include &lt;bindings/ScriptValue.h&gt;
</del><ins>+#include &quot;SharedBuffer.h&quot;
</ins><span class="cx"> #include &lt;wtf/NeverDestroyed.h&gt;
</span><span class="cx"> #include &lt;wtf/Vector.h&gt;
</span><span class="cx"> 
</span><span class="lines">@@ -52,107 +51,134 @@
</span><span class="cx">     return types;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-std::unique_ptr&lt;ContentFilter&gt; ContentFilter::createIfNeeded(const ResourceResponse&amp; response, DocumentLoader&amp; documentLoader)
</del><ins>+std::unique_ptr&lt;ContentFilter&gt; ContentFilter::createIfNeeded(DecisionFunction decisionFunction)
</ins><span class="cx"> {
</span><span class="cx">     Container filters;
</span><span class="cx">     for (auto&amp; type : types()) {
</span><del>-        if (type.canHandleResponse(response))
-            filters.append(type.create(response));
</del><ins>+        if (!type.enabled())
+            continue;
+
+        auto filter = type.create();
+        ASSERT(filter);
+        filters.append(WTF::move(filter));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     if (filters.isEmpty())
</span><span class="cx">         return nullptr;
</span><span class="cx"> 
</span><del>-    return std::make_unique&lt;ContentFilter&gt;(WTF::move(filters), documentLoader);
</del><ins>+    return std::make_unique&lt;ContentFilter&gt;(WTF::move(filters), WTF::move(decisionFunction));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-ContentFilter::ContentFilter(Container contentFilters, DocumentLoader&amp; documentLoader)
</del><ins>+ContentFilter::ContentFilter(Container contentFilters, DecisionFunction decisionFunction)
</ins><span class="cx">     : m_contentFilters { WTF::move(contentFilters) }
</span><del>-    , m_documentLoader { documentLoader }
</del><ins>+    , m_decisionFunction { WTF::move(decisionFunction) }
</ins><span class="cx"> {
</span><span class="cx">     ASSERT(!m_contentFilters.isEmpty());
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void ContentFilter::addData(const char* data, int length)
</del><ins>+ContentFilter::~ContentFilter()
</ins><span class="cx"> {
</span><del>-    ASSERT(needsMoreData());
</del><ins>+    if (!m_mainResource)
+        return;
+    ASSERT(m_mainResource-&gt;hasClient(this));
+    m_mainResource-&gt;removeClient(this);
+}
</ins><span class="cx"> 
</span><del>-    for (auto&amp; contentFilter : m_contentFilters)
-        contentFilter-&gt;addData(data, length);
</del><ins>+void ContentFilter::startFilteringMainResource(CachedRawResource&amp; resource)
+{
+    ASSERT(m_state == State::Initialized);
+    m_state = State::Filtering;
+    ASSERT(!m_mainResource);
+    m_mainResource = &amp;resource;
+    ASSERT(!m_mainResource-&gt;hasClient(this));
+    m_mainResource-&gt;addClient(this);
</ins><span class="cx"> }
</span><del>-    
-void ContentFilter::finishedAddingData()
</del><ins>+
+ContentFilterUnblockHandler ContentFilter::unblockHandler() const
</ins><span class="cx"> {
</span><del>-    ASSERT(needsMoreData());
</del><ins>+    ASSERT(m_state == State::Blocked);
+    ASSERT(m_blockingContentFilter);
+    ASSERT(m_blockingContentFilter-&gt;didBlockData());
+    return m_blockingContentFilter-&gt;unblockHandler();
+}
</ins><span class="cx"> 
</span><del>-    for (auto&amp; contentFilter : m_contentFilters)
-        contentFilter-&gt;finishedAddingData();
</del><ins>+Ref&lt;SharedBuffer&gt; ContentFilter::replacementData() const
+{
+    ASSERT(m_state == State::Blocked);
+    ASSERT(m_blockingContentFilter);
+    ASSERT(m_blockingContentFilter-&gt;didBlockData());
+    return m_blockingContentFilter-&gt;replacementData();
+}
</ins><span class="cx"> 
</span><del>-    ASSERT(!needsMoreData());
</del><ins>+String ContentFilter::unblockRequestDeniedScript() const
+{
+    ASSERT(m_state == State::Blocked);
+    ASSERT(m_blockingContentFilter);
+    ASSERT(m_blockingContentFilter-&gt;didBlockData());
+    return m_blockingContentFilter-&gt;unblockRequestDeniedScript();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool ContentFilter::needsMoreData() const
</del><ins>+void ContentFilter::responseReceived(CachedResource* resource, const ResourceResponse&amp; response)
</ins><span class="cx"> {
</span><del>-    for (auto&amp; contentFilter : m_contentFilters) {
-        if (contentFilter-&gt;needsMoreData())
-            return true;
-    }
</del><ins>+    ASSERT(m_state == State::Filtering);
+    ASSERT_UNUSED(resource, resource == m_mainResource.get());
+    forEachContentFilterUntilBlocked([&amp;response](PlatformContentFilter&amp; contentFilter) {
+        contentFilter.responseReceived(response);
+    });
+}
</ins><span class="cx"> 
</span><del>-    return false;
</del><ins>+void ContentFilter::dataReceived(CachedResource* resource, const char* data, int length)
+{
+    ASSERT(m_state == State::Filtering);
+    ASSERT_UNUSED(resource, resource == m_mainResource.get());
+    forEachContentFilterUntilBlocked([data, length](PlatformContentFilter&amp; contentFilter) {
+        contentFilter.addData(data, length);
+    });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool ContentFilter::didBlockData() const
</del><ins>+void ContentFilter::notifyFinished(CachedResource* resource)
</ins><span class="cx"> {
</span><del>-    for (auto&amp; contentFilter : m_contentFilters) {
-        if (contentFilter-&gt;didBlockData())
-            return true;
-    }
-
-    return false;
</del><ins>+    ASSERT(m_state == State::Filtering);
+    ASSERT_UNUSED(resource, resource == m_mainResource.get());
+    forEachContentFilterUntilBlocked([](PlatformContentFilter&amp; contentFilter) {
+        contentFilter.finishedAddingData();
+    });
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-const char* ContentFilter::getReplacementData(int&amp; length) const
</del><ins>+void ContentFilter::forEachContentFilterUntilBlocked(std::function&lt;void(PlatformContentFilter&amp;)&gt; function)
</ins><span class="cx"> {
</span><del>-    ASSERT(!needsMoreData());
</del><ins>+    bool allFiltersAllowedLoad { true };
+    for (auto&amp; contentFilter : m_contentFilters) {
+        if (!contentFilter-&gt;needsMoreData()) {
+            ASSERT(!contentFilter-&gt;didBlockData());
+            continue;
+        }
</ins><span class="cx"> 
</span><del>-    for (auto&amp; contentFilter : m_contentFilters) {
-        if (contentFilter-&gt;didBlockData())
-            return contentFilter-&gt;getReplacementData(length);
</del><ins>+        function(*contentFilter);
+
+        if (contentFilter-&gt;didBlockData()) {
+            ASSERT(!m_blockingContentFilter);
+            m_blockingContentFilter = contentFilter.get();
+            didDecide(State::Blocked);
+            return;
+        } else if (contentFilter-&gt;needsMoreData())
+            allFiltersAllowedLoad = false;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return m_contentFilters[0]-&gt;getReplacementData(length);
</del><ins>+    if (allFiltersAllowedLoad)
+        didDecide(State::Allowed);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-ContentFilterUnblockHandler ContentFilter::unblockHandler() const
</del><ins>+void ContentFilter::didDecide(State state)
</ins><span class="cx"> {
</span><del>-    ASSERT(didBlockData());
</del><ins>+    ASSERT(m_state != State::Allowed);
+    ASSERT(m_state != State::Blocked);
+    ASSERT(state == State::Allowed || state == State::Blocked);
+    m_state = state;
</ins><span class="cx"> 
</span><del>-    PlatformContentFilter* blockingFilter = nullptr;
-    for (auto&amp; contentFilter : m_contentFilters) {
-        if (contentFilter-&gt;didBlockData()) {
-            blockingFilter = contentFilter.get();
-            break;
-        }
-    }
-    ASSERT(blockingFilter);
-
-    StringCapture unblockRequestDeniedScript { blockingFilter-&gt;unblockRequestDeniedScript() };
-    if (unblockRequestDeniedScript.string().isEmpty())
-        return blockingFilter-&gt;unblockHandler();
-
-    // It would be a layering violation for the unblock handler to access its frame,
-    // so we will execute the unblock denied script on its behalf.
-    ContentFilterUnblockHandler unblockHandler { blockingFilter-&gt;unblockHandler() };
-    RefPtr&lt;Frame&gt; frame { m_documentLoader.frame() };
-    return ContentFilterUnblockHandler {
-        unblockHandler.unblockURLHost(), [unblockHandler, frame, unblockRequestDeniedScript](ContentFilterUnblockHandler::DecisionHandlerFunction decisionHandler) {
-            unblockHandler.requestUnblockAsync([decisionHandler, frame, unblockRequestDeniedScript](bool unblocked) {
-                decisionHandler(unblocked);
-                if (!unblocked &amp;&amp; frame)
-                    frame-&gt;script().executeScript(unblockRequestDeniedScript.string());
-            });
-        }
-    };
</del><ins>+    // Calling m_decisionFunction might delete |this|.
+    if (m_decisionFunction)
+        m_decisionFunction();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderContentFilterh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/ContentFilter.h (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/ContentFilter.h        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/loader/ContentFilter.h        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -28,47 +28,78 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CONTENT_FILTERING)
</span><span class="cx"> 
</span><del>-#include &quot;PlatformContentFilter.h&quot;
</del><ins>+#include &quot;CachedRawResourceClient.h&quot;
+#include &quot;CachedResourceHandle.h&quot;
+#include &lt;functional&gt;
</ins><span class="cx"> #include &lt;wtf/Vector.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-class DocumentLoader;
-class ResourceResponse;
</del><ins>+class CachedRawResource;
+class ContentFilterUnblockHandler;
+class PlatformContentFilter;
+class SharedBuffer;
</ins><span class="cx"> 
</span><del>-class ContentFilter final : public PlatformContentFilter {
</del><ins>+class ContentFilter final : private CachedRawResourceClient {
+    WTF_MAKE_FAST_ALLOCATED;
+    WTF_MAKE_NONCOPYABLE(ContentFilter);
+
</ins><span class="cx"> public:
</span><span class="cx">     template &lt;typename T&gt; static void addType() { types().append(type&lt;T&gt;()); }
</span><del>-    static std::unique_ptr&lt;ContentFilter&gt; createIfNeeded(const ResourceResponse&amp;, DocumentLoader&amp;);
</del><span class="cx"> 
</span><del>-    void addData(const char* data, int length) override;
-    void finishedAddingData() override;
-    bool needsMoreData() const override;
-    bool didBlockData() const override;
-    const char* getReplacementData(int&amp; length) const override;
-    ContentFilterUnblockHandler unblockHandler() const override;
</del><ins>+    using DecisionFunction = std::function&lt;void()&gt;;
+    static std::unique_ptr&lt;ContentFilter&gt; createIfNeeded(DecisionFunction);
+    ~ContentFilter() override;
</ins><span class="cx"> 
</span><ins>+    static const char* urlScheme() { return &quot;x-apple-content-filter&quot;; }
+
+    void startFilteringMainResource(CachedRawResource&amp;);
+
+    enum class State {
+        Initialized,
+        Filtering,
+        Allowed,
+        Blocked
+    };
+    State state() const { return m_state; }
+    ContentFilterUnblockHandler unblockHandler() const;
+    Ref&lt;SharedBuffer&gt; replacementData() const;
+    String unblockRequestDeniedScript() const;
+
</ins><span class="cx"> private:
</span><span class="cx">     struct Type {
</span><del>-        const std::function&lt;bool(const ResourceResponse&amp;)&gt; canHandleResponse;
-        const std::function&lt;std::unique_ptr&lt;PlatformContentFilter&gt;(const ResourceResponse&amp;)&gt; create;
</del><ins>+        const std::function&lt;bool()&gt; enabled;
+        const std::function&lt;std::unique_ptr&lt;PlatformContentFilter&gt;()&gt; create;
</ins><span class="cx">     };
</span><span class="cx">     template &lt;typename T&gt; static Type type();
</span><span class="cx">     WEBCORE_EXPORT static Vector&lt;Type&gt;&amp; types();
</span><span class="cx"> 
</span><span class="cx">     using Container = Vector&lt;std::unique_ptr&lt;PlatformContentFilter&gt;&gt;;
</span><del>-    friend std::unique_ptr&lt;ContentFilter&gt; std::make_unique&lt;ContentFilter&gt;(Container&amp;&amp;, DocumentLoader&amp;);
-    explicit ContentFilter(Container, DocumentLoader&amp;);
</del><ins>+    friend std::unique_ptr&lt;ContentFilter&gt; std::make_unique&lt;ContentFilter&gt;(Container&amp;&amp;, DecisionFunction&amp;&amp;);
+    ContentFilter(Container, DecisionFunction);
</ins><span class="cx"> 
</span><del>-    Container m_contentFilters;
-    DocumentLoader&amp; m_documentLoader;
</del><ins>+    // CachedRawResourceClient
+    void responseReceived(CachedResource*, const ResourceResponse&amp;) override;
+    void dataReceived(CachedResource*, const char* data, int length) override;
+
+    // CachedResourceClient
+    void notifyFinished(CachedResource*) override;
+
+    void forEachContentFilterUntilBlocked(std::function&lt;void(PlatformContentFilter&amp;)&gt;);
+    void didDecide(State);
+
+    const Container m_contentFilters;
+    const DecisionFunction m_decisionFunction;
+    CachedResourceHandle&lt;CachedRawResource&gt; m_mainResource;
+    PlatformContentFilter* m_blockingContentFilter { nullptr };
+    State m_state { State::Initialized };
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> template &lt;typename T&gt;
</span><span class="cx"> ContentFilter::Type ContentFilter::type()
</span><span class="cx"> {
</span><span class="cx">     static_assert(std::is_base_of&lt;PlatformContentFilter, T&gt;::value, &quot;Type must be a PlatformContentFilter.&quot;);
</span><del>-    return { T::canHandleResponse, T::create };
</del><ins>+    return { T::enabled, T::create };
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderDocumentLoadercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/DocumentLoader.cpp (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/DocumentLoader.cpp        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/loader/DocumentLoader.cpp        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -58,6 +58,7 @@
</span><span class="cx"> #include &quot;ProgressTracker.h&quot;
</span><span class="cx"> #include &quot;ResourceHandle.h&quot;
</span><span class="cx"> #include &quot;SchemeRegistry.h&quot;
</span><ins>+#include &quot;ScriptController.h&quot;
</ins><span class="cx"> #include &quot;SecurityPolicy.h&quot;
</span><span class="cx"> #include &quot;Settings.h&quot;
</span><span class="cx"> #include &quot;SubresourceLoader.h&quot;
</span><span class="lines">@@ -140,6 +141,9 @@
</span><span class="cx">     , m_waitingForContentPolicy(false)
</span><span class="cx">     , m_subresourceLoadersArePageCacheAcceptable(false)
</span><span class="cx">     , m_applicationCacheHost(adoptPtr(new ApplicationCacheHost(*this)))
</span><ins>+#if ENABLE(CONTENT_FILTERING)
+    , m_contentFilter(!substituteData.isValid() ? ContentFilter::createIfNeeded(std::bind(&amp;DocumentLoader::contentFilterDidDecide, this)) : nullptr)
+#endif
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -401,21 +405,6 @@
</span><span class="cx">         frameLoader()-&gt;notifier().dispatchDidFinishLoading(this, identifier, finishTime);
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-#if ENABLE(CONTENT_FILTERING)
-    if (m_contentFilter &amp;&amp; m_contentFilter-&gt;needsMoreData()) {
-        m_contentFilter-&gt;finishedAddingData();
-        int length;
-        const char* data = m_contentFilter-&gt;getReplacementData(length);
-        if (data)
-            dataReceived(m_mainResource.get(), data, length);
-
-        if (m_contentFilter-&gt;didBlockData()) {
-            frameLoader()-&gt;client().contentFilterDidBlockLoad(m_contentFilter-&gt;unblockHandler());
-            m_contentFilter = nullptr;
-        }
-    }
-#endif
-
</del><span class="cx">     maybeFinishLoadingMultipartContent();
</span><span class="cx"> 
</span><span class="cx">     double responseEndTime = finishTime;
</span><span class="lines">@@ -662,10 +651,6 @@
</span><span class="cx">     }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-#if ENABLE(CONTENT_FILTERING)
-    m_contentFilter = ContentFilter::createIfNeeded(response, *this);
-#endif
-
</del><span class="cx">     frameLoader()-&gt;policyChecker().checkContentPolicy(m_response, [this](PolicyAction policy) {
</span><span class="cx">         continueAfterContentPolicy(policy);
</span><span class="cx">     });
</span><span class="lines">@@ -818,14 +803,6 @@
</span><span class="cx"> 
</span><span class="cx">         bool userChosen;
</span><span class="cx">         String encoding;
</span><del>-#if ENABLE(CONTENT_FILTERING)
-        // The content filter's replacement data has a known encoding that might
-        // differ from the response's encoding.
-        if (m_contentFilter &amp;&amp; m_contentFilter-&gt;didBlockData()) {
-            ASSERT(!m_contentFilter-&gt;needsMoreData());
-            userChosen = false;
-        } else
-#endif
</del><span class="cx">         if (overrideEncoding().isNull()) {
</span><span class="cx">             userChosen = false;
</span><span class="cx">             encoding = response().textEncodingName();
</span><span class="lines">@@ -870,26 +847,6 @@
</span><span class="cx">     ASSERT(!mainResourceLoader() || !mainResourceLoader()-&gt;defersLoading());
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><del>-#if ENABLE(CONTENT_FILTERING)
-    bool loadWasBlockedBeforeFinishing = false;
-    if (m_contentFilter &amp;&amp; m_contentFilter-&gt;needsMoreData()) {
-        m_contentFilter-&gt;addData(data, length);
-
-        if (m_contentFilter-&gt;needsMoreData()) {
-            // Since the filter still needs more data to make a decision,
-            // avoid committing this data to prevent partial rendering of
-            // content that might later be blocked.
-            return;
-        }
-
-        data = m_contentFilter-&gt;getReplacementData(length);
-        loadWasBlockedBeforeFinishing = m_contentFilter-&gt;didBlockData();
-
-        if (loadWasBlockedBeforeFinishing)
-            frameLoader()-&gt;client().contentFilterDidBlockLoad(m_contentFilter-&gt;unblockHandler());
-    }
-#endif
-
</del><span class="cx">     if (m_identifierForLoadWithoutResourceLoader)
</span><span class="cx">         frameLoader()-&gt;notifier().dispatchDidReceiveData(this, m_identifierForLoadWithoutResourceLoader, data, length, -1);
</span><span class="cx"> 
</span><span class="lines">@@ -898,13 +855,6 @@
</span><span class="cx"> 
</span><span class="cx">     if (!isMultipartReplacingLoad())
</span><span class="cx">         commitLoad(data, length);
</span><del>-
-#if ENABLE(CONTENT_FILTERING)
-    if (loadWasBlockedBeforeFinishing) {
-        cancelMainResourceLoad(frameLoader()-&gt;cancelledError(m_request));
-        m_contentFilter = nullptr;
-    }
-#endif
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void DocumentLoader::setupForReplace()
</span><span class="lines">@@ -960,6 +910,9 @@
</span><span class="cx">     stopLoading();
</span><span class="cx">     if (m_mainResource &amp;&amp; m_mainResource-&gt;hasClient(this))
</span><span class="cx">         m_mainResource-&gt;removeClient(this);
</span><ins>+#if ENABLE(CONTENT_FILTERING)
+    m_contentFilter = nullptr;
+#endif
</ins><span class="cx"> 
</span><span class="cx">     m_applicationCacheHost-&gt;setDOMApplicationCache(nullptr);
</span><span class="cx">     InspectorInstrumentation::loaderDetachedFromFrame(*m_frame, *this);
</span><span class="lines">@@ -1471,7 +1424,12 @@
</span><span class="cx">         frameLoader()-&gt;notifier().assignIdentifierToInitialRequest(m_identifierForLoadWithoutResourceLoader, this, request);
</span><span class="cx">         frameLoader()-&gt;notifier().dispatchWillSendRequest(this, m_identifierForLoadWithoutResourceLoader, request, ResourceResponse());
</span><span class="cx">     }
</span><ins>+
+#if ENABLE(CONTENT_FILTERING)
+    becomeMainResourceClientIfFilterAllows();
+#else
</ins><span class="cx">     m_mainResource-&gt;addClient(this);
</span><ins>+#endif
</ins><span class="cx"> 
</span><span class="cx">     // A bunch of headers are set when the underlying ResourceLoader is created, and m_request needs to include those.
</span><span class="cx">     if (mainResourceLoader())
</span><span class="lines">@@ -1507,6 +1465,9 @@
</span><span class="cx"> {
</span><span class="cx">     if (m_mainResource &amp;&amp; m_mainResource-&gt;hasClient(this))
</span><span class="cx">         m_mainResource-&gt;removeClient(this);
</span><ins>+#if ENABLE(CONTENT_FILTERING)
+    m_contentFilter = nullptr;
+#endif
</ins><span class="cx"> 
</span><span class="cx">     m_mainResource = 0;
</span><span class="cx"> }
</span><span class="lines">@@ -1600,4 +1561,56 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+#if ENABLE(CONTENT_FILTERING)
+void DocumentLoader::becomeMainResourceClientIfFilterAllows()
+{
+    ASSERT(m_mainResource);
+    if (m_contentFilter) {
+        ASSERT(m_contentFilter-&gt;state() == ContentFilter::State::Initialized);
+        m_contentFilter-&gt;startFilteringMainResource(*m_mainResource);
+    } else
+        m_mainResource-&gt;addClient(this);
+}
+
+void DocumentLoader::installContentFilterUnblockHandler(ContentFilter&amp; contentFilter)
+{
+    ContentFilterUnblockHandler unblockHandler { contentFilter.unblockHandler() };
+    unblockHandler.setUnreachableURL(documentURL());
+    RefPtr&lt;Frame&gt; frame { this-&gt;frame() };
+    String unblockRequestDeniedScript { contentFilter.unblockRequestDeniedScript() };
+    if (!unblockRequestDeniedScript.isEmpty() &amp;&amp; frame) {
+        static_assert(std::is_base_of&lt;ThreadSafeRefCounted&lt;Frame&gt;, Frame&gt;::value, &quot;Frame must be ThreadSafeRefCounted.&quot;);
+        StringCapture capturedScript { unblockRequestDeniedScript };
+        unblockHandler.wrapWithDecisionHandler([frame, capturedScript](bool unblocked) {
+            if (!unblocked)
+                frame-&gt;script().executeScript(capturedScript.string());
+        });
+    }
+    frameLoader()-&gt;client().contentFilterDidBlockLoad(WTF::move(unblockHandler));
+}
+
+void DocumentLoader::contentFilterDidDecide()
+{
+    using State = ContentFilter::State;
+    ASSERT(m_contentFilter);
+    ASSERT(m_contentFilter-&gt;state() == State::Blocked || m_contentFilter-&gt;state() == State::Allowed);
+    std::unique_ptr&lt;ContentFilter&gt; contentFilter;
+    std::swap(contentFilter, m_contentFilter);
+    if (contentFilter-&gt;state() == State::Allowed) {
+        if (m_mainResource)
+            m_mainResource-&gt;addClient(this);
+        return;
+    }
+
+    installContentFilterUnblockHandler(*contentFilter);
+
+    URL blockedURL;
+    blockedURL.setProtocol(ContentFilter::urlScheme());
+    blockedURL.setHost(ASCIILiteral(&quot;blocked-page&quot;));
+    SubstituteData substituteData { contentFilter-&gt;replacementData(), ASCIILiteral(&quot;text/html&quot;), ASCIILiteral(&quot;UTF-8&quot;), documentURL() };
+    frame()-&gt;navigationScheduler().scheduleSubstituteDataLoad(blockedURL, substituteData);
+}
+#endif
+
+
</ins><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderDocumentLoaderh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/DocumentLoader.h (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/DocumentLoader.h        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/loader/DocumentLoader.h        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -64,7 +64,6 @@
</span><span class="cx">     class ArchiveResourceCollection;
</span><span class="cx">     class CachedRawResource;
</span><span class="cx">     class CachedResourceLoader;
</span><del>-    class ContentFilter;
</del><span class="cx">     class FormState;
</span><span class="cx">     class Frame;
</span><span class="cx">     class FrameLoader;
</span><span class="lines">@@ -73,6 +72,10 @@
</span><span class="cx">     class SharedBuffer;
</span><span class="cx">     class SubstituteResource;
</span><span class="cx"> 
</span><ins>+#if ENABLE(CONTENT_FILTERING)
+    class ContentFilter;
+#endif
+
</ins><span class="cx">     typedef HashMap&lt;unsigned long, RefPtr&lt;ResourceLoader&gt;&gt; ResourceLoaderMap;
</span><span class="cx">     typedef Vector&lt;ResourceResponse&gt; ResponseVector;
</span><span class="cx"> 
</span><span class="lines">@@ -330,6 +333,12 @@
</span><span class="cx"> 
</span><span class="cx">         void clearMainResource();
</span><span class="cx"> 
</span><ins>+#if ENABLE(CONTENT_FILTERING)
+        void becomeMainResourceClientIfFilterAllows();
+        void installContentFilterUnblockHandler(ContentFilter&amp;);
+        void contentFilterDidDecide();
+#endif
+
</ins><span class="cx">         Frame* m_frame;
</span><span class="cx">         Ref&lt;CachedResourceLoader&gt; m_cachedResourceLoader;
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderFrameLoadercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/FrameLoader.cpp (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/FrameLoader.cpp        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/loader/FrameLoader.cpp        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -1124,7 +1124,6 @@
</span><span class="cx"> 
</span><span class="cx"> void FrameLoader::prepareForLoadStart()
</span><span class="cx"> {
</span><del>-    policyChecker().prepareForLoadStart();
</del><span class="cx">     m_progressTracker-&gt;progressStarted();
</span><span class="cx">     m_client.dispatchDidStartProvisionalLoad();
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderNavigationSchedulercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/NavigationScheduler.cpp (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/NavigationScheduler.cpp        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/loader/NavigationScheduler.cpp        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -278,6 +278,26 @@
</span><span class="cx">     bool m_haveToldClient;
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+class ScheduledSubstituteDataLoad : public ScheduledNavigation {
+public:
+    ScheduledSubstituteDataLoad(const URL&amp; baseURL, const SubstituteData&amp; substituteData)
+        : ScheduledNavigation { 0, LockHistory::No, LockBackForwardList::No, false, false }
+        , m_baseURL { baseURL }
+        , m_substituteData { substituteData }
+    {
+    }
+
+    void fire(Frame&amp; frame) override
+    {
+        UserGestureIndicator gestureIndicator { wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture };
+        frame.loader().load(FrameLoadRequest { &amp;frame, m_baseURL, m_substituteData });
+    }
+
+private:
+    URL m_baseURL;
+    SubstituteData m_substituteData;
+};
+
</ins><span class="cx"> NavigationScheduler::NavigationScheduler(Frame&amp; frame)
</span><span class="cx">     : m_frame(frame)
</span><span class="cx">     , m_timer(*this, &amp;NavigationScheduler::timerFired)
</span><span class="lines">@@ -430,6 +450,12 @@
</span><span class="cx">     schedule(std::make_unique&lt;ScheduledHistoryNavigation&gt;(steps));
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void NavigationScheduler::scheduleSubstituteDataLoad(const URL&amp; baseURL, const SubstituteData&amp; substituteData)
+{
+    if (shouldScheduleNavigation())
+        schedule(std::make_unique&lt;ScheduledSubstituteDataLoad&gt;(baseURL, substituteData));
+}
+
</ins><span class="cx"> void NavigationScheduler::timerFired()
</span><span class="cx"> {
</span><span class="cx">     if (!m_frame.page())
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderNavigationSchedulerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/NavigationScheduler.h (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/NavigationScheduler.h        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/loader/NavigationScheduler.h        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -41,6 +41,7 @@
</span><span class="cx"> class Frame;
</span><span class="cx"> class ScheduledNavigation;
</span><span class="cx"> class SecurityOrigin;
</span><ins>+class SubstituteData;
</ins><span class="cx"> class URL;
</span><span class="cx"> 
</span><span class="cx"> class NavigationDisablerForBeforeUnload {
</span><span class="lines">@@ -73,6 +74,7 @@
</span><span class="cx">     void scheduleFormSubmission(PassRefPtr&lt;FormSubmission&gt;);
</span><span class="cx">     void scheduleRefresh();
</span><span class="cx">     void scheduleHistoryNavigation(int steps);
</span><ins>+    void scheduleSubstituteDataLoad(const URL&amp; baseURL, const SubstituteData&amp;);
</ins><span class="cx"> 
</span><span class="cx">     void startTimer();
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderPolicyCheckercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/PolicyChecker.cpp (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/PolicyChecker.cpp        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/loader/PolicyChecker.cpp        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -56,13 +56,6 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void PolicyChecker::prepareForLoadStart()
-{
-#if ENABLE(CONTENT_FILTERING)
-    m_contentFilterUnblockHandler = { };
-#endif
-}
-
</del><span class="cx"> void PolicyChecker::checkNavigationPolicy(const ResourceRequest&amp; newRequest, NavigationPolicyDecisionFunction function)
</span><span class="cx"> {
</span><span class="cx">     checkNavigationPolicy(newRequest, m_frame.loader().activeDocumentLoader(), nullptr, WTF::move(function));
</span><span class="lines">@@ -120,7 +113,9 @@
</span><span class="cx">                 frame-&gt;loader().reload();
</span><span class="cx">         });
</span><span class="cx">         continueAfterNavigationPolicy(PolicyIgnore);
</span><ins>+        return;
</ins><span class="cx">     }
</span><ins>+    m_contentFilterUnblockHandler = { };
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx">     m_delegateIsDecidingNavigationPolicy = true;
</span></span></pre></div>
<a id="trunkSourceWebCoreloaderPolicyCheckerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/loader/PolicyChecker.h (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/loader/PolicyChecker.h        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/loader/PolicyChecker.h        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -54,7 +54,6 @@
</span><span class="cx"> public:
</span><span class="cx">     explicit PolicyChecker(Frame&amp;);
</span><span class="cx"> 
</span><del>-    void prepareForLoadStart();
</del><span class="cx">     void checkNavigationPolicy(const ResourceRequest&amp;, DocumentLoader*, PassRefPtr&lt;FormState&gt;, NavigationPolicyDecisionFunction);
</span><span class="cx">     void checkNavigationPolicy(const ResourceRequest&amp;, NavigationPolicyDecisionFunction);
</span><span class="cx">     void checkNewWindowPolicy(const NavigationAction&amp;, const ResourceRequest&amp;, PassRefPtr&lt;FormState&gt;, const String&amp; frameName, NewWindowPolicyDecisionFunction);
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformContentFilterUnblockHandlerh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/ContentFilterUnblockHandler.h (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/ContentFilterUnblockHandler.h        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/platform/ContentFilterUnblockHandler.h        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CONTENT_FILTERING)
</span><span class="cx"> 
</span><ins>+#include &quot;URL.h&quot;
</ins><span class="cx"> #include &lt;functional&gt;
</span><span class="cx"> #include &lt;wtf/RetainPtr.h&gt;
</span><span class="cx"> #include &lt;wtf/text/WTFString.h&gt;
</span><span class="lines">@@ -47,8 +48,6 @@
</span><span class="cx">     using DecisionHandlerFunction = std::function&lt;void(bool unblocked)&gt;;
</span><span class="cx">     using UnblockRequesterFunction = std::function&lt;void(DecisionHandlerFunction)&gt;;
</span><span class="cx"> 
</span><del>-    static const char* unblockURLScheme() { return &quot;x-apple-content-filter&quot;; }
-
</del><span class="cx">     ContentFilterUnblockHandler() = default;
</span><span class="cx">     WEBCORE_EXPORT ContentFilterUnblockHandler(String unblockURLHost, UnblockRequesterFunction);
</span><span class="cx"> #if PLATFORM(IOS)
</span><span class="lines">@@ -60,11 +59,15 @@
</span><span class="cx">     WEBCORE_EXPORT static bool decode(NSCoder *, ContentFilterUnblockHandler&amp;);
</span><span class="cx">     WEBCORE_EXPORT bool canHandleRequest(const ResourceRequest&amp;) const;
</span><span class="cx">     WEBCORE_EXPORT void requestUnblockAsync(DecisionHandlerFunction) const;
</span><ins>+    void wrapWithDecisionHandler(const DecisionHandlerFunction&amp;);
</ins><span class="cx"> 
</span><span class="cx">     const String&amp; unblockURLHost() const { return m_unblockURLHost; }
</span><ins>+    const URL&amp; unreachableURL() const { return m_unreachableURL; }
+    void setUnreachableURL(const URL&amp; url) { m_unreachableURL = url; }
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     String m_unblockURLHost;
</span><ins>+    URL m_unreachableURL;
</ins><span class="cx">     UnblockRequesterFunction m_unblockRequester;
</span><span class="cx"> #if PLATFORM(IOS)
</span><span class="cx">     RetainPtr&lt;WebFilterEvaluator&gt; m_webFilterEvaluator;
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformPlatformContentFilterh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/PlatformContentFilter.h (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/PlatformContentFilter.h        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/platform/PlatformContentFilter.h        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -26,12 +26,14 @@
</span><span class="cx"> #ifndef PlatformContentFilter_h
</span><span class="cx"> #define PlatformContentFilter_h
</span><span class="cx"> 
</span><del>-#include &quot;ContentFilterUnblockHandler.h&quot;
</del><ins>+#include &lt;wtf/Ref.h&gt;
</ins><span class="cx"> #include &lt;wtf/text/WTFString.h&gt;
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><ins>+class ContentFilterUnblockHandler;
</ins><span class="cx"> class ResourceResponse;
</span><ins>+class SharedBuffer;
</ins><span class="cx"> 
</span><span class="cx"> class PlatformContentFilter {
</span><span class="cx">     WTF_MAKE_FAST_ALLOCATED;
</span><span class="lines">@@ -42,11 +44,12 @@
</span><span class="cx"> 
</span><span class="cx"> public:
</span><span class="cx">     virtual ~PlatformContentFilter() { }
</span><ins>+    virtual void responseReceived(const ResourceResponse&amp;) = 0;
</ins><span class="cx">     virtual void addData(const char* data, int length) = 0;
</span><span class="cx">     virtual void finishedAddingData() = 0;
</span><span class="cx">     virtual bool needsMoreData() const = 0;
</span><span class="cx">     virtual bool didBlockData() const = 0;
</span><del>-    virtual const char* getReplacementData(int&amp; length) const = 0;
</del><ins>+    virtual Ref&lt;SharedBuffer&gt; replacementData() const = 0;
</ins><span class="cx">     virtual ContentFilterUnblockHandler unblockHandler() const = 0;
</span><span class="cx">     virtual String unblockRequestDeniedScript() const { return emptyString(); }
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaContentFilterUnblockHandlerCocoamm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/ContentFilterUnblockHandlerCocoa.mm (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/ContentFilterUnblockHandlerCocoa.mm        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/platform/cocoa/ContentFilterUnblockHandlerCocoa.mm        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -29,6 +29,7 @@
</span><span class="cx"> #if ENABLE(CONTENT_FILTERING)
</span><span class="cx"> 
</span><span class="cx"> #import &quot;BlockExceptions.h&quot;
</span><ins>+#import &quot;ContentFilter.h&quot;
</ins><span class="cx"> #import &quot;ResourceRequest.h&quot;
</span><span class="cx"> 
</span><span class="cx"> #if PLATFORM(IOS)
</span><span class="lines">@@ -43,6 +44,7 @@
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> static NSString * const unblockURLHostKey { @&quot;unblockURLHost&quot; };
</span><ins>+static NSString * const unreachableURLKey { @&quot;unreachableURL&quot; };
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="lines">@@ -60,6 +62,21 @@
</span><span class="cx"> }
</span><span class="cx"> #endif
</span><span class="cx"> 
</span><ins>+void ContentFilterUnblockHandler::wrapWithDecisionHandler(const DecisionHandlerFunction&amp; decisionHandler)
+{
+    ContentFilterUnblockHandler wrapped { *this };
+    UnblockRequesterFunction wrappedRequester { [wrapped, decisionHandler](DecisionHandlerFunction wrappedDecisionHandler) {
+        wrapped.requestUnblockAsync([wrappedDecisionHandler, decisionHandler](bool unblocked) {
+            wrappedDecisionHandler(unblocked);
+            decisionHandler(unblocked);
+        });
+    }};
+#if PLATFORM(IOS)
+    m_webFilterEvaluator = nullptr;
+#endif
+    std::swap(m_unblockRequester, wrappedRequester);
+}
+
</ins><span class="cx"> bool ContentFilterUnblockHandler::needsUIProcess() const
</span><span class="cx"> {
</span><span class="cx"> #if PLATFORM(IOS)
</span><span class="lines">@@ -74,6 +91,7 @@
</span><span class="cx">     ASSERT_ARG(coder, coder.allowsKeyedCoding &amp;&amp; coder.requiresSecureCoding);
</span><span class="cx">     BEGIN_BLOCK_OBJC_EXCEPTIONS;
</span><span class="cx">     [coder encodeObject:m_unblockURLHost forKey:unblockURLHostKey];
</span><ins>+    [coder encodeObject:(NSURL *)m_unreachableURL forKey:unreachableURLKey];
</ins><span class="cx"> #if PLATFORM(IOS)
</span><span class="cx">     [coder encodeObject:m_webFilterEvaluator.get() forKey:webFilterEvaluatorKey];
</span><span class="cx"> #endif
</span><span class="lines">@@ -85,6 +103,7 @@
</span><span class="cx">     ASSERT_ARG(coder, coder.allowsKeyedCoding &amp;&amp; coder.requiresSecureCoding);
</span><span class="cx">     BEGIN_BLOCK_OBJC_EXCEPTIONS;
</span><span class="cx">     unblockHandler.m_unblockURLHost = [coder decodeObjectOfClass:[NSString class] forKey:unblockURLHostKey];
</span><ins>+    unblockHandler.m_unreachableURL = [coder decodeObjectOfClass:[NSURL class] forKey:unreachableURLKey];
</ins><span class="cx"> #if PLATFORM(IOS)
</span><span class="cx">     unblockHandler.m_webFilterEvaluator = [coder decodeObjectOfClass:getWebFilterEvaluatorClass() forKey:webFilterEvaluatorKey];
</span><span class="cx"> #endif
</span><span class="lines">@@ -104,7 +123,7 @@
</span><span class="cx"> #endif
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    return request.url().protocolIs(unblockURLScheme()) &amp;&amp; equalIgnoringCase(request.url().host(), m_unblockURLHost);
</del><ins>+    return request.url().protocolIs(ContentFilter::urlScheme()) &amp;&amp; equalIgnoringCase(request.url().host(), m_unblockURLHost);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> static inline void dispatchToMainThread(void (^block)())
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaNetworkExtensionContentFilterh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/NetworkExtensionContentFilter.h (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/NetworkExtensionContentFilter.h        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/platform/cocoa/NetworkExtensionContentFilter.h        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -27,11 +27,9 @@
</span><span class="cx"> #define NetworkExtensionContentFilter_h
</span><span class="cx"> 
</span><span class="cx"> #include &quot;PlatformContentFilter.h&quot;
</span><del>-#include &quot;SharedBuffer.h&quot;
</del><span class="cx"> #include &lt;objc/NSObjCRuntime.h&gt;
</span><span class="cx"> #include &lt;wtf/Compiler.h&gt;
</span><span class="cx"> #include &lt;wtf/OSObjectPtr.h&gt;
</span><del>-#include &lt;wtf/Ref.h&gt;
</del><span class="cx"> #include &lt;wtf/RetainPtr.h&gt;
</span><span class="cx"> 
</span><span class="cx"> #define HAVE_NETWORK_EXTENSION PLATFORM(IOS) || (PLATFORM(MAC) &amp;&amp; __MAC_OS_X_VERSION_MIN_REQUIRED &gt;= 101000 &amp;&amp; CPU(X86_64))
</span><span class="lines">@@ -42,31 +40,31 @@
</span><span class="cx"> OBJC_CLASS NSData;
</span><span class="cx"> OBJC_CLASS NSDictionary;
</span><span class="cx"> OBJC_CLASS NSMutableData;
</span><del>-
</del><ins>+OBJC_CLASS NSString;
</ins><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> class NetworkExtensionContentFilter final : public PlatformContentFilter {
</span><del>-    friend std::unique_ptr&lt;NetworkExtensionContentFilter&gt; std::make_unique&lt;NetworkExtensionContentFilter&gt;(const ResourceResponse&amp;);
</del><ins>+    friend std::unique_ptr&lt;NetworkExtensionContentFilter&gt; std::make_unique&lt;NetworkExtensionContentFilter&gt;();
</ins><span class="cx"> 
</span><span class="cx"> public:
</span><del>-    static bool canHandleResponse(const ResourceResponse&amp;);
-    static std::unique_ptr&lt;NetworkExtensionContentFilter&gt; create(const ResourceResponse&amp;);
</del><ins>+    static bool enabled();
+    static std::unique_ptr&lt;NetworkExtensionContentFilter&gt; create();
</ins><span class="cx"> 
</span><ins>+    void responseReceived(const ResourceResponse&amp;) override;
</ins><span class="cx">     void addData(const char* data, int length) override;
</span><span class="cx">     void finishedAddingData() override;
</span><span class="cx">     bool needsMoreData() const override;
</span><span class="cx">     bool didBlockData() const override;
</span><del>-    const char* getReplacementData(int&amp; length) const override;
</del><ins>+    Ref&lt;SharedBuffer&gt; replacementData() const override;
</ins><span class="cx">     ContentFilterUnblockHandler unblockHandler() const override;
</span><span class="cx"> 
</span><span class="cx"> private:
</span><del>-    explicit NetworkExtensionContentFilter(const ResourceResponse&amp;);
</del><ins>+    NetworkExtensionContentFilter();
</ins><span class="cx">     void handleDecision(NEFilterSourceStatus, NSData *replacementData);
</span><span class="cx"> 
</span><span class="cx">     NEFilterSourceStatus m_status;
</span><span class="cx">     OSObjectPtr&lt;dispatch_queue_t&gt; m_queue;
</span><span class="cx">     OSObjectPtr&lt;dispatch_semaphore_t&gt; m_semaphore;
</span><del>-    Ref&lt;SharedBuffer&gt; m_originalData;
</del><span class="cx">     RetainPtr&lt;NSData&gt; m_replacementData;
</span><span class="cx">     RetainPtr&lt;NEFilterSource&gt; m_neFilterSource;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaNetworkExtensionContentFiltermm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/NetworkExtensionContentFilter.mm (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/NetworkExtensionContentFilter.mm        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/platform/cocoa/NetworkExtensionContentFilter.mm        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -28,21 +28,21 @@
</span><span class="cx"> 
</span><span class="cx"> #if HAVE(NETWORK_EXTENSION)
</span><span class="cx"> 
</span><ins>+#import &quot;ContentFilterUnblockHandler.h&quot;
</ins><span class="cx"> #import &quot;NEFilterSourceSPI.h&quot;
</span><span class="cx"> #import &quot;ResourceResponse.h&quot;
</span><ins>+#import &quot;SharedBuffer.h&quot;
</ins><span class="cx"> #import &quot;SoftLinking.h&quot;
</span><ins>+#import &quot;URL.h&quot;
</ins><span class="cx"> #import &lt;objc/runtime.h&gt;
</span><span class="cx"> 
</span><span class="cx"> SOFT_LINK_FRAMEWORK(NetworkExtension);
</span><span class="cx"> SOFT_LINK_CLASS(NetworkExtension, NEFilterSource);
</span><span class="cx"> 
</span><span class="cx"> #if HAVE(MODERN_NE_FILTER_SOURCE)
</span><del>-// FIXME: &lt;rdar://problem/20165664&gt; Expose decisionHandler dictionary keys as NSString constants in NEFilterSource.h
-static NSString * const optionsPageData = @&quot;PageData&quot;;
-
</del><span class="cx"> static inline NSData *replacementDataFromDecisionInfo(NSDictionary *decisionInfo)
</span><span class="cx"> {
</span><del>-    id replacementData = decisionInfo[optionsPageData];
</del><ins>+    id replacementData = decisionInfo[NEFilterSourceOptionsPageData];
</ins><span class="cx">     ASSERT(!replacementData || [replacementData isKindOfClass:[NSData class]]);
</span><span class="cx">     return replacementData;
</span><span class="cx"> }
</span><span class="lines">@@ -50,37 +50,38 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-bool NetworkExtensionContentFilter::canHandleResponse(const ResourceResponse&amp; response)
</del><ins>+bool NetworkExtensionContentFilter::enabled()
</ins><span class="cx"> {
</span><del>-    return response.url().protocolIsInHTTPFamily() &amp;&amp; [getNEFilterSourceClass() filterRequired];
</del><ins>+    return [getNEFilterSourceClass() filterRequired];
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-std::unique_ptr&lt;NetworkExtensionContentFilter&gt; NetworkExtensionContentFilter::create(const ResourceResponse&amp; response)
</del><ins>+std::unique_ptr&lt;NetworkExtensionContentFilter&gt; NetworkExtensionContentFilter::create()
</ins><span class="cx"> {
</span><del>-    return std::make_unique&lt;NetworkExtensionContentFilter&gt;(response);
</del><ins>+    return std::make_unique&lt;NetworkExtensionContentFilter&gt;();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-static inline RetainPtr&lt;NEFilterSource&gt; createNEFilterSource(const URL&amp; url, dispatch_queue_t decisionQueue)
-{
-#if HAVE(MODERN_NE_FILTER_SOURCE)
-    UNUSED_PARAM(url);
-    return adoptNS([allocNEFilterSourceInstance() initWithDecisionQueue:decisionQueue]);
-#else
-    UNUSED_PARAM(decisionQueue);
-    return adoptNS([allocNEFilterSourceInstance() initWithURL:url direction:NEFilterSourceDirectionInbound socketIdentifier:0]);
-#endif
-}
-
-NetworkExtensionContentFilter::NetworkExtensionContentFilter(const ResourceResponse&amp; response)
</del><ins>+NetworkExtensionContentFilter::NetworkExtensionContentFilter()
</ins><span class="cx">     : m_status { NEFilterSourceStatusNeedsMoreData }
</span><span class="cx">     , m_queue { adoptOSObject(dispatch_queue_create(&quot;com.apple.WebCore.NEFilterSourceQueue&quot;, DISPATCH_QUEUE_SERIAL)) }
</span><span class="cx">     , m_semaphore { adoptOSObject(dispatch_semaphore_create(0)) }
</span><del>-    , m_originalData { *SharedBuffer::create() }
-    , m_neFilterSource { createNEFilterSource(response.url(), m_queue.get()) }
</del><ins>+#if HAVE(MODERN_NE_FILTER_SOURCE)
+    , m_neFilterSource { adoptNS([allocNEFilterSourceInstance() initWithDecisionQueue:m_queue.get()]) }
+#endif
</ins><span class="cx"> {
</span><span class="cx">     ASSERT([getNEFilterSourceClass() filterRequired]);
</span><ins>+}
</ins><span class="cx"> 
</span><del>-#if HAVE(MODERN_NE_FILTER_SOURCE)
</del><ins>+void NetworkExtensionContentFilter::responseReceived(const ResourceResponse&amp; response)
+{
+    if (!response.url().protocolIsInHTTPFamily()) {
+        m_status = NEFilterSourceStatusPass;
+        return;
+    }
+
+#if !HAVE(MODERN_NE_FILTER_SOURCE)
+    ASSERT(!m_neFilterSource);
+    m_neFilterSource = adoptNS([allocNEFilterSourceInstance() initWithURL:response.url() direction:NEFilterSourceDirectionInbound socketIdentifier:0]);
+#else
</ins><span class="cx">     [m_neFilterSource receivedResponse:response.nsURLResponse() decisionHandler:[this](NEFilterSourceStatus status, NSDictionary *decisionInfo) {
</span><span class="cx">         handleDecision(status, replacementDataFromDecisionInfo(decisionInfo));
</span><span class="cx">     }];
</span><span class="lines">@@ -96,12 +97,6 @@
</span><span class="cx"> {
</span><span class="cx">     RetainPtr&lt;NSData&gt; copiedData { [NSData dataWithBytes:(void*)data length:length] };
</span><span class="cx"> 
</span><del>-    // FIXME: NEFilterSource doesn't buffer data like WebFilterEvaluator does,
-    // so we need to do it ourselves so getReplacementData() can return the
-    // original bytes back to the loader. We should find a way to remove this
-    // additional copy.
-    m_originalData-&gt;append((CFDataRef)copiedData.get());
-
</del><span class="cx"> #if HAVE(MODERN_NE_FILTER_SOURCE)
</span><span class="cx">     [m_neFilterSource receivedData:copiedData.get() decisionHandler:[this](NEFilterSourceStatus status, NSDictionary *decisionInfo) {
</span><span class="cx">         handleDecision(status, replacementDataFromDecisionInfo(decisionInfo));
</span><span class="lines">@@ -148,15 +143,10 @@
</span><span class="cx">     return m_status == NEFilterSourceStatusBlock;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-const char* NetworkExtensionContentFilter::getReplacementData(int&amp; length) const
</del><ins>+Ref&lt;SharedBuffer&gt; NetworkExtensionContentFilter::replacementData() const
</ins><span class="cx"> {
</span><del>-    if (didBlockData()) {
-        length = [m_replacementData length];
-        return static_cast&lt;const char*&gt;([m_replacementData bytes]);
-    }
-
-    length = m_originalData-&gt;size();
-    return m_originalData-&gt;data();
</del><ins>+    ASSERT(didBlockData());
+    return adoptRef(*SharedBuffer::wrapNSData(m_replacementData.get()).leakRef());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> ContentFilterUnblockHandler NetworkExtensionContentFilter::unblockHandler() const
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaParentalControlsContentFilterh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.h (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.h        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.h        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -36,22 +36,25 @@
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> class ParentalControlsContentFilter final : public PlatformContentFilter {
</span><del>-    friend std::unique_ptr&lt;ParentalControlsContentFilter&gt; std::make_unique&lt;ParentalControlsContentFilter&gt;(const ResourceResponse&amp;);
</del><ins>+    friend std::unique_ptr&lt;ParentalControlsContentFilter&gt; std::make_unique&lt;ParentalControlsContentFilter&gt;();
</ins><span class="cx"> 
</span><span class="cx"> public:
</span><del>-    static bool canHandleResponse(const ResourceResponse&amp;);
-    static std::unique_ptr&lt;ParentalControlsContentFilter&gt; create(const ResourceResponse&amp;);
</del><ins>+    static bool enabled();
+    static std::unique_ptr&lt;ParentalControlsContentFilter&gt; create();
</ins><span class="cx"> 
</span><ins>+    void responseReceived(const ResourceResponse&amp;) override;
</ins><span class="cx">     void addData(const char* data, int length) override;
</span><span class="cx">     void finishedAddingData() override;
</span><span class="cx">     bool needsMoreData() const override;
</span><span class="cx">     bool didBlockData() const override;
</span><del>-    const char* getReplacementData(int&amp; length) const override;
</del><ins>+    Ref&lt;SharedBuffer&gt; replacementData() const override;
</ins><span class="cx">     ContentFilterUnblockHandler unblockHandler() const override;
</span><span class="cx"> 
</span><span class="cx"> private:
</span><del>-    explicit ParentalControlsContentFilter(const ResourceResponse&amp;);
</del><ins>+    ParentalControlsContentFilter();
+    void updateFilterState();
</ins><span class="cx"> 
</span><ins>+    OSStatus m_filterState;
</ins><span class="cx">     RetainPtr&lt;WebFilterEvaluator&gt; m_webFilterEvaluator;
</span><span class="cx">     RetainPtr&lt;NSData&gt; m_replacementData;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformcocoaParentalControlsContentFiltermm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.mm (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.mm        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.mm        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -26,7 +26,9 @@
</span><span class="cx"> #import &quot;config.h&quot;
</span><span class="cx"> #import &quot;ParentalControlsContentFilter.h&quot;
</span><span class="cx"> 
</span><ins>+#import &quot;ContentFilterUnblockHandler.h&quot;
</ins><span class="cx"> #import &quot;ResourceResponse.h&quot;
</span><ins>+#import &quot;SharedBuffer.h&quot;
</ins><span class="cx"> #import &quot;SoftLinking.h&quot;
</span><span class="cx"> #import &quot;WebFilterEvaluatorSPI.h&quot;
</span><span class="cx"> #import &lt;objc/runtime.h&gt;
</span><span class="lines">@@ -36,36 +38,49 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-bool ParentalControlsContentFilter::canHandleResponse(const ResourceResponse&amp; response)
</del><ins>+bool ParentalControlsContentFilter::enabled()
</ins><span class="cx"> {
</span><del>-    if (!response.url().protocolIsInHTTPFamily())
-        return false;
</del><ins>+    return [getWebFilterEvaluatorClass() isManagedSession];
+}
</ins><span class="cx"> 
</span><del>-    if ([getWebFilterEvaluatorClass() isManagedSession]) {
-#if PLATFORM(MAC)
-        if (response.url().protocolIs(&quot;https&quot;))
-#endif
-            return true;
-    }
</del><ins>+std::unique_ptr&lt;ParentalControlsContentFilter&gt; ParentalControlsContentFilter::create()
+{
+    return std::make_unique&lt;ParentalControlsContentFilter&gt;();
+}
</ins><span class="cx"> 
</span><del>-    return false;
</del><ins>+ParentalControlsContentFilter::ParentalControlsContentFilter()
+    : m_filterState { kWFEStateBuffering }
+{
+    ASSERT([getWebFilterEvaluatorClass() isManagedSession]);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-std::unique_ptr&lt;ParentalControlsContentFilter&gt; ParentalControlsContentFilter::create(const ResourceResponse&amp; response)
</del><ins>+static inline bool canHandleResponse(const ResourceResponse&amp; response)
</ins><span class="cx"> {
</span><del>-    return std::make_unique&lt;ParentalControlsContentFilter&gt;(response);
</del><ins>+#if PLATFORM(MAC)
+    return response.url().protocolIs(&quot;https&quot;);
+#else
+    return response.url().protocolIsInHTTPFamily();
+#endif
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-ParentalControlsContentFilter::ParentalControlsContentFilter(const ResourceResponse&amp; response)
-    : m_webFilterEvaluator { adoptNS([allocWebFilterEvaluatorInstance() initWithResponse:response.nsURLResponse()]) }
</del><ins>+void ParentalControlsContentFilter::responseReceived(const ResourceResponse&amp; response)
</ins><span class="cx"> {
</span><del>-    ASSERT([getWebFilterEvaluatorClass() isManagedSession]);
</del><ins>+    ASSERT(!m_webFilterEvaluator);
+
+    if (!canHandleResponse(response)) {
+        m_filterState = kWFEStateAllowed;
+        return;
+    }
+
+    m_webFilterEvaluator = adoptNS([allocWebFilterEvaluatorInstance() initWithResponse:response.nsURLResponse()]);
+    updateFilterState();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void ParentalControlsContentFilter::addData(const char* data, int length)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(![m_replacementData.get() length]);
</span><span class="cx">     m_replacementData = [m_webFilterEvaluator addData:[NSData dataWithBytesNoCopy:(void*)data length:length freeWhenDone:NO]];
</span><ins>+    updateFilterState();
</ins><span class="cx">     ASSERT(needsMoreData() || [m_replacementData.get() length]);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -73,22 +88,23 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(![m_replacementData.get() length]);
</span><span class="cx">     m_replacementData = [m_webFilterEvaluator dataComplete];
</span><ins>+    updateFilterState();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool ParentalControlsContentFilter::needsMoreData() const
</span><span class="cx"> {
</span><del>-    return [m_webFilterEvaluator filterState] == kWFEStateBuffering;
</del><ins>+    return m_filterState == kWFEStateBuffering;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool ParentalControlsContentFilter::didBlockData() const
</span><span class="cx"> {
</span><del>-    return [m_webFilterEvaluator wasBlocked];
</del><ins>+    return m_filterState == kWFEStateBlocked;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-const char* ParentalControlsContentFilter::getReplacementData(int&amp; length) const
</del><ins>+Ref&lt;SharedBuffer&gt; ParentalControlsContentFilter::replacementData() const
</ins><span class="cx"> {
</span><del>-    length = [m_replacementData length];
-    return static_cast&lt;const char*&gt;([m_replacementData bytes]);
</del><ins>+    ASSERT(didBlockData());
+    return adoptRef(*SharedBuffer::wrapNSData(m_replacementData.get()).leakRef());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> ContentFilterUnblockHandler ParentalControlsContentFilter::unblockHandler() const
</span><span class="lines">@@ -100,4 +116,9 @@
</span><span class="cx"> #endif
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void ParentalControlsContentFilter::updateFilterState()
+{
+    m_filterState = [m_webFilterEvaluator filterState];
+}
+
</ins><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformspicocoaNEFilterSourceSPIh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/spi/cocoa/NEFilterSourceSPI.h (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/spi/cocoa/NEFilterSourceSPI.h        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/platform/spi/cocoa/NEFilterSourceSPI.h        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -68,6 +68,7 @@
</span><span class="cx"> - (void)finishedLoadingWithDecisionHandler:(NEFilterSourceDecisionHandler)decisionHandler;
</span><span class="cx"> - (void)remediateWithDecisionHandler:(NEFilterSourceDecisionHandler)decisionHandler;
</span><span class="cx"> @end
</span><ins>+
</ins><span class="cx"> #endif
</span><span class="cx"> 
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformspicocoaWebFilterEvaluatorSPIh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/spi/cocoa/WebFilterEvaluatorSPI.h (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/spi/cocoa/WebFilterEvaluatorSPI.h        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/platform/spi/cocoa/WebFilterEvaluatorSPI.h        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -32,7 +32,10 @@
</span><span class="cx"> #import &lt;TargetConditionals.h&gt;
</span><span class="cx"> 
</span><span class="cx"> enum {
</span><del>-    kWFEStateBuffering = 2
</del><ins>+    kWFEStateAllowed = 0,
+    kWFEStateBlocked = 1,
+    kWFEStateBuffering = 2,
+    kWFEStateEvaluating = 3
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> @interface WebFilterEvaluator : NSObject
</span></span></pre></div>
<a id="trunkSourceWebCoretestingMockContentFiltercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/MockContentFilter.cpp (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/MockContentFilter.cpp        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/testing/MockContentFilter.cpp        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -29,8 +29,11 @@
</span><span class="cx"> #if ENABLE(CONTENT_FILTERING)
</span><span class="cx"> 
</span><span class="cx"> #include &quot;ContentFilter.h&quot;
</span><ins>+#include &quot;ContentFilterUnblockHandler.h&quot;
+#include &quot;SharedBuffer.h&quot;
</ins><span class="cx"> #include &lt;mutex&gt;
</span><span class="cx"> #include &lt;wtf/text/CString.h&gt;
</span><ins>+#include &lt;wtf/text/StringBuilder.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="lines">@@ -50,24 +53,23 @@
</span><span class="cx">     return MockContentFilterSettings::singleton();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-bool MockContentFilter::canHandleResponse(const ResourceResponse&amp;)
</del><ins>+bool MockContentFilter::enabled()
</ins><span class="cx"> {
</span><span class="cx">     return settings().enabled();
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-std::unique_ptr&lt;MockContentFilter&gt; MockContentFilter::create(const ResourceResponse&amp; response)
</del><ins>+std::unique_ptr&lt;MockContentFilter&gt; MockContentFilter::create()
</ins><span class="cx"> {
</span><del>-    return std::make_unique&lt;MockContentFilter&gt;(response);
</del><ins>+    return std::make_unique&lt;MockContentFilter&gt;();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-MockContentFilter::MockContentFilter(const ResourceResponse&amp;)
</del><ins>+void MockContentFilter::responseReceived(const ResourceResponse&amp;)
</ins><span class="cx"> {
</span><span class="cx">     maybeDetermineStatus(DecisionPoint::AfterResponse);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void MockContentFilter::addData(const char* data, int length)
</del><ins>+void MockContentFilter::addData(const char*, int)
</ins><span class="cx"> {
</span><del>-    m_replacementData.append(data, length);
</del><span class="cx">     maybeDetermineStatus(DecisionPoint::AfterAddData);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -86,14 +88,15 @@
</span><span class="cx">     return m_status == Status::Blocked;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-const char* MockContentFilter::getReplacementData(int&amp; length) const
</del><ins>+Ref&lt;SharedBuffer&gt; MockContentFilter::replacementData() const
</ins><span class="cx"> {
</span><del>-    length = m_replacementData.size();
-    return m_replacementData.data();
</del><ins>+    ASSERT(didBlockData());
+    return adoptRef(*SharedBuffer::create(m_replacementData.data(), m_replacementData.size()).leakRef());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> ContentFilterUnblockHandler MockContentFilter::unblockHandler() const
</span><span class="cx"> {
</span><ins>+    ASSERT(didBlockData());
</ins><span class="cx">     using DecisionHandlerFunction = ContentFilterUnblockHandler::DecisionHandlerFunction;
</span><span class="cx"> 
</span><span class="cx">     return ContentFilterUnblockHandler {
</span></span></pre></div>
<a id="trunkSourceWebCoretestingMockContentFilterh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/MockContentFilter.h (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/MockContentFilter.h        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/testing/MockContentFilter.h        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -32,18 +32,19 @@
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> class MockContentFilter final : public PlatformContentFilter {
</span><del>-    friend std::unique_ptr&lt;MockContentFilter&gt; std::make_unique&lt;MockContentFilter&gt;(const ResourceResponse&amp;);
</del><ins>+    friend std::unique_ptr&lt;MockContentFilter&gt; std::make_unique&lt;MockContentFilter&gt;();
</ins><span class="cx"> 
</span><span class="cx"> public:
</span><span class="cx">     static void ensureInstalled();
</span><del>-    static bool canHandleResponse(const ResourceResponse&amp;);
-    static std::unique_ptr&lt;MockContentFilter&gt; create(const ResourceResponse&amp;);
</del><ins>+    static bool enabled();
+    static std::unique_ptr&lt;MockContentFilter&gt; create();
</ins><span class="cx"> 
</span><ins>+    void responseReceived(const ResourceResponse&amp;) override;
</ins><span class="cx">     void addData(const char* data, int length) override;
</span><span class="cx">     void finishedAddingData() override;
</span><span class="cx">     bool needsMoreData() const override;
</span><span class="cx">     bool didBlockData() const override;
</span><del>-    const char* getReplacementData(int&amp; length) const override;
</del><ins>+    Ref&lt;SharedBuffer&gt; replacementData() const override;
</ins><span class="cx">     ContentFilterUnblockHandler unblockHandler() const override;
</span><span class="cx">     String unblockRequestDeniedScript() const override;
</span><span class="cx"> 
</span><span class="lines">@@ -54,7 +55,7 @@
</span><span class="cx">         Blocked
</span><span class="cx">     };
</span><span class="cx"> 
</span><del>-    explicit MockContentFilter(const ResourceResponse&amp;);
</del><ins>+    MockContentFilter() = default;
</ins><span class="cx">     void maybeDetermineStatus(MockContentFilterSettings::DecisionPoint);
</span><span class="cx"> 
</span><span class="cx">     Vector&lt;char&gt; m_replacementData;
</span></span></pre></div>
<a id="trunkSourceWebCoretestingMockContentFilterSettingscpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/testing/MockContentFilterSettings.cpp (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/testing/MockContentFilterSettings.cpp        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebCore/testing/MockContentFilterSettings.cpp        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -28,9 +28,11 @@
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(CONTENT_FILTERING)
</span><span class="cx"> 
</span><ins>+#include &quot;ContentFilter.h&quot;
</ins><span class="cx"> #include &quot;ContentFilterUnblockHandler.h&quot;
</span><span class="cx"> #include &lt;mutex&gt;
</span><span class="cx"> #include &lt;wtf/NeverDestroyed.h&gt;
</span><ins>+#include &lt;wtf/text/StringBuilder.h&gt;
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="lines">@@ -50,9 +52,11 @@
</span><span class="cx">     static LazyNeverDestroyed&lt;String&gt; unblockRequestURL;
</span><span class="cx">     static std::once_flag onceFlag;
</span><span class="cx">     std::call_once(onceFlag, [] {
</span><del>-        unblockRequestURL.construct(ContentFilterUnblockHandler::unblockURLScheme());
-        unblockRequestURL.get().append(&quot;://&quot;);
-        unblockRequestURL.get().append(unblockURLHost());
</del><ins>+        StringBuilder unblockRequestURLBuilder;
+        unblockRequestURLBuilder.append(ContentFilter::urlScheme());
+        unblockRequestURLBuilder.append(&quot;://&quot;);
+        unblockRequestURLBuilder.append(unblockURLHost());
+        unblockRequestURL.construct(unblockRequestURLBuilder.toString());
</ins><span class="cx">     });
</span><span class="cx">     return unblockRequestURL;
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebKit2ChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/ChangeLog (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/ChangeLog        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebKit2/ChangeLog        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2015-04-04  Andy Estes  &lt;aestes@apple.com&gt;
+
+        [Content Filtering] Blocked page is not always displayed when it should be
+        https://bugs.webkit.org/show_bug.cgi?id=143410
+
+        Reviewed by Andreas Kling.
+
+        * UIProcess/WebFrameProxy.cpp:
+        (WebKit::WebFrameProxy::didStartProvisionalLoad): Stopped clearing m_contentFilterUnblockHandler here.
+        (WebKit::WebFrameProxy::didHandleContentFilterUnblockNavigation): Started doing it here instead.
+
</ins><span class="cx"> 2015-04-04  Chris Dumez  &lt;cdumez@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [WK2][Cocoa] Add a way to temporarily disable the WebKit Network Cache for testing
</span></span></pre></div>
<a id="trunkSourceWebKit2UIProcessWebFrameProxycpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebKit2/UIProcess/WebFrameProxy.cpp (182355 => 182356)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebKit2/UIProcess/WebFrameProxy.cpp        2015-04-05 06:51:15 UTC (rev 182355)
+++ trunk/Source/WebKit2/UIProcess/WebFrameProxy.cpp        2015-04-05 07:52:14 UTC (rev 182356)
</span><span class="lines">@@ -127,9 +127,6 @@
</span><span class="cx"> void WebFrameProxy::didStartProvisionalLoad(const String&amp; url)
</span><span class="cx"> {
</span><span class="cx">     m_frameLoadState.didStartProvisionalLoad(url);
</span><del>-#if ENABLE(CONTENT_FILTERING)
-    m_contentFilterUnblockHandler = { };
-#endif
</del><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void WebFrameProxy::didReceiveServerRedirectForProvisionalLoad(const String&amp; url)
</span><span class="lines">@@ -236,8 +233,10 @@
</span><span class="cx"> #if ENABLE(CONTENT_FILTERING)
</span><span class="cx"> bool WebFrameProxy::didHandleContentFilterUnblockNavigation(const WebCore::ResourceRequest&amp; request)
</span><span class="cx"> {
</span><del>-    if (!m_contentFilterUnblockHandler.canHandleRequest(request))
</del><ins>+    if (!m_contentFilterUnblockHandler.canHandleRequest(request)) {
+        m_contentFilterUnblockHandler = { };
</ins><span class="cx">         return false;
</span><ins>+    }
</ins><span class="cx"> 
</span><span class="cx">     RefPtr&lt;WebPageProxy&gt; page { m_page };
</span><span class="cx">     ASSERT(page);
</span></span></pre>
</div>
</div>

</body>
</html>