diff options
author | Matt A. Tobin <email@mattatobin.com> | 2018-02-04 14:35:31 -0500 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2018-02-04 14:35:31 -0500 |
commit | 7edd685eee95759d66a457cf428f42e0dda94671 (patch) | |
tree | c4514958ea133084552be32d331c115afc509daa /devtools | |
parent | 0083d404eff36f873cde465d50cd34b112bd124f (diff) | |
parent | fc7d9fade54dfbe275c4808dabe30a19415082e0 (diff) | |
download | uxp-7edd685eee95759d66a457cf428f42e0dda94671.tar.gz |
Merge branch 'master' into configurebuild-work
Diffstat (limited to 'devtools')
22 files changed, 351 insertions, 32 deletions
diff --git a/devtools/client/inspector/inspector.js b/devtools/client/inspector/inspector.js index c056c213f3..d0458fc1f7 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 b54157242d..44768b46c6 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 8a02d7e3d0..336dae05b8 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 59dbbbcc0e..052e9da68c 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 0c96e9bbea..57a5dbaa08 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 4f48296787..b6f3e072b6 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 726c93b8b0..b4fb1c6aa9 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 64a2995812..38a21cef6f 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 c3ef5a5c2c..a081f7698e 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 bf211b8b9d..6a18ec12cb 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 4b4cfd031f..0569b7ce76 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 d24c133d42..b22d8005fa 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 20a227a40d..883809b6cf 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 b6f8c0ee4c..a5df148c22 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 f632871e15..c4a073635c 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 b354043d7a..98ddeff195 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 9b3172a220..d61bb4b072 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 67793b2dc8..0af4be620a 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 317f80240c..7d1cc90877 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 ea3d1b264c..022d7f1acf 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 85ece7c485..3e4e028d1a 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 0000000000..2c444308ac --- /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> |