Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Sat, 13 Oct 2012 19:27:51 -0400
changeset 118143 f82c3f3bb6548edf4840ca7cafa7e0a9937dadb4
parent 118142 801ed9c31fd5ea40a797f4641a80ec3452ce3d38 (current diff)
parent 118110 d750d39139d1bad9c744626c3b2852ac9a67463e (diff)
child 118144 632ce3e7d3efa25a537974502ba4e5c2e55feaa0
push id1997
push userakeybl@mozilla.com
push dateMon, 07 Jan 2013 21:25:26 +0000
treeherdermozilla-beta@4baf45cdcf21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone19.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
Merge m-c to inbound.
--- a/browser/devtools/responsivedesign/responsivedesign.jsm
+++ b/browser/devtools/responsivedesign/responsivedesign.jsm
@@ -164,41 +164,46 @@ function ResponsiveUI(aWindow, aTab)
   this.inspectorWasOpen = this.mainWindow.InspectorUI.isInspectorOpen;
 
   try {
     if (Services.prefs.getBoolPref("devtools.responsiveUI.rotate")) {
       this.rotate();
     }
   } catch(e) {}
 
-  switchToFloatingScrollbars(this.tab);
+  if (this._floatingScrollbars)
+    switchToFloatingScrollbars(this.tab);
+
   ResponsiveUIManager.events.emit("on", this.tab, this);
 }
 
 ResponsiveUI.prototype = {
   _transitionsEnabled: true,
+  _floatingScrollbars: false, // See bug 799471
   get transitionsEnabled() this._transitionsEnabled,
   set transitionsEnabled(aValue) {
     this._transitionsEnabled = aValue;
     if (aValue && !this._resizing && this.stack.hasAttribute("responsivemode")) {
       this.stack.removeAttribute("notransition");
     } else if (!aValue) {
       this.stack.setAttribute("notransition", "true");
     }
   },
 
   /**
    * Destroy the nodes. Remove listeners. Reset the style.
    */
   close: function RUI_unload() {
     if (this.closing)
       return;
-    switchToNativeScrollbars(this.tab);
     this.closing = true;
 
+    if (this._floatingScrollbars)
+      switchToNativeScrollbars(this.tab);
+
     this.unCheckMenus();
     // Reset style of the stack.
     let style = "max-width: none;" +
                 "min-width: 0;" +
                 "max-height: none;" +
                 "min-height: 0;";
     this.stack.setAttribute("style", style);
 
--- a/browser/devtools/responsivedesign/test/Makefile.in
+++ b/browser/devtools/responsivedesign/test/Makefile.in
@@ -40,28 +40,19 @@ topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = @relativesrcdir@
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _BROWSER_FILES = \
+		browser_responsiveui.js \
+		browser_responsiveuiaddcustompreset.js \
 		browser_responsiveruleview.js \
 		browser_responsive_cmd.js \
 		browser_responsivecomputedview.js \
 		head.js \
 		helpers.js \
 		$(NULL)
 
-# Disabled on Mac for frequent intermittent failures
-ifneq ($(OS_ARCH), Darwin)
-_BROWSER_FILES += \
-		browser_responsiveui.js \
-		browser_responsiveuiaddcustompreset.js \
-	$(NULL)
-else
-$(warning browser_responsiveui.js is disabled on OS X for intermittent failures. Bug 798772) \
-$(warning browser_responsiveuiaddcustompreset.js is disabled on OS X for intermittent failures. Bugs 798775, 798777)
-endif
-
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/devtools/responsivedesign/test/browser_responsiveui.js
+++ b/browser/devtools/responsivedesign/test/browser_responsiveui.js
@@ -27,17 +27,19 @@ function test() {
     is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
 
     // Menus are correctly updated?
     is(document.getElementById("Tools:ResponsiveUI").getAttribute("checked"), "true", "menus checked");
 
     instance = gBrowser.selectedTab.__responsiveUI;
     ok(instance, "instance of the module is attached to the tab.");
 
-    ensureScrollbarsAreFloating();
+    if (instance._floatingScrollbars) {
+      ensureScrollbarsAreFloating();
+    }
 
     instance.transitionsEnabled = false;
 
     testPresets();
   }
 
   function ensureScrollbarsAreFloating() {
     let body = gBrowser.contentDocument.body;
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -683,16 +683,28 @@ var Scratchpad = {
             file = aFile;
           } else {
             file = Components.classes["@mozilla.org/file/local;1"].
                    createInstance(Components.interfaces.nsILocalFile);
             let filePath = this.getRecentFiles()[aIndex];
             file.initWithPath(filePath);
           }
 
+          if (!file.exists()) {
+            this.notificationBox.appendNotification(
+              this.strings.GetStringFromName("fileNoLongerExists.notification"),
+              "file-no-longer-exists",
+              null,
+              this.notificationBox.PRIORITY_WARNING_HIGH,
+              null);
+
+            this.clearFiles(aIndex, 1);
+            return;
+          }
+
           this.setFilename(file.path);
           this.importFromFile(file, false);
           this.setRecentFile(file);
         }
       }.bind(this));
     }.bind(this);
 
     if (aIndex > -1) {
@@ -825,16 +837,33 @@ var Scratchpad = {
                               this.strings.
                               GetStringFromName("clearRecentMenuItems.label"));
       clearItems.setAttribute("command", "sp-cmd-clearRecentFiles");
       recentFilesPopup.appendChild(clearItems);
     }
   },
 
   /**
+   * Clear a range of files from the list.
+   *
+   * @param integer aIndex
+   *        Index of file in menu to remove.
+   * @param integer aLength
+   *        Number of files from the index 'aIndex' to remove.
+   */
+  clearFiles: function SP_clearFile(aIndex, aLength)
+  {
+    let filePaths = this.getRecentFiles();
+    let branch = Services.prefs.
+                 getBranch("devtools.scratchpad.");
+    filePaths.splice(aIndex, aLength);
+    branch.setCharPref("recentFilePaths", JSON.stringify(filePaths));
+  },
+
+  /**
    * Clear all recent files.
    */
   clearRecentFiles: function SP_clearRecentFiles()
   {
     Services.prefs.clearUserPref("devtools.scratchpad.recentFilePaths");
   },
 
   /**
@@ -854,21 +883,18 @@ var Scratchpad = {
           this.populateRecentFilesMenu();
         }
 
         menu.removeAttribute("hidden");
       }
 
       let filePaths = this.getRecentFiles();
       if (maxRecent < filePaths.length) {
-        let branch = Services.prefs.
-                     getBranch("devtools.scratchpad.");
         let diff = filePaths.length - maxRecent;
-        filePaths.splice(0, diff);
-        branch.setCharPref("recentFilePaths", JSON.stringify(filePaths));
+        this.clearFiles(0, diff);
       }
     }
   },
   /**
    * Save the textbox content to the currently open file.
    *
    * @param function aCallback
    *        Optional function you want to call when file is saved
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug_651942_recent_files.js
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug_651942_recent_files.js
@@ -96,45 +96,71 @@ function testOpenOldestRecent()
 // The "devtools.scratchpad.recentFilesMax"-preference was set to zero (0).
 // This should disable the "Open Recent"-menu by hiding it (this should not
 // remove any files from the list). Test to see if it's been hidden.
 function testHideMenu()
 {
   let menu = gScratchpadWindow.document.getElementById("sp-open_recent-menu");
   ok(menu.hasAttribute("hidden"), "The menu was hidden successfully.");
 
-  Services.prefs.setIntPref("devtools.scratchpad.recentFilesMax", 1);
+  Services.prefs.setIntPref("devtools.scratchpad.recentFilesMax", 2);
 }
 
 // We have set the recentFilesMax-pref to one (1), this enables the feature,
 // removes the two oldest files, rebuilds the menu and removes the
 // "hidden"-attribute from it. Test to see if this works.
 function testChangedMaxRecent()
 {
   let menu = gScratchpadWindow.document.getElementById("sp-open_recent-menu");
   ok(!menu.hasAttribute("hidden"), "The menu is visible. \\o/");
 
   lists.recentFiles04 = gScratchpad.getRecentFiles();
 
-  is(lists.recentFiles04.length, 1,
+  is(lists.recentFiles04.length, 2,
      "Two recent files were successfully removed from the 'recent files'-list");
 
   let doc = gScratchpadWindow.document;
   let popup = doc.getElementById("sp-menu-open_recentPopup");
 
   let menuitemLabel = popup.children[0].getAttribute("label");
   let correctMenuItem = false;
   if (menuitemLabel === lists.recentFiles03[2] &&
-      menuitemLabel === lists.recentFiles04[0]) {
+      menuitemLabel === lists.recentFiles04[1]) {
     correctMenuItem = true;
   }
 
   is(correctMenuItem, true,
      "Two recent files were successfully removed from the 'Open Recent'-menu");
 
+  // We now remove one file from the harddrive and use the recent-menuitem for
+  // it to make sure the user is notified that the file no longer exists.
+  // This is tested in testOpenDeletedFile().
+  gFile02.remove(false);
+  gFile02 = null;
+  gScratchpad.openFile(1);
+}
+
+// By now we should have two recent files stored in the list but one of the
+// files should be missing on the harddrive.
+function testOpenDeletedFile() {
+  let doc = gScratchpadWindow.document;
+  let popup = doc.getElementById("sp-menu-open_recentPopup");
+
+  is(gScratchpad.getRecentFiles().length, 1,
+     "The missing file was successfully removed from the list.");
+  // The number of recent files stored, plus the separator and the
+  // clearRecentMenuItems-item.
+  is(popup.children.length, 3,
+     "The missing file was successfully removed from the menu.");
+  ok(gScratchpad.notificationBox.currentNotification,
+     "The notification was successfully displayed.");
+  is(gScratchpad.notificationBox.currentNotification.label,
+     gScratchpad.strings.GetStringFromName("fileNoLongerExists.notification"),
+     "The notification label is correct.");
+
   gScratchpad.clearRecentFiles();
 }
 
 // We have cleared the last file. Test to see if the last file was removed,
 // the menu is empty and was disabled successfully.
 function testClearedAll()
 {
   let doc = gScratchpadWindow.document;
@@ -255,16 +281,20 @@ var PreferenceObserver = {
         testHideMenu();
         break;
       case 6:
         this.timesFired = 7;
         testChangedMaxRecent();
         break;
       case 7:
         this.timesFired = 8;
+        testOpenDeletedFile();
+        break;
+      case 8:
+        this.timesFired = 9;
         testClearedAll();
         break;
     }
   },
 
   uninit: function PO_uninit () {
     this.branch.removeObserver("", this);
   }
@@ -272,18 +302,17 @@ var PreferenceObserver = {
 
 function test()
 {
   waitForExplicitFinish();
 
   registerCleanupFunction(function () {
     gFile01.remove(false);
     gFile01 = null;
-    gFile02.remove(false);
-    gFile02 = null;
+    // gFile02 was removed earlier.
     gFile03.remove(false);
     gFile03 = null;
     gFile04.remove(false);
     gFile04 = null;
     lists.recentFiles01 = null;
     lists.recentFiles02 = null;
     lists.recentFiles03 = null;
     lists.recentFiles04 = null;
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js
@@ -49,32 +49,44 @@ function checkStyleEditorForSheetAndLine
       }
       return;
     }
 
     ok(true, "Correct Style Sheet is selected in the editor");
 
     // Editor is already loaded, check the current line of caret.
     if (aEditor.sourceEditor) {
+      ok(true, "Editor is already loaded, check the current line of caret");
       executeSoon(function() {
+        ok(true, "Execute soon occured");
+        ok(aEditor.sourceEditor != null, "sourceeditor not null");
+        ok(aEditor.sourceEditor.getCaretPosition() != null, "position not null");
+        ok(aEditor.sourceEditor.getCaretPosition().line != null, "line not null");
         is(aEditor.sourceEditor.getCaretPosition().line, aLine,
            "Correct line is selected");
         if (aCallback) {
           aCallback();
         }
       });
       return;
     }
 
+    ok(true, "Editor is not loaded, waiting for it.");
+    ok(aEditor, "aEditor is defined.");
     // Wait for source editor to be loaded.
     aEditor.addActionListener({
       onAttach: function onAttach() {
+        ok(true, "on attach happened");
         aEditor.removeActionListener(this);
-
+        ok(true, "this removed");
         executeSoon(function() {
+          ok(true, "execute soon");
+          ok(aEditor.sourceEditor != null, "sourceeditor not null");
+          ok(aEditor.sourceEditor.getCaretPosition() != null, "position not null");
+          ok(aEditor.sourceEditor.getCaretPosition().line != null, "line not null");
           is(aEditor.sourceEditor.getCaretPosition().line, aLine,
              "Correct line is selected");
           if (aCallback) {
             aCallback();
           }
         });
       }
     });
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -848,22 +848,22 @@ jsbJslintHappyDesc=Enforce jslint-strict
 # 'jsb <jslintHappy>' parameter, displayed when the user asks for help
 # on what it does.
 jsbJslintHappyManual=When set to true, jslint-stricter mode is enforced
 
 # LOCALIZATION NOTE (jsbBraceStyleDesc) A very short description of the
 # 'jsb <braceStyle>' parameter. This string is designed to be shown
 # in a menu alongside the command name, which is why it should be as short as
 # possible.
-jsbBraceStyleDesc=Select the coding style of braces
+jsbBraceStyleDesc=Collapse, expand, end-expand, expand-strict
 
 # LOCALIZATION NOTE (jsbBraceStyleManual) A fuller description of the
 # 'jsb <braceStyle>' parameter, displayed when the user asks for help
 # on what it does.
-jsbBraceStyleManual=<p class="nowrap">The coding style of braces. Select from one of the following:</p><ul><li>collapse<br/><pre>if (x == 1) {\n  ...\n} else {\n  ...\n}</pre></li><li>expand<br/><pre>if (x == 1)\n{\n  ...\n}\nelse\n{\n  ...\n}</pre></li><li>end-expand<br/><pre>if (x == 1) {\n  ...\n}\nelse {\n  ...\n}</pre></li><li>expand-strict<br/><pre>if (x == 1)\n{\n  return // This option can break scripts\n  {\n    a: 1\n  };\n} else {\n  ...\n}</pre></li></ul>
+jsbBraceStyleManual=The coding style of braces. Either collapse, expand, end-expand or expand-strict
 
 # LOCALIZATION NOTE (jsbNoSpaceBeforeConditionalDesc) A very short description
 # of the 'jsb <noSpaceBeforeConditional>' parameter. This string is designed to
 # be shown in a menu alongside the command name, which is why it should be as
 # short as possible.
 jsbNoSpaceBeforeConditionalDesc=No space before conditional statements
 
 # LOCALIZATION NOTE (jsbUnescapeStringsDesc) A very short description of the
--- a/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/scratchpad.properties
@@ -73,8 +73,12 @@ scratchpadIntro1=/*\n * This is a JavaSc
 
 # LOCALIZATION NOTE  (notification.browserContext): This is the message displayed
 # over the top of the editor when the user has switched to browser context.
 browserContext.notification=This scratchpad executes in the Browser context.
 
 # LOCALIZATION NOTE (help.openDocumentationPage): This returns a localized link with
 # documentation for Scratchpad on MDN.
 help.openDocumentationPage=https://developer.mozilla.org/en/Tools/Scratchpad
+
+# LOCALIZATION NOTE (fileExists.notification): This is the message displayed
+# over the top of the the editor when a file does not exist.
+fileNoLongerExists.notification=This file no longer exists.
--- a/toolkit/devtools/debugger/dbg-client.jsm
+++ b/toolkit/devtools/debugger/dbg-client.jsm
@@ -376,23 +376,26 @@ DebuggerClient.prototype = {
     });
   },
 
   /**
    * Release an object actor.
    *
    * @param string aActor
    *        The actor ID to send the request to.
+   * @param aOnResponse function
+   *        If specified, will be called with the response packet when
+   *        debugging server responds.
    */
-  release: function DC_release(aActor) {
+  release: function DC_release(aActor, aOnResponse) {
     let packet = {
       to: aActor,
       type: "release",
     };
-    this.request(packet);
+    this.request(packet, aOnResponse);
   },
 
   /**
    * Send a request to the debugging server.
    *
    * @param aRequest object
    *        A JSON packet to send to the debugging server.
    * @param aOnResponse function
@@ -782,16 +785,33 @@ ThreadClient.prototype = {
         aOnResponse(aResponse);
         return;
       }
       doSetBreakpoint(this.resume.bind(this));
     }.bind(this));
   },
 
   /**
+   * Release multiple thread-lifetime object actors. If any pause-lifetime
+   * actors are included in the request, a |notReleasable| error will return,
+   * but all the thread-lifetime ones will have been released.
+   *
+   * @param array aActors
+   *        An array with actor IDs to release.
+   */
+  releaseMany: function TC_releaseMany(aActors, aOnResponse) {
+    let packet = {
+      to: this._actor,
+      type: "releaseMany",
+      actors: aActors
+    };
+    this._client.request(packet, aOnResponse);
+  },
+
+  /**
    * Request the loaded scripts for the current thread.
    *
    * @param aOnResponse integer
    *        Called with the thread's response.
    */
   getScripts: function TC_getScripts(aOnResponse) {
     let packet = { to: this._actor, type: "scripts" };
     this._client.request(packet, aOnResponse);
--- a/toolkit/devtools/debugger/server/dbg-browser-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-browser-actors.js
@@ -275,18 +275,18 @@ BrowserTabActor.prototype = {
    */
   addToBreakpointPool: function BTA_addToBreakpointPool(aActor) {
     this.conn.addActor(aActor);
   },
 
   /**
    * Remove the specified breakpint from the default actor pool.
    *
-   * @param string aActor
-   *        The actor ID.
+   * @param BreakpointActor aActor
+   *        The actor object.
    */
   removeFromBreakpointPool: function BTA_removeFromBreakpointPool(aActor) {
     this.conn.removeActor(aActor);
   },
 
   // A constant prefix that will be used to form the actor ID by the server.
   actorPrefix: "tab",
 
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -372,22 +372,29 @@ ThreadActor.prototype = {
   },
 
   onReleaseMany: function TA_onReleaseMany(aRequest) {
     if (!aRequest.actors) {
       return { error: "missingParameter",
                message: "no actors were specified" };
     }
 
+    let res;
     for each (let actorID in aRequest.actors) {
       let actor = this.threadLifetimePool.get(actorID);
-      this.threadLifetimePool.objectActors.delete(actor.obj);
-      this.threadLifetimePool.removeActor(actorID);
+      if (!actor) {
+        if (!res) {
+          res = { error: "notReleasable",
+                  message: "Only thread-lifetime actors can be released." };
+        }
+        continue;
+      }
+      actor.onRelease();
     }
-    return {};
+    return res ? res : {};
   },
 
   /**
    * Handle a protocol request to set a breakpoint.
    */
   onSetBreakpoint: function TA_onSetBreakpoint(aRequest) {
     if (this.state !== "paused") {
       return { error: "wrongState",
@@ -867,23 +874,30 @@ ThreadActor.prototype = {
     if (!this._pausePool) {
       throw "Object grip requested while not paused.";
     }
 
     return this.objectGrip(aValue, this._pausePool);
   },
 
   /**
-   * Create a grip for the given debuggee object with a thread lifetime.
+   * Extend the lifetime of the provided object actor to thread lifetime.
    *
-   * @param aValue Debugger.Object
-   *        The debuggee object value.
+   * @param aActor object
+   *        The object actor.
    */
-  threadObjectGrip: function TA_threadObjectGrip(aValue) {
-    return this.objectGrip(aValue, this.threadLifetimePool);
+  threadObjectGrip: function TA_threadObjectGrip(aActor) {
+    if (!this.threadLifetimePool.objectActors) {
+      this.threadLifetimePool.objectActors = new WeakMap();
+    }
+    // We want to reuse the existing actor ID, so we just remove it from the
+    // current pool's weak map and then let pool.addActor do the rest.
+    aActor.registeredPool.objectActors.delete(aActor.obj);
+    this.threadLifetimePool.addActor(aActor);
+    this.threadLifetimePool.objectActors.set(aActor.obj, aActor);
   },
 
   /**
    * Create a grip for the given string.
    *
    * @param aString String
    *        The string we are creating a grip for.
    * @param aPool ActorPool
@@ -1376,17 +1390,17 @@ update(ObjectActor.prototype, {
              "actor": this.actorID };
   },
 
   /**
    * Releases this actor from the pool.
    */
   release: function OA_release() {
     this.registeredPool.objectActors.delete(this.obj);
-    this.registeredPool.removeActor(this.actorID);
+    this.registeredPool.removeActor(this);
   },
 
   /**
    * Handle a protocol request to provide the names of the properties defined on
    * the object and not its prototype.
    *
    * @param aRequest object
    *        The protocol request object.
@@ -1538,29 +1552,30 @@ update(ObjectActor.prototype, {
   /**
    * Handle a protocol request to promote a pause-lifetime grip to a
    * thread-lifetime grip.
    *
    * @param aRequest object
    *        The protocol request object.
    */
   onThreadGrip: PauseScopedActor.withPaused(function OA_onThreadGrip(aRequest) {
-    return { threadGrip: this.threadActor.threadObjectGrip(this.obj) };
+    this.threadActor.threadObjectGrip(this);
+    return {};
   }),
 
   /**
    * Handle a protocol request to release a thread-lifetime grip.
    *
    * @param aRequest object
    *        The protocol request object.
    */
   onRelease: PauseScopedActor.withPaused(function OA_onRelease(aRequest) {
     if (this.registeredPool !== this.threadActor.threadLifetimePool) {
       return { error: "notReleasable",
-               message: "only thread-lifetime actors can be released." };
+               message: "Only thread-lifetime actors can be released." };
     }
 
     this.release();
     return {};
   }),
 });
 
 ObjectActor.prototype.requestTypes = {
@@ -1792,17 +1807,17 @@ BreakpointActor.prototype = {
    * @param aRequest object
    *        The protocol request object.
    */
   onDelete: function BA_onDelete(aRequest) {
     // Remove from the breakpoint store.
     let scriptBreakpoints = this.threadActor._breakpointStore[this.location.url];
     delete scriptBreakpoints[this.location.line];
     // Remove the actual breakpoint.
-    this.threadActor._hooks.removeFromBreakpointPool(this.actorID);
+    this.threadActor._hooks.removeFromBreakpointPool(this);
     for (let script of this.scripts) {
       script.clearBreakpoint(this);
     }
     this.scripts = null;
 
     return { from: this.actorID };
   }
 };
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -386,19 +386,19 @@ ActorPool.prototype = {
    */
   isEmpty: function AP_isEmpty() {
     return Object.keys(this._actors).length == 0;
   },
 
   /**
    * Remove an actor from the actor pool.
    */
-  removeActor: function AP_remove(aActorID) {
-    delete this._actors[aActorID];
-    delete this._cleanups[aActorID];
+  removeActor: function AP_remove(aActor) {
+    delete this._actors[aActor.actorID];
+    delete this._cleanups[aActor.actorID];
   },
 
   /**
    * Run all cleanups previously registered with addCleanup.
    */
   cleanup: function AP_cleanup() {
     for each (let actor in this._cleanups) {
       actor.disconnect();
--- a/toolkit/devtools/debugger/tests/unit/test_threadlifetime-01.js
+++ b/toolkit/devtools/debugger/tests/unit/test_threadlifetime-01.js
@@ -23,33 +23,27 @@ function run_test()
   do_test_pending();
 }
 
 function test_thread_lifetime()
 {
   gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
     let pauseGrip = aPacket.frame.arguments[0];
 
+    // Create a thread-lifetime actor for this object.
     gClient.request({ to: pauseGrip.actor, type: "threadGrip" }, function(aResponse) {
-      let threadGrip = aResponse.threadGrip;
-
-      do_check_neq(pauseGrip.actor, threadGrip.actor);
-
-      // Create a thread-lifetime actor for this object.
-
+      // Successful promotion won't return an error.
+      do_check_eq(aResponse.error, undefined);
       gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
-        // Now that we've resumed, should get noSuchActor for the
-        // pause grip, but unrecognizePacketType for the thread grip.
-        gClient.request({ to: pauseGrip.actor, type: "bogusRequest" }, function(aResponse) {
-          do_check_eq(aResponse.error, "noSuchActor");
-          gClient.request({ to: threadGrip.actor, type: "bogusRequest"}, function(aResponse) {
-            do_check_eq(aResponse.error, "unrecognizedPacketType");
-            gThreadClient.resume(function() {
-              finishClient(gClient);
-            });
+        // Now that we've resumed, should get unrecognizePacketType for the
+        // promoted grip.
+        gClient.request({ to: pauseGrip.actor, type: "bogusRequest"}, function(aResponse) {
+          do_check_eq(aResponse.error, "unrecognizedPacketType");
+          gThreadClient.resume(function() {
+            finishClient(gClient);
           });
         });
       });
       gThreadClient.resume();
     });
   });
 
   gDebuggee.eval("(" + function() {
--- a/toolkit/devtools/debugger/tests/unit/test_threadlifetime-02.js
+++ b/toolkit/devtools/debugger/tests/unit/test_threadlifetime-02.js
@@ -23,23 +23,24 @@ function run_test()
   do_test_pending();
 }
 
 function test_thread_lifetime()
 {
   gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
     let pauseGrip = aPacket.frame.arguments[0];
 
+    // Create a thread-lifetime actor for this object.
     gClient.request({ to: pauseGrip.actor, type: "threadGrip" }, function(aResponse) {
-      let threadGrip = aResponse.threadGrip;
-      // Create a thread-lifetime actor for this object.
+      // Successful promotion won't return an error.
+      do_check_eq(aResponse.error, undefined);
       gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
         // Now that we've resumed, release the thread-lifetime grip.
-        gClient.request({ to: threadGrip.actor, type: "release" }, function(aResponse) {
-          gClient.request({ to: threadGrip.actor, type: "bogusRequest" }, function(aResponse) {
+        gClient.release(pauseGrip.actor, function(aResponse) {
+          gClient.request({ to: pauseGrip.actor, type: "bogusRequest" }, function(aResponse) {
             do_check_eq(aResponse.error, "noSuchActor");
             gThreadClient.resume(function(aResponse) {
               finishClient(gClient);
             });
           });
         });
       });
       gThreadClient.resume();
--- a/toolkit/devtools/debugger/tests/unit/test_threadlifetime-03.js
+++ b/toolkit/devtools/debugger/tests/unit/test_threadlifetime-03.js
@@ -26,17 +26,21 @@ function run_test()
 function test_thread_lifetime()
 {
   // Get three thread-lifetime grips.
   gThreadClient.addOneTimeListener("paused", function(aEvent, aPacket) {
     aPacket.frame.arguments[0];
     let grips = [];
 
     let handler = function(aResponse) {
-      grips.push(aResponse.threadGrip);
+      if (aResponse.error) {
+        do_check_eq(aResponse.error, '');
+        finishClient(gClient);
+      }
+      grips.push(aResponse.from);
       if (grips.length == 3) {
         test_release_many(grips);
       }
     };
     for (let i = 0; i < 3; i++) {
       gClient.request({ to: aPacket.frame.arguments[i].actor, type: "threadGrip" },
                       handler);
     }
@@ -50,29 +54,29 @@ function test_thread_lifetime()
     ")"
   } + ")()");
 }
 
 function test_release_many(grips)
 {
   // Release the first two grips, leave the third alive.
 
-  let release = [grips[0].actor, grips[1].actor];
+  let release = [grips[0], grips[1]];
 
-  gClient.request({ to: gThreadClient.actor, type: "releaseMany", "actors": release }, function(aResponse) {
+  gThreadClient.releaseMany(release, function(aResponse) {
     // First two actors should return a noSuchActor error, because
     // they're gone now.
-    gClient.request({ to: grips[0].actor, type: "bogusRequest" }, function(aResponse) {
+    gClient.request({ to: grips[0], type: "bogusRequest" }, function(aResponse) {
       do_check_eq(aResponse.error, "noSuchActor");
-      gClient.request({ to: grips[1].actor, type: "bogusRequest" }, function(aResponse) {
+      gClient.request({ to: grips[1], type: "bogusRequest" }, function(aResponse) {
         do_check_eq(aResponse.error, "noSuchActor");
 
         // Last actor should return unrecognizedPacketType, because it's still
         // alive.
-        gClient.request({ to: grips[2].actor, type: "bogusRequest" }, function(aResponse) {
+        gClient.request({ to: grips[2], type: "bogusRequest" }, function(aResponse) {
           do_check_eq(aResponse.error, "unrecognizedPacketType");
           gThreadClient.resume(function() {
             finishClient(gClient);
           });
         });
       });
     });
   });
--- a/toolkit/devtools/debugger/tests/unit/test_threadlifetime-04.js
+++ b/toolkit/devtools/debugger/tests/unit/test_threadlifetime-04.js
@@ -25,22 +25,23 @@ function run_test()
 }
 
 function test_thread_lifetime()
 {
   gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
     let pauseGrip = aPacket.frame.arguments[0];
 
     gClient.request({ to: pauseGrip.actor, type: "threadGrip" }, function (aResponse) {
-      let threadGrip1 = aResponse.threadGrip;
+      // Successful promotion won't return an error.
+      do_check_eq(aResponse.error, undefined);
 
-      do_check_neq(pauseGrip.actor, threadGrip1.actor);
+      let threadGrip1 = aResponse.from;
 
       gClient.request({ to: pauseGrip.actor, type: "threadGrip" }, function (aResponse) {
-        do_check_eq(threadGrip1.actor, aResponse.threadGrip.actor);
+        do_check_eq(threadGrip1, aResponse.from);
         gThreadClient.resume(function() {
           finishClient(gClient);
         });
       });
     });
   });
 
   gDebuggee.eval("(" + function() {
--- a/toolkit/devtools/debugger/tests/unit/test_threadlifetime-05.js
+++ b/toolkit/devtools/debugger/tests/unit/test_threadlifetime-05.js
@@ -1,19 +1,20 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
- * Make sure that an actor will not be reused after a release or
- * releaseMany.
+ * Make sure that releasing a pause-lifetime actorin a releaseMany returns an
+ * error, but releases all the thread-lifetime actors.
  */
 
 var gDebuggee;
 var gClient;
 var gThreadClient;
+var gPauseGrip;
 
 function run_test()
 {
   initTestDebuggerServer();
   gDebuggee = addTestGlobal("test-grips");
   gClient = new DebuggerClient(DebuggerServer.connectPipe());
   gClient.connect(function() {
     attachTestGlobalClientAndResume(gClient, "test-grips", function(aResponse, aThreadClient) {
@@ -22,33 +23,38 @@ function run_test()
     });
   });
   do_test_pending();
 }
 
 function arg_grips(aFrameArgs, aOnResponse) {
   let grips = [];
   let handler = function (aResponse) {
-    grips.push(aResponse.threadGrip);
+    if (aResponse.error) {
+      grips.push(aResponse.error);
+    } else {
+      grips.push(aResponse.from);
+    }
     if (grips.length == aFrameArgs.length) {
       aOnResponse(grips);
     }
   };
   for (let i = 0; i < aFrameArgs.length; i++) {
     gClient.request({ to: aFrameArgs[i].actor, type: "threadGrip" },
                     handler);
   }
 }
 
 function test_thread_lifetime()
 {
-  // Get three thread-lifetime grips.
+  // Get two thread-lifetime grips.
   gThreadClient.addOneTimeListener("paused", function (aEvent, aPacket) {
 
-    let frameArgs = aPacket.frame.arguments;
+    let frameArgs = [ aPacket.frame.arguments[0], aPacket.frame.arguments[1] ];
+    gPauseGrip = aPacket.frame.arguments[2];
     arg_grips(frameArgs, function (aGrips) {
       release_grips(frameArgs, aGrips);
     });
   });
 
   gDebuggee.eval("(" + function() {
     function stopMe(arg1, arg2, arg3) {
       debugger;
@@ -56,25 +62,23 @@ function test_thread_lifetime()
     stopMe({obj: 1}, {obj: 2}, {obj: 3});
     ")"
   } + ")()");
 }
 
 
 function release_grips(aFrameArgs, aThreadGrips)
 {
-  // Release the first grip with release, and the second two with releaseMany...
-
-  gClient.request({ to: aThreadGrips[0].actor, type: "release" }, function (aResponse) {
-    let release = [aThreadGrips[1].actor, aThreadGrips[2].actor];
-    gClient.request({ to: gThreadClient.actor, type: "releaseMany", "actors": release }, function (aResponse) {
-      // Now ask for thread grips again, they should be different.
-      arg_grips(aFrameArgs, function (aNewGrips) {
-        for (let i = 0; i < aNewGrips.length; i++) {
-          do_check_neq(aThreadGrips[i].actor, aNewGrips[i].actor);
-        }
-        gThreadClient.resume(function () {
-          finishClient(gClient);
-        });
+  // Release all actors with releaseMany...
+  let release = [aThreadGrips[0], aThreadGrips[1], gPauseGrip.actor];
+  gThreadClient.releaseMany(release, function (aResponse) {
+    do_check_eq(aResponse.error, "notReleasable");
+    // Now ask for thread grips again, they should not exist.
+    arg_grips(aFrameArgs, function (aNewGrips) {
+      for (let i = 0; i < aNewGrips.length; i++) {
+        do_check_eq(aNewGrips[i], "noSuchActor");
+      }
+      gThreadClient.resume(function () {
+        finishClient(gClient);
       });
     });
   });
 }