Diff
Modified: trunk/LayoutTests/ChangeLog (184115 => 184116)
--- trunk/LayoutTests/ChangeLog 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/LayoutTests/ChangeLog 2015-05-11 19:54:15 UTC (rev 184116)
@@ -1,3 +1,14 @@
+2015-05-11 Alex Christensen <[email protected]>
+
+ [Content Extensions] Support domain-specific rules and exceptions.
+ https://bugs.webkit.org/show_bug.cgi?id=144833
+
+ Reviewed by Darin Adler.
+
+ * http/tests/contentextensions/domain-rules-expected.txt: Added.
+ * http/tests/contentextensions/domain-rules.html: Added.
+ * http/tests/contentextensions/domain-rules.html.json: Added.
+
2015-05-10 Filip Pizlo <[email protected]>
We shouldn't promote LoadVarargs to a sequence of GetStacks and PutStacks if doing so would exceed the LoadVarargs' limit
Added: trunk/LayoutTests/http/tests/contentextensions/domain-rules-expected.txt (0 => 184116)
--- trunk/LayoutTests/http/tests/contentextensions/domain-rules-expected.txt (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/domain-rules-expected.txt 2015-05-11 19:54:15 UTC (rev 184116)
@@ -0,0 +1,21 @@
+layer at (0,0) size 800x600
+ RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+ RenderBlock {HTML} at (0,0) size 800x600
+ RenderBody {BODY} at (8,8) size 784x584
+ RenderText {#text} at (0,0) size 200x18
+ text run at (0,0) width 200: "The images below should load."
+ RenderBR {BR} at (199,14) size 1x0
+ RenderImage {IMG} at (0,18) size 100x100
+ RenderBR {BR} at (100,118) size 0x0
+ RenderImage {IMG} at (0,118) size 100x100
+ RenderBR {BR} at (100,218) size 0x0
+ RenderText {#text} at (0,218) size 242x18
+ text run at (0,218) width 242: "The images below should be blocked."
+ RenderBR {BR} at (241,232) size 1x0
+ RenderImage {IMG} at (0,236) size 0x0
+ RenderBR {BR} at (0,236) size 0x0
+ RenderImage {IMG} at (0,236) size 0x0
+ RenderBR {BR} at (0,236) size 0x0
+ RenderImage {IMG} at (0,236) size 0x0
+ RenderBR {BR} at (0,236) size 0x0
Added: trunk/LayoutTests/http/tests/contentextensions/domain-rules.html (0 => 184116)
--- trunk/LayoutTests/http/tests/contentextensions/domain-rules.html (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/domain-rules.html 2015-05-11 19:54:15 UTC (rev 184116)
@@ -0,0 +1,9 @@
+<body>
+The images below should load.<br>
+<img src=""
+<img src=""
+The images below should be blocked.<br>
+<img src=""
+<img src=""
+<img src=""
+</body>
Added: trunk/LayoutTests/http/tests/contentextensions/domain-rules.html.json (0 => 184116)
--- trunk/LayoutTests/http/tests/contentextensions/domain-rules.html.json (rev 0)
+++ trunk/LayoutTests/http/tests/contentextensions/domain-rules.html.json 2015-05-11 19:54:15 UTC (rev 184116)
@@ -0,0 +1,46 @@
+[
+ {
+ "action": {
+ "type": "block"
+ },
+ "trigger": {
+ "url-filter": "should_be_blocked_1"
+ }
+ },
+ {
+ "action": {
+ "type": "block"
+ },
+ "trigger": {
+ "url-filter": "should_be_blocked_2",
+ "if-domain": ["127.0.0.1"]
+ }
+ },
+ {
+ "action": {
+ "type": "block"
+ },
+ "trigger": {
+ "url-filter": "should_be_blocked_3",
+ "unless-domain": ["this domain should not match"]
+ }
+ },
+ {
+ "action": {
+ "type": "block"
+ },
+ "trigger": {
+ "url-filter": "should_not_be_blocked_1",
+ "if-domain": ["this domain should not match either"]
+ }
+ },
+ {
+ "action": {
+ "type": "block"
+ },
+ "trigger": {
+ "url-filter": "should_not_be_blocked_2",
+ "unless-domain": ["127.0.0.1"]
+ }
+ }
+]
Modified: trunk/Source/WebCore/ChangeLog (184115 => 184116)
--- trunk/Source/WebCore/ChangeLog 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/ChangeLog 2015-05-11 19:54:15 UTC (rev 184116)
@@ -1,3 +1,73 @@
+2015-05-11 Alex Christensen <[email protected]>
+
+ [Content Extensions] Support domain-specific rules and exceptions.
+ https://bugs.webkit.org/show_bug.cgi?id=144833
+
+ Reviewed by Darin Adler.
+
+ Test: http/tests/contentextensions/domain-rules.html
+ (And lots of API tests)
+
+ This patch adds if-domain and unless-domain to the trigger in the JSON format.
+ if-domain makes the rule apply only to domains in the list.
+ unless-domain makes the rule apply to domains that are not in the list.
+
+ All rules without if-domain or unless-domain are compiled into a set of DFAs. This behavior is unchanged.
+ All rules with if-domain or unless-domain are compiled into a separate set of DFAs.
+ The domains are also compiled into another set of DFAs. This makes 3 arrays of DFA bytecode instead of 1.
+
+ If there are no domain specific rules, there is no change in behavior.
+ If there are domain specific rules, the URL will be checked for matches in both
+ filtersWithoutDomainsBytecode and filtersWithDomainsBytecode. If there are matches from
+ filtersWithDomainsBytecode then the domain of the main document will be checked with
+ domainFiltersBytecode to see which of the matches applies to this domain.
+
+ * contentextensions/CombinedURLFilters.cpp:
+ (WebCore::ContentExtensions::CombinedURLFilters::isEmpty):
+ (WebCore::ContentExtensions::CombinedURLFilters::addDomain):
+ * contentextensions/CombinedURLFilters.h:
+ Added addDomain, which adds characters from a domain to the prefix tree directly without using YARR.
+ * contentextensions/CompiledContentExtension.cpp:
+ (WebCore::ContentExtensions::CompiledContentExtension::globalDisplayNoneSelectors):
+ * contentextensions/CompiledContentExtension.h:
+ * contentextensions/ContentExtensionCompiler.cpp:
+ (WebCore::ContentExtensions::serializeActions):
+ (WebCore::ContentExtensions::compileRuleList):
+ Separate the rules into rules with domains and rules without domains and compile to bytecode.
+ * contentextensions/ContentExtensionCompiler.h:
+ Updated compiler interface to reflect the slightly more complicated structure.
+ * contentextensions/ContentExtensionError.cpp:
+ (WebCore::ContentExtensions::contentExtensionErrorCategory):
+ * contentextensions/ContentExtensionError.h:
+ Added new parsing errors.
+ * contentextensions/ContentExtensionParser.cpp:
+ (WebCore::ContentExtensions::getDomainList):
+ (WebCore::ContentExtensions::loadTrigger):
+ (WebCore::ContentExtensions::loadEncodedRules):
+ * contentextensions/ContentExtensionRule.h:
+ (WebCore::ContentExtensions::Trigger::operator==):
+ Parse the new domain structures from JSON into the Trigger structure.
+ * contentextensions/ContentExtensionsBackend.cpp:
+ (WebCore::ContentExtensions::ContentExtensionsBackend::actionsForResourceLoad):
+ Interpret as much bytecode as necessary to determine which rules apply to this URL and domain.
+ * contentextensions/DFABytecode.h:
+ (WebCore::ContentExtensions::instructionSizeWithArguments):
+ * contentextensions/DFABytecodeCompiler.cpp:
+ (WebCore::ContentExtensions::DFABytecodeCompiler::emitAppendAction):
+ (WebCore::ContentExtensions::DFABytecodeCompiler::emitTestFlagsAndAppendAction):
+ (WebCore::ContentExtensions::DFABytecodeCompiler::compileNode):
+ * contentextensions/DFABytecodeCompiler.h:
+ * contentextensions/DFABytecodeInterpreter.cpp:
+ (WebCore::ContentExtensions::DFABytecodeInterpreter::interpretAppendAction):
+ (WebCore::ContentExtensions::DFABytecodeInterpreter::interpretTestFlagsAndAppendAction):
+ (WebCore::ContentExtensions::DFABytecodeInterpreter::actionsFromDFARoot):
+ (WebCore::ContentExtensions::DFABytecodeInterpreter::interpret):
+ * contentextensions/DFABytecodeInterpreter.h:
+ * loader/ResourceLoadInfo.h:
+ Keep track of which actions have an if-domain trigger. If an action is in the
+ filtersWithDomainsBytecode, then it either has an if-domain or an unless-domain in its trigger.
+ This requires additional information in the bytecode, so there are two new bytecode types.
+
2015-05-11 Yoav Weiss <[email protected]>
Add srcset, sizes and picture to the features json
Modified: trunk/Source/WebCore/contentextensions/CombinedURLFilters.cpp (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/CombinedURLFilters.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/CombinedURLFilters.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -115,11 +115,40 @@
{
}
-bool CombinedURLFilters::isEmpty()
+bool CombinedURLFilters::isEmpty() const
{
return m_prefixTreeRoot->edges.isEmpty();
}
+void CombinedURLFilters::addDomain(uint64_t actionId, const String& domain)
+{
+ // This is like adding (.|^)domain$ by adding two Vector<Term>'s,
+ // but interpreting domain as a series of characters, not a regular _expression_.
+ // This way a domain of "webkit.org" will match "bugs.webkit.org" and "webkit.org".
+ // FIXME: Add support for matching only subdomains or no subdomains.
+ Vector<Term> prependDot;
+ Vector<Term> prependBeginningOfLine;
+ prependDot.reserveInitialCapacity(domain.length() + 3);
+ prependBeginningOfLine.reserveInitialCapacity(domain.length() + 1); // This is just no .* at the beginning.
+
+ Term canonicalDotStar(Term::UniversalTransition);
+ canonicalDotStar.quantify(AtomQuantifier::ZeroOrMore);
+ prependDot.uncheckedAppend(canonicalDotStar);
+ prependDot.uncheckedAppend(Term('.', true));
+
+ for (unsigned i = 0; i < domain.length(); i++) {
+ ASSERT(isASCII(domain[i]));
+ ASSERT(!isASCIIUpper(domain[i]));
+ prependDot.uncheckedAppend(Term(domain[i], true));
+ prependBeginningOfLine.uncheckedAppend(Term(domain[i], true));
+ }
+ prependDot.uncheckedAppend(Term::EndOfLineAssertionTerm);
+ prependBeginningOfLine.uncheckedAppend(Term::EndOfLineAssertionTerm);
+
+ addPattern(actionId, prependDot);
+ addPattern(actionId, prependBeginningOfLine);
+}
+
void CombinedURLFilters::addPattern(uint64_t actionId, const Vector<Term>& pattern)
{
ASSERT_WITH_MESSAGE(!pattern.isEmpty(), "The parser should have excluded empty patterns before reaching CombinedURLFilters.");
Modified: trunk/Source/WebCore/contentextensions/CombinedURLFilters.h (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/CombinedURLFilters.h 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/CombinedURLFilters.h 2015-05-11 19:54:15 UTC (rev 184116)
@@ -30,6 +30,7 @@
#include "ContentExtensionsDebugging.h"
#include "NFA.h"
+#include <wtf/Forward.h>
#include <wtf/Vector.h>
namespace WebCore {
@@ -44,10 +45,11 @@
CombinedURLFilters();
~CombinedURLFilters();
- void addPattern(uint64_t patternId, const Vector<Term>& pattern);
+ void addPattern(uint64_t actionId, const Vector<Term>& pattern);
+ void addDomain(uint64_t actionId, const String& domain);
void processNFAs(size_t maxNFASize, std::function<void(NFA&&)> handler);
- bool isEmpty();
+ bool isEmpty() const;
#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
size_t memoryUsed() const;
Modified: trunk/Source/WebCore/contentextensions/CompiledContentExtension.cpp (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/CompiledContentExtension.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/CompiledContentExtension.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -38,7 +38,7 @@
Vector<String> CompiledContentExtension::globalDisplayNoneSelectors(Vector<bool>& pagesUsed)
{
- DFABytecodeInterpreter interpreter(bytecode(), bytecodeLength(), pagesUsed);
+ DFABytecodeInterpreter interpreter(filtersWithoutDomainsBytecode(), filtersWithoutDomainsBytecodeLength(), pagesUsed);
DFABytecodeInterpreter::Actions actionLocations = interpreter.actionsFromDFARoot();
Vector<Action> globalActions;
Modified: trunk/Source/WebCore/contentextensions/CompiledContentExtension.h (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/CompiledContentExtension.h 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/CompiledContentExtension.h 2015-05-11 19:54:15 UTC (rev 184116)
@@ -40,8 +40,12 @@
public:
virtual ~CompiledContentExtension();
- virtual const DFABytecode* bytecode() const = 0;
- virtual unsigned bytecodeLength() const = 0;
+ virtual const DFABytecode* filtersWithoutDomainsBytecode() const = 0;
+ virtual unsigned filtersWithoutDomainsBytecodeLength() const = 0;
+ virtual const DFABytecode* filtersWithDomainsBytecode() const = 0;
+ virtual unsigned filtersWithDomainsBytecodeLength() const = 0;
+ virtual const DFABytecode* domainFiltersBytecode() const = 0;
+ virtual unsigned domainFiltersBytecodeLength() const = 0;
virtual const SerializedActionByte* actions() const = 0;
virtual unsigned actionsLength() const = 0;
Vector<String> globalDisplayNoneSelectors(Vector<bool>& pagesUsed);
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -96,8 +96,10 @@
continue;
}
- // Identical sequential actions should not be rewritten.
- if (ruleIndex && rule.action() == ruleList[ruleIndex - 1].action()) {
+ // Identical sequential actions should not be rewritten unless there are domains in the trigger.
+ // If there are domains in the trigger, we need to distinguish the actions by index to tell if we need to apply it
+ // by comparing the output of the filters with domains and the domain filters.
+ if (ruleIndex && rule.action() == ruleList[ruleIndex - 1].action() && rule.trigger().domains.isEmpty()) {
actionLocations.append(actionLocations[ruleIndex - 1]);
continue;
}
@@ -156,8 +158,13 @@
UniversalActionLocationsSet universalActionLocations;
- CombinedURLFilters combinedURLFilters;
- URLFilterParser urlFilterParser(combinedURLFilters);
+ // FIXME: These don't all need to be in memory at the same time.
+ CombinedURLFilters filtersWithoutDomains;
+ CombinedURLFilters filtersWithDomains;
+ CombinedURLFilters domainFilters;
+ URLFilterParser filtersWithoutDomainParser(filtersWithoutDomains);
+ URLFilterParser filtersWithDomainParser(filtersWithDomains);
+
bool ignorePreviousRulesSeen = false;
for (unsigned ruleIndex = 0; ruleIndex < parsedRuleList.size(); ++ruleIndex) {
const ContentExtensionRule& contentExtensionRule = parsedRuleList[ruleIndex];
@@ -166,18 +173,39 @@
// High bits are used for flags. This should match how they are used in DFABytecodeCompiler::compileNode.
uint64_t actionLocationAndFlags = (static_cast<uint64_t>(trigger.flags) << 32) | static_cast<uint64_t>(actionLocations[ruleIndex]);
- URLFilterParser::ParseStatus status = urlFilterParser.addPattern(trigger.urlFilter, trigger.urlFilterIsCaseSensitive, actionLocationAndFlags);
-
- if (status == URLFilterParser::MatchesEverything) {
- if (ignorePreviousRulesSeen)
- return ContentExtensionError::RegexMatchesEverythingAfterIgnorePreviousRules;
- universalActionLocations.add(actionLocationAndFlags);
+ URLFilterParser::ParseStatus status = URLFilterParser::Ok;
+ if (trigger.domains.isEmpty()) {
+ ASSERT(trigger.domainCondition == Trigger::DomainCondition::None);
+ status = filtersWithoutDomainParser.addPattern(trigger.urlFilter, trigger.urlFilterIsCaseSensitive, actionLocationAndFlags);
+ if (status == URLFilterParser::MatchesEverything) {
+ if (ignorePreviousRulesSeen)
+ return ContentExtensionError::RegexMatchesEverythingAfterIgnorePreviousRules;
+ universalActionLocations.add(actionLocationAndFlags);
+ status = URLFilterParser::Ok;
+ }
+ if (status != URLFilterParser::Ok) {
+ dataLogF("Error while parsing %s: %s\n", trigger.urlFilter.utf8().data(), URLFilterParser::statusString(status).utf8().data());
+ return ContentExtensionError::JSONInvalidRegex;
+ }
+ } else {
+ if (trigger.domainCondition == Trigger::DomainCondition::IfDomain)
+ actionLocationAndFlags |= IfDomainFlag;
+ else {
+ ASSERT(trigger.domainCondition == Trigger::DomainCondition::UnlessDomain);
+ ASSERT(!(actionLocationAndFlags & IfDomainFlag));
+ }
+
+ status = filtersWithDomainParser.addPattern(trigger.urlFilter, trigger.urlFilterIsCaseSensitive, actionLocationAndFlags);
+ if (status == URLFilterParser::MatchesEverything)
+ return ContentExtensionError::RegexMatchesEverythingWithDomains;
+ if (status != URLFilterParser::Ok) {
+ dataLogF("Error while parsing %s: %s\n", trigger.urlFilter.utf8().data(), URLFilterParser::statusString(status).utf8().data());
+ return ContentExtensionError::JSONInvalidRegex;
+ }
+ for (const String& domain : trigger.domains)
+ domainFilters.addDomain(actionLocationAndFlags, domain);
}
-
- if (status != URLFilterParser::Ok && status != URLFilterParser::MatchesEverything) {
- dataLogF("Error while parsing %s: %s\n", trigger.urlFilter.utf8().data(), URLFilterParser::statusString(status).utf8().data());
- return ContentExtensionError::JSONInvalidRegex;
- }
+ ASSERT(status == URLFilterParser::Ok);
if (contentExtensionRule.action().type() == ActionType::IgnorePreviousRules)
ignorePreviousRulesSeen = true;
@@ -192,7 +220,9 @@
dataLogF(" Time spent partitioning the rules into groups: %f\n", (patternPartitioningEnd - patternPartitioningStart));
#endif
- LOG_LARGE_STRUCTURES(combinedURLFilters, combinedURLFilters.memoryUsed());
+ LOG_LARGE_STRUCTURES(filtersWithoutDomains, filtersWithoutDomains.memoryUsed());
+ LOG_LARGE_STRUCTURES(filtersWithDomains, filtersWithDomains.memoryUsed());
+ LOG_LARGE_STRUCTURES(domainFilters, domainFilters.memoryUsed());
#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
double totalNFAToByteCodeBuildTimeStart = monotonicallyIncreasingTime();
@@ -203,8 +233,9 @@
bool firstNFASeen = false;
// FIXME: Combine small NFAs to reduce the number of NFAs.
- combinedURLFilters.processNFAs(maxNFASize, [&](NFA&& nfa) {
+ filtersWithoutDomains.processNFAs(maxNFASize, [&](NFA&& nfa) {
#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+ dataLogF("filtersWithoutDomains NFA\n");
nfa.debugPrintDot();
#endif
@@ -216,7 +247,7 @@
dfa.minimize();
#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
- WTFLogAlways("DFA");
+ dataLogF("filtersWithoutDomains 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.");
@@ -230,11 +261,11 @@
DFABytecodeCompiler compiler(dfa, bytecode);
compiler.compile();
LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
- client.writeBytecode(WTF::move(bytecode));
+ client.writeFiltersWithoutDomainsBytecode(WTF::move(bytecode));
firstNFASeen = true;
});
- ASSERT(combinedURLFilters.isEmpty());
+ ASSERT(filtersWithoutDomains.isEmpty());
if (!firstNFASeen) {
// Our bytecode interpreter expects to have at least one DFA, so if we haven't seen any
@@ -249,12 +280,62 @@
DFABytecodeCompiler compiler(dummyDFA, bytecode);
compiler.compile();
LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
- client.writeBytecode(WTF::move(bytecode));
+ client.writeFiltersWithoutDomainsBytecode(WTF::move(bytecode));
}
-
LOG_LARGE_STRUCTURES(universalActionLocations, universalActionLocations.capacity() * sizeof(unsigned));
universalActionLocations.clear();
+
+ filtersWithDomains.processNFAs(maxNFASize, [&](NFA&& nfa) {
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+ dataLogF("filtersWithDomains NFA\n");
+ nfa.debugPrintDot();
+#endif
+ LOG_LARGE_STRUCTURES(nfa, nfa.memoryUsed());
+ DFA dfa = NFAToDFA::convert(nfa);
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+ dataLogF("filtersWithDomains PRE MINIMIZING DFA\n");
+ dfa.debugPrintDot();
+#endif
+ LOG_LARGE_STRUCTURES(dfa, dfa.memoryUsed());
+ // Minimizing this DFA would not be effective because all actions with domains are unique.
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+ dataLogF("filtersWithDomains POST MINIMIZING DFA\n");
+ dfa.debugPrintDot();
+#endif
+ ASSERT_WITH_MESSAGE(!dfa.nodes[dfa.root].hasActions(), "Filters with domains that match everything are not allowed right now.");
+
+ Vector<DFABytecode> bytecode;
+ DFABytecodeCompiler compiler(dfa, bytecode);
+ compiler.compile();
+ LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
+ client.writeFiltersWithDomainsBytecode(WTF::move(bytecode));
+ });
+ ASSERT(filtersWithDomains.isEmpty());
+ domainFilters.processNFAs(maxNFASize, [&](NFA&& nfa) {
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+ dataLogF("domainFilters NFA\n");
+ nfa.debugPrintDot();
+#endif
+ LOG_LARGE_STRUCTURES(nfa, nfa.memoryUsed());
+ DFA dfa = NFAToDFA::convert(nfa);
+#if CONTENT_EXTENSIONS_STATE_MACHINE_DEBUGGING
+ dataLogF("domainFilters 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 domains that match everything.");
+
+ Vector<DFABytecode> bytecode;
+ DFABytecodeCompiler compiler(dfa, bytecode);
+ compiler.compile();
+ LOG_LARGE_STRUCTURES(bytecode, bytecode.capacity() * sizeof(uint8_t));
+ client.writeDomainFiltersBytecode(WTF::move(bytecode));
+ });
+ ASSERT(domainFilters.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 (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.h 2015-05-11 19:54:15 UTC (rev 184116)
@@ -36,17 +36,15 @@
namespace WebCore {
namespace ContentExtensions {
-struct CompiledContentExtensionData {
- Vector<DFABytecode> bytecode;
- Vector<SerializedActionByte> actions;
-};
-
class ContentExtensionCompilationClient {
public:
virtual ~ContentExtensionCompilationClient() { }
- virtual void writeBytecode(Vector<DFABytecode>&&) = 0;
+ // 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 writeFiltersWithoutDomainsBytecode(Vector<DFABytecode>&&) = 0;
+ virtual void writeFiltersWithDomainsBytecode(Vector<DFABytecode>&&) = 0;
+ virtual void writeDomainFiltersBytecode(Vector<DFABytecode>&&) = 0;
virtual void finalize() = 0;
};
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionError.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -77,6 +77,14 @@
return "Invalid or unsupported regular _expression_.";
case ContentExtensionError::RegexMatchesEverythingAfterIgnorePreviousRules:
return "Regular expressions that match everything are only allowed before the first ignore-previous-rules.";
+ case ContentExtensionError::RegexMatchesEverythingWithDomains:
+ return "Regular expressions that match everything are not allowed with unless- or if-domain.";
+ case ContentExtensionError::JSONInvalidDomainList:
+ return "Invalid domain list.";
+ case ContentExtensionError::JSONDomainNotLowerCaseASCII:
+ return "Domains must be lower case ASCII. Use punycode to encode non-ASCII characters.";
+ case ContentExtensionError::JSONUnlessAndIfDomain:
+ return "A trigger cannot have both unless- and if-domain.";
}
return std::string();
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionError.h (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/ContentExtensionError.h 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionError.h 2015-05-11 19:54:15 UTC (rev 184116)
@@ -49,6 +49,9 @@
JSONInvalidTriggerFlagsArray,
JSONInvalidObjectInTriggerFlagsArray,
JSONInvalidStringInTriggerFlagsArray,
+ JSONInvalidDomainList,
+ JSONDomainNotLowerCaseASCII,
+ JSONUnlessAndIfDomain,
JSONInvalidAction,
JSONInvalidActionType,
@@ -56,9 +59,10 @@
JSONInvalidRegex,
RegexMatchesEverythingAfterIgnorePreviousRules,
+ RegexMatchesEverythingWithDomains,
};
-const std::error_category& contentExtensionErrorCategory();
+WEBCORE_EXPORT const std::error_category& contentExtensionErrorCategory();
inline std::error_code make_error_code(ContentExtensionError error)
{
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionParser.cpp (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/ContentExtensionParser.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionParser.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -46,6 +46,41 @@
namespace WebCore {
namespace ContentExtensions {
+
+static bool containsOnlyASCIIWithNoUppercase(const String& domain)
+{
+ for (unsigned i = 0; i < domain.length(); ++i) {
+ UChar c = domain.at(i);
+ if (!isASCII(c) || isASCIIUpper(c))
+ return false;
+ }
+ return true;
+}
+
+static std::error_code getDomainList(ExecState& exec, JSObject* arrayObject, Vector<String>& vector)
+{
+ ASSERT(vector.isEmpty());
+ if (!arrayObject || !isJSArray(arrayObject))
+ return ContentExtensionError::JSONInvalidDomainList;
+ JSArray* array = jsCast<JSArray*>(arrayObject);
+
+ unsigned length = array->length();
+ for (unsigned i = 0; i < length; ++i) {
+ // FIXME: JSObject::getIndex should be marked as const.
+ JSValue value = array->getIndex(&exec, i);
+ if (exec.hadException() || !value.isString())
+ return ContentExtensionError::JSONInvalidDomainList;
+
+ // Domains should be punycode encoded lower case.
+ const String& domain = jsCast<JSString*>(value)->value(&exec);
+ if (domain.isEmpty())
+ return ContentExtensionError::JSONInvalidDomainList;
+ if (!containsOnlyASCIIWithNoUppercase(domain))
+ return ContentExtensionError::JSONDomainNotLowerCaseASCII;
+ vector.append(domain);
+ }
+ return { };
+}
static std::error_code getTypeFlags(ExecState& exec, const JSValue& typeValue, ResourceFlags& flags, uint16_t (*stringToType)(const String&))
{
@@ -109,6 +144,29 @@
return typeFlagsError;
}
+ JSValue ifDomain = triggerObject.get(&exec, Identifier::fromString(&exec, "if-domain"));
+ if (!exec.hadException() && ifDomain.isObject()) {
+ auto ifDomainError = getDomainList(exec, asObject(ifDomain), trigger.domains);
+ if (ifDomainError)
+ return ifDomainError;
+ if (trigger.domains.isEmpty())
+ return ContentExtensionError::JSONInvalidDomainList;
+ ASSERT(trigger.domainCondition == Trigger::DomainCondition::None);
+ trigger.domainCondition = Trigger::DomainCondition::IfDomain;
+ }
+
+ JSValue unlessDomain = triggerObject.get(&exec, Identifier::fromString(&exec, "unless-domain"));
+ if (!exec.hadException() && unlessDomain.isObject()) {
+ if (trigger.domainCondition != Trigger::DomainCondition::None)
+ return ContentExtensionError::JSONUnlessAndIfDomain;
+ auto unlessDomainError = getDomainList(exec, asObject(unlessDomain), trigger.domains);
+ if (unlessDomainError)
+ return unlessDomainError;
+ if (trigger.domains.isEmpty())
+ return ContentExtensionError::JSONInvalidDomainList;
+ trigger.domainCondition = Trigger::DomainCondition::UnlessDomain;
+ }
+
return { };
}
@@ -160,6 +218,7 @@
static std::error_code loadEncodedRules(ExecState& exec, const String& rules, Vector<ContentExtensionRule>& ruleList)
{
+ // FIXME: JSONParse should require callbacks instead of an ExecState.
JSValue decodedRules = JSONParse(&exec, rules);
if (exec.hadException() || !decodedRules)
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionRule.h (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/ContentExtensionRule.h 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionRule.h 2015-05-11 19:54:15 UTC (rev 184116)
@@ -45,11 +45,25 @@
String urlFilter;
bool urlFilterIsCaseSensitive { false };
ResourceFlags flags { 0 };
+ Vector<String> domains;
+ enum class DomainCondition {
+ None,
+ IfDomain,
+ UnlessDomain,
+ } domainCondition { DomainCondition::None };
+
+ ~Trigger()
+ {
+ ASSERT(domains.isEmpty() == (domainCondition == DomainCondition::None));
+ }
+
bool operator==(const Trigger& other) const
{
return urlFilter == other.urlFilter
&& urlFilterIsCaseSensitive == other.urlFilterIsCaseSensitive
- && flags == other.flags;
+ && flags == other.flags
+ && domains == other.domains
+ && domainCondition == other.domainCondition;
}
};
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -88,9 +88,41 @@
for (auto& contentExtension : m_contentExtensions.values()) {
RELEASE_ASSERT(contentExtension);
const CompiledContentExtension& compiledExtension = contentExtension->compiledExtension();
- DFABytecodeInterpreter interpreter(compiledExtension.bytecode(), compiledExtension.bytecodeLength(), contentExtension->m_pagesUsed);
- DFABytecodeInterpreter::Actions triggeredActions = interpreter.interpret(urlCString, flags);
+ // FIXME: These should use a different Vector<bool> to keep track of which memory pages are used when doing memory reporting. Or just remove the memory reporting completely.
+ DFABytecodeInterpreter withoutDomainsInterpreter(compiledExtension.filtersWithoutDomainsBytecode(), compiledExtension.filtersWithoutDomainsBytecodeLength(), contentExtension->m_pagesUsed);
+ DFABytecodeInterpreter::Actions triggeredActions = withoutDomainsInterpreter.interpret(urlCString, flags);
+
+ // Check to see if there are any actions triggered with if- or unless-domain and check the domain if there are.
+ DFABytecodeInterpreter withDomainsInterpreter(compiledExtension.filtersWithDomainsBytecode(), compiledExtension.filtersWithDomainsBytecodeLength(), contentExtension->m_pagesUsed);
+
+ DFABytecodeInterpreter::Actions withDomainsPossibleActions = withDomainsInterpreter.interpret(urlCString, flags);
+ if (!withDomainsPossibleActions.isEmpty()) {
+ DFABytecodeInterpreter domainsInterpreter(compiledExtension.domainFiltersBytecode(), compiledExtension.domainFiltersBytecodeLength(), contentExtension->m_pagesUsed);
+ DFABytecodeInterpreter::Actions domainsActions = domainsInterpreter.interpret(resourceLoadInfo.mainDocumentURL.host().utf8(), flags);
+
+ DFABytecodeInterpreter::Actions ifDomainActions;
+ DFABytecodeInterpreter::Actions unlessDomainActions;
+ for (uint64_t action : domainsActions) {
+ if (action & IfDomainFlag)
+ ifDomainActions.add(action);
+ else
+ unlessDomainActions.add(action);
+ }
+
+ for (uint64_t action : withDomainsPossibleActions) {
+ if (ifDomainActions.contains(action)) {
+ // If an if-domain trigger matches, add the action.
+ ASSERT(action & IfDomainFlag);
+ triggeredActions.add(action & ~IfDomainFlag);
+ } else if (!(action & IfDomainFlag) && !unlessDomainActions.contains(action)) {
+ // If this action did not need an if-domain, it must have been an unless-domain rule.
+ // Add the action unless it matched an unless-domain trigger.
+ triggeredActions.add(action);
+ }
+ }
+ }
+
const SerializedActionByte* actions = compiledExtension.actions();
const unsigned actionsLength = compiledExtension.actionsLength();
@@ -114,8 +146,9 @@
}
}
if (!sawIgnorePreviousRules) {
- DFABytecodeInterpreter::Actions universalActions = interpreter.actionsFromDFARoot();
- for (auto actionLocation : universalActions) {
+ DFABytecodeInterpreter::Actions universalActions = withoutDomainsInterpreter.actionsFromDFARoot();
+ for (uint64_t actionLocation : universalActions) {
+ // FIXME: We shouldn't deserialize an action all the way if it is a css-display-none selector.
Action action = "" actionsLength, static_cast<unsigned>(actionLocation));
action.setExtensionIdentifier(contentExtension->identifier());
Modified: trunk/Source/WebCore/contentextensions/DFABytecode.h (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/DFABytecode.h 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/DFABytecode.h 2015-05-11 19:54:15 UTC (rev 184116)
@@ -52,11 +52,13 @@
// AppendAction has one argument:
// The action to append (4 bytes).
AppendAction,
+ AppendActionWithIfDomain,
// TestFlagsAndAppendAction has two arguments:
- // The flags to check before appending (2 bytes),
- // The action to append (4bytes).
+ // The flags to check before appending (2 bytes).
+ // The action to append (4 bytes).
TestFlagsAndAppendAction,
+ TestFlagsAndAppendActionWithIfDomain,
// Terminate has no arguments.
Terminate,
@@ -76,8 +78,10 @@
case DFABytecodeInstruction::CheckValueRangeCaseInsensitive:
return sizeof(DFABytecodeInstruction) + sizeof(uint8_t) + sizeof(uint8_t) + sizeof(unsigned);
case DFABytecodeInstruction::AppendAction:
+ case DFABytecodeInstruction::AppendActionWithIfDomain:
return sizeof(DFABytecodeInstruction) + sizeof(unsigned);
case DFABytecodeInstruction::TestFlagsAndAppendAction:
+ case DFABytecodeInstruction::TestFlagsAndAppendActionWithIfDomain:
return sizeof(DFABytecodeInstruction) + sizeof(uint16_t) + sizeof(unsigned);
case DFABytecodeInstruction::Terminate:
return sizeof(DFABytecodeInstruction);
Modified: trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -48,16 +48,22 @@
*reinterpret_cast<unsigned*>(&bytecode[index]) = value;
}
-void DFABytecodeCompiler::emitAppendAction(unsigned action)
+void DFABytecodeCompiler::emitAppendAction(unsigned action, bool ifDomain)
{
- append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::AppendAction);
+ if (ifDomain)
+ append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::AppendActionWithIfDomain);
+ else
+ append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::AppendAction);
append<unsigned>(m_bytecode, action);
}
-void DFABytecodeCompiler::emitTestFlagsAndAppendAction(uint16_t flags, unsigned action)
+void DFABytecodeCompiler::emitTestFlagsAndAppendAction(uint16_t flags, unsigned action, bool ifDomain)
{
ASSERT(flags);
- append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::TestFlagsAndAppendAction);
+ if (ifDomain)
+ append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::TestFlagsAndAppendActionWithIfDomain);
+ else
+ append<DFABytecodeInstruction>(m_bytecode, DFABytecodeInstruction::TestFlagsAndAppendAction);
append<uint16_t>(m_bytecode, flags);
append<unsigned>(m_bytecode, action);
}
@@ -106,11 +112,11 @@
m_nodeStartOffsets[index] = m_bytecode.size();
for (uint64_t action : node.actions(m_dfa)) {
- // High bits are used to store flags. See compileRuleList.
+ // High bits are used to store flags. A boolean is stored in the 48th bit. See compileRuleList.
if (action & 0xFFFF00000000)
- emitTestFlagsAndAppendAction(static_cast<uint16_t>(action >> 32), static_cast<unsigned>(action));
+ emitTestFlagsAndAppendAction(static_cast<uint16_t>(action >> 32), static_cast<unsigned>(action), action & IfDomainFlag);
else
- emitAppendAction(static_cast<unsigned>(action));
+ emitAppendAction(static_cast<unsigned>(action), action & IfDomainFlag);
}
// If we jump to the root, we don't want to re-add its actions to a HashSet.
Modified: trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.h (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.h 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.h 2015-05-11 19:54:15 UTC (rev 184116)
@@ -66,8 +66,8 @@
void compileNodeTransitions(const DFANode&);
void compileCheckForRange(const Range&);
- void emitAppendAction(unsigned);
- void emitTestFlagsAndAppendAction(uint16_t flags, unsigned);
+ void emitAppendAction(unsigned, bool ifDomain);
+ void emitTestFlagsAndAppendAction(uint16_t flags, unsigned, bool ifDomain);
void emitJump(unsigned destinationNodeIndex);
void emitCheckValue(uint8_t value, unsigned destinationNodeIndex, bool caseSensitive);
void emitCheckValueRange(uint8_t lowValue, uint8_t highValue, unsigned destinationNodeIndex, bool caseSensitive);
Modified: trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -50,18 +50,20 @@
return *reinterpret_cast<const IntType*>(&bytecode[index]);
}
-void DFABytecodeInterpreter::interpretAppendAction(unsigned& programCounter, Actions& actions)
+void DFABytecodeInterpreter::interpretAppendAction(unsigned& programCounter, Actions& actions, bool ifDomain)
{
- ASSERT(getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::AppendAction);
- actions.add(static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode), m_pagesUsed)));
+ ASSERT(getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::AppendAction
+ || getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::AppendActionWithIfDomain);
+ actions.add((ifDomain ? IfDomainFlag : 0) | static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode), m_pagesUsed)));
programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction);
}
-void DFABytecodeInterpreter::interpretTestFlagsAndAppendAction(unsigned& programCounter, uint16_t flags, Actions& actions)
+void DFABytecodeInterpreter::interpretTestFlagsAndAppendAction(unsigned& programCounter, uint16_t flags, Actions& actions, bool ifDomain)
{
- ASSERT(getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::TestFlagsAndAppendAction);
+ ASSERT(getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::TestFlagsAndAppendAction
+ || getBits<DFABytecodeInstruction>(m_bytecode, m_bytecodeLength, programCounter, m_pagesUsed) == DFABytecodeInstruction::TestFlagsAndAppendActionWithIfDomain);
if (flags & getBits<uint16_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode), m_pagesUsed))
- actions.add(static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint16_t), m_pagesUsed)));
+ actions.add((ifDomain ? IfDomainFlag : 0) | static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint16_t), m_pagesUsed)));
programCounter += instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendAction);
}
@@ -76,7 +78,7 @@
while (programCounter < dfaBytecodeLength) {
DFABytecodeInstruction instruction = static_cast<DFABytecodeInstruction>(m_bytecode[programCounter]);
if (instruction == DFABytecodeInstruction::AppendAction)
- interpretAppendAction(programCounter, actions);
+ interpretAppendAction(programCounter, actions, false);
else if (instruction == DFABytecodeInstruction::TestFlagsAndAppendAction)
programCounter += instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendAction);
else
@@ -107,7 +109,7 @@
if (instruction == DFABytecodeInstruction::AppendAction)
programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction);
else if (instruction == DFABytecodeInstruction::TestFlagsAndAppendAction)
- interpretTestFlagsAndAppendAction(programCounter, flags, actions);
+ interpretTestFlagsAndAppendAction(programCounter, flags, actions, false);
else
break;
}
@@ -203,12 +205,20 @@
break;
case DFABytecodeInstruction::AppendAction:
- interpretAppendAction(programCounter, actions);
+ interpretAppendAction(programCounter, actions, false);
break;
+ case DFABytecodeInstruction::AppendActionWithIfDomain:
+ interpretAppendAction(programCounter, actions, true);
+ break;
+
case DFABytecodeInstruction::TestFlagsAndAppendAction:
- interpretTestFlagsAndAppendAction(programCounter, flags, actions);
+ interpretTestFlagsAndAppendAction(programCounter, flags, actions, false);
break;
+
+ case DFABytecodeInstruction::TestFlagsAndAppendActionWithIfDomain:
+ interpretTestFlagsAndAppendAction(programCounter, flags, actions, true);
+ break;
default:
RELEASE_ASSERT_NOT_REACHED(); // Invalid bytecode.
Modified: trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.h (184115 => 184116)
--- trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.h 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.h 2015-05-11 19:54:15 UTC (rev 184116)
@@ -63,8 +63,8 @@
Actions actionsFromDFARoot();
private:
- void interpretAppendAction(unsigned& programCounter, Actions&);
- void interpretTestFlagsAndAppendAction(unsigned& programCounter, uint16_t flags, Actions&);
+ void interpretAppendAction(unsigned& programCounter, Actions&, bool ifDomain);
+ void interpretTestFlagsAndAppendAction(unsigned& programCounter, uint16_t flags, Actions&, bool ifDomain);
const DFABytecode* m_bytecode;
const unsigned m_bytecodeLength;
Vector<bool>& m_pagesUsed;
Modified: trunk/Source/WebCore/loader/ResourceLoadInfo.h (184115 => 184116)
--- trunk/Source/WebCore/loader/ResourceLoadInfo.h 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebCore/loader/ResourceLoadInfo.h 2015-05-11 19:54:15 UTC (rev 184116)
@@ -53,6 +53,12 @@
typedef uint16_t ResourceFlags;
+// The first 32 bits of a uint64_t action are used for the action location.
+// The next 16 bits are used for the flags (ResourceType and LoadType).
+// The next bit is used to mark actions that are from a rule with an if-domain condition.
+// The values -1 and -2 are used for removed and empty values in HashTables.
+const uint64_t IfDomainFlag = 0x0001000000000000;
+
ResourceType toResourceType(CachedResource::Type);
uint16_t readResourceType(const String&);
uint16_t readLoadType(const String&);
Modified: trunk/Source/WebKit2/ChangeLog (184115 => 184116)
--- trunk/Source/WebKit2/ChangeLog 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/ChangeLog 2015-05-11 19:54:15 UTC (rev 184116)
@@ -1,3 +1,33 @@
+2015-05-11 Alex Christensen <[email protected]>
+
+ [Content Extensions] Support domain-specific rules and exceptions.
+ https://bugs.webkit.org/show_bug.cgi?id=144833
+
+ Reviewed by Darin Adler.
+
+ * Shared/WebCompiledContentExtension.cpp:
+ (WebKit::WebCompiledContentExtension::filtersWithoutDomainsBytecode):
+ (WebKit::WebCompiledContentExtension::filtersWithoutDomainsBytecodeLength):
+ (WebKit::WebCompiledContentExtension::filtersWithDomainsBytecode):
+ (WebKit::WebCompiledContentExtension::filtersWithDomainsBytecodeLength):
+ (WebKit::WebCompiledContentExtension::domainFiltersBytecode):
+ (WebKit::WebCompiledContentExtension::domainFiltersBytecodeLength):
+ (WebKit::WebCompiledContentExtension::bytecode): Deleted.
+ (WebKit::WebCompiledContentExtension::bytecodeLength): Deleted.
+ * Shared/WebCompiledContentExtension.h:
+ * Shared/WebCompiledContentExtensionData.cpp:
+ (WebKit::WebCompiledContentExtensionData::encode):
+ (WebKit::WebCompiledContentExtensionData::decode):
+ * Shared/WebCompiledContentExtensionData.h:
+ (WebKit::WebCompiledContentExtensionData::WebCompiledContentExtensionData):
+ * UIProcess/API/APIUserContentExtensionStore.cpp:
+ (API::ContentExtensionMetaData::fileSize):
+ (API::encodeContentExtensionMetaData):
+ (API::decodeContentExtensionMetaData):
+ (API::compiledToFile):
+ (API::createExtension):
+ Keep track of 3 different types of bytecode to be able to handle domain-specific rules.
+
2015-05-11 Tim Horton <[email protected]>
Page overlay action context override should indicate the source of the request
Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp (184115 => 184116)
--- trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtension.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -44,16 +44,36 @@
{
}
-const WebCore::ContentExtensions::DFABytecode* WebCompiledContentExtension::bytecode() const
+const WebCore::ContentExtensions::DFABytecode* WebCompiledContentExtension::filtersWithoutDomainsBytecode() const
{
- return static_cast<const WebCore::ContentExtensions::DFABytecode*>(m_data.data->data()) + m_data.bytecodeOffset;
+ return static_cast<const WebCore::ContentExtensions::DFABytecode*>(m_data.data->data()) + m_data.filtersWithoutDomainsBytecodeOffset;
}
-unsigned WebCompiledContentExtension::bytecodeLength() const
+unsigned WebCompiledContentExtension::filtersWithoutDomainsBytecodeLength() const
{
- return m_data.bytecodeSize;
+ return m_data.filtersWithoutDomainsBytecodeSize;
}
+const WebCore::ContentExtensions::DFABytecode* WebCompiledContentExtension::filtersWithDomainsBytecode() const
+{
+ return static_cast<const WebCore::ContentExtensions::DFABytecode*>(m_data.data->data()) + m_data.filtersWithDomainsBytecodeOffset;
+}
+
+unsigned WebCompiledContentExtension::filtersWithDomainsBytecodeLength() const
+{
+ return m_data.filtersWithDomainsBytecodeSize;
+}
+
+const WebCore::ContentExtensions::DFABytecode* WebCompiledContentExtension::domainFiltersBytecode() const
+{
+ return static_cast<const WebCore::ContentExtensions::DFABytecode*>(m_data.data->data()) + m_data.domainFiltersBytecodeOffset;
+}
+
+unsigned WebCompiledContentExtension::domainFiltersBytecodeLength() const
+{
+ return m_data.domainFiltersBytecodeSize;
+}
+
const WebCore::ContentExtensions::SerializedActionByte* WebCompiledContentExtension::actions() const
{
return static_cast<const WebCore::ContentExtensions::SerializedActionByte*>(m_data.data->data()) + m_data.actionsOffset;
Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtension.h (184115 => 184116)
--- trunk/Source/WebKit2/Shared/WebCompiledContentExtension.h 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtension.h 2015-05-11 19:54:15 UTC (rev 184116)
@@ -44,8 +44,13 @@
private:
WebCompiledContentExtension(WebCompiledContentExtensionData&&);
- virtual const WebCore::ContentExtensions::DFABytecode* bytecode() const override;
- virtual unsigned bytecodeLength() const override;
+ virtual const WebCore::ContentExtensions::DFABytecode* filtersWithoutDomainsBytecode() const override;
+ virtual unsigned filtersWithoutDomainsBytecodeLength() const override;
+ virtual const WebCore::ContentExtensions::DFABytecode* filtersWithDomainsBytecode() const override;
+ virtual unsigned filtersWithDomainsBytecodeLength() const override;
+ virtual const WebCore::ContentExtensions::DFABytecode* domainFiltersBytecode() const override;
+ virtual unsigned domainFiltersBytecodeLength() const override;
+
virtual const WebCore::ContentExtensions::SerializedActionByte* actions() const override;
virtual unsigned actionsLength() const override;
Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp (184115 => 184116)
--- trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -40,8 +40,12 @@
encoder << actionsOffset;
encoder << actionsSize;
- encoder << bytecodeOffset;
- encoder << bytecodeSize;
+ encoder << filtersWithoutDomainsBytecodeOffset;
+ encoder << filtersWithoutDomainsBytecodeSize;
+ encoder << filtersWithDomainsBytecodeOffset;
+ encoder << filtersWithDomainsBytecodeSize;
+ encoder << domainFiltersBytecodeOffset;
+ encoder << domainFiltersBytecodeSize;
}
bool WebCompiledContentExtensionData::decode(IPC::ArgumentDecoder& decoder, WebCompiledContentExtensionData& compiledContentExtensionData)
@@ -55,10 +59,18 @@
return false;
if (!decoder.decode(compiledContentExtensionData.actionsSize))
return false;
- if (!decoder.decode(compiledContentExtensionData.bytecodeOffset))
+ if (!decoder.decode(compiledContentExtensionData.filtersWithoutDomainsBytecodeOffset))
return false;
- if (!decoder.decode(compiledContentExtensionData.bytecodeSize))
+ if (!decoder.decode(compiledContentExtensionData.filtersWithoutDomainsBytecodeSize))
return false;
+ if (!decoder.decode(compiledContentExtensionData.filtersWithDomainsBytecodeOffset))
+ return false;
+ if (!decoder.decode(compiledContentExtensionData.filtersWithDomainsBytecodeSize))
+ return false;
+ if (!decoder.decode(compiledContentExtensionData.domainFiltersBytecodeOffset))
+ return false;
+ if (!decoder.decode(compiledContentExtensionData.domainFiltersBytecodeSize))
+ return false;
return true;
}
Modified: trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h (184115 => 184116)
--- trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/Shared/WebCompiledContentExtensionData.h 2015-05-11 19:54:15 UTC (rev 184116)
@@ -45,13 +45,17 @@
{
}
- WebCompiledContentExtensionData(RefPtr<SharedMemory>&& data, NetworkCache::Data fileData, unsigned actionsOffset, unsigned actionsSize, unsigned bytecodeOffset, unsigned bytecodeSize)
+ WebCompiledContentExtensionData(RefPtr<SharedMemory>&& data, NetworkCache::Data fileData, unsigned actionsOffset, unsigned actionsSize, unsigned filtersWithoutDomainsBytecodeOffset, unsigned filtersWithoutDomainsBytecodeSize, unsigned filtersWithDomainsBytecodeOffset, unsigned filtersWithDomainsBytecodeSize, unsigned domainFiltersBytecodeOffset, unsigned domainFiltersBytecodeSize)
: data(WTF::move(data))
, fileData(fileData)
, actionsOffset(actionsOffset)
, actionsSize(actionsSize)
- , bytecodeOffset(bytecodeOffset)
- , bytecodeSize(bytecodeSize)
+ , filtersWithoutDomainsBytecodeOffset(filtersWithoutDomainsBytecodeOffset)
+ , filtersWithoutDomainsBytecodeSize(filtersWithoutDomainsBytecodeSize)
+ , filtersWithDomainsBytecodeOffset(filtersWithDomainsBytecodeOffset)
+ , filtersWithDomainsBytecodeSize(filtersWithDomainsBytecodeSize)
+ , domainFiltersBytecodeOffset(domainFiltersBytecodeOffset)
+ , domainFiltersBytecodeSize(domainFiltersBytecodeSize)
{
}
@@ -62,8 +66,12 @@
NetworkCache::Data fileData;
unsigned actionsOffset { 0 };
unsigned actionsSize { 0 };
- unsigned bytecodeOffset { 0 };
- unsigned bytecodeSize { 0 };
+ unsigned filtersWithoutDomainsBytecodeOffset { 0 };
+ unsigned filtersWithoutDomainsBytecodeSize { 0 };
+ unsigned filtersWithDomainsBytecodeOffset { 0 };
+ unsigned filtersWithDomainsBytecodeSize { 0 };
+ unsigned domainFiltersBytecodeOffset { 0 };
+ unsigned domainFiltersBytecodeSize { 0 };
};
}
Modified: trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp (184115 => 184116)
--- trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Source/WebKit2/UIProcess/API/APIUserContentExtensionStore.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -74,11 +74,22 @@
return WebCore::pathByAppendingComponent(base, "ContentExtension-" + WebCore::encodeForFileName(identifier));
}
-const size_t ContentExtensionFileHeaderSize = sizeof(uint32_t) + 2 * sizeof(uint64_t);
+const size_t ContentExtensionFileHeaderSize = sizeof(uint32_t) + 4 * sizeof(uint64_t);
struct ContentExtensionMetaData {
- uint32_t version { 1 };
+ uint32_t version { 2 };
uint64_t actionsSize { 0 };
- uint64_t bytecodeSize { 0 };
+ uint64_t filtersWithoutDomainsBytecodeSize { 0 };
+ uint64_t filtersWithDomainBytecodeSize { 0 };
+ uint64_t domainFiltersBytecodeSize { 0 };
+
+ size_t fileSize() const
+ {
+ return ContentExtensionFileHeaderSize
+ + actionsSize
+ + filtersWithoutDomainsBytecodeSize
+ + filtersWithDomainBytecodeSize
+ + domainFiltersBytecodeSize;
+ }
};
static Data encodeContentExtensionMetaData(const ContentExtensionMetaData& metaData)
@@ -87,7 +98,9 @@
encoder << metaData.version;
encoder << metaData.actionsSize;
- encoder << metaData.bytecodeSize;
+ encoder << metaData.filtersWithoutDomainsBytecodeSize;
+ encoder << metaData.filtersWithDomainBytecodeSize;
+ encoder << metaData.domainFiltersBytecodeSize;
ASSERT(encoder.bufferSize() == ContentExtensionFileHeaderSize);
return Data(encoder.buffer(), encoder.bufferSize());
@@ -107,8 +120,12 @@
return false;
if (!decoder.decode(metaData.actionsSize))
return false;
- if (!decoder.decode(metaData.bytecodeSize))
+ if (!decoder.decode(metaData.filtersWithoutDomainsBytecodeSize))
return false;
+ if (!decoder.decode(metaData.filtersWithDomainBytecodeSize))
+ return false;
+ if (!decoder.decode(metaData.domainFiltersBytecodeSize))
+ return false;
success = true;
return false;
});
@@ -163,39 +180,62 @@
: m_fileHandle(fileHandle)
, m_metaData(metaData)
{
+ ASSERT(!metaData.actionsSize);
+ ASSERT(!metaData.filtersWithoutDomainsBytecodeSize);
+ ASSERT(!metaData.filtersWithDomainBytecodeSize);
+ ASSERT(!metaData.domainFiltersBytecodeSize);
}
-
- virtual void writeBytecode(Vector<DFABytecode>&& bytecode) override
+
+ virtual void writeFiltersWithoutDomainsBytecode(Vector<DFABytecode>&& bytecode)
{
- m_bytecodeWritten += bytecode.size();
- write(Data(bytecode.data(), bytecode.size()));
+ ASSERT(!m_filtersWithDomainBytecodeWritten);
+ ASSERT(!m_domainFiltersBytecodeWritten);
+ m_filtersWithoutDomainsBytecodeWritten += bytecode.size();
+ writeToFile(Data(bytecode.data(), bytecode.size()));
}
+
+ virtual void writeFiltersWithDomainsBytecode(Vector<DFABytecode>&& bytecode)
+ {
+ ASSERT(!m_domainFiltersBytecodeWritten);
+ m_filtersWithDomainBytecodeWritten += bytecode.size();
+ writeToFile(Data(bytecode.data(), bytecode.size()));
+ }
+
+ virtual void writeDomainFiltersBytecode(Vector<DFABytecode>&& bytecode)
+ {
+ m_domainFiltersBytecodeWritten += bytecode.size();
+ writeToFile(Data(bytecode.data(), bytecode.size()));
+ }
virtual void writeActions(Vector<SerializedActionByte>&& actions) override
{
- ASSERT(!m_bytecodeWritten);
+ ASSERT(!m_filtersWithoutDomainsBytecodeWritten);
+ ASSERT(!m_filtersWithDomainBytecodeWritten);
+ ASSERT(!m_domainFiltersBytecodeWritten);
ASSERT(!m_actionsWritten);
m_actionsWritten += actions.size();
- write(Data(actions.data(), actions.size()));
+ writeToFile(Data(actions.data(), actions.size()));
}
virtual void finalize() override
{
m_metaData.actionsSize = m_actionsWritten;
- m_metaData.bytecodeSize = m_bytecodeWritten;
+ m_metaData.filtersWithoutDomainsBytecodeSize = m_filtersWithoutDomainsBytecodeWritten;
+ m_metaData.filtersWithDomainBytecodeSize = m_filtersWithDomainBytecodeWritten;
+ m_metaData.domainFiltersBytecodeSize = m_domainFiltersBytecodeWritten;
Data header = encodeContentExtensionMetaData(m_metaData);
if (!m_fileError && WebCore::seekFile(m_fileHandle, 0ll, WebCore::FileSeekOrigin::SeekFromBeginning) == -1) {
WebCore::closeFile(m_fileHandle);
m_fileError = true;
}
- write(header);
+ writeToFile(header);
}
bool hadErrorWhileWritingToFile() { return m_fileError; }
private:
- void write(const Data& data)
+ void writeToFile(const Data& data)
{
if (!m_fileError && !writeDataToFile(data, m_fileHandle)) {
WebCore::closeFile(m_fileHandle);
@@ -205,7 +245,9 @@
WebCore::PlatformFileHandle m_fileHandle;
ContentExtensionMetaData& m_metaData;
- size_t m_bytecodeWritten { 0 };
+ size_t m_filtersWithoutDomainsBytecodeWritten { 0 };
+ size_t m_filtersWithDomainBytecodeWritten { 0 };
+ size_t m_domainFiltersBytecodeWritten { 0 };
size_t m_actionsWritten { 0 };
bool m_fileError { false };
};
@@ -228,7 +270,7 @@
if (compilationClient.hadErrorWhileWritingToFile())
return UserContentExtensionStore::Error::CompileFailed;
- mappedData = mapFile(temporaryFileHandle, 0, ContentExtensionFileHeaderSize + metaData.actionsSize + metaData.bytecodeSize);
+ mappedData = mapFile(temporaryFileHandle, 0, metaData.fileSize());
WebCore::closeFile(temporaryFileHandle);
if (mappedData.isNull())
@@ -248,8 +290,18 @@
fileData,
ContentExtensionFileHeaderSize,
metaData.actionsSize,
- ContentExtensionFileHeaderSize + metaData.actionsSize,
- metaData.bytecodeSize
+ ContentExtensionFileHeaderSize
+ + metaData.actionsSize,
+ metaData.filtersWithoutDomainsBytecodeSize,
+ ContentExtensionFileHeaderSize
+ + metaData.actionsSize
+ + metaData.filtersWithoutDomainsBytecodeSize,
+ metaData.filtersWithDomainBytecodeSize,
+ ContentExtensionFileHeaderSize
+ + metaData.actionsSize
+ + metaData.filtersWithoutDomainsBytecodeSize
+ + metaData.filtersWithDomainBytecodeSize,
+ metaData.domainFiltersBytecodeSize
);
auto compiledContentExtension = WebKit::WebCompiledContentExtension::create(WTF::move(compiledContentExtensionData));
return API::UserContentExtension::create(identifier, WTF::move(compiledContentExtension));
Modified: trunk/Tools/ChangeLog (184115 => 184116)
--- trunk/Tools/ChangeLog 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Tools/ChangeLog 2015-05-11 19:54:15 UTC (rev 184116)
@@ -1,3 +1,21 @@
+2015-05-11 Alex Christensen <[email protected]>
+
+ [Content Extensions] Support domain-specific rules and exceptions.
+ https://bugs.webkit.org/show_bug.cgi?id=144833
+
+ Reviewed by Darin Adler.
+
+ * TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
+ (TestWebKitAPI::InMemoryCompiledContentExtension::createFromFilter):
+ (TestWebKitAPI::InMemoryCompiledContentExtension::create):
+ (TestWebKitAPI::InMemoryCompiledContentExtension::InMemoryCompiledContentExtension):
+ Moved CompiledContentExtensionData from ContentExtensionCompiler.h because it is only used for testing.
+ (TestWebKitAPI::mainDocumentRequest):
+ (TestWebKitAPI::subResourceRequest):
+ (TestWebKitAPI::TEST_F):
+ (TestWebKitAPI::checkCompilerError):
+ Added tests for parsing and functionality of if-domain and unless-domain.
+
2015-05-11 Ryosuke Niwa <[email protected]>
run-benchmark should support Chrome Canary and Firefox Nightly
Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp (184115 => 184116)
--- trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp 2015-05-11 19:32:59 UTC (rev 184115)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp 2015-05-11 19:54:15 UTC (rev 184116)
@@ -80,34 +80,62 @@
}
};
-class InMemoryContentExtensionCompilationClient final : public WebCore::ContentExtensions::ContentExtensionCompilationClient {
+struct CompiledContentExtensionData {
+ Vector<ContentExtensions::SerializedActionByte> actions;
+ Vector<ContentExtensions::DFABytecode> filtersWithoutDomains;
+ Vector<ContentExtensions::DFABytecode> filtersWithDomains;
+ Vector<ContentExtensions::DFABytecode> domainFilters;
+};
+
+class InMemoryContentExtensionCompilationClient final : public ContentExtensions::ContentExtensionCompilationClient {
public:
- InMemoryContentExtensionCompilationClient(WebCore::ContentExtensions::CompiledContentExtensionData& data)
+ InMemoryContentExtensionCompilationClient(CompiledContentExtensionData& data)
: m_data(data)
{
- EXPECT_EQ(data.bytecode.size(), 0ull);
EXPECT_EQ(data.actions.size(), 0ull);
+ EXPECT_EQ(data.filtersWithoutDomains.size(), 0ull);
+ EXPECT_EQ(data.filtersWithDomains.size(), 0ull);
+ EXPECT_EQ(data.domainFilters.size(), 0ull);
}
- virtual void writeBytecode(Vector<WebCore::ContentExtensions::DFABytecode>&& bytecode) override
+ virtual void writeActions(Vector<ContentExtensions::SerializedActionByte>&& actions) override
{
EXPECT_FALSE(finalized);
- m_data.bytecode.appendVector(bytecode);
+ EXPECT_EQ(m_data.actions.size(), 0ull);
+ EXPECT_EQ(m_data.filtersWithoutDomains.size(), 0ull);
+ EXPECT_EQ(m_data.filtersWithDomains.size(), 0ull);
+ EXPECT_EQ(m_data.domainFilters.size(), 0ull);
+ m_data.actions.appendVector(actions);
}
- virtual void writeActions(Vector<WebCore::ContentExtensions::SerializedActionByte>&& actions) override
+ virtual void writeFiltersWithoutDomainsBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) override
{
EXPECT_FALSE(finalized);
- m_data.actions.appendVector(actions);
+ EXPECT_EQ(m_data.filtersWithDomains.size(), 0ull);
+ EXPECT_EQ(m_data.domainFilters.size(), 0ull);
+ m_data.filtersWithoutDomains.appendVector(bytecode);
}
+ virtual void writeFiltersWithDomainsBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) override
+ {
+ EXPECT_FALSE(finalized);
+ EXPECT_EQ(m_data.domainFilters.size(), 0ull);
+ m_data.filtersWithDomains.appendVector(bytecode);
+ }
+
+ virtual void writeDomainFiltersBytecode(Vector<ContentExtensions::DFABytecode>&& bytecode) override
+ {
+ EXPECT_FALSE(finalized);
+ m_data.domainFilters.appendVector(bytecode);
+ }
+
virtual void finalize() override
{
finalized = true;
}
private:
- WebCore::ContentExtensions::CompiledContentExtensionData& m_data;
+ CompiledContentExtensionData& m_data;
bool finalized { false };
};
@@ -115,16 +143,19 @@
public:
static RefPtr<InMemoryCompiledContentExtension> createFromFilter(String&& filter)
{
- WebCore::ContentExtensions::CompiledContentExtensionData extensionData;
+ CompiledContentExtensionData extensionData;
InMemoryContentExtensionCompilationClient client(extensionData);
auto compilerError = ContentExtensions::compileRuleList(client, WTF::move(filter));
- if (compilerError)
+ if (compilerError) {
+ // Compiling should always succeed here. We have other tests for compile failures.
+ EXPECT_TRUE(false);
return nullptr;
+ }
return InMemoryCompiledContentExtension::create(WTF::move(extensionData));
}
- static RefPtr<InMemoryCompiledContentExtension> create(ContentExtensions::CompiledContentExtensionData&& data)
+ static RefPtr<InMemoryCompiledContentExtension> create(CompiledContentExtensionData&& data)
{
return adoptRef(new InMemoryCompiledContentExtension(WTF::move(data)));
}
@@ -133,18 +164,22 @@
{
}
- virtual const ContentExtensions::DFABytecode* bytecode() const override { return m_data.bytecode.data(); }
- virtual unsigned bytecodeLength() const override { return m_data.bytecode.size(); }
virtual const ContentExtensions::SerializedActionByte* actions() const override { return m_data.actions.data(); }
virtual unsigned actionsLength() const override { return m_data.actions.size(); }
+ virtual const ContentExtensions::DFABytecode* filtersWithoutDomainsBytecode() const override { return m_data.filtersWithoutDomains.data(); }
+ virtual unsigned filtersWithoutDomainsBytecodeLength() const override { return m_data.filtersWithoutDomains.size(); }
+ virtual const ContentExtensions::DFABytecode* filtersWithDomainsBytecode() const override { return m_data.filtersWithDomains.data(); }
+ virtual unsigned filtersWithDomainsBytecodeLength() const override { return m_data.filtersWithDomains.size(); }
+ virtual const ContentExtensions::DFABytecode* domainFiltersBytecode() const override { return m_data.domainFilters.data(); }
+ virtual unsigned domainFiltersBytecodeLength() const override { return m_data.domainFilters.size(); }
private:
- InMemoryCompiledContentExtension(ContentExtensions::CompiledContentExtensionData&& data)
+ InMemoryCompiledContentExtension(CompiledContentExtensionData&& data)
: m_data(WTF::move(data))
{
}
- ContentExtensions::CompiledContentExtensionData m_data;
+ CompiledContentExtensionData m_data;
};
void static testRequest(ContentExtensions::ContentExtensionsBackend contentExtensionsBackend, const ResourceLoadInfo& resourceLoadInfo, Vector<ContentExtensions::ActionType> expectedActions, bool ignorePreviousRules = false)
@@ -164,11 +199,16 @@
EXPECT_EQ(actions[actions.size() - 1].type(), ContentExtensions::ActionType::CSSDisplayNoneStyleSheet);
}
-ResourceLoadInfo mainDocumentRequest(const char* url, ResourceType resourceType = ResourceType::Document)
+static ResourceLoadInfo mainDocumentRequest(const char* url, ResourceType resourceType = ResourceType::Document)
{
return { URL(URL(), url), URL(URL(), url), resourceType };
}
+static ResourceLoadInfo subResourceRequest(const char* url, const char* mainDocumentURL, ResourceType resourceType = ResourceType::Document)
+{
+ return { URL(URL(), url), URL(URL(), mainDocumentURL), resourceType };
+}
+
ContentExtensions::ContentExtensionsBackend makeBackend(const char* json)
{
auto extension = InMemoryCompiledContentExtension::createFromFilter(json);
@@ -362,6 +402,79 @@
testRequest(backend, mainDocumentRequest("https://post.org/post"), { });
}
+TEST_F(ContentExtensionTest, DomainTriggers)
+{
+ auto ifDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"webkit.org\"]}}]");
+ testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.org/test.htm"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+ testRequest(ifDomainBackend, mainDocumentRequest("http://webkit.organization/test.html"), { });
+
+ auto unlessDomainBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"unless-domain\":[\"webkit.org\"]}}]");
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.org/test.htm"), { });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.htm"), { });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://bugs.webkit.org/test.html"), { });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://not_webkit.org/test.htm"), { });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.organization/test.htm"), { });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(unlessDomainBackend, mainDocumentRequest("http://webkit.organization/test.html"), { ContentExtensions::ActionType::BlockLoad });
+
+ auto combinedBackend1 = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test_block_load\", \"if-domain\":[\"webkit.org\"]}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"test_block_cookies\", \"unless-domain\":[\"webkit.org\"]}}]");
+ testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org"), { });
+ testRequest(combinedBackend1, mainDocumentRequest("http://not_webkit.org"), { });
+ testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org/test_block_load.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_load.html", "http://webkit.org/"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://webkit.org/"), { });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_load.html", "http://not_webkit.org/"), { });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://not_webkit.org/"), { });
+ testRequest(combinedBackend1, mainDocumentRequest("http://webkit.org/test_block_cookies.html"), { });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_cookies.html", "http://webkit.org/"), { });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://webkit.org/"), { });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/test_block_cookies.html", "http://not_webkit.org/path/to/main/document.html"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(combinedBackend1, subResourceRequest("http://whatwg.org/shouldnt_match.html", "http://not_webkit.org/"), { });
+
+ auto combinedBackend2 = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test_block_load\\\\.html\", \"if-domain\":[\"webkit.org\"]}},"
+ "{\"action\":{\"type\":\"block-cookies\"},\"trigger\":{\"url-filter\":\"test_block_cookies\\\\.html\", \"unless-domain\":[\"w3c.org\"]}},"
+ "{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\"test_css\\\\.html\"}}]");
+ testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_css.htm"), { });
+ testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/test_block_load.html"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(combinedBackend2, mainDocumentRequest("http://not_webkit.org/test_block_load.html"), { });
+ testRequest(combinedBackend2, mainDocumentRequest("http://not_webkit.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(combinedBackend2, mainDocumentRequest("http://webkit.org/TEST_CSS.hTmL/test_block_load.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockLoad});
+ testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_css.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_block_load.html"), { });
+ testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_block_cookies.html"), { });
+ testRequest(combinedBackend2, mainDocumentRequest("http://w3c.org/test_css.html/test_block_cookies.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector });
+ testRequest(combinedBackend2, mainDocumentRequest("http://not_w3c.org/test_block_cookies.html"), { ContentExtensions::ActionType::BlockCookies });
+ testRequest(combinedBackend2, mainDocumentRequest("http://not_w3c.org/test_css.html/test_block_cookies.html"), { ContentExtensions::ActionType::CSSDisplayNoneSelector, ContentExtensions::ActionType::BlockCookies });
+
+ auto ifDomainWithFlagsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\", \"if-domain\":[\"webkit.org\"],\"resource-type\":[\"image\"]}}]");
+ testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.png", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+ testRequest(ifDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.png", ResourceType::Image), { });
+
+ auto unlessDomainWithFlagsBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\", \"unless-domain\":[\"webkit.org\"],\"resource-type\":[\"image\"]}}]");
+ testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://webkit.org/test.png", ResourceType::Image), { });
+ testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.html"), { });
+ testRequest(unlessDomainWithFlagsBackend, mainDocumentRequest("http://not_webkit.org/test.png", ResourceType::Image), { ContentExtensions::ActionType::BlockLoad });
+
+ // Domains should not be interepted as regular expressions.
+ auto domainRegexBackend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"test\\\\.html\", \"if-domain\":[\"we?bkit.org\"]}}]");
+ testRequest(domainRegexBackend, mainDocumentRequest("http://webkit.org/test.html"), { });
+ testRequest(domainRegexBackend, mainDocumentRequest("http://wbkit.org/test.html"), { });
+
+ // FIXME: Add and test domain-specific popup-only blocking (with layout tests).
+}
+
TEST_F(ContentExtensionTest, TermsKnownToMatchAnything)
{
auto backend = makeBackend("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"^pre1.*post1$\"}},"
@@ -577,12 +690,14 @@
EXPECT_EQ(1ul, createNFAs(combinedURLFilters).size());
}
-void checkCompilerError(const char* json, ContentExtensions::ContentExtensionError expectedError)
+void checkCompilerError(const char* json, std::error_code expectedError)
{
- WebCore::ContentExtensions::CompiledContentExtensionData extensionData;
+ CompiledContentExtensionData extensionData;
InMemoryContentExtensionCompilationClient client(extensionData);
std::error_code compilerError = ContentExtensions::compileRuleList(client, json);
- EXPECT_EQ(compilerError.value(), static_cast<int>(expectedError));
+ EXPECT_EQ(compilerError.value(), expectedError.value());
+ if (compilerError.value())
+ EXPECT_STREQ(compilerError.category().name(), expectedError.category().name());
}
TEST_F(ContentExtensionTest, InvalidJSON)
@@ -605,11 +720,38 @@
ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[\"invalid\"]}}]",
ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"load-type\":[5]}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":{}}}]",
ContentExtensions::ContentExtensionError::JSONInvalidTriggerFlagsArray);
checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":[\"invalid\"]}}]",
ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"resource-type\":[5]}}]",
+ ContentExtensions::ContentExtensionError::JSONInvalidStringInTriggerFlagsArray);
+
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":{}}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[5]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"]}}]", { });
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":{}}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[5]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"\"]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"A\"]}}]", ContentExtensions::ContentExtensionError::JSONDomainNotLowerCaseASCII);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"\\u00DC\"]}}]", ContentExtensions::ContentExtensionError::JSONDomainNotLowerCaseASCII);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"0\"]}}]", { });
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[\"a\"]}}]", { });
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[],\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":5}}]", { });
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"unless-domain\":5}}]", { });
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":5,\"unless-domain\":5}}]", { });
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONInvalidDomainList);
+
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"],\"unless-domain\":[]}}]", ContentExtensions::ContentExtensionError::JSONUnlessAndIfDomain);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\",\"if-domain\":[\"a\"],\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::JSONUnlessAndIfDomain);
+
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"webkit.org\", \"unexpected-identifier-should-be-ignored\":5}}]", { });
+
checkCompilerError("[{\"action\":5,\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
ContentExtensions::ContentExtensionError::JSONInvalidAction);
checkCompilerError("[{\"action\":{\"type\":\"invalid\"},\"trigger\":{\"url-filter\":\"webkit.org\"}}]",
@@ -620,6 +762,8 @@
checkCompilerError("[{\"action\":{\"type\":\"ignore-previous-rules\"},\"trigger\":{\"url-filter\":\"webkit.org\"}},"
"{\"action\":{\"type\":\"css-display-none\",\"selector\":\".hidden\"},\"trigger\":{\"url-filter\":\".*\"}}]",
ContentExtensions::ContentExtensionError::RegexMatchesEverythingAfterIgnorePreviousRules);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"if-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::RegexMatchesEverythingWithDomains);
+ checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\".*\",\"unless-domain\":[\"a\"]}}]", ContentExtensions::ContentExtensionError::RegexMatchesEverythingWithDomains);
checkCompilerError("[{\"action\":{\"type\":\"block\"},\"trigger\":{\"url-filter\":\"[\"}}]",
ContentExtensions::ContentExtensionError::JSONInvalidRegex);
}