diff --git a/devtools/client/themes/webconsole.css b/devtools/client/themes/webconsole.css index e804f88d9e86..f5edf4772a7a 100644 --- a/devtools/client/themes/webconsole.css +++ b/devtools/client/themes/webconsole.css @@ -102,8 +102,14 @@ a { text-decoration: underline; } -.message-location > .frame-link { - width: 10em; +.message-location > .filename { + text-overflow: ellipsis; + text-align: end; + overflow: hidden; +} + +.message-location > .line-number { + flex: none; } .message-flex-body { diff --git a/devtools/client/webconsole/console-output.js b/devtools/client/webconsole/console-output.js index 69e332ea8094..d1318e1ff31f 100644 --- a/devtools/client/webconsole/console-output.js +++ b/devtools/client/webconsole/console-output.js @@ -3667,7 +3667,8 @@ Widgets.Stacktrace.prototype = Heritage.extend(Widgets.BaseWidget.prototype, } let location = this.output.owner.createLocationNode({url: frame.filename, - line: frame.lineNumber}); + line: frame.lineNumber}, + "jsdebugger"); // .devtools-monospace sets font-size to 80%, however .body already has // .devtools-monospace. If we keep it here, the location would be rendered diff --git a/devtools/client/webconsole/test/browser_console_addonsdk_loader_exception.js b/devtools/client/webconsole/test/browser_console_addonsdk_loader_exception.js index 5ddee3db9fc1..1a49f879dd6c 100644 --- a/devtools/client/webconsole/test/browser_console_addonsdk_loader_exception.js +++ b/devtools/client/webconsole/test/browser_console_addonsdk_loader_exception.js @@ -57,12 +57,12 @@ function test() { let msg = [...result.matched][0]; ok(msg, "message element found"); - let locationNode = msg.querySelector(".message > .message-location > .frame-link"); + let locationNode = msg.querySelector(".message > .message-location"); ok(locationNode, "message location element found"); - let url = locationNode.getAttribute("data-url"); - info("location node url: " + url); - ok(url.indexOf("resource://") === 0, "error comes from a subscript"); + let title = locationNode.getAttribute("title"); + info("location node title: " + title); + isnot(title.indexOf(" -> "), -1, "error comes from a subscript"); let viewSource = browserconsole.viewSource; let URL = null; diff --git a/devtools/client/webconsole/test/browser_console_error_source_click.js b/devtools/client/webconsole/test/browser_console_error_source_click.js index b3596549f9fa..8023fc1ca3b6 100644 --- a/devtools/client/webconsole/test/browser_console_error_source_click.js +++ b/devtools/client/webconsole/test/browser_console_error_source_click.js @@ -61,7 +61,7 @@ function test() { let msg = [...results[0].matched][0]; ok(msg, "message element found for: " + result.text); - let locationNode = msg.querySelector(".message > .message-location .frame-link-filename"); + let locationNode = msg.querySelector(".message > .message-location"); ok(locationNode, "message location element found"); EventUtils.synthesizeMouse(locationNode, 2, 2, {}, hud.iframeWindow); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_587617_output_copy.js b/devtools/client/webconsole/test/browser_webconsole_bug_587617_output_copy.js index a0733e2acbfe..4182de70ebf3 100644 --- a/devtools/client/webconsole/test/browser_webconsole_bug_587617_output_copy.js +++ b/devtools/client/webconsole/test/browser_webconsole_bug_587617_output_copy.js @@ -54,17 +54,15 @@ function consoleOpened(aHud) { .getControllerForCommand("cmd_copy"); is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled"); - // Remove new lines and whitespace since getSelection() includes - // a new line between message and line number, but the clipboard doesn't - // @see bug 1119503 + // Remove new lines since getSelection() includes one between message and + // line number, but the clipboard doesn't (see bug 1119503) let selection = (HUD.iframeWindow.getSelection() + "") - .replace(/\r?\n|\r| /g, ""); + .replace(/\r?\n|\r/g, " "); isnot(selection.indexOf("bug587617"), -1, "selection text includes 'bug587617'"); waitForClipboard((str) => { - // Strip out spaces for comparison ease - return selection.trim() == str.trim().replace(/ /g, ""); + return selection.trim() == str.trim(); }, () => { goDoCommand("cmd_copy"); }, deferred.resolve, deferred.resolve); @@ -84,17 +82,15 @@ function testContextMenuCopy() { let copyItem = contextMenu.querySelector("*[command='cmd_copy']"); ok(copyItem, "the context menu on the output node has a \"Copy\" item"); - // Remove new lines and whitespace since getSelection() includes - // a new line between message and line number, but the clipboard doesn't - // @see bug 1119503 + // Remove new lines since getSelection() includes one between message and line + // number, but the clipboard doesn't (see bug 1119503) let selection = (HUD.iframeWindow.getSelection() + "") - .replace(/\r?\n|\r| /g, ""); + .replace(/\r?\n|\r/g, " "); copyItem.doCommand(); waitForClipboard((str) => { - // Strip out spaces for comparison ease - return selection.trim() == str.trim().replace(/ /g, ""); + return selection.trim() == str.trim(); }, () => { goDoCommand("cmd_copy"); }, deferred.resolve, deferred.resolve); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js b/devtools/client/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js index b7e9ee7e4db4..7418ef185cb6 100644 --- a/devtools/client/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js +++ b/devtools/client/webconsole/test/browser_webconsole_bug_613280_jsterm_copy.js @@ -68,15 +68,14 @@ function performTest(HUD, [result]) { .getControllerForCommand("cmd_copy"); is(controller.isCommandEnabled("cmd_copy"), true, "cmd_copy is enabled"); - // Remove new lines and whitespace since getSelection() includes - // a new line between message and line number, but the clipboard doesn't - // @see bug 1119503 + // Remove new lines since getSelection() includes one between message and line + // number, but the clipboard doesn't (see bug 1119503) let selectionText = (HUD.iframeWindow.getSelection() + "") - .replace(/\r?\n|\r| /g, ""); + .replace(/\r?\n|\r/g, " "); isnot(selectionText.indexOf("foobarBazBug613280"), -1, "selection text includes 'foobarBazBug613280'"); waitForClipboard((str) => { - return selectionText.trim() === str.trim().replace(/ /g, ""); + return str.trim() == selectionText.trim(); }, clipboardSetup, clipboardCopyDone, clipboardCopyDone); } diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js b/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js index a2c28f0f640c..b8237b003c0d 100644 --- a/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js +++ b/devtools/client/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js @@ -43,8 +43,8 @@ function test() { let exceptionMsg = [...exceptionRule.matched][0]; let consoleMsg = [...consoleRule.matched][0]; - let nodes = [exceptionMsg.querySelector(".message-location > .frame-link"), - consoleMsg.querySelector(".message-location > .frame-link")]; + let nodes = [exceptionMsg.querySelector(".message-location"), + consoleMsg.querySelector(".message-location")]; ok(nodes[0], ".location node for the exception message"); ok(nodes[1], ".location node for the console message"); @@ -60,14 +60,14 @@ function test() { function* checkClickOnNode(index, node) { info("checking click on node index " + index); - let url = node.getAttribute("data-url"); + let url = node.getAttribute("title"); ok(url, "source url found for index " + index); - let line = node.getAttribute("data-line"); + let line = node.sourceLine; ok(line, "found source line for index " + index); executeSoon(() => { - EventUtils.sendMouseEvent({ type: "click" }, node.querySelector(".frame-link-filename")); + EventUtils.sendMouseEvent({ type: "click" }, node); }); yield hud.ui.once("source-in-debugger-opened"); diff --git a/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js b/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js index a9196a0e9a8c..8f1ce15eae8c 100644 --- a/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js +++ b/devtools/client/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js @@ -39,10 +39,10 @@ function testViewSource() { }).then(([error1Rule, error2Rule]) => { let error1Msg = [...error1Rule.matched][0]; let error2Msg = [...error2Rule.matched][0]; - nodes = [error1Msg.querySelector(".message-location .frame-link"), - error2Msg.querySelector(".message-location .frame-link")]; - ok(nodes[0], ".frame-link node for the first error"); - ok(nodes[1], ".frame-link node for the second error"); + nodes = [error1Msg.querySelector(".message-location"), + error2Msg.querySelector(".message-location")]; + ok(nodes[0], ".message-location node for the first error"); + ok(nodes[1], ".message-location node for the second error"); let target = TargetFactory.forTab(gBrowser.selectedTab); let toolbox = gDevTools.getToolbox(target); @@ -51,7 +51,7 @@ function testViewSource() { deferred.resolve(panel); }); - EventUtils.sendMouseEvent({ type: "click" }, nodes[0].querySelector(".frame-link-filename")); + EventUtils.sendMouseEvent({ type: "click" }, nodes[0]); }); return deferred.promise; @@ -71,14 +71,14 @@ function onStyleEditorReady(panel) { checkStyleEditorForSheetAndLine(href, line - 1).then(deferred.resolve); }); - EventUtils.sendMouseEvent({ type: "click" }, nodes[1].querySelector(".frame-link-filename")); + EventUtils.sendMouseEvent({ type: "click" }, nodes[1]); } waitForFocus(function() { info("style editor window focused"); - let href = nodes[0].getAttribute("data-url"); - let line = nodes[0].getAttribute("data-line"); + let href = nodes[0].getAttribute("title"); + let line = nodes[0].sourceLine; ok(line, "found source line"); checkStyleEditorForSheetAndLine(href, line - 1).then(function() { @@ -87,8 +87,8 @@ function onStyleEditorReady(panel) { let target = TargetFactory.forTab(gBrowser.selectedTab); let toolbox = gDevTools.getToolbox(target); - href = nodes[1].getAttribute("data-url"); - line = nodes[1].getAttribute("data-line"); + href = nodes[1].getAttribute("title"); + line = nodes[1].sourceLine; ok(line, "found source line"); toolbox.selectTool("webconsole").then(function() { diff --git a/devtools/client/webconsole/test/browser_webconsole_scratchpad_panel_link.js b/devtools/client/webconsole/test/browser_webconsole_scratchpad_panel_link.js index d7fd87aff9a5..c805600eb62d 100644 --- a/devtools/client/webconsole/test/browser_webconsole_scratchpad_panel_link.js +++ b/devtools/client/webconsole/test/browser_webconsole_scratchpad_panel_link.js @@ -58,7 +58,7 @@ add_task(function*() { let [matched] = [...messages[0].matched]; ok(matched, "Found logged message from Scratchpad"); - let anchor = matched.querySelector(".message-location .frame-link-filename"); + let anchor = matched.querySelector("a.message-location"); toolbox.on("scratchpad-selected", function selected() { toolbox.off("scratchpad-selected", selected); diff --git a/devtools/client/webconsole/test/browser_webconsole_view_source.js b/devtools/client/webconsole/test/browser_webconsole_view_source.js index 2a2e43857dbd..deda99261fab 100644 --- a/devtools/client/webconsole/test/browser_webconsole_view_source.js +++ b/devtools/client/webconsole/test/browser_webconsole_view_source.js @@ -11,6 +11,10 @@ const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" + "test/test-error.html"; +var getItemForAttachment; +var Sources; +var getItemInvoked = false; + add_task(function*() { yield loadTab(TEST_URI); let hud = yield openConsole(null); @@ -26,6 +30,12 @@ add_task(function*() { } EventUtils.sendMouseEvent({ type: "click" }, button, content); + let { panelWin: { DebuggerView } } = yield openDebugger(); + info("debugger opened"); + Sources = DebuggerView.Sources; + hud = yield openConsole(); + info("console opened again"); + let [result] = yield waitForMessages({ webconsole: hud, messages: [{ @@ -37,15 +47,25 @@ add_task(function*() { let msg = [...result.matched][0]; ok(msg, "error message"); - let locationNode = msg.querySelector(".message-location .frame-link-filename"); + let locationNode = msg.querySelector(".message-location"); ok(locationNode, "location node"); let onTabOpen = waitForTab(); + getItemForAttachment = Sources.getItemForAttachment; + Sources.getItemForAttachment = () => { + getItemInvoked = true; + return false; + }; + EventUtils.sendMouseEvent({ type: "click" }, locationNode); let tab = yield onTabOpen; ok(true, "the view source tab was opened in response to clicking " + "the location node"); gBrowser.removeTab(tab); + + ok(getItemInvoked, "custom getItemForAttachment() was invoked"); + Sources.getItemForAttachment = getItemForAttachment; + Sources = getItemForAttachment = null; }); diff --git a/devtools/client/webconsole/test/head.js b/devtools/client/webconsole/test/head.js index 8582becdff24..abe291bc6e9f 100644 --- a/devtools/client/webconsole/test/head.js +++ b/devtools/client/webconsole/test/head.js @@ -1070,16 +1070,16 @@ function waitForMessages(options) { } function checkSource(rule, element) { - let location = getRenderedSource(element); + let location = element.querySelector(".message-location"); if (!location) { return false; } - if (!checkText(rule.source.url, location.url)) { + if (!checkText(rule.source.url, location.getAttribute("title"))) { return false; } - if ("line" in rule.source && location.line === rule.source.line) { + if ("line" in rule.source && location.sourceLine != rule.source.line) { return false; } @@ -1111,10 +1111,10 @@ function waitForMessages(options) { } if (expected.file) { - let url = getRenderedSource(frame).url; - if (!checkText(expected.file, url)) { + let file = frame.querySelector(".message-location").title; + if (!checkText(expected.file, file)) { ok(false, "frame #" + i + " does not match file name: " + - expected.file + " != " + url); + expected.file + " != " + file); displayErrorContext(rule, element); return false; } @@ -1131,7 +1131,7 @@ function waitForMessages(options) { } if (expected.line) { - let line = getRenderedSource(frame).line; + let line = frame.querySelector(".message-location").sourceLine; if (!checkText(expected.line, line)) { ok(false, "frame #" + i + " does not match the line number: " + expected.line + " != " + line); @@ -1317,9 +1317,9 @@ function waitForMessages(options) { function onMessagesAdded(event, newMessages) { for (let msg of newMessages) { let elem = msg.node; - let location = getRenderedSource(elem); - if (location && location.url) { - let url = location.url; + let location = elem.querySelector(".message-location"); + if (location) { + let url = location.title; // Prevent recursion with the browser console and any potential // messages coming from head.js. if (url.indexOf("devtools/client/webconsole/test/head.js") != -1) { @@ -1753,12 +1753,3 @@ function simulateMessageLinkClick(element, expectedLink) { return deferred.promise; } - -function getRenderedSource (root) { - let location = root.querySelector(".message-location .frame-link"); - return location ? { - url: location.getAttribute("data-url"), - line: location.getAttribute("data-line"), - column: location.getAttribute("data-column"), - } : null; -} diff --git a/devtools/client/webconsole/webconsole.js b/devtools/client/webconsole/webconsole.js index 7deeaf2436a0..153dba663617 100644 --- a/devtools/client/webconsole/webconsole.js +++ b/devtools/client/webconsole/webconsole.js @@ -11,9 +11,6 @@ const {Cc, Ci, Cu} = require("chrome"); const {Utils: WebConsoleUtils, CONSOLE_WORKER_IDS} = require("devtools/shared/webconsole/utils"); const { getSourceNames } = require("devtools/client/shared/source-utils"); -const BrowserLoaderModule = {}; -Cu.import("resource://devtools/client/shared/browser-loader.js", BrowserLoaderModule); - const promise = require("promise"); const Debugger = require("Debugger"); const Services = require("Services"); @@ -222,7 +219,6 @@ function WebConsoleFrame(webConsoleOwner) { this.output = new ConsoleOutput(this); - this.unmountMessage = this.unmountMessage.bind(this); this._toggleFilter = this._toggleFilter.bind(this); this.resize = this.resize.bind(this); this._onPanelSelected = this._onPanelSelected.bind(this); @@ -233,15 +229,6 @@ function WebConsoleFrame(webConsoleOwner) { this._outputTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); this._outputTimerInitialized = false; - let require = BrowserLoaderModule.BrowserLoader({ - window: this.window, - useOnlyShared: true - }).require; - - this.React = require("devtools/client/shared/vendor/react"); - this.ReactDOM = require("devtools/client/shared/vendor/react-dom"); - this.FrameView = this.React.createFactory(require("devtools/client/shared/components/frame")); - EventEmitter.decorate(this); } exports.WebConsoleFrame = WebConsoleFrame; @@ -1175,10 +1162,6 @@ WebConsoleFrame.prototype = { if (dupeNode) { this.mergeFilteredMessageNode(dupeNode); - // Even though this node was never rendered, we create the location - // nodes before rendering, so we still have to clean up any - // React components - this.unmountMessage(node); return dupeNode; } @@ -2287,22 +2270,6 @@ WebConsoleFrame.prototype = { } }, - /** - * Cleans up a message via a node that may or may not - * have actually been rendered in the DOM. Currently, only - * cleans up React components. - * - * @param nsIDOMNode node - * The message node you want to clean up. - */ - unmountMessage(node) { - // Select all `.message-location` within this node to ensure we get - // messages of stacktraces, which contain multiple location nodes. - for (let locationNode of node.querySelectorAll(".message-location")) { - this.ReactDOM.unmountComponentAtNode(locationNode); - } - }, - /** * Ensures that the number of message nodes of type category don't exceed that * category's line limit by removing old messages as needed. @@ -2357,8 +2324,6 @@ WebConsoleFrame.prototype = { node._variablesView = null; } - this.unmountMessage(node); - node.remove(); }, @@ -2513,56 +2478,80 @@ WebConsoleFrame.prototype = { * Creates the anchor that displays the textual location of an incoming * message. * - * @param {Object} aLocation + * @param object aLocation * An object containing url, line and column number of the message * source (destructured). - * @return {Element} + * @param string target [optional] + * Tells which tool to open the link with, on click. Supported tools: + * jsdebugger, styleeditor, scratchpad. + * @return nsIDOMNode * The new anchor element, ready to be added to the message node. */ - createLocationNode: function({url, line, column}) { + createLocationNode: function({url, line, column}, target) { if (!url) { url = ""; } - - let fullURL = url.split(" -> ").pop(); let locationNode = this.document.createElementNS(XHTML_NS, "a"); + let filenameNode = this.document.createElementNS(XHTML_NS, "span"); + + // Create the text, which consists of an abbreviated version of the URL + // Scratchpad URLs should not be abbreviated. + let filename; + let fullURL; + let isScratchpad = false; + + if (/^Scratchpad\/\d+$/.test(url)) { + filename = url; + fullURL = url; + isScratchpad = true; + } else { + fullURL = url.split(" -> ").pop(); + filename = getSourceNames(fullURL).short; + } + + filenameNode.className = "filename"; + filenameNode.textContent = ` ${filename}`; + locationNode.appendChild(filenameNode); + + locationNode.href = isScratchpad || !fullURL ? "#" : fullURL; locationNode.draggable = false; - locationNode.className = "message-location devtools-monospace"; + if (target) { + locationNode.target = target; + } + locationNode.setAttribute("title", url); + locationNode.className = "message-location theme-link devtools-monospace"; // Make the location clickable. let onClick = () => { - let category = locationNode.parentNode.category; - let target = category === CATEGORY_CSS ? "styleeditor" : - category === CATEGORY_JS ? "jsdebugger" : - category === CATEGORY_WEBDEV ? "jsdebugger" : - /^Scratchpad\/\d+$/.test(url) ? "scratchpad" : - // If it ends in .js, let's attempt to open in debugger - // anyway, as this falls back to normal view-source. - /\.js$/.test(fullURL) ? "jsdebugger" : null; - - switch (target) { - case "scratchpad": - this.owner.viewSourceInScratchpad(url, line); - return; - case "jsdebugger": - this.owner.viewSourceInDebugger(fullURL, line); - return; - case "styleeditor": - this.owner.viewSourceInStyleEditor(fullURL, line); - return; + let nodeTarget = locationNode.target; + if (nodeTarget == "scratchpad" || isScratchpad) { + this.owner.viewSourceInScratchpad(url, line); + return; + } + + let category = locationNode.parentNode.category; + if (nodeTarget == "styleeditor" || category == CATEGORY_CSS) { + this.owner.viewSourceInStyleEditor(fullURL, line); + } else if (nodeTarget == "jsdebugger" || + category == CATEGORY_JS || category == CATEGORY_WEBDEV) { + this.owner.viewSourceInDebugger(fullURL, line); + } else { + this.owner.viewSource(fullURL, line); } - // No matching tool found; use old school view-source - this.owner.viewSource(fullURL, line); }; - this.ReactDOM.render(this.FrameView({ - frame: { - source: fullURL, - line, - column, - }, - onClick - }), locationNode); + if (fullURL) { + this._addMessageLinkCallback(locationNode, onClick); + } + + if (line) { + let lineNumberNode = this.document.createElementNS(XHTML_NS, "span"); + lineNumberNode.className = "line-number"; + lineNumberNode.textContent = + ":" + line + (column >= 0 ? ":" + column : ""); + locationNode.appendChild(lineNumberNode); + locationNode.sourceLine = line; + } return locationNode; }, @@ -2799,12 +2788,6 @@ WebConsoleFrame.prototype = { this._pruneCategoriesQueue = {}; this.webConsoleClient.clearNetworkRequests(); - // Unmount any currently living frame components in DOM, since - // currently we only clean up messages in `this.removeOutputMessage`, - // via `this.pruneOutputIfNecessary`. - let liveMessages = this.outputNode.querySelectorAll(".message"); - Array.prototype.forEach.call(liveMessages, this.unmountMessage); - if (this._outputTimerInitialized) { this._outputTimerInitialized = false; this._outputTimer.cancel(); @@ -2819,8 +2802,6 @@ WebConsoleFrame.prototype = { this.output.destroy(); this.output = null; - this.React = this.ReactDOM = this.FrameView = null; - if (this._contextMenuHandler) { this._contextMenuHandler.destroy(); this._contextMenuHandler = null; diff --git a/devtools/client/webconsole/webconsole.xul b/devtools/client/webconsole/webconsole.xul index 2d5b5603fa19..131497e8c78c 100644 --- a/devtools/client/webconsole/webconsole.xul +++ b/devtools/client/webconsole/webconsole.xul @@ -11,8 +11,6 @@ type="text/css"?> -