Diff
Modified: trunk/Source/WebCore/ChangeLog (181931 => 181932)
--- trunk/Source/WebCore/ChangeLog 2015-03-25 05:12:49 UTC (rev 181931)
+++ trunk/Source/WebCore/ChangeLog 2015-03-25 05:19:42 UTC (rev 181932)
@@ -1,3 +1,27 @@
+2015-03-24 Alex Christensen <achristen...@webkit.org>
+
+ [Content Extensions] Add multi-DFA compiling and interpreting.
+ https://bugs.webkit.org/show_bug.cgi?id=143010
+
+ Reviewed by Benjamin Poulain.
+
+ * contentextensions/ContentExtensionCompiler.cpp:
+ (WebCore::ContentExtensions::compileRuleList):
+ Compile multiple NFAs to DFAs.
+ * contentextensions/ContentExtensionsBackend.cpp:
+ (WebCore::ContentExtensions::ContentExtensionsBackend::actionsForResourceLoad):
+ Fixed a bug when there are no non-universal actions.
+ We still need to report that no ignore-previous-rules was hit to apply the
+ universal actions which are now accessed through DFABytecodeInterpreter::actionsFromDFARoot
+ and skipped in DFABytecodeInterpreter::interpret.
+ * contentextensions/DFABytecodeCompiler.cpp:
+ (WebCore::ContentExtensions::DFABytecodeCompiler::compile):
+ Add a header for each DFA.
+ * contentextensions/DFABytecodeInterpreter.cpp:
+ (WebCore::ContentExtensions::DFABytecodeInterpreter::actionsFromDFARoot):
+ (WebCore::ContentExtensions::DFABytecodeInterpreter::interpret):
+ Interpret as many DFAs as there are in the bytecode.
+
2015-03-24 Dan Bernstein <m...@apple.com>
Tried to fix the EWS build.
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp (181931 => 181932)
--- trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp 2015-03-25 05:12:49 UTC (rev 181931)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionCompiler.cpp 2015-03-25 05:19:42 UTC (rev 181932)
@@ -111,16 +111,23 @@
Vector<unsigned> actionLocations = serializeActions(parsedRuleList, actions);
Vector<uint64_t> universalActionLocations;
- NFA nfa;
- URLFilterParser urlFilterParser(nfa);
+ Vector<NFA> nfas;
+ nfas.append(NFA());
bool nonUniversalActionSeen = false;
for (unsigned ruleIndex = 0; ruleIndex < parsedRuleList.size(); ++ruleIndex) {
+
+ // FIXME: Tune this better and adjust ContentExtensionTest.MultiDFA accordingly.
+ if (nfas[nfas.size() - 1].graphSize() > 500)
+ nfas.append(NFA());
+
+ NFA& lastNFA = nfas[nfas.size() - 1];
+ URLFilterParser urlFilterParser(lastNFA);
const ContentExtensionRule& contentExtensionRule = parsedRuleList[ruleIndex];
const Trigger& trigger = contentExtensionRule.trigger();
ASSERT(trigger.urlFilter.length());
// 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]);
+ 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) {
@@ -149,9 +156,17 @@
double dfaBuildTimeStart = monotonicallyIncreasingTime();
#endif
- DFA dfa = NFAToDFA::convert(nfa);
- for (uint64_t actionLocation : universalActionLocations)
- dfa.nodeAt(dfa.root()).actions.append(actionLocation);
+ Vector<DFABytecode> bytecode;
+ for (size_t i = 0; i < nfas.size(); ++i) {
+ DFA dfa = NFAToDFA::convert(nfas[i]);
+ if (!i) {
+ // Put all the universal actions on the first DFA.
+ for (uint64_t actionLocation : universalActionLocations)
+ dfa.nodeAt(dfa.root()).actions.append(actionLocation);
+ }
+ DFABytecodeCompiler compiler(dfa, bytecode);
+ compiler.compile();
+ }
#if CONTENT_EXTENSIONS_PERFORMANCE_REPORTING
double dfaBuildTimeEnd = monotonicallyIncreasingTime();
@@ -164,10 +179,6 @@
dfa.debugPrintDot();
#endif
- Vector<DFABytecode> bytecode;
- DFABytecodeCompiler compiler(dfa, bytecode);
- compiler.compile();
-
return { WTF::move(bytecode), WTF::move(actions) };
}
Modified: trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp (181931 => 181932)
--- trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp 2015-03-25 05:12:49 UTC (rev 181931)
+++ trunk/Source/WebCore/contentextensions/ContentExtensionsBackend.cpp 2015-03-25 05:19:42 UTC (rev 181932)
@@ -80,6 +80,7 @@
const SerializedActionByte* actions = compiledExtension.actions();
const unsigned actionsLength = compiledExtension.actionsLength();
+ bool sawIgnorePreviousRules = false;
if (!triggeredActions.isEmpty()) {
Vector<unsigned> actionLocations;
actionLocations.reserveInitialCapacity(triggeredActions.size());
@@ -87,8 +88,6 @@
actionLocations.append(static_cast<unsigned>(actionLocation));
std::sort(actionLocations.begin(), actionLocations.end());
- bool sawIgnorePreviousRules = false;
-
// Add actions in reverse order to properly deal with IgnorePreviousRules.
for (unsigned i = actionLocations.size(); i; i--) {
Action action = "" actionsLength, actionLocations[i - 1]);
@@ -98,10 +97,9 @@
}
finalActions.append(action);
}
-
- if (!sawIgnorePreviousRules)
- finalActions.append(Action(ActionType::CSSDisplayNoneStyleSheet, contentExtension->identifier()));
}
+ if (!sawIgnorePreviousRules)
+ finalActions.append(Action(ActionType::CSSDisplayNoneStyleSheet, contentExtension->identifier()));
}
return finalActions;
}
Modified: trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp (181931 => 181932)
--- trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp 2015-03-25 05:12:49 UTC (rev 181931)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeCompiler.cpp 2015-03-25 05:19:42 UTC (rev 181932)
@@ -167,7 +167,9 @@
void DFABytecodeCompiler::compile()
{
- ASSERT(!m_bytecode.size());
+ // DFA header.
+ unsigned startLocation = m_bytecode.size();
+ append<unsigned>(m_bytecode, 0);
m_nodeStartOffsets.resize(m_dfa.size());
// Make sure the root is always at the beginning of the bytecode.
@@ -180,6 +182,9 @@
// Link.
for (const auto& linkRecord : m_linkRecords)
set32Bits(m_bytecode, linkRecord.first, m_nodeStartOffsets[linkRecord.second]);
+
+ // Set size header.
+ set32Bits(m_bytecode, startLocation, m_bytecode.size() - startLocation);
}
} // namespace ContentExtensions
Modified: trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp (181931 => 181932)
--- trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp 2015-03-25 05:12:49 UTC (rev 181931)
+++ trunk/Source/WebCore/contentextensions/DFABytecodeInterpreter.cpp 2015-03-25 05:19:42 UTC (rev 181932)
@@ -43,13 +43,15 @@
DFABytecodeInterpreter::Actions DFABytecodeInterpreter::actionsFromDFARoot()
{
- unsigned programCounter = 0;
- DFABytecodeInterpreter::Actions globalActionLocations;
+ DFABytecodeInterpreter::Actions universalActionLocations;
+
+ // Skip first DFA header. All universal actions are in the first DFA root.
+ unsigned programCounter = sizeof(unsigned);
while (static_cast<DFABytecodeInstruction>(m_bytecode[programCounter]) == DFABytecodeInstruction::AppendAction) {
- globalActionLocations.add(static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode))));
+ universalActionLocations.add(static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode))));
programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction);
}
- return globalActionLocations;
+ return universalActionLocations;
}
DFABytecodeInterpreter::Actions DFABytecodeInterpreter::interpret(const CString& urlCString, uint16_t flags)
@@ -57,78 +59,92 @@
const char* url = ""
ASSERT(url);
- unsigned programCounter = 0;
- unsigned urlIndex = 0;
- bool urlIndexIsAfterEndOfString = false;
Actions actions;
- while (static_cast<DFABytecodeInstruction>(m_bytecode[programCounter]) == DFABytecodeInstruction::AppendAction)
- programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction);
+ unsigned programCounter = 0;
+ while (programCounter < m_bytecodeLength) {
- // This should always terminate if interpreting correctly compiled bytecode.
- while (true) {
- ASSERT(programCounter <= m_bytecodeLength);
- switch (static_cast<DFABytecodeInstruction>(m_bytecode[programCounter])) {
+ // DFA header.
+ unsigned dfaStart = programCounter;
+ unsigned dfaBytecodeLength = getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter);
+ programCounter += sizeof(unsigned);
- case DFABytecodeInstruction::Terminate:
- return actions;
+ // Skip the universal actions.
+ // FIXME: Replace AppendAction with AppendActions to make this just one jump and make sure there aren't universal actions with flags.
+ while (static_cast<DFABytecodeInstruction>(m_bytecode[programCounter]) == DFABytecodeInstruction::AppendAction)
+ programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction);
+
+ // Interpret the bytecode from this DFA.
+ // This should always terminate if interpreting correctly compiled bytecode.
+ unsigned urlIndex = 0;
+ bool urlIndexIsAfterEndOfString = false;
+ while (true) {
+ ASSERT(programCounter <= m_bytecodeLength);
+ switch (static_cast<DFABytecodeInstruction>(m_bytecode[programCounter])) {
- case DFABytecodeInstruction::CheckValue:
- if (urlIndexIsAfterEndOfString)
- return actions;
+ case DFABytecodeInstruction::Terminate:
+ goto nextDFA;
+
+ case DFABytecodeInstruction::CheckValue:
+ if (urlIndexIsAfterEndOfString)
+ goto nextDFA;
- // Check to see if the next character in the url is the value stored with the bytecode.
- if (url[urlIndex] == getBits<uint8_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode))) {
- programCounter = getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint8_t));
- if (!url[urlIndex])
- urlIndexIsAfterEndOfString = true;
- urlIndex++; // This represents an edge in the DFA.
- } else
- programCounter += instructionSizeWithArguments(DFABytecodeInstruction::CheckValue);
- break;
+ // Check to see if the next character in the url is the value stored with the bytecode.
+ if (url[urlIndex] == getBits<uint8_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode))) {
+ programCounter = getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint8_t));
+ if (!url[urlIndex])
+ urlIndexIsAfterEndOfString = true;
+ urlIndex++; // This represents an edge in the DFA.
+ } else
+ programCounter += instructionSizeWithArguments(DFABytecodeInstruction::CheckValue);
+ break;
+
+ case DFABytecodeInstruction::CheckValueRange: {
+ if (urlIndexIsAfterEndOfString)
+ goto nextDFA;
+
+ char character = url[urlIndex];
+ if (character >= getBits<uint8_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode))
+ && character <= getBits<uint8_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint8_t))) {
+ programCounter = getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint8_t) + sizeof(uint8_t));
+ if (!character)
+ urlIndexIsAfterEndOfString = true;
+ urlIndex++; // This represents an edge in the DFA.
+ } else
+ programCounter += instructionSizeWithArguments(DFABytecodeInstruction::CheckValueRange);
+ break;
+ }
- case DFABytecodeInstruction::CheckValueRange: {
- if (urlIndexIsAfterEndOfString)
- return actions;
-
- char character = url[urlIndex];
- if (character >= getBits<uint8_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode))
- && character <= getBits<uint8_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint8_t))) {
- programCounter = getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint8_t) + sizeof(uint8_t));
- if (!character)
- urlIndexIsAfterEndOfString = true;
+ case DFABytecodeInstruction::Jump:
+ if (!url[urlIndex] || urlIndexIsAfterEndOfString)
+ goto nextDFA;
+
+ programCounter = getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode));
urlIndex++; // This represents an edge in the DFA.
- } else
- programCounter += instructionSizeWithArguments(DFABytecodeInstruction::CheckValueRange);
- break;
+ break;
+
+ case DFABytecodeInstruction::AppendAction:
+ actions.add(static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode))));
+ programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction);
+ break;
+
+ case DFABytecodeInstruction::TestFlagsAndAppendAction:
+ if (flags & getBits<uint16_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode)))
+ actions.add(static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint16_t))));
+ programCounter += instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendAction);
+ break;
+
+ default:
+ RELEASE_ASSERT_NOT_REACHED(); // Invalid bytecode.
+ }
+ // We should always terminate before or at a null character at the end of a String.
+ ASSERT(urlIndex <= urlCString.length() || (urlIndexIsAfterEndOfString && urlIndex <= urlCString.length() + 1));
}
-
- case DFABytecodeInstruction::Jump:
- if (!url[urlIndex] || urlIndexIsAfterEndOfString)
- return actions;
-
- programCounter = getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode));
- urlIndex++; // This represents an edge in the DFA.
- break;
-
- case DFABytecodeInstruction::AppendAction:
- actions.add(static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode))));
- programCounter += instructionSizeWithArguments(DFABytecodeInstruction::AppendAction);
- break;
-
- case DFABytecodeInstruction::TestFlagsAndAppendAction:
- if (flags & getBits<uint16_t>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode)))
- actions.add(static_cast<uint64_t>(getBits<unsigned>(m_bytecode, m_bytecodeLength, programCounter + sizeof(DFABytecode) + sizeof(uint16_t))));
- programCounter += instructionSizeWithArguments(DFABytecodeInstruction::TestFlagsAndAppendAction);
- break;
-
- default:
- RELEASE_ASSERT_NOT_REACHED(); // Invalid bytecode.
- }
- // We should always terminate before or at a null character at the end of a String.
- ASSERT(urlIndex <= urlCString.length() || (urlIndexIsAfterEndOfString && urlIndex <= urlCString.length() + 1));
+ nextDFA:
+ ASSERT(dfaBytecodeLength);
+ programCounter = dfaStart + dfaBytecodeLength;
}
- RELEASE_ASSERT_NOT_REACHED();
+ return actions;
}
} // namespace ContentExtensions
Modified: trunk/Tools/ChangeLog (181931 => 181932)
--- trunk/Tools/ChangeLog 2015-03-25 05:12:49 UTC (rev 181931)
+++ trunk/Tools/ChangeLog 2015-03-25 05:19:42 UTC (rev 181932)
@@ -1,3 +1,15 @@
+2015-03-24 Alex Christensen <achristen...@webkit.org>
+
+ [Content Extensions] Add multi-DFA compiling and interpreting.
+ https://bugs.webkit.org/show_bug.cgi?id=143010
+
+ Reviewed by Benjamin Poulain.
+
+ * TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp:
+ (TestWebKitAPI::testRequest):
+ (TestWebKitAPI::TEST_F):
+ Add some tests for ignore-previous-rules and large rulesets.
+
2015-03-24 Benjamin Poulain <bpoul...@apple.com>
Make URL filter patterns matching consistent and add a simple canonicalization step
Modified: trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp (181931 => 181932)
--- trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp 2015-03-25 05:12:49 UTC (rev 181931)
+++ trunk/Tools/TestWebKitAPI/Tests/WebCore/ContentExtensions.cpp 2015-03-25 05:19:42 UTC (rev 181932)
@@ -36,6 +36,7 @@
#include <wtf/MainThread.h>
#include <wtf/RunLoop.h>
#include <wtf/text/CString.h>
+#include <wtf/text/StringBuilder.h>
namespace WebCore {
namespace ContentExtensions {
@@ -98,16 +99,22 @@
ContentExtensions::CompiledContentExtensionData m_data;
};
-void static testRequest(ContentExtensions::ContentExtensionsBackend contentExtensionsBackend, const ResourceLoadInfo& resourceLoadInfo, Vector<ContentExtensions::ActionType> expectedActions)
+void static testRequest(ContentExtensions::ContentExtensionsBackend contentExtensionsBackend, const ResourceLoadInfo& resourceLoadInfo, Vector<ContentExtensions::ActionType> expectedActions, bool ignorePreviousRules = false)
{
auto actions = contentExtensionsBackend.actionsForResourceLoad(resourceLoadInfo);
- // The last action is applying the compiled stylesheet.
- EXPECT_EQ(expectedActions.size(), actions.size() ? actions.size() - 1 : 0);
- if (expectedActions.size() != (actions.size() ? actions.size() - 1 : 0))
+
+ unsigned expectedSize = actions.size();
+ if (actions.size() && !ignorePreviousRules)
+ expectedSize--; // The last action is applying the compiled stylesheet.
+
+ EXPECT_EQ(expectedActions.size(), expectedSize);
+ if (expectedActions.size() != expectedSize)
return;
for (unsigned i = 0; i < expectedActions.size(); ++i)
EXPECT_EQ(expectedActions[i], actions[i].type());
+ if (!ignorePreviousRules)
+ EXPECT_EQ(actions[actions.size() - 1].type(), ContentExtensions::ActionType::CSSDisplayNoneStyleSheet);
}
ResourceLoadInfo mainDocumentRequest(const char* url, ResourceType resourceType = ResourceType::Document)
@@ -510,6 +517,47 @@
testRequest(backend, mainDocumentRequest("http://block_only_images.org", ResourceType::Document), { });
}
+TEST_F(ContentExtensionTest, MultiDFA)
+{
+ // Make an NFA with about 2000 nodes.
+ StringBuilder ruleList;
+ ruleList.append('[');
+ for (char c1 = 'A'; c1 <= 'Z'; ++c1) {
+ for (char c2 = 'A'; c2 <= 'Z'; ++c2) {
+ for (char c3 = 'A'; c3 <= 'C'; ++c3) {
+ if (c1 != 'A' || c2 != 'A' || c3 != 'A')
+ ruleList.append(',');
+ ruleList.append("{\"action\":{\"type\":\"");
+
+ // Put an ignore-previous-rules near the middle.
+ if (c1 == 'L' && c2 == 'L' && c3 == 'A')
+ ruleList.append("ignore-previous-rules");
+ else
+ ruleList.append("block");
+
+ ruleList.append("\"},\"trigger\":{\"url-filter\":\".*");
+ ruleList.append(c1);
+ ruleList.append(c2);
+ ruleList.append(c3);
+ ruleList.append("\", \"url-filter-is-case-sensitive\":true}}");
+ }
+ }
+ }
+ ruleList.append(']');
+
+ auto extensionData = ContentExtensions::compileRuleList(ruleList.toString());
+ auto extension = InMemoryCompiledContentExtension::create(WTF::move(extensionData));
+
+ ContentExtensions::ContentExtensionsBackend backend;
+ backend.addContentExtension("ResourceTypeFilter", extension);
+
+ testRequest(backend, mainDocumentRequest("http://webkit.org/AAA"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/ZZC"), { ContentExtensions::ActionType::BlockLoad });
+ testRequest(backend, mainDocumentRequest("http://webkit.org/LLA/AAA"), { }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/LLA/MMC"), { ContentExtensions::ActionType::BlockLoad }, true);
+ testRequest(backend, mainDocumentRequest("http://webkit.org/"), { });
+}
+
static void testPatternStatus(String pattern, ContentExtensions::URLFilterParser::ParseStatus status)
{
ContentExtensions::NFA nfa;