[Content Filtering] Crash when allowing a 0-byte resource to load
[WebKit-https.git] / Source / WebCore / loader / ContentFilter.cpp
index 2b1d68196b03c0dc68f123d16ddab7cddee4f969..2f600cb816b4f46d662efa745bc37c83f9a1750b 100644 (file)
 
 #include "CachedRawResource.h"
 #include "ContentFilterUnblockHandler.h"
+#include "DocumentLoader.h"
+#include "Logging.h"
 #include "NetworkExtensionContentFilter.h"
 #include "ParentalControlsContentFilter.h"
 #include "SharedBuffer.h"
 #include <wtf/NeverDestroyed.h>
 #include <wtf/Vector.h>
 
+#if !LOG_DISABLED
+#include <wtf/text/CString.h>
+#endif
+
 namespace WebCore {
 
 Vector<ContentFilter::Type>& ContentFilter::types()
 {
     static NeverDestroyed<Vector<ContentFilter::Type>> types {
         Vector<ContentFilter::Type> {
+#if HAVE(PARENTAL_CONTROLS)
             type<ParentalControlsContentFilter>(),
+#endif
 #if HAVE(NETWORK_EXTENSION)
             type<NetworkExtensionContentFilter>()
 #endif
@@ -51,7 +59,7 @@ Vector<ContentFilter::Type>& ContentFilter::types()
     return types;
 }
 
-std::unique_ptr<ContentFilter> ContentFilter::createIfNeeded(DecisionFunction decisionFunction)
+std::unique_ptr<ContentFilter> ContentFilter::createIfEnabled(DocumentLoader& documentLoader)
 {
     Container filters;
     for (auto& type : types()) {
@@ -66,18 +74,20 @@ std::unique_ptr<ContentFilter> ContentFilter::createIfNeeded(DecisionFunction de
     if (filters.isEmpty())
         return nullptr;
 
-    return std::make_unique<ContentFilter>(WTF::move(filters), WTF::move(decisionFunction));
+    return std::make_unique<ContentFilter>(WTF::move(filters), documentLoader);
 }
 
-ContentFilter::ContentFilter(Container contentFilters, DecisionFunction decisionFunction)
+ContentFilter::ContentFilter(Container contentFilters, DocumentLoader& documentLoader)
     : m_contentFilters { WTF::move(contentFilters) }
-    , m_decisionFunction { WTF::move(decisionFunction) }
+    , m_documentLoader { documentLoader }
 {
+    LOG(ContentFiltering, "Creating ContentFilter with %zu platform content filter(s).\n", m_contentFilters.size());
     ASSERT(!m_contentFilters.isEmpty());
 }
 
 ContentFilter::~ContentFilter()
 {
+    LOG(ContentFiltering, "Destroying ContentFilter.\n");
     if (!m_mainResource)
         return;
     ASSERT(m_mainResource->hasClient(this));
@@ -86,6 +96,7 @@ ContentFilter::~ContentFilter()
 
 void ContentFilter::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
 {
+    LOG(ContentFiltering, "ContentFilter received request for <%s> with redirect response from <%s>.\n", request.url().string().ascii().data(), redirectResponse.url().string().ascii().data());
     ResourceRequest requestCopy { request };
     ASSERT(m_state == State::Initialized || m_state == State::Filtering);
     forEachContentFilterUntilBlocked([&requestCopy, &redirectResponse](PlatformContentFilter& contentFilter) {
@@ -93,11 +104,16 @@ void ContentFilter::willSendRequest(ResourceRequest& request, const ResourceResp
         if (contentFilter.didBlockData())
             requestCopy = ResourceRequest();
     });
+#if !LOG_DISABLED
+    if (request != requestCopy)
+        LOG(ContentFiltering, "ContentFilter changed request url to <%s>.\n", requestCopy.url().string().ascii().data());
+#endif
     request = requestCopy;
 }
 
 void ContentFilter::startFilteringMainResource(CachedRawResource& resource)
 {
+    LOG(ContentFiltering, "ContentFilter will start filtering main resource at <%s>.\n", resource.url().string().ascii().data());
     ASSERT(m_state == State::Initialized);
     m_state = State::Filtering;
     ASSERT(!m_mainResource);
@@ -132,36 +148,78 @@ String ContentFilter::unblockRequestDeniedScript() const
 
 void ContentFilter::responseReceived(CachedResource* resource, const ResourceResponse& response)
 {
-    ASSERT(m_state == State::Filtering);
-    ASSERT_UNUSED(resource, resource == m_mainResource.get());
-    forEachContentFilterUntilBlocked([&response](PlatformContentFilter& contentFilter) {
-        contentFilter.responseReceived(response);
-    });
+    ASSERT(resource);
+    ASSERT(resource == m_mainResource);
+    ASSERT(m_state != State::Initialized);
+    LOG(ContentFiltering, "ContentFilter received response from <%s>.\n", response.url().string().ascii().data());
+
+    if (m_state == State::Filtering) {
+        forEachContentFilterUntilBlocked([&response](PlatformContentFilter& contentFilter) {
+            contentFilter.responseReceived(response);
+        });
+    }
+
+    if (m_state != State::Blocked)
+        m_documentLoader.responseReceived(resource, response);
 }
 
 void ContentFilter::dataReceived(CachedResource* resource, const char* data, int length)
 {
-    ASSERT(m_state == State::Filtering);
-    ASSERT_UNUSED(resource, resource == m_mainResource.get());
-    forEachContentFilterUntilBlocked([data, length](PlatformContentFilter& contentFilter) {
-        contentFilter.addData(data, length);
-    });
+    ASSERT(resource);
+    ASSERT(resource == m_mainResource);
+    ASSERT(m_state != State::Initialized);
+    LOG(ContentFiltering, "ContentFilter received %d bytes of data from <%s>.\n", length, resource->url().string().ascii().data());
+
+    if (m_state == State::Filtering) {
+        forEachContentFilterUntilBlocked([data, length](PlatformContentFilter& contentFilter) {
+            contentFilter.addData(data, length);
+        });
+
+        if (m_state == State::Allowed)
+            deliverResourceData(*resource);
+        return;
+    }
+
+    if (m_state == State::Allowed)
+        m_documentLoader.dataReceived(resource, data, length);
 }
 
 void ContentFilter::redirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
 {
-    ASSERT(m_state == State::Filtering);
-    ASSERT_UNUSED(resource, resource == m_mainResource.get());
-    willSendRequest(request, redirectResponse);
+    ASSERT(resource);
+    ASSERT(resource == m_mainResource);
+    ASSERT(m_state != State::Initialized);
+
+    if (m_state == State::Filtering)
+        willSendRequest(request, redirectResponse);
+
+    if (m_state != State::Blocked)
+        m_documentLoader.redirectReceived(resource, request, redirectResponse);
 }
 
 void ContentFilter::notifyFinished(CachedResource* resource)
 {
-    ASSERT(m_state == State::Filtering);
-    ASSERT_UNUSED(resource, resource == m_mainResource.get());
-    forEachContentFilterUntilBlocked([](PlatformContentFilter& contentFilter) {
-        contentFilter.finishedAddingData();
-    });
+    ASSERT(resource);
+    ASSERT(resource == m_mainResource);
+    ASSERT(m_state != State::Initialized);
+    LOG(ContentFiltering, "ContentFilter will finish filtering main resource at <%s>.\n", resource->url().string().ascii().data());
+
+    if (resource->errorOccurred()) {
+        m_documentLoader.notifyFinished(resource);
+        return;
+    }
+
+    if (m_state == State::Filtering) {
+        forEachContentFilterUntilBlocked([](PlatformContentFilter& contentFilter) {
+            contentFilter.finishedAddingData();
+        });
+
+        if (m_state != State::Blocked)
+            deliverResourceData(*resource);
+    }
+
+    if (m_state != State::Blocked)
+        m_documentLoader.notifyFinished(resource);
 }
 
 void ContentFilter::forEachContentFilterUntilBlocked(std::function<void(PlatformContentFilter&)> function)
@@ -193,11 +251,16 @@ void ContentFilter::didDecide(State state)
     ASSERT(m_state != State::Allowed);
     ASSERT(m_state != State::Blocked);
     ASSERT(state == State::Allowed || state == State::Blocked);
+    LOG(ContentFiltering, "ContentFilter decided load should be %s for main resource at <%s>.\n", state == State::Allowed ? "allowed" : "blocked", m_mainResource ? m_mainResource->url().string().ascii().data() : "");
     m_state = state;
+    m_documentLoader.contentFilterDidDecide();
+}
 
-    // Calling m_decisionFunction might delete |this|.
-    if (m_decisionFunction)
-        m_decisionFunction();
+void ContentFilter::deliverResourceData(CachedResource& resource)
+{
+    ASSERT(resource.dataBufferingPolicy() == BufferData);
+    if (auto* resourceBuffer = resource.resourceBuffer())
+        m_documentLoader.dataReceived(&resource, resourceBuffer->data(), resourceBuffer->size());
 }
 
 } // namespace WebCore