Add a mechanism to ignore previous content extension rules
[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 "ContentExtensionsDebugging.h"
32 #include "NFA.h"
33 #include "NFAToDFA.h"
34 #include "URL.h"
35 #include "URLFilterParser.h"
36 #include <wtf/CurrentTime.h>
37 #include <wtf/DataLog.h>
38 #include <wtf/NeverDestroyed.h>
39 #include <wtf/text/CString.h>
40
41 namespace WebCore {
42
43 namespace ContentExtensions {
44
45 void ContentExtensionsBackend::setRuleList(const String& identifier, const Vector<ContentExtensionRule>& ruleList)
46 {
47     ASSERT(!identifier.isEmpty());
48     if (identifier.isEmpty())
49         return;
50
51     if (ruleList.isEmpty()) {
52         removeRuleList(identifier);
53         return;
54     }
55
56 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
57     double nfaBuildTimeStart = monotonicallyIncreasingTime();
58 #endif
59
60     NFA nfa;
61     URLFilterParser urlFilterParser(nfa);
62     for (unsigned ruleIndex = 0; ruleIndex < ruleList.size(); ++ruleIndex) {
63         const ContentExtensionRule& contentExtensionRule = ruleList[ruleIndex];
64         const ContentExtensionRule::Trigger& trigger = contentExtensionRule.trigger();
65         ASSERT(trigger.urlFilter.length());
66
67         String error = urlFilterParser.addPattern(trigger.urlFilter, trigger.urlFilterIsCaseSensitive, ruleIndex);
68
69         if (!error.isNull()) {
70             dataLogF("Error while parsing %s: %s\n", trigger.urlFilter.utf8().data(), error.utf8().data());
71             continue;
72         }
73     }
74
75 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
76     double nfaBuildTimeEnd = monotonicallyIncreasingTime();
77     dataLogF("    Time spent building the NFA: %f\n", (nfaBuildTimeEnd - nfaBuildTimeStart));
78 #endif
79
80 #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
81     nfa.debugPrintDot();
82 #endif
83
84 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
85     double dfaBuildTimeStart = monotonicallyIncreasingTime();
86 #endif
87
88     CompiledContentExtension compiledContentExtension = { NFAToDFA::convert(nfa), ruleList };
89
90 #if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
91     double dfaBuildTimeEnd = monotonicallyIncreasingTime();
92     dataLogF("    Time spent building the DFA: %f\n", (dfaBuildTimeEnd - dfaBuildTimeStart));
93 #endif
94
95     // FIXME: never add a DFA that only matches the empty set.
96
97 #if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
98     compiledContentExtension.dfa.debugPrintDot();
99 #endif
100
101     m_ruleLists.set(identifier, compiledContentExtension);
102 }
103
104 void ContentExtensionsBackend::removeRuleList(const String& identifier)
105 {
106     m_ruleLists.remove(identifier);
107 }
108
109 void ContentExtensionsBackend::removeAllRuleLists()
110 {
111     m_ruleLists.clear();
112 }
113
114 bool ContentExtensionsBackend::shouldBlockURL(const URL& url)
115 {
116     const String& urlString = url.string();
117     ASSERT_WITH_MESSAGE(urlString.containsOnlyASCII(), "A decoded URL should only contain ASCII characters. The matching algorithm assumes the input is ASCII.");
118
119     for (auto& ruleListSlot : m_ruleLists) {
120         CompiledContentExtension& compiledContentExtension = ruleListSlot.value;
121         unsigned state = compiledContentExtension.dfa.root();
122
123         HashSet<uint64_t, DefaultHash<uint64_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> triggeredActions;
124
125         for (unsigned i = 0; i < urlString.length(); ++i) {
126             char character = static_cast<char>(urlString[i]);
127             bool ok;
128             state = compiledContentExtension.dfa.nextState(state, character, ok);
129             if (!ok)
130                 break;
131
132             const Vector<uint64_t>& actions = compiledContentExtension.dfa.actions(state);
133             if (!actions.isEmpty())
134                 triggeredActions.add(actions.begin(), actions.end());
135         }
136         if (!triggeredActions.isEmpty()) {
137             Vector<uint64_t> sortedActions;
138             copyToVector(triggeredActions, sortedActions);
139             std::sort(sortedActions.begin(), sortedActions.end());
140             uint64_t lastAction = sortedActions.last();
141             if (compiledContentExtension.ruleList[lastAction].action().type == ExtensionActionType::BlockLoad)
142                 return true;
143         }
144     }
145
146     return false;
147 }
148
149 } // namespace ContentExtensions
150
151 } // namespace WebCore
152
153 #endif // ENABLE(CONTENT_EXTENSIONS)