Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(658)

Unified Diff: Source/core/inspector/InspectorStyleSheet.cpp

Issue 172593003: DevTools: [CSS] Add CSS.editRangeInStyleSheetText() to the protocol (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: fix glitch Created 6 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698