mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-02 01:48:05 +00:00
Merge m-c to inbound.
This commit is contained in:
commit
6cc28e31e6
@ -23,27 +23,53 @@ let gDropTargetShim = {
|
||||
/**
|
||||
* Initializes the drop target shim.
|
||||
*/
|
||||
init: function DropTargetShim_init() {
|
||||
let node = gGrid.node;
|
||||
init: function () {
|
||||
gGrid.node.addEventListener("dragstart", this, true);
|
||||
},
|
||||
|
||||
// Add drag event handlers.
|
||||
node.addEventListener("dragstart", this, true);
|
||||
node.addEventListener("dragend", this, true);
|
||||
/**
|
||||
* Add all event listeners needed during a drag operation.
|
||||
*/
|
||||
_addEventListeners: function () {
|
||||
gGrid.node.addEventListener("dragend", this);
|
||||
|
||||
let docElement = document.documentElement;
|
||||
docElement.addEventListener("dragover", this);
|
||||
docElement.addEventListener("dragenter", this);
|
||||
docElement.addEventListener("drop", this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Remove all event listeners that were needed during a drag operation.
|
||||
*/
|
||||
_removeEventListeners: function () {
|
||||
gGrid.node.removeEventListener("dragend", this);
|
||||
|
||||
let docElement = document.documentElement;
|
||||
docElement.removeEventListener("dragover", this);
|
||||
docElement.removeEventListener("dragenter", this);
|
||||
docElement.removeEventListener("drop", this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles all shim events.
|
||||
*/
|
||||
handleEvent: function DropTargetShim_handleEvent(aEvent) {
|
||||
handleEvent: function (aEvent) {
|
||||
switch (aEvent.type) {
|
||||
case "dragstart":
|
||||
this._start(aEvent);
|
||||
this._dragstart(aEvent);
|
||||
break;
|
||||
case "dragenter":
|
||||
aEvent.preventDefault();
|
||||
break;
|
||||
case "dragover":
|
||||
this._dragover(aEvent);
|
||||
break;
|
||||
case "drop":
|
||||
this._drop(aEvent);
|
||||
break;
|
||||
case "dragend":
|
||||
this._end(aEvent);
|
||||
this._dragend(aEvent);
|
||||
break;
|
||||
}
|
||||
},
|
||||
@ -52,20 +78,76 @@ let gDropTargetShim = {
|
||||
* Handles the 'dragstart' event.
|
||||
* @param aEvent The 'dragstart' event.
|
||||
*/
|
||||
_start: function DropTargetShim_start(aEvent) {
|
||||
_dragstart: function (aEvent) {
|
||||
if (aEvent.target.classList.contains("newtab-link")) {
|
||||
gGrid.lock();
|
||||
|
||||
// XXX bug 505521 - Listen for dragover on the document.
|
||||
document.documentElement.addEventListener("dragover", this, false);
|
||||
this._addEventListeners();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'drag' event and determines the current drop target.
|
||||
* @param aEvent The 'drag' event.
|
||||
* Handles the 'dragover' event.
|
||||
* @param aEvent The 'dragover' event.
|
||||
*/
|
||||
_drag: function DropTargetShim_drag(aEvent) {
|
||||
_dragover: function (aEvent) {
|
||||
// XXX bug 505521 - Use the dragover event to retrieve the
|
||||
// current mouse coordinates while dragging.
|
||||
let sourceNode = aEvent.dataTransfer.mozSourceNode.parentNode;
|
||||
gDrag.drag(sourceNode._newtabSite, aEvent);
|
||||
|
||||
// Find the current drop target, if there's one.
|
||||
this._updateDropTarget(aEvent);
|
||||
|
||||
// If we have a valid drop target,
|
||||
// let the drag-and-drop service know.
|
||||
if (this._lastDropTarget) {
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'drop' event.
|
||||
* @param aEvent The 'drop' event.
|
||||
*/
|
||||
_drop: function (aEvent) {
|
||||
// We're accepting all drops.
|
||||
aEvent.preventDefault();
|
||||
|
||||
// Make sure to determine the current drop target
|
||||
// in case the dragover event hasn't been fired.
|
||||
this._updateDropTarget(aEvent);
|
||||
|
||||
// A site was successfully dropped.
|
||||
this._dispatchEvent(aEvent, "drop", this._lastDropTarget);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'dragend' event.
|
||||
* @param aEvent The 'dragend' event.
|
||||
*/
|
||||
_dragend: function (aEvent) {
|
||||
if (this._lastDropTarget) {
|
||||
if (aEvent.dataTransfer.mozUserCancelled) {
|
||||
// The drag operation was cancelled.
|
||||
this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget);
|
||||
this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget);
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
this._lastDropTarget = null;
|
||||
this._cellPositions = null;
|
||||
}
|
||||
|
||||
gGrid.unlock();
|
||||
this._removeEventListeners();
|
||||
},
|
||||
|
||||
/**
|
||||
* Tries to find the current drop target and will fire
|
||||
* appropriate dragenter, dragexit, and dragleave events.
|
||||
* @param aEvent The current drag event.
|
||||
*/
|
||||
_updateDropTarget: function (aEvent) {
|
||||
// Let's see if we find a drop target.
|
||||
let target = this._findDropTarget(aEvent);
|
||||
|
||||
@ -86,54 +168,12 @@ let gDropTargetShim = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'dragover' event as long as bug 505521 isn't fixed to get
|
||||
* current mouse cursor coordinates while dragging.
|
||||
* @param aEvent The 'dragover' event.
|
||||
*/
|
||||
_dragover: function DropTargetShim_dragover(aEvent) {
|
||||
let sourceNode = aEvent.dataTransfer.mozSourceNode.parentNode;
|
||||
gDrag.drag(sourceNode._newtabSite, aEvent);
|
||||
|
||||
this._drag(aEvent);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles the 'dragend' event.
|
||||
* @param aEvent The 'dragend' event.
|
||||
*/
|
||||
_end: function DropTargetShim_end(aEvent) {
|
||||
// Make sure to determine the current drop target in case the dragenter
|
||||
// event hasn't been fired.
|
||||
this._drag(aEvent);
|
||||
|
||||
if (this._lastDropTarget) {
|
||||
if (aEvent.dataTransfer.mozUserCancelled) {
|
||||
// The drag operation was cancelled.
|
||||
this._dispatchEvent(aEvent, "dragexit", this._lastDropTarget);
|
||||
this._dispatchEvent(aEvent, "dragleave", this._lastDropTarget);
|
||||
} else {
|
||||
// A site was successfully dropped.
|
||||
this._dispatchEvent(aEvent, "drop", this._lastDropTarget);
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
this._lastDropTarget = null;
|
||||
this._cellPositions = null;
|
||||
}
|
||||
|
||||
gGrid.unlock();
|
||||
|
||||
// XXX bug 505521 - Remove the document's dragover listener.
|
||||
document.documentElement.removeEventListener("dragover", this, false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines the current drop target by matching the dragged site's position
|
||||
* against all cells in the grid.
|
||||
* @return The currently hovered drop target or null.
|
||||
*/
|
||||
_findDropTarget: function DropTargetShim_findDropTarget() {
|
||||
_findDropTarget: function () {
|
||||
// These are the minimum intersection values - we want to use the cell if
|
||||
// the site is >= 50% hovering its position.
|
||||
let minWidth = gDrag.cellWidth / 2;
|
||||
@ -174,13 +214,12 @@ let gDropTargetShim = {
|
||||
* @param aType The event type.
|
||||
* @param aTarget The target node that receives the event.
|
||||
*/
|
||||
_dispatchEvent:
|
||||
function DropTargetShim_dispatchEvent(aEvent, aType, aTarget) {
|
||||
|
||||
_dispatchEvent: function (aEvent, aType, aTarget) {
|
||||
let node = aTarget.node;
|
||||
let event = document.createEvent("DragEvents");
|
||||
|
||||
event.initDragEvent(aType, true, true, window, 0, 0, 0, 0, 0, false, false,
|
||||
// The event should not bubble to prevent recursion.
|
||||
event.initDragEvent(aType, false, true, window, 0, 0, 0, 0, 0, false, false,
|
||||
false, false, 0, node, aEvent.dataTransfer);
|
||||
|
||||
node.dispatchEvent(event);
|
||||
|
@ -195,9 +195,6 @@ Site.prototype = {
|
||||
case "dragstart":
|
||||
gDrag.start(this, aEvent);
|
||||
break;
|
||||
case "drag":
|
||||
gDrag.drag(this, aEvent);
|
||||
break;
|
||||
case "dragend":
|
||||
gDrag.end(this, aEvent);
|
||||
break;
|
||||
|
@ -156,7 +156,7 @@ let gTransformation = {
|
||||
finish();
|
||||
} else {
|
||||
this.setSitePosition(aSite, targetPosition);
|
||||
this._whenTransitionEnded(aSite.node, finish);
|
||||
this._whenTransitionEnded(aSite.node, ["left", "top"], finish);
|
||||
}
|
||||
},
|
||||
|
||||
@ -202,15 +202,19 @@ let gTransformation = {
|
||||
* Listens for the 'transitionend' event on a given node and calls the given
|
||||
* callback.
|
||||
* @param aNode The node that is transitioned.
|
||||
* @param aProperties The properties we'll wait to be transitioned.
|
||||
* @param aCallback The callback to call when finished.
|
||||
*/
|
||||
_whenTransitionEnded:
|
||||
function Transformation_whenTransitionEnded(aNode, aCallback) {
|
||||
function Transformation_whenTransitionEnded(aNode, aProperties, aCallback) {
|
||||
|
||||
aNode.addEventListener("transitionend", function onEnd() {
|
||||
aNode.removeEventListener("transitionend", onEnd, false);
|
||||
aCallback();
|
||||
}, false);
|
||||
let props = new Set(aProperties);
|
||||
aNode.addEventListener("transitionend", function onEnd(e) {
|
||||
if (props.has(e.propertyName)) {
|
||||
aNode.removeEventListener("transitionend", onEnd);
|
||||
aCallback();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
@ -236,8 +240,9 @@ let gTransformation = {
|
||||
if (aCallback)
|
||||
aCallback();
|
||||
} else {
|
||||
if (aCallback)
|
||||
this._whenTransitionEnded(aNode, aCallback);
|
||||
if (aCallback) {
|
||||
this._whenTransitionEnded(aNode, ["opacity"], aCallback);
|
||||
}
|
||||
|
||||
aNode.style.opacity = aOpacity;
|
||||
}
|
||||
|
@ -566,19 +566,6 @@ let SessionStoreInternal = {
|
||||
});
|
||||
},
|
||||
|
||||
_initWindow: function ssi_initWindow(aWindow) {
|
||||
if (aWindow) {
|
||||
this.onLoad(aWindow);
|
||||
} else if (this._loadState == STATE_STOPPED) {
|
||||
// If init is being called with a null window, it's possible that we
|
||||
// just want to tell sessionstore that a session is live (as is the case
|
||||
// with starting Firefox with -private, for example; see bug 568816),
|
||||
// so we should mark the load state as running to make sure that
|
||||
// things like setBrowserState calls will succeed in restoring the session.
|
||||
this._loadState = STATE_RUNNING;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Start tracking a window.
|
||||
*
|
||||
@ -586,11 +573,15 @@ let SessionStoreInternal = {
|
||||
* initialized yet.
|
||||
*/
|
||||
init: function ssi_init(aWindow) {
|
||||
if (!aWindow) {
|
||||
throw new Error("init() must be called with a valid window.");
|
||||
}
|
||||
|
||||
let self = this;
|
||||
this.initService();
|
||||
this._promiseInitialization.promise.then(
|
||||
function onSuccess() {
|
||||
self._initWindow(aWindow);
|
||||
self.onLoad(aWindow);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
@ -423,6 +423,7 @@ function StackFrames() {
|
||||
this._onResumed = this._onResumed.bind(this);
|
||||
this._onFrames = this._onFrames.bind(this);
|
||||
this._onFramesCleared = this._onFramesCleared.bind(this);
|
||||
this._onBlackBoxChange = this._onBlackBoxChange.bind(this);
|
||||
this._afterFramesCleared = this._afterFramesCleared.bind(this);
|
||||
this.evaluate = this.evaluate.bind(this);
|
||||
}
|
||||
@ -447,6 +448,7 @@ StackFrames.prototype = {
|
||||
this.activeThread.addListener("resumed", this._onResumed);
|
||||
this.activeThread.addListener("framesadded", this._onFrames);
|
||||
this.activeThread.addListener("framescleared", this._onFramesCleared);
|
||||
window.addEventListener("Debugger:BlackBoxChange", this._onBlackBoxChange, false);
|
||||
this._handleTabNavigation();
|
||||
},
|
||||
|
||||
@ -462,6 +464,7 @@ StackFrames.prototype = {
|
||||
this.activeThread.removeListener("resumed", this._onResumed);
|
||||
this.activeThread.removeListener("framesadded", this._onFrames);
|
||||
this.activeThread.removeListener("framescleared", this._onFramesCleared);
|
||||
window.removeEventListener("Debugger:BlackBoxChange", this._onBlackBoxChange, false);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -594,12 +597,22 @@ StackFrames.prototype = {
|
||||
// Make sure all the previous stackframes are removed before re-adding them.
|
||||
DebuggerView.StackFrames.empty();
|
||||
|
||||
let previousBlackBoxed = null;
|
||||
for (let frame of this.activeThread.cachedFrames) {
|
||||
let { depth, where: { url, line } } = frame;
|
||||
let { depth, where: { url, line }, isBlackBoxed } = frame;
|
||||
let frameLocation = NetworkHelper.convertToUnicode(unescape(url));
|
||||
let frameTitle = StackFrameUtils.getFrameTitle(frame);
|
||||
|
||||
DebuggerView.StackFrames.addFrame(frameTitle, frameLocation, line, depth);
|
||||
if (isBlackBoxed) {
|
||||
if (previousBlackBoxed == url) {
|
||||
continue;
|
||||
}
|
||||
previousBlackBoxed = url;
|
||||
} else {
|
||||
previousBlackBoxed = null;
|
||||
}
|
||||
|
||||
DebuggerView.StackFrames.addFrame(frameTitle, frameLocation, line, depth, isBlackBoxed);
|
||||
}
|
||||
if (this.currentFrame == null) {
|
||||
DebuggerView.StackFrames.selectedDepth = 0;
|
||||
@ -626,6 +639,18 @@ StackFrames.prototype = {
|
||||
window.setTimeout(this._afterFramesCleared, FRAME_STEP_CLEAR_DELAY);
|
||||
},
|
||||
|
||||
/**
|
||||
* Handler for the debugger's BlackBoxChange notification.
|
||||
*/
|
||||
_onBlackBoxChange: function() {
|
||||
if (this.activeThread.state == "paused") {
|
||||
// We have to clear out the existing frames and refetch them to get their
|
||||
// updated black boxed status.
|
||||
this.activeThread._clearFrames();
|
||||
this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Called soon after the thread client's framescleared notification.
|
||||
*/
|
||||
@ -1000,6 +1025,27 @@ SourceScripts.prototype = {
|
||||
window.dispatchEvent(document, "Debugger:AfterSourcesAdded");
|
||||
},
|
||||
|
||||
/**
|
||||
* Set the black boxed status of the given source.
|
||||
*
|
||||
* @param Object aSource
|
||||
* The source form.
|
||||
* @param bool aBlackBoxFlag
|
||||
* True to black box the source, false to un-black box it.
|
||||
*/
|
||||
blackBox: function(aSource, aBlackBoxFlag) {
|
||||
const sourceClient = this.activeThread.source(aSource);
|
||||
sourceClient[aBlackBoxFlag ? "blackBox" : "unblackBox"](function({ error, message }) {
|
||||
if (error) {
|
||||
let msg = "Could not toggle black boxing for "
|
||||
+ aSource.url + ": " + message;
|
||||
dumpn(msg);
|
||||
return void Cu.reportError(msg);
|
||||
}
|
||||
window.dispatchEvent(document, "Debugger:BlackBoxChange", sourceClient);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets a specified source's text.
|
||||
*
|
||||
|
@ -18,6 +18,7 @@ function SourcesView() {
|
||||
this._onSourceSelect = this._onSourceSelect.bind(this);
|
||||
this._onSourceClick = this._onSourceClick.bind(this);
|
||||
this._onBreakpointRemoved = this._onBreakpointRemoved.bind(this);
|
||||
this._onSourceCheck = this._onSourceCheck.bind(this);
|
||||
this._onBreakpointClick = this._onBreakpointClick.bind(this);
|
||||
this._onBreakpointCheckboxClick = this._onBreakpointCheckboxClick.bind(this);
|
||||
this._onConditionalPopupShowing = this._onConditionalPopupShowing.bind(this);
|
||||
@ -34,9 +35,12 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
initialize: function() {
|
||||
dumpn("Initializing the SourcesView");
|
||||
|
||||
this.widget = new SideMenuWidget(document.getElementById("sources"));
|
||||
this.widget = new SideMenuWidget(document.getElementById("sources"), {
|
||||
showCheckboxes: true
|
||||
});
|
||||
this.emptyText = L10N.getStr("noSourcesText");
|
||||
this.unavailableText = L10N.getStr("noMatchingSourcesText");
|
||||
this._blackBoxCheckboxTooltip = L10N.getStr("blackBoxCheckboxTooltip");
|
||||
|
||||
this._commandset = document.getElementById("debuggerCommands");
|
||||
this._popupset = document.getElementById("debuggerPopupset");
|
||||
@ -48,6 +52,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
window.addEventListener("Debugger:EditorUnloaded", this._onEditorUnload, false);
|
||||
this.widget.addEventListener("select", this._onSourceSelect, false);
|
||||
this.widget.addEventListener("click", this._onSourceClick, false);
|
||||
this.widget.addEventListener("check", this._onSourceCheck, false);
|
||||
this._cbPanel.addEventListener("popupshowing", this._onConditionalPopupShowing, false);
|
||||
this._cbPanel.addEventListener("popupshown", this._onConditionalPopupShown, false);
|
||||
this._cbPanel.addEventListener("popuphiding", this._onConditionalPopupHiding, false);
|
||||
@ -70,6 +75,7 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
window.removeEventListener("Debugger:EditorUnloaded", this._onEditorUnload, false);
|
||||
this.widget.removeEventListener("select", this._onSourceSelect, false);
|
||||
this.widget.removeEventListener("click", this._onSourceClick, false);
|
||||
this.widget.removeEventListener("check", this._onSourceCheck, false);
|
||||
this._cbPanel.removeEventListener("popupshowing", this._onConditionalPopupShowing, false);
|
||||
this._cbPanel.removeEventListener("popupshowing", this._onConditionalPopupShown, false);
|
||||
this._cbPanel.removeEventListener("popuphiding", this._onConditionalPopupHiding, false);
|
||||
@ -109,6 +115,8 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
this.push([label, url, group], {
|
||||
staged: aOptions.staged, /* stage the item to be appended later? */
|
||||
attachment: {
|
||||
checkboxState: !aSource.isBlackBoxed,
|
||||
checkboxTooltip: this._blackBoxCheckboxTooltip,
|
||||
source: aSource
|
||||
}
|
||||
});
|
||||
@ -639,6 +647,14 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
DebuggerView.Filtering.target = this;
|
||||
},
|
||||
|
||||
/**
|
||||
* The check listener for the sources container.
|
||||
*/
|
||||
_onSourceCheck: function({ detail: { checked }, target }) {
|
||||
let item = this.getItemForElement(target);
|
||||
DebuggerController.SourceScripts.blackBox(item.attachment.source, !checked);
|
||||
},
|
||||
|
||||
/**
|
||||
* The click listener for a breakpoint container.
|
||||
*/
|
||||
|
@ -424,8 +424,10 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* The line number to be displayed in the list.
|
||||
* @param number aDepth
|
||||
* The frame depth specified by the debugger.
|
||||
* @param boolean aIsBlackBoxed
|
||||
* Whether or not the frame is black boxed.
|
||||
*/
|
||||
addFrame: function(aFrameTitle, aSourceLocation, aLineNumber, aDepth) {
|
||||
addFrame: function(aFrameTitle, aSourceLocation, aLineNumber, aDepth, aIsBlackBoxed) {
|
||||
// Create the element node and menu entry for the stack frame item.
|
||||
let frameView = this._createFrameView.apply(this, arguments);
|
||||
let menuEntry = this._createMenuEntry.apply(this, arguments);
|
||||
@ -471,29 +473,35 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* The line number to be displayed in the list.
|
||||
* @param number aDepth
|
||||
* The frame depth specified by the debugger.
|
||||
* @param boolean aIsBlackBoxed
|
||||
* Whether or not the frame is black boxed.
|
||||
* @return nsIDOMNode
|
||||
* The stack frame view.
|
||||
*/
|
||||
_createFrameView: function(aFrameTitle, aSourceLocation, aLineNumber, aDepth) {
|
||||
let frameDetails =
|
||||
SourceUtils.trimUrlLength(
|
||||
SourceUtils.getSourceLabel(aSourceLocation),
|
||||
STACK_FRAMES_SOURCE_URL_MAX_LENGTH,
|
||||
STACK_FRAMES_SOURCE_URL_TRIM_SECTION) + SEARCH_LINE_FLAG + aLineNumber;
|
||||
|
||||
let frameTitleNode = document.createElement("label");
|
||||
frameTitleNode.className = "plain dbg-stackframe-title breadcrumbs-widget-item-tag";
|
||||
frameTitleNode.setAttribute("value", aFrameTitle);
|
||||
|
||||
let frameDetailsNode = document.createElement("label");
|
||||
frameDetailsNode.className = "plain dbg-stackframe-details breadcrumbs-widget-item-id";
|
||||
frameDetailsNode.setAttribute("value", frameDetails);
|
||||
|
||||
_createFrameView: function(aFrameTitle, aSourceLocation, aLineNumber, aDepth, aIsBlackBoxed) {
|
||||
let container = document.createElement("hbox");
|
||||
container.id = "stackframe-" + aDepth;
|
||||
container.className = "dbg-stackframe";
|
||||
|
||||
container.appendChild(frameTitleNode);
|
||||
let frameDetails = SourceUtils.trimUrlLength(
|
||||
SourceUtils.getSourceLabel(aSourceLocation),
|
||||
STACK_FRAMES_SOURCE_URL_MAX_LENGTH,
|
||||
STACK_FRAMES_SOURCE_URL_TRIM_SECTION);
|
||||
|
||||
if (aIsBlackBoxed) {
|
||||
container.classList.add("dbg-stackframe-black-boxed");
|
||||
} else {
|
||||
let frameTitleNode = document.createElement("label");
|
||||
frameTitleNode.className = "plain dbg-stackframe-title breadcrumbs-widget-item-tag";
|
||||
frameTitleNode.setAttribute("value", aFrameTitle);
|
||||
container.appendChild(frameTitleNode);
|
||||
|
||||
frameDetails += SEARCH_LINE_FLAG + aLineNumber;
|
||||
}
|
||||
|
||||
let frameDetailsNode = document.createElement("label");
|
||||
frameDetailsNode.className = "plain dbg-stackframe-details breadcrumbs-widget-item-id";
|
||||
frameDetailsNode.setAttribute("value", frameDetails);
|
||||
container.appendChild(frameDetailsNode);
|
||||
|
||||
return container;
|
||||
@ -510,10 +518,12 @@ StackFramesView.prototype = Heritage.extend(WidgetMethods, {
|
||||
* The line number to be displayed in the list.
|
||||
* @param number aDepth
|
||||
* The frame depth specified by the debugger.
|
||||
* @param boolean aIsBlackBoxed
|
||||
* Whether or not the frame is black boxed.
|
||||
* @return object
|
||||
* An object containing the stack frame command and menu item.
|
||||
*/
|
||||
_createMenuEntry: function(aFrameTitle, aSourceLocation, aLineNumber, aDepth) {
|
||||
_createMenuEntry: function(aFrameTitle, aSourceLocation, aLineNumber, aDepth, aIsBlackBoxed) {
|
||||
let frameDescription =
|
||||
SourceUtils.trimUrlLength(
|
||||
SourceUtils.getSourceLabel(aSourceLocation),
|
||||
|
@ -12,6 +12,10 @@ include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
MOCHITEST_BROWSER_TESTS = \
|
||||
browser_dbg_aaa_run_first_leaktest.js \
|
||||
browser_dbg_blackboxing-01.js \
|
||||
browser_dbg_blackboxing-02.js \
|
||||
browser_dbg_blackboxing-03.js \
|
||||
browser_dbg_blackboxing-04.js \
|
||||
browser_dbg_clean-exit.js \
|
||||
browser_dbg_cmd.js \
|
||||
browser_dbg_cmd_break.js \
|
||||
@ -111,6 +115,11 @@ MOCHITEST_BROWSER_TESTS = \
|
||||
$(NULL)
|
||||
|
||||
MOCHITEST_BROWSER_PAGES = \
|
||||
browser_dbg_blackboxing.html \
|
||||
blackboxing_blackboxme.js \
|
||||
blackboxing_one.js \
|
||||
blackboxing_two.js \
|
||||
blackboxing_three.js \
|
||||
browser_dbg_cmd_break.html \
|
||||
browser_dbg_cmd.html \
|
||||
testactors.js \
|
||||
|
9
browser/devtools/debugger/test/blackboxing_blackboxme.js
Normal file
9
browser/devtools/debugger/test/blackboxing_blackboxme.js
Normal file
@ -0,0 +1,9 @@
|
||||
function blackboxme(fn) {
|
||||
(function one() {
|
||||
(function two() {
|
||||
(function three() {
|
||||
fn();
|
||||
}());
|
||||
}());
|
||||
}());
|
||||
}
|
1
browser/devtools/debugger/test/blackboxing_one.js
Normal file
1
browser/devtools/debugger/test/blackboxing_one.js
Normal file
@ -0,0 +1 @@
|
||||
function one() { two(); }
|
1
browser/devtools/debugger/test/blackboxing_three.js
Normal file
1
browser/devtools/debugger/test/blackboxing_three.js
Normal file
@ -0,0 +1 @@
|
||||
function three() { doDebuggerStatement(); }
|
1
browser/devtools/debugger/test/blackboxing_two.js
Normal file
1
browser/devtools/debugger/test/blackboxing_two.js
Normal file
@ -0,0 +1 @@
|
||||
function two() { three(); }
|
76
browser/devtools/debugger/test/browser_dbg_blackboxing-01.js
Normal file
76
browser/devtools/debugger/test/browser_dbg_blackboxing-01.js
Normal file
@ -0,0 +1,76 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that if we black box a source and then refresh, it is still black boxed.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "binary_search.html";
|
||||
|
||||
var gPane = null;
|
||||
var gTab = null;
|
||||
var gDebuggee = null;
|
||||
var gDebugger = null;
|
||||
|
||||
function test()
|
||||
{
|
||||
let scriptShown = false;
|
||||
let framesAdded = false;
|
||||
let resumed = false;
|
||||
let testStarted = false;
|
||||
|
||||
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
|
||||
resumed = true;
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.panelWin;
|
||||
|
||||
testBlackBoxSource();
|
||||
});
|
||||
}
|
||||
|
||||
function testBlackBoxSource() {
|
||||
once(gDebugger, "Debugger:SourceShown", function () {
|
||||
const checkbox = gDebugger.document.querySelector(".side-menu-widget-item-checkbox");
|
||||
ok(checkbox, "Should get the checkbox for black boxing the source");
|
||||
ok(checkbox.checked, "Should not be black boxed by default");
|
||||
|
||||
once(gDebugger, "Debugger:BlackBoxChange", function (event) {
|
||||
const sourceClient = event.detail;
|
||||
ok(sourceClient.isBlackBoxed, "The source should be black boxed now");
|
||||
ok(!checkbox.checked, "The checkbox should no longer be checked.");
|
||||
|
||||
testBlackBoxReload();
|
||||
});
|
||||
|
||||
checkbox.click();
|
||||
});
|
||||
}
|
||||
|
||||
function testBlackBoxReload() {
|
||||
once(gDebugger, "Debugger:SourceShown", function () {
|
||||
const checkbox = gDebugger.document.querySelector(".side-menu-widget-item-checkbox");
|
||||
ok(checkbox, "Should get the checkbox for black boxing the source");
|
||||
ok(!checkbox.checked, "Should still be black boxed");
|
||||
|
||||
closeDebuggerAndFinish();
|
||||
});
|
||||
|
||||
gDebuggee.location.reload();
|
||||
}
|
||||
|
||||
function once(target, event, callback) {
|
||||
target.addEventListener(event, function _listener(...args) {
|
||||
target.removeEventListener(event, _listener, false);
|
||||
callback.apply(null, args);
|
||||
}, false);
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
removeTab(gTab);
|
||||
gPane = null;
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gDebugger = null;
|
||||
});
|
87
browser/devtools/debugger/test/browser_dbg_blackboxing-02.js
Normal file
87
browser/devtools/debugger/test/browser_dbg_blackboxing-02.js
Normal file
@ -0,0 +1,87 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that black boxed frames are compressed into a single frame on the stack
|
||||
* view.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "browser_dbg_blackboxing.html";
|
||||
const BLACKBOXME_URL = EXAMPLE_URL + "blackboxing_blackboxme.js"
|
||||
|
||||
var gPane = null;
|
||||
var gTab = null;
|
||||
var gDebuggee = null;
|
||||
var gDebugger = null;
|
||||
|
||||
function test()
|
||||
{
|
||||
let scriptShown = false;
|
||||
let framesAdded = false;
|
||||
let resumed = false;
|
||||
let testStarted = false;
|
||||
|
||||
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
|
||||
resumed = true;
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.panelWin;
|
||||
|
||||
testBlackBoxSource();
|
||||
});
|
||||
}
|
||||
|
||||
function testBlackBoxSource() {
|
||||
once(gDebugger, "Debugger:SourceShown", function () {
|
||||
const checkbox = getBlackBoxCheckbox(BLACKBOXME_URL);
|
||||
ok(checkbox, "Should get the checkbox for blackBoxing the source");
|
||||
|
||||
once(gDebugger, "Debugger:BlackBoxChange", function (event) {
|
||||
const sourceClient = event.detail;
|
||||
ok(sourceClient.isBlackBoxed, "The source should be black boxed now");
|
||||
|
||||
testBlackBoxStack();
|
||||
});
|
||||
|
||||
checkbox.click();
|
||||
});
|
||||
}
|
||||
|
||||
function testBlackBoxStack() {
|
||||
const { activeThread } = gDebugger.DebuggerController;
|
||||
activeThread.addOneTimeListener("framesadded", function () {
|
||||
const frames = gDebugger.DebuggerView.StackFrames.widget._list;
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
|
||||
"Should only get 3 frames");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe-black-boxed").length, 1,
|
||||
"And one of them should be the combined black boxed frames");
|
||||
|
||||
closeDebuggerAndFinish();
|
||||
});
|
||||
|
||||
gDebuggee.runTest();
|
||||
}
|
||||
|
||||
function getBlackBoxCheckbox(url) {
|
||||
return gDebugger.document.querySelector(
|
||||
".side-menu-widget-item[tooltiptext=\""
|
||||
+ url + "\"] .side-menu-widget-item-checkbox");
|
||||
}
|
||||
|
||||
function once(target, event, callback) {
|
||||
target.addEventListener(event, function _listener(...args) {
|
||||
target.removeEventListener(event, _listener, false);
|
||||
callback.apply(null, args);
|
||||
}, false);
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
removeTab(gTab);
|
||||
gPane = null;
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gDebugger = null;
|
||||
});
|
96
browser/devtools/debugger/test/browser_dbg_blackboxing-03.js
Normal file
96
browser/devtools/debugger/test/browser_dbg_blackboxing-03.js
Normal file
@ -0,0 +1,96 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that black boxed frames are compressed into a single frame on the stack
|
||||
* view when we are already paused.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "browser_dbg_blackboxing.html";
|
||||
const BLACKBOXME_URL = EXAMPLE_URL + "blackboxing_blackboxme.js"
|
||||
|
||||
var gPane = null;
|
||||
var gTab = null;
|
||||
var gDebuggee = null;
|
||||
var gDebugger = null;
|
||||
|
||||
function test()
|
||||
{
|
||||
let scriptShown = false;
|
||||
let framesAdded = false;
|
||||
let resumed = false;
|
||||
let testStarted = false;
|
||||
|
||||
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
|
||||
resumed = true;
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.panelWin;
|
||||
|
||||
once(gDebugger, "Debugger:SourceShown", function () {
|
||||
testBlackBoxStack();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function testBlackBoxStack() {
|
||||
const { activeThread } = gDebugger.DebuggerController;
|
||||
activeThread.addOneTimeListener("framesadded", function () {
|
||||
const frames = gDebugger.DebuggerView.StackFrames.widget._list;
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 6,
|
||||
"Should get 6 frames");
|
||||
|
||||
is(frames.querySelectorAll(".dbg-stackframe-black-boxed").length, 0,
|
||||
"And none of them are black boxed");
|
||||
|
||||
testBlackBoxSource();
|
||||
});
|
||||
|
||||
gDebuggee.runTest();
|
||||
}
|
||||
|
||||
function testBlackBoxSource() {
|
||||
const checkbox = getBlackBoxCheckbox(BLACKBOXME_URL);
|
||||
ok(checkbox, "Should get the checkbox for black boxing the source");
|
||||
|
||||
once(gDebugger, "Debugger:BlackBoxChange", function (event) {
|
||||
const { activeThread } = gDebugger.DebuggerController;
|
||||
activeThread.addOneTimeListener("framesadded", function () {
|
||||
const sourceClient = event.detail;
|
||||
ok(sourceClient.isBlackBoxed, "The source should be black boxed now");
|
||||
|
||||
const frames = gDebugger.DebuggerView.StackFrames.widget._list;
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 3,
|
||||
"Should only get 3 frames");
|
||||
is(frames.querySelectorAll(".dbg-stackframe-black-boxed").length, 1,
|
||||
"And one of them is the combined black boxed frames");
|
||||
|
||||
closeDebuggerAndFinish();
|
||||
});
|
||||
});
|
||||
|
||||
checkbox.click();
|
||||
}
|
||||
|
||||
function getBlackBoxCheckbox(url) {
|
||||
return gDebugger.document.querySelector(
|
||||
".side-menu-widget-item[tooltiptext=\""
|
||||
+ url + "\"] .side-menu-widget-item-checkbox");
|
||||
}
|
||||
|
||||
function once(target, event, callback) {
|
||||
target.addEventListener(event, function _listener(...args) {
|
||||
target.removeEventListener(event, _listener, false);
|
||||
callback.apply(null, args);
|
||||
}, false);
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
removeTab(gTab);
|
||||
gPane = null;
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gDebugger = null;
|
||||
});
|
84
browser/devtools/debugger/test/browser_dbg_blackboxing-04.js
Normal file
84
browser/devtools/debugger/test/browser_dbg_blackboxing-04.js
Normal file
@ -0,0 +1,84 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Test that we get a stack frame for each black boxed source, not a single one
|
||||
* for all of them.
|
||||
*/
|
||||
|
||||
const TAB_URL = EXAMPLE_URL + "browser_dbg_blackboxing.html";
|
||||
|
||||
var gPane = null;
|
||||
var gTab = null;
|
||||
var gDebuggee = null;
|
||||
var gDebugger = null;
|
||||
|
||||
function test()
|
||||
{
|
||||
let scriptShown = false;
|
||||
let framesAdded = false;
|
||||
let resumed = false;
|
||||
let testStarted = false;
|
||||
|
||||
debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
|
||||
resumed = true;
|
||||
gTab = aTab;
|
||||
gDebuggee = aDebuggee;
|
||||
gPane = aPane;
|
||||
gDebugger = gPane.panelWin;
|
||||
|
||||
once(gDebugger, "Debugger:SourceShown", function () {
|
||||
blackBoxSources();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function blackBoxSources() {
|
||||
let timesFired = 0;
|
||||
gDebugger.addEventListener("Debugger:BlackBoxChange", function _onBlackboxChange() {
|
||||
if (++timesFired !== 3) {
|
||||
return;
|
||||
}
|
||||
gDebugger.removeEventListener("Debugger:BlackBoxChange", _onBlackboxChange, false);
|
||||
|
||||
const { activeThread } = gDebugger.DebuggerController;
|
||||
activeThread.addOneTimeListener("framesadded", testStackFrames);
|
||||
|
||||
gDebuggee.one();
|
||||
}, false);
|
||||
|
||||
getBlackBoxCheckbox(EXAMPLE_URL + "blackboxing_one.js").click();
|
||||
getBlackBoxCheckbox(EXAMPLE_URL + "blackboxing_two.js").click();
|
||||
getBlackBoxCheckbox(EXAMPLE_URL + "blackboxing_three.js").click();
|
||||
}
|
||||
|
||||
function testStackFrames() {
|
||||
const frames = gDebugger.DebuggerView.StackFrames.widget._list;
|
||||
is(frames.querySelectorAll(".dbg-stackframe").length, 4,
|
||||
"Should get 4 frames (one -> two -> three -> doDebuggerStatement)");
|
||||
is(frames.querySelectorAll(".dbg-stackframe-black-boxed").length, 3,
|
||||
"And one, two, and three should each have their own black boxed frame.");
|
||||
|
||||
closeDebuggerAndFinish();
|
||||
}
|
||||
|
||||
function getBlackBoxCheckbox(url) {
|
||||
return gDebugger.document.querySelector(
|
||||
".side-menu-widget-item[tooltiptext=\""
|
||||
+ url + "\"] .side-menu-widget-item-checkbox");
|
||||
}
|
||||
|
||||
function once(target, event, callback) {
|
||||
target.addEventListener(event, function _listener(...args) {
|
||||
target.removeEventListener(event, _listener, false);
|
||||
callback.apply(null, args);
|
||||
}, false);
|
||||
}
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
removeTab(gTab);
|
||||
gPane = null;
|
||||
gTab = null;
|
||||
gDebuggee = null;
|
||||
gDebugger = null;
|
||||
});
|
23
browser/devtools/debugger/test/browser_dbg_blackboxing.html
Normal file
23
browser/devtools/debugger/test/browser_dbg_blackboxing.html
Normal file
@ -0,0 +1,23 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset='utf-8'/>
|
||||
<title>Browser Debugger Blackbox Test</title>
|
||||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<script type="text/javascript" src="blackboxing_blackboxme.js"></script>
|
||||
<script type="text/javascript" src="blackboxing_one.js"></script>
|
||||
<script type="text/javascript" src="blackboxing_two.js"></script>
|
||||
<script type="text/javascript" src="blackboxing_three.js"></script>
|
||||
<script>
|
||||
function runTest() {
|
||||
blackboxme(doDebuggerStatement);
|
||||
}
|
||||
function doDebuggerStatement() {
|
||||
debugger;
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
@ -484,11 +484,13 @@ HTMLBreadcrumbs.prototype = {
|
||||
if (originalLength > 0) {
|
||||
stopNode = this.nodeHierarchy[originalLength - 1].node;
|
||||
}
|
||||
while (toAppend && toAppend.tagName && toAppend != stopNode) {
|
||||
let button = this.buildButton(toAppend);
|
||||
fragment.insertBefore(button, lastButtonInserted);
|
||||
lastButtonInserted = button;
|
||||
this.nodeHierarchy.splice(originalLength, 0, {node: toAppend, button: button});
|
||||
while (toAppend && toAppend != stopNode) {
|
||||
if (toAppend.tagName) {
|
||||
let button = this.buildButton(toAppend);
|
||||
fragment.insertBefore(button, lastButtonInserted);
|
||||
lastButtonInserted = button;
|
||||
this.nodeHierarchy.splice(originalLength, 0, {node: toAppend, button: button});
|
||||
}
|
||||
toAppend = toAppend.parentNode();
|
||||
}
|
||||
this.container.appendChild(fragment, this.container.firstChild);
|
||||
|
@ -156,14 +156,17 @@ InspectorPanel.prototype = {
|
||||
/**
|
||||
* Return a promise that will resolve to the default node for selection.
|
||||
*/
|
||||
_getDefaultNodeForSelection : function() {
|
||||
_getDefaultNodeForSelection: function() {
|
||||
if (this._defaultNode) {
|
||||
return this._defaultNode;
|
||||
}
|
||||
let walker = this.walker;
|
||||
|
||||
// if available set body node as default selected node
|
||||
// else set documentElement
|
||||
return walker.querySelector(this.walker.rootNode, "body").then(front => {
|
||||
return walker.getRootNode().then(rootNode => {
|
||||
return walker.querySelector(rootNode, "body");
|
||||
}).then(front => {
|
||||
if (front) {
|
||||
return front;
|
||||
}
|
||||
@ -285,33 +288,21 @@ InspectorPanel.prototype = {
|
||||
*/
|
||||
onNavigatedAway: function InspectorPanel_onNavigatedAway(event, payload) {
|
||||
let newWindow = payload._navPayload || payload;
|
||||
this.walker.release().then(null, console.error);
|
||||
this.walker = null;
|
||||
this._defaultNode = null;
|
||||
this.selection.setNodeFront(null);
|
||||
this.selection.setWalker(null);
|
||||
this._destroyMarkup();
|
||||
this.isDirty = false;
|
||||
|
||||
this.target.inspector.getWalker().then(walker => {
|
||||
this._getDefaultNodeForSelection().then(defaultNode => {
|
||||
if (this._destroyPromise) {
|
||||
walker.release().then(null, console.error);
|
||||
return;
|
||||
}
|
||||
this.selection.setNodeFront(defaultNode, "navigateaway");
|
||||
|
||||
this.walker = walker;
|
||||
this.selection.setWalker(walker);
|
||||
this._getDefaultNodeForSelection().then(defaultNode => {
|
||||
if (this._destroyPromise) {
|
||||
return;
|
||||
}
|
||||
this.selection.setNodeFront(defaultNode, "navigateaway");
|
||||
|
||||
this._initMarkup();
|
||||
this.once("markuploaded", () => {
|
||||
this.markup.expandNode(this.selection.nodeFront);
|
||||
this.setupSearchBox();
|
||||
});
|
||||
this._initMarkup();
|
||||
this.once("markuploaded", () => {
|
||||
this.markup.expandNode(this.selection.nodeFront);
|
||||
this.setupSearchBox();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
@ -74,7 +74,28 @@ function performTestComparisons2()
|
||||
is(i.selection.node, div2, "selection matches div2 node");
|
||||
is(getHighlitNode(), div2, "highlighter matches selection");
|
||||
|
||||
finish();
|
||||
selectRoot();
|
||||
}
|
||||
|
||||
function selectRoot()
|
||||
{
|
||||
// Select the root document element to clear the breadcrumbs.
|
||||
let i = getActiveInspector();
|
||||
i.selection.setNode(doc.documentElement);
|
||||
i.once("inspector-updated", selectIframe);
|
||||
}
|
||||
|
||||
function selectIframe()
|
||||
{
|
||||
// Directly select an element in an iframe (without navigating to it
|
||||
// with mousemoves).
|
||||
let i = getActiveInspector();
|
||||
i.selection.setNode(div2);
|
||||
i.once("inspector-updated", () => {
|
||||
let breadcrumbs = i.breadcrumbs;
|
||||
is(breadcrumbs.nodeHierarchy.length, 9, "Should have 9 items");
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
function test() {
|
||||
|
@ -14,9 +14,12 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
const Cu = Components.utils;
|
||||
let require = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
|
||||
|
||||
let { Cc, Ci, Cu } = require("chrome");
|
||||
let promise = require("sdk/core/promise");
|
||||
let Telemetry = require("devtools/shared/telemetry");
|
||||
let TargetFactory = require("devtools/framework/target").TargetFactory;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
@ -27,16 +30,30 @@ Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
|
||||
Cu.import("resource://gre/modules/jsdebugger.jsm");
|
||||
Cu.import("resource:///modules/devtools/gDevTools.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
|
||||
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
|
||||
"resource:///modules/devtools/VariablesView.jsm");
|
||||
"resource:///modules/devtools/VariablesView.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "devtools",
|
||||
"resource://gre/modules/devtools/Loader.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "VariablesViewController",
|
||||
"resource:///modules/devtools/VariablesViewController.jsm");
|
||||
|
||||
let Telemetry = devtools.require("devtools/shared/telemetry");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "GripClient",
|
||||
"resource://gre/modules/devtools/dbg-client.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebConsoleUtils",
|
||||
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
|
||||
"resource://gre/modules/devtools/dbg-server.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DebuggerClient",
|
||||
"resource://gre/modules/devtools/dbg-client.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "REMOTE_TIMEOUT", () =>
|
||||
Services.prefs.getIntPref("devtools.debugger.remote-timeout")
|
||||
);
|
||||
|
||||
const SCRATCHPAD_CONTEXT_CONTENT = 1;
|
||||
const SCRATCHPAD_CONTEXT_BROWSER = 2;
|
||||
const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
|
||||
@ -237,12 +254,6 @@ var Scratchpad = {
|
||||
*/
|
||||
get browserWindow() Services.wm.getMostRecentWindow("navigator:browser"),
|
||||
|
||||
/**
|
||||
* Reference to the last chrome window of type navigator:browser. We use this
|
||||
* to check if the chrome window changed since the last code evaluation.
|
||||
*/
|
||||
_previousWindow: null,
|
||||
|
||||
/**
|
||||
* Get the gBrowser object of the most recent browser window.
|
||||
*/
|
||||
@ -252,11 +263,6 @@ var Scratchpad = {
|
||||
return recentWin ? recentWin.gBrowser : null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cached Cu.Sandbox object for the active tab content window object.
|
||||
*/
|
||||
_contentSandbox: null,
|
||||
|
||||
/**
|
||||
* Unique name for the current Scratchpad instance. Used to distinguish
|
||||
* Scratchpad windows between each other. See bug 661762.
|
||||
@ -278,73 +284,6 @@ var Scratchpad = {
|
||||
return this._sidebar;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the Cu.Sandbox object for the active tab content window object. Note
|
||||
* that the returned object is cached for later reuse. The cached object is
|
||||
* kept only for the current location in the current tab of the current
|
||||
* browser window and it is reset for each context switch,
|
||||
* navigator:browser window switch, tab switch or navigation.
|
||||
*/
|
||||
get contentSandbox()
|
||||
{
|
||||
if (!this.browserWindow) {
|
||||
Cu.reportError(this.strings.
|
||||
GetStringFromName("browserWindow.unavailable"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._contentSandbox ||
|
||||
this.browserWindow != this._previousBrowserWindow ||
|
||||
this._previousBrowser != this.gBrowser.selectedBrowser ||
|
||||
this._previousLocation != this.gBrowser.contentWindow.location.href) {
|
||||
let contentWindow = this.gBrowser.selectedBrowser.contentWindow;
|
||||
this._contentSandbox = new Cu.Sandbox(contentWindow,
|
||||
{ sandboxPrototype: contentWindow, wantXrays: false,
|
||||
sandboxName: 'scratchpad-content'});
|
||||
this._contentSandbox.__SCRATCHPAD__ = this;
|
||||
|
||||
this._previousBrowserWindow = this.browserWindow;
|
||||
this._previousBrowser = this.gBrowser.selectedBrowser;
|
||||
this._previousLocation = contentWindow.location.href;
|
||||
}
|
||||
|
||||
return this._contentSandbox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Cached Cu.Sandbox object for the most recently active navigator:browser
|
||||
* chrome window object.
|
||||
*/
|
||||
_chromeSandbox: null,
|
||||
|
||||
/**
|
||||
* Get the Cu.Sandbox object for the most recently active navigator:browser
|
||||
* chrome window object. Note that the returned object is cached for later
|
||||
* reuse. The cached object is kept only for the current browser window and it
|
||||
* is reset for each context switch or navigator:browser window switch.
|
||||
*/
|
||||
get chromeSandbox()
|
||||
{
|
||||
if (!this.browserWindow) {
|
||||
Cu.reportError(this.strings.
|
||||
GetStringFromName("browserWindow.unavailable"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._chromeSandbox ||
|
||||
this.browserWindow != this._previousBrowserWindow) {
|
||||
this._chromeSandbox = new Cu.Sandbox(this.browserWindow,
|
||||
{ sandboxPrototype: this.browserWindow, wantXrays: false,
|
||||
sandboxName: 'scratchpad-chrome'});
|
||||
this._chromeSandbox.__SCRATCHPAD__ = this;
|
||||
addDebuggerToGlobal(this._chromeSandbox);
|
||||
|
||||
this._previousBrowserWindow = this.browserWindow;
|
||||
}
|
||||
|
||||
return this._chromeSandbox;
|
||||
},
|
||||
|
||||
/**
|
||||
* Drop the editor selection.
|
||||
*/
|
||||
@ -387,30 +326,38 @@ var Scratchpad = {
|
||||
* @return Promise
|
||||
* The promise for the script evaluation result.
|
||||
*/
|
||||
evalForContext: function SP_evaluateForContext(aString)
|
||||
evaluate: function SP_evaluate(aString)
|
||||
{
|
||||
let deferred = promise.defer();
|
||||
let connection;
|
||||
if (this.executionContext == SCRATCHPAD_CONTEXT_CONTENT) {
|
||||
connection = ScratchpadTab.consoleFor(this.gBrowser.selectedTab);
|
||||
}
|
||||
else {
|
||||
connection = ScratchpadWindow.consoleFor(this.browserWindow);
|
||||
}
|
||||
|
||||
// This setTimeout is temporary and will be replaced by DebuggerClient
|
||||
// execution in a future patch (bug 825039). The purpose for using
|
||||
// setTimeout is to ensure there is no accidental dependency on the
|
||||
// promise being resolved synchronously, which can cause subtle bugs.
|
||||
setTimeout(() => {
|
||||
let chrome = this.executionContext != SCRATCHPAD_CONTEXT_CONTENT;
|
||||
let sandbox = chrome ? this.chromeSandbox : this.contentSandbox;
|
||||
let name = this.uniqueName;
|
||||
let evalOptions = { url: this.uniqueName };
|
||||
|
||||
try {
|
||||
let result = Cu.evalInSandbox(aString, sandbox, "1.8", name, 1);
|
||||
deferred.resolve([aString, undefined, result]);
|
||||
}
|
||||
catch (ex) {
|
||||
deferred.resolve([aString, ex]);
|
||||
}
|
||||
}, 0);
|
||||
return connection.then(({ debuggerClient, webConsoleClient }) => {
|
||||
let deferred = promise.defer();
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
webConsoleClient.evaluateJS(aString, aResponse => {
|
||||
this.debuggerClient = debuggerClient;
|
||||
this.webConsoleClient = webConsoleClient;
|
||||
if (aResponse.error) {
|
||||
deferred.reject(aResponse);
|
||||
}
|
||||
else if (aResponse.exception) {
|
||||
deferred.resolve([aString, aResponse]);
|
||||
}
|
||||
else {
|
||||
deferred.resolve([aString, undefined, aResponse.result]);
|
||||
}
|
||||
}, evalOptions);
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Execute the selected text (if any) or the entire editor content in the
|
||||
@ -422,7 +369,7 @@ var Scratchpad = {
|
||||
execute: function SP_execute()
|
||||
{
|
||||
let selection = this.selectedText || this.getText();
|
||||
return this.evalForContext(selection);
|
||||
return this.evaluate(selection);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -434,16 +381,22 @@ var Scratchpad = {
|
||||
*/
|
||||
run: function SP_run()
|
||||
{
|
||||
let execPromise = this.execute();
|
||||
execPromise.then(([, aError, ]) => {
|
||||
let deferred = promise.defer();
|
||||
let reject = aReason => deferred.reject(aReason);
|
||||
|
||||
this.execute().then(([aString, aError, aResult]) => {
|
||||
let resolve = () => deferred.resolve([aString, aError, aResult]);
|
||||
|
||||
if (aError) {
|
||||
this.writeAsErrorComment(aError);
|
||||
this.writeAsErrorComment(aError.exception).then(resolve, reject);
|
||||
}
|
||||
else {
|
||||
this.deselect();
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
return execPromise;
|
||||
}, reject);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -463,11 +416,10 @@ var Scratchpad = {
|
||||
let resolve = () => deferred.resolve([aString, aError, aResult]);
|
||||
|
||||
if (aError) {
|
||||
this.writeAsErrorComment(aError);
|
||||
resolve();
|
||||
this.writeAsErrorComment(aError.exception).then(resolve, reject);
|
||||
}
|
||||
else if (!isObject(aResult)) {
|
||||
this.writeAsComment(aResult);
|
||||
else if (VariablesView.isPrimitive({ value: aResult })) {
|
||||
this.writeAsComment(aResult.type || aResult);
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
@ -526,16 +478,39 @@ var Scratchpad = {
|
||||
*/
|
||||
display: function SP_display()
|
||||
{
|
||||
let execPromise = this.execute();
|
||||
execPromise.then(([aString, aError, aResult]) => {
|
||||
let deferred = promise.defer();
|
||||
let reject = aReason => deferred.reject(aReason);
|
||||
|
||||
this.execute().then(([aString, aError, aResult]) => {
|
||||
let resolve = () => deferred.resolve([aString, aError, aResult]);
|
||||
|
||||
if (aError) {
|
||||
this.writeAsErrorComment(aError);
|
||||
this.writeAsErrorComment(aError.exception).then(resolve, reject);
|
||||
}
|
||||
else if (VariablesView.isPrimitive({ value: aResult })) {
|
||||
this.writeAsComment(aResult.type || aResult);
|
||||
resolve();
|
||||
}
|
||||
else {
|
||||
this.writeAsComment(aResult);
|
||||
let gripClient = new GripClient(this.debuggerClient, aResult);
|
||||
gripClient.getDisplayString(aResponse => {
|
||||
if (aResponse.error) {
|
||||
reject(aResponse);
|
||||
}
|
||||
else {
|
||||
let string = aResponse.displayString;
|
||||
if (string && string.type == "null") {
|
||||
string = "Exception: " +
|
||||
this.strings.GetStringFromName("stringConversionFailed");
|
||||
}
|
||||
this.writeAsComment(string);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return execPromise;
|
||||
}, reject);
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -563,28 +538,119 @@ var Scratchpad = {
|
||||
* Write out an error at the current insertion point as a block comment
|
||||
* @param object aValue
|
||||
* The Error object to write out the message and stack trace
|
||||
* @return Promise
|
||||
* The promise that indicates when writing the comment completes.
|
||||
*/
|
||||
writeAsErrorComment: function SP_writeAsErrorComment(aError)
|
||||
{
|
||||
let stack = "";
|
||||
if (aError.stack) {
|
||||
stack = aError.stack;
|
||||
let deferred = promise.defer();
|
||||
|
||||
if (VariablesView.isPrimitive({ value: aError })) {
|
||||
deferred.resolve(aError);
|
||||
}
|
||||
else if (aError.fileName) {
|
||||
if (aError.lineNumber) {
|
||||
stack = "@" + aError.fileName + ":" + aError.lineNumber;
|
||||
else {
|
||||
let reject = aReason => deferred.reject(aReason);
|
||||
let gripClient = new GripClient(this.debuggerClient, aError);
|
||||
|
||||
// Because properties on Error objects are lazily added, this roundabout
|
||||
// way of getting all the properties is required, rather than simply
|
||||
// using getPrototypeAndProperties. See bug 724768.
|
||||
let names = ["message", "stack", "fileName", "lineNumber"];
|
||||
let promises = names.map(aName => {
|
||||
let deferred = promise.defer();
|
||||
|
||||
gripClient.getProperty(aName, aResponse => {
|
||||
if (aResponse.error) {
|
||||
deferred.reject(aResponse);
|
||||
}
|
||||
else {
|
||||
deferred.resolve({
|
||||
name: aName,
|
||||
descriptor: aResponse.descriptor
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
});
|
||||
|
||||
{
|
||||
// We also need to use getPrototypeAndProperties to retrieve any
|
||||
// safeGetterValues in case this is a DOM error.
|
||||
let deferred = promise.defer();
|
||||
gripClient.getPrototypeAndProperties(aResponse => {
|
||||
if (aResponse.error) {
|
||||
deferred.reject(aResponse);
|
||||
}
|
||||
else {
|
||||
deferred.resolve(aResponse);
|
||||
}
|
||||
});
|
||||
promises.push(deferred.promise);
|
||||
}
|
||||
else {
|
||||
stack = "@" + aError.fileName;
|
||||
}
|
||||
}
|
||||
else if (aError.lineNumber) {
|
||||
stack = "@" + aError.lineNumber;
|
||||
|
||||
promise.all(promises).then(aProperties => {
|
||||
let error = {};
|
||||
let safeGetters;
|
||||
|
||||
// Combine all the property descriptor/getter values into one object.
|
||||
for (let property of aProperties) {
|
||||
if (property.descriptor) {
|
||||
error[property.name] = property.descriptor.value;
|
||||
}
|
||||
else if (property.safeGetterValues) {
|
||||
safeGetters = property.safeGetterValues;
|
||||
}
|
||||
}
|
||||
|
||||
if (safeGetters) {
|
||||
for (let key of Object.keys(safeGetters)) {
|
||||
if (!error.hasOwnProperty(key)) {
|
||||
error[key] = safeGetters[key].getterValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Assemble the best possible stack we can given the properties we have.
|
||||
let stack;
|
||||
if (typeof error.stack == "string") {
|
||||
stack = error.stack;
|
||||
}
|
||||
else if (typeof error.fileName == "number") {
|
||||
stack = "@" + error.fileName;
|
||||
if (typeof error.lineNumber == "number") {
|
||||
stack += ":" + error.lineNumber;
|
||||
}
|
||||
}
|
||||
else if (typeof error.lineNumber == "number") {
|
||||
stack = "@" + error.lineNumber;
|
||||
}
|
||||
|
||||
stack = stack ? "\n" + stack.replace(/\n$/, "") : "";
|
||||
|
||||
if (typeof error.message == "string") {
|
||||
deferred.resolve(error.message + stack);
|
||||
}
|
||||
else {
|
||||
gripClient.getDisplayString(aResult => {
|
||||
if (aResult.error) {
|
||||
deferred.reject(aResult);
|
||||
}
|
||||
else if (aResult.displayString.type == "null") {
|
||||
deferred.resolve(stack);
|
||||
}
|
||||
else {
|
||||
deferred.resolve(aResult.displayString + stack);
|
||||
}
|
||||
}, reject);
|
||||
}
|
||||
}, reject);
|
||||
}
|
||||
|
||||
let newComment = "Exception: " + ( aError.message || aError) + ( stack == "" ? stack : "\n" + stack.replace(/\n$/, "") );
|
||||
|
||||
this.writeAsComment(newComment);
|
||||
return deferred.promise.then(aMessage => {
|
||||
console.log(aMessage);
|
||||
this.writeAsComment("Exception: " + aMessage);
|
||||
});
|
||||
},
|
||||
|
||||
// Menu Operations
|
||||
@ -1074,7 +1140,7 @@ var Scratchpad = {
|
||||
*/
|
||||
openWebConsole: function SP_openWebConsole()
|
||||
{
|
||||
let target = devtools.TargetFactory.forTab(this.gBrowser.selectedTab);
|
||||
let target = TargetFactory.forTab(this.gBrowser.selectedTab);
|
||||
gDevTools.showToolbox(target, "webconsole");
|
||||
this.browserWindow.focus();
|
||||
},
|
||||
@ -1094,7 +1160,6 @@ var Scratchpad = {
|
||||
content.setAttribute("checked", true);
|
||||
this.executionContext = SCRATCHPAD_CONTEXT_CONTENT;
|
||||
this.notificationBox.removeAllNotifications(false);
|
||||
this.resetContext();
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1120,19 +1185,6 @@ var Scratchpad = {
|
||||
null,
|
||||
this.notificationBox.PRIORITY_WARNING_HIGH,
|
||||
null);
|
||||
this.resetContext();
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset the cached Cu.Sandbox object for the current context.
|
||||
*/
|
||||
resetContext: function SP_resetContext()
|
||||
{
|
||||
this._chromeSandbox = null;
|
||||
this._contentSandbox = null;
|
||||
this._previousWindow = null;
|
||||
this._previousBrowser = null;
|
||||
this._previousLocation = null;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1229,9 +1281,7 @@ var Scratchpad = {
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
|
||||
this._triggerObservers("Ready");
|
||||
|
||||
this.populateRecentFilesMenu();
|
||||
PreferenceObserver.init();
|
||||
},
|
||||
@ -1299,8 +1349,6 @@ var Scratchpad = {
|
||||
return;
|
||||
}
|
||||
|
||||
this.resetContext();
|
||||
|
||||
// This event is created only after user uses 'reload and run' feature.
|
||||
if (this._reloadAndRunEvent) {
|
||||
this.gBrowser.selectedBrowser.removeEventListener("load",
|
||||
@ -1313,6 +1361,12 @@ var Scratchpad = {
|
||||
|
||||
this.editor.destroy();
|
||||
this.editor = null;
|
||||
if (this._sidebar) {
|
||||
this._sidebar.destroy();
|
||||
this._sidebar = null;
|
||||
}
|
||||
this.webConsoleClient = null;
|
||||
this.debuggerClient = null;
|
||||
this.initialized = false;
|
||||
},
|
||||
|
||||
@ -1480,13 +1534,152 @@ var Scratchpad = {
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Represents the DebuggerClient connection to a specific tab as used by the
|
||||
* Scratchpad.
|
||||
*
|
||||
* @param object aTab
|
||||
* The tab to connect to.
|
||||
*/
|
||||
function ScratchpadTab(aTab)
|
||||
{
|
||||
this._tab = aTab;
|
||||
}
|
||||
|
||||
let scratchpadTargets = new WeakMap();
|
||||
|
||||
/**
|
||||
* Returns the object containing the DebuggerClient and WebConsoleClient for a
|
||||
* given tab or window.
|
||||
*
|
||||
* @param object aSubject
|
||||
* The tab or window to obtain the connection for.
|
||||
* @return Promise
|
||||
* The promise for the connection information.
|
||||
*/
|
||||
ScratchpadTab.consoleFor = function consoleFor(aSubject)
|
||||
{
|
||||
if (!scratchpadTargets.has(aSubject)) {
|
||||
scratchpadTargets.set(aSubject, new this(aSubject));
|
||||
}
|
||||
return scratchpadTargets.get(aSubject).connect();
|
||||
};
|
||||
|
||||
|
||||
ScratchpadTab.prototype = {
|
||||
/**
|
||||
* The promise for the connection.
|
||||
*/
|
||||
_connector: null,
|
||||
|
||||
/**
|
||||
* Initialize a debugger client and connect it to the debugger server.
|
||||
*
|
||||
* @return Promise
|
||||
* The promise for the result of connecting to this tab or window.
|
||||
*/
|
||||
connect: function ST_connect()
|
||||
{
|
||||
if (this._connector) {
|
||||
return this._connector;
|
||||
}
|
||||
|
||||
let deferred = promise.defer();
|
||||
this._connector = deferred.promise;
|
||||
|
||||
let connectTimer = setTimeout(() => {
|
||||
deferred.reject({
|
||||
error: "timeout",
|
||||
message: Scratchpad.strings.GetStringFromName("connectionTimeout"),
|
||||
});
|
||||
}, REMOTE_TIMEOUT);
|
||||
|
||||
deferred.promise.then(() => clearTimeout(connectTimer));
|
||||
|
||||
this._attach().then(aTarget => {
|
||||
let consoleActor = aTarget.form.consoleActor;
|
||||
let client = aTarget.client;
|
||||
client.attachConsole(consoleActor, [], (aResponse, aWebConsoleClient) => {
|
||||
if (aResponse.error) {
|
||||
reportError("attachConsole", aResponse);
|
||||
deferred.reject(aResponse);
|
||||
}
|
||||
else {
|
||||
deferred.resolve({
|
||||
webConsoleClient: aWebConsoleClient,
|
||||
debuggerClient: client
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* Attach to this tab.
|
||||
*
|
||||
* @return Promise
|
||||
* The promise for the TabTarget for this tab.
|
||||
*/
|
||||
_attach: function ST__attach()
|
||||
{
|
||||
let target = TargetFactory.forTab(this._tab);
|
||||
return target.makeRemote().then(() => target);
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Represents the DebuggerClient connection to a specific window as used by the
|
||||
* Scratchpad.
|
||||
*/
|
||||
function ScratchpadWindow() {}
|
||||
|
||||
ScratchpadWindow.consoleFor = ScratchpadTab.consoleFor;
|
||||
|
||||
ScratchpadWindow.prototype = Heritage.extend(ScratchpadTab.prototype, {
|
||||
/**
|
||||
* Attach to this window.
|
||||
*
|
||||
* @return Promise
|
||||
* The promise for the target for this window.
|
||||
*/
|
||||
_attach: function SW__attach()
|
||||
{
|
||||
let deferred = promise.defer();
|
||||
|
||||
if (!DebuggerServer.initialized) {
|
||||
DebuggerServer.init();
|
||||
DebuggerServer.addBrowserActors();
|
||||
}
|
||||
|
||||
let client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||
client.connect(() => {
|
||||
client.listTabs(aResponse => {
|
||||
if (aResponse.error) {
|
||||
reportError("listTabs", aResponse);
|
||||
deferred.reject(aResponse);
|
||||
}
|
||||
else {
|
||||
deferred.resolve({ form: aResponse, client: client });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Encapsulates management of the sidebar containing the VariablesView for
|
||||
* object inspection.
|
||||
*/
|
||||
function ScratchpadSidebar(aScratchpad)
|
||||
{
|
||||
let ToolSidebar = devtools.require("devtools/framework/sidebar").ToolSidebar;
|
||||
let ToolSidebar = require("devtools/framework/sidebar").ToolSidebar;
|
||||
let tabbox = document.querySelector("#scratchpad-sidebar");
|
||||
this._sidebar = new ToolSidebar(tabbox, this, "scratchpad");
|
||||
this._scratchpad = aScratchpad;
|
||||
@ -1526,14 +1719,30 @@ ScratchpadSidebar.prototype = {
|
||||
let deferred = promise.defer();
|
||||
|
||||
let onTabReady = () => {
|
||||
if (!this.variablesView) {
|
||||
if (this.variablesView) {
|
||||
this.variablesView.controller.releaseActors();
|
||||
}
|
||||
else {
|
||||
let window = this._sidebar.getWindowForTab("variablesview");
|
||||
let container = window.document.querySelector("#variables");
|
||||
|
||||
this.variablesView = new VariablesView(container, {
|
||||
searchEnabled: true,
|
||||
searchPlaceholder: this._scratchpad.strings
|
||||
.GetStringFromName("propertiesFilterPlaceholder")
|
||||
});
|
||||
|
||||
VariablesViewController.attach(this.variablesView, {
|
||||
getGripClient: aGrip => {
|
||||
return new GripClient(this._scratchpad.debuggerClient, aGrip);
|
||||
},
|
||||
getLongStringClient: aActor => {
|
||||
return this._scratchpad.webConsoleClient.longString(aActor);
|
||||
},
|
||||
releaseActor: aActor => {
|
||||
this._scratchpad.debuggerClient.release(aActor);
|
||||
}
|
||||
});
|
||||
}
|
||||
this._update(aObject).then(() => deferred.resolve());
|
||||
};
|
||||
@ -1571,6 +1780,21 @@ ScratchpadSidebar.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Destroy the sidebar.
|
||||
*
|
||||
* @return Promise
|
||||
* The promise that resolves when the sidebar is destroyed.
|
||||
*/
|
||||
destroy: function SS_destroy()
|
||||
{
|
||||
if (this.variablesView) {
|
||||
this.variablesView.controller.releaseActors();
|
||||
this.variablesView = null;
|
||||
}
|
||||
return this._sidebar.destroy();
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the object currently inspected by the sidebar.
|
||||
*
|
||||
@ -1581,24 +1805,31 @@ ScratchpadSidebar.prototype = {
|
||||
*/
|
||||
_update: function SS__update(aObject)
|
||||
{
|
||||
let deferred = promise.defer();
|
||||
let view = this.variablesView;
|
||||
view.empty();
|
||||
|
||||
this.variablesView.rawObject = aObject;
|
||||
let scope = view.addScope();
|
||||
scope.expanded = true;
|
||||
scope.locked = true;
|
||||
|
||||
// In the future this will work on remote values (bug 825039).
|
||||
setTimeout(() => deferred.resolve(), 0);
|
||||
return deferred.promise;
|
||||
let container = scope.addItem();
|
||||
return view.controller.expand(container, aObject);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Check whether a value is non-primitive.
|
||||
* Report an error coming over the remote debugger protocol.
|
||||
*
|
||||
* @param string aAction
|
||||
* The name of the action or method that failed.
|
||||
* @param object aResponse
|
||||
* The response packet that contains the error.
|
||||
*/
|
||||
function isObject(aValue)
|
||||
function reportError(aAction, aResponse)
|
||||
{
|
||||
let type = typeof aValue;
|
||||
return type == "object" ? aValue != null : type == "function";
|
||||
Cu.reportError(aAction + " failed: " + aResponse.error + " " +
|
||||
aResponse.message);
|
||||
}
|
||||
|
||||
|
||||
|
@ -49,7 +49,6 @@
|
||||
<command id="sp-cmd-contentContext" oncommand="Scratchpad.setContentContext();"/>
|
||||
<command id="sp-cmd-browserContext" oncommand="Scratchpad.setBrowserContext();" disabled="true"/>
|
||||
<command id="sp-cmd-reloadAndRun" oncommand="Scratchpad.reloadAndRun();"/>
|
||||
<command id="sp-cmd-resetContext" oncommand="Scratchpad.resetContext();"/>
|
||||
<command id="sp-cmd-errorConsole" oncommand="Scratchpad.openErrorConsole();" disabled="true"/>
|
||||
<command id="sp-cmd-webConsole" oncommand="Scratchpad.openWebConsole();"/>
|
||||
<command id="sp-cmd-documentationLink" oncommand="Scratchpad.openDocumentationPage();"/>
|
||||
@ -205,10 +204,6 @@
|
||||
key="sp-key-reloadAndRun"
|
||||
accesskey="&reloadAndRun.accesskey;"
|
||||
command="sp-cmd-reloadAndRun"/>
|
||||
<menuitem id="sp-text-resetContext"
|
||||
label="&resetContext2.label;"
|
||||
accesskey="&resetContext2.accesskey;"
|
||||
command="sp-cmd-resetContext"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
|
||||
@ -305,11 +300,6 @@
|
||||
accesskey="&display.accesskey;"
|
||||
key="sp-key-display"
|
||||
command="sp-cmd-display"/>
|
||||
<menuseparator/>
|
||||
<menuitem id="sp-text-resetContext"
|
||||
label="&resetContext2.label;"
|
||||
accesskey="&resetContext2.accesskey;"
|
||||
command="sp-cmd-resetContext"/>
|
||||
</menupopup>
|
||||
</popupset>
|
||||
|
||||
|
@ -36,6 +36,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_scratchpad_bug740948_reload_and_run.js \
|
||||
browser_scratchpad_bug_661762_wrong_window_focus.js \
|
||||
browser_scratchpad_bug_644413_modeline.js \
|
||||
browser_scratchpad_bug807924_cannot_convert_to_string.js \
|
||||
head.js \
|
||||
|
||||
# Disable test due to bug 807234 becoming basically permanent
|
||||
|
@ -0,0 +1,33 @@
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onLoad() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
|
||||
openScratchpad(runTests);
|
||||
}, true);
|
||||
|
||||
content.location = "data:text/html;charset=utf8,test display of values" +
|
||||
" which can't be converted to string in Scratchpad";
|
||||
}
|
||||
|
||||
|
||||
function runTests()
|
||||
{
|
||||
let sp = gScratchpadWindow.Scratchpad;
|
||||
sp.setText("Object.create(null);");
|
||||
|
||||
sp.display().then(([, , aResult]) => {
|
||||
is(sp.getText(),
|
||||
"Object.create(null);\n" +
|
||||
"/*\nException: Cannot convert value to string.\n*/",
|
||||
"'Cannot convert value to string' comment is shown");
|
||||
|
||||
finish();
|
||||
});
|
||||
}
|
@ -89,6 +89,6 @@ function testFocus(sw, hud) {
|
||||
sp.run().then(function ([selection, error, result]) {
|
||||
is(selection, "console.log('foo');", "selection is correct");
|
||||
is(error, undefined, "error is correct");
|
||||
is(result, undefined, "result is correct");
|
||||
is(result.type, "undefined", "result is correct");
|
||||
});
|
||||
}
|
||||
|
@ -44,14 +44,6 @@ function runTests()
|
||||
ok(!notificationBox.currentNotification,
|
||||
"there is no notification in content context");
|
||||
|
||||
let dsp = sp.contentSandbox.__SCRATCHPAD__;
|
||||
|
||||
ok(sp.contentSandbox.__SCRATCHPAD__,
|
||||
"there is a variable named __SCRATCHPAD__");
|
||||
|
||||
ok(sp.contentSandbox.__SCRATCHPAD__.editor,
|
||||
"scratchpad is actually an instance of Scratchpad");
|
||||
|
||||
sp.setText("window.foobarBug636725 = 'aloha';");
|
||||
|
||||
ok(!content.wrappedJSObject.foobarBug636725,
|
||||
@ -76,12 +68,6 @@ function runTests()
|
||||
isnot(contentMenu.getAttribute("checked"), "true",
|
||||
"content menuitem is not checked");
|
||||
|
||||
ok(sp.chromeSandbox.__SCRATCHPAD__,
|
||||
"there is a variable named __SCRATCHPAD__");
|
||||
|
||||
ok(sp.chromeSandbox.__SCRATCHPAD__.editor,
|
||||
"scratchpad is actually an instance of Scratchpad");
|
||||
|
||||
ok(notificationBox.currentNotification,
|
||||
"there is a notification in browser context");
|
||||
|
||||
@ -107,7 +93,7 @@ function runTests()
|
||||
"setText() worked with no end for the replace range");
|
||||
},
|
||||
then: function([, , result]) {
|
||||
is(typeof result.addTab, "function",
|
||||
is(result.class, "XULElement",
|
||||
"chrome context has access to chrome objects");
|
||||
}
|
||||
},
|
||||
@ -132,17 +118,6 @@ function runTests()
|
||||
"global variable exists across two different executions");
|
||||
}
|
||||
},
|
||||
{
|
||||
method: "run",
|
||||
prepare: function() {
|
||||
sp.resetContext();
|
||||
sp.setText("typeof foobarBug636725cache;");
|
||||
},
|
||||
then: function([, , result]) {
|
||||
is(result, "undefined",
|
||||
"global variable no longer exists after calling resetContext()");
|
||||
}
|
||||
},
|
||||
{
|
||||
method: "run",
|
||||
prepare: function() {
|
||||
@ -170,5 +145,10 @@ function runTests()
|
||||
}
|
||||
}];
|
||||
|
||||
runAsyncCallbackTests(sp, tests).then(finish);
|
||||
runAsyncCallbackTests(sp, tests).then(() => {
|
||||
sp.setBrowserContext();
|
||||
sp.setText("delete foobarBug636725cache;" +
|
||||
"delete foobarBug636725cache2;");
|
||||
sp.run().then(finish);
|
||||
});
|
||||
}
|
@ -29,7 +29,6 @@ function runTests()
|
||||
"sp-text-run": "run",
|
||||
"sp-text-inspect": "inspect",
|
||||
"sp-text-display": "display",
|
||||
"sp-text-resetContext": "resetContext",
|
||||
"sp-menu-content": "setContentContext",
|
||||
"sp-menu-browser": "setBrowserContext",
|
||||
};
|
||||
|
@ -25,21 +25,28 @@ this.EXPORTED_SYMBOLS = ["SideMenuWidget"];
|
||||
*
|
||||
* @param nsIDOMNode aNode
|
||||
* The element associated with the widget.
|
||||
* @param boolean aShowArrows
|
||||
* Specifies if items in this container should display horizontal arrows.
|
||||
* @param Object aOptions
|
||||
* - showArrows: Specifies if items in this container should display
|
||||
* horizontal arrows.
|
||||
* - showCheckboxes: Specifies if items in this container should display
|
||||
* checkboxes.
|
||||
*/
|
||||
this.SideMenuWidget = function SideMenuWidget(aNode, aShowArrows = true) {
|
||||
this.SideMenuWidget = function SideMenuWidget(aNode, aOptions={}) {
|
||||
this.document = aNode.ownerDocument;
|
||||
this.window = this.document.defaultView;
|
||||
this._parent = aNode;
|
||||
this._showArrows = aShowArrows;
|
||||
|
||||
let { showArrows, showCheckboxes } = aOptions;
|
||||
this._showArrows = showArrows || false;
|
||||
this._showCheckboxes = showCheckboxes || false;
|
||||
|
||||
// Create an internal scrollbox container.
|
||||
this._list = this.document.createElement("scrollbox");
|
||||
this._list.className = "side-menu-widget-container";
|
||||
this._list.setAttribute("flex", "1");
|
||||
this._list.setAttribute("orient", "vertical");
|
||||
this._list.setAttribute("with-arrow", aShowArrows);
|
||||
this._list.setAttribute("with-arrow", showArrows);
|
||||
this._list.setAttribute("with-checkboxes", showCheckboxes);
|
||||
this._list.setAttribute("tabindex", "0");
|
||||
this._list.addEventListener("keypress", e => this.emit("keyPress", e), false);
|
||||
this._list.addEventListener("mousedown", e => this.emit("mousePress", e), false);
|
||||
@ -91,10 +98,12 @@ SideMenuWidget.prototype = {
|
||||
* A tooltip attribute for the displayed item.
|
||||
* @param string aGroup [optional]
|
||||
* The group to place the displayed item into.
|
||||
* @param Object aAttachment [optional]
|
||||
* Extra data for the user.
|
||||
* @return nsIDOMNode
|
||||
* The element associated with the displayed item.
|
||||
*/
|
||||
insertItemAt: function(aIndex, aContents, aTooltip = "", aGroup = "") {
|
||||
insertItemAt: function(aIndex, aContents, aTooltip = "", aGroup = "", aAttachment={}) {
|
||||
aTooltip = NetworkHelper.convertToUnicode(unescape(aTooltip));
|
||||
aGroup = NetworkHelper.convertToUnicode(unescape(aGroup));
|
||||
|
||||
@ -115,7 +124,7 @@ SideMenuWidget.prototype = {
|
||||
(this._list.scrollTop + this._list.clientHeight >= this._list.scrollHeight);
|
||||
|
||||
let group = this._getMenuGroupForName(aGroup);
|
||||
let item = this._getMenuItemForGroup(group, aContents, aTooltip);
|
||||
let item = this._getMenuItemForGroup(group, aContents, aTooltip, aAttachment);
|
||||
let element = item.insertSelfAt(aIndex);
|
||||
|
||||
if (this.maintainSelectionVisible) {
|
||||
@ -397,14 +406,17 @@ SideMenuWidget.prototype = {
|
||||
* The string or node displayed in the container.
|
||||
* @param string aTooltip [optional]
|
||||
* A tooltip attribute for the displayed item.
|
||||
* @param object aAttachment [optional]
|
||||
* The attachement object.
|
||||
*/
|
||||
_getMenuItemForGroup: function(aGroup, aContents, aTooltip) {
|
||||
return new SideMenuItem(aGroup, aContents, aTooltip, this._showArrows);
|
||||
_getMenuItemForGroup: function(aGroup, aContents, aTooltip, aAttachment) {
|
||||
return new SideMenuItem(aGroup, aContents, aTooltip, this._showArrows, this._showCheckboxes, aAttachment);
|
||||
},
|
||||
|
||||
window: null,
|
||||
document: null,
|
||||
_showArrows: false,
|
||||
_showCheckboxes: false,
|
||||
_parent: null,
|
||||
_list: null,
|
||||
_boxObject: null,
|
||||
@ -527,14 +539,30 @@ SideMenuGroup.prototype = {
|
||||
* The string or node displayed in the container.
|
||||
* @param boolean aArrowFlag
|
||||
* True if a horizontal arrow should be shown.
|
||||
* @param boolean aCheckboxFlag
|
||||
* True if a checkbox should be shown.
|
||||
* @param object aAttachment [optional]
|
||||
* The attachment object.
|
||||
*/
|
||||
function SideMenuItem(aGroup, aContents, aTooltip, aArrowFlag) {
|
||||
function SideMenuItem(aGroup, aContents, aTooltip, aArrowFlag, aCheckboxFlag, aAttachment={}) {
|
||||
this.document = aGroup.document;
|
||||
this.window = aGroup.window;
|
||||
this.ownerView = aGroup;
|
||||
|
||||
// Show a horizontal arrow towards the content.
|
||||
if (aArrowFlag) {
|
||||
let makeCheckbox = () => {
|
||||
let checkbox = this.document.createElement("checkbox");
|
||||
checkbox.className = "side-menu-widget-item-checkbox";
|
||||
checkbox.setAttribute("checked", aAttachment.checkboxState);
|
||||
checkbox.setAttribute("tooltiptext", aAttachment.checkboxTooltip);
|
||||
checkbox.addEventListener("command", function () {
|
||||
ViewHelpers.dispatchEvent(checkbox, "check", {
|
||||
checked: checkbox.checked,
|
||||
});
|
||||
}, false);
|
||||
return checkbox;
|
||||
};
|
||||
|
||||
if (aArrowFlag || aCheckboxFlag) {
|
||||
let container = this._container = this.document.createElement("hbox");
|
||||
container.className = "side-menu-widget-item";
|
||||
container.setAttribute("tooltiptext", aTooltip);
|
||||
@ -542,13 +570,22 @@ function SideMenuItem(aGroup, aContents, aTooltip, aArrowFlag) {
|
||||
let target = this._target = this.document.createElement("vbox");
|
||||
target.className = "side-menu-widget-item-contents";
|
||||
|
||||
let arrow = this._arrow = this.document.createElement("hbox");
|
||||
arrow.className = "side-menu-widget-item-arrow";
|
||||
// Show a checkbox before the content.
|
||||
if (aCheckboxFlag) {
|
||||
let checkbox = this._checkbox = makeCheckbox();
|
||||
container.appendChild(checkbox);
|
||||
}
|
||||
|
||||
container.appendChild(target);
|
||||
container.appendChild(arrow);
|
||||
|
||||
// Show a horizontal arrow towards the content.
|
||||
if (aArrowFlag) {
|
||||
let arrow = this._arrow = this.document.createElement("hbox");
|
||||
arrow.className = "side-menu-widget-item-arrow";
|
||||
container.appendChild(arrow);
|
||||
}
|
||||
}
|
||||
// Skip a few redundant nodes when no horizontal arrow is shown.
|
||||
// Skip a few redundant nodes when no horizontal arrow or checkbox is shown.
|
||||
else {
|
||||
let target = this._target = this._container = this.document.createElement("hbox");
|
||||
target.className = "side-menu-widget-item side-menu-widget-item-contents";
|
||||
|
@ -11,6 +11,7 @@ const Cu = Components.utils;
|
||||
|
||||
const PANE_APPEARANCE_DELAY = 50;
|
||||
const PAGE_SIZE_ITEM_COUNT_RATIO = 5;
|
||||
const WIDGET_FOCUSABLE_NODES = new Set(["vbox", "hbox"]);
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
@ -609,7 +610,7 @@ this.WidgetMethods = {
|
||||
* - relaxed: true if this container should allow dupes & degenerates
|
||||
* - attachment: some attached primitive/object for the item
|
||||
* - attributes: a batch of attributes set to the displayed element
|
||||
* - finalize: function invoked when the item is removed
|
||||
* - finalize: function invokde when the item is removed
|
||||
* @return Item
|
||||
* The item associated with the displayed element if an unstaged push,
|
||||
* undefined if the item was staged for a later commit.
|
||||
@ -1117,16 +1118,21 @@ this.WidgetMethods = {
|
||||
_focusChange: function(aDirection) {
|
||||
let commandDispatcher = this._commandDispatcher;
|
||||
let prevFocusedElement = commandDispatcher.focusedElement;
|
||||
let currFocusedElement;
|
||||
|
||||
commandDispatcher.suppressFocusScroll = true;
|
||||
commandDispatcher[aDirection]();
|
||||
do {
|
||||
commandDispatcher.suppressFocusScroll = true;
|
||||
commandDispatcher[aDirection]();
|
||||
currFocusedElement = commandDispatcher.focusedElement;
|
||||
|
||||
// Make sure the newly focused item is a part of this container. If the
|
||||
// focus goes out of bounds, revert the previously focused item.
|
||||
if (!this.getItemForElement(currFocusedElement)) {
|
||||
prevFocusedElement.focus();
|
||||
return false;
|
||||
}
|
||||
} while (!WIDGET_FOCUSABLE_NODES.has(currFocusedElement.tagName));
|
||||
|
||||
// Make sure the newly focused item is a part of this container.
|
||||
// If the focus goes out of bounds, revert the previously focused item.
|
||||
if (!this.getItemForElement(commandDispatcher.focusedElement)) {
|
||||
prevFocusedElement.focus();
|
||||
return false;
|
||||
}
|
||||
// Focus remained within bounds.
|
||||
return true;
|
||||
},
|
||||
@ -1208,7 +1214,10 @@ this.WidgetMethods = {
|
||||
*/
|
||||
getItemForElement: function(aElement) {
|
||||
while (aElement) {
|
||||
let item = this._itemsByElement.get(aElement);
|
||||
let item =
|
||||
this._itemsByElement.get(aElement) ||
|
||||
this._itemsByElement.get(aElement.nextElementSibling) ||
|
||||
this._itemsByElement.get(aElement.previousElementSibling);
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
|
@ -65,6 +65,11 @@ noMatchingGlobalsText=No matching globals
|
||||
# when there are no scripts.
|
||||
noSourcesText=This page has no sources.
|
||||
|
||||
# LOCALIZATION NOTE (blackBoxCheckboxTooltip) = The tooltip text to display when
|
||||
# the user hovers over the checkbox used to toggle black boxing its associated
|
||||
# source.
|
||||
blackBoxCheckboxTooltip=Toggle black boxing
|
||||
|
||||
# LOCALIZATION NOTE (noMatchingSourcesText): The text to display in the
|
||||
# sources menu when there are no matching scripts after filtering.
|
||||
noMatchingSourcesText=No matching sources.
|
||||
|
@ -85,3 +85,11 @@ fileNoLongerExists.notification=This file no longer exists.
|
||||
# LOCALIZATION NOTE (propertiesFilterPlaceholder): this is the text that
|
||||
# appears in the filter text box for the properties view container.
|
||||
propertiesFilterPlaceholder=Filter properties
|
||||
|
||||
# LOCALIZATION NOTE (stringConversionFailed): Happens when a value cannot be
|
||||
# converted to a string for inspection.
|
||||
stringConversionFailed=Cannot convert value to string.
|
||||
|
||||
# LOCALIZATION NOTE (connectionTimeout): message displayed when the Remote Scratchpad
|
||||
# fails to connect to the server due to a timeout.
|
||||
connectionTimeout=Connection timeout. Check the Error Console on both ends for potential error messages. Reopen the Scratchpad to try again.
|
||||
|
@ -17,6 +17,28 @@
|
||||
-moz-border-start-color: transparent;
|
||||
}
|
||||
|
||||
.side-menu-widget-item-checkbox {
|
||||
-moz-appearance: none;
|
||||
padding: 0;
|
||||
margin: 0 -4px 0 4px;
|
||||
}
|
||||
|
||||
.side-menu-widget-item-checkbox > .checkbox-check {
|
||||
-moz-appearance: none;
|
||||
background: none;
|
||||
background-image: url(itemToggle.png);
|
||||
background-repeat: no-repeat;
|
||||
background-clip: content-box;
|
||||
background-position: -24px 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.side-menu-widget-item-checkbox[checked] > .checkbox-check {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
/* ListWidget items */
|
||||
|
||||
.list-widget-item {
|
||||
|
@ -19,6 +19,28 @@
|
||||
-moz-border-start-color: transparent;
|
||||
}
|
||||
|
||||
.side-menu-widget-item-checkbox {
|
||||
-moz-appearance: none;
|
||||
padding: 0;
|
||||
margin: 0 -4px 0 4px;
|
||||
}
|
||||
|
||||
.side-menu-widget-item-checkbox > .checkbox-check {
|
||||
-moz-appearance: none;
|
||||
background: none;
|
||||
background-image: url(itemToggle.png);
|
||||
background-repeat: no-repeat;
|
||||
background-clip: content-box;
|
||||
background-position: -24px 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.side-menu-widget-item-checkbox[checked] > .checkbox-check {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
/* ListWidget items */
|
||||
|
||||
.list-widget-item {
|
||||
|
@ -17,6 +17,28 @@
|
||||
-moz-border-start-color: transparent;
|
||||
}
|
||||
|
||||
.side-menu-widget-item-checkbox {
|
||||
-moz-appearance: none;
|
||||
padding: 0;
|
||||
margin: 0 -4px 0 4px;
|
||||
}
|
||||
|
||||
.side-menu-widget-item-checkbox > .checkbox-check {
|
||||
-moz-appearance: none;
|
||||
background: none;
|
||||
background-image: url(itemToggle.png);
|
||||
background-repeat: no-repeat;
|
||||
background-clip: content-box;
|
||||
background-position: -24px 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.side-menu-widget-item-checkbox[checked] > .checkbox-check {
|
||||
background-position: 0 0;
|
||||
}
|
||||
|
||||
/* ListWidget items */
|
||||
|
||||
.list-widget-item {
|
||||
|
14
js/src/jit-test/tests/basic/testBug895774.js
Normal file
14
js/src/jit-test/tests/basic/testBug895774.js
Normal file
@ -0,0 +1,14 @@
|
||||
var g1 = newGlobal();
|
||||
var g2 = newGlobal();
|
||||
g1.eval("function f1() { debugger; evaluate('debugger', {newContext:true}) }");
|
||||
g2.eval("function f2() { f1(); assertEq(Number(this), 42) }");
|
||||
g2.f1 = g1.f1;
|
||||
|
||||
var dbg = new Debugger(g1,g2);
|
||||
dbg.onDebuggerStatement = function(frame) {
|
||||
var target = frame.older;
|
||||
dbg.onDebuggerStatement = function(frame) {
|
||||
assertEq(Number(target.this.unsafeDereference()), 42);
|
||||
}
|
||||
}
|
||||
g2.f2.call(42);
|
@ -1060,7 +1060,7 @@ FormatFrame(JSContext *cx, const NonBuiltinScriptFrameIter &iter, char *buf, int
|
||||
|
||||
RootedValue thisVal(cx);
|
||||
AutoPropertyDescArray thisProps(cx);
|
||||
if (iter.computeThis()) {
|
||||
if (iter.computeThis(cx)) {
|
||||
thisVal = iter.thisv();
|
||||
if (showThisProps && !thisVal.isPrimitive())
|
||||
thisProps.fetch(&thisVal.toObject());
|
||||
|
@ -3820,7 +3820,7 @@ DebuggerFrame_getThis(JSContext *cx, unsigned argc, Value *vp)
|
||||
RootedValue thisv(cx);
|
||||
{
|
||||
AutoCompartment ac(cx, iter.scopeChain());
|
||||
if (!iter.computeThis())
|
||||
if (!iter.computeThis(cx))
|
||||
return false;
|
||||
thisv = iter.thisv();
|
||||
}
|
||||
@ -4220,7 +4220,7 @@ DebuggerGenericEval(JSContext *cx, const char *fullMethodName, const Value &code
|
||||
Rooted<Env *> env(cx);
|
||||
if (iter) {
|
||||
/* ExecuteInEnv requires 'fp' to have a computed 'this" value. */
|
||||
if (!iter->computeThis())
|
||||
if (!iter->computeThis(cx))
|
||||
return false;
|
||||
thisv = iter->thisv();
|
||||
env = GetDebugScopeForFrame(cx, iter->abstractFramePtr());
|
||||
|
@ -1097,12 +1097,12 @@ ScriptFrameIter::argsObj() const
|
||||
}
|
||||
|
||||
bool
|
||||
ScriptFrameIter::computeThis() const
|
||||
ScriptFrameIter::computeThis(JSContext *cx) const
|
||||
{
|
||||
JS_ASSERT(!done());
|
||||
if (!isIon()) {
|
||||
JS_ASSERT(data_.cx_);
|
||||
return ComputeThis(data_.cx_, abstractFramePtr());
|
||||
assertSameCompartment(cx, scopeChain());
|
||||
return ComputeThis(cx, abstractFramePtr());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1520,7 +1520,7 @@ class ScriptFrameIter
|
||||
ArgumentsObject &argsObj() const;
|
||||
|
||||
// Ensure that thisv is correct, see ComputeThis.
|
||||
bool computeThis() const;
|
||||
bool computeThis(JSContext *cx) const;
|
||||
Value thisv() const;
|
||||
|
||||
Value returnValue() const;
|
||||
|
@ -494,7 +494,7 @@ BackgroundFileSaver::ProcessStateChange()
|
||||
|
||||
// When we are requested to append to an existing file, we should read the
|
||||
// existing data and ensure we include it as part of the final hash.
|
||||
if (append && !isContinuation) {
|
||||
if (mDigestContext && append && !isContinuation) {
|
||||
nsCOMPtr<nsIInputStream> inputStream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream),
|
||||
mActualTarget,
|
||||
|
@ -459,6 +459,34 @@ add_task(function test_setTarget_multiple()
|
||||
destFile.remove(false);
|
||||
});
|
||||
|
||||
add_task(function test_enableAppend()
|
||||
{
|
||||
// This test checks append mode with hashing disabled.
|
||||
let destFile = getTempFile(TEST_FILE_NAME_1);
|
||||
|
||||
// Test the case where the file does not already exists first, then the case
|
||||
// where the file already exists.
|
||||
for (let i = 0; i < 2; i++) {
|
||||
let saver = new BackgroundFileSaverOutputStream();
|
||||
saver.enableAppend();
|
||||
let completionPromise = promiseSaverComplete(saver);
|
||||
|
||||
saver.setTarget(destFile, false);
|
||||
yield promiseCopyToSaver(TEST_DATA_LONG, saver, true);
|
||||
|
||||
saver.finish(Cr.NS_OK);
|
||||
yield completionPromise;
|
||||
|
||||
// Verify results.
|
||||
let expectedContents = (i == 0 ? TEST_DATA_LONG
|
||||
: TEST_DATA_LONG + TEST_DATA_LONG);
|
||||
yield promiseVerifyContents(destFile, expectedContents);
|
||||
}
|
||||
|
||||
// Clean up.
|
||||
destFile.remove(false);
|
||||
});
|
||||
|
||||
add_task(function test_enableAppend_hash()
|
||||
{
|
||||
// This test checks append mode, also verifying that the computed hash
|
||||
|
@ -3340,6 +3340,18 @@
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'prototype' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_REMOTE_DISPLAYSTRING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'displayString' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_LOCAL_DISPLAYSTRING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
"n_buckets": "1000",
|
||||
"description": "The time (in milliseconds) that it took a 'displayString' request to go round trip."
|
||||
},
|
||||
"DEVTOOLS_DEBUGGER_RDP_LOCAL_SUBSTRING_MS": {
|
||||
"kind": "exponential",
|
||||
"high": "10000",
|
||||
|
@ -1679,6 +1679,17 @@ GripClient.prototype = {
|
||||
}, {
|
||||
telemetry: "PROTOTYPE"
|
||||
}),
|
||||
|
||||
/**
|
||||
* Request the display string of the object.
|
||||
*
|
||||
* @param aOnResponse function Called with the request's response.
|
||||
*/
|
||||
getDisplayString: DebuggerClient.requester({
|
||||
type: "displayString"
|
||||
}, {
|
||||
telemetry: "DISPLAYSTRING"
|
||||
}),
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1581,6 +1581,14 @@ var WalkerActor = protocol.ActorClass({
|
||||
|
||||
onFrameLoad: function(window) {
|
||||
let frame = window.frameElement;
|
||||
if (!frame && !this.rootDoc) {
|
||||
this.rootDoc = window.document;
|
||||
this.rootNode = this.document();
|
||||
this.queueMutation({
|
||||
type: "newRoot",
|
||||
target: this.rootNode.form()
|
||||
});
|
||||
}
|
||||
let frameActor = this._refMap.get(frame);
|
||||
if (!frameActor) {
|
||||
return;
|
||||
@ -1641,6 +1649,11 @@ var WalkerActor = protocol.ActorClass({
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.rootDoc === doc) {
|
||||
this.rootDoc = null;
|
||||
this.rootNode = null;
|
||||
}
|
||||
|
||||
this.queueMutation({
|
||||
type: "documentUnload",
|
||||
target: documentActor.actorID
|
||||
@ -1673,6 +1686,7 @@ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
|
||||
autoCleanup: true,
|
||||
|
||||
initialize: function(client, form) {
|
||||
this._rootNodeDeferred = promise.defer();
|
||||
protocol.Front.prototype.initialize.call(this, client, form);
|
||||
this._orphaned = new Set();
|
||||
this._retainedOrphans = new Set();
|
||||
@ -1684,8 +1698,19 @@ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
|
||||
|
||||
// Update the object given a form representation off the wire.
|
||||
form: function(json) {
|
||||
this.actorID = json.actorID;
|
||||
this.actorID = json.actor;
|
||||
this.rootNode = types.getType("domnode").read(json.root, this);
|
||||
this._rootNodeDeferred.resolve(this.rootNode);
|
||||
},
|
||||
|
||||
/**
|
||||
* Clients can use walker.rootNode to get the current root node of the
|
||||
* walker, but during a reload the root node might be null. This
|
||||
* method returns a promise that will resolve to the root node when it is
|
||||
* set.
|
||||
*/
|
||||
getRootNode: function() {
|
||||
return this._rootNodeDeferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1793,8 +1818,19 @@ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
|
||||
let emitMutations = [];
|
||||
for (let change of mutations) {
|
||||
// The target is only an actorID, get the associated front.
|
||||
let targetID = change.target;
|
||||
let targetFront = this.get(targetID);
|
||||
let targetID;
|
||||
let targetFront;
|
||||
|
||||
if (change.type === "newRoot") {
|
||||
this.rootNode = types.getType("domnode").read(change.target, this);
|
||||
this._rootNodeDeferred.resolve(this.rootNode);
|
||||
targetID = this.rootNode.actorID;
|
||||
targetFront = this.rootNode;
|
||||
} else {
|
||||
targetID = change.target;
|
||||
targetFront = this.get(targetID);
|
||||
}
|
||||
|
||||
if (!targetFront) {
|
||||
console.trace("Got a mutation for an unexpected actor: " + targetID + ", please file a bug on bugzilla.mozilla.org!");
|
||||
continue;
|
||||
@ -1848,6 +1884,11 @@ var WalkerFront = exports.WalkerFront = protocol.FrontClass(WalkerActor, {
|
||||
}
|
||||
}
|
||||
} else if (change.type === "documentUnload") {
|
||||
if (targetFront === this.rootNode) {
|
||||
this.rootNode = null;
|
||||
this._rootNodeDeferred = promise.defer();
|
||||
}
|
||||
|
||||
// We try to give fronts instead of actorIDs, but these fronts need
|
||||
// to be destroyed now.
|
||||
emittedMutation.target = targetFront.actorID;
|
||||
@ -1991,7 +2032,12 @@ var InspectorActor = protocol.ActorClass({
|
||||
},
|
||||
|
||||
getWalker: method(function(options={}) {
|
||||
if (this._walkerPromise) {
|
||||
return this._walkerPromise;
|
||||
}
|
||||
|
||||
let deferred = promise.defer();
|
||||
this._walkerPromise = deferred.promise;
|
||||
|
||||
let window = this.window;
|
||||
|
||||
@ -2007,7 +2053,7 @@ var InspectorActor = protocol.ActorClass({
|
||||
domReady();
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
return this._walkerPromise;
|
||||
}, {
|
||||
request: {},
|
||||
response: {
|
||||
|
@ -1966,6 +1966,42 @@ ObjectActor.prototype = {
|
||||
descriptor: this._propertyDescriptor(aRequest.name) };
|
||||
},
|
||||
|
||||
/**
|
||||
* Handle a protocol request to provide the display string for the object.
|
||||
*
|
||||
* @param aRequest object
|
||||
* The protocol request object.
|
||||
*/
|
||||
onDisplayString: function OA_onDisplayString(aRequest) {
|
||||
let toString;
|
||||
try {
|
||||
// Attempt to locate the object's "toString" method.
|
||||
let obj = this.obj;
|
||||
do {
|
||||
let desc = obj.getOwnPropertyDescriptor("toString");
|
||||
if (desc) {
|
||||
toString = desc.value;
|
||||
break;
|
||||
}
|
||||
} while (obj = obj.proto)
|
||||
} catch (e) {
|
||||
dumpn(e);
|
||||
}
|
||||
|
||||
let result = null;
|
||||
if (toString && toString.callable) {
|
||||
// If a toString method was found then call it on the object.
|
||||
let ret = toString.call(this.obj).return;
|
||||
if (typeof ret == "string") {
|
||||
// Only use the result if it was a returned string.
|
||||
result = ret;
|
||||
}
|
||||
}
|
||||
|
||||
return { from: this.actorID,
|
||||
displayString: this.threadActor.createValueGrip(result) };
|
||||
},
|
||||
|
||||
/**
|
||||
* A helper method that creates a property descriptor for the provided object,
|
||||
* properly formatted for sending in a protocol response.
|
||||
@ -1989,6 +2025,10 @@ ObjectActor.prototype = {
|
||||
};
|
||||
}
|
||||
|
||||
if (!desc) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let retval = {
|
||||
configurable: desc.configurable,
|
||||
enumerable: desc.enumerable
|
||||
@ -2058,6 +2098,7 @@ ObjectActor.prototype.requestTypes = {
|
||||
"prototypeAndProperties": ObjectActor.prototype.onPrototypeAndProperties,
|
||||
"prototype": ObjectActor.prototype.onPrototype,
|
||||
"property": ObjectActor.prototype.onProperty,
|
||||
"displayString": ObjectActor.prototype.onDisplayString,
|
||||
"ownPropertyNames": ObjectActor.prototype.onOwnPropertyNames,
|
||||
"decompile": ObjectActor.prototype.onDecompile,
|
||||
"release": ObjectActor.prototype.onRelease,
|
||||
@ -2090,6 +2131,9 @@ update(PauseScopedObjectActor.prototype, {
|
||||
onProperty: PauseScopedActor.withPaused(ObjectActor.prototype.onProperty),
|
||||
onDecompile: PauseScopedActor.withPaused(ObjectActor.prototype.onDecompile),
|
||||
|
||||
onDisplayString:
|
||||
PauseScopedActor.withPaused(ObjectActor.prototype.onDisplayString),
|
||||
|
||||
onParameterNames:
|
||||
PauseScopedActor.withPaused(ObjectActor.prototype.onParameterNames),
|
||||
|
||||
|
@ -407,7 +407,7 @@ StyleSheetActor.prototype = {
|
||||
// get parent actor if this sheet was @imported
|
||||
let parent = this.styleSheet.parentStyleSheet;
|
||||
if (parent) {
|
||||
form.parentActor = this.parentActor._sheets.get(parent);
|
||||
form.parentActor = this.parentActor._sheets.get(parent).form();
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -580,6 +580,7 @@ WebConsoleActor.prototype =
|
||||
let evalOptions = {
|
||||
bindObjectActor: aRequest.bindObjectActor,
|
||||
frameActor: aRequest.frameActor,
|
||||
url: aRequest.url,
|
||||
};
|
||||
let evalInfo = this.evalWithDebugger(input, evalOptions);
|
||||
let evalResult = evalInfo.result;
|
||||
@ -796,6 +797,8 @@ WebConsoleActor.prototype =
|
||||
* evaluated.
|
||||
* - result: the result of the evaluation.
|
||||
* - helperResult: any result coming from a JSTerm helper function.
|
||||
* - url: the url to evaluate the script as. Defaults to
|
||||
* "debugger eval code".
|
||||
*/
|
||||
evalWithDebugger: function WCA_evalWithDebugger(aString, aOptions = {})
|
||||
{
|
||||
@ -894,12 +897,17 @@ WebConsoleActor.prototype =
|
||||
// Ready to evaluate the string.
|
||||
helpers.evalInput = aString;
|
||||
|
||||
let evalOptions;
|
||||
if (typeof aOptions.url == "string") {
|
||||
evalOptions = { url: aOptions.url };
|
||||
}
|
||||
|
||||
let result;
|
||||
if (frame) {
|
||||
result = frame.evalWithBindings(aString, bindings);
|
||||
result = frame.evalWithBindings(aString, bindings, evalOptions);
|
||||
}
|
||||
else {
|
||||
result = dbgWindow.evalInGlobalWithBindings(aString, bindings);
|
||||
result = dbgWindow.evalInGlobalWithBindings(aString, bindings, evalOptions);
|
||||
}
|
||||
|
||||
let helperResult = helpers.helperResult;
|
||||
|
@ -23,6 +23,7 @@ MOCHITEST_CHROME_FILES = \
|
||||
test_inspector-mutations-value.html \
|
||||
test_inspector-release.html \
|
||||
test_inspector-remove.html \
|
||||
test_inspector-reload.html \
|
||||
test_inspector-retain.html \
|
||||
test_inspector-pseudoclass-lock.html \
|
||||
test_inspector-traversal.html \
|
||||
|
@ -235,6 +235,10 @@ function isChildList(change) {
|
||||
return change.type === "childList";
|
||||
}
|
||||
|
||||
function isNewRoot(change) {
|
||||
return change.type === "newRoot";
|
||||
}
|
||||
|
||||
// Make sure an iframe's src attribute changed and then
|
||||
// strip that mutation out of the list.
|
||||
function assertSrcChange(mutations) {
|
||||
|
@ -0,0 +1,83 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug </title>
|
||||
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript;version=1.8" src="inspector-helpers.js"></script>
|
||||
<script type="application/javascript;version=1.8">
|
||||
Components.utils.import("resource://gre/modules/devtools/Loader.jsm");
|
||||
const Ci = Components.interfaces;
|
||||
const promise = devtools.require("sdk/core/promise");
|
||||
const inspector = devtools.require("devtools/server/actors/inspector");
|
||||
|
||||
window.onload = function() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
var gInspectee = null;
|
||||
var gClient = null;
|
||||
var gWalker = null;
|
||||
|
||||
addTest(function setup() {
|
||||
let url = document.getElementById("inspectorContent").href;
|
||||
attachURL(url, function(err, client, tab, doc) {
|
||||
gInspectee = doc;
|
||||
let {InspectorFront} = devtools.require("devtools/server/actors/inspector");
|
||||
let inspector = InspectorFront(client, tab);
|
||||
promiseDone(inspector.getWalker().then(walker => {
|
||||
ok(walker, "getWalker() should return an actor.");
|
||||
gClient = client;
|
||||
gWalker = walker;
|
||||
return inspector.getWalker();
|
||||
}).then(walker => {
|
||||
dump(walker.actorID + "\n");
|
||||
ok(walker === gWalker, "getWalker() twice should return the same walker.");
|
||||
}).then(runNextTest));
|
||||
});
|
||||
});
|
||||
|
||||
addTest(function testReload() {
|
||||
let nodeFront;
|
||||
let oldRootID = gWalker.rootNode.actorID;
|
||||
// Load a node to populate the tree a bit.
|
||||
promiseDone(gWalker.querySelector(gWalker.rootNode, "#a").then(front => {
|
||||
gInspectee.defaultView.location.reload();
|
||||
return waitForMutation(gWalker, isNewRoot);
|
||||
}).then(() => {
|
||||
ok(gWalker.rootNode.actorID != oldRootID, "Root node should have changed.");
|
||||
}).then(() => {
|
||||
// Make sure we can still access the document
|
||||
return gWalker.querySelector(gWalker.rootNode, "#a");
|
||||
}).then(front => {
|
||||
ok(front.actorID, "Got a new actor ID");
|
||||
}).then(runNextTest));
|
||||
});
|
||||
|
||||
addTest(function cleanup() {
|
||||
delete gWalker;
|
||||
delete gInspectee;
|
||||
delete gClient;
|
||||
runNextTest();
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
|
||||
<a id="inspectorContent" target="_blank" href="inspector-traversal-data.html">Test Document</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -101,6 +101,9 @@ WebConsoleClient.prototype = {
|
||||
* - frameActor: a FrameActor ID. The FA holds a reference to
|
||||
* a Debugger.Frame. This option allows you to evaluate the string in
|
||||
* the frame of the given FA.
|
||||
*
|
||||
* - url: the url to evaluate the script as. Defaults to
|
||||
* "debugger eval code".
|
||||
*/
|
||||
evaluateJS: function WCC_evaluateJS(aString, aOnResponse, aOptions = {})
|
||||
{
|
||||
@ -110,6 +113,7 @@ WebConsoleClient.prototype = {
|
||||
text: aString,
|
||||
bindObjectActor: aOptions.bindObjectActor,
|
||||
frameActor: aOptions.frameActor,
|
||||
url: aOptions.url,
|
||||
};
|
||||
this._client.request(packet, aOnResponse);
|
||||
},
|
||||
|
@ -6219,8 +6219,34 @@ nsWindow::SynthesizeNativeMouseEvent(nsIntPoint aPoint,
|
||||
}
|
||||
|
||||
GdkDisplay* display = gdk_window_get_display(mGdkWindow);
|
||||
GdkScreen* screen = gdk_window_get_screen(mGdkWindow);
|
||||
gdk_display_warp_pointer(display, screen, aPoint.x, aPoint.y);
|
||||
|
||||
// When a button-release event is requested, create it here and put it in the
|
||||
// event queue. This will not emit a motion event - this needs to be done
|
||||
// explicitly *before* requesting a button-release. You will also need to wait
|
||||
// for the motion event to be dispatched before requesting a button-release
|
||||
// event to maintain the desired event order.
|
||||
if (aNativeMessage == GDK_BUTTON_RELEASE) {
|
||||
GdkEvent event;
|
||||
memset(&event, 0, sizeof(GdkEvent));
|
||||
event.type = (GdkEventType)aNativeMessage;
|
||||
event.button.button = 1;
|
||||
event.button.window = mGdkWindow;
|
||||
event.button.time = GDK_CURRENT_TIME;
|
||||
|
||||
#if (MOZ_WIDGET_GTK == 3)
|
||||
// Get device for event source
|
||||
GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
|
||||
event.button.device = gdk_device_manager_get_client_pointer(device_manager);
|
||||
#endif
|
||||
|
||||
gdk_event_put(&event);
|
||||
} else {
|
||||
// We don't support specific events other than button-release. In case
|
||||
// aNativeMessage != GDK_BUTTON_RELEASE we'll synthesize a motion event
|
||||
// that will be emitted by gdk_display_warp_pointer().
|
||||
GdkScreen* screen = gdk_window_get_screen(mGdkWindow);
|
||||
gdk_display_warp_pointer(display, screen, aPoint.x, aPoint.y);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user