Title: [181791] trunk
Revision
181791
Author
[email protected]
Date
2015-03-20 01:42:59 -0700 (Fri, 20 Mar 2015)

Log Message

[Content Filtering] Add tests for unblock requests
https://bugs.webkit.org/show_bug.cgi?id=142900

Reviewed by Andreas Kling.

Source/WebCore:

Currently the iOS Parental Controls content filter has a mechanism for requesting that a page be unblocked.
WebKit implements this by listening for navigations originating from the filter's error page to a special URL,
and requesting the page be unblocked via platform API, which might cause UI to be displayed. If the unblock is
successful then we schedule a reload of the frame in order to display the unblocked document.

NetworkExtension also supports unblock requests, so in preparation for adopting its API, this patch allows
content filters to specify their own unblock request method, teaches MockContentFilter to provide such a method,
and writes tests to cover both allowed and denied unblock requests.

The content filter that blocks a load creates a ContentFilterUnblockHandler, passing it a lambda that is executed
when a navigation matches the filter's special unblock URL. Filters can also specify that a script be executed in
the context of its error page if the unblock is denied.

All platform content filters can handle unblock requests like this with the exception of iOS Parental Controls in WebKit2.
Since UI can be displayed by the system in this case, the request must be made from within the UI process. Therefore the
existing method is retained of serializing a WebFilterEvaluator and intercepting navigation policy calls in the UI process.

Tests: contentfiltering/allow-after-unblock-request.html
       contentfiltering/block-after-unblock-request.html

* bindings/js/JSMockContentFilterSettingsCustom.cpp:
(WebCore::JSMockContentFilterSettings::decisionPoint): Added some using statements for clarity.
(WebCore::JSMockContentFilterSettings::setDecisionPoint): Ditto.
(WebCore::toJSValue): Returns a JSValue from a Decision.
(WebCore::toDecision): Returns a Decision from a JSValue.
(WebCore::JSMockContentFilterSettings::decision): Used toJSValue.
(WebCore::JSMockContentFilterSettings::setDecision): Used toDecision.
(WebCore::JSMockContentFilterSettings::unblockRequestDecision): Used toJSValue.
(WebCore::JSMockContentFilterSettings::setUnblockRequestDecision): Used toDecision.
* loader/ContentFilter.cpp:
(WebCore::ContentFilter::createIfNeeded): Passed a reference to the owning DocumentLoader.
(WebCore::ContentFilter::ContentFilter): Ditto.
(WebCore::ContentFilter::unblockHandler): If the unblockHandler requests that a script be executed when an
unblock request is denied, create a wrapper unblockHandler that executes that script in m_documentLoader's frame.
* loader/ContentFilter.h:
* loader/DocumentLoader.cpp:
(WebCore::DocumentLoader::responseReceived): Passed this to ContentFilter::createIfNeeded.
* loader/FrameLoader.cpp:
(WebCore::FrameLoader::prepareForLoadStart): Called PolicyChecker::prepareForLoadStart.
* loader/PolicyChecker.cpp:
(WebCore::PolicyChecker::prepareForLoadStart): Reset m_contentFilterUnblockHandler.
(WebCore::PolicyChecker::checkNavigationPolicy): Moved logic to here from WebKit1's WebFrameLoaderClient.
Placing it here allows it to be shared between WebKit1 and WebKit2 (when the unblock handler does not need to
be called in the UI process).
* loader/PolicyChecker.h:
(WebCore::PolicyChecker::setContentFilterUnblockHandler): Added.
* page/Frame.h: Made Frame ThreadSafeRefCounted, since RefPtr<Frames> are captured in lambdas that can be
copied by background threads managed by the underlying platform.
* platform/ContentFilterUnblockHandler.h:
(WebCore::ContentFilterUnblockHandler::unblockURLScheme): Returned the Apple content filter scheme.
(WebCore::ContentFilterUnblockHandler::unblockURLHost): Returned the unblock URL host.
(WebCore::ContentFilterUnblockHandler::clear): Deleted.
* platform/PlatformContentFilter.h:
(WebCore::PlatformContentFilter::unblockRequestDeniedScript): Returned the unblock request denied script.
* platform/cocoa/ContentFilterUnblockHandlerCocoa.mm:
(WebCore::ContentFilterUnblockHandler::ContentFilterUnblockHandler): Added a constructor that takes an
unblockURLHost and a UnblockRequesterFunction. Added an alternate constructor for iOS Parental Controls on
WebKit2 that takes an unblockURLHost and a WebFilterEvaluator.
(WebCore::ContentFilterUnblockHandler::needsUIProcess): Returned true if m_webFilterEvaluator is non-null.
(WebCore::ContentFilterUnblockHandler::encode): Encoded m_unblockURLHost in addition to m_webFilterEvaluator.
(WebCore::ContentFilterUnblockHandler::decode): Decoded m_unblockURLHost in addition to m_webFilterEvaluator.
(WebCore::ContentFilterUnblockHandler::canHandleRequest): Returned true if there is a either a m_unblockRequester
or a m_webFilterEvaluator and the request's host and scheme match those of the unblock request URL.
(WebCore::dispatchToMainThread): Added a helper to dispatch a block to the main thread. Then if the web thread
is enabled on iOS, dispatch it there.
(WebCore::ContentFilterUnblockHandler::requestUnblockAsync): Renamed from handleUnblockRequestAndDispatchIfSuccessful.
Requested an unblock using either m_unblockRequester or m_webFilterEvaluator, then called decisionHandler with the response.
(WebCore::scheme): Moved to ContentFilterUnblockHandler::unblockURLScheme.
(WebCore::ContentFilterUnblockHandler::handleUnblockRequestAndDispatchIfSuccessful): Renamed to requestUnblockAsync.
* platform/cocoa/ParentalControlsContentFilter.mm:
(WebCore::ParentalControlsContentFilter::unblockHandler): Returned an unblock handler using the WebFilterEvaluator constructor.
* testing/MockContentFilter.cpp: Added using statments for clarity.
(WebCore::settings): Added a helper to get MockContentFilterSettings::singleton().
(WebCore::MockContentFilter::canHandleResponse): Used the helper.
(WebCore::MockContentFilter::MockContentFilter): Took advantage of the using statements.
(WebCore::MockContentFilter::addData): Ditto.
(WebCore::MockContentFilter::finishedAddingData): Ditto.
(WebCore::MockContentFilter::unblockHandler): Returned a ContentFilterUnblockHandler that checks settings() for its decision.
(WebCore::MockContentFilter::unblockRequestDeniedScript): Returned the script to execute in MockContentFilter's
error page when an unblock request is denied.
(WebCore::MockContentFilter::maybeDetermineStatus): Took advantage of settings() and using statements.
* testing/MockContentFilterSettings.cpp:
(WebCore::MockContentFilterSettings::unblockRequestURL): Constructed a static unblock URL and returned it.
* testing/MockContentFilterSettings.h:
(WebCore::MockContentFilterSettings::unblockURLHost): Returned the filter's unblock URL host.
(WebCore::MockContentFilterSettings::unblockRequestDecision): Returns the decision to make for an unblock request.
(WebCore::MockContentFilterSettings::setUnblockRequestDecision): Sets the decision to make for an unblock request.
* testing/MockContentFilterSettings.idl: Added the unblockRequestDecision and unblockRequestURL attributes.

Source/WebKit/mac:

* WebCoreSupport/WebFrameLoaderClient.mm:
(WebFrameLoaderClient::dispatchDidStartProvisionalLoad): This now happens in PolicyChecker.
(WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction): Ditto.
* WebView/WebFrame.mm:
(-[WebFrame _contentFilterDidHandleNavigationAction:]): Deleted.
* WebView/WebFrameInternal.h: Removed contentFilterUnblockHandler from WebFramePrivate.

Source/WebKit2:

* UIProcess/Cocoa/WebPageProxyCocoa.mm:
(WebKit::WebPageProxy::contentFilterDidBlockLoadForFrame): Called WebFrameProxy::contentFilterDidBlockLoad.
* UIProcess/WebFrameProxy.cpp:
(WebKit::WebFrameProxy::didStartProvisionalLoad): Assigned a default-constructed ContentFilterUnblockHandler instead of calling clear().
(WebKit::WebFrameProxy::didHandleContentFilterUnblockNavigation): Renamed from contentFilterDidHandleNavigationAction.
Updated to use ContentFilterUnblockHandler's new API.
(WebKit::WebFrameProxy::contentFilterDidHandleNavigationAction): Deleted.
* UIProcess/WebFrameProxy.h:
(WebKit::WebFrameProxy::contentFilterDidBlockLoad): Renamed from setContentFilterUnblockHandler.
(WebKit::WebFrameProxy::setContentFilterUnblockHandler): Deleted.
* UIProcess/WebPageProxy.cpp:
(WebKit::WebPageProxy::decidePolicyForNavigationAction): Called WebFrameProxy::didHandleContentFilterUnblockNavigation.
* WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::contentFilterDidBlockLoad): If the unblock handler needs the UI process,
send WebPageProxy::ContentFilterDidBlockLoadForFrame. Oterwise, call PolicyChecker::setContentFilterUnblockHandler.

LayoutTests:

Taught contentfiltering.js how to perform an unblock request test, and added tests for both allowed and blocked requests.

* contentfiltering/allow-after-unblock-request-expected.html: Added.
* contentfiltering/allow-after-unblock-request.html: Added.
* contentfiltering/block-after-unblock-request-expected.html: Added.
* contentfiltering/block-after-unblock-request.html: Added.
* contentfiltering/resources/contentfiltering.js:
(testContentFiltering): Added an argument specifying if the decision applies to the initial load or the unblock request.
(_doTest): When testing unblock handling, navigate the test iframe to settings.unblockRequestURL when the error page is displayed.
If the unblock is denied, the test harness will call window.unblockRequestDenied(). If the unblock is successful,
the iframe will reload, which we detect by listening for its load event.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (181790 => 181791)


--- trunk/LayoutTests/ChangeLog	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/LayoutTests/ChangeLog	2015-03-20 08:42:59 UTC (rev 181791)
@@ -1,5 +1,24 @@
 2015-03-19  Andy Estes  <[email protected]>
 
+        [Content Filtering] Add tests for unblock requests
+        https://bugs.webkit.org/show_bug.cgi?id=142900
+
+        Reviewed by Andreas Kling.
+
+        Taught contentfiltering.js how to perform an unblock request test, and added tests for both allowed and blocked requests.
+
+        * contentfiltering/allow-after-unblock-request-expected.html: Added.
+        * contentfiltering/allow-after-unblock-request.html: Added.
+        * contentfiltering/block-after-unblock-request-expected.html: Added.
+        * contentfiltering/block-after-unblock-request.html: Added.
+        * contentfiltering/resources/contentfiltering.js:
+        (testContentFiltering): Added an argument specifying if the decision applies to the initial load or the unblock request.
+        (_doTest): When testing unblock handling, navigate the test iframe to settings.unblockRequestURL when the error page is displayed.
+        If the unblock is denied, the test harness will call window.unblockRequestDenied(). If the unblock is successful,
+        the iframe will reload, which we detect by listening for its load event.
+
+2015-03-19  Andy Estes  <[email protected]>
+
         [Content Filtering] Give contentfiltering tests a _javascript_ harness
         https://bugs.webkit.org/show_bug.cgi?id=142899
 

Added: trunk/LayoutTests/contentfiltering/allow-after-unblock-request-expected.html (0 => 181791)


--- trunk/LayoutTests/contentfiltering/allow-after-unblock-request-expected.html	                        (rev 0)
+++ trunk/LayoutTests/contentfiltering/allow-after-unblock-request-expected.html	2015-03-20 08:42:59 UTC (rev 181791)
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<iframe src=""

Added: trunk/LayoutTests/contentfiltering/allow-after-unblock-request.html (0 => 181791)


--- trunk/LayoutTests/contentfiltering/allow-after-unblock-request.html	                        (rev 0)
+++ trunk/LayoutTests/contentfiltering/allow-after-unblock-request.html	2015-03-20 08:42:59 UTC (rev 181791)
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src=""
+<script>
+var internals = window.internals;
+if (internals) {
+    var settings = internals.mockContentFilterSettings;
+    testContentFiltering(/* decisionPoint */settings.DECISION_POINT_AFTER_FINISHED_ADDING_DATA, /* decision */settings.DECISION_ALLOW, /* decideAfterUnblockRequest */true);
+}
+</script>

Added: trunk/LayoutTests/contentfiltering/block-after-unblock-request-expected.html (0 => 181791)


--- trunk/LayoutTests/contentfiltering/block-after-unblock-request-expected.html	                        (rev 0)
+++ trunk/LayoutTests/contentfiltering/block-after-unblock-request-expected.html	2015-03-20 08:42:59 UTC (rev 181791)
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<iframe src=""

Added: trunk/LayoutTests/contentfiltering/block-after-unblock-request.html (0 => 181791)


--- trunk/LayoutTests/contentfiltering/block-after-unblock-request.html	                        (rev 0)
+++ trunk/LayoutTests/contentfiltering/block-after-unblock-request.html	2015-03-20 08:42:59 UTC (rev 181791)
@@ -0,0 +1,9 @@
+<!DOCTYPE html>
+<script src=""
+<script>
+var internals = window.internals;
+if (internals) {
+    var settings = internals.mockContentFilterSettings;
+    testContentFiltering(/* decisionPoint */settings.DECISION_POINT_AFTER_FINISHED_ADDING_DATA, /* decision */settings.DECISION_BLOCK, /* decideAfterUnblockRequest */true);
+}
+</script>

Modified: trunk/LayoutTests/contentfiltering/resources/contentfiltering.js (181790 => 181791)


--- trunk/LayoutTests/contentfiltering/resources/contentfiltering.js	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/LayoutTests/contentfiltering/resources/contentfiltering.js	2015-03-20 08:42:59 UTC (rev 181791)
@@ -1,22 +1,37 @@
-function _doTest(decisionPoint, decision)
+function _doTest(decisionPoint, decision, decideAfterUnblockRequest)
 {
     var settings = window.internals.mockContentFilterSettings;
     settings.enabled = true;
     settings.decisionPoint = decisionPoint;
-    settings.decision = decision;
+    settings.decision = (decideAfterUnblockRequest ? settings.DECISION_BLOCK : decision);
     
     var blockedStringText = (decision === settings.DECISION_ALLOW ? "FAIL" : "PASS");
-    settings.blockedString = "<!DOCTYPE html><body>" + blockedStringText;
+    if (decideAfterUnblockRequest) {
+        settings.unblockRequestDecision = decision;
+        settings.blockedString = "<!DOCTYPE html><script>function unblockRequestDenied() { window.top.postMessage('unblockrequestdenied', '*'); }</script><body>" + blockedStringText;
+    } else
+        settings.blockedString = "<!DOCTYPE html><body>" + blockedStringText;
 
+    var isUnblocking = false;
     var iframe = document.createElement("iframe");
     document.body.appendChild(iframe);
     iframe.addEventListener("load", function(event) {
-        window.testRunner.notifyDone();
+        if (isUnblocking || !decideAfterUnblockRequest) {
+            window.testRunner.notifyDone();
+            return;
+        }
+
+        isUnblocking = true;
+        window.addEventListener("message", function(event) {
+            if (event.data ="" "unblockrequestdenied")
+                window.testRunner.notifyDone();
+        }, false);
+        iframe.contentDocument.location = settings.unblockRequestURL;
     }, false);
     iframe.src = "" html><body>" + (blockedStringText === "FAIL" ? "PASS" : "FAIL");
 }
 
-function testContentFiltering(decisionPoint, decision)
+function testContentFiltering(decisionPoint, decision, decideAfterUnblockRequest)
 {
     if (!window.internals) {
         console.log("This test requires window.internals");
@@ -30,6 +45,6 @@
 
     window.testRunner.waitUntilDone();
     window.addEventListener("load", function(event) {
-        _doTest(decisionPoint, decision);
+        _doTest(decisionPoint, decision, decideAfterUnblockRequest);
     }, false);
 }
\ No newline at end of file

Modified: trunk/Source/WebCore/ChangeLog (181790 => 181791)


--- trunk/Source/WebCore/ChangeLog	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/ChangeLog	2015-03-20 08:42:59 UTC (rev 181791)
@@ -1,3 +1,99 @@
+2015-03-19  Andy Estes  <[email protected]>
+
+        [Content Filtering] Add tests for unblock requests
+        https://bugs.webkit.org/show_bug.cgi?id=142900
+
+        Reviewed by Andreas Kling.
+
+        Currently the iOS Parental Controls content filter has a mechanism for requesting that a page be unblocked.
+        WebKit implements this by listening for navigations originating from the filter's error page to a special URL,
+        and requesting the page be unblocked via platform API, which might cause UI to be displayed. If the unblock is
+        successful then we schedule a reload of the frame in order to display the unblocked document.
+
+        NetworkExtension also supports unblock requests, so in preparation for adopting its API, this patch allows
+        content filters to specify their own unblock request method, teaches MockContentFilter to provide such a method,
+        and writes tests to cover both allowed and denied unblock requests.
+
+        The content filter that blocks a load creates a ContentFilterUnblockHandler, passing it a lambda that is executed
+        when a navigation matches the filter's special unblock URL. Filters can also specify that a script be executed in
+        the context of its error page if the unblock is denied.
+
+        All platform content filters can handle unblock requests like this with the exception of iOS Parental Controls in WebKit2.
+        Since UI can be displayed by the system in this case, the request must be made from within the UI process. Therefore the
+        existing method is retained of serializing a WebFilterEvaluator and intercepting navigation policy calls in the UI process.
+
+        Tests: contentfiltering/allow-after-unblock-request.html
+               contentfiltering/block-after-unblock-request.html
+
+        * bindings/js/JSMockContentFilterSettingsCustom.cpp:
+        (WebCore::JSMockContentFilterSettings::decisionPoint): Added some using statements for clarity.
+        (WebCore::JSMockContentFilterSettings::setDecisionPoint): Ditto.
+        (WebCore::toJSValue): Returns a JSValue from a Decision.
+        (WebCore::toDecision): Returns a Decision from a JSValue.
+        (WebCore::JSMockContentFilterSettings::decision): Used toJSValue.
+        (WebCore::JSMockContentFilterSettings::setDecision): Used toDecision.
+        (WebCore::JSMockContentFilterSettings::unblockRequestDecision): Used toJSValue.
+        (WebCore::JSMockContentFilterSettings::setUnblockRequestDecision): Used toDecision.
+        * loader/ContentFilter.cpp:
+        (WebCore::ContentFilter::createIfNeeded): Passed a reference to the owning DocumentLoader.
+        (WebCore::ContentFilter::ContentFilter): Ditto.
+        (WebCore::ContentFilter::unblockHandler): If the unblockHandler requests that a script be executed when an
+        unblock request is denied, create a wrapper unblockHandler that executes that script in m_documentLoader's frame.
+        * loader/ContentFilter.h:
+        * loader/DocumentLoader.cpp:
+        (WebCore::DocumentLoader::responseReceived): Passed this to ContentFilter::createIfNeeded.
+        * loader/FrameLoader.cpp:
+        (WebCore::FrameLoader::prepareForLoadStart): Called PolicyChecker::prepareForLoadStart.
+        * loader/PolicyChecker.cpp:
+        (WebCore::PolicyChecker::prepareForLoadStart): Reset m_contentFilterUnblockHandler.
+        (WebCore::PolicyChecker::checkNavigationPolicy): Moved logic to here from WebKit1's WebFrameLoaderClient.
+        Placing it here allows it to be shared between WebKit1 and WebKit2 (when the unblock handler does not need to
+        be called in the UI process).
+        * loader/PolicyChecker.h:
+        (WebCore::PolicyChecker::setContentFilterUnblockHandler): Added.
+        * page/Frame.h: Made Frame ThreadSafeRefCounted, since RefPtr<Frames> are captured in lambdas that can be
+        copied by background threads managed by the underlying platform.
+        * platform/ContentFilterUnblockHandler.h:
+        (WebCore::ContentFilterUnblockHandler::unblockURLScheme): Returned the Apple content filter scheme.
+        (WebCore::ContentFilterUnblockHandler::unblockURLHost): Returned the unblock URL host.
+        (WebCore::ContentFilterUnblockHandler::clear): Deleted.
+        * platform/PlatformContentFilter.h:
+        (WebCore::PlatformContentFilter::unblockRequestDeniedScript): Returned the unblock request denied script.
+        * platform/cocoa/ContentFilterUnblockHandlerCocoa.mm:
+        (WebCore::ContentFilterUnblockHandler::ContentFilterUnblockHandler): Added a constructor that takes an
+        unblockURLHost and a UnblockRequesterFunction. Added an alternate constructor for iOS Parental Controls on
+        WebKit2 that takes an unblockURLHost and a WebFilterEvaluator.
+        (WebCore::ContentFilterUnblockHandler::needsUIProcess): Returned true if m_webFilterEvaluator is non-null.
+        (WebCore::ContentFilterUnblockHandler::encode): Encoded m_unblockURLHost in addition to m_webFilterEvaluator.
+        (WebCore::ContentFilterUnblockHandler::decode): Decoded m_unblockURLHost in addition to m_webFilterEvaluator.
+        (WebCore::ContentFilterUnblockHandler::canHandleRequest): Returned true if there is a either a m_unblockRequester
+        or a m_webFilterEvaluator and the request's host and scheme match those of the unblock request URL.
+        (WebCore::dispatchToMainThread): Added a helper to dispatch a block to the main thread. Then if the web thread
+        is enabled on iOS, dispatch it there.
+        (WebCore::ContentFilterUnblockHandler::requestUnblockAsync): Renamed from handleUnblockRequestAndDispatchIfSuccessful.
+        Requested an unblock using either m_unblockRequester or m_webFilterEvaluator, then called decisionHandler with the response.
+        (WebCore::scheme): Moved to ContentFilterUnblockHandler::unblockURLScheme.
+        (WebCore::ContentFilterUnblockHandler::handleUnblockRequestAndDispatchIfSuccessful): Renamed to requestUnblockAsync.
+        * platform/cocoa/ParentalControlsContentFilter.mm:
+        (WebCore::ParentalControlsContentFilter::unblockHandler): Returned an unblock handler using the WebFilterEvaluator constructor.
+        * testing/MockContentFilter.cpp: Added using statments for clarity.
+        (WebCore::settings): Added a helper to get MockContentFilterSettings::singleton().
+        (WebCore::MockContentFilter::canHandleResponse): Used the helper.
+        (WebCore::MockContentFilter::MockContentFilter): Took advantage of the using statements.
+        (WebCore::MockContentFilter::addData): Ditto.
+        (WebCore::MockContentFilter::finishedAddingData): Ditto.
+        (WebCore::MockContentFilter::unblockHandler): Returned a ContentFilterUnblockHandler that checks settings() for its decision.
+        (WebCore::MockContentFilter::unblockRequestDeniedScript): Returned the script to execute in MockContentFilter's
+        error page when an unblock request is denied.
+        (WebCore::MockContentFilter::maybeDetermineStatus): Took advantage of settings() and using statements.
+        * testing/MockContentFilterSettings.cpp:
+        (WebCore::MockContentFilterSettings::unblockRequestURL): Constructed a static unblock URL and returned it.
+        * testing/MockContentFilterSettings.h:
+        (WebCore::MockContentFilterSettings::unblockURLHost): Returned the filter's unblock URL host.
+        (WebCore::MockContentFilterSettings::unblockRequestDecision): Returns the decision to make for an unblock request.
+        (WebCore::MockContentFilterSettings::setUnblockRequestDecision): Sets the decision to make for an unblock request.
+        * testing/MockContentFilterSettings.idl: Added the unblockRequestDecision and unblockRequestURL attributes.
+
 2015-03-20  Carlos Garcia Campos  <[email protected]>
 
         [GTK] Crash due to empty drag image during drag-and-drop

Modified: trunk/Source/WebCore/bindings/js/JSMockContentFilterSettingsCustom.cpp (181790 => 181791)


--- trunk/Source/WebCore/bindings/js/JSMockContentFilterSettingsCustom.cpp	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/bindings/js/JSMockContentFilterSettingsCustom.cpp	2015-03-20 08:42:59 UTC (rev 181791)
@@ -35,6 +35,9 @@
 
 namespace WebCore {
 
+using Decision = MockContentFilterSettings::Decision;
+using DecisionPoint = MockContentFilterSettings::DecisionPoint;
+
 // Must be kept in sync with values in MockContentFilterSettings.idl.
 const uint8_t decisionPointAfterResponse = 0;
 const uint8_t decisionPointAfterAddData = 1;
@@ -42,22 +45,22 @@
 const uint8_t decisionAllow = 0;
 const uint8_t decisionBlock = 1;
 
-JSC::JSValue JSMockContentFilterSettings::decisionPoint(JSC::ExecState*) const
+JSValue JSMockContentFilterSettings::decisionPoint(ExecState*) const
 {
     switch (impl().decisionPoint()) {
-    case MockContentFilterSettings::DecisionPoint::AfterResponse:
+    case DecisionPoint::AfterResponse:
         return jsNumber(decisionPointAfterResponse);
-    case MockContentFilterSettings::DecisionPoint::AfterAddData:
+    case DecisionPoint::AfterAddData:
         return jsNumber(decisionPointAfterAddData);
-    case MockContentFilterSettings::DecisionPoint::AfterFinishedAddingData:
+    case DecisionPoint::AfterFinishedAddingData:
         return jsNumber(decisionPointAfterFinishedAddingData);
     }
 
     ASSERT_NOT_REACHED();
-    return { };
+    return jsUndefined();
 }
 
-void JSMockContentFilterSettings::setDecisionPoint(JSC::ExecState* exec, JSC::JSValue value)
+void JSMockContentFilterSettings::setDecisionPoint(ExecState* exec, JSValue value)
 {
     uint8_t nativeValue { toUInt8(exec, value, EnforceRange) };
     if (exec->hadException())
@@ -65,50 +68,77 @@
 
     switch (nativeValue) {
     case decisionPointAfterResponse:
-        impl().setDecisionPoint(MockContentFilterSettings::DecisionPoint::AfterResponse);
+        impl().setDecisionPoint(DecisionPoint::AfterResponse);
         return;
     case decisionPointAfterAddData:
-        impl().setDecisionPoint(MockContentFilterSettings::DecisionPoint::AfterAddData);
+        impl().setDecisionPoint(DecisionPoint::AfterAddData);
         return;
     case decisionPointAfterFinishedAddingData:
-        impl().setDecisionPoint(MockContentFilterSettings::DecisionPoint::AfterFinishedAddingData);
+        impl().setDecisionPoint(DecisionPoint::AfterFinishedAddingData);
         return;
-    default:
-        throwTypeError(exec, String::format("%u is not a valid decisionPoint value.", nativeValue));
     }
+
+    throwTypeError(exec, String::format("%u is not a valid decisionPoint value.", nativeValue));
 }
 
-JSC::JSValue JSMockContentFilterSettings::decision(JSC::ExecState*) const
+static inline JSValue toJSValue(Decision decision)
 {
-    switch (impl().decision()) {
-    case MockContentFilterSettings::Decision::Allow:
+    switch (decision) {
+    case Decision::Allow:
         return jsNumber(decisionAllow);
-    case MockContentFilterSettings::Decision::Block:
+    case Decision::Block:
         return jsNumber(decisionBlock);
     }
 
     ASSERT_NOT_REACHED();
-    return { };
+    return jsUndefined();
 }
 
-void JSMockContentFilterSettings::setDecision(JSC::ExecState* exec, JSC::JSValue value)
+static inline Decision toDecision(ExecState* exec, JSValue value)
 {
     uint8_t nativeValue { toUInt8(exec, value, EnforceRange) };
     if (exec->hadException())
-        return;
+        return Decision::Allow;
 
     switch (nativeValue) {
     case decisionAllow:
-        impl().setDecision(MockContentFilterSettings::Decision::Allow);
-        return;
+        return Decision::Allow;
     case decisionBlock:
-        impl().setDecision(MockContentFilterSettings::Decision::Block);
-        return;
-    default:
-        throwTypeError(exec, String::format("%u is not a valid decision value.", nativeValue));
+        return Decision::Block;
     }
+
+    throwTypeError(exec, String::format("%u is not a valid decision value.", nativeValue));
+    return Decision::Allow;
 }
 
+JSValue JSMockContentFilterSettings::decision(ExecState*) const
+{
+    return toJSValue(impl().decision());
+}
+
+void JSMockContentFilterSettings::setDecision(ExecState* exec, JSValue value)
+{
+    Decision decision { toDecision(exec, value) };
+    if (exec->hadException())
+        return;
+
+    impl().setDecision(decision);
+}
+
+JSValue JSMockContentFilterSettings::unblockRequestDecision(ExecState*) const
+{
+    return toJSValue(impl().unblockRequestDecision());
+}
+
+void JSMockContentFilterSettings::setUnblockRequestDecision(ExecState* exec, JSValue value)
+{
+    Decision unblockRequestDecision { toDecision(exec, value) };
+    if (exec->hadException())
+        return;
+
+    impl().setUnblockRequestDecision(unblockRequestDecision);
+}
+
 }; // namespace WebCore
 
 #endif // ENABLE(CONTENT_FILTERING)

Modified: trunk/Source/WebCore/loader/ContentFilter.cpp (181790 => 181791)


--- trunk/Source/WebCore/loader/ContentFilter.cpp	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/loader/ContentFilter.cpp	2015-03-20 08:42:59 UTC (rev 181791)
@@ -28,9 +28,14 @@
 
 #if ENABLE(CONTENT_FILTERING)
 
+#include "DocumentLoader.h"
+#include "Frame.h"
 #include "NetworkExtensionContentFilter.h"
 #include "ParentalControlsContentFilter.h"
+#include "ScriptController.h"
+#include <bindings/ScriptValue.h>
 #include <wtf/NeverDestroyed.h>
+#include <wtf/Vector.h>
 
 namespace WebCore {
 
@@ -47,7 +52,7 @@
     return types;
 }
 
-std::unique_ptr<ContentFilter> ContentFilter::createIfNeeded(const ResourceResponse& response)
+std::unique_ptr<ContentFilter> ContentFilter::createIfNeeded(const ResourceResponse& response, DocumentLoader& documentLoader)
 {
     Container filters;
     for (auto& type : types()) {
@@ -58,11 +63,12 @@
     if (filters.isEmpty())
         return nullptr;
 
-    return std::make_unique<ContentFilter>(WTF::move(filters));
+    return std::make_unique<ContentFilter>(WTF::move(filters), documentLoader);
 }
 
-ContentFilter::ContentFilter(Container contentFilters)
+ContentFilter::ContentFilter(Container contentFilters, DocumentLoader& documentLoader)
     : m_contentFilters { WTF::move(contentFilters) }
+    , m_documentLoader { documentLoader }
 {
     ASSERT(!m_contentFilters.isEmpty());
 }
@@ -121,13 +127,32 @@
 {
     ASSERT(didBlockData());
 
+    PlatformContentFilter* blockingFilter = nullptr;
     for (auto& contentFilter : m_contentFilters) {
-        if (contentFilter->didBlockData())
-            return contentFilter->unblockHandler();
+        if (contentFilter->didBlockData()) {
+            blockingFilter = contentFilter.get();
+            break;
+        }
     }
+    ASSERT(blockingFilter);
 
-    ASSERT_NOT_REACHED();
-    return { };
+    StringCapture unblockRequestDeniedScript { blockingFilter->unblockRequestDeniedScript() };
+    if (unblockRequestDeniedScript.string().isEmpty())
+        return blockingFilter->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->unblockHandler() };
+    RefPtr<Frame> 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 && frame)
+                    frame->script().executeScript(unblockRequestDeniedScript.string());
+            });
+        }
+    };
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/loader/ContentFilter.h (181790 => 181791)


--- trunk/Source/WebCore/loader/ContentFilter.h	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/loader/ContentFilter.h	2015-03-20 08:42:59 UTC (rev 181791)
@@ -33,12 +33,13 @@
 
 namespace WebCore {
 
+class DocumentLoader;
 class ResourceResponse;
 
 class ContentFilter final : public PlatformContentFilter {
 public:
     template <typename T> static void addType() { types().append(type<T>()); }
-    static std::unique_ptr<ContentFilter> createIfNeeded(const ResourceResponse&);
+    static std::unique_ptr<ContentFilter> createIfNeeded(const ResourceResponse&, DocumentLoader&);
 
     void addData(const char* data, int length) override;
     void finishedAddingData() override;
@@ -56,10 +57,11 @@
     WEBCORE_EXPORT static Vector<Type>& types();
 
     using Container = Vector<std::unique_ptr<PlatformContentFilter>>;
-    friend std::unique_ptr<ContentFilter> std::make_unique<ContentFilter>(Container&&);
-    explicit ContentFilter(Container);
+    friend std::unique_ptr<ContentFilter> std::make_unique<ContentFilter>(Container&&, DocumentLoader&);
+    explicit ContentFilter(Container, DocumentLoader&);
 
     Container m_contentFilters;
+    DocumentLoader& m_documentLoader;
 };
 
 template <typename T>

Modified: trunk/Source/WebCore/loader/DocumentLoader.cpp (181790 => 181791)


--- trunk/Source/WebCore/loader/DocumentLoader.cpp	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/loader/DocumentLoader.cpp	2015-03-20 08:42:59 UTC (rev 181791)
@@ -663,7 +663,7 @@
 #endif
 
 #if ENABLE(CONTENT_FILTERING)
-    m_contentFilter = ContentFilter::createIfNeeded(response);
+    m_contentFilter = ContentFilter::createIfNeeded(response, *this);
 #endif
 
     frameLoader()->policyChecker().checkContentPolicy(m_response, [this](PolicyAction policy) {

Modified: trunk/Source/WebCore/loader/FrameLoader.cpp (181790 => 181791)


--- trunk/Source/WebCore/loader/FrameLoader.cpp	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/loader/FrameLoader.cpp	2015-03-20 08:42:59 UTC (rev 181791)
@@ -1124,6 +1124,7 @@
 
 void FrameLoader::prepareForLoadStart()
 {
+    policyChecker().prepareForLoadStart();
     m_progressTracker->progressStarted();
     m_client.dispatchDidStartProvisionalLoad();
 

Modified: trunk/Source/WebCore/loader/PolicyChecker.cpp (181790 => 181791)


--- trunk/Source/WebCore/loader/PolicyChecker.cpp	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/loader/PolicyChecker.cpp	2015-03-20 08:42:59 UTC (rev 181791)
@@ -56,6 +56,13 @@
 {
 }
 
+void PolicyChecker::prepareForLoadStart()
+{
+#if ENABLE(CONTENT_FILTERING)
+    m_contentFilterUnblockHandler = { };
+#endif
+}
+
 void PolicyChecker::checkNavigationPolicy(const ResourceRequest& newRequest, NavigationPolicyDecisionFunction function)
 {
     checkNavigationPolicy(newRequest, m_frame.loader().activeDocumentLoader(), nullptr, WTF::move(function));
@@ -105,6 +112,17 @@
     }
 #endif
 
+#if ENABLE(CONTENT_FILTERING)
+    if (m_contentFilterUnblockHandler.canHandleRequest(request)) {
+        RefPtr<Frame> frame { &m_frame };
+        m_contentFilterUnblockHandler.requestUnblockAsync([frame](bool unblocked) {
+            if (unblocked)
+                frame->loader().reload();
+        });
+        continueAfterNavigationPolicy(PolicyIgnore);
+    }
+#endif
+
     m_delegateIsDecidingNavigationPolicy = true;
     m_frame.loader().client().dispatchDecidePolicyForNavigationAction(action, request, formState, [this](PolicyAction action) {
         continueAfterNavigationPolicy(action);

Modified: trunk/Source/WebCore/loader/PolicyChecker.h (181790 => 181791)


--- trunk/Source/WebCore/loader/PolicyChecker.h	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/loader/PolicyChecker.h	2015-03-20 08:42:59 UTC (rev 181791)
@@ -36,6 +36,10 @@
 #include <wtf/PassRefPtr.h>
 #include <wtf/text/WTFString.h>
 
+#if ENABLE(CONTENT_FILTERING)
+#include "ContentFilterUnblockHandler.h"
+#endif
+
 namespace WebCore {
 
 class DocumentLoader;
@@ -50,6 +54,7 @@
 public:
     explicit PolicyChecker(Frame&);
 
+    void prepareForLoadStart();
     void checkNavigationPolicy(const ResourceRequest&, DocumentLoader*, PassRefPtr<FormState>, NavigationPolicyDecisionFunction);
     void checkNavigationPolicy(const ResourceRequest&, NavigationPolicyDecisionFunction);
     void checkNewWindowPolicy(const NavigationAction&, const ResourceRequest&, PassRefPtr<FormState>, const String& frameName, NewWindowPolicyDecisionFunction);
@@ -74,6 +79,10 @@
     // the heart to hack on all the platforms to make that happen right now.
     void continueLoadAfterWillSubmitForm(PolicyAction);
 
+#if ENABLE(CONTENT_FILTERING)
+    void setContentFilterUnblockHandler(ContentFilterUnblockHandler unblockHandler) { m_contentFilterUnblockHandler = WTF::move(unblockHandler); }
+#endif
+
 private:
     void continueAfterNavigationPolicy(PolicyAction);
     void continueAfterNewWindowPolicy(PolicyAction);
@@ -91,6 +100,10 @@
     // on navigation action delegate callbacks.
     FrameLoadType m_loadType;
     PolicyCallback m_callback;
+
+#if ENABLE(CONTENT_FILTERING)
+    ContentFilterUnblockHandler m_contentFilterUnblockHandler;
+#endif
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/page/Frame.h (181790 => 181791)


--- trunk/Source/WebCore/page/Frame.h	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/page/Frame.h	2015-03-20 08:42:59 UTC (rev 181791)
@@ -36,7 +36,7 @@
 #include "ScrollTypes.h"
 #include "UserScriptTypes.h"
 #include <memory>
-#include <wtf/RefCounted.h>
+#include <wtf/ThreadSafeRefCounted.h>
 
 #if PLATFORM(IOS)
 #include "ViewportArguments.h"
@@ -113,7 +113,7 @@
     };
     typedef unsigned LayerTreeFlags;
 
-    class Frame : public RefCounted<Frame> {
+    class Frame : public ThreadSafeRefCounted<Frame> {
     public:
         WEBCORE_EXPORT static Ref<Frame> create(Page*, HTMLFrameOwnerElement*, FrameLoaderClient*);
 

Modified: trunk/Source/WebCore/platform/ContentFilterUnblockHandler.h (181790 => 181791)


--- trunk/Source/WebCore/platform/ContentFilterUnblockHandler.h	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/platform/ContentFilterUnblockHandler.h	2015-03-20 08:42:59 UTC (rev 181791)
@@ -30,10 +30,13 @@
 
 #include <functional>
 #include <wtf/RetainPtr.h>
+#include <wtf/text/WTFString.h>
 
-OBJC_CLASS NSKeyedArchiver;
-OBJC_CLASS NSKeyedUnarchiver;
+OBJC_CLASS NSCoder;
+
+#if PLATFORM(IOS)
 OBJC_CLASS WebFilterEvaluator;
+#endif
 
 namespace WebCore {
 
@@ -41,19 +44,31 @@
 
 class ContentFilterUnblockHandler {
 public:
-    ContentFilterUnblockHandler() = default;
-    explicit ContentFilterUnblockHandler(WebFilterEvaluator *evaluator) : m_webFilterEvaluator { evaluator } { }
+    using DecisionHandlerFunction = std::function<void(bool unblocked)>;
+    using UnblockRequesterFunction = std::function<void(DecisionHandlerFunction)>;
 
-    void clear() { m_webFilterEvaluator = nullptr; }
-    WEBCORE_EXPORT void encode(NSKeyedArchiver *) const;
-    WEBCORE_EXPORT static bool decode(NSKeyedUnarchiver *, ContentFilterUnblockHandler&);
+    static const char* unblockURLScheme() { return "x-apple-content-filter"; }
 
+    ContentFilterUnblockHandler() = default;
+    WEBCORE_EXPORT ContentFilterUnblockHandler(String unblockURLHost, UnblockRequesterFunction);
 #if PLATFORM(IOS)
-    WEBCORE_EXPORT bool handleUnblockRequestAndDispatchIfSuccessful(const ResourceRequest&, std::function<void()>);
+    ContentFilterUnblockHandler(String unblockURLHost, RetainPtr<WebFilterEvaluator>);
 #endif
 
+    WEBCORE_EXPORT bool needsUIProcess() const;
+    WEBCORE_EXPORT void encode(NSCoder *) const;
+    WEBCORE_EXPORT static bool decode(NSCoder *, ContentFilterUnblockHandler&);
+    WEBCORE_EXPORT bool canHandleRequest(const ResourceRequest&) const;
+    WEBCORE_EXPORT void requestUnblockAsync(DecisionHandlerFunction) const;
+
+    const String& unblockURLHost() const { return m_unblockURLHost; }
+
 private:
+    String m_unblockURLHost;
+    UnblockRequesterFunction m_unblockRequester;
+#if PLATFORM(IOS)
     RetainPtr<WebFilterEvaluator> m_webFilterEvaluator;
+#endif
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/PlatformContentFilter.h (181790 => 181791)


--- trunk/Source/WebCore/platform/PlatformContentFilter.h	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/platform/PlatformContentFilter.h	2015-03-20 08:42:59 UTC (rev 181791)
@@ -48,6 +48,7 @@
     virtual bool didBlockData() const = 0;
     virtual const char* getReplacementData(int& length) const = 0;
     virtual ContentFilterUnblockHandler unblockHandler() const = 0;
+    virtual String unblockRequestDeniedScript() const { return emptyString(); }
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/platform/cocoa/ContentFilterUnblockHandlerCocoa.mm (181790 => 181791)


--- trunk/Source/WebCore/platform/cocoa/ContentFilterUnblockHandlerCocoa.mm	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/platform/cocoa/ContentFilterUnblockHandlerCocoa.mm	2015-03-20 08:42:59 UTC (rev 181791)
@@ -28,62 +28,118 @@
 
 #if ENABLE(CONTENT_FILTERING)
 
+#import "BlockExceptions.h"
 #import "ResourceRequest.h"
+
+#if PLATFORM(IOS)
 #import "SoftLinking.h"
+#import "WebCoreThreadRun.h"
 #import "WebFilterEvaluatorSPI.h"
-#import <objc/runtime.h>
 
 SOFT_LINK_PRIVATE_FRAMEWORK(WebContentAnalysis);
 SOFT_LINK_CLASS(WebContentAnalysis, WebFilterEvaluator);
 
+static NSString * const webFilterEvaluatorKey { @"webFilterEvaluator" };
+#endif
+
+static NSString * const unblockURLHostKey { @"unblockURLHost" };
+
 namespace WebCore {
 
-static NSString * const platformContentFilterKey = @"platformContentFilter";
+ContentFilterUnblockHandler::ContentFilterUnblockHandler(String unblockURLHost, UnblockRequesterFunction unblockRequester)
+    : m_unblockURLHost { WTF::move(unblockURLHost) }
+    , m_unblockRequester { WTF::move(unblockRequester) }
+{
+}
 
-void ContentFilterUnblockHandler::encode(NSKeyedArchiver *archiver) const
+#if PLATFORM(IOS)
+ContentFilterUnblockHandler::ContentFilterUnblockHandler(String unblockURLHost, RetainPtr<WebFilterEvaluator> evaluator)
+    : m_unblockURLHost { WTF::move(unblockURLHost) }
+    , m_webFilterEvaluator { WTF::move(evaluator) }
 {
-    if ([getWebFilterEvaluatorClass() conformsToProtocol:@protocol(NSSecureCoding)])
-        [archiver encodeObject:m_webFilterEvaluator.get() forKey:platformContentFilterKey];
 }
+#endif
 
-bool ContentFilterUnblockHandler::decode(NSKeyedUnarchiver *unarchiver, ContentFilterUnblockHandler& unblockHandler)
+bool ContentFilterUnblockHandler::needsUIProcess() const
 {
-    @try {
-        if ([getWebFilterEvaluatorClass() conformsToProtocol:@protocol(NSSecureCoding)])
-            unblockHandler.m_webFilterEvaluator = (WebFilterEvaluator *)[unarchiver decodeObjectOfClass:getWebFilterEvaluatorClass() forKey:platformContentFilterKey];
-        return true;
-    } @catch (NSException *exception) {
-        LOG_ERROR("The platform content filter being decoded is not a WebFilterEvaluator.");
-    }
-    
+#if PLATFORM(IOS)
+    return m_webFilterEvaluator;
+#else
     return false;
+#endif
 }
 
+void ContentFilterUnblockHandler::encode(NSCoder *coder) const
+{
+    ASSERT_ARG(coder, coder.allowsKeyedCoding && coder.requiresSecureCoding);
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+    [coder encodeObject:m_unblockURLHost forKey:unblockURLHostKey];
 #if PLATFORM(IOS)
-static inline const char* scheme()
+    [coder encodeObject:m_webFilterEvaluator.get() forKey:webFilterEvaluatorKey];
+#endif
+    END_BLOCK_OBJC_EXCEPTIONS;
+}
+
+bool ContentFilterUnblockHandler::decode(NSCoder *coder, ContentFilterUnblockHandler& unblockHandler)
 {
-    return "x-apple-content-filter";
+    ASSERT_ARG(coder, coder.allowsKeyedCoding && coder.requiresSecureCoding);
+    BEGIN_BLOCK_OBJC_EXCEPTIONS;
+    unblockHandler.m_unblockURLHost = [coder decodeObjectOfClass:[NSString class] forKey:unblockURLHostKey];
+#if PLATFORM(IOS)
+    unblockHandler.m_webFilterEvaluator = [coder decodeObjectOfClass:getWebFilterEvaluatorClass() forKey:webFilterEvaluatorKey];
+#endif
+    return true;
+    END_BLOCK_OBJC_EXCEPTIONS;
+    return false;
 }
 
-bool ContentFilterUnblockHandler::handleUnblockRequestAndDispatchIfSuccessful(const ResourceRequest& request, std::function<void()> function)
+bool ContentFilterUnblockHandler::canHandleRequest(const ResourceRequest& request) const
 {
-    if (!m_webFilterEvaluator)
+    if (!m_unblockRequester) {
+#if PLATFORM(IOS)
+        if (!m_webFilterEvaluator)
+            return false;
+#else
         return false;
+#endif
+    }
 
-    if (!request.url().protocolIs(scheme()))
-        return false;
+    return request.url().protocolIs(unblockURLScheme()) && equalIgnoringCase(request.url().host(), m_unblockURLHost);
+}
 
-    if (!equalIgnoringCase(request.url().host(), "unblock"))
-        return false;
+static inline void dispatchToMainThread(void (^block)())
+{
+    dispatch_async(dispatch_get_main_queue(), ^{
+#if PLATFORM(IOS)
+        WebThreadRun(block);
+#else
+        block();
+#endif
+    });
+}
 
-    [m_webFilterEvaluator unblockWithCompletion:^(BOOL unblocked, NSError *) {
-        if (unblocked)
-            function();
-    }];
-    return true;
-}
+void ContentFilterUnblockHandler::requestUnblockAsync(DecisionHandlerFunction decisionHandler) const
+{
+#if PLATFORM(IOS)
+    if (m_webFilterEvaluator) {
+        [m_webFilterEvaluator unblockWithCompletion:[decisionHandler](BOOL unblocked, NSError *) {
+            dispatchToMainThread([decisionHandler, unblocked] {
+                decisionHandler(unblocked);
+            });
+        }];
+        return;
+    }
 #endif
 
+    if (m_unblockRequester) {
+        m_unblockRequester([decisionHandler](bool unblocked) {
+            dispatchToMainThread([decisionHandler, unblocked] {
+                decisionHandler(unblocked);
+            });
+        });
+    }
+}
+
 } // namespace WebCore
 
 #endif // PLATFORM(IOS) && ENABLE(CONTENT_FILTERING)

Modified: trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.mm (181790 => 181791)


--- trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.mm	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.mm	2015-03-20 08:42:59 UTC (rev 181791)
@@ -93,7 +93,11 @@
 
 ContentFilterUnblockHandler ParentalControlsContentFilter::unblockHandler() const
 {
-    return ContentFilterUnblockHandler { m_webFilterEvaluator.get() };
+#if PLATFORM(IOS)
+    return ContentFilterUnblockHandler { "unblock", m_webFilterEvaluator };
+#else
+    return { };
+#endif
 }
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/testing/MockContentFilter.cpp (181790 => 181791)


--- trunk/Source/WebCore/testing/MockContentFilter.cpp	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/testing/MockContentFilter.cpp	2015-03-20 08:42:59 UTC (rev 181791)
@@ -34,6 +34,9 @@
 
 namespace WebCore {
 
+using Decision = MockContentFilterSettings::Decision;
+using DecisionPoint = MockContentFilterSettings::DecisionPoint;
+
 void MockContentFilter::ensureInstalled()
 {
     static std::once_flag onceFlag;
@@ -42,9 +45,14 @@
     });
 }
 
+static inline MockContentFilterSettings& settings()
+{
+    return MockContentFilterSettings::singleton();
+}
+
 bool MockContentFilter::canHandleResponse(const ResourceResponse&)
 {
-    return MockContentFilterSettings::singleton().enabled();
+    return settings().enabled();
 }
 
 std::unique_ptr<MockContentFilter> MockContentFilter::create(const ResourceResponse& response)
@@ -54,18 +62,18 @@
 
 MockContentFilter::MockContentFilter(const ResourceResponse&)
 {
-    maybeDetermineStatus(MockContentFilterSettings::DecisionPoint::AfterResponse);
+    maybeDetermineStatus(DecisionPoint::AfterResponse);
 }
 
 void MockContentFilter::addData(const char* data, int length)
 {
     m_replacementData.append(data, length);
-    maybeDetermineStatus(MockContentFilterSettings::DecisionPoint::AfterAddData);
+    maybeDetermineStatus(DecisionPoint::AfterAddData);
 }
 
 void MockContentFilter::finishedAddingData()
 {
-    maybeDetermineStatus(MockContentFilterSettings::DecisionPoint::AfterFinishedAddingData);
+    maybeDetermineStatus(DecisionPoint::AfterFinishedAddingData);
 }
 
 bool MockContentFilter::needsMoreData() const
@@ -86,20 +94,34 @@
 
 ContentFilterUnblockHandler MockContentFilter::unblockHandler() const
 {
-    return { };
+    using DecisionHandlerFunction = ContentFilterUnblockHandler::DecisionHandlerFunction;
+
+    return ContentFilterUnblockHandler {
+        MockContentFilterSettings::unblockURLHost(), [](DecisionHandlerFunction decisionHandler) {
+            bool shouldAllow { settings().unblockRequestDecision() == Decision::Allow };
+            if (shouldAllow)
+                settings().setDecision(Decision::Allow);
+            decisionHandler(shouldAllow);
+        }
+    };
 }
 
-void MockContentFilter::maybeDetermineStatus(MockContentFilterSettings::DecisionPoint decisionPoint)
+String MockContentFilter::unblockRequestDeniedScript() const
 {
-    if (m_status != Status::NeedsMoreData || decisionPoint != MockContentFilterSettings::singleton().decisionPoint())
+    return ASCIILiteral("unblockRequestDenied()");
+}
+
+void MockContentFilter::maybeDetermineStatus(DecisionPoint decisionPoint)
+{
+    if (m_status != Status::NeedsMoreData || decisionPoint != settings().decisionPoint())
         return;
 
-    m_status = MockContentFilterSettings::singleton().decision() == MockContentFilterSettings::Decision::Allow ? Status::Allowed : Status::Blocked;
+    m_status = settings().decision() == Decision::Allow ? Status::Allowed : Status::Blocked;
     if (m_status != Status::Blocked)
         return;
 
     m_replacementData.clear();
-    const CString utf8BlockedString = MockContentFilterSettings::singleton().blockedString().utf8();
+    const CString utf8BlockedString = settings().blockedString().utf8();
     m_replacementData.append(utf8BlockedString.data(), utf8BlockedString.length());
 }
 

Modified: trunk/Source/WebCore/testing/MockContentFilter.h (181790 => 181791)


--- trunk/Source/WebCore/testing/MockContentFilter.h	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/testing/MockContentFilter.h	2015-03-20 08:42:59 UTC (rev 181791)
@@ -45,6 +45,7 @@
     bool didBlockData() const override;
     const char* getReplacementData(int& length) const override;
     ContentFilterUnblockHandler unblockHandler() const override;
+    String unblockRequestDeniedScript() const override;
 
 private:
     enum class Status {

Modified: trunk/Source/WebCore/testing/MockContentFilterSettings.cpp (181790 => 181791)


--- trunk/Source/WebCore/testing/MockContentFilterSettings.cpp	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/testing/MockContentFilterSettings.cpp	2015-03-20 08:42:59 UTC (rev 181791)
@@ -28,6 +28,8 @@
 
 #if ENABLE(CONTENT_FILTERING)
 
+#include "ContentFilterUnblockHandler.h"
+#include <mutex>
 #include <wtf/NeverDestroyed.h>
 
 namespace WebCore {
@@ -43,6 +45,18 @@
     singleton() = MockContentFilterSettings();
 }
 
+const String& MockContentFilterSettings::unblockRequestURL() const
+{
+    static LazyNeverDestroyed<String> unblockRequestURL;
+    static std::once_flag onceFlag;
+    std::call_once(onceFlag, [] {
+        unblockRequestURL.construct(ContentFilterUnblockHandler::unblockURLScheme());
+        unblockRequestURL.get().append("://");
+        unblockRequestURL.get().append(unblockURLHost());
+    });
+    return unblockRequestURL;
+}
+
 }; // namespace WebCore
 
 #endif // ENABLE(CONTENT_FILTERING)

Modified: trunk/Source/WebCore/testing/MockContentFilterSettings.h (181790 => 181791)


--- trunk/Source/WebCore/testing/MockContentFilterSettings.h	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/testing/MockContentFilterSettings.h	2015-03-20 08:42:59 UTC (rev 181791)
@@ -32,8 +32,8 @@
 namespace WebCore {
 
 class MockContentFilterSettings {
-    WTF_MAKE_FAST_ALLOCATED;
     friend class NeverDestroyed<MockContentFilterSettings>;
+
 public:
     enum class DecisionPoint {
         AfterResponse,
@@ -48,6 +48,7 @@
 
     static MockContentFilterSettings& singleton();
     static void reset();
+    static const char* unblockURLHost() { return "mock-unblock"; }
 
     // Trick the generated bindings into thinking we're RefCounted.
     void ref() { }
@@ -65,6 +66,11 @@
     Decision decision() const { return m_decision; }
     void setDecision(Decision decision) { m_decision = decision; }
 
+    Decision unblockRequestDecision() const { return m_unblockRequestDecision; }
+    void setUnblockRequestDecision(Decision unblockRequestDecision) { m_unblockRequestDecision = unblockRequestDecision; }
+
+    const String& unblockRequestURL() const;
+
 private:
     MockContentFilterSettings() = default;
     MockContentFilterSettings(const MockContentFilterSettings&) = delete;
@@ -73,6 +79,7 @@
     bool m_enabled { false };
     DecisionPoint m_decisionPoint { DecisionPoint::AfterResponse };
     Decision m_decision { Decision::Allow };
+    Decision m_unblockRequestDecision { Decision::Block };
     String m_blockedString;
 };
 

Modified: trunk/Source/WebCore/testing/MockContentFilterSettings.idl (181790 => 181791)


--- trunk/Source/WebCore/testing/MockContentFilterSettings.idl	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebCore/testing/MockContentFilterSettings.idl	2015-03-20 08:42:59 UTC (rev 181791)
@@ -39,4 +39,7 @@
     const octet DECISION_ALLOW = 0;
     const octet DECISION_BLOCK = 1;
     [Custom] attribute octet decision;
+    [Custom] attribute octet unblockRequestDecision;
+
+    readonly attribute DOMString unblockRequestURL;
 };

Modified: trunk/Source/WebKit/mac/ChangeLog (181790 => 181791)


--- trunk/Source/WebKit/mac/ChangeLog	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebKit/mac/ChangeLog	2015-03-20 08:42:59 UTC (rev 181791)
@@ -1,3 +1,17 @@
+2015-03-19  Andy Estes  <[email protected]>
+
+        [Content Filtering] Add tests for unblock requests
+        https://bugs.webkit.org/show_bug.cgi?id=142900
+
+        Reviewed by Andreas Kling.
+
+        * WebCoreSupport/WebFrameLoaderClient.mm:
+        (WebFrameLoaderClient::dispatchDidStartProvisionalLoad): This now happens in PolicyChecker.
+        (WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction): Ditto.
+        * WebView/WebFrame.mm:
+        (-[WebFrame _contentFilterDidHandleNavigationAction:]): Deleted.
+        * WebView/WebFrameInternal.h: Removed contentFilterUnblockHandler from WebFramePrivate.
+
 2015-03-19  Enrica Casucci  <[email protected]>
 
         <attachment> should put URLs on the pasteboard so that Finder can accept drops.

Modified: trunk/Source/WebKit/mac/WebCoreSupport/WebFrameLoaderClient.mm (181790 => 181791)


--- trunk/Source/WebKit/mac/WebCoreSupport/WebFrameLoaderClient.mm	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebKit/mac/WebCoreSupport/WebFrameLoaderClient.mm	2015-03-20 08:42:59 UTC (rev 181791)
@@ -151,6 +151,10 @@
 #import <WebCore/RuntimeApplicationChecksIOS.h>
 #endif
 
+#if ENABLE(CONTENT_FILTERING)
+#import <WebCore/PolicyChecker.h>
+#endif
+
 using namespace WebCore;
 using namespace HTMLNames;
 
@@ -665,7 +669,6 @@
 {
     ASSERT(!m_webFrame->_private->provisionalURL);
     m_webFrame->_private->provisionalURL = core(m_webFrame.get())->loader().provisionalDocumentLoader()->url().string();
-    m_webFrame->_private->contentFilterUnblockHandler.clear();
 
     WebView *webView = getWebView(m_webFrame.get());
 #if !PLATFORM(IOS)
@@ -880,11 +883,6 @@
 
 void WebFrameLoaderClient::dispatchDecidePolicyForNavigationAction(const NavigationAction& action, const ResourceRequest& request, PassRefPtr<FormState> formState, FramePolicyFunction function)
 {
-    if ([m_webFrame _contentFilterDidHandleNavigationAction:request]) {
-        function(PolicyIgnore);
-        return;
-    }
-
     WebView *webView = getWebView(m_webFrame.get());
     [[webView _policyDelegateForwarder] webView:webView
                 decidePolicyForNavigationAction:actionDictionary(action, formState)
@@ -2242,10 +2240,12 @@
 }
 #endif
 
+#if ENABLE(CONTENT_FILTERING)
 void WebFrameLoaderClient::contentFilterDidBlockLoad(WebCore::ContentFilterUnblockHandler unblockHandler)
 {
-    m_webFrame->_private->contentFilterUnblockHandler = WTF::move(unblockHandler);
+    core(m_webFrame.get())->loader().policyChecker().setContentFilterUnblockHandler(WTF::move(unblockHandler));
 }
+#endif
 
 @implementation WebFramePolicyListener
 

Modified: trunk/Source/WebKit/mac/WebView/WebFrame.mm (181790 => 181791)


--- trunk/Source/WebKit/mac/WebView/WebFrame.mm	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebKit/mac/WebView/WebFrame.mm	2015-03-20 08:42:59 UTC (rev 181791)
@@ -1002,20 +1002,6 @@
     _private->coreFrame->loader().documentLoader()->commitData((const char *)[data bytes], [data length]);
 }
 
-- (BOOL)_contentFilterDidHandleNavigationAction:(const WebCore::ResourceRequest &)request
-{
-#if PLATFORM(IOS)
-    RetainPtr<WebFrame> retainedMainFrame = [[self webView] mainFrame];
-    return _private->contentFilterUnblockHandler.handleUnblockRequestAndDispatchIfSuccessful(request, [retainedMainFrame] {
-        WebThreadRun(^ {
-            [retainedMainFrame reload];
-        });
-    });
-#else
-    return NO;
-#endif
-}
-
 @end
 
 @implementation WebFrame (WebPrivate)

Modified: trunk/Source/WebKit/mac/WebView/WebFrameInternal.h (181790 => 181791)


--- trunk/Source/WebKit/mac/WebView/WebFrameInternal.h	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebKit/mac/WebView/WebFrameInternal.h	2015-03-20 08:42:59 UTC (rev 181791)
@@ -30,7 +30,6 @@
 
 #import "WebFramePrivate.h"
 #import "WebPreferencesPrivate.h"
-#import <WebCore/ContentFilterUnblockHandler.h>
 #import <WebCore/EditAction.h>
 #import <WebCore/FrameLoaderTypes.h>
 #import <WebCore/FrameSelection.h>
@@ -92,7 +91,6 @@
 #if PLATFORM(IOS)
     BOOL isCommitting;
 #endif
-    WebCore::ContentFilterUnblockHandler contentFilterUnblockHandler;
 }
 @end
 
@@ -184,8 +182,6 @@
 
 - (void)_commitData:(NSData *)data;
 
-- (BOOL)_contentFilterDidHandleNavigationAction:(const WebCore::ResourceRequest&)request;
-
 @end
 
 @interface NSObject (WebInternalFrameLoadDelegate)

Modified: trunk/Source/WebKit2/ChangeLog (181790 => 181791)


--- trunk/Source/WebKit2/ChangeLog	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebKit2/ChangeLog	2015-03-20 08:42:59 UTC (rev 181791)
@@ -1,3 +1,26 @@
+2015-03-19  Andy Estes  <[email protected]>
+
+        [Content Filtering] Add tests for unblock requests
+        https://bugs.webkit.org/show_bug.cgi?id=142900
+
+        Reviewed by Andreas Kling.
+
+        * UIProcess/Cocoa/WebPageProxyCocoa.mm:
+        (WebKit::WebPageProxy::contentFilterDidBlockLoadForFrame): Called WebFrameProxy::contentFilterDidBlockLoad.
+        * UIProcess/WebFrameProxy.cpp:
+        (WebKit::WebFrameProxy::didStartProvisionalLoad): Assigned a default-constructed ContentFilterUnblockHandler instead of calling clear().
+        (WebKit::WebFrameProxy::didHandleContentFilterUnblockNavigation): Renamed from contentFilterDidHandleNavigationAction.
+        Updated to use ContentFilterUnblockHandler's new API.
+        (WebKit::WebFrameProxy::contentFilterDidHandleNavigationAction): Deleted.
+        * UIProcess/WebFrameProxy.h:
+        (WebKit::WebFrameProxy::contentFilterDidBlockLoad): Renamed from setContentFilterUnblockHandler.
+        (WebKit::WebFrameProxy::setContentFilterUnblockHandler): Deleted.
+        * UIProcess/WebPageProxy.cpp:
+        (WebKit::WebPageProxy::decidePolicyForNavigationAction): Called WebFrameProxy::didHandleContentFilterUnblockNavigation.
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
+        (WebKit::WebFrameLoaderClient::contentFilterDidBlockLoad): If the unblock handler needs the UI process,
+        send WebPageProxy::ContentFilterDidBlockLoadForFrame. Oterwise, call PolicyChecker::setContentFilterUnblockHandler.
+
 2015-03-20  Zan Dobersek  <[email protected]>
 
         [GTK] Properly guard X11-specific code in BackingStore::createBackend()

Modified: trunk/Source/WebKit2/UIProcess/Cocoa/WebPageProxyCocoa.mm (181790 => 181791)


--- trunk/Source/WebKit2/UIProcess/Cocoa/WebPageProxyCocoa.mm	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebKit2/UIProcess/Cocoa/WebPageProxyCocoa.mm	2015-03-20 08:42:59 UTC (rev 181791)
@@ -75,7 +75,7 @@
 void WebPageProxy::contentFilterDidBlockLoadForFrame(const WebCore::ContentFilterUnblockHandler& unblockHandler, uint64_t frameID)
 {
     if (WebFrameProxy* frame = m_process->webFrame(frameID))
-        frame->setContentFilterUnblockHandler(unblockHandler);
+        frame->contentFilterDidBlockLoad(unblockHandler);
 }
 
 }

Modified: trunk/Source/WebKit2/UIProcess/WebFrameProxy.cpp (181790 => 181791)


--- trunk/Source/WebKit2/UIProcess/WebFrameProxy.cpp	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebKit2/UIProcess/WebFrameProxy.cpp	2015-03-20 08:42:59 UTC (rev 181791)
@@ -128,7 +128,7 @@
 {
     m_frameLoadState.didStartProvisionalLoad(url);
 #if ENABLE(CONTENT_FILTERING)
-    m_contentFilterUnblockHandler.clear();
+    m_contentFilterUnblockHandler = { };
 #endif
 }
 
@@ -234,16 +234,18 @@
 }
 
 #if ENABLE(CONTENT_FILTERING)
-bool WebFrameProxy::contentFilterDidHandleNavigationAction(const WebCore::ResourceRequest& request)
+bool WebFrameProxy::didHandleContentFilterUnblockNavigation(const WebCore::ResourceRequest& request)
 {
-#if PLATFORM(IOS)
-    RefPtr<WebPageProxy> retainedPage = m_page;
-    return m_contentFilterUnblockHandler.handleUnblockRequestAndDispatchIfSuccessful(request, [retainedPage] {
-        retainedPage->reload(false);
+    if (!m_contentFilterUnblockHandler.canHandleRequest(request))
+        return false;
+
+    RefPtr<WebPageProxy> page { m_page };
+    ASSERT(page);
+    m_contentFilterUnblockHandler.requestUnblockAsync([page](bool unblocked) {
+        if (unblocked)
+            page->reload(false);
     });
-#else
-    return false;
-#endif
+    return true;
 }
 #endif
 

Modified: trunk/Source/WebKit2/UIProcess/WebFrameProxy.h (181790 => 181791)


--- trunk/Source/WebKit2/UIProcess/WebFrameProxy.h	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebKit2/UIProcess/WebFrameProxy.h	2015-03-20 08:42:59 UTC (rev 181791)
@@ -123,8 +123,8 @@
     WebFormSubmissionListenerProxy& setUpFormSubmissionListenerProxy(uint64_t listenerID);
 
 #if ENABLE(CONTENT_FILTERING)
-    void setContentFilterUnblockHandler(WebCore::ContentFilterUnblockHandler contentFilterUnblockHandler) { m_contentFilterUnblockHandler = WTF::move(contentFilterUnblockHandler); }
-    bool contentFilterDidHandleNavigationAction(const WebCore::ResourceRequest&);
+    void contentFilterDidBlockLoad(WebCore::ContentFilterUnblockHandler contentFilterUnblockHandler) { m_contentFilterUnblockHandler = WTF::move(contentFilterUnblockHandler); }
+    bool didHandleContentFilterUnblockNavigation(const WebCore::ResourceRequest&);
 #endif
 
 private:

Modified: trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp (181790 => 181791)


--- trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebKit2/UIProcess/WebPageProxy.cpp	2015-03-20 08:42:59 UTC (rev 181791)
@@ -3092,7 +3092,7 @@
     }
 
 #if ENABLE(CONTENT_FILTERING)
-    if (frame->contentFilterDidHandleNavigationAction(request)) {
+    if (frame->didHandleContentFilterUnblockNavigation(request)) {
         receivedPolicyAction = true;
         policyAction = PolicyIgnore;
         return;

Modified: trunk/Source/WebKit2/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp (181790 => 181791)


--- trunk/Source/WebKit2/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp	2015-03-20 08:35:33 UTC (rev 181790)
+++ trunk/Source/WebKit2/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp	2015-03-20 08:42:59 UTC (rev 181791)
@@ -1648,7 +1648,12 @@
 #if ENABLE(CONTENT_FILTERING)
 void WebFrameLoaderClient::contentFilterDidBlockLoad(WebCore::ContentFilterUnblockHandler unblockHandler)
 {
-    if (WebPage* webPage = m_frame->page())
+    if (!unblockHandler.needsUIProcess()) {
+        m_frame->coreFrame()->loader().policyChecker().setContentFilterUnblockHandler(WTF::move(unblockHandler));
+        return;
+    }
+
+    if (WebPage* webPage { m_frame->page() })
         webPage->send(Messages::WebPageProxy::ContentFilterDidBlockLoadForFrame(unblockHandler, m_frame->frameID()));
 }
 #endif
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to