summaryrefslogtreecommitdiffstats
path: root/devtools
diff options
context:
space:
mode:
Diffstat (limited to 'devtools')
-rw-r--r--devtools/client/inspector/inspector.js32
-rw-r--r--devtools/client/inspector/markup/views/markup-container.js2
-rw-r--r--devtools/client/inspector/shared/tooltips-overlay.js4
-rw-r--r--devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js1
-rw-r--r--devtools/client/inspector/test/browser_inspector_menu-02-copy-items.js6
-rw-r--r--devtools/client/locales/en-US/inspector.properties6
-rw-r--r--devtools/client/shared/output-parser.js17
-rw-r--r--devtools/client/shared/telemetry.js6
-rw-r--r--devtools/client/shared/test/unit/test_cssColor-03.js6
-rw-r--r--devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js11
-rw-r--r--devtools/client/themes/markup.css19
-rw-r--r--devtools/server/actors/css-properties.js6
-rw-r--r--devtools/server/actors/inspector.js12
-rw-r--r--devtools/server/actors/root.js2
-rw-r--r--devtools/server/css-logic.js49
-rw-r--r--devtools/shared/css/color.js32
-rw-r--r--devtools/shared/fronts/css-properties.js34
-rw-r--r--devtools/shared/gcli/source/lib/gcli/commands/commands.js6
-rw-r--r--devtools/shared/gcli/source/lib/gcli/commands/help.js2
-rw-r--r--devtools/shared/specs/node.js6
-rw-r--r--devtools/shared/tests/mochitest/chrome.ini3
-rw-r--r--devtools/shared/tests/mochitest/test_css-logic-getCssPath.html121
22 files changed, 351 insertions, 32 deletions
diff --git a/devtools/client/inspector/inspector.js b/devtools/client/inspector/inspector.js
index c056c213f..d0458fc1f 100644
--- a/devtools/client/inspector/inspector.js
+++ b/devtools/client/inspector/inspector.js
@@ -169,6 +169,10 @@ Inspector.prototype = {
return this._target.client.traits.getUniqueSelector;
},
+ get canGetCssPath() {
+ return this._target.client.traits.getCssPath;
+ },
+
get canGetUsedFontFaces() {
return this._target.client.traits.getUsedFontFaces;
},
@@ -1074,6 +1078,15 @@ Inspector.prototype = {
click: () => this.copyUniqueSelector(),
}));
copySubmenu.append(new MenuItem({
+ id: "node-menu-copycsspath",
+ label: INSPECTOR_L10N.getStr("inspectorCopyCSSPath.label"),
+ accesskey:
+ INSPECTOR_L10N.getStr("inspectorCopyCSSPath.accesskey"),
+ disabled: !isSelectionElement,
+ hidden: !this.canGetCssPath,
+ click: () => this.copyCssPath(),
+ }));
+ copySubmenu.append(new MenuItem({
id: "node-menu-copyimagedatauri",
label: INSPECTOR_L10N.getStr("inspectorImageDataUri.label"),
disabled: !isSelectionElement || !markupContainer ||
@@ -1677,9 +1690,24 @@ Inspector.prototype = {
return;
}
- this.selection.nodeFront.getUniqueSelector().then((selector) => {
+ this.telemetry.toolOpened("copyuniquecssselector");
+ this.selection.nodeFront.getUniqueSelector().then(selector => {
clipboardHelper.copyString(selector);
- }).then(null, console.error);
+ }).catch(e => console.error);
+ },
+
+ /**
+ * Copy the full CSS Path of the selected Node to the clipboard.
+ */
+ copyCssPath: function () {
+ if (!this.selection.isNode()) {
+ return;
+ }
+
+ this.telemetry.toolOpened("copyfullcssselector");
+ this.selection.nodeFront.getCssPath().then(path => {
+ clipboardHelper.copyString(path);
+ }).catch(e => console.error);
},
/**
diff --git a/devtools/client/inspector/markup/views/markup-container.js b/devtools/client/inspector/markup/views/markup-container.js
index b54157242..44768b46c 100644
--- a/devtools/client/inspector/markup/views/markup-container.js
+++ b/devtools/client/inspector/markup/views/markup-container.js
@@ -211,10 +211,12 @@ MarkupContainer.prototype = {
}
if (this.showExpander) {
+ this.elt.classList.add("expandable");
this.expander.style.visibility = "visible";
// Update accessibility expanded state.
this.tagLine.setAttribute("aria-expanded", this.expanded);
} else {
+ this.elt.classList.remove("expandable");
this.expander.style.visibility = "hidden";
// No need for accessible expanded state indicator when expander is not
// shown.
diff --git a/devtools/client/inspector/shared/tooltips-overlay.js b/devtools/client/inspector/shared/tooltips-overlay.js
index 8a02d7e3d..336dae05b 100644
--- a/devtools/client/inspector/shared/tooltips-overlay.js
+++ b/devtools/client/inspector/shared/tooltips-overlay.js
@@ -88,7 +88,9 @@ TooltipsOverlay.prototype = {
if (this.isRuleView) {
// Color picker tooltip
- this.colorPicker = new SwatchColorPickerTooltip(toolbox.doc, this.view.inspector);
+ this.colorPicker = new SwatchColorPickerTooltip(toolbox.doc,
+ this.view.inspector,
+ this._cssProperties);
// Cubic bezier tooltip
this.cubicBezier = new SwatchCubicBezierTooltip(toolbox.doc);
// Filter editor tooltip
diff --git a/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js b/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
index 59dbbbcc0..052e9da68 100644
--- a/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-01-sensitivity.js
@@ -26,6 +26,7 @@ const ALL_MENU_ITEMS = [
"node-menu-copyinner",
"node-menu-copyouter",
"node-menu-copyuniqueselector",
+ "node-menu-copycsspath",
"node-menu-copyimagedatauri",
"node-menu-delete",
"node-menu-pseudo-hover",
diff --git a/devtools/client/inspector/test/browser_inspector_menu-02-copy-items.js b/devtools/client/inspector/test/browser_inspector_menu-02-copy-items.js
index 0c96e9bbe..57a5dbaa0 100644
--- a/devtools/client/inspector/test/browser_inspector_menu-02-copy-items.js
+++ b/devtools/client/inspector/test/browser_inspector_menu-02-copy-items.js
@@ -26,6 +26,12 @@ const COPY_ITEMS_TEST_DATA = [
text: "body > div:nth-child(1) > p:nth-child(2)",
},
{
+ desc: "copy css path",
+ id: "node-menu-copycsspath",
+ selector: "[data-id=\"copy\"]",
+ text: "html body div p",
+ },
+ {
desc: "copy image data uri",
id: "node-menu-copyimagedatauri",
selector: "#copyimage",
diff --git a/devtools/client/locales/en-US/inspector.properties b/devtools/client/locales/en-US/inspector.properties
index 4f4829678..b6f3e072b 100644
--- a/devtools/client/locales/en-US/inspector.properties
+++ b/devtools/client/locales/en-US/inspector.properties
@@ -154,6 +154,12 @@ inspectorCopyOuterHTML.accesskey=O
inspectorCopyCSSSelector.label=CSS Selector
inspectorCopyCSSSelector.accesskey=S
+# LOCALIZATION NOTE (inspectorCopyCSSPath.label): This is the label
+# shown in the inspector contextual-menu for the item that lets users copy
+# the full CSS path of the current node
+inspectorCopyCSSPath.label=CSS Path
+inspectorCopyCSSPath.accesskey=P
+
# LOCALIZATION NOTE (inspectorPasteOuterHTML.label): This is the label shown
# in the inspector contextual-menu for the item that lets users paste outer
# HTML in the current node
diff --git a/devtools/client/shared/output-parser.js b/devtools/client/shared/output-parser.js
index 726c93b8b..b4fb1c6aa 100644
--- a/devtools/client/shared/output-parser.js
+++ b/devtools/client/shared/output-parser.js
@@ -40,8 +40,11 @@ const CSS_GRID_ENABLED_PREF = "layout.css.grid.enabled";
* where CSS_TYPES is defined in devtools/shared/css/properties-db.js
* @param {Function} isValidOnClient - A function that checks if a css property
* name/value combo is valid.
+ * @param {Function} supportsCssColor4ColorFunction - A function for checking
+ * the supporting of css-color-4 color function.
*/
-function OutputParser(document, {supportsType, isValidOnClient}) {
+function OutputParser(document,
+ {supportsType, isValidOnClient, supportsCssColor4ColorFunction}) {
this.parsed = [];
this.doc = document;
this.supportsType = supportsType;
@@ -50,6 +53,8 @@ function OutputParser(document, {supportsType, isValidOnClient}) {
this.angleSwatches = new WeakMap();
this._onColorSwatchMouseDown = this._onColorSwatchMouseDown.bind(this);
this._onAngleSwatchMouseDown = this._onAngleSwatchMouseDown.bind(this);
+
+ this.cssColor4 = supportsCssColor4ColorFunction();
}
exports.OutputParser = OutputParser;
@@ -188,7 +193,8 @@ OutputParser.prototype = {
if (options.expectCubicBezier && token.text === "cubic-bezier") {
this._appendCubicBezier(functionText, options);
- } else if (colorOK() && colorUtils.isValidCSSColor(functionText)) {
+ } else if (colorOK() &&
+ colorUtils.isValidCSSColor(functionText, this.cssColor4)) {
this._appendColor(functionText, options);
} else {
this._appendTextNode(functionText);
@@ -205,7 +211,8 @@ OutputParser.prototype = {
options.expectDisplay && token.text === "grid" &&
text === token.text) {
this._appendGrid(token.text, options);
- } else if (colorOK() && colorUtils.isValidCSSColor(token.text)) {
+ } else if (colorOK() &&
+ colorUtils.isValidCSSColor(token.text, this.cssColor4)) {
this._appendColor(token.text, options);
} else if (angleOK(token.text)) {
this._appendAngle(token.text, options);
@@ -218,7 +225,7 @@ OutputParser.prototype = {
case "id":
case "hash": {
let original = text.substring(token.startOffset, token.endOffset);
- if (colorOK() && colorUtils.isValidCSSColor(original)) {
+ if (colorOK() && colorUtils.isValidCSSColor(original, this.cssColor4)) {
this._appendColor(original, options);
} else {
this._appendTextNode(original);
@@ -394,7 +401,7 @@ OutputParser.prototype = {
* _mergeOptions().
*/
_appendColor: function (color, options = {}) {
- let colorObj = new colorUtils.CssColor(color);
+ let colorObj = new colorUtils.CssColor(color, this.cssColor4);
if (this._isValidColor(colorObj)) {
let container = this._createNode("span", {
diff --git a/devtools/client/shared/telemetry.js b/devtools/client/shared/telemetry.js
index 64a299581..38a21cef6 100644
--- a/devtools/client/shared/telemetry.js
+++ b/devtools/client/shared/telemetry.js
@@ -163,6 +163,12 @@ Telemetry.prototype = {
toolbareyedropper: {
histogram: "DEVTOOLS_TOOLBAR_EYEDROPPER_OPENED_COUNT",
},
+ copyuniquecssselector: {
+ histogram: "DEVTOOLS_COPY_UNIQUE_CSS_SELECTOR_OPENED_COUNT",
+ },
+ copyfullcssselector: {
+ histogram: "DEVTOOLS_COPY_FULL_CSS_SELECTOR_OPENED_COUNT",
+ },
developertoolbar: {
histogram: "DEVTOOLS_DEVELOPERTOOLBAR_OPENED_COUNT",
timerHistogram: "DEVTOOLS_DEVELOPERTOOLBAR_TIME_ACTIVE_SECONDS"
diff --git a/devtools/client/shared/test/unit/test_cssColor-03.js b/devtools/client/shared/test/unit/test_cssColor-03.js
index c3ef5a5c2..a081f7698 100644
--- a/devtools/client/shared/test/unit/test_cssColor-03.js
+++ b/devtools/client/shared/test/unit/test_cssColor-03.js
@@ -42,15 +42,15 @@ const CSS_COLOR_4_TESTS = [
function run_test() {
for (let test of OLD_STYLE_TESTS) {
- let ours = colorUtils.colorToRGBA(test, true);
+ let ours = colorUtils.colorToRGBA(test, false);
let platform = DOMUtils.colorToRGBA(test);
deepEqual(ours, platform, "color " + test + " matches DOMUtils");
ok(ours !== null, "'" + test + "' is a color");
}
for (let test of CSS_COLOR_4_TESTS) {
- let oursOld = colorUtils.colorToRGBA(test, true);
- let oursNew = colorUtils.colorToRGBA(test, false);
+ let oursOld = colorUtils.colorToRGBA(test, false);
+ let oursNew = colorUtils.colorToRGBA(test, true);
let platform = DOMUtils.colorToRGBA(test);
notEqual(oursOld, platform, "old style parser for color " + test +
" should not match DOMUtils");
diff --git a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
index bf211b8b9..6a18ec12c 100644
--- a/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
+++ b/devtools/client/shared/widgets/tooltip/SwatchColorPickerTooltip.js
@@ -28,8 +28,12 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml";
* inline editor.
* @param {InspectorPanel} inspector
* The inspector panel, needed for the eyedropper.
+ * @param {Function} supportsCssColor4ColorFunction
+ * A function for checking the supporting of css-color-4 color function.
*/
-function SwatchColorPickerTooltip(document, inspector) {
+function SwatchColorPickerTooltip(document,
+ inspector,
+ {supportsCssColor4ColorFunction}) {
let stylesheet = "chrome://devtools/content/shared/widgets/spectrum.css";
SwatchBasedEditorTooltip.call(this, document, stylesheet);
@@ -40,6 +44,7 @@ function SwatchColorPickerTooltip(document, inspector) {
this.spectrum = this.setColorPickerContent([0, 0, 0, 1]);
this._onSpectrumColorChange = this._onSpectrumColorChange.bind(this);
this._openEyeDropper = this._openEyeDropper.bind(this);
+ this.cssColor4 = supportsCssColor4ColorFunction();
}
SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.prototype, {
@@ -159,14 +164,14 @@ SwatchColorPickerTooltip.prototype = Heritage.extend(SwatchBasedEditorTooltip.pr
},
_colorToRgba: function (color) {
- color = new colorUtils.CssColor(color);
+ color = new colorUtils.CssColor(color, this.cssColor4);
let rgba = color._getRGBATuple();
return [rgba.r, rgba.g, rgba.b, rgba.a];
},
_toDefaultType: function (color) {
let colorObj = new colorUtils.CssColor(color);
- colorObj.setAuthoredUnitFromColor(this._originalColor);
+ colorObj.setAuthoredUnitFromColor(this._originalColor, this.cssColor4);
return colorObj.toString();
},
diff --git a/devtools/client/themes/markup.css b/devtools/client/themes/markup.css
index 4b4cfd031..0569b7ce7 100644
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -197,6 +197,22 @@ ul.children + .tag-line::before {
display: inline;
}
+.expandable.collapsed .close::before {
+ /* Display an ellipsis character in collapsed nodes that can be expanded. */
+ content: "\2026";
+ display: inline-block;
+ width: 12px;
+ height: 8px;
+ margin: 0 2px;
+ line-height: 3px;
+ color: var(--theme-body-color-inactive);
+ border-radius: 3px;
+ border-style: solid;
+ border-width: 1px;
+ text-align: center;
+ vertical-align: middle;
+}
+
/* Hide HTML void elements (img, hr, br, …) closing tag when the element is not
* expanded (it can be if it has pseudo-elements attached) */
.child.collapsed > .tag-line .void-element .close {
@@ -318,7 +334,8 @@ ul.children + .tag-line::before {
.theme-selected ~ .editor .theme-fg-color4,
.theme-selected ~ .editor .theme-fg-color5,
.theme-selected ~ .editor .theme-fg-color6,
-.theme-selected ~ .editor .theme-fg-color7 {
+.theme-selected ~ .editor .theme-fg-color7,
+.theme-selected ~ .editor .close::before {
color: var(--theme-selection-color);
}
diff --git a/devtools/server/actors/css-properties.js b/devtools/server/actors/css-properties.js
index d24c133d4..b22d8005f 100644
--- a/devtools/server/actors/css-properties.js
+++ b/devtools/server/actors/css-properties.js
@@ -31,8 +31,12 @@ exports.CssPropertiesActor = ActorClassWithSpec(cssPropertiesSpec, {
getCSSDatabase() {
const properties = generateCssProperties();
const pseudoElements = DOMUtils.getCSSPseudoElementNames();
+ const supportedFeature = {
+ // checking for css-color-4 color function support.
+ "css-color-4-color-function": DOMUtils.isValidCSSColor("rgb(1 1 1 / 100%)"),
+ };
- return { properties, pseudoElements };
+ return { properties, pseudoElements, supportedFeature };
}
});
diff --git a/devtools/server/actors/inspector.js b/devtools/server/actors/inspector.js
index 20a227a40..883809b6c 100644
--- a/devtools/server/actors/inspector.js
+++ b/devtools/server/actors/inspector.js
@@ -626,6 +626,18 @@ var NodeActor = exports.NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
},
/**
+ * Get the full CSS path for this node.
+ *
+ * @return {String} A CSS selector with a part for the node and each of its ancestors.
+ */
+ getCssPath: function () {
+ if (Cu.isDeadWrapper(this.rawNode)) {
+ return "";
+ }
+ return CssLogic.getCssPath(this.rawNode);
+ },
+
+ /**
* Scroll the selected node into view.
*/
scrollIntoView: function () {
diff --git a/devtools/server/actors/root.js b/devtools/server/actors/root.js
index b6f8c0ee4..a5df148c2 100644
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -145,6 +145,8 @@ RootActor.prototype = {
addNewRule: true,
// Whether the dom node actor implements the getUniqueSelector method
getUniqueSelector: true,
+ // Whether the dom node actor implements the getCssPath method
+ getCssPath: true,
// Whether the director scripts are supported
directorScripts: true,
// Whether the debugger server supports
diff --git a/devtools/server/css-logic.js b/devtools/server/css-logic.js
index f632871e1..c4a073635 100644
--- a/devtools/server/css-logic.js
+++ b/devtools/server/css-logic.js
@@ -793,6 +793,55 @@ CssLogic.findCssSelector = function (ele) {
};
/**
+ * Get the full CSS path for a given element.
+ * @returns a string that can be used as a CSS selector for the element. It might not
+ * match the element uniquely. It does however, represent the full path from the root
+ * node to the element.
+ */
+CssLogic.getCssPath = function (ele) {
+ ele = getRootBindingParent(ele);
+ const document = ele.ownerDocument;
+ if (!document || !document.contains(ele)) {
+ throw new Error("getCssPath received element not inside document");
+ }
+
+ const getElementSelector = element => {
+ if (!element.localName) {
+ return "";
+ }
+
+ let label = element.nodeName == element.nodeName.toUpperCase()
+ ? element.localName.toLowerCase()
+ : element.localName;
+
+ if (element.id) {
+ label += "#" + element.id;
+ }
+
+ if (element.classList) {
+ for (let cl of element.classList) {
+ label += "." + cl;
+ }
+ }
+
+ return label;
+ };
+
+ let paths = [];
+
+ while (ele) {
+ if (!ele || ele.nodeType !== Node.ELEMENT_NODE) {
+ break;
+ }
+
+ paths.splice(0, 0, getElementSelector(ele));
+ ele = ele.parentNode;
+ }
+
+ return paths.length ? paths.join(" ") : "";
+}
+
+/**
* A safe way to access cached bits of information about a stylesheet.
*
* @constructor
diff --git a/devtools/shared/css/color.js b/devtools/shared/css/color.js
index b354043d7..98ddeff19 100644
--- a/devtools/shared/css/color.js
+++ b/devtools/shared/css/color.js
@@ -28,6 +28,10 @@ const SPECIALVALUES = new Set([
* Usage:
* let {colorUtils} = require("devtools/shared/css/color");
* let color = new colorUtils.CssColor("red");
+ * // In order to support css-color-4 color function, pass true to the
+ * // second argument.
+ * // e.g.
+ * // let color = new colorUtils.CssColor("red", true);
*
* color.authored === "red"
* color.hasAlpha === false
@@ -58,8 +62,9 @@ const SPECIALVALUES = new Set([
* Valid values for COLOR_UNIT_PREF are contained in CssColor.COLORUNIT.
*/
-function CssColor(colorValue) {
+function CssColor(colorValue, supportsCssColor4ColorFunction = false) {
this.newColor(colorValue);
+ this.cssColor4 = supportsCssColor4ColorFunction;
}
module.exports.colorUtils = {
@@ -92,6 +97,9 @@ CssColor.prototype = {
// A lower-cased copy of |authored|.
lowerCased: null,
+ // Whether the value should be parsed using css-color-4 rules.
+ cssColor4: false,
+
_setColorUnitUppercase: function (color) {
// Specifically exclude the case where the color is
// case-insensitive. This makes it so that "#000" isn't
@@ -136,7 +144,7 @@ CssColor.prototype = {
},
get valid() {
- return isValidCSSColor(this.authored);
+ return isValidCSSColor(this.authored, this.cssColor4);
},
/**
@@ -393,7 +401,7 @@ CssColor.prototype = {
* appropriate.
*/
_getRGBATuple: function () {
- let tuple = colorToRGBA(this.authored);
+ let tuple = colorToRGBA(this.authored, this.cssColor4);
tuple.a = parseFloat(tuple.a.toFixed(1));
@@ -481,11 +489,13 @@ function roundTo(number, digits) {
* Color in the form of hex, hsl, hsla, rgb, rgba.
* @param {Number} alpha
* Alpha value for the color, between 0 and 1.
+ * @param {Boolean} useCssColor4ColorFunction
+ * use css-color-4 color function or not.
* @return {String}
* Converted color with `alpha` value in rgba form.
*/
-function setAlpha(colorValue, alpha) {
- let color = new CssColor(colorValue);
+function setAlpha(colorValue, alpha, useCssColor4ColorFunction = false) {
+ let color = new CssColor(colorValue, useCssColor4ColorFunction);
// Throw if the color supplied is not valid.
if (!color.valid) {
@@ -1049,12 +1059,11 @@ function parseOldStyleRgb(lexer, hasAlpha) {
* color's components. Any valid CSS color form can be passed in.
*
* @param {String} name the color
- * @param {Boolean} oldColorFunctionSyntax use old color function syntax or the
- * css-color-4 syntax
+ * @param {Boolean} useCssColor4ColorFunction use css-color-4 color function or not.
* @return {Object} an object of the form {r, g, b, a}; or null if the
* name was not a valid color
*/
-function colorToRGBA(name, oldColorFunctionSyntax = true) {
+function colorToRGBA(name, useCssColor4ColorFunction = false) {
name = name.trim().toLowerCase();
if (name in cssColors) {
@@ -1089,7 +1098,7 @@ function colorToRGBA(name, oldColorFunctionSyntax = true) {
let hsl = func.text === "hsl" || func.text === "hsla";
let vals;
- if (oldColorFunctionSyntax) {
+ if (!useCssColor4ColorFunction) {
let hasAlpha = (func.text === "rgba" || func.text === "hsla");
vals = hsl ? parseOldStyleHsl(lexer, hasAlpha) : parseOldStyleRgb(lexer, hasAlpha);
} else {
@@ -1110,8 +1119,9 @@ function colorToRGBA(name, oldColorFunctionSyntax = true) {
* Check whether a string names a valid CSS color.
*
* @param {String} name The string to check
+ * @param {Boolean} useCssColor4ColorFunction use css-color-4 color function or not.
* @return {Boolean} True if the string is a CSS color name.
*/
-function isValidCSSColor(name) {
- return colorToRGBA(name) !== null;
+function isValidCSSColor(name, useCssColor4ColorFunction = false) {
+ return colorToRGBA(name, useCssColor4ColorFunction) !== null;
}
diff --git a/devtools/shared/fronts/css-properties.js b/devtools/shared/fronts/css-properties.js
index 9b3172a22..d61bb4b07 100644
--- a/devtools/shared/fronts/css-properties.js
+++ b/devtools/shared/fronts/css-properties.js
@@ -47,6 +47,20 @@ const CssPropertiesFront = FrontClassWithSpec(cssPropertiesSpec, {
});
/**
+ * Query the feature supporting status in the featureSet.
+ *
+ * @param {Hashmap} featureSet the feature set hashmap
+ * @param {String} feature the feature name string
+ * @return {Boolean} has the feature or not
+ */
+function hasFeature(featureSet, feature) {
+ if (feature in featureSet) {
+ return featureSet[feature];
+ }
+ return false;
+}
+
+/**
* Ask questions to a CSS database. This class does not care how the database
* gets loaded in, only the questions that you can ask to it.
* Prototype functions are bound to 'this' so they can be passed around as helper
@@ -62,10 +76,16 @@ function CssProperties(db) {
this.properties = db.properties;
this.pseudoElements = db.pseudoElements;
+ // supported feature
+ this.cssColor4ColorFunction = hasFeature(db.supportedFeature,
+ "css-color-4-color-function");
+
this.isKnown = this.isKnown.bind(this);
this.isInherited = this.isInherited.bind(this);
this.supportsType = this.supportsType.bind(this);
this.isValidOnClient = this.isValidOnClient.bind(this);
+ this.supportsCssColor4ColorFunction =
+ this.supportsCssColor4ColorFunction.bind(this);
// A weakly held dummy HTMLDivElement to test CSS properties on the client.
this._dummyElements = new WeakMap();
@@ -181,6 +201,15 @@ CssProperties.prototype = {
}
return [];
},
+
+ /**
+ * Checking for the css-color-4 color function support.
+ *
+ * @return {Boolean} Return true if the server supports css-color-4 color function.
+ */
+ supportsCssColor4ColorFunction() {
+ return this.cssColor4ColorFunction;
+ },
};
/**
@@ -292,6 +321,11 @@ function normalizeCssData(db) {
reattachCssColorValues(db);
+ // If there is no supportedFeature in db, create an empty one.
+ if (!db.supportedFeature) {
+ db.supportedFeature = {};
+ }
+
return db;
}
diff --git a/devtools/shared/gcli/source/lib/gcli/commands/commands.js b/devtools/shared/gcli/source/lib/gcli/commands/commands.js
index 67793b2dc..0af4be620 100644
--- a/devtools/shared/gcli/source/lib/gcli/commands/commands.js
+++ b/devtools/shared/gcli/source/lib/gcli/commands/commands.js
@@ -335,10 +335,10 @@ Parameter.prototype.toJson = function() {
};
// Values do not need to be serializable, so we don't try. For the client
- // side (which doesn't do any executing) we don't actually care what the
- // default value is, just that it exists
+ // side (which doesn't do any executing) we only care whether default value is
+ // undefined, null, or something else.
if (this.paramSpec.defaultValue !== undefined) {
- json.defaultValue = {};
+ json.defaultValue = (this.paramSpec.defaultValue === null) ? null : {};
}
if (this.paramSpec.description != null) {
json.description = this.paramSpec.description;
diff --git a/devtools/shared/gcli/source/lib/gcli/commands/help.js b/devtools/shared/gcli/source/lib/gcli/commands/help.js
index 317f80240..7d1cc9087 100644
--- a/devtools/shared/gcli/source/lib/gcli/commands/help.js
+++ b/devtools/shared/gcli/source/lib/gcli/commands/help.js
@@ -69,7 +69,7 @@ function getHelpManData(commandData, context) {
}
else {
// We need defaultText to work the text version of defaultValue
- input = l10n.lookupFormat('helpManOptional');
+ input = l10n.lookup('helpManOptional');
/*
var val = param.type.stringify(param.defaultValue);
input = Promise.resolve(val).then(function(defaultValue) {
diff --git a/devtools/shared/specs/node.js b/devtools/shared/specs/node.js
index ea3d1b264..022d7f1ac 100644
--- a/devtools/shared/specs/node.js
+++ b/devtools/shared/specs/node.js
@@ -37,6 +37,12 @@ const nodeSpec = generateActorSpec({
value: RetVal("string")
}
},
+ getCssPath: {
+ request: {},
+ response: {
+ value: RetVal("string")
+ }
+ },
scrollIntoView: {
request: {},
response: {}
diff --git a/devtools/shared/tests/mochitest/chrome.ini b/devtools/shared/tests/mochitest/chrome.ini
index 85ece7c48..3e4e028d1 100644
--- a/devtools/shared/tests/mochitest/chrome.ini
+++ b/devtools/shared/tests/mochitest/chrome.ini
@@ -2,6 +2,7 @@
tags = devtools
skip-if = os == 'android'
-[test_eventemitter_basic.html]
+[test_css-logic-getCssPath.html]
[test_devtools_extensions.html]
+[test_eventemitter_basic.html]
skip-if = os == 'linux' && debug # Bug 1205739
diff --git a/devtools/shared/tests/mochitest/test_css-logic-getCssPath.html b/devtools/shared/tests/mochitest/test_css-logic-getCssPath.html
new file mode 100644
index 000000000..2c444308a
--- /dev/null
+++ b/devtools/shared/tests/mochitest/test_css-logic-getCssPath.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1323700
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1323700</title>
+
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ <script type="application/javascript;version=1.8">
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+const CssLogic = require("devtools/shared/inspector/css-logic");
+
+var _tests = [];
+function addTest(test) {
+ _tests.push(test);
+}
+
+function runNextTest() {
+ if (_tests.length == 0) {
+ SimpleTest.finish()
+ return;
+ }
+ _tests.shift()();
+}
+
+window.onload = function() {
+ SimpleTest.waitForExplicitFinish();
+ runNextTest();
+}
+
+addTest(function getCssPathForUnattachedElement() {
+ var unattached = document.createElement("div");
+ unattached.id = "unattached";
+ try {
+ CssLogic.getCssPath(unattached);
+ ok(false, "Unattached node did not throw")
+ } catch(e) {
+ ok(e, "Unattached node throws an exception");
+ }
+
+ var unattachedChild = document.createElement("div");
+ unattached.appendChild(unattachedChild);
+ try {
+ CssLogic.getCssPath(unattachedChild);
+ ok(false, "Unattached child node did not throw")
+ } catch(e) {
+ ok(e, "Unattached child node throws an exception");
+ }
+
+ var unattachedBody = document.createElement("body");
+ try {
+ CssLogic.getCssPath(unattachedBody);
+ ok(false, "Unattached body node did not throw")
+ } catch(e) {
+ ok(e, "Unattached body node throws an exception");
+ }
+
+ runNextTest();
+});
+
+addTest(function cssPathHasOneStepForEachAncestor() {
+ for (let el of [...document.querySelectorAll('*')]) {
+ let splitPath = CssLogic.getCssPath(el).split(" ");
+
+ let expectedNbOfParts = 0;
+ var parent = el.parentNode;
+ while (parent) {
+ expectedNbOfParts ++;
+ parent = parent.parentNode;
+ }
+
+ is(splitPath.length, expectedNbOfParts, "There are enough parts in the full path");
+ }
+
+ runNextTest();
+});
+
+addTest(function getCssPath() {
+ let data = [{
+ selector: "#id",
+ path: "html body div div div.class div#id"
+ }, {
+ selector: "html",
+ path: "html"
+ }, {
+ selector: "body",
+ path: "html body"
+ }, {
+ selector: ".c1.c2.c3",
+ path: "html body span.c1.c2.c3"
+ }, {
+ selector: "#i",
+ path: "html body span#i.c1.c2"
+ }];
+
+ for (let {selector, path} of data) {
+ let node = document.querySelector(selector);
+ is (CssLogic.getCssPath(node), path, `Full css path is correct for ${selector}`);
+ }
+
+ runNextTest();
+});
+ </script>
+</head>
+<body>
+ <div>
+ <div>
+ <div class="class">
+ <div id="id"></div>
+ </div>
+ </div>
+ </div>
+ <span class="c1 c2 c3"></span>
+ <span id="i" class="c1 c2"></span>
+</body>
+</html>