Files
palemoon27/devtools/client/debugger/views/variable-bubble-view.js
roytam1 c656138b3f import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1165052 - Part 9: Use ArraySpeciesCreate in Array.prototype.filter. r=efaust (198da18845)
- Bug 1165052 - Part 10: Use ArraySpeciesCreate in Array.prototype.map. r=efaust (9dcd625daa)
- Bug 1263618: Adapt assertions in RegExp*Raw functions for int32_t; r=arai (8ad46db66d)
- Bug 1263340 - Part 1: Use internal slot for global and sticky flags in RegExpBuiltinExec. r=h4writer (9b5fbe6358)
- Bug 1263340 - Part 2: Use internal slot for sticky flag in @@replace and @@search optimized path. r=h4writer (2b64567c6a)
- Bug 1264264 - Add optimized path for RegExp.prototype[@@replace] with functional replace and substitution. r=till (e55722bc0a)
- Bug 1263340 - Part 3: Use internal slot for sticky flag in RegExp native functions. r=h4writer (725cf7b9b0)
- Bug 1264264 - Part 2: Enable optimization for packers again in RegExp.prototype[@@replace]. r=h4writer (7f4b819e40)
- Bug 1268056 - Check if |this| value is a RegExp object in the optimized path in RegExpSplit. r=h4writer (8891f0a3ef)
- Bug 1263340 - Part 4: Followup for @@split - Apply optimized path for empty string too. r=till (956d1804a5)
- Bug 1261207 - Forward declare js::ScriptSource instead of casting `¦void*` pointers; r=jimb (4914273e96)
- Bug 1041586 - Implement Symbol.isConcatSpreadable. r=arai (238e5f97c6)
- Bug 1041586 - Use IsConcatSpreadable in Array.prototype.concat. r=arai (33333cb30f)
- Bug 1165052 - Part 11: Use ArraySpeciesCreate in Array.prototype.slice. r=efaust (45f34c0a87)
- Bug 1165052 - Part 12: Use ArraySpeciesCreate in Array.prototype.splice. r=efaust,evilpie (096f9ff03d)
- Bug 1165052 - Part 13: Add tests for ArraySpeciesCreate. r=efaust (4403642d3c)
- Bug 1041586 - Tests. r=arai (efc2d06863)
- Bug 1267364 - Check isNative every time in GetStringDataProperty. r=h4writer (6e7898c494)
- Bug 1041586 - Fold away property accesses to not-defined properties. r=jandem (216e6387db)
- add emacs (fa858771bf)
- Bug 1268574 - Check the outparam JSFunction* value after GetGetterPure. r=lth (3d80c79337)
- Bug 1263888 - Push TypeBarrier after ArraySlice. r=jandem (2617518401)
- Bug 1255316 - IonMonkey: Enable folding of MLoadUnboxedObjectOrNull with the stored value, r=jandem (0f75675721)
- Bug 1252313 - Fix wasm i64 shift ops with a constant rhs. r=bbouvier (2c05b901c6)
- Bug 1263609: SharedStubs - Port JSOP_POW to shared stubs, r=efaust (54523590d9)
- Bug 1247880 - Only remove MUrsh operands when the input of MUrsh is guaranteed to be unsigned. r=sunfish (4a29e0b071)
- Bug 1254528: IonMonkey - Check slot before removing load with value of store, r=nbp (3527eb5263)
- Bug 1255316 - IonMonkey: Also take into account the offsetAdjustment when folding MLoadUnboxedObjectOrNull, r=jandem (9cbea97df1)
- Bug 1263558 - Part 1: Self-host Array generics. r=till,bholley (e1dc0c54c8)
- Bug 1103588 - Part 1: Replace deprecated String#contains with String#includes in browser tests and extensions. r=dolske (37558174e8)
- Bug 1103588 - Part 3: Replace deprecated String#contains warning with an expression closure warning in sharedWorker_sharedWorker.js test. r=bz (221ff40ebc)
- Bug 1103588 - Part 4: Replace deprecated String#contains in js tests. r=till (a794c0b385)
- Bug 1103588 - Part 5: Remove deprecated String#contains function; use String#includes instead. r=till (bc1365603e)
- Bug 964709 - Updates Parser regex and tests to support self-closing script tags, r=vporof (f04c475ec8)
- Bug 1224726 - Do not attempt to parse source file when searching in debugger if text > 1MB;r=jlongster (8743b7bf19)
- Bug 1243243 - Use standard license boilerplate in debugger js files. r=jlongster (82684adab3)
- Bug 1240804 - Allow remaining unhandled rejections. r=ejpbruel (c30bd6dad2)
- Bug 1207702 - Fix a bug in test_promises_object_timetosettle-02.js;r=fitzgen (de776ce006)
- Bug 1103588 - Part 2: Replace deprecated String#contains with String#includes in devtools tests. r=jryans (801926270d)
- Bug 1263558 - Part 2: Self-host String generics. r=till (4e5766489b)
- Bug 1263558 - Part 3: Remove JSFUN_GENERIC_NATIVE. r=till (3f94c198b0)
- Bug 1263558 - Part 4: Call initBuiltinConstructor after defining properties in InitStringClass. r=till (f25edc6096)
- Bug 1263558 - Part 0.1: Handle OOM inside BuildDominatorTree at AnalyzeNewScriptDefiniteProperties and AnalyzeArgumentsUsage. r=jandem (0f4745e9d4)
- Bug 1266573 - Add the JS::ubi::dumpPaths debug utility; r=jimb (9a8680d7c7)
- Bug 1266835 - Request names from the rootlist in JS::ubi::dumpPaths and clean up formatting of dumped paths. r=jimb (1e15dd3ab9)
- Bug 1263332 - Avoid startup JavaScript strict warning in DirectoryLinksProvider.jsm. r=MattN (06ff76be4d)
- Bug 1259911: Only add predecessors to the join block once; r=sunfish (53a9522e68)
- Bug 1258905: Remove a bunch of dead IPC code. r=jld (0d9f930a14)
- Bug 1266869 - Print a message when no retaining paths are found in JS::ubi::dumpPaths. r=jimb (77a541b4d1)
2024-04-12 11:43:55 +08:00

307 lines
11 KiB
JavaScript

/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from ../debugger-controller.js */
/* import-globals-from ../debugger-view.js */
/* import-globals-from ../utils.js */
/* globals document, window */
"use strict";
/**
* Functions handling the variables bubble UI.
*/
function VariableBubbleView(DebuggerController, DebuggerView) {
dumpn("VariableBubbleView was instantiated");
this.StackFrames = DebuggerController.StackFrames;
this.Parser = DebuggerController.Parser;
this.DebuggerView = DebuggerView;
this._onMouseMove = this._onMouseMove.bind(this);
this._onMouseOut = this._onMouseOut.bind(this);
this._onPopupHiding = this._onPopupHiding.bind(this);
}
VariableBubbleView.prototype = {
/**
* Initialization function, called when the debugger is started.
*/
initialize: function() {
dumpn("Initializing the VariableBubbleView");
this._toolbox = DebuggerController._toolbox;
this._editorContainer = document.getElementById("editor");
this._editorContainer.addEventListener("mousemove", this._onMouseMove, false);
this._editorContainer.addEventListener("mouseout", this._onMouseOut, false);
this._tooltip = new Tooltip(document, {
closeOnEvents: [{
emitter: this._toolbox,
event: "select"
}, {
emitter: this._editorContainer,
event: "scroll",
useCapture: true
}]
});
this._tooltip.defaultPosition = EDITOR_VARIABLE_POPUP_POSITION;
this._tooltip.defaultShowDelay = EDITOR_VARIABLE_HOVER_DELAY;
this._tooltip.panel.addEventListener("popuphiding", this._onPopupHiding);
},
/**
* Destruction function, called when the debugger is closed.
*/
destroy: function() {
dumpn("Destroying the VariableBubbleView");
this._tooltip.panel.removeEventListener("popuphiding", this._onPopupHiding);
this._editorContainer.removeEventListener("mousemove", this._onMouseMove, false);
this._editorContainer.removeEventListener("mouseout", this._onMouseOut, false);
},
/**
* Specifies whether literals can be (redundantly) inspected in a popup.
* This behavior is deprecated, but still tested in a few places.
*/
_ignoreLiterals: true,
/**
* Searches for an identifier underneath the specified position in the
* source editor, and if found, opens a VariablesView inspection popup.
*
* @param number x, y
* The left/top coordinates where to look for an identifier.
*/
_findIdentifier: function(x, y) {
let editor = this.DebuggerView.editor;
// Calculate the editor's line and column at the current x and y coords.
let hoveredPos = editor.getPositionFromCoords({ left: x, top: y });
let hoveredOffset = editor.getOffset(hoveredPos);
let hoveredLine = hoveredPos.line;
let hoveredColumn = hoveredPos.ch;
// A source contains multiple scripts. Find the start index of the script
// containing the specified offset relative to its parent source.
let contents = editor.getText();
let location = this.DebuggerView.Sources.selectedValue;
let parsedSource = this.Parser.get(contents, location);
let scriptInfo = parsedSource.getScriptInfo(hoveredOffset);
// If the script length is negative, we're not hovering JS source code.
if (scriptInfo.length == -1) {
return;
}
// Using the script offset, determine the actual line and column inside the
// script, to use when finding identifiers.
let scriptStart = editor.getPosition(scriptInfo.start);
let scriptLineOffset = scriptStart.line;
let scriptColumnOffset = (hoveredLine == scriptStart.line ? scriptStart.ch : 0);
let scriptLine = hoveredLine - scriptLineOffset;
let scriptColumn = hoveredColumn - scriptColumnOffset;
let identifierInfo = parsedSource.getIdentifierAt({
line: scriptLine + 1,
column: scriptColumn,
scriptIndex: scriptInfo.index,
ignoreLiterals: this._ignoreLiterals
});
// If the info is null, we're not hovering any identifier.
if (!identifierInfo) {
return;
}
// Transform the line and column relative to the parsed script back
// to the context of the parent source.
let { start: identifierStart, end: identifierEnd } = identifierInfo.location;
let identifierCoords = {
line: identifierStart.line + scriptLineOffset,
column: identifierStart.column + scriptColumnOffset,
length: identifierEnd.column - identifierStart.column
};
// Evaluate the identifier in the current stack frame and show the
// results in a VariablesView inspection popup.
this.StackFrames.evaluate(identifierInfo.evalString)
.then(frameFinished => {
if ("return" in frameFinished) {
this.showContents({
coords: identifierCoords,
evalPrefix: identifierInfo.evalString,
objectActor: frameFinished.return
});
} else {
let msg = "Evaluation has thrown for: " + identifierInfo.evalString;
console.warn(msg);
dumpn(msg);
}
})
.then(null, err => {
let msg = "Couldn't evaluate: " + err.message;
console.error(msg);
dumpn(msg);
});
},
/**
* Shows an inspection popup for a specified object actor grip.
*
* @param string object
* An object containing the following properties:
* - coords: the inspected identifier coordinates in the editor,
* containing the { line, column, length } properties.
* - evalPrefix: a prefix for the variables view evaluation macros.
* - objectActor: the value grip for the object actor.
*/
showContents: function({ coords, evalPrefix, objectActor }) {
let editor = this.DebuggerView.editor;
let { line, column, length } = coords;
// Highlight the function found at the mouse position.
this._markedText = editor.markText(
{ line: line - 1, ch: column },
{ line: line - 1, ch: column + length });
// If the grip represents a primitive value, use a more lightweight
// machinery to display it.
if (VariablesView.isPrimitive({ value: objectActor })) {
let className = VariablesView.getClass(objectActor);
let textContent = VariablesView.getString(objectActor);
this._tooltip.setTextContent({
messages: [textContent],
messagesClass: className,
containerClass: "plain"
}, [{
label: L10N.getStr('addWatchExpressionButton'),
className: "dbg-expression-button",
command: () => {
this.DebuggerView.VariableBubble.hideContents();
this.DebuggerView.WatchExpressions.addExpression(evalPrefix, true);
}
}]);
} else {
this._tooltip.setVariableContent(objectActor, {
searchPlaceholder: L10N.getStr("emptyPropertiesFilterText"),
searchEnabled: Prefs.variablesSearchboxVisible,
eval: (variable, value) => {
let string = variable.evaluationMacro(variable, value);
this.StackFrames.evaluate(string);
this.DebuggerView.VariableBubble.hideContents();
}
}, {
getEnvironmentClient: aObject => gThreadClient.environment(aObject),
getObjectClient: aObject => gThreadClient.pauseGrip(aObject),
simpleValueEvalMacro: this._getSimpleValueEvalMacro(evalPrefix),
getterOrSetterEvalMacro: this._getGetterOrSetterEvalMacro(evalPrefix),
overrideValueEvalMacro: this._getOverrideValueEvalMacro(evalPrefix)
}, {
fetched: (aEvent, aType) => {
if (aType == "properties") {
window.emit(EVENTS.FETCHED_BUBBLE_PROPERTIES);
}
}
}, [{
label: L10N.getStr("addWatchExpressionButton"),
className: "dbg-expression-button",
command: () => {
this.DebuggerView.VariableBubble.hideContents();
this.DebuggerView.WatchExpressions.addExpression(evalPrefix, true);
}
}], this._toolbox);
}
this._tooltip.show(this._markedText.anchor);
},
/**
* Hides the inspection popup.
*/
hideContents: function() {
clearNamedTimeout("editor-mouse-move");
this._tooltip.hide();
},
/**
* Checks whether the inspection popup is shown.
*
* @return boolean
* True if the panel is shown or showing, false otherwise.
*/
contentsShown: function() {
return this._tooltip.isShown();
},
/**
* Functions for getting customized variables view evaluation macros.
*
* @param string aPrefix
* See the corresponding VariablesView.* functions.
*/
_getSimpleValueEvalMacro: function(aPrefix) {
return (item, string) =>
VariablesView.simpleValueEvalMacro(item, string, aPrefix);
},
_getGetterOrSetterEvalMacro: function(aPrefix) {
return (item, string) =>
VariablesView.getterOrSetterEvalMacro(item, string, aPrefix);
},
_getOverrideValueEvalMacro: function(aPrefix) {
return (item, string) =>
VariablesView.overrideValueEvalMacro(item, string, aPrefix);
},
/**
* The mousemove listener for the source editor.
*/
_onMouseMove: function(e) {
// Prevent the variable inspection popup from showing when the thread client
// is not paused, or while a popup is already visible, or when the user tries
// to select text in the editor.
let isResumed = gThreadClient && gThreadClient.state != "paused";
let isSelecting = this.DebuggerView.editor.somethingSelected() && e.buttons > 0;
let isPopupVisible = !this._tooltip.isHidden();
if (isResumed || isSelecting || isPopupVisible) {
clearNamedTimeout("editor-mouse-move");
return;
}
// Allow events to settle down first. If the mouse hovers over
// a certain point in the editor long enough, try showing a variable bubble.
setNamedTimeout("editor-mouse-move",
EDITOR_VARIABLE_HOVER_DELAY, () => this._findIdentifier(e.clientX, e.clientY));
},
/**
* The mouseout listener for the source editor container node.
*/
_onMouseOut: function() {
clearNamedTimeout("editor-mouse-move");
},
/**
* Listener handling the popup hiding event.
*/
_onPopupHiding: function({ target }) {
if (this._tooltip.panel != target) {
return;
}
if (this._markedText) {
this._markedText.clear();
this._markedText = null;
}
if (!this._tooltip.isEmpty()) {
this._tooltip.empty();
}
},
_editorContainer: null,
_markedText: null,
_tooltip: null
};
DebuggerView.VariableBubble = new VariableBubbleView(DebuggerController, DebuggerView);