2f600cb816b4f46d662efa745bc37c83f9a1750b
[WebKit-https.git] / Source / WebCore / loader / ContentFilter.cpp
1 /*
2  * Copyright (C) 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "ContentFilter.h"
28
29 #if ENABLE(CONTENT_FILTERING)
30
31 #include "CachedRawResource.h"
32 #include "ContentFilterUnblockHandler.h"
33 #include "DocumentLoader.h"
34 #include "Logging.h"
35 #include "NetworkExtensionContentFilter.h"
36 #include "ParentalControlsContentFilter.h"
37 #include "SharedBuffer.h"
38 #include <wtf/NeverDestroyed.h>
39 #include <wtf/Vector.h>
40
41 #if !LOG_DISABLED
42 #include <wtf/text/CString.h>
43 #endif
44
45 namespace WebCore {
46
47 Vector<ContentFilter::Type>& ContentFilter::types()
48 {
49     static NeverDestroyed<Vector<ContentFilter::Type>> types {
50         Vector<ContentFilter::Type> {
51 #if HAVE(PARENTAL_CONTROLS)
52             type<ParentalControlsContentFilter>(),
53 #endif
54 #if HAVE(NETWORK_EXTENSION)
55             type<NetworkExtensionContentFilter>()
56 #endif
57         }
58     };
59     return types;
60 }
61
62 std::unique_ptr<ContentFilter> ContentFilter::createIfEnabled(DocumentLoader& documentLoader)
63 {
64     Container filters;
65     for (auto& type : types()) {
66         if (!type.enabled())
67             continue;
68
69         auto filter = type.create();
70         ASSERT(filter);
71         filters.append(WTF::move(filter));
72     }
73
74     if (filters.isEmpty())
75         return nullptr;
76
77     return std::make_unique<ContentFilter>(WTF::move(filters), documentLoader);
78 }
79
80 ContentFilter::ContentFilter(Container contentFilters, DocumentLoader& documentLoader)
81     : m_contentFilters { WTF::move(contentFilters) }
82     , m_documentLoader { documentLoader }
83 {
84     LOG(ContentFiltering, "Creating ContentFilter with %zu platform content filter(s).\n", m_contentFilters.size());
85     ASSERT(!m_contentFilters.isEmpty());
86 }
87
88 ContentFilter::~ContentFilter()
89 {
90     LOG(ContentFiltering, "Destroying ContentFilter.\n");
91     if (!m_mainResource)
92         return;
93     ASSERT(m_mainResource->hasClient(this));
94     m_mainResource->removeClient(this);
95 }
96
97 void ContentFilter::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
98 {
99     LOG(ContentFiltering, "ContentFilter received request for <%s> with redirect response from <%s>.\n", request.url().string().ascii().data(), redirectResponse.url().string().ascii().data());
100     ResourceRequest requestCopy { request };
101     ASSERT(m_state == State::Initialized || m_state == State::Filtering);
102     forEachContentFilterUntilBlocked([&requestCopy, &redirectResponse](PlatformContentFilter& contentFilter) {
103         contentFilter.willSendRequest(requestCopy, redirectResponse);
104         if (contentFilter.didBlockData())
105             requestCopy = ResourceRequest();
106     });
107 #if !LOG_DISABLED
108     if (request != requestCopy)
109         LOG(ContentFiltering, "ContentFilter changed request url to <%s>.\n", requestCopy.url().string().ascii().data());
110 #endif
111     request = requestCopy;
112 }
113
114 void ContentFilter::startFilteringMainResource(CachedRawResource& resource)
115 {
116     LOG(ContentFiltering, "ContentFilter will start filtering main resource at <%s>.\n", resource.url().string().ascii().data());
117     ASSERT(m_state == State::Initialized);
118     m_state = State::Filtering;
119     ASSERT(!m_mainResource);
120     m_mainResource = &resource;
121     ASSERT(!m_mainResource->hasClient(this));
122     m_mainResource->addClient(this);
123 }
124
125 ContentFilterUnblockHandler ContentFilter::unblockHandler() const
126 {
127     ASSERT(m_state == State::Blocked);
128     ASSERT(m_blockingContentFilter);
129     ASSERT(m_blockingContentFilter->didBlockData());
130     return m_blockingContentFilter->unblockHandler();
131 }
132
133 Ref<SharedBuffer> ContentFilter::replacementData() const
134 {
135     ASSERT(m_state == State::Blocked);
136     ASSERT(m_blockingContentFilter);
137     ASSERT(m_blockingContentFilter->didBlockData());
138     return m_blockingContentFilter->replacementData();
139 }
140
141 String ContentFilter::unblockRequestDeniedScript() const
142 {
143     ASSERT(m_state == State::Blocked);
144     ASSERT(m_blockingContentFilter);
145     ASSERT(m_blockingContentFilter->didBlockData());
146     return m_blockingContentFilter->unblockRequestDeniedScript();
147 }
148
149 void ContentFilter::responseReceived(CachedResource* resource, const ResourceResponse& response)
150 {
151     ASSERT(resource);
152     ASSERT(resource == m_mainResource);
153     ASSERT(m_state != State::Initialized);
154     LOG(ContentFiltering, "ContentFilter received response from <%s>.\n", response.url().string().ascii().data());
155
156     if (m_state == State::Filtering) {
157         forEachContentFilterUntilBlocked([&response](PlatformContentFilter& contentFilter) {
158             contentFilter.responseReceived(response);
159         });
160     }
161
162     if (m_state != State::Blocked)
163         m_documentLoader.responseReceived(resource, response);
164 }
165
166 void ContentFilter::dataReceived(CachedResource* resource, const char* data, int length)
167 {
168     ASSERT(resource);
169     ASSERT(resource == m_mainResource);
170     ASSERT(m_state != State::Initialized);
171     LOG(ContentFiltering, "ContentFilter received %d bytes of data from <%s>.\n", length, resource->url().string().ascii().data());
172
173     if (m_state == State::Filtering) {
174         forEachContentFilterUntilBlocked([data, length](PlatformContentFilter& contentFilter) {
175             contentFilter.addData(data, length);
176         });
177
178         if (m_state == State::Allowed)
179             deliverResourceData(*resource);
180         return;
181     }
182
183     if (m_state == State::Allowed)
184         m_documentLoader.dataReceived(resource, data, length);
185 }
186
187 void ContentFilter::redirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
188 {
189     ASSERT(resource);
190     ASSERT(resource == m_mainResource);
191     ASSERT(m_state != State::Initialized);
192
193     if (m_state == State::Filtering)
194         willSendRequest(request, redirectResponse);
195
196     if (m_state != State::Blocked)
197         m_documentLoader.redirectReceived(resource, request, redirectResponse);
198 }
199
200 void ContentFilter::notifyFinished(CachedResource* resource)
201 {
202     ASSERT(resource);
203     ASSERT(resource == m_mainResource);
204     ASSERT(m_state != State::Initialized);
205     LOG(ContentFiltering, "ContentFilter will finish filtering main resource at <%s>.\n", resource->url().string().ascii().data());
206
207     if (resource->errorOccurred()) {
208         m_documentLoader.notifyFinished(resource);
209         return;
210     }
211
212     if (m_state == State::Filtering) {
213         forEachContentFilterUntilBlocked([](PlatformContentFilter& contentFilter) {
214             contentFilter.finishedAddingData();
215         });
216
217         if (m_state != State::Blocked)
218             deliverResourceData(*resource);
219     }
220
221     if (m_state != State::Blocked)
222         m_documentLoader.notifyFinished(resource);
223 }
224
225 void ContentFilter::forEachContentFilterUntilBlocked(std::function<void(PlatformContentFilter&)> function)
226 {
227     bool allFiltersAllowedLoad { true };
228     for (auto& contentFilter : m_contentFilters) {
229         if (!contentFilter->needsMoreData()) {
230             ASSERT(!contentFilter->didBlockData());
231             continue;
232         }
233
234         function(*contentFilter);
235
236         if (contentFilter->didBlockData()) {
237             ASSERT(!m_blockingContentFilter);
238             m_blockingContentFilter = contentFilter.get();
239             didDecide(State::Blocked);
240             return;
241         } else if (contentFilter->needsMoreData())
242             allFiltersAllowedLoad = false;
243     }
244
245     if (allFiltersAllowedLoad)
246         didDecide(State::Allowed);
247 }
248
249 void ContentFilter::didDecide(State state)
250 {
251     ASSERT(m_state != State::Allowed);
252     ASSERT(m_state != State::Blocked);
253     ASSERT(state == State::Allowed || state == State::Blocked);
254     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() : "");
255     m_state = state;
256     m_documentLoader.contentFilterDidDecide();
257 }
258
259 void ContentFilter::deliverResourceData(CachedResource& resource)
260 {
261     ASSERT(resource.dataBufferingPolicy() == BufferData);
262     if (auto* resourceBuffer = resource.resourceBuffer())
263         m_documentLoader.dataReceived(&resource, resourceBuffer->data(), resourceBuffer->size());
264 }
265
266 } // namespace WebCore
267
268 #endif // ENABLE(CONTENT_FILTERING)