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 "DFABytecodeInterpreter.h"
35 #include "DocumentLoader.h"
37 #include "MainFrame.h"
38 #include "ResourceLoadInfo.h"
40 #include "UserContentController.h"
41 #include <wtf/NeverDestroyed.h>
42 #include <wtf/text/CString.h>
46 namespace ContentExtensions {
48 void ContentExtensionsBackend::addContentExtension(const String& identifier, RefPtr<CompiledContentExtension> compiledContentExtension)
50 ASSERT(!identifier.isEmpty());
51 if (identifier.isEmpty())
54 if (!compiledContentExtension) {
55 removeContentExtension(identifier);
59 RefPtr<ContentExtension> extension = ContentExtension::create(identifier, adoptRef(*compiledContentExtension.leakRef()));
60 m_contentExtensions.set(identifier, WTF::move(extension));
63 void ContentExtensionsBackend::removeContentExtension(const String& identifier)
65 m_contentExtensions.remove(identifier);
68 void ContentExtensionsBackend::removeAllContentExtensions()
70 m_contentExtensions.clear();
73 Vector<Action> ContentExtensionsBackend::actionsForResourceLoad(const ResourceLoadInfo& resourceLoadInfo) const
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();
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);
87 const SerializedActionByte* actions = compiledExtension.actions();
88 const unsigned actionsLength = compiledExtension.actionsLength();
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());
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;
105 finalActions.append(action);
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));
113 // CSS selectors were already compiled into a stylesheet using globalDisplayNoneSelectors.
114 if (action.type() != ActionType::CSSDisplayNoneSelector)
115 finalActions.append(action);
117 finalActions.append(Action(ActionType::CSSDisplayNoneStyleSheet, contentExtension->identifier()));
123 StyleSheetContents* ContentExtensionsBackend::globalDisplayNoneStyleSheet(const String& identifier) const
125 const auto& contentExtension = m_contentExtensions.get(identifier);
126 return contentExtension ? contentExtension->globalDisplayNoneStyleSheet() : nullptr;
129 void ContentExtensionsBackend::processContentExtensionRulesForLoad(ResourceRequest& request, ResourceType resourceType, DocumentLoader& initiatingDocumentLoader)
131 Document* currentDocument = nullptr;
134 if (initiatingDocumentLoader.frame()) {
135 currentDocument = initiatingDocumentLoader.frame()->document();
137 if (Document* mainDocument = initiatingDocumentLoader.frame()->mainFrame().document())
138 mainDocumentURL = mainDocument->url();
141 ResourceLoadInfo resourceLoadInfo = { request.url(), mainDocumentURL, resourceType };
142 Vector<ContentExtensions::Action> actions = actionsForResourceLoad(resourceLoadInfo);
145 bool willBlockLoad = false;
146 for (const auto& action : actions) {
147 switch (action.type()) {
148 case ContentExtensions::ActionType::BlockLoad:
149 willBlockLoad = true;
151 case ContentExtensions::ActionType::BlockCookies:
152 request.setAllowCookies(false);
154 case ContentExtensions::ActionType::CSSDisplayNoneSelector:
155 css.append(action.stringArgument());
156 css.append(displayNoneCSSRule());
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);
168 case ContentExtensions::ActionType::IgnorePreviousRules:
169 case ContentExtensions::ActionType::InvalidAction:
170 RELEASE_ASSERT_NOT_REACHED();
175 Ref<StyleSheetContents> styleSheet = StyleSheetContents::create();
176 styleSheet->setIsUserStyleSheet(true);
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));
187 request = ResourceRequest();
190 const String& ContentExtensionsBackend::displayNoneCSSRule()
192 static NeverDestroyed<const String> rule(ASCIILiteral("{display:none !important;}\n"));
196 } // namespace ContentExtensions
198 } // namespace WebCore
200 #endif // ENABLE(CONTENT_EXTENSIONS)