1 | /* |
2 | * Copyright (C) 2015 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 "ContentExtension.h" |
28 | |
29 | #include "CompiledContentExtension.h" |
30 | #include "ContentExtensionsBackend.h" |
31 | #include "StyleSheetContents.h" |
32 | #include <wtf/text/StringBuilder.h> |
33 | |
34 | #if ENABLE(CONTENT_EXTENSIONS) |
35 | |
36 | namespace WebCore { |
37 | namespace ContentExtensions { |
38 | |
39 | Ref<ContentExtension> ContentExtension::create(const String& identifier, Ref<CompiledContentExtension>&& compiledExtension, ShouldCompileCSS shouldCompileCSS) |
40 | { |
41 | return adoptRef(*new ContentExtension(identifier, WTFMove(compiledExtension), shouldCompileCSS)); |
42 | } |
43 | |
44 | ContentExtension::ContentExtension(const String& identifier, Ref<CompiledContentExtension>&& compiledExtension, ShouldCompileCSS shouldCompileCSS) |
45 | : m_identifier(identifier) |
46 | , m_compiledExtension(WTFMove(compiledExtension)) |
47 | { |
48 | DFABytecodeInterpreter withoutConditions(m_compiledExtension->filtersWithoutConditionsBytecode(), m_compiledExtension->filtersWithoutConditionsBytecodeLength()); |
49 | DFABytecodeInterpreter withConditions(m_compiledExtension->filtersWithConditionsBytecode(), m_compiledExtension->filtersWithConditionsBytecodeLength()); |
50 | for (uint64_t action : withoutConditions.actionsMatchingEverything()) { |
51 | ASSERT(static_cast<uint32_t>(action) == action); |
52 | m_universalActionsWithoutConditions.append(static_cast<uint32_t>(action)); |
53 | } |
54 | for (uint64_t action : withConditions.actionsMatchingEverything()) { |
55 | ASSERT((action & ~IfConditionFlag) == static_cast<uint32_t>(action)); |
56 | m_universalActionsWithConditions.append(action); |
57 | } |
58 | |
59 | if (shouldCompileCSS == ShouldCompileCSS::Yes) |
60 | compileGlobalDisplayNoneStyleSheet(); |
61 | m_universalActionsWithoutConditions.shrinkToFit(); |
62 | m_universalActionsWithConditions.shrinkToFit(); |
63 | } |
64 | |
65 | uint32_t ContentExtension::findFirstIgnorePreviousRules() const |
66 | { |
67 | auto* actions = m_compiledExtension->actions(); |
68 | uint32_t actionsLength = m_compiledExtension->actionsLength(); |
69 | uint32_t currentActionIndex = 0; |
70 | while (currentActionIndex < actionsLength) { |
71 | if (Action::deserializeType(actions, actionsLength, currentActionIndex) == ActionType::IgnorePreviousRules) |
72 | return currentActionIndex; |
73 | currentActionIndex += Action::serializedLength(actions, actionsLength, currentActionIndex); |
74 | } |
75 | return std::numeric_limits<uint32_t>::max(); |
76 | } |
77 | |
78 | StyleSheetContents* ContentExtension::globalDisplayNoneStyleSheet() |
79 | { |
80 | return m_globalDisplayNoneStyleSheet.get(); |
81 | } |
82 | |
83 | void ContentExtension::compileGlobalDisplayNoneStyleSheet() |
84 | { |
85 | uint32_t firstIgnorePreviousRules = findFirstIgnorePreviousRules(); |
86 | |
87 | auto* actions = m_compiledExtension->actions(); |
88 | uint32_t actionsLength = m_compiledExtension->actionsLength(); |
89 | |
90 | auto inGlobalDisplayNoneStyleSheet = [&](const uint32_t location) |
91 | { |
92 | return location < firstIgnorePreviousRules && Action::deserializeType(actions, actionsLength, location) == ActionType::CSSDisplayNoneSelector; |
93 | }; |
94 | |
95 | StringBuilder css; |
96 | for (uint32_t universalActionLocation : m_universalActionsWithoutConditions) { |
97 | if (inGlobalDisplayNoneStyleSheet(universalActionLocation)) { |
98 | if (!css.isEmpty()) |
99 | css.append(','); |
100 | Action action = Action::deserialize(actions, actionsLength, universalActionLocation); |
101 | ASSERT(action.type() == ActionType::CSSDisplayNoneSelector); |
102 | css.append(action.stringArgument()); |
103 | } |
104 | } |
105 | if (css.isEmpty()) |
106 | return; |
107 | css.append('{'); |
108 | css.append(ContentExtensionsBackend::displayNoneCSSRule()); |
109 | css.append('}'); |
110 | |
111 | m_globalDisplayNoneStyleSheet = StyleSheetContents::create(); |
112 | m_globalDisplayNoneStyleSheet->setIsUserStyleSheet(true); |
113 | if (!m_globalDisplayNoneStyleSheet->parseString(css.toString())) |
114 | m_globalDisplayNoneStyleSheet = nullptr; |
115 | |
116 | // These actions don't need to be applied individually any more. They will all be applied to every page as a precompiled style sheet. |
117 | m_universalActionsWithoutConditions.removeAllMatching(inGlobalDisplayNoneStyleSheet); |
118 | } |
119 | |
120 | void ContentExtension::populateConditionCacheIfNeeded(const URL& topURL) |
121 | { |
122 | if (m_cachedTopURL != topURL) { |
123 | DFABytecodeInterpreter interpreter(m_compiledExtension->topURLFiltersBytecode(), m_compiledExtension->topURLFiltersBytecodeLength()); |
124 | const uint16_t allLoadTypesAndResourceTypes = LoadTypeMask | ResourceTypeMask; |
125 | String string = m_compiledExtension->conditionsApplyOnlyToDomain() ? topURL.host().toString() : topURL.string(); |
126 | auto topURLActions = interpreter.interpret(string.utf8(), allLoadTypesAndResourceTypes); |
127 | |
128 | m_cachedTopURLActions.clear(); |
129 | for (uint64_t action : topURLActions) |
130 | m_cachedTopURLActions.add(action); |
131 | for (uint64_t action : interpreter.actionsMatchingEverything()) |
132 | m_cachedTopURLActions.add(action); |
133 | |
134 | m_cachedUniversalConditionedActions.clear(); |
135 | for (uint64_t action : m_universalActionsWithConditions) { |
136 | ASSERT_WITH_MESSAGE((action & ~IfConditionFlag) == static_cast<uint32_t>(action), "Universal actions with conditions should not have flags." ); |
137 | if (!!(action & IfConditionFlag) == m_cachedTopURLActions.contains(action)) |
138 | m_cachedUniversalConditionedActions.append(static_cast<uint32_t>(action)); |
139 | } |
140 | m_cachedUniversalConditionedActions.shrinkToFit(); |
141 | m_cachedTopURL = topURL; |
142 | } |
143 | } |
144 | |
145 | const DFABytecodeInterpreter::Actions& ContentExtension::topURLActions(const URL& topURL) |
146 | { |
147 | populateConditionCacheIfNeeded(topURL); |
148 | return m_cachedTopURLActions; |
149 | } |
150 | |
151 | const Vector<uint32_t>& ContentExtension::universalActionsWithConditions(const URL& topURL) |
152 | { |
153 | populateConditionCacheIfNeeded(topURL); |
154 | return m_cachedUniversalConditionedActions; |
155 | } |
156 | |
157 | } // namespace ContentExtensions |
158 | } // namespace WebCore |
159 | |
160 | #endif // ENABLE(CONTENT_EXTENSIONS) |
161 | |