ad16740e528e74c018b3516f326e2c07decbd9eb
[WebKit-https.git] / Source / WebCore / contentextensions / ContentExtensionsBackend.cpp
1 /*
2  * Copyright (C) 2014 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 "CompiledContentExtension.h"
32 #include "ContentExtension.h"
33 #include "DFABytecodeInterpreter.h"
34 #include "Document.h"
35 #include "DocumentLoader.h"
36 #include "Frame.h"
37 #include "MainFrame.h"
38 #include "ResourceLoadInfo.h"
39 #include "URL.h"
40 #include "UserContentController.h"
41 #include <wtf/NeverDestroyed.h>
42 #include <wtf/text/CString.h>
43
44 namespace WebCore {
45
46 namespace ContentExtensions {
47     
48 void ContentExtensionsBackend::addContentExtension(const String& identifier, RefPtr<CompiledContentExtension> compiledContentExtension)
49 {
50     ASSERT(!identifier.isEmpty());
51     if (identifier.isEmpty())
52         return;
53
54     if (!compiledContentExtension) {
55         removeContentExtension(identifier);
56         return;
57     }
58
59     RefPtr<ContentExtension> extension = ContentExtension::create(identifier, adoptRef(*compiledContentExtension.leakRef()));
60     m_contentExtensions.set(identifier, WTF::move(extension));
61 }
62
63 void ContentExtensionsBackend::removeContentExtension(const String& identifier)
64 {
65     m_contentExtensions.remove(identifier);
66 }
67
68 void ContentExtensionsBackend::removeAllContentExtensions()
69 {
70     m_contentExtensions.clear();
71 }
72
73 Vector<Action> ContentExtensionsBackend::actionsForResourceLoad(const ResourceLoadInfo& resourceLoadInfo) const
74 {
75     const String& urlString = resourceLoadInfo.resourceURL.string();
76     ASSERT_WITH_MESSAGE(urlString.containsOnlyASCII(), "A decoded URL should only contain ASCII characters. The matching algorithm assumes the input is ASCII.");
77     const CString& urlCString = urlString.utf8();
78
79     Vector<Action> finalActions;
80     ResourceFlags flags = resourceLoadInfo.getResourceFlags();
81     for (auto& contentExtension : m_contentExtensions.values()) {
82         RELEASE_ASSERT(contentExtension);
83         const CompiledContentExtension& compiledExtension = contentExtension->compiledExtension();
84         DFABytecodeInterpreter interpreter(compiledExtension.bytecode(), compiledExtension.bytecodeLength(), contentExtension->m_pagesUsed);
85         DFABytecodeInterpreter::Actions triggeredActions = interpreter.interpret(urlCString, flags);
86         
87         const SerializedActionByte* actions = compiledExtension.actions();
88         const unsigned actionsLength = compiledExtension.actionsLength();
89         
90         bool sawIgnorePreviousRules = false;
91         if (!triggeredActions.isEmpty()) {
92             Vector<unsigned> actionLocations;
93             actionLocations.reserveInitialCapacity(triggeredActions.size());
94             for (auto actionLocation : triggeredActions)
95                 actionLocations.append(static_cast<unsigned>(actionLocation));
96             std::sort(actionLocations.begin(), actionLocations.end());
97
98             // Add actions in reverse order to properly deal with IgnorePreviousRules.
99             for (unsigned i = actionLocations.size(); i; i--) {
100                 Action action = Action::deserialize(actions, actionsLength, actionLocations[i - 1]);
101                 if (action.type() == ActionType::IgnorePreviousRules) {
102                     sawIgnorePreviousRules = true;
103                     break;
104                 }
105                 finalActions.append(action);
106             }
107         }
108         if (!sawIgnorePreviousRules) {
109             DFABytecodeInterpreter::Actions universalActions = interpreter.actionsFromDFARoot();
110             for (auto actionLocation : universalActions) {
111                 Action action = Action::deserialize(actions, actionsLength, static_cast<unsigned>(actionLocation));
112                 
113                 // CSS selectors were already compiled into a stylesheet using globalDisplayNoneSelectors.
114                 if (action.type() != ActionType::CSSDisplayNoneSelector)
115                     finalActions.append(action);
116             }
117             finalActions.append(Action(ActionType::CSSDisplayNoneStyleSheet, contentExtension->identifier()));
118         }
119     }
120     return finalActions;
121 }
122
123 StyleSheetContents* ContentExtensionsBackend::globalDisplayNoneStyleSheet(const String& identifier) const
124 {
125     const auto& contentExtension = m_contentExtensions.get(identifier);
126     return contentExtension ? contentExtension->globalDisplayNoneStyleSheet() : nullptr;
127 }
128
129 void ContentExtensionsBackend::processContentExtensionRulesForLoad(ResourceRequest& request, ResourceType resourceType, DocumentLoader& initiatingDocumentLoader)
130 {
131     Document* currentDocument = nullptr;
132     URL mainDocumentURL;
133
134     if (initiatingDocumentLoader.frame()) {
135         currentDocument = initiatingDocumentLoader.frame()->document();
136
137         if (Document* mainDocument = initiatingDocumentLoader.frame()->mainFrame().document())
138             mainDocumentURL = mainDocument->url();
139     }
140
141     ResourceLoadInfo resourceLoadInfo = { request.url(), mainDocumentURL, resourceType };
142     Vector<ContentExtensions::Action> actions = actionsForResourceLoad(resourceLoadInfo);
143
144     StringBuilder css;
145     bool willBlockLoad = false;
146     for (const auto& action : actions) {
147         switch (action.type()) {
148         case ContentExtensions::ActionType::BlockLoad:
149             willBlockLoad = true;
150             break;
151         case ContentExtensions::ActionType::BlockCookies:
152             request.setAllowCookies(false);
153             break;
154         case ContentExtensions::ActionType::CSSDisplayNoneSelector:
155             css.append(action.stringArgument());
156             css.append(displayNoneCSSRule());
157             break;
158         case ContentExtensions::ActionType::CSSDisplayNoneStyleSheet: {
159             StyleSheetContents* styleSheetContents = globalDisplayNoneStyleSheet(action.stringArgument());
160             if (styleSheetContents) {
161                 if (resourceType == ResourceType::Document)
162                     initiatingDocumentLoader.addPendingContentExtensionSheet(action.stringArgument(), *styleSheetContents);
163                 else if (currentDocument)
164                     currentDocument->styleSheetCollection().maybeAddContentExtensionSheet(action.stringArgument(), *styleSheetContents);
165             }
166             break;
167         }
168         case ContentExtensions::ActionType::IgnorePreviousRules:
169         case ContentExtensions::ActionType::InvalidAction:
170             RELEASE_ASSERT_NOT_REACHED();
171         }
172     }
173
174     if (css.length()) {
175         Ref<StyleSheetContents> styleSheet = StyleSheetContents::create();
176         styleSheet->setIsUserStyleSheet(true);
177
178         if (styleSheet->parseString(css.toString())) {
179             if (resourceType == ResourceType::Document)
180                 initiatingDocumentLoader.addPendingContentExtensionSheet(styleSheet);
181             else if (currentDocument)
182                 currentDocument->styleSheetCollection().addUserSheet(WTF::move(styleSheet));
183         }
184     }
185
186     if (willBlockLoad)
187         request = ResourceRequest();
188 }
189
190 const String& ContentExtensionsBackend::displayNoneCSSRule()
191 {
192     static NeverDestroyed<const String> rule(ASCIILiteral("{display:none !important;}\n"));
193     return rule;
194 }
195
196 } // namespace ContentExtensions
197
198 } // namespace WebCore
199
200 #endif // ENABLE(CONTENT_EXTENSIONS)