2 * Copyright (C) 2014 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
27 #include "ContentExtensionsBackend.h"
29 #if ENABLE(CONTENT_EXTENSIONS)
31 #include "CompiledContentExtension.h"
32 #include "ContentExtension.h"
33 #include "ContentExtensionsDebugging.h"
34 #include "DFABytecodeInterpreter.h"
36 #include "DocumentLoader.h"
38 #include "MainFrame.h"
39 #include "ResourceLoadInfo.h"
41 #include "UserContentController.h"
42 #include <wtf/NeverDestroyed.h>
43 #include <wtf/text/CString.h>
47 namespace ContentExtensions {
49 void ContentExtensionsBackend::addContentExtension(const String& identifier, RefPtr<CompiledContentExtension> compiledContentExtension)
51 ASSERT(!identifier.isEmpty());
52 if (identifier.isEmpty())
55 if (!compiledContentExtension) {
56 removeContentExtension(identifier);
60 RefPtr<ContentExtension> extension = ContentExtension::create(identifier, adoptRef(*compiledContentExtension.leakRef()));
61 m_contentExtensions.set(identifier, WTF::move(extension));
64 void ContentExtensionsBackend::removeContentExtension(const String& identifier)
66 m_contentExtensions.remove(identifier);
69 void ContentExtensionsBackend::removeAllContentExtensions()
71 m_contentExtensions.clear();
74 Vector<Action> ContentExtensionsBackend::actionsForResourceLoad(const ResourceLoadInfo& resourceLoadInfo) const
76 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
77 double addedTimeStart = monotonicallyIncreasingTime();
79 if (resourceLoadInfo.resourceURL.protocolIsData())
80 return Vector<Action>();
82 const String& urlString = resourceLoadInfo.resourceURL.string();
83 ASSERT_WITH_MESSAGE(urlString.containsOnlyASCII(), "A decoded URL should only contain ASCII characters. The matching algorithm assumes the input is ASCII.");
84 const CString& urlCString = urlString.utf8();
86 Vector<Action> finalActions;
87 ResourceFlags flags = resourceLoadInfo.getResourceFlags();
88 for (auto& contentExtension : m_contentExtensions.values()) {
89 RELEASE_ASSERT(contentExtension);
90 const CompiledContentExtension& compiledExtension = contentExtension->compiledExtension();
91 DFABytecodeInterpreter interpreter(compiledExtension.bytecode(), compiledExtension.bytecodeLength(), contentExtension->m_pagesUsed);
92 DFABytecodeInterpreter::Actions triggeredActions = interpreter.interpret(urlCString, flags);
94 const SerializedActionByte* actions = compiledExtension.actions();
95 const unsigned actionsLength = compiledExtension.actionsLength();
97 bool sawIgnorePreviousRules = false;
98 if (!triggeredActions.isEmpty()) {
99 Vector<unsigned> actionLocations;
100 actionLocations.reserveInitialCapacity(triggeredActions.size());
101 for (auto actionLocation : triggeredActions)
102 actionLocations.append(static_cast<unsigned>(actionLocation));
103 std::sort(actionLocations.begin(), actionLocations.end());
105 // Add actions in reverse order to properly deal with IgnorePreviousRules.
106 for (unsigned i = actionLocations.size(); i; i--) {
107 Action action = Action::deserialize(actions, actionsLength, actionLocations[i - 1]);
108 if (action.type() == ActionType::IgnorePreviousRules) {
109 sawIgnorePreviousRules = true;
112 finalActions.append(action);
115 if (!sawIgnorePreviousRules) {
116 DFABytecodeInterpreter::Actions universalActions = interpreter.actionsFromDFARoot();
117 for (auto actionLocation : universalActions) {
118 Action action = Action::deserialize(actions, actionsLength, static_cast<unsigned>(actionLocation));
120 // CSS selectors were already compiled into a stylesheet using globalDisplayNoneSelectors.
121 if (action.type() != ActionType::CSSDisplayNoneSelector)
122 finalActions.append(action);
124 finalActions.append(Action(ActionType::CSSDisplayNoneStyleSheet, contentExtension->identifier()));
127 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
128 double addedTimeEnd = monotonicallyIncreasingTime();
129 WTFLogAlways("Time added: %f microseconds %s", (addedTimeEnd - addedTimeStart) * 1.0e6, resourceLoadInfo.resourceURL.string().utf8().data());
134 StyleSheetContents* ContentExtensionsBackend::globalDisplayNoneStyleSheet(const String& identifier) const
136 const auto& contentExtension = m_contentExtensions.get(identifier);
137 return contentExtension ? contentExtension->globalDisplayNoneStyleSheet() : nullptr;
140 void ContentExtensionsBackend::processContentExtensionRulesForLoad(ResourceRequest& request, ResourceType resourceType, DocumentLoader& initiatingDocumentLoader)
142 Document* currentDocument = nullptr;
145 if (initiatingDocumentLoader.frame()) {
146 currentDocument = initiatingDocumentLoader.frame()->document();
148 if (Document* mainDocument = initiatingDocumentLoader.frame()->mainFrame().document())
149 mainDocumentURL = mainDocument->url();
152 ResourceLoadInfo resourceLoadInfo = { request.url(), mainDocumentURL, resourceType };
153 Vector<ContentExtensions::Action> actions = actionsForResourceLoad(resourceLoadInfo);
156 bool willBlockLoad = false;
157 for (const auto& action : actions) {
158 switch (action.type()) {
159 case ContentExtensions::ActionType::BlockLoad:
160 willBlockLoad = true;
162 case ContentExtensions::ActionType::BlockCookies:
163 request.setAllowCookies(false);
165 case ContentExtensions::ActionType::CSSDisplayNoneSelector:
166 css.append(action.stringArgument());
167 css.append(displayNoneCSSRule());
169 case ContentExtensions::ActionType::CSSDisplayNoneStyleSheet: {
170 StyleSheetContents* styleSheetContents = globalDisplayNoneStyleSheet(action.stringArgument());
171 if (styleSheetContents) {
172 if (resourceType == ResourceType::Document)
173 initiatingDocumentLoader.addPendingContentExtensionSheet(action.stringArgument(), *styleSheetContents);
174 else if (currentDocument)
175 currentDocument->styleSheetCollection().maybeAddContentExtensionSheet(action.stringArgument(), *styleSheetContents);
179 case ContentExtensions::ActionType::IgnorePreviousRules:
180 case ContentExtensions::ActionType::InvalidAction:
181 RELEASE_ASSERT_NOT_REACHED();
186 Ref<StyleSheetContents> styleSheet = StyleSheetContents::create();
187 styleSheet->setIsUserStyleSheet(true);
189 if (styleSheet->parseString(css.toString())) {
190 if (resourceType == ResourceType::Document)
191 initiatingDocumentLoader.addPendingContentExtensionSheet(styleSheet);
192 else if (currentDocument)
193 currentDocument->styleSheetCollection().addUserSheet(WTF::move(styleSheet));
198 request = ResourceRequest();
201 const String& ContentExtensionsBackend::displayNoneCSSRule()
203 static NeverDestroyed<const String> rule(ASCIILiteral("{display:none !important;}\n"));
207 } // namespace ContentExtensions
209 } // namespace WebCore
211 #endif // ENABLE(CONTENT_EXTENSIONS)