diff --git a/browser/devtools/shared/test/unit/test_advanceValidate.js b/browser/devtools/shared/test/unit/test_advanceValidate.js index bbffe726b9aa..9bc54b167577 100644 --- a/browser/devtools/shared/test/unit/test_advanceValidate.js +++ b/browser/devtools/shared/test/unit/test_advanceValidate.js @@ -10,7 +10,7 @@ const Cu = Components.utils; const Ci = Components.interfaces; let {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); -let {_advanceValidate} = require("devtools/styleinspector/rule-view"); +let {advanceValidate} = require("devtools/styleinspector/utils"); // 1 2 3 // 0123456789012345678901234567890 @@ -18,8 +18,8 @@ let sampleInput = '\\symbol "string" url(somewhere)'; function testInsertion(where, result, testName) { do_print(testName); - equal(_advanceValidate(Ci.nsIDOMKeyEvent.DOM_VK_SEMICOLON, sampleInput, where), - result, "testing _advanceValidate at " + where); + equal(advanceValidate(Ci.nsIDOMKeyEvent.DOM_VK_SEMICOLON, sampleInput, where), + result, "testing advanceValidate at " + where); } function run_test() { diff --git a/browser/devtools/styleinspector/computed-view.js b/browser/devtools/styleinspector/computed-view.js index 5661494218c0..2e04531d55f4 100644 --- a/browser/devtools/styleinspector/computed-view.js +++ b/browser/devtools/styleinspector/computed-view.js @@ -15,8 +15,10 @@ const ToolDefinitions = require("main").Tools; const {CssLogic} = require("devtools/styleinspector/css-logic"); const {ELEMENT_STYLE} = require("devtools/server/actors/styles"); const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); +const {setTimeout, clearTimeout} = Cu.import("resource://gre/modules/Timer.jsm", {}); const {OutputParser} = require("devtools/output-parser"); const {PrefObserver, PREF_ORIG_SOURCES} = require("devtools/styleeditor/utils"); +const {createChild} = require("devtools/styleinspector/utils"); const {gDevTools} = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); loader.lazyRequireGetter(this, "overlays", @@ -70,7 +72,7 @@ UpdateProcess.prototype = { if (this.canceled) { return; } - this._timeout = this.win.setTimeout(this._timeoutHandler.bind(this), 0); + this._timeout = setTimeout(this._timeoutHandler.bind(this), 0); }, /** @@ -79,7 +81,7 @@ UpdateProcess.prototype = { */ cancel: function() { if (this._timeout) { - this.win.clearTimeout(this._timeout); + clearTimeout(this._timeout); this._timeout = 0; } this.canceled = true; @@ -515,17 +517,15 @@ CssComputedView.prototype = { * Called when the user enters a search term in the filter style search box. */ _onFilterStyles: function() { - let win = this.styleWindow; - if (this._filterChangedTimeout) { - win.clearTimeout(this._filterChangedTimeout); + clearTimeout(this._filterChangedTimeout); } let filterTimeout = (this.searchField.value.length > 0) ? FILTER_CHANGED_TIMEOUT : 0; this.searchClearButton.hidden = this.searchField.value.length === 0; - this._filterChangedTimeout = win.setTimeout(() => { + this._filterChangedTimeout = setTimeout(() => { if (this.searchField.value.length > 0) { this.searchField.setAttribute("filled", true); } else { @@ -1397,32 +1397,5 @@ SelectorView.prototype = { } }; -/** - * Create a child element with a set of attributes. - * - * @param {Element} parent - * The parent node. - * @param {String} tag - * The tag name. - * @param {Object} attributes - * A set of attributes to set on the node. - */ -function createChild(parent, tag, attributes={}) { - let elt = parent.ownerDocument.createElementNS(HTML_NS, tag); - for (let attr in attributes) { - if (attributes.hasOwnProperty(attr)) { - if (attr === "textContent") { - elt.textContent = attributes[attr]; - } else if (attr === "child") { - elt.appendChild(attributes[attr]); - } else { - elt.setAttribute(attr, attributes[attr]); - } - } - } - parent.appendChild(elt); - return elt; -} - exports.CssComputedView = CssComputedView; exports.PropertyView = PropertyView; diff --git a/browser/devtools/styleinspector/moz.build b/browser/devtools/styleinspector/moz.build index e50d91589b86..b0ffaa71c3ea 100644 --- a/browser/devtools/styleinspector/moz.build +++ b/browser/devtools/styleinspector/moz.build @@ -14,4 +14,5 @@ EXTRA_JS_MODULES.devtools.styleinspector += [ 'style-inspector-menu.js', 'style-inspector-overlays.js', 'style-inspector.js', + 'utils.js', ] diff --git a/browser/devtools/styleinspector/rule-view.js b/browser/devtools/styleinspector/rule-view.js index f420ff306ffc..315275d75255 100644 --- a/browser/devtools/styleinspector/rule-view.js +++ b/browser/devtools/styleinspector/rule-view.js @@ -12,6 +12,8 @@ const {Cc, Ci, Cu} = require("chrome"); const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); +const {setTimeout, clearTimeout} = + Cu.import("resource://gre/modules/Timer.jsm", {}); const {CssLogic} = require("devtools/styleinspector/css-logic"); const {InplaceEditor, editableField, editableItem} = require("devtools/shared/inplace-editor"); @@ -19,6 +21,14 @@ 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 { + createChild, + appendText, + advanceValidate, + blurOnMultipleProperties, + promiseWarn, + throttle +} = require("devtools/styleinspector/utils"); const { parseDeclarations, parseSingleValue, @@ -27,7 +37,6 @@ const { SELECTOR_ELEMENT, SELECTOR_PSEUDO_CLASS } = require("devtools/styleinspector/css-parsing-utils"); - loader.lazyRequireGetter(this, "overlays", "devtools/styleinspector/style-inspector-overlays"); loader.lazyRequireGetter(this, "EventEmitter", @@ -53,11 +62,6 @@ const FILTER_PROP_RE = /\s*([^:\s]*)\s*:\s*(.*?)\s*;?$/; const IOService = Cc["@mozilla.org/network/io-service;1"] .getService(Ci.nsIIOService); -function promiseWarn(err) { - console.error(err); - return promise.reject(err); -} - /** * To figure out how shorthand properties are interpreted by the * engine, we will set properties on a dummy element and observe @@ -3642,77 +3646,6 @@ UserProperties.prototype = { * Helper functions */ -/** - * Create a child element with a set of attributes. - * - * @param {Element} parent - * The parent node. - * @param {String} tag - * The tag name. - * @param {Object} attributes - * A set of attributes to set on the node. - */ -function createChild(parent, tag, attributes) { - let elt = parent.ownerDocument.createElementNS(HTML_NS, tag); - for (let attr in attributes) { - if (attributes.hasOwnProperty(attr)) { - if (attr === "textContent") { - elt.textContent = attributes[attr]; - } else if (attr === "child") { - elt.appendChild(attributes[attr]); - } else { - elt.setAttribute(attr, attributes[attr]); - } - } - } - parent.appendChild(elt); - return elt; -} - -function setTimeout() { - let window = Services.appShell.hiddenDOMWindow; - return window.setTimeout.apply(window, arguments); -} - -function clearTimeout() { - let window = Services.appShell.hiddenDOMWindow; - return window.clearTimeout.apply(window, arguments); -} - -function throttle(func, wait, scope) { - let timer = null; - return function() { - if (timer) { - clearTimeout(timer); - } - let args = arguments; - timer = setTimeout(function() { - timer = null; - func.apply(scope, args); - }, wait); - }; -} - -/** - * Event handler that causes a blur on the target if the input has - * multiple CSS properties as the value. - */ -function blurOnMultipleProperties(e) { - setTimeout(() => { - let props = parseDeclarations(e.target.value); - if (props.length > 1) { - e.target.blur(); - } - }, 0); -} - -/** - * Append a text node to an element. - */ -function appendText(aParent, aText) { - aParent.appendChild(aParent.ownerDocument.createTextNode(aText)); -} - /** * Walk up the DOM from a given node until a parent property holder is found. * For elements inside the computed property list, the non-computed parent @@ -3781,51 +3714,6 @@ function getPropertyNameAndValue(node) { } } -/** - * Called when a character is typed in a value editor. This decides - * whether to advance or not, first by checking to see if ";" was - * typed, and then by lexing the input and seeing whether the ";" - * would be a terminator at this point. - * - * @param {Number} keyCode - * Key code to be checked. - * @param {String} value - * Current text editor value. - * @param {Number} insertionPoint - * The index of the insertion point. - * @return {Boolean} True if the focus should advance; false if - * the character should be inserted. - */ -function advanceValidate(keyCode, value, insertionPoint) { - // Only ";" has special handling here. - if (keyCode !== 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. - value = value.slice(0, insertionPoint) + ";" + - value.slice(insertionPoint); - let lexer = domUtils.getCSSLexer(value); - while (true) { - let token = lexer.nextToken(); - if (token.endOffset > insertionPoint) { - if (token.tokenType === "symbol" && token.text === ";") { - // The ";" is a terminator. - return true; - } - // 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); diff --git a/browser/devtools/styleinspector/utils.js b/browser/devtools/styleinspector/utils.js new file mode 100644 index 000000000000..5085ac0e4fe9 --- /dev/null +++ b/browser/devtools/styleinspector/utils.js @@ -0,0 +1,166 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ft=javascript 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/. */ + +/* global domUtils */ + +"use strict"; + +const {Cc, Ci, Cu} = require("chrome"); +const {setTimeout, clearTimeout} = + Cu.import("resource://gre/modules/Timer.jsm", {}); +const {parseDeclarations} = + require("devtools/styleinspector/css-parsing-utils"); +const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); + +loader.lazyServiceGetter(this, "domUtils", + "@mozilla.org/inspector/dom-utils;1", "inIDOMUtils"); + +const HTML_NS = "http://www.w3.org/1999/xhtml"; + +/** + * Create a child element with a set of attributes. + * + * @param {Element} parent + * The parent node. + * @param {string} tagName + * The tag name. + * @param {object} attributes + * A set of attributes to set on the node. + */ +function createChild(parent, tagName, attributes={}) { + let elt = parent.ownerDocument.createElementNS(HTML_NS, tagName); + for (let attr in attributes) { + if (attributes.hasOwnProperty(attr)) { + if (attr === "textContent") { + elt.textContent = attributes[attr]; + } else if (attr === "child") { + elt.appendChild(attributes[attr]); + } else { + elt.setAttribute(attr, attributes[attr]); + } + } + } + parent.appendChild(elt); + return elt; +} + +exports.createChild = createChild; + +/** + * Append a text node to an element. + * + * @param {Element} parent + * The parent node. + * @param {string} text + * The text content for the text node. + */ +function appendText(parent, text) { + parent.appendChild(parent.ownerDocument.createTextNode(text)); +} + +exports.appendText = appendText; + +/** + * Called when a character is typed in a value editor. This decides + * whether to advance or not, first by checking to see if ";" was + * typed, and then by lexing the input and seeing whether the ";" + * would be a terminator at this point. + * + * @param {number} keyCode + * Key code to be checked. + * @param {string} aValue + * Current text editor value. + * @param {number} insertionPoint + * The index of the insertion point. + * @return {Boolean} True if the focus should advance; false if + * the character should be inserted. + */ +function advanceValidate(keyCode, value, insertionPoint) { + // Only ";" has special handling here. + if (keyCode !== 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. + value = value.slice(0, insertionPoint) + ";" + value.slice(insertionPoint); + let lexer = domUtils.getCSSLexer(value); + while (true) { + let token = lexer.nextToken(); + if (token.endOffset > insertionPoint) { + if (token.tokenType === "symbol" && token.text === ";") { + // The ";" is a terminator. + return true; + } + // The ";" is not a terminator in this context. + break; + } + } + return false; +} + +exports.advanceValidate = advanceValidate; + +/** + * Create a throttling function wrapper to regulate its frequency. + * + * @param {Function} func + * The function to throttle + * @param {number} wait + * The throttling period + * @param {Object} scope + * The scope to use for func + * @return {Function} The throttled function + */ +function throttle(func, wait, scope) { + let timer = null; + + return function() { + if (timer) { + clearTimeout(timer); + } + + let args = arguments; + timer = setTimeout(function() { + timer = null; + func.apply(scope, args); + }, wait); + }; +} + +exports.throttle = throttle; + +/** + * Event handler that causes a blur on the target if the input has + * multiple CSS properties as the value. + */ +function blurOnMultipleProperties(e) { + setTimeout(() => { + let props = parseDeclarations(e.target.value); + if (props.length > 1) { + e.target.blur(); + } + }, 0); +} + +exports.blurOnMultipleProperties = blurOnMultipleProperties; + +/** + * Log the provided error to the console and return a rejected Promise for + * this error. + * + * @param {Error} error + * The error to log + * @return {Promise} A rejected promise + */ +function promiseWarn(error) { + console.error(error); + return promise.reject(error); +} + +exports.promiseWarn = promiseWarn;