Bug 939098 - Stop markup view parsing color names out of context. r=jwalker

This commit is contained in:
Michael Ratcliffe 2013-11-18 16:07:24 +00:00
parent bef0cc03d8
commit 377a84d5ff
2 changed files with 115 additions and 41 deletions

View File

@ -49,7 +49,6 @@ function testParseHTMLAttribute() {
let attrib = "color:red; font-size: 12px; background-image: " +
"url(chrome://branding/content/about-logo.png)";
let frag = parser.parseHTMLAttribute(attrib, {
colorSwatchClass: "test-colorswatch",
urlClass: "theme-link"
});
@ -57,16 +56,32 @@ function testParseHTMLAttribute() {
ok(target, "captain, we have the div");
target.appendChild(frag);
let expected = 'color:<span style="background-color:red" ' +
'class="test-colorswatch"></span>#F00; font-size: 12px; ' +
let expected = 'color:#F00; font-size: 12px; ' +
'background-image: url(\'<a href="chrome://branding/content/about-logo.png" ' +
'class="theme-link" ' +
'target="_blank">chrome://branding/content/about-logo.png</a>\')';
is(target.innerHTML, expected, "HTML Attribute correctly parsed");
target.innerHTML = "";
testParseNonCssHTMLAttribute();
}
function testParseNonCssHTMLAttribute() {
let attrib = "someclass background someotherclass red";
let frag = parser.parseHTMLAttribute(attrib);
let target = doc.querySelector("div");
ok(target, "captain, we have the div");
target.appendChild(frag);
let expected = 'someclass background someotherclass red';
is(target.innerHTML, expected, "Non-CSS HTML Attribute correctly parsed");
target.innerHTML = "";
finishUp();
}
function finishUp() {
Services = Loader = OutputParser = parser = doc = null;
gBrowser.removeCurrentTab();

View File

@ -113,6 +113,7 @@ OutputParser.prototype = {
* A document fragment. Colors will not be parsed.
*/
parseHTMLAttribute: function(value, options={}) {
options.isHTMLAttribute = true;
options = this._mergeOptions(options);
return this._parse(value, options);
@ -132,80 +133,130 @@ OutputParser.prototype = {
_parse: function(text, options={}) {
text = text.trim();
this.parsed.length = 0;
let dirty = false;
let matched = null;
let i = 0;
let trimMatchFromStart = function(match) {
text = text.substr(match.length);
dirty = true;
matched = null;
};
while (text.length > 0) {
let matched = null;
// Prevent this loop from slowing down the browser with too
// many nodes being appended into output. In practice it is very unlikely
// that this will ever happen.
i++;
if (i > MAX_ITERATIONS) {
this._appendTextNode(text);
text = "";
break;
}
matched = text.match(REGEX_QUOTES);
if (matched) {
let match = matched[0];
trimMatchFromStart(match);
text = this._trimMatchFromStart(text, match);
this._appendTextNode(match);
continue;
}
matched = text.match(REGEX_WHITESPACE);
if (matched) {
let match = matched[0];
trimMatchFromStart(match);
text = this._trimMatchFromStart(text, match);
this._appendTextNode(match);
continue;
}
matched = text.match(REGEX_URL);
if (matched) {
let [match, url] = matched;
trimMatchFromStart(match);
text = this._trimMatchFromStart(text, match);
this._appendURL(match, url, options);
continue;
}
matched = text.match(REGEX_ALL_CSS_PROPERTIES);
if (matched) {
let [match] = matched;
trimMatchFromStart(match);
text = this._trimMatchFromStart(text, match);
this._appendTextNode(match);
dirty = true;
if (options.isHTMLAttribute) {
[text] = this._appendColorOnMatch(text, options);
}
continue;
}
matched = text.match(REGEX_ALL_COLORS);
if (!options.isHTMLAttribute) {
let dirty;
[text, dirty] = this._appendColorOnMatch(text, options);
if (dirty) {
continue;
}
}
// This test must always be last as it indicates use of an unknown
// character that needs to be removed to prevent infinite loops.
matched = text.match(REGEX_FIRST_WORD_OR_CHAR);
if (matched) {
let match = matched[0];
if (this._appendColor(match, options)) {
trimMatchFromStart(match);
}
}
if (!dirty) {
// This test must always be last as it indicates use of an unknown
// character that needs to be removed to prevent infinite loops.
matched = text.match(REGEX_FIRST_WORD_OR_CHAR);
if (matched) {
let match = matched[0];
trimMatchFromStart(match);
this._appendTextNode(match);
}
}
dirty = false;
// Prevent this loop from slowing down the browser with too
// many nodes being appended into output.
i++;
if (i > MAX_ITERATIONS) {
trimMatchFromStart(text);
this._appendTextNode(text);
text = this._trimMatchFromStart(text, match);
this._appendTextNode(match);
}
}
return this._toDOM();
},
/**
* Convenience function to make the parser a little more readable.
*
* @param {String} text
* Main text
* @param {String} match
* Text to remove from the beginning
*
* @return {String}
* The string passed as 'text' with 'match' stripped from the start.
*/
_trimMatchFromStart: function(text, match) {
return text.substr(match.length);
},
/**
* Check if there is a color match and append it if it is valid.
*
* @param {String} text
* Main text
* @param {Object} options
* Options object. For valid options and default values see
* _mergeOptions().
*
* @return {Array}
* An array containing the remaining text and a dirty flag. This array
* is designed for deconstruction using [text, dirty].
*/
_appendColorOnMatch: function(text, options) {
let dirty;
let matched = text.match(REGEX_ALL_COLORS);
if (matched) {
let match = matched[0];
if (this._appendColor(match, options)) {
text = this._trimMatchFromStart(text, match);
dirty = true;
}
} else {
dirty = false;
}
return [text, dirty];
},
/**
* Check if a CSS property supports a specific value.
*
@ -332,7 +383,7 @@ OutputParser.prototype = {
_appendTextNode: function(text) {
let lastItem = this.parsed[this.parsed.length - 1];
if (typeof lastItem === "string") {
this.parsed[this.parsed.length - 1] = lastItem + text
this.parsed[this.parsed.length - 1] = lastItem + text;
} else {
this.parsed.push(text);
}
@ -371,6 +422,13 @@ OutputParser.prototype = {
* - defaultColorType: true // Convert colors to the default type
* // selected in the options panel.
* - colorSwatchClass: "" // The class to use for color swatches.
* - isHTMLAttribute: false // This property indicates whether we
* // are parsing an HTML attribute value.
* // When the value is passed in from an
* // HTML attribute we need to check that
* // any CSS property values are supported
* // by the property name before
* // processing the property value.
* - urlClass: "" // The class to be used for url() links.
* - baseURI: "" // A string or nsIURI used to resolve
* // relative links.
@ -381,6 +439,7 @@ OutputParser.prototype = {
let defaults = {
defaultColorType: true,
colorSwatchClass: "",
isHTMLAttribute: false,
urlClass: "",
baseURI: ""
};