Chromium Code Reviews| Index: Source/core/inspector/InspectorStyleSheet.cpp |
| diff --git a/Source/core/inspector/InspectorStyleSheet.cpp b/Source/core/inspector/InspectorStyleSheet.cpp |
| index d1d6846bc12574d514f7f93b3ea205586584a20b..d88cac1afd07741ace2ee779ab1431be759dfaa6 100644 |
| --- a/Source/core/inspector/InspectorStyleSheet.cpp |
| +++ b/Source/core/inspector/InspectorStyleSheet.cpp |
| @@ -29,6 +29,7 @@ |
| #include "bindings/core/v8/ExceptionStatePlaceholder.h" |
| #include "bindings/core/v8/ScriptRegexp.h" |
| #include "core/CSSPropertyNames.h" |
| +#include "core/css/CSSImportRule.h" |
| #include "core/css/CSSKeyframesRule.h" |
| #include "core/css/CSSMediaRule.h" |
| #include "core/css/CSSRuleList.h" |
| @@ -335,10 +336,11 @@ void StyleSheetHandler::endMediaQuery() |
| bool verifyRuleText(Document* document, const String& ruleText) |
| { |
| DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee")); |
| + RefPtrWillBeRawPtr<StyleSheetContents> styleSheet = StyleSheetContents::create(strictCSSParserContext()); |
| RuleSourceDataList sourceData; |
| String text = ruleText + " div { " + bogusPropertyName + ": none; }"; |
| StyleSheetHandler handler(text, document, &sourceData); |
| - CSSParser::parseSheetForInspector(parserContextForDocument(document), text, handler); |
| + CSSParser::parseSheetForInspector(parserContextForDocument(document), styleSheet.get(), text, handler); |
| unsigned ruleCount = sourceData.size(); |
| // Exactly two rules should be parsed. |
| @@ -371,10 +373,11 @@ bool verifyStyleText(Document* document, const String& text) |
| bool verifySelectorText(Document* document, const String& selectorText) |
| { |
| DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee")); |
| + RefPtrWillBeRawPtr<StyleSheetContents> styleSheet = StyleSheetContents::create(strictCSSParserContext()); |
| RuleSourceDataList sourceData; |
| String text = selectorText + " { " + bogusPropertyName + ": none; }"; |
| StyleSheetHandler handler(text, document, &sourceData); |
| - CSSParser::parseSheetForInspector(parserContextForDocument(document), text, handler); |
| + CSSParser::parseSheetForInspector(parserContextForDocument(document), styleSheet.get(), text, handler); |
| // Exactly one rule should be parsed. |
| unsigned ruleCount = sourceData.size(); |
| @@ -397,10 +400,11 @@ bool verifySelectorText(Document* document, const String& selectorText) |
| bool verifyMediaText(Document* document, const String& mediaText) |
| { |
| DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee")); |
| + RefPtrWillBeRawPtr<StyleSheetContents> styleSheet = StyleSheetContents::create(strictCSSParserContext()); |
| RuleSourceDataList sourceData; |
| String text = "@media " + mediaText + " { div { " + bogusPropertyName + ": none; } }"; |
| StyleSheetHandler handler(text, document, &sourceData); |
| - CSSParser::parseSheetForInspector(parserContextForDocument(document), text, handler); |
| + CSSParser::parseSheetForInspector(parserContextForDocument(document), styleSheet.get(), text, handler); |
| // Exactly one media rule should be parsed. |
| unsigned ruleCount = sourceData.size(); |
| @@ -497,6 +501,104 @@ void collectFlatRules(RuleList ruleList, CSSRuleVector* result) |
| } |
| } |
| +typedef HashMap<unsigned, unsigned, WTF::IntHash<unsigned>, WTF::UnsignedWithZeroKeyHashTraits<unsigned>> IndexMap; |
| + |
| +void diff(const Vector<String>& listA, const Vector<String>& listB, IndexMap* aToB, IndexMap* bToA) |
| +{ |
| + // Cut of common prefix. |
| + size_t startOffset = 0; |
| + while (startOffset < listA.size() && startOffset < listB.size()) { |
| + if (listA.at(startOffset) != listB.at(startOffset)) |
| + break; |
| + aToB->set(startOffset, startOffset); |
| + bToA->set(startOffset, startOffset); |
| + ++startOffset; |
| + } |
| + |
| + // Cut of common suffix. |
| + size_t endOffset = 0; |
| + while (endOffset < listA.size() - startOffset && endOffset < listB.size() - startOffset) { |
| + size_t indexA = listA.size() - endOffset - 1; |
| + size_t indexB = listB.size() - endOffset - 1; |
| + if (listA.at(indexA) != listB.at(indexB)) |
| + break; |
| + aToB->set(indexA, indexB); |
| + bToA->set(indexB, indexA); |
| + ++endOffset; |
| + } |
| + |
| + int n = listA.size() - startOffset - endOffset; |
| + int m = listB.size() - startOffset - endOffset; |
| + |
| + // If we mapped either of arrays, we have no more work to do. |
| + if (n == 0 || m == 0) |
| + return; |
| + |
| + int** diff = new int*[n]; |
| + int** backtrack = new int*[n]; |
| + for (int i = 0; i < n; ++i) { |
| + diff[i] = new int[m]; |
| + backtrack[i] = new int[m]; |
| + } |
| + |
| + // Compute longest common subsequence of two cssom models. |
| + for (int i = 0; i < n; ++i) { |
| + for (int j = 0; j < m; ++j) { |
| + int max = 0; |
| + int track = 0; |
| + |
| + if (i > 0 && diff[i - 1][j] > max) { |
| + max = diff[i - 1][j]; |
| + track = 1; |
| + } |
| + |
| + if (j > 0 && diff[i][j - 1] > max) { |
| + max = diff[i][j - 1]; |
| + track = 2; |
| + } |
| + |
| + if (listA.at(i + startOffset) == listB.at(j + startOffset)) { |
| + int value = i > 0 && j > 0 ? diff[i - 1][j - 1] + 1 : 1; |
| + if (value > max) { |
| + max = value; |
| + track = 3; |
| + } |
| + } |
| + |
| + diff[i][j] = max; |
| + backtrack[i][j] = track; |
| + } |
| + } |
| + |
| + // Backtrack and add missing mapping. |
| + int i = n - 1, j = m - 1; |
| + while (i >= 0 && j >= 0 && backtrack[i][j]) { |
| + switch (backtrack[i][j]) { |
| + case 1: |
| + i -= 1; |
| + break; |
| + case 2: |
| + j -= 1; |
| + break; |
| + case 3: |
| + aToB->set(i + startOffset, j + startOffset); |
| + bToA->set(j + startOffset, i + startOffset); |
| + i -= 1; |
| + j -= 1; |
| + break; |
| + default: |
| + ASSERT_NOT_REACHED(); |
| + } |
| + } |
| + |
| + for (int i = 0; i < n; ++i) { |
| + delete [] diff[i]; |
| + delete [] backtrack[i]; |
| + } |
| + delete [] diff; |
| + delete [] backtrack; |
| +} |
| + |
| } // namespace |
| namespace blink { |
| @@ -766,7 +868,7 @@ InspectorStyleSheet::InspectorStyleSheet(InspectorResourceAgent* resourceAgent, |
| success = resourceStyleSheetText(&text); |
| if (success) |
| innerSetText(text, false); |
| - collectFlatRules(); |
| + mapSourceDataToCSSOM(); |
| } |
| InspectorStyleSheet::~InspectorStyleSheet() |
| @@ -819,7 +921,7 @@ bool InspectorStyleSheet::setText(const String& text, ExceptionState& exceptionS |
| onStyleSheetTextChanged(); |
| m_pageStyleSheet->ownerDocument()->styleResolverChanged(FullStyleUpdate); |
| - collectFlatRules(); |
| + mapSourceDataToCSSOM(); |
| return true; |
| } |
| @@ -836,7 +938,7 @@ RefPtrWillBeRawPtr<CSSStyleRule> InspectorStyleSheet::setRuleSelector(const Sour |
| return nullptr; |
| } |
| - RefPtrWillBeRawPtr<CSSRule> rule = ruleForSourceData(sourceData.get()); |
| + RefPtrWillBeRawPtr<CSSRule> rule = ruleForSourceData(sourceData); |
| if (!rule || !rule->parentStyleSheet() || rule->type() != CSSRule::STYLE_RULE) { |
| exceptionState.throwDOMException(NotFoundError, "Source range didn't match existing style source range"); |
| return nullptr; |
| @@ -845,7 +947,8 @@ RefPtrWillBeRawPtr<CSSStyleRule> InspectorStyleSheet::setRuleSelector(const Sour |
| RefPtrWillBeRawPtr<CSSStyleRule> styleRule = InspectorCSSAgent::asCSSStyleRule(rule.get()); |
| styleRule->setSelectorText(text); |
| - replaceText(sourceData->ruleHeaderRange, text, newRange, oldText); |
| + if (!replaceText(sourceData->ruleHeaderRange, text, newRange, oldText)) |
| + mapSourceDataToCSSOM(); |
| onStyleSheetTextChanged(); |
| return styleRule; |
| @@ -864,7 +967,7 @@ RefPtrWillBeRawPtr<CSSStyleRule> InspectorStyleSheet::setStyleText(const SourceR |
| return nullptr; |
| } |
| - RefPtrWillBeRawPtr<CSSRule> rule = ruleForSourceData(sourceData.get()); |
| + RefPtrWillBeRawPtr<CSSRule> rule = ruleForSourceData(sourceData); |
| if (!rule || !rule->parentStyleSheet() || rule->type() != CSSRule::STYLE_RULE) { |
| exceptionState.throwDOMException(NotFoundError, "Source range didn't match existing style source range"); |
| return nullptr; |
| @@ -873,7 +976,8 @@ RefPtrWillBeRawPtr<CSSStyleRule> InspectorStyleSheet::setStyleText(const SourceR |
| RefPtrWillBeRawPtr<CSSStyleRule> styleRule = InspectorCSSAgent::asCSSStyleRule(rule.get()); |
| styleRule->style()->setCSSText(text, exceptionState); |
| - replaceText(sourceData->ruleBodyRange, text, newRange, oldText); |
| + if (!replaceText(sourceData->ruleBodyRange, text, newRange, oldText)) |
| + mapSourceDataToCSSOM(); |
| onStyleSheetTextChanged(); |
| return styleRule; |
| @@ -892,7 +996,7 @@ RefPtrWillBeRawPtr<CSSMediaRule> InspectorStyleSheet::setMediaRuleText(const Sou |
| return nullptr; |
| } |
| - RefPtrWillBeRawPtr<CSSRule> rule = ruleForSourceData(sourceData.get()); |
| + RefPtrWillBeRawPtr<CSSRule> rule = ruleForSourceData(sourceData); |
| if (!rule || !rule->parentStyleSheet() || rule->type() != CSSRule::MEDIA_RULE) { |
| exceptionState.throwDOMException(NotFoundError, "Source range didn't match existing style source range"); |
| return nullptr; |
| @@ -901,7 +1005,9 @@ RefPtrWillBeRawPtr<CSSMediaRule> InspectorStyleSheet::setMediaRuleText(const Sou |
| RefPtrWillBeRawPtr<CSSMediaRule> mediaRule = InspectorCSSAgent::asCSSMediaRule(rule.get()); |
| mediaRule->media()->setMediaText(text); |
| - replaceText(sourceData->ruleHeaderRange, text, newRange, oldText); |
| + if (!replaceText(sourceData->ruleHeaderRange, text, newRange, oldText)) |
| + mapSourceDataToCSSOM(); |
| + |
| onStyleSheetTextChanged(); |
| return mediaRule; |
| @@ -977,12 +1083,12 @@ CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleBySourceRange(const SourceRang |
| } |
| RefPtrWillBeRawPtr<CSSRuleSourceData> insertBefore = ruleAfterSourceRange(sourceRange); |
| - RefPtrWillBeRawPtr<CSSRule> insertBeforeRule = ruleForSourceData(insertBefore.get()); |
| + RefPtrWillBeRawPtr<CSSRule> insertBeforeRule = ruleForSourceData(insertBefore); |
| if (!containingRuleSourceData) |
| return insertCSSOMRuleInStyleSheet(insertBeforeRule.get(), ruleText, exceptionState); |
| - RefPtrWillBeRawPtr<CSSRule> rule = ruleForSourceData(containingRuleSourceData.get()); |
| + RefPtrWillBeRawPtr<CSSRule> rule = ruleForSourceData(containingRuleSourceData); |
| if (!rule || rule->type() != CSSRule::MEDIA_RULE) { |
| exceptionState.throwDOMException(NotFoundError, "Cannot insert rule in non-media rule."); |
| return nullptr; |
| @@ -1013,7 +1119,7 @@ RefPtrWillBeRawPtr<CSSStyleRule> InspectorStyleSheet::addRule(const String& rule |
| return nullptr; |
| replaceText(location, ruleText, addedRange, nullptr); |
| - collectFlatRules(); |
| + mapSourceDataToCSSOM(); |
| onStyleSheetTextChanged(); |
| return styleRule; |
| @@ -1043,7 +1149,7 @@ bool InspectorStyleSheet::deleteRule(const SourceRange& range, ExceptionState& e |
| if (!foundData || foundData->ruleBodyRange.length() > ruleSourceData->ruleBodyRange.length()) |
| foundData = ruleSourceData; |
| } |
| - RefPtrWillBeRawPtr<CSSRule> rule = ruleForSourceData(foundData.get()); |
| + RefPtrWillBeRawPtr<CSSRule> rule = ruleForSourceData(foundData); |
| if (!rule) { |
| exceptionState.throwDOMException(NotFoundError, "No style rule could be found in given range."); |
| return false; |
| @@ -1078,13 +1184,13 @@ bool InspectorStyleSheet::deleteRule(const SourceRange& range, ExceptionState& e |
| return false; |
| replaceText(range, "", nullptr, nullptr); |
| - collectFlatRules(); |
| + mapSourceDataToCSSOM(); |
| onStyleSheetTextChanged(); |
| return true; |
| } |
| -void InspectorStyleSheet::replaceText(const SourceRange& range, const String& text, SourceRange* newRange, String* oldText) |
| +bool InspectorStyleSheet::replaceText(const SourceRange& range, const String& text, SourceRange* newRange, String* oldText) |
| { |
| String sheetText = m_text; |
| if (oldText) |
| @@ -1092,14 +1198,21 @@ void InspectorStyleSheet::replaceText(const SourceRange& range, const String& te |
| sheetText.replace(range.start, range.length(), text); |
| if (newRange) |
| *newRange = SourceRange(range.start, range.start + text.length()); |
| - innerSetText(sheetText, true); |
| + return innerSetText(sheetText, true); |
| } |
| -void InspectorStyleSheet::innerSetText(const String& text, bool markAsLocallyModified) |
| +bool InspectorStyleSheet::innerSetText(const String& text, bool markAsLocallyModified) |
| { |
| + unsigned sizeBefore = m_sourceData ? m_sourceData->size() : 0; |
| + |
| OwnPtrWillBeRawPtr<RuleSourceDataList> ruleTree = adoptPtrWillBeNoop(new RuleSourceDataList()); |
| + RefPtrWillBeRawPtr<StyleSheetContents> styleSheet = StyleSheetContents::create(m_pageStyleSheet->contents()->parserContext()); |
| StyleSheetHandler handler(text, m_pageStyleSheet->ownerDocument(), ruleTree.get()); |
| - CSSParser::parseSheetForInspector(parserContextForDocument(m_pageStyleSheet->ownerDocument()), text, handler); |
| + CSSParser::parseSheetForInspector(m_pageStyleSheet->contents()->parserContext(), styleSheet.get(), text, handler); |
| + if (toCSSImportRule(m_pageStyleSheet->ownerRule())) |
| + m_sourceDataSheet = CSSStyleSheet::create(styleSheet, toCSSImportRule(m_pageStyleSheet->ownerRule())); |
| + else |
| + m_sourceDataSheet = CSSStyleSheet::create(styleSheet, m_pageStyleSheet->ownerNode()); |
| m_sourceData = adoptPtrWillBeNoop(new RuleSourceDataList()); |
| flattenSourceData(ruleTree.get(), m_sourceData.get()); |
| m_text = text; |
| @@ -1111,6 +1224,7 @@ void InspectorStyleSheet::innerSetText(const String& text, bool markAsLocallyMod |
| else |
| m_cssAgent->addEditedStyleSheet(finalURL(), text); |
| } |
| + return sizeBefore == m_sourceData->size(); |
| } |
| PassRefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() |
| @@ -1358,30 +1472,57 @@ RefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheet::findRuleByBodyRange(c |
| return nullptr; |
| } |
| -RefPtrWillBeRawPtr<CSSRule> InspectorStyleSheet::ruleForSourceData(CSSRuleSourceData* sourceData) |
| +RefPtrWillBeRawPtr<CSSRule> InspectorStyleSheet::ruleForSourceData(RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData) |
| { |
| if (!m_sourceData || !sourceData) |
| return nullptr; |
| - for (size_t i = 0; i < m_sourceData->size(); ++i) { |
| - if (m_sourceData->at(i).get() == sourceData) |
| - return i < m_flatRules.size() ? m_flatRules.at(i) : nullptr; |
| - } |
| - return nullptr; |
| + size_t index = m_sourceData->find(sourceData.get()); |
| + if (index == kNotFound) |
| + return nullptr; |
| + IndexMap::iterator it = m_sourceDataToRule.find(index); |
| + if (it == m_sourceDataToRule.end()) |
| + return nullptr; |
| + ASSERT(it->value < m_flatRules.size()); |
| + return m_flatRules.at(it->value); |
|
lushnikov
2015/06/25 15:44:12
i think you want to re-calc the mapping if you don
|
| } |
| -RefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheet::sourceDataForRule(CSSRule* rule) |
| +RefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheet::sourceDataForRule(RefPtrWillBeRawPtr<CSSRule> rule) |
| { |
| if (!m_sourceData || !rule) |
| return nullptr; |
| - |
| - size_t index = m_flatRules.find(rule); |
| - return index < m_sourceData->size() ? m_sourceData->at(index) : nullptr; |
| + size_t index = m_flatRules.find(rule.get()); |
| + if (index == kNotFound) |
| + return nullptr; |
| + IndexMap::iterator it = m_ruleToSourceData.find(index); |
| + if (it == m_ruleToSourceData.end()) |
| + return nullptr; |
| + ASSERT(it->value < m_sourceData->size()); |
| + return m_sourceData->at(it->value); |
|
lushnikov
2015/06/25 15:44:12
ditto
|
| } |
| -void InspectorStyleSheet::collectFlatRules() |
| +void InspectorStyleSheet::mapSourceDataToCSSOM() |
| { |
| + m_ruleToSourceData.clear(); |
| + m_sourceDataToRule.clear(); |
| + |
| m_flatRules.clear(); |
| - ::collectFlatRules(m_pageStyleSheet.get(), &m_flatRules); |
| + CSSRuleVector& cssomRules = m_flatRules; |
| + collectFlatRules(m_pageStyleSheet.get(), &cssomRules); |
| + |
| + if (!m_sourceData) |
| + return; |
| + |
| + CSSRuleVector parsedRules; |
| + collectFlatRules(m_sourceDataSheet.get(), &parsedRules); |
| + |
| + Vector<String> cssomRulesText = Vector<String>(); |
| + Vector<String> parsedRulesText = Vector<String>(); |
| + for (size_t i = 0; i < cssomRules.size(); ++i) |
| + cssomRulesText.append(cssomRules.at(i)->cssText()); |
| + for (size_t j = 0; j < parsedRules.size(); ++j) |
| + parsedRulesText.append(parsedRules.at(j)->cssText()); |
| + |
| + diff(cssomRulesText, parsedRulesText, &m_ruleToSourceData, &m_sourceDataToRule); |
| } |
| const CSSRuleVector& InspectorStyleSheet::flatRules() |