Chromium Code Reviews| Index: Source/core/inspector/InspectorStyleSheet.cpp |
| diff --git a/Source/core/inspector/InspectorStyleSheet.cpp b/Source/core/inspector/InspectorStyleSheet.cpp |
| index 8bcbe9da32d59a0bfba160c2b742e5cfa2f83538..2f1ebd3d4cd59a52f95c501e1a3e2e17fd6f5a3a 100644 |
| --- a/Source/core/inspector/InspectorStyleSheet.cpp |
| +++ b/Source/core/inspector/InspectorStyleSheet.cpp |
| @@ -367,6 +367,7 @@ public: |
| bool ensureSourceData(); |
| bool hasSourceData() const { return m_sourceData; } |
| PassRefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned) const; |
| + size_t size() const { return m_sourceData->size(); } |
| private: |
| void flattenSourceData(RuleSourceDataList*); |
| @@ -387,7 +388,7 @@ ParsedStyleSheet::ParsedStyleSheet(CSSStyleSheet* pageStyleSheet) |
| void ParsedStyleSheet::setText(const String& text) |
| { |
| m_hasText = true; |
| - m_text = text; |
| + m_text = text.isNull() ? "" : text; |
| setSourceData(nullptr); |
| } |
| @@ -931,6 +932,192 @@ bool InspectorStyleSheet::setText(const String& text, ExceptionState& exceptionS |
| return true; |
| } |
| +static SourceRange ruleSourceRange(const CSSRuleSourceData* ruleSourceData) |
| +{ |
| + return SourceRange(ruleSourceData->ruleHeaderRange.start, ruleSourceData->ruleBodyRange.end); |
| +} |
| + |
| +static bool isSubRange(const SourceRange& ownerRange, const SourceRange& subRange) |
| +{ |
| + return ownerRange.start <= subRange.start && subRange.end <= ownerRange.end; |
| +} |
| + |
| +static bool verifyEditLocalizedToRule(const ParsedStyleSheet* originalData, const ParsedStyleSheet* modifiedData, const unsigned ruleIndex, ExceptionState& exceptionState) |
|
vsevik
2014/03/13 10:44:51
This method does something different from what its
|
| +{ |
| + if (originalData->size() != modifiedData->size()) { |
| + exceptionState.throwDOMException(NotFoundError, "Edit should not damage rules."); |
| + return false; |
| + } |
| + |
| + int sourceRangeDelta = originalData->text().length() - modifiedData->text().length(); |
| + for (size_t i = ruleIndex + 1; i < originalData->size(); ++i) { |
| + SourceRange originalRuleRange = ruleSourceRange(originalData->ruleSourceDataAt(i).get()); |
| + SourceRange modifiedRuleRange = ruleSourceRange(modifiedData->ruleSourceDataAt(i).get()); |
| + if (originalRuleRange.start != modifiedRuleRange.start + sourceRangeDelta |
| + || originalRuleRange.end != modifiedRuleRange.end + sourceRangeDelta) { |
| + exceptionState.throwDOMException(NotFoundError, "Edit should not damage rules."); |
| + return false; |
| + } |
| + } |
| + return true; |
| +} |
| + |
| +static void countPropertiesDelta(const CSSStyleSourceData* originalStyle, const CSSStyleSourceData* modifiedStyle, size_t* firstChangedIndex, size_t* addedCount, size_t* removedCount) |
| +{ |
| + const Vector<CSSPropertySourceData>& originalPropertyData = originalStyle->propertyData; |
| + const Vector<CSSPropertySourceData>& modifiedPropertyData = modifiedStyle->propertyData; |
| + int originalSize = originalPropertyData.size(); |
| + int modifiedSize = modifiedPropertyData.size(); |
| + int minSize = std::min(originalSize, modifiedSize); |
| + int leadingCongruent = 0; |
| + while (leadingCongruent < minSize) { |
| + if (originalPropertyData.at(leadingCongruent).hash() != modifiedPropertyData.at(leadingCongruent).hash()) |
| + break; |
| + ++leadingCongruent; |
| + } |
| + int trailingCongruent = 0; |
| + while (trailingCongruent < minSize) { |
| + if (originalPropertyData.at(originalSize - trailingCongruent - 1).hash() != modifiedPropertyData.at(modifiedSize - trailingCongruent - 1).hash()) |
| + break; |
| + ++trailingCongruent; |
| + } |
| + *firstChangedIndex = leadingCongruent; |
| + *addedCount = std::max(modifiedSize - leadingCongruent - trailingCongruent, 0); |
| + *removedCount = std::max(originalSize - leadingCongruent - trailingCongruent, 0); |
| +} |
| + |
| +bool InspectorStyleSheet::handleSelectorEditScope(const CSSRuleSourceData* originalData, const CSSRuleSourceData* modifiedData, const String& modifiedText, const unsigned ruleIndex, EditRangeResult* editRangeResult, ExceptionState& exceptionState) |
| +{ |
| + size_t firstChangedIndex; |
| + size_t addedCount; |
| + size_t removedCount; |
| + countPropertiesDelta(originalData->styleSourceData.get(), modifiedData->styleSourceData.get(), &firstChangedIndex, &addedCount, &removedCount); |
| + if (addedCount || removedCount) { |
| + exceptionState.throwDOMException(NotFoundError, "Rule selector edit should not damage rule properties."); |
| + return false; |
| + } |
| + const String selectorText = modifiedText.substring(modifiedData->ruleHeaderRange.start, modifiedData->ruleHeaderRange.length()); |
| + if (!setRuleSelector(InspectorCSSId(id(), ruleIndex), selectorText, exceptionState)) |
| + return false; |
| + editRangeResult->rule = ruleForId(InspectorCSSId(id(), ruleIndex)); |
| + return true; |
| +} |
| + |
| +bool InspectorStyleSheet::handlePropertyScope(const CSSStyleSourceData* originalData, const CSSStyleSourceData* modifiedData, const unsigned ruleIndex, const PropertyScopeEditType editType, EditRangeResult* editRangeResult, ExceptionState& exceptionState) |
| +{ |
| + size_t firstChangedIndex; |
| + size_t addedCount; |
| + size_t removedCount; |
| + countPropertiesDelta(originalData, modifiedData, &firstChangedIndex, &addedCount, &removedCount); |
| + if (!addedCount && !removedCount) { |
| + editRangeResult->style = styleForId(InspectorCSSId(id(), ruleIndex)); |
| + return true; |
| + } |
| + if (editType == EditExistingProperty && removedCount != 1) { |
| + exceptionState.throwDOMException(NotFoundError, "Rule property edit should not damage more then one property."); |
| + return false; |
| + } |
| + if (editType == AppendNewProperty && removedCount) { |
| + exceptionState.throwDOMException(NotFoundError, "Rule property insertion should not damage any properties."); |
| + return false; |
| + } |
| + StringBuilder newPropertyText; |
| + for (size_t i = firstChangedIndex; i < firstChangedIndex + addedCount; ++i) |
| + newPropertyText.append(modifiedData->propertyData.at(i).toString()); |
| + String placeholder; |
| + if (!setPropertyText(InspectorCSSId(id(), ruleIndex), firstChangedIndex, newPropertyText.toString(), editType == EditExistingProperty, &placeholder, exceptionState)) |
| + return false; |
| + editRangeResult->style = styleForId(InspectorCSSId(id(), ruleIndex)); |
| + return true; |
| +} |
| + |
| +bool InspectorStyleSheet::handleStyleSheetEditScope(const ParsedStyleSheet* originalData, const ParsedStyleSheet* modifiedData, EditRangeResult* editRangeResult, ExceptionState& exceptionState) |
| +{ |
| + size_t originalSize = originalData->size(); |
| + size_t modifiedSize = modifiedData->size(); |
| + if (modifiedSize - originalSize != 1) { |
| + exceptionState.throwDOMException(NotFoundError, "Editing in stylesheet scope should insert a single rule."); |
| + return false; |
| + } |
| + size_t minSize = std::min(originalSize, modifiedSize); |
|
vsevik
2014/03/13 10:44:51
size_t minSize = originalSize;
So let's remove it.
|
| + size_t leadingCongruent = 0; |
| + while (leadingCongruent < minSize) { |
|
vsevik
2014/03/13 10:44:51
Can we use text for this instead?
|
| + SourceRange originalRuleRange = ruleSourceRange(originalData->ruleSourceDataAt(leadingCongruent).get()); |
| + SourceRange modifiedRuleRange = ruleSourceRange(modifiedData->ruleSourceDataAt(leadingCongruent).get()); |
| + if (originalRuleRange.start != modifiedRuleRange.start || originalRuleRange.end != modifiedRuleRange.end) |
| + break; |
| + ++leadingCongruent; |
| + } |
| + |
| + int sourceRangeDelta = originalData->text().length() - modifiedData->text().length(); |
| + size_t trailingCongruent = 0; |
| + while (trailingCongruent < minSize) { |
| + SourceRange originalRuleRange = ruleSourceRange(originalData->ruleSourceDataAt(originalSize - trailingCongruent - 1).get()); |
| + SourceRange modifiedRuleRange = ruleSourceRange(modifiedData->ruleSourceDataAt(modifiedSize - trailingCongruent - 1).get()); |
| + if (originalRuleRange.start != modifiedRuleRange.start + sourceRangeDelta || originalRuleRange.end != modifiedRuleRange.end + sourceRangeDelta) |
| + break; |
| + ++trailingCongruent; |
| + } |
| + if (leadingCongruent + trailingCongruent != originalSize) { |
| + exceptionState.throwDOMException(NotFoundError, "Rules should not damage with edit in stylesheet scope."); |
| + return false; |
| + } |
| + setText(modifiedData->text(), ASSERT_NO_EXCEPTION); |
| + reparseStyleSheet(modifiedData->text()); |
| + editRangeResult->rule = ruleForId(InspectorCSSId(id(), leadingCongruent)); |
| + return true; |
| +} |
| + |
| +bool InspectorStyleSheet::editRange(const String& text, unsigned startOffset, unsigned endOffset, EditRangeResult* editRangeResult, ExceptionState& exceptionState) |
| +{ |
| + String modifiedText; |
| + if (!getText(&modifiedText)) { |
| + exceptionState.throwDOMException(NotFoundError, "Stylesheet text could not be fetched."); |
| + return false; |
| + } |
| + modifiedText.replace(startOffset, endOffset - startOffset, text); |
| + OwnPtr<ParsedStyleSheet> editedParsedData = adoptPtr(new ParsedStyleSheet(m_pageStyleSheet.get())); |
| + editedParsedData->setText(modifiedText); |
| + if (!m_parsedStyleSheet->ensureSourceData()) { |
| + exceptionState.throwDOMException(NotFoundError, "Original text could not be parsed."); |
| + return false; |
| + } |
| + if (!editedParsedData->ensureSourceData()) { |
| + exceptionState.throwDOMException(NotFoundError, "Edited text could not be parsed."); |
| + return false; |
| + } |
| + if (m_parsedStyleSheet->size() - editedParsedData->size()) |
| + return handleStyleSheetEditScope(m_parsedStyleSheet, editedParsedData.get(), editRangeResult, exceptionState); |
| + |
| + SourceRange editRange = SourceRange(startOffset, endOffset); |
| + for (size_t i = 0; i < m_parsedStyleSheet->size(); ++i) { |
| + RefPtr<CSSRuleSourceData> ruleData = m_parsedStyleSheet->ruleSourceDataAt(i); |
| + if (!ruleData->styleSourceData) |
| + continue; |
|
vsevik
2014/03/13 10:44:51
if (!editRange intersects with rule range)
con
|
| + if (isSubRange(ruleData->ruleHeaderRange, editRange)) { |
| + return verifyEditLocalizedToRule(m_parsedStyleSheet, editedParsedData.get(), i, exceptionState) |
| + && handleSelectorEditScope(m_parsedStyleSheet->ruleSourceDataAt(i).get(), editedParsedData->ruleSourceDataAt(i).get(), modifiedText, i, editRangeResult, exceptionState); |
| + } |
| + Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData; |
| + const CSSStyleSourceData* originalStyleData = m_parsedStyleSheet->ruleSourceDataAt(i)->styleSourceData.get(); |
| + const CSSStyleSourceData* modifiedStyleData = editedParsedData->ruleSourceDataAt(i)->styleSourceData.get(); |
| + if (editRange.length()) { |
| + for (size_t j = 0; j < propertyData.size(); ++j) { |
| + CSSPropertySourceData& property = propertyData.at(j); |
| + SourceRange globalPropertyRange = SourceRange(ruleData->ruleBodyRange.start + property.range.start, ruleData->ruleBodyRange.start + property.range.end); |
| + if (isSubRange(globalPropertyRange, editRange)) { |
| + return verifyEditLocalizedToRule(m_parsedStyleSheet, editedParsedData.get(), i, exceptionState) |
| + && handlePropertyScope(originalStyleData, modifiedStyleData, i, EditExistingProperty, editRangeResult, exceptionState); |
| + } |
| + } |
| + } else if (isSubRange(ruleData->ruleBodyRange, editRange)) { |
| + return verifyEditLocalizedToRule(m_parsedStyleSheet, editedParsedData.get(), i, exceptionState) |
| + && handlePropertyScope(originalStyleData, modifiedStyleData, i, AppendNewProperty, editRangeResult, exceptionState); |
| + } |
| + } |
| + return handleStyleSheetEditScope(m_parsedStyleSheet, editedParsedData.get(), editRangeResult, exceptionState); |
|
vsevik
2014/03/13 10:44:51
return false;
|
| +} |
| + |
| String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionState& exceptionState) |
| { |
| CSSStyleRule* rule = ruleForId(id); |
| @@ -1376,7 +1563,7 @@ PassRefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDec |
| PassOwnPtr<Vector<unsigned> > InspectorStyleSheet::lineEndings() const |
| { |
| - if (!m_parsedStyleSheet->hasText()) |
| + if (!ensureText()) |
| return PassOwnPtr<Vector<unsigned> >(); |
| return WTF::lineEndings(m_parsedStyleSheet->text()); |
| } |
| @@ -1486,6 +1673,19 @@ InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const |
| return ruleOrStyleId(rule->style()); |
| } |
| +bool InspectorStyleSheet::lineNumberAndColumnToOffset(unsigned lineNumber, unsigned columnNumber, unsigned* offset) const |
| +{ |
| + OwnPtr<Vector<unsigned> > endings = lineEndings(); |
| + if (lineNumber >= endings->size()) |
| + return false; |
| + unsigned charactersInLine = lineNumber > 0 ? endings->at(lineNumber) - endings->at(lineNumber - 1) - 1 : endings->at(0); |
| + if (columnNumber > charactersInLine) |
| + return false; |
| + TextPosition position(OrdinalNumber::fromZeroBasedInt(lineNumber), OrdinalNumber::fromZeroBasedInt(columnNumber)); |
| + *offset = position.toOffsetPosition(*endings).zeroBasedInt(); |
| + return true; |
| +} |
| + |
| bool InspectorStyleSheet::originalStyleSheetText(String* result) const |
| { |
| bool success = inlineStyleSheetText(result); |
| @@ -1560,12 +1760,53 @@ bool InspectorStyleSheetForInlineStyle::setText(const String& text, ExceptionSta |
| return success; |
| } |
| +static PassRefPtr<CSSRuleSourceData> parseStyleDeclaration(Document& document, const String& text) |
| +{ |
| + RefPtr<MutableStylePropertySet> tempDeclaration = MutableStylePropertySet::create(); |
| + RuleSourceDataList ruleSourceDataResult; |
| + StyleSheetHandler handler(text, &document, document.elementSheet().contents(), &ruleSourceDataResult); |
| + createCSSParser(&document)->parseDeclaration(tempDeclaration.get(), text, &handler, document.elementSheet().contents()); |
| + return ruleSourceDataResult.first().release(); |
| +} |
| + |
| +bool InspectorStyleSheetForInlineStyle::editRange(const String& text, unsigned startOffset, unsigned endOffset, EditRangeResult* editRangeResult, ExceptionState& exceptionState) |
| +{ |
| + String modifiedText; |
| + if (!getText(&modifiedText)) { |
| + exceptionState.throwDOMException(NotFoundError, "Inline style attribute text could not be fetched."); |
| + return false; |
| + } |
| + modifiedText.replace(startOffset, endOffset - startOffset, text); |
| + |
| + RefPtr<CSSRuleSourceData> originalRuleData = getStyleAttributeData(); |
| + RefPtr<CSSRuleSourceData> modifiedRuleData = parseStyleDeclaration(m_element->document(), modifiedText); |
| + |
| + CSSStyleSourceData* originalStyleData = originalRuleData->styleSourceData.get(); |
| + CSSStyleSourceData* modifiedStyleData = modifiedRuleData->styleSourceData.get(); |
| + |
| + SourceRange editRange = SourceRange(startOffset, endOffset); |
| + |
| + Vector<CSSPropertySourceData>& propertyData = originalStyleData->propertyData; |
| + if (editRange.length()) { |
| + for (size_t j = 0; j < propertyData.size(); ++j) { |
| + CSSPropertySourceData& property = propertyData.at(j); |
| + if (isSubRange(property.range, editRange)) |
| + return handlePropertyScope(originalStyleData, modifiedStyleData, 0, EditExistingProperty, editRangeResult, exceptionState); |
| + } |
| + exceptionState.throwDOMException(NotFoundError, "Specified edit range does not belong to any property."); |
| + return false; |
| + } |
| + return handlePropertyScope(originalStyleData, modifiedStyleData, 0, AppendNewProperty, editRangeResult, exceptionState); |
| +} |
| + |
| bool InspectorStyleSheetForInlineStyle::getText(String* result) const |
| { |
| if (!m_isStyleTextValid) { |
| m_styleText = elementStyleText(); |
| m_isStyleTextValid = true; |
| } |
| + if (m_styleText.isNull()) |
| + m_styleText = ""; |
| *result = m_styleText; |
| return true; |
| } |
| @@ -1648,11 +1889,7 @@ PassRefPtr<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::getStyleAttribu |
| return result.release(); |
| } |
| - RefPtr<MutableStylePropertySet> tempDeclaration = MutableStylePropertySet::create(); |
| - RuleSourceDataList ruleSourceDataResult; |
| - StyleSheetHandler handler(m_styleText, &m_element->document(), m_element->document().elementSheet().contents(), &ruleSourceDataResult); |
| - createCSSParser(&m_element->document())->parseDeclaration(tempDeclaration.get(), m_styleText, &handler, m_element->document().elementSheet().contents()); |
| - return ruleSourceDataResult.first().release(); |
| + return parseStyleDeclaration(m_element->document(), m_styleText); |
| } |
| } // namespace WebCore |