Bug 717749 - Part 3: Terminate instead of resume debuggee script on tab closure. (r=past)
authorShu-yu Guo <shu@rfrn.org>
Tue, 20 May 2014 18:27:25 -0700
changeset 184040 94e7e63ae385c87cbb1de52a1641b628b72e8401
parent 184039 ae9ea46cbe269a706e177f676f6e550182808cde
child 184041 1541c3c6e894c3162edff99f6a0114c23620d4be
push id26810
push usercbook@mozilla.com
push dateWed, 21 May 2014 11:46:36 +0000
treeherdermozilla-central@50fb8c4db2fd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspast
bugs717749
milestone32.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 717749 - Part 3: Terminate instead of resume debuggee script on tab closure. (r=past)
browser/devtools/debugger/test/browser.ini
browser/devtools/debugger/test/browser_dbg_terminate-on-tab-close.js
browser/devtools/debugger/test/doc_terminate-on-tab-close.html
toolkit/devtools/server/actors/script.js
toolkit/devtools/server/actors/webbrowser.js
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -73,16 +73,17 @@ support-files =
   doc_recursion-stack.html
   doc_scope-variable.html
   doc_scope-variable-2.html
   doc_scope-variable-3.html
   doc_scope-variable-4.html
   doc_script-switching-01.html
   doc_script-switching-02.html
   doc_step-out.html
+  doc_terminate-on-tab-close.html
   doc_tracing-01.html
   doc_watch-expressions.html
   doc_watch-expression-button.html
   doc_with-frame.html
   head.js
   sjs_random-javascript.sjs
   testactors.js
 
@@ -232,16 +233,17 @@ skip-if = true # Bug 933950 (leaky test)
 [browser_dbg_stack-03.js]
 [browser_dbg_stack-04.js]
 [browser_dbg_stack-05.js]
 [browser_dbg_stack-06.js]
 [browser_dbg_stack-07.js]
 [browser_dbg_step-out.js]
 [browser_dbg_tabactor-01.js]
 [browser_dbg_tabactor-02.js]
+[browser_dbg_terminate-on-tab-close.js]
 [browser_dbg_tracing-01.js]
 [browser_dbg_tracing-02.js]
 [browser_dbg_tracing-03.js]
 [browser_dbg_tracing-04.js]
 [browser_dbg_tracing-05.js]
 [browser_dbg_tracing-06.js]
 [browser_dbg_variables-view-01.js]
 [browser_dbg_variables-view-02.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_terminate-on-tab-close.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that debuggee scripts are terminated on tab closure.
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_terminate-on-tab-close.html";
+
+let gTab, gDebuggee, gDebugger, gPanel;
+
+function test() {
+  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPanel = aPanel;
+    gDebugger = gPanel.panelWin;
+
+    testTerminate();
+  });
+}
+
+function testTerminate() {
+  gDebugger.gThreadClient.addOneTimeListener("paused", () => {
+    resumeDebuggerThenCloseAndFinish(gPanel).then(function () {
+      ok(true, "should not throw after this point");
+    });
+  });
+
+  gDebuggee.debuggerThenThrow();
+}
+
+registerCleanupFunction(function() {
+  gTab = null;
+  gDebuggee = null;
+  gPanel = null;
+  gDebugger = null;
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/doc_terminate-on-tab-close.html
@@ -0,0 +1,20 @@
+<!-- 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 type="text/javascript">
+      function debuggerThenThrow() {
+        debugger;
+        throw "unreachable";
+      }
+    </script>
+  </body>
+
+</html>
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -504,16 +504,17 @@ function ThreadActor(aHooks, aGlobal)
   this._allEventsListener = this._allEventsListener.bind(this);
 
   this._options = {
     useSourceMaps: false
   };
 
   this._gripDepth = 0;
   this._threadLifetimePool = null;
+  this._tabClosed = false;
 }
 
 /**
  * The breakpoint store must be shared across instances of ThreadActor so that
  * page reloads don't blow away all of our breakpoints.
  */
 ThreadActor.breakpointStore = new BreakpointStore();
 
@@ -882,17 +883,20 @@ ThreadActor.prototype = {
           });
       });
 
       this._pushThreadPause();
     } catch(e) {
       reportError(e, "Got an exception during TA__pauseAndRespond: ");
     }
 
-    return undefined;
+    // If the browser tab has been closed, terminate the debuggee script
+    // instead of continuing. Executing JS after the content window is gone is
+    // a bad idea.
+    return this._tabClosed ? null : undefined;
   },
 
   /**
    * Handle resume requests that include a forceCompletion request.
    *
    * @param Object aRequest
    *        The request packet received over the RDP.
    * @returns A response packet.
@@ -1006,17 +1010,17 @@ ThreadActor.prototype = {
    */
   _makeSteppingHooks: function (aStartLocation, steppingType) {
     // Bind these methods and state because some of the hooks are called
     // with 'this' set to the current frame. Rather than repeating the
     // binding in each _makeOnX method, just do it once here and pass it
     // in to each function.
     const steppingHookState = {
       pauseAndRespond: (aFrame, onPacket=(k)=>k) => {
-        this._pauseAndRespond(aFrame, { type: "resumeLimit" }, onPacket);
+        return this._pauseAndRespond(aFrame, { type: "resumeLimit" }, onPacket);
       },
       createValueGrip: this.createValueGrip.bind(this),
       thread: this,
       startFrame: this.youngestFrame,
       startLocation: aStartLocation,
       steppingType: steppingType
     };
 
--- a/toolkit/devtools/server/actors/webbrowser.js
+++ b/toolkit/devtools/server/actors/webbrowser.js
@@ -663,16 +663,22 @@ TabActor.prototype = {
   /**
    * Called by the root actor when the underlying tab is closed.
    */
   exit: function BTA_exit() {
     if (this.exited) {
       return;
     }
 
+    // Tell the thread actor that the tab is closed, so that it may terminate
+    // instead of resuming the debuggee script.
+    if (this._attached) {
+      this.threadActor._tabClosed = true;
+    }
+
     if (this._detach()) {
       this.conn.send({ from: this.actorID,
                        type: "tabDetached" });
     }
 
     this._exited = true;
   },