Diff
Modified: trunk/Source/WebCore/ChangeLog (213668 => 213669)
--- trunk/Source/WebCore/ChangeLog 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/ChangeLog 2017-03-09 22:17:13 UTC (rev 213669)
@@ -1,3 +1,55 @@
+2017-03-09 Alex Christensen <[email protected]>
+
+ [Content Extensions] Introduce if-top-url and unless-top-url
+ https://bugs.webkit.org/show_bug.cgi?id=169433
+
+ Reviewed by Brady Eidson.
+
+ In r184116 I added if-domain and unless-domain to control whether a rule applies
+ based on the domain of the main document URL. I'm expanding this by adding if-top-url
+ and unless-top-url that run regular expressions on the entire main document URL so that
+ example.com/user1content can be distinguished from example.com/user2content.
+ To not add to the number of passes we make on the URLs for each load and to maintain JSON
+ backwards compatibility, I've made it so that if-top-url and unless-top-url can be used
+ instead of if-domain and unless-domain (which continue to work) but the two condition types
+ cannot be used together since running regular expressions on the entire main document URL
+ is strictly more powerful than checking the domain and subdomains.
+ As a minor detail, content extension regexes are by default ASCII-case-insensitive, so I've
+ done the same with top URL regexes, adding top-url-filter-is-case-sensitive to mirror the existing
+ url-filter-is-case-sensitive if the JSON author decides to make regexes case sensitive.
+
+ Covered by new API tests.
+
+ * contentextensions/CompiledContentExtension.h:
+ * contentextensions/ContentExtension.cpp:
+ (WebCore::ContentExtensions::ContentExtension::populateConditionCacheIfNeeded):
+ (WebCore::ContentExtensions::ContentExtension::topURLActions):
+ (WebCore::ContentExtensions::ContentExtension::cachedConditionedActions): Deleted.
+ * contentextensions/ContentExtension.h:
+ * contentextensions/ContentExtensionCompiler.cpp:
+ (WebCore::ContentExtensions::addUniversalActionsToDFA):
+ (WebCore::ContentExtensions::compileToBytecode):
+ We had three copies of compiling to bytecode that were almost the same and would've been made into three copies of the same code.
+ I moved them to one helper function that is called three times.
+ (WebCore::ContentExtensions::compileRuleList):
+ * contentextensions/ContentExtensionCompiler.h:
+ * contentextensions/ContentExtensionError.cpp:
+ (WebCore::ContentExtensions::contentExtensionErrorCategory):
+ Add the new error type for JSON that tries to use if-top-url and unless-top-url with if-domain and unless-domain.
+ * contentextensions/ContentExtensionError.h:
+ * contentextensions/ContentExtensionParser.cpp:
+ (WebCore::ContentExtensions::loadTrigger):
+ Parse the new values if-top-url, unless-top-url, and top-url-filter-is-case-sensitive.
+ * contentextensions/ContentExtensionRule.h:
+ (WebCore::ContentExtensions::Trigger::~Trigger):
+ * contentextensions/ContentExtensionsBackend.cpp:
+ (WebCore::ContentExtensions::ContentExtensionsBackend::actionsForResourceLoad):
+ * contentextensions/DFABytecodeInterpreter.cpp:
+ (WebCore::ContentExtensions::DFABytecodeInterpreter::interpretAppendAction):
+ (WebCore::ContentExtensions::DFABytecodeInterpreter::interpretTestFlagsAndAppendAction):
+ (WebCore::ContentExtensions::DFABytecodeInterpreter::interpretWithConditions):
+ * contentextensions/DFABytecodeInterpreter.h:
+
2017-03-09 Nikita Vasilyev <[email protected]>
Web Inspector: Show individual messages in the content pane for a WebSocket
Modified: trunk/Source/WebCore/contentextensions/CompiledContentExtension.h (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/CompiledContentExtension.h 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/CompiledContentExtension.h 2017-03-09 22:17:13 UTC (rev 213669)
@@ -42,10 +42,11 @@
virtual unsigned filtersWithoutConditionsBytecodeLength() const = 0;
virtual const DFABytecode* filtersWithConditionsBytecode() const = 0;
virtual unsigned filtersWithConditionsBytecodeLength() const = 0;
- virtual const DFABytecode* conditionedFiltersBytecode() const = 0;
- virtual unsigned conditionedFiltersBytecodeLength() const = 0;
+ virtual const DFABytecode* topURLFiltersBytecode() const = 0;
+ virtual unsigned topURLFiltersBytecodeLength() const = 0;
virtual const SerializedActionByte* actions() const = 0;
virtual unsigned actionsLength() const = 0;
+ virtual bool conditionsApplyOnlyToDomain() const = 0;
};
} // namespace ContentExtensions
Modified: trunk/Source/WebCore/contentextensions/ContentExtension.cpp (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/ContentExtension.cpp 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/ContentExtension.cpp 2017-03-09 22:17:13 UTC (rev 213669)
@@ -118,31 +118,33 @@
void ContentExtension::populateConditionCacheIfNeeded(const URL& topURL)
{
- String domain = topURL.host();
- if (m_cachedDomain != domain) {
- DFABytecodeInterpreter interpreter(m_compiledExtension->conditionedFiltersBytecode(), m_compiledExtension->conditionedFiltersBytecodeLength());
+ if (m_cachedTopURL != topURL) {
+ DFABytecodeInterpreter interpreter(m_compiledExtension->topURLFiltersBytecode(), m_compiledExtension->topURLFiltersBytecodeLength());
const uint16_t allLoadTypesAndResourceTypes = LoadTypeMask | ResourceTypeMask;
- auto domainActions = interpreter.interpret(domain.utf8(), allLoadTypesAndResourceTypes);
+ String string = m_compiledExtension->conditionsApplyOnlyToDomain() ? topURL.host() : topURL.string();
+ auto topURLActions = interpreter.interpret(string.utf8(), allLoadTypesAndResourceTypes);
- m_cachedConditionedActions.clear();
- for (uint64_t action : domainActions)
- m_cachedConditionedActions.add(action);
+ m_cachedTopURLActions.clear();
+ for (uint64_t action : topURLActions)
+ m_cachedTopURLActions.add(action);
+ for (uint64_t action : interpreter.actionsMatchingEverything())
+ m_cachedTopURLActions.add(action);
m_cachedUniversalConditionedActions.clear();
for (uint64_t action : m_universalActionsWithConditions) {
- ASSERT_WITH_MESSAGE((action & ~IfConditionFlag) == static_cast<uint32_t>(action), "Universal actions with domains should not have flags.");
- if (!!(action & IfConditionFlag) == m_cachedConditionedActions.contains(action))
+ ASSERT_WITH_MESSAGE((action & ~IfConditionFlag) == static_cast<uint32_t>(action), "Universal actions with conditions should not have flags.");
+ if (!!(action & IfConditionFlag) == m_cachedTopURLActions.contains(action))
m_cachedUniversalConditionedActions.append(static_cast<uint32_t>(action));
}
m_cachedUniversalConditionedActions.shrinkToFit();
- m_cachedDomain = domain;
+ m_cachedTopURL = topURL;
}
}
-const DFABytecodeInterpreter::Actions& ContentExtension::cachedConditionedActions(const URL& topURL)
+const DFABytecodeInterpreter::Actions& ContentExtension::topURLActions(const URL& topURL)
{
populateConditionCacheIfNeeded(topURL);
- return m_cachedConditionedActions;
+ return m_cachedTopURLActions;
}
const Vector<uint32_t>& ContentExtension::universalActionsWithConditions(const URL& topURL)
Modified: trunk/Source/WebCore/contentextensions/ContentExtension.h (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/ContentExtension.h 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/ContentExtension.h 2017-03-09 22:17:13 UTC (rev 213669)
@@ -46,7 +46,7 @@
const String& identifier() const { return m_identifier; }
const CompiledContentExtension& compiledExtension() const { return m_compiledExtension.get(); }
StyleSheetContents* globalDisplayNoneStyleSheet();
- const DFABytecodeInterpreter::Actions& cachedConditionedActions(const URL& topURL);
+ const DFABytecodeInterpreter::Actions& topURLActions(const URL& topURL);
const Vector<uint32_t>& universalActionsWithoutConditions() { return m_universalActionsWithoutConditions; }
const Vector<uint32_t>& universalActionsWithConditions(const URL& topURL);
@@ -60,9 +60,9 @@
RefPtr<StyleSheetContents> m_globalDisplayNoneStyleSheet;
void compileGlobalDisplayNoneStyleSheet();
- String m_cachedDomain;
+ URL m_cachedTopURL;
void populateConditionCacheIfNeeded(const URL& topURL);
- DFABytecodeInterpreter::Actions m_cachedConditionedActions;
+ DFABytecodeInterpreter::Actions m_cachedTopURLActions;
Vector<uint32_t> m_cachedUniversalConditionedActions;
Vector<uint32_t> m_universalActionsWithoutConditions;
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp 2017-03-09 22:17:13 UTC (rev 213669)
@@ -187,7 +187,7 @@
typedef HashSet<uint64_t, DefaultHash<uint64_t>::Hash, WTF::UnsignedWithZeroKeyHashTraits<uint64_t>> UniversalActionSet;
-static void addUniversalActionsToDFA(DFA& dfa, const UniversalActionSet& universalActions)
+static void addUniversalActionsToDFA(DFA& dfa, UniversalActionSet&& universalActions)
{
if (universalActions.isEmpty())
return;
@@ -205,6 +205,78 @@
root.setActions(actionsStart, static_cast<uint16_t>(actionsLength));
}
+template<typename Functor>
+static void compileToBytecode(CombinedURLFilters&& filters, UniversalActionSet&& universalActions, Functor writeBytecodeToClient)
+{
+ // Smaller maxNFASizes risk high compiling and interpreting times from having too many DFAs,
+ // larger maxNFASizes use too much memory when compiling.
+ const unsigned maxNFASize = 75000;
+
+ bool firstNFASeen = false;
+
+ auto lowerDFAToBytecode = [&](DFA&& dfa)
+ {
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+ dataLogF("DFA\n");
+ dfa.debugPrintDot();
+#endif
+ ASSERT_WITH_MESSAGE(!dfa.nodes[dfa.root].hasActions(), "All actions on the DFA root should come from regular expressions that match everything.");
+
+ if (!firstNFASeen) {
+ // Put all the universal actions on the first DFA.
+ addUniversalActionsToDFA(dfa, WTFMove(universalActions));
+ }
+
+ Vector<DFABytecode> bytecode;
+ DFABytecodeCompiler compiler(dfa, bytecode);
+ compiler.compile();
+ LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
+ writeBytecodeToClient(WTFMove(bytecode));
+ firstNFASeen = true;
+ };
+
+ const unsigned smallDFASize = 100;
+ DFACombiner smallDFACombiner;
+ filters.processNFAs(maxNFASize, [&](NFA&& nfa) {
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+ dataLogF("NFA\n");
+ nfa.debugPrintDot();
+#endif
+ LOG_LARGE_STRUCTURES(nfa, nfa.memoryUsed());
+ DFA dfa = NFAToDFA::convert(nfa);
+ LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
+
+ if (dfa.graphSize() < smallDFASize)
+ smallDFACombiner.addDFA(WTFMove(dfa));
+ else {
+ dfa.minimize();
+ lowerDFAToBytecode(WTFMove(dfa));
+ }
+ });
+
+ smallDFACombiner.combineDFAs(smallDFASize, [&](DFA&& dfa) {
+ LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
+ lowerDFAToBytecode(WTFMove(dfa));
+ });
+
+ ASSERT(filters.isEmpty());
+
+ if (!firstNFASeen) {
+ // Our bytecode interpreter expects to have at least one DFA, so if we haven't seen any
+ // create a dummy one and add any universal actions.
+
+ DFA dummyDFA = DFA::empty();
+ addUniversalActionsToDFA(dummyDFA, WTFMove(universalActions));
+
+ Vector<DFABytecode> bytecode;
+ DFABytecodeCompiler compiler(dummyDFA, bytecode);
+ compiler.compile();
+ LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
+ writeBytecodeToClient(WTFMove(bytecode));
+ }
+ LOG_LARGE_STRUCTURES(universalActions, universalActions.capacity() * sizeof(unsigned));
+}
+
std::error_code compileRuleList(ContentExtensionCompilationClient& client, String&& ruleJSON)
{
auto ruleList = parseRuleList(WTFMove(ruleJSON));
@@ -212,6 +284,25 @@
return ruleList.error();
Vector<ContentExtensionRule> parsedRuleList = WTFMove(ruleList.value());
+ bool domainConditionSeen = false;
+ bool topURLConditionSeen = false;
+ for (const auto& rule : parsedRuleList) {
+ switch (rule.trigger().conditionType) {
+ case Trigger::ConditionType::None:
+ break;
+ case Trigger::ConditionType::IfDomain:
+ case Trigger::ConditionType::UnlessDomain:
+ domainConditionSeen = true;
+ break;
+ case Trigger::ConditionType::IfTopURL:
+ case Trigger::ConditionType::UnlessTopURL:
+ topURLConditionSeen = true;
+ break;
+ }
+ }
+ if (topURLConditionSeen && domainConditionSeen)
+ return ContentExtensionError::JSONTopURLAndDomainConditions;
+
#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
double patternPartitioningStart = monotonicallyIncreasingTime();
#endif
@@ -218,19 +309,20 @@
Vector<SerializedActionByte> actions;
Vector<unsigned> actionLocations = serializeActions(parsedRuleList, actions);
- client.writeActions(WTFMove(actions));
LOG_LARGE_STRUCTURES(actions, actions.capacity() * sizeof(SerializedActionByte));
- actions.clear();
+ client.writeActions(WTFMove(actions), domainConditionSeen);
UniversalActionSet universalActionsWithoutConditions;
UniversalActionSet universalActionsWithConditions;
+ UniversalActionSet universalTopURLActions;
// FIXME: These don't all need to be in memory at the same time.
CombinedURLFilters filtersWithoutConditions;
CombinedURLFilters filtersWithConditions;
- CombinedURLFilters conditionFilters;
+ CombinedURLFilters topURLFilters;
URLFilterParser filtersWithoutConditionParser(filtersWithoutConditions);
URLFilterParser filtersWithConditionParser(filtersWithConditions);
+ URLFilterParser topURLFilterParser(topURLFilters);
for (unsigned ruleIndex = 0; ruleIndex < parsedRuleList.size(); ++ruleIndex) {
const ContentExtensionRule& contentExtensionRule = parsedRuleList[ruleIndex];
@@ -256,10 +348,12 @@
} else {
switch (trigger.conditionType) {
case Trigger::ConditionType::IfDomain:
+ case Trigger::ConditionType::IfTopURL:
actionLocationAndFlags |= IfConditionFlag;
break;
case Trigger::ConditionType::None:
case Trigger::ConditionType::UnlessDomain:
+ case Trigger::ConditionType::UnlessTopURL:
ASSERT(!(actionLocationAndFlags & IfConditionFlag));
break;
}
@@ -273,8 +367,23 @@
dataLogF("Error while parsing %s: %s\n", trigger.urlFilter.utf8().data(), URLFilterParser::statusString(status).utf8().data());
return ContentExtensionError::JSONInvalidRegex;
}
- for (const String& condition : trigger.conditions)
- conditionFilters.addDomain(actionLocationAndFlags, condition);
+ for (const String& condition : trigger.conditions) {
+ if (domainConditionSeen) {
+ ASSERT(!topURLConditionSeen);
+ topURLFilters.addDomain(actionLocationAndFlags, condition);
+ } else {
+ ASSERT(topURLConditionSeen);
+ status = topURLFilterParser.addPattern(condition, trigger.topURLConditionIsCaseSensitive, actionLocationAndFlags);
+ if (status == URLFilterParser::MatchesEverything) {
+ universalTopURLActions.add(actionLocationAndFlags);
+ status = URLFilterParser::Ok;
+ }
+ if (status != URLFilterParser::Ok) {
+ dataLogF("Error while parsing %s: %s\n", condition.utf8().data(), URLFilterParser::statusString(status).utf8().data());
+ return ContentExtensionError::JSONInvalidRegex;
+ }
+ }
+ }
}
ASSERT(status == URLFilterParser::Ok);
}
@@ -290,182 +399,22 @@
LOG_LARGE_STRUCTURES(filtersWithoutConditions, filtersWithoutConditions.memoryUsed());
LOG_LARGE_STRUCTURES(filtersWithConditions, filtersWithConditions.memoryUsed());
- LOG_LARGE_STRUCTURES(conditionFilters, conditionFilters.memoryUsed());
+ LOG_LARGE_STRUCTURES(topURLFilters, topURLFilters.memoryUsed());
#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
- unsigned machinesWithoutConditionsCount = 0;
- unsigned totalBytecodeSizeForMachinesWithoutConditions = 0;
- unsigned machinesWithConditionsCount = 0;
- unsigned totalBytecodeSizeForMachinesWithConditions = 0;
double totalNFAToByteCodeBuildTimeStart = monotonicallyIncreasingTime();
#endif
- // Smaller maxNFASizes risk high compiling and interpreting times from having too many DFAs,
- // larger maxNFASizes use too much memory when compiling.
- const unsigned maxNFASize = 75000;
-
- bool firstNFAWithoutConditionsSeen = false;
-
- auto lowerFiltersWithoutConditionsDFAToBytecode = [&](DFA&& dfa)
- {
-#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
- dataLogF("filtersWithoutConditions DFA\n");
- dfa.debugPrintDot();
-#endif
- ASSERT_WITH_MESSAGE(!dfa.nodes[dfa.root].hasActions(), "All actions on the DFA root should come from regular expressions that match everything.");
-
- if (!firstNFAWithoutConditionsSeen) {
- // Put all the universal actions on the first DFA.
- addUniversalActionsToDFA(dfa, universalActionsWithoutConditions);
- }
-
- Vector<DFABytecode> bytecode;
- DFABytecodeCompiler compiler(dfa, bytecode);
- compiler.compile();
- LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
-#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
- ++machinesWithoutConditionsCount;
- totalBytecodeSizeForMachinesWithoutConditions += bytecode.size();
-#endif
+ compileToBytecode(WTFMove(filtersWithoutConditions), WTFMove(universalActionsWithoutConditions), [&](Vector<DFABytecode>&& bytecode) {
client.writeFiltersWithoutConditionsBytecode(WTFMove(bytecode));
-
- firstNFAWithoutConditionsSeen = true;
- };
-
- const unsigned smallDFASize = 100;
- DFACombiner smallFiltersWithoutConditionsDFACombiner;
- filtersWithoutConditions.processNFAs(maxNFASize, [&](NFA&& nfa) {
-#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
- dataLogF("filtersWithoutConditions NFA\n");
- nfa.debugPrintDot();
-#endif
-
- LOG_LARGE_STRUCTURES(nfa, nfa.memoryUsed());
- DFA dfa = NFAToDFA::convert(nfa);
- LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
-
- if (dfa.graphSize() < smallDFASize)
- smallFiltersWithoutConditionsDFACombiner.addDFA(WTFMove(dfa));
- else {
- dfa.minimize();
- lowerFiltersWithoutConditionsDFAToBytecode(WTFMove(dfa));
- }
});
-
-
- smallFiltersWithoutConditionsDFACombiner.combineDFAs(smallDFASize, [&](DFA&& dfa) {
- LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
- lowerFiltersWithoutConditionsDFAToBytecode(WTFMove(dfa));
- });
-
- ASSERT(filtersWithoutConditions.isEmpty());
-
- if (!firstNFAWithoutConditionsSeen) {
- // Our bytecode interpreter expects to have at least one DFA, so if we haven't seen any
- // create a dummy one and add any universal actions.
-
- DFA dummyDFA = DFA::empty();
- addUniversalActionsToDFA(dummyDFA, universalActionsWithoutConditions);
-
- Vector<DFABytecode> bytecode;
- DFABytecodeCompiler compiler(dummyDFA, bytecode);
- compiler.compile();
- LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
- client.writeFiltersWithoutConditionsBytecode(WTFMove(bytecode));
- }
- LOG_LARGE_STRUCTURES(universalActionsWithoutConditions, universalActionsWithoutConditions.capacity() * sizeof(unsigned));
- universalActionsWithoutConditions.clear();
-
- bool firstNFAWithConditionsSeen = false;
- auto lowerFiltersWithConditionsDFAToBytecode = [&](DFA&& dfa)
- {
- if (!firstNFAWithConditionsSeen) {
- // Put all the universal actions on the first DFA.
- addUniversalActionsToDFA(dfa, universalActionsWithConditions);
- }
-
- Vector<DFABytecode> bytecode;
- DFABytecodeCompiler compiler(dfa, bytecode);
- compiler.compile();
- LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
-#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
- ++machinesWithConditionsCount;
- totalBytecodeSizeForMachinesWithConditions += bytecode.size();
-#endif
+ compileToBytecode(WTFMove(filtersWithConditions), WTFMove(universalActionsWithConditions), [&](Vector<DFABytecode>&& bytecode) {
client.writeFiltersWithConditionsBytecode(WTFMove(bytecode));
-
- firstNFAWithConditionsSeen = true;
- };
-
- DFACombiner smallFiltersWithConditionsDFACombiner;
- filtersWithConditions.processNFAs(maxNFASize, [&](NFA&& nfa) {
-#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
- dataLogF("filtersWithConditions NFA\n");
- nfa.debugPrintDot();
-#endif
- LOG_LARGE_STRUCTURES(nfa, nfa.memoryUsed());
- DFA dfa = NFAToDFA::convert(nfa);
-#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
- dataLogF("filtersWithConditions PRE MINIMIZING DFA\n");
- dfa.debugPrintDot();
-#endif
- LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
-
- ASSERT_WITH_MESSAGE(!dfa.nodes[dfa.root].hasActions(), "Filters with Conditions that match everything are not allowed right now.");
-
- if (dfa.graphSize() < smallDFASize)
- smallFiltersWithConditionsDFACombiner.addDFA(WTFMove(dfa));
- else {
- dfa.minimize();
- lowerFiltersWithConditionsDFAToBytecode(WTFMove(dfa));
- }
});
- smallFiltersWithConditionsDFACombiner.combineDFAs(smallDFASize, [&](DFA&& dfa) {
- LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
- lowerFiltersWithConditionsDFAToBytecode(WTFMove(dfa));
+ compileToBytecode(WTFMove(topURLFilters), WTFMove(universalTopURLActions), [&](Vector<DFABytecode>&& bytecode) {
+ client.writeTopURLFiltersBytecode(WTFMove(bytecode));
});
- ASSERT(filtersWithConditions.isEmpty());
- if (!firstNFAWithConditionsSeen) {
- // Our bytecode interpreter expects to have at least one DFA, so if we haven't seen any
- // create a dummy one and add any universal actions.
-
- DFA dummyDFA = DFA::empty();
- addUniversalActionsToDFA(dummyDFA, universalActionsWithConditions);
-
- Vector<DFABytecode> bytecode;
- DFABytecodeCompiler compiler(dummyDFA, bytecode);
- compiler.compile();
- LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
- client.writeFiltersWithConditionsBytecode(WTFMove(bytecode));
- }
- LOG_LARGE_STRUCTURES(universalActionsWithConditions, universalActionsWithConditions.capacity() * sizeof(unsigned));
- universalActionsWithConditions.clear();
-
- conditionFilters.processNFAs(maxNFASize, [&](NFA&& nfa) {
-#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
- dataLogF("conditionFilters NFA\n");
- nfa.debugPrintDot();
-#endif
- LOG_LARGE_STRUCTURES(nfa, nfa.memoryUsed());
- DFA dfa = NFAToDFA::convert(nfa);
-#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
- dataLogF("conditionFilters DFA\n");
- dfa.debugPrintDot();
-#endif
- LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
- // Minimizing this DFA would not be effective because all actions are unique
- // and because of the tree-like structure of this DFA.
- ASSERT_WITH_MESSAGE(!dfa.nodes[dfa.root].hasActions(), "There should not be any conditions that match everything.");
-
- Vector<DFABytecode> bytecode;
- DFABytecodeCompiler compiler(dfa, bytecode);
- compiler.compile();
- LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
- client.writeConditionedFiltersBytecode(WTFMove(bytecode));
- });
- ASSERT(conditionFilters.isEmpty());
-
#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
double totalNFAToByteCodeBuildTimeEnd = monotonicallyIncreasingTime();
dataLogF(" Time spent building and compiling the DFAs: %f\n", (totalNFAToByteCodeBuildTimeEnd - totalNFAToByteCodeBuildTimeStart));
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h 2017-03-09 22:17:13 UTC (rev 213669)
@@ -40,10 +40,10 @@
virtual ~ContentExtensionCompilationClient() { }
// Functions should be called in this order. All except writeActions and finalize can be called multiple times, though.
- virtual void writeActions(Vector<SerializedActionByte>&&) = 0;
+ virtual void writeActions(Vector<SerializedActionByte>&&, bool conditionsApplyOnlyToDomain) = 0;
virtual void writeFiltersWithoutConditionsBytecode(Vector<DFABytecode>&&) = 0;
virtual void writeFiltersWithConditionsBytecode(Vector<DFABytecode>&&) = 0;
- virtual void writeConditionedFiltersBytecode(Vector<DFABytecode>&&) = 0;
+ virtual void writeTopURLFiltersBytecode(Vector<DFABytecode>&&) = 0;
virtual void finalize() = 0;
};
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp 2017-03-09 22:17:13 UTC (rev 213669)
@@ -78,13 +78,15 @@
case ContentExtensionError::JSONInvalidRegex:
return "Invalid or unsupported regular _expression_.";
case ContentExtensionError::JSONInvalidConditionList:
- return "Invalid list of if-domain or unless-domain conditions.";
+ return "Invalid list of if-domain, unless-domain, if-top-url, or unless-top-url conditions.";
case ContentExtensionError::JSONTooManyRules:
return "Too many rules in JSON array.";
case ContentExtensionError::JSONDomainNotLowerCaseASCII:
return "Domains must be lower case ASCII. Use punycode to encode non-ASCII characters.";
case ContentExtensionError::JSONMultipleConditions:
- return "A trigger cannot have more than one condition (if-domain or unless-domain)";
+ return "A trigger cannot have more than one condition (if-domain, unless-domain, if-top-url, or unless-top-url)";
+ case ContentExtensionError::JSONTopURLAndDomainConditions:
+ return "A list cannot have if-domain and unless-domain mixed with if-top-url and unless-top-url";
}
return std::string();
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionError.h (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/ContentExtensionError.h 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionError.h 2017-03-09 22:17:13 UTC (rev 213669)
@@ -52,6 +52,7 @@
JSONInvalidConditionList,
JSONDomainNotLowerCaseASCII,
JSONMultipleConditions,
+ JSONTopURLAndDomainConditions,
JSONTooManyRules,
JSONInvalidAction,
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionParser.cpp (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/ContentExtensionParser.cpp 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionParser.cpp 2017-03-09 22:17:13 UTC (rev 213669)
@@ -153,6 +153,10 @@
if (urlFilterCaseValue && !scope.exception() && urlFilterCaseValue.isBoolean())
trigger.urlFilterIsCaseSensitive = urlFilterCaseValue.toBoolean(&exec);
+ const JSValue topURLFilterCaseValue = triggerObject.get(&exec, Identifier::fromString(&exec, "top-url-filter-is-case-sensitive"));
+ if (topURLFilterCaseValue && !scope.exception() && topURLFilterCaseValue.isBoolean())
+ trigger.topURLConditionIsCaseSensitive = topURLFilterCaseValue.toBoolean(&exec);
+
const JSValue resourceTypeValue = triggerObject.get(&exec, Identifier::fromString(&exec, "resource-type"));
if (!scope.exception() && resourceTypeValue.isObject()) {
auto typeFlagsError = getTypeFlags(exec, resourceTypeValue, trigger.flags, readResourceType);
@@ -196,6 +200,34 @@
} else if (!unlessDomainValue.isUndefined())
return makeUnexpected(ContentExtensionError::JSONInvalidConditionList);
+ const JSValue ifTopURLValue = triggerObject.get(&exec, Identifier::fromString(&exec, "if-top-url"));
+ if (!scope.exception() && ifTopURLValue.isObject()) {
+ if (trigger.conditionType != Trigger::ConditionType::None)
+ return makeUnexpected(ContentExtensionError::JSONMultipleConditions);
+ auto ifTopURL = getStringList(exec, asObject(ifTopURLValue));
+ if (!ifTopURL.hasValue())
+ return makeUnexpected(ifTopURL.error());
+ trigger.conditions = WTFMove(ifTopURL.value());
+ if (trigger.conditions.isEmpty())
+ return makeUnexpected(ContentExtensionError::JSONInvalidConditionList);
+ trigger.conditionType = Trigger::ConditionType::IfTopURL;
+ } else if (!ifTopURLValue.isUndefined())
+ return makeUnexpected(ContentExtensionError::JSONInvalidConditionList);
+
+ const JSValue unlessTopURLValue = triggerObject.get(&exec, Identifier::fromString(&exec, "unless-top-url"));
+ if (!scope.exception() && unlessTopURLValue.isObject()) {
+ if (trigger.conditionType != Trigger::ConditionType::None)
+ return makeUnexpected(ContentExtensionError::JSONMultipleConditions);
+ auto unlessTopURL = getStringList(exec, asObject(unlessTopURLValue));
+ if (!unlessTopURL.hasValue())
+ return makeUnexpected(unlessTopURL.error());
+ trigger.conditions = WTFMove(unlessTopURL.value());
+ if (trigger.conditions.isEmpty())
+ return makeUnexpected(ContentExtensionError::JSONInvalidConditionList);
+ trigger.conditionType = Trigger::ConditionType::UnlessTopURL;
+ } else if (!unlessTopURLValue.isUndefined())
+ return makeUnexpected(ContentExtensionError::JSONInvalidConditionList);
+
return WTFMove(trigger);
}
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionRule.h (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/ContentExtensionRule.h 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionRule.h 2017-03-09 22:17:13 UTC (rev 213669)
@@ -43,6 +43,7 @@
struct Trigger {
String urlFilter;
bool urlFilterIsCaseSensitive { false };
+ bool topURLConditionIsCaseSensitive { false };
ResourceFlags flags { 0 };
Vector<String> conditions;
enum class ConditionType {
@@ -49,11 +50,16 @@
None,
IfDomain,
UnlessDomain,
- } conditionType { ConditionType::None };
+ IfTopURL,
+ UnlessTopURL,
+ };
+ ConditionType conditionType { ConditionType::None };
~Trigger()
{
ASSERT(conditions.isEmpty() == (conditionType == ConditionType::None));
+ if (topURLConditionIsCaseSensitive)
+ ASSERT(conditionType == ConditionType::IfTopURL || conditionType == ConditionType::UnlessTopURL);
}
bool isEmpty() const
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp 2017-03-09 22:17:13 UTC (rev 213669)
@@ -96,7 +96,7 @@
URL topURL = resourceLoadInfo.mainDocumentURL;
DFABytecodeInterpreter withConditionsInterpreter(compiledExtension.filtersWithConditionsBytecode(), compiledExtension.filtersWithConditionsBytecodeLength());
- DFABytecodeInterpreter::Actions withConditionsActions = withConditionsInterpreter.interpretWithConditions(urlCString, flags, contentExtension->cachedConditionedActions(topURL));
+ DFABytecodeInterpreter::Actions withConditionsActions = withConditionsInterpreter.interpretWithConditions(urlCString, flags, contentExtension->topURLActions(topURL));
const SerializedActionByte* actions = compiledExtension.actions();
const unsigned actionsLength = compiledExtension.actionsLength();
Modified: trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp 2017-03-09 22:17:13 UTC (rev 213669)
@@ -98,7 +98,7 @@
ASSERT(getInstruction(m_bytecode, m_bytecodeLength, programCounter) == DFABytecodeInstruction::AppendAction
|| getInstruction(m_bytecode, m_bytecodeLength, programCounter) == DFABytecodeInstruction::AppendActionWithIfCondition);
uint64_t action = "" ? IfConditionFlag : 0) | static_cast<uint64_t>(getBits<uint32_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction)));
- if (!m_conditionActions || matchesCondition(action, *m_conditionActions))
+ if (!m_topURLActions || matchesCondition(action, *m_topURLActions))
actions.add(action);
programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction);
@@ -119,7 +119,7 @@
if (loadTypeMatches && resourceTypeMatches) {
uint64_t actionAndFlags = (ifCondition ? IfConditionFlag : 0) | (static_cast<uint64_t>(flagsToCheck) << 32) | static_cast<uint64_t>(getBits<uint32_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecodeInstruction) + sizeof(uint16_t)));
- if (!m_conditionActions || matchesCondition(actionAndFlags, *m_conditionActions))
+ if (!m_topURLActions || matchesCondition(actionAndFlags, *m_topURLActions))
actions.add(actionAndFlags);
}
programCounter += instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendAction);
@@ -169,12 +169,12 @@
return actions;
}
-DFABytecodeInterpreter::Actions DFABytecodeInterpreter::interpretWithConditions(const CString& urlCString, uint16_t flags, const DFABytecodeInterpreter::Actions& conditionActions)
+DFABytecodeInterpreter::Actions DFABytecodeInterpreter::interpretWithConditions(const CString& urlCString, uint16_t flags, const DFABytecodeInterpreter::Actions& topURLActions)
{
- ASSERT(!m_conditionActions);
- m_conditionActions = &conditionActions;
+ ASSERT(!m_topURLActions);
+ m_topURLActions = &topURLActions;
DFABytecodeInterpreter::Actions actions = interpret(urlCString, flags);
- m_conditionActions = nullptr;
+ m_topURLActions = nullptr;
return actions;
}
Modified: trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.h (213668 => 213669)
--- trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.h 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.h 2017-03-09 22:17:13 UTC (rev 213669)
@@ -60,7 +60,7 @@
const DFABytecode* m_bytecode;
const unsigned m_bytecodeLength;
- const DFABytecodeInterpreter::Actions* m_conditionActions { nullptr };
+ const DFABytecodeInterpreter::Actions* m_topURLActions { nullptr };
};
} // namespace ContentExtensions
Modified: trunk/Source/WebKit2/ChangeLog (213668 => 213669)
--- trunk/Source/WebKit2/ChangeLog 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebKit2/ChangeLog 2017-03-09 22:17:13 UTC (rev 213669)
@@ -1,3 +1,37 @@
+2017-03-09 Alex Christensen <[email protected]>
+
+ [Content Extensions] Introduce if-top-url and unless-top-url
+ https://bugs.webkit.org/show_bug.cgi?id=169433
+
+ Reviewed by Brady Eidson.
+
+ Rename conditionedFilters to topURLFilters to reflect the fact that they are the filters
+ that are run on the top URL, and possibly just the domain of the top url.
+ I was a bit too aggressive when renaming domain* to condition* in r213533.
+
+ * Shared/WebCompiledContentExtension.cpp:
+ (WebKit::WebCompiledContentExtension::conditionsApplyOnlyToDomain):
+ (WebKit::WebCompiledContentExtension::topURLFiltersBytecode):
+ (WebKit::WebCompiledContentExtension::topURLFiltersBytecodeLength):
+ (WebKit::WebCompiledContentExtension::conditionedFiltersBytecode): Deleted.
+ (WebKit::WebCompiledContentExtension::conditionedFiltersBytecodeLength): Deleted.
+ * Shared/WebCompiledContentExtension.h:
+ * Shared/WebCompiledContentExtensionData.cpp:
+ (WebKit::WebCompiledContentExtensionData::encode):
+ (WebKit::WebCompiledContentExtensionData::decode):
+ * Shared/WebCompiledContentExtensionData.h:
+ (WebKit::WebCompiledContentExtensionData::WebCompiledContentExtensionData):
+ * UIProcess/API/APIUserContentExtensionStore.cpp:
+ (API::encodeContentExtensionMetaData):
+ (API::decodeContentExtensionMetaData):
+ (API::compiledToFile):
+ (API::createExtension):
+ (API::UserContentExtensionStore::invalidateContentExtensionVersion):
+ (API::userContentExtensionStoreErrorCategory):
+ * UIProcess/API/APIUserContentExtensionStore.h:
+ Increment CurrentContentExtensionFileVersion because we have changed the format of the binary on disk.
+ We only added 4 bytes, but that's binary incompatible and requires re-compiling any existing content extensions.
+
2017-03-09 Brent Fulgham <[email protected]>
[WK2][iOS] Extend WebProcess sandbox to support audio and video compression/decompression
Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp (213668 => 213669)
--- trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp 2017-03-09 22:17:13 UTC (rev 213669)
@@ -44,6 +44,11 @@
{
}
+bool WebCompiledContentExtension::conditionsApplyOnlyToDomain() const
+{
+ return *reinterpret_cast<uint32_t*>(reinterpret_cast<uint8_t*>(m_data.data->data()) + m_data.conditionsApplyOnlyToDomainOffset);
+}
+
const WebCore::ContentExtensions::DFABytecode* WebCompiledContentExtension::filtersWithoutConditionsBytecode() const
{
return static_cast<const WebCore::ContentExtensions::DFABytecode*>(m_data.data->data()) + m_data.filtersWithoutConditionsBytecodeOffset;
@@ -64,14 +69,14 @@
return m_data.filtersWithConditionsBytecodeSize;
}
-const WebCore::ContentExtensions::DFABytecode* WebCompiledContentExtension::conditionedFiltersBytecode() const
+const WebCore::ContentExtensions::DFABytecode* WebCompiledContentExtension::topURLFiltersBytecode() const
{
- return static_cast<const WebCore::ContentExtensions::DFABytecode*>(m_data.data->data()) + m_data.conditionedFiltersBytecodeOffset;
+ return static_cast<const WebCore::ContentExtensions::DFABytecode*>(m_data.data->data()) + m_data.topURLFiltersBytecodeOffset;
}
-unsigned WebCompiledContentExtension::conditionedFiltersBytecodeLength() const
+unsigned WebCompiledContentExtension::topURLFiltersBytecodeLength() const
{
- return m_data.conditionedFiltersBytecodeSize;
+ return m_data.topURLFiltersBytecodeSize;
}
const WebCore::ContentExtensions::SerializedActionByte* WebCompiledContentExtension::actions() const
Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtension.h (213668 => 213669)
--- trunk/Source/WebKit2/Shared/WebCompiledContentExtension.h 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtension.h 2017-03-09 22:17:13 UTC (rev 213669)
@@ -47,8 +47,9 @@
unsigned filtersWithoutConditionsBytecodeLength() const final;
const WebCore::ContentExtensions::DFABytecode* filtersWithConditionsBytecode() const final;
unsigned filtersWithConditionsBytecodeLength() const final;
- const WebCore::ContentExtensions::DFABytecode* conditionedFiltersBytecode() const final;
- unsigned conditionedFiltersBytecodeLength() const final;
+ const WebCore::ContentExtensions::DFABytecode* topURLFiltersBytecode() const final;
+ unsigned topURLFiltersBytecodeLength() const final;
+ bool conditionsApplyOnlyToDomain() const final;
const WebCore::ContentExtensions::SerializedActionByte* actions() const final;
unsigned actionsLength() const final;
Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp (213668 => 213669)
--- trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp 2017-03-09 22:17:13 UTC (rev 213669)
@@ -44,8 +44,8 @@
encoder << filtersWithoutConditionsBytecodeSize;
encoder << filtersWithConditionsBytecodeOffset;
encoder << filtersWithConditionsBytecodeSize;
- encoder << conditionedFiltersBytecodeOffset;
- encoder << conditionedFiltersBytecodeSize;
+ encoder << topURLFiltersBytecodeOffset;
+ encoder << topURLFiltersBytecodeSize;
}
bool WebCompiledContentExtensionData::decode(IPC::Decoder& decoder, WebCompiledContentExtensionData& compiledContentExtensionData)
@@ -67,9 +67,9 @@
return false;
if (!decoder.decode(compiledContentExtensionData.filtersWithConditionsBytecodeSize))
return false;
- if (!decoder.decode(compiledContentExtensionData.conditionedFiltersBytecodeOffset))
+ if (!decoder.decode(compiledContentExtensionData.topURLFiltersBytecodeOffset))
return false;
- if (!decoder.decode(compiledContentExtensionData.conditionedFiltersBytecodeSize))
+ if (!decoder.decode(compiledContentExtensionData.topURLFiltersBytecodeSize))
return false;
return true;
Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h (213668 => 213669)
--- trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h 2017-03-09 22:17:13 UTC (rev 213669)
@@ -42,9 +42,10 @@
public:
WebCompiledContentExtensionData() = default;
- WebCompiledContentExtensionData(RefPtr<SharedMemory>&& data, NetworkCache::Data fileData, unsigned actionsOffset, unsigned actionsSize, unsigned filtersWithoutConditionsBytecodeOffset, unsigned filtersWithoutConditionsBytecodeSize, unsigned filtersWithConditionsBytecodeOffset, unsigned filtersWithConditionsBytecodeSize, unsigned conditionedFiltersBytecodeOffset, unsigned conditionedFiltersBytecodeSize)
+ WebCompiledContentExtensionData(RefPtr<SharedMemory>&& data, NetworkCache::Data fileData, unsigned conditionsApplyOnlyToDomainOffset, unsigned actionsOffset, unsigned actionsSize, unsigned filtersWithoutConditionsBytecodeOffset, unsigned filtersWithoutConditionsBytecodeSize, unsigned filtersWithConditionsBytecodeOffset, unsigned filtersWithConditionsBytecodeSize, unsigned topURLFiltersBytecodeOffset, unsigned topURLFiltersBytecodeSize)
: data(WTFMove(data))
, fileData(fileData)
+ , conditionsApplyOnlyToDomainOffset(conditionsApplyOnlyToDomainOffset)
, actionsOffset(actionsOffset)
, actionsSize(actionsSize)
, filtersWithoutConditionsBytecodeOffset(filtersWithoutConditionsBytecodeOffset)
@@ -51,8 +52,8 @@
, filtersWithoutConditionsBytecodeSize(filtersWithoutConditionsBytecodeSize)
, filtersWithConditionsBytecodeOffset(filtersWithConditionsBytecodeOffset)
, filtersWithConditionsBytecodeSize(filtersWithConditionsBytecodeSize)
- , conditionedFiltersBytecodeOffset(conditionedFiltersBytecodeOffset)
- , conditionedFiltersBytecodeSize(conditionedFiltersBytecodeSize)
+ , topURLFiltersBytecodeOffset(topURLFiltersBytecodeOffset)
+ , topURLFiltersBytecodeSize(topURLFiltersBytecodeSize)
{
}
@@ -61,6 +62,7 @@
RefPtr<SharedMemory> data;
NetworkCache::Data fileData;
+ unsigned conditionsApplyOnlyToDomainOffset { 0 };
unsigned actionsOffset { 0 };
unsigned actionsSize { 0 };
unsigned filtersWithoutConditionsBytecodeOffset { 0 };
@@ -67,8 +69,8 @@
unsigned filtersWithoutConditionsBytecodeSize { 0 };
unsigned filtersWithConditionsBytecodeOffset { 0 };
unsigned filtersWithConditionsBytecodeSize { 0 };
- unsigned conditionedFiltersBytecodeOffset { 0 };
- unsigned conditionedFiltersBytecodeSize { 0 };
+ unsigned topURLFiltersBytecodeOffset { 0 };
+ unsigned topURLFiltersBytecodeSize { 0 };
};
}
Modified: trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp (213668 => 213669)
--- trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp 2017-03-09 22:17:13 UTC (rev 213669)
@@ -79,7 +79,11 @@
return WebCore::pathByAppendingComponent(base, "ContentExtension-" + WebCore::encodeForFileName(identifier));
}
-const size_t ContentExtensionFileHeaderSize = sizeof(uint32_t) + 4 * sizeof(uint64_t);
+// The size and offset of the densely packed bytes in the file, not sizeof and offsetof, which would
+// represent the size and offset of the structure in memory, possibly with compiler-added padding.
+const size_t ContentExtensionFileHeaderSize = 2 * sizeof(uint32_t) + 4 * sizeof(uint64_t);
+const size_t ConditionsApplyOnlyToDomainOffset = sizeof(uint32_t) + 4 * sizeof(uint64_t);
+
struct ContentExtensionMetaData {
uint32_t version { UserContentExtensionStore::CurrentContentExtensionFileVersion };
uint64_t actionsSize { 0 };
@@ -86,6 +90,7 @@
uint64_t filtersWithoutConditionsBytecodeSize { 0 };
uint64_t filtersWithConditionsBytecodeSize { 0 };
uint64_t conditionedFiltersBytecodeSize { 0 };
+ uint32_t conditionsApplyOnlyToDomain { false };
size_t fileSize() const
{
@@ -106,6 +111,7 @@
encoder << metaData.filtersWithoutConditionsBytecodeSize;
encoder << metaData.filtersWithConditionsBytecodeSize;
encoder << metaData.conditionedFiltersBytecodeSize;
+ encoder << metaData.conditionsApplyOnlyToDomain;
ASSERT(encoder.bufferSize() == ContentExtensionFileHeaderSize);
return Data(encoder.buffer(), encoder.bufferSize());
@@ -131,6 +137,8 @@
return false;
if (!decoder.decode(metaData.conditionedFiltersBytecodeSize))
return false;
+ if (!decoder.decode(metaData.conditionsApplyOnlyToDomain))
+ return false;
success = true;
return false;
});
@@ -177,9 +185,10 @@
ASSERT(!metaData.filtersWithoutConditionsBytecodeSize);
ASSERT(!metaData.filtersWithConditionsBytecodeSize);
ASSERT(!metaData.conditionedFiltersBytecodeSize);
+ ASSERT(!metaData.conditionsApplyOnlyToDomain);
}
- void writeFiltersWithoutConditionsBytecode(Vector<DFABytecode>&& bytecode) override
+ void writeFiltersWithoutConditionsBytecode(Vector<DFABytecode>&& bytecode) final
{
ASSERT(!m_filtersWithConditionBytecodeWritten);
ASSERT(!m_conditionFiltersBytecodeWritten);
@@ -187,7 +196,7 @@
writeToFile(Data(bytecode.data(), bytecode.size()));
}
- void writeFiltersWithConditionsBytecode(Vector<DFABytecode>&& bytecode) override
+ void writeFiltersWithConditionsBytecode(Vector<DFABytecode>&& bytecode) final
{
ASSERT(!m_conditionFiltersBytecodeWritten);
m_filtersWithConditionBytecodeWritten += bytecode.size();
@@ -194,13 +203,13 @@
writeToFile(Data(bytecode.data(), bytecode.size()));
}
- void writeConditionedFiltersBytecode(Vector<DFABytecode>&& bytecode) override
+ void writeTopURLFiltersBytecode(Vector<DFABytecode>&& bytecode) final
{
m_conditionFiltersBytecodeWritten += bytecode.size();
writeToFile(Data(bytecode.data(), bytecode.size()));
}
- void writeActions(Vector<SerializedActionByte>&& actions) override
+ void writeActions(Vector<SerializedActionByte>&& actions, bool conditionsApplyOnlyToDomain) final
{
ASSERT(!m_filtersWithoutConditionsBytecodeWritten);
ASSERT(!m_filtersWithConditionBytecodeWritten);
@@ -207,15 +216,17 @@
ASSERT(!m_conditionFiltersBytecodeWritten);
ASSERT(!m_actionsWritten);
m_actionsWritten += actions.size();
+ m_conditionsApplyOnlyToDomain = conditionsApplyOnlyToDomain;
writeToFile(Data(actions.data(), actions.size()));
}
- void finalize() override
+ void finalize() final
{
m_metaData.actionsSize = m_actionsWritten;
m_metaData.filtersWithoutConditionsBytecodeSize = m_filtersWithoutConditionsBytecodeWritten;
m_metaData.filtersWithConditionsBytecodeSize = m_filtersWithConditionBytecodeWritten;
m_metaData.conditionedFiltersBytecodeSize = m_conditionFiltersBytecodeWritten;
+ m_metaData.conditionsApplyOnlyToDomain = m_conditionsApplyOnlyToDomain;
Data header = encodeContentExtensionMetaData(m_metaData);
if (!m_fileError && WebCore::seekFile(m_fileHandle, 0ll, WebCore::FileSeekOrigin::SeekFromBeginning) == -1) {
@@ -242,6 +253,7 @@
size_t m_filtersWithConditionBytecodeWritten { 0 };
size_t m_conditionFiltersBytecodeWritten { 0 };
size_t m_actionsWritten { 0 };
+ bool m_conditionsApplyOnlyToDomain { false };
bool m_fileError { false };
};
@@ -285,6 +297,7 @@
auto compiledContentExtensionData = WebKit::WebCompiledContentExtensionData(
WTFMove(sharedMemory),
fileData,
+ ConditionsApplyOnlyToDomainOffset,
ContentExtensionFileHeaderSize,
metaData.actionsSize,
ContentExtensionFileHeaderSize
@@ -383,7 +396,7 @@
auto file = WebCore::openFile(constructedPath(m_storePath, identifier), WebCore::OpenForWrite);
if (file == WebCore::invalidPlatformFileHandle)
return;
- ContentExtensionMetaData invalidHeader = {0, 0, 0, 0, 0};
+ ContentExtensionMetaData invalidHeader;
auto bytesWritten = WebCore::writeToFile(file, reinterpret_cast<const char*>(&invalidHeader), sizeof(invalidHeader));
ASSERT_UNUSED(bytesWritten, bytesWritten == sizeof(invalidHeader));
WebCore::closeFile(file);
@@ -392,12 +405,12 @@
const std::error_category& userContentExtensionStoreErrorCategory()
{
class UserContentExtensionStoreErrorCategory : public std::error_category {
- const char* name() const noexcept override
+ const char* name() const noexcept final
{
return "user content extension store";
}
- std::string message(int errorCode) const override
+ std::string message(int errorCode) const final
{
switch (static_cast<UserContentExtensionStore::Error>(errorCode)) {
case UserContentExtensionStore::Error::LookupFailed:
Modified: trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.h (213668 => 213669)
--- trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.h 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.h 2017-03-09 22:17:13 UTC (rev 213669)
@@ -50,7 +50,7 @@
// This should be incremented every time a functional change is made to the bytecode, file format, etc.
// to prevent crashing while loading old data.
- const static uint32_t CurrentContentExtensionFileVersion = 7;
+ const static uint32_t CurrentContentExtensionFileVersion = 8;
static UserContentExtensionStore& defaultStore();
static Ref<UserContentExtensionStore> storeWithPath(const WTF::String& storePath);
Modified: trunk/Tools/ChangeLog (213668 => 213669)
--- trunk/Tools/ChangeLog 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Tools/ChangeLog 2017-03-09 22:17:13 UTC (rev 213669)
@@ -1,3 +1,14 @@
+2017-03-09 Alex Christensen <[email protected]>
+
+ [Content Extensions] Introduce if-top-url and unless-top-url
+ https://bugs.webkit.org/show_bug.cgi?id=169433
+
+ Reviewed by Brady Eidson.
+
+ * TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
+ (TestWebKitAPI::TEST_F):
+ Add tests for new functionality and new failure types.
+
2017-03-09 Srinivasan Vijayaraghavan <[email protected]>
JSC EWS repeat the log message thrice about patch not being relevant
Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp (213668 => 213669)
--- trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp 2017-03-09 22:07:46 UTC (rev 213668)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp 2017-03-09 22:17:13 UTC (rev 213669)
@@ -85,7 +85,8 @@
Vector<ContentExtensions::SerializedActionByte> actions;
Vector<ContentExtensions::DFABytecode> filtersWithoutConditions;
Vector<ContentExtensions::DFABytecode> filtersWithConditions;
- Vector<ContentExtensions::DFABytecode> conditionedFilters;
+ Vector<ContentExtensions::DFABytecode> topURLFilters;
+ bool conditionsApplyOnlyToDomain { false };
};
class InMemoryContentExtensionCompilationClient final : public ContentExtensions::ContentExtensionCompilationClient {
@@ -96,17 +97,19 @@
EXPECT_EQ(data.actions.size(), 0ull);
EXPECT_EQ(data.filtersWithoutConditions.size(), 0ull);
EXPECT_EQ(data.filtersWithConditions.size(), 0ull);
- EXPECT_EQ(data.conditionedFilters.size(), 0ull);
+ EXPECT_EQ(data.topURLFilters.size(), 0ull);
}
- void writeActions(Vector<ContentExtensions::SerializedActionByte>&& actions) final
+ void writeActions(Vector<ContentExtensions::SerializedActionByte>&& actions, bool conditionsApplyOnlyToDomain) final
{
EXPECT_FALSE(finalized);
EXPECT_EQ(m_data.actions.size(), 0ull);
EXPECT_EQ(m_data.filtersWithoutConditions.size(), 0ull);
EXPECT_EQ(m_data.filtersWithConditions.size(), 0ull);
- EXPECT_EQ(m_data.conditionedFilters.size(), 0ull);
+ EXPECT_EQ(m_data.topURLFilters.size(), 0ull);
+ EXPECT_EQ(m_data.actions.size(), 0ull);
m_data.actions.appendVector(actions);
+ m_data.conditionsApplyOnlyToDomain = conditionsApplyOnlyToDomain;
}
void writeFiltersWithoutConditionsBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) final
@@ -113,7 +116,7 @@
{
EXPECT_FALSE(finalized);
EXPECT_EQ(m_data.filtersWithConditions.size(), 0ull);
- EXPECT_EQ(m_data.conditionedFilters.size(), 0ull);
+ EXPECT_EQ(m_data.topURLFilters.size(), 0ull);
m_data.filtersWithoutConditions.appendVector(bytecode);
}
@@ -120,14 +123,14 @@
void writeFiltersWithConditionsBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) final
{
EXPECT_FALSE(finalized);
- EXPECT_EQ(m_data.conditionedFilters.size(), 0ull);
+ EXPECT_EQ(m_data.topURLFilters.size(), 0ull);
m_data.filtersWithConditions.appendVector(bytecode);
}
- void writeConditionedFiltersBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) final
+ void writeTopURLFiltersBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) final
{
EXPECT_FALSE(finalized);
- m_data.conditionedFilters.appendVector(bytecode);
+ m_data.topURLFilters.appendVector(bytecode);
}
void finalize() final
@@ -171,8 +174,9 @@
unsigned filtersWithoutConditionsBytecodeLength() const final { return m_data.filtersWithoutConditions.size(); }
const ContentExtensions::DFABytecode* filtersWithConditionsBytecode() const final { return m_data.filtersWithConditions.data(); }
unsigned filtersWithConditionsBytecodeLength() const final { return m_data.filtersWithConditions.size(); }
- const ContentExtensions::DFABytecode* conditionedFiltersBytecode() const final { return m_data.conditionedFilters.data(); }
- unsigned conditionedFiltersBytecodeLength() const final { return m_data.conditionedFilters.size(); }
+ const ContentExtensions::DFABytecode* topURLFiltersBytecode() const final { return m_data.topURLFilters.data(); }
+ unsigned topURLFiltersBytecodeLength() const final { return m_data.topURLFilters.size(); }
+ bool conditionsApplyOnlyToDomain() const final { return m_data.conditionsApplyOnlyToDomain; }
private:
InMemoryCompiledContentExtension(CompiledContentExtensionData&& data)
@@ -788,7 +792,72 @@
testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector });
testRequest(backend, mainDocumentRequest("http://notwebkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies });
testRequest(backend, mainDocumentRequest("http://webkit.org/except-this-ignore-previous-trigger-on-scripts.html.test.html", ResourceType::Script), { ContentExtensions::ActionType::BlockCookies, ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad, ContentExtensions::ActionType::BlockCookies });
+}
+TEST_F(ContentExtensionTest, TopURL)
+{
+ const Vector<ContentExtensions::ActionType> blockLoad = { ContentExtensions::ActionType::BlockLoad };
+ const Vector<ContentExtensions::ActionType> blockCookies = { ContentExtensions::ActionType::BlockCookies };
+
+ auto ifTopURL = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-top-url\":[\"^http://web.*kit.org\"]}}]");
+ testRequest(ifTopURL, mainDocumentRequest("http://webkit.org/test.html"), blockLoad);
+ testRequest(ifTopURL, mainDocumentRequest("http://webkit.org/test.not_html"), { });
+ testRequest(ifTopURL, mainDocumentRequest("http://WEBKIT.org/test.html"), blockLoad);
+ testRequest(ifTopURL, mainDocumentRequest("http://webkit.org/TEST.html"), blockLoad);
+ testRequest(ifTopURL, mainDocumentRequest("http://web__kit.org/test.html"), blockLoad);
+ testRequest(ifTopURL, mainDocumentRequest("http://webk__it.org/test.html"), { });
+ testRequest(ifTopURL, mainDocumentRequest("http://web__kit.org/test.not_html"), { });
+ testRequest(ifTopURL, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+ testRequest(ifTopURL, subResourceRequest("http://not_webkit.org/test.html", "http://webkit.org/not_test.html"), blockLoad);
+ testRequest(ifTopURL, subResourceRequest("http://not_webkit.org/test.html", "http://not_webkit.org/not_test.html"), { });
+ testRequest(ifTopURL, subResourceRequest("http://webkit.org/test.html", "http://not_webkit.org/not_test.html"), { });
+ testRequest(ifTopURL, subResourceRequest("http://webkit.org/test.html", "http://not_webkit.org/test.html"), { });
+ testRequest(ifTopURL, subResourceRequest("http://webkit.org/test.html", "http://webkit.org/test.html"), blockLoad);
+ testRequest(ifTopURL, subResourceRequest("http://webkit.org/test.html", "http://example.com/#http://webkit.org/test.html"), { });
+ testRequest(ifTopURL, subResourceRequest("http://example.com/#http://webkit.org/test.html", "http://webkit.org/test.html"), blockLoad);
+
+ auto unlessTopURL = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-top-url\":[\"^http://web.*kit.org\"]}}]");
+ testRequest(unlessTopURL, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(unlessTopURL, mainDocumentRequest("http://WEBKIT.org/test.html"), { });
+ testRequest(unlessTopURL, mainDocumentRequest("http://webkit.org/TEST.html"), { });
+ testRequest(unlessTopURL, mainDocumentRequest("http://webkit.org/test.not_html"), { });
+ testRequest(unlessTopURL, mainDocumentRequest("http://web__kit.org/test.html"), { });
+ testRequest(unlessTopURL, mainDocumentRequest("http://webk__it.org/test.html"), blockLoad);
+ testRequest(unlessTopURL, mainDocumentRequest("http://web__kit.org/test.not_html"), { });
+ testRequest(unlessTopURL, mainDocumentRequest("http://not_webkit.org/test.html"), blockLoad);
+ testRequest(unlessTopURL, subResourceRequest("http://not_webkit.org/test.html", "http://webkit.org/not_test.html"), { });
+ testRequest(unlessTopURL, subResourceRequest("http://not_webkit.org/test.html", "http://not_webkit.org/not_test.html"), blockLoad);
+ testRequest(unlessTopURL, subResourceRequest("http://webkit.org/test.html", "http://not_webkit.org/not_test.html"), blockLoad);
+ testRequest(unlessTopURL, subResourceRequest("http://webkit.org/test.html", "http://not_webkit.org/test.html"), blockLoad);
+ testRequest(unlessTopURL, subResourceRequest("http://webkit.org/test.html", "http://webkit.org/test.html"), { });
+ testRequest(unlessTopURL, subResourceRequest("http://webkit.org/test.html", "http://example.com/#http://webkit.org/test.html"), blockLoad);
+ testRequest(unlessTopURL, subResourceRequest("http://example.com/#http://webkit.org/test.html", "http://webkit.org/test.html"), { });
+
+ auto ifTopURLMatchesEverything = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-top-url\":[\".*\"]}}]");
+ testRequest(ifTopURLMatchesEverything, mainDocumentRequest("http://webkit.org/test.html"), blockLoad);
+ testRequest(ifTopURLMatchesEverything, mainDocumentRequest("http://webkit.org/test.not_html"), { });
+ testRequest(ifTopURLMatchesEverything, mainDocumentRequest("http://not_webkit.org/test.html"), blockLoad);
+
+ auto unlessTopURLMatchesEverything = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-top-url\":[\".*\"]}}]");
+ testRequest(unlessTopURLMatchesEverything, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(unlessTopURLMatchesEverything, mainDocumentRequest("http://webkit.org/test.not_html"), { });
+ testRequest(unlessTopURLMatchesEverything, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+
+ auto mixedConditions = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-top-url\":[\"^http://web.*kit.org\"]}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"resource-type\":[\"document\"], \"url-filter\":\"test\\\\.html\", \"unless-top-url\":[\"^http://web.*kit.org\"]}}]");
+ testRequest(mixedConditions, mainDocumentRequest("http://webkit.org/test.html"), blockLoad);
+ testRequest(mixedConditions, mainDocumentRequest("http://not_webkit.org/test.html"), blockCookies);
+ testRequest(mixedConditions, subResourceRequest("http://webkit.org/test.html", "http://not_webkit.org", ResourceType::Document), blockCookies);
+ testRequest(mixedConditions, subResourceRequest("http://webkit.org/test.html", "http://not_webkit.org", ResourceType::Image), { });
+
+ auto caseSensitive = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-top-url\":[\"^http://web.*kit.org/test\"], \"top-url-filter-is-case-sensitive\":true}}]");
+ testRequest(caseSensitive, mainDocumentRequest("http://webkit.org/test.html"), blockLoad);
+ testRequest(caseSensitive, mainDocumentRequest("http://WEBKIT.org/test.html"), blockLoad); // domains are canonicalized before running regexes.
+ testRequest(caseSensitive, mainDocumentRequest("http://webkit.org/TEST.html"), { });
+
+ auto caseInsensitive = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-top-url\":[\"^http://web.*kit.org/test\"]}}]");
+ testRequest(caseInsensitive, mainDocumentRequest("http://webkit.org/test.html"), blockLoad);
+ testRequest(caseInsensitive, mainDocumentRequest("http://webkit.org/TEST.html"), blockLoad);
}
TEST_F(ContentExtensionTest, MultipleExtensions)
@@ -1364,6 +1433,17 @@
checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"unless-domain\":[\"a\"]}}]", { });
checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[\"}}]",
ContentExtensions::ContentExtensionError::JSONInvalidRegex);
+
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"],\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONMultipleConditions);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-top-url\":[\"a\"],\"unless-top-url\":[]}}]", ContentExtensions::ContentExtensionError::JSONMultipleConditions);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-top-url\":[\"a\"],\"unless-top-url\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONMultipleConditions);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"],\"if-top-url\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONMultipleConditions);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-top-url\":[],\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONMultipleConditions);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-top-url\":[\"a\"],\"if-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONMultipleConditions);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-top-url\":[\"a\"]}}, {\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONTopURLAndDomainConditions);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-top-url\":[\"a\"]}}, {\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONTopURLAndDomainConditions);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-top-url\":[\"[\"]}}]", ContentExtensions::ContentExtensionError::JSONInvalidRegex);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\", \"unexpected-identifier-should-be-ignored\":5}}]", { });
}
TEST_F(ContentExtensionTest, StrictPrefixSeparatedMachines1)