Bug 1107541 - display eval scripts with a sourceURL pragma correctly in the debugger r=fitzgen
☠☠ backed out by b46dc702c59c ☠ ☠
authorJames Long <longster@gmail.com>
Mon, 08 Dec 2014 15:49:34 -0800
changeset 218828 eb203c9db11de72c79bb452144d3dfc31bd22168
parent 218827 27cf288a8f5c46f447ef669245ac4e3b2f575779
child 218829 fb4c144263b2078ded281217482930e23d7f3053
push id27945
push usercbook@mozilla.com
push dateTue, 09 Dec 2014 12:09:06 +0000
treeherdermozilla-central@47f0671e2c65 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen
bugs1107541
milestone37.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1107541 - display eval scripts with a sourceURL pragma correctly in the debugger r=fitzgen
browser/devtools/debugger/debugger-panes.js
browser/devtools/debugger/test/browser.ini
browser/devtools/debugger/test/browser_dbg_sources-eval-01.js
browser/devtools/debugger/test/browser_dbg_sources-eval-02.js
browser/devtools/debugger/test/browser_dbg_sources-eval.js
browser/devtools/debugger/test/code_script-eval.js
browser/devtools/debugger/test/doc_script-eval.html
toolkit/devtools/moz.build
toolkit/devtools/path.js
toolkit/devtools/server/actors/script.js
--- a/browser/devtools/debugger/debugger-panes.js
+++ b/browser/devtools/debugger/debugger-panes.js
@@ -166,17 +166,17 @@ SourcesView.prototype = Heritage.extend(
     });
   },
 
   _parseUrl: function(aSource) {
     let fullUrl = aSource.url || aSource.introductionUrl;
     let url = fullUrl.split(" -> ").pop();
     let label = aSource.addonPath ? aSource.addonPath : SourceUtils.getSourceLabel(url);
 
-    if (aSource.introductionUrl) {
+    if (!aSource.url && aSource.introductionUrl) {
       label += ' > eval';
     }
 
     return {
       label: label,
       group: aSource.addonID ? aSource.addonID : SourceUtils.getSourceGroup(url),
       unicodeUrl: NetworkHelper.convertToUnicode(unescape(fullUrl))
     };
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -400,17 +400,19 @@ skip-if = e10s && debug
 [browser_dbg_source-maps-02.js]
 skip-if = e10s && debug
 [browser_dbg_source-maps-03.js]
 skip-if = e10s && debug
 [browser_dbg_source-maps-04.js]
 skip-if = e10s # Bug 1093535
 [browser_dbg_sources-cache.js]
 skip-if = e10s && debug
-[browser_dbg_sources-eval.js]
+[browser_dbg_sources-eval-01.js]
+skip-if = e10s && debug
+[browser_dbg_sources-eval-02.js]
 skip-if = e10s && debug
 [browser_dbg_sources-labels.js]
 skip-if = e10s && debug
 [browser_dbg_sources-sorting.js]
 skip-if = e10s && debug
 [browser_dbg_split-console-paused-reload.js]
 skip-if = e10s && debug
 [browser_dbg_stack-01.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_sources-eval-01.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Make sure eval scripts appear in the source list
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_script-eval.html";
+
+function test() {
+  let gTab, gPanel, gDebugger;
+  let gSources, gBreakpoints;
+
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
+    gTab = aTab;
+    gPanel = aPanel;
+    gDebugger = gPanel.panelWin;
+    gSources = gDebugger.DebuggerView.Sources;
+    gBreakpoints = gDebugger.DebuggerController.Breakpoints;
+
+    return Task.spawn(function*() {
+      yield waitForSourceShown(gPanel, "-eval.js");
+      is(gSources.values.length, 1, "Should have 1 source");
+
+      let newSource = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.NEW_SOURCE);
+      callInTab(gTab, "evalSource");
+      yield newSource;
+
+      is(gSources.values.length, 2, "Should have 2 sources");
+
+      yield closeDebuggerAndFinish(gPanel);
+    });
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_sources-eval-02.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Make sure eval scripts with the sourceURL pragma are correctly
+ * displayed
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_script-eval.html";
+
+function test() {
+  let gTab, gPanel, gDebugger;
+  let gSources, gBreakpoints, gEditor;
+
+  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
+    gTab = aTab;
+    gPanel = aPanel;
+    gDebugger = gPanel.panelWin;
+    gSources = gDebugger.DebuggerView.Sources;
+    gBreakpoints = gDebugger.DebuggerController.Breakpoints;
+    gEditor = gDebugger.DebuggerView.editor;
+
+    return Task.spawn(function*() {
+      yield waitForSourceShown(gPanel, "-eval.js");
+      is(gSources.values.length, 1, "Should have 1 source");
+
+      let newSource = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.NEW_SOURCE);
+      callInTab(gTab, "evalSourceWithSourceURL");
+      yield newSource;
+
+      is(gSources.values.length, 2, "Should have 2 sources");
+
+      let item = gSources.getItemForAttachment(e => e.label == "bar.js");
+      ok(item, "Source label is incorrect.");
+      ok(item.attachment.group === 'http://example.com', 'Source group is incorrect');
+
+      let shown = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN);
+      gSources.selectedItem = item;
+      yield shown;
+
+      ok(gEditor.getText().indexOf('bar = function() {') === 0,
+         'Correct source is shown');
+
+      yield closeDebuggerAndFinish(gPanel);
+    });
+  });
+}
deleted file mode 100644
--- a/browser/devtools/debugger/test/browser_dbg_sources-eval.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Make sure eval scripts appear in the source list
- */
-
-const TAB_URL = EXAMPLE_URL + "doc_script-eval.html";
-
-function test() {
-  let gTab, gPanel, gDebugger;
-  let gSources, gBreakpoints;
-
-  initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
-    gTab = aTab;
-    gPanel = aPanel;
-    gDebugger = gPanel.panelWin;
-    gSources = gDebugger.DebuggerView.Sources;
-    gBreakpoints = gDebugger.DebuggerController.Breakpoints;
-
-    waitForSourceShown(gPanel, "-eval.js")
-      .then(run)
-      .then(null, aError => {
-        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
-      });
-  });
-
-  function run() {
-    return Task.spawn(function*() {
-      is(gSources.values.length, 1, "Should have 1 source");
-
-      let newSource = waitForDebuggerEvents(gPanel, gDebugger.EVENTS.NEW_SOURCE);
-      callInTab(gTab, "evalSource");
-      yield newSource;
-
-      is(gSources.values.length, 2, "Should have 2 sources");
-
-      yield closeDebuggerAndFinish(gPanel);
-    });
-  }
-}
--- a/browser/devtools/debugger/test/code_script-eval.js
+++ b/browser/devtools/debugger/test/code_script-eval.js
@@ -1,6 +1,10 @@
 
 var bar;
 
 function evalSource() {
   eval('bar = function() {\nvar x = 5;\n}');
 }
+
+function evalSourceWithSourceURL() {
+   eval('bar = function() {\nvar x = 6;\n} //# sourceURL=bar.js');
+}
--- a/browser/devtools/debugger/test/doc_script-eval.html
+++ b/browser/devtools/debugger/test/doc_script-eval.html
@@ -8,10 +8,9 @@
     <title>Debugger test page</title>
   </head>
 
   <body>
     <button onclick="evalSource()">Click me!</button>
 
     <script type="text/javascript" src="code_script-eval.js"></script>
   </body>
-
 </html>
--- a/toolkit/devtools/moz.build
+++ b/toolkit/devtools/moz.build
@@ -29,16 +29,17 @@ EXTRA_JS_MODULES.devtools += [
     'async-utils.js',
     'content-observer.js',
     'css-color.js',
     'deprecated-sync-thenables.js',
     'DevToolsUtils.js',
     'event-emitter.js',
     'event-parsers.js',
     'output-parser.js',
+    'path.js',
     'touch-events.js',
     'worker-loader.js',
 ]
 
 EXTRA_JS_MODULES.devtools += [
     'Console.jsm',
     'DevToolsUtils.jsm',
     'LayoutHelpers.jsm',
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/path.js
@@ -0,0 +1,36 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+/*
+ * Returns the directory name of the path
+ */
+exports.dirname = path => {
+  return Services.io.newURI(
+    ".", null, Services.io.newURI(path, null, null)).spec;
+}
+
+/*
+ * Join all the arguments together and normalize the resulting URI.
+ * The initial path must be an full URI with a protocol (i.e. http://).
+ */
+exports.joinURI = (initialPath, ...paths) => {
+  let uri;
+
+  try {
+    uri = Services.io.newURI(initialPath, null, null);
+  }
+  catch(e) {
+    return;
+  }
+
+  for(let path of paths) {
+    if(path) {
+      uri = Services.io.newURI(path, null, uri);
+    }
+  }
+
+  return uri.spec;
+}
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -7,16 +7,17 @@
 "use strict";
 
 const Services = require("Services");
 const { Cc, Ci, Cu, components, ChromeWorker } = require("chrome");
 const { ActorPool, getOffsetColumn } = require("devtools/server/actors/common");
 const { DebuggerServer } = require("devtools/server/main");
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 const { dbg_assert, dumpn, update, fetch } = DevToolsUtils;
+const { dirname, joinURI } = require("devtools/toolkit/path");
 const { SourceMapConsumer, SourceMapGenerator } = require("source-map");
 const promise = require("promise");
 const PromiseDebugging = require("PromiseDebugging");
 const Debugger = require("Debugger");
 const xpcInspector = require("xpcInspector");
 const mapURIToAddonID = require("./utils/map-uri-to-addon-id");
 
 const { defer, resolve, reject, all } = require("devtools/toolkit/deprecated-sync-thenables");
@@ -5359,17 +5360,19 @@ ThreadSources.prototype = {
       return MINIFIED_SOURCE_REGEXP.test(aURL);
     }
   },
 
   /**
    * Only to be used when we aren't source mapping.
    */
   _sourceForScript: function (aScript) {
-    let url = getSourceURL(aScript.source);
+    // Don't use getSourceURL because we don't want to consider the
+    // displayURL property if it's an eval source
+    let url = isEvalSource(aScript.source) ? null : aScript.source.url;
     let spec = {
       source: aScript.source
     };
 
     // XXX bug 915433: We can't rely on Debugger.Source.prototype.text
     // if the source is an HTML-embedded <script> tag. Since we don't
     // have an API implemented to detect whether this is the case, we
     // need to be conservative and only treat valid js files as real
@@ -5850,23 +5853,36 @@ function getInnerId(window) {
 
 const symbolProtoToString = typeof Symbol === "function" ? Symbol.prototype.toString : null;
 
 function getSymbolName(symbol) {
   const name = symbolProtoToString.call(symbol).slice("Symbol(".length, -1);
   return name || undefined;
 }
 
-function getSourceURL(source) {
+function isEvalSource(source) {
   let introType = source.introductionType;
   // These are all the sources that are essentially eval-ed (either
   // by calling eval or passing a string to one of these functions).
-  // Current these have a `url` property when the shouldn't, so
-  // forcefully only consider displayURL
-  if (introType === 'eval' ||
-      introType === 'Function' ||
-      introType === 'eventHandler' ||
-      introType === 'setTimeout' ||
-      introType === 'setInterval') {
+  return (introType === 'eval' ||
+          introType === 'Function' ||
+          introType === 'eventHandler' ||
+          introType === 'setTimeout' ||
+          introType === 'setInterval');
+}
+
+function getSourceURL(source) {
+  if(isEvalSource(source)) {
+    // Eval sources have no urls, but they might have a `displayURL`
+    // created with the sourceURL pragma. If the introduction script
+    // is a non-eval script, generate an full absolute URL relative to it.
+
+    if(source.displayURL &&
+       source.introductionScript &&
+       !isEvalSource(source.introductionScript.source)) {
+      return joinURI(dirname(source.introductionScript.source.url),
+                     source.displayURL);
+    }
+
     return source.displayURL;
   }
   return source.url;
 }