mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
merge fx-team to mozilla-central a=merge
This commit is contained in:
commit
fa1497dd83
@ -100,7 +100,6 @@ devtools.jar:
|
||||
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)
|
||||
@ -219,6 +218,8 @@ devtools.jar:
|
||||
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)
|
||||
|
31
devtools/client/locales/en-US/jit-optimizations.properties
Normal file
31
devtools/client/locales/en-US/jit-optimizations.properties
Normal file
@ -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.
|
@ -140,10 +140,6 @@
|
||||
<!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")" -->
|
||||
|
@ -141,22 +141,6 @@ recordingsList.saveDialogJSONFilter=JSON Files
|
||||
# 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.
|
||||
|
@ -14,6 +14,7 @@
|
||||
<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"/>
|
||||
|
9
devtools/client/performance/components/moz.build
Normal file
9
devtools/client/performance/components/moz.build
Normal file
@ -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',
|
||||
)
|
145
devtools/client/performance/components/optimizations-item.js
Normal file
145
devtools/client/performance/components/optimizations-item.js
Normal file
@ -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);
|
||||
},
|
||||
});
|
183
devtools/client/performance/components/optimizations.js
Normal file
183
devtools/client/performance/components/optimizations.js
Normal file
@ -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,
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
@ -5,6 +5,7 @@
|
||||
|
||||
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");
|
||||
|
||||
@ -20,6 +21,8 @@ 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();
|
||||
|
||||
@ -149,6 +152,26 @@ function parseLocation(location, fallbackLine, fallbackColumn) {
|
||||
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;
|
||||
|
@ -52,7 +52,7 @@ const SUCCESSFUL_OUTCOMES = [
|
||||
* 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
|
||||
@ -184,23 +184,33 @@ const JITOptimizations = function (rawSites, stringTable) {
|
||||
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
|
||||
|
@ -4,6 +4,7 @@
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
DIRS += [
|
||||
'components',
|
||||
'legacy',
|
||||
'modules',
|
||||
]
|
||||
|
@ -18,6 +18,9 @@ Object.defineProperty(this, "EVENTS", {
|
||||
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");
|
||||
|
@ -7,6 +7,8 @@
|
||||
<?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;
|
||||
@ -26,7 +28,6 @@
|
||||
<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"/>
|
||||
@ -318,17 +319,7 @@
|
||||
<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>
|
||||
|
||||
|
@ -43,7 +43,9 @@ skip-if = true # Bug 1161817
|
||||
[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]
|
||||
|
@ -11,7 +11,7 @@ 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] };
|
||||
|
||||
|
@ -20,6 +20,9 @@ const CONTENT_LOCATIONS = [
|
||||
"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)",
|
||||
@ -72,6 +75,7 @@ add_task(function () {
|
||||
["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],
|
||||
|
@ -29,14 +29,15 @@ var JsCallTreeView = Heritage.extend(DetailsSubview, {
|
||||
|
||||
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);
|
||||
@ -67,25 +68,48 @@ var JsCallTreeView = Heritage.extend(DetailsSubview, {
|
||||
} 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);
|
||||
},
|
||||
|
||||
|
@ -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);
|
@ -26,7 +26,7 @@ const Frame = module.exports = createClass({
|
||||
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,
|
||||
@ -46,7 +46,7 @@ const Frame = module.exports = createClass({
|
||||
tooltip += `:${frame.column}`;
|
||||
}
|
||||
|
||||
let sourceString = `${frame.source}:${frame.line}`;
|
||||
let sourceString = `${long}:${frame.line}`;
|
||||
if (frame.column) {
|
||||
sourceString += `:${frame.column}`;
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
support-files =
|
||||
head.js
|
||||
|
||||
[test_frame_01.html]
|
||||
[test_frame_02.html]
|
||||
[test_tree_01.html]
|
||||
[test_tree_02.html]
|
||||
[test_tree_03.html]
|
||||
|
@ -23,6 +23,59 @@ var { require: browserRequire } = BrowserLoader("resource://devtools/client/shar
|
||||
|
||||
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
|
||||
@ -78,51 +131,26 @@ var TEST_TREE = {
|
||||
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 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);
|
||||
function checkFrameTooltips (component, mainTooltip, linkTooltip) {
|
||||
let el = component.getDOMNode();
|
||||
is(el.getAttribute("title"), mainTooltip);
|
||||
is(el.querySelector("a.frame-link-filename").getAttribute("title"), linkTooltip);
|
||||
}
|
||||
|
@ -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>
|
@ -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>
|
46
devtools/client/themes/components-frame.css
Normal file
46
devtools/client/themes/components-frame.css
Normal file
@ -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);
|
||||
}
|
143
devtools/client/themes/jit-optimizations.css
Normal file
143
devtools/client/themes/jit-optimizations.css
Normal file
@ -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;
|
||||
}
|
@ -465,39 +465,6 @@ html, body, #app, #memory-tool {
|
||||
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;
|
||||
|
@ -272,6 +272,8 @@
|
||||
.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,
|
||||
@ -609,153 +611,6 @@ menuitem.marker-color-graphs-grey:before,
|
||||
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
|
||||
*
|
||||
@ -787,3 +642,32 @@ menuitem.experimental-option::before {
|
||||
#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;
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -134,7 +134,7 @@ public class PostSearchFragment extends Fragment {
|
||||
|
||||
// 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 {
|
||||
|
@ -67,7 +67,7 @@ public class SearchWidget extends AppWidgetProvider {
|
||||
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,
|
||||
@ -75,7 +75,7 @@ public class SearchWidget extends AppWidgetProvider {
|
||||
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,
|
||||
@ -83,7 +83,7 @@ public class SearchWidget extends AppWidgetProvider {
|
||||
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,
|
||||
|
@ -185,6 +185,7 @@
|
||||
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"
|
||||
|
@ -134,6 +134,11 @@
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
||||
button.warning {
|
||||
list-style-image: url("chrome://mozapps/skin/extensions/alerticon-warning.svg");
|
||||
list-stye-position: inside;
|
||||
}
|
||||
|
||||
|
||||
/*** category selector ***/
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user