Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 30 Apr 2013 21:58:19 -0400
changeset 130429 02aa81c59df65d69dab43cb5e6981067f38c3e23
parent 130428 9f86b0aa679e2acc961eede5047e9eaef3b89058 (current diff)
parent 130403 0c6b9bedfa654a57d7c41e83f9c323e8370274d3 (diff)
child 130430 b20baaf5024edfa34e1e458de448088ef6a2323f
child 130459 07ce0c1d04188854e0007c46a08e1bf4bfec7732
push id27364
push userryanvm@gmail.com
push dateWed, 01 May 2013 02:02:08 +0000
treeherdermozilla-inbound@b20baaf5024e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone23.0a1
first release with
nightly linux32
02aa81c59df6 / 23.0a1 / 20130501031041 / files
nightly linux64
02aa81c59df6 / 23.0a1 / 20130501031041 / files
nightly mac
02aa81c59df6 / 23.0a1 / 20130501031041 / files
nightly win32
02aa81c59df6 / 23.0a1 / 20130501031041 / files
nightly win64
02aa81c59df6 / 23.0a1 / 20130501031041 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c.
--- a/browser/devtools/debugger/test/browser_dbg_bug727429_watch-expressions-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_bug727429_watch-expressions-02.js
@@ -112,111 +112,111 @@ function test()
       gDebuggee.test(); // ermahgerd!!
     });
   }
 
   function test2(callback) {
     waitForWatchExpressions(function() {
       info("Performing test2");
       checkWatchExpressions(undefined,
-                            { type: "object", class: "Proxy" },
+                            { type: "object", class: "Window" },
                             undefined,
                             "sensational",
                             26);
       callback();
     });
     EventUtils.sendMouseEvent({ type: "mousedown" },
       gDebugger.document.getElementById("resume"),
       gDebugger);
   }
 
   function test3(callback) {
     waitForWatchExpressions(function() {
       info("Performing test3");
       checkWatchExpressions({ type: "object", class: "Object" },
-                            { type: "object", class: "Proxy" },
+                            { type: "object", class: "Window" },
                             undefined,
                             "sensational",
                             26);
       callback();
     });
     EventUtils.sendMouseEvent({ type: "mousedown" },
       gDebugger.document.getElementById("resume"),
       gDebugger);
   }
 
   function test4(callback) {
     waitForWatchExpressions(function() {
       info("Performing test4");
       checkWatchExpressions(5,
-                            { type: "object", class: "Proxy" },
+                            { type: "object", class: "Window" },
                             undefined,
                             "sensational",
                             27);
       callback();
     });
     executeSoon(function() {
       gWatch.addExpression("a = 5");
       EventUtils.sendKey("RETURN", gDebugger);
     });
   }
 
   function test5(callback) {
     waitForWatchExpressions(function() {
       info("Performing test5");
       checkWatchExpressions(5,
-                            { type: "object", class: "Proxy" },
+                            { type: "object", class: "Window" },
                             undefined,
                             "sensational",
                             27);
       callback();
     });
     executeSoon(function() {
       gWatch.addExpression("encodeURI(\"\\\")");
       EventUtils.sendKey("RETURN", gDebugger);
     });
   }
 
   function test6(callback) {
     waitForWatchExpressions(function() {
       info("Performing test6");
       checkWatchExpressions(5,
-                            { type: "object", class: "Proxy" },
+                            { type: "object", class: "Window" },
                             undefined,
                             "sensational",
                             27);
       callback();
     })
     executeSoon(function() {
       gWatch.addExpression("decodeURI(\"\\\")");
       EventUtils.sendKey("RETURN", gDebugger);
     });
   }
 
   function test7(callback) {
     waitForWatchExpressions(function() {
       info("Performing test7");
       checkWatchExpressions(5,
-                            { type: "object", class: "Proxy" },
+                            { type: "object", class: "Window" },
                             undefined,
                             "sensational",
                             27);
       callback();
     });
     executeSoon(function() {
       gWatch.addExpression("?");
       EventUtils.sendKey("RETURN", gDebugger);
     });
   }
 
   function test8(callback) {
     waitForWatchExpressions(function() {
       info("Performing test8");
       checkWatchExpressions(5,
-                            { type: "object", class: "Proxy" },
+                            { type: "object", class: "Window" },
                             undefined,
                             "sensational",
                             27);
       callback();
     });
     executeSoon(function() {
       gWatch.addExpression("a");
       EventUtils.sendKey("RETURN", gDebugger);
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
@@ -37,17 +37,17 @@ function testFrameParameters()
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 3,
         "Should have three frames.");
 
       is(localNodes.length, 12,
         "The localScope should contain all the created variable elements.");
 
-      is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Proxy]",
+      is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Window]",
         "Should have the right property value for 'this'.");
 
       is(localNodes[1].querySelector(".value").getAttribute("value"), "[object Object]",
         "Should have the right property value for 'aArg'.");
 
       is(localNodes[2].querySelector(".value").getAttribute("value"), '"beta"',
         "Should have the right property value for 'bArg'.");
 
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
@@ -38,17 +38,17 @@ function testFrameParameters()
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 3,
         "Should have three frames.");
 
       is(localNodes.length + localNonEnums.length, 12,
         "The localScope and localNonEnums should contain all the created variable elements.");
 
-      is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Proxy]",
+      is(localNodes[0].querySelector(".value").getAttribute("value"), "[object Window]",
         "Should have the right property value for 'this'.");
       is(localNodes[8].querySelector(".value").getAttribute("value"), "[object Arguments]",
         "Should have the right property value for 'arguments'.");
       is(localNodes[10].querySelector(".value").getAttribute("value"), "[object Object]",
         "Should have the right property value for 'c'.");
 
 
       let gVars = gDebugger.DebuggerView.Variables;
@@ -112,17 +112,17 @@ function testFrameParameters()
         if (!thisNode._retrieved ||
             !argumentsNode._retrieved ||
             !cNode._retrieved) {
           return;
         }
         window.clearInterval(intervalID);
 
         is(thisNode.target.querySelector(".value")
-           .getAttribute("value"), "[object Proxy]",
+           .getAttribute("value"), "[object Window]",
           "Should have the right property value for 'this'.");
 
         is(thisNode.get("window").target.querySelector(".name")
            .getAttribute("value"), "window",
           "Should have the right property name for 'window'.");
         ok(thisNode.get("window").target.querySelector(".value")
            .getAttribute("value").search(/object/) != -1,
           "'window' should be an object.");
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-09.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-09.js
@@ -45,33 +45,33 @@ function testFrameParameters()
           globalNodes = globalScope.querySelector(".variables-view-element-details").childNodes;
 
       is(gDebugger.DebuggerController.activeThread.state, "paused",
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 3,
         "Should have three frames.");
 
-      is(globalNodes[0].querySelector(".name").getAttribute("value"), "InstallTrigger",
-        "Should have the right property name for |InstallTrigger|.");
-
-      is(globalNodes[0].querySelector(".value").getAttribute("value"), "",
-        "Should have the right property value for |InstallTrigger|.");
-
       is(globalNodes[1].querySelector(".name").getAttribute("value"), "SpecialPowers",
         "Should have the right property name for |SpecialPowers|.");
 
-      is(globalNodes[1].querySelector(".value").getAttribute("value"), "[object Proxy]",
+      is(globalNodes[1].querySelector(".value").getAttribute("value"), "[object Object]",
         "Should have the right property value for |SpecialPowers|.");
 
+      is(globalNodes[3].querySelector(".name").getAttribute("value"), "document",
+        "Should have the right property name for |document|.");
+
+      is(globalNodes[3].querySelector(".value").getAttribute("value"), "[object HTMLDocument]",
+        "Should have the right property value for |document|.");
+
       let len = globalNodes.length - 1;
       is(globalNodes[len].querySelector(".name").getAttribute("value"), "window",
         "Should have the right property name for |window|.");
 
-      is(globalNodes[len].querySelector(".value").getAttribute("value"), "[object Proxy]",
+      is(globalNodes[len].querySelector(".value").getAttribute("value"), "[object Window]",
         "Should have the right property value for |window|.");
 
       resumeAndFinish();
     }}, 0);
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
     content.document.querySelector("button"),
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-10.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-10.js
@@ -56,27 +56,27 @@ function testWithFrame()
       is(scopes.childNodes.length, 5, "Should have 5 variable scopes.");
 
       is(innerNodes[1].querySelector(".name").getAttribute("value"), "one",
         "Should have the right property name for |one|.");
 
       is(innerNodes[1].querySelector(".value").getAttribute("value"), "1",
         "Should have the right property value for |one|.");
 
-      is(globalNodes[0].querySelector(".name").getAttribute("value"), "InstallTrigger",
-        "Should have the right property name for |InstallTrigger|.");
+      is(globalNodes[3].querySelector(".name").getAttribute("value"), "document",
+        "Should have the right property name for |document|.");
 
-      is(globalNodes[0].querySelector(".value").getAttribute("value"), "",
-        "Should have the right property value for |InstallTrigger|.");
+      is(globalNodes[3].querySelector(".value").getAttribute("value"), "[object HTMLDocument]",
+        "Should have the right property value for |document|.");
 
       let len = globalNodes.length - 1;
       is(globalNodes[len].querySelector(".name").getAttribute("value"), "window",
         "Should have the right property name for |window|.");
 
-      is(globalNodes[len].querySelector(".value").getAttribute("value"), "[object Proxy]",
+      is(globalNodes[len].querySelector(".value").getAttribute("value"), "[object Window]",
         "Should have the right property value for |window|.");
 
       resumeAndFinish();
     }}, 0);
   }, false);
 
   EventUtils.sendMouseEvent({ type: "click" },
     content.document.querySelector("button"),
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-edit-watch.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-edit-watch.js
@@ -71,17 +71,17 @@ function testFrameEval() {
       is(scope.get("this")._isContentVisible, true,
         "Should have the right visibility state for 'this'.");
       is(scope.get("this").target.querySelectorAll(".variables-view-delete").length, 1,
         "Should have the one close button visible for 'this'.");
       is(scope.get("this").name, "this",
         "Should have the right name for 'this'.");
       is(scope.get("this").value.type, "object",
         "Should have the right value type for 'this'.");
-      is(scope.get("this").value.class, "Proxy",
+      is(scope.get("this").value.class, "Window",
         "Should have the right value type for 'this'.");
 
       is(scope.get("ermahgerd")._isContentVisible, true,
         "Should have the right visibility state for 'ermahgerd'.");
       is(scope.get("ermahgerd").target.querySelectorAll(".variables-view-delete").length, 1,
         "Should have the one close button visible for 'ermahgerd'.");
       is(scope.get("ermahgerd").name, "ermahgerd",
         "Should have the right name for 'ermahgerd'.");
--- a/browser/metro/base/content/browser.xul
+++ b/browser/metro/base/content/browser.xul
@@ -177,17 +177,17 @@
     <key id="key_selectLastTab" oncommand="BrowserUI.selectTabAtIndex(-1);" key="9" modifiers="accel"/>
   </keyset>
 
   <stack id="stack" flex="1">
     <!-- Page Area -->
     <vbox id="page">
       <vbox id="tray" class="tray-toolbar" visible="true" observes="bcast_windowState" >
         <!-- Tabs -->
-        <hbox id="tabs-container">
+        <hbox id="tabs-container" observes="bcast_windowState">
           <box id="tabs" flex="1"
                 observes="bcast_preciseInput"
                 onselect="BrowserUI.selectTabAndDismiss(this);"
                 onclosetab="BrowserUI.closeTab(this);"/>
           <vbox id="tabs-controls">
             <toolbarbutton id="newtab-button" command="cmd_newTab" label="&newtab.label;"/>
           </vbox>
         </hbox>
@@ -533,27 +533,26 @@
 
     <box id="syncpair-container" class="perm-modal-block" hidden="true">
       <dialog id="syncpair-dialog" class="content-dialog" flex="1">
         <vbox class="prompt-inner">
           <hbox class="prompt-title">
             <description>&sync.pair.title;</description>
           </hbox>
           <vbox id="syncpair-simple" class="syncsetup-page" flex="1">
-            <scrollbox id="sync-message" class="prompt-message" orient="vertical" flex="1">
+            <vbox id="sync-message" class="prompt-message" orient="vertical" flex="1">
               <description class="syncsetup-desc" flex="1">&sync.pair.description;</description>
               <description class="link" flex="1" onclick="SyncPairDevice.close(); WeaveGlue.openTutorial();">&sync.setup.tutorial;</description>
               <separator/>
               <vbox align="center" flex="1">
                 <textbox id="syncpair-code1" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
                 <textbox id="syncpair-code2" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
                 <textbox id="syncpair-code3" class="syncsetup-code" oninput="SyncPairDevice.onTextBoxInput(this);"/>
               </vbox>
-              <separator flex="1"/>
-            </scrollbox>
+            </vbox>
             <hbox class="prompt-buttons" pack="center">
               <button class="prompt-button" oncommand="SyncPairDevice.close();">&sync.setup.cancel;</button>
               <button id="syncpair-connectbutton" class="prompt-button" disabled="true" oncommand="SyncPairDevice.connect();">&sync.setup.connect;</button>
             </hbox>
           </vbox>
         </vbox>
       </dialog>
     </box>
--- a/browser/metro/theme/browser.css
+++ b/browser/metro/theme/browser.css
@@ -109,16 +109,19 @@
 }
 #tabs > .tabs-scrollbox > .scrollbutton-down:active {
   -moz-image-region: rect(72px 152px 120px 108px) !important;
 }
 #tabs > .tabs-scrollbox > .scrollbutton-down[disabled] {
   -moz-image-region: rect(73px 196px 121px 152px) !important;
 }
 
+#tabs-container[viewstate="snapped"] {
+  visibility: hidden;
+}
 
 @-moz-keyframes open-documenttab {
   0% {
     opacity: 0;
     transform: scale(0, 0);
   }
   
   100% {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -825,19 +825,28 @@ toolbar[mode=full] .toolbarbutton-1 > .t
 @navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
   -moz-border-end: none;
 }
 
 @navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon {
   padding: 8px 5px 7px;
 }
 
+@navbarLargeIcons@ #social-toolbar-item {
+  margin-left: 2px;
+  margin-right: 2px;
+}
+
+@navbarLargeIcons@ #social-toolbar-item > .toolbarbutton-1 {
+  padding-left: 0;
+  padding-right: 0;
+}
+
 @navbarLargeIcons@ .toolbarbutton-1:not(:hover):not(:active):not([open]) > .toolbarbutton-menubutton-dropmarker::before,
-@navbarLargeIcons@ > #social-toolbar-item > .toolbarbutton-1:not(:hover) + .social-notification-container:not(:hover) > .toolbarbutton-1::before,
-@navbarLargeIcons@ > #social-toolbar-item > .social-notification-container:not(:hover) + .social-notification-container:not(:hover) > .toolbarbutton-1::before {
+@navbarLargeIcons@ #social-toolbar-item > .toolbarbutton-1:not(:hover):not(:active):not([open]) + .toolbarbutton-1:not(:hover):not(:active):not([open])::before {
   content: "";
   display: -moz-box;
   width: 1px;
   height: 18px;
   -moz-margin-end: -1px;
   background-image: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 18px);
   background-clip: padding-box;
   background-position: center;
@@ -2804,16 +2813,21 @@ toolbarbutton.bookmark-item[dragover="tr
   padding: 2px 5px;
 }
 
 .toolbarbutton-1 > .toolbarbutton-badge-container > .toolbar-icon {
   position: absolute;
   top: 2px;
   right: 2px;
 }
+
+.toolbarbutton-badge-container > .toolbarbutton-icon[label]:not([label=""]) {
+  -moz-margin-end: 0;
+}
+
 .toolbarbutton-badge[badge=""] {
   display: none;
 }
 .toolbarbutton-badge[badge]:not([badge=""])::after {
   /* The |content| property is set in the content stylesheet. */
   font-size: 9px;
   font-weight: bold;
   padding: 0 1px;
@@ -2824,26 +2838,26 @@ toolbarbutton.bookmark-item[dragover="tr
   box-shadow: 0 1px 0 rgba(0,39,121,0.77);
   position: absolute;
   top: 0;
   right: 0;
 }
 
 @navbarLargeIcons@ *|* > .toolbarbutton-badge[badge]:not([badge=""])::after {
   top: 1px;
-  right: 5px;
+  right: 1px;
 }
 
 .toolbarbutton-badge[badge]:not([badge=""]):-moz-locale-dir(rtl)::after {
   left: 0;
   right: auto;
 }
 
 @navbarLargeIcons@ *|* > .toolbarbutton-badge[badge]:not([badge=""]):-moz-locale-dir(rtl)::after {
-  left: 5px;
+  left: 1px;
   right: auto;
 }
 
 .popup-notification-icon[popupid="servicesInstall"] {
   list-style-image: url(chrome://browser/skin/social/services-64.png);
 }
 #servicesInstall-notification-icon {
   list-style-image: url(chrome://browser/skin/social/services-16.png);
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -670,19 +670,18 @@ nsFrameMessageManager::ReceiveMessage(ns
           // Because we want JS messages to have always the same properties,
           // create array even if len == 0.
           aObjectsArray = JS_NewArrayObject(ctx, 0, nullptr);
           if (!aObjectsArray) {
             return NS_ERROR_OUT_OF_MEMORY;
           }
         }
 
-        JS::AutoValueRooter objectsv(ctx);
-        objectsv.set(OBJECT_TO_JSVAL(aObjectsArray));
-        if (!JS_WrapValue(ctx, objectsv.jsval_addr()))
+        JS::Rooted<JS::Value> objectsv(ctx, JS::ObjectValue(*aObjectsArray));
+        if (!JS_WrapValue(ctx, objectsv.address()))
             return NS_ERROR_UNEXPECTED;
 
         JS::Value json = JSVAL_NULL;
         if (aCloneData && aCloneData->mDataLength &&
             !ReadStructuredClone(ctx, *aCloneData, &json)) {
           JS_ClearPendingException(ctx);
           return NS_OK;
         }
@@ -693,17 +692,17 @@ nsFrameMessageManager::ReceiveMessage(ns
         NS_ENSURE_TRUE(jsMessage, NS_ERROR_OUT_OF_MEMORY);
         JS_DefineProperty(ctx, param, "target", targetv, nullptr, nullptr, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "name",
                           STRING_TO_JSVAL(jsMessage), nullptr, nullptr, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "sync",
                           BOOLEAN_TO_JSVAL(aSync), nullptr, nullptr, JSPROP_ENUMERATE);
         JS_DefineProperty(ctx, param, "json", json, nullptr, nullptr, JSPROP_ENUMERATE); // deprecated
         JS_DefineProperty(ctx, param, "data", json, nullptr, nullptr, JSPROP_ENUMERATE);
-        JS_DefineProperty(ctx, param, "objects", objectsv.jsval_value(), nullptr, nullptr, JSPROP_ENUMERATE);
+        JS_DefineProperty(ctx, param, "objects", objectsv, nullptr, nullptr, JSPROP_ENUMERATE);
 
         JS::Value thisValue = JSVAL_VOID;
 
         JS::Value funval;
         if (JS_ObjectIsCallable(ctx, object)) {
           // If the listener is a JS function:
           funval.setObject(*object);
 
@@ -724,33 +723,31 @@ nsFrameMessageManager::ReceiveMessage(ns
               !funval.isObject())
             return NS_ERROR_UNEXPECTED;
 
           // Check if the object is even callable.
           NS_ENSURE_STATE(JS_ObjectIsCallable(ctx, &funval.toObject()));
           thisValue.setObject(*object);
         }
 
-        JS::Value rval = JSVAL_VOID;
-
-        JS::AutoValueRooter argv(ctx);
-        argv.set(OBJECT_TO_JSVAL(param));
+        JS::Rooted<JS::Value> rval(ctx, JSVAL_VOID);
+        JS::Rooted<JS::Value> argv(ctx, JS::ObjectValue(*param));
 
         {
           JSObject* thisObject = JSVAL_TO_OBJECT(thisValue);
 
           JSAutoCompartment tac(ctx, thisObject);
-          if (!JS_WrapValue(ctx, argv.jsval_addr()))
+          if (!JS_WrapValue(ctx, argv.address()))
             return NS_ERROR_UNEXPECTED;
 
           JS_CallFunctionValue(ctx, thisObject,
-                               funval, 1, argv.jsval_addr(), &rval);
+                               funval, 1, argv.address(), rval.address());
           if (aJSONRetVal) {
             nsString json;
-            if (JS_Stringify(ctx, &rval, nullptr, JSVAL_NULL,
+            if (JS_Stringify(ctx, rval.address(), nullptr, JSVAL_NULL,
                              JSONCreator, &json)) {
               aJSONRetVal->AppendElement(json);
             }
           }
         }
       }
     }
   }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -547,18 +547,18 @@ public:
   virtual bool isOuterWindow() {
     return true;
   }
 
   virtual bool finalizeInBackground(JS::Value priv) {
     return false;
   }
 
-  virtual JSString *obj_toString(JSContext *cx,
-                                 JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
+  virtual const char *className(JSContext *cx,
+                                JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
   virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE;
 
   // Fundamental traps
   virtual bool isExtensible(JSObject *proxy) MOZ_OVERRIDE;
   virtual bool preventExtensions(JSContext *cx,
                                  JS::Handle<JSObject*> proxy) MOZ_OVERRIDE;
   virtual bool getPropertyDescriptor(JSContext* cx,
                                      JS::Handle<JSObject*> proxy,
@@ -641,22 +641,22 @@ nsOuterWindowProxy::preventExtensions(JS
                                       JS::Handle<JSObject*> proxy)
 {
   // See above.
   JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                        JSMSG_CANT_CHANGE_EXTENSIBILITY);
   return false;
 }
 
-JSString *
-nsOuterWindowProxy::obj_toString(JSContext *cx, JS::Handle<JSObject*> proxy)
+const char *
+nsOuterWindowProxy::className(JSContext *cx, JS::Handle<JSObject*> proxy)
 {
     MOZ_ASSERT(js::IsProxy(proxy));
 
-    return JS_NewStringCopyZ(cx, "[object Window]");
+    return "Window";
 }
 
 void
 nsOuterWindowProxy::finalize(JSFreeOp *fop, JSObject *proxy)
 {
   nsGlobalWindow* global = GetWindow(proxy);
   if (global) {
     global->ClearWrapper();
@@ -926,28 +926,28 @@ nsOuterWindowProxy::AppendIndexedPropert
 nsOuterWindowProxy
 nsOuterWindowProxy::singleton;
 
 class nsChromeOuterWindowProxy : public nsOuterWindowProxy
 {
 public:
   nsChromeOuterWindowProxy() : nsOuterWindowProxy() {}
 
-  virtual JSString *obj_toString(JSContext *cx, JS::Handle<JSObject*> wrapper);
+  virtual const char *className(JSContext *cx, JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
 
   static nsChromeOuterWindowProxy singleton;
 };
 
-JSString *
-nsChromeOuterWindowProxy::obj_toString(JSContext *cx,
-                                       JS::Handle<JSObject*> proxy)
+const char *
+nsChromeOuterWindowProxy::className(JSContext *cx,
+                                    JS::Handle<JSObject*> proxy)
 {
     MOZ_ASSERT(js::IsProxy(proxy));
 
-    return JS_NewStringCopyZ(cx, "[object ChromeWindow]");
+    return "ChromeWindow";
 }
 
 nsChromeOuterWindowProxy
 nsChromeOuterWindowProxy::singleton;
 
 static JSObject*
 NewOuterWindowProxy(JSContext *cx, JSObject *parent, bool isChrome)
 {
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -1368,17 +1368,17 @@ NativeToString(JSContext* cx, JSObject* 
       if (JS_CallFunctionValue(cx, obj, toString, 0, nullptr,
                                &toStringResult)) {
         str = toStringResult.toString();
       } else {
         str = nullptr;
       }
     } else {
       if (IsDOMProxy(obj)) {
-        str = js::GetProxyHandler(obj)->obj_toString(cx, obj);
+        str = JS_BasicObjectToString(cx, obj);
       } else {
         js::Class* clasp = js::GetObjectClass(obj);
         if (IsDOMClass(clasp)) {
           str = ConcatJSString(cx, "[object ",
                                JS_NewStringCopyZ(cx, clasp->name), "]");
         } else if (IsDOMIfaceAndProtoClass(clasp)) {
           const DOMIfaceAndProtoJSClass* ifaceAndProtoJSClass =
             DOMIfaceAndProtoJSClass::FromJSClass(clasp);
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -6863,23 +6863,24 @@ if (expando) {
   if (found) {
     return true;
   }
 }
 %s
 vp.setUndefined();
 return true;""" % (getIndexedOrExpando, getNamed)
 
-class CGDOMJSProxyHandler_obj_toString(ClassMethod):
+class CGDOMJSProxyHandler_className(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JSContext*', 'cx'), Argument('JS::Handle<JSObject*>', 'proxy')]
-        ClassMethod.__init__(self, "obj_toString", "JSString*", args)
+        ClassMethod.__init__(self, "className", "const char*", args,
+                             virtual=True, override=True)
         self.descriptor = descriptor
     def getBody(self):
-        return "return mozilla::dom::DOMProxyHandler::obj_toString(cx, \"%s\");" % self.descriptor.name
+        return 'return "%s";' % self.descriptor.name
 
 class CGDOMJSProxyHandler_finalizeInBackground(ClassMethod):
     def __init__(self, descriptor):
         args = [Argument('JS::Value', 'priv')]
         ClassMethod.__init__(self, "finalizeInBackground", "bool", args)
         self.descriptor = descriptor
     def getBody(self):
         return ("return false;")
@@ -6969,17 +6970,17 @@ class CGDOMJSProxyHandler(CGClass):
         # because we don't know whether we're in strict mode.
         if (descriptor.operations['IndexedSetter'] or
             descriptor.operations['NamedSetter'] or
             UseHolderForUnforgeable(descriptor)):
             methods.append(CGDOMJSProxyHandler_defineProperty(descriptor))
         methods.extend([CGDOMJSProxyHandler_getOwnPropertyNames(descriptor),
                         CGDOMJSProxyHandler_hasOwn(descriptor),
                         CGDOMJSProxyHandler_get(descriptor),
-                        CGDOMJSProxyHandler_obj_toString(descriptor),
+                        CGDOMJSProxyHandler_className(descriptor),
                         CGDOMJSProxyHandler_finalizeInBackground(descriptor),
                         CGDOMJSProxyHandler_finalize(descriptor),
                         CGDOMJSProxyHandler_getElementIfPresent(descriptor),
                         CGDOMJSProxyHandler_getInstance(),
                         CGDOMJSProxyHandler_delete(descriptor)])
         CGClass.__init__(self, 'DOMProxyHandler',
                          bases=[ClassBase('mozilla::dom::DOMProxyHandler')],
                          constructors=constructors,
--- a/dom/bindings/DOMJSProxyHandler.cpp
+++ b/dom/bindings/DOMJSProxyHandler.cpp
@@ -200,44 +200,16 @@ DOMProxyHandler::has(JSContext* cx, JS::
   JSBool protoHasProp;
   bool ok = JS_HasPropertyById(cx, proto, id, &protoHasProp);
   if (ok) {
     *bp = protoHasProp;
   }
   return ok;
 }
 
-// static
-JSString*
-DOMProxyHandler::obj_toString(JSContext* cx, const char* className)
-{
-  size_t nchars = sizeof("[object ]") - 1 + strlen(className);
-  jschar* chars = static_cast<jschar*>(JS_malloc(cx, (nchars + 1) * sizeof(jschar)));
-  if (!chars) {
-    return NULL;
-  }
-
-  const char* prefix = "[object ";
-  nchars = 0;
-  while ((chars[nchars] = (jschar)*prefix) != 0) {
-    nchars++, prefix++;
-  }
-  while ((chars[nchars] = (jschar)*className) != 0) {
-    nchars++, className++;
-  }
-  chars[nchars++] = ']';
-  chars[nchars] = 0;
-
-  JSString* str = JS_NewUCString(cx, chars, nchars);
-  if (!str) {
-    JS_free(cx, chars);
-  }
-  return str;
-}
-
 bool
 DOMProxyHandler::AppendNamedPropertyIds(JSContext* cx, JSObject* proxy,
                                         nsTArray<nsString>& names,
                                         JS::AutoIdVector& props)
 {
   for (uint32_t i = 0; i < names.Length(); ++i) {
     JS::Value v;
     if (!xpc::NonVoidStringToJsval(cx, names[i], &v)) {
--- a/dom/bindings/DOMJSProxyHandler.h
+++ b/dom/bindings/DOMJSProxyHandler.h
@@ -41,32 +41,29 @@ public:
                             JSPropertyDescriptor* desc, unsigned flags) MOZ_OVERRIDE;
   bool defineProperty(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id,
                       JSPropertyDescriptor* desc) MOZ_OVERRIDE;
   bool delete_(JSContext* cx, JS::Handle<JSObject*> proxy,
                JS::Handle<jsid> id, bool* bp) MOZ_OVERRIDE;
   bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy, JS::AutoIdVector& props) MOZ_OVERRIDE;
   bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp) MOZ_OVERRIDE;
   bool isExtensible(JSObject *proxy) MOZ_OVERRIDE;
-  using js::BaseProxyHandler::obj_toString;
 
   static JSObject* GetExpandoObject(JSObject* obj)
   {
     MOZ_ASSERT(IsDOMProxy(obj), "expected a DOM proxy object");
     JS::Value v = js::GetProxyExtra(obj, JSPROXYSLOT_EXPANDO);
     return v.isUndefined() ? NULL : v.toObjectOrNull();
   }
   static JSObject* GetAndClearExpandoObject(JSObject* obj);
   static JSObject* EnsureExpandoObject(JSContext* cx, JSObject* obj);
 
   const DOMClass& mClass;
 
 protected:
-  static JSString* obj_toString(JSContext* cx, const char* className);
-
   // Append the property names in "names" that don't live on our proto
   // chain to "props"
   bool AppendNamedPropertyIds(JSContext* cx, JSObject* proxy,
                               nsTArray<nsString>& names,
                               JS::AutoIdVector& props);
 };
 
 extern jsid s_length_id;
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -782,22 +782,22 @@ nsJSObjWrapper::NP_SetProperty(NPObject 
   JSBool ok = JS_FALSE;
 
   nsCxPusher pusher;
   pusher.Push(cx);
   JSAutoRequest ar(cx);
   AutoJSExceptionReporter reporter(cx);
   JSAutoCompartment ac(cx, npjsobj->mJSObj);
 
-  JS::Value v = NPVariantToJSVal(npp, cx, value);
-  JS::AutoValueRooter tvr(cx, v);
+  JS::Rooted<JS::Value> v(cx, NPVariantToJSVal(npp, cx, value));
 
   NS_ASSERTION(NPIdentifierIsInt(id) || NPIdentifierIsString(id),
                "id must be either string or int!\n");
-  ok = ::JS_SetPropertyById(cx, npjsobj->mJSObj, NPIdentifierToJSId(id), &v);
+  ok = ::JS_SetPropertyById(cx, npjsobj->mJSObj, NPIdentifierToJSId(id),
+                            v.address());
 
   // return ok == JS_TRUE to quiet down compiler warning, even if
   // return ok is what we really want.
   return ok == JS_TRUE;
 }
 
 // static
 bool
--- a/js/src/builtin/Object.cpp
+++ b/js/src/builtin/Object.cpp
@@ -282,23 +282,21 @@ obj_toSource(JSContext *cx, unsigned arg
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 #endif /* JS_HAS_TOSOURCE */
 
 JSString *
-js::obj_toStringHelper(JSContext *cx, HandleObject obj)
+JS_BasicObjectToString(JSContext *cx, HandleObject obj)
 {
-    if (obj->isProxy())
-        return Proxy::obj_toString(cx, obj);
+    const char *className = JSObject::className(cx, obj);
 
     StringBuffer sb(cx);
-    const char *className = obj->getClass()->name;
     if (!sb.append("[object ") || !sb.appendInflated(className, strlen(className)) ||
         !sb.append("]"))
     {
         return NULL;
     }
     return sb.finishString();
 }
 
@@ -321,17 +319,17 @@ obj_toString(JSContext *cx, unsigned arg
     }
 
     /* Step 3. */
     RootedObject obj(cx, ToObject(cx, args.thisv()));
     if (!obj)
         return false;
 
     /* Steps 4-5. */
-    RawString str = js::obj_toStringHelper(cx, obj);
+    RawString str = JS_BasicObjectToString(cx, obj);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 /* ES5 15.2.4.3. */
 static JSBool
--- a/js/src/builtin/Object.h
+++ b/js/src/builtin/Object.h
@@ -13,14 +13,11 @@ namespace js {
 
 extern const JSFunctionSpec object_methods[];
 extern const JSFunctionSpec object_static_methods[];
 
 /* Object constructor native. Exposed only so the JIT can know its address. */
 extern JSBool
 obj_construct(JSContext *cx, unsigned argc, js::Value *vp);
 
-extern JSString *
-obj_toStringHelper(JSContext *cx, HandleObject obj);
-
 } /* namespace js */
 
 #endif
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -2452,22 +2452,22 @@ ImplicitConvert(JSContext* cx,
       size_t arraySize = elementSize * targetLength;
       AutoPtr<char> intermediate(cx->pod_malloc<char>(arraySize));
       if (!intermediate) {
         JS_ReportAllocationOverflow(cx);
         return false;
       }
 
       for (uint32_t i = 0; i < sourceLength; ++i) {
-        js::AutoValueRooter item(cx);
-        if (!JS_GetElement(cx, valObj, i, item.jsval_addr()))
+        RootedValue item(cx);
+        if (!JS_GetElement(cx, valObj, i, item.address()))
           return false;
 
         char* data = intermediate.get() + elementSize * i;
-        if (!ImplicitConvert(cx, item.jsval_value(), baseType, data, false, NULL))
+        if (!ImplicitConvert(cx, item, baseType, data, false, NULL))
           return false;
       }
 
       memcpy(buffer, intermediate.get(), arraySize);
 
     } else if (!JSVAL_IS_PRIMITIVE(val) &&
                JS_IsArrayBufferObject(valObj)) {
       // Check that array is consistent with type, then
@@ -2534,23 +2534,23 @@ ImplicitConvert(JSContext* cx,
           return false;
         }
 
         JSFlatString *name = JSID_TO_FLAT_STRING(id);
         const FieldInfo* field = StructType::LookupField(cx, targetType, name);
         if (!field)
           return false;
 
-        js::AutoValueRooter prop(cx);
-        if (!JS_GetPropertyById(cx, valObj, id, prop.jsval_addr()))
+        RootedValue prop(cx);
+        if (!JS_GetPropertyById(cx, valObj, id, prop.address()))
           return false;
 
         // Convert the field via ImplicitConvert().
         char* fieldData = intermediate.get() + field->mOffset;
-        if (!ImplicitConvert(cx, prop.jsval_value(), field->mType, fieldData, false, NULL))
+        if (!ImplicitConvert(cx, prop, field->mType, fieldData, false, NULL))
           return false;
 
         ++i;
       }
 
       const FieldInfoHash* fields = StructType::GetFieldInfo(targetType);
       if (i != fields->count()) {
         JS_ReportError(cx, "missing fields");
@@ -2580,18 +2580,18 @@ ExplicitConvert(JSContext* cx, HandleVal
 {
   // If ImplicitConvert succeeds, use that result.
   if (ImplicitConvert(cx, val, targetType, buffer, false, NULL))
     return true;
 
   // If ImplicitConvert failed, and there is no pending exception, then assume
   // hard failure (out of memory, or some other similarly serious condition).
   // We store any pending exception in case we need to re-throw it.
-  js::AutoValueRooter ex(cx);
-  if (!JS_GetPendingException(cx, ex.jsval_addr()))
+  RootedValue ex(cx);
+  if (!JS_GetPendingException(cx, ex.address()))
     return false;
 
   // Otherwise, assume soft failure. Clear the pending exception so that we
   // can throw a different one as required.
   JS_ClearPendingException(cx);
 
   TypeCode type = CType::GetTypeCode(targetType);
 
@@ -2629,17 +2629,17 @@ ExplicitConvert(JSContext* cx, HandleVal
   }
   case TYPE_float32_t:
   case TYPE_float64_t:
   case TYPE_float:
   case TYPE_double:
   case TYPE_array:
   case TYPE_struct:
     // ImplicitConvert is sufficient. Re-throw the exception it generated.
-    JS_SetPendingException(cx, ex.jsval_value());
+    JS_SetPendingException(cx, ex);
     return false;
   case TYPE_void_t:
   case TYPE_function:
     JS_NOT_REACHED("invalid type");
     return false;
   }
   return true;
 }
@@ -4283,19 +4283,19 @@ ArrayType::ConstructData(JSContext* cx,
     if (jsvalToSize(cx, args[0], false, &length)) {
       // Have a length, rather than an object to initialize from.
       convertObject = false;
 
     } else if (!JSVAL_IS_PRIMITIVE(args[0])) {
       // We were given an object with a .length property.
       // This could be a JS array, or a CData array.
       RootedObject arg(cx, &args[0].toObject());
-      js::AutoValueRooter lengthVal(cx);
-      if (!JS_GetProperty(cx, arg, "length", lengthVal.jsval_addr()) ||
-          !jsvalToSize(cx, lengthVal.jsval_value(), false, &length)) {
+      RootedValue lengthVal(cx);
+      if (!JS_GetProperty(cx, arg, "length", lengthVal.address()) ||
+          !jsvalToSize(cx, lengthVal, false, &length)) {
         JS_ReportError(cx, "argument must be an array object or length");
         return JS_FALSE;
       }
 
     } else if (args[0].isString()) {
       // We were given a string. Size the array to the appropriate length,
       // including space for the terminator.
       JSString* sourceString = args[0].toString();
@@ -4639,30 +4639,30 @@ ExtractStructField(JSContext* cx, jsval 
   jsid id;
   if (!JS_NextProperty(cx, iter, &id))
     return NULL;
   if (!JSID_IS_VOID(id)) {
     JS_ReportError(cx, "struct field descriptors must contain one property");
     return NULL;
   }
 
-  js::AutoValueRooter propVal(cx);
-  if (!JS_GetPropertyById(cx, obj, nameid, propVal.jsval_addr()))
+  RootedValue propVal(cx);
+  if (!JS_GetPropertyById(cx, obj, nameid, propVal.address()))
     return NULL;
 
-  if (propVal.value().isPrimitive() ||
-      !CType::IsCType(JSVAL_TO_OBJECT(propVal.jsval_value()))) {
+  if (propVal.isPrimitive() ||
+      !CType::IsCType(propVal.toObjectOrNull())) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
     return NULL;
   }
 
   // Undefined size or zero size struct members are illegal.
   // (Zero-size arrays are legal as struct members in C++, but libffi will
   // choke on a zero-size struct, so we disallow them.)
-  *typeObj = JSVAL_TO_OBJECT(propVal.jsval_value());
+  *typeObj = propVal.toObjectOrNull();
   size_t size;
   if (!CType::GetSafeSize(*typeObj, &size) || size == 0) {
     JS_ReportError(cx, "struct field types must have defined and nonzero size");
     return NULL;
   }
 
   return JSID_TO_FLAT_STRING(nameid);
 }
@@ -4777,22 +4777,22 @@ StructType::DefineInternal(JSContext* cx
 
   // Process the field types.
   size_t structSize, structAlign;
   if (len != 0) {
     structSize = 0;
     structAlign = 0;
 
     for (uint32_t i = 0; i < len; ++i) {
-      js::AutoValueRooter item(cx);
-      if (!JS_GetElement(cx, fieldsObj, i, item.jsval_addr()))
+      RootedValue item(cx);
+      if (!JS_GetElement(cx, fieldsObj, i, item.address()))
         return JS_FALSE;
 
       RootedObject fieldType(cx, NULL);
-      JSFlatString* flat = ExtractStructField(cx, item.jsval_value(), fieldType.address());
+      JSFlatString* flat = ExtractStructField(cx, item, fieldType.address());
       if (!flat)
         return JS_FALSE;
       Rooted<JSStableString*> name(cx, flat->ensureStable(cx));
       if (!name)
         return JS_FALSE;
       fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType);
 
       // Make sure each field name is unique, and add it to the hash.
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -382,20 +382,16 @@ JS::AutoIdArray::trace(JSTracer *trc)
     JS_ASSERT(tag_ == IDARRAY);
     gc::MarkIdRange(trc, idArray->length, idArray->vector, "JSAutoIdArray.idArray");
 }
 
 inline void
 AutoGCRooter::trace(JSTracer *trc)
 {
     switch (tag_) {
-      case JSVAL:
-        MarkValueRoot(trc, &static_cast<AutoValueRooter *>(this)->val, "JS::AutoValueRooter.val");
-        return;
-
       case PARSER:
         static_cast<frontend::Parser<frontend::FullParseHandler> *>(this)->trace(trc);
         return;
 
       case IDARRAY: {
         JSIdArray *ida = static_cast<AutoIdArray *>(this)->idArray;
         MarkIdRange(trc, ida->length, ida->vector, "JS::AutoIdArray.idArray");
         return;
--- a/js/src/ion/CompileInfo.h
+++ b/js/src/ion/CompileInfo.h
@@ -129,17 +129,17 @@ class CompileInfo
     uint32_t argsObjSlot() const {
         JS_ASSERT(hasArguments());
         return 1;
     }
     uint32_t thisSlot() const {
         JS_ASSERT(fun());
         return hasArguments() ? 2 : 1;
     }
-    uint32_t firstActualArgSlot() const {
+    uint32_t firstArgSlot() const {
         return nimplicit_;
     }
     uint32_t argSlotUnchecked(uint32_t i) const {
         // During initialization, some routines need to get at arg
         // slots regardless of how regular argument access is done.
         JS_ASSERT(i < nargs_);
         return nimplicit_ + i;
     }
--- a/js/src/ion/IonAnalysis.cpp
+++ b/js/src/ion/IonAnalysis.cpp
@@ -199,20 +199,21 @@ IsPhiObservable(MPhi *phi, Observability
     CompileInfo &info = phi->block()->info();
     if (info.fun() && slot == info.thisSlot())
         return true;
 
     // If the Phi is one of the formal argument, and we are using an argument
     // object in the function. The phi might be observable after a bailout.
     // For inlined frames this is not needed, as they are captured in the inlineResumePoint.
     if (info.fun() && info.hasArguments()) {
-        uint32_t first = info.firstActualArgSlot();
+        uint32_t first = info.firstArgSlot();
         if (first <= slot && slot - first < info.nargs()) {
-            // If arguments obj aliases formals, then no arguments slots should ever be phis.
-            JS_ASSERT(!info.argsObjAliasesFormals());
+            // If arguments obj aliases formals, then the arg slots will never be used.
+            if (info.argsObjAliasesFormals())
+                return false;
             return true;
         }
     }
     return false;
 }
 
 // Handles cases like:
 //    x is phi(a, x) --> a
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -5404,17 +5404,17 @@ IonBuilder::newPendingLoopHeader(MBasicB
 
         // Unbox the MOsrValue if it is known to be unboxable.
         for (uint32_t i = info().startArgSlot(); i < block->stackDepth(); i++) {
             MPhi *phi = block->getSlot(i)->toPhi();
 
             bool haveValue = false;
             Value existingValue;
             {
-                uint32_t arg = i - info().firstActualArgSlot();
+                uint32_t arg = i - info().firstArgSlot();
                 uint32_t var = i - info().firstLocalSlot();
                 if (arg < info().nargs()) {
                     if (!script()->formalIsAliased(arg)) {
                         haveValue = true;
                         existingValue = fp.unaliasedFormal(arg);
                     }
                 } else if (var < info().nlocals()) {
                     if (!script()->varIsAliased(var)) {
--- a/js/src/ion/MIR.h
+++ b/js/src/ion/MIR.h
@@ -9,16 +9,17 @@
 
 // This file declares everything needed to build actual MIR instructions: the
 // actual opcodes and instructions themselves, the instruction interface, and
 // use chains.
 #include "jscntxt.h"
 #include "jslibmath.h"
 #include "jsinfer.h"
 #include "jsinferinlines.h"
+#include "jstypedarrayinlines.h"
 #include "TypePolicy.h"
 #include "IonAllocPolicy.h"
 #include "InlineList.h"
 #include "MOpcodes.h"
 #include "FixedArityList.h"
 #include "IonMacroAssembler.h"
 #include "Bailouts.h"
 #include "FixedList.h"
--- a/js/src/jit-test/tests/debug/Object-class.js
+++ b/js/src/jit-test/tests/debug/Object-class.js
@@ -1,15 +1,26 @@
 // Basic tests for Debugger.Object.prototype.class.
-var g = newGlobal('new-compartment');
+var g = newGlobal();
 var dbg = new Debugger(g);
 var hits = 0;
+g.eval('function f() { debugger; }');
+
 dbg.onDebuggerStatement = function (frame) {
     var arr = frame.arguments;
     assertEq(arr[0].class, "Object");
     assertEq(arr[1].class, "Array");
     assertEq(arr[2].class, "Function");
     assertEq(arr[3].class, "Date");
-    assertEq(arr[4].class, "Proxy");
+    assertEq(arr[4].class, "Object");
+    assertEq(arr[5].class, "Function");
+    assertEq(arr[6].class, "Date");
     hits++;
 };
-g.eval("(function () { debugger; })(Object.prototype, [], eval, new Date, Proxy.create({}));");
+g.f(Object.prototype, [], eval, new Date,
+    Proxy.create({}), Proxy.createFunction({}, eval), new Proxy(new Date, {}));
 assertEq(hits, 1);
+
+// Debugger.Object.prototype.class should see through cross-compartment
+// wrappers.
+g.eval('f(Object.prototype, [], eval, new Date,\
+          Proxy.create({}), Proxy.createFunction({}, f), new Proxy(new Date, {}));');
+assertEq(hits, 2);
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -107,17 +107,16 @@ class JS_PUBLIC_API(AutoGCRooter) {
      * subclass roots an array of values of the length stored in this field.
      * If negative, meaning is indicated by the corresponding value in the enum
      * below.  Any other negative value indicates some deeper problem such as
      * memory corruption.
      */
     ptrdiff_t tag_;
 
     enum {
-        JSVAL =        -1, /* js::AutoValueRooter */
         VALARRAY =     -2, /* js::AutoValueArray */
         PARSER =       -3, /* js::frontend::Parser */
         SHAPEVECTOR =  -4, /* js::AutoShapeVector */
         IDARRAY =      -6, /* js::AutoIdArray */
         DESCRIPTORS =  -7, /* js::AutoPropDescArrayRooter */
         OBJECT =       -8, /* js::AutoObjectRooter */
         ID =           -9, /* js::AutoIdRooter */
         VALVECTOR =   -10, /* js::AutoValueVector */
@@ -143,71 +142,16 @@ class JS_PUBLIC_API(AutoGCRooter) {
   private:
     AutoGCRooter ** const stackTop;
 
     /* No copy or assignment semantics. */
     AutoGCRooter(AutoGCRooter &ida) MOZ_DELETE;
     void operator=(AutoGCRooter &ida) MOZ_DELETE;
 };
 
-class AutoValueRooter : private AutoGCRooter
-{
-  public:
-    explicit AutoValueRooter(JSContext *cx
-                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, JSVAL), val(NullValue())
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    AutoValueRooter(JSContext *cx, const Value &v
-                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : AutoGCRooter(cx, JSVAL), val(v)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-    }
-
-    /*
-     * If you are looking for Object* overloads, use AutoObjectRooter instead;
-     * rooting Object*s as a js::Value requires discerning whether or not it is
-     * a function object. Also, AutoObjectRooter is smaller.
-     */
-
-    void set(Value v) {
-        JS_ASSERT(tag_ == JSVAL);
-        val = v;
-    }
-
-    const Value &value() const {
-        JS_ASSERT(tag_ == JSVAL);
-        return val;
-    }
-
-    Value *addr() {
-        JS_ASSERT(tag_ == JSVAL);
-        return &val;
-    }
-
-    const Value &jsval_value() const {
-        JS_ASSERT(tag_ == JSVAL);
-        return val;
-    }
-
-    Value *jsval_addr() {
-        JS_ASSERT(tag_ == JSVAL);
-        return &val;
-    }
-
-    friend void AutoGCRooter::trace(JSTracer *trc);
-
-  private:
-    Value val;
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-};
-
 class AutoObjectRooter : private AutoGCRooter
 {
   public:
     AutoObjectRooter(JSContext *cx, JSObject *obj = NULL
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, OBJECT), obj_(obj)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
@@ -5106,17 +5050,16 @@ using JS::TwoByteChars;
 using JS::Latin1CharsZ;
 
 using JS::AutoIdVector;
 using JS::AutoValueVector;
 using JS::AutoScriptVector;
 using JS::AutoIdArray;
 
 using JS::AutoGCRooter;
-using JS::AutoValueRooter;
 using JS::AutoObjectRooter;
 using JS::AutoArrayRooter;
 using JS::AutoVectorRooter;
 using JS::AutoHashMapRooter;
 using JS::AutoHashSetRooter;
 
 using JS::CallArgs;
 using JS::IsAcceptableThis;
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -1010,17 +1010,17 @@ array_toString(JSContext *cx, unsigned a
     if (!obj)
         return false;
 
     RootedValue join(cx, args.calleev());
     if (!JSObject::getProperty(cx, obj, obj, cx->names().join, &join))
         return false;
 
     if (!js_IsCallable(join)) {
-        JSString *str = obj_toStringHelper(cx, obj);
+        JSString *str = JS_BasicObjectToString(cx, obj);
         if (!str)
             return false;
         args.rval().setString(str);
         return true;
     }
 
     InvokeArgsGuard ag;
     if (!cx->stack.pushInvokeArgs(cx, 0, &ag))
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -112,16 +112,19 @@ JS_ObjectToInnerObject(JSContext *cx, JS
 
 /* Requires obj != NULL. */
 extern JS_FRIEND_API(JSObject *)
 JS_ObjectToOuterObject(JSContext *cx, JSObject *obj);
 
 extern JS_FRIEND_API(JSObject *)
 JS_CloneObject(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent);
 
+extern JS_FRIEND_API(JSString *)
+JS_BasicObjectToString(JSContext *cx, JSHandleObject obj);
+
 extern JS_FRIEND_API(JSBool)
 js_GetterOnlyPropertyStub(JSContext *cx, JSHandleObject obj, JSHandleId id, JSBool strict, JSMutableHandleValue vp);
 
 JS_FRIEND_API(void)
 js_ReportOverRecursed(JSContext *maybecx);
 
 #ifdef DEBUG
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1205,16 +1205,26 @@ JSObject::isSealedOrFrozen(JSContext *cx
         }
     }
 
     /* All properties checked out. This object is sealed/frozen. */
     *resultp = true;
     return true;
 }
 
+/* static */
+const char *
+JSObject::className(JSContext *cx, HandleObject obj)
+{
+    if (obj->isProxy())
+        return Proxy::className(cx, obj);
+
+    return obj->getClass()->name;
+}
+
 /*
  * Get the GC kind to use for scripted 'new' on the given class.
  * FIXME bug 547327: estimate the size from the allocation site.
  */
 static inline gc::AllocKind
 NewObjectGCKind(js::Class *clasp)
 {
     if (clasp == &ArrayClass)
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -556,16 +556,19 @@ class JSObject : public js::ObjectImpl
 
     static inline bool isSealed(JSContext *cx, js::HandleObject obj, bool *resultp) {
         return isSealedOrFrozen(cx, obj, SEAL, resultp);
     }
     static inline bool isFrozen(JSContext *cx, js::HandleObject obj, bool *resultp) {
         return isSealedOrFrozen(cx, obj, FREEZE, resultp);
     }
 
+    /* toString support. */
+    static const char *className(JSContext *cx, js::HandleObject obj);
+
     /* Accessors for elements. */
 
     struct MaybeContext {
         js::Allocator *allocator;
         JSContext *context;
 
         MaybeContext(JSContext *cx) : allocator(NULL), context(cx) {}
         MaybeContext(js::Allocator *alloc) : allocator(alloc), context(NULL) {}
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -315,22 +315,20 @@ BaseProxyHandler::call(JSContext *cx, Ha
 
 bool
 BaseProxyHandler::construct(JSContext *cx, HandleObject proxy, const CallArgs &args)
 {
     JS_NOT_REACHED("callable proxies should implement construct trap");
     return false;
 }
 
-JSString *
-BaseProxyHandler::obj_toString(JSContext *cx, HandleObject proxy)
+const char *
+BaseProxyHandler::className(JSContext *cx, HandleObject proxy)
 {
-    return JS_NewStringCopyZ(cx, IsFunctionProxy(proxy)
-                                 ? "[object Function]"
-                                 : "[object Object]");
+    return IsFunctionProxy(proxy) ? "Function" : "Object";
 }
 
 JSString *
 BaseProxyHandler::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
 {
     JS_NOT_REACHED("callable proxies should implement fun_toString trap");
     return NULL;
 }
@@ -519,22 +517,22 @@ DirectProxyHandler::hasInstance(JSContex
 bool
 DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,
                                   JSContext *cx)
 {
     RootedObject obj(cx, GetProxyTargetObject(proxy));
     return ObjectClassIs(obj, classValue, cx);
 }
 
-JSString *
-DirectProxyHandler::obj_toString(JSContext *cx, HandleObject proxy)
+const char *
+DirectProxyHandler::className(JSContext *cx, HandleObject proxy)
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID);
     RootedObject target(cx, GetProxyTargetObject(proxy));
-    return obj_toStringHelper(cx, target);
+    return JSObject::className(cx, target);
 }
 
 JSString *
 DirectProxyHandler::fun_toString(JSContext *cx, HandleObject proxy,
                                  unsigned indent)
 {
     assertEnteredPolicy(cx, proxy, JSID_VOID);
     RootedObject target(cx, GetProxyTargetObject(proxy));
@@ -2657,28 +2655,28 @@ Proxy::hasInstance(JSContext *cx, Handle
 
 bool
 Proxy::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext *cx)
 {
     JS_CHECK_RECURSION(cx, return false);
     return GetProxyHandler(proxy)->objectClassIs(proxy, classValue, cx);
 }
 
-JSString *
-Proxy::obj_toString(JSContext *cx, HandleObject proxy)
+const char *
+Proxy::className(JSContext *cx, HandleObject proxy)
 {
     JS_CHECK_RECURSION(cx, return NULL);
     BaseProxyHandler *handler = GetProxyHandler(proxy);
     AutoEnterPolicy policy(cx, handler, proxy, JS::JSID_VOIDHANDLE,
                            BaseProxyHandler::GET, /* mayThrow = */ false);
     // Do the safe thing if the policy rejects.
     if (!policy.allowed()) {
-        return handler->BaseProxyHandler::obj_toString(cx, proxy);
+        return handler->BaseProxyHandler::className(cx, proxy);
     }
-    return handler->obj_toString(cx, proxy);
+    return handler->className(cx, proxy);
 }
 
 JSString *
 Proxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
 {
     JS_CHECK_RECURSION(cx, return NULL);
     BaseProxyHandler *handler = GetProxyHandler(proxy);
     AutoEnterPolicy policy(cx, handler, proxy, JS::JSID_VOIDHANDLE,
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -130,17 +130,17 @@ class JS_FRIEND_API(BaseProxyHandler)
 
     /* Spidermonkey extensions. */
     virtual bool isExtensible(JSObject *proxy) = 0;
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args);
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args);
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
     virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
     virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
-    virtual JSString *obj_toString(JSContext *cx, HandleObject proxy);
+    virtual const char *className(JSContext *cx, HandleObject proxy);
     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
     virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
     virtual void finalize(JSFreeOp *fop, JSObject *proxy);
     virtual bool getElementIfPresent(JSContext *cx, HandleObject obj, HandleObject receiver,
                                      uint32_t index, MutableHandleValue vp, bool *present);
     virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
 
@@ -194,17 +194,17 @@ class JS_PUBLIC_API(DirectProxyHandler) 
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) MOZ_OVERRIDE;
     virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
                              bool *bp) MOZ_OVERRIDE;
     virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
                                JSContext *cx) MOZ_OVERRIDE;
-    virtual JSString *obj_toString(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
+    virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, HandleObject proxy,
                                    unsigned indent) MOZ_OVERRIDE;
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy,
                                  RegExpGuard *g) MOZ_OVERRIDE;
     virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint,
                               MutableHandleValue vp) MOZ_OVERRIDE;
     virtual JSObject *weakmapKeyDelegate(JSObject *proxy);
 };
@@ -243,17 +243,17 @@ class Proxy
 
     /* Spidermonkey extensions. */
     static bool isExtensible(JSObject *proxy);
     static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args);
     static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args);
     static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
     static bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
     static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
-    static JSString *obj_toString(JSContext *cx, HandleObject proxy);
+    static const char *className(JSContext *cx, HandleObject proxy);
     static JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
     static bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
     static bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
     static bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
 
     static JSObject * const LazyProto;
 };
 
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -521,29 +521,21 @@ CrossCompartmentWrapper::hasInstance(JSC
                                      bool *bp)
 {
     AutoCompartment call(cx, wrappedObject(wrapper));
     if (!cx->compartment->wrap(cx, v))
         return false;
     return Wrapper::hasInstance(cx, wrapper, v, bp);
 }
 
-JSString *
-CrossCompartmentWrapper::obj_toString(JSContext *cx, HandleObject wrapper)
+const char *
+CrossCompartmentWrapper::className(JSContext *cx, HandleObject wrapper)
 {
-    RootedString str(cx);
-    {
-        AutoCompartment call(cx, wrappedObject(wrapper));
-        str = Wrapper::obj_toString(cx, wrapper);
-        if (!str)
-            return NULL;
-    }
-    if (!cx->compartment->wrap(cx, str.address()))
-        return NULL;
-    return str;
+    AutoCompartment call(cx, wrappedObject(wrapper));
+    return Wrapper::className(cx, wrapper);
 }
 
 JSString *
 CrossCompartmentWrapper::fun_toString(JSContext *cx, HandleObject wrapper, unsigned indent)
 {
     RootedString str(cx);
     {
         AutoCompartment call(cx, wrappedObject(wrapper));
@@ -784,20 +776,20 @@ DeadObjectProxy::hasInstance(JSContext *
 
 bool
 DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEAD_OBJECT);
     return false;
 }
 
-JSString *
-DeadObjectProxy::obj_toString(JSContext *cx, HandleObject wrapper)
+const char *
+DeadObjectProxy::className(JSContext *cx, HandleObject wrapper)
 {
-    return JS_NewStringCopyZ(cx, "[object DeadObject]");
+    return "DeadObject";
 }
 
 JSString *
 DeadObjectProxy::fun_toString(JSContext *cx, HandleObject proxy, unsigned indent)
 {
     return NULL;
 }
 
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -107,17 +107,17 @@ class JS_FRIEND_API(CrossCompartmentWrap
     /* Spidermonkey extensions. */
     virtual bool isExtensible(JSObject *proxy) MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, HandleObject wrapper, const CallArgs &args) MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) MOZ_OVERRIDE;
     virtual bool hasInstance(JSContext *cx, HandleObject wrapper, MutableHandleValue v,
                              bool *bp) MOZ_OVERRIDE;
-    virtual JSString *obj_toString(JSContext *cx, HandleObject wrapper) MOZ_OVERRIDE;
+    virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, HandleObject wrapper,
                                    unsigned indent) MOZ_OVERRIDE;
     virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE;
     virtual bool defaultValue(JSContext *cx, HandleObject wrapper, JSType hint,
                               MutableHandleValue vp) MOZ_OVERRIDE;
     virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
 
     static CrossCompartmentWrapper singleton;
@@ -185,25 +185,30 @@ class JS_FRIEND_API(DeadObjectProxy) : p
     virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) MOZ_OVERRIDE;
 
     /* Spidermonkey extensions. */
     virtual bool isExtensible(JSObject *proxy) MOZ_OVERRIDE;
     virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
     virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
     virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                             CallArgs args) MOZ_OVERRIDE;
-    virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v, bool *bp);
-    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext *cx);
-    virtual JSString *obj_toString(JSContext *cx, HandleObject proxy);
-    virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent);
-    virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g);
-    virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp);
+    virtual bool hasInstance(JSContext *cx, HandleObject proxy, MutableHandleValue v,
+                             bool *bp) MOZ_OVERRIDE;
+    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
+                               JSContext *cx) MOZ_OVERRIDE;
+    virtual const char *className(JSContext *cx, HandleObject proxy) MOZ_OVERRIDE;
+    virtual JSString *fun_toString(JSContext *cx, HandleObject proxy, unsigned indent) MOZ_OVERRIDE;
+    virtual bool regexp_toShared(JSContext *cx, HandleObject proxy, RegExpGuard *g) MOZ_OVERRIDE;
+    virtual bool defaultValue(JSContext *cx, HandleObject obj, JSType hint,
+                              MutableHandleValue vp) MOZ_OVERRIDE;
     virtual bool getElementIfPresent(JSContext *cx, HandleObject obj, HandleObject receiver,
-                                     uint32_t index, MutableHandleValue vp, bool *present);
-    virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop);
+                                     uint32_t index, MutableHandleValue vp,
+                                     bool *present) MOZ_OVERRIDE;
+    virtual bool getPrototypeOf(JSContext *cx, HandleObject proxy,
+                                MutableHandleObject protop) MOZ_OVERRIDE;
 
     static DeadObjectProxy singleton;
 };
 
 extern JSObject *
 TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
                          HandleObject wrappedProto, HandleObject parent,
                          unsigned flags);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4168,18 +4168,18 @@ DebuggerObject_getProto(JSContext *cx, u
     args.rval().set(protov);
     return true;
 }
 
 static JSBool
 DebuggerObject_getClass(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get class", args, refobj);
-    const char *s = refobj->getClass()->name;
-    JSAtom *str = Atomize(cx, s, strlen(s));
+    const char *className = JSObject::className(cx, refobj);
+    JSAtom *str = Atomize(cx, className, strlen(className));
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static JSBool
 DebuggerObject_getCallable(JSContext *cx, unsigned argc, Value *vp)
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -1131,33 +1131,33 @@ XPCConvert::ConstructException(nsresult 
 
     if (sz)
         JS_smprintf_free(sz);
     return res;
 }
 
 /********************************/
 
-class AutoExceptionRestorer
+class MOZ_STACK_CLASS AutoExceptionRestorer
 {
 public:
-    AutoExceptionRestorer(JSContext *cx, jsval v)
+    AutoExceptionRestorer(JSContext *cx, Value v)
         : mContext(cx), tvr(cx, v)
     {
         JS_ClearPendingException(mContext);
     }
 
     ~AutoExceptionRestorer()
     {
-        JS_SetPendingException(mContext, tvr.jsval_value());
+        JS_SetPendingException(mContext, tvr);
     }
 
 private:
     JSContext * const mContext;
-    AutoValueRooter tvr;
+    RootedValue tvr;
 };
 
 // static
 nsresult
 XPCConvert::JSValToXPCException(XPCCallContext& ccx,
                                 jsval sArg,
                                 const char* ifaceName,
                                 const char* methodName,
--- a/startupcache/test/TestStartupCache.cpp
+++ b/startupcache/test/TestStartupCache.cpp
@@ -20,16 +20,18 @@
 #include "nsStringAPI.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsITelemetry.h"
 #include "nsIXPConnect.h"
 #include "jsapi.h"
 #include "prio.h"
 
+using namespace JS;
+
 namespace mozilla {
 namespace scache {
 
 NS_IMPORT nsresult
 NewObjectInputStreamFromBuffer(char* buffer, uint32_t len, 
                                nsIObjectInputStream** stream);
 
 // We can't retrieve the wrapped stream from the objectOutputStream later,
@@ -392,56 +394,55 @@ TestEarlyShutdown() {
   } else {
     fail("PutBuffer gave an unexpected failure, expected NOT_AVAILABLE");
     return rv;
   }
  
   return NS_OK;
 }
 
-bool
+static bool
 GetHistogramCounts(const char *testmsg, const nsACString &histogram_id,
-                   JSContext *cx, JS::Value *counts)
+                   JSContext *cx, MutableHandle<Value> counts)
 {
   nsCOMPtr<nsITelemetry> telemetry = do_GetService("@mozilla.org/base/telemetry;1");
-  JS::AutoValueRooter h(cx);
-  nsresult trv = telemetry->GetHistogramById(histogram_id, cx, h.addr());
+  Rooted<Value> h(cx);
+  nsresult trv = telemetry->GetHistogramById(histogram_id, cx, h.address());
   if (NS_FAILED(trv)) {
     fail("%s: couldn't get histogram %s", testmsg, ToNewCString(histogram_id));
     return false;
   }
   passed(testmsg);
 
-  JS::AutoValueRooter snapshot_val(cx);
+  Rooted<Value> snapshot_val(cx);
   JSFunction *snapshot_fn = NULL;
-  JS::AutoValueRooter ss(cx);
-  return (JS_GetProperty(cx, JSVAL_TO_OBJECT(h.value()), "snapshot",
-                         snapshot_val.addr())
-          && (snapshot_fn = JS_ValueToFunction(cx, snapshot_val.value()))
-          && JS::Call(cx, JSVAL_TO_OBJECT(h.value()),
-                      snapshot_fn, 0, NULL, ss.addr())
-          && JS_GetProperty(cx, JSVAL_TO_OBJECT(ss.value()),
-                            "counts", counts));
+  Rooted<Value> ss(cx);
+  return (JS_GetProperty(cx, JSVAL_TO_OBJECT(h), "snapshot",
+                         snapshot_val.address())
+          && (snapshot_fn = JS_ValueToFunction(cx, snapshot_val))
+          && JS::Call(cx, JSVAL_TO_OBJECT(h),
+                      snapshot_fn, 0, NULL, ss.address())
+          && JS_GetProperty(cx, JSVAL_TO_OBJECT(ss), "counts", counts.address()));
 }
 
 nsresult
 CompareCountArrays(JSContext *cx, JSObject *before, JSObject *after)
 {
   uint32_t before_size, after_size;
   if (!(JS_GetArrayLength(cx, before, &before_size)
         && JS_GetArrayLength(cx, after, &after_size))) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (before_size != after_size) {
     return NS_ERROR_UNEXPECTED;
   }
 
   for (uint32_t i = 0; i < before_size; ++i) {
-    JS::Value before_num, after_num;
+    Value before_num, after_num;
 
     if (!(JS_GetElement(cx, before, i, &before_num)
           && JS_GetElement(cx, after, i, &after_num))) {
       return NS_ERROR_UNEXPECTED;
     }
 
     JSBool same = JS_TRUE;
     if (!JS_LooselyEqual(cx, before_num, after_num, &same)) {
@@ -527,24 +528,26 @@ int main(int argc, char** argv)
   if (use_js)
     ac.construct(cx, glob);
   if (use_js && !JS_InitStandardClasses(cx, glob))
     use_js = false;
 
   NS_NAMED_LITERAL_CSTRING(age_histogram_id, "STARTUP_CACHE_AGE_HOURS");
   NS_NAMED_LITERAL_CSTRING(invalid_histogram_id, "STARTUP_CACHE_INVALID");
 
-  JS::AutoValueRooter age_before_counts(cx);
-  if (use_js && !GetHistogramCounts("STARTUP_CACHE_AGE_HOURS histogram before test",
-                                    age_histogram_id, cx, age_before_counts.addr()))
+  Rooted<Value> age_before_counts(cx);
+  if (use_js &&
+      !GetHistogramCounts("STARTUP_CACHE_AGE_HOURS histogram before test",
+                          age_histogram_id, cx, &age_before_counts))
     use_js = false;
   
-  JS::AutoValueRooter invalid_before_counts(cx);
-  if (use_js && !GetHistogramCounts("STARTUP_CACHE_INVALID histogram before test",
-                                    invalid_histogram_id, cx, invalid_before_counts.addr()))
+  Rooted<Value> invalid_before_counts(cx);
+  if (use_js &&
+      !GetHistogramCounts("STARTUP_CACHE_INVALID histogram before test",
+                          invalid_histogram_id, cx, &invalid_before_counts))
     use_js = false;
   
   nsresult scrv;
   nsCOMPtr<nsIStartupCache> sc 
     = do_GetService("@mozilla.org/startupcache/cache;1", &scrv);
   if (NS_FAILED(scrv))
     rv = 1;
   else
@@ -556,31 +559,33 @@ int main(int argc, char** argv)
   if (NS_FAILED(TestWriteObject()))
     rv = 1;
   nsCOMPtr<nsIFile> profileDir = xpcom.GetProfileDirectory();
   if (NS_FAILED(TestIgnoreDiskCache(profileDir)))
     rv = 1;
   if (NS_FAILED(TestEarlyShutdown()))
     rv = 1;
 
-  JS::AutoValueRooter age_after_counts(cx);
-  if (use_js && !GetHistogramCounts("STARTUP_CACHE_AGE_HOURS histogram after test",
-                                    age_histogram_id, cx, age_after_counts.addr()))
+  Rooted<Value> age_after_counts(cx);
+  if (use_js &&
+      !GetHistogramCounts("STARTUP_CACHE_AGE_HOURS histogram after test",
+                          age_histogram_id, cx, &age_after_counts))
     use_js = false;
 
   if (NS_FAILED(TestHistogramValues("age samples", use_js, cx,
-                                    JSVAL_TO_OBJECT(age_before_counts.value()),
-                                    JSVAL_TO_OBJECT(age_after_counts.value()))))
+                                    age_before_counts.toObjectOrNull(),
+                                    age_after_counts.toObjectOrNull())))
     rv = 1;
                                                     
-  JS::AutoValueRooter invalid_after_counts(cx);
-  if (use_js && !GetHistogramCounts("STARTUP_CACHE_INVALID histogram after test",
-                                    invalid_histogram_id, cx, invalid_after_counts.addr()))
+  Rooted<Value> invalid_after_counts(cx);
+  if (use_js &&
+      !GetHistogramCounts("STARTUP_CACHE_INVALID histogram after test",
+                          invalid_histogram_id, cx, &invalid_after_counts))
     use_js = false;
 
   // STARTUP_CACHE_INVALID should have been triggered by TestIgnoreDiskCache()
   if (NS_FAILED(TestHistogramValues("invalid disk cache", use_js, cx,
-                                    JSVAL_TO_OBJECT(invalid_before_counts.value()),
-                                    JSVAL_TO_OBJECT(invalid_after_counts.value()))))
+                                    invalid_before_counts.toObjectOrNull(),
+                                    invalid_after_counts.toObjectOrNull())))
     rv = 1;
 
   return rv;
 }
--- a/testing/xpcshell/xpcshell_android.ini
+++ b/testing/xpcshell/xpcshell_android.ini
@@ -5,12 +5,11 @@
 [include:chrome/test/unit/xpcshell.ini]
 [include:intl/locale/tests/unit/xpcshell.ini]
 [include:netwerk/cookie/test/unit/xpcshell.ini]
 [include:modules/libjar/zipwriter/test/unit/xpcshell.ini]
 [include:parser/xml/test/unit/xpcshell.ini]
 [include:image/test/unit/xpcshell.ini]
 [include:testing/xpcshell/example/unit/xpcshell.ini]
 [include:xpcom/tests/unit/xpcshell.ini]
-[include:netwerk/test/unit/xpcshell.ini]
 [include:rdf/tests/unit/xpcshell.ini]
 [include:gfx/tests/unit/xpcshell.ini]
 
--- a/toolkit/devtools/debugger/dbg-transport.js
+++ b/toolkit/devtools/debugger/dbg-transport.js
@@ -2,16 +2,72 @@
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* 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/NetUtil.jsm");
 
+/* Turn the error e into a string, without fail. */
+function safeErrorString(aError) {
+  try {
+    var s = aError.toString();
+    if (typeof s === "string")
+      return s;
+  } catch (ee) { }
+
+  return "<failed trying to find error description>";
+}
+
+/**
+ * Given a handler function that may throw, return an infallible handler
+ * function that calls the fallible handler, and logs any exceptions it
+ * throws.
+ *
+ * @param aHandler function
+ *      A handler function, which may throw.
+ * @param aName string
+ *      A name for aHandler, for use in error messages. If omitted, we use
+ *      aHandler.name.
+ *
+ * (SpiderMonkey does generate good names for anonymous functions, but we
+ * don't have a way to get at them from JavaScript at the moment.)
+ */
+function makeInfallible(aHandler, aName) {
+  if (!aName)
+    aName = aHandler.name;
+
+  return function (/* arguments */) {
+    try {
+      return aHandler.apply(this, arguments);
+    } catch (ex) {
+      let msg = "Handler function ";
+      if (aName) {
+        msg += aName + " ";
+      }
+      msg += "threw an exception: " + safeErrorString(ex);
+      if (ex.stack) {
+        msg += "\nCall stack:\n" + ex.stack;
+      }
+
+      dump(msg + "\n");
+
+      if (Cu.reportError) {
+        /*
+         * Note that the xpcshell test harness registers an observer for
+         * console messages, so when we're running tests, this will cause
+         * the test to quit.
+         */
+        Cu.reportError(msg);
+      }
+    }
+  }
+}
+
 /**
  * An adapter that handles data transfers between the debugger client and
  * server. It can work with both nsIPipe and nsIServerSocket transports so
  * long as the properly created input and output streams are specified.
  *
  * @param aInput nsIInputStream
  *        The input stream.
  * @param aOutput nsIAsyncOutputStream
@@ -85,58 +141,53 @@ DebuggerTransport.prototype = {
    */
   _flushOutgoing: function DT_flushOutgoing() {
     if (this._outgoing.length > 0) {
       var threadManager = Cc["@mozilla.org/thread-manager;1"].getService();
       this._output.asyncWait(this, 0, 0, threadManager.currentThread);
     }
   },
 
-  onOutputStreamReady: function DT_onOutputStreamReady(aStream) {
+  onOutputStreamReady:
+  makeInfallible(function DT_onOutputStreamReady(aStream) {
     let written = aStream.write(this._outgoing, this._outgoing.length);
     this._outgoing = this._outgoing.slice(written);
     this._flushOutgoing();
-  },
+  }, "DebuggerTransport.prototype.onOutputStreamReady"),
 
   /**
    * Initialize the input stream for reading. Once this method has been
    * called, we watch for packets on the input stream, and pass them to
    * this.hook.onPacket.
    */
   ready: function DT_ready() {
     let pump = Cc["@mozilla.org/network/input-stream-pump;1"]
       .createInstance(Ci.nsIInputStreamPump);
     pump.init(this._input, -1, -1, 0, 0, false);
     pump.asyncRead(this, null);
   },
 
   // nsIStreamListener
-  onStartRequest: function DT_onStartRequest(aRequest, aContext) {},
+  onStartRequest:
+  makeInfallible(function DT_onStartRequest(aRequest, aContext) {},
+                 "DebuggerTransport.prototype.onStartRequest"),
 
-  onStopRequest: function DT_onStopRequest(aRequest, aContext, aStatus) {
+  onStopRequest:
+  makeInfallible(function DT_onStopRequest(aRequest, aContext, aStatus) {
     this.close();
     this.hooks.onClosed(aStatus);
-  },
+  }, "DebuggerTransport.prototype.onStopRequest"),
 
-  onDataAvailable: function DT_onDataAvailable(aRequest, aContext,
-                                                aStream, aOffset, aCount) {
-    try {
-      this._incoming += NetUtil.readInputStreamToString(aStream,
-                                                        aStream.available());
-      while (this._processIncoming()) {};
-    } catch(e) {
-      let msg = "Unexpected error reading from debugging connection: " + e + " - " + e.stack;
-      if (Cu.reportError) {
-        Cu.reportError(msg);
-      }
-      dump(msg + "\n");
-      this.close();
-      return;
-    }
-  },
+  onDataAvailable:
+  makeInfallible(function DT_onDataAvailable(aRequest, aContext,
+                                             aStream, aOffset, aCount) {
+    this._incoming += NetUtil.readInputStreamToString(aStream,
+                                                      aStream.available());
+    while (this._processIncoming()) {};
+  }, "DebuggerTransport.prototype.onDataAvailable"),
 
   /**
    * Process incoming packets. Returns true if a packet has been received, either
    * if it was properly parsed or not. Returns false if the incoming stream does
    * not contain a full packet yet. After a proper packet is parsed, the dispatch
    * handler DebuggerTransport.hooks.onPacket is called with the packet as a
    * parameter.
    */
@@ -165,30 +216,21 @@ DebuggerTransport.prototype = {
       let msg = "Error parsing incoming packet: " + packet + " (" + e + " - " + e.stack + ")";
       if (Cu.reportError) {
         Cu.reportError(msg);
       }
       dump(msg + "\n");
       return true;
     }
 
-    try {
-      dumpn("Got: " + packet);
-      let self = this;
-      Services.tm.currentThread.dispatch({run: function() {
-        self.hooks.onPacket(parsed);
-      }}, 0);
-    } catch(e) {
-      let msg = "Error handling incoming packet: " + e + " - " + e.stack;
-      if (Cu.reportError) {
-        Cu.reportError(msg);
-      }
-      dump(msg + "\n");
-      dumpn("Packet was: " + JSON.stringify(packet));
-    }
+    dumpn("Got: " + packet);
+    let self = this;
+    Services.tm.currentThread.dispatch(makeInfallible(function() {
+      self.hooks.onPacket(parsed);
+    }, "DebuggerTransport instance's this.hooks.onPacket"), 0);
 
     return true;
   }
 }
 
 
 /**
  * An adapter that handles data transfers between the debugger client and
@@ -215,42 +257,33 @@ this.LocalDebuggerTransport = function L
 }
 
 LocalDebuggerTransport.prototype = {
   /**
    * Transmit a message by directly calling the onPacket handler of the other
    * endpoint.
    */
   send: function LDT_send(aPacket) {
-    try {
-      let serial = this._serial.count++;
-      if (wantLogging) {
-        if (aPacket.to) {
-          dumpn("Packet " + serial + " sent to " + uneval(aPacket.to));
-        } else if (aPacket.from) {
-          dumpn("Packet " + serial + " sent from " + uneval(aPacket.from));
-        }
+    let serial = this._serial.count++;
+    if (wantLogging) {
+      if (aPacket.to) {
+        dumpn("Packet " + serial + " sent to " + uneval(aPacket.to));
+      } else if (aPacket.from) {
+        dumpn("Packet " + serial + " sent from " + uneval(aPacket.from));
       }
-      this._deepFreeze(aPacket);
-      let other = this.other;
-      Services.tm.currentThread.dispatch(function() {
-        // Avoid the cost of JSON.stringify() when logging is disabled.
-        if (wantLogging) {
-          dumpn("Received packet " + serial + ": " + JSON.stringify(aPacket, null, 2));
-        }
-        other.hooks.onPacket(aPacket);
-      }, 0);
-    } catch(e) {
-      let msg = "Error handling incoming packet: " + e + " - " + e.stack;
-      if (Cu.reportError) {
-        Cu.reportError(msg);
+    }
+    this._deepFreeze(aPacket);
+    let other = this.other;
+    Services.tm.currentThread.dispatch(makeInfallible(function() {
+      // Avoid the cost of JSON.stringify() when logging is disabled.
+      if (wantLogging) {
+        dumpn("Received packet " + serial + ": " + JSON.stringify(aPacket, null, 2));
       }
-      dump(msg + "\n");
-      dump("Packet was: " + JSON.stringify(aPacket) + "\n");
-    }
+      other.hooks.onPacket(aPacket);
+    }, "LocalDebuggerTransport instance's this.other.hooks.onPacket"), 0);
   },
 
   /**
    * Close the transport.
    */
   close: function LDT_close() {
     if (this.other) {
       // Remove the reference to the other endpoint before calling close(), to
@@ -278,11 +311,9 @@ LocalDebuggerTransport.prototype = {
       // somewhere in the object if there is an already frozen object containing
       // an unfrozen object.
       if (aObject.hasOwnProperty(prop) && typeof aObject === "object" &&
           !Object.isFrozen(aObject)) {
         this._deepFreeze(o[prop]);
       }
     }
   }
-
-}
-
+};
--- a/toolkit/devtools/debugger/server/dbg-browser-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-browser-actors.js
@@ -195,19 +195,20 @@ BrowserRootActor.prototype = {
   getTabContainer: function BRA_getTabContainer(aWindow) {
     return aWindow.getBrowser().tabContainer;
   },
 
   /**
    * When a tab is closed, exit its tab actor.  The actor
    * will be dropped at the next listTabs request.
    */
-  onTabClosed: function BRA_onTabClosed(aEvent) {
+  onTabClosed:
+  makeInfallible(function BRA_onTabClosed(aEvent) {
     this.exitTabActor(aEvent.target.linkedBrowser);
-  },
+  }, "BrowserRootActor.prototype.onTabClosed"),
 
   /**
    * Exit the tab actor of the specified tab.
    */
   exitTabActor: function BRA_exitTabActor(aWindow) {
     let actor = this._tabActors.get(aWindow);
     if (actor) {
       this._tabActors.delete(actor.browser);
@@ -268,25 +269,26 @@ BrowserRootActor.prototype = {
       windowUtils.suppressEventHandling(false);
     }
   },
 
   // nsIWindowMediatorListener.
 
   onWindowTitleChange: function BRA_onWindowTitleChange(aWindow, aTitle) { },
   onOpenWindow: function BRA_onOpenWindow(aWindow) { },
-  onCloseWindow: function BRA_onCloseWindow(aWindow) {
+  onCloseWindow:
+  makeInfallible(function BRA_onCloseWindow(aWindow) {
     // An nsIWindowMediatorListener's onCloseWindow method gets passed all
     // sorts of windows; we only care about the tab containers. Those have
     // 'getBrowser' methods.
     if (aWindow.getBrowser) {
       this.unwatchWindow(aWindow);
     }
-  }
-}
+  }, "BrowserRootActor.prototype.onCloseWindow"),
+};
 
 /**
  * The request types this actor can handle.
  */
 BrowserRootActor.prototype.requestTypes = {
   "listTabs": BrowserRootActor.prototype.onListTabs
 };
 
@@ -578,17 +580,18 @@ BrowserTabActor.prototype = {
       this._pendingNavigation = null;
     }
   },
 
   /**
    * Handle location changes, by sending a tabNavigated notification to the
    * client.
    */
-  onWindowCreated: function BTA_onWindowCreated(evt) {
+  onWindowCreated:
+  makeInfallible(function BTA_onWindowCreated(evt) {
     if (evt.target === this.browser.contentDocument) {
       // pageshow events for non-persisted pages have already been handled by a
       // prior DOMWindowCreated event.
       if (evt.type == "pageshow" && !evt.persisted) {
         return;
       }
       if (this._attached) {
         this.threadActor.clearDebuggees();
@@ -599,17 +602,17 @@ BrowserTabActor.prototype = {
     }
 
     if (this._attached) {
       this.threadActor.global = evt.target.defaultView.wrappedJSObject;
       if (this.threadActor.attached) {
         this.threadActor.findGlobals();
       }
     }
-  },
+  }, "BrowserTabActor.prototype.onWindowCreated"),
 
   /**
    * Tells if the window.console object is native or overwritten by script in
    * the page.
    *
    * @param nsIDOMWindow aWindow
    *        The window object you want to check.
    * @return boolean
@@ -645,17 +648,17 @@ BrowserTabActor.prototype.requestTypes =
  */
 function DebuggerProgressListener(aBrowserTabActor) {
   this._tabActor = aBrowserTabActor;
   this._tabActor._tabbrowser.addProgressListener(this);
 }
 
 DebuggerProgressListener.prototype = {
   onStateChange:
-  function DPL_onStateChange(aProgress, aRequest, aFlag, aStatus) {
+  makeInfallible(function DPL_onStateChange(aProgress, aRequest, aFlag, aStatus) {
     let isStart = aFlag & Ci.nsIWebProgressListener.STATE_START;
     let isStop = aFlag & Ci.nsIWebProgressListener.STATE_STOP;
     let isDocument = aFlag & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT;
     let isNetwork = aFlag & Ci.nsIWebProgressListener.STATE_IS_NETWORK;
     let isRequest = aFlag & Ci.nsIWebProgressListener.STATE_IS_REQUEST;
     let isWindow = aFlag & Ci.nsIWebProgressListener.STATE_IS_WINDOW;
 
     // Skip non-interesting states.
@@ -693,17 +696,17 @@ DebuggerProgressListener.prototype = {
         from: this._tabActor.actorID,
         type: "tabNavigated",
         url: this._tabActor.url,
         title: this._tabActor.title,
         nativeConsoleAPI: this._tabActor.hasNativeConsoleAPI(window),
         state: "stop",
       });
     }
-  },
+  }, "DebuggerProgressListener.prototype.onStateChange"),
 
   /**
    * Destroy the progress listener instance.
    */
   destroy: function DPL_destroy() {
     if (this._tabActor._tabbrowser.removeProgressListener) {
       try {
         this._tabActor._tabbrowser.removeProgressListener(this);
--- a/toolkit/devtools/debugger/server/dbg-profiler-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-profiler-actors.js
@@ -93,47 +93,52 @@ ProfilerActor.prototype = {
       if (idx == -1)
         continue;
       Services.obs.removeObserver(this, event);
       this._observedEvents.splice(idx, 1);
       unregistered.push(event);
     }
     return { unregistered: unregistered }
   },
-  observe: function(aSubject, aTopic, aData) {
-    function unWrapper(obj) {
-      if (obj && typeof obj == "object" && ("wrappedJSObject" in obj)) {
-        obj = obj.wrappedJSObject;
-        if (("wrappedJSObject" in obj) && (obj.wrappedJSObject == obj)) {
-          /* If the object defines wrappedJSObject as itself, which is the
-           * typical idiom for wrapped JS objects, JSON.stringify won't be
-           * able to work because the object is cyclic.
-           * But removing the wrappedJSObject property will break aSubject
-           * for possible other observers of the same topic, so we need
-           * to restore wrappedJSObject afterwards */
-          delete obj.wrappedJSObject;
-          return { unwrapped: obj,
-                   fixup: function() {
-                     this.unwrapped.wrappedJSObject = this.unwrapped;
-                   }
-                 }
-        }
+  observe: makeInfallible(function(aSubject, aTopic, aData) {
+    /*
+     * this.conn.send can only transmit acyclic values. However, it is
+     * idiomatic for wrapped JS objects like aSubject (and possibly aData?)
+     * to have a 'wrappedJSObject' property pointing to themselves.
+     *
+     * this.conn.send also assumes that it can retain the object it is
+     * passed to be handled on later event ticks; and that it's okay to
+     * freeze it. Since we don't really know what aSubject and aData are,
+     * we need to pass this.conn.send a copy of them, not the originals.
+     *
+     * We break the cycle and make the copy by JSON.stringifying those
+     * values with a replacer that omits properties known to introduce
+     * cycles, and then JSON.parsing the result. This spends processor
+     * time, but it's simple.
+     */
+    function cycleBreaker(key, value) {
+      if (key === 'wrappedJSObject') {
+        return undefined;
       }
-      return { unwrapped: obj, fixup: function() { } }
+      return value;
     }
-    var subject = unWrapper(aSubject);
-    var data = unWrapper(aData);
+
+    /*
+     * If these values are objects with a non-null 'wrappedJSObject'
+     * property, use its value. Otherwise, use the value unchanged.
+     */
+    aSubject = (aSubject && aSubject.wrappedJSObject) || aSubject;
+    aData    = (aData    && aData.wrappedJSObject)    || aData;
+
     this.conn.send({ from: this.actorID,
                      type: "eventNotification",
                      event: aTopic,
-                     subject: subject.unwrapped,
-                     data: data.unwrapped });
-    data.fixup();
-    subject.fixup();
-  },
+                     subject: JSON.parse(JSON.stringify(aSubject, cycleBreaker)),
+                     data:    JSON.parse(JSON.stringify(aData,    cycleBreaker)) });
+  }, "ProfilerActor.prototype.observe"),
 };
 
 /**
  * The request types this actor can handle.
  */
 ProfilerActor.prototype.requestTypes = {
   "startProfiler": ProfilerActor.prototype.onStartProfiler,
   "stopProfiler": ProfilerActor.prototype.onStopProfiler,
--- a/toolkit/devtools/debugger/server/dbg-script-actors.js
+++ b/toolkit/devtools/debugger/server/dbg-script-actors.js
@@ -174,16 +174,17 @@ ThreadActor.prototype = {
           // TODO: after bug 801084 lands see if we need to JSONify this.
           hostAnnotations: aGlobal.hostAnnotations
         });
       }
     }
   },
 
   disconnect: function TA_disconnect() {
+    dumpn("in ThreadActor.prototype.disconnect");
     if (this._state == "paused") {
       this.onResume();
     }
 
     this._state = "exited";
 
     this._protoChains.clear();
     this.clearDebuggees();
@@ -243,16 +244,17 @@ ThreadActor.prototype = {
     } catch (e) {
       reportError(e);
       return { error: "notAttached", message: e.toString() };
     }
   },
 
   onDetach: function TA_onDetach(aRequest) {
     this.disconnect();
+    dumpn("ThreadActor.prototype.onDetach: returning 'detached' packet");
     return {
       type: "detached"
     };
   },
 
   onReconfigure: function TA_onReconfigure(aRequest) {
     if (this.state == "exited") {
       return { error: "wrongState" };
--- a/toolkit/devtools/debugger/server/dbg-server.js
+++ b/toolkit/devtools/debugger/server/dbg-server.js
@@ -36,27 +36,16 @@ function dumpn(str) {
 }
 
 function dbg_assert(cond, e) {
   if (!cond) {
     return e;
   }
 }
 
-/* Turn the error e into a string, without fail. */
-function safeErrorString(aError) {
-  try {
-    var s = aError.toString();
-    if (typeof s === "string")
-      return s;
-  } catch (ee) { }
-
-  return "<failed trying to find error description>";
-}
-
 loadSubScript.call(this, "chrome://global/content/devtools/dbg-transport.js");
 
 // XPCOM constructors
 const ServerSocket = CC("@mozilla.org/network/server-socket;1",
                         "nsIServerSocket",
                         "initSpecialConnection");
 
 /***
@@ -276,31 +265,28 @@ var DebuggerServer = {
     this._onConnection(serverTransport);
 
     return clientTransport;
   },
 
 
   // nsIServerSocketListener implementation
 
-  onSocketAccepted: function DS_onSocketAccepted(aSocket, aTransport) {
+  onSocketAccepted:
+  makeInfallible(function DS_onSocketAccepted(aSocket, aTransport) {
     if (!this._allowConnection()) {
       return;
     }
     dumpn("New debugging connection on " + aTransport.host + ":" + aTransport.port);
 
-    try {
-      let input = aTransport.openInputStream(0, 0, 0);
-      let output = aTransport.openOutputStream(0, 0, 0);
-      let transport = new DebuggerTransport(input, output);
-      DebuggerServer._onConnection(transport);
-    } catch (e) {
-      dumpn("Couldn't initialize connection: " + e + " - " + e.stack);
-    }
-  },
+    let input = aTransport.openInputStream(0, 0, 0);
+    let output = aTransport.openOutputStream(0, 0, 0);
+    let transport = new DebuggerTransport(input, output);
+    DebuggerServer._onConnection(transport);
+  }, "DebuggerServer.onSocketAccepted"),
 
   onStopListening: function DS_onStopListening(aSocket, status) {
     dumpn("onStopListening, status: " + status);
   },
 
   /**
    * Raises an exception if the server has not been properly initialized.
    */