author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Fri, 29 Jan 2016 11:47:23 +0100 | |
changeset 282280 | 8a3c5c9b1486c3328bc4684d5ac5b3b849b09474 |
parent 282273 | ac21d6f878e6fdd69ec4140d1b63a94601524abe (current diff) |
parent 282279 | 103a122789a33d1f1b12a5a1d34425c5b730213e (diff) |
child 282297 | 3ba18d54f7578ff2d212c2cf55f14927851e7706 |
child 282346 | df5126ec78738d3f9e8a127d693724e01abe2ee0 |
push id | 29954 |
push user | cbook@mozilla.com |
push date | Fri, 29 Jan 2016 10:47:31 +0000 |
treeherder | mozilla-central@8a3c5c9b1486 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 47.0a1 |
first release with | nightly linux32
8a3c5c9b1486
/
47.0a1
/
20160129030206
/
files
nightly linux64
8a3c5c9b1486
/
47.0a1
/
20160129030206
/
files
nightly mac
8a3c5c9b1486
/
47.0a1
/
20160129030206
/
files
nightly win32
8a3c5c9b1486
/
47.0a1
/
20160129030206
/
files
nightly win64
8a3c5c9b1486
/
47.0a1
/
20160129030206
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
47.0a1
/
20160129030206
/
pushlog to previous
nightly linux64
47.0a1
/
20160129030206
/
pushlog to previous
nightly mac
47.0a1
/
20160129030206
/
pushlog to previous
nightly win32
47.0a1
/
20160129030206
/
pushlog to previous
nightly win64
47.0a1
/
20160129030206
/
pushlog to previous
|
devtools/client/performance/views/optimizations-list.js | file | annotate | diff | comparison | revisions |
--- a/devtools/client/jar.mn +++ b/devtools/client/jar.mn @@ -95,17 +95,16 @@ devtools.jar: content/performance/views/toolbar.js (performance/views/toolbar.js) content/performance/views/details.js (performance/views/details.js) content/performance/views/details-abstract-subview.js (performance/views/details-abstract-subview.js) content/performance/views/details-waterfall.js (performance/views/details-waterfall.js) content/performance/views/details-js-call-tree.js (performance/views/details-js-call-tree.js) content/performance/views/details-js-flamegraph.js (performance/views/details-js-flamegraph.js) content/performance/views/details-memory-call-tree.js (performance/views/details-memory-call-tree.js) content/performance/views/details-memory-flamegraph.js (performance/views/details-memory-flamegraph.js) - content/performance/views/optimizations-list.js (performance/views/optimizations-list.js) content/performance/views/recordings.js (performance/views/recordings.js) content/memory/memory.xhtml (memory/memory.xhtml) content/memory/initializer.js (memory/initializer.js) content/promisedebugger/promise-controller.js (promisedebugger/promise-controller.js) content/promisedebugger/promise-panel.js (promisedebugger/promise-panel.js) content/promisedebugger/promise-debugger.xhtml (promisedebugger/promise-debugger.xhtml) content/commandline/commandline.css (commandline/commandline.css) content/commandline/commandlineoutput.xhtml (commandline/commandlineoutput.xhtml) @@ -214,16 +213,18 @@ devtools.jar: skin/promisedebugger.css (themes/promisedebugger.css) skin/images/timeline-filter.svg (themes/images/timeline-filter.svg) skin/scratchpad.css (themes/scratchpad.css) skin/shadereditor.css (themes/shadereditor.css) skin/storage.css (themes/storage.css) skin/splitview.css (themes/splitview.css) skin/styleeditor.css (themes/styleeditor.css) skin/webaudioeditor.css (themes/webaudioeditor.css) + skin/components-frame.css (themes/components-frame.css) + skin/jit-optimizations.css (themes/jit-optimizations.css) skin/images/magnifying-glass.png (themes/images/magnifying-glass.png) skin/images/magnifying-glass@2x.png (themes/images/magnifying-glass@2x.png) skin/images/magnifying-glass-light.png (themes/images/magnifying-glass-light.png) skin/images/magnifying-glass-light@2x.png (themes/images/magnifying-glass-light@2x.png) skin/images/itemToggle.png (themes/images/itemToggle.png) skin/images/itemToggle@2x.png (themes/images/itemToggle@2x.png) skin/images/itemArrow-dark-rtl.svg (themes/images/itemArrow-dark-rtl.svg) skin/images/itemArrow-dark-ltr.svg (themes/images/itemArrow-dark-ltr.svg)
new file mode 100644 --- /dev/null +++ b/devtools/client/locales/en-US/jit-optimizations.properties @@ -0,0 +1,31 @@ +# 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/. + +# LOCALIZATION NOTE These strings are used within the JIT tools +# in the Performance Tools which is available from the Web Developer +# sub-menu -> 'Performance' The correct localization of this file might +# be to keep it in English, or another language commonly spoken among +# web developers. You want to make that choice consistent across the +# developer tools. A good criteria is the language in which you'd find the best +# documentation on web development on the web. + +# LOCALIZATION NOTE (jit.title): +# This string is displayed in the header of the JIT Optimizations view. +jit.title=JIT Optimizations + +# LOCALIZATION NOTE (jit.optimizationFailure): +# This string is displayed in a tooltip when no JIT optimizations were detected. +jit.optimizationFailure=Optimization failed + +# LOCALIZATION NOTE (jit.samples): +# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals +# This string is displayed for the unit representing the number of times a +# frame is sampled. +# "#1" represents the number of samples +# example: 30 samples +jit.samples=#1 sample;#1 samples + +# LOCALIZATION NOTE (jit.empty): +# This string is displayed when there are no JIT optimizations to display. +jit.empty=No JIT optimizations recorded for this frame.
--- a/devtools/client/locales/en-US/performance.dtd +++ b/devtools/client/locales/en-US/performance.dtd @@ -135,20 +135,16 @@ <!ENTITY performanceUI.enableFramerate.tooltiptext "Record framerate while profiling."> <!-- LOCALIZATION NOTE (performanceUI.enableJITOptimizations): This string - is displayed next to a checkbox determining whether or not JIT optimization data - should be recorded. --> <!ENTITY performanceUI.enableJITOptimizations "Record JIT Optimizations"> <!ENTITY performanceUI.enableJITOptimizations.tooltiptext "Record JIT optimization data sampled in each JavaScript frame."> -<!-- LOCALIZATION NOTE (performanceUI.JITOptimizationsTitle): This string - - is displayed as the title of the JIT Optimizations panel. --> -<!ENTITY performanceUI.JITOptimizationsTitle "JIT Optimizations"> - <!-- LOCALIZATION NOTE (performanceUI.console.recordingNoticeStart/recordingNoticeEnd): - This string is displayed when a recording is selected that started via console.profile. - Wraps the command used to start, like "Currently recording via console.profile("label")" --> <!ENTITY performanceUI.console.recordingNoticeStart "Currently recording via"> <!ENTITY performanceUI.console.recordingNoticeEnd ""> <!-- LOCALIZATION NOTE (performanceUI.console.stopCommandStart/stopCommandEnd): - This string is displayed when a recording is selected that started via console.profile.
--- a/devtools/client/locales/en-US/performance.properties +++ b/devtools/client/locales/en-US/performance.properties @@ -136,32 +136,16 @@ recordingsList.saveDialogTitle=Save recording… # LOCALIZATION NOTE (recordingsList.saveDialogJSONFilter): # This string is displayed as a filter for saving a recording to disk. recordingsList.saveDialogJSONFilter=JSON Files # LOCALIZATION NOTE (recordingsList.saveDialogAllFilter): # This string is displayed as a filter for saving a recording to disk. recordingsList.saveDialogAllFilter=All Files -# LOCALIZATION NOTE (jit.optimizationFailure): -# This string is displayed in a tooltip when no JIT optimizations were detected. -jit.optimizationFailure=Optimization failed - -# LOCALIZATION NOTE (jit.samples): -# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals -# This string is displayed for the unit representing the number of times a -# frame is sampled. -# "#1" represents the number of samples -# example: 30 samples -jit.samples=#1 sample;#1 samples - -# LOCALIZATION NOTE (jit.empty): -# This string is displayed when there are no JIT optimizations to display. -jit.empty=No JIT optimizations recorded for this frame. - # LOCALIZATION NOTE (timeline.tick): # This string is displayed in the timeline overview, for delimiting ticks # by time, in milliseconds. timeline.tick=%S ms # LOCALIZATION NOTE (timeline.records): # This string is displayed in the timeline waterfall, as a title for the menu. timeline.records=RECORDS
--- a/devtools/client/memory/memory.xhtml +++ b/devtools/client/memory/memory.xhtml @@ -9,16 +9,17 @@ <!-- 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/. --> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <link rel="stylesheet" href="chrome://devtools/skin/common.css" type="text/css"/> <link rel="stylesheet" href="chrome://devtools/skin/widgets.css" type="text/css"/> <link rel="stylesheet" href="chrome://devtools/skin/memory.css" type="text/css"/> + <link rel="stylesheet" href="chrome://devtools/skin/components-frame.css" type="text/css"/> <script type="application/javascript;version=1.8" src="chrome://devtools/content/shared/theme-switching.js"/> <script type="application/javascript;version=1.8" src="initializer.js"></script> </head> <body class="theme-body"> <div id="app">
new file mode 100644 --- /dev/null +++ b/devtools/client/performance/components/moz.build @@ -0,0 +1,9 @@ +# vim: set filetype=python: +# 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/. + +DevToolsModules( + 'optimizations-item.js', + 'optimizations.js', +)
new file mode 100644 --- /dev/null +++ b/devtools/client/performance/components/optimizations-item.js @@ -0,0 +1,145 @@ +/* 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/. */ + +const { Cu } = require("chrome"); +Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm"); +const STRINGS_URI = "chrome://devtools/locale/jit-optimizations.properties"; +const L10N = new ViewHelpers.L10N(STRINGS_URI); +const { DOM: dom, PropTypes, createClass, createFactory } = require("devtools/client/shared/vendor/react"); +const Frame = createFactory(require("devtools/client/shared/components/frame")); +const OPTIMIZATION_FAILURE = L10N.getStr("jit.optimizationFailure"); +const JIT_SAMPLES = L10N.getStr("jit.samples"); +const JIT_EMPTY_TEXT = L10N.getStr("jit.empty"); +const PROPNAME_MAX_LENGTH = 4; +// If TREE_ROW_HEIGHT changes, be sure to change `var(--jit-tree-row-height)` +// in `devtools/client/themes/jit-optimizations.css` +const TREE_ROW_HEIGHT = 14; + +const OPTIMIZATION_ITEM_TYPES = ["site", "attempts", "types", "attempt", "type", "observedtype"]; +const OptimizationsItem = module.exports = createClass({ + displayName: "OptimizationsItem", + + propTypes: { + onViewSourceInDebugger: PropTypes.func.isRequired, + frameData: PropTypes.object.isRequired, + type: PropTypes.oneOf(OPTIMIZATION_ITEM_TYPES).isRequired, + }, + + render() { + let { + item, + depth, + arrow, + focused, + type, + frameData, + onViewSourceInDebugger, + } = this.props; + + let content; + switch (type) { + case "site": content = this._renderSite(this.props); break; + case "attempts": content = this._renderAttempts(this.props); break; + case "types": content = this._renderTypes(this.props); break; + case "attempt": content = this._renderAttempt(this.props); break; + case "type": content = this._renderType(this.props); break; + case "observedtype": content = this._renderObservedType(this.props); break; + }; + + return dom.div( + { + className: `optimization-tree-item optimization-tree-item-${type}`, + style: { marginLeft: depth * TREE_ROW_HEIGHT } + }, + arrow, + content + ); + }, + + _renderSite({ item: site, onViewSourceInDebugger, frameData }) { + let attempts = site.data.attempts; + let lastStrategy = attempts[attempts.length - 1].strategy; + let propString = ""; + let propertyName = site.data.propertyName; + + // Display property name if it exists + if (propertyName) { + if (propertyName.length > PROPNAME_MAX_LENGTH) { + propString = ` (.${propertyName.substr(0, PROPNAME_MAX_LENGTH)}…)`; + } else { + propString = ` (.${propertyName})`; + } + } + + let sampleString = PluralForm.get(site.samples, JIT_SAMPLES).replace("#1", site.samples); + let text = `${lastStrategy}${propString} – (${sampleString})`; + let frame = Frame({ + onClick: () => onViewSourceInDebugger(frameData.url, site.data.line), + frame: { + source: frameData.url, + line: site.data.line, + column: site.data.column, + } + }) + let children = [text, frame]; + + if (!site.hasSuccessfulOutcome()) { + children.unshift(dom.span({ className: "opt-icon warning" })); + } + + return dom.span({ className: "optimization-site" }, ...children); + }, + + _renderAttempts({ item: attempts }) { + return dom.span({ className: "optimization-attempts" }, + `Attempts (${attempts.length})` + ); + }, + + _renderTypes({ item: types }) { + return dom.span({ className: "optimization-types" }, + `Types (${types.length})` + ); + }, + + _renderAttempt({ item: attempt }) { + let success = JITOptimizations.isSuccessfulOutcome(attempt.outcome); + let { strategy, outcome } = attempt; + return dom.span({ className: "optimization-attempt" }, + dom.span({ className: "optimization-strategy" }, strategy), + " → ", + dom.span({ className: `optimization-outcome ${success ? "success" : "failure"}` }, outcome) + ); + }, + + _renderType({ item: type }) { + return dom.span({ className: "optimization-ion-type" }, `${type.site}:${type.mirType}`); + }, + + _renderObservedType({ onViewSourceInDebugger, item: type }) { + let children = [ + `${type.keyedBy}${type.name ? ` → ${type.name}` : ""}` + ]; + + // If we have a line and location, make a link to the debugger + if (type.location && type.line) { + children.push( + Frame({ + onClick: () => onViewSourceInDebugger(type.location, type.line), + frame: { + source: type.location, + line: type.line, + column: type.column, + } + }) + ); + } + // Otherwise if we just have a location, it's probably just a memory location. + else if (type.location) { + children.push(`@${type.location}`); + } + + return dom.span({ className: "optimization-observed-type" }, ...children); + }, +});
new file mode 100644 --- /dev/null +++ b/devtools/client/performance/components/optimizations.js @@ -0,0 +1,183 @@ +/* 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/. */ + +const { Cu } = require("chrome"); +Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm"); +const STRINGS_URI = "chrome://devtools/locale/jit-optimizations.properties"; +const L10N = new ViewHelpers.L10N(STRINGS_URI); +const { assert } = require("devtools/shared/DevToolsUtils"); +const { DOM: dom, createClass, createFactory, PropTypes } = require("devtools/client/shared/vendor/react"); +const Tree = createFactory(require("../../shared/components/tree")); +const OptimizationsItem = createFactory(require("./optimizations-item")); +const FrameView = createFactory(require("../../shared/components/frame")); + +const onClickTooltipString = frame => + L10N.getFormatStr("viewsourceindebugger",`${frame.source}:${frame.line}:${frame.column}`); +const JIT_TITLE = L10N.getStr("jit.title"); +// If TREE_ROW_HEIGHT changes, be sure to change `var(--jit-tree-row-height)` +// in `devtools/client/themes/jit-optimizations.css` +const TREE_ROW_HEIGHT = 14; + +const Optimizations = module.exports = createClass({ + displayName: "Optimizations", + + propTypes: { + onViewSourceInDebugger: PropTypes.func.isRequired, + frameData: PropTypes.object.isRequired, + optimizationSites: PropTypes.array.isRequired, + }, + + getInitialState() { + return { + expanded: new Set() + }; + }, + + getDefaultProps() { + return {}; + }, + + render() { + let header = this._createHeader(this.props); + let tree = this._createTree(this.props); + + return dom.div({}, header, tree); + }, + + /** + * Frame data generated from `frameNode.getInfo()`, or an empty + * object, as well as a handler for clicking on the frame component. + * + * @param {?Object} .frameData + * @param {Function} .onViewSourceInDebugger + * @return {ReactElement} + */ + _createHeader: function ({ frameData, onViewSourceInDebugger }) { + let { isMetaCategory, url, line } = frameData; + let name = isMetaCategory ? frameData.categoryData.label : + frameData.functionName || ""; + + // Simulate `SavedFrame`s interface + let frame = { source: url, line: +line, functionDisplayName: name }; + + // Neither Meta Category nodes, or the lack of a selected frame node, + // renders out a frame source, like "file.js:123"; so just use + // an empty span. + let frameComponent; + if (isMetaCategory || !name) { + frameComponent = dom.span(); + } else { + frameComponent = FrameView({ + frame, + onClick: () => onViewSourceInDebugger(frame), + }); + } + + return dom.div({ className: "optimization-header" }, + dom.span({ className: "header-title" }, JIT_TITLE), + dom.span({ className: "header-function-name" }, name), + frameComponent + ); + }, + + _createTree(props) { + let { frameData, onViewSourceInDebugger, optimizationSites: sites } = this.props; + + let getSite = id => sites.find(site => site.id === id); + let getIonTypeForObserved = type => + getSite(type.id).data.types.find(iontype => (iontype.typeset || []).indexOf(type) !== -1); + let isSite = site => getSite(site.id) === site; + let isAttempts = attempts => getSite(attempts.id).data.attempts === attempts; + let isAttempt = attempt => getSite(attempt.id).data.attempts.indexOf(attempt) !== -1; + let isTypes = types => getSite(types.id).data.types === types; + let isType = type => getSite(type.id).data.types.indexOf(type) !== -1; + let isObservedType = type => getIonTypeForObserved(type); + + let getRowType = node => { + return isSite(node) ? "site" : + isAttempts(node) ? "attempts" : + isTypes(node) ? "types" : + isAttempt(node) ? "attempt" : + isType(node) ? "type": + isObservedType(node) ? "observedtype": null; + }; + + // Creates a unique key for each node in the + // optimizations data + let getKey = node => { + let site = getSite(node.id); + if (isSite(node)) { + return node.id; + } else if (isAttempts(node)) { + return `${node.id}-A`; + } else if (isTypes(node)) { + return `${node.id}-T`; + } else if (isType(node)) { + return `${node.id}-T-${site.data.types.indexOf(node)}`; + } else if (isAttempt(node)) { + return `${node.id}-A-${site.data.attempts.indexOf(node)}`; + } else if (isObservedType(node)) { + let iontype = getIonTypeForObserved(node); + return `${getKey(iontype)}-O-${iontype.typeset.indexOf(node)}`; + } + }; + + return Tree({ + autoExpandDepth: 0, + getParent: node => { + let site = getSite(node.id); + let parent; + if (isAttempts(node) || isTypes(node)) { + parent = site; + } else if (isType(node)) { + parent = site.data.types; + } else if (isAttempt(node)) { + parent = site.data.attempts; + } else if (isObservedType(node)) { + parent = getIonTypeForObserved(node); + } + assert(parent, "Could not find a parent for optimization data node"); + + return parent; + }, + getChildren: node => { + if (isSite(node)) { + return [node.data.types, node.data.attempts]; + } else if (isAttempts(node) || isTypes(node)) { + return node; + } else if (isType(node)) { + return node.typeset || []; + } else { + return []; + } + }, + isExpanded: node => this.state.expanded.has(node), + onExpand: node => this.setState(state => { + let expanded = new Set(state.expanded); + expanded.add(node); + return { expanded }; + }), + onCollapse: node => this.setState(state => { + let expanded = new Set(state.expanded); + expanded.delete(node); + return { expanded }; + }), + onFocus: function () {}, + getKey, + getRoots: () => sites || [], + itemHeight: TREE_ROW_HEIGHT, + renderItem: (item, depth, focused, arrow, expanded) => + new OptimizationsItem({ + onViewSourceInDebugger, + item, + depth, + focused, + arrow, + expanded, + type: getRowType(item), + frameData, + }), + }); + } +});
--- a/devtools/client/performance/modules/logic/frame-utils.js +++ b/devtools/client/performance/modules/logic/frame-utils.js @@ -1,30 +1,33 @@ /* 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 global = require("devtools/client/performance/modules/global"); const demangle = require("devtools/client/shared/demangle"); +const { assert } = require("devtools/shared/DevToolsUtils"); const { isChromeScheme, isContentScheme, parseURL } = require("devtools/client/shared/source-utils"); // Character codes used in various parsing helper functions. const CHAR_CODE_R = "r".charCodeAt(0); const CHAR_CODE_0 = "0".charCodeAt(0); const CHAR_CODE_9 = "9".charCodeAt(0); const CHAR_CODE_CAP_Z = "Z".charCodeAt(0); const CHAR_CODE_LPAREN = "(".charCodeAt(0); const CHAR_CODE_RPAREN = ")".charCodeAt(0); const CHAR_CODE_COLON = ":".charCodeAt(0); const CHAR_CODE_SPACE = " ".charCodeAt(0); const CHAR_CODE_UNDERSCORE = "_".charCodeAt(0); +const EVAL_TOKEN = "%20%3E%20eval"; + // The cache used to store inflated frames. const gInflatedFrameStore = new WeakMap(); // The cache used to store frame data from `getInfo`. const gFrameData = new WeakMap(); /** * Parses the raw location of this function call to retrieve the actual @@ -144,16 +147,36 @@ function parseLocation(location, fallbac column = column || fallbackColumn; // If the URL digged out from the `location` is valid, this is a JS frame. if (parsedUrl) { functionName = location.substring(0, parenIndex - 1); fileName = parsedUrl.fileName; port = parsedUrl.port; host = parsedUrl.host; + + // Check for the case of the filename containing eval + // e.g. "file.js%20line%2065%20%3E%20eval" + let evalIndex = fileName.indexOf(EVAL_TOKEN); + if (evalIndex !== -1 && evalIndex === (fileName.length - EVAL_TOKEN.length)) { + // Match the filename + let evalLine = line; + let [, _fileName, , _line] = fileName.match(/(.+)(%20line%20(\d+)%20%3E%20eval)/) || []; + fileName = `${_fileName} (eval:${evalLine})`; + line = _line; + assert(_fileName !== undefined, + "Filename could not be found from an eval location site"); + assert(_line !== undefined, + "Line could not be found from an eval location site"); + + // Match the url as well + [, url] = url.match(/(.+)( line (\d+) > eval)/) || []; + assert(url !== undefined, + "The URL could not be parsed correctly from an eval location site"); + } } else { functionName = location; url = null; } return { functionName, fileName, host, port, url, line, column }; };
--- a/devtools/client/performance/modules/logic/jit.js +++ b/devtools/client/performance/modules/logic/jit.js @@ -47,17 +47,17 @@ const SUCCESSFUL_OUTCOMES = [ * Based off of the observed types for a value (like a variable that could be a * string or an instance of an object), it determines what kind of type it should be classified * as. Each IonType here contains an array of all ObservedTypes under `types`, * the Ion type that IonMonkey decided this value should be (Int32, Object, etc.) as `mirType`, * and the component of this optimization type that this value refers to -- like * a "getter" optimization, `a[b]`, has site `a` (the "Receiver") and `b` (the "Index"). * * Generally the more ObservedTypes, the more deoptimized this OptimizationSite is. - * There could be no ObservedTypes, in which case `types` is undefined. + * There could be no ObservedTypes, in which case `typeset` is undefined. * * @type {?Array<ObservedType>} typeset * @type {string} site * @type {string} mirType * * * @struct ObservedType * When IonMonkey attempts to determine what type a value is, it checks on each sample. @@ -179,33 +179,43 @@ const JITOptimizations = function (rawSi } } // Inflate the optimization information. for (let site of sites) { let data = site.data; let STRATEGY_SLOT = data.attempts.schema.strategy; let OUTCOME_SLOT = data.attempts.schema.outcome; + let attempts = data.attempts.data.map((a) => { + return { + id: site.id, + strategy: stringTable[a[STRATEGY_SLOT]], + outcome: stringTable[a[OUTCOME_SLOT]] + } + }); + let types = data.types.map((t) => { + let typeset = maybeTypeset(t.typeset, stringTable); + if (typeset) { + typeset.forEach(t => t.id = site.id); + } + + return { + id: site.id, + typeset, + site: stringTable[t.site], + mirType: stringTable[t.mirType] + }; + }); + // Add IDs to to all children objects, so we can correllate sites when + // just looking at a specific type, attempt, etc.. + attempts.id = types.id = site.id; site.data = { - attempts: data.attempts.data.map((a) => { - return { - strategy: stringTable[a[STRATEGY_SLOT]], - outcome: stringTable[a[OUTCOME_SLOT]] - } - }), - - types: data.types.map((t) => { - return { - typeset: maybeTypeset(t.typeset, stringTable), - site: stringTable[t.site], - mirType: stringTable[t.mirType] - }; - }), - + attempts, + types, propertyName: maybeString(stringTable, data.propertyName), line: data.line, column: data.column }; } this.optimizationSites = sites.sort((a, b) => b.samples - a.samples); };
--- a/devtools/client/performance/moz.build +++ b/devtools/client/performance/moz.build @@ -1,14 +1,15 @@ # vim: set filetype=python: # 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/. DIRS += [ + 'components', 'legacy', 'modules', ] DevToolsModules( 'events.js', 'panel.js' )
--- a/devtools/client/performance/performance-controller.js +++ b/devtools/client/performance/performance-controller.js @@ -13,16 +13,19 @@ var { Heritage, ViewHelpers, WidgetMetho // Events emitted by various objects in the panel. var EVENTS = require("devtools/client/performance/events"); Object.defineProperty(this, "EVENTS", { value: EVENTS, enumerable: true, writable: false }); +var React = require("devtools/client/shared/vendor/react"); +var ReactDOM = require("devtools/client/shared/vendor/react-dom"); +var Optimizations = React.createFactory(require("devtools/client/performance/components/optimizations")); var Services = require("Services"); var promise = require("promise"); var EventEmitter = require("devtools/shared/event-emitter"); var DevToolsUtils = require("devtools/shared/DevToolsUtils"); var system = require("devtools/shared/system"); // Logic modules
--- a/devtools/client/performance/performance.xul +++ b/devtools/client/performance/performance.xul @@ -2,16 +2,18 @@ <!-- 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/. --> <?xml-stylesheet href="chrome://global/skin/" type="text/css"?> <?xml-stylesheet href="chrome://devtools/content/shared/widgets/widgets.css" type="text/css"?> <?xml-stylesheet href="chrome://devtools/skin/common.css" type="text/css"?> <?xml-stylesheet href="chrome://devtools/skin/widgets.css" type="text/css"?> <?xml-stylesheet href="chrome://devtools/skin/performance.css" type="text/css"?> +<?xml-stylesheet href="chrome://devtools/skin/jit-optimizations.css" type="text/css"?> +<?xml-stylesheet href="chrome://devtools/skin/components-frame.css" type="text/css"?> <!DOCTYPE window [ <!ENTITY % performanceDTD SYSTEM "chrome://devtools/locale/performance.dtd"> %performanceDTD; ]> <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> <script src="chrome://devtools/content/shared/theme-switching.js"/> <script type="application/javascript" src="performance-controller.js"/> @@ -21,17 +23,16 @@ <script type="application/javascript" src="views/details-abstract-subview.js"/> <script type="application/javascript" src="views/details-waterfall.js"/> <script type="application/javascript" src="views/details-js-call-tree.js"/> <script type="application/javascript" src="views/details-js-flamegraph.js"/> <script type="application/javascript" src="views/details-memory-call-tree.js"/> <script type="application/javascript" src="views/details-memory-flamegraph.js"/> <script type="application/javascript" src="views/details.js"/> <script type="application/javascript" src="views/recordings.js"/> - <script type="application/javascript" src="views/optimizations-list.js"/> <popupset id="performance-options-popupset"> <menupopup id="performance-filter-menupopup"/> <menupopup id="performance-options-menupopup" position="before_end"> <menuitem id="option-show-platform-data" type="checkbox" data-pref="show-platform-data" label="&performanceUI.showPlatformData;" @@ -313,27 +314,17 @@ value="&performanceUI.table.function;" tooltiptext="&performanceUI.table.function.tooltip;"/> </hbox> <vbox class="call-tree-cells-container" flex="1"/> </vbox> <splitter class="devtools-side-splitter"/> <!-- Optimizations Panel --> <vbox id="jit-optimizations-view" - class="hidden"> - <toolbar id="jit-optimizations-toolbar" class="devtools-toolbar"> - <hbox id="jit-optimizations-header"> - <span class="jit-optimizations-title">&performanceUI.JITOptimizationsTitle;</span> - <span class="header-function-name" /> - <span class="header-file opt-url debugger-link" /> - <span class="header-line opt-line" /> - </hbox> - </toolbar> - <hbox id="optimizations-graph"></hbox> - <vbox id="jit-optimizations-raw-view"></vbox> + class="hidden"> </vbox> </hbox> <!-- JS FlameChart --> <hbox id="js-flamegraph-view" flex="1"> </hbox> <!-- Memory Tree -->
--- a/devtools/client/performance/test/browser.ini +++ b/devtools/client/performance/test/browser.ini @@ -38,17 +38,19 @@ skip-if = true # Bug 1161817 [browser_perf-details-03.js] [browser_perf-details-04.js] [browser_perf-details-05.js] [browser_perf-details-06.js] [browser_perf-details-07.js] [browser_perf-events-calltree.js] [browser_perf-highlighted.js] [browser_perf-jit-view-01.js] +skip-if = true # Bug 1176056 [browser_perf-jit-view-02.js] +skip-if = true # Bug 1176056 [browser_perf-legacy-front-01.js] [browser_perf-legacy-front-02.js] [browser_perf-legacy-front-03.js] [browser_perf-legacy-front-04.js] [browser_perf-legacy-front-05.js] [browser_perf-legacy-front-06.js] [browser_perf-legacy-front-07.js] [browser_perf-legacy-front-08.js]
--- a/devtools/client/performance/test/browser_profiler_tree-view-11.js +++ b/devtools/client/performance/test/browser_profiler_tree-view-11.js @@ -6,17 +6,17 @@ * icon is next to the frame with optimizations */ var { CATEGORY_MASK } = require("devtools/client/performance/modules/global"); function* spawnTest() { let { panel } = yield initPerformance(SIMPLE_URL); let { EVENTS, $, $$, window, PerformanceController } = panel.panelWin; - let { OverviewView, DetailsView, JITOptimizationsView, JsCallTreeView, RecordingsView } = panel.panelWin; + let { OverviewView, DetailsView, JsCallTreeView, RecordingsView } = panel.panelWin; let profilerData = { threads: [gThread] }; Services.prefs.setBoolPref(JIT_PREF, true); Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false); Services.prefs.setBoolPref(INVERT_PREF, false); // Make two recordings, so we have one to switch to later, as the
--- a/devtools/client/performance/test/unit/test_frame-utils-01.js +++ b/devtools/client/performance/test/unit/test_frame-utils-01.js @@ -15,16 +15,19 @@ const CONTENT_LOCATIONS = [ "hello/<.world (http://foo/#bar:123:987)", "hello/<.world (http://foo/:123:987)", "hello/<.world (app://myfxosapp/file.js:100:1)", // Test scripts with port numbers (bug 1164131) "hello/<.world (http://localhost:8888/file.js:100:1)", "hello/<.world (http://localhost:8888/file.js:100)", + // Eval + "hello/<.world (http://localhost:8888/file.js line 65 > eval:1)", + // Occurs when executing an inline script on a root html page with port // (I've never seen it with a column number but check anyway) bug 1164131 "hello/<.world (http://localhost:8888/:1)", "hello/<.world (http://localhost:8888/:100:50)", // bug 1197636 "Native[\"arraycopy(blah)\"] (http://localhost:8888/profiler.html:4)", "Native[\"arraycopy(blah)\"] (http://localhost:8888/profiler.html:4:5)", @@ -67,16 +70,17 @@ add_task(function () { ["hello/<.world", "bar.js", "foo", "http://foo/bar.js", 123, null, "foo", null], ["hello/<.world", "bar.js", "foo", "http://foo/bar.js#baz", 123, 987, "foo", null], ["hello/<.world", "bar.js", "foo", "http://foo/bar.js?myquery=params&search=1", 123, 987, "foo", null], ["hello/<.world", "/", "foo", "http://foo/#bar", 123, 987, "foo", null], ["hello/<.world", "/", "foo", "http://foo/", 123, 987, "foo", null], ["hello/<.world", "file.js", "myfxosapp", "app://myfxosapp/file.js", 100, 1, "myfxosapp", null], ["hello/<.world", "file.js", "localhost:8888", "http://localhost:8888/file.js", 100, 1, "localhost:8888", 8888], ["hello/<.world", "file.js", "localhost:8888", "http://localhost:8888/file.js", 100, null, "localhost:8888", 8888], + ["hello/<.world", "file.js (eval:1)", "localhost:8888", "http://localhost:8888/file.js", 65, null, "localhost:8888", 8888], ["hello/<.world", "/", "localhost:8888", "http://localhost:8888/", 1, null, "localhost:8888", 8888], ["hello/<.world", "/", "localhost:8888", "http://localhost:8888/", 100, 50, "localhost:8888", 8888], ["Native[\"arraycopy(blah)\"]", "profiler.html", "localhost:8888", "http://localhost:8888/profiler.html", 4, null, "localhost:8888", 8888], ["Native[\"arraycopy(blah)\"]", "profiler.html", "localhost:8888", "http://localhost:8888/profiler.html", 4, 5, "localhost:8888", 8888], ]; for (let i = 0; i < PARSED_CONTENT.length; i++) { let parsed = parseLocation.apply(null, CONTENT_LOCATIONS[i]);
--- a/devtools/client/performance/views/details-js-call-tree.js +++ b/devtools/client/performance/views/details-js-call-tree.js @@ -24,24 +24,25 @@ var JsCallTreeView = Heritage.extend(Det initialize: function () { DetailsSubview.initialize.call(this); this._onLink = this._onLink.bind(this); this._onFocus = this._onFocus.bind(this); this.container = $("#js-calltree-view .call-tree-cells-container"); - OptimizationsListView.initialize(); + this.optimizationsElement = $("#jit-optimizations-view"); }, /** * Unbinds events. */ destroy: function () { - OptimizationsListView.destroy(); + ReactDOM.unmountComponentAtNode(this.optimizationsElement); + this.optimizationsElement = null; this.container = null; this.threadNode = null; DetailsSubview.destroy.call(this); }, /** * Method for handling all the set up for rendering a new call tree. * @@ -62,35 +63,58 @@ var JsCallTreeView = Heritage.extend(Det let threadNode = this.threadNode = this._prepareCallTree(profile, interval, options); this._populateCallTree(threadNode, options); if (optimizations) { this.showOptimizations(); } else { this.hideOptimizations(); } - OptimizationsListView.reset(); this.emit(EVENTS.JS_CALL_TREE_RENDERED); }, showOptimizations: function () { - $("#jit-optimizations-view").classList.remove("hidden"); + this.optimizationsElement.classList.remove("hidden"); }, hideOptimizations: function () { - $("#jit-optimizations-view").classList.add("hidden"); + this.optimizationsElement.classList.add("hidden"); }, _onFocus: function (_, treeItem) { - if (PerformanceController.getCurrentRecording().getConfiguration().withJITOptimizations) { - OptimizationsListView.setCurrentFrame(this.threadNode, treeItem.frame); - OptimizationsListView.render(); + let recording = PerformanceController.getCurrentRecording(); + let frameNode = treeItem.frame; + + if (!frameNode) { + console.warn("No frame found!"); + return; } + let frameData = frameNode.getInfo(); + let optimizationSites = frameNode.hasOptimizations() + ? frameNode.getOptimizations().optimizationSites + : []; + + let optimizations = Optimizations({ + frameData, + optimizationSites, + onViewSourceInDebugger: (url, line) => { + gToolbox.viewSourceInDebugger(url, line).then(success => { + if (success) { + this.emit(EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER); + } else { + this.emit(EVENTS.SOURCE_NOT_FOUND_IN_JS_DEBUGGER); + } + }); + } + }); + + ReactDOM.render(optimizations, this.optimizationsElement); + this.emit("focus", treeItem); }, /** * Fired on the "link" event for the call tree in this container. */ _onLink: function (_, treeItem) { let { url, line } = treeItem.frame.getInfo();
deleted file mode 100644 --- a/devtools/client/performance/views/optimizations-list.js +++ /dev/null @@ -1,396 +0,0 @@ -/* 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/. */ -/* import-globals-from ../performance-controller.js */ -/* import-globals-from ../performance-view.js */ -/* globals document */ -"use strict"; - -const URL_LABEL_TOOLTIP = L10N.getStr("table.url.tooltiptext"); -const OPTIMIZATION_FAILURE = L10N.getStr("jit.optimizationFailure"); -const JIT_SAMPLES = L10N.getStr("jit.samples"); -const JIT_EMPTY_TEXT = L10N.getStr("jit.empty"); -const PROPNAME_MAX_LENGTH = 4; - -/** - * View for rendering a list of all optmizations found in a frame. - * The terminology and types used here can be referenced: - * @see devtools/client/performance/modules/logic/jit.js - */ - -var OptimizationsListView = { - - _currentFrame: null, - - /** - * Initialization function called when the tool starts up. - */ - initialize: function () { - this.reset = this.reset.bind(this); - this._onThemeChanged = this._onThemeChanged.bind(this); - - this.el = $("#jit-optimizations-view"); - this.$headerName = $("#jit-optimizations-header .header-function-name"); - this.$headerFile = $("#jit-optimizations-header .header-file"); - this.$headerLine = $("#jit-optimizations-header .header-line"); - - this.tree = new TreeWidget($("#jit-optimizations-raw-view"), { - sorted: false, - emptyText: JIT_EMPTY_TEXT - }); - this.graph = new OptimizationsGraph($("#optimizations-graph")); - this.graph.setTheme(PerformanceController.getTheme()); - - // Start the tree by resetting. - this.reset(); - - PerformanceController.on(EVENTS.THEME_CHANGED, this._onThemeChanged); - }, - - /** - * Destruction function called when the tool cleans up. - */ - destroy: function () { - PerformanceController.off(EVENTS.THEME_CHANGED, this._onThemeChanged); - this.tree = null; - this.$headerName = this.$headerFile = this.$headerLine = this.el = null; - }, - - /** - * Takes a FrameNode, with corresponding optimization data to be displayed - * in the view. - * - * @param {FrameNode} frameNode - */ - setCurrentFrame: function (threadNode, frameNode) { - if (threadNode !== this.getCurrentThread()) { - this._currentThread = threadNode; - } - if (frameNode !== this.getCurrentFrame()) { - this._currentFrame = frameNode; - } - }, - - /** - * Returns the current frame node for this view. - * - * @return {?FrameNode} - */ - getCurrentFrame: function () { - return this._currentFrame; - }, - - /** - * Returns the current thread node for this view. - * - * @return {?ThreadNode} - */ - getCurrentThread: function () { - return this._currentThread; - }, - - /** - * Clears out data in the tree, sets to an empty state, - * and removes current frame. - */ - reset: function () { - this.setCurrentFrame(null, null); - this.clear(); - this.el.classList.add("empty"); - this.emit(EVENTS.OPTIMIZATIONS_RESET); - this.emit(EVENTS.OPTIMIZATIONS_RENDERED, this.getCurrentFrame()); - }, - - /** - * Clears out data in the tree. - */ - clear: function () { - this.tree.clear(); - }, - - /** - * Takes a JITOptimizations object and builds a view containing all attempted - * optimizations for this frame. This view is very verbose and meant for those - * who understand JIT compilers. - */ - render: function () { - let frameNode = this.getCurrentFrame(); - - if (!frameNode) { - this.reset(); - return; - } - - let view = this.tree; - - // Set header information, even if the frame node - // does not have any optimization data - let frameData = frameNode.getInfo(); - this._setHeaders(frameData); - this.clear(); - - // If this frame node does not have optimizations, or if its a meta node in the - // case of only showing content, reset the view. - if (!frameNode.hasOptimizations() || frameNode.isMetaCategory) { - this.reset(); - return; - } - this.el.classList.remove("empty"); - - // An array of sorted OptimizationSites. - let sites = frameNode.getOptimizations().optimizationSites; - - for (let site of sites) { - this._renderSite(view, site, frameData); - } - - this._renderTierGraph(); - - this.emit(EVENTS.OPTIMIZATIONS_RENDERED, this.getCurrentFrame()); - }, - - /** - * Renders the optimization tier graph over time. - */ - _renderTierGraph: function () { - this.graph.render(this.getCurrentThread(), this.getCurrentFrame()); - }, - - /** - * Creates an entry in the tree widget for an optimization site. - */ - _renderSite: function (view, site, frameData) { - let { id, samples, data } = site; - let { types, attempts } = data; - let siteNode = this._createSiteNode(frameData, site); - - // Cast `id` to a string so TreeWidget doesn't think it does not exist - id = id + ""; - - view.add([{ id: id, node: siteNode }]); - - // Add types -- Ion types are the parent, with - // the observed types as children. - view.add([id, { id: `${id}-types`, label: `Types (${types.length})` }]); - this._renderIonType(view, site); - - // Add attempts - view.add([id, { id: `${id}-attempts`, label: `Attempts (${attempts.length})` }]); - for (let i = attempts.length - 1; i >= 0; i--) { - let node = this._createAttemptNode(attempts[i]); - view.add([id, `${id}-attempts`, { node }]); - } - }, - - /** - * Renders all Ion types from an optimization site, with its children - * ObservedTypes. - */ - _renderIonType: function (view, site) { - let { id, data: { types }} = site; - // Cast `id` to a string so TreeWidget doesn't think it does not exist - id = id + ""; - for (let i = 0; i < types.length; i++) { - let ionType = types[i]; - - let ionNode = this._createIonNode(ionType); - view.add([id, `${id}-types`, { id: `${id}-types-${i}`, node: ionNode }]); - for (let observedType of (ionType.typeset || [])) { - let node = this._createObservedTypeNode(observedType); - view.add([id, `${id}-types`, `${id}-types-${i}`, { node }]); - } - } - }, - - /** - * Creates an element for insertion in the raw view for an OptimizationSite. - */ - _createSiteNode: function (frameData, site) { - let node = document.createElement("span"); - let desc = document.createElement("span"); - let line = document.createElement("span"); - let column = document.createElement("span"); - let urlNode = this._createDebuggerLinkNode(frameData.url, site.data.line); - - let attempts = site.getAttempts(); - let lastStrategy = attempts[attempts.length - 1].strategy; - - let propString = ""; - if (site.data.propertyName) { - if (site.data.propertyName.length > PROPNAME_MAX_LENGTH) { - propString = ` (.${site.data.propertyName.substr(0, PROPNAME_MAX_LENGTH)}…)`; - desc.setAttribute("tooltiptext", site.data.propertyName); - } else { - propString = ` (.${site.data.propertyName})`; - } - } - - if (!site.hasSuccessfulOutcome()) { - let icon = document.createElement("span"); - icon.setAttribute("tooltiptext", OPTIMIZATION_FAILURE); - icon.setAttribute("severity", "warning"); - icon.className = "opt-icon"; - node.appendChild(icon); - } - - let sampleString = PluralForm.get(site.samples, JIT_SAMPLES).replace("#1", site.samples); - desc.textContent = `${lastStrategy}${propString} – (${sampleString})`; - line.textContent = site.data.line; - line.className = "opt-line"; - column.textContent = site.data.column; - column.className = "opt-line"; - node.appendChild(desc); - node.appendChild(urlNode); - node.appendChild(line); - node.appendChild(column); - - return node; - }, - - /** - * Creates an element for insertion in the raw view for an IonType. - * - * @see devtools/client/performance/modules/logic/jit.js - * @param {IonType} ionType - * @return {Element} - */ - _createIonNode: function (ionType) { - let node = document.createElement("span"); - node.textContent = `${ionType.site} : ${ionType.mirType}`; - node.className = "opt-ion-type"; - return node; - }, - - /** - * Creates an element for insertion in the raw view for an ObservedType. - * - * @see devtools/client/performance/modules/logic/jit.js - * @param {ObservedType} type - * @return {Element} - */ - _createObservedTypeNode: function (type) { - let node = document.createElement("span"); - let typeNode = document.createElement("span"); - - typeNode.textContent = `${type.keyedBy}` + (type.name ? ` → ${type.name}` : ""); - typeNode.className = "opt-type"; - node.appendChild(typeNode); - - // If we have a type and a location, try to make a - // link to the debugger - if (type.location && type.line) { - let urlNode = this._createDebuggerLinkNode(type.location, type.line); - node.appendChild(urlNode); - } - // Otherwise if we just have a location, it could just - // be a memory location - else if (type.location) { - let locNode = document.createElement("span"); - locNode.textContent = `@${type.location}`; - locNode.className = "opt-url"; - node.appendChild(locNode); - } - - if (type.line) { - let line = document.createElement("span"); - line.textContent = type.line; - line.className = "opt-line"; - node.appendChild(line); - } - return node; - }, - - /** - * Creates an element for insertion in the raw view for an OptimizationAttempt. - * - * @see devtools/client/performance/modules/logic/jit.js - * @param {OptimizationAttempt} attempt - * @return {Element} - */ - _createAttemptNode: function (attempt) { - let node = document.createElement("span"); - let strategyNode = document.createElement("span"); - let outcomeNode = document.createElement("span"); - - strategyNode.textContent = attempt.strategy; - strategyNode.className = "opt-strategy"; - outcomeNode.textContent = attempt.outcome; - outcomeNode.className = "opt-outcome"; - outcomeNode.setAttribute("outcome", - JITOptimizations.isSuccessfulOutcome(attempt.outcome) ? "success" : "failure"); - - node.appendChild(strategyNode); - node.appendChild(outcomeNode); - node.className = "opt-attempt"; - return node; - }, - - /** - * Creates a new element, linking it up to the debugger upon clicking. - * Can also optionally pass in an element to modify it rather than - * creating a new one. - * - * @param {String} url - * @param {Number} line - * @param {?Element} el - * @return {Element} - */ - _createDebuggerLinkNode: function (url, line, el) { - let node = el || document.createElement("span"); - node.className = "opt-url"; - let fileName; - - if (this._isLinkableURL(url)) { - fileName = url.slice(url.lastIndexOf("/") + 1); - node.classList.add("debugger-link"); - node.setAttribute("tooltiptext", URL_LABEL_TOOLTIP + " → " + url); - node.addEventListener("click", () => gToolbox.viewSourceInDebugger(url, line)); - } - fileName = fileName || url || ""; - node.textContent = fileName ? `@${fileName}` : ""; - return node; - }, - - /** - * Updates the headers with the current frame's data. - */ - _setHeaders: function (frameData) { - let isMeta = frameData.isMetaCategory; - let name = isMeta ? frameData.categoryData.label : frameData.functionName; - let url = isMeta ? "" : frameData.url; - let line = isMeta ? "" : frameData.line; - - this.$headerName.textContent = name; - this.$headerLine.textContent = line; - this._createDebuggerLinkNode(url, line, this.$headerFile); - - this.$headerLine.hidden = isMeta; - this.$headerFile.hidden = isMeta; - }, - - /** - * Takes a string and returns a boolean indicating whether or not - * this is a valid url for linking to the debugger. - * - * @param {String} url - * @return {Boolean} - */ - _isLinkableURL: function (url) { - return url && url.indexOf && - (url.indexOf("http") === 0 || - url.indexOf("resource://") === 0 || - url.indexOf("file://") === 0); - }, - - /** - * Called when `devtools.theme` changes. - */ - _onThemeChanged: function (_, theme) { - this.graph.setTheme(theme); - this.graph.refresh({ force: true }); - }, - - toString: () => "[object OptimizationsListView]" -}; - -EventEmitter.decorate(OptimizationsListView);
--- a/devtools/client/shared/components/frame.js +++ b/devtools/client/shared/components/frame.js @@ -21,17 +21,17 @@ const Frame = module.exports = createCla }, propTypes: { // SavedFrame, or an object containing all the required properties. frame: PropTypes.shape({ functionDisplayName: PropTypes.string, source: PropTypes.string.isRequired, line: PropTypes.number.isRequired, - column: PropTypes.number.isRequired, + column: PropTypes.number, }).isRequired, // Clicking on the frame link -- probably should link to the debugger. onClick: PropTypes.func.isRequired, // Option to display a function name before the source link. showFunctionName: PropTypes.bool, // Option to display a host name after the source link. showHost: PropTypes.bool, }, @@ -41,17 +41,17 @@ const Frame = module.exports = createCla const { short, long, host } = getSourceNames(frame.source, UNKNOWN_SOURCE_STRING); let tooltip = `${long}:${frame.line}`; if (frame.column) { tooltip += `:${frame.column}`; } - let sourceString = `${frame.source}:${frame.line}`; + let sourceString = `${long}:${frame.line}`; if (frame.column) { sourceString += `:${frame.column}`; } let onClickTooltipString = L10N.getFormatStr("frame.viewsourceindebugger", sourceString); let fields = [ dom.a({
--- a/devtools/client/shared/components/test/mochitest/chrome.ini +++ b/devtools/client/shared/components/test/mochitest/chrome.ini @@ -1,12 +1,14 @@ [DEFAULT] support-files = head.js +[test_frame_01.html] +[test_frame_02.html] [test_tree_01.html] [test_tree_02.html] [test_tree_03.html] [test_tree_04.html] [test_tree_05.html] [test_tree_06.html] [test_tree_07.html] [test_tree_08.html]
--- a/devtools/client/shared/components/test/mochitest/head.js +++ b/devtools/client/shared/components/test/mochitest/head.js @@ -18,16 +18,69 @@ var DevToolsUtils = require("devtools/sh var { TargetFactory } = require("devtools/client/framework/target"); var { Toolbox } = require("devtools/client/framework/toolbox"); DevToolsUtils.testing = true; var { require: browserRequire } = BrowserLoader("resource://devtools/client/shared/", this); var EXAMPLE_URL = "http://example.com/browser/browser/devtools/shared/test/"; +function forceRender(comp) { + return setState(comp, {}) + .then(() => setState(comp, {})); +} + +// All tests are asynchronous. +SimpleTest.waitForExplicitFinish(); + +function onNextAnimationFrame(fn) { + return () => + requestAnimationFrame(() => + requestAnimationFrame(fn)); +} + +function setState(component, newState) { + var deferred = promise.defer(); + component.setState(newState, onNextAnimationFrame(deferred.resolve)); + return deferred.promise; +} + +function setProps(component, newState) { + var deferred = promise.defer(); + component.setProps(newState, onNextAnimationFrame(deferred.resolve)); + return deferred.promise; +} + +function dumpn(msg) { + dump(`SHARED-COMPONENTS-TEST: ${msg}\n`); +} + +/** + * Tree + */ + +var TEST_TREE_INTERFACE = { + getParent: x => TEST_TREE.parent[x], + getChildren: x => TEST_TREE.children[x], + renderItem: (x, depth, focused, arrow) => "-".repeat(depth) + x + ":" + focused + "\n", + getRoots: () => ["A", "M"], + getKey: x => "key-" + x, + itemHeight: 1, + onExpand: x => TEST_TREE.expanded.add(x), + onCollapse: x => TEST_TREE.expanded.delete(x), + isExpanded: x => TEST_TREE.expanded.has(x), +}; + +function isRenderedTree(actual, expectedDescription, msg) { + const expected = expectedDescription.map(x => x + "\n").join(""); + dumpn(`Expected tree:\n${expected}`); + dumpn(`Actual tree:\n${actual}`); + is(actual, expected, msg); +} + // Encoding of the following tree/forest: // // A // |-- B // | |-- E // | | |-- K // | | `-- L // | |-- F @@ -73,56 +126,31 @@ var TEST_TREE = { L: "E", M: null, N: "M", O: "N" }, expanded: new Set(), }; -var TEST_TREE_INTERFACE = { - getParent: x => TEST_TREE.parent[x], - getChildren: x => TEST_TREE.children[x], - renderItem: (x, depth, focused, arrow) => "-".repeat(depth) + x + ":" + focused + "\n", - getRoots: () => ["A", "M"], - getKey: x => "key-" + x, - itemHeight: 1, - onExpand: x => TEST_TREE.expanded.add(x), - onCollapse: x => TEST_TREE.expanded.delete(x), - isExpanded: x => TEST_TREE.expanded.has(x), -}; - -function forceRender(tree) { - return setState(tree, {}) - .then(() => setState(tree, {})); +/** + * Frame + */ +function checkFrameString (component, file, line, column) { + let el = component.getDOMNode(); + is(el.querySelector(".frame-link-filename").textContent, file); + is(+el.querySelector(".frame-link-line").textContent, +line); + if (column != null) { + is(+el.querySelector(".frame-link-column").textContent, +column); + is(el.querySelectorAll(".frame-link-colon").length, 2); + } else { + is(el.querySelector(".frame-link-column"), null, + "Should not render column when none specified"); + is(el.querySelectorAll(".frame-link-colon").length, 1, + "Should only render one colon when no column specified"); + } } -// All tests are asynchronous. -SimpleTest.waitForExplicitFinish(); - -function onNextAnimationFrame(fn) { - return () => - requestAnimationFrame(() => - requestAnimationFrame(fn)); -} - -function setState(component, newState) { - var deferred = promise.defer(); - component.setState(newState, onNextAnimationFrame(deferred.resolve)); - return deferred.promise; +function checkFrameTooltips (component, mainTooltip, linkTooltip) { + let el = component.getDOMNode(); + is(el.getAttribute("title"), mainTooltip); + is(el.querySelector("a.frame-link-filename").getAttribute("title"), linkTooltip); } - -function setProps(component, newState) { - var deferred = promise.defer(); - component.setProps(newState, onNextAnimationFrame(deferred.resolve)); - return deferred.promise; -} - -function dumpn(msg) { - dump(`MEMORY-TEST: ${msg}\n`); -} - -function isRenderedTree(actual, expectedDescription, msg) { - const expected = expectedDescription.map(x => x + "\n").join(""); - dumpn(`Expected tree:\n${expected}`); - dumpn(`Actual tree:\n${actual}`); - is(actual, expected, msg); -}
new file mode 100644 --- /dev/null +++ b/devtools/client/shared/components/test/mochitest/test_frame_01.html @@ -0,0 +1,89 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test the formatting of the file name, line and columns are correct in frame components, +with optional columns, unknown and non-URL sources. +--> +<head> + <meta charset="utf-8"> + <title>Frame component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> +<pre id="test"> +<script src="head.js" type="application/javascript;version=1.8"></script> +<script type="application/javascript;version=1.8"> +window.onload = Task.async(function* () { + try { + let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom"); + let React = browserRequire("devtools/client/shared/vendor/react"); + let Frame = React.createFactory(browserRequire("devtools/client/shared/components/frame")); + ok(Frame, "Should get Frame"); + let frame; + + // Check when there's a column + frame = ReactDOM.render(Frame({ + frame: { + source: "http://myfile.com/mahscripts.js", + line: 55, + column: 10, + }, + onClick: ()=>{}, + }), window.document.body); + yield forceRender(frame); + checkFrameString(frame, "mahscripts.js", 55, 10); + + // Check when there's no column + frame = ReactDOM.render(Frame({ + frame: { + source: "http://myfile.com/mahscripts.js", + line: 55, + }, + onClick: ()=>{}, + }), window.document.body); + yield forceRender(frame); + checkFrameString(frame, "mahscripts.js", 55); + + // Check when column === 0 + frame = ReactDOM.render(Frame({ + frame: { + source: "http://myfile.com/mahscripts.js", + line: 55, + column: 0, + }, + onClick: ()=>{}, + }), window.document.body); + yield forceRender(frame); + checkFrameString(frame, "mahscripts.js", 55, 0); + + // Check when there's no parseable URL source + frame = ReactDOM.render(Frame({ + frame: { + source: "self-hosted", + line: 1, + }, + onClick: ()=>{}, + }), window.document.body); + yield forceRender(frame); + checkFrameString(frame, "self-hosted",1); + + // Check when there's no source + frame = ReactDOM.render(Frame({ + frame: { + line: 1, + }, + onClick: ()=>{}, + }), window.document.body); + yield forceRender(frame); + checkFrameString(frame, "(unknown)",1); + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } +}); +</script> +</pre> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/devtools/client/shared/components/test/mochitest/test_frame_02.html @@ -0,0 +1,85 @@ +<!DOCTYPE HTML> +<html> +<!-- +Test the formatting of the tooltips in the frame component. +--> +<head> + <meta charset="utf-8"> + <title>Frame component test</title> + <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"> +</head> +<body> +<pre id="test"> +<script src="head.js" type="application/javascript;version=1.8"></script> +<script type="application/javascript;version=1.8"> +window.onload = Task.async(function* () { + try { + let ReactDOM = browserRequire("devtools/client/shared/vendor/react-dom"); + let React = browserRequire("devtools/client/shared/vendor/react"); + let Frame = React.createFactory(browserRequire("devtools/client/shared/components/frame")); + ok(Frame, "Should get Frame"); + let frame; + + // Check when there's a column + frame = ReactDOM.render(Frame({ + frame: { + source: "http://myfile.com/mahscripts.js", + line: 55, + column: 10, + }, + onClick: ()=>{}, + }), window.document.body); + yield forceRender(frame); + checkFrameTooltips(frame, + "http://myfile.com/mahscripts.js:55:10", + "View source in Debugger → http://myfile.com/mahscripts.js:55:10"); + + // Check when there's no column + frame = ReactDOM.render(Frame({ + frame: { + source: "http://myfile.com/mahscripts.js", + line: 55, + }, + onClick: ()=>{}, + }), window.document.body); + yield forceRender(frame); + checkFrameTooltips(frame, + "http://myfile.com/mahscripts.js:55", + "View source in Debugger → http://myfile.com/mahscripts.js:55"); + + // Check when there's no parseable URL source + frame = ReactDOM.render(Frame({ + frame: { + source: "self-hosted", + line: 1, + }, + onClick: ()=>{}, + }), window.document.body); + yield forceRender(frame); + checkFrameTooltips(frame, + "self-hosted:1", + "View source in Debugger → self-hosted:1"); + + // Check when there's no source + frame = ReactDOM.render(Frame({ + frame: { + line: 1, + }, + onClick: ()=>{}, + }), window.document.body); + yield forceRender(frame); + checkFrameTooltips(frame, + "(unknown):1", + "View source in Debugger → (unknown):1"); + + } catch(e) { + ok(false, "Got an error: " + DevToolsUtils.safeErrorString(e)); + } finally { + SimpleTest.finish(); + } +}); +</script> +</pre> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/devtools/client/themes/components-frame.css @@ -0,0 +1,46 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* 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/. */ + +/** + * Frame Component + * Styles for React component at `devtools/client/shared/components/frame.js` + */ + +.frame-link { + margin-left: 7px; +} + +.focused .frame-link-filename, +.focused .frame-link-column, +.focused .frame-link-line, +.focused .frame-link-host, +.focused .frame-link-colon { + color: var(--theme-selection-color); +} + +.frame-link .frame-link-filename { + color: var(--theme-highlight-blue); + cursor: pointer; +} + +.frame-link .frame-link-filename:hover { + text-decoration: underline; +} + +.frame-link .frame-link-host { + margin-inline-start: 5px; + font-size: 90%; + color: var(--theme-content-color2); +} + +.frame-link .frame-link-function-display-name { + margin-inline-end: 5px; +} + +.frame-link .frame-link-column, +.frame-link .frame-link-line, +.frame-link .frame-link-colon { + color: var(--theme-highlight-orange); +}
new file mode 100644 --- /dev/null +++ b/devtools/client/themes/jit-optimizations.css @@ -0,0 +1,143 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* 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/. */ + +/** + * JIT View + */ + +#jit-optimizations-view { + width: 350px; + overflow-x: auto; + min-width: 200px; + white-space: nowrap; + --jit-tree-row-height: 14; + --jit-tree-header-height: 16; +} + +#jit-optimizations-view > div { + flex: 1; +} + +#jit-optimizations-view div { + display: block; +} + +.tree { + /** + * Flexing to fill out remaining vertical space. + */ + flex: 1; + overflow-y: auto; + height: 100%; + background-color: var(--theme-body-background); +} + +.optimization-header { + height: var(--jit-tree-header-height); + padding: 2px 5px; + background-color: var(--theme-tab-toolbar-background); +} + +#jit-optimizations-view .header-title { + font-weight: bold; + padding-right: 7px; +} + +.tree-node { + height: var(--jit-tree-row-height); + clear: both; +} + +.tree-node button { + display: none; +} + +#jit-optimizations-view .optimization-tree-item { + display: flex; +} + +#jit-optimizations-view .arrow, +#jit-optimizations-view .optimization-site, +#jit-optimizations-view .optimization-attempts, +#jit-optimizations-view .optimization-attempt, +#jit-optimizations-view .optimization-types, +#jit-optimizations-view .optimization-ion-type, +#jit-optimizations-view .optimization-observed-type { + float: left; +} + +#jit-optimizations-view .optimization-outcome.success { + color: var(--theme-highlight-green); +} +#jit-optimizations-view .optimization-outcome.failure { + color: var(--theme-highlight-red); +} + +.opt-icon::before { + content: ""; + background-image: url(chrome://devtools/skin/images/webconsole.svg); + background-repeat: no-repeat; + background-size: 72px 60px; + /* show grey "i" bubble by default */ + background-position: -36px -36px; + width: 10px; + height: 10px; + display: inline-block; + + max-height: 12px; +} + +#jit-optimizations-view .opt-icon { + float: left; +} + +#jit-optimizations-view .opt-icon::before { + margin: 1px 6px 0 0; +} + +.theme-light .opt-icon::before { + background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons); +} +.opt-icon.warning::before { + background-position: -24px -24px; +} + +/* Frame Component */ +.focused .frame-link-filename, +.focused .frame-link-column, +.focused .frame-link-line, +.focused .frame-link-host, +.focused .frame-link-colon { + color: var(--theme-selection-color); +} + +.frame-link { + margin-left: 7px; +} + +.frame-link-filename { + color: var(--theme-highlight-blue); + cursor: pointer; +} + +.frame-link-filename:hover { + text-decoration: underline; +} + +.frame-link-column, +.frame-link-line, +.frame-link-colon { + color: var(--theme-highlight-orange); +} + +.frame-link-host { + margin-inline-start: 5px; + font-size: 90%; + color: var(--theme-content-color2); +} + +.frame-link-function-display-name { + margin-inline-end: 5px; +}
--- a/devtools/client/themes/memory.css +++ b/devtools/client/themes/memory.css @@ -460,48 +460,15 @@ html, body, #app, #memory-tool { .separator, .not-available, .heap-tree-item-address { opacity: .5; margin-left: .5em; margin-right: .5em; } -.focused .frame-link-filename, -.focused .frame-link-column, -.focused .frame-link-line, -.focused .frame-link-host, -.focused .frame-link-colon { - color: var(--theme-selection-color); -} - -.frame-link-filename { - color: var(--theme-highlight-blue); - cursor: pointer; -} - -.frame-link-filename:hover { - text-decoration: underline; -} - -.frame-link-column, -.frame-link-line, -.frame-link-colon { - color: var(--theme-highlight-orange); -} - -.frame-link-host { - margin-inline-start: 5px; - font-size: 90%; - color: var(--theme-content-color2); -} - -.frame-link-function-display-name { - margin-inline-end: 5px; -} - .no-allocation-stacks { border-color: var(--theme-splitter-color); border-style: solid; border-width: 0px 0px 1px 0px; text-align: center; padding: 5px; }
--- a/devtools/client/themes/performance.css +++ b/devtools/client/themes/performance.css @@ -267,16 +267,18 @@ .call-tree-header { background-color: var(--theme-tab-toolbar-background); } .call-tree-item .call-tree-cell, .call-tree-item .call-tree-cell[type=function] description { -moz-user-select: text; + /* so that optimizations view doesn't break the lines in call tree */ + white-space: nowrap; } .call-tree-item .call-tree-cell::-moz-selection, .call-tree-item .call-tree-cell[type=function] description::-moz-selection { background-color: var(--theme-highlight-orange); } .call-tree-item:last-child { @@ -605,163 +607,16 @@ menuitem.marker-color-graphs-red:before, } menuitem.marker-color-graphs-grey:before, .marker-color-graphs-grey{ background-color: var(--theme-graphs-grey); border-color: var(--theme-graphs-grey); } /** - * JIT View - */ - -#jit-optimizations-view { - width: 350px; - overflow-x: hidden; - overflow-y: auto; - min-width: 200px; -} - -#optimizations-graph { - height: 30px; -} - -#jit-optimizations-view.empty #optimizations-graph { - display: none !important; -} - -/* override default styles for tree widget */ -#jit-optimizations-view .tree-widget-empty-text { - font-size: inherit; - padding: 0px; - margin: 8px; -} - -#jit-optimizations-view:not(.empty) .tree-widget-empty-text { - display: none; -} - -#jit-optimizations-toolbar { - height: 18px; - min-height: 0px; /* override .devtools-toolbar min-height */ -} - -.jit-optimizations-title { - margin: 0px 4px; - font-weight: 600; -} - -#jit-optimizations-raw-view { - font-size: 90%; -} - -/* override default .tree-widget-item line-height */ -#jit-optimizations-raw-view .tree-widget-item { - line-height: 20px !important; - display: block; - overflow: hidden; -} - -#jit-optimizations-raw-view .tree-widget-item[level="1"] { - font-weight: 600; -} - -#jit-optimizations-view .opt-outcome::before { - content: "→"; - margin: 4px 0px; - color: var(--theme-body-color); -} -#jit-optimizations-view .theme-selected .opt-outcome::before { - color: var(--theme-selection-color); -} - -#jit-optimizations-view .tree-widget-item:not(.theme-selected) .opt-outcome[outcome=success] { - color: var(--theme-highlight-green); -} -#jit-optimizations-view .tree-widget-item:not(.theme-selected) .opt-outcome[outcome=failure] { - color: var(--theme-highlight-red); -} -#jit-optimizations-view .tree-widget-container { - -moz-margin-end: 0px; -} -#jit-optimizations-view .tree-widget-container > li, -#jit-optimizations-view .tree-widget-children > li { - overflow: hidden; -} - -.opt-line::before { - content: ":"; - color: var(--theme-highlight-orange); -} -.theme-selected .opt-line::before { - color: var(--theme-selection-color); -} -.opt-line.header-line::before { - color: var(--theme-body-color); -} -#jit-optimizations-view.empty .opt-line.header-line::before { - display: none; -} - -.opt-url { - -moz-margin-start: 4px !important; -} -.opt-url:hover { - text-decoration: underline; -} -.opt-url.debugger-link { - cursor: pointer; -} - -.opt-icon::before { - content: ""; - background-image: url(chrome://devtools/skin/images/webconsole.svg); - background-repeat: no-repeat; - background-size: 72px 60px; - /* show grey "i" bubble by default */ - background-position: -36px -36px; - width: 12px; - height: 12px; - display: inline-block; - - max-height: 12px; -} - -#jit-optimizations-view .opt-icon::before { - margin: 5px 6px 0 0; -} -description.opt-icon { - margin: 0px 0px 0px 0px; -} -description.opt-icon::before { - margin: 1px 4px 0px 0px; -} -.theme-light .opt-icon::before { - background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons); -} -.opt-icon[severity=warning]::before { - background-position: -24px -24px; -} - -ul.frames-list { - list-style-type: none; - padding: 0px; - margin: 0px; -} - -ul.frames-list li { - cursor: pointer; -} - -ul.frames-list li.selected { - background-color: var(--theme-selection-background); - color: var(--theme-selection-color); -} - -/** * Configurable Options * * Elements can be tagged with a class and visibility is controlled via a * preference being applied or removed. */ /** * devtools.performance.ui.experimental @@ -782,8 +637,37 @@ menuitem.experimental-option::before { .theme-light menuitem.experimental-option::before { background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons); } #performance-options-menupopup:not(.experimental-enabled) .experimental-option, #performance-options-menupopup:not(.experimental-enabled) .experimental-option::before { display: none; } + +.opt-icon::before { + content: ""; + background-image: url(chrome://devtools/skin/images/webconsole.svg); + background-repeat: no-repeat; + background-size: 72px 60px; + /* show grey "i" bubble by default */ + background-position: -36px -36px; + width: 10px; + height: 10px; + display: inline-block; + + max-height: 12px; +} + +.theme-light .opt-icon::before { + background-image: url(chrome://devtools/skin/images/webconsole.svg#light-icons); +} +.opt-icon.warning::before { + background-position: -24px -24px; +} + +/* for call tree */ +description.opt-icon { + margin: 0px 0px 0px 0px; +} +description.opt-icon::before { + margin: 1px 4px 0px 0px; +}
--- a/devtools/client/webconsole/webconsole.js +++ b/devtools/client/webconsole/webconsole.js @@ -192,18 +192,17 @@ const PREF_INPUT_HISTORY_COUNT = "devtoo * * The WebConsoleFrame is responsible for the actual Web Console UI * implementation. * * @constructor * @param object webConsoleOwner * The WebConsole owner object. */ -function WebConsoleFrame(webConsoleOwner) -{ +function WebConsoleFrame(webConsoleOwner) { this.owner = webConsoleOwner; this.hudId = this.owner.hudId; this.window = this.owner.iframeWindow; this._repeatNodes = {}; this._outputQueue = []; this._itemDestroyQueue = []; this._pruneCategoriesQueue = {}; @@ -394,18 +393,17 @@ WebConsoleFrame.prototype = { "NetworkMonitor.saveRequestAndResponseBodies": newValue, }; // Make sure the web console client connection is established first. this.webConsoleClient.setPreferences(toSet, response => { if (!response.error) { this._saveRequestAndResponseBodies = newValue; deferred.resolve(response); - } - else { + } else { deferred.reject(response.error); } }); return deferred.promise; }, /** @@ -420,18 +418,17 @@ WebConsoleFrame.prototype = { return this.owner._browserConsole || Services.prefs.getBoolPref(PREF_PERSISTLOG); }, /** * Initialize the WebConsoleFrame instance. * @return object * A promise object that resolves once the frame is ready to use. */ - init: function() - { + init: function() { this._initUI(); let connectionInited = this._initConnection(); // Don't reject if the history fails to load for some reason. // This would be fine, the panel will just start with empty history. let allReady = this.jsterm.historyLoaded.catch(() => {}).then(() => { return connectionInited; }); @@ -451,18 +448,17 @@ WebConsoleFrame.prototype = { /** * Connect to the server using the remote debugging protocol. * * @private * @return object * A promise object that is resolved/reject based on the connection * result. */ - _initConnection: function WCF__initConnection() - { + _initConnection: function WCF__initConnection() { if (this._initDefer) { return this._initDefer.promise; } this._initDefer = promise.defer(); this.proxy = new WebConsoleConnectionProxy(this, this.owner.target); this.proxy.connect().then(() => { // on success @@ -476,18 +472,17 @@ WebConsoleFrame.prototype = { return this._initDefer.promise; }, /** * Find the Web Console UI elements and setup event listeners as needed. * @private */ - _initUI: function WCF__initUI() - { + _initUI: function WCF__initUI() { this.document = this.window.document; this.rootElement = this.document.documentElement; this._initDefaultFilterPrefs(); // Register the controller to handle "select all" properly. this._commandController = new CommandController(this); this.window.controllers.insertControllerAt(0, this._commandController); @@ -585,27 +580,25 @@ WebConsoleFrame.prototype = { this.outputNode.style.width = this.outputWrapper.clientWidth + "px"; }, /** * Sets the focus to JavaScript input field when the web console tab is * selected or when there is a split console present. * @private */ - _onPanelSelected: function WCF__onPanelSelected(evt, id) - { + _onPanelSelected: function WCF__onPanelSelected(evt, id) { this.jsterm.inputNode.focus(); }, /** * Initialize the default filter preferences. * @private */ - _initDefaultFilterPrefs: function WCF__initDefaultFilterPrefs() - { + _initDefaultFilterPrefs: function WCF__initDefaultFilterPrefs() { let prefs = ["network", "networkinfo", "csserror", "cssparser", "csslog", "exception", "jswarn", "jslog", "error", "info", "warn", "log", "secerror", "secwarn", "netwarn", "netxhr", "sharedworkers", "serviceworkers", "windowlessworkers", "servererror", "serverwarn", "serverinfo", "serverlog"]; for (let pref of prefs) { this.filterPrefs[pref] = Services.prefs.getBoolPref( @@ -617,18 +610,17 @@ WebConsoleFrame.prototype = { * Attach / detach reflow listeners depending on the checked status * of the `CSS > Log` menuitem. * * @param function [callback=null] * Optional function to invoke when the listener has been * added/removed. */ _updateReflowActivityListener: - function WCF__updateReflowActivityListener(callback) - { + function WCF__updateReflowActivityListener(callback) { if (this.webConsoleClient) { let pref = this._filterPrefsPrefix + "csslog"; if (Services.prefs.getBoolPref(pref)) { this.webConsoleClient.startListeners(["ReflowActivity"], callback); } else { this.webConsoleClient.stopListeners(["ReflowActivity"], callback); } } @@ -639,18 +631,17 @@ WebConsoleFrame.prototype = { * preferences. If the user isn't interested in the server logs at * all the listener is not registered. * * @param function [callback=null] * Optional function to invoke when the listener has been * added/removed. */ _updateServerLoggingListener: - function WCF__updateServerLoggingListener(callback) - { + function WCF__updateServerLoggingListener(callback) { if (!this.webConsoleClient) { return; } let startListener = false; let prefs = ["servererror", "serverwarn", "serverinfo", "serverlog"]; for (let i = 0; i < prefs.length; i++) { if (this.filterPrefs[prefs[i]]) { @@ -665,18 +656,17 @@ WebConsoleFrame.prototype = { this.webConsoleClient.stopListeners(["ServerLogging"], callback); } }, /** * Sets the events for the filter input field. * @private */ - _setFilterTextBoxEvents: function WCF__setFilterTextBoxEvents() - { + _setFilterTextBoxEvents: function WCF__setFilterTextBoxEvents() { let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); let timerEvent = this.adjustVisibilityOnSearchStringChange.bind(this); let onChange = function _onChange() { // To improve responsiveness, we let the user finish typing before we // perform the search. timer.cancel(); timer.initWithCallback(timerEvent, SEARCH_DELAY, @@ -693,18 +683,17 @@ WebConsoleFrame.prototype = { * @private * @param nsIDOMNode aParent * The node to which the filter button should be appended. * @param object aDescriptor * A descriptor that contains info about the button. Contains "name", * "category", and "prefKey" properties, and optionally a "severities" * property. */ - _initFilterButtons: function WCF__initFilterButtons() - { + _initFilterButtons: function WCF__initFilterButtons() { let categories = this.document .querySelectorAll(".webconsole-filter-button[category]"); Array.forEach(categories, function(button) { button.addEventListener("contextmenu", (event) => { button.open = true; }, false); button.addEventListener("click", this._toggleFilter, false); @@ -746,64 +735,60 @@ WebConsoleFrame.prototype = { /** * Increase, decrease or reset the font size. * * @param string size * The size of the font change. Accepted values are "+" and "-". * An unmatched size assumes a font reset. */ - changeFontSize: function WCF_changeFontSize(size) - { + changeFontSize: function WCF_changeFontSize(size) { let fontSize = this.window .getComputedStyle(this.outputNode, null) .getPropertyValue("font-size").replace("px", ""); if (this.outputNode.style.fontSize) { fontSize = this.outputNode.style.fontSize.replace("px", ""); } if (size == "+" || size == "-") { fontSize = parseInt(fontSize, 10); if (size == "+") { fontSize += 1; - } - else { + } else { fontSize -= 1; } if (fontSize < MIN_FONT_SIZE) { fontSize = MIN_FONT_SIZE; } Services.prefs.setIntPref("devtools.webconsole.fontSize", fontSize); fontSize = fontSize + "px"; this.completeNode.style.fontSize = fontSize; this.inputNode.style.fontSize = fontSize; this.outputNode.style.fontSize = fontSize; - } - else { + } else { this.completeNode.style.fontSize = ""; this.inputNode.style.fontSize = ""; this.outputNode.style.fontSize = ""; Services.prefs.clearUserPref("devtools.webconsole.fontSize"); } this._updateCharSize(); }, /** * Calculates the width and height of a single character of the input box. * This will be used in opening the popup at the correct offset. * * @private */ - _updateCharSize: function WCF__updateCharSize() - { + _updateCharSize: function WCF__updateCharSize() { let doc = this.document; let tempLabel = doc.createElementNS(XHTML_NS, "span"); let style = tempLabel.style; style.position = "fixed"; style.padding = "0"; style.margin = "0"; style.width = "auto"; style.color = "transparent"; @@ -821,18 +806,17 @@ WebConsoleFrame.prototype = { /** * The event handler that is called whenever a user switches a filter on or * off. * * @private * @param nsIDOMEvent event * The event that triggered the filter change. */ - _toggleFilter: function WCF__toggleFilter(event) - { + _toggleFilter: function WCF__toggleFilter(event) { let target = event.target; let tagName = target.tagName; // Prevent toggle if generated from a contextmenu event (right click) let isRightClick = (event.button === 2); // right click is button 2; if (tagName != event.currentTarget.tagName || isRightClick) { return; } @@ -923,35 +907,33 @@ WebConsoleFrame.prototype = { * Set the menu attributes for a specific toggle button. * * @private * @param XULElement target * Button with drop down items to be toggled. * @param boolean state * True if the menu item is being toggled on, and false otherwise. */ - _setMenuState: function WCF__setMenuState(target, state) - { + _setMenuState: function WCF__setMenuState(target, state) { let menuItems = target.querySelectorAll("menuitem"); Array.forEach(menuItems, (item) => { item.setAttribute("checked", state); let prefKey = item.getAttribute("prefKey"); this.setFilterState(prefKey, state); }); }, /** * Set the filter state for a specific toggle button. * * @param string toggleType * @param boolean state * @returns void */ - setFilterState: function WCF_setFilterState(toggleType, state) - { + setFilterState: function WCF_setFilterState(toggleType, state) { this.filterPrefs[toggleType] = state; this.adjustVisibilityForMessageType(toggleType, state); Services.prefs.setBoolPref(this._filterPrefsPrefix + toggleType, state); if (this._updateListenersTimeout) { Timers.clearTimeout(this._updateListenersTimeout); } @@ -961,18 +943,17 @@ WebConsoleFrame.prototype = { }, /** * Get the filter state for a specific toggle button. * * @param string toggleType * @returns boolean */ - getFilterState: function WCF_getFilterState(toggleType) - { + getFilterState: function WCF_getFilterState(toggleType) { return this.filterPrefs[toggleType]; }, /** * Called when a logging filter changes. Allows to stop/start * listeners according to the current filter state. */ _onUpdateListeners: function() { @@ -984,18 +965,17 @@ WebConsoleFrame.prototype = { * Check that the passed string matches the filter arguments. * * @param String str * to search for filter words in. * @param String filter * is a string containing all of the words to filter on. * @returns boolean */ - stringMatchesFilters: function WCF_stringMatchesFilters(str, filter) - { + stringMatchesFilters: function WCF_stringMatchesFilters(str, filter) { if (!filter || !str) { return true; } let searchStr = str.toLowerCase(); let filterStrings = filter.toLowerCase().split(/\s+/); return !filterStrings.some(function (f) { return searchStr.indexOf(f) == -1; @@ -1010,18 +990,17 @@ WebConsoleFrame.prototype = { * The preference key for the message type being filtered: one of the * values in the MESSAGE_PREFERENCE_KEYS table. * @param boolean state * True if the filter named by @messageType is being turned on; false * otherwise. * @returns void */ adjustVisibilityForMessageType: - function WCF_adjustVisibilityForMessageType(prefKey, state) - { + function WCF_adjustVisibilityForMessageType(prefKey, state) { let outputNode = this.outputNode; let doc = this.document; // Look for message nodes (".message") with the given preference key // (filter="error", filter="cssparser", etc.) and add or remove the // "filtered-by-type" class, which turns on or off the display. let attribute = WORKERTYPES_PREFKEYS.indexOf(prefKey) == -1 @@ -1030,60 +1009,56 @@ WebConsoleFrame.prototype = { let xpath = ".//*[contains(@class, 'message') and " + "@" + attribute + "='" + prefKey + "']"; let result = doc.evaluate(xpath, outputNode, null, Ci.nsIDOMXPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null); for (let i = 0; i < result.snapshotLength; i++) { let node = result.snapshotItem(i); if (state) { node.classList.remove("filtered-by-type"); - } - else { + } else { node.classList.add("filtered-by-type"); } } }, /** * Turns the display of log nodes on and off appropriately to reflect the * adjustment of the search string. */ adjustVisibilityOnSearchStringChange: - function WCF_adjustVisibilityOnSearchStringChange() - { + function WCF_adjustVisibilityOnSearchStringChange() { let nodes = this.outputNode.getElementsByClassName("message"); let searchString = this.filterBox.value; for (let i = 0, n = nodes.length; i < n; ++i) { let node = nodes[i]; // hide nodes that match the strings let text = node.textContent; // if the text matches the words in aSearchString... if (this.stringMatchesFilters(text, searchString)) { node.classList.remove("filtered-by-string"); - } - else { + } else { node.classList.add("filtered-by-string"); } } }, /** * Applies the user's filters to a newly-created message node via CSS * classes. * * @param nsIDOMNode node * The newly-created message node. * @return boolean * True if the message was filtered or false otherwise. */ - filterMessageNode: function WCF_filterMessageNode(node) - { + filterMessageNode: function WCF_filterMessageNode(node) { let isFiltered = false; // Filter by the message type. let prefKey = MESSAGE_PREFERENCE_KEYS[node.category][node.severity]; if (prefKey && !this.getFilterState(prefKey)) { // The node is filtered by type. node.classList.add("filtered-by-type"); isFiltered = true; @@ -1117,18 +1092,17 @@ WebConsoleFrame.prototype = { * Increment the number of repeats of original. * * @param nsIDOMNode original * The Original Node. The one being merged into. * @param nsIDOMNode filtered * The node being filtered out because it is repeated. */ mergeFilteredMessageNode: - function WCF_mergeFilteredMessageNode(original, filtered) - { + function WCF_mergeFilteredMessageNode(original, filtered) { let repeatNode = original.getElementsByClassName("message-repeats")[0]; if (!repeatNode) { return; // no repeat node, return early. } let occurrences = parseInt(repeatNode.getAttribute("value")) + 1; repeatNode.setAttribute("value", occurrences); repeatNode.textContent = occurrences; @@ -1142,37 +1116,35 @@ WebConsoleFrame.prototype = { * * @private * @param nsIDOMNode node * The message node to be filtered or not. * @returns nsIDOMNode|null * Returns the duplicate node if the message was filtered, null * otherwise. */ - _filterRepeatedMessage: function WCF__filterRepeatedMessage(node) - { + _filterRepeatedMessage: function WCF__filterRepeatedMessage(node) { let repeatNode = node.getElementsByClassName("message-repeats")[0]; if (!repeatNode) { return null; } let uid = repeatNode._uid; let dupeNode = null; if (node.category == CATEGORY_CSS || node.category == CATEGORY_SECURITY) { dupeNode = this._repeatNodes[uid]; if (!dupeNode) { this._repeatNodes[uid] = node; } - } - else if ((node.category == CATEGORY_WEBDEV || - node.category == CATEGORY_JS) && - node.category != CATEGORY_NETWORK && - !node.classList.contains("inlined-variables-view")) { + } else if ((node.category == CATEGORY_WEBDEV || + node.category == CATEGORY_JS) && + node.category != CATEGORY_NETWORK && + !node.classList.contains("inlined-variables-view")) { let lastMessage = this.outputNode.lastChild; if (!lastMessage) { return null; } let lastRepeatNode = lastMessage.getElementsByClassName("message-repeats")[0]; if (lastRepeatNode && lastRepeatNode._uid == uid) { dupeNode = lastMessage; @@ -1190,18 +1162,17 @@ WebConsoleFrame.prototype = { /** * Display cached messages that may have been collected before the UI is * displayed. * * @param array remoteMessages * Array of cached messages coming from the remote Web Console * content instance. */ - displayCachedMessages: function WCF_displayCachedMessages(remoteMessages) - { + displayCachedMessages: function WCF_displayCachedMessages(remoteMessages) { if (!remoteMessages.length) { return; } remoteMessages.forEach(function(message) { switch (message._type) { case "PageError": { let category = Utils.categoryForScriptError(message); @@ -1227,18 +1198,17 @@ WebConsoleFrame.prototype = { * Logs a message to the Web Console that originates from the Web Console * server. * * @param object message * The message received from the server. * @return nsIDOMElement|null * The message element to display in the Web Console output. */ - logConsoleAPIMessage: function WCF_logConsoleAPIMessage(message) - { + logConsoleAPIMessage: function WCF_logConsoleAPIMessage(message) { let body = null; let clipboardText = null; let sourceURL = message.filename; let sourceLine = message.lineNumber; let level = message.level; let args = message.arguments; let objectActors = new Set(); let node = null; @@ -1395,31 +1365,29 @@ WebConsoleFrame.prototype = { /** * Handle ConsoleAPICall objects received from the server. This method outputs * the window.console API call. * * @param object message * The console API message received from the server. */ - handleConsoleAPICall: function WCF_handleConsoleAPICall(message) - { + handleConsoleAPICall: function WCF_handleConsoleAPICall(message) { this.outputMessage(CATEGORY_WEBDEV, this.logConsoleAPIMessage, [message]); }, /** * Reports an error in the page source, either JavaScript or CSS. * * @param nsIScriptError scriptError * The error message to report. * @return nsIDOMElement|undefined * The message element to display in the Web Console output. */ - reportPageError: function WCF_reportPageError(category, scriptError) - { + reportPageError: function WCF_reportPageError(category, scriptError) { // Warnings and legacy strict errors become warnings; other types become // errors. let severity = 'error'; if (scriptError.warning || scriptError.strict) { severity = 'warning'; } else if (scriptError.info) { severity = 'log'; } @@ -1492,47 +1460,44 @@ WebConsoleFrame.prototype = { /** * Handle PageError objects received from the server. This method outputs the * given error. * * @param nsIScriptError pageError * The error received from the server. */ - handlePageError: function WCF_handlePageError(pageError) - { + handlePageError: function WCF_handlePageError(pageError) { let category = Utils.categoryForScriptError(pageError); this.outputMessage(category, this.reportPageError, [category, pageError]); }, /** * Handle log messages received from the server. This method outputs the given * message. * * @param object packet * The message packet received from the server. */ - handleLogMessage: function WCF_handleLogMessage(packet) - { + handleLogMessage: function WCF_handleLogMessage(packet) { if (packet.message) { this.outputMessage(CATEGORY_JS, this._reportLogMessage, [packet]); } }, /** * Display log messages received from the server. * * @private * @param object packet * The message packet received from the server. * @return nsIDOMElement * The message element to render for the given log message. */ - _reportLogMessage: function WCF__reportLogMessage(packet) - { + _reportLogMessage: function WCF__reportLogMessage(packet) { let msg = packet.message; if (msg.type && msg.type == "longString") { msg = msg.initial; } let node = this.createMessageNode(CATEGORY_JS, SEVERITY_LOG, msg, null, null, null, null, packet.timeStamp); if (WebConsoleUtils.isActorGrip(packet.message)) { node._objectActors = new Set([packet.message.actor]); @@ -1543,18 +1508,17 @@ WebConsoleFrame.prototype = { /** * Log network event. * * @param object networkInfo * The network request information to log. * @return nsIDOMElement|null * The message element to display in the Web Console output. */ - logNetEvent: function(networkInfo) - { + logNetEvent: function(networkInfo) { let actorId = networkInfo.actor; let request = networkInfo.request; let clipboardText = request.method + " " + request.url; let severity = SEVERITY_LOG; if (networkInfo.isXHR) { clipboardText = request.method + " XHR " + request.url; severity = SEVERITY_INFO; } @@ -1626,18 +1590,17 @@ WebConsoleFrame.prototype = { }, /** * Create a mixed content warning Node. * * @param linkNode * Parent to the requested urlNode. */ - makeMixedContentNode: function WCF_makeMixedContentNode(linkNode) - { + makeMixedContentNode: function WCF_makeMixedContentNode(linkNode) { let mixedContentWarning = "[" + l10n.getStr("webConsoleMixedContentWarning") + "]"; // Mixed content warning message links to a Learn More page let mixedContentWarningNode = this.document.createElementNS(XHTML_NS, "a"); mixedContentWarningNode.title = MIXED_CONTENT_LEARN_MORE; mixedContentWarningNode.href = MIXED_CONTENT_LEARN_MORE; mixedContentWarningNode.className = "learn-more-link"; mixedContentWarningNode.textContent = mixedContentWarning; @@ -1655,18 +1618,17 @@ WebConsoleFrame.prototype = { * Adds a more info link node to messages based on the nsIScriptError object * that we need to report to the console * * @param node * The node to which we will be adding the more info link node * @param scriptError * The script error object that we are reporting to the console */ - addMoreInfoLink: function WCF_addMoreInfoLink(node, scriptError) - { + addMoreInfoLink: function WCF_addMoreInfoLink(node, scriptError) { let url; switch (scriptError.category) { case "Insecure Password Field": url = INSECURE_PASSWORDS_LEARN_MORE; break; case "Mixed Content Message": case "Mixed Content Blocker": url = MIXED_CONTENT_LEARN_MORE; @@ -1699,18 +1661,17 @@ WebConsoleFrame.prototype = { * @param node * The node to which we will be adding a clickable warning node. * @param url * The url which points to the page where the user can learn more * about security issues associated with the specific message that's * being logged. */ addLearnMoreWarningNode: - function WCF_addLearnMoreWarningNode(node, url) - { + function WCF_addLearnMoreWarningNode(node, url) { let moreInfoLabel = "[" + l10n.getStr("webConsoleMoreInfoLabel") + "]"; let warningNode = this.document.createElementNS(XHTML_NS, "a"); warningNode.title = url; warningNode.href = url; warningNode.draggable = false; warningNode.textContent = moreInfoLabel; warningNode.className = "learn-more-link"; @@ -1726,18 +1687,17 @@ WebConsoleFrame.prototype = { /** * Log file activity. * * @param string fileURI * The file URI that was loaded. * @return nsIDOMElement|undefined * The message element to display in the Web Console output. */ - logFileActivity: function WCF_logFileActivity(fileURI) - { + logFileActivity: function WCF_logFileActivity(fileURI) { let urlNode = this.document.createElementNS(XHTML_NS, "a"); urlNode.setAttribute("title", fileURI); urlNode.className = "url"; urlNode.textContent = fileURI; urlNode.draggable = false; urlNode.href = fileURI; let outputNode = this.createMessageNode(CATEGORY_NETWORK, SEVERITY_LOG, @@ -1751,29 +1711,27 @@ WebConsoleFrame.prototype = { }, /** * Handle the file activity messages coming from the remote Web Console. * * @param string fileURI * The file URI that was requested. */ - handleFileActivity: function WCF_handleFileActivity(fileURI) - { + handleFileActivity: function WCF_handleFileActivity(fileURI) { this.outputMessage(CATEGORY_NETWORK, this.logFileActivity, [fileURI]); }, /** * Handle the reflow activity messages coming from the remote Web Console. * * @param object msg * An object holding information about a reflow batch. */ - logReflowActivity: function WCF_logReflowActivity(message) - { + logReflowActivity: function WCF_logReflowActivity(message) { let {start, end, sourceURL, sourceLine} = message; let duration = Math.round((end - start) * 100) / 100; let node = this.document.createElementNS(XHTML_NS, "span"); if (sourceURL) { node.textContent = l10n.getFormatStr("reflow.messageWithLink", [duration]); let a = this.document.createElementNS(XHTML_NS, "a"); a.href = "#"; a.draggable = "false"; @@ -1787,53 +1745,49 @@ WebConsoleFrame.prototype = { node.appendChild(a); } else { node.textContent = l10n.getFormatStr("reflow.messageWithNoLink", [duration]); } return this.createMessageNode(CATEGORY_CSS, SEVERITY_LOG, node); }, - handleReflowActivity: function WCF_handleReflowActivity(message) - { + handleReflowActivity: function WCF_handleReflowActivity(message) { this.outputMessage(CATEGORY_CSS, this.logReflowActivity, [message]); }, /** * Inform user that the window.console API has been replaced by a script * in a content page. */ - logWarningAboutReplacedAPI: function WCF_logWarningAboutReplacedAPI() - { + logWarningAboutReplacedAPI: function WCF_logWarningAboutReplacedAPI() { let node = this.createMessageNode(CATEGORY_JS, SEVERITY_WARNING, l10n.getStr("ConsoleAPIDisabled")); this.outputMessage(CATEGORY_JS, node); }, /** * Handle the network events coming from the remote Web Console. * * @param object networkInfo * The network request information. */ - handleNetworkEvent: function(networkInfo) - { + handleNetworkEvent: function(networkInfo) { this.outputMessage(CATEGORY_NETWORK, this.logNetEvent, [networkInfo]); }, /** * Handle network event updates coming from the server. * * @param object networkInfo * The network request information. * @param object packet * Update details. */ - handleNetworkEventUpdate: function(networkInfo, packet) - { + handleNetworkEventUpdate: function(networkInfo, packet) { if (networkInfo.node && this._updateNetMessage(packet.from)) { this.emit("new-messages", new Set([{ update: true, node: networkInfo.node, response: packet, }])); } @@ -1851,18 +1805,17 @@ WebConsoleFrame.prototype = { * given a network event actor ID. * * @private * @param string actorId * The network event actor ID for which you want to update the message. * @return boolean * |true| if the message node was updated, or |false| otherwise. */ - _updateNetMessage: function WCF__updateNetMessage(actorId) - { + _updateNetMessage: function WCF__updateNetMessage(actorId) { let networkInfo = this.webConsoleClient.getNetworkRequest(actorId); if (!networkInfo || !networkInfo.node) { return; } let messageNode = networkInfo.node; let updates = networkInfo.updates; let hasEventTimings = updates.indexOf("eventTimings") > -1; @@ -1905,18 +1858,17 @@ WebConsoleFrame.prototype = { }, /** * Opens the network monitor and highlights the specified request. * * @param string requestId * The actor ID of the network request. */ - openNetworkPanel: function WCF_openNetworkPanel(requestId) - { + openNetworkPanel: function WCF_openNetworkPanel(requestId) { let toolbox = gDevTools.getToolbox(this.owner.target); // The browser console doesn't have a toolbox. if (!toolbox) { return; } return toolbox.selectTool("netmonitor").then(panel => { return panel.panelWin.NetMonitorController.inspectRequest(requestId); }); @@ -1925,40 +1877,37 @@ WebConsoleFrame.prototype = { /** * Handler for page location changes. * * @param string uri * New page location. * @param string title * New page title. */ - onLocationChange: function WCF_onLocationChange(uri, title) - { + onLocationChange: function WCF_onLocationChange(uri, title) { this.contentLocation = uri; if (this.owner.onLocationChange) { this.owner.onLocationChange(uri, title); } }, /** * Handler for the tabNavigated notification. * * @param string event * Event name. * @param object packet * Notification packet received from the server. */ - handleTabNavigated: function WCF_handleTabNavigated(event, packet) - { + handleTabNavigated: function WCF_handleTabNavigated(event, packet) { if (event == "will-navigate") { if (this.persistLog) { let marker = new Messages.NavigationMarker(packet, Date.now()); this.output.addMessage(marker); - } - else { + } else { this.jsterm.clearOutput(); } } if (packet.url) { this.onLocationChange(packet.url, packet.title); } @@ -1982,18 +1931,17 @@ WebConsoleFrame.prototype = { * the actual element. If a method is given it will be bound to the HUD * object and the arguments will be |args|. * @param array [args] * If a method is given to output the message element then the method * will be invoked with the list of arguments given here. The last * object in this array should be the packet received from the * back end. */ - outputMessage: function WCF_outputMessage(category, methodOrNode, args) - { + outputMessage: function WCF_outputMessage(category, methodOrNode, args) { if (!this._outputQueue.length) { // If the queue is empty we consider that now was the last output flush. // This avoid an immediate output flush when the timer executes. this._lastOutputFlush = Date.now(); } this._outputQueue.push([category, methodOrNode, args]); @@ -2002,18 +1950,17 @@ WebConsoleFrame.prototype = { /** * Try to flush the output message queue. This takes the messages in the * output queue and displays them. Outputting stops at MESSAGES_IN_INTERVAL. * Further output is queued to happen later - see OUTPUT_INTERVAL. * * @private */ - _flushMessageQueue: function WCF__flushMessageQueue() - { + _flushMessageQueue: function WCF__flushMessageQueue() { this._outputTimerInitialized = false; if (!this._outputTimer) { return; } let startTime = Date.now(); let timeSinceFlush = startTime - this._lastOutputFlush; let shouldThrottle = this._outputQueue.length > MESSAGES_IN_INTERVAL && @@ -2096,19 +2043,18 @@ WebConsoleFrame.prototype = { (lastVisibleNode.category == CATEGORY_INPUT || lastVisibleNode.category == CATEGORY_OUTPUT); // Scroll to the new node if it is not filtered, and if the output node is // scrolled at the bottom or if the new node is a jsterm input/output // message. if (lastVisibleNode && (scrolledToBottom || isInputOutput)) { Utils.scrollToVisible(lastVisibleNode); - } - else if (!scrolledToBottom && removedNodes > 0 && - oldScrollHeight != scrollNode.scrollHeight) { + } else if (!scrolledToBottom && removedNodes > 0 && + oldScrollHeight != scrollNode.scrollHeight) { // If there were pruned messages and if scroll is not at the bottom, then // we need to adjust the scroll location. scrollNode.scrollTop -= oldScrollHeight - scrollNode.scrollHeight; } if (messages.size) { this.emit("new-messages", messages); } @@ -2124,18 +2070,17 @@ WebConsoleFrame.prototype = { this._lastOutputFlush = Date.now(); }, /** * Initialize the output timer. * @private */ - _initOutputTimer: function WCF__initOutputTimer() - { + _initOutputTimer: function WCF__initOutputTimer() { let panelIsDestroyed = !this._outputTimer; let alreadyScheduled = this._outputTimerInitialized; let nothingToDo = !this._itemDestroyQueue.length && !this._outputQueue.length; // Don't schedule a callback in the following cases: if (panelIsDestroyed || alreadyScheduled || nothingToDo) { return; @@ -2158,18 +2103,17 @@ WebConsoleFrame.prototype = { * @return object * An object that holds the following properties: * - node: the DOM element of the message. * - isRepeated: the DOM element of the original message, if this is * a repeated message, otherwise null. * - visible: boolean that tells if the message is visible. */ _outputMessageFromQueue: - function WCF__outputMessageFromQueue(hudIdSupportsString, item) - { + function WCF__outputMessageFromQueue(hudIdSupportsString, item) { let [category, methodOrNode, args] = item; // The last object in the args array should be message // object or response packet received from the server. let message = (args && args.length) ? args[args.length-1] : null; let node = typeof methodOrNode == "function" ? methodOrNode.apply(this, args || []) : @@ -2206,18 +2150,17 @@ WebConsoleFrame.prototype = { }; }, /** * Prune the queue of messages to display. This avoids displaying messages * that will be removed at the end of the queue anyway. * @private */ - _pruneOutputQueue: function WCF__pruneOutputQueue() - { + _pruneOutputQueue: function WCF__pruneOutputQueue() { let nodes = {}; // Group the messages per category. this._outputQueue.forEach(function(item, index) { let [category] = item; if (!(category in nodes)) { nodes[category] = []; } @@ -2247,18 +2190,17 @@ WebConsoleFrame.prototype = { * Destroy an item that was once in the outputQueue but isn't needed * after all. * * @private * @param array item * The item you want to destroy. Does not remove it from the output * queue. */ - _destroyItem: function WCF__destroyItem(item) - { + _destroyItem: function WCF__destroyItem(item) { // TODO: handle object releasing in a more elegant way once all console // messages use the new API - bug 778766. let [category, methodOrNode, args] = item; if (typeof methodOrNode != "function" && methodOrNode._objectActors) { for (let actor of methodOrNode._objectActors) { this._releaseObject(actor); } methodOrNode._objectActors.clear(); @@ -2276,78 +2218,72 @@ WebConsoleFrame.prototype = { arg._objectActors.clear(); } } if (category == CATEGORY_NETWORK) { let connectionId = null; if (methodOrNode == this.logNetEvent) { connectionId = args[0].actor; - } - else if (typeof methodOrNode != "function") { + } else if (typeof methodOrNode != "function") { connectionId = methodOrNode._connectionId; } if (connectionId && this.webConsoleClient.hasNetworkRequest(connectionId)) { this.webConsoleClient.removeNetworkRequest(connectionId); this._releaseObject(connectionId); } - } - else if (category == CATEGORY_WEBDEV && - methodOrNode == this.logConsoleAPIMessage) { + } else if (category == CATEGORY_WEBDEV && + methodOrNode == this.logConsoleAPIMessage) { args[0].arguments.forEach((value) => { if (WebConsoleUtils.isActorGrip(value)) { this._releaseObject(value.actor); } }); - } - else if (category == CATEGORY_JS && - methodOrNode == this.reportPageError) { + } else if (category == CATEGORY_JS && + methodOrNode == this.reportPageError) { let pageError = args[1]; for (let prop of ["errorMessage", "lineText"]) { let grip = pageError[prop]; if (WebConsoleUtils.isActorGrip(grip)) { this._releaseObject(grip.actor); } } - } - else if (category == CATEGORY_JS && - methodOrNode == this._reportLogMessage) { + } else if (category == CATEGORY_JS && + methodOrNode == this._reportLogMessage) { if (WebConsoleUtils.isActorGrip(args[0].message)) { this._releaseObject(args[0].message.actor); } } }, /** * Ensures that the number of message nodes of type category don't exceed that * category's line limit by removing old messages as needed. * * @param integer category * The category of message nodes to prune if needed. * @return number * The number of removed nodes. */ - pruneOutputIfNecessary: function WCF_pruneOutputIfNecessary(category) - { + pruneOutputIfNecessary: function WCF_pruneOutputIfNecessary(category) { let logLimit = Utils.logLimitForCategory(category); let messageNodes = this.outputNode.querySelectorAll(".message[category=" + CATEGORY_CLASS_FRAGMENTS[category] + "]"); let n = Math.max(0, messageNodes.length - logLimit); [...messageNodes].slice(0, n).forEach(this.removeOutputMessage, this); return n; }, /** * Remove a given message from the output. * * @param nsIDOMNode node * The message node you want to remove. */ - removeOutputMessage: function WCF_removeOutputMessage(node) - { + removeOutputMessage: function WCF_removeOutputMessage(node) { if (node._messageObject) { node._messageObject.destroy(); } if (node._objectActors) { for (let actor of node._objectActors) { this._releaseObject(actor); } @@ -2355,23 +2291,21 @@ WebConsoleFrame.prototype = { } if (node.category == CATEGORY_CSS || node.category == CATEGORY_SECURITY) { let repeatNode = node.getElementsByClassName("message-repeats")[0]; if (repeatNode && repeatNode._uid) { delete this._repeatNodes[repeatNode._uid]; } - } - else if (node._connectionId && - node.category == CATEGORY_NETWORK) { + } else if (node._connectionId && + node.category == CATEGORY_NETWORK) { this.webConsoleClient.removeNetworkRequest(node._connectionId); this._releaseObject(node._connectionId); - } - else if (node.classList.contains("inlined-variables-view")) { + } else if (node.classList.contains("inlined-variables-view")) { let view = node._variablesView; if (view) { view.controller.releaseActors(); } node._variablesView = null; } node.remove(); @@ -2402,18 +2336,17 @@ WebConsoleFrame.prototype = { * The timestamp to use for this message node. If omitted, the current * date and time is used. * @return nsIDOMNode * The message node: a DIV ready to be inserted into the Web Console * output node. */ createMessageNode: function WCF_createMessageNode(category, severity, body, sourceURL, - sourceLine, clipboardText, level, timestamp) - { + sourceLine, clipboardText, level, timestamp) { if (typeof body != "string" && clipboardText == null && body.innerText) { clipboardText = body.innerText; } let indentNode = this.document.createElementNS(XHTML_NS, "span"); indentNode.className = "indent"; // Apply the current group by indenting appropriately. @@ -2445,23 +2378,21 @@ WebConsoleFrame.prototype = { node.id = "console-msg-" + gSequenceId(); node.className = "message"; node.clipboardText = clipboardText; node.timestamp = timestamp; this.setMessageType(node, category, severity); if (body instanceof Ci.nsIDOMNode) { bodyNode.appendChild(body); - } - else { + } else { let str = undefined; if (level == "dir") { str = VariablesView.getString(body.arguments[0]); - } - else { + } else { str = body; } if (str !== undefined) { body = this.document.createTextNode(str); bodyNode.appendChild(body); } } @@ -2536,36 +2467,34 @@ WebConsoleFrame.prototype = { * An object containing url, line and column number of the message source (destructured). * @param string target [optional] * Tells which tool to open the link with, on click. Supported tools: * jsdebugger, styleeditor, scratchpad. * @return nsIDOMNode * The new anchor element, ready to be added to the message node. */ createLocationNode: - function WCF_createLocationNode({url, line, column}, target) - { + function WCF_createLocationNode({url, line, column}, target) { if (!url) { url = ""; } let locationNode = this.document.createElementNS(XHTML_NS, "a"); let filenameNode = this.document.createElementNS(XHTML_NS, "span"); // Create the text, which consists of an abbreviated version of the URL // Scratchpad URLs should not be abbreviated. let filename; let fullURL; let isScratchpad = false; if (/^Scratchpad\/\d+$/.test(url)) { filename = url; fullURL = url; isScratchpad = true; - } - else { + } else { fullURL = url.split(" -> ").pop(); filename = WebConsoleUtils.abbreviateSourceURL(fullURL); } filenameNode.className = "filename"; filenameNode.textContent = " " + (filename || l10n.getStr("unknownLocation")); locationNode.appendChild(filenameNode); @@ -2583,22 +2512,20 @@ WebConsoleFrame.prototype = { if (target == "scratchpad" || isScratchpad) { this.owner.viewSourceInScratchpad(url, line); return; } let category = locationNode.parentNode.category; if (target == "styleeditor" || category == CATEGORY_CSS) { this.owner.viewSourceInStyleEditor(fullURL, line); - } - else if (target == "jsdebugger" || - category == CATEGORY_JS || category == CATEGORY_WEBDEV) { + } else if (target == "jsdebugger" || + category == CATEGORY_JS || category == CATEGORY_WEBDEV) { this.owner.viewSourceInDebugger(fullURL, line); - } - else { + } else { this.owner.viewSource(fullURL, line); } }; if (fullURL) { this._addMessageLinkCallback(locationNode, onClick); } @@ -2620,36 +2547,34 @@ WebConsoleFrame.prototype = { * The message node to alter. * @param number category * The category for the message; one of the CATEGORY_ constants. * @param number severity * The severity for the message; one of the SEVERITY_ constants. * @return void */ setMessageType: - function WCF_setMessageType(messageNode, category, severity) - { + function WCF_setMessageType(messageNode, category, severity) { messageNode.category = category; messageNode.severity = severity; messageNode.setAttribute("category", CATEGORY_CLASS_FRAGMENTS[category]); messageNode.setAttribute("severity", SEVERITY_CLASS_FRAGMENTS[severity]); messageNode.setAttribute("filter", MESSAGE_PREFERENCE_KEYS[category][severity]); }, /** * Add the mouse event handlers needed to make a link. * * @private * @param nsIDOMNode node * The node for which you want to add the event handlers. * @param function callback * The function you want to invoke on click. */ - _addMessageLinkCallback: function WCF__addMessageLinkCallback(node, callback) - { + _addMessageLinkCallback: function WCF__addMessageLinkCallback(node, callback) { node.addEventListener("mousedown", (event) => { this._mousedown = true; this._startX = event.clientX; this._startY = event.clientY; }, false); node.addEventListener("click", (event) => { let mousedown = this._mousedown; @@ -2661,30 +2586,28 @@ WebConsoleFrame.prototype = { if (event.detail != 1 || event.button != 0) { return; } // If this event started with a mousedown event and it ends at a different // location, we consider this text selection. if (mousedown && (this._startX != event.clientX) && - (this._startY != event.clientY)) - { + (this._startY != event.clientY)) { this._startX = this._startY = undefined; return; } this._startX = this._startY = undefined; callback.call(this, event); }, false); }, - _addFocusCallback: function WCF__addFocusCallback(node, callback) - { + _addFocusCallback: function WCF__addFocusCallback(node, callback) { node.addEventListener("mousedown", (event) => { this._mousedown = true; this._startX = event.clientX; this._startY = event.clientY; }, false); node.addEventListener("click", (event) => { let mousedown = this._mousedown; @@ -2696,18 +2619,17 @@ WebConsoleFrame.prototype = { } // If this event started with a mousedown event and it ends at a different // location, we consider this text selection. // Add a fuzz modifier of two pixels in any direction to account for sloppy // clicking. if (mousedown && (Math.abs(event.clientX - this._startX) >= 2) && - (Math.abs(event.clientY - this._startY) >= 1)) - { + (Math.abs(event.clientY - this._startY) >= 1)) { this._startX = this._startY = undefined; return; } this._startX = this._startY = undefined; callback.call(this, event); }, false); @@ -2719,41 +2641,38 @@ WebConsoleFrame.prototype = { * * @private * @param object event * This parameter is a string that holds the event name * pref-changed in this case. * @param object data * This is the pref-changed data object. */ - _onToolboxPrefChanged: function WCF__onToolboxPrefChanged(event, data) - { + _onToolboxPrefChanged: function WCF__onToolboxPrefChanged(event, data) { if (data.pref == PREF_MESSAGE_TIMESTAMP) { if (data.newValue) { this.outputNode.classList.remove("hideTimestamps"); - } - else { + } else { this.outputNode.classList.add("hideTimestamps"); } } }, /** * Copies the selected items to the system clipboard. * * @param object options * - linkOnly: * An optional flag to copy only URL without timestamp and * other meta-information. Default is false. * - contextmenu: * An optional flag to copy the last clicked item which brought * up the context menu if nothing is selected. Default is false. */ - copySelectedItems: function WCF_copySelectedItems(options) - { + copySelectedItems: function WCF_copySelectedItems(options) { options = options || { linkOnly: false, contextmenu: false }; // Gather up the selected items and concatenate their clipboard text. let strings = []; let children = this.output.getSelectedMessages(); if (!children.length && options.contextmenu) { children = [this._contextMenuHandler.lastClickedMessage]; @@ -2761,18 +2680,17 @@ WebConsoleFrame.prototype = { for (let item of children) { // Ensure the selected item hasn't been filtered by type or string. if (!item.classList.contains("filtered-by-type") && !item.classList.contains("filtered-by-string")) { let timestampString = l10n.timestampString(item.timestamp); if (options.linkOnly) { strings.push(item.url); - } - else { + } else { strings.push(item.clipboardText); } } } clipboardHelper.copyString(strings.join("\n")); }, @@ -2781,18 +2699,17 @@ WebConsoleFrame.prototype = { * remote object you want. * * @param string actor * The object actor ID from which you want the properties. * @param function callback * Function you want invoked once the properties are received. */ objectPropertiesProvider: - function WCF_objectPropertiesProvider(actor, callback) - { + function WCF_objectPropertiesProvider(actor, callback) { this.webConsoleClient.inspectObjectProperties(actor, function(response) { if (response.error) { Cu.reportError("Failed to retrieve the object properties from the " + "server. Error: " + response.error); return; } callback(response.properties); @@ -2801,28 +2718,26 @@ WebConsoleFrame.prototype = { /** * Release an actor. * * @private * @param string actor * The actor ID you want to release. */ - _releaseObject: function WCF__releaseObject(actor) - { + _releaseObject: function WCF__releaseObject(actor) { if (this.proxy) { this.proxy.releaseActor(actor); } }, /** * Open the selected item's URL in a new tab. */ - openSelectedItemInTab: function WCF_openSelectedItemInTab() - { + openSelectedItemInTab: function WCF_openSelectedItemInTab() { let item = this.output.getSelectedMessages(1)[0] || this._contextMenuHandler.lastClickedMessage; if (!item || !item.url) { return; } this.owner.openLink(item.url); @@ -2831,18 +2746,17 @@ WebConsoleFrame.prototype = { /** * Destroy the WebConsoleFrame object. Call this method to avoid memory leaks * when the Web Console is closed. * * @return object * A promise that is resolved when the WebConsoleFrame instance is * destroyed. */ - destroy: function WCF_destroy() - { + destroy: function WCF_destroy() { if (this._destroyer) { return this._destroyer.promise; } this._destroyer = promise.defer(); let toolbox = gDevTools.getToolbox(this.owner.target); if (toolbox) { @@ -2883,65 +2797,60 @@ WebConsoleFrame.prototype = { let onDestroy = () => { this._destroyer.resolve(null); }; if (this.proxy) { this.proxy.disconnect().then(onDestroy); this.proxy = null; - } - else { + } else { onDestroy(); } return this._destroyer.promise; }, }; /** * @see VariablesView.simpleValueEvalMacro */ -function simpleValueEvalMacro(item, currentString) -{ +function simpleValueEvalMacro(item, currentString) { return VariablesView.simpleValueEvalMacro(item, currentString, "_self"); }; /** * @see VariablesView.overrideValueEvalMacro */ -function overrideValueEvalMacro(item, currentString) -{ +function overrideValueEvalMacro(item, currentString) { return VariablesView.overrideValueEvalMacro(item, currentString, "_self"); }; /** * @see VariablesView.getterOrSetterEvalMacro */ -function getterOrSetterEvalMacro(item, currentString) -{ +function getterOrSetterEvalMacro(item, currentString) { return VariablesView.getterOrSetterEvalMacro(item, currentString, "_self"); } /** * Create a JSTerminal (a JavaScript command line). This is attached to an * existing HeadsUpDisplay (a Web Console instance). This code is responsible * with handling command line input, code evaluation and result output. * * @constructor * @param object webConsoleFrame * The WebConsoleFrame object that owns this JSTerm instance. */ -function JSTerm(webConsoleFrame) -{ +function JSTerm(webConsoleFrame) { this.hud = webConsoleFrame; this.hudId = this.hud.hudId; this.inputHistoryCount = Services.prefs.getIntPref(PREF_INPUT_HISTORY_COUNT); this.lastCompletion = { value: null }; this._loadHistory(); this._objectActorsInVariablesViews = new Map(); @@ -3097,41 +3006,38 @@ JSTerm.prototype = { autocompletePopup: null, inputNode: null, completeNode: null, /** * Getter for the element that holds the messages we display. * @type nsIDOMElement */ - get outputNode() - { + get outputNode() { return this.hud.outputNode; }, /** * Getter for the debugger WebConsoleClient. * @type object */ - get webConsoleClient() - { + get webConsoleClient() { return this.hud.webConsoleClient; }, COMPLETE_FORWARD: 0, COMPLETE_BACKWARD: 1, COMPLETE_HINT_ONLY: 2, COMPLETE_PAGEUP: 3, COMPLETE_PAGEDOWN: 4, /** * Initialize the JSTerminal UI. */ - init: function JST_init() - { + init: function JST_init() { let autocompleteOptions = { onSelect: this.onAutocompleteSelect.bind(this), onClick: this.acceptProposedCompletion.bind(this), panelId: "webConsole_autocompletePopup", listBoxId: "webConsole_autocompletePopupListBox", position: "before_start", theme: "auto", direction: "ltr", @@ -3143,18 +3049,17 @@ JSTerm.prototype = { let doc = this.hud.document; let inputContainer = doc.querySelector(".jsterm-input-container"); this.completeNode = doc.querySelector(".jsterm-complete-node"); this.inputNode = doc.querySelector(".jsterm-input-node"); if (this.hud.owner._browserConsole && !Services.prefs.getBoolPref("devtools.chrome.enabled")) { inputContainer.style.display = "none"; - } - else { + } else { let okstring = l10n.getStr("selfxss.okstring"); let msg = l10n.getFormatStr("selfxss.msg", [okstring]); this._onPaste = WebConsoleUtils.pasteHandlerGen(this.inputNode, doc.getElementById("webconsole-notificationbox"), msg, okstring); this.inputNode.addEventListener("keypress", this._keyPress, false); this.inputNode.addEventListener("paste", this._onPaste); this.inputNode.addEventListener("drop", this._onPaste); @@ -3180,18 +3085,17 @@ JSTerm.prototype = { * @private * @param function [callback] * Optional function to invoke when the evaluation result is added to * the output. * @param object response * The message received from the server. */ _executeResultCallback: - function JST__executeResultCallback(callback, response) - { + function JST__executeResultCallback(callback, response) { if (!this.hud) { return; } if (response.error) { Cu.reportError("Evaluation error " + response.error + ": " + response.message); return; } @@ -3212,18 +3116,17 @@ JSTerm.prototype = { this.openVariablesView({ label: VariablesView.getString(helperResult.object, { concise: true }), objectActor: helperResult.object, }); break; case "error": try { errorMessage = l10n.getStr(helperResult.message); - } - catch (ex) { + } catch (ex) { errorMessage = helperResult.message; } break; case "help": this.hud.owner.openLink(HELP_URL); break; case "copyValueToClipboard": clipboardHelper.copyString(helperResult.value); @@ -3274,18 +3177,17 @@ JSTerm.prototype = { * The string you want to execute. If this is not provided, the current * user input is used - taken from |this.getInputValue()|. * @param function [callback] * Optional function to invoke when the result is displayed. * This is deprecated - please use the promise return value instead. * @returns Promise * Resolves with the message once the result is displayed. */ - execute: function JST_execute(executeString, callback) - { + execute: function JST_execute(executeString, callback) { let deferred = promise.defer(); let resultCallback = function(msg) { deferred.resolve(msg); if (callback) { callback(msg); } } @@ -3351,25 +3253,23 @@ JSTerm.prototype = { * global content window. * - selectedNodeActor: tells the NodeActor ID of the current selection in * the Inspector, if such a selection exists. This is used by helper * functions that can evaluate on the current selection. * @return object * A promise object that is resolved when the server response is * received. */ - requestEvaluation: function JST_requestEvaluation(str, options = {}) - { + requestEvaluation: function JST_requestEvaluation(str, options = {}) { let deferred = promise.defer(); function onResult(response) { if (!response.error) { deferred.resolve(response); - } - else { + } else { deferred.reject(response); } } let frameActor = null; if ("frame" in options) { frameActor = this.getFrameActor(options.frame); } @@ -3388,28 +3288,26 @@ JSTerm.prototype = { /** * Retrieve the FrameActor ID given a frame depth. * * @param number frame * Frame depth. * @return string|null * The FrameActor ID for the given frame depth. */ - getFrameActor: function JST_getFrameActor(frame) - { + getFrameActor: function JST_getFrameActor(frame) { let state = this.hud.owner.getDebuggerFrames(); if (!state) { return null; } let grip; if (frame == this.SELECTED_FRAME) { grip = state.frames[state.selected]; - } - else { + } else { grip = state.frames[frame]; } return grip ? grip.actor : null; }, /** * Opens a new variables view that allows the inspection of the given object. @@ -3427,18 +3325,17 @@ JSTerm.prototype = { * to. An iframe element is used as a container for the view. If this * option is not used, then the variables view opens in the sidebar. * - autofocus: optional boolean, |true| if you want to give focus to * the variables view window after open, |false| otherwise. * @return object * A promise object that is resolved when the variables view has * opened. The new variables view instance is given to the callbacks. */ - openVariablesView: function JST_openVariablesView(options) - { + openVariablesView: function JST_openVariablesView(options) { let onContainerReady = (window) => { let container = window.document.querySelector("#variables"); let view = this._variablesView; if (!view || options.targetElement) { let viewOptions = { container: container, hideFilterInput: options.hideFilterInput, }; @@ -3471,85 +3368,79 @@ JSTerm.prototype = { iframe.style.visibility = "visible"; deferred.resolve(iframe.contentWindow); }, true); iframe.flex = 1; iframe.style.visibility = "hidden"; iframe.setAttribute("src", VARIABLES_VIEW_URL); options.targetElement.appendChild(iframe); - } - else { + } else { if (!this.sidebar) { this._createSidebar(); } openPromise = this._addVariablesViewSidebarTab(); } return openPromise.then(onContainerReady); }, /** * Create the Web Console sidebar. * * @see devtools/framework/sidebar.js * @private */ - _createSidebar: function JST__createSidebar() - { + _createSidebar: function JST__createSidebar() { let tabbox = this.hud.document.querySelector("#webconsole-sidebar"); this.sidebar = new ToolSidebar(tabbox, this, "webconsole"); this.sidebar.show(); this.emit("sidebar-opened"); }, /** * Add the variables view tab to the sidebar. * * @private * @return object * A promise object for the adding of the new tab. */ - _addVariablesViewSidebarTab: function JST__addVariablesViewSidebarTab() - { + _addVariablesViewSidebarTab: function JST__addVariablesViewSidebarTab() { let deferred = promise.defer(); let onTabReady = () => { let window = this.sidebar.getWindowForTab("variablesview"); deferred.resolve(window); }; let tabPanel = this.sidebar.getTabPanel("variablesview"); if (tabPanel) { if (this.sidebar.getCurrentTabID() == "variablesview") { onTabReady(); - } - else { + } else { this.sidebar.once("variablesview-selected", onTabReady); this.sidebar.select("variablesview"); } - } - else { + } else { this.sidebar.once("variablesview-ready", onTabReady); this.sidebar.addTab("variablesview", VARIABLES_VIEW_URL, true); } return deferred.promise; }, /** * The keypress event handler for the Variables View sidebar. Currently this * is used for removing the sidebar when Escape is pressed. * * @private * @param nsIDOMEvent event * The keypress DOM event object. */ - _onKeypressInVariablesView: function JST__onKeypressInVariablesView(event) - { + _onKeypressInVariablesView: function JST__onKeypressInVariablesView(event) { let tag = event.target.nodeName; if (event.keyCode != Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE || event.shiftKey || event.altKey || event.ctrlKey || event.metaKey || ["input", "textarea", "select", "textbox"].indexOf(tag) > -1) { return; } this._sidebarDestroy(); @@ -3564,18 +3455,17 @@ JSTerm.prototype = { * @param object options * Options for the new Variables View instance: * - container: the DOM element where the variables view is inserted. * - hideFilterInput: boolean, if true the variables filter input is * hidden. * @return object * The new Variables View instance. */ - _createVariablesView: function JST__createVariablesView(options) - { + _createVariablesView: function JST__createVariablesView(options) { let view = new VariablesView(options.container); view.toolbox = gDevTools.getToolbox(this.hud.owner.target); view.searchPlaceholder = l10n.getStr("propertiesFilterPlaceholder"); view.emptyText = l10n.getStr("emptyPropertiesList"); view.searchEnabled = !options.hideFilterInput; view.lazyEmpty = this._lazyVariablesView; VariablesViewController.attach(view, { @@ -3611,51 +3501,47 @@ JSTerm.prototype = { * @param object options * Options for updating the variables view: * - view: the view you want to update. * - objectActor: the grip of the new ObjectActor you want to show in * the view. * - rawObject: the new raw object you want to show. * - label: the new label for the inspected object. */ - _updateVariablesView: function JST__updateVariablesView(options) - { + _updateVariablesView: function JST__updateVariablesView(options) { let view = options.view; view.empty(); // We need to avoid pruning the object inspection starting point. // That one is pruned when the console message is removed. view.controller.releaseActors(actor => { return view._consoleLastObjectActor != actor; }); if (options.objectActor && (!this.hud.owner._browserConsole || Services.prefs.getBoolPref("devtools.chrome.enabled"))) { // Make sure eval works in the correct context. view.eval = this._variablesViewEvaluate.bind(this, options); view.switch = this._variablesViewSwitch.bind(this, options); view.delete = this._variablesViewDelete.bind(this, options); - } - else { + } else { view.eval = null; view.switch = null; view.delete = null; } let { variable, expanded } = view.controller.setSingleVariable(options); variable.evaluationMacro = simpleValueEvalMacro; if (options.objectActor) { view._consoleLastObjectActor = options.objectActor.actor; - } - else if (options.rawObject) { + } else if (options.rawObject) { view._consoleLastObjectActor = null; - } - else { + } else { throw new Error("Variables View cannot open without giving it an object " + "display."); } expanded.then(() => { this.emit("variablesview-updated", view, options); }); }, @@ -3668,18 +3554,17 @@ JSTerm.prototype = { * @param object options * The options used for |this._updateVariablesView()|. * @param object variableObject * The Variable object instance for the edited property. * @param string value * The value the edited property was changed to. */ _variablesViewEvaluate: - function JST__variablesViewEvaluate(options, variableObject, value) - { + function JST__variablesViewEvaluate(options, variableObject, value) { let updater = this._updateVariablesView.bind(this, options); let onEval = this._silentEvalCallback.bind(this, updater); let string = variableObject.evaluationMacro(variableObject, value); let evalOptions = { frame: this.SELECTED_FRAME, bindObjectActor: options.objectActor.actor, }; @@ -3692,18 +3577,17 @@ JSTerm.prototype = { * is deleted. * * @private * @param object options * The options used for |this._updateVariablesView()|. * @param object variableObject * The Variable object instance for the deleted property. */ - _variablesViewDelete: function JST__variablesViewDelete(options, variableObject) - { + _variablesViewDelete: function JST__variablesViewDelete(options, variableObject) { let onEval = this._silentEvalCallback.bind(this, null); let evalOptions = { frame: this.SELECTED_FRAME, bindObjectActor: options.objectActor.actor, }; this.requestEvaluation("delete _self" + variableObject.symbolicName, evalOptions) @@ -3718,18 +3602,17 @@ JSTerm.prototype = { * @param object options * The options used for |this._updateVariablesView()|. * @param object variableObject * The Variable object instance for the renamed property. * @param string newName * The new name for the property. */ _variablesViewSwitch: - function JST__variablesViewSwitch(options, variableObject, newName) - { + function JST__variablesViewSwitch(options, variableObject, newName) { let updater = this._updateVariablesView.bind(this, options); let onEval = this._silentEvalCallback.bind(this, updater); let evalOptions = { frame: this.SELECTED_FRAME, bindObjectActor: options.objectActor.actor, }; @@ -3753,18 +3636,17 @@ JSTerm.prototype = { * Exceptions are displayed in the output. * * @private * @param function callback * Function to invoke once the response is received. * @param object response * The response packet received from the server. */ - _silentEvalCallback: function JST__silentEvalCallback(callback, response) - { + _silentEvalCallback: function JST__silentEvalCallback(callback, response) { if (response.error) { Cu.reportError("Web Console evaluation failed. " + response.error + ":" + response.message); callback && callback(response); return; } @@ -3802,18 +3684,17 @@ JSTerm.prototype = { * Clear the Web Console output. * * This method emits the "messages-cleared" notification. * * @param boolean clearStorage * True if you want to clear the console messages storage associated to * this Web Console. */ - clearOutput: function JST_clearOutput(clearStorage) - { + clearOutput: function JST_clearOutput(clearStorage) { let hud = this.hud; let outputNode = hud.outputNode; let node; while ((node = outputNode.firstChild)) { hud.removeOutputMessage(node); } hud.groupDepth = 0; @@ -3831,32 +3712,30 @@ JSTerm.prototype = { this.emit("messages-cleared"); }, /** * Remove all of the private messages from the Web Console output. * * This method emits the "private-messages-cleared" notification. */ - clearPrivateMessages: function JST_clearPrivateMessages() - { + clearPrivateMessages: function JST_clearPrivateMessages() { let nodes = this.hud.outputNode.querySelectorAll(".message[private]"); for (let node of nodes) { this.hud.removeOutputMessage(node); } this.emit("private-messages-cleared"); }, /** * Updates the size of the input field (command line) to fit its contents. * * @returns void */ - resizeInput: function JST_resizeInput() - { + resizeInput: function JST_resizeInput() { let inputNode = this.inputNode; // Reset the height so that scrollHeight will reflect the natural height of // the contents of the input field. inputNode.style.height = "auto"; // Now resize the input field to fit its contents. let scrollHeight = inputNode.inputField.scrollHeight; @@ -3869,68 +3748,63 @@ JSTerm.prototype = { * Sets the value of the input field (command line), and resizes the field to * fit its contents. This method is preferred over setting "inputNode.value" * directly, because it correctly resizes the field. * * @param string newValue * The new value to set. * @returns void */ - setInputValue: function JST_setInputValue(newValue) - { + setInputValue: function JST_setInputValue(newValue) { this.inputNode.value = newValue; this.lastInputValue = newValue; this.completeNode.value = ""; this.resizeInput(); this._inputChanged = true; this.emit("set-input-value"); }, /** * Gets the value from the input field * @returns string */ - getInputValue: function() - { + getInputValue: function() { return this.inputNode.value || ""; }, /** * The inputNode "input" and "keyup" event handler. * @private */ - _inputEventHandler: function JST__inputEventHandler() - { + _inputEventHandler: function JST__inputEventHandler() { if (this.lastInputValue != this.getInputValue()) { this.resizeInput(); this.complete(this.COMPLETE_HINT_ONLY); this.lastInputValue = this.getInputValue(); this._inputChanged = true; } }, /** * The window "blur" event handler. * @private */ - _blurEventHandler: function JST__blurEventHandler() - { + _blurEventHandler: function JST__blurEventHandler() { if (this.autocompletePopup) { this.clearCompletion(); } }, /** * The inputNode "keypress" event handler. * * @private * @param nsIDOMEvent event */ - _keyPress: function JST__keyPress(event) - { + _keyPress: function JST__keyPress(event) { let inputNode = this.inputNode; let inputValue = this.getInputValue(); let inputUpdated = false; if (event.ctrlKey) { switch (event.charCode) { case 101: // control-e @@ -3983,106 +3857,99 @@ JSTerm.prototype = { inputNode.focus(); } this.clearCompletion(); break; default: break; } return; - } - else if (event.shiftKey && + } else if (event.shiftKey && event.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) { // shift return // TODO: expand the inputNode height by one line return; } switch (event.keyCode) { case Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE: if (this.autocompletePopup.isOpen) { this.clearCompletion(); event.preventDefault(); event.stopPropagation(); - } - else if (this.sidebar) { + } else if (this.sidebar) { this._sidebarDestroy(); event.preventDefault(); event.stopPropagation(); } break; case Ci.nsIDOMKeyEvent.DOM_VK_RETURN: if (this._autocompletePopupNavigated && this.autocompletePopup.isOpen && this.autocompletePopup.selectedIndex > -1) { this.acceptProposedCompletion(); - } - else { + } else { this.execute(); this._inputChanged = false; } event.preventDefault(); break; case Ci.nsIDOMKeyEvent.DOM_VK_UP: if (this.autocompletePopup.isOpen) { inputUpdated = this.complete(this.COMPLETE_BACKWARD); if (inputUpdated) { this._autocompletePopupNavigated = true; } - } - else if (this.canCaretGoPrevious()) { + } else if (this.canCaretGoPrevious()) { inputUpdated = this.historyPeruse(HISTORY_BACK); } if (inputUpdated) { event.preventDefault(); } break; case Ci.nsIDOMKeyEvent.DOM_VK_DOWN: if (this.autocompletePopup.isOpen) { inputUpdated = this.complete(this.COMPLETE_FORWARD); if (inputUpdated) { this._autocompletePopupNavigated = true; } - } - else if (this.canCaretGoNext()) { + } else if (this.canCaretGoNext()) { inputUpdated = this.historyPeruse(HISTORY_FORWARD); } if (inputUpdated) { event.preventDefault(); } break; case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP: if (this.autocompletePopup.isOpen) { inputUpdated = this.complete(this.COMPLETE_PAGEUP); if (inputUpdated) { this._autocompletePopupNavigated = true; } - } - else { + } else { this.hud.outputWrapper.scrollTop = Math.max(0, this.hud.outputWrapper.scrollTop - this.hud.outputWrapper.clientHeight ); } event.preventDefault(); break; case Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN: if (this.autocompletePopup.isOpen) { inputUpdated = this.complete(this.COMPLETE_PAGEDOWN); if (inputUpdated) { this._autocompletePopupNavigated = true; } - } - else { + } else { this.hud.outputWrapper.scrollTop = Math.min(this.hud.outputWrapper.scrollHeight, this.hud.outputWrapper.scrollTop + this.hud.outputWrapper.clientHeight ); } event.preventDefault(); break; @@ -4133,47 +4000,44 @@ JSTerm.prototype = { break; } case Ci.nsIDOMKeyEvent.DOM_VK_TAB: // Generate a completion and accept the first proposed value. if (this.complete(this.COMPLETE_HINT_ONLY) && this.lastCompletion && this.acceptProposedCompletion()) { event.preventDefault(); - } - else if (this._inputChanged) { + } else if (this._inputChanged) { this.updateCompleteNode(l10n.getStr("Autocomplete.blank")); event.preventDefault(); } break; default: break; } }, /** * The inputNode "focus" event handler. * @private */ - _focusEventHandler: function JST__focusEventHandler() - { + _focusEventHandler: function JST__focusEventHandler() { this._inputChanged = false; }, /** * Go up/down the history stack of input values. * * @param number direction * History navigation direction: HISTORY_BACK or HISTORY_FORWARD. * * @returns boolean * True if the input value changed, false otherwise. */ - historyPeruse: function JST_historyPeruse(direction) - { + historyPeruse: function JST_historyPeruse(direction) { if (!this.history.length) { return false; } // Up Arrow key if (direction == HISTORY_BACK) { if (this.historyPlaceHolder <= 0) { return false; @@ -4184,55 +4048,51 @@ JSTerm.prototype = { // the user is already at the last entry. // Note: this code does not store changes to items that are already in // history. if (this.historyPlaceHolder+1 == this.historyIndex) { this.history[this.historyIndex] = this.getInputValue() || ""; } this.setInputValue(inputVal); - } - // Down Arrow key - else if (direction == HISTORY_FORWARD) { + } else if (direction == HISTORY_FORWARD) { + // Down Arrow key if (this.historyPlaceHolder >= (this.history.length-1)) { return false; } let inputVal = this.history[++this.historyPlaceHolder]; this.setInputValue(inputVal); - } - else { + } else { throw new Error("Invalid argument 0"); } return true; }, /** * Test for multiline input. * * @return boolean * True if CR or LF found in node value; else false. */ - hasMultilineInput: function JST_hasMultilineInput() - { + hasMultilineInput: function JST_hasMultilineInput() { return /[\r\n]/.test(this.getInputValue()); }, /** * Check if the caret is at a location that allows selecting the previous item * in history when the user presses the Up arrow key. * * @return boolean * True if the caret is at a location that allows selecting the * previous item in history when the user presses the Up arrow key, * otherwise false. */ - canCaretGoPrevious: function JST_canCaretGoPrevious() - { + canCaretGoPrevious: function JST_canCaretGoPrevious() { let node = this.inputNode; if (node.selectionStart != node.selectionEnd) { return false; } let multiline = /[\r\n]/.test(node.value); return node.selectionStart == 0 ? true : node.selectionStart == node.value.length && !multiline; @@ -4242,18 +4102,17 @@ JSTerm.prototype = { * Check if the caret is at a location that allows selecting the next item in * history when the user presses the Down arrow key. * * @return boolean * True if the caret is at a location that allows selecting the next * item in history when the user presses the Down arrow key, otherwise * false. */ - canCaretGoNext: function JST_canCaretGoNext() - { + canCaretGoNext: function JST_canCaretGoNext() { let node = this.inputNode; if (node.selectionStart != node.selectionEnd) { return false; } let multiline = /[\r\n]/.test(node.value); return node.selectionStart == node.value.length ? true : node.selectionStart == 0 && !multiline; @@ -4287,18 +4146,17 @@ JSTerm.prototype = { * the this.getInputValue() is set to this value and the selection is set * from the current cursor position to the end of the completed text. * @param function callback * Optional function invoked when the autocomplete properties are * updated. * @returns boolean true if there existed a completion for the current input, * or false otherwise. */ - complete: function JSTF_complete(type, callback) - { + complete: function JSTF_complete(type, callback) { let inputNode = this.inputNode; let inputValue = this.getInputValue(); let frameActor = this.getFrameActor(this.SELECTED_FRAME); // If the inputNode has no value, then don't try to complete on it. if (!inputValue) { this.clearCompletion(); callback && callback(this); @@ -4321,27 +4179,23 @@ JSTerm.prototype = { } let popup = this.autocompletePopup; let accepted = false; if (type != this.COMPLETE_HINT_ONLY && popup.itemCount == 1) { this.acceptProposedCompletion(); accepted = true; - } - else if (type == this.COMPLETE_BACKWARD) { + } else if (type == this.COMPLETE_BACKWARD) { popup.selectPreviousItem(); - } - else if (type == this.COMPLETE_FORWARD) { + } else if (type == this.COMPLETE_FORWARD) { popup.selectNextItem(); - } - else if (type == this.COMPLETE_PAGEUP) { + } else if (type == this.COMPLETE_PAGEUP) { popup.selectPreviousPageItem(); - } - else if (type == this.COMPLETE_PAGEDOWN) { + } else if (type == this.COMPLETE_PAGEDOWN) { popup.selectNextPageItem(); } callback && callback(this); this.emit("autocomplete-updated"); return accepted || popup.itemCount > 0; }, @@ -4351,18 +4205,17 @@ JSTerm.prototype = { * * @private * @param int type * Completion type. See this.complete() for details. * @param function [callback] * Optional, function to invoke when completion results are received. */ _updateCompletionResult: - function JST__updateCompletionResult(type, callback) - { + function JST__updateCompletionResult(type, callback) { let frameActor = this.getFrameActor(this.SELECTED_FRAME); if (this.lastCompletion.value == this.getInputValue() && frameActor == this._lastFrameActorId) { return; } let requestId = gSequenceId(); let cursor = this.inputNode.selectionStart; @@ -4428,18 +4281,17 @@ JSTerm.prototype = { * Request ID. * @param function [callback=null] * Optional, function to invoke when the completion result is received. * @param object message * The JSON message which holds the completion results received from * the content process. */ _receiveAutocompleteProperties: - function JST__receiveAutocompleteProperties(requestId, callback, message) - { + function JST__receiveAutocompleteProperties(requestId, callback, message) { let inputNode = this.inputNode; let inputValue = this.getInputValue(); if (this.lastCompletion.value == inputValue || requestId != this.lastCompletion.requestId) { return; } // Cache whatever came from the server if the last char is alphanumeric or '.' let cursor = inputNode.selectionStart; @@ -4473,84 +4325,77 @@ JSTerm.prototype = { }; if (items.length > 1 && !popup.isOpen) { let str = this.getInputValue().substr(0, this.inputNode.selectionStart); let offset = str.length - (str.lastIndexOf("\n") + 1) - lastPart.length; let x = offset * this.hud._inputCharWidth; popup.openPopup(inputNode, x + this.hud._chevronWidth); this._autocompletePopupNavigated = false; - } - else if (items.length < 2 && popup.isOpen) { + } else if (items.length < 2 && popup.isOpen) { popup.hidePopup(); this._autocompletePopupNavigated = false; } if (items.length == 1) { popup.selectedIndex = 0; } this.onAutocompleteSelect(); if (completionType != this.COMPLETE_HINT_ONLY && popup.itemCount == 1) { this.acceptProposedCompletion(); - } - else if (completionType == this.COMPLETE_BACKWARD) { + } else if (completionType == this.COMPLETE_BACKWARD) { popup.selectPreviousItem(); - } - else if (completionType == this.COMPLETE_FORWARD) { + } else if (completionType == this.COMPLETE_FORWARD) { popup.selectNextItem(); } callback && callback(this); this.emit("autocomplete-updated"); }, - onAutocompleteSelect: function JSTF_onAutocompleteSelect() - { + onAutocompleteSelect: function JSTF_onAutocompleteSelect() { // Render the suggestion only if the cursor is at the end of the input. if (this.inputNode.selectionStart != this.getInputValue().length) { return; } let currentItem = this.autocompletePopup.selectedItem; if (currentItem && this.lastCompletion.value) { let suffix = currentItem.label.substring(this.lastCompletion. matchProp.length); this.updateCompleteNode(suffix); - } - else { + } else { this.updateCompleteNode(""); } }, /** * Clear the current completion information and close the autocomplete popup, * if needed. */ - clearCompletion: function JSTF_clearCompletion() - { + clearCompletion: function JSTF_clearCompletion() { this.autocompletePopup.clearItems(); this.lastCompletion = { value: null }; this.updateCompleteNode(""); if (this.autocompletePopup.isOpen) { this.autocompletePopup.hidePopup(); this._autocompletePopupNavigated = false; } }, /** * Accept the proposed input completion. * * @return boolean * True if there was a selected completion item and the input value * was updated, false otherwise. */ - acceptProposedCompletion: function JSTF_acceptProposedCompletion() - { + acceptProposedCompletion: function JSTF_acceptProposedCompletion() { let updated = false; let currentItem = this.autocompletePopup.selectedItem; if (currentItem && this.lastCompletion.value) { let suffix = currentItem.label.substring(this.lastCompletion. matchProp.length); let cursor = this.inputNode.selectionStart; let value = this.getInputValue(); @@ -4566,30 +4411,28 @@ JSTerm.prototype = { }, /** * Update the node that displays the currently selected autocomplete proposal. * * @param string suffix * The proposed suffix for the inputNode value. */ - updateCompleteNode: function JSTF_updateCompleteNode(suffix) - { + updateCompleteNode: function JSTF_updateCompleteNode(suffix) { // completion prefix = input, with non-control chars replaced by spaces let prefix = suffix ? this.getInputValue().replace(/[\S]/g, " ") : ""; this.completeNode.value = prefix + suffix; }, /** * Destroy the sidebar. * @private */ - _sidebarDestroy: function JST__sidebarDestroy() - { + _sidebarDestroy: function JST__sidebarDestroy() { if (this._variablesView) { this._variablesView.controller.releaseActors(); this._variablesView = null; } if (this.sidebar) { this.sidebar.hide(); this.sidebar.destroy(); @@ -4597,18 +4440,17 @@ JSTerm.prototype = { } this.emit("sidebar-closed"); }, /** * Destroy the JSTerm object. Call this method to avoid memory leaks. */ - destroy: function JST_destroy() - { + destroy: function JST_destroy() { this._sidebarDestroy(); this.clearCompletion(); this.clearOutput(); this.autocompletePopup.destroy(); this.autocompletePopup = null; @@ -4640,49 +4482,46 @@ JSTerm.prototype = { var Utils = { /** * Scrolls a node so that it's visible in its containing element. * * @param nsIDOMNode node * The node to make visible. * @returns void */ - scrollToVisible: function Utils_scrollToVisible(node) - { + scrollToVisible: function Utils_scrollToVisible(node) { node.scrollIntoView(false); }, /** * Check if the given output node is scrolled to the bottom. * * @param nsIDOMNode outputNode * @param nsIDOMNode scrollNode * @return boolean * True if the output node is scrolled to the bottom, or false * otherwise. */ - isOutputScrolledToBottom: function (outputNode, scrollNode) - { + isOutputScrolledToBottom: function (outputNode, scrollNode) { let lastNodeHeight = outputNode.lastChild ? outputNode.lastChild.clientHeight : 0; return scrollNode.scrollTop + scrollNode.clientHeight >= scrollNode.scrollHeight - lastNodeHeight / 2; }, /** * Determine the category of a given nsIScriptError. * * @param nsIScriptError scriptError * The script error you want to determine the category for. * @return CATEGORY_JS|CATEGORY_CSS|CATEGORY_SECURITY * Depending on the script error CATEGORY_JS, CATEGORY_CSS, or * CATEGORY_SECURITY can be returned. */ - categoryForScriptError: function Utils_categoryForScriptError(scriptError) - { + categoryForScriptError: function Utils_categoryForScriptError(scriptError) { let category = scriptError.category; if (/^(?:CSS|Layout)\b/.test(category)) { return CATEGORY_CSS; } switch (category) { case "Mixed Content Blocker": @@ -4708,84 +4547,75 @@ var Utils = { * Retrieve the limit of messages for a specific category. * * @param number category * The category of messages you want to retrieve the limit for. See the * CATEGORY_* constants. * @return number * The number of messages allowed for the specific category. */ - logLimitForCategory: function Utils_logLimitForCategory(category) - { + logLimitForCategory: function Utils_logLimitForCategory(category) { let logLimit = DEFAULT_LOG_LIMIT; try { let prefName = CATEGORY_CLASS_FRAGMENTS[category]; logLimit = Services.prefs.getIntPref("devtools.hud.loglimit." + prefName); logLimit = Math.max(logLimit, 1); - } - catch (e) { } + } catch (e) { } return logLimit; }, }; /////////////////////////////////////////////////////////////////////////////// // CommandController /////////////////////////////////////////////////////////////////////////////// /** * A controller (an instance of nsIController) that makes editing actions * behave appropriately in the context of the Web Console. */ -function CommandController(webConsole) -{ +function CommandController(webConsole) { this.owner = webConsole; } CommandController.prototype = { /** * Selects all the text in the HUD output. */ - selectAll: function CommandController_selectAll() - { + selectAll: function CommandController_selectAll() { this.owner.output.selectAllMessages(); }, /** * Open the URL of the selected message in a new tab. */ - openURL: function CommandController_openURL() - { + openURL: function CommandController_openURL() { this.owner.openSelectedItemInTab(); }, - copyURL: function CommandController_copyURL() - { + copyURL: function CommandController_copyURL() { this.owner.copySelectedItems({ linkOnly: true, contextmenu: true }); }, /** * Copies the last clicked message. */ - copyLastClicked: function CommandController_copy() - { + copyLastClicked: function CommandController_copy() { this.owner.copySelectedItems({ linkOnly: false, contextmenu: true }); }, - supportsCommand: function CommandController_supportsCommand(command) - { + supportsCommand: function CommandController_supportsCommand(command) { if (!this.owner || !this.owner.output) { return false; } return this.isCommandEnabled(command); }, - isCommandEnabled: function CommandController_isCommandEnabled(command) - { + isCommandEnabled: function CommandController_isCommandEnabled(command) { switch (command) { case "consoleCmd_openURL": case "consoleCmd_copyURL": { // Only enable URL-related actions if node is Net Activity. let selectedItem = this.owner.output.getSelectedMessages(1)[0] || this.owner._contextMenuHandler.lastClickedMessage; return selectedItem && "url" in selectedItem; } @@ -4803,18 +4633,17 @@ CommandController.prototype = { case "cmd_fontSizeReduce": case "cmd_fontSizeReset": case "cmd_close": return this.owner.owner._browserConsole; } return false; }, - doCommand: function CommandController_doCommand(command) - { + doCommand: function CommandController_doCommand(command) { switch (command) { case "consoleCmd_openURL": this.openURL(); break; case "consoleCmd_copyURL": this.copyURL(); break; case "consoleCmd_clearOutput": @@ -4854,18 +4683,17 @@ CommandController.prototype = { * and the application we connect to through the remote debug protocol. * * @constructor * @param object webConsoleFrame * The WebConsoleFrame object that owns this connection proxy. * @param RemoteTarget target * The target that the console will connect to. */ -function WebConsoleConnectionProxy(webConsoleFrame, target) -{ +function WebConsoleConnectionProxy(webConsoleFrame, target) { this.webConsoleFrame = webConsoleFrame; this.target = target; this._onPageError = this._onPageError.bind(this); this._onLogMessage = this._onLogMessage.bind(this); this._onConsoleAPICall = this._onConsoleAPICall.bind(this); this._onNetworkEvent = this._onNetworkEvent.bind(this); this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this); @@ -4944,18 +4772,17 @@ WebConsoleConnectionProxy.prototype = { /** * Initialize a debugger client and connect it to the debugger server. * * @return object * A promise object that is resolved/rejected based on the success of * the connection initialization. */ - connect: function WCCP_connect() - { + connect: function WCCP_connect() { if (this._connectDefer) { return this._connectDefer.promise; } this._connectDefer = promise.defer(); let timeout = Services.prefs.getIntPref(PREF_CONNECTION_TIMEOUT); this._connectTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); @@ -4995,50 +4822,47 @@ WebConsoleConnectionProxy.prototype = { return connPromise; }, /** * Connection timeout handler. * @private */ - _connectionTimeout: function WCCP__connectionTimeout() - { + _connectionTimeout: function WCCP__connectionTimeout() { let error = { error: "timeout", message: l10n.getStr("connectionTimeout"), }; this._connectDefer.reject(error); }, /** * Attach to the Web Console actor. * @private */ - _attachConsole: function WCCP__attachConsole() - { + _attachConsole: function WCCP__attachConsole() { let listeners = ["PageError", "ConsoleAPI", "NetworkActivity", "FileActivity"]; this.client.attachConsole(this._consoleActor, listeners, this._onAttachConsole); }, /** * The "attachConsole" response handler. * * @private * @param object response * The JSON response object received from the server. * @param object webConsoleClient * The WebConsoleClient instance for the attached console, for the * specific tab we work with. */ - _onAttachConsole: function WCCP__onAttachConsole(response, webConsoleClient) - { + _onAttachConsole: function WCCP__onAttachConsole(response, webConsoleClient) { if (response.error) { Cu.reportError("attachConsole failed: " + response.error + " " + response.message); this._connectDefer.reject(response); return; } this.webConsoleClient = webConsoleClient; @@ -5060,18 +4884,17 @@ WebConsoleConnectionProxy.prototype = { /** * The "cachedMessages" response handler. * * @private * @param object response * The JSON response object received from the server. */ - _onCachedMessages: function WCCP__onCachedMessages(response) - { + _onCachedMessages: function WCCP__onCachedMessages(response) { if (response.error) { Cu.reportError("Web Console getCachedMessages error: " + response.error + " " + response.message); this._connectDefer.reject(response); return; } if (!this._connectTimer) { @@ -5098,69 +4921,65 @@ WebConsoleConnectionProxy.prototype = { * for displaying. * * @private * @param string type * Message type. * @param object packet * The message received from the server. */ - _onPageError: function WCCP__onPageError(type, packet) - { + _onPageError: function WCCP__onPageError(type, packet) { if (this.webConsoleFrame && packet.from == this._consoleActor) { this.webConsoleFrame.handlePageError(packet.pageError); } }, /** * The "logMessage" message type handler. We redirect any message to the UI * for displaying. * * @private * @param string type * Message type. * @param object packet * The message received from the server. */ - _onLogMessage: function WCCP__onLogMessage(type, packet) - { + _onLogMessage: function WCCP__onLogMessage(type, packet) { if (this.webConsoleFrame && packet.from == this._consoleActor) { this.webConsoleFrame.handleLogMessage(packet); } }, /** * The "consoleAPICall" message type handler. We redirect any message to * the UI for displaying. * * @private * @param string type * Message type. * @param object packet * The message received from the server. */ - _onConsoleAPICall: function WCCP__onConsoleAPICall(type, packet) - { + _onConsoleAPICall: function WCCP__onConsoleAPICall(type, packet) { if (this.webConsoleFrame && packet.from == this._consoleActor) { this.webConsoleFrame.handleConsoleAPICall(packet.message); } }, /** * The "networkEvent" message type handler. We redirect any message to * the UI for displaying. * * @private * @param string type * Message type. * @param object networkInfo * The network request information. */ - _onNetworkEvent: function(type, networkInfo) - { + _onNetworkEvent: function(type, networkInfo) { if (this.webConsoleFrame) { this.webConsoleFrame.handleNetworkEvent(networkInfo); } }, /** * The "networkEventUpdate" message type handler. We redirect any message to * the UI for displaying. @@ -5168,122 +4987,114 @@ WebConsoleConnectionProxy.prototype = { * @private * @param string type * Message type. * @param object packet * The message received from the server. * @param object networkInfo * The network request information. */ - _onNetworkEventUpdate: function(type, { packet, networkInfo }) - { + _onNetworkEventUpdate: function(type, { packet, networkInfo }) { if (this.webConsoleFrame) { this.webConsoleFrame.handleNetworkEventUpdate(networkInfo, packet); } }, /** * The "fileActivity" message type handler. We redirect any message to * the UI for displaying. * * @private * @param string type * Message type. * @param object packet * The message received from the server. */ - _onFileActivity: function WCCP__onFileActivity(type, packet) - { + _onFileActivity: function WCCP__onFileActivity(type, packet) { if (this.webConsoleFrame && packet.from == this._consoleActor) { this.webConsoleFrame.handleFileActivity(packet.uri); } }, - _onReflowActivity: function WCCP__onReflowActivity(type, packet) - { + _onReflowActivity: function WCCP__onReflowActivity(type, packet) { if (this.webConsoleFrame && packet.from == this._consoleActor) { this.webConsoleFrame.handleReflowActivity(packet); } }, /** * The "serverLogCall" message type handler. We redirect any message to * the UI for displaying. * * @private * @param string type * Message type. * @param object packet * The message received from the server. */ - _onServerLogCall: function WCCP__onServerLogCall(type, packet) - { + _onServerLogCall: function WCCP__onServerLogCall(type, packet) { if (this.webConsoleFrame && packet.from == this._consoleActor) { this.webConsoleFrame.handleConsoleAPICall(packet.message); } }, /** * The "lastPrivateContextExited" message type handler. When this message is * received the Web Console UI is cleared. * * @private * @param string type * Message type. * @param object packet * The message received from the server. */ _onLastPrivateContextExited: - function WCCP__onLastPrivateContextExited(type, packet) - { + function WCCP__onLastPrivateContextExited(type, packet) { if (this.webConsoleFrame && packet.from == this._consoleActor) { this.webConsoleFrame.jsterm.clearPrivateMessages(); } }, /** * The "will-navigate" and "navigate" event handlers. We redirect any message * to the UI for displaying. * * @private * @param string event * Event type. * @param object packet * The message received from the server. */ - _onTabNavigated: function WCCP__onTabNavigated(event, packet) - { + _onTabNavigated: function WCCP__onTabNavigated(event, packet) { if (!this.webConsoleFrame) { return; } this.webConsoleFrame.handleTabNavigated(event, packet); }, /** * Release an object actor. * * @param string actor * The actor ID to send the request to. */ - releaseActor: function WCCP_releaseActor(actor) - { + releaseActor: function WCCP_releaseActor(actor) { if (this.client) { this.client.release(actor); } }, /** * Disconnect the Web Console from the remote server. * * @return object * A promise object that is resolved when disconnect completes. */ - disconnect: function WCCP_disconnect() - { + disconnect: function WCCP_disconnect() { if (this._disconnecter) { return this._disconnecter.promise; } this._disconnecter = promise.defer(); if (!this.client) { this._disconnecter.resolve(null); @@ -5308,65 +5119,61 @@ WebConsoleConnectionProxy.prototype = { this.connected = false; this.webConsoleFrame = null; this._disconnecter.resolve(null); return this._disconnecter.promise; }, }; -function gSequenceId() -{ +function gSequenceId() { return gSequenceId.n++; } gSequenceId.n = 0; /////////////////////////////////////////////////////////////////////////////// // Context Menu /////////////////////////////////////////////////////////////////////////////// /* * ConsoleContextMenu this used to handle the visibility of context menu items. * * @constructor * @param object owner * The WebConsoleFrame instance that owns this object. */ -function ConsoleContextMenu(owner) -{ +function ConsoleContextMenu(owner) { this.owner = owner; this.popup = this.owner.document.getElementById("output-contextmenu"); this.build = this.build.bind(this); this.popup.addEventListener("popupshowing", this.build); } ConsoleContextMenu.prototype = { lastClickedMessage: null, /* * Handle to show/hide context menu item. */ - build: function CCM_build(event) - { + build: function CCM_build(event) { let metadata = this.getSelectionMetadata(event.rangeParent); for (let element of this.popup.children) { element.hidden = this.shouldHideMenuItem(element, metadata); } }, /* * Get selection information from the view. * * @param nsIDOMElement clickElement * The DOM element the user clicked on. * @return object * Selection metadata. */ - getSelectionMetadata: function CCM_getSelectionMetadata(clickElement) - { + getSelectionMetadata: function CCM_getSelectionMetadata(clickElement) { let metadata = { selectionType: "", selection: new Set(), }; let selectedItems = this.owner.output.getSelectedMessages(); if (!selectedItems.length) { let clickedItem = this.owner.output.getMessageForElement(clickElement); if (clickedItem) { @@ -5404,18 +5211,17 @@ ConsoleContextMenu.prototype = { /* * Determine if an item should be hidden. * * @param nsIDOMElement menuItem * @param object metadata * @return boolean * Whether the given item should be hidden or not. */ - shouldHideMenuItem: function CCM_shouldHideMenuItem(menuItem, metadata) - { + shouldHideMenuItem: function CCM_shouldHideMenuItem(menuItem, metadata) { let selectionType = menuItem.getAttribute("selectiontype"); if (selectionType && !metadata.selectionType == selectionType) { return true; } let selection = menuItem.getAttribute("selection"); if (!selection) { return false; @@ -5432,16 +5238,15 @@ ConsoleContextMenu.prototype = { } return shouldHide; }, /** * Destroy the ConsoleContextMenu object instance. */ - destroy: function CCM_destroy() - { + destroy: function CCM_destroy() { this.popup.removeEventListener("popupshowing", this.build); this.popup = null; this.owner = null; this.lastClickedMessage = null; }, };
--- a/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java +++ b/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java @@ -129,17 +129,17 @@ public class PostSearchFragment extends try { // If the url URI does not have an intent scheme, the intent data will be the entire // URI and its action will be ACTION_VIEW. final Intent i = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); // If the intent URI didn't specify a package, open this in Fennec. if (i.getPackage() == null) { - i.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS); + i.setClassName(view.getContext().getPackageName(), AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS); Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.CONTENT, "search-result"); } else { Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH, TelemetryContract.Method.INTENT, "search-result"); } i.addCategory(Intent.CATEGORY_BROWSABLE);
--- a/mobile/android/search/java/org/mozilla/search/SearchWidget.java +++ b/mobile/android/search/java/org/mozilla/search/SearchWidget.java @@ -62,33 +62,33 @@ public class SearchWidget extends AppWid @Override public void onReceive(final Context context, final Intent intent) { // This will hold the intent to redispatch. final Intent redirect; switch (intent.getAction()) { case ACTION_LAUNCH_BROWSER: redirect = buildRedirectIntent(Intent.ACTION_MAIN, - AppConstants.ANDROID_PACKAGE_NAME, + context.getPackageName(), AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS, intent); Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH, TelemetryContract.Method.WIDGET, "browser"); break; case ACTION_LAUNCH_NEW_TAB: redirect = buildRedirectIntent(Intent.ACTION_VIEW, - AppConstants.ANDROID_PACKAGE_NAME, + context.getPackageName(), AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS, intent); Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH, TelemetryContract.Method.WIDGET, "new-tab"); break; case ACTION_LAUNCH_SEARCH: redirect = buildRedirectIntent(Intent.ACTION_VIEW, - AppConstants.ANDROID_PACKAGE_NAME, + context.getPackageName(), AppConstants.MOZ_ANDROID_SEARCH_INTENT_CLASS, intent); Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH, TelemetryContract.Method.WIDGET, "search"); break; default: redirect = null; }
--- a/toolkit/mozapps/extensions/content/extensions.xul +++ b/toolkit/mozapps/extensions/content/extensions.xul @@ -180,16 +180,17 @@ value="&updates.installed.label;"/> <label id="updates-downloaded" hidden="true" value="&updates.downloaded.label;"/> <button id="updates-restart-btn" class="button-link" hidden="true" label="&updates.restart.label;" command="cmd_restartApp"/> </hbox> <button id="show-disabled-unsigned-extensions" hidden="true" + class="warning" label="&showUnsignedExtensions.button.label;" command="cmd_showUnsignedExtensions"/> <toolbarbutton id="header-utils-btn" class="header-button" type="menu" tooltiptext="&toolsMenu.tooltip;"> <menupopup id="utils-menu"> <menuitem id="utils-updateNow" label="&updates.checkForUpdates.label;" accesskey="&updates.checkForUpdates.accesskey;"
--- a/toolkit/themes/shared/extensions/extensions.inc.css +++ b/toolkit/themes/shared/extensions/extensions.inc.css @@ -129,16 +129,21 @@ } .loading { list-style-image: url("chrome://global/skin/icons/loading_16.png"); padding-left: 20px; padding-right: 20px; } +button.warning { + list-style-image: url("chrome://mozapps/skin/extensions/alerticon-warning.svg"); + list-stye-position: inside; +} + /*** category selector ***/ #categories { padding-top: 0; } .category[selected="true"]:hover {