Make the debugger more resilient in the presence of a bogus source map (bug 906889). r=fitzgen
authorPanos Astithas <past@mozilla.com>
Thu, 24 Oct 2013 11:47:10 +0300
changeset 152067 b50aa73e2a16afdd0e59064a9ec1e8fc633ae181
parent 152066 00a2a5ad182e602238ceb94fbfc8fa32967a02da
child 152068 b9694abda53fd3dd7ff2bb3cc4a58b56a28689d6
push id25515
push usercbook@mozilla.com
push dateFri, 25 Oct 2013 09:21:40 +0000
treeherdermozilla-central@dff937614268 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfitzgen
bugs906889
milestone27.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
Make the debugger more resilient in the presence of a bogus source map (bug 906889). r=fitzgen
browser/devtools/debugger/test/browser.ini
browser/devtools/debugger/test/browser_dbg_source-maps-04.js
browser/devtools/debugger/test/code_math_bogus_map.min.js
browser/devtools/debugger/test/doc_minified_bogus_map.html
toolkit/devtools/DevToolsUtils.jsm
toolkit/devtools/server/actors/script.js
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -11,16 +11,17 @@ support-files =
   code_blackboxing_two.js
   code_function-search-01.js
   code_function-search-02.js
   code_function-search-03.js
   code_location-changes.js
   code_math.js
   code_math.map
   code_math.min.js
+  code_math_bogus_map.min.js
   code_script-switching-01.js
   code_script-switching-02.js
   code_test-editor-mode
   code_ugly.js
   code_ugly-2.js
   code_ugly-3.js
   code_ugly-4.js
   doc_binary_search.html
@@ -38,16 +39,17 @@ support-files =
   doc_function-display-name.html
   doc_function-search.html
   doc_iframes.html
   doc_included-script.html
   doc_inline-debugger-statement.html
   doc_inline-script.html
   doc_large-array-buffer.html
   doc_minified.html
+  doc_minified_bogus_map.html
   doc_pause-exceptions.html
   doc_pretty-print.html
   doc_pretty-print-2.html
   doc_random-javascript.html
   doc_recursion-stack.html
   doc_script-switching-01.html
   doc_script-switching-02.html
   doc_step-out.html
@@ -147,16 +149,17 @@ support-files =
 [browser_dbg_search-sources-03.js]
 [browser_dbg_search-symbols.js]
 [browser_dbg_searchbox-help-popup-01.js]
 [browser_dbg_searchbox-help-popup-02.js]
 [browser_dbg_searchbox-parse.js]
 [browser_dbg_source-maps-01.js]
 [browser_dbg_source-maps-02.js]
 [browser_dbg_source-maps-03.js]
+[browser_dbg_source-maps-04.js]
 [browser_dbg_sources-cache.js]
 [browser_dbg_sources-labels.js]
 [browser_dbg_sources-sorting.js]
 [browser_dbg_stack-01.js]
 [browser_dbg_stack-02.js]
 [browser_dbg_stack-03.js]
 [browser_dbg_stack-04.js]
 [browser_dbg_stack-05.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_source-maps-04.js
@@ -0,0 +1,167 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test that bogus source maps don't break debugging.
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_minified_bogus_map.html";
+const JS_URL = EXAMPLE_URL + "code_math_bogus_map.min.js";
+
+// This test causes an error to be logged in the console, which appears in TBPL
+// logs, so we are disabling that here.
+let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+DevToolsUtils.reportingDisabled = true;
+
+let gPanel, gDebugger, gFrames, gSources, gPrefs, gOptions;
+
+function test() {
+  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+    gPanel = aPanel;
+    gDebugger = gPanel.panelWin;
+    gFrames = gDebugger.DebuggerView.StackFrames;
+    gSources = gDebugger.DebuggerView.Sources;
+    gPrefs = gDebugger.Prefs;
+    gOptions = gDebugger.DebuggerView.Options;
+
+    is(gPrefs.pauseOnExceptions, false,
+      "The pause-on-exceptions pref should be disabled by default.");
+    isnot(gOptions._pauseOnExceptionsItem.getAttribute("checked"), "true",
+      "The pause-on-exceptions menu item should not be checked.");
+
+    waitForSourceShown(gPanel, JS_URL)
+      .then(checkInitialSource)
+      .then(enablePauseOnExceptions)
+      .then(disableIgnoreCaughtExceptions)
+      .then(testSetBreakpoint)
+      .then(reloadPage)
+      .then(testHitBreakpoint)
+      .then(enableIgnoreCaughtExceptions)
+      .then(disablePauseOnExceptions)
+      .then(() => closeDebuggerAndFinish(gPanel))
+      .then(null, aError => {
+        ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+      });
+  });
+}
+
+function checkInitialSource() {
+  isnot(gSources.selectedValue.indexOf(".min.js"), -1,
+    "The debugger should show the minified js file.");
+}
+
+function enablePauseOnExceptions() {
+  let deferred = promise.defer();
+
+  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
+    is(gPrefs.pauseOnExceptions, true,
+      "The pause-on-exceptions pref should now be enabled.");
+
+    ok(true, "Pausing on exceptions was enabled.");
+    deferred.resolve();
+  });
+
+  gOptions._pauseOnExceptionsItem.setAttribute("checked", "true");
+  gOptions._togglePauseOnExceptions();
+
+  return deferred.promise;
+}
+
+function disableIgnoreCaughtExceptions() {
+  let deferred = promise.defer();
+
+  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
+    is(gPrefs.ignoreCaughtExceptions, false,
+      "The ignore-caught-exceptions pref should now be disabled.");
+
+    ok(true, "Ignore caught exceptions was disabled.");
+    deferred.resolve();
+  });
+
+  gOptions._ignoreCaughtExceptionsItem.setAttribute("checked", "false");
+  gOptions._toggleIgnoreCaughtExceptions();
+
+  return deferred.promise;
+}
+
+function testSetBreakpoint() {
+  let deferred = promise.defer();
+
+  gDebugger.gThreadClient.setBreakpoint({ url: JS_URL, line: 3, column: 61 }, aResponse => {
+    ok(!aResponse.error,
+      "Should be able to set a breakpoint in a js file.");
+    ok(!aResponse.actualLocation,
+      "Should be able to set a breakpoint on line 3 and column 61.");
+
+    deferred.resolve();
+  });
+
+  return deferred.promise;
+}
+
+function reloadPage() {
+  let loaded = waitForSourceAndCaret(gPanel, ".js", 3);
+  gDebugger.gClient.activeTab.reload();
+  return loaded.then(() => ok(true, "Page was reloaded and execution resumed."));
+}
+
+function testHitBreakpoint() {
+  let deferred = promise.defer();
+
+  gDebugger.gThreadClient.resume(aResponse => {
+    ok(!aResponse.error, "Shouldn't get an error resuming.");
+    is(aResponse.type, "resumed", "Type should be 'resumed'.");
+
+    waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES).then(() => {
+      is(gFrames.itemCount, 1, "Should have one frame.");
+
+      gDebugger.gThreadClient.resume(deferred.resolve);
+    });
+  });
+
+  return deferred.promise;
+}
+
+function enableIgnoreCaughtExceptions() {
+  let deferred = promise.defer();
+
+  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
+    is(gPrefs.ignoreCaughtExceptions, true,
+      "The ignore-caught-exceptions pref should now be enabled.");
+
+    ok(true, "Ignore caught exceptions was enabled.");
+    deferred.resolve();
+  });
+
+  gOptions._ignoreCaughtExceptionsItem.setAttribute("checked", "true");
+  gOptions._toggleIgnoreCaughtExceptions();
+
+  return deferred.promise;
+}
+
+function disablePauseOnExceptions() {
+  let deferred = promise.defer();
+
+  gDebugger.gThreadClient.addOneTimeListener("resumed", () => {
+    is(gPrefs.pauseOnExceptions, false,
+      "The pause-on-exceptions pref should now be disabled.");
+
+    ok(true, "Pausing on exceptions was disabled.");
+    deferred.resolve();
+  });
+
+  gOptions._pauseOnExceptionsItem.setAttribute("checked", "false");
+  gOptions._togglePauseOnExceptions();
+
+  return deferred.promise;
+}
+
+registerCleanupFunction(function() {
+  gPanel = null;
+  gDebugger = null;
+  gFrames = null;
+  gSources = null;
+  gPrefs = null;
+  gOptions = null;
+  DevToolsUtils.reportingDisabled = false;
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/code_math_bogus_map.min.js
@@ -0,0 +1,4 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+function stopMe(){throw Error("boom");}try{stopMe();var a=1;a=a*2;}catch(e){};
+//# sourceMappingURL=bogus.map
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/doc_minified_bogus_map.html
@@ -0,0 +1,14 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Debugger test page</title>
+  </head>
+
+  <body>
+    <script src="code_math_bogus_map.min.js"></script>
+  </body>
+</html>
--- a/toolkit/devtools/DevToolsUtils.jsm
+++ b/toolkit/devtools/DevToolsUtils.jsm
@@ -18,10 +18,11 @@ Components.classes["@mozilla.org/moz/jss
   .getService(Components.interfaces.mozIJSSubScriptLoader)
   .loadSubScript("resource://gre/modules/devtools/DevToolsUtils.js", this);
 
 this.DevToolsUtils = {
   safeErrorString: safeErrorString,
   reportException: reportException,
   makeInfallible: makeInfallible,
   yieldingEach: yieldingEach,
+  reportingDisabled: false , // Used by tests.
   defineLazyPrototypeGetter: defineLazyPrototypeGetter
 };
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -3951,16 +3951,22 @@ ThreadSources.prototype = {
             line: line,
             column: column
           });
           return {
             url: aSourceURL,
             line: aLine,
             column: aColumn
           };
+        })
+        .then(null, error => {
+          if (!DevToolsUtils.reportingDisabled) {
+            DevToolsUtils.reportException(error);
+          }
+          return { url: null, line: null, column: null };
         });
     }
 
     // No source map
     return resolve({
       url: url,
       line: line,
       column: column