Back out 2601d36dc1f1 (bug 918716) for bc test failures on a CLOSED TREE
This commit is contained in:
@@ -1,385 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cc, Ci, Cu} = require("chrome");
|
||||
const {colorUtils} = require("devtools/css-color");
|
||||
const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
|
||||
|
||||
const REGEX_QUOTES = /^".*?"|^".*/;
|
||||
const REGEX_URL = /^url\(["']?(.+?)(?::(\d+))?["']?\)/;
|
||||
const REGEX_WHITESPACE = /^\s+/;
|
||||
const REGEX_FIRST_WORD_OR_CHAR = /^\w+|^./;
|
||||
const REGEX_CSS_PROPERTY_VALUE = /(^[^;]+)/;
|
||||
|
||||
/**
|
||||
* This regex matches:
|
||||
* - #F00
|
||||
* - #FF0000
|
||||
* - hsl()
|
||||
* - hsla()
|
||||
* - rgb()
|
||||
* - rgba()
|
||||
* - color names
|
||||
*/
|
||||
const REGEX_ALL_COLORS = /^#[0-9a-fA-F]{3}\b|^#[0-9a-fA-F]{6}\b|^hsl\(.*?\)|^hsla\(.*?\)|^rgba?\(.*?\)|^[a-zA-Z-]+/;
|
||||
|
||||
loader.lazyGetter(this, "DOMUtils", function () {
|
||||
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||
});
|
||||
|
||||
/**
|
||||
* This regular expression catches all css property names with their trailing
|
||||
* spaces and semicolon. This is used to ensure a value is valid for a property
|
||||
* name within style="" attributes.
|
||||
*/
|
||||
loader.lazyGetter(this, "REGEX_ALL_CSS_PROPERTIES", function () {
|
||||
let names = DOMUtils.getCSSPropertyNames();
|
||||
let pattern = "^(";
|
||||
|
||||
for (let i = 0; i < names.length; i++) {
|
||||
if (i > 0) {
|
||||
pattern += "|";
|
||||
}
|
||||
pattern += names[i];
|
||||
}
|
||||
pattern += ")\\s*:\\s*";
|
||||
|
||||
return new RegExp(pattern);
|
||||
});
|
||||
|
||||
/**
|
||||
* This module is used to process text for output by developer tools. This means
|
||||
* linking JS files with the debugger, CSS files with the style editor, JS
|
||||
* functions with the debugger, placing color swatches next to colors and
|
||||
* adding doorhanger previews where possible (images, angles, lengths,
|
||||
* border radius, cubic-bezier etc.).
|
||||
*
|
||||
* Usage:
|
||||
* const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
* const {OutputParser} = devtools.require("devtools/output-parser");
|
||||
*
|
||||
* let parser = new OutputParser();
|
||||
*
|
||||
* parser.parseCssProperty("color", "red"); // Returns document fragment.
|
||||
* parser.parseHTMLAttribute("color:red; font-size: 12px;"); // Returns document
|
||||
* // fragment.
|
||||
*/
|
||||
function OutputParser() {
|
||||
this.parsed = [];
|
||||
}
|
||||
|
||||
exports.OutputParser = OutputParser;
|
||||
|
||||
OutputParser.prototype = {
|
||||
/**
|
||||
* Parse a CSS property value given a property name.
|
||||
*
|
||||
* @param {String} name
|
||||
* CSS Property Name
|
||||
* @param {String} value
|
||||
* CSS Property value
|
||||
* @param {Object} [options]
|
||||
* Options object. For valid options and default values see
|
||||
* _mergeOptions().
|
||||
* @return {DocumentFragment}
|
||||
* A document fragment containing color swatches etc.
|
||||
*/
|
||||
parseCssProperty: function(name, value, options={}) {
|
||||
options = this._mergeOptions(options);
|
||||
options.cssPropertyName = name;
|
||||
|
||||
// Detect if "name" supports colors by checking if "papayawhip" is a valid
|
||||
// value.
|
||||
options.colors = this._cssPropertySupportsValue(name, "papayawhip");
|
||||
|
||||
return this._parse(value, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a string.
|
||||
*
|
||||
* @param {String} value
|
||||
* Text to parse.
|
||||
* @param {Object} [options]
|
||||
* Options object. For valid options and default values see
|
||||
* _mergeOptions().
|
||||
* @return {DocumentFragment}
|
||||
* A document fragment containing events etc. Colors will not be
|
||||
* parsed.
|
||||
*/
|
||||
parseHTMLAttribute: function(value, options={}) {
|
||||
options = this._mergeOptions(options);
|
||||
options.colors = false;
|
||||
|
||||
return this._parse(value, options);
|
||||
},
|
||||
|
||||
/**
|
||||
* Parse a string.
|
||||
*
|
||||
* @param {String} text
|
||||
* Text to parse.
|
||||
* @param {Object} [options]
|
||||
* Options object. For valid options and default values see
|
||||
* _mergeOptions().
|
||||
* @return {DocumentFragment}
|
||||
* A document fragment containing events etc. Colors will not be
|
||||
* parsed.
|
||||
*/
|
||||
_parse: function(text, options={}) {
|
||||
text = text.trim();
|
||||
this.parsed.length = 0;
|
||||
let dirty = false;
|
||||
let matched = null;
|
||||
let nameValueSupported = false;
|
||||
|
||||
let trimMatchFromStart = function(match) {
|
||||
text = text.substr(match.length);
|
||||
dirty = true;
|
||||
matched = null;
|
||||
};
|
||||
|
||||
while (text.length > 0) {
|
||||
matched = text.match(REGEX_QUOTES);
|
||||
if (matched) {
|
||||
let match = matched[0];
|
||||
trimMatchFromStart(match);
|
||||
this._appendTextNode(match);
|
||||
}
|
||||
|
||||
matched = text.match(REGEX_WHITESPACE);
|
||||
if (matched) {
|
||||
let match = matched[0];
|
||||
trimMatchFromStart(match);
|
||||
this._appendTextNode(match);
|
||||
}
|
||||
|
||||
matched = text.match(REGEX_URL);
|
||||
if (matched) {
|
||||
let [match, url, line] = matched;
|
||||
trimMatchFromStart(match);
|
||||
this._appendURL(match, url, line, options);
|
||||
}
|
||||
|
||||
// This block checks for valid name and value combinations setting
|
||||
// nameValueSupported to true as appropriate.
|
||||
matched = text.match(REGEX_ALL_CSS_PROPERTIES);
|
||||
if (matched) {
|
||||
let [match, propertyName] = matched;
|
||||
trimMatchFromStart(match);
|
||||
this._appendTextNode(match);
|
||||
|
||||
matched = text.match(REGEX_CSS_PROPERTY_VALUE);
|
||||
if (matched) {
|
||||
let [, value] = matched;
|
||||
nameValueSupported = this._cssPropertySupportsValue(propertyName, value);
|
||||
}
|
||||
}
|
||||
|
||||
// This block should only be used for CSS properties.
|
||||
// options.cssPropertyName is only set if the parse call comes from a CSS
|
||||
// tool containing either a name and value or a string with valid name and
|
||||
// value combinations.
|
||||
if (options.cssPropertyName || (!options.cssPropertyName && nameValueSupported)) {
|
||||
matched = text.match(REGEX_ALL_COLORS);
|
||||
if (matched) {
|
||||
let match = matched[0];
|
||||
trimMatchFromStart(match);
|
||||
this._appendColor(match, options);
|
||||
}
|
||||
|
||||
nameValueSupported = false;
|
||||
}
|
||||
|
||||
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);
|
||||
nameValueSupported = false;
|
||||
}
|
||||
}
|
||||
|
||||
dirty = false;
|
||||
}
|
||||
|
||||
return this._toDOM();
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a CSS property supports a specific value.
|
||||
*
|
||||
* @param {String} propertyName
|
||||
* CSS Property name to check
|
||||
* @param {String} propertyValue
|
||||
* CSS Property value to check
|
||||
*/
|
||||
_cssPropertySupportsValue: function(propertyName, propertyValue) {
|
||||
let autoCompleteValues = DOMUtils.getCSSValuesForProperty(propertyName);
|
||||
|
||||
// Detect if propertyName supports colors by checking if papayawhip is a
|
||||
// valid value.
|
||||
if (autoCompleteValues.indexOf("papayawhip") !== -1) {
|
||||
return this._isColorValid(propertyValue);
|
||||
}
|
||||
|
||||
// For the rest we can trust autocomplete value matches.
|
||||
return autoCompleteValues.indexOf(propertyValue) !== -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Append a color to the output.
|
||||
*
|
||||
* @param {String} color
|
||||
* Color to append
|
||||
* @param {Object} [options]
|
||||
* Options object. For valid options and default values see
|
||||
* _mergeOptions().
|
||||
*/
|
||||
_appendColor: function(color, options={}) {
|
||||
if (options.colors && this._isColorValid(color)) {
|
||||
if (options.colorSwatchClass) {
|
||||
this._appendNode("span", {
|
||||
class: options.colorSwatchClass,
|
||||
style: "background-color:" + color
|
||||
});
|
||||
}
|
||||
if (options.defaultColorType) {
|
||||
color = new colorUtils.CssColor(color).toString();
|
||||
}
|
||||
}
|
||||
this._appendTextNode(color);
|
||||
},
|
||||
|
||||
/**
|
||||
* Append a URL to the output.
|
||||
*
|
||||
* @param {String} match
|
||||
* Complete match that may include "url(xxx)""
|
||||
* @param {String} url
|
||||
* Actual URL
|
||||
* @param {Number} line
|
||||
* Line number from URL e.g. http://blah:42
|
||||
* @param {Object} [options]
|
||||
* Options object. For valid options and default values see
|
||||
* _mergeOptions().
|
||||
*/
|
||||
_appendURL: function(match, url, line, options={}) {
|
||||
this._appendTextNode(match);
|
||||
},
|
||||
|
||||
/**
|
||||
* Append a node to the output.
|
||||
*
|
||||
* @param {String} tagName
|
||||
* Tag type e.g. "div"
|
||||
* @param {Object} attributes
|
||||
* e.g. {class: "someClass", style: "cursor:pointer"};
|
||||
* @param {String} [value]
|
||||
* If a value is included it will be appended as a text node inside
|
||||
* the tag. This is useful e.g. for span tags.
|
||||
*/
|
||||
_appendNode: function(tagName, attributes, value="") {
|
||||
let win = Services.appShell.hiddenDOMWindow;
|
||||
let doc = win.document;
|
||||
let node = doc.createElement(tagName);
|
||||
let attrs = Object.getOwnPropertyNames(attributes);
|
||||
|
||||
for (let attr of attrs) {
|
||||
node.setAttribute(attr, attributes[attr]);
|
||||
}
|
||||
|
||||
if (value) {
|
||||
let textNode = content.document.createTextNode(value);
|
||||
node.appendChild(textNode);
|
||||
}
|
||||
|
||||
this.parsed.push(node);
|
||||
},
|
||||
|
||||
/**
|
||||
* Append a text node to the output. If the previously output item was a text
|
||||
* node then we append the text to that node.
|
||||
*
|
||||
* @param {String} text
|
||||
* Text to append
|
||||
*/
|
||||
_appendTextNode: function(text) {
|
||||
let lastItem = this.parsed[this.parsed.length - 1];
|
||||
|
||||
if (typeof lastItem !== "undefined" && lastItem.nodeName === "#text") {
|
||||
lastItem.nodeValue += text;
|
||||
} else {
|
||||
let win = Services.appShell.hiddenDOMWindow;
|
||||
let doc = win.document;
|
||||
let textNode = doc.createTextNode(text);
|
||||
this.parsed.push(textNode);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Take all output and append it into a single DocumentFragment.
|
||||
*
|
||||
* @return {DocumentFragment}
|
||||
* Document Fragment
|
||||
*/
|
||||
_toDOM: function() {
|
||||
let win = Services.appShell.hiddenDOMWindow;
|
||||
let doc = win.document;
|
||||
let frag = doc.createDocumentFragment();
|
||||
|
||||
for (let item of this.parsed) {
|
||||
frag.appendChild(item);
|
||||
}
|
||||
|
||||
this.parsed.length = 0;
|
||||
return frag;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check that a string represents a valid volor.
|
||||
*
|
||||
* @param {String} color
|
||||
* Color to check
|
||||
*/
|
||||
_isColorValid: function(color) {
|
||||
return new colorUtils.CssColor(color).valid;
|
||||
},
|
||||
|
||||
/**
|
||||
* Merges options objects. Default values are set here.
|
||||
*
|
||||
* @param {Object} overrides
|
||||
* The option values to override e.g. _mergeOptions({colors: false})
|
||||
*
|
||||
* Valid options are:
|
||||
* - colors: true // Allow processing of colors
|
||||
* - defaultColorType: true // Convert colors to the default type
|
||||
* // selected in the options panel.
|
||||
* - colorSwatchClass: "" // The class to use for color swatches.
|
||||
* - cssPropertyName: "" // Used by CSS tools. Passing in the
|
||||
* // property name allows appropriate
|
||||
* // processing of the property value.
|
||||
* @return {Object}
|
||||
* Overridden options object
|
||||
*/
|
||||
_mergeOptions: function(overrides) {
|
||||
let defaults = {
|
||||
colors: true,
|
||||
defaultColorType: true,
|
||||
colorSwatchClass: "",
|
||||
cssPropertyName: ""
|
||||
};
|
||||
|
||||
for (let item in overrides) {
|
||||
defaults[item] = overrides[item];
|
||||
}
|
||||
return defaults;
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user