imported/w3c/web-platform-tests/url/failure.html crashes on debug builds
[WebKit-https.git] / Source / WebCore / contentextensions / ContentExtensionsBackend.cpp
1 /*
2  * Copyright (C) 2014-2017 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 "ContentExtensionsBackend.h"
28
29 #if ENABLE(CONTENT_EXTENSIONS)
30
31 #include "Chrome.h"
32 #include "ChromeClient.h"
33 #include "CompiledContentExtension.h"
34 #include "ContentExtension.h"
35 #include "ContentExtensionsDebugging.h"
36 #include "DFABytecodeInterpreter.h"
37 #include "Document.h"
38 #include "DocumentLoader.h"
39 #include "ExtensionStyleSheets.h"
40 #include "Frame.h"
41 #include "FrameLoaderClient.h"
42 #include "MainFrame.h"
43 #include "Page.h"
44 #include "ResourceLoadInfo.h"
45 #include "URL.h"
46 #include "UserContentController.h"
47 #include <wtf/NeverDestroyed.h>
48 #include <wtf/text/CString.h>
49
50 namespace WebCore {
51
52 namespace ContentExtensions {
53     
54 void ContentExtensionsBackend::addContentExtension(const String& identifier, Ref<CompiledContentExtension> compiledContentExtension)
55 {
56     ASSERT(!identifier.isEmpty());
57     if (identifier.isEmpty())
58         return;
59     
60     auto contentExtension = ContentExtension::create(identifier, WTFMove(compiledContentExtension));
61     m_contentExtensions.set(identifier, WTFMove(contentExtension));
62 }
63
64 void ContentExtensionsBackend::removeContentExtension(const String& identifier)
65 {
66     m_contentExtensions.remove(identifier);
67 }
68
69 void ContentExtensionsBackend::removeAllContentExtensions()
70 {
71     m_contentExtensions.clear();
72 }
73
74 std::pair<Vector<Action>, Vector<String>> ContentExtensionsBackend::actionsForResourceLoad(const ResourceLoadInfo& resourceLoadInfo) const
75 {
76 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
77     double addedTimeStart = monotonicallyIncreasingTime();
78 #endif
79     if (m_contentExtensions.isEmpty()
80         || !resourceLoadInfo.resourceURL.isValid()
81         || resourceLoadInfo.resourceURL.protocolIsData())
82         return { };
83
84     const String& urlString = resourceLoadInfo.resourceURL.string();
85     ASSERT_WITH_MESSAGE(urlString.isAllASCII(), "A decoded URL should only contain ASCII characters. The matching algorithm assumes the input is ASCII.");
86     const auto urlCString = urlString.utf8();
87
88     Vector<Action> finalActions;
89     Vector<String> stylesheetIdentifiers;
90     ResourceFlags flags = resourceLoadInfo.getResourceFlags();
91     for (auto& contentExtension : m_contentExtensions.values()) {
92         const CompiledContentExtension& compiledExtension = contentExtension->compiledExtension();
93         
94         DFABytecodeInterpreter withoutConditionsInterpreter(compiledExtension.filtersWithoutConditionsBytecode(), compiledExtension.filtersWithoutConditionsBytecodeLength());
95         DFABytecodeInterpreter::Actions withoutConditionsActions = withoutConditionsInterpreter.interpret(urlCString, flags);
96         
97         URL topURL = resourceLoadInfo.mainDocumentURL;
98         DFABytecodeInterpreter withConditionsInterpreter(compiledExtension.filtersWithConditionsBytecode(), compiledExtension.filtersWithConditionsBytecodeLength());
99         DFABytecodeInterpreter::Actions withConditionsActions = withConditionsInterpreter.interpretWithConditions(urlCString, flags, contentExtension->topURLActions(topURL));
100         
101         const SerializedActionByte* actions = compiledExtension.actions();
102         const unsigned actionsLength = compiledExtension.actionsLength();
103         
104         bool sawIgnorePreviousRules = false;
105         const Vector<uint32_t>& universalWithConditions = contentExtension->universalActionsWithConditions(topURL);
106         const Vector<uint32_t>& universalWithoutConditions = contentExtension->universalActionsWithoutConditions();
107         if (!withoutConditionsActions.isEmpty() || !withConditionsActions.isEmpty() || !universalWithConditions.isEmpty() || !universalWithoutConditions.isEmpty()) {
108             Vector<uint32_t> actionLocations;
109             actionLocations.reserveInitialCapacity(withoutConditionsActions.size() + withConditionsActions.size() + universalWithoutConditions.size() + universalWithConditions.size());
110             for (uint64_t actionLocation : withoutConditionsActions)
111                 actionLocations.uncheckedAppend(static_cast<uint32_t>(actionLocation));
112             for (uint64_t actionLocation : withConditionsActions)
113                 actionLocations.uncheckedAppend(static_cast<uint32_t>(actionLocation));
114             for (uint32_t actionLocation : universalWithoutConditions)
115                 actionLocations.uncheckedAppend(actionLocation);
116             for (uint32_t actionLocation : universalWithConditions)
117                 actionLocations.uncheckedAppend(actionLocation);
118             std::sort(actionLocations.begin(), actionLocations.end());
119
120             // Add actions in reverse order to properly deal with IgnorePreviousRules.
121             for (unsigned i = actionLocations.size(); i; i--) {
122                 Action action = Action::deserialize(actions, actionsLength, actionLocations[i - 1]);
123                 action.setExtensionIdentifier(contentExtension->identifier());
124                 if (action.type() == ActionType::IgnorePreviousRules) {
125                     sawIgnorePreviousRules = true;
126                     break;
127                 }
128                 finalActions.append(action);
129             }
130         }
131         if (!sawIgnorePreviousRules)
132             stylesheetIdentifiers.append(contentExtension->identifier());
133     }
134 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
135     double addedTimeEnd = monotonicallyIncreasingTime();
136     dataLogF("Time added: %f microseconds %s \n", (addedTimeEnd - addedTimeStart) * 1.0e6, resourceLoadInfo.resourceURL.string().utf8().data());
137 #endif
138     return { WTFMove(finalActions), WTFMove(stylesheetIdentifiers) };
139 }
140
141 void ContentExtensionsBackend::forEach(const WTF::Function<void(const String&, ContentExtension&)>& apply)
142 {
143     for (auto& pair : m_contentExtensions)
144         apply(pair.key, pair.value);
145 }
146
147 StyleSheetContents* ContentExtensionsBackend::globalDisplayNoneStyleSheet(const String& identifier) const
148 {
149     const auto& contentExtension = m_contentExtensions.get(identifier);
150     return contentExtension ? contentExtension->globalDisplayNoneStyleSheet() : nullptr;
151 }
152
153 BlockedStatus ContentExtensionsBackend::processContentExtensionRulesForLoad(const URL& url, ResourceType resourceType, DocumentLoader& initiatingDocumentLoader)
154 {
155     if (m_contentExtensions.isEmpty())
156         return { };
157
158     Document* currentDocument = nullptr;
159     URL mainDocumentURL;
160
161     if (Frame* frame = initiatingDocumentLoader.frame()) {
162         currentDocument = frame->document();
163
164         if (initiatingDocumentLoader.isLoadingMainResource()
165             && frame->isMainFrame()
166             && resourceType == ResourceType::Document)
167             mainDocumentURL = url;
168         else if (Document* mainDocument = frame->mainFrame().document())
169             mainDocumentURL = mainDocument->url();
170     }
171
172     ResourceLoadInfo resourceLoadInfo = { url, mainDocumentURL, resourceType };
173     auto actions = actionsForResourceLoad(resourceLoadInfo);
174
175     bool willBlockLoad = false;
176     bool willBlockCookies = false;
177     bool willMakeHTTPS = false;
178     HashSet<std::pair<String, String>> notifications;
179     for (const auto& action : actions.first) {
180         switch (action.type()) {
181         case ContentExtensions::ActionType::BlockLoad:
182             willBlockLoad = true;
183             break;
184         case ContentExtensions::ActionType::BlockCookies:
185             willBlockCookies = true;
186             break;
187         case ContentExtensions::ActionType::CSSDisplayNoneSelector:
188             if (resourceType == ResourceType::Document)
189                 initiatingDocumentLoader.addPendingContentExtensionDisplayNoneSelector(action.extensionIdentifier(), action.stringArgument(), action.actionID());
190             else if (currentDocument)
191                 currentDocument->extensionStyleSheets().addDisplayNoneSelector(action.extensionIdentifier(), action.stringArgument(), action.actionID());
192             break;
193         case ContentExtensions::ActionType::Notify:
194             notifications.add(std::make_pair(action.extensionIdentifier(), action.stringArgument()));
195             break;
196         case ContentExtensions::ActionType::MakeHTTPS: {
197             if ((url.protocolIs("http") || url.protocolIs("ws"))
198                 && (!url.port() || isDefaultPortForProtocol(url.port().value(), url.protocol())))
199                 willMakeHTTPS = true;
200             break;
201         }
202         case ContentExtensions::ActionType::IgnorePreviousRules:
203             RELEASE_ASSERT_NOT_REACHED();
204         }
205     }
206
207     for (const auto& identifier : actions.second) {
208         if (auto* styleSheetContents = globalDisplayNoneStyleSheet(identifier)) {
209             if (resourceType == ResourceType::Document)
210                 initiatingDocumentLoader.addPendingContentExtensionSheet(identifier, *styleSheetContents);
211             else if (currentDocument)
212                 currentDocument->extensionStyleSheets().maybeAddContentExtensionSheet(identifier, *styleSheetContents);
213         }
214     }
215
216     if (currentDocument) {
217         if (willMakeHTTPS) {
218             ASSERT(url.protocolIs("http") || url.protocolIs("ws"));
219             String newProtocol = url.protocolIs("http") ? ASCIILiteral("https") : ASCIILiteral("wss");
220             currentDocument->addConsoleMessage(MessageSource::ContentBlocker, MessageLevel::Info, makeString("Content blocker promoted URL from ", url.string(), " to ", newProtocol));
221         }
222         if (willBlockLoad)
223             currentDocument->addConsoleMessage(MessageSource::ContentBlocker, MessageLevel::Info, makeString("Content blocker prevented frame displaying ", mainDocumentURL.string(), " from loading a resource from ", url.string()));
224     }
225     return { willBlockLoad, willBlockCookies, willMakeHTTPS, WTFMove(notifications) };
226 }
227
228 BlockedStatus ContentExtensionsBackend::processContentExtensionRulesForPingLoad(const URL& url, const URL& mainDocumentURL)
229 {
230     if (m_contentExtensions.isEmpty())
231         return { };
232
233     ResourceLoadInfo resourceLoadInfo = { url, mainDocumentURL, ResourceType::Raw };
234     auto actions = actionsForResourceLoad(resourceLoadInfo);
235
236     bool willBlockLoad = false;
237     bool willBlockCookies = false;
238     bool willMakeHTTPS = false;
239     for (const auto& action : actions.first) {
240         switch (action.type()) {
241         case ContentExtensions::ActionType::BlockLoad:
242             willBlockLoad = true;
243             break;
244         case ContentExtensions::ActionType::BlockCookies:
245             willBlockCookies = true;
246             break;
247         case ContentExtensions::ActionType::MakeHTTPS:
248             if ((url.protocolIs("http") || url.protocolIs("ws")) && (!url.port() || isDefaultPortForProtocol(url.port().value(), url.protocol())))
249                 willMakeHTTPS = true;
250             break;
251         case ContentExtensions::ActionType::CSSDisplayNoneSelector:
252         case ContentExtensions::ActionType::Notify:
253             break;
254         case ContentExtensions::ActionType::IgnorePreviousRules:
255             RELEASE_ASSERT_NOT_REACHED();
256         }
257     }
258
259     return { willBlockLoad, willBlockCookies, willMakeHTTPS, { } };
260 }
261
262 const String& ContentExtensionsBackend::displayNoneCSSRule()
263 {
264     static NeverDestroyed<const String> rule(MAKE_STATIC_STRING_IMPL("display:none !important;"));
265     return rule;
266 }
267
268 void applyBlockedStatusToRequest(const BlockedStatus& status, Page* page, ResourceRequest& request)
269 {
270     if (page && !status.notifications.isEmpty())
271         page->chrome().client().contentRuleListNotification(request.url(), status.notifications);
272
273     if (status.blockedCookies)
274         request.setAllowCookies(false);
275
276     if (status.madeHTTPS) {
277         const URL& originalURL = request.url();
278         ASSERT(originalURL.protocolIs("http"));
279         ASSERT(!originalURL.port() || isDefaultPortForProtocol(originalURL.port().value(), originalURL.protocol()));
280
281         URL newURL = originalURL;
282         newURL.setProtocol("https");
283         if (originalURL.port())
284             newURL.setPort(defaultPortForProtocol("https").value());
285         request.setURL(newURL);
286     }
287 }
288     
289 } // namespace ContentExtensions
290
291 } // namespace WebCore
292
293 #endif // ENABLE(CONTENT_EXTENSIONS)