Bug 717749 - Part 1: Add a slow script debug handler service. (r=smaug)
authorShu-yu Guo <shu@rfrn.org>
Tue, 20 May 2014 18:27:24 -0700
changeset 184038 3ea744b43a24a9bf7e666026b9a15ee9d5927c2e
parent 184037 47fbda634a67deb2df667a40121c92c8c205bee0
child 184039 ae9ea46cbe269a706e177f676f6e550182808cde
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)
reviewerssmaug
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 1: Add a slow script debug handler service. (r=smaug)
browser/installer/package-manifest.in
dom/base/SlowScriptDebug.js
dom/base/SlowScriptDebug.manifest
dom/base/moz.build
dom/base/nsGlobalWindow.cpp
dom/base/nsISlowScriptDebug.idl
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -526,16 +526,19 @@
 @BINPATH@/components/NotificationStorage.js
 @BINPATH@/components/NotificationStorage.manifest
 @BINPATH@/components/AlarmsManager.js
 @BINPATH@/components/AlarmsManager.manifest
 @BINPATH@/components/Push.js
 @BINPATH@/components/Push.manifest
 @BINPATH@/components/PushServiceLauncher.js
 
+@BINPATH@/components/SlowScriptDebug.manifest
+@BINPATH@/components/SlowScriptDebug.js
+
 #ifndef RELEASE_BUILD
 @BINPATH@/components/InterAppComm.manifest
 @BINPATH@/components/InterAppCommService.js
 @BINPATH@/components/InterAppConnection.js
 @BINPATH@/components/InterAppMessagePort.js
 #endif
 
 @BINPATH@/components/TCPSocket.js
new file mode 100644
--- /dev/null
+++ b/dom/base/SlowScriptDebug.js
@@ -0,0 +1,21 @@
+/* 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";
+
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+function SlowScriptDebug() { }
+
+SlowScriptDebug.prototype = {
+  classID: Components.ID("{e740ddb4-18b4-4aac-8ae1-9b0f4320769d}"),
+  classDescription: "Slow script debug handler",
+  contractID: "@mozilla.org/dom/slow-script-debug;1",
+  QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsISlowScriptDebug]),
+
+  get activationHandler()   { return this._activationHandler; },
+  set activationHandler(cb) { return this._activationHandler = cb; },
+};
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SlowScriptDebug]);
new file mode 100644
--- /dev/null
+++ b/dom/base/SlowScriptDebug.manifest
@@ -0,0 +1,2 @@
+component {e740ddb4-18b4-4aac-8ae1-9b0f4320769d} SlowScriptDebug.js
+contract @mozilla.org/dom/slow-script-debug;1 {e740ddb4-18b4-4aac-8ae1-9b0f4320769d}
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -8,16 +8,17 @@ TEST_DIRS += ['test']
 
 XPIDL_SOURCES += [
     'nsIConsoleAPIStorage.idl',
     'nsIDOMDOMCursor.idl',
     'nsIDOMDOMRequest.idl',
     'nsIEntropyCollector.idl',
     'nsIScriptChannel.idl',
     'nsISiteSpecificUserAgent.idl',
+    'nsISlowScriptDebug.idl',
 ]
 
 XPIDL_MODULE = 'dom'
 
 EXPORTS += [
     'Crypto.h',
     'nsContentPermissionHelper.h',
     'nsDOMCID.h',
@@ -123,16 +124,18 @@ SOURCES += [
     'nsPluginArray.cpp',
 ]
 
 EXTRA_COMPONENTS += [
     'ConsoleAPI.manifest',
     'ConsoleAPIStorage.js',
     'SiteSpecificUserAgent.js',
     'SiteSpecificUserAgent.manifest',
+    'SlowScriptDebug.js',
+    'SlowScriptDebug.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'DOMRequestHelper.jsm',
     'IndexedDBHelper.jsm',
     'ObjectWrapper.jsm',
 ]
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -30,16 +30,17 @@
 #include "mozilla/dom/WakeLock.h"
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptTimeoutHandler.h"
 #include "nsIController.h"
 #include "nsScriptNameSpaceManager.h"
+#include "nsISlowScriptDebug.h"
 #include "nsWindowMemoryReporter.h"
 #include "WindowNamedPropertiesHandler.h"
 
 // Helper Classes
 #include "nsJSUtils.h"
 #include "jsapi.h"              // for JSAutoRequest
 #include "js/OldDebugAPI.h"     // for JS_ClearWatchPointsForObject
 #include "jswrapper.h"
@@ -10764,37 +10765,53 @@ nsGlobalWindow::ShowSlowScriptDialog()
   nsCOMPtr<nsIPrompt> prompt = do_GetInterface(ds);
   NS_ENSURE_TRUE(prompt, KillSlowScript);
 
   // Check if we should offer the option to debug
   JS::AutoFilename filename;
   unsigned lineno;
   bool hasFrame = JS::DescribeScriptedCaller(cx, &filename, &lineno);
 
-  bool debugPossible = hasFrame && js::CanCallContextDebugHandler(cx);
-#ifdef MOZ_JSDEBUGGER
-  // Get the debugger service if necessary.
-  if (debugPossible) {
-    bool jsds_IsOn = false;
-    const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
-    nsCOMPtr<jsdIExecutionHook> jsdHook;
-    nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
-
-    // Check if there's a user for the debugger service that's 'on' for us
+  // Prioritize the SlowScriptDebug interface over JSD1.
+  nsCOMPtr<nsISlowScriptDebugCallback> debugCallback;
+  bool oldDebugPossible = false;
+
+  if (hasFrame) {
+    const char *debugCID = "@mozilla.org/dom/slow-script-debug;1";
+    nsCOMPtr<nsISlowScriptDebug> debugService = do_GetService(debugCID, &rv);
     if (NS_SUCCEEDED(rv)) {
-      jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
-      jsds->GetIsOn(&jsds_IsOn);
-    }
-
-    // If there is a debug handler registered for this runtime AND
-    // ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
-    // then something useful will be done with our request to debug.
-    debugPossible = ((jsds_IsOn && (jsdHook != nullptr)) || !jsds_IsOn);
-  }
-#endif
+      debugService->GetActivationHandler(getter_AddRefs(debugCallback));
+    }
+
+    if (!debugCallback) {
+      oldDebugPossible = js::CanCallContextDebugHandler(cx);
+#ifdef MOZ_JSDEBUGGER
+      // Get the debugger service if necessary.
+      if (oldDebugPossible) {
+        bool jsds_IsOn = false;
+        const char jsdServiceCtrID[] = "@mozilla.org/js/jsd/debugger-service;1";
+        nsCOMPtr<jsdIExecutionHook> jsdHook;
+        nsCOMPtr<jsdIDebuggerService> jsds = do_GetService(jsdServiceCtrID, &rv);
+
+        // Check if there's a user for the debugger service that's 'on' for us
+        if (NS_SUCCEEDED(rv)) {
+          jsds->GetDebuggerHook(getter_AddRefs(jsdHook));
+          jsds->GetIsOn(&jsds_IsOn);
+        }
+
+        // If there is a debug handler registered for this runtime AND
+        // ((jsd is on AND has a hook) OR (jsd isn't on (something else debugs)))
+        // then something useful will be done with our request to debug.
+        oldDebugPossible = ((jsds_IsOn && (jsdHook != nullptr)) || !jsds_IsOn);
+      }
+#endif
+    }
+  }
+
+  bool showDebugButton = debugCallback || oldDebugPossible;
 
   // Get localizable strings
   nsXPIDLString title, msg, stopButton, waitButton, debugButton, neverShowDlg;
 
   rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                           "KillScriptTitle",
                                           title);
 
@@ -10815,17 +10832,17 @@ nsGlobalWindow::ShowSlowScriptDialog()
   tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                            "DontAskAgain",
                                            neverShowDlg);
   if (NS_FAILED(tmp)) {
     rv = tmp;
   }
 
 
-  if (debugPossible) {
+  if (showDebugButton) {
     tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
                                              "DebugScriptButton",
                                              debugButton);
     if (NS_FAILED(tmp)) {
       rv = tmp;
     }
 
     tmp = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
@@ -10841,17 +10858,17 @@ nsGlobalWindow::ShowSlowScriptDialog()
                                              msg);
     if (NS_FAILED(tmp)) {
       rv = tmp;
     }
   }
 
   // GetStringFromName can return NS_OK and still give nullptr string
   if (NS_FAILED(rv) || !title || !msg || !stopButton || !waitButton ||
-      (!debugButton && debugPossible) || !neverShowDlg) {
+      (!debugButton && showDebugButton) || !neverShowDlg) {
     NS_ERROR("Failed to get localized strings.");
     return ContinueSlowScript;
   }
 
   // Append file and line number information, if available
   if (filename.get()) {
     nsXPIDLString scriptLocation;
     NS_ConvertUTF8toUTF16 filenameUTF16(filename.get());
@@ -10871,35 +10888,42 @@ nsGlobalWindow::ShowSlowScriptDialog()
 
   int32_t buttonPressed = 0; // In case the user exits dialog by clicking X.
   bool neverShowDlgChk = false;
   uint32_t buttonFlags = nsIPrompt::BUTTON_POS_1_DEFAULT +
                          (nsIPrompt::BUTTON_TITLE_IS_STRING *
                           (nsIPrompt::BUTTON_POS_0 + nsIPrompt::BUTTON_POS_1));
 
   // Add a third button if necessary.
-  if (debugPossible)
+  if (showDebugButton)
     buttonFlags += nsIPrompt::BUTTON_TITLE_IS_STRING * nsIPrompt::BUTTON_POS_2;
 
   // Null out the operation callback while we're re-entering JS here.
   JSRuntime* rt = JS_GetRuntime(cx);
   JSInterruptCallback old = JS_SetInterruptCallback(rt, nullptr);
 
   // Open the dialog.
   rv = prompt->ConfirmEx(title, msg, buttonFlags, waitButton, stopButton,
                          debugButton, neverShowDlg, &neverShowDlgChk,
                          &buttonPressed);
 
   JS_SetInterruptCallback(rt, old);
 
   if (NS_SUCCEEDED(rv) && (buttonPressed == 0)) {
     return neverShowDlgChk ? AlwaysContinueSlowScript : ContinueSlowScript;
   }
-  if ((buttonPressed == 2) && debugPossible) {
-    return js_CallContextDebugHandler(cx) ? ContinueSlowScript : KillSlowScript;
+  if (buttonPressed == 2) {
+    if (debugCallback) {
+      rv = debugCallback->HandleSlowScriptDebug(this);
+      return NS_SUCCEEDED(rv) ? ContinueSlowScript : KillSlowScript;
+    }
+
+    if (oldDebugPossible) {
+      return js_CallContextDebugHandler(cx) ? ContinueSlowScript : KillSlowScript;
+    }
   }
   JS_ClearPendingException(cx);
   return KillSlowScript;
 }
 
 uint32_t
 nsGlobalWindow::FindInsertionIndex(IdleObserverHolder* aIdleObserver)
 {
new file mode 100644
--- /dev/null
+++ b/dom/base/nsISlowScriptDebug.idl
@@ -0,0 +1,19 @@
+/* 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/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMWindow;
+
+[scriptable, function, uuid(f7dbb80c-5d1e-4fd9-b55c-a9ffda4a75b1)]
+interface nsISlowScriptDebugCallback : nsISupports
+{
+  void handleSlowScriptDebug(in nsIDOMWindow aWindow);
+};
+
+[scriptable, uuid(f75d4164-3aa7-4395-ba44-a5f95b2e8427)]
+interface nsISlowScriptDebug : nsISupports
+{
+  attribute nsISlowScriptDebugCallback activationHandler;
+};