mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge fx-team to m-c
This commit is contained in:
commit
413b9710ef
@ -374,6 +374,8 @@ StackFrames.prototype = {
|
||||
get activeThread() DebuggerController.activeThread,
|
||||
autoScopeExpand: false,
|
||||
currentFrame: null,
|
||||
syncedWatchExpressions: null,
|
||||
currentWatchExpressions: null,
|
||||
currentBreakpointLocation: null,
|
||||
currentEvaluation: null,
|
||||
currentException: null,
|
||||
@ -428,7 +430,7 @@ StackFrames.prototype = {
|
||||
break;
|
||||
// If paused by a client evaluation, store the evaluated value.
|
||||
case "clientEvaluated":
|
||||
this.currentEvaluation = aPacket.why.frameFinished.return;
|
||||
this.currentEvaluation = aPacket.why.frameFinished;
|
||||
break;
|
||||
// If paused by an exception, store the exception value.
|
||||
case "exception":
|
||||
@ -445,6 +447,11 @@ StackFrames.prototype = {
|
||||
*/
|
||||
_onResumed: function SF__onResumed() {
|
||||
DebuggerView.editor.setDebugLocation(-1);
|
||||
|
||||
// Prepare the watch expression evaluation string for the next pause.
|
||||
if (!this._isWatchExpressionsEvaluation) {
|
||||
this.currentWatchExpressions = this.syncedWatchExpressions;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -455,7 +462,6 @@ StackFrames.prototype = {
|
||||
if (!this.activeThread.cachedFrames.length) {
|
||||
return;
|
||||
}
|
||||
DebuggerView.StackFrames.empty();
|
||||
|
||||
// Conditional breakpoints are { breakpoint, expression } tuples. The
|
||||
// boolean evaluation of the expression decides if the active thread
|
||||
@ -468,28 +474,56 @@ StackFrames.prototype = {
|
||||
// Evaluating the current breakpoint's conditional expression will
|
||||
// cause the stack frames to be cleared and active thread to pause,
|
||||
// sending a 'clientEvaluated' packed and adding the frames again.
|
||||
this.evaluate("(" + conditionalExpression + ")", 0);
|
||||
this.evaluate(conditionalExpression, 0);
|
||||
this._isConditionalBreakpointEvaluation = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Got our evaluation of the current breakpoint's conditional expression.
|
||||
if (this._isConditionalBreakpointEvaluation) {
|
||||
this._isConditionalBreakpointEvaluation = false;
|
||||
|
||||
// If the breakpoint's conditional expression evaluation is falsy,
|
||||
// automatically resume execution.
|
||||
if (VariablesView.isFalsy({ value: this.currentEvaluation })) {
|
||||
if (VariablesView.isFalsy({ value: this.currentEvaluation.return })) {
|
||||
this.activeThread.resume();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Watch expressions are evaluated in the context of the topmost frame,
|
||||
// and the results and displayed in the variables view.
|
||||
if (this.currentWatchExpressions) {
|
||||
// Evaluation causes the stack frames to be cleared and active thread to
|
||||
// pause, sending a 'clientEvaluated' packed and adding the frames again.
|
||||
this.evaluate(this.currentWatchExpressions, 0);
|
||||
this._isWatchExpressionsEvaluation = true;
|
||||
return;
|
||||
}
|
||||
// Got our evaluation of the current watch expressions.
|
||||
if (this._isWatchExpressionsEvaluation) {
|
||||
this._isWatchExpressionsEvaluation = false;
|
||||
// If an error was thrown during the evaluation of the watch expressions,
|
||||
// then at least one expression evaluation could not be performed.
|
||||
if (this.currentEvaluation.throw) {
|
||||
DebuggerView.WatchExpressions.removeExpression(0);
|
||||
DebuggerController.StackFrames.syncWatchExpressions();
|
||||
return;
|
||||
}
|
||||
// If the watch expressions were evaluated successfully, attach
|
||||
// the results to the topmost frame.
|
||||
let topmostFrame = this.activeThread.cachedFrames[0];
|
||||
topmostFrame.watchExpressionsEvaluation = this.currentEvaluation.return;
|
||||
}
|
||||
|
||||
|
||||
// Make sure all the previous stackframes are removed before re-adding them.
|
||||
DebuggerView.StackFrames.empty();
|
||||
|
||||
for (let frame of this.activeThread.cachedFrames) {
|
||||
this._addFrame(frame);
|
||||
}
|
||||
if (!this.currentFrame) {
|
||||
if (this.currentFrame == null) {
|
||||
this.selectFrame(0);
|
||||
}
|
||||
if (this.activeThread.moreFrames) {
|
||||
@ -502,6 +536,7 @@ StackFrames.prototype = {
|
||||
*/
|
||||
_onFramesCleared: function SF__onFramesCleared() {
|
||||
this.currentFrame = null;
|
||||
this.currentWatchExpressions = null;
|
||||
this.currentBreakpointLocation = null;
|
||||
this.currentEvaluation = null;
|
||||
this.currentException = null;
|
||||
@ -523,6 +558,7 @@ StackFrames.prototype = {
|
||||
DebuggerView.StackFrames.empty();
|
||||
DebuggerView.Variables.empty(0);
|
||||
DebuggerView.Breakpoints.unhighlightBreakpoint();
|
||||
DebuggerView.WatchExpressions.toggleContents(true);
|
||||
window.dispatchEvent("Debugger:AfterFramesCleared");
|
||||
},
|
||||
|
||||
@ -538,7 +574,7 @@ StackFrames.prototype = {
|
||||
if (!frame) {
|
||||
return;
|
||||
}
|
||||
let environment = frame.environment;
|
||||
let { environment, watchExpressionsEvaluation } = frame;
|
||||
let { url, line } = frame.where;
|
||||
|
||||
// Check if the frame does not represent the evaluation of debuggee code.
|
||||
@ -552,11 +588,27 @@ StackFrames.prototype = {
|
||||
DebuggerView.StackFrames.highlightFrame(aDepth);
|
||||
// Highlight the breakpoint at the specified url and line if it exists.
|
||||
DebuggerView.Breakpoints.highlightBreakpoint(url, line);
|
||||
// Don't display the watch expressions textbox inputs in the pane.
|
||||
DebuggerView.WatchExpressions.toggleContents(false);
|
||||
// Start recording any added variables or properties in any scope.
|
||||
DebuggerView.Variables.createHierarchy();
|
||||
// Clear existing scopes and create each one dynamically.
|
||||
DebuggerView.Variables.empty();
|
||||
|
||||
// If watch expressions evaluation results are available, create a scope
|
||||
// to contain all the values.
|
||||
if (watchExpressionsEvaluation) {
|
||||
let label = L10N.getStr("watchExpressionsScopeLabel");
|
||||
let arrow = L10N.getStr("watchExpressionsSeparatorLabel");
|
||||
let scope = DebuggerView.Variables.addScope(label);
|
||||
scope.separator = arrow;
|
||||
|
||||
// The evaluation hasn't thrown, so display the returned results and
|
||||
// always expand the watch expressions scope by default.
|
||||
this._fetchWatchExpressions(scope, watchExpressionsEvaluation);
|
||||
scope.expand();
|
||||
}
|
||||
|
||||
do {
|
||||
// Create a scope to contain all the inspected variables.
|
||||
let label = this._getScopeLabel(environment);
|
||||
@ -624,6 +676,39 @@ StackFrames.prototype = {
|
||||
aVar.onexpand = callback;
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds the watch expressions evaluation results to a scope in the view.
|
||||
*
|
||||
* @param Scope aScope
|
||||
* The scope where the watch expressions will be placed into.
|
||||
* @param object aExp
|
||||
* The grip of the evaluation results.
|
||||
*/
|
||||
_fetchWatchExpressions: function SF__fetchWatchExpressions(aScope, aExp) {
|
||||
// Retrieve the expressions only once.
|
||||
if (aScope.fetched) {
|
||||
return;
|
||||
}
|
||||
aScope.fetched = true;
|
||||
|
||||
// Add nodes for every watch expression in scope.
|
||||
this.activeThread.pauseGrip(aExp).getPrototypeAndProperties(function(aResponse) {
|
||||
let ownProperties = aResponse.ownProperties;
|
||||
let totalExpressions = DebuggerView.WatchExpressions.totalItems;
|
||||
|
||||
for (let i = 0; i < totalExpressions; i++) {
|
||||
let name = DebuggerView.WatchExpressions.getExpression(i);
|
||||
let expVal = ownProperties[i].value;
|
||||
let expRef = aScope.addVar(name, ownProperties[i]);
|
||||
this._addVarExpander(expRef, expVal);
|
||||
}
|
||||
|
||||
// Signal that watch expressions have been fetched.
|
||||
window.dispatchEvent("Debugger:FetchedWatchExpressions");
|
||||
DebuggerView.Variables.commitHierarchy();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds variables to a scope in the view. Triggered when a scope is
|
||||
* expanded or is hovered. It does not expand the scope.
|
||||
@ -760,7 +845,7 @@ StackFrames.prototype = {
|
||||
}
|
||||
|
||||
// Add the variable's __proto__.
|
||||
if (prototype.type != "null") {
|
||||
if (prototype && prototype.type != "null") {
|
||||
aVar.addProperty("__proto__", { value: prototype });
|
||||
// Expansion handlers must be set after the properties are added.
|
||||
this._addVarExpander(aVar.get("__proto__"), prototype);
|
||||
@ -830,6 +915,27 @@ StackFrames.prototype = {
|
||||
this.activeThread.cachedFrames.length + CALL_STACK_PAGE_SIZE);
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates a list of watch expressions to evaluate on each pause.
|
||||
*/
|
||||
syncWatchExpressions: function SF_syncWatchExpressions() {
|
||||
let list = DebuggerView.WatchExpressions.getExpressions();
|
||||
|
||||
if (list.length) {
|
||||
this.syncedWatchExpressions =
|
||||
this.currentWatchExpressions = "[" + list.map(function(str)
|
||||
"(function() {" +
|
||||
"try { return eval(\"" + str.replace(/"/g, "\\$&") + "\"); }" +
|
||||
"catch(e) { return e.name + ': ' + e.message; }" +
|
||||
"})()"
|
||||
).join(",") + "]";
|
||||
} else {
|
||||
this.syncedWatchExpressions =
|
||||
this.currentWatchExpressions = null;
|
||||
}
|
||||
this._onFrames();
|
||||
},
|
||||
|
||||
/**
|
||||
* Evaluate an expression in the context of the selected frame. This is used
|
||||
* for modifying the value of variables or properties in scope.
|
||||
@ -839,7 +945,7 @@ StackFrames.prototype = {
|
||||
* @param number aFrame [optional]
|
||||
* The frame depth used for evaluation.
|
||||
*/
|
||||
evaluate: function SF_evaluate(aExpression, aFrame = this.currentFrame) {
|
||||
evaluate: function SF_evaluate(aExpression, aFrame = this.currentFrame || 0) {
|
||||
let frame = this.activeThread.cachedFrames[aFrame];
|
||||
this.activeThread.eval(frame.actor, aExpression);
|
||||
}
|
||||
@ -958,6 +1064,10 @@ SourceScripts.prototype = {
|
||||
_onScriptsAdded: function SS__onScriptsAdded(aResponse) {
|
||||
// Add all the sources in the debugger view sources container.
|
||||
for (let script of aResponse.scripts) {
|
||||
// Ignore scripts generated from 'clientEvaluate' packets.
|
||||
if (script.url == "debugger eval code") {
|
||||
continue;
|
||||
}
|
||||
this._addSource(script);
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,7 @@ create({ constructor: StackFramesView, proto: MenuContainer.prototype }, {
|
||||
*/
|
||||
destroy: function DVSF_destroy() {
|
||||
dumpn("Destroying the StackFramesView");
|
||||
this._container.removeEventListener("click", this._onClick, true);
|
||||
this._container.removeEventListener("click", this._onClick, false);
|
||||
this._container.removeEventListener("scroll", this._onScroll, true);
|
||||
window.removeEventListener("resize", this._onScroll, true);
|
||||
},
|
||||
@ -909,7 +909,8 @@ create({ constructor: BreakpointsView, proto: MenuContainer.prototype }, {
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets an identifier for a breakpoint item for the current cache.
|
||||
* Gets an identifier for a breakpoint item in the current cache.
|
||||
* @return string
|
||||
*/
|
||||
_key: function DVB__key(aSourceLocation, aLineNumber) {
|
||||
return aSourceLocation + aLineNumber;
|
||||
@ -924,6 +925,247 @@ create({ constructor: BreakpointsView, proto: MenuContainer.prototype }, {
|
||||
_editorContextMenuLineNumber: -1
|
||||
});
|
||||
|
||||
/**
|
||||
* Functions handling the watch expressions UI.
|
||||
*/
|
||||
function WatchExpressionsView() {
|
||||
dumpn("WatchExpressionsView was instantiated");
|
||||
MenuContainer.call(this);
|
||||
this._createItemView = this._createItemView.bind(this);
|
||||
this._onClick = this._onClick.bind(this);
|
||||
this._onClose = this._onClose.bind(this);
|
||||
this._onBlur = this._onBlur.bind(this);
|
||||
this._onKeyPress = this._onKeyPress.bind(this);
|
||||
this._onMouseOver = this._onMouseOver.bind(this);
|
||||
this._onMouseOut = this._onMouseOut.bind(this);
|
||||
}
|
||||
|
||||
create({ constructor: WatchExpressionsView, proto: MenuContainer.prototype }, {
|
||||
/**
|
||||
* Initialization function, called when the debugger is started.
|
||||
*/
|
||||
initialize: function DVWE_initialize() {
|
||||
dumpn("Initializing the WatchExpressionsView");
|
||||
this._container = new StackList(document.getElementById("expressions"));
|
||||
this._variables = document.getElementById("variables");
|
||||
|
||||
this._container.permaText = L10N.getStr("addWatchExpressionText");
|
||||
this._container.itemFactory = this._createItemView;
|
||||
this._container.addEventListener("click", this._onClick, false);
|
||||
|
||||
this._cache = [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Destruction function, called when the debugger is closed.
|
||||
*/
|
||||
destroy: function DVWE_destroy() {
|
||||
dumpn("Destroying the WatchExpressionsView");
|
||||
this._container.removeEventListener("click", this._onClick, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a watch expression in this container.
|
||||
*
|
||||
* @param string aExpression [optional]
|
||||
* An optional initial watch expression text.
|
||||
*/
|
||||
addExpression: function DVWE_addExpression(aExpression = "") {
|
||||
// Watch expressions are UI elements which benefit from visible panes.
|
||||
DebuggerView.showPanesSoon();
|
||||
|
||||
// Append a watch expression item to this container.
|
||||
let expressionItem = this.push("", aExpression, {
|
||||
forced: { atIndex: 0 },
|
||||
unsorted: true,
|
||||
relaxed: true,
|
||||
attachment: {
|
||||
expression: "",
|
||||
initialExpression: aExpression,
|
||||
id: this._generateId()
|
||||
}
|
||||
});
|
||||
|
||||
// Check if watch expression was already appended.
|
||||
if (!expressionItem) {
|
||||
return;
|
||||
}
|
||||
|
||||
let element = expressionItem.target;
|
||||
element.id = "expression-" + expressionItem.attachment.id;
|
||||
element.className = "dbg-expression list-item";
|
||||
element.arrowNode.className = "dbg-expression-arrow";
|
||||
element.inputNode.className = "dbg-expression-input plain";
|
||||
element.closeNode.className = "dbg-expression-delete plain devtools-closebutton";
|
||||
|
||||
// Automatically focus the new watch expression input and
|
||||
// scroll the variables view to top.
|
||||
element.inputNode.value = aExpression;
|
||||
element.inputNode.select();
|
||||
element.inputNode.focus();
|
||||
this._variables.scrollTop = 0;
|
||||
|
||||
this._cache.splice(0, 0, expressionItem);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the watch expression with the specified index from this container.
|
||||
*
|
||||
* @param number aIndex
|
||||
* The index used to identify the watch expression.
|
||||
*/
|
||||
removeExpression: function DVWE_removeExpression(aIndex) {
|
||||
this.remove(this._cache[aIndex]);
|
||||
this._cache.splice(aIndex, 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the watch expression code string for an item in this container.
|
||||
*
|
||||
* @param number aIndex
|
||||
* The index used to identify the watch expression.
|
||||
* @return string
|
||||
* The watch expression code string.
|
||||
*/
|
||||
getExpression: function DVWE_getExpression(aIndex) {
|
||||
return this._cache[aIndex].attachment.expression;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the watch expressions code strings for all items in this container.
|
||||
*
|
||||
* @return array
|
||||
* The watch expressions code strings.
|
||||
*/
|
||||
getExpressions: function DVWE_getExpressions() {
|
||||
return [item.attachment.expression for (item of this._cache)];
|
||||
},
|
||||
|
||||
/**
|
||||
* Customization function for creating an item's UI.
|
||||
*
|
||||
* @param nsIDOMNode aElementNode
|
||||
* The element associated with the displayed item.
|
||||
* @param string aExpression
|
||||
* The initial watch expression text.
|
||||
*/
|
||||
_createItemView: function DVWE__createItemView(aElementNode, aExpression) {
|
||||
let arrowNode = document.createElement("box");
|
||||
let inputNode = document.createElement("textbox");
|
||||
let closeNode = document.createElement("toolbarbutton");
|
||||
|
||||
inputNode.setAttribute("value", aExpression);
|
||||
inputNode.setAttribute("flex", "1");
|
||||
|
||||
closeNode.addEventListener("click", this._onClose, false);
|
||||
inputNode.addEventListener("blur", this._onBlur, false);
|
||||
inputNode.addEventListener("keypress", this._onKeyPress, false);
|
||||
aElementNode.addEventListener("mouseover", this._onMouseOver, false);
|
||||
aElementNode.addEventListener("mouseout", this._onMouseOut, false);
|
||||
|
||||
aElementNode.appendChild(arrowNode);
|
||||
aElementNode.appendChild(inputNode);
|
||||
aElementNode.appendChild(closeNode);
|
||||
aElementNode.arrowNode = arrowNode;
|
||||
aElementNode.inputNode = inputNode;
|
||||
aElementNode.closeNode = closeNode;
|
||||
},
|
||||
|
||||
/**
|
||||
* The click listener for this container.
|
||||
*/
|
||||
_onClick: function DVWE__onClick(e) {
|
||||
let expressionItem = this.getItemForElement(e.target);
|
||||
if (!expressionItem) {
|
||||
// The container is empty or we didn't click on an actual item.
|
||||
this.addExpression();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The click listener for a watch expression's close button.
|
||||
*/
|
||||
_onClose: function DVWE__onClose(e) {
|
||||
let expressionItem = this.getItemForElement(e.target);
|
||||
this.removeExpression(this._cache.indexOf(expressionItem));
|
||||
|
||||
// Synchronize with the controller's watch expressions store.
|
||||
DebuggerController.StackFrames.syncWatchExpressions();
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
},
|
||||
|
||||
/**
|
||||
* The blur listener for a watch expression's textbox.
|
||||
*/
|
||||
_onBlur: function DVWE__onBlur({ target: textbox }) {
|
||||
let expressionItem = this.getItemForElement(textbox);
|
||||
let oldExpression = expressionItem.attachment.expression;
|
||||
let newExpression = textbox.value;
|
||||
|
||||
// Remove the watch expression if it's empty.
|
||||
if (!newExpression) {
|
||||
this.removeExpression(this._cache.indexOf(expressionItem));
|
||||
}
|
||||
// Remove the watch expression if it's a duplicate.
|
||||
else if (!oldExpression && this.getExpressions().indexOf(newExpression) != -1) {
|
||||
this.removeExpression(this._cache.indexOf(expressionItem));
|
||||
}
|
||||
// Expression is eligible.
|
||||
else {
|
||||
// Save the watch expression code string.
|
||||
expressionItem.attachment.expression = newExpression;
|
||||
// Make sure the close button is hidden when the textbox is unfocused.
|
||||
expressionItem.target.closeNode.hidden = true;
|
||||
}
|
||||
|
||||
// Synchronize with the controller's watch expressions store.
|
||||
DebuggerController.StackFrames.syncWatchExpressions();
|
||||
},
|
||||
|
||||
/**
|
||||
* The keypress listener for a watch expression's textbox.
|
||||
*/
|
||||
_onKeyPress: function DVWE__onKeyPress(e) {
|
||||
switch(e.keyCode) {
|
||||
case e.DOM_VK_RETURN:
|
||||
case e.DOM_VK_ENTER:
|
||||
case e.DOM_VK_ESCAPE:
|
||||
DebuggerView.editor.focus();
|
||||
return;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* The mouse over listener for a watch expression.
|
||||
*/
|
||||
_onMouseOver: function DVWE__onMouseOver({ target: element }) {
|
||||
this.getItemForElement(element).target.closeNode.hidden = false;
|
||||
},
|
||||
|
||||
/**
|
||||
* The mouse out listener for a watch expression.
|
||||
*/
|
||||
_onMouseOut: function DVWE__onMouseOut({ target: element }) {
|
||||
this.getItemForElement(element).target.closeNode.hidden = true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets an identifier for a new watch expression item in the current cache.
|
||||
* @return string
|
||||
*/
|
||||
_generateId: (function() {
|
||||
let count = 0;
|
||||
return function DVWE__generateId() {
|
||||
return (++count) + "";
|
||||
};
|
||||
})(),
|
||||
|
||||
_variables: null,
|
||||
_cache: null
|
||||
});
|
||||
|
||||
/**
|
||||
* Functions handling the global search UI.
|
||||
*/
|
||||
@ -1778,4 +2020,5 @@ LineResults.size = function DVGS_size() {
|
||||
*/
|
||||
DebuggerView.StackFrames = new StackFramesView();
|
||||
DebuggerView.Breakpoints = new BreakpointsView();
|
||||
DebuggerView.WatchExpressions = new WatchExpressionsView();
|
||||
DebuggerView.GlobalSearch = new GlobalSearchView();
|
||||
|
@ -42,6 +42,7 @@ let DebuggerView = {
|
||||
this.Filtering.initialize();
|
||||
this.StackFrames.initialize();
|
||||
this.Breakpoints.initialize();
|
||||
this.WatchExpressions.initialize();
|
||||
this.GlobalSearch.initialize();
|
||||
|
||||
this.Variables = new VariablesView(document.getElementById("variables"));
|
||||
@ -71,6 +72,7 @@ let DebuggerView = {
|
||||
this.Filtering.destroy();
|
||||
this.StackFrames.destroy();
|
||||
this.Breakpoints.destroy();
|
||||
this.WatchExpressions.destroy();
|
||||
this.GlobalSearch.destroy();
|
||||
|
||||
this._destroyWindow();
|
||||
@ -122,10 +124,10 @@ let DebuggerView = {
|
||||
|
||||
this._togglePanesButton = document.getElementById("toggle-panes");
|
||||
this._stackframesAndBreakpoints = document.getElementById("stackframes+breakpoints");
|
||||
this._variables = document.getElementById("variables");
|
||||
this._variablesAndExpressions = document.getElementById("variables+expressions");
|
||||
|
||||
this._stackframesAndBreakpoints.setAttribute("width", Prefs.stackframesWidth);
|
||||
this._variables.setAttribute("width", Prefs.variablesWidth);
|
||||
this._variablesAndExpressions.setAttribute("width", Prefs.variablesWidth);
|
||||
this.togglePanes({
|
||||
visible: Prefs.panesVisibleOnStartup,
|
||||
animated: false
|
||||
@ -139,11 +141,11 @@ let DebuggerView = {
|
||||
dumpn("Destroying the DebuggerView panes");
|
||||
|
||||
Prefs.stackframesWidth = this._stackframesAndBreakpoints.getAttribute("width");
|
||||
Prefs.variablesWidth = this._variables.getAttribute("width");
|
||||
Prefs.variablesWidth = this._variablesAndExpressions.getAttribute("width");
|
||||
|
||||
this._togglePanesButton = null;
|
||||
this._stackframesAndBreakpoints = null;
|
||||
this._variables = null;
|
||||
this._variablesAndExpressions = null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -401,21 +403,21 @@ let DebuggerView = {
|
||||
|
||||
if (aFlags.visible) {
|
||||
this._stackframesAndBreakpoints.style.marginLeft = "0";
|
||||
this._variables.style.marginRight = "0";
|
||||
this._variablesAndExpressions.style.marginRight = "0";
|
||||
this._togglePanesButton.removeAttribute("panesHidden");
|
||||
this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("collapsePanes"));
|
||||
} else {
|
||||
let marginL = ~~(this._stackframesAndBreakpoints.getAttribute("width")) + 1;
|
||||
let marginR = ~~(this._variables.getAttribute("width")) + 1;
|
||||
let marginR = ~~(this._variablesAndExpressions.getAttribute("width")) + 1;
|
||||
this._stackframesAndBreakpoints.style.marginLeft = -marginL + "px";
|
||||
this._variables.style.marginRight = -marginR + "px";
|
||||
this._variablesAndExpressions.style.marginRight = -marginR + "px";
|
||||
this._togglePanesButton.setAttribute("panesHidden", "true");
|
||||
this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("expandPanes"));
|
||||
}
|
||||
|
||||
if (aFlags.animated) {
|
||||
this._stackframesAndBreakpoints.setAttribute("animated", "");
|
||||
this._variables.setAttribute("animated", "");
|
||||
this._variablesAndExpressions.setAttribute("animated", "");
|
||||
|
||||
// Displaying the panes may have the effect of triggering scrollbars to
|
||||
// appear in the source editor, which would render the currently
|
||||
@ -429,7 +431,7 @@ let DebuggerView = {
|
||||
}, false);
|
||||
} else {
|
||||
this._stackframesAndBreakpoints.removeAttribute("animated");
|
||||
this._variables.removeAttribute("animated");
|
||||
this._variablesAndExpressions.removeAttribute("animated");
|
||||
aFlags.callback && aFlags.callback();
|
||||
}
|
||||
},
|
||||
@ -487,7 +489,7 @@ let DebuggerView = {
|
||||
_editorSource: null,
|
||||
_togglePanesButton: null,
|
||||
_stackframesAndBreakpoints: null,
|
||||
_variables: null,
|
||||
_variablesAndExpressions: null,
|
||||
_isInitialized: false,
|
||||
_isDestroyed: false
|
||||
};
|
||||
@ -609,8 +611,8 @@ MenuContainer.prototype = {
|
||||
* The actual internal value of the item.
|
||||
* @param object aOptions [optional]
|
||||
* Additional options or flags supported by this operation:
|
||||
* - forced: true to force the item to be immediately added
|
||||
* - unsorted: true if the items should not remain sorted
|
||||
* - forced: true to force the item to be immediately appended
|
||||
* - unsorted: true if the items should not always remain sorted
|
||||
* - relaxed: true if this container should allow dupes & degenerates
|
||||
* - description: an optional description of the item
|
||||
* - attachment: some attached primitive/object
|
||||
@ -626,6 +628,10 @@ MenuContainer.prototype = {
|
||||
if (!aOptions.forced) {
|
||||
this._stagedItems.push(item);
|
||||
}
|
||||
// Immediately insert the item at the specified index.
|
||||
else if (aOptions.forced && aOptions.forced.atIndex !== undefined) {
|
||||
return this._insertItemAt(aOptions.forced.atIndex, item, aOptions);
|
||||
}
|
||||
// Find the target position in this container and insert the item there.
|
||||
else if (!aOptions.unsorted) {
|
||||
return this._insertItemAt(this._findExpectedIndex(aLabel), item, aOptions);
|
||||
@ -709,6 +715,18 @@ MenuContainer.prototype = {
|
||||
this._stagedItems = [];
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles all the items in this container hidden or visible.
|
||||
*
|
||||
* @param boolean aVisibleFlag
|
||||
* Specifies the intended visibility.
|
||||
*/
|
||||
toggleContents: function DVMC_toggleContents(aVisibleFlag) {
|
||||
for (let [, item] of this._itemsByElement) {
|
||||
item.target.hidden = !aVisibleFlag;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Does not remove any item in this container. Instead, it overrides the
|
||||
* current label to signal that it is unavailable and removes the tooltip.
|
||||
@ -840,6 +858,18 @@ MenuContainer.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the item in the container having the specified index.
|
||||
*
|
||||
* @param number aIndex
|
||||
* The index used to identify the element.
|
||||
* @return MenuItem
|
||||
* The matched item, or null if nothing is found.
|
||||
*/
|
||||
getItemAtIndex: function DVMC_getItemAtIndex(aIndex) {
|
||||
return this.getItemForElement(this._container.getItemAtIndex(aIndex));
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the item in the container having the specified label.
|
||||
*
|
||||
@ -908,6 +938,14 @@ MenuContainer.prototype = {
|
||||
return values;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the total items in this container.
|
||||
* @return number
|
||||
*/
|
||||
get totalItems() {
|
||||
return this._itemsByElement.size;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the total visible (non-hidden) items in this container.
|
||||
* @return number
|
||||
@ -1097,6 +1135,7 @@ MenuContainer.prototype = {
|
||||
*
|
||||
* Custom methods introduced by this view, not necessary for a MenuContainer:
|
||||
* set emptyText(aValue:string)
|
||||
* set permaText(aValue:string)
|
||||
* set itemType(aType:string)
|
||||
* set itemFactory(aCallback:function)
|
||||
*
|
||||
@ -1107,7 +1146,6 @@ MenuContainer.prototype = {
|
||||
*/
|
||||
function StackList(aAssociatedNode) {
|
||||
this._parent = aAssociatedNode;
|
||||
this._appendEmptyNotice();
|
||||
|
||||
// Create an internal list container.
|
||||
this._list = document.createElement("vbox");
|
||||
@ -1319,6 +1357,18 @@ StackList.prototype = {
|
||||
this._parent.removeEventListener(aName, aCallback, aBubbleFlag);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the text displayed permanently in this container's header.
|
||||
* @param string aValue
|
||||
*/
|
||||
set permaText(aValue) {
|
||||
if (this._permaTextNode) {
|
||||
this._permaTextNode.setAttribute("value", aValue);
|
||||
}
|
||||
this._permaTextValue = aValue;
|
||||
this._appendPermaNotice();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sets the text displayed in this container when there are no available items.
|
||||
* @param string aValue
|
||||
@ -1328,6 +1378,7 @@ StackList.prototype = {
|
||||
this._emptyTextNode.setAttribute("value", aValue);
|
||||
}
|
||||
this._emptyTextValue = aValue;
|
||||
this._appendEmptyNotice();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1369,11 +1420,27 @@ StackList.prototype = {
|
||||
aElementNode.valueNode = valueNode;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates and appends a label displayed permanently in this container's header.
|
||||
*/
|
||||
_appendPermaNotice: function DVSL__appendPermaNotice() {
|
||||
if (this._permaTextNode || !this._permaTextValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
let label = document.createElement("label");
|
||||
label.className = "empty list-item";
|
||||
label.setAttribute("value", this._permaTextValue);
|
||||
|
||||
this._parent.insertBefore(label, this._list);
|
||||
this._permaTextNode = label;
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates and appends a label signaling that this container is empty.
|
||||
*/
|
||||
_appendEmptyNotice: function DVSL__appendEmptyNotice() {
|
||||
if (this._emptyTextNode) {
|
||||
if (this._emptyTextNode || !this._emptyTextValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1401,6 +1468,8 @@ StackList.prototype = {
|
||||
_list: null,
|
||||
_selectedIndex: -1,
|
||||
_selectedItem: null,
|
||||
_permaTextNode: null,
|
||||
_permaTextValue: "",
|
||||
_emptyTextNode: null,
|
||||
_emptyTextValue: ""
|
||||
};
|
||||
|
@ -245,7 +245,11 @@
|
||||
<splitter class="devtools-side-splitter"/>
|
||||
<vbox id="editor" flex="1"/>
|
||||
<splitter class="devtools-side-splitter"/>
|
||||
<vbox id="variables"/>
|
||||
<vbox id="variables+expressions">
|
||||
<vbox id="expressions"/>
|
||||
<splitter class="devtools-horizontal-splitter"/>
|
||||
<vbox id="variables" flex="1"/>
|
||||
</vbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
|
@ -74,6 +74,8 @@ MOCHITEST_BROWSER_TESTS = \
|
||||
browser_dbg_bug723071_editor-breakpoints-pane.js \
|
||||
browser_dbg_bug740825_conditional-breakpoints-01.js \
|
||||
browser_dbg_bug740825_conditional-breakpoints-02.js \
|
||||
browser_dbg_bug727429_watch-expressions-01.js \
|
||||
browser_dbg_bug727429_watch-expressions-02.js \
|
||||
browser_dbg_bug731394_editor-contextmenu.js \
|
||||
browser_dbg_bug786070_hide_nonenums.js \
|
||||
browser_dbg_displayName.js \
|
||||
@ -108,6 +110,7 @@ MOCHITEST_BROWSER_PAGES = \
|
||||
browser_dbg_pause-exceptions.html \
|
||||
browser_dbg_breakpoint-new-script.html \
|
||||
browser_dbg_conditional-breakpoints.html \
|
||||
browser_dbg_watch-expressions.html \
|
||||
$(NULL)
|
||||
|
||||
MOCHITEST_BROWSER_FILES_PARTS = MOCHITEST_BROWSER_TESTS MOCHITEST_BROWSER_PAGES
|
||||
|
@ -0,0 +1,238 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Bug 727429: test the debugger watch expressions.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "browser_dbg_watch-expressions.html";
|
||||
|
||||
let gPane = null;
|
||||
let gTab = null;
|
||||
let gDebuggee = null;
|
||||
let gDebugger = null;
|
||||
let gWatch = null;
|
||||
|
||||
function test()
|
||||
{
|
||||
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.contentWindow;
|
||||
gWatch = gDebugger.DebuggerView.WatchExpressions;
|
||||
|
||||
gDebugger.DebuggerView.togglePanes({ visible: true, animated: false });
|
||||
|
||||
executeSoon(function() {
|
||||
performTest();
|
||||
});
|
||||
});
|
||||
|
||||
function performTest()
|
||||
{
|
||||
is(gWatch.getExpressions().length, 0,
|
||||
"There should initially be no watch expressions");
|
||||
|
||||
addAndCheckExpressions(1, 0, "a");
|
||||
addAndCheckExpressions(2, 0, "b");
|
||||
addAndCheckExpressions(3, 0, "c");
|
||||
|
||||
removeAndCheckExpression(2, 1, "a");
|
||||
removeAndCheckExpression(1, 0, "a");
|
||||
|
||||
|
||||
addAndCheckExpressions(2, 0, "", true);
|
||||
gDebugger.editor.focus();
|
||||
is(gWatch.getExpressions().length, 1,
|
||||
"Empty watch expressions are automatically removed");
|
||||
|
||||
addAndCheckExpressions(2, 0, "a", true);
|
||||
gDebugger.editor.focus();
|
||||
is(gWatch.getExpressions().length, 1,
|
||||
"Duplicate watch expressions are automatically removed");
|
||||
|
||||
|
||||
addAndCheckCustomExpression(2, 0, "bazΩΩka");
|
||||
addAndCheckCustomExpression(3, 0, "bambøøcha");
|
||||
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
gWatch.getItemAtIndex(0).target.closeNode,
|
||||
gDebugger);
|
||||
|
||||
is(gWatch.getExpressions().length, 2,
|
||||
"Watch expressions are removed when the close button is pressed");
|
||||
is(gWatch.getExpressions()[0], "bazΩΩka",
|
||||
"The expression at index " + 0 + " should be correct (1)");
|
||||
is(gWatch.getExpressions()[1], "a",
|
||||
"The expression at index " + 1 + " should be correct (2)");
|
||||
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
gWatch.getItemAtIndex(0).target.closeNode,
|
||||
gDebugger);
|
||||
|
||||
is(gWatch.getExpressions().length, 1,
|
||||
"Watch expressions are removed when the close button is pressed");
|
||||
is(gWatch.getExpressions()[0], "a",
|
||||
"The expression at index " + 0 + " should be correct (3)");
|
||||
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
gWatch.getItemAtIndex(0).target.closeNode,
|
||||
gDebugger);
|
||||
|
||||
is(gWatch.getExpressions().length, 0,
|
||||
"Watch expressions are removed when the close button is pressed");
|
||||
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "click" },
|
||||
gWatch._container._parent,
|
||||
gDebugger);
|
||||
|
||||
is(gWatch.getExpressions().length, 1,
|
||||
"Watch expressions are added when the view container is pressed");
|
||||
|
||||
|
||||
closeDebuggerAndFinish();
|
||||
}
|
||||
|
||||
function addAndCheckCustomExpression(total, index, string, noBlur) {
|
||||
addAndCheckExpressions(total, index, "", true);
|
||||
|
||||
for (let i = 0; i < string.length; i++) {
|
||||
EventUtils.sendChar(string[i]);
|
||||
}
|
||||
|
||||
gDebugger.editor.focus();
|
||||
|
||||
let id = gWatch.getItemAtIndex(index).attachment.id;
|
||||
let element = gDebugger.document.getElementById("expression-" + id);
|
||||
|
||||
is(gWatch.getItemAtIndex(index).attachment.initialExpression, "",
|
||||
"The initial expression at index " + index + " should be correct (1)");
|
||||
is(gWatch.getItemForElement(element).attachment.initialExpression, "",
|
||||
"The initial expression at index " + index + " should be correct (2)");
|
||||
|
||||
is(gWatch.getItemAtIndex(index).attachment.expression, string,
|
||||
"The expression at index " + index + " should be correct (1)");
|
||||
is(gWatch.getItemForElement(element).attachment.expression, string,
|
||||
"The expression at index " + index + " should be correct (2)");
|
||||
|
||||
is(gWatch.getExpression(index), string,
|
||||
"The expression at index " + index + " should be correct (3)");
|
||||
is(gWatch.getExpressions()[index], string,
|
||||
"The expression at index " + index + " should be correct (4)");
|
||||
}
|
||||
|
||||
function addAndCheckExpressions(total, index, string, noBlur) {
|
||||
gWatch.addExpression(string);
|
||||
|
||||
is(gWatch.getExpressions().length, total,
|
||||
"There should be " + total + " watch expressions available (1)");
|
||||
is(gWatch.totalItems, total,
|
||||
"There should be " + total + " watch expressions available (2)");
|
||||
|
||||
ok(gWatch.getItemAtIndex(index),
|
||||
"The expression at index " + index + " should be available");
|
||||
ok(gWatch.getItemAtIndex(index).attachment.id,
|
||||
"The expression at index " + index + " should have an id");
|
||||
is(gWatch.getItemAtIndex(index).attachment.initialExpression, string,
|
||||
"The expression at index " + index + " should have an initial expression");
|
||||
|
||||
let id = gWatch.getItemAtIndex(index).attachment.id;
|
||||
let element = gDebugger.document.getElementById("expression-" + id);
|
||||
|
||||
ok(element,
|
||||
"Three should be a new expression item in the view");
|
||||
ok(gWatch.getItemForElement(element),
|
||||
"The watch expression item should be accessible");
|
||||
is(gWatch.getItemForElement(element), gWatch.getItemAtIndex(index),
|
||||
"The correct watch expression item was accessed");
|
||||
|
||||
ok(gWatch.getItemAtIndex(index) instanceof gDebugger.MenuItem,
|
||||
"The correct watch expression element was accessed (1)");
|
||||
ok(gWatch._container.getItemAtIndex(index) instanceof XULElement,
|
||||
"The correct watch expression element was accessed (2)");
|
||||
is(element, gWatch._container.getItemAtIndex(index),
|
||||
"The correct watch expression element was accessed (3)");
|
||||
|
||||
is(element.arrowNode.hidden, false,
|
||||
"The arrow node should be visible");
|
||||
is(element.closeNode.hidden, false,
|
||||
"The close button should be visible");
|
||||
is(element.inputNode.getAttribute("focused"), "true",
|
||||
"The textbox input should be focused");
|
||||
|
||||
is(gWatch._variables.scrollTop, 0,
|
||||
"The variables view should be scrolled to top");
|
||||
|
||||
is(gWatch._cache[0], gWatch.getItemAtIndex(index),
|
||||
"The correct watch expression was added to the cache (1)");
|
||||
is(gWatch._cache[0], gWatch.getItemForElement(element),
|
||||
"The correct watch expression was added to the cache (2)");
|
||||
|
||||
if (!noBlur) {
|
||||
gDebugger.editor.focus();
|
||||
|
||||
is(gWatch.getItemAtIndex(index).attachment.initialExpression, string,
|
||||
"The initial expression at index " + index + " should be correct (1)");
|
||||
is(gWatch.getItemForElement(element).attachment.initialExpression, string,
|
||||
"The initial expression at index " + index + " should be correct (2)");
|
||||
|
||||
is(gWatch.getItemAtIndex(index).attachment.expression, string,
|
||||
"The expression at index " + index + " should be correct (1)");
|
||||
is(gWatch.getItemForElement(element).attachment.expression, string,
|
||||
"The expression at index " + index + " should be correct (2)");
|
||||
|
||||
is(gWatch.getExpression(index), string,
|
||||
"The expression at index " + index + " should be correct (3)");
|
||||
is(gWatch.getExpressions()[index], string,
|
||||
"The expression at index " + index + " should be correct (4)");
|
||||
}
|
||||
}
|
||||
|
||||
function removeAndCheckExpression(total, index, string) {
|
||||
gWatch.removeExpression(index);
|
||||
|
||||
is(gWatch.getExpressions().length, total,
|
||||
"There should be " + total + " watch expressions available (1)");
|
||||
is(gWatch.totalItems, total,
|
||||
"There should be " + total + " watch expressions available (2)");
|
||||
|
||||
ok(gWatch.getItemAtIndex(index),
|
||||
"The expression at index " + index + " should still be available");
|
||||
ok(gWatch.getItemAtIndex(index).attachment.id,
|
||||
"The expression at index " + index + " should still have an id");
|
||||
is(gWatch.getItemAtIndex(index).attachment.initialExpression, string,
|
||||
"The expression at index " + index + " should still have an initial expression");
|
||||
|
||||
let id = gWatch.getItemAtIndex(index).attachment.id;
|
||||
let element = gDebugger.document.getElementById("expression-" + id);
|
||||
|
||||
is(gWatch.getItemAtIndex(index).attachment.initialExpression, string,
|
||||
"The initial expression at index " + index + " should be correct (1)");
|
||||
is(gWatch.getItemForElement(element).attachment.initialExpression, string,
|
||||
"The initial expression at index " + index + " should be correct (2)");
|
||||
|
||||
is(gWatch.getItemAtIndex(index).attachment.expression, string,
|
||||
"The expression at index " + index + " should be correct (1)");
|
||||
is(gWatch.getItemForElement(element).attachment.expression, string,
|
||||
"The expression at index " + index + " should be correct (2)");
|
||||
|
||||
is(gWatch.getExpression(index), string,
|
||||
"The expression at index " + index + " should be correct (3)");
|
||||
is(gWatch.getExpressions()[index], string,
|
||||
"The expression at index " + index + " should be correct (4)");
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
removeTab(gTab);
|
||||
gPane = null;
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gDebugger = null;
|
||||
gWatch = null;
|
||||
});
|
||||
}
|
@ -0,0 +1,275 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Bug 727429: test the debugger watch expressions.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "browser_dbg_watch-expressions.html";
|
||||
|
||||
let gPane = null;
|
||||
let gTab = null;
|
||||
let gDebuggee = null;
|
||||
let gDebugger = null;
|
||||
let gWatch = null;
|
||||
let gVars = null;
|
||||
|
||||
function test()
|
||||
{
|
||||
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.contentWindow;
|
||||
gWatch = gDebugger.DebuggerView.WatchExpressions;
|
||||
gVars = gDebugger.DebuggerView.Variables;
|
||||
|
||||
gDebugger.DebuggerView.togglePanes({ visible: true, animated: false });
|
||||
addExpressions();
|
||||
performTest();
|
||||
});
|
||||
|
||||
function addExpressions()
|
||||
{
|
||||
gWatch.addExpression("'a'");
|
||||
gWatch.addExpression("\"a\"");
|
||||
gWatch.addExpression("'a\"\"'");
|
||||
gWatch.addExpression("\"a''\"");
|
||||
gWatch.addExpression("?");
|
||||
gWatch.addExpression("a");
|
||||
gWatch.addExpression("[1, 2, 3]");
|
||||
gWatch.addExpression("x = [1, 2, 3]");
|
||||
gWatch.addExpression("y = [1, 2, 3]; y.test = 4");
|
||||
gWatch.addExpression("z = [1, 2, 3]; z.test = 4; z");
|
||||
gWatch.addExpression("t = [1, 2, 3]; t.test = 4; !t");
|
||||
gWatch.addExpression("encodeURI(\"\\\")");
|
||||
gWatch.addExpression("decodeURI(\"\\\")");
|
||||
}
|
||||
|
||||
function performTest()
|
||||
{
|
||||
is(gWatch._container._parent.querySelectorAll(".dbg-expression[hidden=true]").length, 0,
|
||||
"There should be 0 hidden nodes in the watch expressions container");
|
||||
is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 13,
|
||||
"There should be 13 visible nodes in the watch expressions container");
|
||||
|
||||
test1(function() {
|
||||
test2(function() {
|
||||
test3(function() {
|
||||
test4(function() {
|
||||
test5(function() {
|
||||
test6(function() {
|
||||
test7(function() {
|
||||
test8(function() {
|
||||
test9(function() {
|
||||
finishTest();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function finishTest()
|
||||
{
|
||||
is(gWatch._container._parent.querySelectorAll(".dbg-expression[hidden=true]").length, 0,
|
||||
"There should be 0 hidden nodes in the watch expressions container");
|
||||
is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 12,
|
||||
"There should be 12 visible nodes in the watch expressions container");
|
||||
|
||||
closeDebuggerAndFinish();
|
||||
}
|
||||
|
||||
function test1(callback) {
|
||||
waitForWatchExpressions(function() {
|
||||
info("Performing test1");
|
||||
checkWatchExpressions("ReferenceError: a is not defined");
|
||||
callback();
|
||||
});
|
||||
executeSoon(function() {
|
||||
gDebuggee.ermahgerd(); // ermahgerd!!
|
||||
});
|
||||
}
|
||||
|
||||
function test2(callback) {
|
||||
waitForWatchExpressions(function() {
|
||||
info("Performing test2");
|
||||
checkWatchExpressions(undefined);
|
||||
callback();
|
||||
});
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gDebugger.document.getElementById("resume"),
|
||||
gDebugger);
|
||||
}
|
||||
|
||||
function test3(callback) {
|
||||
waitForWatchExpressions(function() {
|
||||
info("Performing test3");
|
||||
checkWatchExpressions({ type: "object", class: "Object" });
|
||||
callback();
|
||||
});
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gDebugger.document.getElementById("resume"),
|
||||
gDebugger);
|
||||
}
|
||||
|
||||
function test4(callback) {
|
||||
waitForWatchExpressions(function() {
|
||||
info("Performing test4");
|
||||
checkWatchExpressions(5, 12);
|
||||
callback();
|
||||
});
|
||||
executeSoon(function() {
|
||||
gWatch.addExpression("a = 5");
|
||||
EventUtils.sendKey("RETURN");
|
||||
});
|
||||
}
|
||||
|
||||
function test5(callback) {
|
||||
waitForWatchExpressions(function() {
|
||||
info("Performing test5");
|
||||
checkWatchExpressions(5, 12);
|
||||
callback();
|
||||
});
|
||||
executeSoon(function() {
|
||||
gWatch.addExpression("encodeURI(\"\\\")");
|
||||
EventUtils.sendKey("RETURN");
|
||||
});
|
||||
}
|
||||
|
||||
function test6(callback) {
|
||||
waitForWatchExpressions(function() {
|
||||
info("Performing test6");
|
||||
checkWatchExpressions(5, 12);
|
||||
callback();
|
||||
})
|
||||
executeSoon(function() {
|
||||
gWatch.addExpression("decodeURI(\"\\\")");
|
||||
EventUtils.sendKey("RETURN");
|
||||
});
|
||||
}
|
||||
|
||||
function test7(callback) {
|
||||
waitForWatchExpressions(function() {
|
||||
info("Performing test7");
|
||||
checkWatchExpressions(5, 12);
|
||||
callback();
|
||||
});
|
||||
executeSoon(function() {
|
||||
gWatch.addExpression("?");
|
||||
EventUtils.sendKey("RETURN");
|
||||
});
|
||||
}
|
||||
|
||||
function test8(callback) {
|
||||
waitForWatchExpressions(function() {
|
||||
info("Performing test8");
|
||||
checkWatchExpressions(5, 12);
|
||||
callback();
|
||||
});
|
||||
executeSoon(function() {
|
||||
gWatch.addExpression("a");
|
||||
EventUtils.sendKey("RETURN");
|
||||
});
|
||||
}
|
||||
|
||||
function test9(callback) {
|
||||
waitForAfterFramesCleared(function() {
|
||||
info("Performing test9");
|
||||
callback();
|
||||
});
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
gDebugger.document.getElementById("resume"),
|
||||
gDebugger);
|
||||
}
|
||||
|
||||
function waitForAfterFramesCleared(callback) {
|
||||
gDebugger.addEventListener("Debugger:AfterFramesCleared", function onClear() {
|
||||
gDebugger.removeEventListener("Debugger:AfterFramesCleared", onClear, false);
|
||||
executeSoon(callback);
|
||||
}, false);
|
||||
}
|
||||
|
||||
function waitForWatchExpressions(callback) {
|
||||
gDebugger.addEventListener("Debugger:FetchedWatchExpressions", function onFetch() {
|
||||
gDebugger.removeEventListener("Debugger:FetchedWatchExpressions", onFetch, false);
|
||||
executeSoon(callback);
|
||||
}, false);
|
||||
}
|
||||
|
||||
function checkWatchExpressions(expected, total = 11) {
|
||||
is(gWatch._container._parent.querySelectorAll(".dbg-expression[hidden=true]").length, total,
|
||||
"There should be " + total + " hidden nodes in the watch expressions container");
|
||||
is(gWatch._container._parent.querySelectorAll(".dbg-expression:not([hidden=true])").length, 0,
|
||||
"There should be 0 visible nodes in the watch expressions container");
|
||||
|
||||
let label = gDebugger.L10N.getStr("watchExpressionsScopeLabel");
|
||||
let scope = gVars._currHierarchy.get(label);
|
||||
|
||||
ok(scope, "There should be a wach expressions scope in the variables view");
|
||||
is(scope._store.size, total, "There should be " + total + " evaluations availalble");
|
||||
|
||||
let w1 = scope.get("'a'");
|
||||
let w2 = scope.get("\"a\"");
|
||||
let w3 = scope.get("'a\"\"'");
|
||||
let w4 = scope.get("\"a''\"");
|
||||
let w5 = scope.get("?");
|
||||
let w6 = scope.get("a");
|
||||
let w7 = scope.get("x = [1, 2, 3]");
|
||||
let w8 = scope.get("y = [1, 2, 3]; y.test = 4");
|
||||
let w9 = scope.get("z = [1, 2, 3]; z.test = 4; z");
|
||||
let w10 = scope.get("t = [1, 2, 3]; t.test = 4; !t");
|
||||
let w11 = scope.get("encodeURI(\"\\\")");
|
||||
let w12 = scope.get("decodeURI(\"\\\")");
|
||||
|
||||
ok(w1, "The first watch expression should be present in the scope");
|
||||
ok(w2, "The second watch expression should be present in the scope");
|
||||
ok(w3, "The third watch expression should be present in the scope");
|
||||
ok(w4, "The fourth watch expression should be present in the scope");
|
||||
ok(w5, "The fifth watch expression should be present in the scope");
|
||||
ok(w6, "The sixth watch expression should be present in the scope");
|
||||
ok(w7, "The seventh watch expression should be present in the scope");
|
||||
ok(w8, "The eight watch expression should be present in the scope");
|
||||
ok(w9, "The ninth watch expression should be present in the scope");
|
||||
ok(w10, "The tenth watch expression should be present in the scope");
|
||||
ok(!w11, "The eleventh watch expression should not be present in the scope");
|
||||
ok(!w12, "The twelveth watch expression should not be present in the scope");
|
||||
|
||||
is(w1.value, "a", "The first value is correct");
|
||||
is(w2.value, "a", "The second value is correct");
|
||||
is(w3.value, "a\"\"", "The third value is correct");
|
||||
is(w4.value, "a''", "The fourth value is correct");
|
||||
is(w5.value, "SyntaxError: syntax error", "The fifth value is correct");
|
||||
|
||||
if (typeof expected == "object") {
|
||||
is(w6.value.type, expected.type, "The sixth value type is correct");
|
||||
is(w6.value.class, expected.class, "The sixth value class is correct");
|
||||
} else {
|
||||
is(w6.value, expected, "The sixth value is correct");
|
||||
}
|
||||
|
||||
is(w7.value.type, "object", "The seventh value type is correct");
|
||||
is(w7.value.class, "Array", "The seventh value class is correct");
|
||||
|
||||
is(w8.value, "4", "The eight value is correct");
|
||||
|
||||
is(w9.value.type, "object", "The ninth value type is correct");
|
||||
is(w9.value.class, "Array", "The ninth value class is correct");
|
||||
|
||||
is(w10.value, false, "The tenth value is correct");
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
removeTab(gTab);
|
||||
gPane = null;
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gDebugger = null;
|
||||
gWatch = null;
|
||||
gVars = null;
|
||||
});
|
||||
}
|
@ -226,10 +226,10 @@ function test()
|
||||
conditionalExpression: "(function() { return false; })()"
|
||||
});
|
||||
}, {
|
||||
conditionalExpression: "function() {}"
|
||||
conditionalExpression: "(function() {})"
|
||||
});
|
||||
}, {
|
||||
conditionalExpression: "{}"
|
||||
conditionalExpression: "({})"
|
||||
});
|
||||
}, {
|
||||
conditionalExpression: "/regexp/"
|
||||
|
@ -91,20 +91,20 @@ function testPaneCollapse1() {
|
||||
}
|
||||
|
||||
function testPaneCollapse2() {
|
||||
let variables =
|
||||
gDebugger.document.getElementById("variables");
|
||||
let variablesAndExpressions =
|
||||
gDebugger.document.getElementById("variables+expressions");
|
||||
let togglePanesButton =
|
||||
gDebugger.document.getElementById("toggle-panes");
|
||||
|
||||
let width = parseInt(variables.getAttribute("width"));
|
||||
let width = parseInt(variablesAndExpressions.getAttribute("width"));
|
||||
is(width, gDebugger.Prefs.variablesWidth,
|
||||
"The variables pane has an incorrect width.");
|
||||
is(variables.style.marginRight, "0px",
|
||||
"The variables pane has an incorrect right margin.");
|
||||
ok(!variables.hasAttribute("animated"),
|
||||
"The variables pane has an incorrect animated attribute.");
|
||||
"The variables and expressions pane has an incorrect width.");
|
||||
is(variablesAndExpressions.style.marginRight, "0px",
|
||||
"The variables and expressions pane has an incorrect right margin.");
|
||||
ok(!variablesAndExpressions.hasAttribute("animated"),
|
||||
"The variables and expressions pane has an incorrect animated attribute.");
|
||||
ok(!togglePanesButton.getAttribute("panesHidden"),
|
||||
"The variables pane should at this point be visible.");
|
||||
"The variables and expressions pane should at this point be visible.");
|
||||
|
||||
gView.togglePanes({ visible: false, animated: true });
|
||||
|
||||
@ -115,13 +115,13 @@ function testPaneCollapse2() {
|
||||
|
||||
let margin = -(width + 1) + "px";
|
||||
is(width, gDebugger.Prefs.variablesWidth,
|
||||
"The variables pane has an incorrect width after collapsing.");
|
||||
is(variables.style.marginRight, margin,
|
||||
"The variables pane has an incorrect right margin after collapsing.");
|
||||
ok(variables.hasAttribute("animated"),
|
||||
"The variables pane has an incorrect attribute after an animated collapsing.");
|
||||
"The variables and expressions pane has an incorrect width after collapsing.");
|
||||
is(variablesAndExpressions.style.marginRight, margin,
|
||||
"The variables and expressions pane has an incorrect right margin after collapsing.");
|
||||
ok(variablesAndExpressions.hasAttribute("animated"),
|
||||
"The variables and expressions pane has an incorrect attribute after an animated collapsing.");
|
||||
ok(togglePanesButton.hasAttribute("panesHidden"),
|
||||
"The variables pane should not be visible after collapsing.");
|
||||
"The variables and expressions pane should not be visible after collapsing.");
|
||||
|
||||
gView.togglePanes({ visible: true, animated: false });
|
||||
|
||||
@ -131,20 +131,20 @@ function testPaneCollapse2() {
|
||||
"The options menu item should still not be checked.");
|
||||
|
||||
is(width, gDebugger.Prefs.variablesWidth,
|
||||
"The variables pane has an incorrect width after uncollapsing.");
|
||||
is(variables.style.marginRight, "0px",
|
||||
"The variables pane has an incorrect right margin after uncollapsing.");
|
||||
ok(!variables.hasAttribute("animated"),
|
||||
"The variables pane has an incorrect attribute after an unanimated uncollapsing.");
|
||||
"The variables and expressions pane has an incorrect width after uncollapsing.");
|
||||
is(variablesAndExpressions.style.marginRight, "0px",
|
||||
"The variables and expressions pane has an incorrect right margin after uncollapsing.");
|
||||
ok(!variablesAndExpressions.hasAttribute("animated"),
|
||||
"The variables and expressions pane has an incorrect attribute after an unanimated uncollapsing.");
|
||||
ok(!togglePanesButton.getAttribute("panesHidden"),
|
||||
"The variables pane should be visible again after uncollapsing.");
|
||||
"The variables and expressions pane should be visible again after uncollapsing.");
|
||||
}
|
||||
|
||||
function testPanesStartupPref(aCallback) {
|
||||
let stackframesAndBrekpoints =
|
||||
gDebugger.document.getElementById("stackframes+breakpoints");
|
||||
let variables =
|
||||
gDebugger.document.getElementById("variables");
|
||||
let variablesAndExpressions =
|
||||
gDebugger.document.getElementById("variables+expressions");
|
||||
let togglePanesButton =
|
||||
gDebugger.document.getElementById("toggle-panes");
|
||||
|
||||
|
@ -43,7 +43,7 @@ function test() {
|
||||
"The debugger preferences should have a saved variablesWidth value.");
|
||||
|
||||
stackframes = content.document.getElementById("stackframes+breakpoints");
|
||||
variables = content.document.getElementById("variables");
|
||||
variables = content.document.getElementById("variables+expressions");
|
||||
|
||||
is(content.Prefs.stackframesWidth, stackframes.getAttribute("width"),
|
||||
"The stackframes pane width should be the same as the preferred value.");
|
||||
|
@ -0,0 +1,22 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<title>Browser Debugger Watch Expressions Test</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript">
|
||||
function ermahgerd() {
|
||||
debugger;
|
||||
(function() {
|
||||
var a = undefined;
|
||||
debugger;
|
||||
var a = {};
|
||||
debugger;
|
||||
}());
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
@ -446,7 +446,15 @@ ResponsiveUI.prototype = {
|
||||
|
||||
let title = this.strings.GetStringFromName("responsiveUI.customNamePromptTitle");
|
||||
let message = this.strings.formatStringFromName("responsiveUI.customNamePromptMsg", [w, h], 2);
|
||||
Services.prompt.prompt(null, title, message, newName, null, {});
|
||||
let promptOk = Services.prompt.prompt(null, title, message, newName, null, {});
|
||||
|
||||
if (!promptOk) {
|
||||
// Prompt has been cancelled
|
||||
let menuitem = this.customMenuitem;
|
||||
this.menulist.selectedItem = menuitem;
|
||||
this.currentPresetKey = this.customPreset.key;
|
||||
return;
|
||||
}
|
||||
|
||||
let newPreset = {
|
||||
key: w + "x" + h,
|
||||
|
@ -18,8 +18,11 @@ function test() {
|
||||
// Mocking prompt
|
||||
oldPrompt = Services.prompt;
|
||||
Services.prompt = {
|
||||
value: "",
|
||||
returnBool: true,
|
||||
prompt: function(aParent, aDialogTitle, aText, aValue, aCheckMsg, aCheckState) {
|
||||
aValue.value = "Testing preset";
|
||||
aValue.value = this.value;
|
||||
return this.returnBool;
|
||||
}
|
||||
};
|
||||
|
||||
@ -42,10 +45,23 @@ function test() {
|
||||
}
|
||||
|
||||
function testAddCustomPreset() {
|
||||
// Tries to add a custom preset and cancel the prompt
|
||||
let idx = instance.menulist.selectedIndex;
|
||||
let presetCount = instance.presets.length;
|
||||
|
||||
Services.prompt.value = "";
|
||||
Services.prompt.returnBool = false;
|
||||
instance.addbutton.doCommand();
|
||||
|
||||
is(idx, instance.menulist.selectedIndex, "selected item didn't change after add preset and cancel");
|
||||
is(presetCount, instance.presets.length, "number of presets didn't change after add preset and cancel");
|
||||
|
||||
let customHeight = 123, customWidth = 456;
|
||||
instance.setSize(customWidth, customHeight);
|
||||
|
||||
// Adds the custom preset with "Testing preset" as label (look at mock upper)
|
||||
// Adds the custom preset with "Testing preset"
|
||||
Services.prompt.value = "Testing preset";
|
||||
Services.prompt.returnBool = true;
|
||||
instance.addbutton.doCommand();
|
||||
|
||||
instance.menulist.selectedIndex = 1;
|
||||
|
@ -5,9 +5,11 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
"use strict";
|
||||
|
||||
const DBG_STRINGS_URI = "chrome://browser/locale/devtools/debugger.properties";
|
||||
const LAZY_EMPTY_DELAY = 150; // ms
|
||||
|
||||
Components.utils.import('resource://gre/modules/Services.jsm');
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["VariablesView", "create"];
|
||||
|
||||
@ -140,7 +142,7 @@ VariablesView.prototype = {
|
||||
this._enumVisible = aFlag;
|
||||
|
||||
for (let [, scope] in this) {
|
||||
scope._nonEnumVisible = aFlag;
|
||||
scope._enumVisible = aFlag;
|
||||
}
|
||||
},
|
||||
|
||||
@ -1119,7 +1121,7 @@ create({ constructor: Variable, proto: Scope.prototype }, {
|
||||
|
||||
let separatorLabel = this._separatorLabel = document.createElement("label");
|
||||
separatorLabel.className = "plain";
|
||||
separatorLabel.setAttribute("value", ":");
|
||||
separatorLabel.setAttribute("value", this.ownerView.separator);
|
||||
|
||||
let valueLabel = this._valueLabel = document.createElement("label");
|
||||
valueLabel.className = "value plain";
|
||||
@ -1127,10 +1129,13 @@ create({ constructor: Variable, proto: Scope.prototype }, {
|
||||
this._title.appendChild(separatorLabel);
|
||||
this._title.appendChild(valueLabel);
|
||||
|
||||
if (VariablesView.isPrimitive(aDescriptor)) {
|
||||
let isPrimitive = VariablesView.isPrimitive(aDescriptor);
|
||||
let isUndefined = VariablesView.isUndefined(aDescriptor);
|
||||
|
||||
if (isPrimitive || isUndefined) {
|
||||
this.hideArrow();
|
||||
}
|
||||
if (aDescriptor.get || aDescriptor.set) {
|
||||
if (!isUndefined && (aDescriptor.get || aDescriptor.set)) {
|
||||
this.addProperty("get", { value: aDescriptor.get });
|
||||
this.addProperty("set", { value: aDescriptor.set });
|
||||
this.expand();
|
||||
@ -1493,6 +1498,33 @@ VariablesView.isPrimitive = function VV_isPrimitive(aDescriptor) {
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the descriptor represents an undefined value.
|
||||
*
|
||||
* @param object aDescriptor
|
||||
* The variable's descriptor.
|
||||
*/
|
||||
VariablesView.isUndefined = function VV_isUndefined(aDescriptor) {
|
||||
// For accessor property descriptors, the getter and setter need to be
|
||||
// contained in 'get' and 'set' properties.
|
||||
let getter = aDescriptor.get;
|
||||
let setter = aDescriptor.set;
|
||||
if (typeof getter == "object" && getter.type == "undefined" &&
|
||||
typeof setter == "object" && setter.type == "undefined") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// As described in the remote debugger protocol, the value grip
|
||||
// must be contained in a 'value' property.
|
||||
// For convenience, undefined is considered a type.
|
||||
let grip = aDescriptor.value;
|
||||
if (grip && grip.type == "undefined") {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the descriptor represents a falsy value.
|
||||
*
|
||||
@ -1609,6 +1641,30 @@ VariablesView.getClass = function VV_getClass(aGrip) {
|
||||
return "token-other";
|
||||
};
|
||||
|
||||
/**
|
||||
* Localization convenience methods.
|
||||
*/
|
||||
let L10N = {
|
||||
/**
|
||||
* L10N shortcut function.
|
||||
*
|
||||
* @param string aName
|
||||
* @return string
|
||||
*/
|
||||
getStr: function L10N_getStr(aName) {
|
||||
return this.stringBundle.GetStringFromName(aName);
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(L10N, "stringBundle", function() {
|
||||
return Services.strings.createBundle(DBG_STRINGS_URI);
|
||||
});
|
||||
|
||||
/**
|
||||
* The separator label between the variables or properties name and value.
|
||||
*/
|
||||
Scope.prototype.separator = L10N.getStr("variablesSeparatorLabel");
|
||||
|
||||
/**
|
||||
* A monotonically-increasing counter, that guarantees the uniqueness of scope,
|
||||
* variables and properties ids.
|
||||
@ -1620,9 +1676,9 @@ VariablesView.getClass = function VV_getClass(aGrip) {
|
||||
*/
|
||||
let generateId = (function() {
|
||||
let count = 0;
|
||||
return function(aName = "") {
|
||||
return function VV_generateId(aName = "") {
|
||||
return aName.toLowerCase().trim().replace(/\s+/g, "-") + (++count);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
|
@ -59,9 +59,9 @@ this.EXPORTED_SYMBOLS = ["CssRuleView",
|
||||
/**
|
||||
* ElementStyle maintains a list of Rule objects for a given element.
|
||||
*
|
||||
* @param Element aElement
|
||||
* @param {Element} aElement
|
||||
* The element whose style we are viewing.
|
||||
* @param object aStore
|
||||
* @param {object} aStore
|
||||
* The ElementStyle can use this object to store metadata
|
||||
* that might outlast the rule view, particularly the current
|
||||
* set of disabled properties.
|
||||
@ -189,7 +189,7 @@ ElementStyle.prototype = {
|
||||
* @param {object} aOptions
|
||||
* Options for creating the Rule, see the Rule constructor.
|
||||
*
|
||||
* @return true if we added the rule.
|
||||
* @return {bool} true if we added the rule.
|
||||
*/
|
||||
_maybeAddRule: function ElementStyle_maybeAddRule(aOptions)
|
||||
{
|
||||
@ -305,7 +305,7 @@ ElementStyle.prototype = {
|
||||
* @param {TextProperty} aProp
|
||||
* The text property to update.
|
||||
*
|
||||
* @return True if the TextProperty's overridden state (or any of its
|
||||
* @return {bool} true if the TextProperty's overridden state (or any of its
|
||||
* computed properties overridden state) changed.
|
||||
*/
|
||||
_updatePropertyOverridden: function ElementStyle_updatePropertyOverridden(aProp)
|
||||
@ -420,9 +420,8 @@ Rule.prototype = {
|
||||
* Returns true if the rule matches the creation options
|
||||
* specified.
|
||||
*
|
||||
* @param object aOptions
|
||||
* Creation options. See the Rule constructor for
|
||||
* documentation.
|
||||
* @param {object} aOptions
|
||||
* Creation options. See the Rule constructor for documentation.
|
||||
*/
|
||||
matches: function Rule_matches(aOptions)
|
||||
{
|
||||
@ -673,12 +672,12 @@ Rule.prototype = {
|
||||
*
|
||||
* If no existing properties match the property, nothing happens.
|
||||
*
|
||||
* @param TextProperty aNewProp
|
||||
* @param {TextProperty} aNewProp
|
||||
* The current version of the property, as parsed from the
|
||||
* cssText in Rule._getTextProperties().
|
||||
*
|
||||
* @returns true if a property was updated, false if no properties
|
||||
* were updated.
|
||||
* @return {bool} true if a property was updated, false if no properties
|
||||
* were updated.
|
||||
*/
|
||||
_updateTextProperty: function Rule__updateTextProperty(aNewProp) {
|
||||
let match = { rank: 0, prop: null };
|
||||
@ -801,7 +800,7 @@ TextProperty.prototype = {
|
||||
* Set all the values from another TextProperty instance into
|
||||
* this TextProperty instance.
|
||||
*
|
||||
* @param TextProperty aOther
|
||||
* @param {TextProperty} aOther
|
||||
* The other TextProperty instance.
|
||||
*/
|
||||
set: function TextProperty_set(aOther)
|
||||
@ -866,9 +865,9 @@ TextProperty.prototype = {
|
||||
* apply to a given element. After construction, the 'element'
|
||||
* property will be available with the user interface.
|
||||
*
|
||||
* @param Document aDoc
|
||||
* @param {Document} aDoc
|
||||
* The document that will contain the rule view.
|
||||
* @param object aStore
|
||||
* @param {object} aStore
|
||||
* The CSS rule view can use this object to store metadata
|
||||
* that might outlast the rule view, particularly the current
|
||||
* set of disabled properties.
|
||||
@ -895,7 +894,7 @@ CssRuleView.prototype = {
|
||||
_viewedElement: null,
|
||||
|
||||
/**
|
||||
* Returns true if the rule view currently has an input editor visible.
|
||||
* Return {bool} true if the rule view currently has an input editor visible.
|
||||
*/
|
||||
get isEditing() {
|
||||
return this.element.querySelectorAll(".styleinspector-propertyeditor").length > 0;
|
||||
@ -1120,7 +1119,8 @@ CssRuleView.prototype = {
|
||||
* Update the rule view's context menu by disabling irrelevant menuitems and
|
||||
* enabling relevant ones.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
* @param {Event} aEvent
|
||||
* The event object.
|
||||
*/
|
||||
_onMenuUpdate: function CssRuleView_onMenuUpdate(aEvent)
|
||||
{
|
||||
@ -1159,7 +1159,8 @@ CssRuleView.prototype = {
|
||||
/**
|
||||
* Copy selected text from the rule view.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
* @param {Event} aEvent
|
||||
* The event object.
|
||||
*/
|
||||
_onCopy: function CssRuleView_onCopy(aEvent)
|
||||
{
|
||||
@ -1194,7 +1195,8 @@ CssRuleView.prototype = {
|
||||
/**
|
||||
* Copy a rule from the rule view.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
* @param {Event} aEvent
|
||||
* The event object.
|
||||
*/
|
||||
_onCopyRule: function CssRuleView_onCopyRule(aEvent)
|
||||
{
|
||||
@ -1247,7 +1249,8 @@ CssRuleView.prototype = {
|
||||
/**
|
||||
* Copy a declaration from the rule view.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
* @param {Event} aEvent
|
||||
* The event object.
|
||||
*/
|
||||
_onCopyDeclaration: function CssRuleView_onCopyDeclaration(aEvent)
|
||||
{
|
||||
@ -1286,7 +1289,8 @@ CssRuleView.prototype = {
|
||||
/**
|
||||
* Copy a property name from the rule view.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
* @param {Event} aEvent
|
||||
* The event object.
|
||||
*/
|
||||
_onCopyProperty: function CssRuleView_onCopyProperty(aEvent)
|
||||
{
|
||||
@ -1308,7 +1312,8 @@ CssRuleView.prototype = {
|
||||
/**
|
||||
* Copy a property value from the rule view.
|
||||
*
|
||||
* @param aEvent The event object
|
||||
* @param {Event} aEvent
|
||||
* The event object.
|
||||
*/
|
||||
_onCopyPropertyValue: function CssRuleView_onCopyPropertyValue(aEvent)
|
||||
{
|
||||
@ -1330,9 +1335,9 @@ CssRuleView.prototype = {
|
||||
/**
|
||||
* Create a RuleEditor.
|
||||
*
|
||||
* @param CssRuleView aRuleView
|
||||
* @param {CssRuleView} aRuleView
|
||||
* The CssRuleView containg the document holding this rule editor.
|
||||
* @param Rule aRule
|
||||
* @param {Rule} aRule
|
||||
* The Rule object we're editing.
|
||||
* @constructor
|
||||
*/
|
||||
@ -1451,7 +1456,8 @@ RuleEditor.prototype = {
|
||||
textContent: ", "
|
||||
});
|
||||
}
|
||||
let cls = element.mozMatchesSelector(selector) ? "ruleview-selector-matched" : "ruleview-selector-unmatched";
|
||||
let cls = element.mozMatchesSelector(selector) ? "ruleview-selector-matched" :
|
||||
"ruleview-selector-unmatched";
|
||||
createChild(this.selectorText, "span", {
|
||||
class: cls,
|
||||
textContent: selector
|
||||
@ -1472,11 +1478,11 @@ RuleEditor.prototype = {
|
||||
/**
|
||||
* Programatically add a new property to the rule.
|
||||
*
|
||||
* @param string aName
|
||||
* @param {string} aName
|
||||
* Property name.
|
||||
* @param string aValue
|
||||
* @param {string} aValue
|
||||
* Property value.
|
||||
* @param string aPriority
|
||||
* @param {string} aPriority
|
||||
* Property priority.
|
||||
*/
|
||||
addProperty: function RuleEditor_addProperty(aName, aValue, aPriority)
|
||||
@ -1524,9 +1530,9 @@ RuleEditor.prototype = {
|
||||
* Called when the new property input has been dismissed.
|
||||
* Will create a new TextProperty if necessary.
|
||||
*
|
||||
* @param string aValue
|
||||
* @param {string} aValue
|
||||
* The value in the editor.
|
||||
* @param bool aCommit
|
||||
* @param {bool} aCommit
|
||||
* True if the value should be committed.
|
||||
*/
|
||||
_onNewProperty: function RuleEditor__onNewProperty(aValue, aCommit)
|
||||
@ -1836,7 +1842,7 @@ TextPropertyEditor.prototype = {
|
||||
*
|
||||
* @param {string} aValue
|
||||
* The value from the text editor.
|
||||
* @return an object with 'value' and 'priority' properties.
|
||||
* @return {object} an object with 'value' and 'priority' properties.
|
||||
*/
|
||||
_parseValue: function TextPropertyEditor_parseValue(aValue)
|
||||
{
|
||||
@ -1853,7 +1859,7 @@ TextPropertyEditor.prototype = {
|
||||
*
|
||||
* @param {string} aValue
|
||||
* The value contained in the editor.
|
||||
* @param {boolean} aCommit
|
||||
* @param {bool} aCommit
|
||||
* True if the change should be applied.
|
||||
*/
|
||||
_onValueDone: function PropertyEditor_onValueDone(aValue, aCommit)
|
||||
@ -1871,12 +1877,11 @@ TextPropertyEditor.prototype = {
|
||||
/**
|
||||
* Validate this property.
|
||||
*
|
||||
* @param {String} [aValue]
|
||||
* @param {string} [aValue]
|
||||
* Override the actual property value used for validation without
|
||||
* applying property values e.g. validate as you type.
|
||||
*
|
||||
* @returns {Boolean}
|
||||
* True if the property value is valid, false otherwise.
|
||||
* @return {bool} true if the property value is valid, false otherwise.
|
||||
*/
|
||||
_validate: function TextPropertyEditor_validate(aValue)
|
||||
{
|
||||
@ -1945,12 +1950,12 @@ function editableField(aOptions)
|
||||
* clicks and sit in the editing tab order, and call
|
||||
* a callback when it is activated.
|
||||
*
|
||||
* @param object aOptions
|
||||
* @param {object} aOptions
|
||||
* The options for this editor, including:
|
||||
* {Element} element: The DOM element.
|
||||
* {string} trigger: The DOM event that should trigger editing,
|
||||
* defaults to "click"
|
||||
* @param function aCallback
|
||||
* @param {function} aCallback
|
||||
* Called when the editor is activated.
|
||||
*/
|
||||
this.editableItem = function editableItem(aOptions, aCallback)
|
||||
@ -2159,6 +2164,355 @@ InplaceEditor.prototype = {
|
||||
this.input.style.width = width + "px";
|
||||
},
|
||||
|
||||
/**
|
||||
* Increment property values in rule view.
|
||||
*
|
||||
* @param {number} increment
|
||||
* The amount to increase/decrease the property value.
|
||||
* @return {bool} true if value has been incremented.
|
||||
*/
|
||||
_incrementValue: function InplaceEditor_incrementValue(increment)
|
||||
{
|
||||
let value = this.input.value;
|
||||
let selectionStart = this.input.selectionStart;
|
||||
let selectionEnd = this.input.selectionEnd;
|
||||
|
||||
let newValue = this._incrementCSSValue(value, increment, selectionStart, selectionEnd);
|
||||
|
||||
if (!newValue) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.input.value = newValue.value;
|
||||
this.input.setSelectionRange(newValue.start, newValue.end);
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Increment the property value based on the property type.
|
||||
*
|
||||
* @param {string} value
|
||||
* Property value.
|
||||
* @param {number} increment
|
||||
* Amount to increase/decrease the property value.
|
||||
* @param {number} selStart
|
||||
* Starting index of the value.
|
||||
* @param {number} selEnd
|
||||
* Ending index of the value.
|
||||
* @return {object} object with properties 'value', 'start', and 'end'.
|
||||
*/
|
||||
_incrementCSSValue: function InplaceEditor_incrementCSSValue(value, increment, selStart,
|
||||
selEnd)
|
||||
{
|
||||
let range = this._parseCSSValue(value, selStart);
|
||||
let type = (range && range.type) || "";
|
||||
let rawValue = (range ? value.substring(range.start, range.end) : "");
|
||||
let incrementedValue = null, selection;
|
||||
|
||||
if (type === "num") {
|
||||
let newValue = this._incrementRawValue(rawValue, increment);
|
||||
if (newValue !== null) {
|
||||
incrementedValue = newValue;
|
||||
selection = [0, incrementedValue.length];
|
||||
}
|
||||
} else if (type === "hex") {
|
||||
let exprOffset = selStart - range.start;
|
||||
let exprOffsetEnd = selEnd - range.start;
|
||||
let newValue = this._incHexColor(rawValue, increment, exprOffset, exprOffsetEnd);
|
||||
if (newValue) {
|
||||
incrementedValue = newValue.value;
|
||||
selection = newValue.selection;
|
||||
}
|
||||
} else {
|
||||
let info;
|
||||
if (type === "rgb" || type === "hsl") {
|
||||
info = {};
|
||||
let part = value.substring(range.start, selStart).split(",").length - 1;
|
||||
if (part === 3) { // alpha
|
||||
info.minValue = 0;
|
||||
info.maxValue = 1;
|
||||
} else if (type === "rgb") {
|
||||
info.minValue = 0;
|
||||
info.maxValue = 255;
|
||||
} else if (part !== 0) { // hsl percentage
|
||||
info.minValue = 0;
|
||||
info.maxValue = 100;
|
||||
|
||||
// select the previous number if the selection is at the end of a percentage sign
|
||||
if (value.charAt(selStart - 1) === "%") {
|
||||
--selStart;
|
||||
}
|
||||
}
|
||||
}
|
||||
return this._incrementGenericValue(value, increment, selStart, selEnd, info);
|
||||
}
|
||||
|
||||
if (incrementedValue === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let preRawValue = value.substr(0, range.start);
|
||||
let postRawValue = value.substr(range.end);
|
||||
|
||||
return {
|
||||
value: preRawValue + incrementedValue + postRawValue,
|
||||
start: range.start + selection[0],
|
||||
end: range.start + selection[1]
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Parses the property value and type.
|
||||
*
|
||||
* @param {string} value
|
||||
* Property value.
|
||||
* @param {number} offset
|
||||
* Starting index of value.
|
||||
* @return {object} object with properties 'value', 'start', 'end', and 'type'.
|
||||
*/
|
||||
_parseCSSValue: function InplaceEditor_parseCSSValue(value, offset)
|
||||
{
|
||||
const reSplitCSS = /(url\("?[^"\)]+"?\)?)|(rgba?\([^)]*\)?)|(hsla?\([^)]*\)?)|(#[\dA-Fa-f]+)|(-?\d+(\.\d+)?(%|[a-z]{1,4})?)|"([^"]*)"?|'([^']*)'?|([^,\s\/!\(\)]+)|(!(.*)?)/;
|
||||
let start = 0;
|
||||
let m;
|
||||
|
||||
// retreive values from left to right until we find the one at our offset
|
||||
while ((m = reSplitCSS.exec(value)) &&
|
||||
(m.index + m[0].length < offset)) {
|
||||
value = value.substr(m.index + m[0].length);
|
||||
start += m.index + m[0].length;
|
||||
offset -= m.index + m[0].length;
|
||||
}
|
||||
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
|
||||
let type;
|
||||
if (m[1]) {
|
||||
type = "url";
|
||||
} else if (m[2]) {
|
||||
type = "rgb";
|
||||
} else if (m[3]) {
|
||||
type = "hsl";
|
||||
} else if (m[4]) {
|
||||
type = "hex";
|
||||
} else if (m[5]) {
|
||||
type = "num";
|
||||
}
|
||||
|
||||
return {
|
||||
value: m[0],
|
||||
start: start + m.index,
|
||||
end: start + m.index + m[0].length,
|
||||
type: type
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Increment the property value for types other than
|
||||
* number or hex, such as rgb, hsl, and file names.
|
||||
*
|
||||
* @param {string} value
|
||||
* Property value.
|
||||
* @param {number} increment
|
||||
* Amount to increment/decrement.
|
||||
* @param {number} offset
|
||||
* Starting index of the property value.
|
||||
* @param {number} offsetEnd
|
||||
* Ending index of the property value.
|
||||
* @param {object} info
|
||||
* Object with details about the property value.
|
||||
* @return {object} object with properties 'value', 'start', and 'end'.
|
||||
*/
|
||||
_incrementGenericValue: function InplaceEditor_incrementGenericValue(value, increment, offset,
|
||||
offsetEnd, info)
|
||||
{
|
||||
// Try to find a number around the cursor to increment.
|
||||
let start, end;
|
||||
// Check if we are incrementing in a non-number context (such as a URL)
|
||||
if (/^-?[0-9.]/.test(value.substring(offset, offsetEnd)) &&
|
||||
!(/\d/.test(value.charAt(offset - 1) + value.charAt(offsetEnd)))) {
|
||||
// We have a number selected, possibly with a suffix, and we are not in
|
||||
// the disallowed case of just part of a known number being selected.
|
||||
// Use that number.
|
||||
start = offset;
|
||||
end = offsetEnd;
|
||||
} else {
|
||||
// Parse periods as belonging to the number only if we are in a known number
|
||||
// context. (This makes incrementing the 1 in 'image1.gif' work.)
|
||||
let pattern = "[" + (info ? "0-9." : "0-9") + "]*";
|
||||
let before = new RegExp(pattern + "$").exec(value.substr(0, offset))[0].length;
|
||||
let after = new RegExp("^" + pattern).exec(value.substr(offset))[0].length;
|
||||
|
||||
start = offset - before;
|
||||
end = offset + after;
|
||||
|
||||
// Expand the number to contain an initial minus sign if it seems
|
||||
// free-standing.
|
||||
if (value.charAt(start - 1) === "-" &&
|
||||
(start - 1 === 0 || /[ (:,='"]/.test(value.charAt(start - 2)))) {
|
||||
--start;
|
||||
}
|
||||
}
|
||||
|
||||
if (start !== end)
|
||||
{
|
||||
// Include percentages as part of the incremented number (they are
|
||||
// common enough).
|
||||
if (value.charAt(end) === "%") {
|
||||
++end;
|
||||
}
|
||||
|
||||
let first = value.substr(0, start);
|
||||
let mid = value.substring(start, end);
|
||||
let last = value.substr(end);
|
||||
|
||||
mid = this._incrementRawValue(mid, increment, info);
|
||||
|
||||
if (mid !== null) {
|
||||
return {
|
||||
value: first + mid + last,
|
||||
start: start,
|
||||
end: start + mid.length
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Increment the property value for numbers.
|
||||
*
|
||||
* @param {string} rawValue
|
||||
* Raw value to increment.
|
||||
* @param {number} increment
|
||||
* Amount to increase/decrease the raw value.
|
||||
* @param {object} info
|
||||
* Object with info about the property value.
|
||||
* @return {string} the incremented value.
|
||||
*/
|
||||
_incrementRawValue: function InplaceEditor_incrementRawValue(rawValue, increment, info)
|
||||
{
|
||||
let num = parseFloat(rawValue);
|
||||
|
||||
if (isNaN(num)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let number = /\d+(\.\d+)?/.exec(rawValue);
|
||||
let units = rawValue.substr(number.index + number[0].length);
|
||||
|
||||
// avoid rounding errors
|
||||
let newValue = Math.round((num + increment) * 1000) / 1000;
|
||||
|
||||
if (info && "minValue" in info) {
|
||||
newValue = Math.max(newValue, info.minValue);
|
||||
}
|
||||
if (info && "maxValue" in info) {
|
||||
newValue = Math.min(newValue, info.maxValue);
|
||||
}
|
||||
|
||||
newValue = newValue.toString();
|
||||
|
||||
return newValue + units;
|
||||
},
|
||||
|
||||
/**
|
||||
* Increment the property value for hex.
|
||||
*
|
||||
* @param {string} value
|
||||
* Property value.
|
||||
* @param {number} increment
|
||||
* Amount to increase/decrease the property value.
|
||||
* @param {number} offset
|
||||
* Starting index of the property value.
|
||||
* @param {number} offsetEnd
|
||||
* Ending index of the property value.
|
||||
* @return {object} object with properties 'value' and 'selection'.
|
||||
*/
|
||||
_incHexColor: function InplaceEditor_incHexColor(rawValue, increment, offset, offsetEnd)
|
||||
{
|
||||
// Return early if no part of the rawValue is selected.
|
||||
if (offsetEnd > rawValue.length && offset >= rawValue.length) {
|
||||
return;
|
||||
}
|
||||
if (offset < 1 && offsetEnd <= 1) {
|
||||
return;
|
||||
}
|
||||
// Ignore the leading #.
|
||||
rawValue = rawValue.substr(1);
|
||||
--offset;
|
||||
--offsetEnd;
|
||||
|
||||
// Clamp the selection to within the actual value.
|
||||
offset = Math.max(offset, 0);
|
||||
offsetEnd = Math.min(offsetEnd, rawValue.length);
|
||||
offsetEnd = Math.max(offsetEnd, offset);
|
||||
|
||||
// Normalize #ABC -> #AABBCC.
|
||||
if (rawValue.length === 3) {
|
||||
rawValue = rawValue.charAt(0) + rawValue.charAt(0) +
|
||||
rawValue.charAt(1) + rawValue.charAt(1) +
|
||||
rawValue.charAt(2) + rawValue.charAt(2);
|
||||
offset *= 2;
|
||||
offsetEnd *= 2;
|
||||
}
|
||||
|
||||
if (rawValue.length !== 6) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If no selection, increment an adjacent color, preferably one to the left.
|
||||
if (offset === offsetEnd) {
|
||||
if (offset === 0) {
|
||||
offsetEnd = 1;
|
||||
} else {
|
||||
offset = offsetEnd - 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Make the selection cover entire parts.
|
||||
offset -= offset % 2;
|
||||
offsetEnd += offsetEnd % 2;
|
||||
|
||||
// Remap the increments from [0.1, 1, 10] to [1, 1, 16].
|
||||
if (-1 < increment && increment < 1) {
|
||||
increment = (increment < 0 ? -1 : 1);
|
||||
}
|
||||
if (Math.abs(increment) === 10) {
|
||||
increment = (increment < 0 ? -16 : 16);
|
||||
}
|
||||
|
||||
let isUpper = (rawValue.toUpperCase() === rawValue);
|
||||
|
||||
for (let pos = offset; pos < offsetEnd; pos += 2) {
|
||||
// Increment the part in [pos, pos+2).
|
||||
let mid = rawValue.substr(pos, 2);
|
||||
let value = parseInt(mid, 16);
|
||||
|
||||
if (isNaN(value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mid = Math.min(Math.max(value + increment, 0), 255).toString(16);
|
||||
|
||||
while (mid.length < 2) {
|
||||
mid = "0" + mid;
|
||||
}
|
||||
if (isUpper) {
|
||||
mid = mid.toUpperCase();
|
||||
}
|
||||
|
||||
rawValue = rawValue.substr(0, pos) + mid + rawValue.substr(pos + 2);
|
||||
}
|
||||
|
||||
return {
|
||||
value: "#" + rawValue,
|
||||
selection: [offset + 1, offsetEnd + 1]
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Call the client's done handler and clear out.
|
||||
*/
|
||||
@ -2174,6 +2528,7 @@ InplaceEditor.prototype = {
|
||||
let val = this.input.value.trim();
|
||||
return this.done(this.cancelled ? this.initial : val, !this.cancelled);
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
@ -2188,9 +2543,43 @@ InplaceEditor.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle the input field's keypress event.
|
||||
*/
|
||||
_onKeyPress: function InplaceEditor_onKeyPress(aEvent)
|
||||
{
|
||||
let prevent = false;
|
||||
|
||||
const largeIncrement = 100;
|
||||
const mediumIncrement = 10;
|
||||
const smallIncrement = 0.1;
|
||||
|
||||
let increment = 0;
|
||||
|
||||
if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_UP
|
||||
|| aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP) {
|
||||
increment = 1;
|
||||
} else if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_DOWN
|
||||
|| aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN) {
|
||||
increment = -1;
|
||||
}
|
||||
|
||||
if (aEvent.shiftKey && !aEvent.altKey) {
|
||||
if (aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP
|
||||
|| aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN) {
|
||||
increment *= largeIncrement;
|
||||
} else {
|
||||
increment *= mediumIncrement;
|
||||
}
|
||||
} else if (aEvent.altKey && !aEvent.shiftKey) {
|
||||
increment *= smallIncrement;
|
||||
}
|
||||
|
||||
if (increment && this._incrementValue(increment) ) {
|
||||
this._updateSize();
|
||||
prevent = true;
|
||||
}
|
||||
|
||||
if (this.multiline &&
|
||||
aEvent.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN &&
|
||||
aEvent.shiftKey) {
|
||||
@ -2258,7 +2647,7 @@ InplaceEditor.prototype = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle changes the input text.
|
||||
* Handle changes to the input text.
|
||||
*/
|
||||
_onInput: function InplaceEditor_onInput(aEvent)
|
||||
{
|
||||
@ -2285,7 +2674,10 @@ InplaceEditor.prototype = {
|
||||
* own compartment, those expandos live on Xray wrappers that are only visible
|
||||
* within this JSM. So we provide a little workaround here.
|
||||
*/
|
||||
this._getInplaceEditorForSpan = function _getInplaceEditorForSpan(aSpan) { return aSpan.inplaceEditor; };
|
||||
this._getInplaceEditorForSpan = function _getInplaceEditorForSpan(aSpan)
|
||||
{
|
||||
return aSpan.inplaceEditor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Store of CSSStyleDeclarations mapped to properties that have been changed by
|
||||
@ -2304,13 +2696,13 @@ UserProperties.prototype = {
|
||||
*
|
||||
* @param {CSSStyleDeclaration} aStyle
|
||||
* The CSSStyleDeclaration against which the property is mapped.
|
||||
* @param {String} aName
|
||||
* @param {string} aName
|
||||
* The name of the property to get.
|
||||
* @param {String} aComputedValue
|
||||
* @param {string} aComputedValue
|
||||
* The computed value of the property. The user value will only be
|
||||
* returned if the computed value hasn't changed since, and this will
|
||||
* be returned as the default if no user value is available.
|
||||
* @returns {String}
|
||||
* @return {string}
|
||||
* The property value if it has previously been set by the user, null
|
||||
* otherwise.
|
||||
*/
|
||||
|
@ -36,6 +36,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_computedview_bug_703643_context_menu_copy.js \
|
||||
browser_ruleview_734259_style_editor_link.js \
|
||||
browser_computedview_734259_style_editor_link.js \
|
||||
browser_bug722691_rule_view_increment.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
@ -0,0 +1,205 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that increasing/decreasing values in rule view using
|
||||
// arrow keys works correctly.
|
||||
|
||||
let tempScope = {};
|
||||
Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
|
||||
let CssRuleView = tempScope.CssRuleView;
|
||||
let _ElementStyle = tempScope._ElementStyle;
|
||||
let _editableField = tempScope._editableField;
|
||||
let inplaceEditor = tempScope._getInplaceEditorForSpan;
|
||||
|
||||
let doc;
|
||||
let ruleDialog;
|
||||
let ruleView;
|
||||
|
||||
function setUpTests()
|
||||
{
|
||||
doc.body.innerHTML = '<div id="test" style="' +
|
||||
'margin-top:0px;' +
|
||||
'padding-top: 0px;' +
|
||||
'color:#000000;' +
|
||||
'background-color: #000000; >"'+
|
||||
'</div>';
|
||||
let testElement = doc.getElementById("test");
|
||||
ruleDialog = openDialog("chrome://browser/content/devtools/cssruleview.xul",
|
||||
"cssruleviewtest",
|
||||
"width=350,height=350");
|
||||
ruleDialog.addEventListener("load", function onLoad(evt) {
|
||||
ruleDialog.removeEventListener("load", onLoad, true);
|
||||
let doc = ruleDialog.document;
|
||||
ruleView = new CssRuleView(doc);
|
||||
doc.documentElement.appendChild(ruleView.element);
|
||||
ruleView.highlight(testElement);
|
||||
waitForFocus(runTests, ruleDialog);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function runTests()
|
||||
{
|
||||
let idRuleEditor = ruleView.element.children[0]._ruleEditor;
|
||||
let marginPropEditor = idRuleEditor.rule.textProps[0].editor;
|
||||
let paddingPropEditor = idRuleEditor.rule.textProps[1].editor;
|
||||
let hexColorPropEditor = idRuleEditor.rule.textProps[2].editor;
|
||||
let rgbColorPropEditor = idRuleEditor.rule.textProps[3].editor;
|
||||
|
||||
(function() {
|
||||
info("INCREMENTS");
|
||||
newTest( marginPropEditor, {
|
||||
1: { alt: true, start: "0px", end: "0.1px", selectAll: true },
|
||||
2: { start: "0px", end: "1px", selectAll: true },
|
||||
3: { shift: true, start: "0px", end: "10px", selectAll: true },
|
||||
4: { down: true, alt: true, start: "0.1px", end: "0px", selectAll: true },
|
||||
5: { down: true, start: "0px", end: "-1px", selectAll: true },
|
||||
6: { down: true, shift: true, start: "0px", end: "-10px", selectAll: true },
|
||||
7: { pageUp: true, shift: true, start: "0px", end: "100px", selectAll: true },
|
||||
8: { pageDown: true, shift: true, start: "0px", end: "-100px", selectAll: true,
|
||||
nextTest: test2 }
|
||||
});
|
||||
EventUtils.synthesizeMouse(marginPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
||||
})();
|
||||
|
||||
function test2() {
|
||||
info("UNITS");
|
||||
newTest( paddingPropEditor, {
|
||||
1: { start: "0px", end: "1px", selectAll: true },
|
||||
2: { start: "0pt", end: "1pt", selectAll: true },
|
||||
3: { start: "0pc", end: "1pc", selectAll: true },
|
||||
4: { start: "0em", end: "1em", selectAll: true },
|
||||
5: { start: "0%", end: "1%", selectAll: true },
|
||||
6: { start: "0in", end: "1in", selectAll: true },
|
||||
7: { start: "0cm", end: "1cm", selectAll: true },
|
||||
8: { start: "0mm", end: "1mm", selectAll: true },
|
||||
9: { start: "0ex", end: "1ex", selectAll: true,
|
||||
nextTest: test3 }
|
||||
});
|
||||
EventUtils.synthesizeMouse(paddingPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
||||
};
|
||||
|
||||
function test3() {
|
||||
info("HEX COLORS");
|
||||
newTest( hexColorPropEditor, {
|
||||
1: { start: "#CCCCCC", end: "#CDCDCD", selectAll: true},
|
||||
2: { shift: true, start: "#CCCCCC", end: "#DCDCDC", selectAll: true },
|
||||
3: { start: "#CCCCCC", end: "#CDCCCC", selection: [1,3] },
|
||||
4: { shift: true, start: "#CCCCCC", end: "#DCCCCC", selection: [1,3] },
|
||||
5: { start: "#FFFFFF", end: "#FFFFFF", selectAll: true },
|
||||
6: { down: true, shift: true, start: "#000000", end: "#000000", selectAll: true,
|
||||
nextTest: test4 }
|
||||
});
|
||||
EventUtils.synthesizeMouse(hexColorPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
||||
};
|
||||
|
||||
function test4() {
|
||||
info("RGB COLORS");
|
||||
newTest( rgbColorPropEditor, {
|
||||
1: { start: "rgb(0,0,0)", end: "rgb(0,1,0)", selection: [6,7] },
|
||||
2: { shift: true, start: "rgb(0,0,0)", end: "rgb(0,10,0)", selection: [6,7] },
|
||||
3: { start: "rgb(0,255,0)", end: "rgb(0,255,0)", selection: [6,9] },
|
||||
4: { shift: true, start: "rgb(0,250,0)", end: "rgb(0,255,0)", selection: [6,9] },
|
||||
5: { down: true, start: "rgb(0,0,0)", end: "rgb(0,0,0)", selection: [6,7] },
|
||||
6: { down: true, shift: true, start: "rgb(0,5,0)", end: "rgb(0,0,0)", selection: [6,7],
|
||||
nextTest: test5 }
|
||||
});
|
||||
EventUtils.synthesizeMouse(rgbColorPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
||||
};
|
||||
|
||||
function test5() {
|
||||
info("SHORTHAND");
|
||||
newTest( paddingPropEditor, {
|
||||
1: { start: "0px 0px 0px 0px", end: "0px 1px 0px 0px", selection: [4,7] },
|
||||
2: { shift: true, start: "0px 0px 0px 0px", end: "0px 10px 0px 0px", selection: [4,7] },
|
||||
3: { start: "0px 0px 0px 0px", end: "1px 0px 0px 0px", selectAll: true },
|
||||
4: { shift: true, start: "0px 0px 0px 0px", end: "10px 0px 0px 0px", selectAll: true },
|
||||
5: { down: true, start: "0px 0px 0px 0px", end: "0px 0px -1px 0px", selection: [8,11] },
|
||||
6: { down: true, shift: true, start: "0px 0px 0px 0px", end: "-10px 0px 0px 0px", selectAll: true,
|
||||
nextTest: test6 }
|
||||
});
|
||||
EventUtils.synthesizeMouse(paddingPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
||||
};
|
||||
|
||||
function test6() {
|
||||
info("ODD CASES");
|
||||
newTest( marginPropEditor, {
|
||||
1: { start: "98.7%", end: "99.7%", selection: [3,3] },
|
||||
2: { alt: true, start: "98.7%", end: "98.8%", selection: [3,3] },
|
||||
3: { start: "0", end: "1" },
|
||||
4: { down: true, start: "0", end: "-1" },
|
||||
5: { start: "'a=-1'", end: "'a=0'", selection: [4,4] },
|
||||
6: { start: "0 -1px", end: "0 0px", selection: [2,2] },
|
||||
7: { start: "url(-1)", end: "url(-1)", selection: [4,4] },
|
||||
8: { start: "url('test1.1.png')", end: "url('test1.2.png')", selection: [11,11] },
|
||||
9: { start: "url('test1.png')", end: "url('test2.png')", selection: [9,9] },
|
||||
10: { shift: true, start: "url('test1.1.png')", end: "url('test11.1.png')", selection: [9,9] },
|
||||
11: { down: true, start: "url('test-1.png')", end: "url('test-2.png')", selection: [9,11] },
|
||||
12: { start: "url('test1.1.png')", end: "url('test1.2.png')", selection: [11,12] },
|
||||
13: { down: true, alt: true, start: "url('test-0.png')", end: "url('test--0.1.png')", selection: [10,11] },
|
||||
14: { alt: true, start: "url('test--0.1.png')", end: "url('test-0.png')", selection: [10,14],
|
||||
endTest: true }
|
||||
});
|
||||
EventUtils.synthesizeMouse(marginPropEditor.valueSpan, 1, 1, {}, ruleDialog);
|
||||
};
|
||||
}
|
||||
|
||||
function newTest( propEditor, tests )
|
||||
{
|
||||
waitForEditorFocus(propEditor.element, function onElementFocus(aEditor) {
|
||||
for( test in tests) {
|
||||
testIncrement( aEditor, tests[test] );
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
function testIncrement( aEditor, aOptions )
|
||||
{
|
||||
aEditor.input.value = aOptions.start;
|
||||
let input = aEditor.input;
|
||||
if ( aOptions.selectAll ) {
|
||||
input.select();
|
||||
} else if ( aOptions.selection ) {
|
||||
input.setSelectionRange(aOptions.selection[0], aOptions.selection[1]);
|
||||
}
|
||||
is(input.value, aOptions.start, "Value initialized at " + aOptions.start);
|
||||
input.addEventListener("keyup", function onIncrementUp() {
|
||||
input.removeEventListener("keyup", onIncrementUp, false);
|
||||
input = aEditor.input;
|
||||
is(input.value, aOptions.end, "Value changed to " + aOptions.end);
|
||||
if( aOptions.nextTest) {
|
||||
aOptions.nextTest();
|
||||
}
|
||||
else if( aOptions.endTest ) {
|
||||
finishTest();
|
||||
}
|
||||
}, false);
|
||||
let key;
|
||||
key = ( aOptions.down ) ? "VK_DOWN" : "VK_UP";
|
||||
key = ( aOptions.pageDown ) ? "VK_PAGE_DOWN" : ( aOptions.pageUp ) ? "VK_PAGE_UP" : key;
|
||||
EventUtils.synthesizeKey(key,
|
||||
{altKey: aOptions.alt, shiftKey: aOptions.shift},
|
||||
ruleDialog);
|
||||
}
|
||||
|
||||
function finishTest()
|
||||
{
|
||||
ruleView.clear();
|
||||
ruleDialog.close();
|
||||
ruleDialog = ruleView = null;
|
||||
doc = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload(evt) {
|
||||
gBrowser.selectedBrowser.removeEventListener(evt.type, onload, true);
|
||||
doc = content.document;
|
||||
waitForFocus(setUpTests, content);
|
||||
}, true);
|
||||
content.location = "data:text/html,sample document for bug 722691";
|
||||
}
|
@ -77,11 +77,11 @@ stepOutTooltip=Step Out (%S)
|
||||
|
||||
# LOCALIZATION NOTE (emptyStackText): The text that is displayed in the stack
|
||||
# frames list when there are no frames to display.
|
||||
emptyStackText=No stacks to display.
|
||||
emptyStackText=No stacks to display
|
||||
|
||||
# LOCALIZATION NOTE (emptyBreakpointsText): The text that is displayed in the
|
||||
# breakpoints list when there are no breakpoints to display.
|
||||
emptyBreakpointsText=No breakpoints to display.
|
||||
emptyBreakpointsText=No breakpoints to display
|
||||
|
||||
# LOCALIZATION NOTE (emptyGlobalsText): The text to display in the menulist
|
||||
# when there are no chrome globals available.
|
||||
@ -150,16 +150,33 @@ breakpointMenuItem.deleteAll=Remove all breakpoints
|
||||
# yet.
|
||||
loadingText=Loading\u2026
|
||||
|
||||
# LOCALIZATION NOTE (emptyStackText): The text that is displayed in the watch
|
||||
# expressions list to add a new item.
|
||||
addWatchExpressionText=Add watch expression
|
||||
|
||||
# LOCALIZATION NOTE (emptyVariablesText): The text that is displayed in the
|
||||
# variables pane when there are no variables to display.
|
||||
emptyVariablesText=No variables to display.
|
||||
emptyVariablesText=No variables to display
|
||||
|
||||
# LOCALIZATION NOTE (scopeLabel): The text that is displayed in the variables
|
||||
# pane as a header for each variable scope (e.g. "Global scope, "With scope",
|
||||
# etc.).
|
||||
scopeLabel=%S scope
|
||||
|
||||
# LOCALIZATION NOTE (watchExpressionsScopeLabel): The name of the watch
|
||||
# expressions scope. This text is displayed in the variables pane as a header for
|
||||
# the watch expressions scope.
|
||||
watchExpressionsScopeLabel=Watch expressions
|
||||
|
||||
# LOCALIZATION NOTE (globalScopeLabel): The name of the global scope. This text
|
||||
# is added to scopeLabel and displayed in the variables pane as a header for
|
||||
# the global scope.
|
||||
globalScopeLabel=Global
|
||||
|
||||
# LOCALIZATION NOTE (variablesSeparatorLabel): The text that is displayed
|
||||
# in the variables list as a separator between the name and value.
|
||||
variablesSeparatorLabel=:
|
||||
|
||||
# LOCALIZATION NOTE (watchExpressionsSeparatorLabel): The text that is displayed
|
||||
# in the watch expressions list as a separator between the code and evaluation.
|
||||
watchExpressionsSeparatorLabel=\ →
|
||||
|
@ -35,7 +35,7 @@
|
||||
|
||||
.list-item.empty {
|
||||
color: GrayText;
|
||||
padding: 4px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -130,6 +130,19 @@
|
||||
transition: margin 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variables and watch expressions pane
|
||||
*/
|
||||
|
||||
#variables\+expressions {
|
||||
background-color: white;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
#variables\+expressions[animated] {
|
||||
transition: margin 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack frames view
|
||||
*/
|
||||
@ -182,17 +195,45 @@
|
||||
margin: 0 0 -2px 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch expressions view
|
||||
*/
|
||||
|
||||
#expressions {
|
||||
background-color: white;
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
.dbg-expression {
|
||||
height: 20px;
|
||||
-moz-padding-start: 8px;
|
||||
}
|
||||
|
||||
.dbg-expression:last-child {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.dbg-expression-arrow {
|
||||
width: 10px;
|
||||
height: auto;
|
||||
background: url("chrome://browser/skin/devtools/commandline.png") 0px 4px no-repeat;
|
||||
}
|
||||
|
||||
.dbg-expression-input {
|
||||
font: 9pt monospace;
|
||||
}
|
||||
|
||||
.dbg-expression-delete {
|
||||
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variables view
|
||||
*/
|
||||
|
||||
#variables {
|
||||
background-color: white;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
#variables[animated] {
|
||||
transition: margin 0.25s ease-in-out;
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -37,7 +37,7 @@
|
||||
|
||||
.list-item.empty {
|
||||
color: GrayText;
|
||||
padding: 4px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -132,6 +132,19 @@
|
||||
transition: margin 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variables and watch expressions pane
|
||||
*/
|
||||
|
||||
#variables\+expressions {
|
||||
background-color: white;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
#variables\+expressions[animated] {
|
||||
transition: margin 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack frames view
|
||||
*/
|
||||
@ -184,17 +197,45 @@
|
||||
margin: 0 0 -2px 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch expressions view
|
||||
*/
|
||||
|
||||
#expressions {
|
||||
background-color: white;
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
.dbg-expression {
|
||||
height: 20px;
|
||||
-moz-padding-start: 8px;
|
||||
}
|
||||
|
||||
.dbg-expression:last-child {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.dbg-expression-arrow {
|
||||
width: 10px;
|
||||
height: auto;
|
||||
background: url("chrome://browser/skin/devtools/commandline.png") 0px 4px no-repeat;
|
||||
}
|
||||
|
||||
.dbg-expression-input {
|
||||
font: 9pt monospace;
|
||||
}
|
||||
|
||||
.dbg-expression-delete {
|
||||
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variables view
|
||||
*/
|
||||
|
||||
#variables {
|
||||
background-color: white;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
#variables[animated] {
|
||||
transition: margin 0.25s ease-in-out;
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -43,7 +43,7 @@
|
||||
|
||||
.list-item.empty {
|
||||
color: GrayText;
|
||||
padding: 4px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -138,6 +138,19 @@
|
||||
transition: margin 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Variables and watch expressions pane
|
||||
*/
|
||||
|
||||
#variables\+expressions {
|
||||
background-color: white;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
#variables\+expressions[animated] {
|
||||
transition: margin 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack frames view
|
||||
*/
|
||||
@ -190,17 +203,45 @@
|
||||
margin: 0 0 -2px 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Watch expressions view
|
||||
*/
|
||||
|
||||
#expressions {
|
||||
background-color: white;
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
.dbg-expression {
|
||||
height: 20px;
|
||||
-moz-padding-start: 8px;
|
||||
}
|
||||
|
||||
.dbg-expression:last-child {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.dbg-expression-arrow {
|
||||
width: 10px;
|
||||
height: auto;
|
||||
background: url("chrome://browser/skin/devtools/commandline.png") 0px 4px no-repeat;
|
||||
}
|
||||
|
||||
.dbg-expression-input {
|
||||
font: 9pt monospace;
|
||||
}
|
||||
|
||||
.dbg-expression-delete {
|
||||
-moz-image-region: rect(0, 32px, 16px, 16px);
|
||||
}
|
||||
|
||||
/**
|
||||
* Variables view
|
||||
*/
|
||||
|
||||
#variables {
|
||||
background-color: white;
|
||||
min-width: 50px;
|
||||
}
|
||||
|
||||
#variables[animated] {
|
||||
transition: margin 0.25s ease-in-out;
|
||||
min-height: 10px;
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
Reference in New Issue
Block a user