[Content Filtering] Determine navigation and content policy before continuing to...
authoraestes@apple.com <aestes@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 31 Aug 2015 23:27:20 +0000 (23:27 +0000)
committeraestes@apple.com <aestes@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 31 Aug 2015 23:27:20 +0000 (23:27 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148506

Reviewed by Brady Eidson.

Source/WebCore:

Prior to this change, ContentFilter would hide from DocumentLoader all CachedRawResourceClient callbacks until
a decision was made, then replay the missed callbacks. This approach interacted poorly with some features of
the loader, notably appcache and downloads. In the case of appcache, DocumentLoader might not have a chance to
check for substitute data until the original load has finished, wasting bandwidth, and might receive duplicate
or out-of-order callbacks. In the case of downloads, it would often be too late to convert the existing
connection to a download, leading to restarted downloads or outright failures.

Bandaids were put in place for these issues in r188150, r188486, and r188851 to fix crashes or serious
regressions in behavior, but these weren't complete fixes. They did not solve any of the duplicate data loading
problems, and they did not make downloads work reliably in all cases.

This patch rolls out the bandaids (but keeps their tests) and replaces them with a more robust fix. Instead of
hiding callbacks from DocumentLoader, ContentFilter now delivers willSendRequest(), redirectReceived(), and
responseReceived() to DocumentLoader immediately, and cancels filtering if DocumentLoader decides to ignore the
load, download it, or load substitute data. ContentFilter continues to buffer incoming data to prevent partial
rendering of blocked content.

The existing tests for r188150 and r188851 were kept, the test for r188486 was rewritten to be specific to
content filtering, and new tests were added to cover the case where ContentFilter is still undecided after a
load finishes.

Tests: contentfiltering/allow-never.html
       contentfiltering/block-never.html
       ContentFiltering.AllowDownloadAfterAddData
       ContentFiltering.AllowDownloadAfterFinishedAddingData
       ContentFiltering.AllowDownloadAfterRedirect
       ContentFiltering.AllowDownloadAfterResponse
       ContentFiltering.AllowDownloadAfterWillSendRequest
       ContentFiltering.AllowDownloadNever
       ContentFiltering.BlockDownloadAfterAddData
       ContentFiltering.BlockDownloadAfterFinishedAddingData
       ContentFiltering.BlockDownloadAfterRedirect
       ContentFiltering.BlockDownloadAfterResponse
       ContentFiltering.BlockDownloadAfterWillSendRequest
       ContentFiltering.BlockDownloadNever

* bindings/js/JSMockContentFilterSettingsCustom.cpp:
(WebCore::JSMockContentFilterSettings::decisionPoint): Taught to handle DecisionPoint::Never, and rewrote to
not need a set of const uint8_ts that mirror the DecisionPoint enum.
(WebCore::JSMockContentFilterSettings::setDecisionPoint): Ditto.
(WebCore::toJSValue): Rewrote to not need a set of const uint8_ts that mirror the Decision enum.
(WebCore::toDecision): Ditto.
* loader/ContentFilter.cpp:
(WebCore::ContentFilter::createIfEnabled): Renamed from createIfNeeded, and changed to take a DocumentLoader&
instead of a DecisionFunction.
(WebCore::ContentFilter::ContentFilter):
(WebCore::ContentFilter::responseReceived): If m_state != Blocked after filtering, call DocumentLoader::responseReceived().
(WebCore::ContentFilter::dataReceived): If m_state == Allowed after filtering, deliver buffered data to DocumentLoader.
If no filtering was necessary, call DocumentLoader::dataReceived() directly.
(WebCore::ContentFilter::redirectReceived): If m_state != Blocked after filtering, call DocumentLoader::redirectReceived().
(WebCore::ContentFilter::notifyFinished): If an error occured, call DocumentLoader::notifyFinished() immediately and return.
If m_state != Blocked after filtering, deliver buffered data to DocumentLoader and call DocumentLoader::notifyFinished().
If no filtering was necessary and m_state != Blocked, call DocumentLoader::notifyFinished() directly.
(WebCore::ContentFilter::didDecide): Called DocumentLoader::contentFilterDidDecide() instead of m_decisionFunction().
(WebCore::ContentFilter::deliverResourceData): Added a helper function to deliver buffered data to DocumentLoader.
(WebCore::ContentFilter::createIfNeeded): Renamed to createIfEnabled().
* loader/ContentFilter.h:
* loader/DocumentLoader.cpp:
(WebCore::DocumentLoader::DocumentLoader):
(WebCore::DocumentLoader::willSendRequest): Stopped asserting that redirectResponse is null and made it part of
the if condition instead, since willSendRequest() will now be called on redirects when there is an active ContentFilter.
(WebCore::DocumentLoader::startLoadingMainResource): Called becomeMainResourceClient() instead of becomeMainResourceClientIfFilterAllows().
(WebCore::DocumentLoader::becomeMainResourceClient): Renamed from becomeMainResourceClientIfFilterAllows().
Only called ContentFilter::startFilteringMainResource() if the filter state is Initialized, since ContentFilter
might have already made a decision in willSendRequest().
(WebCore::DocumentLoader::contentFilterDidDecide): Stopped deleting m_contentFilter, since it will continue to deliver callbacks
even after making a decision. Fixed a bug where we were creating two copies of ContentFilter's replacement data.
(WebCore::DocumentLoader::syntheticRedirectReceived): Deleted.
(WebCore::DocumentLoader::becomeMainResourceClientIfFilterAllows): Renamed to becomeMainResourceClient().
* loader/DocumentLoader.h:
* loader/EmptyClients.h:
* loader/FrameLoaderClient.h:
* loader/ResourceLoader.cpp:
(WebCore::ResourceLoader::willSendRequestInternal): Removed part of r188851.
* loader/SubresourceLoader.cpp:
(WebCore::SubresourceLoader::didReceiveResponse): Removed part of r188486.
* loader/SubresourceLoader.h:
* loader/cache/CachedRawResource.cpp:
(WebCore::CachedRawResource::didAddClient): Removed part of r188150.
* loader/cache/CachedRawResourceClient.h:
(WebCore::CachedRawResourceClient::syntheticRedirectReceived): Removed part of r188150.
* testing/MockContentFilterSettings.h: Defined DecisionPoint::Never.
* testing/MockContentFilterSettings.idl: Defined DECISION_POINT_NEVER.

Source/WebKit/mac:

Deleted parts of r188486 and r188851.

* WebCoreSupport/WebFrameLoaderClient.h:
* WebCoreSupport/WebFrameLoaderClient.mm:
(WebFrameLoaderClient::convertMainResourceLoadToDownload):
(WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad):

Source/WebKit/win:

Deleted part of r188851.

* WebCoreSupport/WebFrameLoaderClient.cpp:
(WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad):
* WebCoreSupport/WebFrameLoaderClient.h:

Source/WebKit2:

Deleted parts of r188486 and r188851.

* WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
(WebKit::WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad):
* WebProcess/WebCoreSupport/WebFrameLoaderClient.h:
* WebProcess/WebPage/WebFrame.cpp:
(WebKit::WebFrame::convertMainResourceLoadToDownload):

Tools:

Added download API tests covering every decision and decision point.
Removed _WKDownload.AsynchronousDownloadPolicy in favor of these new tests.

* TestWebKitAPI/Configurations/Base.xcconfig: Added $(BUILT_PRODUCTS_DIR)/WebCoreTestSupport to the header search path.
* TestWebKitAPI/Configurations/WebProcessPlugIn.xcconfig: Removed it from here.
* TestWebKitAPI/Tests/WebKit2Cocoa/BundleParametersPlugIn.mm:
(-[BundleParametersPlugIn observeValueForKeyPath:ofObject:change:context:]): Called -valueForKeyPath:, which
returns an id, instead of -valueForKey:, which always returns an NSString even if the object is of another type.
* TestWebKitAPI/Tests/WebKit2Cocoa/ContentFiltering.mm: Added a class that can send a MockContentFilter
decision and decision point as a bundle parameter.
(+[MockContentFilterEnabler supportsSecureCoding]):
(-[MockContentFilterEnabler copyWithZone:]):
(-[MockContentFilterEnabler initWithCoder:]):
(-[MockContentFilterEnabler initWithDecision:decisionPoint:]):
(-[MockContentFilterEnabler encodeWithCoder:]):
(configurationWithContentFilterSettings): Added a helper function to create a WKWebViewConfiguration with MockConentFilter settings.
(TEST): Renamed ContentFiltering.ServerRedirect to ContentFiltering.URLAfterServerRedirect.
(-[BecomeDownloadDelegate webView:decidePolicyForNavigationResponse:decisionHandler:]): Decided _WKNavigationResponsePolicyBecomeDownload.
(-[BecomeDownloadDelegate webView:didFailProvisionalNavigation:withError:]): Set isDone to true.
(-[BecomeDownloadDelegate webView:didFinishNavigation:]): Ditto.
(-[ContentFilteringDownloadDelegate _downloadDidStart:]): Set downloadDidStart to true.
(downloadTest): Added a helper function to test downloads with a given decision and decision point.
* TestWebKitAPI/Tests/WebKit2Cocoa/ContentFilteringPlugIn.mm: Added a corresponding bundle class that decodes
the MockContentFilter decision and decision point, and sets these values in the MockContentFilterSettings
singleton. This class is duplicated in the bundle to prevent TestWebKitAPI from having to link to WebCoreTestSupport.
(+[MockContentFilterEnabler supportsSecureCoding]):
(-[MockContentFilterEnabler copyWithZone:]):
(-[MockContentFilterEnabler initWithCoder:]):
(-[MockContentFilterEnabler dealloc]):
(-[MockContentFilterEnabler encodeWithCoder:]):
(-[ContentFilteringPlugIn webProcessPlugIn:initializeWithObject:]): Start observing changes to the MockContentFilterEnabler key path.
(-[ContentFilteringPlugIn dealloc]): Stop observing.
(-[ContentFilteringPlugIn observeValueForKeyPath:ofObject:change:context:]): Store a MockContentFilterEnabler in _contentFilterEnabler.
(+[ServerRedirectPlugIn initialize]): Deleted.
* TestWebKitAPI/Tests/WebKit2Cocoa/Download.mm:
(-[AsynchronousDownloadNavigationDelegate webView:decidePolicyForNavigationResponse:decisionHandler:]): Deleted.
(-[AsynchronousDownloadDelegate _downloadDidStart:]): Deleted.
(TEST): Deleted.

LayoutTests:

Added tests for what happens if the content filter does not make a decision when the load finishes.

* contentfiltering/allow-never-expected.html: Added.
* contentfiltering/allow-never.html: Added.
* contentfiltering/block-never-expected.html: Added.
* contentfiltering/block-never.html: Added.
* contentfiltering/resources/contentfiltering.js:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@189193 268f45cc-cd09-0410-ab3c-d52691b4dbfc

38 files changed:
LayoutTests/ChangeLog
LayoutTests/contentfiltering/allow-never-expected.html [new file with mode: 0644]
LayoutTests/contentfiltering/allow-never.html [new file with mode: 0644]
LayoutTests/contentfiltering/block-never-expected.html [new file with mode: 0644]
LayoutTests/contentfiltering/block-never.html [new file with mode: 0644]
LayoutTests/contentfiltering/resources/contentfiltering.js
Source/WebCore/ChangeLog
Source/WebCore/bindings/js/JSMockContentFilterSettingsCustom.cpp
Source/WebCore/loader/ContentFilter.cpp
Source/WebCore/loader/ContentFilter.h
Source/WebCore/loader/DocumentLoader.cpp
Source/WebCore/loader/DocumentLoader.h
Source/WebCore/loader/EmptyClients.h
Source/WebCore/loader/FrameLoaderClient.h
Source/WebCore/loader/ResourceLoader.cpp
Source/WebCore/loader/SubresourceLoader.cpp
Source/WebCore/loader/SubresourceLoader.h
Source/WebCore/loader/cache/CachedRawResource.cpp
Source/WebCore/loader/cache/CachedRawResourceClient.h
Source/WebCore/testing/MockContentFilterSettings.h
Source/WebCore/testing/MockContentFilterSettings.idl
Source/WebKit/mac/ChangeLog
Source/WebKit/mac/WebCoreSupport/WebFrameLoaderClient.h
Source/WebKit/mac/WebCoreSupport/WebFrameLoaderClient.mm
Source/WebKit/win/ChangeLog
Source/WebKit/win/WebCoreSupport/WebFrameLoaderClient.cpp
Source/WebKit/win/WebCoreSupport/WebFrameLoaderClient.h
Source/WebKit2/ChangeLog
Source/WebKit2/WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp
Source/WebKit2/WebProcess/WebCoreSupport/WebFrameLoaderClient.h
Source/WebKit2/WebProcess/WebPage/WebFrame.cpp
Tools/ChangeLog
Tools/TestWebKitAPI/Configurations/Base.xcconfig
Tools/TestWebKitAPI/Configurations/WebProcessPlugIn.xcconfig
Tools/TestWebKitAPI/Tests/WebKit2Cocoa/BundleParametersPlugIn.mm
Tools/TestWebKitAPI/Tests/WebKit2Cocoa/ContentFiltering.mm
Tools/TestWebKitAPI/Tests/WebKit2Cocoa/ContentFilteringPlugIn.mm
Tools/TestWebKitAPI/Tests/WebKit2Cocoa/Download.mm

index 6c7f1a7..a4f1022 100644 (file)
@@ -1,3 +1,18 @@
+2015-08-26  Andy Estes  <aestes@apple.com>
+
+        [Content Filtering] Determine navigation and content policy before continuing to filter a load
+        https://bugs.webkit.org/show_bug.cgi?id=148506
+
+        Reviewed by Brady Eidson.
+
+        Added tests for what happens if the content filter does not make a decision when the load finishes.
+
+        * contentfiltering/allow-never-expected.html: Added.
+        * contentfiltering/allow-never.html: Added.
+        * contentfiltering/block-never-expected.html: Added.
+        * contentfiltering/block-never.html: Added.
+        * contentfiltering/resources/contentfiltering.js:
+
 2015-08-31  Zalan Bujtas  <zalan@apple.com>
 
         Repaint cleanup:
diff --git a/LayoutTests/contentfiltering/allow-never-expected.html b/LayoutTests/contentfiltering/allow-never-expected.html
new file mode 100644 (file)
index 0000000..645112f
--- /dev/null
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<iframe src="resources/pass.html"></iframe>
diff --git a/LayoutTests/contentfiltering/allow-never.html b/LayoutTests/contentfiltering/allow-never.html
new file mode 100644 (file)
index 0000000..ae881b7
--- /dev/null
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script src="resources/contentfiltering.js"></script>
+<script>
+if (window.internals) {
+    var settings = window.internals.mockContentFilterSettings;
+    testContentFiltering(/* decisionPoint */settings.DECISION_POINT_NEVER, /* decision */settings.DECISION_ALLOW);
+}
+</script>
diff --git a/LayoutTests/contentfiltering/block-never-expected.html b/LayoutTests/contentfiltering/block-never-expected.html
new file mode 100644 (file)
index 0000000..645112f
--- /dev/null
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<iframe src="resources/pass.html"></iframe>
diff --git a/LayoutTests/contentfiltering/block-never.html b/LayoutTests/contentfiltering/block-never.html
new file mode 100644 (file)
index 0000000..d865e1a
--- /dev/null
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<script src="resources/contentfiltering.js"></script>
+<script>
+if (window.internals) {
+    var settings = window.internals.mockContentFilterSettings;
+    testContentFiltering(/* decisionPoint */settings.DECISION_POINT_NEVER, /* decision */settings.DECISION_BLOCK);
+}
+</script>
index b68339b..9ae4fed 100644 (file)
@@ -5,7 +5,12 @@ function _doTest(decisionPoint, decision, decideAfterUnblockRequest)
     settings.decisionPoint = decisionPoint;
     settings.decision = (decideAfterUnblockRequest ? settings.DECISION_BLOCK : decision);
     
-    var blockedStringText = (decision === settings.DECISION_ALLOW ? "FAIL" : "PASS");
+    var blockedStringText;
+    if (decisionPoint === settings.DECISION_POINT_NEVER || decision === settings.DECISION_ALLOW)
+        blockedStringText = "FAIL";
+    else
+        blockedStringText =  "PASS";
+
     if (decideAfterUnblockRequest) {
         settings.unblockRequestDecision = decision;
         settings.blockedString = "<!DOCTYPE html><script>function unblockRequestDenied() { window.top.postMessage('unblockrequestdenied', '*'); }</script><body>" + blockedStringText;
index 563787e..3f2950e 100644 (file)
@@ -1,3 +1,94 @@
+2015-08-26  Andy Estes  <aestes@apple.com>
+
+        [Content Filtering] Determine navigation and content policy before continuing to filter a load
+        https://bugs.webkit.org/show_bug.cgi?id=148506
+
+        Reviewed by Brady Eidson.
+
+        Prior to this change, ContentFilter would hide from DocumentLoader all CachedRawResourceClient callbacks until
+        a decision was made, then replay the missed callbacks. This approach interacted poorly with some features of
+        the loader, notably appcache and downloads. In the case of appcache, DocumentLoader might not have a chance to
+        check for substitute data until the original load has finished, wasting bandwidth, and might receive duplicate
+        or out-of-order callbacks. In the case of downloads, it would often be too late to convert the existing
+        connection to a download, leading to restarted downloads or outright failures.
+
+        Bandaids were put in place for these issues in r188150, r188486, and r188851 to fix crashes or serious
+        regressions in behavior, but these weren't complete fixes. They did not solve any of the duplicate data loading
+        problems, and they did not make downloads work reliably in all cases.
+
+        This patch rolls out the bandaids (but keeps their tests) and replaces them with a more robust fix. Instead of
+        hiding callbacks from DocumentLoader, ContentFilter now delivers willSendRequest(), redirectReceived(), and
+        responseReceived() to DocumentLoader immediately, and cancels filtering if DocumentLoader decides to ignore the
+        load, download it, or load substitute data. ContentFilter continues to buffer incoming data to prevent partial
+        rendering of blocked content.
+
+        The existing tests for r188150 and r188851 were kept, the test for r188486 was rewritten to be specific to
+        content filtering, and new tests were added to cover the case where ContentFilter is still undecided after a
+        load finishes.
+
+        Tests: contentfiltering/allow-never.html
+               contentfiltering/block-never.html
+               ContentFiltering.AllowDownloadAfterAddData
+               ContentFiltering.AllowDownloadAfterFinishedAddingData
+               ContentFiltering.AllowDownloadAfterRedirect
+               ContentFiltering.AllowDownloadAfterResponse
+               ContentFiltering.AllowDownloadAfterWillSendRequest
+               ContentFiltering.AllowDownloadNever
+               ContentFiltering.BlockDownloadAfterAddData
+               ContentFiltering.BlockDownloadAfterFinishedAddingData
+               ContentFiltering.BlockDownloadAfterRedirect
+               ContentFiltering.BlockDownloadAfterResponse
+               ContentFiltering.BlockDownloadAfterWillSendRequest
+               ContentFiltering.BlockDownloadNever
+
+        * bindings/js/JSMockContentFilterSettingsCustom.cpp:
+        (WebCore::JSMockContentFilterSettings::decisionPoint): Taught to handle DecisionPoint::Never, and rewrote to
+        not need a set of const uint8_ts that mirror the DecisionPoint enum.
+        (WebCore::JSMockContentFilterSettings::setDecisionPoint): Ditto.
+        (WebCore::toJSValue): Rewrote to not need a set of const uint8_ts that mirror the Decision enum.
+        (WebCore::toDecision): Ditto.
+        * loader/ContentFilter.cpp:
+        (WebCore::ContentFilter::createIfEnabled): Renamed from createIfNeeded, and changed to take a DocumentLoader&
+        instead of a DecisionFunction.
+        (WebCore::ContentFilter::ContentFilter):
+        (WebCore::ContentFilter::responseReceived): If m_state != Blocked after filtering, call DocumentLoader::responseReceived().
+        (WebCore::ContentFilter::dataReceived): If m_state == Allowed after filtering, deliver buffered data to DocumentLoader.
+        If no filtering was necessary, call DocumentLoader::dataReceived() directly.
+        (WebCore::ContentFilter::redirectReceived): If m_state != Blocked after filtering, call DocumentLoader::redirectReceived().
+        (WebCore::ContentFilter::notifyFinished): If an error occured, call DocumentLoader::notifyFinished() immediately and return.
+        If m_state != Blocked after filtering, deliver buffered data to DocumentLoader and call DocumentLoader::notifyFinished().
+        If no filtering was necessary and m_state != Blocked, call DocumentLoader::notifyFinished() directly.
+        (WebCore::ContentFilter::didDecide): Called DocumentLoader::contentFilterDidDecide() instead of m_decisionFunction().
+        (WebCore::ContentFilter::deliverResourceData): Added a helper function to deliver buffered data to DocumentLoader.
+        (WebCore::ContentFilter::createIfNeeded): Renamed to createIfEnabled().
+        * loader/ContentFilter.h:
+        * loader/DocumentLoader.cpp:
+        (WebCore::DocumentLoader::DocumentLoader):
+        (WebCore::DocumentLoader::willSendRequest): Stopped asserting that redirectResponse is null and made it part of
+        the if condition instead, since willSendRequest() will now be called on redirects when there is an active ContentFilter.
+        (WebCore::DocumentLoader::startLoadingMainResource): Called becomeMainResourceClient() instead of becomeMainResourceClientIfFilterAllows().
+        (WebCore::DocumentLoader::becomeMainResourceClient): Renamed from becomeMainResourceClientIfFilterAllows().
+        Only called ContentFilter::startFilteringMainResource() if the filter state is Initialized, since ContentFilter
+        might have already made a decision in willSendRequest().
+        (WebCore::DocumentLoader::contentFilterDidDecide): Stopped deleting m_contentFilter, since it will continue to deliver callbacks
+        even after making a decision. Fixed a bug where we were creating two copies of ContentFilter's replacement data.
+        (WebCore::DocumentLoader::syntheticRedirectReceived): Deleted.
+        (WebCore::DocumentLoader::becomeMainResourceClientIfFilterAllows): Renamed to becomeMainResourceClient().
+        * loader/DocumentLoader.h:
+        * loader/EmptyClients.h:
+        * loader/FrameLoaderClient.h:
+        * loader/ResourceLoader.cpp:
+        (WebCore::ResourceLoader::willSendRequestInternal): Removed part of r188851.
+        * loader/SubresourceLoader.cpp:
+        (WebCore::SubresourceLoader::didReceiveResponse): Removed part of r188486.
+        * loader/SubresourceLoader.h:
+        * loader/cache/CachedRawResource.cpp:
+        (WebCore::CachedRawResource::didAddClient): Removed part of r188150.
+        * loader/cache/CachedRawResourceClient.h:
+        (WebCore::CachedRawResourceClient::syntheticRedirectReceived): Removed part of r188150.
+        * testing/MockContentFilterSettings.h: Defined DecisionPoint::Never.
+        * testing/MockContentFilterSettings.idl: Defined DECISION_POINT_NEVER.
+
 2015-08-31  Chris Dumez  <cdumez@apple.com>
 
         Unreviewed, rebaseline bindings tests after r189184.
index 8331875..9bc14eb 100644 (file)
@@ -38,28 +38,17 @@ namespace WebCore {
 using Decision = MockContentFilterSettings::Decision;
 using DecisionPoint = MockContentFilterSettings::DecisionPoint;
 
-// Must be kept in sync with values in MockContentFilterSettings.idl.
-const uint8_t decisionPointAfterWillSendRequest = 0;
-const uint8_t decisionPointAfterRedirect = 1;
-const uint8_t decisionPointAfterResponse = 2;
-const uint8_t decisionPointAfterAddData = 3;
-const uint8_t decisionPointAfterFinishedAddingData = 4;
-const uint8_t decisionAllow = 0;
-const uint8_t decisionBlock = 1;
-
 JSValue JSMockContentFilterSettings::decisionPoint(ExecState*) const
 {
-    switch (impl().decisionPoint()) {
+    DecisionPoint decisionPoint = impl().decisionPoint();
+    switch (decisionPoint) {
     case DecisionPoint::AfterWillSendRequest:
-        return jsNumber(decisionPointAfterWillSendRequest);
     case DecisionPoint::AfterRedirect:
-        return jsNumber(decisionPointAfterRedirect);
     case DecisionPoint::AfterResponse:
-        return jsNumber(decisionPointAfterResponse);
     case DecisionPoint::AfterAddData:
-        return jsNumber(decisionPointAfterAddData);
     case DecisionPoint::AfterFinishedAddingData:
-        return jsNumber(decisionPointAfterFinishedAddingData);
+    case DecisionPoint::Never:
+        return jsNumber(static_cast<uint8_t>(decisionPoint));
     }
 
     ASSERT_NOT_REACHED();
@@ -72,21 +61,15 @@ void JSMockContentFilterSettings::setDecisionPoint(ExecState* exec, JSValue valu
     if (exec->hadException())
         return;
 
-    switch (nativeValue) {
-    case decisionPointAfterWillSendRequest:
-        impl().setDecisionPoint(DecisionPoint::AfterWillSendRequest);
-        return;
-    case decisionPointAfterRedirect:
-        impl().setDecisionPoint(DecisionPoint::AfterRedirect);
-        return;
-    case decisionPointAfterResponse:
-        impl().setDecisionPoint(DecisionPoint::AfterResponse);
-        return;
-    case decisionPointAfterAddData:
-        impl().setDecisionPoint(DecisionPoint::AfterAddData);
-        return;
-    case decisionPointAfterFinishedAddingData:
-        impl().setDecisionPoint(DecisionPoint::AfterFinishedAddingData);
+    DecisionPoint decisionPoint { static_cast<DecisionPoint>(nativeValue) };
+    switch (decisionPoint) {
+    case DecisionPoint::AfterWillSendRequest:
+    case DecisionPoint::AfterRedirect:
+    case DecisionPoint::AfterResponse:
+    case DecisionPoint::AfterAddData:
+    case DecisionPoint::AfterFinishedAddingData:
+    case DecisionPoint::Never:
+        impl().setDecisionPoint(decisionPoint);
         return;
     }
 
@@ -97,9 +80,8 @@ static inline JSValue toJSValue(Decision decision)
 {
     switch (decision) {
     case Decision::Allow:
-        return jsNumber(decisionAllow);
     case Decision::Block:
-        return jsNumber(decisionBlock);
+        return jsNumber(static_cast<uint8_t>(decision));
     }
 
     ASSERT_NOT_REACHED();
@@ -112,11 +94,11 @@ static inline Decision toDecision(ExecState* exec, JSValue value)
     if (exec->hadException())
         return Decision::Allow;
 
-    switch (nativeValue) {
-    case decisionAllow:
-        return Decision::Allow;
-    case decisionBlock:
-        return Decision::Block;
+    Decision decision { static_cast<Decision>(nativeValue) };
+    switch (decision) {
+    case Decision::Allow:
+    case Decision::Block:
+        return decision;
     }
 
     throwTypeError(exec, String::format("%u is not a valid decision value.", nativeValue));
index 32a873e..25d70ee 100644 (file)
@@ -30,6 +30,7 @@
 
 #include "CachedRawResource.h"
 #include "ContentFilterUnblockHandler.h"
+#include "DocumentLoader.h"
 #include "Logging.h"
 #include "NetworkExtensionContentFilter.h"
 #include "ParentalControlsContentFilter.h"
@@ -58,7 +59,7 @@ Vector<ContentFilter::Type>& ContentFilter::types()
     return types;
 }
 
-std::unique_ptr<ContentFilter> ContentFilter::createIfNeeded(DecisionFunction decisionFunction)
+std::unique_ptr<ContentFilter> ContentFilter::createIfEnabled(DocumentLoader& documentLoader)
 {
     Container filters;
     for (auto& type : types()) {
@@ -73,12 +74,12 @@ std::unique_ptr<ContentFilter> ContentFilter::createIfNeeded(DecisionFunction de
     if (filters.isEmpty())
         return nullptr;
 
-    return std::make_unique<ContentFilter>(WTF::move(filters), WTF::move(decisionFunction));
+    return std::make_unique<ContentFilter>(WTF::move(filters), documentLoader);
 }
 
-ContentFilter::ContentFilter(Container contentFilters, DecisionFunction decisionFunction)
+ContentFilter::ContentFilter(Container contentFilters, DocumentLoader& documentLoader)
     : m_contentFilters { WTF::move(contentFilters) }
-    , m_decisionFunction { WTF::move(decisionFunction) }
+    , m_documentLoader { documentLoader }
 {
     LOG(ContentFiltering, "Creating ContentFilter with %zu platform content filter(s).\n", m_contentFilters.size());
     ASSERT(!m_contentFilters.isEmpty());
@@ -147,39 +148,78 @@ String ContentFilter::unblockRequestDeniedScript() const
 
 void ContentFilter::responseReceived(CachedResource* resource, const ResourceResponse& response)
 {
+    ASSERT(resource);
+    ASSERT(resource == m_mainResource);
+    ASSERT(m_state != State::Initialized);
     LOG(ContentFiltering, "ContentFilter received response from <%s>.\n", response.url().string().ascii().data());
-    ASSERT(m_state == State::Filtering);
-    ASSERT_UNUSED(resource, resource == m_mainResource.get());
-    forEachContentFilterUntilBlocked([&response](PlatformContentFilter& contentFilter) {
-        contentFilter.responseReceived(response);
-    });
+
+    if (m_state == State::Filtering) {
+        forEachContentFilterUntilBlocked([&response](PlatformContentFilter& contentFilter) {
+            contentFilter.responseReceived(response);
+        });
+    }
+
+    if (m_state != State::Blocked)
+        m_documentLoader.responseReceived(resource, response);
 }
 
 void ContentFilter::dataReceived(CachedResource* resource, const char* data, int length)
 {
+    ASSERT(resource);
+    ASSERT(resource == m_mainResource);
+    ASSERT(m_state != State::Initialized);
     LOG(ContentFiltering, "ContentFilter received %d bytes of data from <%s>.\n", length, resource->url().string().ascii().data());
-    ASSERT(m_state == State::Filtering);
-    ASSERT_UNUSED(resource, resource == m_mainResource.get());
-    forEachContentFilterUntilBlocked([data, length](PlatformContentFilter& contentFilter) {
-        contentFilter.addData(data, length);
-    });
+
+    if (m_state == State::Filtering) {
+        forEachContentFilterUntilBlocked([data, length](PlatformContentFilter& contentFilter) {
+            contentFilter.addData(data, length);
+        });
+
+        if (m_state == State::Allowed)
+            deliverResourceData(*resource);
+        return;
+    }
+
+    if (m_state == State::Allowed)
+        m_documentLoader.dataReceived(resource, data, length);
 }
 
 void ContentFilter::redirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
 {
-    ASSERT(m_state == State::Filtering);
-    ASSERT_UNUSED(resource, resource == m_mainResource.get());
-    willSendRequest(request, redirectResponse);
+    ASSERT(resource);
+    ASSERT(resource == m_mainResource);
+    ASSERT(m_state != State::Initialized);
+
+    if (m_state == State::Filtering)
+        willSendRequest(request, redirectResponse);
+
+    if (m_state != State::Blocked)
+        m_documentLoader.redirectReceived(resource, request, redirectResponse);
 }
 
 void ContentFilter::notifyFinished(CachedResource* resource)
 {
+    ASSERT(resource);
+    ASSERT(resource == m_mainResource);
+    ASSERT(m_state != State::Initialized);
     LOG(ContentFiltering, "ContentFilter will finish filtering main resource at <%s>.\n", resource->url().string().ascii().data());
-    ASSERT(m_state == State::Filtering);
-    ASSERT_UNUSED(resource, resource == m_mainResource.get());
-    forEachContentFilterUntilBlocked([](PlatformContentFilter& contentFilter) {
-        contentFilter.finishedAddingData();
-    });
+
+    if (resource->errorOccurred()) {
+        m_documentLoader.notifyFinished(resource);
+        return;
+    }
+
+    if (m_state == State::Filtering) {
+        forEachContentFilterUntilBlocked([](PlatformContentFilter& contentFilter) {
+            contentFilter.finishedAddingData();
+        });
+
+        if (m_state != State::Blocked)
+            deliverResourceData(*resource);
+    }
+
+    if (m_state != State::Blocked)
+        m_documentLoader.notifyFinished(resource);
 }
 
 void ContentFilter::forEachContentFilterUntilBlocked(std::function<void(PlatformContentFilter&)> function)
@@ -213,10 +253,14 @@ void ContentFilter::didDecide(State state)
     ASSERT(state == State::Allowed || state == State::Blocked);
     LOG(ContentFiltering, "ContentFilter decided load should be %s for main resource at <%s>.\n", state == State::Allowed ? "allowed" : "blocked", m_mainResource ? m_mainResource->url().string().ascii().data() : "");
     m_state = state;
+    m_documentLoader.contentFilterDidDecide();
+}
 
-    // Calling m_decisionFunction might delete |this|.
-    if (m_decisionFunction)
-        m_decisionFunction();
+void ContentFilter::deliverResourceData(CachedResource& resource)
+{
+    ASSERT(resource.dataBufferingPolicy() == BufferData);
+    const SharedBuffer& resourceBuffer = *resource.resourceBuffer();
+    m_documentLoader.dataReceived(&resource, resourceBuffer.data(), resourceBuffer.size());
 }
 
 } // namespace WebCore
index b4e9dd9..a207ced 100644 (file)
@@ -37,6 +37,7 @@ namespace WebCore {
 
 class CachedRawResource;
 class ContentFilterUnblockHandler;
+class DocumentLoader;
 class PlatformContentFilter;
 class SharedBuffer;
 
@@ -47,8 +48,7 @@ class ContentFilter final : private CachedRawResourceClient {
 public:
     template <typename T> static void addType() { types().append(type<T>()); }
 
-    using DecisionFunction = std::function<void()>;
-    static std::unique_ptr<ContentFilter> createIfNeeded(DecisionFunction);
+    static std::unique_ptr<ContentFilter> createIfEnabled(DocumentLoader&);
     ~ContentFilter() override;
 
     static const char* urlScheme() { return "x-apple-content-filter"; }
@@ -76,8 +76,8 @@ private:
     WEBCORE_EXPORT static Vector<Type>& types();
 
     using Container = Vector<std::unique_ptr<PlatformContentFilter>>;
-    friend std::unique_ptr<ContentFilter> std::make_unique<ContentFilter>(Container&&, DecisionFunction&&);
-    ContentFilter(Container, DecisionFunction);
+    friend std::unique_ptr<ContentFilter> std::make_unique<ContentFilter>(Container&&, DocumentLoader&);
+    ContentFilter(Container, DocumentLoader&);
 
     // CachedRawResourceClient
     void responseReceived(CachedResource*, const ResourceResponse&) override;
@@ -89,9 +89,10 @@ private:
 
     void forEachContentFilterUntilBlocked(std::function<void(PlatformContentFilter&)>);
     void didDecide(State);
+    void deliverResourceData(CachedResource&);
 
     const Container m_contentFilters;
-    const DecisionFunction m_decisionFunction;
+    DocumentLoader& m_documentLoader;
     CachedResourceHandle<CachedRawResource> m_mainResource;
     PlatformContentFilter* m_blockingContentFilter { nullptr };
     State m_state { State::Initialized };
index 8ca97ba..df6e90a 100644 (file)
@@ -142,7 +142,7 @@ DocumentLoader::DocumentLoader(const ResourceRequest& req, const SubstituteData&
     , m_subresourceLoadersArePageCacheAcceptable(false)
     , m_applicationCacheHost(std::make_unique<ApplicationCacheHost>(*this))
 #if ENABLE(CONTENT_FILTERING)
-    , m_contentFilter(!substituteData.isValid() ? ContentFilter::createIfNeeded(std::bind(&DocumentLoader::contentFilterDidDecide, this)) : nullptr)
+    , m_contentFilter(!substituteData.isValid() ? ContentFilter::createIfEnabled(*this) : nullptr)
 #endif
 {
 }
@@ -489,14 +489,6 @@ void DocumentLoader::redirectReceived(CachedResource* resource, ResourceRequest&
     willSendRequest(request, redirectResponse);
 }
 
-void DocumentLoader::syntheticRedirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse, bool& shouldContinue)
-{
-    redirectReceived(resource, request, redirectResponse);
-
-    // If we will soon remove our reference to the CachedRawResource in favor of a SubstituteData load, we don't want to continue receiving synthetic CachedRawResource callbacks.
-    shouldContinue = !m_substituteData.isValid();
-}
-
 void DocumentLoader::willSendRequest(ResourceRequest& newRequest, const ResourceResponse& redirectResponse)
 {
     // Note that there are no asserts here as there are for the other callbacks. This is due to the
@@ -544,8 +536,7 @@ void DocumentLoader::willSendRequest(ResourceRequest& newRequest, const Resource
     }
 
 #if ENABLE(CONTENT_FILTERING)
-    if (m_contentFilter) {
-        ASSERT(redirectResponse.isNull());
+    if (m_contentFilter && redirectResponse.isNull()) {
         m_contentFilter->willSendRequest(newRequest, redirectResponse);
         if (newRequest.isNull())
             return;
@@ -1462,11 +1453,7 @@ void DocumentLoader::startLoadingMainResource()
         frameLoader()->notifier().dispatchWillSendRequest(this, m_identifierForLoadWithoutResourceLoader, request, ResourceResponse());
     }
 
-#if ENABLE(CONTENT_FILTERING)
-    becomeMainResourceClientIfFilterAllows();
-#else
-    m_mainResource->addClient(this);
-#endif
+    becomeMainResourceClient();
 
     // A bunch of headers are set when the underlying ResourceLoader is created, and m_request needs to include those.
     if (mainResourceLoader())
@@ -1604,6 +1591,18 @@ ShouldOpenExternalURLsPolicy DocumentLoader::shouldOpenExternalURLsPolicyToPropa
     return m_shouldOpenExternalURLsPolicy;
 }
 
+void DocumentLoader::becomeMainResourceClient()
+{
+#if ENABLE(CONTENT_FILTERING)
+    if (m_contentFilter && m_contentFilter->state() == ContentFilter::State::Initialized) {
+        // ContentFilter will synthesize CachedRawResourceClient callbacks.
+        m_contentFilter->startFilteringMainResource(*m_mainResource);
+        return;
+    }
+#endif
+    m_mainResource->addClient(this);
+}
+
 #if ENABLE(CONTENT_EXTENSIONS)
 void DocumentLoader::addPendingContentExtensionSheet(const String& identifier, StyleSheetContents& sheet)
 {
@@ -1620,16 +1619,6 @@ void DocumentLoader::addPendingContentExtensionDisplayNoneSelector(const String&
 #endif
 
 #if ENABLE(CONTENT_FILTERING)
-void DocumentLoader::becomeMainResourceClientIfFilterAllows()
-{
-    ASSERT(m_mainResource);
-    if (m_contentFilter) {
-        ASSERT(m_contentFilter->state() == ContentFilter::State::Initialized);
-        m_contentFilter->startFilteringMainResource(*m_mainResource);
-    } else
-        m_mainResource->addClient(this);
-}
-
 void DocumentLoader::installContentFilterUnblockHandler(ContentFilter& contentFilter)
 {
     ContentFilterUnblockHandler unblockHandler { contentFilter.unblockHandler() };
@@ -1652,21 +1641,17 @@ void DocumentLoader::contentFilterDidDecide()
     using State = ContentFilter::State;
     ASSERT(m_contentFilter);
     ASSERT(m_contentFilter->state() == State::Blocked || m_contentFilter->state() == State::Allowed);
-    std::unique_ptr<ContentFilter> contentFilter;
-    std::swap(contentFilter, m_contentFilter);
-    if (contentFilter->state() == State::Allowed) {
-        if (m_mainResource)
-            m_mainResource->addClient(this);
+    if (m_contentFilter->state() == State::Allowed)
         return;
-    }
 
-    installContentFilterUnblockHandler(*contentFilter);
+    installContentFilterUnblockHandler(*m_contentFilter);
 
     URL blockedURL;
     blockedURL.setProtocol(ContentFilter::urlScheme());
     blockedURL.setHost(ASCIILiteral("blocked-page"));
-    ResourceResponse response(URL(), ASCIILiteral("text/html"), contentFilter->replacementData()->size(), ASCIILiteral("UTF-8"));
-    SubstituteData substituteData { contentFilter->replacementData(), documentURL(), response, SubstituteData::SessionHistoryVisibility::Hidden };
+    auto replacementData = m_contentFilter->replacementData();
+    ResourceResponse response(URL(), ASCIILiteral("text/html"), replacementData->size(), ASCIILiteral("UTF-8"));
+    SubstituteData substituteData { adoptRef(&replacementData.leakRef()), documentURL(), response, SubstituteData::SessionHistoryVisibility::Hidden };
     frame()->navigationScheduler().scheduleSubstituteDataLoad(blockedURL, substituteData);
 }
 #endif
index f5e97da..bd6f358 100644 (file)
@@ -307,7 +307,6 @@ namespace WebCore {
         void finishedLoading(double finishTime);
         void mainReceivedError(const ResourceError&);
         WEBCORE_EXPORT virtual void redirectReceived(CachedResource*, ResourceRequest&, const ResourceResponse&) override;
-        WEBCORE_EXPORT virtual void syntheticRedirectReceived(CachedResource*, ResourceRequest&, const ResourceResponse&, bool& /* shouldContinue */) override;
         WEBCORE_EXPORT virtual void responseReceived(CachedResource*, const ResourceResponse&) override;
         WEBCORE_EXPORT virtual void dataReceived(CachedResource*, const char* data, int length) override;
         WEBCORE_EXPORT virtual void notifyFinished(CachedResource*) override;
@@ -339,9 +338,10 @@ namespace WebCore {
         void clearMainResource();
 
         void cancelPolicyCheckIfNeeded();
+        void becomeMainResourceClient();
 
 #if ENABLE(CONTENT_FILTERING)
-        void becomeMainResourceClientIfFilterAllows();
+        friend class ContentFilter;
         void installContentFilterUnblockHandler(ContentFilter&);
         void contentFilterDidDecide();
 #endif
index 6e5ee74..8c57d6e 100644 (file)
@@ -272,7 +272,7 @@ public:
     virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int) override { return false; }
 
     virtual void dispatchDidHandleOnloadEvents() override { }
-    virtual void dispatchDidReceiveServerRedirectForProvisionalLoad(const URL&) override { }
+    virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() override { }
     virtual void dispatchDidCancelClientRedirect() override { }
     virtual void dispatchWillPerformClientRedirect(const URL&, double, double) override { }
     virtual void dispatchDidChangeLocationWithinPage() override { }
index afd1b10..e04bbf5 100644 (file)
@@ -154,7 +154,7 @@ namespace WebCore {
         virtual bool dispatchDidLoadResourceFromMemoryCache(DocumentLoader*, const ResourceRequest&, const ResourceResponse&, int length) = 0;
 
         virtual void dispatchDidHandleOnloadEvents() = 0;
-        virtual void dispatchDidReceiveServerRedirectForProvisionalLoad(const URL&) = 0;
+        virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() = 0;
         virtual void dispatchDidChangeProvisionalURL() { }
         virtual void dispatchDidCancelClientRedirect() = 0;
         virtual void dispatchWillPerformClientRedirect(const URL&, double interval, double fireDate) = 0;
index b49074b..482fe90 100644 (file)
@@ -372,7 +372,7 @@ void ResourceLoader::willSendRequestInternal(ResourceRequest& request, const Res
     if (isRedirect) {
         auto& redirectURL = request.url();
         if (!m_documentLoader->isCommitted())
-            frameLoader()->client().dispatchDidReceiveServerRedirectForProvisionalLoad(redirectURL);
+            frameLoader()->client().dispatchDidReceiveServerRedirectForProvisionalLoad();
 
         if (redirectURL.protocolIsData()) {
             // Handle data URL decoding locally.
index 077ad1d..75679c6 100644 (file)
@@ -207,8 +207,6 @@ void SubresourceLoader::didReceiveResponse(const ResourceResponse& response)
     // anything including removing the last reference to this object; one example of this is 3266216.
     Ref<SubresourceLoader> protect(*this);
 
-    TemporaryChange<bool> callingDidReceiveResponse(m_callingDidReceiveResponse, true);
-
     if (shouldIncludeCertificateInfo())
         response.includeCertificateInfo();
 
index dda692f..69a2422 100644 (file)
@@ -58,8 +58,6 @@ public:
     virtual const ResourceRequest& iOSOriginalRequest() const override { return m_iOSOriginalRequest; }
 #endif
 
-    bool callingDidReceiveResponse() const { return m_callingDidReceiveResponse; }
-
 private:
     SubresourceLoader(Frame*, CachedResource*, const ResourceLoaderOptions&);
 
@@ -126,7 +124,6 @@ private:
     bool m_loadingMultipartContent;
     SubresourceLoaderState m_state;
     std::unique_ptr<RequestCountTracker> m_requestCountTracker;
-    bool m_callingDidReceiveResponse { false };
 };
 
 }
index c78c3d4..1ebc193 100644 (file)
@@ -132,9 +132,8 @@ void CachedRawResource::didAddClient(CachedResourceClient* c)
     for (size_t i = 0; i < redirectCount; i++) {
         RedirectPair redirect = m_redirectChain[i];
         ResourceRequest request(redirect.m_request);
-        bool shouldContinue = true;
-        client->syntheticRedirectReceived(this, request, redirect.m_redirectResponse, shouldContinue);
-        if (!hasClient(c) || !shouldContinue)
+        client->redirectReceived(this, request, redirect.m_redirectResponse);
+        if (!hasClient(c))
             return;
     }
     ASSERT(redirectCount == m_redirectChain.size());
index 3959f13..3d7fd5e 100644 (file)
@@ -41,10 +41,6 @@ public:
     virtual void responseReceived(CachedResource*, const ResourceResponse&) { }
     virtual void dataReceived(CachedResource*, const char* /* data */, int /* length */) { }
     virtual void redirectReceived(CachedResource*, ResourceRequest&, const ResourceResponse&) { }
-
-    // In response to a redirect, some clients wish to receive no futher callbacks, but cannot immediately remove themselves as a client.
-    // Those clients can express their desire to recieve no futher callbacks by setting shouldContinue to false.
-    virtual void syntheticRedirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& response, bool& /* shouldContinue */) { redirectReceived(resource, request, response); }
 #if USE(SOUP)
     virtual char* getOrCreateReadBuffer(CachedResource*, size_t /* requestedSize */, size_t& /* actualSize */) { return 0; }
 #endif
index d162689..691fe2f 100644 (file)
@@ -40,7 +40,8 @@ public:
         AfterRedirect,
         AfterResponse,
         AfterAddData,
-        AfterFinishedAddingData
+        AfterFinishedAddingData,
+        Never
     };
 
     enum class Decision {
index 5f00057..d401ae9 100644 (file)
@@ -37,6 +37,7 @@
     const octet DECISION_POINT_AFTER_RESPONSE = 2;
     const octet DECISION_POINT_AFTER_ADD_DATA = 3;
     const octet DECISION_POINT_AFTER_FINISHED_ADDING_DATA = 4;
+    const octet DECISION_POINT_NEVER = 5;
     [Custom] attribute octet decisionPoint;
 
     const octet DECISION_ALLOW = 0;
index 10feceb..04694a0 100644 (file)
@@ -1,3 +1,17 @@
+2015-08-26  Andy Estes  <aestes@apple.com>
+
+        [Content Filtering] Determine navigation and content policy before continuing to filter a load
+        https://bugs.webkit.org/show_bug.cgi?id=148506
+
+        Reviewed by Brady Eidson.
+
+        Deleted parts of r188486 and r188851.
+
+        * WebCoreSupport/WebFrameLoaderClient.h:
+        * WebCoreSupport/WebFrameLoaderClient.mm:
+        (WebFrameLoaderClient::convertMainResourceLoadToDownload):
+        (WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad):
+
 2015-08-31  Chris Dumez  <cdumez@apple.com>
 
         Range.detach() / NodeIterator.detach() should be no-ops as per the latest DOM specification
index 4a955cb..cd1f24b 100644 (file)
@@ -96,7 +96,7 @@ private:
     virtual NSCachedURLResponse* willCacheResponse(WebCore::DocumentLoader*, unsigned long identifier, NSCachedURLResponse*) const override;
 
     virtual void dispatchDidHandleOnloadEvents() override;
-    virtual void dispatchDidReceiveServerRedirectForProvisionalLoad(const WebCore::URL&) override;
+    virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() override;
     virtual void dispatchDidCancelClientRedirect() override;
     virtual void dispatchWillPerformClientRedirect(const WebCore::URL&, double interval, double fireDate) override;
     virtual void dispatchDidChangeLocationWithinPage() override;
index 222af16..fa6b90d 100644 (file)
@@ -288,7 +288,7 @@ void WebFrameLoaderClient::convertMainResourceLoadToDownload(DocumentLoader* doc
     WebView *webView = getWebView(m_webFrame.get());
     SubresourceLoader* mainResourceLoader = documentLoader->mainResourceLoader();
 
-    if (!mainResourceLoader || !mainResourceLoader->callingDidReceiveResponse()) {
+    if (!mainResourceLoader) {
         // The resource has already been cached, or the conversion is being attmpted when not calling SubresourceLoader::didReceiveResponse().
 #pragma clang diagnostic push
 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
@@ -578,9 +578,9 @@ void WebFrameLoaderClient::dispatchDidHandleOnloadEvents()
         CallFrameLoadDelegate(implementations->didHandleOnloadEventsForFrameFunc, webView, @selector(webView:didHandleOnloadEventsForFrame:), m_webFrame.get());
 }
 
-void WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad(const URL& url)
+void WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad()
 {
-    m_webFrame->_private->provisionalURL = url.string();
+    m_webFrame->_private->provisionalURL = core(m_webFrame.get())->loader().provisionalDocumentLoader()->url().string();
 
     WebView *webView = getWebView(m_webFrame.get());
     WebFrameLoadDelegateImplementationCache* implementations = WebViewGetFrameLoadDelegateImplementations(webView);
index ebeaa58..b8b136f 100644 (file)
@@ -1,3 +1,16 @@
+2015-08-27  Andy Estes  <aestes@apple.com>
+
+        [Content Filtering] Determine navigation and content policy before continuing to filter a load
+        https://bugs.webkit.org/show_bug.cgi?id=148506
+
+        Reviewed by Brady Eidson.
+
+        Deleted part of r188851.
+
+        * WebCoreSupport/WebFrameLoaderClient.cpp:
+        (WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad):
+        * WebCoreSupport/WebFrameLoaderClient.h:
+
 2015-08-31  Chris Dumez  <cdumez@apple.com>
 
         Range.detach() / NodeIterator.detach() should be no-ops as per the latest DOM specification
index 565b738..24ef19c 100644 (file)
@@ -330,7 +330,7 @@ void WebFrameLoaderClient::dispatchDidHandleOnloadEvents()
         frameLoadDelegatePriv->didHandleOnloadEventsForFrame(webView, m_webFrame);
 }
 
-void WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad(const URL&)
+void WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad()
 {
     WebView* webView = m_webFrame->webView();
     COMPtr<IWebFrameLoadDelegate> frameLoadDelegate;
index 9af931e..5262671 100644 (file)
@@ -77,7 +77,7 @@ public:
     virtual bool shouldCacheResponse(WebCore::DocumentLoader*, unsigned long identifier, const WebCore::ResourceResponse&, const unsigned char* data, unsigned long long length);
 
     virtual void dispatchDidHandleOnloadEvents() override;
-    virtual void dispatchDidReceiveServerRedirectForProvisionalLoad(const WebCore::URL&) override;
+    virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() override;
     virtual void dispatchDidCancelClientRedirect() override;
     virtual void dispatchWillPerformClientRedirect(const WebCore::URL&, double interval, double fireDate) override;
     virtual void dispatchDidChangeLocationWithinPage() override;
index 98fe4e3..ae5206c 100644 (file)
@@ -1,3 +1,18 @@
+2015-08-26  Andy Estes  <aestes@apple.com>
+
+        [Content Filtering] Determine navigation and content policy before continuing to filter a load
+        https://bugs.webkit.org/show_bug.cgi?id=148506
+
+        Reviewed by Brady Eidson.
+
+        Deleted parts of r188486 and r188851.
+
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.cpp:
+        (WebKit::WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad):
+        * WebProcess/WebCoreSupport/WebFrameLoaderClient.h:
+        * WebProcess/WebPage/WebFrame.cpp:
+        (WebKit::WebFrame::convertMainResourceLoadToDownload):
+
 2015-08-31  Chris Dumez  <cdumez@apple.com>
 
         Range.detach() / NodeIterator.detach() should be no-ops as per the latest DOM specification
index 08375ba..f906396 100644 (file)
@@ -278,20 +278,21 @@ void WebFrameLoaderClient::dispatchDidHandleOnloadEvents()
     webPage->injectedBundleLoaderClient().didHandleOnloadEventsForFrame(webPage, m_frame);
 }
 
-void WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad(const URL& url)
+void WebFrameLoaderClient::dispatchDidReceiveServerRedirectForProvisionalLoad()
 {
     WebPage* webPage = m_frame->page();
     if (!webPage)
         return;
 
     WebDocumentLoader& documentLoader = static_cast<WebDocumentLoader&>(*m_frame->coreFrame()->loader().provisionalDocumentLoader());
+    const String& url = documentLoader.url().string();
     RefPtr<API::Object> userData;
 
     // Notify the bundle client.
     webPage->injectedBundleLoaderClient().didReceiveServerRedirectForProvisionalLoadForFrame(webPage, m_frame, userData);
 
     // Notify the UIProcess.
-    webPage->send(Messages::WebPageProxy::DidReceiveServerRedirectForProvisionalLoadForFrame(m_frame->frameID(), documentLoader.navigationID(), url.string(), UserData(WebProcess::singleton().transformObjectsToHandles(userData.get()).get())));
+    webPage->send(Messages::WebPageProxy::DidReceiveServerRedirectForProvisionalLoadForFrame(m_frame->frameID(), documentLoader.navigationID(), url, UserData(WebProcess::singleton().transformObjectsToHandles(userData.get()).get())));
 }
 
 void WebFrameLoaderClient::dispatchDidChangeProvisionalURL()
index d111dfa..e8869a9 100644 (file)
@@ -79,7 +79,7 @@ private:
     virtual bool dispatchDidLoadResourceFromMemoryCache(WebCore::DocumentLoader*, const WebCore::ResourceRequest&, const WebCore::ResourceResponse&, int length) override;
     
     virtual void dispatchDidHandleOnloadEvents() override;
-    virtual void dispatchDidReceiveServerRedirectForProvisionalLoad(const WebCore::URL&) override;
+    virtual void dispatchDidReceiveServerRedirectForProvisionalLoad() override;
     virtual void dispatchDidChangeProvisionalURL() override;
     virtual void dispatchDidCancelClientRedirect() override;
     virtual void dispatchWillPerformClientRedirect(const WebCore::URL&, double interval, double fireDate) override;
index 5b33153..cccb8b6 100644 (file)
@@ -283,7 +283,7 @@ void WebFrame::convertMainResourceLoadToDownload(DocumentLoader* documentLoader,
         // This can happen if there is no loader because the main resource is in the WebCore memory cache,
         // or because the conversion was attempted when not calling SubresourceLoader::didReceiveResponse().
         uint64_t mainResourceLoadIdentifier;
-        if (mainResourceLoader && mainResourceLoader->callingDidReceiveResponse())
+        if (mainResourceLoader)
             mainResourceLoadIdentifier = mainResourceLoader->identifier();
         else
             mainResourceLoadIdentifier = 0;
@@ -293,7 +293,7 @@ void WebFrame::convertMainResourceLoadToDownload(DocumentLoader* documentLoader,
     }
 #endif
 
-    if (!mainResourceLoader || !mainResourceLoader->callingDidReceiveResponse()) {
+    if (!mainResourceLoader) {
         // The main resource has already been loaded. Start a new download instead.
         webProcess.downloadManager().startDownload(policyDownloadID, request);
         return;
index fe03c70..39163fa 100644 (file)
@@ -1,3 +1,49 @@
+2015-08-26  Andy Estes  <aestes@apple.com>
+
+        [Content Filtering] Determine navigation and content policy before continuing to filter a load
+        https://bugs.webkit.org/show_bug.cgi?id=148506
+
+        Reviewed by Brady Eidson.
+
+        Added download API tests covering every decision and decision point.
+        Removed _WKDownload.AsynchronousDownloadPolicy in favor of these new tests.
+
+        * TestWebKitAPI/Configurations/Base.xcconfig: Added $(BUILT_PRODUCTS_DIR)/WebCoreTestSupport to the header search path.
+        * TestWebKitAPI/Configurations/WebProcessPlugIn.xcconfig: Removed it from here.
+        * TestWebKitAPI/Tests/WebKit2Cocoa/BundleParametersPlugIn.mm:
+        (-[BundleParametersPlugIn observeValueForKeyPath:ofObject:change:context:]): Called -valueForKeyPath:, which
+        returns an id, instead of -valueForKey:, which always returns an NSString even if the object is of another type.
+        * TestWebKitAPI/Tests/WebKit2Cocoa/ContentFiltering.mm: Added a class that can send a MockContentFilter
+        decision and decision point as a bundle parameter.
+        (+[MockContentFilterEnabler supportsSecureCoding]):
+        (-[MockContentFilterEnabler copyWithZone:]):
+        (-[MockContentFilterEnabler initWithCoder:]):
+        (-[MockContentFilterEnabler initWithDecision:decisionPoint:]):
+        (-[MockContentFilterEnabler encodeWithCoder:]):
+        (configurationWithContentFilterSettings): Added a helper function to create a WKWebViewConfiguration with MockConentFilter settings.
+        (TEST): Renamed ContentFiltering.ServerRedirect to ContentFiltering.URLAfterServerRedirect.
+        (-[BecomeDownloadDelegate webView:decidePolicyForNavigationResponse:decisionHandler:]): Decided _WKNavigationResponsePolicyBecomeDownload.
+        (-[BecomeDownloadDelegate webView:didFailProvisionalNavigation:withError:]): Set isDone to true.
+        (-[BecomeDownloadDelegate webView:didFinishNavigation:]): Ditto.
+        (-[ContentFilteringDownloadDelegate _downloadDidStart:]): Set downloadDidStart to true.
+        (downloadTest): Added a helper function to test downloads with a given decision and decision point.
+        * TestWebKitAPI/Tests/WebKit2Cocoa/ContentFilteringPlugIn.mm: Added a corresponding bundle class that decodes
+        the MockContentFilter decision and decision point, and sets these values in the MockContentFilterSettings
+        singleton. This class is duplicated in the bundle to prevent TestWebKitAPI from having to link to WebCoreTestSupport.
+        (+[MockContentFilterEnabler supportsSecureCoding]):
+        (-[MockContentFilterEnabler copyWithZone:]):
+        (-[MockContentFilterEnabler initWithCoder:]):
+        (-[MockContentFilterEnabler dealloc]):
+        (-[MockContentFilterEnabler encodeWithCoder:]):
+        (-[ContentFilteringPlugIn webProcessPlugIn:initializeWithObject:]): Start observing changes to the MockContentFilterEnabler key path.
+        (-[ContentFilteringPlugIn dealloc]): Stop observing.
+        (-[ContentFilteringPlugIn observeValueForKeyPath:ofObject:change:context:]): Store a MockContentFilterEnabler in _contentFilterEnabler.
+        (+[ServerRedirectPlugIn initialize]): Deleted.
+        * TestWebKitAPI/Tests/WebKit2Cocoa/Download.mm:
+        (-[AsynchronousDownloadNavigationDelegate webView:decidePolicyForNavigationResponse:decisionHandler:]): Deleted.
+        (-[AsynchronousDownloadDelegate _downloadDidStart:]): Deleted.
+        (TEST): Deleted.
+
 2015-08-31  Filip Pizlo  <fpizlo@apple.com>
 
         Skip slow lock tests on Windows/debug
index fa9e3c5..b10a980 100644 (file)
@@ -24,7 +24,7 @@
 CLANG_CXX_LANGUAGE_STANDARD = gnu++0x;
 CLANG_CXX_LIBRARY = libc++;
 CLANG_WARN_CXX0X_EXTENSIONS = NO;
-HEADER_SEARCH_PATHS = ${BUILT_PRODUCTS_DIR}/usr/local/include $(WEBCORE_PRIVATE_HEADERS_DIR)/ForwardingHeaders $(WEBCORE_PRIVATE_HEADERS_DIR)/icu;
+HEADER_SEARCH_PATHS = ${BUILT_PRODUCTS_DIR}/usr/local/include $(WEBCORE_PRIVATE_HEADERS_DIR)/ForwardingHeaders $(WEBCORE_PRIVATE_HEADERS_DIR)/icu $(BUILT_PRODUCTS_DIR)/WebCoreTestSupport;
 FRAMEWORK_SEARCH_PATHS = $(FRAMEWORK_SEARCH_PATHS_$(PLATFORM_NAME));
 FRAMEWORK_SEARCH_PATHS_macosx = $(SYSTEM_LIBRARY_DIR)/Frameworks/Quartz.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/ApplicationServices.framework/Frameworks $(SYSTEM_LIBRARY_DIR)/Frameworks/CoreServices.framework/Frameworks;
 
index c888a06..2985516 100644 (file)
@@ -27,7 +27,6 @@ INFOPLIST_FILE = cocoa/WebProcessPlugIn/Info.plist;
 PRODUCT_NAME = TestWebKitAPI;
 EXECUTABLE_SUFFIX = .bundle;
 WRAPPER_EXTENSION = wkbundle;
-HEADER_SEARCH_PATHS = $(inherited) $(BUILT_PRODUCTS_DIR)/WebCoreTestSupport;
 OTHER_LDFLAGS = $(inherited) -framework JavaScriptCore -framework WebKit -lWebCoreTestSupport;
 LD_RUNPATH_SEARCH_PATHS[sdk=macosx*] = "@loader_path/../../..";
 LD_RUNPATH_SEARCH_PATHS[sdk=iphone*] = "@loader_path/..";
index 2b51a05..fcee7b5 100644 (file)
@@ -64,7 +64,7 @@ static NSString * const testParameter = @"TestParameter";
 - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
 {
     JSContext *jsContext = [[_browserContextController mainFrame] jsContextForWorld:[WKWebProcessPlugInScriptWorld normalWorld]];
-    [jsContext setObject:[object valueForKey:keyPath] forKeyedSubscript:keyPath];
+    [jsContext setObject:[object valueForKeyPath:keyPath] forKeyedSubscript:keyPath];
 }
 
 @end
index e661230..67811db 100644 (file)
 
 #if WK_API_ENABLED && PLATFORM(MAC)
 
+#import "MockContentFilterSettings.h"
 #import "PlatformUtilities.h"
 #import "TestProtocol.h"
 #import "WKWebViewConfigurationExtras.h"
 #import <WebKit/WKBrowsingContextController.h>
-#import <WebKit/WKNavigationDelegate.h>
+#import <WebKit/WKNavigationDelegatePrivate.h>
+#import <WebKit/WKProcessPoolPrivate.h>
 #import <WebKit/WKWebView.h>
+#import <WebKit/_WKDownloadDelegate.h>
 #import <wtf/RetainPtr.h>
 
+using Decision = WebCore::MockContentFilterSettings::Decision;
+using DecisionPoint = WebCore::MockContentFilterSettings::DecisionPoint;
+
 static bool isDone;
 
+@interface MockContentFilterEnabler : NSObject <NSCopying, NSSecureCoding>
+- (instancetype)initWithDecision:(Decision)decision decisionPoint:(DecisionPoint)decisionPoint;
+@end
+
+@implementation MockContentFilterEnabler {
+    Decision _decision;
+    DecisionPoint _decisionPoint;
+}
+
++ (BOOL)supportsSecureCoding
+{
+    return YES;
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+    return [self retain];
+}
+
+- (instancetype)initWithCoder:(NSCoder *)decoder
+{
+    return [super init];
+}
+
+- (instancetype)initWithDecision:(Decision)decision decisionPoint:(DecisionPoint)decisionPoint
+{
+    if (!(self = [super init]))
+        return nil;
+
+    _decision = decision;
+    _decisionPoint = decisionPoint;
+    return self;
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder
+{
+    [coder encodeInt:static_cast<int>(_decision) forKey:@"Decision"];
+    [coder encodeInt:static_cast<int>(_decisionPoint) forKey:@"DecisionPoint"];
+}
+
+@end
+
+static RetainPtr<WKWebViewConfiguration> configurationWithContentFilterSettings(Decision decision, DecisionPoint decisionPoint)
+{
+    auto configuration = retainPtr([WKWebViewConfiguration testwebkitapi_configurationWithTestPlugInClassName:@"ContentFilteringPlugIn"]);
+    auto contentFilterEnabler = adoptNS([[MockContentFilterEnabler alloc] initWithDecision:decision decisionPoint:decisionPoint]);
+    [[configuration processPool] _setObject:contentFilterEnabler.get() forBundleParameter:NSStringFromClass([MockContentFilterEnabler class])];
+    return configuration;
+}
+
 @interface ServerRedirectNavigationDelegate : NSObject <WKNavigationDelegate>
 @end
 
@@ -59,20 +115,144 @@ static bool isDone;
 
 @end
 
-TEST(ContentFiltering, ServerRedirect)
+TEST(ContentFiltering, URLAfterServerRedirect)
+{
+    @autoreleasepool {
+        [NSURLProtocol registerClass:[TestProtocol class]];
+        [WKBrowsingContextController registerSchemeForCustomProtocol:[TestProtocol scheme]];
+
+        auto configuration = configurationWithContentFilterSettings(Decision::Allow, DecisionPoint::AfterAddData);
+        auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
+        auto navigationDelegate = adoptNS([[ServerRedirectNavigationDelegate alloc] init]);
+        [webView setNavigationDelegate:navigationDelegate.get()];
+        [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://redirect?pass"]]];
+        TestWebKitAPI::Util::run(&isDone);
+
+        [WKBrowsingContextController unregisterSchemeForCustomProtocol:[TestProtocol scheme]];
+        [NSURLProtocol unregisterClass:[TestProtocol class]];
+    }
+}
+
+@interface BecomeDownloadDelegate : NSObject <WKNavigationDelegate>
+@end
+
+@implementation BecomeDownloadDelegate
+
+- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
+{
+    decisionHandler(_WKNavigationResponsePolicyBecomeDownload);
+}
+
+- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error
+{
+    isDone = true;
+}
+
+- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation
+{
+    isDone = true;
+}
+
+@end
+
+static bool downloadDidStart;
+
+@interface ContentFilteringDownloadDelegate : NSObject <_WKDownloadDelegate>
+@end
+
+@implementation ContentFilteringDownloadDelegate
+
+- (void)_downloadDidStart:(_WKDownload *)download
+{
+    downloadDidStart = true;
+}
+
+@end
+
+static void downloadTest(Decision decision, DecisionPoint decisionPoint)
+{
+    @autoreleasepool {
+        [NSURLProtocol registerClass:[TestProtocol class]];
+        [WKBrowsingContextController registerSchemeForCustomProtocol:[TestProtocol scheme]];
+
+        auto configuration = configurationWithContentFilterSettings(decision, decisionPoint);
+        auto downloadDelegate = adoptNS([[ContentFilteringDownloadDelegate alloc] init]);
+        [[configuration processPool] _setDownloadDelegate:downloadDelegate.get()];
+        auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
+        auto navigationDelegate = adoptNS([[BecomeDownloadDelegate alloc] init]);
+        [webView setNavigationDelegate:navigationDelegate.get()];
+        [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://redirect/?download"]]];
+
+        isDone = false;
+        downloadDidStart = false;
+        TestWebKitAPI::Util::run(&isDone);
+
+        const bool downloadShouldStart = decision == Decision::Allow || decisionPoint > DecisionPoint::AfterResponse;
+        EXPECT_EQ(downloadShouldStart, downloadDidStart);
+
+        [WKBrowsingContextController unregisterSchemeForCustomProtocol:[TestProtocol scheme]];
+        [NSURLProtocol unregisterClass:[TestProtocol class]];
+    }
+}
+
+TEST(ContentFiltering, AllowDownloadAfterWillSendRequest)
+{
+    downloadTest(Decision::Allow, DecisionPoint::AfterWillSendRequest);
+}
+
+TEST(ContentFiltering, BlockDownloadAfterWillSendRequest)
 {
-    [NSURLProtocol registerClass:[TestProtocol class]];
-    [WKBrowsingContextController registerSchemeForCustomProtocol:[TestProtocol scheme]];
+    downloadTest(Decision::Block, DecisionPoint::AfterWillSendRequest);
+}
 
-    auto configuration = retainPtr([WKWebViewConfiguration testwebkitapi_configurationWithTestPlugInClassName:@"ServerRedirectPlugIn"]);
-    auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
-    auto navigationDelegate = adoptNS([[ServerRedirectNavigationDelegate alloc] init]);
-    [webView setNavigationDelegate:navigationDelegate.get()];
-    [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"http://redirect?pass"]]];
-    TestWebKitAPI::Util::run(&isDone);
+TEST(ContentFiltering, AllowDownloadAfterRedirect)
+{
+    downloadTest(Decision::Allow, DecisionPoint::AfterRedirect);
+}
 
-    [WKBrowsingContextController unregisterSchemeForCustomProtocol:[TestProtocol scheme]];
-    [NSURLProtocol unregisterClass:[TestProtocol class]];
+TEST(ContentFiltering, BlockDownloadAfterRedirect)
+{
+    downloadTest(Decision::Block, DecisionPoint::AfterRedirect);
+}
+
+TEST(ContentFiltering, AllowDownloadAfterResponse)
+{
+    downloadTest(Decision::Allow, DecisionPoint::AfterResponse);
+}
+
+TEST(ContentFiltering, BlockDownloadAfterResponse)
+{
+    downloadTest(Decision::Block, DecisionPoint::AfterResponse);
+}
+
+TEST(ContentFiltering, AllowDownloadAfterAddData)
+{
+    downloadTest(Decision::Allow, DecisionPoint::AfterAddData);
+}
+
+TEST(ContentFiltering, BlockDownloadAfterAddData)
+{
+    downloadTest(Decision::Block, DecisionPoint::AfterAddData);
+}
+
+TEST(ContentFiltering, AllowDownloadAfterFinishedAddingData)
+{
+    downloadTest(Decision::Allow, DecisionPoint::AfterFinishedAddingData);
+}
+
+TEST(ContentFiltering, BlockDownloadAfterFinishedAddingData)
+{
+    downloadTest(Decision::Block, DecisionPoint::AfterFinishedAddingData);
+}
+
+TEST(ContentFiltering, AllowDownloadNever)
+{
+    downloadTest(Decision::Allow, DecisionPoint::Never);
+}
+
+TEST(ContentFiltering, BlockDownloadNever)
+{
+    downloadTest(Decision::Block, DecisionPoint::Never);
 }
 
 #endif // WK_API_ENABLED
index 43acd52..d1cb7e4 100644 (file)
 #import "MockContentFilterSettings.h"
 #import <WebKit/WKWebProcessPlugIn.h>
 
-@interface ServerRedirectPlugIn : NSObject <WKWebProcessPlugIn>
+using MockContentFilterSettings = WebCore::MockContentFilterSettings;
+using Decision = MockContentFilterSettings::Decision;
+using DecisionPoint = MockContentFilterSettings::DecisionPoint;
+
+@interface MockContentFilterEnabler : NSObject <NSCopying, NSSecureCoding>
 @end
 
-@implementation ServerRedirectPlugIn
+@implementation MockContentFilterEnabler
+
++ (BOOL)supportsSecureCoding
+{
+    return YES;
+}
+
+- (id)copyWithZone:(NSZone *)zone
+{
+    return [self retain];
+}
 
-+ (void)initialize
+- (instancetype)initWithCoder:(NSCoder *)decoder
 {
-    using MockContentFilterSettings = WebCore::MockContentFilterSettings;
-    using Decision = MockContentFilterSettings::Decision;
-    using DecisionPoint = MockContentFilterSettings::DecisionPoint;
-    MockContentFilterSettings& settings = MockContentFilterSettings::singleton();
+    if (!(self = [super init]))
+        return nil;
+
+    auto& settings = MockContentFilterSettings::singleton();
     settings.setEnabled(true);
-    settings.setDecision(Decision::Allow);
-    settings.setDecisionPoint(DecisionPoint::AfterAddData);
+    settings.setDecision(static_cast<Decision>([decoder decodeIntForKey:@"Decision"]));
+    settings.setDecisionPoint(static_cast<DecisionPoint>([decoder decodeIntForKey:@"DecisionPoint"]));
+    return self;
+}
+
+- (void)dealloc
+{
+    MockContentFilterSettings::singleton().setEnabled(false);
+    [super dealloc];
+}
+
+- (void)encodeWithCoder:(NSCoder *)coder
+{
+}
+
+@end
+
+@interface ContentFilteringPlugIn : NSObject <WKWebProcessPlugIn>
+@end
+
+@implementation ContentFilteringPlugIn {
+    RetainPtr<MockContentFilterEnabler> _contentFilterEnabler;
+    RetainPtr<WKWebProcessPlugInController> _plugInController;
+}
+
+- (void)webProcessPlugIn:(WKWebProcessPlugInController *)plugInController initializeWithObject:(id)initializationObject
+{
+    ASSERT(!_plugInController);
+    _plugInController = plugInController;
+    [plugInController.parameters addObserver:self forKeyPath:NSStringFromClass([MockContentFilterEnabler class]) options:NSKeyValueObservingOptionInitial context:NULL];
+}
+
+- (void)dealloc
+{
+    [[_plugInController parameters] removeObserver:self forKeyPath:NSStringFromClass([MockContentFilterEnabler class])];
+    [super dealloc];
+}
+
+- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
+{
+    id contentFilterEnabler = [object valueForKeyPath:keyPath];
+    ASSERT([contentFilterEnabler isKindOfClass:[MockContentFilterEnabler class]]);
+    _contentFilterEnabler = contentFilterEnabler;
 }
 
 @end
index c3f7bee..9227220 100644 (file)
@@ -265,34 +265,5 @@ TEST(_WKDownload, OriginatingWebView)
     TestWebKitAPI::Util::run(&isDone);
 }
 
-@interface AsynchronousDownloadNavigationDelegate : NSObject <WKNavigationDelegate>
-@end
-
-@implementation AsynchronousDownloadNavigationDelegate
-- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
-{
-    dispatch_async(dispatch_get_main_queue(), ^ {
-        decisionHandler(_WKNavigationResponsePolicyBecomeDownload);
-    });
-}
-@end
-
-@interface AsynchronousDownloadDelegate : NSObject <_WKDownloadDelegate>
-@end
-
-@implementation AsynchronousDownloadDelegate
-
-- (void)_downloadDidStart:(_WKDownload *)download
-{
-    isDone = true;
-}
-
-@end
-
-TEST(_WKDownload, AsynchronousDownloadPolicy)
-{
-    runTest(adoptNS([[AsynchronousDownloadNavigationDelegate alloc] init]).get(), adoptNS([[AsynchronousDownloadDelegate alloc] init]).get(), sourceURL);
-}
-
 #endif
 #endif