author | Tom Tromey <tromey@mozilla.com> |
Mon, 08 Jun 2015 12:26:00 -0400 | |
changeset 248137 | 34d7922bed374935df91f8b162e4b49b7c7c03ca |
parent 248136 | fdaad6bb56fdf82a5cbc1c4053c3e84df3ef31f0 |
child 248138 | d94caa738b3fb71b96630ff4c87841c21f3033d8 |
push id | 60888 |
push user | kwierso@gmail.com |
push date | Thu, 11 Jun 2015 01:38:38 +0000 |
treeherder | mozilla-inbound@39e638ed06bf [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | pbrosset |
bugs | 1158288 |
milestone | 41.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/browser/devtools/shared/test/browser_outputparser.js +++ b/browser/devtools/shared/test/browser_outputparser.js @@ -1,34 +1,37 @@ /* Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ */ +"use strict"; + let {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); -let {Loader} = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {}); +let {Loader} = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", + {}); let {OutputParser} = devtools.require("devtools/output-parser"); add_task(function*() { yield promiseTab("about:blank"); yield performTest(); gBrowser.removeCurrentTab(); }); function* performTest() { - let [host, win, doc] = yield createHost("bottom", "data:text/html," + + let [host, , doc] = yield createHost("bottom", "data:text/html," + "<h1>browser_outputParser.js</h1><div></div>"); let parser = new OutputParser(); testParseCssProperty(doc, parser); testParseCssVar(doc, parser); host.destroy(); } // Class name used in color swatch. -let COLOR_TEST_CLASS = 'test-class'; +let COLOR_TEST_CLASS = "test-class"; // Create a new CSS color-parsing test. |name| is the name of the CSS // property. |value| is the CSS text to use. |segments| is an array // describing the expected result. If an element of |segments| is a // string, it is simply appended to the expected string. Otherwise, // it must be an object with a |value| property and a |name| property. // These describe the color and are both used in the generated // expected output -- |name| is the color name as it appears in the @@ -37,27 +40,27 @@ let COLOR_TEST_CLASS = 'test-class'; // "#F00"). // // This approach is taken to reduce boilerplate and to make it simpler // to modify the test when the parseCssProperty output changes. function makeColorTest(name, value, segments) { let result = { name, value, - expected: '' + expected: "" }; for (let segment of segments) { - if (typeof(segment) === 'string') { + if (typeof (segment) === "string") { result.expected += segment; } else { - result.expected += '<span data-color="' + segment.value + '">' + - '<span style="background-color:' + segment.name + - '" class="' + COLOR_TEST_CLASS + '"></span><span>' + - segment.value + '</span></span>'; + result.expected += "<span data-color=\"" + segment.value + "\">" + + "<span style=\"background-color:" + segment.name + + "\" class=\"" + COLOR_TEST_CLASS + "\"></span><span>" + + segment.value + "</span></span>"; } } result.desc = "Testing " + name + ": " + value; return result; } @@ -79,17 +82,17 @@ function testParseCssProperty(doc, parse ["0 0 1em ", {name: "red", value: "#F00"}]), makeColorTest("box-shadow", "0 0 1em red, 2px 2px 0 0 rgba(0,0,0,.5)", ["0 0 1em ", {name: "red", value: "#F00"}, ", 2px 2px 0 0 ", {name: "rgba(0,0,0,.5)", value: "rgba(0,0,0,.5)"}]), - makeColorTest("content", '"red"', ['"red"']), + makeColorTest("content", "\"red\"", ["\"red\""]), // Invalid property names should not cause exceptions. makeColorTest("hellothere", "'red'", ["'red'"]), // This requires better parsing than we currently have available. // See bug 1158288. // makeColorTest("filter", // "blur(1px) drop-shadow(0 0 0 blue) url(red.svg#blue)", @@ -122,12 +125,13 @@ function testParseCssVar(doc, parser) { let frag = parser.parseCssProperty("color", "var(--some-kind-of-green)", { colorSwatchClass: "test-colorswatch" }); let target = doc.querySelector("div"); ok(target, "captain, we have the div"); target.appendChild(frag); - is(target.innerHTML, "var(--some-kind-of-green)", "CSS property correctly parsed"); + is(target.innerHTML, "var(--some-kind-of-green)", + "CSS property correctly parsed"); target.innerHTML = ""; }
--- a/browser/devtools/shared/widgets/Tooltip.js +++ b/browser/devtools/shared/widgets/Tooltip.js @@ -1,20 +1,22 @@ /* 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, Cu, Ci} = require("chrome"); +/* globals beautify, setNamedTimeout, clearNamedTimeout, VariablesView, + VariablesViewController, Task */ + +const {Cu, Ci} = require("chrome"); const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); -const IOService = Cc["@mozilla.org/network/io-service;1"] - .getService(Ci.nsIIOService); const {Spectrum} = require("devtools/shared/widgets/Spectrum"); -const {CubicBezierWidget} = require("devtools/shared/widgets/CubicBezierWidget"); +const {CubicBezierWidget} = + require("devtools/shared/widgets/CubicBezierWidget"); const {MdnDocsWidget} = require("devtools/shared/widgets/MdnDocsWidget"); const {CSSFilterEditorWidget} = require("devtools/shared/widgets/FilterWidget"); const EventEmitter = require("devtools/toolkit/event-emitter"); const {colorUtils} = require("devtools/css-color"); const Heritage = require("sdk/core/heritage"); const {Eyedropper} = require("devtools/eyedropper/eyedropper"); const Editor = require("devtools/sourceeditor/editor"); const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); @@ -32,17 +34,18 @@ XPCOMUtils.defineLazyModuleGetter(this, "resource:///modules/devtools/VariablesView.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "VariablesViewController", "resource:///modules/devtools/VariablesViewController.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); const XHTML_NS = "http://www.w3.org/1999/xhtml"; const SPECTRUM_FRAME = "chrome://browser/content/devtools/spectrum-frame.xhtml"; -const CUBIC_BEZIER_FRAME = "chrome://browser/content/devtools/cubic-bezier-frame.xhtml"; +const CUBIC_BEZIER_FRAME = + "chrome://browser/content/devtools/cubic-bezier-frame.xhtml"; const MDN_DOCS_FRAME = "chrome://browser/content/devtools/mdn-docs-frame.xhtml"; const FILTER_FRAME = "chrome://browser/content/devtools/filter-frame.xhtml"; const ESCAPE_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE; const RETURN_KEYCODE = Ci.nsIDOMKeyEvent.DOM_VK_RETURN; const POPUP_EVENTS = ["shown", "hidden", "showing", "hiding"]; /** * Tooltip widget. @@ -82,19 +85,18 @@ OptionsStore.prototype = { * Get the value for a given option name. * @return {Object} Returns the value for that option, coming either for the * actual values that have been set in the constructor, or from the * defaults if that options was not specified. */ get: function(name) { if (typeof this.options[name] !== "undefined") { return this.options[name]; - } else { - return this.defaults[name]; } + return this.defaults[name]; } }; /** * The low level structure of a tooltip is a XUL element (a <panel>). */ let PanelFactory = { /** @@ -106,17 +108,18 @@ let PanelFactory = { */ get: function(doc, options) { // Create the tooltip let panel = doc.createElement("panel"); panel.setAttribute("hidden", true); panel.setAttribute("ignorekeys", true); panel.setAttribute("animate", false); - panel.setAttribute("consumeoutsideclicks", options.get("consumeOutsideClick")); + panel.setAttribute("consumeoutsideclicks", + options.get("consumeOutsideClick")); panel.setAttribute("noautofocus", options.get("noAutoFocus")); panel.setAttribute("type", "arrow"); panel.setAttribute("level", "top"); panel.setAttribute("class", "devtools-tooltip theme-tooltip-panel"); doc.querySelector("window").appendChild(panel); return panel; @@ -147,21 +150,23 @@ let PanelFactory = { * The XUL document hosting this tooltip * @param {Object} options * Optional options that give options to consumers: * - consumeOutsideClick {Boolean} Wether the first click outside of the * tooltip should close the tooltip and be consumed or not. * Defaults to false. * - closeOnKeys {Array} An array of key codes that should close the * tooltip. Defaults to [27] (escape key). - * - closeOnEvents [{emitter: {Object}, event: {String}, useCapture: {Boolean}}] + * - closeOnEvents [{emitter: {Object}, event: {String}, + * useCapture: {Boolean}}] * Provide an optional list of emitter objects and event names here to * trigger the closing of the tooltip when these events are fired by the - * emitters. The emitter objects should either implement on/off(event, cb) - * or addEventListener/removeEventListener(event, cb). Defaults to []. + * emitters. The emitter objects should either implement + * on/off(event, cb) or addEventListener/removeEventListener(event, cb). + * Defaults to []. * For instance, the following would close the tooltip whenever the * toolbox selects a new tool and when a DOM node gets scrolled: * new Tooltip(doc, { * closeOnEvents: [ * {emitter: toolbox, event: "select"}, * {emitter: myContainer, event: "scroll", useCapture: true} * ] * }); @@ -231,19 +236,22 @@ function Tooltip(doc, options) { } } } module.exports.Tooltip = Tooltip; Tooltip.prototype = { defaultPosition: "before_start", - defaultOffsetX: 0, // px - defaultOffsetY: 0, // px - defaultShowDelay: 50, // ms + // px + defaultOffsetX: 0, + // px + defaultOffsetY: 0, + // px + defaultShowDelay: 50, /** * Show the tooltip. It might be wise to append some content first if you * don't want the tooltip to be empty. You may access the content of the * tooltip by setting a XUL node to t.content. * @param {node} anchor * Which node should the tooltip be shown on * @param {string} position [optional] @@ -301,17 +309,17 @@ Tooltip.prototype = { */ isEmpty: function() { return !this.panel.hasChildNodes(); }, /** * Get rid of references and event listeners */ - destroy: function () { + destroy: function() { this.hide(); for (let eventName of POPUP_EVENTS) { this.panel.removeEventListener("popup" + eventName, this["_onPopup" + eventName], false); } let win = this.doc.querySelector("window"); @@ -369,17 +377,18 @@ Tooltip.prototype = { * Any other value is going to be logged as unexpected error. * Additionally, the function receives a second argument which is the * tooltip instance itself, to be used to add/modify the content of the * tooltip if needed. If omitted, the tooltip will be shown everytime. * @param {Number} showDelay * An optional delay that will be observed before showing the tooltip. * Defaults to this.defaultShowDelay. */ - startTogglingOnHover: function(baseNode, targetNodeCb, showDelay=this.defaultShowDelay) { + startTogglingOnHover: function(baseNode, targetNodeCb, + showDelay=this.defaultShowDelay) { if (this._basedNode) { this.stopTogglingOnHover(); } if (!baseNode) { // Calling tool is in the process of being destroyed. return; } @@ -450,20 +459,19 @@ Tooltip.prototype = { let res = this._targetNodeCb(target, this); // The callback can additionally return a DOMNode to replace the anchor of // the tooltip when shown if (res && res.then) { return res.then(arg => { return arg instanceof Ci.nsIDOMNode ? arg : target; }); - } else { - let newTarget = res instanceof Ci.nsIDOMNode ? res : target; - return res ? promise.resolve(newTarget) : promise.reject(false); } + let newTarget = res instanceof Ci.nsIDOMNode ? res : target; + return res ? promise.resolve(newTarget) : promise.reject(false); }, _onBaseNodeMouseLeave: function() { clearNamedTimeout(this.uid); this._lastHovered = null; this.hide(); }, @@ -580,24 +588,22 @@ Tooltip.prototype = { * @param {boolean} reuseCachedWidget [optional] * Pass false to instantiate a brand new widget for this variable. * Otherwise, if a variable was previously inspected, its widget * will be reused. * @param {Toolbox} toolbox [optional] * Pass the instance of the current toolbox if you want the variables * view widget to allow highlighting and selection of DOM nodes */ - setVariableContent: function( - objectActor, - viewOptions = {}, - controllerOptions = {}, - relayEvents = {}, - extraButtons = [], - toolbox = null) { - + setVariableContent: function(objectActor, + viewOptions = {}, + controllerOptions = {}, + relayEvents = {}, + extraButtons = [], + toolbox = null) { let vbox = this.doc.createElement("vbox"); vbox.className = "devtools-tooltip-variables-view-box"; vbox.setAttribute("flex", "1"); let innerbox = this.doc.createElement("vbox"); innerbox.className = "devtools-tooltip-variables-view-innerbox"; innerbox.setAttribute("flex", "1"); vbox.appendChild(innerbox); @@ -638,23 +644,25 @@ Tooltip.prototype = { /** * Uses the provided inspectorFront's getImageDataFromURL method to resolve * the relative URL on the server-side, in the page context, and then sets the * tooltip content with the resulting image just like |setImageContent| does. * @return a promise that resolves when the image is shown in the tooltip or * resolves when the broken image tooltip content is ready, but never rejects. */ - setRelativeImageContent: Task.async(function*(imageUrl, inspectorFront, maxDim) { + setRelativeImageContent: Task.async(function*(imageUrl, inspectorFront, + maxDim) { if (imageUrl.startsWith("data:")) { // If the imageUrl already is a data-url, save ourselves a round-trip this.setImageContent(imageUrl, {maxDim: maxDim}); } else if (inspectorFront) { try { - let {data, size} = yield inspectorFront.getImageDataFromURL(imageUrl, maxDim); + let {data, size} = yield inspectorFront.getImageDataFromURL(imageUrl, + maxDim); size.maxDim = maxDim; let str = yield data.string(); this.setImageContent(str, size); } catch (e) { this.setBrokenImageContent(); } } }), @@ -711,22 +719,23 @@ Tooltip.prototype = { label.classList.add("devtools-tooltip-caption"); label.classList.add("theme-comment"); if (options.naturalWidth && options.naturalHeight) { label.textContent = this._getImageDimensionLabel(options.naturalWidth, options.naturalHeight); } else { // If no dimensions were provided, load the image to get them - label.textContent = l10n.strings.GetStringFromName("previewTooltip.image.brokenImage"); + label.textContent = + l10n.strings.GetStringFromName("previewTooltip.image.brokenImage"); let imgObj = new this.doc.defaultView.Image(); imgObj.src = imageUrl; imgObj.onload = () => { imgObj.onload = null; - label.textContent = this._getImageDimensionLabel(imgObj.naturalWidth, + label.textContent = this._getImageDimensionLabel(imgObj.naturalWidth, imgObj.naturalHeight); }; } vbox.appendChild(label); } this.content = vbox; @@ -754,17 +763,16 @@ Tooltip.prototype = { * This function creates an iframe, loads the specified document * into it, sets the tooltip's content to the iframe, and returns * a promise. * * When the document is loaded, the function gets the content window * and resolves the promise with the content window. */ setIFrameContent: function({width, height}, url) { - let def = promise.defer(); // Create an iframe let iframe = this.doc.createElementNS(XHTML_NS, "iframe"); iframe.setAttribute("transparent", true); iframe.setAttribute("width", width); iframe.setAttribute("height", height); iframe.setAttribute("flex", "1"); @@ -805,18 +813,17 @@ Tooltip.prototype = { function finalizeSpectrum() { spectrum.show(); def.resolve(spectrum); } // Finalize spectrum's init when the tooltip becomes visible if (panel.state == "open") { finalizeSpectrum(); - } - else { + } else { panel.addEventListener("popupshown", function shown() { panel.removeEventListener("popupshown", shown, true); finalizeSpectrum(); }, true); } return def.promise; } }, @@ -864,17 +871,17 @@ Tooltip.prototype = { let win = iframe.contentWindow.wrappedJSObject; let doc = win.document.documentElement; let def = promise.defer(); let container = win.document.getElementById("container"); let widget = new CSSFilterEditorWidget(container, filter); iframe.height = doc.offsetHeight; - widget.on("render", e => { + widget.on("render", () => { iframe.height = doc.offsetHeight; }); // Resolve to the widget instance whenever the popup becomes visible if (panel.state == "open") { def.resolve(widget); } else { panel.addEventListener("popupshown", function shown() { @@ -883,37 +890,39 @@ Tooltip.prototype = { }, true); } return def.promise; } }, /** * Set the content of the tooltip to display a font family preview. - * This is based on Lea Verou's Dablet. See https://github.com/LeaVerou/dabblet + * This is based on Lea Verou's Dablet. + * See https://github.com/LeaVerou/dabblet * for more info. * @param {String} font The font family value. * @param {object} nodeFront * The NodeActor that will used to retrieve the dataURL for the font * family tooltip contents. * @return A promise that resolves when the font tooltip content is ready, or * rejects if no font is provided */ setFontFamilyContent: Task.async(function*(font, nodeFront) { if (!font || !nodeFront) { - throw "Missing font"; + throw new Error("Missing font"); } if (typeof nodeFront.getFontFamilyDataURL === "function") { font = font.replace(/"/g, "'"); font = font.replace("!important", ""); font = font.trim(); - let fillStyle = (Services.prefs.getCharPref("devtools.theme") === "light") ? - "black" : "white"; + let fillStyle = + (Services.prefs.getCharPref("devtools.theme") === "light") ? + "black" : "white"; let {data, size} = yield nodeFront.getFontFamilyDataURL(font, fillStyle); let str = yield data.string(); this.setImageContent(str, { hideDimensionLabel: true, maxDim: size }); } }), /** @@ -984,17 +993,18 @@ function SwatchBasedEditorTooltip(doc) { SwatchBasedEditorTooltip.prototype = { show: function() { if (this.activeSwatch) { this.tooltip.show(this.activeSwatch, "topcenter bottomleft"); // When the tooltip is closed by clicking outside the panel we want to // commit any changes. Because the "hidden" event destroys the tooltip we - // need to do this before the tooltip is destroyed (in the "hiding" event). + // need to do this before the tooltip is destroyed (in the "hiding" + // event). this.tooltip.once("hiding", () => { if (!this._reverted && !this.eyedropperOpen) { this.commit(); } this._reverted = false; }); // Once the tooltip is hidden we need to clean up any remaining objects. @@ -1015,25 +1025,32 @@ SwatchBasedEditorTooltip.prototype = { * tooltip knows about. That means from now on, clicking on that swatch will * toggle the editor. * * @param {node} swatchEl * The element to add * @param {object} callbacks * Callbacks that will be executed when the editor wants to preview a * value change, or revert a change, or commit a change. - * - onPreview: will be called when one of the sub-classes calls preview + * - onPreview: will be called when one of the sub-classes calls + * preview * - onRevert: will be called when the user ESCapes out of the tooltip * - onCommit: will be called when the user presses ENTER or clicks * outside the tooltip. */ addSwatch: function(swatchEl, callbacks={}) { - if (!callbacks.onPreview) callbacks.onPreview = function() {}; - if (!callbacks.onRevert) callbacks.onRevert = function() {}; - if (!callbacks.onCommit) callbacks.onCommit = function() {}; + if (!callbacks.onPreview) { + callbacks.onPreview = function() {}; + } + if (!callbacks.onRevert) { + callbacks.onRevert = function() {}; + } + if (!callbacks.onCommit) { + callbacks.onCommit = function() {}; + } this.swatches.set(swatchEl, { callbacks: callbacks }); swatchEl.addEventListener("click", this._onSwatchClick, false); }, removeSwatch: function(swatchEl) { @@ -1259,17 +1276,18 @@ EventTooltip.prototype = { let header = doc.createElement("hbox"); header.className = "event-header devtools-toolbar"; container.appendChild(header); if (!listener.hide.debugger) { let debuggerIcon = doc.createElement("image"); debuggerIcon.className = "event-tooltip-debugger-icon"; debuggerIcon.setAttribute("src", "chrome://browser/skin/devtools/tool-debugger.svg"); - let openInDebugger = l10n.strings.GetStringFromName("eventsTooltip.openInDebugger"); + let openInDebugger = + l10n.strings.GetStringFromName("eventsTooltip.openInDebugger"); debuggerIcon.setAttribute("tooltiptext", openInDebugger); header.appendChild(debuggerIcon); } if (!listener.hide.type) { let eventTypeLabel = doc.createElement("label"); eventTypeLabel.className = "event-tooltip-event-type"; eventTypeLabel.setAttribute("value", listener.type); @@ -1282,17 +1300,18 @@ EventTooltip.prototype = { filename.className = "event-tooltip-filename devtools-monospace"; filename.setAttribute("value", listener.origin); filename.setAttribute("tooltiptext", listener.origin); filename.setAttribute("crop", "left"); header.appendChild(filename); } let attributesContainer = doc.createElement("hbox"); - attributesContainer.setAttribute("class", "event-tooltip-attributes-container"); + attributesContainer.setAttribute("class", + "event-tooltip-attributes-container"); header.appendChild(attributesContainer); if (!listener.hide.capturing) { let attributesBox = doc.createElement("box"); attributesBox.setAttribute("class", "event-tooltip-attributes-box"); attributesContainer.appendChild(attributesBox); let capturing = doc.createElement("label"); @@ -1342,17 +1361,18 @@ EventTooltip.prototype = { content.className = "event-tooltip-content-box"; container.appendChild(content); this._addContentListeners(header); } this._tooltip.content = container; - this._tooltip.panel.setAttribute("clamped-dimensions-no-max-or-min-height", ""); + this._tooltip.panel.setAttribute("clamped-dimensions-no-max-or-min-height", + ""); this._tooltip.panel.setAttribute("wide", ""); this._tooltip.panel.addEventListener("popuphiding", () => { this.destroy(container); }, false); }, _addContentListeners: function(header) { @@ -1448,21 +1468,21 @@ EventTooltip.prototype = { let text = DebuggerView.editor.getText(); let index = text.indexOf(searchString); let lastIndex = text.lastIndexOf(searchString); // To avoid confusion we only search for DOM0 event handlers when // there is only one possible match in the file. if (index !== -1 && index === lastIndex) { text = text.substr(0, index); - let matches = text.match(/\n/g); + let newlineMatches = text.match(/\n/g); - if (matches) { + if (newlineMatches) { DebuggerView.editor.setCursor({ - line: matches.length + line: newlineMatches.length }); } } } }); } }; @@ -1474,17 +1494,18 @@ EventTooltip.prototype = { dbg.once(dbg.EVENTS.SOURCES_ADDED, () => showSource(dbg)); } }); } }, destroy: function(container) { if (this._tooltip) { - this._tooltip.panel.removeEventListener("popuphiding", this.destroy, false); + this._tooltip.panel.removeEventListener("popuphiding", this.destroy, + false); let boxes = container.querySelectorAll(".event-tooltip-content-box"); for (let box of boxes) { let {editor} = this._tooltip.eventEditors.get(box); editor.destroy(); } @@ -1493,17 +1514,18 @@ EventTooltip.prototype = { } let headerNodes = container.querySelectorAll(".event-header"); for (let node of headerNodes) { node.removeEventListener("click", this._headerClicked); } - let sourceNodes = container.querySelectorAll(".event-tooltip-debugger-icon"); + let sourceNodes = + container.querySelectorAll(".event-tooltip-debugger-icon"); for (let node of sourceNodes) { node.removeEventListener("click", this._debugClicked); } this._eventListenerInfos = this._toolbox = this._tooltip = null; } }; @@ -1533,17 +1555,16 @@ SwatchCubicBezierTooltip.prototype = Her * bezier curve in the widget */ show: function() { // Call the parent class' show function SwatchBasedEditorTooltip.prototype.show.call(this); // Then set the curve and listen to changes to preview them if (this.activeSwatch) { this.currentBezierValue = this.activeSwatch.nextSibling; - let swatch = this.swatches.get(this.activeSwatch); this.widget.then(widget => { widget.off("updated", this._onUpdate); widget.cssCubicBezierValue = this.currentBezierValue.textContent; widget.on("updated", this._onUpdate); }); } }, @@ -1583,17 +1604,16 @@ function CssDocsTooltip(doc) { module.exports.CssDocsTooltip = CssDocsTooltip; CssDocsTooltip.prototype = { /** * Load CSS docs for the given property, * then display the tooltip. */ show: function(anchor, propertyName) { - function loadCssDocs(widget) { return widget.loadCssDocs(propertyName); } this.widget.then(loadCssDocs); this.tooltip.show(anchor, "topcenter bottomleft"); }, @@ -1602,18 +1622,18 @@ CssDocsTooltip.prototype = { }, destroy: function() { this.tooltip.destroy(); } }; /** - * The swatch-based css filter tooltip class is a specific class meant to be used - * along with rule-view's generated css filter swatches. + * The swatch-based css filter tooltip class is a specific class meant to be + * used along with rule-view's generated css filter swatches. * It extends the parent SwatchBasedEditorTooltip class. * It just wraps a standard Tooltip and sets its content with an instance of a * CSSFilterEditorWidget. * * @param {XULDocument} doc */ function SwatchFilterTooltip(doc) { SwatchBasedEditorTooltip.call(this, doc);
--- a/browser/devtools/styleinspector/rule-view.js +++ b/browser/devtools/styleinspector/rule-view.js @@ -1,36 +1,41 @@ /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* 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/. */ +/* globals clipboardHelper, _strings, domUtils, AutocompletePopup */ + "use strict"; const {Cc, Ci, Cu} = require("chrome"); const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); const {CssLogic} = require("devtools/styleinspector/css-logic"); -const {InplaceEditor, editableField, editableItem} = require("devtools/shared/inplace-editor"); -const {ELEMENT_STYLE, PSEUDO_ELEMENTS} = require("devtools/server/actors/styles"); -const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); +const {InplaceEditor, editableField, editableItem} = + require("devtools/shared/inplace-editor"); +const {ELEMENT_STYLE, PSEUDO_ELEMENTS} = + require("devtools/server/actors/styles"); const {OutputParser} = require("devtools/output-parser"); const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils"); -const {parseSingleValue, parseDeclarations} = require("devtools/styleinspector/css-parsing-utils"); +const {parseSingleValue, parseDeclarations} = + require("devtools/styleinspector/css-parsing-utils"); const overlays = require("devtools/styleinspector/style-inspector-overlays"); const EventEmitter = require("devtools/toolkit/event-emitter"); Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); const HTML_NS = "http://www.w3.org/1999/xhtml"; const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; const PREF_UA_STYLES = "devtools.inspector.showUserAgentStyles"; const PREF_DEFAULT_COLOR_UNIT = "devtools.defaultColorUnit"; -const PREF_ENABLE_MDN_DOCS_TOOLTIP = "devtools.inspector.mdnDocsTooltip.enabled"; +const PREF_ENABLE_MDN_DOCS_TOOLTIP = + "devtools.inspector.mdnDocsTooltip.enabled"; const PROPERTY_NAME_CLASS = "ruleview-propertyname"; const FILTER_CHANGED_TIMEOUT = 150; // This is used to parse user input when filtering. const FILTER_PROP_RE = /\s*([^:\s]*)\s*:\s*(.*?)\s*;?$/; const IOService = Cc["@mozilla.org/network/io-service;1"] .getService(Ci.nsIIOService); @@ -42,37 +47,38 @@ function promiseWarn(err) { /** * To figure out how shorthand properties are interpreted by the * engine, we will set properties on a dummy element and observe * how their .style attribute reflects them as computed values. * This function creates the document in which those dummy elements * will be created. */ -var gDummyPromise; +let gDummyPromise; function createDummyDocument() { if (gDummyPromise) { return gDummyPromise; } const { getDocShell, create: makeFrame } = require("sdk/frame/utils"); let frame = makeFrame(Services.appShell.hiddenDOMWindow.document, { nodeName: "iframe", namespaceURI: "http://www.w3.org/1999/xhtml", allowJavascript: false, allowPlugins: false, allowAuth: false }); let docShell = getDocShell(frame); let eventTarget = docShell.chromeEventHandler; - docShell.createAboutBlankContentViewer(Cc["@mozilla.org/nullprincipal;1"].createInstance(Ci.nsIPrincipal)); + docShell.createAboutBlankContentViewer(Cc["@mozilla.org/nullprincipal;1"] + .createInstance(Ci.nsIPrincipal)); let window = docShell.contentViewer.DOMDocument.defaultView; window.location = "data:text/html,<html></html>"; let deferred = promise.defer(); - eventTarget.addEventListener("DOMContentLoaded", function handler(event) { + eventTarget.addEventListener("DOMContentLoaded", function handler() { eventTarget.removeEventListener("DOMContentLoaded", handler, false); deferred.resolve(window.document); frame.remove(); }, false); gDummyPromise = deferred.promise; return gDummyPromise; } @@ -136,29 +142,30 @@ exports._ElementStyle = ElementStyle; ElementStyle.prototype = { // The element we're looking at. element: null, // Empty, unconnected element of the same type as this node, used // to figure out how shorthand properties will be parsed. dummyElement: null, - init: function() - { + init: function() { // To figure out how shorthand properties are interpreted by the // engine, we will set properties on a dummy element and observe // how their .style attribute reflects them as computed values. - return this.dummyElementPromise = createDummyDocument().then(document => { + this.dummyElementPromise = createDummyDocument().then(document => { // ::before and ::after do not have a namespaceURI - let namespaceURI = this.element.namespaceURI || document.documentElement.namespaceURI; + let namespaceURI = this.element.namespaceURI || + document.documentElement.namespaceURI; this.dummyElement = document.createElementNS(namespaceURI, this.element.tagName); document.documentElement.appendChild(this.dummyElement); return this.dummyElement; }).then(null, promiseWarn); + return this.dummyElementPromise; }, destroy: function() { if (this.destroyed) { return; } this.destroyed = true; @@ -234,19 +241,19 @@ ElementStyle.prototype = { this.populated = populated; return this.populated; }, /** * Put pseudo elements in front of others. */ _sortRulesForPseudoElement: function() { - this.rules = this.rules.sort((a, b) => { - return (a.pseudoElement || "z") > (b.pseudoElement || "z"); - }); + this.rules = this.rules.sort((a, b) => { + return (a.pseudoElement || "z") > (b.pseudoElement || "z"); + }); }, /** * Add a rule if it's one we care about. Filters out duplicates and * inherited styles with no inherited properties. * * @param {object} aOptions * Options for creating the Rule, see the Rule constructor. @@ -452,50 +459,54 @@ Rule.prototype = { if (this._title) { return this._title; } this._title = CssLogic.shortSource(this.sheet); if (this.domRule.type !== ELEMENT_STYLE && this.ruleLine > 0) { this._title += ":" + this.ruleLine; } - this._title = this._title + (this.mediaText ? " @media " + this.mediaText : ""); + this._title = this._title + + (this.mediaText ? " @media " + this.mediaText : ""); return this._title; }, get inheritedSource() { if (this._inheritedSource) { return this._inheritedSource; } this._inheritedSource = ""; if (this.inherited) { let eltText = this.inherited.tagName.toLowerCase(); if (this.inherited.id) { eltText += "#" + this.inherited.id; } this._inheritedSource = - CssLogic._strings.formatStringFromName("rule.inheritedFrom", [eltText], 1); + CssLogic._strings.formatStringFromName("rule.inheritedFrom", + [eltText], 1); } return this._inheritedSource; }, get keyframesName() { if (this._keyframesName) { return this._keyframesName; } this._keyframesName = ""; if (this.keyframes) { this._keyframesName = - CssLogic._strings.formatStringFromName("rule.keyframe", [this.keyframes.name], 1); + CssLogic._strings.formatStringFromName("rule.keyframe", + [this.keyframes.name], 1); } return this._keyframesName; }, get selectorText() { - return this.domRule.selectors ? this.domRule.selectors.join(", ") : CssLogic.l10n("rule.sourceElement"); + return this.domRule.selectors ? this.domRule.selectors.join(", ") : + CssLogic.l10n("rule.sourceElement"); }, /** * The rule's stylesheet. */ get sheet() { return this.domRule ? this.domRule.parentStyleSheet : null; }, @@ -525,17 +536,18 @@ Rule.prototype = { getOriginalSourceStrings: function() { if (this._originalSourceStrings) { return promise.resolve(this._originalSourceStrings); } return this.domRule.getOriginalLocation().then(({href, line, mediaText}) => { let mediaString = mediaText ? " @" + mediaText : ""; let sourceStrings = { - full: (href || CssLogic.l10n("rule.sourceInline")) + ":" + line + mediaString, + full: (href || CssLogic.l10n("rule.sourceInline")) + ":" + + line + mediaString, short: CssLogic.shortSource({href: href}) + ":" + line + mediaString }; this._originalSourceStrings = sourceStrings; return sourceStrings; }); }, @@ -563,18 +575,17 @@ Rule.prototype = { * Optional, property next to which the new property will be added. */ createProperty: function(aName, aValue, aPriority, aSiblingProp) { let prop = new TextProperty(this, aName, aValue, aPriority); if (aSiblingProp) { let ind = this.textProps.indexOf(aSiblingProp); this.textProps.splice(ind + 1, 0, prop); - } - else { + } else { this.textProps.push(prop); } this.applyProperties(); return prop; }, /** @@ -589,17 +600,16 @@ Rule.prototype = { */ applyProperties: function(aModifications, aName) { this.elementStyle.markOverriddenAll(); if (!aModifications) { aModifications = this.style.startModifyingProperties(); } let disabledProps = []; - let store = this.elementStyle.store; for (let prop of this.textProps) { if (!prop.enabled) { disabledProps.push({ name: prop.name, value: prop.value, priority: prop.priority }); @@ -755,17 +765,18 @@ Rule.prototype = { let textProps = []; let store = this.elementStyle.store; let props = parseDeclarations(this.style.cssText); for (let prop of props) { let name = prop.name; if (this.inherited && !domUtils.isInheritedProperty(name)) { continue; } - let value = store.userProperties.getProperty(this.style, name, prop.value); + let value = store.userProperties.getProperty(this.style, name, + prop.value); let textProp = new TextProperty(this, name, value, prop.priority); textProps.push(textProp); } return textProps; }, /** @@ -778,17 +789,18 @@ Rule.prototype = { let disabledProps = store.disabled.get(this.style); if (!disabledProps) { return []; } let textProps = []; for (let prop of disabledProps) { - let value = store.userProperties.getProperty(this.style, prop.name, prop.value); + let value = store.userProperties.getProperty(this.style, prop.name, + prop.value); let textProp = new TextProperty(this, prop.name, value, prop.priority); textProp.enabled = false; textProps.push(textProp); } return textProps; }, @@ -857,18 +869,19 @@ Rule.prototype = { * * @return {bool} true if a property was updated, false if no properties * were updated. */ _updateTextProperty: function(aNewProp) { let match = { rank: 0, prop: null }; for (let prop of this.textProps) { - if (prop.name != aNewProp.name) + if (prop.name != aNewProp.name) { continue; + } // Mark this property visited. prop._visited = true; // Start at rank 1 for matching name. let rank = 1; // Value and Priority matches add 2 to the rank. @@ -923,18 +936,17 @@ Rule.prototype = { editClosestTextProperty: function(aTextProperty) { let index = this.textProps.indexOf(aTextProperty); let previous = false; // If this is the last element, move to the previous instead of next if (index === this.textProps.length - 1) { index = index - 1; previous = true; - } - else { + } else { index = index + 1; } let nextProp = this.textProps[index]; // If possible, begin editing the next name or previous value. // Otherwise, settle for focusing the new property element. if (nextProp) { @@ -1012,17 +1024,17 @@ TextProperty.prototype = { name: prop, value: dummyStyle.getPropertyValue(prop), priority: dummyStyle.getPropertyPriority(prop), }); } } catch(e) { // This is a partial property name, probably from cutting and pasting // text. At this point don't check for computed properties. - } + } }, /** * Set all the values from another TextProperty instance into * this TextProperty instance. * * @param {TextProperty} aOther * The other TextProperty instance. @@ -1124,55 +1136,60 @@ function CssRuleView(aInspector, aDoc, a this._onCopy = this._onCopy.bind(this); this._onCopyColor = this._onCopyColor.bind(this); this._onCopyImageDataUrl = this._onCopyImageDataUrl.bind(this); this._onToggleOrigSources = this._onToggleOrigSources.bind(this); this._onShowMdnDocs = this._onShowMdnDocs.bind(this); this._onFilterStyles = this._onFilterStyles.bind(this); this._onFilterKeyPress = this._onFilterKeyPress.bind(this); this._onClearSearch = this._onClearSearch.bind(this); - this._onFilterTextboxContextMenu = this._onFilterTextboxContextMenu.bind(this); + this._onFilterTextboxContextMenu = + this._onFilterTextboxContextMenu.bind(this); this._onTogglePseudoClassPanel = this._onTogglePseudoClassPanel.bind(this); this._onTogglePseudoClass = this._onTogglePseudoClass.bind(this); this.element = this.doc.getElementById("ruleview-container"); this.addRuleButton = this.doc.getElementById("ruleview-add-rule-button"); this.searchField = this.doc.getElementById("ruleview-searchbox"); - this.searchClearButton = this.doc.getElementById("ruleview-searchinput-clear"); + this.searchClearButton = + this.doc.getElementById("ruleview-searchinput-clear"); this.pseudoClassPanel = this.doc.getElementById("pseudo-class-panel"); this.pseudoClassToggle = this.doc.getElementById("pseudo-class-panel-toggle"); this.hoverCheckbox = this.doc.getElementById("pseudo-hover-toggle"); this.activeCheckbox = this.doc.getElementById("pseudo-active-toggle"); this.focusCheckbox = this.doc.getElementById("pseudo-focus-toggle"); this.searchClearButton.hidden = true; this.element.addEventListener("copy", this._onCopy); this.element.addEventListener("contextmenu", this._onContextMenu); this.addRuleButton.addEventListener("click", this._onAddRule); this.searchField.addEventListener("input", this._onFilterStyles); this.searchField.addEventListener("keypress", this._onFilterKeyPress); - this.searchField.addEventListener("contextmenu", this._onFilterTextboxContextMenu); + this.searchField.addEventListener("contextmenu", + this._onFilterTextboxContextMenu); this.searchClearButton.addEventListener("click", this._onClearSearch); - this.pseudoClassToggle.addEventListener("click", this._onTogglePseudoClassPanel); + this.pseudoClassToggle.addEventListener("click", + this._onTogglePseudoClassPanel); this.hoverCheckbox.addEventListener("click", this._onTogglePseudoClass); this.activeCheckbox.addEventListener("click", this._onTogglePseudoClass); this.focusCheckbox.addEventListener("click", this._onTogglePseudoClass); this._handlePrefChange = this._handlePrefChange.bind(this); this._onSourcePrefChanged = this._onSourcePrefChanged.bind(this); this._prefObserver = new PrefObserver("devtools."); this._prefObserver.on(PREF_ORIG_SOURCES, this._onSourcePrefChanged); this._prefObserver.on(PREF_UA_STYLES, this._handlePrefChange); this._prefObserver.on(PREF_DEFAULT_COLOR_UNIT, this._handlePrefChange); this._prefObserver.on(PREF_ENABLE_MDN_DOCS_TOOLTIP, this._handlePrefChange); this.showUserAgentStyles = Services.prefs.getBoolPref(PREF_UA_STYLES); - this.enableMdnDocsTooltip = Services.prefs.getBoolPref(PREF_ENABLE_MDN_DOCS_TOOLTIP); + this.enableMdnDocsTooltip = + Services.prefs.getBoolPref(PREF_ENABLE_MDN_DOCS_TOOLTIP); let options = { autoSelect: true, theme: "auto" }; this.popup = new AutocompletePopup(aDoc.defaultView.parent.document, options); this._buildContextMenu(); @@ -1266,20 +1283,21 @@ CssRuleView.prototype = { } if (this.selectorHighlighter) { return this.selectorHighlighter; } try { let h = yield utils.getHighlighterByType("SelectorHighlighter"); - return this.selectorHighlighter = h; + this.selectorHighlighter = h; + return h; } catch (e) { - // The SelectorHighlighter type could not be created in the current target. - // It could be an older server, or a XUL page. + // The SelectorHighlighter type could not be created in the + // current target. It could be an older server, or a XUL page. return null; } }), /** * Highlight/unhighlight all the nodes that match a given set of selectors * inside the document of the current selected node. * Only one selector can be highlighted at a time, so calling the method a @@ -1363,17 +1381,17 @@ CssRuleView.prototype = { // No text selected, disable copy. copy = false; } this.menuitemCopyColor.hidden = !this._isColorPopup(); this.menuitemCopyImageDataUrl.hidden = !this._isImageUrlPopup(); this.menuitemCopy.disabled = !copy; - var showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES); + let showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES); this.menuitemSources.setAttribute("checked", showOrig); this.menuitemShowMdnDocs.hidden = !this.enableMdnDocsTooltip || !this.doc.popupNode.parentNode .classList.contains(PROPERTY_NAME_CLASS); this.menuitemAddRule.disabled = this.inspector.selection.isAnonymousNode(); }, @@ -1411,17 +1429,18 @@ CssRuleView.prototype = { value = { property: getPropertyNameAndValue(node).name, value: node.textContent, enabled: prop.enabled, overridden: prop.overridden, pseudoElement: prop.rule.pseudoElement, sheetHref: prop.rule.domRule.href }; - } else if (classes.contains("theme-link") && !classes.contains("ruleview-rule-source") && prop) { + } else if (classes.contains("theme-link") && + !classes.contains("ruleview-rule-source") && prop) { type = overlays.VIEW_NODE_IMAGE_URL_TYPE; value = { property: getPropertyNameAndValue(node).name, value: node.parentNode.textContent, url: node.href, enabled: prop.enabled, overridden: prop.overridden, pseudoElement: prop.rule.pseudoElement, @@ -1440,72 +1459,73 @@ CssRuleView.prototype = { /** * A helper that determines if the popup was opened with a click to a color * value and saves the color to this._colorToCopy. * * @return {Boolean} * true if click on color opened the popup, false otherwise. */ - _isColorPopup: function () { + _isColorPopup: function() { this._colorToCopy = ""; let container = this._getPopupNodeContainer(); if (!container) { return false; } let isColorNode = el => el.dataset && "color" in el.dataset; while (!isColorNode(container)) { container = container.parentNode; if (!container) { return false; } } - this._colorToCopy = container.dataset["color"]; + this._colorToCopy = container.dataset.color; return true; }, /** * Check if the context menu popup was opened with a click on an image link * If true, save the image url to this._imageUrlToCopy */ - _isImageUrlPopup: function () { + _isImageUrlPopup: function() { this._imageUrlToCopy = ""; let container = this._getPopupNodeContainer(); let isImageUrlNode = this._isImageUrlNode(container); if (isImageUrlNode) { this._imageUrlToCopy = container.href; } return isImageUrlNode; }, /** * Check if a node is an image url * @param {DOMNode} node The node which we want information about * @return {Boolean} true if the node is an image url */ - _isImageUrlNode: function (node) { + _isImageUrlNode: function(node) { let nodeInfo = this.getNodeInfo(node); if (!nodeInfo) { - return false + return false; } return nodeInfo.type == overlays.VIEW_NODE_IMAGE_URL_TYPE; }, /** * Get the DOM Node container for the current popupNode. - * If popupNode is a textNode, return the parent node, otherwise return popupNode itself. + * If popupNode is a textNode, return the parent node, otherwise + * return popupNode itself. * @return {DOMNode} */ - _getPopupNodeContainer: function () { + _getPopupNodeContainer: function() { let container = null; let node = this.doc.popupNode; if (node) { let isTextNode = node.nodeType == node.TEXT_NODE; container = isTextNode ? node.parentElement : node; } @@ -1582,26 +1602,28 @@ CssRuleView.prototype = { /** * Copy the most recently selected color value to clipboard. */ _onCopyColor: function() { clipboardHelper.copyString(this._colorToCopy); }, /** - * Retrieve the image data for the selected image url and copy it to the clipboard + * Retrieve the image data for the selected image url and copy it to + * the clipboard */ _onCopyImageDataUrl: Task.async(function*() { let message; try { let inspectorFront = this.inspector.inspector; let data = yield inspectorFront.getImageDataFromURL(this._imageUrlToCopy); message = yield data.data.string(); } catch (e) { - message = _strings.GetStringFromName("styleinspector.copyImageDataUrlError"); + message = + _strings.GetStringFromName("styleinspector.copyImageDataUrlError"); } clipboardHelper.copyString(message); }), /** * Toggle the original sources pref. */ @@ -1778,17 +1800,18 @@ CssRuleView.prototype = { this.isDestroyed = true; this.clear(); gDummyPromise = null; this._prefObserver.off(PREF_ORIG_SOURCES, this._onSourcePrefChanged); this._prefObserver.off(PREF_UA_STYLES, this._handlePrefChange); this._prefObserver.off(PREF_DEFAULT_COLOR_UNIT, this._handlePrefChange); - this._prefObserver.off(PREF_ENABLE_MDN_DOCS_TOOLTIP, this._handlePrefChange); + this._prefObserver.off(PREF_ENABLE_MDN_DOCS_TOOLTIP, + this._handlePrefChange); this._prefObserver.destroy(); this._outputParser = null; this._editorsExpandedForFilter = null; // Remove context menu if (this._contextmenu) { // Destroy the Add Rule menuitem. @@ -1806,21 +1829,23 @@ CssRuleView.prototype = { // Destroy Copy Color menuitem. this.menuitemCopyColor.removeEventListener("command", this._onCopyColor); this.menuitemCopyColor = null; // Destroy Copy Data URI menuitem. this.menuitemCopyImageDataUrl.removeEventListener("command", this._onCopyImageDataUrl); this.menuitemCopyImageDataUrl = null; - this.menuitemSources.removeEventListener("command", this._onToggleOrigSources); + this.menuitemSources.removeEventListener("command", + this._onToggleOrigSources); this.menuitemSources = null; // Destroy the context menu. - this._contextmenu.removeEventListener("popupshowing", this._contextMenuUpdate); + this._contextmenu.removeEventListener("popupshowing", + this._contextMenuUpdate); this._contextmenu.parentNode.removeChild(this._contextmenu); this._contextmenu = null; } // We manage the popupNode ourselves so we also need to destroy it. this.doc.popupNode = null; this.tooltips.destroy(); @@ -1969,17 +1994,16 @@ CssRuleView.prototype = { this._clearRules(); } this._createEditors(); this.refreshPseudoClassPanel(); // Notify anyone that cares that we refreshed. this.emit("ruleview-refreshed"); - return undefined; }).then(null, promiseWarn); }, /** * Show the user that the rule view has no node selected. */ _showEmpty: function() { if (this.doc.getElementById("noResults") > 0) { @@ -2113,17 +2137,18 @@ CssRuleView.prototype = { toggleContainerVisibility(isPseudo, this.showPseudoElements); } return container; }, _getRuleViewHeaderClassName: function(isPseudo) { let baseClassName = "theme-gutter ruleview-header"; - return isPseudo ? baseClassName + " ruleview-expandable-header" : baseClassName; + return isPseudo ? baseClassName + " ruleview-expandable-header" : + baseClassName; }, /** * Creates editor UI for each of the rules in _elementStyle. */ _createEditors: function() { // Run through the current list of rules, attaching // their editors in order. Create editors if needed. @@ -2174,17 +2199,18 @@ CssRuleView.prototype = { div.className = this._getRuleViewHeaderClassName(); div.textContent = inheritedSource; lastInheritedSource = inheritedSource; this.element.appendChild(div); } if (!seenPseudoElement && rule.pseudoElement) { seenPseudoElement = true; - container = this.createExpandableContainer(this.pseudoElementLabel, true); + container = this.createExpandableContainer(this.pseudoElementLabel, + true); } let keyframes = rule.keyframes; if (keyframes && keyframes != lastKeyframes) { lastKeyframes = keyframes; container = this.createExpandableContainer(rule.keyframesName); } @@ -2372,17 +2398,17 @@ CssRuleView.prototype = { /** * Called when a pseudo class checkbox is clicked and toggles * the pseudo class for the current selected element. */ _onTogglePseudoClass: function(event) { let target = event.currentTarget; this.inspector.togglePseudoClass(target.value); - } + } }; /** * Create a RuleEditor. * * @param {CssRuleView} aRuleView * The CssRuleView containg the document holding this rule editor. * @param {Rule} aRule @@ -2458,17 +2484,18 @@ RuleEditor.prototype = { }); if (this.rule.domRule.type !== Ci.nsIDOMCSSRule.KEYFRAME_RULE && this.rule.domRule.selectors) { let selector = this.rule.domRule.selectors.join(", "); let selectorHighlighter = createChild(header, "span", { class: "ruleview-selectorhighlighter" + - (this.ruleView.highlightedSelector === selector ? " highlighted": ""), + (this.ruleView.highlightedSelector === selector ? + " highlighted" : ""), title: CssLogic.l10n("rule.selectorHighlighter.tooltip") }); selectorHighlighter.addEventListener("click", () => { this.ruleView.toggleSelectorHighlighter(selectorHighlighter, selector); }); } this.selectorText = createChild(this.selectorContainer, "span", { @@ -2515,24 +2542,23 @@ RuleEditor.prototype = { } }, false); this.element.addEventListener("mousedown", () => { this.doc.defaultView.focus(); }, false); // Create a property editor when the close brace is clicked. - editableItem({ element: this.closeBrace }, (aElement) => { + editableItem({ element: this.closeBrace }, () => { this.newProperty(); }); } }, - updateSourceLink: function RuleEditor_updateSourceLink() - { + updateSourceLink: function() { let sourceLabel = this.element.querySelector(".source-link-label"); let sourceHref = (this.rule.sheet && this.rule.sheet.href) ? this.rule.sheet.href : this.rule.title; let sourceLine = this.rule.ruleLine > 0 ? ":" + this.rule.ruleLine : ""; sourceLabel.setAttribute("tooltiptext", sourceHref + sourceLine); if (this.rule.isSystem) { @@ -2550,17 +2576,18 @@ RuleEditor.prototype = { } else { sourceLabel.setAttribute("value", this.rule.title); if (this.rule.ruleLine == -1 && this.rule.domRule.parentStyleSheet) { sourceLabel.parentNode.setAttribute("unselectable", "true"); } } let showOrig = Services.prefs.getBoolPref(PREF_ORIG_SOURCES); - if (showOrig && !this.rule.isSystem && this.rule.domRule.type != ELEMENT_STYLE) { + if (showOrig && !this.rule.isSystem && + this.rule.domRule.type != ELEMENT_STYLE) { this.rule.getOriginalSourceStrings().then((strings) => { sourceLabel.setAttribute("value", strings.short); sourceLabel.setAttribute("tooltiptext", strings.full); }, console.error); } }, /** @@ -2723,17 +2750,18 @@ RuleEditor.prototype = { _onNewProperty: function(aValue, aCommit) { if (!aValue || !aCommit) { return; } // parseDeclarations allows for name-less declarations, but in the present // case, we're creating a new declaration, it doesn't make sense to accept // these entries - this.multipleAddedProperties = parseDeclarations(aValue).filter(d => d.name); + this.multipleAddedProperties = + parseDeclarations(aValue).filter(d => d.name); // Blur the editor field now and deal with adding declarations later when // the field gets destroyed (see _newPropertyDestroy) this.editor.input.blur(); }, /** * Called when the new property editor is destroyed. @@ -2897,17 +2925,16 @@ TextPropertyEditor.prototype = { // Create a span that will hold the property and semicolon. // Use this span to create a slightly larger click target // for the value. let propertyContainer = createChild(this.container, "span", { class: "ruleview-propertyvaluecontainer" }); - // Property value, editable when focused. Changes to the // property value are applied as they are typed, and reverted // if the user presses escape. this.valueSpan = createChild(propertyContainer, "span", { class: "ruleview-propertyvalue theme-fg-color1", tabindex: this.ruleEditor.isEditable ? "0" : "-1", }); @@ -2956,17 +2983,17 @@ TextPropertyEditor.prototype = { } }, false); editableField({ start: this._onStartEditing, element: this.nameSpan, done: this._onNameDone, destroy: this.update, - advanceChars: ':', + advanceChars: ":", contentType: InplaceEditor.CONTENT_TYPES.CSS_PROPERTY, popup: this.popup }); // Auto blur name field on multiple CSS rules get pasted in. this.nameContainer.addEventListener("paste", blurOnMultipleProperties, false); @@ -3104,49 +3131,53 @@ TextPropertyEditor.prototype = { defaultColorType: !propDirty, urlClass: "theme-link", baseURI: this.sheetURI }); this.valueSpan.innerHTML = ""; this.valueSpan.appendChild(frag); // Attach the color picker tooltip to the color swatches - this._colorSwatchSpans = this.valueSpan.querySelectorAll("." + colorSwatchClass); + this._colorSwatchSpans = + this.valueSpan.querySelectorAll("." + colorSwatchClass); if (this.ruleEditor.isEditable) { for (let span of this._colorSwatchSpans) { // Capture the original declaration value to be able to revert later let originalValue = this.valueSpan.textContent; - // Adding this swatch to the list of swatches our colorpicker knows about + // Adding this swatch to the list of swatches our colorpicker + // knows about this.ruleEditor.ruleView.tooltips.colorPicker.addSwatch(span, { onPreview: () => this._previewValue(this.valueSpan.textContent), onCommit: () => this._applyNewValue(this.valueSpan.textContent), onRevert: () => this._applyNewValue(originalValue, false) }); } } // Attach the cubic-bezier tooltip to the bezier swatches - this._bezierSwatchSpans = this.valueSpan.querySelectorAll("." + bezierSwatchClass); + this._bezierSwatchSpans = + this.valueSpan.querySelectorAll("." + bezierSwatchClass); if (this.ruleEditor.isEditable) { for (let span of this._bezierSwatchSpans) { // Capture the original declaration value to be able to revert later let originalValue = this.valueSpan.textContent; - // Adding this swatch to the list of swatches our colorpicker knows about + // Adding this swatch to the list of swatches our colorpicker + // knows about this.ruleEditor.ruleView.tooltips.cubicBezier.addSwatch(span, { onPreview: () => this._previewValue(this.valueSpan.textContent), onCommit: () => this._applyNewValue(this.valueSpan.textContent), onRevert: () => this._applyNewValue(originalValue, false) }); } } // Attach the filter editor tooltip to the filter swatch let span = this.valueSpan.querySelector("." + filterSwatchClass); if (this.ruleEditor.isEditable) { - if(span) { + if (span) { let originalValue = this.valueSpan.textContent; this.ruleEditor.ruleView.tooltips.filterEditor.addSwatch(span, { onPreview: () => this._previewValue(this.valueSpan.textContent), onCommit: () => this._applyNewValue(this.valueSpan.textContent), onRevert: () => this._applyNewValue(originalValue, false) }); } @@ -3159,17 +3190,17 @@ TextPropertyEditor.prototype = { _onStartEditing: function() { this.element.classList.remove("ruleview-overridden"); this._previewValue(this.prop.value); }, /** * Populate the list of computed styles. */ - _updateComputed: function () { + _updateComputed: function() { // Clear out existing viewers. while (this.computed.hasChildNodes()) { this.computed.removeChild(this.computed.lastChild); } let showExpander = false; for (let computed of this.prop.computed) { // Don't bother to duplicate information already @@ -3338,28 +3369,29 @@ TextPropertyEditor.prototype = { * Called when a value editor closes. If the user pressed escape, * revert to the value this property had before editing. * * @param {string} aValue * The value contained in the editor. * @param {bool} aCommit * True if the change should be applied. */ - _onValueDone: function(aValue, aCommit) { + _onValueDone: function(aValue, aCommit) { if (!aCommit && !this.ruleEditor.isEditing) { - // A new property should be removed when escape is pressed. - if (this.removeOnRevert) { - this.remove(); - } else { - this.prop.setValue(this.committed.value, this.committed.priority); - } - return; + // A new property should be removed when escape is pressed. + if (this.removeOnRevert) { + this.remove(); + } else { + this.prop.setValue(this.committed.value, this.committed.priority); + } + return; } - let {propertiesToAdd,firstValue} = this._getValueAndExtraProperties(aValue); + let {propertiesToAdd, firstValue} = + this._getValueAndExtraProperties(aValue); // First, set this property value (common case, only modified a property) let val = parseSingleValue(firstValue); this.prop.setValue(val.value, val.priority); this.removeOnRevert = false; this.committed.value = this.prop.value; this.committed.priority = this.prop.priority; @@ -3403,24 +3435,25 @@ TextPropertyEditor.prototype = { // first, then adds any more onto the property list (below this property). let firstValue = aValue; let propertiesToAdd = []; let properties = parseDeclarations(aValue); // Check to see if the input string can be parsed as multiple properties if (properties.length) { - // Get the first property value (if any), and any remaining properties (if any) + // Get the first property value (if any), and any remaining + // properties (if any) if (!properties[0].name && properties[0].value) { firstValue = properties[0].value; propertiesToAdd = properties.slice(1); - } - // In some cases, the value could be a property:value pair itself. - // Join them as one value string and append potentially following properties - else if (properties[0].name && properties[0].value) { + } else if (properties[0].name && properties[0].value) { + // In some cases, the value could be a property:value pair + // itself. Join them as one value string and append + // potentially following properties firstValue = properties[0].name + ": " + properties[0].value; propertiesToAdd = properties.slice(1); } } return { propertiesToAdd: propertiesToAdd, firstValue: firstValue @@ -3460,17 +3493,18 @@ TextPropertyEditor.prototype = { _previewValue: function(aValue) { // Since function call is throttled, we need to make sure we are still // editing, and any selector modifications have been completed if (!this.editing || this.ruleEditor.isEditing) { return; } let val = parseSingleValue(aValue); - this.ruleEditor.rule.previewPropertyValue(this.prop, val.value, val.priority); + this.ruleEditor.rule.previewPropertyValue(this.prop, val.value, + val.priority); }, /** * Validate this property. Does it make sense for this value to be assigned * to this property name? This does not apply the property value * * @return {bool} true if the property value is valid, false otherwise. */ @@ -3572,32 +3606,33 @@ UserProperties.prototype = { * 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") { + } else if (attr === "child") { elt.appendChild(aAttributes[attr]); } else { elt.setAttribute(attr, aAttributes[attr]); } } } aParent.appendChild(elt); return elt; } function createMenuItem(aMenu, aAttributes) { let item = aMenu.ownerDocument.createElementNS(XUL_NS, "menuitem"); item.setAttribute("label", _strings.GetStringFromName(aAttributes.label)); - item.setAttribute("accesskey", _strings.GetStringFromName(aAttributes.accesskey)); + item.setAttribute("accesskey", + _strings.GetStringFromName(aAttributes.accesskey)); item.addEventListener("command", aAttributes.command); if (aAttributes.type) { item.setAttribute("type", aAttributes.type); } aMenu.appendChild(item); @@ -3610,22 +3645,22 @@ function setTimeout() { } function clearTimeout() { let window = Services.appShell.hiddenDOMWindow; return window.clearTimeout.apply(window, arguments); } function throttle(func, wait, scope) { - var timer = null; + let timer = null; return function() { - if(timer) { + if (timer) { clearTimeout(timer); } - var args = arguments; + let args = arguments; timer = setTimeout(function() { timer = null; func.apply(scope, args); }, wait); }; } /** @@ -3728,43 +3763,45 @@ function advanceValidate(aKeyCode, aValu if (aKeyCode !== Ci.nsIDOMKeyEvent.DOM_VK_SEMICOLON) { return false; } // Insert the character provisionally and see what happens. If we // end up with a ";" symbol token, then the semicolon terminates the // value. Otherwise it's been inserted in some spot where it has a // valid meaning, like a comment or string. - aValue = aValue.slice(0, aInsertionPoint) + ';' + aValue.slice(aInsertionPoint); + aValue = aValue.slice(0, aInsertionPoint) + ";" + + aValue.slice(aInsertionPoint); let lexer = domUtils.getCSSLexer(aValue); while (true) { let token = lexer.nextToken(); if (token.endOffset > aInsertionPoint) { if (token.tokenType === "symbol" && token.text === ";") { // The ";" is a terminator. return true; - } else { - // The ";" is not a terminator in this context. - break; } + // The ";" is not a terminator in this context. + break; } } return false; } // We're exporting _advanceValidate for unit tests. exports._advanceValidate = advanceValidate; XPCOMUtils.defineLazyGetter(this, "clipboardHelper", function() { - return Cc["@mozilla.org/widget/clipboardhelper;1"]. - getService(Ci.nsIClipboardHelper); + return Cc["@mozilla.org/widget/clipboardhelper;1"] + .getService(Ci.nsIClipboardHelper); }); XPCOMUtils.defineLazyGetter(this, "_strings", function() { return Services.strings.createBundle( "chrome://global/locale/devtools/styleinspector.properties"); }); XPCOMUtils.defineLazyGetter(this, "domUtils", function() { return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils); }); -loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup); +loader.lazyGetter(this, "AutocompletePopup", function() { + return require("devtools/shared/autocomplete-popup").AutocompletePopup; +});
--- a/toolkit/devtools/output-parser.js +++ b/toolkit/devtools/output-parser.js @@ -1,47 +1,51 @@ /* 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/. */ +/* globals DOMUtils */ + "use strict"; const {Cc, Ci, Cu} = require("chrome"); const {colorUtils} = require("devtools/css-color"); const {Services} = Cu.import("resource://gre/modules/Services.jsm", {}); const HTML_NS = "http://www.w3.org/1999/xhtml"; const MAX_ITERATIONS = 100; -const BEZIER_KEYWORDS = ["linear", "ease-in-out", "ease-in", "ease-out", "ease"]; +const BEZIER_KEYWORDS = ["linear", "ease-in-out", "ease-in", "ease-out", + "ease"]; // Functions that accept a color argument. const COLOR_TAKING_FUNCTIONS = ["linear-gradient", "-moz-linear-gradient", "repeating-linear-gradient", "-moz-repeating-linear-gradient", "radial-gradient", "-moz-radial-gradient", "repeating-radial-gradient", "-moz-repeating-radial-gradient"]; -loader.lazyGetter(this, "DOMUtils", function () { +loader.lazyGetter(this, "DOMUtils", function() { return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils); }); /** * 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 {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. */ function OutputParser() { this.parsed = []; @@ -141,25 +145,26 @@ OutputParser.prototype = { if (options.expectFilter) { this._appendFilter(text, options); } else { let tokenStream = DOMUtils.getCSSLexer(text); let i = 0; while (true) { let token = tokenStream.nextToken(); - if (!token) + if (!token) { break; + } if (token.tokenType === "comment") { continue; } // 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. + // many nodes being appended to the output. In practice it is + // very unlikely that this will ever happen. i++; if (i > MAX_ITERATIONS) { this._appendTextNode(text.substring(token.startOffset, token.endOffset)); continue; } switch (token.tokenType) { @@ -375,17 +380,17 @@ OutputParser.prototype = { if (options.urlClass) { this._appendTextNode("url(\""); let href = url; if (options.baseURI) { href = options.baseURI.resolve(url); } - this._appendNode("a", { + this._appendNode("a", { target: "_blank", class: options.urlClass, href: href }, url); this._appendTextNode("\")"); } else { this._appendTextNode("url(\"" + url + "\")");