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