diff --git a/browser/devtools/shared/test/browser_css_color.js b/browser/devtools/shared/test/browser_css_color.js index 4f4f29be5f0e..f1dd1d537448 100644 --- a/browser/devtools/shared/test/browser_css_color.js +++ b/browser/devtools/shared/test/browser_css_color.js @@ -1,7 +1,6 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ -const COLOR_UNIT_PREF = "devtools.defaultColorUnit"; const TEST_URI = "data:text/html;charset=utf-8,browser_css_color.js"; let {colorUtils} = devtools.require("devtools/css-color"); let origColorUnit; @@ -9,7 +8,6 @@ let origColorUnit; add_task(function*() { yield promiseTab("about:blank"); let [host, win, doc] = yield createHost("bottom", TEST_URI); - origColorUnit = Services.prefs.getCharPref(COLOR_UNIT_PREF); info("Creating a test canvas element to test colors"); let canvas = createTestCanvas(doc); @@ -49,23 +47,19 @@ function testColorUtils(canvas) { } function testToString(color, name, hex, hsl, rgb) { - switchColorUnit(colorUtils.CssColor.COLORUNIT.name); + color.colorUnit = colorUtils.CssColor.COLORUNIT.name; is(color.toString(), name, "toString() with authored type"); - switchColorUnit(colorUtils.CssColor.COLORUNIT.hex); + color.colorUnit = colorUtils.CssColor.COLORUNIT.hex; is(color.toString(), hex, "toString() with hex type"); - switchColorUnit(colorUtils.CssColor.COLORUNIT.hsl); + color.colorUnit = colorUtils.CssColor.COLORUNIT.hsl; is(color.toString(), hsl, "toString() with hsl type"); - switchColorUnit(colorUtils.CssColor.COLORUNIT.rgb); + color.colorUnit = colorUtils.CssColor.COLORUNIT.rgb; is(color.toString(), rgb, "toString() with rgb type"); } -function switchColorUnit(unit) { - Services.prefs.setCharPref(COLOR_UNIT_PREF, unit); -} - function testColorMatch(name, hex, hsl, rgb, rgba, canvas) { let target; let ctx = canvas.getContext("2d"); @@ -110,7 +104,6 @@ function testColorMatch(name, hex, hsl, rgb, rgba, canvas) { test(hex, "hex"); test(hsl, "hsl"); test(rgb, "rgb"); - switchColorUnit(origColorUnit); } function testProcessCSSString() { diff --git a/browser/devtools/shared/widgets/Tooltip.js b/browser/devtools/shared/widgets/Tooltip.js index bc58325e73c1..abe8a818c856 100644 --- a/browser/devtools/shared/widgets/Tooltip.js +++ b/browser/devtools/shared/widgets/Tooltip.js @@ -1020,6 +1020,11 @@ SwatchBasedEditorTooltip.prototype = { _onSwatchClick: function(event) { let swatch = this.swatches.get(event.target); + + if (event.shiftKey) { + event.stopPropagation(); + return; + } if (swatch) { this.activeSwatch = event.target; this.show(); diff --git a/browser/devtools/styleinspector/computed-view.js b/browser/devtools/styleinspector/computed-view.js index 2cc5a7747bce..07eb0cad1598 100644 --- a/browser/devtools/styleinspector/computed-view.js +++ b/browser/devtools/styleinspector/computed-view.js @@ -980,7 +980,6 @@ function PropertyView(aTree, aName) this.link = "https://developer.mozilla.org/CSS/" + aName; - this.templateMatchedSelectors = aTree.styleDocument.getElementById("templateMatchedSelectors"); this._propertyInfo = new PropertyInfo(aTree, aName); } @@ -1192,6 +1191,7 @@ PropertyView.prototype = { this.propertyInfo.value, { colorSwatchClass: "computedview-colorswatch", + colorClass: "computedview-color", urlClass: "theme-link" // No need to use baseURI here as computed URIs are never relative. }); @@ -1222,9 +1222,10 @@ PropertyView.prototype = { } this._matchedSelectorResponse = matched; - CssHtmlTree.processTemplate(this.templateMatchedSelectors, - this.matchedSelectorsContainer, this); + + this._buildMatchedSelectors(); this.matchedExpander.setAttribute("open", ""); + this.tree.inspector.emit("computed-view-property-expanded"); }).then(null, console.error); } else { @@ -1240,6 +1241,40 @@ PropertyView.prototype = { return this._matchedSelectorResponse; }, + _buildMatchedSelectors: function() { + let frag = this.element.ownerDocument.createDocumentFragment(); + + for (let selector of this.matchedSelectorViews) { + let p = createChild(frag, "p"); + let span = createChild(p, "span", { + class: "rule-link" + }); + let link = createChild(span, "a", { + target: "_blank", + class: "link theme-link", + title: selector.href, + sourcelocation: selector.source, + tabindex: "0", + textContent: selector.source + }); + link.addEventListener("click", selector.openStyleEditor, false); + link.addEventListener("keydown", selector.maybeOpenStyleEditor, false); + + let status = createChild(p, "span", { + dir: "ltr", + class: "rule-text theme-fg-color3 " + selector.statusClass, + title: selector.statusText, + textContent: selector.sourceText + }); + let valueSpan = createChild(status, "span", { + class: "other-property-value theme-fg-color1" + }); + valueSpan.appendChild(selector.outputFragment); + } + + this.matchedSelectorsContainer.appendChild(frag); + }, + /** * Provide access to the matched SelectorViews that we are currently * displaying. @@ -1279,6 +1314,9 @@ PropertyView.prototype = { */ onMatchedToggle: function PropertyView_onMatchedToggle(aEvent) { + if (aEvent.shiftKey) { + return; + } this.matchedExpanded = !this.matchedExpanded; this.refreshMatchedSelectors(); aEvent.preventDefault(); @@ -1328,6 +1366,9 @@ function SelectorView(aTree, aSelectorInfo) this.selectorInfo = aSelectorInfo; this._cacheStatusNames(); + this.openStyleEditor = this.openStyleEditor.bind(this); + this.maybeOpenStyleEditor = this.maybeOpenStyleEditor.bind(this); + this.updateSourceLink(); } @@ -1418,6 +1459,7 @@ SelectorView.prototype = { this.selectorInfo.name, this.selectorInfo.value, { colorSwatchClass: "computedview-colorswatch", + colorClass: "computedview-color", urlClass: "theme-link", baseURI: this.selectorInfo.rule.href }); @@ -1540,5 +1582,32 @@ SelectorView.prototype = { } }; +/** + * Create a child element with a set of attributes. + * + * @param {Element} aParent + * The parent node. + * @param {string} aTag + * The tag name. + * @param {object} aAttributes + * A set of attributes to set on the node. + */ +function createChild(aParent, aTag, aAttributes={}) { + let elt = aParent.ownerDocument.createElementNS(HTML_NS, aTag); + for (let attr in aAttributes) { + if (aAttributes.hasOwnProperty(attr)) { + if (attr === "textContent") { + elt.textContent = aAttributes[attr]; + } else if(attr === "child") { + elt.appendChild(aAttributes[attr]); + } else { + elt.setAttribute(attr, aAttributes[attr]); + } + } + } + aParent.appendChild(elt); + return elt; +} + exports.CssHtmlTree = CssHtmlTree; exports.PropertyView = PropertyView; diff --git a/browser/devtools/styleinspector/computedview.xhtml b/browser/devtools/styleinspector/computedview.xhtml index c922f3979a7f..121475537c57 100644 --- a/browser/devtools/styleinspector/computedview.xhtml +++ b/browser/devtools/styleinspector/computedview.xhtml @@ -72,38 +72,5 @@ &noPropertiesFound; - -
- -
- -

- - ${selector.source} - - - ${selector.sourceText} - ${selector.outputFragment} - -

-
-
-
- diff --git a/browser/devtools/styleinspector/test/browser.ini b/browser/devtools/styleinspector/test/browser.ini index ff074ef9df8f..824fd85bfbda 100644 --- a/browser/devtools/styleinspector/test/browser.ini +++ b/browser/devtools/styleinspector/test/browser.ini @@ -27,6 +27,7 @@ support-files = head.js [browser_computedview_browser-styles.js] +[browser_computedview_cycle_color.js] [browser_computedview_getNodeInfo.js] [browser_computedview_keybindings_01.js] [browser_computedview_keybindings_02.js] @@ -99,6 +100,7 @@ skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s. [browser_ruleview_multiple_properties_01.js] [browser_ruleview_multiple_properties_02.js] [browser_ruleview_original-source-link.js] +[browser_ruleview_cycle-color.js] [browser_ruleview_override.js] [browser_ruleview_pseudo-element_01.js] [browser_ruleview_pseudo-element_02.js] diff --git a/browser/devtools/styleinspector/test/browser_computedview_cycle_color.js b/browser/devtools/styleinspector/test/browser_computedview_cycle_color.js new file mode 100644 index 000000000000..9aef6629b6ca --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_computedview_cycle_color.js @@ -0,0 +1,68 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Computed view color cycling test. + +const PAGE_CONTENT = [ + "", + "Some styled text", + "" +].join("\n"); + +add_task(function*() { + yield addTab("data:text/html;charset=utf-8," + + "Computed view color cycling test."); + content.document.body.innerHTML = PAGE_CONTENT; + + info("Opening the computed view"); + let {toolbox, inspector, view} = yield openComputedView(); + + info("Selecting the test node"); + yield selectNode("#matches", inspector); + + info("Checking the property itself"); + let container = getComputedViewPropertyView(view, "color").valueNode; + checkColorCycling(container, inspector); + + info("Checking matched selectors"); + container = yield getComputedViewMatchedRules(view, "color"); + checkColorCycling(container, inspector); +}); + +function checkColorCycling(container, inspector) { + let swatch = container.querySelector(".computedview-colorswatch"); + let valueNode = container.querySelector(".computedview-color"); + let win = inspector.sidebar.getWindowForTab("computedview"); + + // Hex (default) + is(valueNode.textContent, "#F00", "Color displayed as a hex value."); + + // HSL + EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win); + is(valueNode.textContent, "hsl(0, 100%, 50%)", + "Color displayed as an HSL value."); + + // RGB + EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win); + is(valueNode.textContent, "rgb(255, 0, 0)", + "Color displayed as an RGB value."); + + // Color name + EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win); + is(valueNode.textContent, "red", + "Color displayed as a color name."); + + // "Authored" (currently the computed value) + EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win); + is(valueNode.textContent, "rgb(255, 0, 0)", + "Color displayed as an RGB value."); + + // Back to hex + EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win); + is(valueNode.textContent, "#F00", + "Color displayed as hex again."); +} diff --git a/browser/devtools/styleinspector/test/browser_ruleview_cycle-color.js b/browser/devtools/styleinspector/test/browser_ruleview_cycle-color.js new file mode 100644 index 000000000000..1124cfe02b26 --- /dev/null +++ b/browser/devtools/styleinspector/test/browser_ruleview_cycle-color.js @@ -0,0 +1,60 @@ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test cycling color types in the rule view. + +const PAGE_CONTENT = [ + "", + "Test cycling color types in the rule view!" +].join("\n"); + +add_task(function*() { + yield addTab("data:text/html;charset=utf-8,Test cycling color types in the " + + "rule view."); + content.document.body.innerHTML = PAGE_CONTENT; + let {toolbox, inspector, view} = yield openRuleView(); + + let container = getRuleViewProperty(view, "body", "color").valueSpan; + checkColorCycling(container, inspector); +}); + +function checkColorCycling(container, inspector) { + let swatch = container.querySelector(".ruleview-colorswatch"); + let valueNode = container.querySelector(".ruleview-color"); + let win = inspector.sidebar.getWindowForTab("ruleview"); + + // Hex (default) + is(valueNode.textContent, "#F00", "Color displayed as a hex value."); + + // HSL + EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win); + is(valueNode.textContent, "hsl(0, 100%, 50%)", + "Color displayed as an HSL value."); + + // RGB + EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win); + is(valueNode.textContent, "rgb(255, 0, 0)", + "Color displayed as an RGB value."); + + // Color name + EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win); + is(valueNode.textContent, "red", + "Color displayed as a color name."); + + // "Authored" (currently the computed value) + EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win); + is(valueNode.textContent, "rgb(255, 0, 0)", + "Color displayed as an RGB value."); + + // Back to hex + EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win); + is(valueNode.textContent, "#F00", + "Color displayed as hex again."); +} diff --git a/toolkit/devtools/css-color.js b/toolkit/devtools/css-color.js index 4cfabd667119..4451ecb4f513 100644 --- a/toolkit/devtools/css-color.js +++ b/toolkit/devtools/css-color.js @@ -95,8 +95,22 @@ CssColor.COLORUNIT = { }; CssColor.prototype = { + _colorUnit: null, + authored: null, + get colorUnit() { + if (this._colorUnit === null) { + let defaultUnit = Services.prefs.getCharPref(COLOR_UNIT_PREF); + this._colorUnit = CssColor.COLORUNIT[defaultUnit]; + } + return this._colorUnit; + }, + + set colorUnit(unit) { + this._colorUnit = unit; + }, + get hasAlpha() { if (!this.valid) { return false; @@ -269,15 +283,31 @@ CssColor.prototype = { return this; }, + nextColorUnit: function() { + // Reorder the formats array to have the current format at the + // front so we can cycle through. + let formats = ["authored", "hex", "hsl", "rgb", "name"]; + let putOnEnd = formats.splice(0, formats.indexOf(this.colorUnit)); + formats = formats.concat(putOnEnd); + let currentDisplayedColor = this[formats[0]]; + + for (let format of formats) { + if (this[format].toLowerCase() !== currentDisplayedColor.toLowerCase()) { + this.colorUnit = CssColor.COLORUNIT[format]; + break; + } + } + + return this.toString(); + }, + /** * Return a string representing a color of type defined in COLOR_UNIT_PREF. */ toString: function() { let color; - let defaultUnit = Services.prefs.getCharPref(COLOR_UNIT_PREF); - let unit = CssColor.COLORUNIT[defaultUnit]; - switch(unit) { + switch(this.colorUnit) { case CssColor.COLORUNIT.authored: color = this.authored; break; diff --git a/toolkit/devtools/output-parser.js b/toolkit/devtools/output-parser.js index fa501c332792..0ed2e2ded189 100644 --- a/toolkit/devtools/output-parser.js +++ b/toolkit/devtools/output-parser.js @@ -77,6 +77,8 @@ loader.lazyGetter(this, "REGEX_ALL_CSS_PROPERTIES", function () { */ function OutputParser() { this.parsed = []; + this.colorSwatches = new WeakMap(); + this._onSwatchMouseDown = this._onSwatchMouseDown.bind(this); } exports.OutputParser = OutputParser; @@ -396,12 +398,14 @@ OutputParser.prototype = { class: options.colorSwatchClass, style: "background-color:" + color }); + this.colorSwatches.set(swatch, colorObj); + swatch.addEventListener("mousedown", this._onSwatchMouseDown, false); container.appendChild(swatch); } if (options.defaultColorType) { color = colorObj.toString(); - container.dataset["color"] = color; + container.dataset.color = color; } let value = this._createNode("span", { @@ -435,6 +439,21 @@ OutputParser.prototype = { this.parsed.push(container); }, + _onSwatchMouseDown: function(event) { + // Prevent text selection in the case of shift-click or double-click. + event.preventDefault(); + + if (!event.shiftKey) { + return; + } + + let swatch = event.target; + let color = this.colorSwatches.get(swatch); + let val = color.nextColorUnit(); + + swatch.nextElementSibling.textContent = val; + }, + /** * Append a URL to the output. *