Changeset 182356 in webkit


Ignore:
Timestamp:
Apr 5, 2015 12:52:14 AM (9 years ago)
Author:
aestes@apple.com
Message:

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

rdar://problem/20211099

Reviewed by Andreas Kling.

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

contentfiltering/block-after-response.html

There were several problems with how ContentFilter loaded replacement data:
(1) Replacement data was delivered to DocumentLoader as if it were the original document's data. This assumes

that the original data was a UTF-8 encoded HTML document, which is not always true. We had a way to reset
the encoding, but not the content type.

(2) Replacement data was never delivered when the filter blocks in DocumentLoader::responseReceived().
(3) The main resource load was cancelled before the replacement data could be rendered when the filter blocks

in DocumentLoader::dataReceived().

The result was that only when the load was blocked after DocumentLoader::notifyFinished() would the replacement
data be shown properly, and only when problem (1) wasn't occurring.

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

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

  • loader/ContentFilter.cpp:

(WebCore::ContentFilter::createIfNeeded): Changed to take a DecisionFunction rather than a ResourceResponse and DocumentLoader.
(WebCore::ContentFilter::ContentFilter): Ditto.
(WebCore::ContentFilter::~ContentFilter): Removed ourself as a CachedRawResource client if needed.
(WebCore::ContentFilter::startFilteringMainResource): Became the client of the CachedRawResource in order to start the filtering process.
(WebCore::ContentFilter::unblockHandler): Returned the unblock handler.
(WebCore::ContentFilter::replacementData): Returned the replacement data.
(WebCore::ContentFilter::unblockRequestDeniedScript): Returned the unblock request denied script.
(WebCore::ContentFilter::responseReceived): Called responseReceived() on each filter using forEachContentFilterUntilBlocked().
(WebCore::ContentFilter::dataReceived): Ditto for dataReceived().
(WebCore::ContentFilter::notifyFinished): Ditto for finishedLoading().
(WebCore::ContentFilter::forEachContentFilterUntilBlocked): For each filter that needs more data, called the function.
If the filter blocked the load, called didDecide() with State::Blocked.
If all filters allowed the load, called didDecide() with State::Allowed.
(WebCore::ContentFilter::didDecide): Set m_state and called m_decisionFunction().
(WebCore::ContentFilter::addData): Deleted.
(WebCore::ContentFilter::finishedAddingData): Deleted.
(WebCore::ContentFilter::needsMoreData): Deleted.
(WebCore::ContentFilter::didBlockData): Deleted.
(WebCore::ContentFilter::getReplacementData): Deleted.

  • loader/ContentFilter.h:

(WebCore::ContentFilter::type):

  • loader/DocumentLoader.cpp:

(WebCore::DocumentLoader::DocumentLoader): Called ContentFilter::createIfNeeded() if not loading substitute data.
(WebCore::DocumentLoader::finishedLoading): Removed old ContentFilter code.
(WebCore::DocumentLoader::responseReceived): Ditto.
(WebCore::DocumentLoader::commitData): Ditto.
(WebCore::DocumentLoader::dataReceived): Ditto.
(WebCore::DocumentLoader::detachFromFrame): Set m_contentFilter to nullptr.
(WebCore::DocumentLoader::startLoadingMainResource): Called becomeMainResourceClientIfFilterAllows() instead of
becoming m_mainResource's client.
(WebCore::DocumentLoader::clearMainResource): Set m_contentFilter to nullptr.
(WebCore::DocumentLoader::becomeMainResourceClientIfFilterAllows): If ContentFilter is initialized, called
ContentFilter::startFilteringMainResource(). Otherwise added ourself as a client of m_mainResource.
(WebCore::DocumentLoader::installContentFilterUnblockHandler): Added a helper for creating and notifying
FrameLoaderClient of the unblock handler.
(WebCore::DocumentLoader::contentFilterDidDecide): Set m_contentFilter to nullptr. If the content filter
allowed the load, then added ourself as the CachedRawResource's client. Otherwise, installed the unblock handler
and scheduled a substitute data load with the replacement data.

  • loader/DocumentLoader.h:
  • loader/FrameLoader.cpp:

(WebCore::FrameLoader::prepareForLoadStart): Removed call to PolicyChecker::prepareForLoadStart().

  • loader/NavigationScheduler.cpp:

(WebCore::ScheduledSubstituteDataLoad::ScheduledSubstituteDataLoad): Added a ScheduledNavigation subclass that
calls FrameLoader::load() with a FrameLoadRequest containing substitute data.
(WebCore::NavigationScheduler::scheduleSubstituteDataLoad): Scheduled a substitute data load.

  • loader/NavigationScheduler.h:
  • loader/PolicyChecker.cpp:

(WebCore::PolicyChecker::checkNavigationPolicy): Reset m_contentFilterUnblockHandler if it couldn't handle the request.
(WebCore::PolicyChecker::prepareForLoadStart): Deleted.

  • loader/PolicyChecker.h:
  • platform/ContentFilterUnblockHandler.h:

(WebCore::ContentFilterUnblockHandler::unreachableURL):
(WebCore::ContentFilterUnblockHandler::setUnreachableURL):
(WebCore::ContentFilterUnblockHandler::unblockURLScheme): Renamed to ContentFilter::urlScheme().

  • platform/PlatformContentFilter.h:
  • platform/cocoa/ContentFilterUnblockHandlerCocoa.mm:

(WebCore::ContentFilterUnblockHandler::wrapWithDecisionHandler): Added a helper to wrap the unblock handler with an outer DecisionHandlerFunction.
(WebCore::ContentFilterUnblockHandler::encode): Added m_unreachableURL to the encoding.
(WebCore::ContentFilterUnblockHandler::decode): Ditto for the decoding.
(WebCore::ContentFilterUnblockHandler::canHandleRequest): Changed to call ContentFilter::urlScheme().

  • platform/cocoa/NetworkExtensionContentFilter.h:
  • platform/cocoa/NetworkExtensionContentFilter.mm:

(replacementDataFromDecisionInfo): Added a helper to extract replacement data from the decisionInfo dictionary.
(WebCore::NetworkExtensionContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
(WebCore::NetworkExtensionContentFilter::create): Created a new object.
(WebCore::NetworkExtensionContentFilter::NetworkExtensionContentFilter): Created a NEFilterSource immediately when using the modern API.
(WebCore::NetworkExtensionContentFilter::responseReceived): Created a NEFilterSource when using the legacy API.
Called -[NEFilterSource receivedResponse:decisionHandler:] when using the modern API.
(WebCore::NetworkExtensionContentFilter::addData): Stopped buffering the original data.
(WebCore::NetworkExtensionContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&.
(WebCore::NetworkExtensionContentFilter::canHandleResponse): Deleted.
(WebCore::createNEFilterSource): Deleted.
(WebCore::NetworkExtensionContentFilter::getReplacementData): Deleted.

  • platform/cocoa/ParentalControlsContentFilter.h:
  • platform/cocoa/ParentalControlsContentFilter.mm:

(WebCore::ParentalControlsContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
(WebCore::ParentalControlsContentFilter::create): Created a new object.
(WebCore::ParentalControlsContentFilter::ParentalControlsContentFilter): Initialized m_filterState to kWFEStateBuffering.
(WebCore::canHandleResponse): Added a helper to check if the response can be filtered.
(WebCore::ParentalControlsContentFilter::responseReceived): If !canHandleResponse(), set m_filterState to kWFEStateAllowed and return.
Otherwise created a new WebFilterEvaluator with the response.
(WebCore::ParentalControlsContentFilter::addData): Called updateFilterState().
(WebCore::ParentalControlsContentFilter::finishedAddingData): Ditto.
(WebCore::ParentalControlsContentFilter::needsMoreData): Changed to check m_filterState.
(WebCore::ParentalControlsContentFilter::didBlockData): Ditto.
(WebCore::ParentalControlsContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&.
(WebCore::ParentalControlsContentFilter::updateFilterState): Updated m_filterState by calling -[WebFilterEvaluator filterState].
(WebCore::ParentalControlsContentFilter::canHandleResponse): Deleted.
(WebCore::ParentalControlsContentFilter::getReplacementData): Deleted.

  • platform/spi/cocoa/NEFilterSourceSPI.h:
  • platform/spi/cocoa/WebFilterEvaluatorSPI.h:
  • testing/MockContentFilter.cpp:

(WebCore::MockContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
(WebCore::MockContentFilter::create): Created a new object.
(WebCore::MockContentFilter::responseReceived): Called maybeDetermineStatus().
(WebCore::MockContentFilter::addData): Stopped buffering the original data.
(WebCore::MockContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&.
(WebCore::MockContentFilter::unblockHandler): Asserted that we blocked data.
(WebCore::MockContentFilter::canHandleResponse): Deleted.
(WebCore::MockContentFilter::MockContentFilter): Deleted.
(WebCore::MockContentFilter::getReplacementData): Deleted.

  • testing/MockContentFilter.h:
  • testing/MockContentFilterSettings.cpp:

(WebCore::MockContentFilterSettings::unblockRequestURL): Changed to use a StringBuilder.

Source/WebKit2:

Reviewed by Andreas Kling.

  • UIProcess/WebFrameProxy.cpp:

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

LayoutTests:

Reviewed by Andreas Kling.

  • TestExpectations: Unskipped block-after-add-data.html.
  • contentfiltering/block-after-add-data-expected.html: Added a passing expectation.
  • contentfiltering/block-after-response-expected.html: Ditto.
Location:
trunk
Files:
28 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r182355 r182356  
     12015-04-04  Andy Estes  <aestes@apple.com>
     2
     3        [Content Filtering] Blocked page is not always displayed when it should be
     4        https://bugs.webkit.org/show_bug.cgi?id=143410
     5
     6        Reviewed by Andreas Kling.
     7
     8        * TestExpectations: Unskipped block-after-add-data.html.
     9        * contentfiltering/block-after-add-data-expected.html: Added a passing expectation.
     10        * contentfiltering/block-after-response-expected.html: Ditto.
     11
    1122015-04-04  Chris Fleizach  <cfleizach@apple.com>
    213
  • trunk/LayoutTests/TestExpectations

    r182339 r182356  
    498498webkit.org/b/114280 svg/animations/smil-leak-elements.svg [ Pass Failure ]
    499499
    500 # contentfiltering/block-after-add-data.html times out unexpectedly
    501 webkit.org/b/142894 contentfiltering/block-after-add-data.html [ Skip ]
    502 
    503500webkit.org/b/143085 media/track/track-mode.html [ Pass Timeout ]
    504501
  • trunk/LayoutTests/contentfiltering/block-after-add-data-expected.html

    r181877 r182356  
    11<!DOCTYPE html>
    2 <iframe src="data:text/html,<!DOCTYPE html><body>"></iframe>
     2<iframe src="data:text/html,<!DOCTYPE html><body>PASS"></iframe>
  • trunk/LayoutTests/contentfiltering/block-after-response-expected.html

    r181877 r182356  
    11<!DOCTYPE html>
    2 <iframe src="data:text/html,<!DOCTYPE html><body>FAIL"></iframe>
     2<iframe src="data:text/html,<!DOCTYPE html><body>PASS"></iframe>
  • trunk/Source/WebCore/ChangeLog

    r182355 r182356  
     12015-04-04  Andy Estes  <aestes@apple.com>
     2
     3        [Content Filtering] Blocked page is not always displayed when it should be
     4        https://bugs.webkit.org/show_bug.cgi?id=143410
     5        rdar://problem/20211099
     6
     7        Reviewed by Andreas Kling.
     8
     9        These tests now pass: contentfiltering/block-after-add-data.html
     10                              contentfiltering/block-after-response.html
     11
     12        There were several problems with how ContentFilter loaded replacement data:
     13        (1) Replacement data was delivered to DocumentLoader as if it were the original document's data. This assumes
     14            that the original data was a UTF-8 encoded HTML document, which is not always true. We had a way to reset
     15            the encoding, but not the content type.
     16        (2) Replacement data was never delivered when the filter blocks in DocumentLoader::responseReceived().
     17        (3) The main resource load was cancelled before the replacement data could be rendered when the filter blocks
     18            in DocumentLoader::dataReceived().
     19        The result was that only when the load was blocked after DocumentLoader::notifyFinished() would the replacement
     20        data be shown properly, and only when problem (1) wasn't occurring.
     21
     22        This patch addresses these issues by using the substitute data mechanism to deliver replacement data. By using
     23        substitute data, we can ensure that the original load is cancelled at the earliest opportunity and that the
     24        replacement data is loaded with the proper content type and encoding.
     25
     26        Accomplishing this required changing the way ContentFilter interacts with DocumentLoader. Instead of placing
     27        ContentFilter hooks throughout DocumentLoader, this patch makes ContentFilter itself the client of the
     28        CachedRawResource for the duration of the filtering. If the filter decides to allow the load, DocumentLoader
     29        adds itself as a client causing CachedRawResource to deliver to it the response and buffered data. If the
     30        filter decides to block the load, DocumentLoader schedules a substitute data load. An added benefit of this
     31        approach is that ContentFilter can reuse CachedRawResource's original data buffer instead of keeping its own.
     32
     33        * loader/ContentFilter.cpp:
     34        (WebCore::ContentFilter::createIfNeeded): Changed to take a DecisionFunction rather than a ResourceResponse and DocumentLoader.
     35        (WebCore::ContentFilter::ContentFilter): Ditto.
     36        (WebCore::ContentFilter::~ContentFilter): Removed ourself as a CachedRawResource client if needed.
     37        (WebCore::ContentFilter::startFilteringMainResource): Became the client of the CachedRawResource in order to start the filtering process.
     38        (WebCore::ContentFilter::unblockHandler): Returned the unblock handler.
     39        (WebCore::ContentFilter::replacementData): Returned the replacement data.
     40        (WebCore::ContentFilter::unblockRequestDeniedScript): Returned the unblock request denied script.
     41        (WebCore::ContentFilter::responseReceived): Called responseReceived() on each filter using forEachContentFilterUntilBlocked().
     42        (WebCore::ContentFilter::dataReceived): Ditto for dataReceived().
     43        (WebCore::ContentFilter::notifyFinished): Ditto for finishedLoading().
     44        (WebCore::ContentFilter::forEachContentFilterUntilBlocked): For each filter that needs more data, called the function.
     45        If the filter blocked the load, called didDecide() with State::Blocked.
     46        If all filters allowed the load, called didDecide() with State::Allowed.
     47        (WebCore::ContentFilter::didDecide): Set m_state and called m_decisionFunction().
     48        (WebCore::ContentFilter::addData): Deleted.
     49        (WebCore::ContentFilter::finishedAddingData): Deleted.
     50        (WebCore::ContentFilter::needsMoreData): Deleted.
     51        (WebCore::ContentFilter::didBlockData): Deleted.
     52        (WebCore::ContentFilter::getReplacementData): Deleted.
     53        * loader/ContentFilter.h:
     54        (WebCore::ContentFilter::type):
     55        * loader/DocumentLoader.cpp:
     56        (WebCore::DocumentLoader::DocumentLoader): Called ContentFilter::createIfNeeded() if not loading substitute data.
     57        (WebCore::DocumentLoader::finishedLoading): Removed old ContentFilter code.
     58        (WebCore::DocumentLoader::responseReceived): Ditto.
     59        (WebCore::DocumentLoader::commitData): Ditto.
     60        (WebCore::DocumentLoader::dataReceived): Ditto.
     61        (WebCore::DocumentLoader::detachFromFrame): Set m_contentFilter to nullptr.
     62        (WebCore::DocumentLoader::startLoadingMainResource): Called becomeMainResourceClientIfFilterAllows() instead of
     63        becoming m_mainResource's client.
     64        (WebCore::DocumentLoader::clearMainResource): Set m_contentFilter to nullptr.
     65        (WebCore::DocumentLoader::becomeMainResourceClientIfFilterAllows): If ContentFilter is initialized, called
     66        ContentFilter::startFilteringMainResource(). Otherwise added ourself as a client of m_mainResource.
     67        (WebCore::DocumentLoader::installContentFilterUnblockHandler): Added a helper for creating and notifying
     68        FrameLoaderClient of the unblock handler.
     69        (WebCore::DocumentLoader::contentFilterDidDecide): Set m_contentFilter to nullptr. If the content filter
     70        allowed the load, then added ourself as the CachedRawResource's client. Otherwise, installed the unblock handler
     71        and scheduled a substitute data load with the replacement data.
     72        * loader/DocumentLoader.h:
     73        * loader/FrameLoader.cpp:
     74        (WebCore::FrameLoader::prepareForLoadStart): Removed call to PolicyChecker::prepareForLoadStart().
     75        * loader/NavigationScheduler.cpp:
     76        (WebCore::ScheduledSubstituteDataLoad::ScheduledSubstituteDataLoad): Added a ScheduledNavigation subclass that
     77        calls FrameLoader::load() with a FrameLoadRequest containing substitute data.
     78        (WebCore::NavigationScheduler::scheduleSubstituteDataLoad): Scheduled a substitute data load.
     79        * loader/NavigationScheduler.h:
     80        * loader/PolicyChecker.cpp:
     81        (WebCore::PolicyChecker::checkNavigationPolicy): Reset m_contentFilterUnblockHandler if it couldn't handle the request.
     82        (WebCore::PolicyChecker::prepareForLoadStart): Deleted.
     83        * loader/PolicyChecker.h:
     84        * platform/ContentFilterUnblockHandler.h:
     85        (WebCore::ContentFilterUnblockHandler::unreachableURL):
     86        (WebCore::ContentFilterUnblockHandler::setUnreachableURL):
     87        (WebCore::ContentFilterUnblockHandler::unblockURLScheme): Renamed to ContentFilter::urlScheme().
     88        * platform/PlatformContentFilter.h:
     89        * platform/cocoa/ContentFilterUnblockHandlerCocoa.mm:
     90        (WebCore::ContentFilterUnblockHandler::wrapWithDecisionHandler): Added a helper to wrap the unblock handler with an outer DecisionHandlerFunction.
     91        (WebCore::ContentFilterUnblockHandler::encode): Added m_unreachableURL to the encoding.
     92        (WebCore::ContentFilterUnblockHandler::decode): Ditto for the decoding.
     93        (WebCore::ContentFilterUnblockHandler::canHandleRequest): Changed to call ContentFilter::urlScheme().
     94        * platform/cocoa/NetworkExtensionContentFilter.h:
     95        * platform/cocoa/NetworkExtensionContentFilter.mm:
     96        (replacementDataFromDecisionInfo): Added a helper to extract replacement data from the decisionInfo dictionary.
     97        (WebCore::NetworkExtensionContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
     98        (WebCore::NetworkExtensionContentFilter::create): Created a new object.
     99        (WebCore::NetworkExtensionContentFilter::NetworkExtensionContentFilter): Created a NEFilterSource immediately when using the modern API.
     100        (WebCore::NetworkExtensionContentFilter::responseReceived): Created a NEFilterSource when using the legacy API.
     101        Called -[NEFilterSource receivedResponse:decisionHandler:] when using the modern API.
     102        (WebCore::NetworkExtensionContentFilter::addData): Stopped buffering the original data.
     103        (WebCore::NetworkExtensionContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&.
     104        (WebCore::NetworkExtensionContentFilter::canHandleResponse): Deleted.
     105        (WebCore::createNEFilterSource): Deleted.
     106        (WebCore::NetworkExtensionContentFilter::getReplacementData): Deleted.
     107        * platform/cocoa/ParentalControlsContentFilter.h:
     108        * platform/cocoa/ParentalControlsContentFilter.mm:
     109        (WebCore::ParentalControlsContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
     110        (WebCore::ParentalControlsContentFilter::create): Created a new object.
     111        (WebCore::ParentalControlsContentFilter::ParentalControlsContentFilter): Initialized m_filterState to kWFEStateBuffering.
     112        (WebCore::canHandleResponse): Added a helper to check if the response can be filtered.
     113        (WebCore::ParentalControlsContentFilter::responseReceived): If !canHandleResponse(), set m_filterState to kWFEStateAllowed and return.
     114        Otherwise created a new WebFilterEvaluator with the response.
     115        (WebCore::ParentalControlsContentFilter::addData): Called updateFilterState().
     116        (WebCore::ParentalControlsContentFilter::finishedAddingData): Ditto.
     117        (WebCore::ParentalControlsContentFilter::needsMoreData): Changed to check m_filterState.
     118        (WebCore::ParentalControlsContentFilter::didBlockData): Ditto.
     119        (WebCore::ParentalControlsContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&.
     120        (WebCore::ParentalControlsContentFilter::updateFilterState): Updated m_filterState by calling -[WebFilterEvaluator filterState].
     121        (WebCore::ParentalControlsContentFilter::canHandleResponse): Deleted.
     122        (WebCore::ParentalControlsContentFilter::getReplacementData): Deleted.
     123        * platform/spi/cocoa/NEFilterSourceSPI.h:
     124        * platform/spi/cocoa/WebFilterEvaluatorSPI.h:
     125        * testing/MockContentFilter.cpp:
     126        (WebCore::MockContentFilter::enabled): Renamed from canHandleReponse(); only checks if the filter is enabled.
     127        (WebCore::MockContentFilter::create): Created a new object.
     128        (WebCore::MockContentFilter::responseReceived): Called maybeDetermineStatus().
     129        (WebCore::MockContentFilter::addData): Stopped buffering the original data.
     130        (WebCore::MockContentFilter::replacementData): Returned a SharedBuffer instead of a char* and int&.
     131        (WebCore::MockContentFilter::unblockHandler): Asserted that we blocked data.
     132        (WebCore::MockContentFilter::canHandleResponse): Deleted.
     133        (WebCore::MockContentFilter::MockContentFilter): Deleted.
     134        (WebCore::MockContentFilter::getReplacementData): Deleted.
     135        * testing/MockContentFilter.h:
     136        * testing/MockContentFilterSettings.cpp:
     137        (WebCore::MockContentFilterSettings::unblockRequestURL): Changed to use a StringBuilder.
     138
    11392015-04-04  Chris Fleizach  <cfleizach@apple.com>
    2140
  • trunk/Source/WebCore/loader/ContentFilter.cpp

    r181791 r182356  
    2929#if ENABLE(CONTENT_FILTERING)
    3030
    31 #include "DocumentLoader.h"
    32 #include "Frame.h"
     31#include "CachedRawResource.h"
     32#include "ContentFilterUnblockHandler.h"
    3333#include "NetworkExtensionContentFilter.h"
    3434#include "ParentalControlsContentFilter.h"
    35 #include "ScriptController.h"
    36 #include <bindings/ScriptValue.h>
     35#include "SharedBuffer.h"
    3736#include <wtf/NeverDestroyed.h>
    3837#include <wtf/Vector.h>
     
    5352}
    5453
    55 std::unique_ptr<ContentFilter> ContentFilter::createIfNeeded(const ResourceResponse& response, DocumentLoader& documentLoader)
     54std::unique_ptr<ContentFilter> ContentFilter::createIfNeeded(DecisionFunction decisionFunction)
    5655{
    5756    Container filters;
    5857    for (auto& type : types()) {
    59         if (type.canHandleResponse(response))
    60             filters.append(type.create(response));
     58        if (!type.enabled())
     59            continue;
     60
     61        auto filter = type.create();
     62        ASSERT(filter);
     63        filters.append(WTF::move(filter));
    6164    }
    6265
     
    6467        return nullptr;
    6568
    66     return std::make_unique<ContentFilter>(WTF::move(filters), documentLoader);
     69    return std::make_unique<ContentFilter>(WTF::move(filters), WTF::move(decisionFunction));
    6770}
    6871
    69 ContentFilter::ContentFilter(Container contentFilters, DocumentLoader& documentLoader)
     72ContentFilter::ContentFilter(Container contentFilters, DecisionFunction decisionFunction)
    7073    : m_contentFilters { WTF::move(contentFilters) }
    71     , m_documentLoader { documentLoader }
     74    , m_decisionFunction { WTF::move(decisionFunction) }
    7275{
    7376    ASSERT(!m_contentFilters.isEmpty());
    7477}
    7578
    76 void ContentFilter::addData(const char* data, int length)
     79ContentFilter::~ContentFilter()
    7780{
    78     ASSERT(needsMoreData());
    79 
    80     for (auto& contentFilter : m_contentFilters)
    81         contentFilter->addData(data, length);
    82 }
    83    
    84 void ContentFilter::finishedAddingData()
    85 {
    86     ASSERT(needsMoreData());
    87 
    88     for (auto& contentFilter : m_contentFilters)
    89         contentFilter->finishedAddingData();
    90 
    91     ASSERT(!needsMoreData());
     81    if (!m_mainResource)
     82        return;
     83    ASSERT(m_mainResource->hasClient(this));
     84    m_mainResource->removeClient(this);
    9285}
    9386
    94 bool ContentFilter::needsMoreData() const
     87void ContentFilter::startFilteringMainResource(CachedRawResource& resource)
    9588{
    96     for (auto& contentFilter : m_contentFilters) {
    97         if (contentFilter->needsMoreData())
    98             return true;
    99     }
    100 
    101     return false;
    102 }
    103 
    104 bool ContentFilter::didBlockData() const
    105 {
    106     for (auto& contentFilter : m_contentFilters) {
    107         if (contentFilter->didBlockData())
    108             return true;
    109     }
    110 
    111     return false;
    112 }
    113 
    114 const char* ContentFilter::getReplacementData(int& length) const
    115 {
    116     ASSERT(!needsMoreData());
    117 
    118     for (auto& contentFilter : m_contentFilters) {
    119         if (contentFilter->didBlockData())
    120             return contentFilter->getReplacementData(length);
    121     }
    122 
    123     return m_contentFilters[0]->getReplacementData(length);
     89    ASSERT(m_state == State::Initialized);
     90    m_state = State::Filtering;
     91    ASSERT(!m_mainResource);
     92    m_mainResource = &resource;
     93    ASSERT(!m_mainResource->hasClient(this));
     94    m_mainResource->addClient(this);
    12495}
    12596
    12697ContentFilterUnblockHandler ContentFilter::unblockHandler() const
    12798{
    128     ASSERT(didBlockData());
     99    ASSERT(m_state == State::Blocked);
     100    ASSERT(m_blockingContentFilter);
     101    ASSERT(m_blockingContentFilter->didBlockData());
     102    return m_blockingContentFilter->unblockHandler();
     103}
    129104
    130     PlatformContentFilter* blockingFilter = nullptr;
     105Ref<SharedBuffer> ContentFilter::replacementData() const
     106{
     107    ASSERT(m_state == State::Blocked);
     108    ASSERT(m_blockingContentFilter);
     109    ASSERT(m_blockingContentFilter->didBlockData());
     110    return m_blockingContentFilter->replacementData();
     111}
     112
     113String ContentFilter::unblockRequestDeniedScript() const
     114{
     115    ASSERT(m_state == State::Blocked);
     116    ASSERT(m_blockingContentFilter);
     117    ASSERT(m_blockingContentFilter->didBlockData());
     118    return m_blockingContentFilter->unblockRequestDeniedScript();
     119}
     120
     121void ContentFilter::responseReceived(CachedResource* resource, const ResourceResponse& response)
     122{
     123    ASSERT(m_state == State::Filtering);
     124    ASSERT_UNUSED(resource, resource == m_mainResource.get());
     125    forEachContentFilterUntilBlocked([&response](PlatformContentFilter& contentFilter) {
     126        contentFilter.responseReceived(response);
     127    });
     128}
     129
     130void ContentFilter::dataReceived(CachedResource* resource, const char* data, int length)
     131{
     132    ASSERT(m_state == State::Filtering);
     133    ASSERT_UNUSED(resource, resource == m_mainResource.get());
     134    forEachContentFilterUntilBlocked([data, length](PlatformContentFilter& contentFilter) {
     135        contentFilter.addData(data, length);
     136    });
     137}
     138
     139void ContentFilter::notifyFinished(CachedResource* resource)
     140{
     141    ASSERT(m_state == State::Filtering);
     142    ASSERT_UNUSED(resource, resource == m_mainResource.get());
     143    forEachContentFilterUntilBlocked([](PlatformContentFilter& contentFilter) {
     144        contentFilter.finishedAddingData();
     145    });
     146}
     147
     148void ContentFilter::forEachContentFilterUntilBlocked(std::function<void(PlatformContentFilter&)> function)
     149{
     150    bool allFiltersAllowedLoad { true };
    131151    for (auto& contentFilter : m_contentFilters) {
     152        if (!contentFilter->needsMoreData()) {
     153            ASSERT(!contentFilter->didBlockData());
     154            continue;
     155        }
     156
     157        function(*contentFilter);
     158
    132159        if (contentFilter->didBlockData()) {
    133             blockingFilter = contentFilter.get();
    134             break;
    135         }
     160            ASSERT(!m_blockingContentFilter);
     161            m_blockingContentFilter = contentFilter.get();
     162            didDecide(State::Blocked);
     163            return;
     164        } else if (contentFilter->needsMoreData())
     165            allFiltersAllowedLoad = false;
    136166    }
    137     ASSERT(blockingFilter);
    138167
    139     StringCapture unblockRequestDeniedScript { blockingFilter->unblockRequestDeniedScript() };
    140     if (unblockRequestDeniedScript.string().isEmpty())
    141         return blockingFilter->unblockHandler();
     168    if (allFiltersAllowedLoad)
     169        didDecide(State::Allowed);
     170}
    142171
    143     // It would be a layering violation for the unblock handler to access its frame,
    144     // so we will execute the unblock denied script on its behalf.
    145     ContentFilterUnblockHandler unblockHandler { blockingFilter->unblockHandler() };
    146     RefPtr<Frame> frame { m_documentLoader.frame() };
    147     return ContentFilterUnblockHandler {
    148         unblockHandler.unblockURLHost(), [unblockHandler, frame, unblockRequestDeniedScript](ContentFilterUnblockHandler::DecisionHandlerFunction decisionHandler) {
    149             unblockHandler.requestUnblockAsync([decisionHandler, frame, unblockRequestDeniedScript](bool unblocked) {
    150                 decisionHandler(unblocked);
    151                 if (!unblocked && frame)
    152                     frame->script().executeScript(unblockRequestDeniedScript.string());
    153             });
    154         }
    155     };
     172void ContentFilter::didDecide(State state)
     173{
     174    ASSERT(m_state != State::Allowed);
     175    ASSERT(m_state != State::Blocked);
     176    ASSERT(state == State::Allowed || state == State::Blocked);
     177    m_state = state;
     178
     179    // Calling m_decisionFunction might delete |this|.
     180    if (m_decisionFunction)
     181        m_decisionFunction();
    156182}
    157183
  • trunk/Source/WebCore/loader/ContentFilter.h

    r181791 r182356  
    2929#if ENABLE(CONTENT_FILTERING)
    3030
    31 #include "PlatformContentFilter.h"
     31#include "CachedRawResourceClient.h"
     32#include "CachedResourceHandle.h"
     33#include <functional>
    3234#include <wtf/Vector.h>
    3335
    3436namespace WebCore {
    3537
    36 class DocumentLoader;
    37 class ResourceResponse;
     38class CachedRawResource;
     39class ContentFilterUnblockHandler;
     40class PlatformContentFilter;
     41class SharedBuffer;
    3842
    39 class ContentFilter final : public PlatformContentFilter {
     43class ContentFilter final : private CachedRawResourceClient {
     44    WTF_MAKE_FAST_ALLOCATED;
     45    WTF_MAKE_NONCOPYABLE(ContentFilter);
     46
    4047public:
    4148    template <typename T> static void addType() { types().append(type<T>()); }
    42     static std::unique_ptr<ContentFilter> createIfNeeded(const ResourceResponse&, DocumentLoader&);
    4349
    44     void addData(const char* data, int length) override;
    45     void finishedAddingData() override;
    46     bool needsMoreData() const override;
    47     bool didBlockData() const override;
    48     const char* getReplacementData(int& length) const override;
    49     ContentFilterUnblockHandler unblockHandler() const override;
     50    using DecisionFunction = std::function<void()>;
     51    static std::unique_ptr<ContentFilter> createIfNeeded(DecisionFunction);
     52    ~ContentFilter() override;
     53
     54    static const char* urlScheme() { return "x-apple-content-filter"; }
     55
     56    void startFilteringMainResource(CachedRawResource&);
     57
     58    enum class State {
     59        Initialized,
     60        Filtering,
     61        Allowed,
     62        Blocked
     63    };
     64    State state() const { return m_state; }
     65    ContentFilterUnblockHandler unblockHandler() const;
     66    Ref<SharedBuffer> replacementData() const;
     67    String unblockRequestDeniedScript() const;
    5068
    5169private:
    5270    struct Type {
    53         const std::function<bool(const ResourceResponse&)> canHandleResponse;
    54         const std::function<std::unique_ptr<PlatformContentFilter>(const ResourceResponse&)> create;
     71        const std::function<bool()> enabled;
     72        const std::function<std::unique_ptr<PlatformContentFilter>()> create;
    5573    };
    5674    template <typename T> static Type type();
     
    5876
    5977    using Container = Vector<std::unique_ptr<PlatformContentFilter>>;
    60     friend std::unique_ptr<ContentFilter> std::make_unique<ContentFilter>(Container&&, DocumentLoader&);
    61     explicit ContentFilter(Container, DocumentLoader&);
     78    friend std::unique_ptr<ContentFilter> std::make_unique<ContentFilter>(Container&&, DecisionFunction&&);
     79    ContentFilter(Container, DecisionFunction);
    6280
    63     Container m_contentFilters;
    64     DocumentLoader& m_documentLoader;
     81    // CachedRawResourceClient
     82    void responseReceived(CachedResource*, const ResourceResponse&) override;
     83    void dataReceived(CachedResource*, const char* data, int length) override;
     84
     85    // CachedResourceClient
     86    void notifyFinished(CachedResource*) override;
     87
     88    void forEachContentFilterUntilBlocked(std::function<void(PlatformContentFilter&)>);
     89    void didDecide(State);
     90
     91    const Container m_contentFilters;
     92    const DecisionFunction m_decisionFunction;
     93    CachedResourceHandle<CachedRawResource> m_mainResource;
     94    PlatformContentFilter* m_blockingContentFilter { nullptr };
     95    State m_state { State::Initialized };
    6596};
    6697
     
    69100{
    70101    static_assert(std::is_base_of<PlatformContentFilter, T>::value, "Type must be a PlatformContentFilter.");
    71     return { T::canHandleResponse, T::create };
     102    return { T::enabled, T::create };
    72103}
    73104
  • trunk/Source/WebCore/loader/DocumentLoader.cpp

    r181876 r182356  
    5959#include "ResourceHandle.h"
    6060#include "SchemeRegistry.h"
     61#include "ScriptController.h"
    6162#include "SecurityPolicy.h"
    6263#include "Settings.h"
     
    141142    , m_subresourceLoadersArePageCacheAcceptable(false)
    142143    , m_applicationCacheHost(adoptPtr(new ApplicationCacheHost(*this)))
     144#if ENABLE(CONTENT_FILTERING)
     145    , m_contentFilter(!substituteData.isValid() ? ContentFilter::createIfNeeded(std::bind(&DocumentLoader::contentFilterDidDecide, this)) : nullptr)
     146#endif
    143147{
    144148}
     
    401405        frameLoader()->notifier().dispatchDidFinishLoading(this, identifier, finishTime);
    402406    }
    403 
    404 #if ENABLE(CONTENT_FILTERING)
    405     if (m_contentFilter && m_contentFilter->needsMoreData()) {
    406         m_contentFilter->finishedAddingData();
    407         int length;
    408         const char* data = m_contentFilter->getReplacementData(length);
    409         if (data)
    410             dataReceived(m_mainResource.get(), data, length);
    411 
    412         if (m_contentFilter->didBlockData()) {
    413             frameLoader()->client().contentFilterDidBlockLoad(m_contentFilter->unblockHandler());
    414             m_contentFilter = nullptr;
    415         }
    416     }
    417 #endif
    418407
    419408    maybeFinishLoadingMultipartContent();
     
    663652#endif
    664653
    665 #if ENABLE(CONTENT_FILTERING)
    666     m_contentFilter = ContentFilter::createIfNeeded(response, *this);
    667 #endif
    668 
    669654    frameLoader()->policyChecker().checkContentPolicy(m_response, [this](PolicyAction policy) {
    670655        continueAfterContentPolicy(policy);
     
    819804        bool userChosen;
    820805        String encoding;
    821 #if ENABLE(CONTENT_FILTERING)
    822         // The content filter's replacement data has a known encoding that might
    823         // differ from the response's encoding.
    824         if (m_contentFilter && m_contentFilter->didBlockData()) {
    825             ASSERT(!m_contentFilter->needsMoreData());
    826             userChosen = false;
    827         } else
    828 #endif
    829806        if (overrideEncoding().isNull()) {
    830807            userChosen = false;
     
    871848#endif
    872849
    873 #if ENABLE(CONTENT_FILTERING)
    874     bool loadWasBlockedBeforeFinishing = false;
    875     if (m_contentFilter && m_contentFilter->needsMoreData()) {
    876         m_contentFilter->addData(data, length);
    877 
    878         if (m_contentFilter->needsMoreData()) {
    879             // Since the filter still needs more data to make a decision,
    880             // avoid committing this data to prevent partial rendering of
    881             // content that might later be blocked.
    882             return;
    883         }
    884 
    885         data = m_contentFilter->getReplacementData(length);
    886         loadWasBlockedBeforeFinishing = m_contentFilter->didBlockData();
    887 
    888         if (loadWasBlockedBeforeFinishing)
    889             frameLoader()->client().contentFilterDidBlockLoad(m_contentFilter->unblockHandler());
    890     }
    891 #endif
    892 
    893850    if (m_identifierForLoadWithoutResourceLoader)
    894851        frameLoader()->notifier().dispatchDidReceiveData(this, m_identifierForLoadWithoutResourceLoader, data, length, -1);
     
    899856    if (!isMultipartReplacingLoad())
    900857        commitLoad(data, length);
    901 
    902 #if ENABLE(CONTENT_FILTERING)
    903     if (loadWasBlockedBeforeFinishing) {
    904         cancelMainResourceLoad(frameLoader()->cancelledError(m_request));
    905         m_contentFilter = nullptr;
    906     }
    907 #endif
    908858}
    909859
     
    961911    if (m_mainResource && m_mainResource->hasClient(this))
    962912        m_mainResource->removeClient(this);
     913#if ENABLE(CONTENT_FILTERING)
     914    m_contentFilter = nullptr;
     915#endif
    963916
    964917    m_applicationCacheHost->setDOMApplicationCache(nullptr);
     
    14721425        frameLoader()->notifier().dispatchWillSendRequest(this, m_identifierForLoadWithoutResourceLoader, request, ResourceResponse());
    14731426    }
     1427
     1428#if ENABLE(CONTENT_FILTERING)
     1429    becomeMainResourceClientIfFilterAllows();
     1430#else
    14741431    m_mainResource->addClient(this);
     1432#endif
    14751433
    14761434    // A bunch of headers are set when the underlying ResourceLoader is created, and m_request needs to include those.
     
    15081466    if (m_mainResource && m_mainResource->hasClient(this))
    15091467        m_mainResource->removeClient(this);
     1468#if ENABLE(CONTENT_FILTERING)
     1469    m_contentFilter = nullptr;
     1470#endif
    15101471
    15111472    m_mainResource = 0;
     
    16011562#endif
    16021563
     1564#if ENABLE(CONTENT_FILTERING)
     1565void DocumentLoader::becomeMainResourceClientIfFilterAllows()
     1566{
     1567    ASSERT(m_mainResource);
     1568    if (m_contentFilter) {
     1569        ASSERT(m_contentFilter->state() == ContentFilter::State::Initialized);
     1570        m_contentFilter->startFilteringMainResource(*m_mainResource);
     1571    } else
     1572        m_mainResource->addClient(this);
     1573}
     1574
     1575void DocumentLoader::installContentFilterUnblockHandler(ContentFilter& contentFilter)
     1576{
     1577    ContentFilterUnblockHandler unblockHandler { contentFilter.unblockHandler() };
     1578    unblockHandler.setUnreachableURL(documentURL());
     1579    RefPtr<Frame> frame { this->frame() };
     1580    String unblockRequestDeniedScript { contentFilter.unblockRequestDeniedScript() };
     1581    if (!unblockRequestDeniedScript.isEmpty() && frame) {
     1582        static_assert(std::is_base_of<ThreadSafeRefCounted<Frame>, Frame>::value, "Frame must be ThreadSafeRefCounted.");
     1583        StringCapture capturedScript { unblockRequestDeniedScript };
     1584        unblockHandler.wrapWithDecisionHandler([frame, capturedScript](bool unblocked) {
     1585            if (!unblocked)
     1586                frame->script().executeScript(capturedScript.string());
     1587        });
     1588    }
     1589    frameLoader()->client().contentFilterDidBlockLoad(WTF::move(unblockHandler));
     1590}
     1591
     1592void DocumentLoader::contentFilterDidDecide()
     1593{
     1594    using State = ContentFilter::State;
     1595    ASSERT(m_contentFilter);
     1596    ASSERT(m_contentFilter->state() == State::Blocked || m_contentFilter->state() == State::Allowed);
     1597    std::unique_ptr<ContentFilter> contentFilter;
     1598    std::swap(contentFilter, m_contentFilter);
     1599    if (contentFilter->state() == State::Allowed) {
     1600        if (m_mainResource)
     1601            m_mainResource->addClient(this);
     1602        return;
     1603    }
     1604
     1605    installContentFilterUnblockHandler(*contentFilter);
     1606
     1607    URL blockedURL;
     1608    blockedURL.setProtocol(ContentFilter::urlScheme());
     1609    blockedURL.setHost(ASCIILiteral("blocked-page"));
     1610    SubstituteData substituteData { contentFilter->replacementData(), ASCIILiteral("text/html"), ASCIILiteral("UTF-8"), documentURL() };
     1611    frame()->navigationScheduler().scheduleSubstituteDataLoad(blockedURL, substituteData);
     1612}
     1613#endif
     1614
     1615
    16031616} // namespace WebCore
  • trunk/Source/WebCore/loader/DocumentLoader.h

    r181876 r182356  
    6565    class CachedRawResource;
    6666    class CachedResourceLoader;
    67     class ContentFilter;
    6867    class FormState;
    6968    class Frame;
     
    7372    class SharedBuffer;
    7473    class SubstituteResource;
     74
     75#if ENABLE(CONTENT_FILTERING)
     76    class ContentFilter;
     77#endif
    7578
    7679    typedef HashMap<unsigned long, RefPtr<ResourceLoader>> ResourceLoaderMap;
     
    331334        void clearMainResource();
    332335
     336#if ENABLE(CONTENT_FILTERING)
     337        void becomeMainResourceClientIfFilterAllows();
     338        void installContentFilterUnblockHandler(ContentFilter&);
     339        void contentFilterDidDecide();
     340#endif
     341
    333342        Frame* m_frame;
    334343        Ref<CachedResourceLoader> m_cachedResourceLoader;
  • trunk/Source/WebCore/loader/FrameLoader.cpp

    r182351 r182356  
    11251125void FrameLoader::prepareForLoadStart()
    11261126{
    1127     policyChecker().prepareForLoadStart();
    11281127    m_progressTracker->progressStarted();
    11291128    m_client.dispatchDidStartProvisionalLoad();
  • trunk/Source/WebCore/loader/NavigationScheduler.cpp

    r180110 r182356  
    279279};
    280280
     281class ScheduledSubstituteDataLoad : public ScheduledNavigation {
     282public:
     283    ScheduledSubstituteDataLoad(const URL& baseURL, const SubstituteData& substituteData)
     284        : ScheduledNavigation { 0, LockHistory::No, LockBackForwardList::No, false, false }
     285        , m_baseURL { baseURL }
     286        , m_substituteData { substituteData }
     287    {
     288    }
     289
     290    void fire(Frame& frame) override
     291    {
     292        UserGestureIndicator gestureIndicator { wasUserGesture() ? DefinitelyProcessingUserGesture : DefinitelyNotProcessingUserGesture };
     293        frame.loader().load(FrameLoadRequest { &frame, m_baseURL, m_substituteData });
     294    }
     295
     296private:
     297    URL m_baseURL;
     298    SubstituteData m_substituteData;
     299};
     300
    281301NavigationScheduler::NavigationScheduler(Frame& frame)
    282302    : m_frame(frame)
     
    431451}
    432452
     453void NavigationScheduler::scheduleSubstituteDataLoad(const URL& baseURL, const SubstituteData& substituteData)
     454{
     455    if (shouldScheduleNavigation())
     456        schedule(std::make_unique<ScheduledSubstituteDataLoad>(baseURL, substituteData));
     457}
     458
    433459void NavigationScheduler::timerFired()
    434460{
  • trunk/Source/WebCore/loader/NavigationScheduler.h

    r176459 r182356  
    4242class ScheduledNavigation;
    4343class SecurityOrigin;
     44class SubstituteData;
    4445class URL;
    4546
     
    7475    void scheduleRefresh();
    7576    void scheduleHistoryNavigation(int steps);
     77    void scheduleSubstituteDataLoad(const URL& baseURL, const SubstituteData&);
    7678
    7779    void startTimer();
  • trunk/Source/WebCore/loader/PolicyChecker.cpp

    r181791 r182356  
    5757}
    5858
    59 void PolicyChecker::prepareForLoadStart()
    60 {
    61 #if ENABLE(CONTENT_FILTERING)
    62     m_contentFilterUnblockHandler = { };
    63 #endif
    64 }
    65 
    6659void PolicyChecker::checkNavigationPolicy(const ResourceRequest& newRequest, NavigationPolicyDecisionFunction function)
    6760{
     
    121114        });
    122115        continueAfterNavigationPolicy(PolicyIgnore);
    123     }
     116        return;
     117    }
     118    m_contentFilterUnblockHandler = { };
    124119#endif
    125120
  • trunk/Source/WebCore/loader/PolicyChecker.h

    r181791 r182356  
    5555    explicit PolicyChecker(Frame&);
    5656
    57     void prepareForLoadStart();
    5857    void checkNavigationPolicy(const ResourceRequest&, DocumentLoader*, PassRefPtr<FormState>, NavigationPolicyDecisionFunction);
    5958    void checkNavigationPolicy(const ResourceRequest&, NavigationPolicyDecisionFunction);
  • trunk/Source/WebCore/platform/ContentFilterUnblockHandler.h

    r181791 r182356  
    2929#if ENABLE(CONTENT_FILTERING)
    3030
     31#include "URL.h"
    3132#include <functional>
    3233#include <wtf/RetainPtr.h>
     
    4849    using UnblockRequesterFunction = std::function<void(DecisionHandlerFunction)>;
    4950
    50     static const char* unblockURLScheme() { return "x-apple-content-filter"; }
    51 
    5251    ContentFilterUnblockHandler() = default;
    5352    WEBCORE_EXPORT ContentFilterUnblockHandler(String unblockURLHost, UnblockRequesterFunction);
     
    6160    WEBCORE_EXPORT bool canHandleRequest(const ResourceRequest&) const;
    6261    WEBCORE_EXPORT void requestUnblockAsync(DecisionHandlerFunction) const;
     62    void wrapWithDecisionHandler(const DecisionHandlerFunction&);
    6363
    6464    const String& unblockURLHost() const { return m_unblockURLHost; }
     65    const URL& unreachableURL() const { return m_unreachableURL; }
     66    void setUnreachableURL(const URL& url) { m_unreachableURL = url; }
    6567
    6668private:
    6769    String m_unblockURLHost;
     70    URL m_unreachableURL;
    6871    UnblockRequesterFunction m_unblockRequester;
    6972#if PLATFORM(IOS)
  • trunk/Source/WebCore/platform/PlatformContentFilter.h

    r181791 r182356  
    2727#define PlatformContentFilter_h
    2828
    29 #include "ContentFilterUnblockHandler.h"
     29#include <wtf/Ref.h>
    3030#include <wtf/text/WTFString.h>
    3131
    3232namespace WebCore {
    3333
     34class ContentFilterUnblockHandler;
    3435class ResourceResponse;
     36class SharedBuffer;
    3537
    3638class PlatformContentFilter {
     
    4345public:
    4446    virtual ~PlatformContentFilter() { }
     47    virtual void responseReceived(const ResourceResponse&) = 0;
    4548    virtual void addData(const char* data, int length) = 0;
    4649    virtual void finishedAddingData() = 0;
    4750    virtual bool needsMoreData() const = 0;
    4851    virtual bool didBlockData() const = 0;
    49     virtual const char* getReplacementData(int& length) const = 0;
     52    virtual Ref<SharedBuffer> replacementData() const = 0;
    5053    virtual ContentFilterUnblockHandler unblockHandler() const = 0;
    5154    virtual String unblockRequestDeniedScript() const { return emptyString(); }
  • trunk/Source/WebCore/platform/cocoa/ContentFilterUnblockHandlerCocoa.mm

    r181791 r182356  
    3030
    3131#import "BlockExceptions.h"
     32#import "ContentFilter.h"
    3233#import "ResourceRequest.h"
    3334
     
    4445
    4546static NSString * const unblockURLHostKey { @"unblockURLHost" };
     47static NSString * const unreachableURLKey { @"unreachableURL" };
    4648
    4749namespace WebCore {
     
    6163#endif
    6264
     65void ContentFilterUnblockHandler::wrapWithDecisionHandler(const DecisionHandlerFunction& decisionHandler)
     66{
     67    ContentFilterUnblockHandler wrapped { *this };
     68    UnblockRequesterFunction wrappedRequester { [wrapped, decisionHandler](DecisionHandlerFunction wrappedDecisionHandler) {
     69        wrapped.requestUnblockAsync([wrappedDecisionHandler, decisionHandler](bool unblocked) {
     70            wrappedDecisionHandler(unblocked);
     71            decisionHandler(unblocked);
     72        });
     73    }};
     74#if PLATFORM(IOS)
     75    m_webFilterEvaluator = nullptr;
     76#endif
     77    std::swap(m_unblockRequester, wrappedRequester);
     78}
     79
    6380bool ContentFilterUnblockHandler::needsUIProcess() const
    6481{
     
    7592    BEGIN_BLOCK_OBJC_EXCEPTIONS;
    7693    [coder encodeObject:m_unblockURLHost forKey:unblockURLHostKey];
     94    [coder encodeObject:(NSURL *)m_unreachableURL forKey:unreachableURLKey];
    7795#if PLATFORM(IOS)
    7896    [coder encodeObject:m_webFilterEvaluator.get() forKey:webFilterEvaluatorKey];
     
    86104    BEGIN_BLOCK_OBJC_EXCEPTIONS;
    87105    unblockHandler.m_unblockURLHost = [coder decodeObjectOfClass:[NSString class] forKey:unblockURLHostKey];
     106    unblockHandler.m_unreachableURL = [coder decodeObjectOfClass:[NSURL class] forKey:unreachableURLKey];
    88107#if PLATFORM(IOS)
    89108    unblockHandler.m_webFilterEvaluator = [coder decodeObjectOfClass:getWebFilterEvaluatorClass() forKey:webFilterEvaluatorKey];
     
    105124    }
    106125
    107     return request.url().protocolIs(unblockURLScheme()) && equalIgnoringCase(request.url().host(), m_unblockURLHost);
     126    return request.url().protocolIs(ContentFilter::urlScheme()) && equalIgnoringCase(request.url().host(), m_unblockURLHost);
    108127}
    109128
  • trunk/Source/WebCore/platform/cocoa/NetworkExtensionContentFilter.h

    r181781 r182356  
    2828
    2929#include "PlatformContentFilter.h"
    30 #include "SharedBuffer.h"
    3130#include <objc/NSObjCRuntime.h>
    3231#include <wtf/Compiler.h>
    3332#include <wtf/OSObjectPtr.h>
    34 #include <wtf/Ref.h>
    3533#include <wtf/RetainPtr.h>
    3634
     
    4341OBJC_CLASS NSDictionary;
    4442OBJC_CLASS NSMutableData;
    45 
     43OBJC_CLASS NSString;
    4644namespace WebCore {
    4745
    4846class NetworkExtensionContentFilter final : public PlatformContentFilter {
    49     friend std::unique_ptr<NetworkExtensionContentFilter> std::make_unique<NetworkExtensionContentFilter>(const ResourceResponse&);
     47    friend std::unique_ptr<NetworkExtensionContentFilter> std::make_unique<NetworkExtensionContentFilter>();
    5048
    5149public:
    52     static bool canHandleResponse(const ResourceResponse&);
    53     static std::unique_ptr<NetworkExtensionContentFilter> create(const ResourceResponse&);
     50    static bool enabled();
     51    static std::unique_ptr<NetworkExtensionContentFilter> create();
    5452
     53    void responseReceived(const ResourceResponse&) override;
    5554    void addData(const char* data, int length) override;
    5655    void finishedAddingData() override;
    5756    bool needsMoreData() const override;
    5857    bool didBlockData() const override;
    59     const char* getReplacementData(int& length) const override;
     58    Ref<SharedBuffer> replacementData() const override;
    6059    ContentFilterUnblockHandler unblockHandler() const override;
    6160
    6261private:
    63     explicit NetworkExtensionContentFilter(const ResourceResponse&);
     62    NetworkExtensionContentFilter();
    6463    void handleDecision(NEFilterSourceStatus, NSData *replacementData);
    6564
     
    6766    OSObjectPtr<dispatch_queue_t> m_queue;
    6867    OSObjectPtr<dispatch_semaphore_t> m_semaphore;
    69     Ref<SharedBuffer> m_originalData;
    7068    RetainPtr<NSData> m_replacementData;
    7169    RetainPtr<NEFilterSource> m_neFilterSource;
  • trunk/Source/WebCore/platform/cocoa/NetworkExtensionContentFilter.mm

    r181795 r182356  
    2929#if HAVE(NETWORK_EXTENSION)
    3030
     31#import "ContentFilterUnblockHandler.h"
    3132#import "NEFilterSourceSPI.h"
    3233#import "ResourceResponse.h"
     34#import "SharedBuffer.h"
    3335#import "SoftLinking.h"
     36#import "URL.h"
    3437#import <objc/runtime.h>
    3538
     
    3841
    3942#if HAVE(MODERN_NE_FILTER_SOURCE)
    40 // FIXME: <rdar://problem/20165664> Expose decisionHandler dictionary keys as NSString constants in NEFilterSource.h
    41 static NSString * const optionsPageData = @"PageData";
    42 
    4343static inline NSData *replacementDataFromDecisionInfo(NSDictionary *decisionInfo)
    4444{
    45     id replacementData = decisionInfo[optionsPageData];
     45    id replacementData = decisionInfo[NEFilterSourceOptionsPageData];
    4646    ASSERT(!replacementData || [replacementData isKindOfClass:[NSData class]]);
    4747    return replacementData;
     
    5151namespace WebCore {
    5252
    53 bool NetworkExtensionContentFilter::canHandleResponse(const ResourceResponse& response)
     53bool NetworkExtensionContentFilter::enabled()
    5454{
    55     return response.url().protocolIsInHTTPFamily() && [getNEFilterSourceClass() filterRequired];
     55    return [getNEFilterSourceClass() filterRequired];
    5656}
    5757
    58 std::unique_ptr<NetworkExtensionContentFilter> NetworkExtensionContentFilter::create(const ResourceResponse& response)
     58std::unique_ptr<NetworkExtensionContentFilter> NetworkExtensionContentFilter::create()
    5959{
    60     return std::make_unique<NetworkExtensionContentFilter>(response);
     60    return std::make_unique<NetworkExtensionContentFilter>();
    6161}
    6262
    63 static inline RetainPtr<NEFilterSource> createNEFilterSource(const URL& url, dispatch_queue_t decisionQueue)
    64 {
    65 #if HAVE(MODERN_NE_FILTER_SOURCE)
    66     UNUSED_PARAM(url);
    67     return adoptNS([allocNEFilterSourceInstance() initWithDecisionQueue:decisionQueue]);
    68 #else
    69     UNUSED_PARAM(decisionQueue);
    70     return adoptNS([allocNEFilterSourceInstance() initWithURL:url direction:NEFilterSourceDirectionInbound socketIdentifier:0]);
    71 #endif
    72 }
    73 
    74 NetworkExtensionContentFilter::NetworkExtensionContentFilter(const ResourceResponse& response)
     63NetworkExtensionContentFilter::NetworkExtensionContentFilter()
    7564    : m_status { NEFilterSourceStatusNeedsMoreData }
    7665    , m_queue { adoptOSObject(dispatch_queue_create("com.apple.WebCore.NEFilterSourceQueue", DISPATCH_QUEUE_SERIAL)) }
    7766    , m_semaphore { adoptOSObject(dispatch_semaphore_create(0)) }
    78     , m_originalData { *SharedBuffer::create() }
    79     , m_neFilterSource { createNEFilterSource(response.url(), m_queue.get()) }
     67#if HAVE(MODERN_NE_FILTER_SOURCE)
     68    , m_neFilterSource { adoptNS([allocNEFilterSourceInstance() initWithDecisionQueue:m_queue.get()]) }
     69#endif
    8070{
    8171    ASSERT([getNEFilterSourceClass() filterRequired]);
     72}
    8273
    83 #if HAVE(MODERN_NE_FILTER_SOURCE)
     74void NetworkExtensionContentFilter::responseReceived(const ResourceResponse& response)
     75{
     76    if (!response.url().protocolIsInHTTPFamily()) {
     77        m_status = NEFilterSourceStatusPass;
     78        return;
     79    }
     80
     81#if !HAVE(MODERN_NE_FILTER_SOURCE)
     82    ASSERT(!m_neFilterSource);
     83    m_neFilterSource = adoptNS([allocNEFilterSourceInstance() initWithURL:response.url() direction:NEFilterSourceDirectionInbound socketIdentifier:0]);
     84#else
    8485    [m_neFilterSource receivedResponse:response.nsURLResponse() decisionHandler:[this](NEFilterSourceStatus status, NSDictionary *decisionInfo) {
    8586        handleDecision(status, replacementDataFromDecisionInfo(decisionInfo));
     
    9697{
    9798    RetainPtr<NSData> copiedData { [NSData dataWithBytes:(void*)data length:length] };
    98 
    99     // FIXME: NEFilterSource doesn't buffer data like WebFilterEvaluator does,
    100     // so we need to do it ourselves so getReplacementData() can return the
    101     // original bytes back to the loader. We should find a way to remove this
    102     // additional copy.
    103     m_originalData->append((CFDataRef)copiedData.get());
    10499
    105100#if HAVE(MODERN_NE_FILTER_SOURCE)
     
    149144}
    150145
    151 const char* NetworkExtensionContentFilter::getReplacementData(int& length) const
     146Ref<SharedBuffer> NetworkExtensionContentFilter::replacementData() const
    152147{
    153     if (didBlockData()) {
    154         length = [m_replacementData length];
    155         return static_cast<const char*>([m_replacementData bytes]);
    156     }
    157 
    158     length = m_originalData->size();
    159     return m_originalData->data();
     148    ASSERT(didBlockData());
     149    return adoptRef(*SharedBuffer::wrapNSData(m_replacementData.get()).leakRef());
    160150}
    161151
  • trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.h

    r181781 r182356  
    3737
    3838class ParentalControlsContentFilter final : public PlatformContentFilter {
    39     friend std::unique_ptr<ParentalControlsContentFilter> std::make_unique<ParentalControlsContentFilter>(const ResourceResponse&);
     39    friend std::unique_ptr<ParentalControlsContentFilter> std::make_unique<ParentalControlsContentFilter>();
    4040
    4141public:
    42     static bool canHandleResponse(const ResourceResponse&);
    43     static std::unique_ptr<ParentalControlsContentFilter> create(const ResourceResponse&);
     42    static bool enabled();
     43    static std::unique_ptr<ParentalControlsContentFilter> create();
    4444
     45    void responseReceived(const ResourceResponse&) override;
    4546    void addData(const char* data, int length) override;
    4647    void finishedAddingData() override;
    4748    bool needsMoreData() const override;
    4849    bool didBlockData() const override;
    49     const char* getReplacementData(int& length) const override;
     50    Ref<SharedBuffer> replacementData() const override;
    5051    ContentFilterUnblockHandler unblockHandler() const override;
    5152
    5253private:
    53     explicit ParentalControlsContentFilter(const ResourceResponse&);
     54    ParentalControlsContentFilter();
     55    void updateFilterState();
    5456
     57    OSStatus m_filterState;
    5558    RetainPtr<WebFilterEvaluator> m_webFilterEvaluator;
    5659    RetainPtr<NSData> m_replacementData;
  • trunk/Source/WebCore/platform/cocoa/ParentalControlsContentFilter.mm

    r181795 r182356  
    2727#import "ParentalControlsContentFilter.h"
    2828
     29#import "ContentFilterUnblockHandler.h"
    2930#import "ResourceResponse.h"
     31#import "SharedBuffer.h"
    3032#import "SoftLinking.h"
    3133#import "WebFilterEvaluatorSPI.h"
     
    3739namespace WebCore {
    3840
    39 bool ParentalControlsContentFilter::canHandleResponse(const ResourceResponse& response)
     41bool ParentalControlsContentFilter::enabled()
    4042{
    41     if (!response.url().protocolIsInHTTPFamily())
    42         return false;
     43    return [getWebFilterEvaluatorClass() isManagedSession];
     44}
    4345
    44     if ([getWebFilterEvaluatorClass() isManagedSession]) {
     46std::unique_ptr<ParentalControlsContentFilter> ParentalControlsContentFilter::create()
     47{
     48    return std::make_unique<ParentalControlsContentFilter>();
     49}
     50
     51ParentalControlsContentFilter::ParentalControlsContentFilter()
     52    : m_filterState { kWFEStateBuffering }
     53{
     54    ASSERT([getWebFilterEvaluatorClass() isManagedSession]);
     55}
     56
     57static inline bool canHandleResponse(const ResourceResponse& response)
     58{
    4559#if PLATFORM(MAC)
    46         if (response.url().protocolIs("https"))
     60    return response.url().protocolIs("https");
     61#else
     62    return response.url().protocolIsInHTTPFamily();
    4763#endif
    48             return true;
     64}
     65
     66void ParentalControlsContentFilter::responseReceived(const ResourceResponse& response)
     67{
     68    ASSERT(!m_webFilterEvaluator);
     69
     70    if (!canHandleResponse(response)) {
     71        m_filterState = kWFEStateAllowed;
     72        return;
    4973    }
    5074
    51     return false;
    52 }
    53 
    54 std::unique_ptr<ParentalControlsContentFilter> ParentalControlsContentFilter::create(const ResourceResponse& response)
    55 {
    56     return std::make_unique<ParentalControlsContentFilter>(response);
    57 }
    58 
    59 ParentalControlsContentFilter::ParentalControlsContentFilter(const ResourceResponse& response)
    60     : m_webFilterEvaluator { adoptNS([allocWebFilterEvaluatorInstance() initWithResponse:response.nsURLResponse()]) }
    61 {
    62     ASSERT([getWebFilterEvaluatorClass() isManagedSession]);
     75    m_webFilterEvaluator = adoptNS([allocWebFilterEvaluatorInstance() initWithResponse:response.nsURLResponse()]);
     76    updateFilterState();
    6377}
    6478
     
    6781    ASSERT(![m_replacementData.get() length]);
    6882    m_replacementData = [m_webFilterEvaluator addData:[NSData dataWithBytesNoCopy:(void*)data length:length freeWhenDone:NO]];
     83    updateFilterState();
    6984    ASSERT(needsMoreData() || [m_replacementData.get() length]);
    7085}
     
    7489    ASSERT(![m_replacementData.get() length]);
    7590    m_replacementData = [m_webFilterEvaluator dataComplete];
     91    updateFilterState();
    7692}
    7793
    7894bool ParentalControlsContentFilter::needsMoreData() const
    7995{
    80     return [m_webFilterEvaluator filterState] == kWFEStateBuffering;
     96    return m_filterState == kWFEStateBuffering;
    8197}
    8298
    8399bool ParentalControlsContentFilter::didBlockData() const
    84100{
    85     return [m_webFilterEvaluator wasBlocked];
     101    return m_filterState == kWFEStateBlocked;
    86102}
    87103
    88 const char* ParentalControlsContentFilter::getReplacementData(int& length) const
     104Ref<SharedBuffer> ParentalControlsContentFilter::replacementData() const
    89105{
    90     length = [m_replacementData length];
    91     return static_cast<const char*>([m_replacementData bytes]);
     106    ASSERT(didBlockData());
     107    return adoptRef(*SharedBuffer::wrapNSData(m_replacementData.get()).leakRef());
    92108}
    93109
     
    101117}
    102118
     119void ParentalControlsContentFilter::updateFilterState()
     120{
     121    m_filterState = [m_webFilterEvaluator filterState];
     122}
     123
    103124} // namespace WebCore
  • trunk/Source/WebCore/platform/spi/cocoa/NEFilterSourceSPI.h

    r181523 r182356  
    6969- (void)remediateWithDecisionHandler:(NEFilterSourceDecisionHandler)decisionHandler;
    7070@end
     71
    7172#endif
    7273
  • trunk/Source/WebCore/platform/spi/cocoa/WebFilterEvaluatorSPI.h

    r180872 r182356  
    3333
    3434enum {
    35     kWFEStateBuffering = 2
     35    kWFEStateAllowed = 0,
     36    kWFEStateBlocked = 1,
     37    kWFEStateBuffering = 2,
     38    kWFEStateEvaluating = 3
    3639};
    3740
  • trunk/Source/WebCore/testing/MockContentFilter.cpp

    r181791 r182356  
    3030
    3131#include "ContentFilter.h"
     32#include "ContentFilterUnblockHandler.h"
     33#include "SharedBuffer.h"
    3234#include <mutex>
    3335#include <wtf/text/CString.h>
     36#include <wtf/text/StringBuilder.h>
    3437
    3538namespace WebCore {
     
    5154}
    5255
    53 bool MockContentFilter::canHandleResponse(const ResourceResponse&)
     56bool MockContentFilter::enabled()
    5457{
    5558    return settings().enabled();
    5659}
    5760
    58 std::unique_ptr<MockContentFilter> MockContentFilter::create(const ResourceResponse& response)
     61std::unique_ptr<MockContentFilter> MockContentFilter::create()
    5962{
    60     return std::make_unique<MockContentFilter>(response);
     63    return std::make_unique<MockContentFilter>();
    6164}
    6265
    63 MockContentFilter::MockContentFilter(const ResourceResponse&)
     66void MockContentFilter::responseReceived(const ResourceResponse&)
    6467{
    6568    maybeDetermineStatus(DecisionPoint::AfterResponse);
    6669}
    6770
    68 void MockContentFilter::addData(const char* data, int length)
     71void MockContentFilter::addData(const char*, int)
    6972{
    70     m_replacementData.append(data, length);
    7173    maybeDetermineStatus(DecisionPoint::AfterAddData);
    7274}
     
    8789}
    8890
    89 const char* MockContentFilter::getReplacementData(int& length) const
     91Ref<SharedBuffer> MockContentFilter::replacementData() const
    9092{
    91     length = m_replacementData.size();
    92     return m_replacementData.data();
     93    ASSERT(didBlockData());
     94    return adoptRef(*SharedBuffer::create(m_replacementData.data(), m_replacementData.size()).leakRef());
    9395}
    9496
    9597ContentFilterUnblockHandler MockContentFilter::unblockHandler() const
    9698{
     99    ASSERT(didBlockData());
    97100    using DecisionHandlerFunction = ContentFilterUnblockHandler::DecisionHandlerFunction;
    98101
  • trunk/Source/WebCore/testing/MockContentFilter.h

    r181791 r182356  
    3333
    3434class MockContentFilter final : public PlatformContentFilter {
    35     friend std::unique_ptr<MockContentFilter> std::make_unique<MockContentFilter>(const ResourceResponse&);
     35    friend std::unique_ptr<MockContentFilter> std::make_unique<MockContentFilter>();
    3636
    3737public:
    3838    static void ensureInstalled();
    39     static bool canHandleResponse(const ResourceResponse&);
    40     static std::unique_ptr<MockContentFilter> create(const ResourceResponse&);
     39    static bool enabled();
     40    static std::unique_ptr<MockContentFilter> create();
    4141
     42    void responseReceived(const ResourceResponse&) override;
    4243    void addData(const char* data, int length) override;
    4344    void finishedAddingData() override;
    4445    bool needsMoreData() const override;
    4546    bool didBlockData() const override;
    46     const char* getReplacementData(int& length) const override;
     47    Ref<SharedBuffer> replacementData() const override;
    4748    ContentFilterUnblockHandler unblockHandler() const override;
    4849    String unblockRequestDeniedScript() const override;
     
    5556    };
    5657
    57     explicit MockContentFilter(const ResourceResponse&);
     58    MockContentFilter() = default;
    5859    void maybeDetermineStatus(MockContentFilterSettings::DecisionPoint);
    5960
  • trunk/Source/WebCore/testing/MockContentFilterSettings.cpp

    r181791 r182356  
    2929#if ENABLE(CONTENT_FILTERING)
    3030
     31#include "ContentFilter.h"
    3132#include "ContentFilterUnblockHandler.h"
    3233#include <mutex>
    3334#include <wtf/NeverDestroyed.h>
     35#include <wtf/text/StringBuilder.h>
    3436
    3537namespace WebCore {
     
    5153    static std::once_flag onceFlag;
    5254    std::call_once(onceFlag, [] {
    53         unblockRequestURL.construct(ContentFilterUnblockHandler::unblockURLScheme());
    54         unblockRequestURL.get().append("://");
    55         unblockRequestURL.get().append(unblockURLHost());
     55        StringBuilder unblockRequestURLBuilder;
     56        unblockRequestURLBuilder.append(ContentFilter::urlScheme());
     57        unblockRequestURLBuilder.append("://");
     58        unblockRequestURLBuilder.append(unblockURLHost());
     59        unblockRequestURL.construct(unblockRequestURLBuilder.toString());
    5660    });
    5761    return unblockRequestURL;
  • trunk/Source/WebKit2/ChangeLog

    r182348 r182356  
     12015-04-04  Andy Estes  <aestes@apple.com>
     2
     3        [Content Filtering] Blocked page is not always displayed when it should be
     4        https://bugs.webkit.org/show_bug.cgi?id=143410
     5
     6        Reviewed by Andreas Kling.
     7
     8        * UIProcess/WebFrameProxy.cpp:
     9        (WebKit::WebFrameProxy::didStartProvisionalLoad): Stopped clearing m_contentFilterUnblockHandler here.
     10        (WebKit::WebFrameProxy::didHandleContentFilterUnblockNavigation): Started doing it here instead.
     11
    1122015-04-04  Chris Dumez  <cdumez@apple.com>
    213
  • trunk/Source/WebKit2/UIProcess/WebFrameProxy.cpp

    r181791 r182356  
    128128{
    129129    m_frameLoadState.didStartProvisionalLoad(url);
    130 #if ENABLE(CONTENT_FILTERING)
    131     m_contentFilterUnblockHandler = { };
    132 #endif
    133130}
    134131
     
    237234bool WebFrameProxy::didHandleContentFilterUnblockNavigation(const WebCore::ResourceRequest& request)
    238235{
    239     if (!m_contentFilterUnblockHandler.canHandleRequest(request))
    240         return false;
     236    if (!m_contentFilterUnblockHandler.canHandleRequest(request)) {
     237        m_contentFilterUnblockHandler = { };
     238        return false;
     239    }
    241240
    242241    RefPtr<WebPageProxy> page { m_page };
Note: See TracChangeset for help on using the changeset viewer.