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