32a873e0d4017aa3f4eaeb487b127749d28d4e0e
[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 "Logging.h"
34 #include "NetworkExtensionContentFilter.h"
35 #include "ParentalControlsContentFilter.h"
36 #include "SharedBuffer.h"
37 #include <wtf/NeverDestroyed.h>
38 #include <wtf/Vector.h>
39
40 #if !LOG_DISABLED
41 #include <wtf/text/CString.h>
42 #endif
43
44 namespace WebCore {
45
46 Vector<ContentFilter::Type>& ContentFilter::types()
47 {
48     static NeverDestroyed<Vector<ContentFilter::Type>> types {
49         Vector<ContentFilter::Type> {
50 #if HAVE(PARENTAL_CONTROLS)
51             type<ParentalControlsContentFilter>(),
52 #endif
53 #if HAVE(NETWORK_EXTENSION)
54             type<NetworkExtensionContentFilter>()
55 #endif
56         }
57     };
58     return types;
59 }
60
61 std::unique_ptr<ContentFilter> ContentFilter::createIfNeeded(DecisionFunction decisionFunction)
62 {
63     Container filters;
64     for (auto& type : types()) {
65         if (!type.enabled())
66             continue;
67
68         auto filter = type.create();
69         ASSERT(filter);
70         filters.append(WTF::move(filter));
71     }
72
73     if (filters.isEmpty())
74         return nullptr;
75
76     return std::make_unique<ContentFilter>(WTF::move(filters), WTF::move(decisionFunction));
77 }
78
79 ContentFilter::ContentFilter(Container contentFilters, DecisionFunction decisionFunction)
80     : m_contentFilters { WTF::move(contentFilters) }
81     , m_decisionFunction { WTF::move(decisionFunction) }
82 {
83     LOG(ContentFiltering, "Creating ContentFilter with %zu platform content filter(s).\n", m_contentFilters.size());
84     ASSERT(!m_contentFilters.isEmpty());
85 }
86
87 ContentFilter::~ContentFilter()
88 {
89     LOG(ContentFiltering, "Destroying ContentFilter.\n");
90     if (!m_mainResource)
91         return;
92     ASSERT(m_mainResource->hasClient(this));
93     m_mainResource->removeClient(this);
94 }
95
96 void ContentFilter::willSendRequest(ResourceRequest& request, const ResourceResponse& redirectResponse)
97 {
98     LOG(ContentFiltering, "ContentFilter received request for <%s> with redirect response from <%s>.\n", request.url().string().ascii().data(), redirectResponse.url().string().ascii().data());
99     ResourceRequest requestCopy { request };
100     ASSERT(m_state == State::Initialized || m_state == State::Filtering);
101     forEachContentFilterUntilBlocked([&requestCopy, &redirectResponse](PlatformContentFilter& contentFilter) {
102         contentFilter.willSendRequest(requestCopy, redirectResponse);
103         if (contentFilter.didBlockData())
104             requestCopy = ResourceRequest();
105     });
106 #if !LOG_DISABLED
107     if (request != requestCopy)
108         LOG(ContentFiltering, "ContentFilter changed request url to <%s>.\n", requestCopy.url().string().ascii().data());
109 #endif
110     request = requestCopy;
111 }
112
113 void ContentFilter::startFilteringMainResource(CachedRawResource& resource)
114 {
115     LOG(ContentFiltering, "ContentFilter will start filtering main resource at <%s>.\n", resource.url().string().ascii().data());
116     ASSERT(m_state == State::Initialized);
117     m_state = State::Filtering;
118     ASSERT(!m_mainResource);
119     m_mainResource = &resource;
120     ASSERT(!m_mainResource->hasClient(this));
121     m_mainResource->addClient(this);
122 }
123
124 ContentFilterUnblockHandler ContentFilter::unblockHandler() const
125 {
126     ASSERT(m_state == State::Blocked);
127     ASSERT(m_blockingContentFilter);
128     ASSERT(m_blockingContentFilter->didBlockData());
129     return m_blockingContentFilter->unblockHandler();
130 }
131
132 Ref<SharedBuffer> ContentFilter::replacementData() const
133 {
134     ASSERT(m_state == State::Blocked);
135     ASSERT(m_blockingContentFilter);
136     ASSERT(m_blockingContentFilter->didBlockData());
137     return m_blockingContentFilter->replacementData();
138 }
139
140 String ContentFilter::unblockRequestDeniedScript() const
141 {
142     ASSERT(m_state == State::Blocked);
143     ASSERT(m_blockingContentFilter);
144     ASSERT(m_blockingContentFilter->didBlockData());
145     return m_blockingContentFilter->unblockRequestDeniedScript();
146 }
147
148 void ContentFilter::responseReceived(CachedResource* resource, const ResourceResponse& response)
149 {
150     LOG(ContentFiltering, "ContentFilter received response from <%s>.\n", response.url().string().ascii().data());
151     ASSERT(m_state == State::Filtering);
152     ASSERT_UNUSED(resource, resource == m_mainResource.get());
153     forEachContentFilterUntilBlocked([&response](PlatformContentFilter& contentFilter) {
154         contentFilter.responseReceived(response);
155     });
156 }
157
158 void ContentFilter::dataReceived(CachedResource* resource, const char* data, int length)
159 {
160     LOG(ContentFiltering, "ContentFilter received %d bytes of data from <%s>.\n", length, resource->url().string().ascii().data());
161     ASSERT(m_state == State::Filtering);
162     ASSERT_UNUSED(resource, resource == m_mainResource.get());
163     forEachContentFilterUntilBlocked([data, length](PlatformContentFilter& contentFilter) {
164         contentFilter.addData(data, length);
165     });
166 }
167
168 void ContentFilter::redirectReceived(CachedResource* resource, ResourceRequest& request, const ResourceResponse& redirectResponse)
169 {
170     ASSERT(m_state == State::Filtering);
171     ASSERT_UNUSED(resource, resource == m_mainResource.get());
172     willSendRequest(request, redirectResponse);
173 }
174
175 void ContentFilter::notifyFinished(CachedResource* resource)
176 {
177     LOG(ContentFiltering, "ContentFilter will finish filtering main resource at <%s>.\n", resource->url().string().ascii().data());
178     ASSERT(m_state == State::Filtering);
179     ASSERT_UNUSED(resource, resource == m_mainResource.get());
180     forEachContentFilterUntilBlocked([](PlatformContentFilter& contentFilter) {
181         contentFilter.finishedAddingData();
182     });
183 }
184
185 void ContentFilter::forEachContentFilterUntilBlocked(std::function<void(PlatformContentFilter&)> function)
186 {
187     bool allFiltersAllowedLoad { true };
188     for (auto& contentFilter : m_contentFilters) {
189         if (!contentFilter->needsMoreData()) {
190             ASSERT(!contentFilter->didBlockData());
191             continue;
192         }
193
194         function(*contentFilter);
195
196         if (contentFilter->didBlockData()) {
197             ASSERT(!m_blockingContentFilter);
198             m_blockingContentFilter = contentFilter.get();
199             didDecide(State::Blocked);
200             return;
201         } else if (contentFilter->needsMoreData())
202             allFiltersAllowedLoad = false;
203     }
204
205     if (allFiltersAllowedLoad)
206         didDecide(State::Allowed);
207 }
208
209 void ContentFilter::didDecide(State state)
210 {
211     ASSERT(m_state != State::Allowed);
212     ASSERT(m_state != State::Blocked);
213     ASSERT(state == State::Allowed || state == State::Blocked);
214     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() : "");
215     m_state = state;
216
217     // Calling m_decisionFunction might delete |this|.
218     if (m_decisionFunction)
219         m_decisionFunction();
220 }
221
222 } // namespace WebCore
223
224 #endif // ENABLE(CONTENT_FILTERING)