|
|
1 |
/* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */ |
1 |
* -*- Mode: js2; js2-basic-offset: 2; indent-tabs-mode: nil; -*- */ |
2 |
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ |
2 |
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ |
3 |
/* ***** BEGIN LICENSE BLOCK ***** |
3 |
/* ***** BEGIN LICENSE BLOCK ***** |
4 |
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
4 |
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
|
71 |
"@mozilla.org/widget/clipboardhelper;1", |
71 |
"@mozilla.org/widget/clipboardhelper;1", |
72 |
"nsIClipboardHelper"); |
72 |
"nsIClipboardHelper"); |
|
|
73 |
XPCOMUtils.defineLazyServiceGetter(this, "IOService", |
74 |
"@mozilla.org/network/io-service;1", |
75 |
"nsIIOService"); |
76 |
|
73 |
XPCOMUtils.defineLazyGetter(this, "NetUtil", function () { |
77 |
XPCOMUtils.defineLazyGetter(this, "NetUtil", function () { |
74 |
var obj = {}; |
78 |
var obj = {}; |
75 |
Cu.import("resource://gre/modules/NetUtil.jsm", obj); |
79 |
Cu.import("resource://gre/modules/NetUtil.jsm", obj); |
|
104 |
return obj.namesAndValuesOf; |
108 |
return obj.namesAndValuesOf; |
105 |
}); |
109 |
}); |
|
|
110 |
XPCOMUtils.defineLazyGetter(this, "sourceMapUtils", function () { |
111 |
let smu = {}; |
112 |
Cu.import("resource:///modules/SourceMapUtils.jsm", smu); |
113 |
return smu; |
114 |
}); |
115 |
|
106 |
function LogFactory(aMessagePrefix) |
116 |
function LogFactory(aMessagePrefix) |
107 |
{ |
117 |
{ |
108 |
function log(aMessage) { |
118 |
function log(aMessage) { |
|
1980 |
body, |
1990 |
body, |
1981 |
sourceURL, |
1991 |
sourceURL, |
1982 |
sourceLine, |
1992 |
sourceLine, |
1983 |
clipboardText); |
1993 |
clipboardText, |
|
|
1994 |
this.currentContext()); |
1984 |
// Make the node bring up the property panel, to allow the user to inspect |
1995 |
// Make the node bring up the property panel, to allow the user to inspect |
1985 |
// the stack trace. |
1996 |
// the stack trace. |
|
2072 |
severity, |
2083 |
severity, |
2073 |
aScriptError.errorMessage, |
2084 |
aScriptError.errorMessage, |
2074 |
aScriptError.sourceName, |
2085 |
aScriptError.sourceName, |
2075 |
aScriptError.lineNumber); |
2086 |
aScriptError.lineNumber, |
|
|
2087 |
null, |
2088 |
window); |
2076 |
ConsoleUtils.outputMessageNode(node, hudId); |
2089 |
ConsoleUtils.outputMessageNode(node, hudId); |
2077 |
} |
2090 |
} |
|
5381 |
* The text that should be copied to the clipboard when this node is |
5394 |
* The text that should be copied to the clipboard when this node is |
5382 |
* copied. If omitted, defaults to the body text. If `aBody` is not |
5395 |
* copied. If omitted, defaults to the body text. If `aBody` is not |
5383 |
* a string, then the clipboard text must be supplied. |
5396 |
* a string, then the clipboard text must be supplied. |
|
|
5397 |
* @param nsIDOMWindow aWindow |
5398 |
* The window from which the message originates. This allows us to get |
5399 |
* the source map associated with the script that created this message |
5400 |
* and display the proper originating source file and line numbers. |
5384 |
* @return nsIDOMNode |
5401 |
* @return nsIDOMNode |
5385 |
* The message node: a XUL richlistitem ready to be inserted into |
5402 |
* The message node: a XUL richlistitem ready to be inserted into |
5386 |
* the Web Console output node. |
5403 |
* the Web Console output node. |
|
5388 |
createMessageNode: |
5405 |
createMessageNode: |
5389 |
function ConsoleUtils_createMessageNode(aDocument, aCategory, aSeverity, |
5406 |
function ConsoleUtils_createMessageNode(aDocument, aCategory, aSeverity, |
5390 |
aBody, aSourceURL, aSourceLine, |
5407 |
aBody, aSourceURL, aSourceLine, |
5391 |
aClipboardText) { |
5408 |
aClipboardText, aWindow) { |
|
|
5409 |
|
5392 |
if (aBody instanceof Ci.nsIDOMNode && aClipboardText == null) { |
5410 |
if (aBody instanceof Ci.nsIDOMNode && aClipboardText == null) { |
5393 |
throw new Error("HUDService.createMessageNode(): DOM node supplied " + |
5411 |
throw new Error("HUDService.createMessageNode(): DOM node supplied " + |
5394 |
"without any clipboard text"); |
5412 |
"without any clipboard text"); |
|
5419 |
// If a string was supplied for the body, turn it into a DOM node and an |
5437 |
// If a string was supplied for the body, turn it into a DOM node and an |
5420 |
// associated clipboard string now. |
5438 |
// associated clipboard string now. |
5421 |
aClipboardText = aClipboardText || |
5439 |
aClipboardText = aClipboardText || |
5422 |
(aBody + (aSourceURL ? " @ " + aSourceURL : "") + |
5440 |
this.makeClipboardText(aBody, aSourceURL, aSourceLine); |
5423 |
(aSourceLine ? ":" + aSourceLine : "")); |
|
|
5424 |
aBody = aBody instanceof Ci.nsIDOMNode ? |
5441 |
aBody = aBody instanceof Ci.nsIDOMNode ? |
5425 |
aBody : aDocument.createTextNode(aBody); |
5442 |
aBody : aDocument.createTextNode(aBody); |
|
5466 |
node.setAttribute("id", "console-msg-" + HUDService.sequenceId()); |
5483 |
node.setAttribute("id", "console-msg-" + HUDService.sequenceId()); |
|
|
5484 |
if (aSourceURL && aWindow) { |
5485 |
this.updateLocationInfoWithSourceMap(node, locationNode, aSourceURL, |
5486 |
aSourceLine, aWindow, aDocument, |
5487 |
aBody); |
5488 |
} |
5489 |
|
5467 |
return node; |
5490 |
return node; |
5468 |
}, |
5491 |
}, |
5469 |
/** |
5492 |
/** |
|
|
5493 |
* The panel which holds the original and generated location info for the |
5494 |
* currently focused console message. It is created the first time there is a |
5495 |
* console message which has source mapping info. |
5496 |
*/ |
5497 |
locationContainerPanel: null, |
5498 |
|
5499 |
/** |
5500 |
* Returns the left and top offset of a node. |
5501 |
*/ |
5502 |
elementOffset: |
5503 |
function ConsoleUtils_elementOffset(aNode, aLeft, aTop) { |
5504 |
let elemLeft = typeof aLeft === "number" ? aLeft : 0; |
5505 |
let elemTop = typeof aTop === "number" ? aTop : 0; |
5506 |
if (!aNode) { |
5507 |
return { |
5508 |
left: elemLeft, |
5509 |
top: elemTop |
5510 |
}; |
5511 |
} |
5512 |
let { left, top } = aNode.getBoundingClientRect(); |
5513 |
return this.elementOffset(aNode.offsetParent, |
5514 |
elemLeft + left, |
5515 |
elemTop + top); |
5516 |
}, |
5517 |
|
5518 |
/** |
5519 |
* Returns true/false on whether or not the mouseout handler for the |
5520 |
* locationContainerPanel should fire. |
5521 |
*/ |
5522 |
mouseoutShouldFire: |
5523 |
function ConsoleUtils_mouseoutShouldFire(aTarget) { |
5524 |
if (aTarget === this.locationContainerPanel) { |
5525 |
return true; |
5526 |
} |
5527 |
let el = aTarget; |
5528 |
while (el) { |
5529 |
if (el === this.locationContainerPanel) { |
5530 |
return false; |
5531 |
} |
5532 |
el = el.parentNode; |
5533 |
} |
5534 |
return true; |
5535 |
}, |
5536 |
|
5537 |
/** |
5538 |
* Asynchronously check if there is a source map for this message's |
5539 |
* script. If there is, update the locationNode and clipboardText. |
5540 |
* |
5541 |
* TODO: Use column numbers once bug 568142 is fixed. |
5542 |
* TODO: Don't require aSourceURL once bug 676281 is fixed, just a way to |
5543 |
* get a script. |
5544 |
*/ |
5545 |
updateLocationInfoWithSourceMap: |
5546 |
function ConsoleUtils_updateLocationInfoWithSourceMap (aParentNode, aLocationNode, |
5547 |
aSourceURL, aSourceLine, |
5548 |
aWindow, aDocument, aBody) { |
5549 |
sourceMapUtils.sourceMapForFilename(aSourceURL, aWindow, (function (aSourceMap) { |
5550 |
let { source, line } = aSourceMap.originalPositionFor({ |
5551 |
line: aSourceLine, |
5552 |
column: 0 |
5553 |
}); |
5554 |
|
5555 |
if (source != null && line != null) { |
5556 |
// Resolve the original source url relative to the generated script. |
5557 |
try { |
5558 |
let url = IOService.newURI(aSourceURL, null, null).QueryInterface(Ci.nsIURL); |
5559 |
source = url.resolve(source); |
5560 |
} catch (e) { |
5561 |
Cu.reportError(e); |
5562 |
return; |
5563 |
} |
5564 |
|
5565 |
// Create the new elements we need. |
5566 |
if (!this.locationContainerPanel) { |
5567 |
this.locationContainerPanel = aDocument.createElementNS(XUL_NS, "panel"); |
5568 |
this.locationContainerPanel.classList.add("webconsole-location-container"); |
5569 |
aDocument.documentElement.appendChild(this.locationContainerPanel); |
5570 |
} |
5571 |
let sourceMappedLocationNode = this.createLocationNode(aDocument, |
5572 |
source, |
5573 |
line); |
5574 |
|
5575 |
// Replace the generated source and line with the original, mapped |
5576 |
// source and line. |
5577 |
aParentNode.replaceChild(sourceMappedLocationNode, |
5578 |
aLocationNode); |
5579 |
|
5580 |
// Create another location node with the original, mapped location info |
5581 |
// which will be put in the panel popup. |
5582 |
let sourceMappedLocationNodeForPanel = this.createLocationNode(aDocument, |
5583 |
source, |
5584 |
line); |
5585 |
|
5586 |
sourceMappedLocationNode.addEventListener("mouseover", (function (event) { |
5587 |
if (event.target === sourceMappedLocationNode) { |
5588 |
// Remove all the children from the panel, we are about to populate |
5589 |
// it with our own location infos. |
5590 |
this.locationContainerPanel.hidePopup(); |
5591 |
while (this.locationContainerPanel.firstChild) { |
5592 |
this.locationContainerPanel |
5593 |
.removeChild(this.locationContainerPanel.firstChild); |
5594 |
} |
5595 |
|
5596 |
// Add the full original and generated location info to the panel. |
5597 |
this.locationContainerPanel.appendChild(sourceMappedLocationNodeForPanel); |
5598 |
this.locationContainerPanel.appendChild(aLocationNode); |
5599 |
|
5600 |
// Open the panel over the currently hovered over location node. |
5601 |
let { left, top } = this.elementOffset(sourceMappedLocationNode); |
5602 |
this.locationContainerPanel.openPopup(null, null, left, top, |
5603 |
false, false, null); |
5604 |
} |
5605 |
}).bind(this), false); |
5606 |
|
5607 |
this.locationContainerPanel.addEventListener("mouseout", (function (event) { |
5608 |
if (this.mouseoutShouldFire(event.target) |
5609 |
|| this.mouseoutShouldFire(event.relatedTarget)) { |
5610 |
this.locationContainerPanel.hidePopup(); |
5611 |
} |
5612 |
}).bind(this), false); |
5613 |
|
5614 |
// Update the clipboard text to reflect the original source. |
5615 |
aParentNode.clipboardText = this.makeClipboardText(aBody, source, line); |
5616 |
} |
5617 |
}).bind(this), function (errno, error) { |
5618 |
if (errno != sourceMapUtils.ERRORS.NO_SOURCE_MAP) { |
5619 |
Cu.reportError(error); |
5620 |
} |
5621 |
}); |
5622 |
}, |
5623 |
|
5624 |
/** |
5625 |
* Creates the string that will be copied to the clipboard for individual |
5626 |
* console message nodes. |
5627 |
*/ |
5628 |
makeClipboardText: |
5629 |
function ConsoleUtils_makeClipboardText(aBody, aSourceURL, aSourceLine) { |
5630 |
return aBody |
5631 |
+ (aSourceURL ? " @ " + aSourceURL : "") |
5632 |
+ (aSourceLine ? ":" + aSourceLine : ""); |
5633 |
}, |
5634 |
|
5635 |
/** |
5470 |
* Adjusts the category and severity of the given message, clearing the old |
5636 |
* Adjusts the category and severity of the given message, clearing the old |
5471 |
* category and severity if present. |
5637 |
* category and severity if present. |
5472 |
* |
5638 |
* |