[Content Filtering] Blocked page is not always displayed when it should be
[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 "NetworkExtensionContentFilter.h"
34 #include "ParentalControlsContentFilter.h"
35 #include "SharedBuffer.h"
36 #include <wtf/NeverDestroyed.h>
37 #include <wtf/Vector.h>
38
39 namespace WebCore {
40
41 Vector<ContentFilter::Type>& ContentFilter::types()
42 {
43     static NeverDestroyed<Vector<ContentFilter::Type>> types {
44         Vector<ContentFilter::Type> {
45             type<ParentalControlsContentFilter>(),
46 #if HAVE(NETWORK_EXTENSION)
47             type<NetworkExtensionContentFilter>()
48 #endif
49         }
50     };
51     return types;
52 }
53
54 std::unique_ptr<ContentFilter> ContentFilter::createIfNeeded(DecisionFunction decisionFunction)
55 {
56     Container filters;
57     for (auto& type : types()) {
58         if (!type.enabled())
59             continue;
60
61         auto filter = type.create();
62         ASSERT(filter);
63         filters.append(WTF::move(filter));
64     }
65
66     if (filters.isEmpty())
67         return nullptr;
68
69     return std::make_unique<ContentFilter>(WTF::move(filters), WTF::move(decisionFunction));
70 }
71
72 ContentFilter::ContentFilter(Container contentFilters, DecisionFunction decisionFunction)
73     : m_contentFilters { WTF::move(contentFilters) }
74     , m_decisionFunction { WTF::move(decisionFunction) }
75 {
76     ASSERT(!m_contentFilters.isEmpty());
77 }
78
79 ContentFilter::~ContentFilter()
80 {
81     if (!m_mainResource)
82         return;
83     ASSERT(m_mainResource->hasClient(this));
84     m_mainResource->removeClient(this);
85 }
86
87 void ContentFilter::startFilteringMainResource(CachedRawResource& resource)
88 {
89     ASSERT(m_state == State::Initialized);
90     m_state = State::Filtering;
91     ASSERT(!m_mainResource);
92     m_mainResource = &resource;
93     ASSERT(!m_mainResource->hasClient(this));
94     m_mainResource->addClient(this);
95 }
96
97 ContentFilterUnblockHandler ContentFilter::unblockHandler() const
98 {
99     ASSERT(m_state == State::Blocked);
100     ASSERT(m_blockingContentFilter);
101     ASSERT(m_blockingContentFilter->didBlockData());
102     return m_blockingContentFilter->unblockHandler();
103 }
104
105 Ref<SharedBuffer> ContentFilter::replacementData() const
106 {
107     ASSERT(m_state == State::Blocked);
108     ASSERT(m_blockingContentFilter);
109     ASSERT(m_blockingContentFilter->didBlockData());
110     return m_blockingContentFilter->replacementData();
111 }
112
113 String ContentFilter::unblockRequestDeniedScript() const
114 {
115     ASSERT(m_state == State::Blocked);
116     ASSERT(m_blockingContentFilter);
117     ASSERT(m_blockingContentFilter->didBlockData());
118     return m_blockingContentFilter->unblockRequestDeniedScript();
119 }
120
121 void ContentFilter::responseReceived(CachedResource* resource, const ResourceResponse& response)
122 {
123     ASSERT(m_state == State::Filtering);
124     ASSERT_UNUSED(resource, resource == m_mainResource.get());
125     forEachContentFilterUntilBlocked([&response](PlatformContentFilter& contentFilter) {
126         contentFilter.responseReceived(response);
127     });
128 }
129
130 void ContentFilter::dataReceived(CachedResource* resource, const char* data, int length)
131 {
132     ASSERT(m_state == State::Filtering);
133     ASSERT_UNUSED(resource, resource == m_mainResource.get());
134     forEachContentFilterUntilBlocked([data, length](PlatformContentFilter& contentFilter) {
135         contentFilter.addData(data, length);
136     });
137 }
138
139 void ContentFilter::notifyFinished(CachedResource* resource)
140 {
141     ASSERT(m_state == State::Filtering);
142     ASSERT_UNUSED(resource, resource == m_mainResource.get());
143     forEachContentFilterUntilBlocked([](PlatformContentFilter& contentFilter) {
144         contentFilter.finishedAddingData();
145     });
146 }
147
148 void ContentFilter::forEachContentFilterUntilBlocked(std::function<void(PlatformContentFilter&)> function)
149 {
150     bool allFiltersAllowedLoad { true };
151     for (auto& contentFilter : m_contentFilters) {
152         if (!contentFilter->needsMoreData()) {
153             ASSERT(!contentFilter->didBlockData());
154             continue;
155         }
156
157         function(*contentFilter);
158
159         if (contentFilter->didBlockData()) {
160             ASSERT(!m_blockingContentFilter);
161             m_blockingContentFilter = contentFilter.get();
162             didDecide(State::Blocked);
163             return;
164         } else if (contentFilter->needsMoreData())
165             allFiltersAllowedLoad = false;
166     }
167
168     if (allFiltersAllowedLoad)
169         didDecide(State::Allowed);
170 }
171
172 void ContentFilter::didDecide(State state)
173 {
174     ASSERT(m_state != State::Allowed);
175     ASSERT(m_state != State::Blocked);
176     ASSERT(state == State::Allowed || state == State::Blocked);
177     m_state = state;
178
179     // Calling m_decisionFunction might delete |this|.
180     if (m_decisionFunction)
181         m_decisionFunction();
182 }
183
184 } // namespace WebCore
185
186 #endif // ENABLE(CONTENT_FILTERING)