merge m-c to fx-team
authorTim Taubert <tim.taubert@gmx.de>
Thu, 12 Jul 2012 10:55:19 +0200
changeset 99066 70d92a6ccdfa95b8a015010d4c99b2307864f087
parent 99065 04df150d0cc3157583ecccd804bb8753fc4f2325 (current diff)
parent 99062 46804c31366b9fcf06fc707baa21be6eec5f5152 (diff)
child 99067 b4183760a8130419b8a58399a5ba4a96b4d76f20
child 99131 b24995e163b54055e33db4903a14ae1355abbfee
child 99189 cb7d40a84ae3654f549c7b44faa70af89673cc02
push id11807
push useremorley@mozilla.com
push dateThu, 12 Jul 2012 15:31:52 +0000
treeherdermozilla-inbound@b4183760a813 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone16.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge m-c to fx-team
--- a/accessible/src/base/nsARIAMap.cpp
+++ b/accessible/src/base/nsARIAMap.cpp
@@ -577,17 +577,17 @@ nsRoleMapEntry nsARIAMap::gEmptyRoleMap 
   kNoReqStates
 };
 
 /**
  * Universal (Global) states:
  * The following state rules are applied to any accessible element,
  * whether there is an ARIA role or not:
  */
-EStateRule nsARIAMap::gWAIUnivStateMap[] = {
+static const EStateRule sWAIUnivStateMap[] = {
   eARIABusy,
   eARIADisabled,
   eARIAExpanded,  // Currently under spec review but precedent exists
   eARIAHasPopup,  // Note this is technically a "property"
   eARIAInvalid,
   eARIARequired,  // XXX not global, Bug 553117
   eARIANone
 };
@@ -630,17 +630,18 @@ nsAttributeCharacteristics nsARIAMap::gW
   {&nsGkAtoms::aria_setsize,           ATTR_BYPASSOBJ                 }, /* handled via groupPosition */
   {&nsGkAtoms::aria_sort,                               ATTR_VALTOKEN },
   {&nsGkAtoms::aria_valuenow,          ATTR_BYPASSOBJ                 },
   {&nsGkAtoms::aria_valuemin,          ATTR_BYPASSOBJ                 },
   {&nsGkAtoms::aria_valuemax,          ATTR_BYPASSOBJ                 },
   {&nsGkAtoms::aria_valuetext,         ATTR_BYPASSOBJ                 }
 };
 
-PRUint32 nsARIAMap::gWAIUnivAttrMapLength = NS_ARRAY_LENGTH(nsARIAMap::gWAIUnivAttrMap);
+PRUint32
+nsARIAMap::gWAIUnivAttrMapLength = NS_ARRAY_LENGTH(nsARIAMap::gWAIUnivAttrMap);
 
 nsRoleMapEntry*
 aria::GetRoleMap(nsINode* aNode)
 {
   nsIContent* content = nsCoreUtils::GetRoleContent(aNode);
   nsAutoString roles;
   if (!content ||
       !content->GetAttr(kNameSpaceID_None, nsGkAtoms::role, roles) ||
@@ -667,8 +668,19 @@ aria::GetRoleMap(nsINode* aNode)
         low = idx + 1;
     }
   }
 
   // Always use some entry if there is a non-empty role string
   // To ensure an accessible object is created
   return &sLandmarkRoleMap;
 }
+
+PRUint64
+aria::UniversalStatesFor(mozilla::dom::Element* aElement)
+{
+  PRUint64 state = 0;
+  PRUint32 index = 0;
+  while (MapToState(sWAIUnivStateMap[index], aElement, &state))
+    index++;
+
+  return state;
+}
--- a/accessible/src/base/nsARIAMap.h
+++ b/accessible/src/base/nsARIAMap.h
@@ -195,54 +195,39 @@ struct nsARIAMap
   /**
    * Empty role map entry. Used by accessibility service to create an accessible
    * if the accessible can't use role of used accessible class. For example,
    * it is used for table cells that aren't contained by table.
    */
   static nsRoleMapEntry gEmptyRoleMap;
 
   /**
-   * State map of ARIA states applied to any accessible not depending on
-   * the role.
-   */
-  static mozilla::a11y::aria::EStateRule gWAIUnivStateMap[];
-
-  /**
    * Map of attribute to attribute characteristics.
    */
   static nsAttributeCharacteristics gWAIUnivAttrMap[];
   static PRUint32 gWAIUnivAttrMapLength;
-
-  /**
-   * Return accessible state from ARIA universal states applied to the given
-   * element.
-   */
-  static PRUint64 UniversalStatesFor(mozilla::dom::Element* aElement)
-  {
-    PRUint64 state = 0;
-    PRUint32 index = 0;
-    while (mozilla::a11y::aria::MapToState(gWAIUnivStateMap[index],
-                                           aElement, &state))
-      index++;
-
-    return state;
-  }
 };
 
 namespace mozilla {
 namespace a11y {
 namespace aria {
 
 /**
  * Get the role map entry for a given DOM node. This will use the first
  * ARIA role if the role attribute provides a space delimited list of roles.
  *
  * @param aNode  [in] the DOM node to get the role map entry for
  * @return        a pointer to the role map entry for the ARIA role, or nsnull
  *                if none
  */
 nsRoleMapEntry* GetRoleMap(nsINode* aNode);
 
+/**
+ * Return accessible state from ARIA universal states applied to the given
+ * element.
+ */
+PRUint64 UniversalStatesFor(mozilla::dom::Element* aElement);
+
 } // namespace aria
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -1528,17 +1528,17 @@ void
 Accessible::ApplyARIAState(PRUint64* aState) const
 {
   if (!mContent->IsElement())
     return;
 
   dom::Element* element = mContent->AsElement();
 
   // Test for universal states first
-  *aState |= nsARIAMap::UniversalStatesFor(element);
+  *aState |= aria::UniversalStatesFor(element);
 
   if (mRoleMapEntry) {
 
     // We only force the readonly bit off if we have a real mapping for the aria
     // role. This preserves the ability for screen readers to use readonly
     // (primarily on the document) as the hint for creating a virtual buffer.
     if (mRoleMapEntry->role != roles::NOTHING)
       *aState &= ~states::READONLY;
--- a/browser/app/macbuild/Contents/_CodeSignature/CodeResources
+++ b/browser/app/macbuild/Contents/_CodeSignature/CodeResources
@@ -7,16 +7,22 @@
             <key>^Info.plist$</key>
             <true/>
             <key>^PkgInfo$</key>
             <true/>
             <key>^MacOS/</key>
             <true/>
             <key>^Resources/</key>
             <true/>
+            <key>^MacOS/distribution/.*</key><dict>
+                <key>omit</key>
+                <true/>
+                <key>weight</key>
+                <real>10</real>
+            </dict>
             <key>^MacOS/updates/.*</key><dict>
                 <key>omit</key>
                 <true/>
                 <key>weight</key>
                 <real>10</real>
             </dict>
             <key>^MacOS/active-update.xml$</key><dict>
                 <key>omit</key>
--- a/browser/base/content/abouthome/aboutHome.css
+++ b/browser/base/content/abouthome/aboutHome.css
@@ -92,18 +92,18 @@ body[dir=rtl] #searchText {
   padding: 0 9px;
   border: 1px solid;
   border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
   -moz-border-start: 1px solid transparent;
   border-radius: 0 2.5px 2.5px 0;
   box-shadow: 0 0 2px hsla(0,0%,100%,.5) inset,
               0 1px 0 hsla(0,0%,100%,.2);
   cursor: pointer;
-  -moz-transition-property: background-color, border-color, box-shadow;
-  -moz-transition-duration: 150ms;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 150ms;
 }
 
 body[dir=rtl] #searchSubmit {
   border-radius: 2.5px 0 0 2.5px;
 }
 
 #searchText:focus + #searchSubmit,
 #searchText + #searchSubmit:hover {
@@ -124,17 +124,17 @@ body[dir=rtl] #searchSubmit {
               0 0 0 1px hsla(0,0%,100%,.1) inset,
               0 1px 0 hsla(210,54%,20%,.03),
               0 0 4px hsla(206,100%,20%,.2);
 }
 
 #searchText + #searchSubmit:hover:active {
   box-shadow: 0 1px 1px hsla(211,79%,6%,.1) inset,
               0 0 1px hsla(211,79%,6%,.2) inset;
-  -moz-transition-duration: 0ms;
+  transition-duration: 0ms;
 }
 
 #defaultSnippet1,
 #defaultSnippet2 {
   display: block;
   min-height: 38px;
   background: 30px center no-repeat;
   padding: 6px 0;
@@ -191,35 +191,35 @@ body[narrow] #launcher[session] {
   vertical-align: top;
   white-space: normal;
   background: transparent padding-box;
   border: 1px solid transparent;
   border-radius: 2.5px;
   color: #525c66;
   font-size: 75%;
   cursor: pointer;
-  -moz-transition-property: background-color, border-color, box-shadow;
-  -moz-transition-duration: 150ms;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 150ms;
 }
 
 body[narrow] #launcher[session] > .launchButton {
   margin: 4px 1px;
 }
 
 .launchButton:hover {
   background-color: hsla(211,79%,6%,.03);
   border-color: hsla(210,54%,20%,.15) hsla(210,54%,20%,.17) hsla(210,54%,20%,.2);
 }
 
 .launchButton:hover:active {
   background-image: -moz-linear-gradient(hsla(211,79%,6%,.02), hsla(211,79%,6%,.05));
   border-color: hsla(210,54%,20%,.2) hsla(210,54%,20%,.23) hsla(210,54%,20%,.25);
   box-shadow: 0 1px 1px hsla(211,79%,6%,.05) inset,
               0 0 1px hsla(211,79%,6%,.1) inset;
-  -moz-transition-duration: 0ms;
+  transition-duration: 0ms;
 }
 
 .launchButton[hidden],
 #launcher:not([session]) > #restorePreviousSessionSeparator,
 #launcher:not([session]) > #restorePreviousSession {
   display: none;
 }
 
@@ -309,17 +309,17 @@ body[narrow] #restorePreviousSession::be
   width: 32px;
 }
 
 #aboutMozilla {
   display: block;
   position: relative; /* pin wordmark to edge of document, not of viewport */
   -moz-box-ordinal-group: 0;
   opacity: .5;
-  -moz-transition: opacity 150ms;
+  transition: opacity 150ms;
 }
 
 #aboutMozilla:hover {
   opacity: 1;
 }
 
 #aboutMozilla::before {
   content: url("chrome://browser/content/abouthome/mozilla.png");
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -22,18 +22,23 @@ var FullScreen = {
     document.getElementById("exitFullScreenItem").hidden = !enterFS;
 #endif
 
     // On OS X Lion we don't want to hide toolbars when entering fullscreen, unless
     // we're entering DOM fullscreen, in which case we should hide the toolbars.
     // If we're leaving fullscreen, then we'll go through the exit code below to
     // make sure toolbars are made visible in the case of DOM fullscreen.
     if (enterFS && this.useLionFullScreen) {
-      if (document.mozFullScreen)
+      if (document.mozFullScreen) {
         this.showXULChrome("toolbar", false);
+      }
+      else {
+        gNavToolbox.setAttribute("inFullscreen", true);
+        document.documentElement.setAttribute("inFullscreen", true);
+      }
       return;
     }
 
     // show/hide menubars, toolbars (except the full screen toolbar)
     this.showXULChrome("toolbar", !enterFS);
 
     if (enterFS) {
       // Add a tiny toolbar to receive mouseover and dragenter events, and provide affordance.
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -33,28 +33,28 @@ tabbrowser {
   -moz-binding: url("chrome://browser/content/tabbrowser.xml#tabbrowser-tab");
 }
 
 .tabbrowser-tab:not([pinned]) {
   -moz-box-flex: 100;
   max-width: 250px;
   min-width: 100px;
   width: 0;
-  -moz-transition: min-width 200ms ease-out,
-                   max-width 250ms ease-out,
-                   opacity 50ms ease-out 20ms /* hide the tab for the first 20ms of the max-width transition */;
+  transition: min-width 200ms ease-out,
+              max-width 250ms ease-out,
+              opacity 50ms ease-out 20ms /* hide the tab for the first 20ms of the max-width transition */;
 }
 
 .tabbrowser-tab:not([pinned]):not([fadein]) {
   max-width: 0.1px;
   min-width: 0.1px;
   opacity: 0 !important;
-  -moz-transition: min-width 200ms ease-out,
-                   max-width 250ms ease-out,
-                   opacity 50ms ease-out 180ms /* hide the tab for the last 20ms of the max-width transition */;
+  transition: min-width 200ms ease-out,
+              max-width 250ms ease-out,
+              opacity 50ms ease-out 180ms /* hide the tab for the last 20ms of the max-width transition */;
 }
 
 .tab-throbber:not([fadein]):not([pinned]),
 .tab-label:not([fadein]):not([pinned]),
 .tab-icon-image:not([fadein]):not([pinned]),
 .tab-close-button:not([fadein]):not([pinned]) {
   display: none;
 }
@@ -372,18 +372,18 @@ window[chromehidden~="toolbar"] toolbar:
   position: fixed;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
 }
 
 #full-screen-warning-container[fade-warning-out] {
-  -moz-transition-property: opacity !important;
-  -moz-transition-duration: 500ms !important;
+  transition-property: opacity !important;
+  transition-duration: 500ms !important;
   opacity: 0.0;
 }
 
 #full-screen-warning-message {
   pointer-events: auto;
   /* We must specify a max-width, otherwise word-wrap:break-word doesn't
      work in descendant <description> and <label> elements. Bug 630864. */
   max-width: 800px;  
@@ -513,27 +513,27 @@ statuspanel[type=status] {
 
 @media all and (max-width: 800px) {
   statuspanel[type=status] {
     min-width: 33%;
   }
 }
 
 statuspanel[type=overLink] {
-  -moz-transition: opacity 120ms ease-out;
+  transition: opacity 120ms ease-out;
   direction: ltr;
 }
 
 statuspanel[inactive] {
-  -moz-transition: none;
+  transition: none;
   opacity: 0;
 }
 
 statuspanel[inactive][previoustype=overLink] {
-  -moz-transition: opacity 200ms ease-out;
+  transition: opacity 200ms ease-out;
 }
 
 .statuspanel-inner {
   height: 3em;
   width: 100%;
   -moz-box-align: end;
 }
 
@@ -584,19 +584,19 @@ vbox[anonid=browserContainer][responsive
   overflow: auto;
 }
 
 .devtools-responsiveui-toolbar:-moz-locale-dir(rtl) {
   -moz-box-pack: end;
 }
 
 stack[anonid=browserStack][responsivemode] {
-  -moz-transition-duration: 200ms;
-  -moz-transition-timing-function: linear;
+  transition-duration: 200ms;
+  transition-timing-function: linear;
 }
 
 stack[anonid=browserStack][responsivemode] {
-  -moz-transition-property: min-width, max-width, min-height, max-height;
+  transition-property: min-width, max-width, min-height, max-height;
 }
 
 stack[anonid=browserStack][responsivemode][notransition] {
-  -moz-transition: none;
+  transition: none;
 }
--- a/browser/base/content/highlighter.css
+++ b/browser/base/content/highlighter.css
@@ -20,19 +20,19 @@
 #highlighter-veil-container:not([dim]) > hbox > .highlighter-veil {
   visibility: hidden;
 }
 
 #highlighter-veil-container:not([disable-transitions]) > .highlighter-veil,
 #highlighter-veil-container:not([disable-transitions]) > #highlighter-veil-middlebox,
 #highlighter-veil-container:not([disable-transitions]) > #highlighter-veil-middlebox > .highlighter-veil,
 #highlighter-veil-container:not([disable-transitions]) > #highlighter-veil-middlebox > #highlighter-veil-transparentbox {
-  -moz-transition-property: width, height;
-  -moz-transition-duration: 0.1s;
-  -moz-transition-timing-function: linear;
+  transition-property: width, height;
+  transition-duration: 0.1s;
+  transition-timing-function: linear;
 }
 
 #highlighter-veil-bottombox,
 #highlighter-veil-rightbox {
   -moz-box-flex: 1;
 }
 
 #highlighter-veil-middlebox:-moz-locale-dir(rtl) {
@@ -48,19 +48,19 @@
  */
 
 #highlighter-nodeinfobar-container {
   position: absolute;
   max-width: 95%;
 }
 
 #highlighter-nodeinfobar-container:not([disable-transitions]) {
-  -moz-transition-property: top, left;
-  -moz-transition-duration: 0.1s;
-  -moz-transition-timing-function: linear;
+  transition-property: top, left;
+  transition-duration: 0.1s;
+  transition-timing-function: linear;
 }
 
 #highlighter-nodeinfobar-text {
   overflow: hidden;
   white-space: nowrap;
   text-overflow: ellipsis;
   direction: ltr;
 }
@@ -115,16 +115,16 @@ html|*#highlighter-nodeinfobar-tagname {
 }
 
 #inspector-layoutview-container > iframe {
   /* header size */
   height: 28px;
 }
 
 #inspector-layoutview-container:not([disable-transitions]) > iframe {
-  -moz-transition-property: height;
-  -moz-transition-duration: 0.2s;
+  transition-property: height;
+  transition-duration: 0.2s;
 }
 
 #inspector-layoutview-container > iframe[open] {
   /* header size + layout view size: 28px + 145px */
   height: 173px;
 }
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -63,18 +63,18 @@ input[type=button] {
 
 /* GRID */
 #newtab-grid {
   display: -moz-box;
   -moz-box-flex: 5;
   -moz-box-orient: vertical;
   min-width: 600px;
   min-height: 400px;
-  -moz-transition: 100ms ease-out;
-  -moz-transition-property: opacity;
+  transition: 100ms ease-out;
+  transition-property: opacity;
 }
 
 #newtab-grid[page-disabled] {
   opacity: 0;
 }
 
 #newtab-grid[locked],
 #newtab-grid[page-disabled] {
@@ -94,43 +94,43 @@ input[type=button] {
   display: -moz-box;
   -moz-box-flex: 1;
 }
 
 /* SITES */
 .newtab-site {
   position: relative;
   -moz-box-flex: 1;
-  -moz-transition: 100ms ease-out;
-  -moz-transition-property: top, left, opacity;
+  transition: 100ms ease-out;
+  transition-property: top, left, opacity;
 }
 
 .newtab-site[frozen] {
   position: absolute;
   pointer-events: none;
 }
 
 .newtab-site[dragged] {
-  -moz-transition-property: none;
+  transition-property: none;
   z-index: 10;
 }
 
 /* LINK + THUMBNAILS */
 .newtab-link,
 .newtab-thumbnail {
   position: absolute;
   left: 0;
   top: 0;
   right: 0;
   bottom: 0;
 }
 
 .newtab-thumbnail {
   opacity: .8;
-  -moz-transition: opacity 100ms ease-out;
+  transition: opacity 100ms ease-out;
 }
 
 .newtab-thumbnail[dragged],
 .newtab-link:-moz-focusring > .newtab-thumbnail,
 .newtab-site:hover > .newtab-link > .newtab-thumbnail {
   opacity: 1;
 }
 
@@ -145,17 +145,17 @@ input[type=button] {
   text-overflow: ellipsis;
 }
 
 /* CONTROLS */
 .newtab-control {
   position: absolute;
   top: 4px;
   opacity: 0;
-  -moz-transition: opacity 100ms ease-out;
+  transition: opacity 100ms ease-out;
 }
 
 .newtab-control:-moz-focusring,
 .newtab-site:hover > .newtab-control {
   opacity: 1;
 }
 
 .newtab-control[dragged] {
--- a/browser/base/content/tabbrowser.css
+++ b/browser/base/content/tabbrowser.css
@@ -46,10 +46,10 @@ tabpanels {
   display: none;
 }
 
 .closing-tabs-spacer {
   pointer-events: none;
 }
 
 .tabbrowser-tabs:not(:hover) > .tabbrowser-arrowscrollbox > .closing-tabs-spacer {
-  -moz-transition: width .15s ease-out;
+  transition: width .15s ease-out;
 }
--- a/browser/base/content/test/browser_sanitizeDialog.js
+++ b/browser/base/content/test/browser_sanitizeDialog.js
@@ -460,22 +460,16 @@ var gAllTests = [
     var localStorage = dsm.getLocalStorageForPrincipal(principal, URL);
     localStorage.setItem("test", "value");
 
     // Store something to the offline cache
     const nsICache = Components.interfaces.nsICache;
     var cs = Components.classes["@mozilla.org/network/cache-service;1"]
              .getService(Components.interfaces.nsICacheService);
     var session = cs.createSession(URL + "/manifest", nsICache.STORE_OFFLINE, nsICache.STREAM_BASED);
-    var cacheEntry = session.openCacheEntry(URL, nsICache.ACCESS_READ_WRITE, false);
-    var stream = cacheEntry.openOutputStream(0);
-    var content = "content";
-    stream.write(content, content.length);
-    stream.close();
-    cacheEntry.close();
 
     // Open the dialog
     let wh = new WindowHelper();
     wh.onload = function () {
       this.selectDuration(Sanitizer.TIMESPAN_EVERYTHING);
       // Show details
       this.toggleDetails();
       // Clear only offlineApps
@@ -501,17 +495,30 @@ var gAllTests = [
         {
           // Do not enumerate entries.
           return false;
         }
       };
       cs.visitEntries(visitor);
       is(size, 0, "offline application cache entries evicted");
     };
-    wh.open();
+
+    var cacheListener = {
+      onCacheEntryAvailable: function (entry, access, status) {
+        is(status, Cr.NS_OK);
+        var stream = entry.openOutputStream(0);
+        var content = "content";
+        stream.write(content, content.length);
+        stream.close();
+        entry.close();
+        wh.open();
+      }
+    };
+
+    session.asyncOpenCacheEntry(URL, nsICache.ACCESS_READ_WRITE, cacheListener);
   },
   function () {
     // Test for offline apps permission deletion
 
     // Prepare stuff, we will work with www.example.com
     var URL = "http://www.example.com";
 
     var ios = Cc["@mozilla.org/network/io-service;1"]
--- a/browser/components/places/tests/unit/test_clearHistory_shutdown.js
+++ b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
@@ -61,19 +61,17 @@ let notificationsObserver = {
         do_check_false(stmt.executeStep());
         stmt.reset();
       });
     } finally {
       stmt.finalize();
     }
 
     // Check cache.
-    do_check_false(cacheExists(URL));
-
-    do_test_finished();
+    checkCache(URL);
   }
 }
 
 let timeInMicroseconds = Date.now() * 1000;
 
 function run_test() {
   do_test_pending();
 
@@ -100,17 +98,20 @@ function run_test() {
   print("Add visits.");
   URIS.forEach(function(aUrl) {
     PlacesUtils.history.addVisit(uri(aUrl), timeInMicroseconds++, null,
                                  PlacesUtils.history.TRANSITION_TYPED,
                                  false, 0);
   });
   print("Add cache.");
   storeCache(URL, "testData");
+}
 
+function run_test_continue()
+{
   print("Simulate and wait shutdown.");
   getDistinctNotifications().forEach(
     function (topic)
       Services.obs.addObserver(notificationsObserver, topic, false)
   );
 
   shutdownPlaces();
 
@@ -123,43 +124,51 @@ function getDistinctNotifications() {
   return [ar[i] for (i in ar) if (ar.slice(0, i).indexOf(ar[i]) == -1)];
 }
 
 function storeCache(aURL, aContent) {
   let cache = Cc["@mozilla.org/network/cache-service;1"].
               getService(Ci.nsICacheService);
   let session = cache.createSession("FTP", Ci.nsICache.STORE_ANYWHERE,
                                     Ci.nsICache.STREAM_BASED);
-  let cacheEntry =
-    session.openCacheEntry(aURL, Ci.nsICache.ACCESS_READ_WRITE, false);
 
-  cacheEntry.setMetaDataElement("servertype", "0");
-  var oStream = cacheEntry.openOutputStream(0);
+  var storeCacheListener = {
+    onCacheEntryAvailable: function (entry, access, status) {
+      do_check_eq(status, Cr.NS_OK);
+
+      entry.setMetaDataElement("servertype", "0");
+      var os = entry.openOutputStream(0);
 
-  var written = oStream.write(aContent, aContent.length);
-  if (written != aContent.length) {
-    do_throw("oStream.write has not written all data!\n" +
-             "  Expected: " + written  + "\n" +
-             "  Actual: " + aContent.length + "\n");
-  }
-  oStream.close();
-  cacheEntry.close();
+      var written = os.write(aContent, aContent.length);
+      if (written != aContent.length) {
+        do_throw("os.write has not written all data!\n" +
+                 "  Expected: " + written  + "\n" +
+                 "  Actual: " + aContent.length + "\n");
+      }
+      os.close();
+      entry.close();
+      do_execute_soon(run_test_continue);
+    }
+  };
+
+  session.asyncOpenCacheEntry(aURL,
+                              Ci.nsICache.ACCESS_READ_WRITE,
+                              storeCacheListener);
 }
 
-function cacheExists(aURL) {
+
+function checkCache(aURL) {
   let cache = Cc["@mozilla.org/network/cache-service;1"].
               getService(Ci.nsICacheService);
   let session = cache.createSession("FTP", Ci.nsICache.STORE_ANYWHERE,
                                     Ci.nsICache.STREAM_BASED);
-  try {
-    let cacheEntry =
-      session.openCacheEntry(aURL, Ci.nsICache.ACCESS_READ, true);
-  } catch (e) {
-    if (e.result == Cr.NS_ERROR_CACHE_KEY_NOT_FOUND ||
-        e.result == Cr.NS_ERROR_FAILURE)
-      return false;
- 
-    // Throw the textual error description.
-    do_throw(e);
-  }
-  cacheEntry.close();
-  return true;
+
+  var checkCacheListener = {
+    onCacheEntryAvailable: function (entry, access, status) {
+      do_check_eq(status, Cr.NS_ERROR_CACHE_KEY_NOT_FOUND);
+      do_test_finished();
+    }
+  };
+
+  session.asyncOpenCacheEntry(aURL,
+                              Ci.nsICache.ACCESS_READ,
+                              checkCacheListener);
 }
--- a/browser/components/preferences/applications.js
+++ b/browser/components/preferences/applications.js
@@ -1080,23 +1080,21 @@ var gApplicationsPane = {
    * the last one is the one that the application will use.  That may not be
    * correct, but it's how we've been doing it for years.
    *
    * Perhaps we should instead query navigator.mimeTypes for the set of types
    * supported by the application and then get the plugin from each MIME type's
    * enabledPlugin property.  But if there's a plugin for a type, we need
    * to know about it even if it isn't enabled, since we're going to give
    * the user an option to enable it.
-   * 
-   * I'll also note that my reading of nsPluginTag::RegisterWithCategoryManager
-   * suggests that enabledPlugin is only determined during registration
-   * and does not get updated when plugin.disable_full_page_plugin_for_types
-   * changes (unless modification of that preference spawns reregistration).
-   * So even if we could use enabledPlugin to get the plugin that would be used,
-   * we'd still need to check the pref ourselves to find out if it's enabled.
+   *
+   * Also note that enabledPlugin does not get updated when
+   * plugin.disable_full_page_plugin_for_types changes, so even if we could use
+   * enabledPlugin to get the plugin that would be used, we'd still need to
+   * check the pref ourselves to find out if it's enabled.
    */
   _loadPluginHandlers: function() {
     for (let i = 0; i < navigator.plugins.length; ++i) {
       let plugin = navigator.plugins[i];
       for (let j = 0; j < plugin.length; ++j) {
         let type = plugin[j].type;
 
         let handlerInfoWrapper;
--- a/browser/components/preferences/in-content/applications.js
+++ b/browser/components/preferences/in-content/applications.js
@@ -1067,23 +1067,21 @@ var gApplicationsPane = {
    * the last one is the one that the application will use.  That may not be
    * correct, but it's how we've been doing it for years.
    *
    * Perhaps we should instead query navigator.mimeTypes for the set of types
    * supported by the application and then get the plugin from each MIME type's
    * enabledPlugin property.  But if there's a plugin for a type, we need
    * to know about it even if it isn't enabled, since we're going to give
    * the user an option to enable it.
-   * 
-   * I'll also note that my reading of nsPluginTag::RegisterWithCategoryManager
-   * suggests that enabledPlugin is only determined during registration
-   * and does not get updated when plugin.disable_full_page_plugin_for_types
-   * changes (unless modification of that preference spawns reregistration).
-   * So even if we could use enabledPlugin to get the plugin that would be used,
-   * we'd still need to check the pref ourselves to find out if it's enabled.
+   *
+   * Also note that enabledPlugin does not get updated when
+   * plugin.disable_full_page_plugin_for_types changes, so even if we could use
+   * enabledPlugin to get the plugin that would be used, we'd still need to
+   * check the pref ourselves to find out if it's enabled.
    */
   _loadPluginHandlers: function() {
     for (let i = 0; i < navigator.plugins.length; ++i) {
       let plugin = navigator.plugins[i];
       for (let j = 0; j < plugin.length; ++j) {
         let type = plugin[j].type;
 
         let handlerInfoWrapper;
--- a/browser/components/tabview/iq.js
+++ b/browser/components/tabview/iq.js
@@ -563,29 +563,29 @@ iQClass.prototype = {
       let cStyle = window.getComputedStyle(elem, null);
       for (let prop in css) {
         prop = prop.replace(rupper, "-$1").toLowerCase();
         iQ(elem).css(prop, cStyle.getPropertyValue(prop));
       }
     });
 
     this.css({
-      '-moz-transition-property': Object.keys(css).join(", "),
-      '-moz-transition-duration': (duration / 1000) + 's',
-      '-moz-transition-timing-function': easing
+      'transition-property': Object.keys(css).join(", "),
+      'transition-duration': (duration / 1000) + 's',
+      'transition-timing-function': easing
     });
 
     this.css(css);
 
     let self = this;
     setTimeout(function() {
       self.css({
-        '-moz-transition-property': 'none',
-        '-moz-transition-duration': '',
-        '-moz-transition-timing-function': ''
+        'transition-property': 'none',
+        'transition-duration': '',
+        'transition-timing-function': ''
       });
 
       if (typeof options.complete == "function")
         options.complete.apply(self);
     }, duration);
 
     return this;
   },
--- a/browser/components/tabview/test/browser_tabview_bug580412.js
+++ b/browser/components/tabview/test/browser_tabview_bug580412.js
@@ -22,17 +22,17 @@ function onTabViewShown() {
       ok(!TabView.isVisible(), "TabView is hidden");
       finish();
     });
   }
 
   // we need to stop the setBounds() css animation or else the test will
   // fail in single-mode because the group is newly created "ontabshown".
   let $container = contentWindow.iQ(currentActiveGroup.container);
-  $container.css("-moz-transition-property", "none");
+  $container.css("transition-property", "none");
 
   currentActiveGroup.setPosition(40, 40, true);
   currentActiveGroup.arrange({animate: false});
 
   // move down 20 so we're far enough away from the top.
   checkSnap(currentActiveGroup, 0, 20, contentWindow, function(snapped){
     is(currentActiveGroup.getBounds().top, 60, "group.top is 60px");
     ok(!snapped,"Move away from the edge");
--- a/browser/devtools/styleeditor/StyleEditor.jsm
+++ b/browser/devtools/styleeditor/StyleEditor.jsm
@@ -31,20 +31,20 @@ const UPDATE_STYLESHEET_THROTTLE_DELAY =
 const STYLESHEET_EXPANDO = "-moz-styleeditor-stylesheet-";
 
 const TRANSITIONS_PREF = "devtools.styleeditor.transitions";
 
 const TRANSITION_CLASS = "moz-styleeditor-transitioning";
 const TRANSITION_DURATION_MS = 500;
 const TRANSITION_RULE = "\
 :root.moz-styleeditor-transitioning, :root.moz-styleeditor-transitioning * {\
--moz-transition-duration: " + TRANSITION_DURATION_MS + "ms !important; \
--moz-transition-delay: 0ms !important;\
--moz-transition-timing-function: ease-out !important;\
--moz-transition-property: all !important;\
+transition-duration: " + TRANSITION_DURATION_MS + "ms !important; \
+transition-delay: 0ms !important;\
+transition-timing-function: ease-out !important;\
+transition-property: all !important;\
 }";
 
 /**
  * Style Editor module-global preferences
  */
 const TRANSITIONS_ENABLED = Services.prefs.getBoolPref(TRANSITIONS_PREF);
 
 
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -61,18 +61,18 @@ DEFINES += -D_MSC_VER=$(_MSC_VER)
 endif
 
 ifeq ($(MOZ_CHROME_FILE_FORMAT),jar)
 DEFINES += -DJAREXT=.jar
 else
 DEFINES += -DJAREXT=
 endif
 
-ifdef MOZ_ANGLE
-DEFINES += -DMOZ_ANGLE=$(MOZ_ANGLE)
+ifdef MOZ_ANGLE_RENDERER
+DEFINES += -DMOZ_ANGLE_RENDERER=$(MOZ_ANGLE_RENDERER)
 DEFINES += -DMOZ_D3DX9_DLL=$(MOZ_D3DX9_DLL)
 DEFINES += -DMOZ_D3DCOMPILER_DLL=$(MOZ_D3DCOMPILER_DLL)
 endif
 
 include $(topsrcdir)/ipc/app/defs.mk
 DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME)
 
 # Set MSVC dlls version to package, if any.
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -469,16 +469,19 @@
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/SettingsManager.js
 @BINPATH@/components/SettingsManager.manifest
 @BINPATH@/components/Webapps.js
 @BINPATH@/components/Webapps.manifest
 @BINPATH@/components/AppsService.js
 @BINPATH@/components/AppsService.manifest
+@BINPATH@/components/nsDOMIdentity.js
+@BINPATH@/components/nsIDService.js
+@BINPATH@/components/Identity.manifest
 
 @BINPATH@/components/ContactManager.js
 @BINPATH@/components/ContactManager.manifest
 @BINPATH@/components/AlarmsManager.js
 @BINPATH@/components/AlarmsManager.manifest
 #ifdef ENABLE_MARIONETTE
 @BINPATH@/chrome/marionette@JAREXT@
 @BINPATH@/chrome/marionette.manifest
@@ -503,17 +506,17 @@
 #endif
 
 ; GNOME hooks
 #ifdef MOZ_ENABLE_GNOME_COMPONENT
 @BINPATH@/components/@DLL_PREFIX@mozgnome@DLL_SUFFIX@
 #endif
 
 ; ANGLE GLES-on-D3D rendering library
-#ifdef MOZ_ANGLE
+#ifdef MOZ_ANGLE_RENDERER
 @BINPATH@/libEGL.dll
 @BINPATH@/libGLESv2.dll
 @BINPATH@/@MOZ_D3DX9_DLL@
 @BINPATH@/@MOZ_D3DCOMPILER_DLL@
 #endif
 
 ; [Browser Chrome Files]
 @BINPATH@/chrome/browser@JAREXT@
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -581,17 +581,17 @@ toolbar[mode="full"] .toolbarbutton-1 > 
 #forward-button[disabled] {
   list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled");
 }
 #forward-button[disabled]:-moz-locale-dir(rtl) {
   list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar&state=disabled");
 }
 
 @conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
-  -moz-transition: @forwardTransitionLength@ ease-out;
+  transition: @forwardTransitionLength@ ease-out;
 }
 
 @conditionalForwardWithUrlbar@ > #forward-button[disabled] {
   -moz-transform: scale(0);
   opacity: 0;
   pointer-events: none;
 }
 
@@ -933,18 +933,18 @@ toolbar[iconsize="small"] #feed-button {
   -moz-margin-start: -@conditionalForwardWithUrlbarWidth_small@px;
 }
 
 @conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar {
   pointer-events: all;
 }
 
 @conditionalForwardWithUrlbar@:not([switchingtabs]) + #urlbar-container > #urlbar {
-  -moz-transition: margin-left @forwardTransitionLength@ ease-out,
-                   margin-right @forwardTransitionLength@ ease-out;
+  transition: margin-left @forwardTransitionLength@ ease-out,
+              margin-right @forwardTransitionLength@ ease-out;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar:-moz-locale-dir(ltr) {
   margin-left: -@conditionalForwardWithUrlbarWidth@px;
 }
 @conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar:-moz-locale-dir(rtl) {
   margin-right: -@conditionalForwardWithUrlbarWidth@px;
 }
@@ -1637,23 +1637,23 @@ richlistitem[type~="action"][actiontype=
 .tabbrowser-arrowscrollbox > .scrollbutton-up {
   -moz-border-start: 0;
   -moz-border-end: 2px solid transparent;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-border-start: 2px solid transparent;
   -moz-border-end: 0;
-  -moz-transition: 1s box-shadow ease-out;
+  transition: 1s box-shadow ease-out;
   border-radius: 4px;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down[notifybgtab] {
   box-shadow: 0 0 5px 5px Highlight inset;
-  -moz-transition: none;
+  transition: none;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(ltr),
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(rtl) {
   border-width: 0 2px 0 0;
   border-style: solid;
   -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
 }
--- a/browser/themes/gnomestripe/devtools/debugger.css
+++ b/browser/themes/gnomestripe/devtools/debugger.css
@@ -98,27 +98,27 @@
  */
 
 .variable {
   -moz-margin-start: 1px;
   -moz-margin-end: 1px;
   margin-top: 2px;
   border-bottom: 1px dotted #ddd;
   border-radius: 8px;
-  -moz-transition: background 1s ease-in-out;
+  transition: background 1s ease-in-out;
   background: #fff;
 }
 
 .variable[changed] {
-  -moz-transition-duration: 0.4s;
+  transition-duration: 0.4s;
   background: rgba(255, 255, 0, 0.65);
 }
 
 .variable[added] {
-  -moz-transition-duration: 0.4s;
+  transition-duration: 0.4s;
   background: rgba(0, 255, 0, 0.15);
 }
 
 .variable > .title > .arrow {
   margin-top: -2px;
 }
 
 .variable > .title > .name {
--- a/browser/themes/gnomestripe/devtools/webconsole.css
+++ b/browser/themes/gnomestripe/devtools/webconsole.css
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 .hud-box {
   border-bottom: 1px solid #aaa;
   text-shadow: none;
 }
 
 .hud-box.animated {
-  -moz-transition: height 100ms;
+  transition: height 100ms;
 }
 
 .hud-splitter {
   box-shadow: 0 -1px 0 0 ThreeDShadow inset, 0 0 0 10px -moz-Dialog inset;
 }
 
 .hud-outer-wrapper {
   width: 100%;
--- a/browser/themes/gnomestripe/newtab/newTab.css
+++ b/browser/themes/gnomestripe/newtab/newTab.css
@@ -39,17 +39,17 @@
 
 /* CELLS */
 .newtab-cell {
   -moz-margin-end: 20px;
   background-color: rgba(255,255,255,.2);
   border: 1px solid;
   border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16);
   border-radius: 1px;
-  -moz-transition: border-color 100ms ease-out;
+  transition: border-color 100ms ease-out;
 }
 
 .newtab-cell:empty {
   border: 1px dashed;
   border-color: rgba(8,22,37,.15) rgba(8,22,37,.17) rgba(8,22,37,.19);
 }
 
 .newtab-cell:last-child {
@@ -58,26 +58,26 @@
 
 .newtab-cell:hover:not(:empty):not([dragged]) {
   border-color: rgba(8,22,37,.25) rgba(8,22,37,.27) rgba(8,22,37,.3);
 }
 
 /* SITES */
 .newtab-site {
   text-decoration: none;
-  -moz-transition-property: top, left, opacity, box-shadow, background-color;
+  transition-property: top, left, opacity, box-shadow, background-color;
 }
 
 .newtab-site:hover,
 .newtab-site[dragged] {
   box-shadow: 0 0 10px rgba(8,22,37,.3);
 }
 
 .newtab-site[dragged] {
-  -moz-transition-property: box-shadow, background-color;
+  transition-property: box-shadow, background-color;
   background-color: rgb(242,242,242);
 }
 
 /* THUMBNAILS */
 .newtab-thumbnail {
   background-origin: padding-box;
   background-clip: padding-box;
   background-repeat: no-repeat;
--- a/browser/themes/gnomestripe/tabview/tabview.css
+++ b/browser/themes/gnomestripe/tabview/tabview.css
@@ -121,19 +121,19 @@ html[dir=rtl] .expander {
 
 .expander:hover {
   opacity: 1.0;
 }
 
 .close:hover,
 .expander:hover,
 .appTabIcon:hover {
-  -moz-transition-property: opacity;
-  -moz-transition-duration: 0.5s;
-  -moz-transition-timing-function: ease-out;
+  transition-property: opacity;
+  transition-duration: 0.5s;
+  transition-timing-function: ease-out;
 }
 
 .favicon img:hover, 
 .close img:hover, 
 .expander img:hover {
   opacity: 1;
   border: none;
 }
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -521,17 +521,17 @@ toolbar[mode="icons"] #forward-button {
 }
 
 @conditionalForwardWithUrlbar@ > #forward-button:-moz-lwtheme {
   -moz-padding-start: 2px;
   -moz-padding-end: 0;
 }
 
 @conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
-  -moz-transition: opacity @forwardTransitionLength@ ease-out;
+  transition: opacity @forwardTransitionLength@ ease-out;
 }
 
 @conditionalForwardWithUrlbar@ > #forward-button:hover:active:not(:-moz-lwtheme) {
   background-image: -moz-linear-gradient(hsl(0,0%,74%), hsl(0,0%,61%));
   box-shadow: inset rgba(0,0,0,.3) 0 -6px 10px,
               inset #000 0 1px 3px,
               inset rgba(0,0,0,.2) 0 1px 3px,
               0 1px 0 hsla(0,0%,100%,.2);
@@ -890,17 +890,17 @@ toolbar[mode="icons"] #zoom-in-button {
 
 @conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar {
   -moz-border-start: none;
   margin-left: 0;
   pointer-events: all;
 }
 
 @conditionalForwardWithUrlbar@:not([switchingtabs]) + #urlbar-container > #urlbar {
-  -moz-transition: margin-left @forwardTransitionLength@ ease-out;
+  transition: margin-left @forwardTransitionLength@ ease-out;
 }
 
 @conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar:-moz-locale-dir(ltr) {
   border-top-left-radius: 0;
   border-bottom-left-radius: 0;
 }
 
 @conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar:-moz-locale-dir(rtl) {
@@ -913,17 +913,17 @@ toolbar[mode="icons"] #zoom-in-button {
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar {
   margin-left: -@conditionalForwardWithUrlbarWidth@px;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) + #urlbar-container > #urlbar {
   /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
-  -moz-transition-delay: 100s;
+  transition-delay: 100s;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) + #urlbar-container > #urlbar {
   /* when not hovered anymore, trigger a new transition to hide the forward button immediately */
   margin-left: -@conditionalForwardWithUrlbarWidth@.01px;
 }
 
 @conditionalForwardWithUrlbar@ + #urlbar-container:-moz-locale-dir(rtl),
@@ -956,28 +956,28 @@ toolbar[mode="icons"] #zoom-in-button {
   border-radius: 0;
 }
 
 @conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar > #identity-box {
   border-radius: 0;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
-  -moz-transition: 0s padding-left;
+  transition: 0s padding-left;
   padding-left: 10px;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
-  -moz-transition: 0s padding-right;
+  transition: 0s padding-right;
   padding-right: 10px;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box {
   /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
-  -moz-transition-delay: 100s;
+  transition-delay: 100s;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
   padding-left: 10.01px;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
   padding-right: 10.01px;
@@ -1668,19 +1668,19 @@ toolbarbutton.chevron > .toolbarbutton-m
   list-style-image: url("chrome://browser/skin/tabbrowser/loading.png");
 }
 
 .tabbrowser-tab:not(:hover) > .tab-stack > .tab-content > .tab-icon-image:not([selected="true"]) {
   opacity: .8;
 }
 
 .tabbrowser-tab:not([pinned]):not([fadein]) {
-  -moz-transition: min-width 200ms ease-out /* copied from browser/base/content/browser.css */,
-                   max-width 250ms ease-out /* copied from browser/base/content/browser.css */,
-                   opacity 50ms ease-out 100ms /* hide the tab for the last 100ms of the max-width transition */;
+  transition: min-width 200ms ease-out /* copied from browser/base/content/browser.css */,
+              max-width 250ms ease-out /* copied from browser/base/content/browser.css */,
+              opacity 50ms ease-out 100ms /* hide the tab for the last 100ms of the max-width transition */;
 }
 
 .tab-stack {
   /* ensure stable tab height with and without toolbarbuttons on the tab bar */
   height: 26px;
 }
 
 .tabbrowser-tab,
@@ -2010,22 +2010,22 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up {
   -moz-border-end: 2px solid transparent;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-border-start: 2px solid transparent;
-  -moz-transition: 1s background-color ease-out;
+  transition: 1s background-color ease-out;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down[notifybgtab] {
   background-color: Highlight;
-  -moz-transition: none;
+  transition: none;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(ltr),
 .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(rtl) {
   list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png");
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(ltr),
@@ -2732,16 +2732,30 @@ panel[dimmed="true"] {
   -moz-border-start: 1px solid #242b33;
   min-width: 0;
   width: 3px;
   background-color: transparent;
   -moz-margin-end: -3px;
   position: relative;
 }
 
+/* Lion Fullscreen window styling */
+@media (-moz-mac-lion-theme) {
+  #navigator-toolbox[inFullscreen][tabsontop="true"]:not(:-moz-lwtheme)::before {
+    height: 36px;
+  }
+  #main-window[inFullscreen]:-moz-lwtheme {
+    /* This additional padding matches the change in height in the pseudo-element
+     * above. The rules combined force the top 22px of the background image to
+     * be hidden, so there image doesn't jump around with the loss of the titlebar */
+    padding-top: 11px;
+    background-position: right -11px;
+  }
+}
+
 #devtools-sidebar-box {
   background-color: -moz-Field;
 }
 
 /* Highlighter - Node Infobar */
 
 #highlighter-nodeinfobar {
   color: hsl(200, 100%, 65%);
--- a/browser/themes/pinstripe/devtools/debugger.css
+++ b/browser/themes/pinstripe/devtools/debugger.css
@@ -100,27 +100,27 @@
  */
 
 .variable {
   -moz-margin-start: 1px;
   -moz-margin-end: 1px;
   margin-top: 2px;
   border-bottom: 1px dotted #ddd;
   border-radius: 8px;
-  -moz-transition: background 1s ease-in-out;
+  transition: background 1s ease-in-out;
   background: #fff;
 }
 
 .variable[changed] {
-  -moz-transition-duration: 0.4s;
+  transition-duration: 0.4s;
   background: rgba(255, 255, 0, 0.65);
 }
 
 .variable[added] {
-  -moz-transition-duration: 0.4s;
+  transition-duration: 0.4s;
   background: rgba(0, 255, 0, 0.15);
 }
 
 .variable > .title > .arrow {
   margin-top: -2px;
 }
 
 .variable > .title > .name {
--- a/browser/themes/pinstripe/devtools/webconsole.css
+++ b/browser/themes/pinstripe/devtools/webconsole.css
@@ -5,17 +5,17 @@
 %include ../shared.inc
 
 .hud-box {
   border-bottom: 1px solid #aaa;
   text-shadow: none;
 }
 
 .hud-box.animated {
-  -moz-transition: height 100ms;
+  transition: height 100ms;
 }
 
 .hud-splitter {
   border-bottom: solid #a5a5a5 1px;
   background: url("chrome://global/skin/splitter/dimple.png") no-repeat center,
     -moz-linear-gradient(top, #fcfcfc, #dfdfdf);
 }
 
--- a/browser/themes/pinstripe/newtab/newTab.css
+++ b/browser/themes/pinstripe/newtab/newTab.css
@@ -39,17 +39,17 @@
 
 /* CELLS */
 .newtab-cell {
   -moz-margin-end: 20px;
   background-color: rgba(255,255,255,.2);
   border: 1px solid;
   border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16);
   border-radius: 1px;
-  -moz-transition: border-color 100ms ease-out;
+  transition: border-color 100ms ease-out;
 }
 
 .newtab-cell:empty {
   border: 1px dashed;
   border-color: rgba(8,22,37,.15) rgba(8,22,37,.17) rgba(8,22,37,.19);
 }
 
 .newtab-cell:last-child {
@@ -58,26 +58,26 @@
 
 .newtab-cell:hover:not(:empty):not([dragged]) {
   border-color: rgba(8,22,37,.25) rgba(8,22,37,.27) rgba(8,22,37,.3);
 }
 
 /* SITES */
 .newtab-site {
   text-decoration: none;
-  -moz-transition-property: top, left, opacity, box-shadow, background-color;
+  transition-property: top, left, opacity, box-shadow, background-color;
 }
 
 .newtab-site:hover,
 .newtab-site[dragged] {
   box-shadow: 0 0 10px rgba(8,22,37,.3);
 }
 
 .newtab-site[dragged] {
-  -moz-transition-property: box-shadow, background-color;
+  transition-property: box-shadow, background-color;
   background-color: rgb(242,242,242);
 }
 
 /* THUMBNAILS */
 .newtab-thumbnail {
   background-origin: padding-box;
   background-clip: padding-box;
   background-repeat: no-repeat;
--- a/browser/themes/pinstripe/tabview/tabview.css
+++ b/browser/themes/pinstripe/tabview/tabview.css
@@ -102,33 +102,33 @@ html[dir=rtl] .close {
 }
 
 .expander {
   bottom: 8px;
   right: 6px;
   width: 16px;
   height: 16px;
   background: url(chrome://global/skin/icons/resizer.png) no-repeat;
-  -moz-transition-property: opacity;
-  -moz-transition-duration: 0.5s;
-  -moz-transition-timing-function: ease-out;
+  transition-property: opacity;
+  transition-duration: 0.5s;
+  transition-timing-function: ease-out;
   opacity: 0.2;
 }
 
 html[dir=rtl] .expander {
   right: auto;
   left: 6px;
   -moz-transform: scaleX(-1);
 }
 
 .expander:hover,
 .appTabIcon:hover {
-  -moz-transition-property: opacity;
-  -moz-transition-duration: 0.5s;
-  -moz-transition-timing-function: ease-out;
+  transition-property: opacity;
+  transition-duration: 0.5s;
+  transition-timing-function: ease-out;
   opacity: 1.0;
 }
 
 .favicon img:hover, 
 .expander img:hover {
   opacity: 1;
   border: none;
 }
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -354,17 +354,17 @@
     border-top: 1px solid #d6e5f5;
     border-bottom: none;
   }
 
   .appmenu-edit-button:not([disabled]):hover {
     border: 1px solid #b8d6fb;
     box-shadow: inset 0 0 1px white;
     background: -moz-linear-gradient(#fafbfd, #ebf3fd);
-    -moz-transition: .2s ease-in;
+    transition: .2s ease-in;
   }
 }
 
 #appmenuSecondaryPane-spacer {
   min-height: 1em;
 }
 
 #appmenu-editmenu {
@@ -694,18 +694,18 @@ toolbar[mode=full] .toolbarbutton-1 > .t
   padding: 2px 6px;
   background: hsla(210,32%,93%,0) padding-box;
   border-radius: 2px;
   border: 1px solid;
   border-color: hsla(210,54%,20%,0) hsla(210,54%,20%,0) hsla(210,54%,20%,0);
   box-shadow: 0 1px hsla(0,0%,100%,0) inset,
               0 1px hsla(210,54%,20%,0),
               0 0 2px hsla(210,54%,20%,0);
-  -moz-transition-property: background-color, border-color, box-shadow;
-  -moz-transition-duration: 150ms;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 150ms;
 }
 
 @navbarLargeIcons@ .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon,
 @navbarLargeIcons@ .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
   padding: 3px 7px;
 }
 
 @navbarLargeIcons@ .toolbarbutton-1[type=menu]:not(#back-button):not(#forward-button):not(#feed-button) > .toolbarbutton-icon,
@@ -774,26 +774,26 @@ toolbar[mode=full] .toolbarbutton-1 > .t
   background-color: hsla(210,54%,20%,.15);
   border-color: hsla(210,54%,20%,.3) hsla(210,54%,20%,.35) hsla(210,54%,20%,.4);
   box-shadow: 0 1px 1px hsla(210,54%,20%,.1) inset,
               0 0 1px hsla(210,54%,20%,.2) inset,
               /* allows winstripe-keyhole-forward-clip-path to be used for non-hover as well as hover: */
               0 1px 0 hsla(210,54%,20%,0),
               0 0 2px hsla(210,54%,20%,0);
   text-shadow: none;
-  -moz-transition: none;
+  transition: none;
 }
 
 @navbarLargeIcons@ .toolbarbutton-1:-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon {
   -moz-border-start-color: hsla(210,54%,20%,.35);
 }
 
 @navbarLargeIcons@ .toolbarbutton-1[checked]:not(:active):hover > .toolbarbutton-icon {
   background-color: rgba(90%,90%,90%,.4);
-  -moz-transition: background-color .4s;
+  transition: background-color .4s;
 }
 
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1,
 :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button,
 .tabbrowser-arrowscrollbox > .scrollbutton-up,
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
   -moz-appearance: none;
   border-style: none;
@@ -858,17 +858,17 @@ toolbar[mode=full] .toolbarbutton-1 > .t
   -moz-margin-start: -6px !important;
   border-left-style: none;
   border-radius: 0;
   padding-left: 7px;
   padding-right: 3px;
 }
 
 @conditionalForwardWithUrlbar@:not([switchingtabs]) > #forward-button {
-  -moz-transition: opacity @forwardTransitionLength@ ease-out;
+  transition: opacity @forwardTransitionLength@ ease-out;
 }
 
 @conditionalForwardWithUrlbar@:not(:hover) > #forward-button[disabled] {
   opacity: 0;
 }
 
 @conditionalForwardWithUrlbar@ > #back-button {
   -moz-image-region: rect(18px, 20px, 38px, 0);
@@ -893,18 +893,18 @@ toolbar[mode=full] .toolbarbutton-1 > .t
   border-radius: 10000px;
   padding: 5px;
   border: none;
   background-image: -moz-linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.1));
   box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
               0 0 0 1px hsla(0,0%,100%,.3) inset,
               0 0 0 1px hsla(210,54%,20%,.25),
               0 1px 0 hsla(210,54%,20%,.35);
-  -moz-transition-property: background-color, box-shadow;
-  -moz-transition-duration: 250ms;
+  transition-property: background-color, box-shadow;
+  transition-duration: 250ms;
 }
 
 @conditionalForwardWithUrlbar@ > #back-button:not([disabled="true"]):not([open="true"]):not(:active):hover > .toolbarbutton-icon {
   background-color: hsla(210,48%,96%,.75);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.3) inset,
               0 0 0 1px hsla(0,0%,100%,.3) inset,
               0 0 0 1px hsla(210,54%,20%,.3),
               0 1px 0 hsla(210,54%,20%,.4),
@@ -913,23 +913,23 @@ toolbar[mode=full] .toolbarbutton-1 > .t
 
 @conditionalForwardWithUrlbar@ > #back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon,
 @conditionalForwardWithUrlbar@ > #back-button[open="true"] > .toolbarbutton-icon {
   background-color: hsla(210,54%,20%,.15);
   box-shadow: 0 1px 1px hsla(210,54%,20%,.1) inset,
               0 0 1px hsla(210,54%,20%,.2) inset,
               0 0 0 1px hsla(210,54%,20%,.4),
               0 1px 0 hsla(210,54%,20%,.2);
-  -moz-transition: none;
+  transition: none;
 }
 
 @conditionalForwardWithUrlbar@ > #back-button[disabled] > .toolbarbutton-icon {
   box-shadow: 0 0 0 1px hsla(210,54%,20%,.55),
               0 1px 0 hsla(210,54%,20%,.65);
-  -moz-transition: none;
+  transition: none;
 }
 
 .unified-nav-back[_moz-menuactive]:-moz-locale-dir(ltr),
 .unified-nav-forward[_moz-menuactive]:-moz-locale-dir(rtl) {
   list-style-image: url("chrome://browser/skin/menu-back.png") !important;
 }
 
 .unified-nav-forward[_moz-menuactive]:-moz-locale-dir(ltr),
@@ -1197,17 +1197,17 @@ toolbar[mode=full] .toolbarbutton-1 > .t
 
 @conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar {
   -moz-border-start: none;
   margin-left: 0;
   pointer-events: all;
 }
 
 @conditionalForwardWithUrlbar@:not([switchingtabs]) + #urlbar-container > #urlbar {
-  -moz-transition: margin-left @forwardTransitionLength@ ease-out;
+  transition: margin-left @forwardTransitionLength@ ease-out;
 }
 
 @conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar:-moz-locale-dir(ltr) {
   border-top-left-radius: 0;
   border-bottom-left-radius: 0;
 }
 
 @conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar:-moz-locale-dir(rtl) {
@@ -1220,17 +1220,17 @@ toolbar[mode=full] .toolbarbutton-1 > .t
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar {
   margin-left: -@conditionalForwardWithUrlbarWidth@px;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) + #urlbar-container > #urlbar {
   /* delay the hiding of the forward button when hovered to avoid accidental clicks on the url bar */
-  -moz-transition-delay: 100s;
+  transition-delay: 100s;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) + #urlbar-container > #urlbar {
   /* when not hovered anymore, trigger a new transition to hide the forward button immediately */
   margin-left: -@conditionalForwardWithUrlbarWidth@.01px;
 }
 
 @conditionalForwardWithUrlbar@ + #urlbar-container:-moz-locale-dir(rtl),
@@ -1336,27 +1336,27 @@ html|*.urlbar-input:-moz-lwtheme:-moz-pl
 }
 
 @conditionalForwardWithUrlbar@ + #urlbar-container > #urlbar > #identity-box {
   border-radius: 0;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
   padding-left: 5px;
-  -moz-transition: padding-left;
+  transition: padding-left;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled] + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
   padding-right: 5px;
-  -moz-transition: padding-right;
+  transition: padding-right;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled]:hover:not([switchingtabs]) + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box {
   /* forward button hiding is delayed when hovered */
-  -moz-transition-delay: 100s;
+  transition-delay: 100s;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(ltr) {
   /* when not hovered anymore, trigger a new non-delayed transition to react to the forward button hiding */
   padding-left: 5.01px;
 }
 
 @conditionalForwardWithUrlbar@[forwarddisabled]:not(:hover) + #urlbar-container > #urlbar > #notification-popup-box[hidden] + #identity-box:-moz-locale-dir(rtl) {
@@ -1960,22 +1960,22 @@ richlistitem[type~="action"][actiontype=
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(rtl),
 .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(ltr) {
   -moz-transform: scaleX(-1);
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down {
-  -moz-transition: 1s background-color ease-out;
+  transition: 1s background-color ease-out;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-down[notifybgtab] {
   background-color: Highlight;
-  -moz-transition: none;
+  transition: none;
 }
 
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]),
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]) {
   border-width: 0 2px 0 0;
   border-style: solid;
   -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
 }
--- a/browser/themes/winstripe/devtools/common.css
+++ b/browser/themes/winstripe/devtools/common.css
@@ -87,19 +87,19 @@
   background-color: transparent;
   background-image: url(magnifying-glass.png), -moz-linear-gradient(hsla(210,16%,76%,.15), hsla(210,16%,76%,.35));
   background-repeat: no-repeat;
   background-position: 4px center, top left, top left;
   padding-top: 0;
   padding-bottom: 0;
   -moz-padding-start: 18px;
   -moz-padding-end: 12px;
-  -moz-transition-property: background-color, border-color, box-shadow;
-  -moz-transition-duration: 150ms;
-  -moz-transition-timing-function: ease;
+  transition-property: background-color, border-color, box-shadow;
+  transition-duration: 150ms;
+  transition-timing-function: ease;
   color: inherit;
 }
 
 .devtools-searchinput[focused] {
   border-color: hsl(200,70%,40%) hsl(200,75%,37%) hsl(200,80%,35%);
   background-origin: padding-box;
   background-clip: padding-box;
   box-shadow: inset 0 0 0 1px hsla(211,68%,6%,.1);
--- a/browser/themes/winstripe/devtools/debugger.css
+++ b/browser/themes/winstripe/devtools/debugger.css
@@ -98,27 +98,27 @@
  */
 
 .variable {
   -moz-margin-start: 1px;
   -moz-margin-end: 1px;
   margin-top: 2px;
   border-bottom: 1px dotted #ddd;
   border-radius: 8px;
-  -moz-transition: background 1s ease-in-out;
+  transition: background 1s ease-in-out;
   background: #fff;
 }
 
 .variable[changed] {
-  -moz-transition-duration: 0.4s;
+  transition-duration: 0.4s;
   background: rgba(255, 255, 0, 0.65);
 }
 
 .variable[added] {
-  -moz-transition-duration: 0.4s;
+  transition-duration: 0.4s;
   background: rgba(0, 255, 0, 0.15);
 }
 
 .variable > .title > .arrow {
   margin-top: -2px;
 }
 
 .variable > .title > .name {
--- a/browser/themes/winstripe/devtools/webconsole.css
+++ b/browser/themes/winstripe/devtools/webconsole.css
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 .hud-box {
   border-bottom: 1px solid #aaa;
   text-shadow: none;
 }
 
 .hud-box.animated {
-  -moz-transition: height 100ms;
+  transition: height 100ms;
 }
 
 .hud-splitter {
   border-top: none;
 }
 
 .hud-outer-wrapper {
   width: 100%;
--- a/browser/themes/winstripe/newtab/newTab.css
+++ b/browser/themes/winstripe/newtab/newTab.css
@@ -39,17 +39,17 @@
 
 /* CELLS */
 .newtab-cell {
   -moz-margin-end: 20px;
   background-color: rgba(255,255,255,.2);
   border: 1px solid;
   border-color: rgba(8,22,37,.12) rgba(8,22,37,.14) rgba(8,22,37,.16);
   border-radius: 1px;
-  -moz-transition: border-color 100ms ease-out;
+  transition: border-color 100ms ease-out;
 }
 
 .newtab-cell:empty {
   border: 1px dashed;
   border-color: rgba(8,22,37,.15) rgba(8,22,37,.17) rgba(8,22,37,.19);
 }
 
 .newtab-cell:last-child {
@@ -58,26 +58,26 @@
 
 .newtab-cell:hover:not(:empty):not([dragged]) {
   border-color: rgba(8,22,37,.25) rgba(8,22,37,.27) rgba(8,22,37,.3);
 }
 
 /* SITES */
 .newtab-site {
   text-decoration: none;
-  -moz-transition-property: top, left, opacity, box-shadow, background-color;
+  transition-property: top, left, opacity, box-shadow, background-color;
 }
 
 .newtab-site:hover,
 .newtab-site[dragged] {
   box-shadow: 0 0 10px rgba(8,22,37,.3);
 }
 
 .newtab-site[dragged] {
-  -moz-transition-property: box-shadow, background-color;
+  transition-property: box-shadow, background-color;
   background-color: rgb(242,242,242);
 }
 
 /* THUMBNAILS */
 .newtab-thumbnail {
   background-origin: padding-box;
   background-clip: padding-box;
   background-repeat: no-repeat;
--- a/browser/themes/winstripe/tabview/tabview.css
+++ b/browser/themes/winstripe/tabview/tabview.css
@@ -103,33 +103,33 @@ html[dir=rtl] .close {
 }
 
 .expander {
   bottom: 6px;
   right: 6px;
   width: 16px;
   height: 16px;
   background: url(chrome://global/skin/icons/resizer.png) no-repeat;
-  -moz-transition-property: opacity;
-  -moz-transition-duration: 0.5s;
-  -moz-transition-timing-function: ease-out;
+  transition-property: opacity;
+  transition-duration: 0.5s;
+  transition-timing-function: ease-out;
   opacity: 0.2;
 }
 
 html[dir=rtl] .expander {
   right: auto;
   left: 6px;
   -moz-transform: scaleX(-1);
 }
 
 .expander:hover,
 .appTabIcon:hover {
-  -moz-transition-property: opacity;
-  -moz-transition-duration: 0.5s;
-  -moz-transition-timing-function: ease-out;
+  transition-property: opacity;
+  transition-duration: 0.5s;
+  transition-timing-function: ease-out;
   opacity: 1.0;
 }
 
 .favicon img:hover, 
 .close img:hover, 
 .expander img:hover {
   opacity: 1;
   border: none;
--- a/build/mobile/b2gautomation.py
+++ b/build/mobile/b2gautomation.py
@@ -82,16 +82,21 @@ class B2GRemoteAutomation(Automation):
         dumpDir = tempfile.mkdtemp()
         self._devicemanager.getDirectory(self._remoteProfile + '/minidumps/', dumpDir)
         automationutils.checkForCrashes(dumpDir, symbolsPath, self.lastTestSeen)
         try:
           shutil.rmtree(dumpDir)
         except:
           print "WARNING: unable to remove directory: %s" % (dumpDir)
 
+    def initializeProfile(self, profileDir, extraPrefs = [], useServerLocations = False):
+        # add b2g specific prefs
+        extraPrefs.extend(["browser.manifestURL='dummy (bug 772307)'"])
+        return Automation.initializeProfile(self, profileDir, extraPrefs, useServerLocations)
+
     def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs):
         # if remote profile is specified, use that instead
         if (self._remoteProfile):
             profileDir = self._remoteProfile
 
         cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs)
 
         return app, args
--- a/build/pymake/pymake/parserdata.py
+++ b/build/pymake/pymake/parserdata.py
@@ -24,17 +24,17 @@ class Location(object):
     def offset(self, s, start, end):
         """
         Returns a new location offset by
         the specified string.
         """
 
         if start == end:
             return self
-        
+
         skiplines = s.count('\n', start, end)
         line = self.line + skiplines
         if skiplines:
             lastnl = s.rfind('\n', start, end)
             assert lastnl != -1
             start = lastnl + 1
             column = 0
         else:
@@ -93,35 +93,53 @@ def parsecommandlineargs(args):
                                      targetexp=None, source=data.Variables.SOURCE_COMMANDLINE))
         else:
             r.append(a)
 
     return stmts, r, ' '.join(overrides)
 
 class Statement(object):
     """
-    A statement is an abstract object representing a single "chunk" of makefile syntax. Subclasses
-    must implement the following method:
+    Represents parsed make file syntax.
 
-    def execute(self, makefile, context)
+    This is an abstract base class. Child classes are expected to implement
+    `execute()`.
     """
 
+    def execute(self, makefile, context):
+        raise Exception("Must implement execute() in child classes.")
+
 class DummyRule(object):
     __slots__ = ()
 
     def addcommand(self, r):
         pass
 
 class Rule(Statement):
+    """
+    Rules represent how to make specific targets.
+
+    See https://www.gnu.org/software/make/manual/make.html#Rules.
+
+    An individual rule is composed of a target, dependencies, and a recipe.
+    This class only contains references to the first 2. The recipe will be
+    contained in Command classes which follow this one in a stream of Statement
+    instances.
+
+    Instances also contain a boolean property `doublecolon` which says whether
+    this is a doublecolon rule. Doublecolon rules are rules that are always
+    executed, if they are evaluated. Normally, rules are only executed if their
+    target is out of date.
+    """
     __slots__ = ('targetexp', 'depexp', 'doublecolon')
 
     def __init__(self, targetexp, depexp, doublecolon):
         assert isinstance(targetexp, (data.Expansion, data.StringExpansion))
         assert isinstance(depexp, (data.Expansion, data.StringExpansion))
-        
+
         self.targetexp = targetexp
         self.depexp = depexp
         self.doublecolon = doublecolon
 
     def execute(self, makefile, context):
         atargets = data.stripdotslashes(self.targetexp.resolvesplit(makefile, makefile.variables))
         targets = [data.Pattern(p) for p in _expandwildcards(makefile, atargets)]
 
@@ -149,16 +167,25 @@ class Rule(Statement):
             makefile.foundtarget(targets[0].gettarget())
 
         context.currule = rule
 
     def dump(self, fd, indent):
         print >>fd, "%sRule %s: %s" % (indent, self.targetexp, self.depexp)
 
 class StaticPatternRule(Statement):
+    """
+    Static pattern rules are rules which specify multiple targets based on a
+    string pattern.
+
+    See https://www.gnu.org/software/make/manual/make.html#Static-Pattern
+
+    They are like `Rule` instances except an added property, `patternexp` is
+    present. It contains the Expansion which represents the rule pattern.
+    """
     __slots__ = ('targetexp', 'patternexp', 'depexp', 'doublecolon')
 
     def __init__(self, targetexp, patternexp, depexp, doublecolon):
         assert isinstance(targetexp, (data.Expansion, data.StringExpansion))
         assert isinstance(patternexp, (data.Expansion, data.StringExpansion))
         assert isinstance(depexp, (data.Expansion, data.StringExpansion))
 
         self.targetexp = targetexp
@@ -195,16 +222,26 @@ class StaticPatternRule(Statement):
 
         makefile.foundtarget(targets[0])
         context.currule = rule
 
     def dump(self, fd, indent):
         print >>fd, "%sStaticPatternRule %s: %s: %s" % (indent, self.targetexp, self.patternexp, self.depexp)
 
 class Command(Statement):
+    """
+    Commands are things that get executed by a rule.
+
+    A rule's recipe is composed of 0 or more Commands.
+
+    A command is simply an expansion. Commands typically represent strings to
+    be executed in a shell (e.g. via system()). Although, since make files
+    allow arbitrary shells to be used for command execution, this isn't a
+    guarantee.
+    """
     __slots__ = ('exp',)
 
     def __init__(self, exp):
         assert isinstance(exp, (data.Expansion, data.StringExpansion))
         self.exp = exp
 
     def execute(self, makefile, context):
         assert context.currule is not None
@@ -212,16 +249,36 @@ class Command(Statement):
             raise data.DataError("rules not allowed in includedeps", self.exp.loc)
 
         context.currule.addcommand(self.exp)
 
     def dump(self, fd, indent):
         print >>fd, "%sCommand %s" % (indent, self.exp,)
 
 class SetVariable(Statement):
+    """
+    Represents a variable assignment.
+
+    Variable assignment comes in two different flavors.
+
+    Simple assignment has the form:
+
+      <Expansion> <Assignment Token> <string>
+
+    e.g. FOO := bar
+
+    These correspond to the fields `vnameexp`, `token`, and `value`. In
+    addition, `valueloc` will be a Location and `source` will be a
+    pymake.data.Variables.SOURCE_* constant.
+
+    There are also target-specific variables. These are variables that only
+    apply in the context of a specific target. They are like the aforementioned
+    assignment except the `targetexp` field is set to an Expansion representing
+    the target they apply to.
+    """
     __slots__ = ('vnameexp', 'token', 'value', 'valueloc', 'targetexp', 'source')
 
     def __init__(self, vnameexp, token, value, valueloc, targetexp, source=None):
         assert isinstance(vnameexp, (data.Expansion, data.StringExpansion))
         assert isinstance(value, str)
         assert targetexp is None or isinstance(targetexp, (data.Expansion, data.StringExpansion))
 
         if source is None:
@@ -275,22 +332,35 @@ class SetVariable(Statement):
 
             v.set(vname, flavor, self.source, value)
 
     def dump(self, fd, indent):
         print >>fd, "%sSetVariable<%s> %s %s\n%s %r" % (indent, self.valueloc, self.vnameexp, self.token, indent, self.value)
 
 class Condition(object):
     """
-    An abstract "condition", either ifeq or ifdef, perhaps negated. Subclasses must implement:
+    An abstract "condition", either ifeq or ifdef, perhaps negated.
+
+    See https://www.gnu.org/software/make/manual/make.html#Conditional-Syntax
+
+    Subclasses must implement:
 
     def evaluate(self, makefile)
     """
 
 class EqCondition(Condition):
+    """
+    Represents an ifeq or ifneq conditional directive.
+
+    This directive consists of two Expansions which are compared for equality.
+
+    The `expected` field is a bool indicating what the condition must evaluate
+    to in order for its body to be executed. If True, this is an "ifeq"
+    conditional directive. If False, an "ifneq."
+    """
     __slots__ = ('exp1', 'exp2', 'expected')
 
     def __init__(self, exp1, exp2):
         assert isinstance(exp1, (data.Expansion, data.StringExpansion))
         assert isinstance(exp2, (data.Expansion, data.StringExpansion))
 
         self.expected = True
         self.exp1 = exp1
@@ -300,16 +370,25 @@ class EqCondition(Condition):
         r1 = self.exp1.resolvestr(makefile, makefile.variables)
         r2 = self.exp2.resolvestr(makefile, makefile.variables)
         return (r1 == r2) == self.expected
 
     def __str__(self):
         return "ifeq (expected=%s) %s %s" % (self.expected, self.exp1, self.exp2)
 
 class IfdefCondition(Condition):
+    """
+    Represents an ifdef or ifndef conditional directive.
+
+    This directive consists of a single expansion which represents the name of
+    a variable (without the leading '$') which will be checked for definition.
+
+    The `expected` field is a bool and has the same behavior as EqCondition.
+    If it is True, this represents a "ifdef" conditional. If False, "ifndef."
+    """
     __slots__ = ('exp', 'expected')
 
     def __init__(self, exp):
         assert isinstance(exp, (data.Expansion, data.StringExpansion))
         self.exp = exp
         self.expected = True
 
     def evaluate(self, makefile):
@@ -320,27 +399,41 @@ class IfdefCondition(Condition):
             return not self.expected
 
         return (len(value) > 0) == self.expected
 
     def __str__(self):
         return "ifdef (expected=%s) %s" % (self.expected, self.exp)
 
 class ElseCondition(Condition):
+    """
+    Represents the transition between branches in a ConditionBlock.
+    """
     __slots__ = ()
 
     def evaluate(self, makefile):
         return True
 
     def __str__(self):
         return "else"
 
 class ConditionBlock(Statement):
     """
-    A list of conditions: each condition has an associated list of statements.
+    A set of related Conditions.
+
+    This is essentially a list of 2-tuples of (Condition, list(Statement)).
+
+    The parser creates a ConditionBlock for all statements related to the same
+    conditional group. If iterating over the parser's output, where you think
+    you would see an ifeq, you will see a ConditionBlock containing an IfEq. In
+    other words, the parser collapses separate statements into this container
+    class.
+
+    ConditionBlock instances may exist within other ConditionBlock if the
+    conditional logic is multiple levels deep.
     """
     __slots__ = ('loc', '_groups')
 
     def __init__(self, loc, condition):
         self.loc = loc
         self._groups = []
         self.addcondition(loc, condition)
 
@@ -384,16 +477,25 @@ class ConditionBlock(Statement):
 
     def __len__(self):
         return len(self._groups)
 
     def __getitem__(self, i):
         return self._groups[i]
 
 class Include(Statement):
+    """
+    Represents the include directive.
+
+    See https://www.gnu.org/software/make/manual/make.html#Include
+
+    The file to be included is represented by the Expansion defined in the
+    field `exp`. `required` is a bool indicating whether execution should fail
+    if the specified file could not be processed.
+    """
     __slots__ = ('exp', 'required', 'deps')
 
     def __init__(self, exp, required, weak):
         assert isinstance(exp, (data.Expansion, data.StringExpansion))
         self.exp = exp
         self.required = required
         self.weak = weak
 
@@ -401,16 +503,21 @@ class Include(Statement):
         files = self.exp.resolvesplit(makefile, makefile.variables)
         for f in files:
             makefile.include(f, self.required, loc=self.exp.loc, weak=self.weak)
 
     def dump(self, fd, indent):
         print >>fd, "%sInclude %s" % (indent, self.exp)
 
 class VPathDirective(Statement):
+    """
+    Represents the vpath directive.
+
+    See https://www.gnu.org/software/make/manual/make.html#Selective-Search
+    """
     __slots__ = ('exp',)
 
     def __init__(self, exp):
         assert isinstance(exp, (data.Expansion, data.StringExpansion))
         self.exp = exp
 
     def execute(self, makefile, context):
         words = list(data.stripdotslashes(self.exp.resolvesplit(makefile, makefile.variables)))
@@ -429,16 +536,28 @@ class VPathDirective(Statement):
                                  if dir != ''))
                 if len(dirs):
                     makefile.addvpath(pattern, dirs)
 
     def dump(self, fd, indent):
         print >>fd, "%sVPath %s" % (indent, self.exp)
 
 class ExportDirective(Statement):
+    """
+    Represents the "export" directive.
+
+    This is used to control exporting variables to sub makes.
+
+    See https://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion
+
+    The `single` field defines whether this statement occurred with or without
+    a variable assignment. If True, no variable assignment was present. If
+    False, the SetVariable immediately following this statement originally came
+    from this export directive (the parser splits it into multiple statements).
+    """
     __slots__ = ('exp', 'single')
 
     def __init__(self, exp, single):
         assert isinstance(exp, (data.Expansion, data.StringExpansion))
         self.exp = exp
         self.single = single
 
     def execute(self, makefile, context):
@@ -451,30 +570,43 @@ class ExportDirective(Statement):
 
         for v in vlist:
             makefile.exportedvars[v] = True
 
     def dump(self, fd, indent):
         print >>fd, "%sExport (single=%s) %s" % (indent, self.single, self.exp)
 
 class UnexportDirective(Statement):
+    """
+    Represents the "unexport" directive.
+
+    This is the opposite of ExportDirective.
+    """
     __slots__ = ('exp',)
 
     def __init__(self, exp):
         self.exp = exp
 
     def execute(self, makefile, context):
         vlist = list(self.exp.resolvesplit(makefile, makefile.variables))
         for v in vlist:
             makefile.exportedvars[v] = False
 
     def dump(self, fd, indent):
         print >>fd, "%sUnexport %s" % (indent, self.exp)
 
 class EmptyDirective(Statement):
+    """
+    Represents a standalone statement, usually an Expansion.
+
+    You will encounter EmptyDirective instances if there is a function
+    or similar at the top-level of a make file (e.g. outside of a rule or
+    variable assignment). You can also find them as the bodies of
+    ConditionBlock branches.
+    """
     __slots__ = ('exp',)
 
     def __init__(self, exp):
         assert isinstance(exp, (data.Expansion, data.StringExpansion))
         self.exp = exp
 
     def execute(self, makefile, context):
         v = self.exp.resolvestr(makefile, makefile.variables)
@@ -486,16 +618,24 @@ class EmptyDirective(Statement):
 
 class _EvalContext(object):
     __slots__ = ('currule', 'weak')
 
     def __init__(self, weak):
         self.weak = weak
 
 class StatementList(list):
+    """
+    A list of Statement instances.
+
+    This is what is generated by the parser when a make file is parsed.
+
+    Consumers can iterate over all Statement instances in this collection to
+    statically inspect (and even modify) make files before they are executed.
+    """
     __slots__ = ('mtime',)
 
     def append(self, statement):
         assert isinstance(statement, Statement)
         list.append(self, statement)
 
     def execute(self, makefile, context=None, weak=False):
         if context is None:
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -156,17 +156,17 @@ NS_PRINTING = @NS_PRINTING@
 MOZ_PDF_PRINTING = @MOZ_PDF_PRINTING@
 MOZ_CRASHREPORTER = @MOZ_CRASHREPORTER@
 MOZ_CRASHREPORTER_INJECTOR = @MOZ_CRASHREPORTER_INJECTOR@
 MOZ_HELP_VIEWER = @MOZ_HELP_VIEWER@
 MOC = @MOC@
 RCC = @RCC@
 MOZ_NSS_PATCH = @MOZ_NSS_PATCH@
 MOZ_WEBGL = @MOZ_WEBGL@
-MOZ_ANGLE = @MOZ_ANGLE@
+MOZ_ANGLE_RENDERER = @MOZ_ANGLE_RENDERER@
 MOZ_DIRECTX_SDK_PATH = @MOZ_DIRECTX_SDK_PATH@
 MOZ_DIRECTX_SDK_CPU_SUFFIX = @MOZ_DIRECTX_SDK_CPU_SUFFIX@
 MOZ_D3DX9_VERSION = @MOZ_D3DX9_VERSION@
 MOZ_D3DX9_CAB = @MOZ_D3DX9_CAB@
 MOZ_D3DCOMPILER_CAB = @MOZ_D3DCOMPILER_CAB@
 MOZ_D3DX9_DLL = @MOZ_D3DX9_DLL@
 MOZ_D3DCOMPILER_DLL = @MOZ_D3DCOMPILER_DLL@
 MOZ_GL_PROVIDER = @MOZ_GL_PROVIDER@
--- a/configure.in
+++ b/configure.in
@@ -5752,91 +5752,85 @@ if test -n "${JAVA_BIN_PATH}" -o \
   fi
 fi
 
 dnl ========================================================
 dnl = ANGLE OpenGL->D3D translator for WebGL
 dnl = * only applies to win32
 dnl = * enabled by default (shipping build); requires explicit --disable to disable
 dnl ========================================================
-MOZ_ANGLE=
+MOZ_ANGLE_RENDERER=
 MOZ_DIRECTX_SDK_PATH=
 MOZ_DIRECTX_SDK_CPU_SUFFIX=
 MOZ_D3DX9_VERSION=
 MOZ_D3DX9_CAB=
 MOZ_D3DCOMPILER_CAB=
 MOZ_D3DX9_DLL=
 MOZ_D3DCOMPILER_DLL=
 case "$target_os" in
 *mingw*)
-    MOZ_ANGLE=1
+    MOZ_ANGLE_RENDERER=1
     ;;
 esac
 
 # The DirectX SDK libraries are split into x86 and x64 sub-directories
 case "${target_cpu}" in
 i*86)
   MOZ_DIRECTX_SDK_CPU_SUFFIX=x86
   ;;
 x86_64)
   MOZ_DIRECTX_SDK_CPU_SUFFIX=x64
   ;;
 esac
 
-if test -n "$MOZ_ANGLE"; then
-MOZ_ARG_DISABLE_BOOL(angle,
-[  --disable-angle     Disable building of ANGLE for WebGL->D3D translation],
-    MOZ_ANGLE=,
-    MOZ_ANGLE=1)
-
-if test -n "$MOZ_ANGLE"; then
+MOZ_ARG_DISABLE_BOOL(webgl,
+[  --disable-webgl     Disable building of the WebGL implementation],
+    MOZ_WEBGL_DISABLED=1,
+    MOZ_WEBGL_DISABLED=)
+
+if test -n "$MOZ_WEBGL_DISABLED"; then
+  MOZ_WEBGL=
+  MOZ_ANGLE_RENDERER=
+fi
+
+if test -n "$MOZ_ANGLE_RENDERER"; then
   # Get the SDK path from the registry.
   # First try to get the June 2010 SDK
   MOZ_DIRECTX_SDK_REG_KEY=`reg query 'HKLM\Software\Microsoft\DirectX' //s | grep 'Microsoft DirectX SDK (June 2010)' | head -n 1`
   if test -z "$MOZ_DIRECTX_SDK_REG_KEY" ; then
     # Otherwise just take whatever comes first
     MOZ_DIRECTX_SDK_REG_KEY=`reg query 'HKLM\Software\Microsoft\DirectX' //s | grep 'Microsoft DirectX SDK' | head -n 1`
   fi
 
   if test -n "`echo $MOZ_DIRECTX_SDK_REG_KEY | grep 'February 2010'`" ; then
-    AC_MSG_ERROR([Found the February 2010 DirectX SDK. This is too old. We now require the June 2010 DirectX SDK, or newer.  Upgrade your SDK or to explicitly build without ANGLE, reconfigure with --disable-angle.])
+    AC_MSG_ERROR([Found the February 2010 DirectX SDK. Need the June 2010 DirectX SDK, or newer.  Upgrade your SDK or reconfigure with --disable-webgl.])
   else
     MOZ_DIRECTX_SDK_PATH=`reg query "$MOZ_DIRECTX_SDK_REG_KEY" //v InstallPath | grep REG_SZ | sed 's/.*\([[a-zA-Z]]\)\\:\\\\/\\1\\:\\\\/'`
   fi
 
-  MOZ_ANGLE=
-
-  if test -n "$MOZ_DIRECTX_SDK_PATH" ; then
-    if test -f "$MOZ_DIRECTX_SDK_PATH"/include/d3dx9.h && test -f "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_DIRECTX_SDK_CPU_SUFFIX/dxguid.lib ; then
-      AC_MSG_RESULT([Found DirectX SDK via registry, using $MOZ_DIRECTX_SDK_PATH])
-      MOZ_ANGLE=1
-    fi
-  fi
-
-  if test -z "$MOZ_ANGLE" ; then
-    AC_MSG_ERROR([Couldn't find the DirectX SDK, needed for ANGLE. Please install it (June 2010 or newer). To explicitly build without ANGLE, reconfigure with --disable-angle.])
+  if test -n "$MOZ_DIRECTX_SDK_PATH" &&
+     test -f "$MOZ_DIRECTX_SDK_PATH"/include/d3dx9.h &&
+	 test -f "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_DIRECTX_SDK_CPU_SUFFIX/dxguid.lib ; then
+    AC_MSG_RESULT([Found DirectX SDK via registry, using $MOZ_DIRECTX_SDK_PATH])
+  else
+    AC_MSG_ERROR([Couldn't find the DirectX SDK, needed for WebGL. Either install it (June 2010 version or newer), or reconfigure with --disable-webgl.])
   fi
 
-  if test -n "$MOZ_ANGLE" ; then
-    # Get the SDK numeric version (e.g. 43) by looking at the dependencies of d3dx9.lib
-    MOZ_D3DX9_VERSION=`dumpbin //headers "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_DIRECTX_SDK_CPU_SUFFIX/d3dx9.lib | egrep d3dx9_[[0-9]][[0-9]]\.dll | head -n1 | sed 's/.*\([[0-9]][[0-9]]\).*/\\1/g'`
-
-    if test -z "$MOZ_D3DX9_VERSION" ; then
-      AC_MSG_ERROR([Couldn't determine the D3DX9 version, needed for ANGLE. To explicitly build without ANGLE, reconfigure with --disable-angle.])
-    fi
-
-    if test -n "$MOZ_ANGLE" ; then
-      MOZ_D3DX9_CAB=`find "$MOZ_DIRECTX_SDK_PATH"/Redist -name *d3dx9_${MOZ_D3DX9_VERSION}_${MOZ_DIRECTX_SDK_CPU_SUFFIX}.cab | head -n1`
-      MOZ_D3DCOMPILER_CAB=`find "$MOZ_DIRECTX_SDK_PATH"/Redist -name *D3DCompiler_${MOZ_D3DX9_VERSION}_${MOZ_DIRECTX_SDK_CPU_SUFFIX}.cab | head -n1`
-
-      MOZ_D3DX9_DLL=d3dx9_$MOZ_D3DX9_VERSION.dll
-      MOZ_D3DCOMPILER_DLL=D3DCompiler_$MOZ_D3DX9_VERSION.dll
-    fi
+  # Get the SDK numeric version (e.g. 43) by looking at the dependencies of d3dx9.lib
+  MOZ_D3DX9_VERSION=`dumpbin //headers "$MOZ_DIRECTX_SDK_PATH"/lib/$MOZ_DIRECTX_SDK_CPU_SUFFIX/d3dx9.lib | egrep d3dx9_[[0-9]][[0-9]]\.dll | head -n1 | sed 's/.*\([[0-9]][[0-9]]\).*/\\1/g'`
+
+  if test -z "$MOZ_D3DX9_VERSION" ; then
+  	AC_MSG_ERROR([Couldn't determine the D3DX9 version, needed for WebGL. Either reinstall the DirectX SDK (June 2010 version or newer), or reconfigure with --disable-webgl.])
   fi
-fi
+
+  MOZ_D3DX9_CAB=`find "$MOZ_DIRECTX_SDK_PATH"/Redist -name *d3dx9_${MOZ_D3DX9_VERSION}_${MOZ_DIRECTX_SDK_CPU_SUFFIX}.cab | head -n1`
+  MOZ_D3DCOMPILER_CAB=`find "$MOZ_DIRECTX_SDK_PATH"/Redist -name *D3DCompiler_${MOZ_D3DX9_VERSION}_${MOZ_DIRECTX_SDK_CPU_SUFFIX}.cab | head -n1`
+
+  MOZ_D3DX9_DLL=d3dx9_$MOZ_D3DX9_VERSION.dll
+  MOZ_D3DCOMPILER_DLL=D3DCompiler_$MOZ_D3DX9_VERSION.dll
 fi
 
 dnl ========================================================
 dnl = Breakpad crash reporting (on by default on supported platforms)
 dnl ========================================================
 
 case $target in
 i?86-*-mingw*|x86_64-*-mingw*)
@@ -8352,17 +8346,17 @@ AC_SUBST(MOZ_SPELLCHECK)
 AC_SUBST(MOZ_JAVA_COMPOSITOR)
 AC_SUBST(MOZ_ONLY_TOUCH_EVENTS)
 AC_SUBST(MOZ_CRASHREPORTER)
 AC_SUBST(MOZ_CRASHREPORTER_INJECTOR)
 AC_SUBST(MOZ_MAINTENANCE_SERVICE)
 AC_SUBST(MOZ_VERIFY_MAR_SIGNATURE)
 AC_SUBST(MOZ_ENABLE_SIGNMAR)
 AC_SUBST(MOZ_UPDATER)
-AC_SUBST(MOZ_ANGLE)
+AC_SUBST(MOZ_ANGLE_RENDERER)
 AC_SUBST(MOZ_DIRECTX_SDK_PATH)
 AC_SUBST(MOZ_DIRECTX_SDK_CPU_SUFFIX)
 AC_SUBST(MOZ_D3DX9_VERSION)
 AC_SUBST(MOZ_D3DX9_CAB)
 AC_SUBST(MOZ_D3DCOMPILER_CAB)
 AC_SUBST(MOZ_D3DX9_DLL)
 AC_SUBST(MOZ_D3DCOMPILER_DLL)
 AC_SUBST(MOZ_METRO)
--- a/content/html/content/test/browser_bug649778.js
+++ b/content/html/content/test/browser_bug649778.js
@@ -1,50 +1,60 @@
 // Test for bug 649778 - document.write may cause a document to be written to disk cache even when the page has Cache-Control: no-store
 
 // Globals
 var testPath = "http://mochi.test:8888/browser/content/html/content/test/";
 var popup;
 
-function checkCache(url, policy, shouldExist)
+function checkCache(url, policy, shouldExist, cb)
 {
   var cache = Components.classes["@mozilla.org/network/cache-service;1"].
               getService(Components.interfaces.nsICacheService);
   var session = cache.createSession(
                   "wyciwyg", policy,
                   Components.interfaces.nsICache.STREAM_BASED);
-  try {
-    var cacheEntry = session.openCacheEntry(
-                       url, Components.interfaces.nsICache.ACCESS_READ, true);
-    is(shouldExist, true, "Entry found");
-  }
-  catch (e) {
-    is(shouldExist, false, "Entry not found");
-    is(e.result, Components.results.NS_ERROR_CACHE_KEY_NOT_FOUND,
-       "Invalid error");
-  }
+
+  var checkCacheListener = {
+    onCacheEntryAvailable: function oCEA(entry, access, status) {
+      if (shouldExist) {
+        ok(entry, "Entry not found");
+        is(status, Components.results.NS_OK, "Entry not found");
+      } else {
+        ok(!entry, "Entry found");
+        is(status, Components.results.NS_ERROR_CACHE_KEY_NOT_FOUND,
+           "Invalid error code");
+      }
+
+      setTimeout(cb, 0);
+    }
+  };
+
+  session.asyncOpenCacheEntry(url,
+                              Components.interfaces.nsICache.ACCESS_READ,
+                              checkCacheListener);
 }
-
 function getPopupURL() {
   var sh = popup.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                 .getInterface(Components.interfaces.nsIWebNavigation)
                 .sessionHistory;
 
   return sh.getEntryAtIndex(sh.index, false).URI.spec;
 }
 
+var wyciwygURL;
 function testContinue() {
-  var wyciwygURL = getPopupURL();
+  wyciwygURL = getPopupURL();
   is(wyciwygURL.substring(0, 10), "wyciwyg://", "Unexpected URL.");
   popup.close()
 
-  checkCache(wyciwygURL, Components.interfaces.nsICache.STORE_ON_DISK, false);
-  checkCache(wyciwygURL, Components.interfaces.nsICache.STORE_IN_MEMORY, true);
-
-  finish();
+  checkCache(wyciwygURL, Components.interfaces.nsICache.STORE_ON_DISK, false,
+    function() {
+      checkCache(wyciwygURL, Components.interfaces.nsICache.STORE_IN_MEMORY,
+                 true, finish);
+    });
 }
 
 function waitForWyciwygDocument() {
   try {
     var url = getPopupURL();
     if (url.substring(0, 10) == "wyciwyg://") {
       setTimeout(testContinue, 0);
       return;
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -62,16 +62,17 @@ DIRS += \
   src \
   locales \
   network \
   plugins/base \
   plugins/ipc \
   indexedDB \
   system \
   ipc \
+  identity \
   workers \
   $(NULL)
 
 ifdef MOZ_B2G_RIL
 DIRS += \
   telephony \
   wifi \
   $(NULL)
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -64,18 +64,18 @@
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::widget;
 
-#define DEBUG_FOCUS 1
-#define DEBUG_FOCUS_NAVIGATION 1
+//#define DEBUG_FOCUS 1
+//#define DEBUG_FOCUS_NAVIGATION 1
 #define PRINTTAGF(format, content)                     \
   {                                                    \
     nsAutoString tag(NS_LITERAL_STRING("(none)"));     \
     if (content)                                       \
       content->Tag()->ToString(tag);                   \
     printf(format, NS_ConvertUTF16toUTF8(tag).get());  \
   }
 
new file mode 100644
--- /dev/null
+++ b/dom/identity/DOMIdentity.jsm
@@ -0,0 +1,266 @@
+/* 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";
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+// This is the parent process corresponding to nsDOMIdentity.
+let EXPORTED_SYMBOLS = ["DOMIdentity"];
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "IdentityService",
+                                  "resource://gre/modules/identity/Identity.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this,
+                                  "Logger",
+                                  "resource://gre/modules/identity/LogUtils.jsm");
+
+function log(...aMessageArgs) {
+  Logger.log.apply(Logger, ["DOMIdentity"].concat(aMessageArgs));
+}
+
+function IDDOMMessage(aID) {
+  this.id = aID;
+}
+
+function IDPProvisioningContext(aID, aOrigin, aTargetMM) {
+  this._id = aID;
+  this._origin = aOrigin;
+  this._mm = aTargetMM;
+}
+
+IDPProvisioningContext.prototype = {
+  get id() this._id,
+  get origin() this._origin,
+
+  doBeginProvisioningCallback: function IDPPC_doBeginProvCB(aID, aCertDuration) {
+    let message = new IDDOMMessage(this.id);
+    message.identity = aID;
+    message.certDuration = aCertDuration;
+    this._mm.sendAsyncMessage("Identity:IDP:CallBeginProvisioningCallback",
+                              message);
+  },
+
+  doGenKeyPairCallback: function IDPPC_doGenKeyPairCallback(aPublicKey) {
+    log("doGenKeyPairCallback");
+    let message = new IDDOMMessage(this.id);
+    message.publicKey = aPublicKey;
+    this._mm.sendAsyncMessage("Identity:IDP:CallGenKeyPairCallback", message);
+  },
+
+  doError: function(msg) {
+    log("Provisioning ERROR: " + msg);
+  },
+};
+
+function IDPAuthenticationContext(aID, aOrigin, aTargetMM) {
+  this._id = aID;
+  this._origin = aOrigin;
+  this._mm = aTargetMM;
+}
+
+IDPAuthenticationContext.prototype = {
+  get id() this._id,
+  get origin() this._origin,
+
+  doBeginAuthenticationCallback: function IDPAC_doBeginAuthCB(aIdentity) {
+    let message = new IDDOMMessage(this.id);
+    message.identity = aIdentity;
+    this._mm.sendAsyncMessage("Identity:IDP:CallBeginAuthenticationCallback",
+                              message);
+  },
+
+  doError: function IDPAC_doError(msg) {
+    log("Authentication ERROR: " + msg);
+  },
+};
+
+function RPWatchContext(aID, aOrigin, aLoggedInEmail, aTargetMM) {
+  this._id = aID;
+  this._origin = aOrigin;
+  this._loggedInEmail = aLoggedInEmail;
+  this._mm = aTargetMM;
+}
+
+RPWatchContext.prototype = {
+  get id() this._id,
+  get origin() this._origin,
+  get loggedInEmail() this._loggedInEmail,
+
+  doLogin: function RPWatchContext_onlogin(aAssertion) {
+    log("doLogin: " + this.id);
+    let message = new IDDOMMessage(this.id);
+    message.assertion = aAssertion;
+    this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogin", message);
+  },
+
+  doLogout: function RPWatchContext_onlogout() {
+    log("doLogout :" + this.id);
+    let message = new IDDOMMessage(this.id);
+    this._mm.sendAsyncMessage("Identity:RP:Watch:OnLogout", message);
+  },
+
+  doReady: function RPWatchContext_onready() {
+    log("doReady: " + this.id);
+    let message = new IDDOMMessage(this.id);
+    this._mm.sendAsyncMessage("Identity:RP:Watch:OnReady", message);
+  },
+
+  doError: function RPWatchContext_onerror(aMessage) {
+    log("doError: " + aMessage);
+  }
+};
+
+let DOMIdentity = {
+  // nsIFrameMessageListener
+  receiveMessage: function DOMIdentity_receiveMessage(aMessage) {
+    let msg = aMessage.json;
+
+    // Target is the frame message manager that called us and is
+    // used to send replies back to the proper window.
+    let targetMM = aMessage.target
+                           .QueryInterface(Ci.nsIFrameLoaderOwner)
+                           .frameLoader.messageManager;
+
+    switch (aMessage.name) {
+      // RP
+      case "Identity:RP:Watch":
+        this._watch(msg, targetMM);
+        break;
+      case "Identity:RP:Request":
+        this._request(msg);
+        break;
+      case "Identity:RP:Logout":
+        this._logout(msg);
+        break;
+      // IDP
+      case "Identity:IDP:BeginProvisioning":
+        this._beginProvisioning(msg, targetMM);
+        break;
+      case "Identity:IDP:GenKeyPair":
+        this._genKeyPair(msg);
+        break;
+      case "Identity:IDP:RegisterCertificate":
+        this._registerCertificate(msg);
+        break;
+      case "Identity:IDP:ProvisioningFailure":
+        this._provisioningFailure(msg);
+        break;
+      case "Identity:IDP:BeginAuthentication":
+        this._beginAuthentication(msg, targetMM);
+        break;
+      case "Identity:IDP:CompleteAuthentication":
+        this._completeAuthentication(msg);
+        break;
+      case "Identity:IDP:AuthenticationFailure":
+        this._authenticationFailure(msg);
+        break;
+    }
+  },
+
+  // nsIObserver
+  observe: function DOMIdentity_observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "domwindowopened":
+      case "domwindowclosed":
+        let win = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
+                          .getInterface(Ci.nsIDOMWindow);
+        this._configureMessages(win, aTopic == "domwindowopened");
+        break;
+
+      case "xpcom-shutdown":
+        Services.ww.unregisterNotification(this);
+        Services.obs.removeObserver(this, "xpcom-shutdown");
+        break;
+    }
+  },
+
+  messages: ["Identity:RP:Watch", "Identity:RP:Request", "Identity:RP:Logout",
+             "Identity:IDP:BeginProvisioning", "Identity:IDP:ProvisioningFailure",
+             "Identity:IDP:RegisterCertificate", "Identity:IDP:GenKeyPair",
+             "Identity:IDP:BeginAuthentication",
+             "Identity:IDP:CompleteAuthentication",
+             "Identity:IDP:AuthenticationFailure"],
+
+  // Private.
+  _init: function DOMIdentity__init() {
+    Services.ww.registerNotification(this);
+    Services.obs.addObserver(this, "xpcom-shutdown", false);
+  },
+
+  _configureMessages: function DOMIdentity__configureMessages(aWindow, aRegister) {
+    if (!aWindow.messageManager)
+      return;
+
+    let func = aWindow.messageManager[aRegister ? "addMessageListener"
+                                                : "removeMessageListener"];
+
+    for (let message of this.messages) {
+      func(message, this);
+    }
+  },
+
+  _resetFrameState: function(aContext) {
+    log("_resetFrameState: ", aContext.id);
+    if (!aContext._mm) {
+      throw new Error("ERROR: Trying to reset an invalid context");
+    }
+    let message = new IDDOMMessage(aContext.id);
+    aContext._mm.sendAsyncMessage("Identity:ResetState", message);
+  },
+
+  _watch: function DOMIdentity__watch(message, targetMM) {
+    log("DOMIdentity__watch: " + message.id);
+    // Pass an object with the watch members to Identity.jsm so it can call the
+    // callbacks.
+    let context = new RPWatchContext(message.id, message.origin,
+                                     message.loggedInEmail, targetMM);
+    IdentityService.RP.watch(context);
+  },
+
+  _request: function DOMIdentity__request(message) {
+    IdentityService.RP.request(message.id, message);
+  },
+
+  _logout: function DOMIdentity__logout(message) {
+    IdentityService.RP.logout(message.id, message.origin);
+  },
+
+  _beginProvisioning: function DOMIdentity__beginProvisioning(message, targetMM) {
+    let context = new IDPProvisioningContext(message.id, message.origin,
+                                             targetMM);
+    IdentityService.IDP.beginProvisioning(context);
+  },
+
+  _genKeyPair: function DOMIdentity__genKeyPair(message) {
+    IdentityService.IDP.genKeyPair(message.id);
+  },
+
+  _registerCertificate: function DOMIdentity__registerCertificate(message) {
+    IdentityService.IDP.registerCertificate(message.id, message.cert);
+  },
+
+  _provisioningFailure: function DOMIdentity__provisioningFailure(message) {
+    IdentityService.IDP.raiseProvisioningFailure(message.id, message.reason);
+  },
+
+  _beginAuthentication: function DOMIdentity__beginAuthentication(message, targetMM) {
+    let context = new IDPAuthenticationContext(message.id, message.origin,
+                                               targetMM);
+    IdentityService.IDP.beginAuthentication(context);
+  },
+
+  _completeAuthentication: function DOMIdentity__completeAuthentication(message) {
+    IdentityService.IDP.completeAuthentication(message.id);
+  },
+
+  _authenticationFailure: function DOMIdentity__authenticationFailure(message) {
+    IdentityService.IDP.cancelAuthentication(message.id);
+  },
+};
+
+// Object is initialized by nsIDService.js
new file mode 100644
--- /dev/null
+++ b/dom/identity/Identity.manifest
@@ -0,0 +1,9 @@
+# nsDOMIdentity.js
+component {8bcac6a3-56a4-43a4-a44c-cdf42763002f} nsDOMIdentity.js
+contract @mozilla.org/dom/identity;1 {8bcac6a3-56a4-43a4-a44c-cdf42763002f}
+category JavaScript-navigator-property id @mozilla.org/dom/identity;1
+
+# nsIDService.js (initialization on startup)
+component {baa581e5-8e72-406c-8c9f-dcd4b23a6f82} nsIDService.js
+contract @mozilla.org/dom/identity/service;1 {baa581e5-8e72-406c-8c9f-dcd4b23a6f82}
+category app-startup IDService @mozilla.org/dom/identity/service;1
new file mode 100644
--- /dev/null
+++ b/dom/identity/Makefile.in
@@ -0,0 +1,28 @@
+# 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/.
+
+DEPTH            = ../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+relativesrcdir   = dom/identity
+
+include $(DEPTH)/config/autoconf.mk
+
+EXTRA_COMPONENTS = \
+    nsDOMIdentity.js \
+    nsIDService.js \
+    Identity.manifest \
+    $(NULL)
+
+EXTRA_JS_MODULES = \
+    DOMIdentity.jsm \
+    $(NULL)
+
+ifdef ENABLE_TESTS
+DIRS += tests
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/identity/nsDOMIdentity.js
@@ -0,0 +1,530 @@
+/* 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";
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+const PREF_DEBUG = "toolkit.identity.debug";
+const PREF_ENABLED = "dom.identity.enabled";
+
+// Maximum length of a string that will go through IPC
+const MAX_STRING_LENGTH = 2048;
+// Maximum number of times navigator.id.request can be called for a document
+const MAX_RP_CALLS = 100;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+// This is the child process corresponding to nsIDOMIdentity.
+
+
+function nsDOMIdentity(aIdentityInternal) {
+  this._identityInternal = aIdentityInternal;
+}
+nsDOMIdentity.prototype = {
+  __exposedProps__: {
+    // Relying Party (RP)
+    watch: 'r',
+    request: 'r',
+    logout: 'r',
+
+    // Provisioning
+    beginProvisioning: 'r',
+    genKeyPair: 'r',
+    registerCertificate: 'r',
+    raiseProvisioningFailure: 'r',
+
+    // Authentication
+    beginAuthentication: 'r',
+    completeAuthentication: 'r',
+    raiseAuthenticationFailure: 'r',
+  },
+
+  // nsIDOMIdentity
+  /**
+   * Relying Party (RP) APIs
+   */
+
+  watch: function nsDOMIdentity_watch(aOptions) {
+    this._log("watch");
+    if (this._rpWatcher) {
+      throw new Error("navigator.id.watch was already called");
+    }
+
+    if (!aOptions || typeof(aOptions) !== "object") {
+      throw new Error("options argument to watch is required");
+    }
+
+    // Check for required callbacks
+    let requiredCallbacks = ["onlogin", "onlogout"];
+    for (let cbName of requiredCallbacks) {
+      if ((!(cbName in aOptions))
+          || typeof(aOptions[cbName]) !== "function") {
+           throw new Error(cbName + " callback is required.");
+         }
+    }
+
+    // Optional callback "onready"
+    if (aOptions["onready"]
+        && typeof(aOptions['onready']) !== "function") {
+      throw new Error("onready must be a function");
+    }
+
+    let message = this.DOMIdentityMessage();
+
+    // loggedInEmail
+    message.loggedInEmail = null;
+    let emailType = typeof(aOptions["loggedInEmail"]);
+    if (aOptions["loggedInEmail"] && aOptions["loggedInEmail"] !== "undefined") {
+      if (emailType !== "string") {
+        throw new Error("loggedInEmail must be a String or null");
+      }
+
+      // TODO: Bug 767610 - check email format.
+      // See nsHTMLInputElement::IsValidEmailAddress
+      if (aOptions["loggedInEmail"].indexOf("@") == -1
+          || aOptions["loggedInEmail"].length > MAX_STRING_LENGTH) {
+        throw new Error("loggedInEmail is not valid");
+      }
+      // Set loggedInEmail in this block that "undefined" doesn't get through.
+      message.loggedInEmail = aOptions.loggedInEmail;
+    }
+    this._log("loggedInEmail: " + message.loggedInEmail);
+
+    this._rpWatcher = aOptions;
+    this._identityInternal._mm.sendAsyncMessage("Identity:RP:Watch", message);
+  },
+
+  request: function nsDOMIdentity_request(aOptions) {
+    // TODO: Bug 769569 - "must be invoked from within a click handler"
+
+    // Has the caller called watch() before this?
+    if (!this._rpWatcher) {
+      throw new Error("navigator.id.request called before navigator.id.watch");
+    }
+    if (this._rpCalls > MAX_RP_CALLS) {
+      throw new Error("navigator.id.request called too many times");
+    }
+
+    let message = this.DOMIdentityMessage();
+
+    if (aOptions) {
+      // Optional string properties
+      let optionalStringProps = ["privacyPolicy", "termsOfService"];
+      for (let propName of optionalStringProps) {
+        if (!aOptions[propName] || aOptions[propName] === "undefined")
+          continue;
+        if (typeof(aOptions[propName]) !== "string") {
+          throw new Error(propName + " must be a string representing a URL.");
+        }
+        if (aOptions[propName].length > MAX_STRING_LENGTH) {
+          throw new Error(propName + " is invalid.");
+        }
+        message[propName] = aOptions[propName];
+      }
+
+      if (aOptions["oncancel"]
+            && typeof(aOptions["oncancel"]) !== "function") {
+        throw new Error("oncancel is not a function");
+      } else {
+        // Store optional cancel callback for later.
+        this._onCancelRequestCallback = aOptions.oncancel;
+      }
+    }
+
+    this._rpCalls++;
+    this._identityInternal._mm.sendAsyncMessage("Identity:RP:Request", message);
+  },
+
+  logout: function nsDOMIdentity_logout() {
+    if (!this._rpWatcher) {
+      throw new Error("navigator.id.logout called before navigator.id.watch");
+    }
+    if (this._rpCalls > MAX_RP_CALLS) {
+      throw new Error("navigator.id.logout called too many times");
+    }
+
+    this._rpCalls++;
+    let message = this.DOMIdentityMessage();
+    this._identityInternal._mm.sendAsyncMessage("Identity:RP:Logout", message);
+  },
+
+  /**
+   *  Identity Provider (IDP) Provisioning APIs
+   */
+
+  beginProvisioning: function nsDOMIdentity_beginProvisioning(aCallback) {
+    this._log("beginProvisioning");
+    if (this._beginProvisioningCallback) {
+      throw new Error("navigator.id.beginProvisioning already called.");
+    }
+    if (!aCallback || typeof(aCallback) !== "function") {
+      throw new Error("beginProvisioning callback is required.");
+    }
+
+    this._beginProvisioningCallback = aCallback;
+    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:BeginProvisioning",
+                                                this.DOMIdentityMessage());
+  },
+
+  genKeyPair: function nsDOMIdentity_genKeyPair(aCallback) {
+    this._log("genKeyPair");
+    if (!this._beginProvisioningCallback) {
+      throw new Error("navigator.id.genKeyPair called outside of provisioning");
+    }
+    if (this._genKeyPairCallback) {
+      throw new Error("navigator.id.genKeyPair already called.");
+    }
+    if (!aCallback || typeof(aCallback) !== "function") {
+      throw new Error("genKeyPair callback is required.");
+    }
+
+    this._genKeyPairCallback = aCallback;
+    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:GenKeyPair",
+                                                this.DOMIdentityMessage());
+  },
+
+  registerCertificate: function nsDOMIdentity_registerCertificate(aCertificate) {
+    this._log("registerCertificate");
+    if (!this._genKeyPairCallback) {
+      throw new Error("navigator.id.registerCertificate called outside of provisioning");
+    }
+    if (this._provisioningEnded) {
+      throw new Error("Provisioning already ended");
+    }
+    this._provisioningEnded = true;
+
+    let message = this.DOMIdentityMessage();
+    message.cert = aCertificate;
+    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:RegisterCertificate", message);
+  },
+
+  raiseProvisioningFailure: function nsDOMIdentity_raiseProvisioningFailure(aReason) {
+    this._log("raiseProvisioningFailure '" + aReason + "'");
+    if (this._provisioningEnded) {
+      throw new Error("Provisioning already ended");
+    }
+    if (!aReason || typeof(aReason) != "string") {
+      throw new Error("raiseProvisioningFailure reason is required");
+    }
+    this._provisioningEnded = true;
+
+    let message = this.DOMIdentityMessage();
+    message.reason = aReason;
+    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:ProvisioningFailure", message);
+  },
+
+  /**
+   *  Identity Provider (IDP) Authentication APIs
+   */
+
+  beginAuthentication: function nsDOMIdentity_beginAuthentication(aCallback) {
+    this._log("beginAuthentication");
+    if (this._beginAuthenticationCallback) {
+      throw new Error("navigator.id.beginAuthentication already called.");
+    }
+    if (typeof(aCallback) !== "function") {
+      throw new Error("beginAuthentication callback is required.");
+    }
+    if (!aCallback || typeof(aCallback) !== "function") {
+      throw new Error("beginAuthentication callback is required.");
+    }
+
+    this._beginAuthenticationCallback = aCallback;
+    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:BeginAuthentication",
+                                                this.DOMIdentityMessage());
+  },
+
+  completeAuthentication: function nsDOMIdentity_completeAuthentication() {
+    if (this._authenticationEnded) {
+      throw new Error("Authentication already ended");
+    }
+    if (!this._beginAuthenticationCallback) {
+      throw new Error("navigator.id.completeAuthentication called outside of authentication");
+    }
+    this._authenticationEnded = true;
+
+    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:CompleteAuthentication",
+                                                this.DOMIdentityMessage());
+  },
+
+  raiseAuthenticationFailure: function nsDOMIdentity_raiseAuthenticationFailure(aReason) {
+    if (this._authenticationEnded) {
+      throw new Error("Authentication already ended");
+    }
+    if (!aReason || typeof(aReason) != "string") {
+      throw new Error("raiseProvisioningFailure reason is required");
+    }
+
+    let message = this.DOMIdentityMessage();
+    message.reason = aReason;
+    this._identityInternal._mm.sendAsyncMessage("Identity:IDP:AuthenticationFailure", message);
+  },
+
+  // Private.
+  _init: function nsDOMIdentity__init(aWindow) {
+
+    this._initializeState();
+
+    // Store window and origin URI.
+    this._window = aWindow;
+    this._origin = aWindow.document.nodePrincipal.origin;
+
+    // Setup identifiers for current window.
+    let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIDOMWindowUtils);
+    this._id = util.outerWindowID;
+  },
+
+  /**
+   * Called during init and shutdown.
+   */
+  _initializeState: function nsDOMIdentity__initializeState() {
+    // Some state to prevent abuse
+    // Limit the number of calls to .request
+    this._rpCalls = 0;
+    this._provisioningEnded = false;
+    this._authenticationEnded = false;
+
+    this._rpWatcher = null;
+    this._onCancelRequestCallback = null;
+    this._beginProvisioningCallback = null;
+    this._genKeyPairCallback = null;
+    this._beginAuthenticationCallback = null;
+  },
+
+  _receiveMessage: function nsDOMIdentity_receiveMessage(aMessage) {
+    let msg = aMessage.json;
+    this._log("receiveMessage: " + aMessage.name);
+
+    switch (aMessage.name) {
+      case "Identity:ResetState":
+        if (!this._identityInternal._debug) {
+          return;
+        }
+        this._initializeState();
+        Services.obs.notifyObservers(null, "identity-DOM-state-reset", this._id);
+        break;
+      case "Identity:RP:Watch:OnLogin":
+        // Do we have a watcher?
+        if (!this._rpWatcher) {
+          return;
+        }
+
+        if (this._rpWatcher.onlogin) {
+          this._rpWatcher.onlogin(msg.assertion);
+        }
+        break;
+      case "Identity:RP:Watch:OnLogout":
+        // Do we have a watcher?
+        if (!this._rpWatcher) {
+          return;
+        }
+
+        if (this._rpWatcher.onlogout) {
+          this._rpWatcher.onlogout();
+        }
+        break;
+      case "Identity:RP:Watch:OnReady":
+        // Do we have a watcher?
+        if (!this._rpWatcher) {
+          return;
+        }
+
+        if (this._rpWatcher.onready) {
+          this._rpWatcher.onready();
+        }
+        break;
+      case "Identity:RP:Request:OnCancel":
+        // Do we have a watcher?
+        if (!this._rpWatcher) {
+          return;
+        }
+
+        if (this._onCancelRequestCallback) {
+          this._onCancelRequestCallback();
+        }
+        break;
+      case "Identity:IDP:CallBeginProvisioningCallback":
+        this._callBeginProvisioningCallback(msg);
+        break;
+      case "Identity:IDP:CallGenKeyPairCallback":
+        this._callGenKeyPairCallback(msg);
+        break;
+      case "Identity:IDP:CallBeginAuthenticationCallback":
+        this._callBeginAuthenticationCallback(msg);
+        break;
+    }
+  },
+
+  _log: function nsDOMIdentity__log(msg) {
+    this._identityInternal._log(msg);
+  },
+
+  _callGenKeyPairCallback: function nsDOMIdentity__callGenKeyPairCallback(message) {
+    // create a pubkey object that works
+    let chrome_pubkey = JSON.parse(message.publicKey);
+
+    // bunch of stuff to create a proper object in window context
+    function genPropDesc(value) {
+      return {
+        enumerable: true, configurable: true, writable: true, value: value
+      };
+    }
+
+    let propList = {};
+    for (let k in chrome_pubkey) {
+      propList[k] = genPropDesc(chrome_pubkey[k]);
+    }
+
+    let pubkey = Cu.createObjectIn(this._window);
+    Object.defineProperties(pubkey, propList);
+    Cu.makeObjectPropsNormal(pubkey);
+
+    // do the callback
+    this._genKeyPairCallback(pubkey);
+  },
+
+  _callBeginProvisioningCallback:
+      function nsDOMIdentity__callBeginProvisioningCallback(message) {
+    let identity = message.identity;
+    let certValidityDuration = message.certDuration;
+    this._beginProvisioningCallback(identity,
+                                    certValidityDuration);
+  },
+
+  _callBeginAuthenticationCallback:
+      function nsDOMIdentity__callBeginAuthenticationCallback(message) {
+    let identity = message.identity;
+    this._beginAuthenticationCallback(identity);
+  },
+
+  /**
+   * Helper to create messages to send using a message manager
+   */
+  DOMIdentityMessage: function DOMIdentityMessage() {
+    return {
+      id: this._id,
+      origin: this._origin,
+    };
+  },
+
+};
+
+/**
+ * Internal functions that shouldn't be exposed to content.
+ */
+function nsDOMIdentityInternal() {
+}
+nsDOMIdentityInternal.prototype = {
+
+  // nsIFrameMessageListener
+  receiveMessage: function nsDOMIdentityInternal_receiveMessage(aMessage) {
+    let msg = aMessage.json;
+    // Is this message intended for this window?
+    if (msg.id != this._id) {
+      return;
+    }
+    this._identity._receiveMessage(aMessage);
+  },
+
+  // nsIObserver
+  observe: function nsDOMIdentityInternal_observe(aSubject, aTopic, aData) {
+    let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+    if (wId != this._innerWindowID) {
+      return;
+    }
+
+    Services.obs.removeObserver(this, "inner-window-destroyed");
+    this._identity._initializeState();
+    this._identity = null;
+
+    // TODO: Also send message to DOMIdentity notifiying window is no longer valid
+    // ie. in the case that the user closes the auth. window and we need to know.
+
+    try {
+      for (let msgName of this._messages) {
+        this._mm.removeMessageListener(msgName, this);
+      }
+    } catch (ex) {
+      // Avoid errors when removing more than once.
+    }
+
+    this._mm = null;
+  },
+
+  // nsIDOMGlobalPropertyInitializer
+  init: function nsDOMIdentityInternal_init(aWindow) {
+    if (Services.prefs.getPrefType(PREF_ENABLED) != Ci.nsIPrefBranch.PREF_BOOL
+        || !Services.prefs.getBoolPref(PREF_ENABLED)) {
+      return null;
+    }
+
+    this._debug =
+      Services.prefs.getPrefType(PREF_DEBUG) == Ci.nsIPrefBranch.PREF_BOOL
+      && Services.prefs.getBoolPref(PREF_DEBUG);
+
+    this._identity = new nsDOMIdentity(this);
+
+    this._identity._init(aWindow);
+
+    let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIDOMWindowUtils);
+    this._id = util.outerWindowID;
+    this._innerWindowID = util.currentInnerWindowID;
+
+    this._log("init was called from " + aWindow.document.location);
+
+    this._mm = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIWebNavigation)
+                      .QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIContentFrameMessageManager);
+
+    // Setup listeners for messages from parent process.
+    this._messages = [
+      "Identity:ResetState",
+      "Identity:RP:Watch:OnLogin",
+      "Identity:RP:Watch:OnLogout",
+      "Identity:RP:Watch:OnReady",
+      "Identity:RP:Request:OnCancel",
+      "Identity:IDP:CallBeginProvisioningCallback",
+      "Identity:IDP:CallGenKeyPairCallback",
+      "Identity:IDP:CallBeginAuthenticationCallback",
+    ];
+    this._messages.forEach((function(msgName) {
+      this._mm.addMessageListener(msgName, this);
+    }).bind(this));
+
+    // Setup observers so we can remove message listeners.
+    Services.obs.addObserver(this, "inner-window-destroyed", false);
+
+    return this._identity;
+  },
+
+  // Private.
+  _log: function nsDOMIdentityInternal__log(msg) {
+    if (!this._debug) {
+      return;
+    }
+    dump("nsDOMIdentity (" + this._id + "): " + msg + "\n");
+  },
+
+  // Component setup.
+  classID: Components.ID("{8bcac6a3-56a4-43a4-a44c-cdf42763002f}"),
+
+  QueryInterface: XPCOMUtils.generateQI(
+    [Ci.nsIDOMGlobalPropertyInitializer, Ci.nsIFrameMessageListener]
+  ),
+
+  classInfo: XPCOMUtils.generateCI({
+    classID: Components.ID("{8bcac6a3-56a4-43a4-a44c-cdf42763002f}"),
+    contractID: "@mozilla.org/dom/identity;1",
+    interfaces: [],
+    classDescription: "Identity DOM Implementation"
+  })
+
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([nsDOMIdentityInternal]);
new file mode 100644
--- /dev/null
+++ b/dom/identity/nsIDService.js
@@ -0,0 +1,34 @@
+/* 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";
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+function IDService() {
+  this.wrappedJSObject = this;
+}
+IDService.prototype = {
+  classID: Components.ID("{baa581e5-8e72-406c-8c9f-dcd4b23a6f82}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+                                         Ci.nsISupportsWeakReference]),
+
+  observe: function observe(subject, topic, data) {
+    switch (topic) {
+      case "app-startup":
+        Services.obs.addObserver(this, "final-ui-startup", true);
+        break;
+      case "final-ui-startup":
+        // Startup DOMIdentity.jsm
+        Cu.import("resource://gre/modules/DOMIdentity.jsm");
+        DOMIdentity._init();
+        break;
+    }
+  }
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([IDService]);
new file mode 100644
--- /dev/null
+++ b/dom/identity/tests/Makefile.in
@@ -0,0 +1,28 @@
+# 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/.
+
+DEPTH            = ../../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+relativesrcdir   = dom/identity/tests
+
+include $(DEPTH)/config/autoconf.mk
+
+DIRS = \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES = \
+  head_identity.js \
+  test_identity_idp_auth_basics.html \
+  test_identity_idp_prov_basics.html \
+  test_identity_rp_basics.html \
+  $(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+
new file mode 100644
--- /dev/null
+++ b/dom/identity/tests/head_identity.js
@@ -0,0 +1,83 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const Ci = Components.interfaces;
+const Cu = SpecialPowers.wrap(Components).utils;
+
+SpecialPowers.setBoolPref("toolkit.identity.debug", true);
+SpecialPowers.setBoolPref("dom.identity.enabled", true);
+
+const Services = Cu.import("resource://gre/modules/Services.jsm").Services;
+const DOMIdentity = Cu.import("resource://gre/modules/DOMIdentity.jsm")
+                      .DOMIdentity;
+
+let util = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                 .getInterface(Ci.nsIDOMWindowUtils);
+let outerWinId = util.outerWindowID;
+
+const identity = navigator.id || navigator.mozId;
+
+let index = 0;
+
+// mimicking callback funtionality for ease of testing
+// this observer auto-removes itself after the observe function
+// is called, so this is meant to observe only ONE event.
+function makeObserver(aObserveTopic, aObserveFunc) {
+  function observe(aSubject, aTopic, aData) {
+    if (aTopic == aObserveTopic) {
+      aObserveFunc(aSubject, aTopic, aData);
+      Services.obs.removeObserver(this, aObserveTopic);
+    }
+  }
+
+  Services.obs.addObserver(observe, aObserveTopic, false);
+}
+
+function expectException(aFunc, msg, aErrorType="Error") {
+  info("Expecting an exception: " + msg);
+  msg = msg || "";
+  let caughtEx = null;
+  try {
+    aFunc();
+  } catch (ex) {
+    let exProto = Object.getPrototypeOf(ex);
+    // Don't count NS_* exceptions since they shouldn't be exposed to content
+    if (exProto.toString() == aErrorType
+        && ex.toString().indexOf("NS_ERROR_FAILURE") == -1) {
+      caughtEx = ex;
+    } else {
+      ok(false, ex);
+      return;
+    }
+  }
+  isnot(caughtEx, null, "Check for thrown exception.");
+}
+
+function next() {
+  if (!identity) {
+    todo(false, "DOM API is not available. Skipping tests.");
+    finish_tests();
+    return;
+  }
+  if (index >= steps.length) {
+    ok(false, "Shouldn't get here!");
+    return;
+  }
+  try {
+    let fn = steps[index];
+    info("Begin test " + index + " '" + steps[index].name + "'!");
+    fn();
+  } catch(ex) {
+    ok(false, "Caught exception", ex);
+  }
+  index += 1;
+}
+
+function finish_tests() {
+  info("all done");
+  SpecialPowers.clearUserPref("toolkit.identity.debug");
+  SpecialPowers.clearUserPref("dom.identity.enabled");
+  SimpleTest.finish();
+}
new file mode 100644
--- /dev/null
+++ b/dom/identity/tests/test_identity_idp_auth_basics.html
@@ -0,0 +1,87 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for navigator.id identity provider (IDP) authentication basics</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript;version=1.8" src="head_identity.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank">navigator.id identity provider (IDP) authentication basics</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+
+"use strict"
+
+let steps = [
+  // completeAuthentication tests
+  function completeAuthenticationExists() {
+    is(typeof(identity.completeAuthentication), "function",
+       "Check completeAuthentication is a function");
+    SimpleTest.executeSoon(next);
+  },
+  function completeAuthenticationOutsideFlow() {
+    expectException(function() {
+      identity.completeAuthentication();
+    }, "Check completeAuthentication outside of an auth. flow");
+    SimpleTest.executeSoon(next);
+  },
+
+  // raiseAuthenticationFailure tests
+  function raiseAuthenticationFailureExists() {
+    is(typeof(identity.raiseAuthenticationFailure), "function",
+       "Check raiseAuthenticationFailure is a function");
+    SimpleTest.executeSoon(next);
+  },
+  function raiseAuthenticationFailureNoArgs() {
+    expectException(function() {
+      identity.raiseAuthenticationFailure();
+    }, "raiseAuthenticationFailure with no arguments");
+    SimpleTest.executeSoon(next);
+  },
+
+  // beginAuthentication tests
+  function beginAuthenticationExists() {
+    is(typeof(identity.beginAuthentication), "function",
+       "Check beginAuthentication is a function");
+    SimpleTest.executeSoon(next);
+  },
+  function beginAuthenticationNoArgs() {
+    expectException(function() {
+      identity.beginAuthentication();
+    }, "beginAuthentication with no arguments");
+    SimpleTest.executeSoon(next);
+  },
+  function beginAuthenticationInvalidArg() {
+    expectException(function() {
+      identity.beginAuthentication(999);
+    }, "beginAuthentication with a non-function argument");
+    SimpleTest.executeSoon(next);
+  },
+  function beginAuthenticationArgs() {
+    function beginAuthenticationCb() {
+      throw "beginAuthentication callback shouldn't have been called outside of an "
+              + "auth flow";
+    }
+    is(identity.beginAuthentication(beginAuthenticationCb), undefined,
+       "Check minimum beginAuthentication arguments");
+    SimpleTest.executeSoon(next);
+  },
+
+  finish_tests,
+];
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(next);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/identity/tests/test_identity_idp_prov_basics.html
@@ -0,0 +1,160 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for navigator.id identity provider (IDP) provisioning basics</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript;version=1.8" src="head_identity.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank">navigator.id identity provider (IDP) provisioning basics</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+
+"use strict"
+
+let IDP = Cu.import("resource://gre/modules/identity/IdentityProvider.jsm").IdentityProvider;
+
+function setupProv() {
+  info("setupProv");
+  // Add a provisioning flow so the DOM calls succeed
+  IDP._provisionFlows[outerWinId] = {
+    sandbox: {},
+    callback: function doCallback(aErr) {
+      info("provisioning callback: " + aErr);
+    },
+  }
+}
+
+function resetAndNext() {
+  info("resetAndNext");
+  // reset DOM state for the next test
+  // Give the flow some time to cross the IPC boundary
+  setTimeout(function() {
+    let provContext = IDP._provisionFlows[outerWinId];
+    if (!provContext) {
+      SimpleTest.executeSoon(next);
+      return;
+    }
+    makeObserver("identity-DOM-state-reset", function() {
+      info("reset done");
+      SimpleTest.executeSoon(next);
+    });
+    DOMIdentity._resetFrameState(provContext.caller);
+  }, 700);
+}
+
+let steps = [
+  // genKeyPair tests
+  function genKeyPairExists() {
+    is(typeof(identity.genKeyPair), "function",
+       "Check genKeyPair is a function");
+    SimpleTest.executeSoon(next);
+  },
+  function genKeyPairOutsideProv() {
+    expectException(function(){
+      identity.genKeyPair(function(){});
+    }, "Check genKeyPair outside of a prov. flow");
+    SimpleTest.executeSoon(next);
+  },
+  function genKeyPairNoArgs() {
+    setupProv();
+    identity.beginProvisioning(function() {
+      expectException(function() {
+        identity.genKeyPair();
+      }, "genKeyPair with no arguments");
+      SimpleTest.executeSoon(resetAndNext);
+    });
+  },
+  function genKeyPairInvalidArg() {
+    setupProv();
+    identity.beginProvisioning(function() {
+      expectException(function() {
+        identity.genKeyPair(999);
+      }, "Check genKeyPair with non-function object argument");
+      SimpleTest.executeSoon(resetAndNext);
+    });
+  },
+
+  // registerCertificate tests
+  function registerCertificateExists() {
+    is(typeof(identity.registerCertificate), "function",
+       "Check registerCertificate is a function");
+    SimpleTest.executeSoon(next);
+  },
+  function registerCertificateNoArgs() {
+    setupProv();
+    identity.beginProvisioning(function() {
+      expectException(function() {
+        identity.registerCertificate();
+      }, "Check registerCertificate with no arguments");
+    });
+    SimpleTest.executeSoon(resetAndNext);
+  },
+  function registerCertificateOutsideProv() {
+    expectException(function(){
+      identity.registerCertificate("foo");
+    }, "Check registerCertificate outside of a prov. flow");
+    SimpleTest.executeSoon(next);
+  },
+
+  // raiseProvisioningFailure tests
+  function raiseProvisioningFailureExists() {
+    is(typeof(identity.raiseProvisioningFailure), "function",
+       "Check raiseProvisioningFailure is a function");
+    SimpleTest.executeSoon(next);
+  },
+  function raiseProvisioningFailureNoArgs() {
+    expectException(function() {
+      identity.raiseProvisioningFailure();
+    }, "raiseProvisioningFailure with no arguments");
+    SimpleTest.executeSoon(next);
+  },
+  function raiseProvisioningFailureWithReason() {
+    identity.raiseProvisioningFailure("my test reason");
+    SimpleTest.executeSoon(next);
+  },
+
+  // beginProvisioning tests
+  function beginProvisioningExists() {
+    is(typeof(identity.beginProvisioning), "function",
+       "Check beginProvisioning is a function");
+    SimpleTest.executeSoon(next);
+  },
+  function beginProvisioningNoArgs() {
+    expectException(function() {
+      identity.beginProvisioning();
+    }, "beginProvisioning with no arguments");
+    SimpleTest.executeSoon(next);
+  },
+  function beginProvisioningInvalidArg() {
+    expectException(function() {
+      identity.beginProvisioning(999);
+    }, "beginProvisioning with a non-function argument");
+    SimpleTest.executeSoon(next);
+  },
+  function beginProvisioningArgs() {
+    function beginProvisioningCb() {
+      SimpleTest.executeSoon(resetAndNext);
+    }
+    is(identity.beginProvisioning(beginProvisioningCb), undefined,
+       "Check minimum beginProvisioning arguments");
+  },
+
+  finish_tests,
+];
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(next);
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/identity/tests/test_identity_rp_basics.html
@@ -0,0 +1,172 @@
+<!DOCTYPE html>
+<!-- Any copyright is dedicated to the Public Domain.
+   - http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test for navigator.id relying party (RP) basics</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript;version=1.8" src="head_identity.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank">navigator.id RP basics</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.8">
+
+"use strict";
+
+const RP = Cu.import("resource://gre/modules/identity/RelyingParty.jsm").RelyingParty;
+
+function resetAndNext() {
+  // reset DOM state for the next test
+  makeObserver("identity-DOM-state-reset", function() {
+    SimpleTest.executeSoon(next);
+  });
+  // Give the flow some time to cross the IPC boundary
+  setTimeout(function() {
+    let rpContext = RP._rpFlows[outerWinId];
+    if (!rpContext) {
+      SimpleTest.executeSoon(next);
+      return;
+    }
+    DOMIdentity._resetFrameState(rpContext);
+  }, 700);
+}
+
+let steps = [
+  function nonExistentProp() {
+    is(identity.foobarbaz, undefined, "Check that foobarbaz does not exist");
+    expectException(function() {
+      identity.foobarbaz()
+    }, "Check for exception calling non-existent method", "TypeError");
+    SimpleTest.executeSoon(next);
+  },
+
+  // test request before watch throws an exception
+  function requestBeforeWatch() {
+    expectException(function() {
+      identity.request();
+    });
+    SimpleTest.executeSoon(next);
+  },
+
+  // watch tests
+  function watchExists() {
+    is(typeof(identity.watch), "function", "Check watch is a function");
+    SimpleTest.executeSoon(next);
+  },
+  function watchNoArgs() {
+    expectException(function() {
+      identity.watch();
+    }, "watch with no arguments");
+    SimpleTest.executeSoon(next);
+  },
+  function watchEmptyObj() {
+    expectException(function() {
+      identity.watch({});
+    }, "watch with empty object argument");
+    SimpleTest.executeSoon(next);
+  },
+  function watchOnLoginBool() {
+    expectException(function() {
+      identity.watch({onlogin: true});
+    }, "watch with invalid onlogin member");
+    SimpleTest.executeSoon(next);
+  },
+  function watchOnLoginLogoutBool() {
+    expectException(function() {
+      identity.watch({onlogin: true, onlogout: false});
+    }, "watch with invalid onlogin and onlogout members");
+    SimpleTest.executeSoon(next);
+  },
+  function watchMinimumArgs() {
+    function onLoginLogoutCb() {
+      throw "onlogin/onlogout callback shouldn't have been called";
+    }
+    is(identity.watch({onlogin: onLoginLogoutCb, onlogout: onLoginLogoutCb}),
+       undefined, "Check minimum watch argument members");
+    resetAndNext();
+  },
+  function watchOnReadyType() {
+    function onLoginLogoutCb() {
+      throw "onlogin/onlogout callback shouldn't have been called";
+    }
+    let options = {
+      onlogin: onLoginLogoutCb,
+      onlogout: onLoginLogoutCb,
+      onready: 999,
+    }
+    expectException(function() {
+      identity.watch(options)
+    }, "Check onready type");
+    resetAndNext();
+  },
+  function watchLoggedInEmailType() {
+    function onLoginLogoutCb() {
+      throw "onlogin/onlogout callback shouldn't have been called";
+    }
+    let options = {
+      onlogin: onLoginLogoutCb,
+      onlogout: onLoginLogoutCb,
+      loggedInEmail: {},
+    }
+    expectException(function() {
+      identity.watch(options)
+    }, "Check loggedInEmail type");
+    resetAndNext();
+  },
+  function watchOnReadyCalled() {
+    let onLogoutCalled = false;
+    let options = {
+      loggedInEmail: "loggedOut@user.com",
+      onlogin: function onLoginCb(assertion) {
+        throw "onlogin/onlogout callback shouldn't have been called";
+      },
+      onlogout: function onLogoutCb() {
+        is(arguments.length, 0, "Check onlogout argument length");
+        onLogoutCalled = true;
+      },
+      onready: function onReady() {
+        is(arguments.length, 0, "Check onready argument length");
+        ok(onLogoutCalled, "onlogout callback should be called before onready");
+        SimpleTest.executeSoon(next);
+      },
+    }
+    is(identity.watch(options), undefined, "Check onready is called");
+  },
+
+  // request tests
+  function requestExists() {
+    is(typeof(identity.request), "function", "Check request is a function");
+    SimpleTest.executeSoon(next);
+  },
+  function requestNoArgs() {
+    is(identity.request(), undefined, "Check request with no arguments");
+    SimpleTest.executeSoon(next);
+  },
+  function requestEmptyObj() {
+    is(identity.request({}), undefined, "Check request with empty object argument");
+    SimpleTest.executeSoon(next);
+  },
+
+  // logout tests
+  function logoutExists() {
+    is(typeof(identity.logout), "function", "Check logout is a function");
+    SimpleTest.executeSoon(next);
+  },
+
+  finish_tests,
+];
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(next);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -390,19 +390,19 @@ IDBIndex::Create(IDBObjectStore* aObject
   }
 
   return index.forget();
 }
 
 IDBIndex::IDBIndex()
 : mId(LL_MININT),
   mKeyPath(0),
+  mCachedKeyPath(JSVAL_VOID),
   mActorChild(nsnull),
   mActorParent(nsnull),
-  mCachedKeyPath(JSVAL_VOID),
   mUnique(false),
   mMultiEntry(false),
   mRooted(false)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 IDBIndex::~IDBIndex()
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -49,16 +49,17 @@
 #include "nsHashtable.h"
 #include "nsIProxyInfo.h"
 #include "nsPluginLogging.h"
 #include "nsIScriptChannel.h"
 #include "nsIBlocklistService.h"
 #include "nsVersionComparator.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIWritablePropertyBag2.h"
+#include "nsICategoryManager.h"
 #include "nsPluginStreamListenerPeer.h"
 #include "mozilla/Preferences.h"
 
 #include "nsEnumeratorUtils.h"
 #include "nsXPCOM.h"
 #include "nsXPCOMCID.h"
 #include "nsISupportsPrimitives.h"
 
@@ -148,16 +149,17 @@ using mozilla::TimeStamp;
     }                                                                \
   }
 
 // this is the name of the directory which will be created
 // to cache temporary files.
 #define kPluginTmpDirName NS_LITERAL_CSTRING("plugtmp")
 
 static const char *kPrefWhitelist = "plugin.allowed_types";
+static const char *kPrefDisableFullPage = "plugin.disable_full_page_plugin_for_types";
 
 // Version of cached plugin info
 // 0.01 first implementation
 // 0.02 added caching of CanUnload to fix bug 105935
 // 0.03 changed name, description and mime desc from string to bytes, bug 108246
 // 0.04 added new mime entry point on Mac, bug 113464
 // 0.05 added new entry point check for the default plugin, bug 132430
 // 0.06 strip off suffixes in mime description strings, bug 53895
@@ -228,22 +230,21 @@ nsInvalidPluginTag::nsInvalidPluginTag(c
 
 nsInvalidPluginTag::~nsInvalidPluginTag()
 {
   
 }
 
 // Helper to check for a MIME in a comma-delimited preference
 static bool
-IsTypeInPrefList(nsCString &aMimeType, const char* aPrefName)
+IsTypeInList(nsCString &aMimeType, nsCString aTypeList)
 {
   nsCAutoString searchStr;
   searchStr.Assign(',');
-  nsAdoptingCString prefStr = Preferences::GetCString(aPrefName);
-  searchStr += prefStr;
+  searchStr.Append(aTypeList);
   searchStr.Append(',');
 
   nsACString::const_iterator start, end;
 
   searchStr.BeginReading(start);
   searchStr.EndReading(end);
 
   nsCAutoString commaSeparated;
@@ -1513,16 +1514,21 @@ nsPluginHost::GetPluginTags(PRUint32* aP
 
   return NS_OK;
 }
 
 nsPluginTag*
 nsPluginHost::FindPreferredPlugin(const InfallibleTArray<nsPluginTag*>& matches)
 {
   // We prefer the plugin with the highest version number.
+  /// XXX(johns): This seems to assume the only time multiple plugins will have
+  ///             the same MIME type is if they're multiple versions of the same
+  ///             plugin -- but since plugin filenames and pretty names can both
+  ///             update, it's probably less arbitrary than just going at it
+  ///             alphabetically.
 
   if (matches.IsEmpty()) {
     return nsnull;
   }
 
   nsPluginTag *preferredPlugin = matches[0];
   for (unsigned int i = 1; i < matches.Length(); i++) {
     if (mozilla::Version(matches[i]->mVersion.get()) > preferredPlugin->mVersion.get()) {
@@ -2126,17 +2132,24 @@ nsresult nsPluginHost::ScanPluginsDirect
           prev->mNext = pluginTag;
         }
       }
     } else {
       mPlugins = pluginTag;
     }
 
     if (pluginTag->IsEnabled()) {
-      pluginTag->RegisterWithCategoryManager(mOverrideInternalTypes);
+      nsAdoptingCString disableFullPage =
+        Preferences::GetCString(kPrefDisableFullPage);
+      for (PRUint32 i = 0; i < pluginTag->mMimeTypes.Length(); i++) {
+        if (!IsTypeInList(pluginTag->mMimeTypes[i], disableFullPage)) {
+          RegisterWithCategoryManager(pluginTag->mMimeTypes[i],
+                                      ePluginRegister);
+        }
+      }
     }
   }
 
   if (warnOutdated) {
     Preferences::SetBool("plugins.update.notifyUser", true);
   }
 
   return NS_OK;
@@ -2378,18 +2391,41 @@ nsresult nsPluginHost::FindPlugins(bool 
 nsresult
 nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag)
 {
   ReadPluginInfo();
   WritePluginInfo();
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsPluginTag>, mCachedPlugins, mNext);
   NS_ITERATIVE_UNREF_LIST(nsRefPtr<nsInvalidPluginTag>, mInvalidPlugins, mNext);
 
-  if (!aPluginTag || aPluginTag->IsEnabled())
+  if (!aPluginTag) {
     return NS_OK;
+  }
+
+  // Update types with category manager
+  nsAdoptingCString disableFullPage =
+    Preferences::GetCString(kPrefDisableFullPage);
+  for (PRUint32 i = 0; i < aPluginTag->mMimeTypes.Length(); i++) {
+    nsRegisterType shouldRegister;
+
+    if (IsTypeInList(aPluginTag->mMimeTypes[i], disableFullPage)) {
+      shouldRegister = ePluginUnregister;
+    } else {
+      nsPluginTag *plugin = FindPluginForType(aPluginTag->mMimeTypes[i].get(),
+                                              true);
+      shouldRegister = plugin ? ePluginRegister : ePluginUnregister;
+    }
+
+    RegisterWithCategoryManager(aPluginTag->mMimeTypes[i], shouldRegister);
+  }
+
+  // Reload instances if needed
+  if (aPluginTag->IsEnabled()) {
+    return NS_OK;
+  }
 
   nsCOMPtr<nsISupportsArray> instsToReload;
   NS_NewISupportsArray(getter_AddRefs(instsToReload));
   DestroyRunningInstances(instsToReload, aPluginTag);
   
   PRUint32 c;
   if (instsToReload && NS_SUCCEEDED(instsToReload->Count(&c)) && c > 0) {
     nsCOMPtr<nsIRunnable> ev = new nsPluginDocReframeEvent(instsToReload);
@@ -2398,21 +2434,60 @@ nsPluginHost::UpdatePluginInfo(nsPluginT
   }
 
   return NS_OK;
 }
 
 /* static */ bool
 nsPluginHost::IsTypeWhitelisted(const char *aMimeType)
 {
-  if (!Preferences::HasUserValue(kPrefWhitelist)) {
+  nsAdoptingCString whitelist = Preferences::GetCString(kPrefWhitelist);
+  if (!whitelist.Length()) {
     return true;
   }
   nsDependentCString wrap(aMimeType);
-  return IsTypeInPrefList(wrap, kPrefWhitelist);
+  return IsTypeInList(wrap, whitelist);
+}
+
+void
+nsPluginHost::RegisterWithCategoryManager(nsCString &aMimeType,
+                                          nsRegisterType aType)
+{
+  PLUGIN_LOG(PLUGIN_LOG_NORMAL,
+             ("nsPluginTag::RegisterWithCategoryManager type = %s, removing = %s\n",
+              aMimeType.get(), aType == ePluginUnregister ? "yes" : "no"));
+
+  nsCOMPtr<nsICategoryManager> catMan =
+    do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
+  if (!catMan) {
+    return;
+  }
+
+  const char *contractId =
+    "@mozilla.org/content/plugin/document-loader-factory;1";
+
+  if (aType == ePluginRegister) {
+    catMan->AddCategoryEntry("Gecko-Content-Viewers",
+                             aMimeType.get(),
+                             contractId,
+                             false, /* persist: broken by bug 193031 */
+                             mOverrideInternalTypes,
+                             nsnull);
+  } else {
+    // Only delete the entry if a plugin registered for it
+    nsXPIDLCString value;
+    nsresult rv = catMan->GetCategoryEntry("Gecko-Content-Viewers",
+                                           aMimeType.get(),
+                                           getter_Copies(value));
+    if (NS_SUCCEEDED(rv) && strcmp(value, contractId) == 0) {
+      catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
+                                  aMimeType.get(),
+                                  true);
+    }
+  }
 }
 
 nsresult
 nsPluginHost::WritePluginInfo()
 {
 
   nsresult rv = NS_OK;
   nsCOMPtr<nsIProperties> directoryService(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID,&rv));
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -228,16 +228,21 @@ private:
   FindPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
 
   nsresult
   FindStoppedPluginForURL(nsIURI* aURL, nsIPluginInstanceOwner *aOwner);
 
   nsresult
   FindPlugins(bool aCreatePluginList, bool * aPluginsChanged);
 
+  // Registers or unregisters the given mime type with the category manager
+  // (performs no checks - see UpdateCategoryManager)
+  enum nsRegisterType { ePluginRegister, ePluginUnregister };
+  void RegisterWithCategoryManager(nsCString &aMimeType, nsRegisterType aType);
+
   nsresult
   ScanPluginsDirectory(nsIFile *pluginsDir,
                        bool aCreatePluginList,
                        bool *aPluginsChanged);
 
   nsresult
   ScanPluginsDirectoryList(nsISimpleEnumerator *dirEnum,
                            bool aCreatePluginList,
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -10,17 +10,16 @@
 #include "nsIPluginInstanceOwner.h"
 #include "nsServiceManagerUtils.h"
 #include "nsPluginsDir.h"
 #include "nsPluginHost.h"
 #include "nsIUnicodeDecoder.h"
 #include "nsIPlatformCharset.h"
 #include "nsICharsetConverterManager.h"
 #include "nsPluginLogging.h"
-#include "nsICategoryManager.h"
 #include "nsNPAPIPlugin.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/Preferences.h"
 
 using namespace mozilla;
 using mozilla::TimeStamp;
 
 inline char* new_str(const char* str)
@@ -291,18 +290,17 @@ nsPluginTag::SetDisabled(bool aDisabled)
 {
   if (HasFlag(NS_PLUGIN_FLAG_ENABLED) == !aDisabled)
     return NS_OK;
   
   if (aDisabled)
     UnMark(NS_PLUGIN_FLAG_ENABLED);
   else
     Mark(NS_PLUGIN_FLAG_ENABLED);
-  
-  mPluginHost->UpdatePluginInfo(this);
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPluginTag::GetBlocklisted(bool* aBlocklisted)
 {
   *aBlocklisted = HasFlag(NS_PLUGIN_FLAG_BLOCKLISTED);
   return NS_OK;
@@ -313,112 +311,37 @@ nsPluginTag::SetBlocklisted(bool aBlockl
 {
   if (HasFlag(NS_PLUGIN_FLAG_BLOCKLISTED) == aBlocklisted)
     return NS_OK;
   
   if (aBlocklisted)
     Mark(NS_PLUGIN_FLAG_BLOCKLISTED);
   else
     UnMark(NS_PLUGIN_FLAG_BLOCKLISTED);
-  
-  mPluginHost->UpdatePluginInfo(nsnull);
+
   return NS_OK;
 }
 
-void
-nsPluginTag::RegisterWithCategoryManager(bool aOverrideInternalTypes,
-                                         nsPluginTag::nsRegisterType aType)
-{
-  PLUGIN_LOG(PLUGIN_LOG_NORMAL,
-             ("nsPluginTag::RegisterWithCategoryManager plugin=%s, removing = %s\n",
-              mFileName.get(), aType == ePluginUnregister ? "yes" : "no"));
-  
-  nsCOMPtr<nsICategoryManager> catMan = do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
-  if (!catMan)
-    return;
-  
-  const char *contractId = "@mozilla.org/content/plugin/document-loader-factory;1";
-  
-  // A preference controls whether or not the full page plugin is disabled for
-  // a particular type. The string must be in the form:
-  //   type1,type2,type3,type4
-  // Note: need an actual interface to control this and subsequent disabling 
-  // (and other plugin host settings) so applications can reliably disable 
-  // plugins - without relying on implementation details such as prefs/category
-  // manager entries.
-  nsCAutoString overrideTypesFormatted;
-  if (aType != ePluginUnregister) {
-    overrideTypesFormatted.Assign(',');
-    nsAdoptingCString overrideTypes =
-      Preferences::GetCString("plugin.disable_full_page_plugin_for_types");
-    overrideTypesFormatted += overrideTypes;
-    overrideTypesFormatted.Append(',');
-  }
-  
-  nsACString::const_iterator start, end;
-  for (PRUint32 i = 0; i < mMimeTypes.Length(); i++) {
-    if (aType == ePluginUnregister) {
-      nsXPIDLCString value;
-      if (NS_SUCCEEDED(catMan->GetCategoryEntry("Gecko-Content-Viewers",
-                                                mMimeTypes[i].get(),
-                                                getter_Copies(value)))) {
-        // Only delete the entry if a plugin registered for it
-        if (strcmp(value, contractId) == 0) {
-          catMan->DeleteCategoryEntry("Gecko-Content-Viewers",
-                                      mMimeTypes[i].get(),
-                                      true);
-        }
-      }
-    } else {
-      overrideTypesFormatted.BeginReading(start);
-      overrideTypesFormatted.EndReading(end);
-      
-      nsCAutoString commaSeparated; 
-      commaSeparated.Assign(',');
-      commaSeparated += mMimeTypes[i];
-      commaSeparated.Append(',');
-      if (!FindInReadable(commaSeparated, start, end)) {
-        catMan->AddCategoryEntry("Gecko-Content-Viewers",
-                                 mMimeTypes[i].get(),
-                                 contractId,
-                                 false, /* persist: broken by bug 193031 */
-                                 aOverrideInternalTypes, /* replace if we're told to */
-                                 nsnull);
-      }
-    }
-    
-    PLUGIN_LOG(PLUGIN_LOG_NOISY,
-               ("nsPluginTag::RegisterWithCategoryManager mime=%s, plugin=%s\n",
-                mMimeTypes[i].get(), mFileName.get()));
-  }
-}
-
 void nsPluginTag::Mark(PRUint32 mask)
 {
   bool wasEnabled = IsEnabled();
   mFlags |= mask;
-  // Update entries in the category manager if necessary.
+
   if (mPluginHost && wasEnabled != IsEnabled()) {
-    if (wasEnabled)
-      RegisterWithCategoryManager(false, nsPluginTag::ePluginUnregister);
-    else
-      RegisterWithCategoryManager(false, nsPluginTag::ePluginRegister);
+    mPluginHost->UpdatePluginInfo(this);
   }
 }
 
 void nsPluginTag::UnMark(PRUint32 mask)
 {
   bool wasEnabled = IsEnabled();
   mFlags &= ~mask;
-  // Update entries in the category manager if necessary.
+
   if (mPluginHost && wasEnabled != IsEnabled()) {
-    if (wasEnabled)
-      RegisterWithCategoryManager(false, nsPluginTag::ePluginUnregister);
-    else
-      RegisterWithCategoryManager(false, nsPluginTag::ePluginRegister);
+    mPluginHost->UpdatePluginInfo(this);
   }
 }
 
 bool nsPluginTag::HasFlag(PRUint32 flag)
 {
   return (mFlags & flag) != 0;
 }
 
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -28,21 +28,16 @@ struct nsPluginInfo;
 // no longer used                   0x0008    // reuse only if regenerating pluginreg.dat
 #define NS_PLUGIN_FLAG_BLOCKLISTED  0x0010    // this is a blocklisted plugin
 
 // A linked-list of plugin information that is used for instantiating plugins
 // and reflecting plugin information into JavaScript.
 class nsPluginTag : public nsIPluginTag
 {
 public:
-  enum nsRegisterType {
-    ePluginRegister,
-    ePluginUnregister
-  };
-  
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPLUGINTAG
   
   nsPluginTag(nsPluginTag* aPluginTag);
   nsPluginTag(nsPluginInfo* aPluginInfo);
   nsPluginTag(const char* aName,
               const char* aDescription,
               const char* aFileName,
@@ -58,18 +53,16 @@ public:
   
   void SetHost(nsPluginHost * aHost);
   void TryUnloadPlugin(bool inShutdown);
   void Mark(PRUint32 mask);
   void UnMark(PRUint32 mask);
   bool HasFlag(PRUint32 flag);
   PRUint32 Flags();
   bool IsEnabled();
-  void RegisterWithCategoryManager(bool aOverrideInternalTypes,
-                                   nsRegisterType aType = ePluginRegister);
   
   nsRefPtr<nsPluginTag> mNext;
   nsPluginHost *mPluginHost;
   nsCString     mName; // UTF-8
   nsCString     mDescription; // UTF-8
   nsTArray<nsCString> mMimeTypes; // UTF-8
   nsTArray<nsCString> mMimeDescriptions; // UTF-8
   nsTArray<nsCString> mExtensions; // UTF-8
--- a/dom/tests/mochitest/ajax/offline/offlineTests.js
+++ b/dom/tests/mochitest/ajax/offline/offlineTests.js
@@ -193,36 +193,40 @@ failEvent: function(e)
   OfflineTest.ok(false, "Unexpected event: " + e.type);
 },
 
 // The offline API as specified has no way to watch the load of a resource
 // added with applicationCache.mozAdd().
 waitForAdd: function(url, onFinished) {
   // Check every half second for ten seconds.
   var numChecks = 20;
+
+  var waitForAddListener = {
+    onCacheEntryAvailable: function(entry, access, status) {
+      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+      if (entry) {
+        entry.close();
+        onFinished();
+        return;
+      }
+
+      if (--numChecks == 0) {
+        onFinished();
+        return;
+      }
+
+      setTimeout(OfflineTest.priv(waitFunc), 500);
+    }
+  };
+
   var waitFunc = function() {
     var cacheSession = OfflineTest.getActiveSession();
-    var entry;
-    try {
-      entry = cacheSession.openCacheEntry(url, Ci.nsICache.ACCESS_READ, false);
-    } catch (e) {
-    }
-
-    if (entry) {
-      entry.close();
-      onFinished();
-      return;
-    }
-
-    if (--numChecks == 0) {
-      onFinished();
-      return;
-    }
-
-    setTimeout(OfflineTest.priv(waitFunc), 500);
+    cacheSession.asyncOpenCacheEntry(url,
+                                     Ci.nsICache.ACCESS_READ,
+                                     waitForAddListener);
   }
 
   setTimeout(this.priv(waitFunc), 500);
 },
 
 getManifestUrl: function()
 {
   return window.top.document.documentElement.getAttribute("manifest");
@@ -254,76 +258,101 @@ priv: function(func)
 {
   var self = this;
   return function() {
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
     func(arguments);
   }
 },
 
-checkCustomCache: function(group, url, expectEntry)
+checkCustomCache: function(group, url, expectEntry, callback)
 {
   var serv = Cc["@mozilla.org/network/application-cache-service;1"]
              .getService(Ci.nsIApplicationCacheService);
   var cache = serv.getActiveCache(group);
   var cacheSession = null;
   if (cache) {
     var cacheService = Cc["@mozilla.org/network/cache-service;1"]
                        .getService(Ci.nsICacheService);
     cacheSession = cacheService.createSession(cache.clientID,
                                       Ci.nsICache.STORE_OFFLINE,
                                       true);
   }
 
-  this._checkCache(cacheSession, url, expectEntry);
+  this._checkCache(cacheSession, url, expectEntry, callback);
 },
 
-checkCache: function(url, expectEntry)
+checkCacheEntries: function(entries, callback)
+{
+  var checkNextEntry = function() {
+    if (entries.length == 0) {
+      setTimeout(OfflineTest.priv(callback), 0);
+    } else {
+      OfflineTest.checkCache(entries[0][0], entries[0][1], checkNextEntry);
+      entries.shift();
+    }
+  }
+
+  checkNextEntry();
+},
+
+checkCache: function(url, expectEntry, callback)
 {
   var cacheSession = this.getActiveSession();
-  this._checkCache(cacheSession, url, expectEntry);
+  this._checkCache(cacheSession, url, expectEntry, callback);
 },
 
-_checkCache: function(cacheSession, url, expectEntry)
+_checkCache: function(cacheSession, url, expectEntry, callback)
 {
   if (!cacheSession) {
     if (expectEntry) {
       this.ok(false, url + " should exist in the offline cache");
     } else {
       this.ok(true, url + " should not exist in the offline cache");
     }
+    setTimeout(this.priv(callback), 0);
     return;
   }
 
-  try {
-    var entry = cacheSession.openCacheEntry(url, Ci.nsICache.ACCESS_READ, false);
-    if (expectEntry) {
-      this.ok(true, url + " should exist in the offline cache");
-    } else {
-      this.ok(false, url + " should not exist in the offline cache");
-    }
-    entry.close();
-  } catch (e) {
-    if (e.result == NS_ERROR_CACHE_KEY_NOT_FOUND) {
-      if (expectEntry) {
-        this.ok(false, url + " should exist in the offline cache");
+  var _checkCacheListener = {
+    onCacheEntryAvailable: function(entry, access, status) {
+      netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+      if (entry) {
+        if (expectEntry) {
+          OfflineTest.ok(true, url + " should exist in the offline cache");
+        } else {
+          OfflineTest.ok(false, url + " should not exist in the offline cache");
+        }
+        entry.close();
       } else {
-        this.ok(true, url + " should not exist in the offline cache");
+        if (status == NS_ERROR_CACHE_KEY_NOT_FOUND) {
+          if (expectEntry) {
+            OfflineTest.ok(false, url + " should exist in the offline cache");
+          } else {
+            OfflineTest.ok(true, url + " should not exist in the offline cache");
+          }
+        } else if (status == NS_ERROR_CACHE_KEY_WAIT_FOR_VALIDATION) {
+          // There was a cache key that we couldn't access yet, that's good enough.
+          if (expectEntry) {
+            OfflineTest.ok(!mustBeValid, url + " should exist in the offline cache");
+          } else {
+            OfflineTest.ok(mustBeValid, url + " should not exist in the offline cache");
+          }
+        } else {
+          OfflineTest.ok(false, "got invalid error for " + url);
+        }
       }
-    } else if (e.result == NS_ERROR_CACHE_KEY_WAIT_FOR_VALIDATION) {
-      // There was a cache key that we couldn't access yet, that's good enough.
-      if (expectEntry) {
-        this.ok(!mustBeValid, url + " should exist in the offline cache");
-      } else {
-        this.ok(mustBeValid, url + " should not exist in the offline cache");
-      }
-    } else {
-      throw e;
+      setTimeout(OfflineTest.priv(callback), 0);
     }
-  }
+  };
+
+  cacheSession.asyncOpenCacheEntry(url,
+                                   Ci.nsICache.ACCESS_READ,
+                                   _checkCacheListener,
+                                   false);
 },
 
 setSJSState: function(sjsPath, stateQuery)
 {
   var client = new XMLHttpRequest();
   client.open("GET", sjsPath + "?state=" + stateQuery, false);
 
   var appcachechannel = SpecialPowers.wrap(client).channel.QueryInterface(Ci.nsIApplicationCacheChannel);
--- a/dom/tests/mochitest/ajax/offline/test_badManifestMagic.html
+++ b/dom/tests/mochitest/ajax/offline/test_badManifestMagic.html
@@ -5,27 +5,32 @@
 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
 <script type="text/javascript">
 
 var gGotChecking = false;
 
+function finishTest() {
+  OfflineTest.teardown();
+  OfflineTest.finish();
+}
+
 function handleError() {
   OfflineTest.ok(gGotChecking, "Expected checking event");
   OfflineTest.ok(true, "Expected error event");
 
   // These items are listed in the manifest, but the error should have
   // prevented them from being committed to the cache.
-  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", false);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false);
-
-  OfflineTest.teardown();
-  OfflineTest.finish();
+  var entries = [
+    ["http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", false],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false]
+  ];
+  OfflineTest.checkCacheEntries(entries, finishTest);
 }
 
 
 if (OfflineTest.setup()) {
   // Don't expect a bunch of events.
   applicationCache.ondownloading = OfflineTest.failEvent;
   applicationCache.onupdateready = OfflineTest.failEvent;
   applicationCache.oncached = OfflineTest.failEvent;
--- a/dom/tests/mochitest/ajax/offline/test_badManifestMime.html
+++ b/dom/tests/mochitest/ajax/offline/test_badManifestMime.html
@@ -5,27 +5,32 @@
 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
 <script type="text/javascript">
 
 var gGotChecking = false;
 
+function finishTest() {
+  OfflineTest.teardown();
+  OfflineTest.finish();
+}
+
 function handleError() {
   OfflineTest.ok(gGotChecking, "Expected checking event");
   OfflineTest.ok(true, "Expected error event");
 
   // These items are listed in the manifest, but the error should have
   // prevented them from being committed to the cache.
-  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", false);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false);
-
-  OfflineTest.teardown();
-  OfflineTest.finish();
+  var entries = [
+    ["http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", false],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false]
+  ];
+  OfflineTest.checkCacheEntries(entries, finishTest);
 }
 
 if (OfflineTest.setup()) {
   // Don't expect a bunch of events.
   applicationCache.ondownloading = OfflineTest.failEvent;
   applicationCache.onupdateready = OfflineTest.failEvent;
   applicationCache.oncached = OfflineTest.failEvent;
   applicationCache.onnoupdate = OfflineTest.failEvent;
--- a/dom/tests/mochitest/ajax/offline/test_fallback.html
+++ b/dom/tests/mochitest/ajax/offline/test_fallback.html
@@ -88,27 +88,34 @@ function onFallbackLoad(fallbackIdentifi
   if (gStep == 105) {
     finalize();
     return;
   }
 
   ++gStep;
 }
 
+
+function finishTest()
+{
+  OfflineTest.teardown();
+  OfflineTest.finish();
+}
+
 function finalize()
 {
   netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
 
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/sub-non-existing.html", false);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/sub/non-existing.html", false);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/non-existing.html", false);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace2/non-existing.html", false);
-
-  OfflineTest.teardown();
-  OfflineTest.finish();
+  var entries = [
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/sub-non-existing.html", false],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/sub/non-existing.html", false],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/non-existing.html", false],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace2/non-existing.html", false]
+  ];
+  OfflineTest.checkCacheEntries(entries, finishTest);
 }
 
 SimpleTest.waitForExplicitFinish();
 
 if (OfflineTest.setup()) {
   applicationCache.onerror = OfflineTest.failEvent;
   applicationCache.onupdateready = OfflineTest.failEvent;
   applicationCache.oncached = OfflineTest.priv(manifestUpdated);
--- a/dom/tests/mochitest/ajax/offline/test_identicalManifest.html
+++ b/dom/tests/mochitest/ajax/offline/test_identicalManifest.html
@@ -6,60 +6,70 @@
 <script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
 <script type="text/javascript">
 
 var gGotChecking = false;
 var gGotDownloading = false;
 
+function finishTest()
+{
+  OfflineTest.teardown();
+  OfflineTest.finish();
+}
+
 function noUpdate()
 {
   OfflineTest.ok(gGotChecking, "Should get a checking event");
   OfflineTest.ok(!gGotDownloading, "Should not get a downloading event");
 
-  // The document that requested the manifest should be in the cache
-  OfflineTest.checkCache(window.location.href, true);
+  var entries = [
+    // The document that requested the manifest should be in the cache
+    [window.location.href, true],
 
-  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", true);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
-  
-  OfflineTest.teardown();
-
-  OfflineTest.finish();
+    ["http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", true],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true]
+  ];
+  OfflineTest.checkCacheEntries(entries, finishTest);
 }
 
 function manifestUpdated()
 {
   OfflineTest.ok(gGotChecking, "Should get a checking event");
   OfflineTest.ok(gGotDownloading, "Should get a downloading event");
 
-  // The manifest itself should be in the cache
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest", true);
+  var entries = [
+    // The manifest itself should be in the cache
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest", true],
 
-  // The document that requested the manifest should be in the cache
-  OfflineTest.checkCache(window.location.href, true);
+    // The document that requested the manifest should be in the cache
+    [window.location.href, true],
 
-  // The entries from the manifest should be in the cache
-  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", true);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
+    // The entries from the manifest should be in the cache
+    ["http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", true],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true],
 
-  // The bad entries from the manifest should not be in the cache
-  OfflineTest.checkCache("bad:/uri/invalid", false);
+    // The bad entries from the manifest should not be in the cache
+    ["bad:/uri/invalid", false]
+  ];
+  OfflineTest.checkCacheEntries(entries, manifestUpdatedContinue);
+}
 
+function manifestUpdatedContinue()
+{
   // Now make sure applicationCache.update() does what we expect.
   applicationCache.oncached = OfflineTest.failEvent;
   applicationCache.onnoupdate = OfflineTest.priv(noUpdate);
   
   gGotChecking = false;
   gGotDownloading = false;
   applicationCache.update();
 }
 
-
 if (OfflineTest.setup()) {
   applicationCache.onerror = OfflineTest.failEvent;
   applicationCache.onnoupdate = OfflineTest.failEvent;
   applicationCache.onudpateready = OfflineTest.failEvent;
 
   applicationCache.onchecking = function() { gGotChecking = true; };
   applicationCache.ondownloading = function() { gGotDownloading = true; };
   applicationCache.oncached = OfflineTest.priv(manifestUpdated);
--- a/dom/tests/mochitest/ajax/offline/test_missingFile.html
+++ b/dom/tests/mochitest/ajax/offline/test_missingFile.html
@@ -6,28 +6,33 @@
 <script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
 <script type="text/javascript">
 
 var gGotChecking = false;
 var gGotDownloading = false;
 
+function finishTest() {
+  OfflineTest.teardown();
+  OfflineTest.finish();
+}
+
 function handleError() {
   OfflineTest.ok(gGotChecking, "Expected checking event");
   OfflineTest.ok(gGotDownloading, "Expected downloading event");
   OfflineTest.ok(true, "Expected error event");
 
   // These items are listed in the manifest, but the error should have
   // prevented them from being committed to the cache.
-  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", false);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false);
-
-  OfflineTest.teardown();
-  OfflineTest.finish();
+  var entries = [
+    ["http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", false],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", false]
+  ];
+  OfflineTest.checkCacheEntries(entries, finishTest);
 }
 
 if (OfflineTest.setup()) {
   // Don't expect any "success" events.
   applicationCache.onupdateready = function() { OfflineTest.failEvent("updateready"); }
   applicationCache.oncached = function() { OfflineTest.failEvent("cached"); }
   applicationCache.onnoupdate = function() { OfflineTest.failEvent("noupdate"); }
 
--- a/dom/tests/mochitest/ajax/offline/test_offlineIFrame.html
+++ b/dom/tests/mochitest/ajax/offline/test_offlineIFrame.html
@@ -3,38 +3,39 @@
 <title>offline iframe test</title>
 
 <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
 <script type="text/javascript" src="/tests/dom/tests/mochitest/ajax/offline/offlineTests.js"></script>
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
 <script type="text/javascript">
 
+
+function checkEntries() {
+  var entries = [
+    // The manifest itself should be in the cache
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest", true],
+
+    // The entries from the manifest should be in the cache
+    ["http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", true],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true]
+  ];
+  OfflineTest.checkCacheEntries(entries, function() { window.frames["offlineChild"].doneLoading(); });
+}
+
 function childFinished()
 {
   OfflineTest.teardown();
   OfflineTest.finish();
 }
 
-function manifestUpdated()
-{
-  // The manifest itself should be in the cache
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest", true);
-
-  // The entries from the manifest should be in the cache
-  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", true);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
-
-  window.frames["offlineChild"].doneLoading();
-}
-
 if (OfflineTest.setup()) {
   applicationCache.onerror = OfflineTest.failEvent;
 
-  applicationCache.oncached = OfflineTest.priv(manifestUpdated);
+  applicationCache.oncached = OfflineTest.priv(checkEntries);
 }
 
 SimpleTest.waitForExplicitFinish();
 
 </script>
 
 </head>
 
--- a/dom/tests/mochitest/ajax/offline/test_offlineMode.html
+++ b/dom/tests/mochitest/ajax/offline/test_offlineMode.html
@@ -68,52 +68,30 @@ function finalize()
 {
   window.clearTimeout(gCompleteTimeout);
 
   var ioserv = Cc["@mozilla.org/network/io-service;1"]
       .getService(Ci.nsIIOService);
 
   if (!ioserv.offline)
   {
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/notonwhitelist.html", true);
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingIframe.sjs", true);
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
-
     OfflineTest.is(gGotExplicitVersion, 1, "Explicit entry loaded");
     OfflineTest.is(gGotImplicitVersion, 1, "Implicit entry loaded");
     OfflineTest.is(gGotDynamicVersion, 1, "Dynamic entry loaded");
 
     gGotExplicitVersion = 0;
     gGotImplicitVersion = 0;
     gGotDynamicVersion = 0;
 
-
-    // Delete HTTP cache to ensure we are going from offline cache
-    var sessionServ = Cc["@mozilla.org/network/cache-service;1"]
-      .getService(Ci.nsICacheService);
-    cacheSession = sessionServ.createSession("HTTP", Ci.nsICache.STORE_ANYWHERE, Ci.nsICache.STREAM_BASED);
-    function doomHTTPCacheEntry(entryKey) {
-      entry = cacheSession.openCacheEntry(entryKey, Ci.nsICache.ACCESS_WRITE, false)
-      entry.doom();
-    }
-    doomHTTPCacheEntry("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/notonwhitelist.html");
-    doomHTTPCacheEntry("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingIframe.sjs");
-    doomHTTPCacheEntry("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html");
-
-
-    ioserv.offline = true;
-
-    gCompleteTimeout = window.setTimeout(OfflineTest.priv(finalize), 10000);
-
-    // remove error handling. in offline mode
-    // is correct to get error message
-    applicationCache.onerror = function() {gGotOnError = true;}
-
-    aFrame.location = "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/notonwhitelist.html";
-    // Starts the chain all over again but in offline mode.
+    var entries = [
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/notonwhitelist.html", true],
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingIframe.sjs", true],
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true]
+    ];
+    OfflineTest.checkCacheEntries(entries, goOffline);
   }
   else
   {
     gImplicitWindow.close();
 
     ioserv.offline = false;
 
     OfflineTest.is(gGotExplicitVersion, 1, "Explicit entry loaded");
@@ -121,16 +99,45 @@ function finalize()
     OfflineTest.is(gGotDynamicVersion, 1, "Dynamic entry loaded");
     OfflineTest.ok(gGotOnError, "Got onerror event invoked by implicit page load in offline mode");
 
     OfflineTest.teardown();
     OfflineTest.finish();
   }
 }
 
+function goOffline()
+{
+  var ioserv = Cc["@mozilla.org/network/io-service;1"]
+      .getService(Ci.nsIIOService);
+
+  // Delete HTTP cache to ensure we are going from offline cache
+  var sessionServ = Cc["@mozilla.org/network/cache-service;1"]
+      .getService(Ci.nsICacheService);
+  cacheSession = sessionServ.createSession("HTTP", Ci.nsICache.STORE_ANYWHERE, Ci.nsICache.STREAM_BASED);
+  function doomHTTPCacheEntry(entryKey) {
+    entry = cacheSession.openCacheEntry(entryKey, Ci.nsICache.ACCESS_WRITE, false)
+    entry.doom();
+  }
+  doomHTTPCacheEntry("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/notonwhitelist.html");
+  doomHTTPCacheEntry("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingIframe.sjs");
+  doomHTTPCacheEntry("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html");
+
+  ioserv.offline = true;
+
+  gCompleteTimeout = window.setTimeout(OfflineTest.priv(finalize), 10000);
+
+  // remove error handling. in offline mode
+  // is correct to get error message
+  applicationCache.onerror = function() {gGotOnError = true;}
+
+  aFrame.location = "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/notonwhitelist.html";
+  // Starts the chain all over again but in offline mode.
+}
+
 SimpleTest.waitForExplicitFinish();
 
 if (OfflineTest.setup()) {
   applicationCache.onerror = OfflineTest.failEvent;
   applicationCache.onupdateready = OfflineTest.failEvent;
   applicationCache.oncached = OfflineTest.priv(manifestUpdated);
 }
 
--- a/dom/tests/mochitest/ajax/offline/test_redirectUpdateItem.html
+++ b/dom/tests/mochitest/ajax/offline/test_redirectUpdateItem.html
@@ -7,72 +7,82 @@
 <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
 <script class="testbody" type="text/javascript">
 
 var gCurrentManifestVersion = 1;
 
 function manifestCached()
 {
-  OfflineTest.checkCache(
-    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/dynamicRedirect.sjs", false);
-  OfflineTest.checkCache(
-    "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/explicitRedirect.sjs", true);
+  var entries = [
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/dynamicRedirect.sjs", false],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/explicitRedirect.sjs", true],
+  ];
+  OfflineTest.checkCacheEntries(entries, manifestCachedContinue);
+}
+
+function manifestCachedContinue()
+{
   OfflineTest.is(gCurrentManifestVersion, 1, "Cached event for manifest version one");
 
   // Now add one dynamic entry (now with content overriden redirect sjs)
   applicationCache.mozAdd(
     "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/dynamicRedirect.sjs");
 
   // Wait for the dynamic entry be added to the cache...
   OfflineTest.waitForAdd(
     "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/dynamicRedirect.sjs",
     function() {
       // ...check it is there...
       OfflineTest.checkCache(
-        "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/dynamicRedirect.sjs", true);
-
-      // ...revert state of the dynamic entry on the server, now we get the redirect...
-      OfflineTest.setSJSState(
         "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/dynamicRedirect.sjs",
-        "");
+        true,
+        function() {
+          // ...revert state of the dynamic entry on the server, now we get the redirect...
+          OfflineTest.setSJSState(
+            "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/dynamicRedirect.sjs",
+            "");
 
-      // ...update manifest to the new version on the server...
-      OfflineTest.setSJSState(
-        "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/redirects.sjs",
-        "second");
-      gCurrentManifestVersion = 2;
+          // ...update manifest to the new version on the server...
+          OfflineTest.setSJSState(
+            "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/redirects.sjs",
+            "second");
+          gCurrentManifestVersion = 2;
 
-      // ...and finally invoke the cache update.
-      applicationCache.update();
+          // ...and finally invoke the cache update.
+          applicationCache.update();
+        });
     });
 }
 
 function manifestUpdated()
 {
   switch (gCurrentManifestVersion)
   {
     case 2:
       // Check the dynamic entry was removed from the cache (because of the redirect)...
       OfflineTest.checkCache(
-        "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/dynamicRedirect.sjs", false);
-
-      // ...return back redirect for the explicit entry...
-      OfflineTest.setSJSState(
-        "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/explicitRedirect.sjs",
-        "");
+        "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/dynamicRedirect.sjs",
+        false,
+        function () {
+          // ...return back redirect for the explicit entry...
+          OfflineTest.setSJSState(
+            "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/explicitRedirect.sjs",
+            "");
 
-      // ...update the manifest to the third version...
-      OfflineTest.setSJSState(
-        "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/redirects.sjs",
-        "third");
-      gCurrentManifestVersion = 3;
+          // ...update the manifest to the third version...
+          OfflineTest.setSJSState(
+            "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/redirects.sjs",
+            "third");
+          gCurrentManifestVersion = 3;
 
-      // ...and invoke the cache update, now we must get error.
-      applicationCache.update();
+          // ...and invoke the cache update, now we must get error.
+          applicationCache.update();
+        });
+
       break;
 
     case 3:
       OfflineTest.ok(false, "Update didn't fail for third version of the manifest");
       finish();
       break;
   }
 }
--- a/dom/tests/mochitest/ajax/offline/test_simpleManifest.html
+++ b/dom/tests/mochitest/ajax/offline/test_simpleManifest.html
@@ -11,85 +11,94 @@
 var gGotChecking = false;
 var gGotDownloading = false;
 
 ok(applicationCache.mozItems.length == 0,
    "applicationCache.mozItems should be available and empty before associating with a cache.");
 
 function addFinished()
 {
-  // Check that the entry was added successfully
-  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/EventUtils.js",
-                         true);
-
   OfflineTest.ok(applicationCache.mozLength == 1, "applicationCache should have one dynamic entry (deprecated API)");
   OfflineTest.ok(applicationCache.mozItem(0) == "http://mochi.test:8888/tests/SimpleTest/EventUtils.js",
     "applicationCache's dynamic entry should be the one we expect (deprecated API)");
 
   OfflineTest.ok(applicationCache.mozItems.length == 1, "applicationCache should have one dynamic entry");
   OfflineTest.ok(applicationCache.mozItems[0] == "http://mochi.test:8888/tests/SimpleTest/EventUtils.js",
     "applicationCache's dynamic entry should be the one we expect");
 
   OfflineTest.ok(applicationCache.mozHasItem("http://mochi.test:8888/tests/SimpleTest/EventUtils.js"),
                  "applicationCache.mozHasItem() should see the dynamic entry");
 
+  // Check that the entry was added successfully
+  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/EventUtils.js",
+                         true,
+                         removeItem);
+}
+
+function removeItem()
+{
   // Now test that removes work
   applicationCache.mozRemove("http://mochi.test:8888/tests/SimpleTest/EventUtils.js");
 
-  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/EventUtils.js",
-                         false);
   OfflineTest.ok(applicationCache.mozLength == 0,
                  "applicationCache should have no dynamic entries (deprecated API)");
   OfflineTest.ok(applicationCache.mozItems.length == 0,
                  "applicationCache should have no dynamic entries");
   OfflineTest.ok(!applicationCache.mozHasItem("http://mochi.test:8888/tests/SimpleTest/EventUtils.js"),
                  "applicationCache.mozHasItem() should not see the removed dynamic entry");
 
-  // We're done
-
-  OfflineTest.teardown();
-
-  OfflineTest.finish();
+  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/EventUtils.js",
+                         false,
+                         function() {
+                           // We're done
+                           OfflineTest.teardown();
+                           OfflineTest.finish();
+                         });
 }
 
 function manifestUpdated()
 {
   OfflineTest.ok(gGotChecking, "Should get a checking event");
   OfflineTest.ok(gGotDownloading, "Should get a downloading event");
 
-  // The manifest itself should be in the cache
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest", true);
-
-  // The document that requested the manifest should be in the cache
-  OfflineTest.checkCache(window.location.href, true);
-
-  // The entries from the manifest should be in the cache
-  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", true);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
-
-  // The bad entries from the manifest should not be in the cache
-  OfflineTest.checkCache("bad:/uri/invalid", false);
-
   OfflineTest.is(applicationCache.status, 1, "Cache status should be 1 (CACHED)");
 
-  try {
-    applicationCache.swapCache();
-    OfflineTest.ok(false, "application.swapCache() should fail after initial update.");
-  } catch(ex) {
-    OfflineTest.ok(true, "application.swapCache() should fail after initial update.");
-  }
+  var entries = [
+    // The manifest itself should be in the cache
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/simpleManifest.cacheManifest", true],
+
+    // The document that requested the manifest should be in the cache
+    [window.location.href, true],
+
+    // The entries from the manifest should be in the cache
+    ["http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", true],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true],
 
-  // XXX: make sure that the previous version went away after the swapCache().
+    // The bad entries from the manifest should not be in the cache
+    ["bad:/uri/invalid", false]
+  ];
+  OfflineTest.checkCacheEntries(
+    entries,
+    function() {
+      try {
+        applicationCache.swapCache();
+        OfflineTest.ok(false, "application.swapCache() should fail after initial update.");
+      } catch(ex) {
+        OfflineTest.ok(true, "application.swapCache() should fail after initial update.");
+      }
 
-  // Now add a file using the applicationCache API
-  applicationCache.mozAdd("http://mochi.test:8888/tests/SimpleTest/EventUtils.js");
+      // XXX: make sure that the previous version went away after the swapCache().
+
+      // Now add a file using the applicationCache API
+      applicationCache.mozAdd("http://mochi.test:8888/tests/SimpleTest/EventUtils.js");
 
-  // Wait for the add() to be downloaded
-  OfflineTest.waitForAdd("http://mochi.test:8888/tests/SimpleTest/EventUtils.js",
-                         OfflineTest.priv(addFinished));
+      // Wait for the add() to be downloaded
+      OfflineTest.waitForAdd("http://mochi.test:8888/tests/SimpleTest/EventUtils.js",
+                             OfflineTest.priv(addFinished));
+    });
 }
 
 if (OfflineTest.setup()) {
   OfflineTest.ok(applicationCache instanceof EventTarget,
                  "applicationCache should be an event target");
 
   applicationCache.onerror = OfflineTest.failEvent;
 
--- a/dom/tests/mochitest/ajax/offline/test_updatingManifest.html
+++ b/dom/tests/mochitest/ajax/offline/test_updatingManifest.html
@@ -138,140 +138,161 @@ function implicitLoaded(aWindow, errorOc
   OfflineTest.waitForAdd("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html",
       implicitCached);
 }
 
 function implicitCached()
 {
   // Checking first version of the manifest + another implict page caching
 
-  // Explicit entries
-  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", false);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
-
-  // Fallback entries
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", true);
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback2.html", false);
-
   // Whitelist entries
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", false);
   checkFallbackAndWhitelisting("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", "", true);
 
-  // Implicit entries
-  OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
-
-  // Dynamic entries
-  OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/EventUtils.js", true);
-
   // Fallback URI selection check
   checkFallbackAndWhitelisting("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/opp.html",
       "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", false);
   checkFallbackAndWhitelisting("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/sub/opp.html",
       "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", false);
 
   // Cache object status
   OfflineTest.is(applicationCache.status, Components.interfaces.nsIDOMOfflineResourceList.IDLE,
       "we have associated application cache (1)");
 
   OfflineTest.is(gGotFrameVersion, 1, "IFrame version 1");
 
-  ++gStep;
+  var entries = [
+    // Explicit entries
+    ["http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", false],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true],
+
+    // Fallback entries
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", true],
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback2.html", false],
+
+    // Whitelist entries
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", false],
 
-  OfflineTest.setSJSState("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingManifest.sjs", "second");
+    // Implicit entries
+    ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true],
 
-  applicationCache.update();
-  // Invokes manifestUpdated()
+    // Dynamic entries
+    ["http://mochi.test:8888/tests/SimpleTest/EventUtils.js", true]
+  ];
+  OfflineTest.checkCacheEntries(
+    entries,
+    function() {
+      ++gStep;
+
+      OfflineTest.setSJSState("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingManifest.sjs", "second");
+
+      applicationCache.update();
+      // Invokes manifestUpdated()
+    });
 }
 
 function manifestUpdated()
 {
   OfflineTest.ok(gStep == 1 || gStep == 2);
 
   switch (gStep)
   {
   case 1:
     // Processing second version of the manifest.
 
-    // Explicit entries
-    OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", true);
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
-
-    // Fallback entries
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", true);
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback2.html", true);
-
     // Whitelist entries
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", false);
     checkFallbackAndWhitelisting("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", "", false);
 
-    // Implicit entries
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
-
-    // Dynamic entries
-    OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/EventUtils.js", true);
-
     // Fallback URI selection check
     checkFallbackAndWhitelisting("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/opp.html",
         "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", false);
     checkFallbackAndWhitelisting("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/sub/opp.html",
         "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback2.html", false);
 
     // Cache object status
     OfflineTest.is(applicationCache.status, Components.interfaces.nsIDOMOfflineResourceList.UPDATEREADY,
         "we have associated application cache and update is pending (2)");
 
-    ++gStep;
+    var entries = [
+      // Explicit entries
+      ["http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", true],
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true],
+
+      // Fallback entries
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", true],
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback2.html", true],
+
+      // Whitelist entries
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", false],
+
+      // Implicit entries
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true],
 
-    OfflineTest.setSJSState("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingManifest.sjs", "third");
-    OfflineTest.setSJSState("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingIframe.sjs", "second");
+      // Dynamic entries
+      ["http://mochi.test:8888/tests/SimpleTest/EventUtils.js", true]
+    ];
+    OfflineTest.checkCacheEntries(
+      entries,
+      function() {
+        ++gStep;
 
-    gGotFrameVersion = 0;
-    gCallOnUpdatingFrameLoad = function() {applicationCache.update();};
-    updatingFrame.location.reload();
+        OfflineTest.setSJSState("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingManifest.sjs", "third");
+        OfflineTest.setSJSState("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingIframe.sjs", "second");
+
+        gGotFrameVersion = 0;
+        gCallOnUpdatingFrameLoad = function() {applicationCache.update();};
+        updatingFrame.location.reload();
+      });
 
     break;
 
   case 2:
     // Processing third version of the manifest.
 
-    // Explicit entries
-    OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", false);
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true);
-
-    // Fallback entries
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", false);
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback2.html", true);
-
     // Whitelist entries
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", false);
     checkFallbackAndWhitelisting("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", "", true);
 
-    // Implicit entries
-    OfflineTest.checkCache("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true);
-
-    // Dynamic entries
-    OfflineTest.checkCache("http://mochi.test:8888/tests/SimpleTest/EventUtils.js", true);
-
     // Fallback URI selection check
     checkFallbackAndWhitelisting("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/opp.html",
         "", false);
     checkFallbackAndWhitelisting("http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/namespace1/sub/opp.html",
         "http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback2.html", false);
 
     // Cache object status
     OfflineTest.is(applicationCache.status, Components.interfaces.nsIDOMOfflineResourceList.UPDATEREADY,
         "we have associated application cache and update is pending (3)");
 
     OfflineTest.is(gGotFrameVersion, 1, "IFrame version 1 because cache was not swapped");
 
-    ++gStep;
+    var entries = [
+      // Explicit entries
+      ["http://mochi.test:8888/tests/SimpleTest/SimpleTest.js", false],
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/offlineTests.js", true],
+
+      // Fallback entries
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback.html", false],
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/fallback2.html", true],
+
+      // Whitelist entries
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/onwhitelist.html", false],
 
-    applicationCache.onnoupdate = OfflineTest.priv(manifestNoUpdate);
-    applicationCache.update();
-    // Invokes manifestNoUpdate()
+      // Implicit entries
+      ["http://mochi.test:8888/tests/dom/tests/mochitest/ajax/offline/updatingImplicit.html", true],
+
+      // Dynamic entries
+      ["http://mochi.test:8888/tests/SimpleTest/EventUtils.js", true]
+    ];
+    OfflineTest.checkCacheEntries(
+      entries,
+      function() {
+        ++gStep;
+
+        applicationCache.onnoupdate = OfflineTest.priv(manifestNoUpdate);
+        applicationCache.update();
+        // Invokes manifestNoUpdate()
+      });
 
     break;
   }
 }
 
 function manifestNoUpdate()
 {
   applicationCache.onnoupdate = null;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -122,36 +122,50 @@ SwapToISupportsArray(SmartPtr<T>& aSrc,
     static_cast<typename ISupportsBaseInfo<T>::ISupportsBase*>(raw);
   dest->swap(rawSupports);
 }
 
 NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsWorkerMallocSizeOf, "js-worker")
 
 struct WorkerJSRuntimeStats : public JS::RuntimeStats
 {
-  WorkerJSRuntimeStats()
-   : JS::RuntimeStats(JsWorkerMallocSizeOf) { }
+  WorkerJSRuntimeStats(nsACString &aRtPath)
+   : JS::RuntimeStats(JsWorkerMallocSizeOf), mRtPath(aRtPath) { }
+
+  ~WorkerJSRuntimeStats() {
+    for (size_t i = 0; i != compartmentStatsVector.length(); i++) {
+      free(compartmentStatsVector[i].extra1);
+      // no need to free |extra2|, because it's a static string
+    }
+  }
 
   virtual void initExtraCompartmentStats(JSCompartment *c,
                                          JS::CompartmentStats *cstats) MOZ_OVERRIDE
   {
     MOZ_ASSERT(!cstats->extra1);
     MOZ_ASSERT(!cstats->extra2);
     
     // ReportJSRuntimeExplicitTreeStats expects that cstats->{extra1,extra2}
     // are char pointers.
 
-    // This is the |cPathPrefix|.  Using NULL here means that we'll end up
-    // using WorkerMemoryReporter::mRtPath as the path prefix for each
-    // compartment.  See xpc::ReportJSRuntimeExplicitTreeStats().
-    cstats->extra1 = NULL;
-
-    // This is the |cName|.
-    cstats->extra2 = (void *)(js::IsAtomsCompartment(c) ? "Web Worker Atoms" : "Web Worker");
+    // This is the |cJSPathPrefix|.  Each worker has exactly two compartments:
+    // one for atoms, and one for everything else.
+    nsCString cJSPathPrefix(mRtPath);
+    cJSPathPrefix += js::IsAtomsCompartment(c)
+                   ? NS_LITERAL_CSTRING("compartment(web-worker-atoms)/")
+                   : NS_LITERAL_CSTRING("compartment(web-worker)/");
+    cstats->extra1 = strdup(cJSPathPrefix.get());
+
+    // This is the |cDOMPathPrefix|, which should never be used when reporting
+    // with workers (hence the "?!").
+    cstats->extra2 = (void *)"explicit/workers/?!/";
   }
+
+private:
+  nsCString mRtPath;
 };
   
 class WorkerMemoryReporter MOZ_FINAL : public nsIMemoryMultiReporter
 {
   WorkerPrivate* mWorkerPrivate;
   nsCString mAddressString;
   nsCString mRtPath;
 
@@ -227,17 +241,17 @@ public:
   }
 
   NS_IMETHOD
   CollectReports(nsIMemoryMultiReporterCallback* aCallback,
                  nsISupports* aClosure)
   {
     AssertIsOnMainThread();
 
-    WorkerJSRuntimeStats rtStats;
+    WorkerJSRuntimeStats rtStats(mRtPath);
     nsresult rv = CollectForRuntime(/* isQuick = */false, &rtStats);
     if (NS_FAILED(rv)) {
       return rv;
     }
 
     // Always report, even if we're disabled, so that we at least get an entry
     // in about::memory.
     return xpc::ReportJSRuntimeExplicitTreeStats(rtStats, mRtPath,
@@ -1576,17 +1590,17 @@ public:
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
   {
     JSRuntime *rt = JS_GetRuntime(aCx);
     if (mIsQuick) {
       *static_cast<int64_t*>(mData) = JS::GetExplicitNonHeapForRuntime(rt, JsWorkerMallocSizeOf);
       *mSucceeded = true;
     } else {
-      *mSucceeded = JS::CollectRuntimeStats(rt, static_cast<JS::RuntimeStats*>(mData));
+      *mSucceeded = JS::CollectRuntimeStats(rt, static_cast<JS::RuntimeStats*>(mData), nsnull);
     }
 
     {
       MutexAutoLock lock(mMutex);
       mDone = true;
       mCondVar.Notify();
     }
 
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/base/crashtests/772282.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+  var root = document.documentElement;
+  while(root.firstChild) { root.removeChild(root.firstChild); }
+  var body = document.createElementNS("http://www.w3.org/1999/xhtml", "body");
+  body.setAttributeNS(null, "contenteditable", "true");
+  var img = document.createElementNS("http://www.w3.org/1999/xhtml", "img");
+  body.appendChild(img);
+  root.appendChild(body);
+  document.removeChild(root);
+  document.appendChild(root);
+  document.execCommand("insertText", false, "5");
+  document.execCommand("selectAll", false, null);
+  document.execCommand("insertParagraph", false, null);
+  document.execCommand("increasefontsize", false, null);
+}
+
+</script>
+</head>
+
+<body onload="boom();"></body>
+</html>
--- a/editor/libeditor/base/crashtests/crashtests.list
+++ b/editor/libeditor/base/crashtests/crashtests.list
@@ -11,8 +11,9 @@ load 636074-1.html
 load 713427-1.html
 load 713427-2.xhtml
 load 762183.html
 load 766360.html
 load 766413.html
 load 766845.xhtml
 load 768765.html
 needs-focus load 771749.html
+load 772282.html
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -1725,20 +1725,21 @@ nsEditor::MoveNode(nsIDOMNode *aNode, ns
   nsAutoMoveNodeSelNotify selNotify(mRangeUpdater, oldParent, oldOffset, aParent, aOffset);
   
   // need to adjust aOffset if we are moving aNode further along in its current parent
   if ((aParent == oldParent.get()) && (oldOffset < aOffset)) 
   {
     aOffset--;  // this is because when we delete aNode, it will make the offsets after it off by one
   }
 
-  // put aNode in new parent
-  res = DeleteNode(aNode);
+  // Hold a reference so aNode doesn't go away when we remove it (bug 772282)
+  nsCOMPtr<nsIDOMNode> node = aNode;
+  res = DeleteNode(node);
   NS_ENSURE_SUCCESS(res, res);
-  return InsertNode(aNode, aParent, aOffset);
+  return InsertNode(node, aParent, aOffset);
 }
 
 
 NS_IMETHODIMP
 nsEditor::AddEditorObserver(nsIEditorObserver *aObserver)
 {
   // we don't keep ownership of the observers.  They must
   // remove themselves as observers before they are destroyed.
--- a/editor/libeditor/html/nsHTMLEditRules.cpp
+++ b/editor/libeditor/html/nsHTMLEditRules.cpp
@@ -2811,17 +2811,17 @@ nsresult
 nsHTMLEditRules::DeleteNonTableElements(nsINode* aNode)
 {
   MOZ_ASSERT(aNode);
   if (!aNode->IsElement() ||
       !nsHTMLEditUtils::IsTableElementButNotTable(aNode->AsElement())) {
     return mHTMLEditor->DeleteNode(aNode->AsDOMNode());
   }
 
-  for (nsIContent* child = aNode->GetLastChild();
+  for (nsCOMPtr<nsIContent> child = aNode->GetLastChild();
        child;
        child = child->GetPreviousSibling()) {
     nsresult rv = DeleteNonTableElements(child);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   return NS_OK;
 }
 
--- a/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp
@@ -62,16 +62,17 @@
 #include "imgIContainer.h"
 #include "nsContextMenuInfo.h"
 #include "nsPresContext.h"
 #include "nsIViewManager.h"
 #include "nsIView.h"
 #include "nsEventListenerManager.h"
 #include "nsIDOMDragEvent.h"
 #include "nsIConstraintValidation.h"
+#include "mozilla/Attributes.h"
 
 using namespace mozilla;
 
 //
 // GetEventReceiver
 //
 // A helper routine that navigates the tricky path from a |nsWebBrowser| to
 // a |nsIDOMEventTarget| via the window root and chrome event handler.
@@ -963,17 +964,17 @@ nsDocShellTreeOwner::GetOwnerRequestor()
   }
   return req.forget();
 }
 
 
 ///////////////////////////////////////////////////////////////////////////////
 // DefaultTooltipTextProvider
 
-class DefaultTooltipTextProvider : public nsITooltipTextProvider
+class DefaultTooltipTextProvider MOZ_FINAL : public nsITooltipTextProvider
 {
 public:
     DefaultTooltipTextProvider();
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSITOOLTIPTEXTPROVIDER
     
 protected:
--- a/gfx/angle/Makefile.in
+++ b/gfx/angle/Makefile.in
@@ -103,17 +103,17 @@ CSRCS   = \
 	$(NULL)
 
 DEFINES += -DANGLE_USE_NSPR -DANGLE_BUILD -DCOMPILER_IMPLEMENTATION
 
 #these defines are from ANGLE's build_angle.gyp
 DEFINES += -DANGLE_DISABLE_TRACE
 DEFINES += -DANGLE_COMPILE_OPTIMIZATION_LEVEL=D3DCOMPILE_OPTIMIZATION_LEVEL0
 
-ifdef MOZ_ANGLE
+ifdef MOZ_ANGLE_RENDERER
 
 # libEGL depends on (links against!) libGLESv2!
 DIRS = src/libGLESv2 src/libEGL
 
 libs::
 	expand "$(MOZ_D3DX9_CAB)" -F:$(MOZ_D3DX9_DLL) "$(DIST)/bin"
 	expand "$(MOZ_D3DCOMPILER_CAB)" -F:$(MOZ_D3DCOMPILER_DLL) "$(DIST)/bin"
 
--- a/gfx/angle/angle-use-xmalloc.patch
+++ b/gfx/angle/angle-use-xmalloc.patch
@@ -9,17 +9,17 @@ diff --git a/gfx/angle/Makefile.in b/gfx
  DEFINES += -DANGLE_USE_NSPR -DANGLE_BUILD -DCOMPILER_IMPLEMENTATION
  
  #these defines are from ANGLE's build_angle.gyp
  DEFINES += -DANGLE_DISABLE_TRACE
  DEFINES += -DANGLE_COMPILE_OPTIMIZATION_LEVEL=D3DCOMPILE_OPTIMIZATION_LEVEL0
  
 +EXTRA_DSO_LDOPTS = $(MOZALLOC_LIB)
 +
- ifdef MOZ_ANGLE
+ ifdef MOZ_ANGLE_RENDERER
  
  # libEGL depends on (links against!) libGLESv2!
  DIRS = src/libGLESv2 src/libEGL
  
  libs::
  	expand "$(MOZ_D3DX9_CAB)" -F:$(MOZ_D3DX9_DLL) "$(DIST)/bin"
  	expand "$(MOZ_D3DCOMPILER_CAB)" -F:$(MOZ_D3DCOMPILER_DLL) "$(DIST)/bin"
 diff --git a/gfx/angle/src/compiler/preprocessor/atom.c b/gfx/angle/src/compiler/preprocessor/atom.c
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -87,16 +87,17 @@ GLLibraryEGL::EnsureInitialized()
 {
     if (mInitialized) {
         return true;
     }
 
     mozilla::ScopedGfxFeatureReporter reporter("EGL");
 
 #ifdef XP_WIN
+#ifdef MOZ_WEBGL
     if (!mEGLLibrary) {
         // On Windows, the GLESv2, EGL and DXSDK libraries are shipped with libxul and
         // we should look for them there. We have to load the libs in this
         // order, because libEGL.dll depends on libGLESv2.dll which depends on the DXSDK
         // libraries. This matters especially for WebRT apps which are in a different directory.
         // See bug 760323 and bug 749459
 
 #ifndef MOZ_D3DX9_DLL
@@ -114,16 +115,17 @@ GLLibraryEGL::EnsureInitialized()
         LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libGLESv2.dll"));
         // intentionally leak the libGLESv2.dll library
 
         mEGLLibrary = LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libEGL.dll"));
 
         if (!mEGLLibrary)
             return false;
     }
+#endif // MOZ_WEBGL
 #else // !Windows
 
     // On non-Windows (Android) we use system copies of libEGL. We look for
     // the APITrace lib, libEGL.so, and libEGL.so.1 in that order.
 
 #if defined(ANDROID)
     if (!mEGLLibrary)
         mEGLLibrary = LoadApitraceLibrary();
--- a/gfx/gl/Makefile.in
+++ b/gfx/gl/Makefile.in
@@ -31,19 +31,22 @@ EXPORTS += \
 	$(NULL)
 endif
 
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 EXPORTS	+= \
 	WGLLibrary.h \
 	$(NULL)
+ifdef MOZ_WEBGL
+DEFINES += -DMOZ_WEBGL
 DEFINES += -DMOZ_D3DX9_DLL=$(MOZ_D3DX9_DLL)
 DEFINES += -DMOZ_D3DCOMPILER_DLL=$(MOZ_D3DCOMPILER_DLL)
 endif
+endif
 
 CPPSRCS	= \
 	GLContext.cpp \
 	GLLibraryLoader.cpp \
 	GLContextProviderOSMesa.cpp \
 	$(NULL)
 
 GL_PROVIDER = Null
--- a/js/jsd/idl/jsdIDebuggerService.idl
+++ b/js/jsd/idl/jsdIDebuggerService.idl
@@ -1206,17 +1206,17 @@ interface jsdIObject : nsISupports
      */
     readonly attribute jsdIValue     value;
 };
 
 /**
  * Representation of a property of an object. When an instance is invalid, all
  * method and property access will result in a NS_UNAVAILABLE error.
  */
-[scriptable, uuid(09332485-1419-42bc-ba1f-070815ed4b82)]
+[scriptable, uuid(acf1329e-aaf6-4d6a-a1eb-f75858566f09)]
 interface jsdIProperty : jsdIEphemeral
 {
     /** Internal use only. */
     [noscript] readonly attribute JSDContext  JSDContext;
     /** Internal use only. */
     [noscript] readonly attribute JSDProperty JSDProperty;
 
     /**
@@ -1245,11 +1245,9 @@ interface jsdIProperty : jsdIEphemeral
     /** FLAG_* values OR'd together, representing the flags for this property. */
     readonly attribute unsigned long flags;
     /** jsdIValue representing the alias for this property. */
     readonly attribute jsdIValue     alias;
     /** name for this property. */
     readonly attribute jsdIValue     name;
     /** value of this property. */
     readonly attribute jsdIValue     value;
-    /** slot number if this property is a local variable or parameter. */
-    readonly attribute unsigned long varArgSlot;
 };
--- a/js/jsd/jsd.h
+++ b/js/jsd/jsd.h
@@ -250,17 +250,16 @@ struct JSDValue
 
 struct JSDProperty
 {
     JSCList     links;      /* we are part of a JSCList */
     int        nref;
     JSDValue*   val;
     JSDValue*   name;
     JSDValue*   alias;
-    unsigned       slot;
     unsigned       flags;
 };
 
 struct JSDAtom
 {
     char* str;      /* must be first element in struct for compare */
     int  refcount;
 };
@@ -986,19 +985,16 @@ extern JSDValue*
 jsd_GetPropertyValue(JSDContext* jsdc, JSDProperty* jsdprop);
 
 extern JSDValue*
 jsd_GetPropertyAlias(JSDContext* jsdc, JSDProperty* jsdprop);
 
 extern unsigned
 jsd_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop);
 
-extern unsigned
-jsd_GetPropertyVarArgSlot(JSDContext* jsdc, JSDProperty* jsdprop);
-
 /**************************************************/
 /* Stepping Functions */
 
 extern void *
 jsd_FunctionCallHook(JSContext *cx, JSStackFrame *fp, JSBool before,
                      JSBool *ok, void *closure);
 
 extern void *
--- a/js/jsd/jsd_val.c
+++ b/js/jsd/jsd_val.c
@@ -370,17 +370,16 @@ static JSDProperty* _newProperty(JSDCont
     JSDProperty* jsdprop;
 
     if(!(jsdprop = (JSDProperty*) calloc(1, sizeof(JSDProperty))))
         return NULL;
 
     JS_INIT_CLIST(&jsdprop->links);
     jsdprop->nref = 1;
     jsdprop->flags = pd->flags | additionalFlags;
-    jsdprop->slot = pd->slot;
 
     if(!(jsdprop->name = jsd_NewValue(jsdc, pd->id)))
         goto new_prop_fail;
 
     if(!(jsdprop->val = jsd_NewValue(jsdc, pd->value)))
         goto new_prop_fail;
 
     if((jsdprop->flags & JSDPD_ALIAS) &&
@@ -620,17 +619,17 @@ jsd_GetValueProperty(JSDContext* jsdc, J
     JS_EndRequest(cx);
 
     nameval = STRING_TO_JSVAL(name);
     if (!JS_ValueToId(cx, nameval, &nameid) ||
         !JS_IdToValue(cx, nameid, &pd.id)) {
         return NULL;
     }
 
-    pd.slot = pd.spare = 0;
+    pd.spare = 0;
     pd.alias = JSVAL_NULL;
     pd.flags |= (attrs & JSPROP_ENUMERATE) ? JSPD_ENUMERATE : 0
         | (attrs & JSPROP_READONLY)  ? JSPD_READONLY  : 0
         | (attrs & JSPROP_PERMANENT) ? JSPD_PERMANENT : 0;
 
     return _newProperty(jsdc, &pd, JSDPD_HINTED);
 }
 
@@ -842,22 +841,16 @@ jsd_GetPropertyAlias(JSDContext* jsdc, J
 }
 
 unsigned
 jsd_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop)
 {
     return jsdprop->flags;
 }
 
-unsigned
-jsd_GetPropertyVarArgSlot(JSDContext* jsdc, JSDProperty* jsdprop)
-{
-    return jsdprop->slot;
-}
-
 void
 jsd_DropProperty(JSDContext* jsdc, JSDProperty* jsdprop)
 {
     JS_ASSERT(jsdprop->nref > 0);
     if(0 == --jsdprop->nref)
     {
         JS_ASSERT(JS_CLIST_IS_EMPTY(&jsdprop->links));
         DROP_CLEAR_VALUE(jsdc, jsdprop->val);
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -906,23 +906,16 @@ NS_IMETHODIMP
 jsdProperty::GetValue(jsdIValue **_rval)
 {
     JSDValue *jsdv = JSD_GetPropertyValue (mCx, mProperty);
     
     *_rval = jsdValue::FromPtr (mCx, jsdv);
     return NS_OK;
 }
 
-NS_IMETHODIMP
-jsdProperty::GetVarArgSlot(PRUint32 *_rval)
-{
-    *_rval = JSD_GetPropertyVarArgSlot (mCx, mProperty);
-    return NS_OK;
-}
-
 /* Scripts */
 NS_IMPL_THREADSAFE_ISUPPORTS2(jsdScript, jsdIScript, jsdIEphemeral)
 
 static NS_IMETHODIMP
 AssignToJSString(nsACString *x, JSString *str)
 {
     if (!str) {
         x->SetLength(0);
--- a/js/jsd/jsdebug.c
+++ b/js/jsd/jsdebug.c
@@ -1221,24 +1221,16 @@ JSD_GetPropertyAlias(JSDContext* jsdc, J
 JSD_PUBLIC_API(unsigned)
 JSD_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop)
 {
     JSD_ASSERT_VALID_CONTEXT(jsdc);
     JSD_ASSERT_VALID_PROPERTY(jsdprop);
     return jsd_GetPropertyFlags(jsdc, jsdprop);
 }
 
-JSD_PUBLIC_API(unsigned)
-JSD_GetPropertyVarArgSlot(JSDContext* jsdc, JSDProperty* jsdprop)
-{
-    JSD_ASSERT_VALID_CONTEXT(jsdc);
-    JSD_ASSERT_VALID_PROPERTY(jsdprop);
-    return jsd_GetPropertyVarArgSlot(jsdc, jsdprop);
-}
-
 /**************************************************/
 /* Object Functions */
 
 JSD_PUBLIC_API(void)
 JSD_LockObjectSubsystem(JSDContext* jsdc)
 {
     JSD_ASSERT_VALID_CONTEXT(jsdc);
     JSD_LOCK_OBJECTS(jsdc);
--- a/js/jsd/jsdebug.h
+++ b/js/jsd/jsdebug.h
@@ -1365,18 +1365,16 @@ JSD_GetScriptForValue(JSDContext* jsdc, 
 /* possible or'd together bitflags returned by JSD_GetPropertyFlags
  *
  * XXX these must stay the same as the JSPD_ flags in jsdbgapi.h
  */
 #define JSDPD_ENUMERATE  JSPD_ENUMERATE    /* visible to for/in loop */
 #define JSDPD_READONLY   JSPD_READONLY     /* assignment is error */
 #define JSDPD_PERMANENT  JSPD_PERMANENT    /* property cannot be deleted */
 #define JSDPD_ALIAS      JSPD_ALIAS        /* property has an alias id */
-#define JSDPD_ARGUMENT   JSPD_ARGUMENT     /* argument to function */
-#define JSDPD_VARIABLE   JSPD_VARIABLE     /* local variable in function */
 #define JSDPD_EXCEPTION  JSPD_EXCEPTION    /* exception occurred looking up */
                                            /* proprety, value is exception  */
 #define JSDPD_ERROR      JSPD_ERROR        /* native getter returned JS_FALSE */
                                            /* without throwing an exception */
 /* this is not one of the JSPD_ flags in jsdbgapi.h  - careful not to overlap*/
 #define JSDPD_HINTED     0x800             /* found via explicit lookup */
 
 /*
@@ -1412,23 +1410,16 @@ JSD_GetPropertyAlias(JSDContext* jsdc, J
 
 /*
 * Get the flags for this property
 * *** new for version 1.1 ****
 */
 extern JSD_PUBLIC_API(unsigned)
 JSD_GetPropertyFlags(JSDContext* jsdc, JSDProperty* jsdprop);
 
-/*
-* Get Variable or Argument slot number (if JSDPD_ARGUMENT or JSDPD_VARIABLE set)
-* *** new for version 1.1 ****
-*/
-extern JSD_PUBLIC_API(unsigned)
-JSD_GetPropertyVarArgSlot(JSDContext* jsdc, JSDProperty* jsdprop);
-
 /***************************************************************************/
 /* Object Functions  --- All NEW for 1.1 --- */
 
 /*
 * JSDObjects exist to allow a means of iterating through all JSObjects in the
 * engine. They are created and destroyed as the wrapped JSObjects are created
 * and destroyed in the engine. JSDObjects additionally track the location in
 * the JavaScript source where their wrapped JSObjects were created and the name
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -104,16 +104,17 @@ struct CompartmentStats
     size_t gcHeapTypeObjects;
 #if JS_HAS_XML_SUPPORT
     size_t gcHeapXML;
 #endif
 
     size_t objectSlots;
     size_t objectElements;
     size_t objectMisc;
+    size_t objectPrivate;
     size_t stringChars;
     size_t shapesExtraTreeTables;
     size_t shapesExtraDictTables;
     size_t shapesExtraTreeShapeKids;
     size_t shapesCompartmentTables;
     size_t scriptData;
     size_t mjitData;
     size_t crossCompartmentWrappers;
@@ -137,16 +138,17 @@ struct CompartmentStats
         ADD(gcHeapTypeObjects);
     #if JS_HAS_XML_SUPPORT
         ADD(gcHeapXML);
     #endif
 
         ADD(objectSlots);
         ADD(objectElements);
         ADD(objectMisc);
+        ADD(objectPrivate);
         ADD(stringChars);
         ADD(shapesExtraTreeTables);
         ADD(shapesExtraDictTables);
         ADD(shapesExtraTreeShapeKids);
         ADD(shapesCompartmentTables);
         ADD(scriptData);
         ADD(mjitData);
         ADD(crossCompartmentWrappers);
@@ -216,18 +218,26 @@ struct RuntimeStats
 
     JSMallocSizeOfFun mallocSizeOf;
 
     virtual void initExtraCompartmentStats(JSCompartment *c, CompartmentStats *cstats) = 0;
 };
 
 #ifdef JS_THREADSAFE
 
+class ObjectPrivateVisitor
+{
+public:
+    // Within CollectRuntimeStats, this method is called for each JS object
+    // that has a private slot containing an nsISupports pointer.
+    virtual size_t sizeOfIncludingThis(void *aSupports) = 0;
+};
+
 extern JS_PUBLIC_API(bool)
-CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats);
+CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv);
 
 extern JS_PUBLIC_API(int64_t)
 GetExplicitNonHeapForRuntime(JSRuntime *rt, JSMallocSizeOfFun mallocSizeOf);
 
 #endif /* JS_THREADSAFE */
 
 extern JS_PUBLIC_API(size_t)
 SystemCompartmentCount(const JSRuntime *rt);
--- a/js/src/MemoryMetrics.cpp
+++ b/js/src/MemoryMetrics.cpp
@@ -17,16 +17,23 @@
 #include "jsobjinlines.h"
 
 #ifdef JS_THREADSAFE
 
 namespace JS {
 
 using namespace js;
 
+struct IteratorClosure
+{
+  RuntimeStats *rtStats;
+  ObjectPrivateVisitor *opv;
+  IteratorClosure(RuntimeStats *rt, ObjectPrivateVisitor *v) : rtStats(rt), opv(v) {}
+};
+
 size_t
 CompartmentStats::gcHeapThingsSize()
 {
     // These are just the GC-thing measurements.
     size_t n = 0;
     n += gcHeapObjectsNonFunction;
     n += gcHeapObjectsFunction;
     n += gcHeapStrings;
@@ -49,17 +56,17 @@ CompartmentStats::gcHeapThingsSize()
 
     return n;
 }
 
 static void
 StatsCompartmentCallback(JSRuntime *rt, void *data, JSCompartment *compartment)
 {
     // Append a new CompartmentStats to the vector.
-    RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
+    RuntimeStats *rtStats = static_cast<IteratorClosure *>(data)->rtStats;
 
     // CollectRuntimeStats reserves enough space.
     MOZ_ALWAYS_TRUE(rtStats->compartmentStatsVector.growBy(1));
     CompartmentStats &cStats = rtStats->compartmentStatsVector.back();
     rtStats->initExtraCompartmentStats(compartment, &cStats);
     rtStats->currCompartmentStats = &cStats;
 
     // Get the compartment-level numbers.
@@ -77,17 +84,17 @@ StatsChunkCallback(JSRuntime *rt, void *
         if (chunk->decommittedArenas.get(i))
             rtStats->gcHeapDecommittedArenas += gc::ArenaSize;
 }
 
 static void
 StatsArenaCallback(JSRuntime *rt, void *data, gc::Arena *arena,
                    JSGCTraceKind traceKind, size_t thingSize)
 {
-    RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
+    RuntimeStats *rtStats = static_cast<IteratorClosure *>(data)->rtStats;
 
     // The admin space includes (a) the header and (b) the padding between the
     // end of the header and the start of the first GC thing.
     size_t allocationSpace = arena->thingsSpan(thingSize);
     rtStats->currCompartmentStats->gcHeapArenaAdmin +=
         gc::ArenaSize - allocationSpace;
 
     // We don't call the callback on unused things.  So we compute the
@@ -96,33 +103,43 @@ StatsArenaCallback(JSRuntime *rt, void *
     // subtracting thingSize for every used cell, in StatsCellCallback().
     rtStats->currCompartmentStats->gcHeapUnusedGcThings += allocationSpace;
 }
 
 static void
 StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind,
                   size_t thingSize)
 {
-    RuntimeStats *rtStats = static_cast<RuntimeStats *>(data);
+    IteratorClosure *closure = static_cast<IteratorClosure *>(data);
+    RuntimeStats *rtStats = closure->rtStats;
     CompartmentStats *cStats = rtStats->currCompartmentStats;
     switch (traceKind) {
     case JSTRACE_OBJECT:
     {
         JSObject *obj = static_cast<JSObject *>(thing);
         if (obj->isFunction()) {
             cStats->gcHeapObjectsFunction += thingSize;
         } else {
             cStats->gcHeapObjectsNonFunction += thingSize;
         }
         size_t slotsSize, elementsSize, miscSize;
         obj->sizeOfExcludingThis(rtStats->mallocSizeOf, &slotsSize,
                                  &elementsSize, &miscSize);
         cStats->objectSlots += slotsSize;
         cStats->objectElements += elementsSize;
         cStats->objectMisc += miscSize;
+
+        if (ObjectPrivateVisitor *opv = closure->opv) {
+            js::Class *clazz = js::GetObjectClass(obj);
+            if (clazz->flags & JSCLASS_HAS_PRIVATE &&
+                clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS)
+            {
+                cStats->objectPrivate += opv->sizeOfIncludingThis(GetObjectPrivate(obj));
+            }
+        }
         break;
     }
     case JSTRACE_STRING:
     {
         JSString *str = static_cast<JSString *>(thing);
         cStats->gcHeapStrings += thingSize;
         cStats->stringChars += str->sizeOfExcludingThis(rtStats->mallocSizeOf);
         break;
@@ -173,32 +190,33 @@ StatsCellCallback(JSRuntime *rt, void *d
     }
 #endif
     }
     // Yes, this is a subtraction:  see StatsArenaCallback() for details.
     cStats->gcHeapUnusedGcThings -= thingSize;
 }
 
 JS_PUBLIC_API(bool)
-CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats)
+CollectRuntimeStats(JSRuntime *rt, RuntimeStats *rtStats, ObjectPrivateVisitor *opv)
 {
     if (!rtStats->compartmentStatsVector.reserve(rt->compartments.length()))
         return false;
 
     rtStats->gcHeapChunkTotal =
         size_t(JS_GetGCParameter(rt, JSGC_TOTAL_CHUNKS)) * gc::ChunkSize;
 
     rtStats->gcHeapUnusedChunks =
         size_t(JS_GetGCParameter(rt, JSGC_UNUSED_CHUNKS)) * gc::ChunkSize;
 
     // This just computes rtStats->gcHeapDecommittedArenas.
     IterateChunks(rt, rtStats, StatsChunkCallback);
 
     // Take the per-compartment measurements.
-    IterateCompartmentsArenasCells(rt, rtStats, StatsCompartmentCallback,
+    IteratorClosure closure(rtStats, opv);
+    IterateCompartmentsArenasCells(rt, &closure, StatsCompartmentCallback,
                                    StatsArenaCallback, StatsCellCallback);
 
     // Take the "explicit/js/runtime/" measurements.
     rt->sizeOfIncludingThis(rtStats->mallocSizeOf, &rtStats->runtime);
 
     rtStats->gcHeapGcThings = 0;
     for (size_t i = 0; i < rtStats->compartmentStatsVector.length(); i++) {
         CompartmentStats &cStats = rtStats->compartmentStatsVector[i];
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -346,25 +346,24 @@ EvalKernel(JSContext *cx, const CallArgs
 
 // We once supported a second argument to eval to use as the scope chain
 // when evaluating the code string.  Warn when such uses are seen so that
 // authors will know that support for eval(s, o) has been removed.
 static inline bool
 WarnOnTooManyArgs(JSContext *cx, const CallArgs &args)
 {
     if (args.length() > 1) {
-        if (JSScript *script = cx->stack.currentScript()) {
-            if (!script->warnedAboutTwoArgumentEval) {
-                static const char TWO_ARGUMENT_WARNING[] =
-                    "Support for eval(code, scopeObject) has been removed. "
-                    "Use |with (scopeObject) eval(code);| instead.";
-                if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
-                    return false;
-                script->warnedAboutTwoArgumentEval = true;
-            }
+        Rooted<JSScript*> script(cx, cx->stack.currentScript());
+        if (script && !script->warnedAboutTwoArgumentEval) {
+            static const char TWO_ARGUMENT_WARNING[] =
+                "Support for eval(code, scopeObject) has been removed. "
+                "Use |with (scopeObject) eval(code);| instead.";
+            if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
+                return false;
+            script->warnedAboutTwoArgumentEval = true;
         } else {
             // In the case of an indirect call without a caller frame, avoid a
             // potential warning-flood by doing nothing.
         }
     }
 
     return true;
 }
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -248,17 +248,17 @@ CompileRegExpObject(JSContext *cx, RegEx
         RegExpObject *reobj = builder.build(sourceAtom, flags);
         if (!reobj)
             return false;
 
         args.rval() = ObjectValue(*reobj);
         return true;
     }
 
-    JSAtom *source;
+    RootedAtom source(cx);
     if (sourceValue.isUndefined()) {
         source = cx->runtime->emptyString;
     } else {
         /* Coerce to string and compile. */
         JSString *str = ToString(cx, sourceValue);
         if (!str)
             return false;
 
@@ -566,34 +566,35 @@ ExecuteRegExp(JSContext *cx, Native nati
     RegExpStatics *res = cx->regExpStatics();
 
     /* Step 2. */
     JSString *input = ToString(cx, (args.length() > 0) ? args[0] : UndefinedValue());
     if (!input)
         return false;
 
     /* Step 3. */
-    JSLinearString *linearInput = input->ensureLinear(cx);
+    Rooted<JSLinearString*> linearInput(cx, input->ensureLinear(cx));
     if (!linearInput)
         return false;
-    const jschar *chars = linearInput->chars();
-    size_t length = input->length();
 
     /* Step 4. */
     const Value &lastIndex = reobj->getLastIndex();
 
     /* Step 5. */
     double i;
     if (!ToInteger(cx, lastIndex, &i))
         return false;
 
     /* Steps 6-7 (with sticky extension). */
     if (!re->global() && !re->sticky())
         i = 0;
 
+    const jschar *chars = linearInput->chars();
+    size_t length = input->length();
+
     /* Step 9a. */
     if (i < 0 || i > length) {
         reobj->zeroLastIndex();
         args.rval() = NullValue();
         return true;
     }
 
     /* Steps 8-21. */
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -239,17 +239,17 @@ frontend::CompileScript(JSContext *cx, H
 
     /*
      * Nowadays the threaded interpreter needs a stop instruction, so we
      * do have to emit that here.
      */
     if (Emit1(cx, &bce, JSOP_STOP) < 0)
         return NULL;
 
-    if (!script->fullyInitFromEmitter(cx, &bce))
+    if (!JSScript::fullyInitFromEmitter(cx, script, &bce))
         return NULL;
 
     bce.tellDebuggerAboutCompiledScript(cx);
 
     if (!MarkInnerAndOuterFunctions(cx, script))
         return NULL;
 
     return script;
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2608,17 +2608,17 @@ frontend::EmitFunctionScript(JSContext *
     }
 
     if (!EmitTree(cx, bce, body))
         return false;
         
     if (Emit1(cx, bce, JSOP_STOP) < 0)
         return false;
 
-    if (!bce->script->fullyInitFromEmitter(cx, bce))
+    if (!JSScript::fullyInitFromEmitter(cx, bce->script, bce))
         return false;
 
     // Initialize fun->script() so that the debugger has a valid fun->script().
     RootedFunction fun(cx, bce->script->function());
     JS_ASSERT(fun->isInterpreted());
     if (bce->sc->funIsHeavyweight())
         fun->flags |= JSFUN_HEAVYWEIGHT;
 
@@ -5946,17 +5946,17 @@ frontend::EmitTree(JSContext *cx, Byteco
 
     switch (pn->getKind()) {
       case PNK_FUNCTION:
         ok = EmitFunc(cx, bce, pn);
         break;
 
       case PNK_ARGSBODY:
       {
-        JSFunction *fun = bce->sc->fun();
+        RootedFunction fun(cx, bce->sc->fun());
         ParseNode *pnlast = pn->last();
 
         // Carefully emit everything in the right order:
         // 1. Destructuring
         // 2. Functions
         // 3. Defaults
         ParseNode *pnchild = pnlast->pn_head;
         if (pnlast->pn_xflags & PNX_DESTRUCT) {
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -56,17 +56,17 @@ class StmtInfoBCE;
 struct BytecodeEmitter
 {
     typedef StmtInfoBCE StmtInfo;
 
     SharedContext   *const sc;      /* context shared between parsing and bytecode generation */
 
     BytecodeEmitter *const parent;  /* enclosing function or global context */
 
-    const Rooted<JSScript*> script;       /* the JSScript we're ultimately producing */
+    Rooted<JSScript*> script;       /* the JSScript we're ultimately producing */
 
     struct {
         jsbytecode  *base;          /* base of JS bytecode vector */
         jsbytecode  *limit;         /* one byte beyond end of bytecode */
         jsbytecode  *next;          /* pointer to next free bytecode */
         jssrcnote   *notes;         /* source notes, see below */
         unsigned    noteCount;      /* number of source notes so far */
         unsigned    noteLimit;      /* limit number for source notes in notePool */
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -1048,19 +1048,19 @@ Parser::newFunction(TreeContext *tc, JSA
     RootedObject parent(context);
     parent = tc->sc->inFunction() ? NULL : tc->sc->scopeChain();
 
     RootedFunction fun(context);
     fun = js_NewFunction(context, NULL, NULL, 0,
                          JSFUN_INTERPRETED | (kind == Expression ? JSFUN_LAMBDA : 0),
                          parent, atom);
     if (fun && !compileAndGo) {
-        if (!fun->clearParent(context))
+        if (!JSObject::clearParent(context, fun))
             return NULL;
-        if (!fun->clearType(context))
+        if (!JSObject::clearType(context, fun))
             return NULL;
         fun->setEnvironment(NULL);
     }
     return fun;
 }
 
 static bool
 MatchOrInsertSemicolon(JSContext *cx, TokenStream *ts)
@@ -2096,18 +2096,18 @@ BindLet(JSContext *cx, BindData *data, J
         return false;
     pn->pn_dflags |= PND_LET | PND_BOUND;
 
     /*
      * Define the let binding's property before storing pn in the the binding's
      * slot indexed by blockCount off the class-reserved slot base.
      */
     bool redeclared;
-    jsid id = AtomToId(atom);
-    Shape *shape = blockObj->addVar(cx, id, blockCount, &redeclared);
+    RootedId id(cx, AtomToId(atom));
+    Shape *shape = StaticBlockObject::addVar(cx, blockObj, id, blockCount, &redeclared);
     if (!shape) {
         if (redeclared)
             ReportRedeclaration(cx, parser, pn, false, atom);
         return false;
     }
 
     /* Store pn in the static block object. */
     blockObj->setDefinitionParseNode(blockCount, reinterpret_cast<Definition *>(pn));
@@ -2145,17 +2145,17 @@ PopStatementTC(TreeContext *tc)
         StaticBlockObject &blockObj = *tc->topStmt->blockObj;
         JS_ASSERT(!blockObj.inDictionaryMode());
         ForEachLetDef(tc, blockObj, RemoveDecl());
     }
     FinishPopStatement(tc);
 }
 
 static inline bool
-OuterLet(TreeContext *tc, StmtInfoTC *stmt, JSAtom *atom)
+OuterLet(TreeContext *tc, StmtInfoTC *stmt, HandleAtom atom)
 {
     while (stmt->downScope) {
         stmt = LexicalLookup(tc, atom, NULL, stmt->downScope);
         if (!stmt)
             return false;
         if (stmt->type == STMT_BLOCK)
             return true;
     }
@@ -2193,18 +2193,20 @@ BindFunctionLocal(JSContext *cx, BindDat
     } else {
         JS_ASSERT(kind == VARIABLE || kind == CONSTANT);
     }
 
     return true;
 }
 
 static bool
-BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, Parser *parser)
+BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom_, Parser *parser)
 {
+    RootedAtom atom(cx, atom_);
+
     TreeContext *tc = parser->tc;
     ParseNode *pn = data->pn;
 
     /* Default best op for pn is JSOP_NAME; we'll try to improve below. */
     pn->setOp(JSOP_NAME);
 
     if (!CheckStrictBinding(cx, parser, atom->asPropertyName(), pn))
         return false;
@@ -2393,17 +2395,17 @@ NoteLValue(JSContext *cx, ParseNode *pn,
      */
     if (sc->inFunction() && pn->pn_atom == sc->fun()->atom)
         sc->setFunIsHeavyweight();
 }
 
 static bool
 NoteNameUse(ParseNode *pn, Parser *parser)
 {
-    PropertyName *name = pn->pn_atom->asPropertyName();
+    RootedPropertyName name(parser->context, pn->pn_atom->asPropertyName());
     StmtInfoTC *stmt = LexicalLookup(parser->tc, name, NULL, (StmtInfoTC *)NULL);
 
     DefinitionList::Range defs = parser->tc->decls.lookupMulti(name);
 
     Definition *dn;
     if (!defs.empty()) {
         dn = defs.front();
     } else {
@@ -2645,17 +2647,18 @@ CheckDestructuring(JSContext *cx, BindDa
      * four slots are needed.
      *
      * To satisfy both constraints, we push a dummy slot (and add a
      * corresponding dummy property to the block object) for each initializer
      * that doesn't introduce at least one binding.
      */
     if (toplevel && blockObj && blockCountBefore == blockObj->slotCount()) {
         bool redeclared;
-        if (!blockObj->addVar(cx, INT_TO_JSID(blockCountBefore), blockCountBefore, &redeclared))
+        RootedId id(cx, INT_TO_JSID(blockCountBefore));
+        if (!StaticBlockObject::addVar(cx, blockObj, id, blockCountBefore, &redeclared))
             return false;
         JS_ASSERT(!redeclared);
         JS_ASSERT(blockObj->slotCount() == blockCountBefore + 1);
     }
 
     return true;
 }
 
@@ -5125,17 +5128,17 @@ CompExprTransplanter::transplant(ParseNo
              * will be visited further below.
              */
             if (dn->isPlaceholder() && dn->pn_pos >= root->pn_pos && dn->dn_uses == pn) {
                 if (genexp && !BumpStaticLevel(dn, tc))
                     return false;
                 AdjustBlockId(dn, adjust, tc);
             }
 
-            JSAtom *atom = pn->pn_atom;
+            RootedAtom atom(parser->context, pn->pn_atom);
 #ifdef DEBUG
             StmtInfoTC *stmt = LexicalLookup(tc, atom, NULL, (StmtInfoTC *)NULL);
             JS_ASSERT(!stmt || stmt != tc->topStmt);
 #endif
             if (genexp && !dn->isOp(JSOP_CALLEE)) {
                 JS_ASSERT(!tc->decls.lookupFirst(atom));
 
                 if (dn->pn_pos < root->pn_pos) {
@@ -7011,41 +7014,48 @@ Parser::primaryExpr(TokenKind tt, bool a
         pn = atomNode(PNK_STRING, JSOP_STRING);
         if (!pn)
             return NULL;
         break;
 
 #if JS_HAS_XML_SUPPORT
       case TOK_AT:
       case TOK_STAR:
+        if (!allowsXML())
+            goto syntaxerror;
         pn = starOrAtPropertyIdentifier(tt);
         break;
 
       case TOK_XMLSTAGO:
+        if (!allowsXML())
+            goto syntaxerror;
         pn = xmlElementOrListRoot(true);
         if (!pn)
             return NULL;
         break;
 
       case TOK_XMLCDATA:
-        JS_ASSERT(allowsXML());
+        if (!allowsXML())
+            goto syntaxerror;
         pn = atomNode(PNK_XMLCDATA, JSOP_XMLCDATA);
         if (!pn)
             return NULL;
         break;
 
       case TOK_XMLCOMMENT:
-        JS_ASSERT(allowsXML());
+        if (!allowsXML())
+            goto syntaxerror;
         pn = atomNode(PNK_XMLCOMMENT, JSOP_XMLCOMMENT);
         if (!pn)
             return NULL;
         break;
 
       case TOK_XMLPI: {
-        JS_ASSERT(allowsXML());
+        if (!allowsXML())
+            goto syntaxerror;
         const Token &tok = tokenStream.currentToken();
         pn = new_<XMLProcessingInstruction>(tok.xmlPITarget(), tok.xmlPIData(), tok.pos);
         if (!pn)
             return NULL;
         break;
       }
 #endif
 
@@ -7069,19 +7079,19 @@ Parser::primaryExpr(TokenKind tt, bool a
             reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream);
         else
             reobj = RegExpObject::createNoStatics(context, chars, length, flags, &tokenStream);
 
         if (!reobj)
             return NULL;
 
         if (!compileAndGo) {
-            if (!reobj->clearParent(context))
+            if (!JSObject::clearParent(context, reobj))
                 return NULL;
-            if (!reobj->clearType(context))
+            if (!JSObject::clearType(context, reobj))
                 return NULL;
         }
 
         pn->pn_objbox = newObjectBox(reobj);
         if (!pn->pn_objbox)
             return NULL;
 
         pn->setOp(JSOP_REGEXP);
@@ -7104,16 +7114,17 @@ Parser::primaryExpr(TokenKind tt, bool a
         return new_<ThisLiteral>(tokenStream.currentToken().pos);
       case TOK_NULL:
         return new_<NullLiteral>(tokenStream.currentToken().pos);
 
       case TOK_ERROR:
         /* The scanner or one of its subroutines reported the error. */
         return NULL;
 
+    syntaxerror:
       default:
         reportError(NULL, JSMSG_SYNTAX_ERROR);
         return NULL;
     }
     return pn;
 }
 
 ParseNode *
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -225,18 +225,28 @@ struct Parser : private AutoGCRooter
     ParseNode *returnOrYield(bool useAssignExpr);
     ParseNode *destructuringExpr(BindData *data, TokenKind tt);
 
     bool checkForFunctionNode(PropertyName *name, ParseNode *node);
 
     ParseNode *identifierName(bool afterDoubleDot);
 
 #if JS_HAS_XML_SUPPORT
-    // True if E4X syntax is allowed in the current syntactic context.
-    bool allowsXML() const { return tokenStream.allowsXML(); }
+    // True if E4X syntax is allowed in the current syntactic context. Note this
+    // function may be false while TokenStream::allowsXML() is true!
+    // Specifically, when strictModeState is not STRICT, Parser::allowsXML()
+    // will be false, where TokenStream::allowsXML() is only false when
+    // strictModeState is STRICT. The reason for this is when we are parsing the
+    // directive prologue, the tokenizer looks ahead into the body of the
+    // function. So, we have to be lenient in case the function is not
+    // strict. This also effectively bans XML in function defaults. See bug
+    // 772691.
+    bool allowsXML() const {
+        return tc->sc->strictModeState == StrictMode::NOTSTRICT && tokenStream.allowsXML();
+    }
 
     ParseNode *endBracketedExpr();
 
     ParseNode *propertySelector();
     ParseNode *qualifiedSuffix(ParseNode *pn);
     ParseNode *qualifiedIdentifier();
     ParseNode *attributeIdentifier();
     ParseNode *xmlExpr(bool inTag);
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -495,16 +495,18 @@ class TokenStream
         return type == type1 || type == type2;
     }
     const CharBuffer &getTokenbuf() const { return tokenbuf; }
     const char *getFilename() const { return filename; }
     unsigned getLineno() const { return lineno; }
     /* Note that the version and hasMoarXML can get out of sync via setMoarXML. */
     JSVersion versionNumber() const { return VersionNumber(version); }
     JSVersion versionWithFlags() const { return version; }
+    // TokenStream::allowsXML() can be true even if Parser::allowsXML() is
+    // false. Read the comment at Parser::allowsXML() to find out why.
     bool allowsXML() const { return allowXML && strictModeState() != StrictMode::STRICT; }
     bool hasMoarXML() const { return moarXML || VersionShouldParseXML(versionNumber()); }
     void setMoarXML(bool enabled) { moarXML = enabled; }
 
     bool isCurrentTokenEquality() const {
         return TokenKindIsEquality(currentToken().type);
     }
 
--- a/js/src/frontend/TreeContext-inl.h
+++ b/js/src/frontend/TreeContext-inl.h
@@ -162,17 +162,17 @@ frontend::FinishPopStatement(ContextT *c
         ct->topScopeStmt = stmt->downScope;
         if (stmt->isBlockScope)
             ct->blockChain = stmt->blockObj->enclosingBlock();
     }
 }
 
 template <class ContextT>
 typename ContextT::StmtInfo *
-frontend::LexicalLookup(ContextT *ct, JSAtom *atom, int *slotp, typename ContextT::StmtInfo *stmt)
+frontend::LexicalLookup(ContextT *ct, HandleAtom atom, int *slotp, typename ContextT::StmtInfo *stmt)
 {
     if (!stmt)
         stmt = ct->topScopeStmt;
     for (; stmt; stmt = stmt->downScope) {
         if (stmt->type == STMT_WITH)
             break;
 
         // Skip "maybe scope" statements that don't contain let bindings.
--- a/js/src/frontend/TreeContext.h
+++ b/js/src/frontend/TreeContext.h
@@ -426,15 +426,15 @@ FinishPopStatement(ContextT *ct);
  * scope chain must have had their depth slots computed by the code generator,
  * so the caller must be under EmitTree.
  *
  * In any event, directly return the statement info record in which atom was
  * found. Otherwise return null.
  */
 template <class ContextT>
 typename ContextT::StmtInfo *
-LexicalLookup(ContextT *ct, JSAtom *atom, int *slotp, typename ContextT::StmtInfo *stmt);
+LexicalLookup(ContextT *ct, HandleAtom atom, int *slotp, typename ContextT::StmtInfo *stmt);
 
 } // namespace frontend
 
 } // namespace js
 
 #endif // TreeContext_h__
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -316,18 +316,18 @@ class EncapsulatedValue
     EncapsulatedValue(const EncapsulatedValue &v) MOZ_DELETE;
     EncapsulatedValue &operator=(const Value &v) MOZ_DELETE;
     EncapsulatedValue &operator=(const EncapsulatedValue &v) MOZ_DELETE;
 
     EncapsulatedValue(const Value &v) : value(v) {}
     ~EncapsulatedValue() {}
 
   public:
-    inline bool operator==(const EncapsulatedValue &v) const { return value == v.value; }
-    inline bool operator!=(const EncapsulatedValue &v) const { return value != v.value; }
+    bool operator==(const EncapsulatedValue &v) const { return value == v.value; }
+    bool operator!=(const EncapsulatedValue &v) const { return value != v.value; }
 
     const Value &get() const { return value; }
     Value *unsafeGet() { return &value; }
     operator const Value &() const { return value; }
 
     bool isUndefined() const { return value.isUndefined(); }
     bool isNull() const { return value.isNull(); }
     bool isBoolean() const { return value.isBoolean(); }
--- a/js/src/gc/Root.h
+++ b/js/src/gc/Root.h
@@ -7,17 +7,17 @@
 
 #ifndef jsgc_root_h__
 #define jsgc_root_h__
 
 #ifdef __cplusplus
 
 #include "mozilla/TypeTraits.h"
 
-#include "jspubtd.h"
+#include "jsapi.h"
 
 #include "js/TemplateLib.h"
 #include "js/Utility.h"
 
 namespace JS {
 
 /*
  * Moving GC Stack Rooting
@@ -92,18 +92,17 @@ class Handle
     template <typename S>
     Handle(Handle<S> handle,
            typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0)
     {
         ptr = reinterpret_cast<const T *>(handle.address());
     }
 
     /* Create a handle for a NULL pointer. */
-    Handle(NullPtr)
-    {
+    Handle(NullPtr) {
         typedef typename js::tl::StaticAssert<js::tl::IsPointerType<T>::result>::result _;
         ptr = reinterpret_cast<const T *>(&NullPtr::constNullValue);
     }
 
     /*
      * This may be called only if the location of the T is guaranteed
      * to be marked (for some reason other than being a Rooted),
      * e.g., if it is guaranteed to be reachable from an implicit root.
@@ -130,18 +129,25 @@ class Handle
 
     operator T () const { return get(); }
     T operator ->() const { return get(); }
 
   private:
     Handle() {}
 
     const T *ptr;
+
+    template <typename S>
+    void operator =(S v) MOZ_DELETE;
 };
 
+/* Defined in jsapi.h under Value definition */
+template <>
+class Handle<Value>;
+
 typedef Handle<JSObject*>    HandleObject;
 typedef Handle<JSFunction*>  HandleFunction;
 typedef Handle<JSScript*>    HandleScript;
 typedef Handle<JSString*>    HandleString;
 typedef Handle<jsid>         HandleId;
 typedef Handle<Value>        HandleValue;
 
 /*
@@ -159,17 +165,21 @@ class MutableHandle
         this->ptr = reinterpret_cast<const T *>(handle.address());
     }
 
     template <typename S>
     inline
     MutableHandle(Rooted<S> *root,
                   typename mozilla::EnableIf<mozilla::IsConvertible<S, T>::value, int>::Type dummy = 0);
 
-    void set(T v) { *ptr = v; }
+    void set(T v)
+    {
+        JS_ASSERT(!RootMethods<T>::poisoned(v));
+        *ptr = v;
+    }
 
     T *address() const { return ptr; }
     T get() const { return *ptr; }
 
     operator T () const { return get(); }
     T operator ->() const { return get(); }
 
   private:
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/e4x/defaults-e4x.js
@@ -0,0 +1,14 @@
+load(libdir + "asserts.js");
+
+assertThrowsInstanceOf(function () {
+    eval("function f(a=<x/>) {}");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    eval("function f(a=<x/>) { 'use strict'; }");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    eval("function f(a=(function () { <x/> })) {}");
+}, SyntaxError);
+assertThrowsInstanceOf(function () {
+    eval("function f(a=(function () { <x/> })) { 'use strict'; }");
+}, SyntaxError);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1985,19 +1985,20 @@ JS_EnumerateStandardClasses(JSContext *c
     assertSameCompartment(cx, obj_);
 
     RootedObject obj(cx, obj_);
 
     /*
      * Check whether we need to bind 'undefined' and define it if so.
      * Since ES5 15.1.1.3 undefined can't be deleted.
      */
-    PropertyName *name = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
-    if (!obj->nativeContains(cx, NameToId(name)) &&
-        !obj->defineProperty(cx, name, UndefinedValue(),
+    RootedPropertyName undefinedName(cx, cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
+    RootedId undefinedId(cx, NameToId(undefinedName));
+    if (!obj->nativeContains(cx, undefinedId) &&
+        !obj->defineProperty(cx, undefinedName, UndefinedValue(),
                              JS_PropertyStub, JS_StrictPropertyStub,
                              JSPROP_PERMANENT | JSPROP_READONLY)) {
         return false;
     }
 
     /* Initialize any classes that have not been initialized yet. */
     for (unsigned i = 0; standard_class_atoms[i].init; i++) {
         const JSStdName &stdnm = standard_class_atoms[i];
@@ -2064,17 +2065,18 @@ AddNameToArray(JSContext *cx, PropertyNa
     *ip = i + 1;
     return ida;
 }
 
 static JSIdArray *
 EnumerateIfResolved(JSContext *cx, JSObject *obj, PropertyName *name, JSIdArray *ida,
                     int *ip, JSBool *foundp)
 {
-    *foundp = obj->nativeContains(cx, NameToId(name));
+    RootedId id(cx, NameToId(name));
+    *foundp = obj->nativeContains(cx, id);
     if (*foundp)
         ida = AddNameToArray(cx, name, ida, ip);
     return ida;
 }
 
 JS_PUBLIC_API(JSIdArray *)
 JS_EnumerateResolvedStandardClasses(JSContext *cx, JSObject *obj, JSIdArray *ida)
 {
@@ -3352,17 +3354,18 @@ JS_NewObjectWithGivenProto(JSContext *cx
 
 JS_PUBLIC_API(JSObject *)
 JS_NewObjectForConstructor(JSContext *cx, JSClass *clasp, const jsval *vp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, *vp);
 
-    return js_CreateThis(cx, Valueify(clasp), JSVAL_TO_OBJECT(*vp));
+    RootedObject obj(cx, JSVAL_TO_OBJECT(*vp));
+    return js_CreateThis(cx, Valueify(clasp), obj);
 }
 
 JS_PUBLIC_API(JSBool)
 JS_IsExtensible(JSObject *obj)
 {
     return obj->isExtensible();
 }
 
@@ -5974,22 +5977,25 @@ JS_EncodeStringToBuffer(JSString *str, c
     }
     return necessaryLength;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space,
              JSONWriteCallback callback, void *data)
 {
+    RootedValue value(cx, *vp);
+
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, replacer, space);
     StringBuffer sb(cx);
-    if (!js_Stringify(cx, vp, replacer, space, sb))
+    if (!js_Stringify(cx, &value, replacer, space, sb))
         return false;
+    *vp = value;
     if (sb.empty()) {
         JSAtom *nullAtom = cx->runtime->atomState.nullAtom;
         return callback(nullAtom->chars(), nullAtom->length(), data);
     }
     return callback(sb.begin(), sb.length(), data);
 }
 
 JS_PUBLIC_API(JSBool)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -609,16 +609,85 @@ IsPoisonedValue(const Value &v)
         return IsPoisonedPtr(v.toString());
     if (v.isObject())
         return IsPoisonedPtr(&v.toObject());
     return false;
 }
 
 /************************************************************************/
 
+/* This is a specialization of the general Handle template in gc/Root.h */
+template <>
+class Handle<Value>
+{
+  public:
+    /*
+     * Construct a handle from an explicitly rooted location. This is the
+     * normal way to create a handle, and normally happens implicitly.
+     */
+    inline Handle(Rooted<Value> &root) {
+        ptr = root.address();
+    }
+
+    /*
+     * This may be called only if the location of the T is guaranteed
+     * to be marked (for some reason other than being a Rooted),
+     * e.g., if it is guaranteed to be reachable from an implicit root.
+     *
+     * Create a Handle from a raw location of a T.
+     */
+    static Handle fromMarkedLocation(const Value *p) {
+        Handle h;
+        h.ptr = p;
+        return h;
+    }
+
+    const Value *address() const { return ptr; }
+    const Value &get() const { return *ptr; }
+    operator const Value &() const { return *ptr; }
+
+    bool operator==(const Handle &h) const { return *ptr == *h.ptr; }
+    bool operator!=(const Handle &h) const { return *ptr != *h.ptr; }
+
+    bool isUndefined() const { return ptr->isUndefined(); }
+    bool isNull() const { return ptr->isNull(); }
+    bool isBoolean() const { return ptr->isBoolean(); }
+    bool isTrue() const { return ptr->isTrue(); }
+    bool isFalse() const { return ptr->isFalse(); }
+    bool isNumber() const { return ptr->isNumber(); }
+    bool isInt32() const { return ptr->isInt32(); }
+    bool isDouble() const { return ptr->isDouble(); }
+    bool isString() const { return ptr->isString(); }
+    bool isObject() const { return ptr->isObject(); }
+    bool isMagic() const { return ptr->isMagic(); }
+    bool isMagic(JSWhyMagic why) const { return ptr->isMagic(why); }
+    bool isGCThing() const { return ptr->isGCThing(); }
+    bool isMarkable() const { return ptr->isMarkable(); }
+
+    bool toBoolean() const { return ptr->toBoolean(); }
+    double toNumber() const { return ptr->toNumber(); }
+    int32_t toInt32() const { return ptr->toInt32(); }
+    double toDouble() const { return ptr->toDouble(); }
+    JSString *toString() const { return ptr->toString(); }
+    JSObject &toObject() const { return ptr->toObject(); }
+    JSObject *toObjectOrNull() const { return ptr->toObjectOrNull(); }
+    void *toGCThing() const { return ptr->toGCThing(); }
+
+#ifdef DEBUG
+    JSWhyMagic whyMagic() const { return ptr->whyMagic(); }
+#endif
+
+  private:
+    Handle() {}
+
+    const Value *ptr;
+};
+
+/************************************************************************/
+
 static JS_ALWAYS_INLINE Value
 NullValue()
 {
     Value v;
     v.setNull();
     return v;
 }
 
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3721,24 +3721,24 @@ NewArray(JSContext *cx, uint32_t length,
 
     GlobalObject *parent_ = GetCurrentGlobal(cx);
 
     NewObjectCache &cache = cx->runtime->newObjectCache;
 
     NewObjectCache::EntryIndex entry = -1;
     if (cache.lookupGlobal(&ArrayClass, parent_, kind, &entry)) {
         JSObject *obj = cache.newObjectFromHit(cx, entry);
-        if (!obj)
-            return NULL;
-        /* Fixup the elements pointer and length, which may be incorrect. */
-        obj->setFixedElements();
-        obj->setArrayLength(cx, length);
-        if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length))
-            return NULL;
-        return obj;
+        if (obj) {
+            /* Fixup the elements pointer and length, which may be incorrect. */
+            obj->setFixedElements();
+            obj->setArrayLength(cx, length);
+            if (allocateCapacity && !EnsureNewArrayElements(cx, obj, length))
+                return NULL;
+            return obj;
+        }
     }
 
     Rooted<GlobalObject*> parent(cx, parent_);
     RootedObject proto(cx, proto_);
 
     if (!proto && !FindProto(cx, &ArrayClass, parent, &proto))
         return NULL;
 
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -288,17 +288,17 @@ struct JSAtomState
     }
 
     void checkStaticInvariants();
 };
 
 extern bool
 AtomIsInterned(JSContext *cx, JSAtom *atom);
 
-#define ATOM(name) cx->runtime->atomState.name##Atom
+#define ATOM(name) js::HandlePropertyName::fromMarkedLocation(&cx->runtime->atomState.name##Atom)
 
 #define COMMON_ATOM_INDEX(name)                                               \
     ((offsetof(JSAtomState, name##Atom) - JSAtomState::commonAtomsOffset)     \
      / sizeof(JSAtom*))
 #define COMMON_TYPE_ATOM_INDEX(type)                                          \
     ((offsetof(JSAtomState, typeAtoms[type]) - JSAtomState::commonAtomsOffset)\
      / sizeof(JSAtom*))
 
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -36,16 +36,20 @@ WriteStructuredClone(JSContext *cx, cons
     return w.init() && w.write(v) && out.extractBuffer(bufp, nbytesp);
 }
 
 bool
 ReadStructuredClone(JSContext *cx, const uint64_t *data, size_t nbytes, Value *vp,
                     const JSStructuredCloneCallbacks *cb, void *cbClosure)
 {
     SCInput in(cx, data, nbytes);
+
+    /* XXX disallow callers from using internal pointers to GC things. */
+    SkipRoot skip(cx, &in);
+
     JSStructuredCloneReader r(in, cb, cbClosure);
     return r.read(vp);
 }
 
 } /* namespace js */
 
 enum StructuredDataType {
     /* Structured data types provided by the engine */
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -322,17 +322,17 @@ namespace js {
 
 bool
 AutoResolving::alreadyStartedSlow() const
 {
     JS_ASSERT(link);
     AutoResolving *cursor = link;
     do {
         JS_ASSERT(this != cursor);
-        if (object == cursor->object && id == cursor->id && kind == cursor->kind)
+        if (object.get() == cursor->object && id.get() == cursor->id && kind == cursor->kind)
             return true;
     } while (!!(cursor = cursor->link));
     return false;
 }
 
 } /* namespace js */
 
 static void
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -275,17 +275,21 @@ class NewObjectCache
     /*
      * Get the entry index for the given lookup, return whether there was a hit
      * on an existing entry.
      */
     inline bool lookupProto(Class *clasp, JSObject *proto, gc::AllocKind kind, EntryIndex *pentry);
     inline bool lookupGlobal(Class *clasp, js::GlobalObject *global, gc::AllocKind kind, EntryIndex *pentry);
     inline bool lookupType(Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, EntryIndex *pentry);
 
-    /* Return a new object from a cache hit produced by a lookup method. */
+    /*
+     * Return a new object from a cache hit produced by a lookup method, or
+     * NULL if returning the object could possibly trigger GC (does not
+     * indicate failure).
+     */
     inline JSObject *newObjectFromHit(JSContext *cx, EntryIndex entry);
 
     /* Fill an entry after a cache miss. */
     inline void fillProto(EntryIndex entry, Class *clasp, JSObject *proto, gc::AllocKind kind, JSObject *obj);
     inline void fillGlobal(EntryIndex entry, Class *clasp, js::GlobalObject *global, gc::AllocKind kind, JSObject *obj);
     inline void fillType(EntryIndex entry, Class *clasp, js::types::TypeObject *type, gc::AllocKind kind, JSObject *obj);
 
     /* Invalidate any entries which might produce an object with shape/proto. */
@@ -1092,16 +1096,19 @@ struct JSContext : js::ContextFriendFiel
     /* GC heap compartment. */
     JSCompartment       *compartment;
 
     inline void setCompartment(JSCompartment *compartment);
 
     /* Current execution stack. */
     js::ContextStack    stack;
 
+    /* Current global. */
+    inline js::Handle<js::GlobalObject*> global() const;
+
     /* ContextStack convenience functions */
     inline bool hasfp() const               { return stack.hasfp(); }
     inline js::StackFrame* fp() const       { return stack.fp(); }
     inline js::StackFrame* maybefp() const  { return stack.maybefp(); }
     inline js::FrameRegs& regs() const      { return stack.regs(); }
     inline js::FrameRegs* maybeRegs() const { return stack.maybeRegs(); }
 
     /* Set cx->compartment based on the current scope chain. */
@@ -1369,17 +1376,17 @@ namespace js {
 
 struct AutoResolving {
   public:
     enum Kind {
         LOOKUP,
         WATCH
     };
 
-    AutoResolving(JSContext *cx, JSObject *obj, jsid id, Kind kind = LOOKUP
+    AutoResolving(JSContext *cx, HandleObject obj, HandleId id, Kind kind = LOOKUP
                   JS_GUARD_OBJECT_NOTIFIER_PARAM)
       : context(cx), object(obj), id(id), kind(kind), link(cx->resolvingList)
     {
         JS_GUARD_OBJECT_NOTIFIER_INIT;
         JS_ASSERT(obj);
         cx->resolvingList = this;
     }
 
@@ -1391,18 +1398,18 @@ struct AutoResolving {
     bool alreadyStarted() const {
         return link && alreadyStartedSlow();
     }
 
   private:
     bool alreadyStartedSlow() const;
 
     JSContext           *const context;
-    JSObject            *const object;
-    jsid                const id;
+    HandleObject        object;
+    HandleId            id;
     Kind                const kind;
     AutoResolving       *const link;
     JS_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 #if JS_HAS_XML_SUPPORT
 class AutoXMLRooter : private AutoGCRooter {
   public:
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -107,31 +107,16 @@ NewObjectCache::newObjectFromHit(JSConte
 
     JSObject *obj = js_TryNewGCObject(cx, entry->kind);
     if (obj) {
         copyCachedToObject(obj, reinterpret_cast<JSObject *>(&entry->templateObject));
         Probes::createObject(cx, obj);
         return obj;
     }
 
-    /* Copy the entry to the stack first in case it is purged by a GC. */
-    size_t nbytes = entry->nbytes;
-    char stackObject[sizeof(JSObject_Slots16)];
-    JS_ASSERT(nbytes <= sizeof(stackObject));
-    js_memcpy(&stackObject, &entry->templateObject, nbytes);
-
-    JSObject *baseobj = (JSObject *) stackObject;
-
-    obj = js_NewGCObject(cx, entry->kind);
-    if (obj) {
-        copyCachedToObject(obj, baseobj);
-        Probes::createObject(cx, obj);
-        return obj;
-    }
-
     return NULL;
 }
 
 struct PreserveRegsGuard
 {
     PreserveRegsGuard(JSContext *cx, FrameRegs &regs)
       : prevContextRegs(cx->maybeRegs()), cx(cx), regs_(regs) {
         cx->stack.repointRegs(&regs_);
@@ -224,17 +209,17 @@ class CompartmentChecker
     JSCompartment *compartment;
 
   public:
     explicit CompartmentChecker(JSContext *cx)
       : context(cx), compartment(cx->compartment)
     {
         if (cx->compartment) {
             GlobalObject *global = GetGlobalForScopeChain(cx);
-            JS_ASSERT(cx->compartment->maybeGlobal() == global);
+            JS_ASSERT(cx->global() == global);
         }
     }
 
     /*
      * Set a breakpoint here (break js::CompartmentChecker::fail) to debug
      * compartment mismatches.
      */
     static void fail(JSCompartment *c1, JSCompartment *c2) {
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -112,16 +112,17 @@ class AutoDebugModeGC;
 }
 
 struct JSCompartment
 {
     JSRuntime                    *rt;
     JSPrincipals                 *principals;
 
   private:
+    friend struct JSContext;
     js::GlobalObject             *global_;
   public:
     // Nb: global_ might be NULL, if (a) it's the atoms compartment, or (b) the
     // compartment's global has been collected.  The latter can happen if e.g.
     // a string in a compartment is rooted but no object is, and thus the
     // global isn't rooted, and thus the global can be finalized while the
     // compartment lives on.
     //
@@ -433,16 +434,22 @@ class js::AutoDebugModeGC
 
 inline void
 JSContext::setCompartment(JSCompartment *compartment)
 {
     this->compartment = compartment;
     this->inferenceEnabled = compartment ? compartment->types.inferenceEnabled : false;
 }
 
+inline js::Handle<js::GlobalObject*>
+JSContext::global() const
+{
+    return js::Handle<js::GlobalObject*>::fromMarkedLocation(&compartment->global_);
+}
+
 namespace js {
 
 class PreserveCompartment {
   protected:
     JSContext *cx;
   private:
     JSCompartment *oldCompartment;
     bool oldInferenceEnabled;
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -143,47 +143,54 @@ TimeWithinDay(double t)
     double result = fmod(t, msPerDay);
     if (result < 0)
         result += msPerDay;
     return result;
 }
 
 /* ES5 15.9.1.3. */
 inline bool
-IsLeapYear(int year)
+IsLeapYear(double year)
 {
-    return year % 4 == 0 && (year % 100 || (year % 400 == 0));
+    JS_ASSERT(ToInteger(year) == year);
+    return fmod(year, 4) == 0 && (fmod(year, 100) != 0 || fmod(year, 400) == 0);
 }
 
-inline int
-DaysInYear(int year)
+inline double
+DaysInYear(double year)
 {
+    if (!MOZ_DOUBLE_IS_FINITE(year))
+        return js_NaN;
     return IsLeapYear(year) ? 366 : 365;
 }
 
-inline int
-DayFromYear(int y)
+inline double
+DayFromYear(double y)
 {
-    /* This is floating-point math so that floor((1968 - 1969) / 4) == -1. */
     return 365 * (y - 1970) +
            floor((y - 1969) / 4.0) -
            floor((y - 1901) / 100.0) +
            floor((y - 1601) / 400.0);
 }
 
 inline double
-TimeFromYear(int y)
+TimeFromYear(double y)
 {
     return DayFromYear(y) * msPerDay;
 }
 
-static int
+static double
 YearFromTime(double t)
 {
-    int y = (int) floor(t / (msPerDay * 365.2425)) + 1970;
+    if (!MOZ_DOUBLE_IS_FINITE(t))
+        return js_NaN;
+
+    JS_ASSERT(ToInteger(t) == t);
+
+    double y = floor(t / (msPerDay * 365.2425)) + 1970;
     double t2 = TimeFromYear(y);
 
     /*
      * Adjust the year if the approximation was wrong.  Since the year was
      * computed using the average number of ms per year, it will usually
      * be wrong for dates within several hours of a year transition.
      */
     if (t2 > t) {
@@ -197,28 +204,31 @@ YearFromTime(double t)
 
 inline int
 DaysInFebruary(int year)
 {
     return IsLeapYear(year) ? 29 : 28;
 }
 
 /* ES5 15.9.1.4. */
-inline int
-DayWithinYear(double t, int year)
+inline double
+DayWithinYear(double t, double year)
 {
-    JS_ASSERT(YearFromTime(t) == year);
-    return int(Day(t) - DayFromYear(year));
+    JS_ASSERT_IF(MOZ_DOUBLE_IS_FINITE(t), YearFromTime(t) == year);
+    return Day(t) - DayFromYear(year);
 }
 
-static int
+static double
 MonthFromTime(double t)
 {
-    int year = YearFromTime(t);
-    int d = DayWithinYear(t, year);
+    if (!MOZ_DOUBLE_IS_FINITE(t))
+        return js_NaN;
+
+    double year = YearFromTime(t);
+    double d = DayWithinYear(t, year);
 
     int step;
     if (d < (step = 31))
         return 0;
     if (d < (step += DaysInFebruary(year)))
         return 1;
     if (d < (step += 31))
         return 2;
@@ -237,21 +247,21 @@ MonthFromTime(double t)
     if (d < (step += 31))
         return 9;
     if (d < (step += 30))
         return 10;
     return 11;
 }
 
 /* ES5 15.9.1.5. */
-static int
+static double
 DateFromTime(double t)
 {
-    int year = YearFromTime(t);
-    int d = DayWithinYear(t, year);
+    double year = YearFromTime(t);
+    double d = DayWithinYear(t, year);
 
     int next;
     if (d <= (next = 30))
         return d + 1;
     int step = next;
     if (d <= (next += DaysInFebruary(year)))
         return d - step;
     step = next;
@@ -284,25 +294,92 @@ DateFromTime(double t)
     step = next;
     return d - step;
 }
 
 /* ES5 15.9.1.6. */
 static int
 WeekDay(double t)
 {
+    /*
+     * We can't assert TimeClip(t) == t because we call this function with
+     * local times, which can be offset outside TimeClip's permitted range.
+     */
+    JS_ASSERT(ToInteger(t) == t);
     int result = (int(Day(t)) + 4) % 7;
     if (result < 0)
         result += 7;
     return result;
 }
 
 /* ES5 15.9.1.7. */
 static double LocalTZA; // set by js_InitDateClass
 
+inline int
+DayFromMonth(int month, bool isLeapYear)
+{
+    /*
+     * The following array contains the day of year for the first day of
+     * each month, where index 0 is January, and day 0 is January 1.
+     */
+    static const int firstDayOfMonth[2][13] = {
+        {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
+        {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
+    };
+
+    JS_ASSERT(0 <= month && month <= 12);
+    return firstDayOfMonth[isLeapYear][month];
+}
+
+template<typename T>
+inline int
+DayFromMonth(T month, bool isLeapYear) MOZ_DELETE;
+
+/* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */
+static double
+MakeDay(double year, double month, double date)
+{
+    /* Step 1. */
+    if (!MOZ_DOUBLE_IS_FINITE(year) || !MOZ_DOUBLE_IS_FINITE(month) || !MOZ_DOUBLE_IS_FINITE(date))
+        return js_NaN;
+
+    /* Steps 2-4. */
+    double y = ToInteger(year);
+    double m = ToInteger(month);
+    double dt = ToInteger(date);
+
+    /* Step 5. */
+    double ym = y + floor(m / 12);
+
+    /* Step 6. */
+    int mn = int(fmod(m, 12.0));
+    if (mn < 0)
+        mn += 12;
+
+    /* Steps 7-8. */
+    bool leap = IsLeapYear(ym);
+
+    double yearday = floor(TimeFromYear(ym) / msPerDay);
+    double monthday = DayFromMonth(mn, leap);
+
+    return yearday + monthday + dt - 1;
+}
+
+/* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */
+inline double
+MakeDate(double day, double time)
+{
+    /* Step 1. */
+    if (!MOZ_DOUBLE_IS_FINITE(day) || !MOZ_DOUBLE_IS_FINITE(time))
+        return js_NaN;
+
+    /* Step 2. */
+    return day * msPerDay + time;
+}
+
 /*
  * Find a year for which any given date will fall on the same weekday.
  *
  * This function should be used with caution when used other than
  * for determining DST; it hasn't been proven not to produce an
  * incorrect year for times near year boundaries.
  */
 static int
@@ -324,82 +401,29 @@ EquivalentYearForDST(int year)
 
     int day = int(DayFromYear(year) + 4) % 7;
     if (day < 0)
         day += 7;
 
     return yearStartingWith[IsLeapYear(year)][day];
 }
 
-inline int
-DayFromMonth(int month, bool isLeapYear)
-{
-    /*
-     * The following array contains the day of year for the first day of
-     * each month, where index 0 is January, and day 0 is January 1.
-     */
-    static const int firstDayOfMonth[2][13] = {
-        {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
-        {0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
-    };
-
-    JS_ASSERT(0 <= month && month <= 12);
-    return firstDayOfMonth[isLeapYear][month];
-}
-
-/* ES5 15.9.1.12 (out of order to accommodate DaylightSavingTA). */
-static double
-MakeDay(double year, double month, double date)
-{
-    if (!MOZ_DOUBLE_IS_FINITE(year) || !MOZ_DOUBLE_IS_FINITE(month) || !MOZ_DOUBLE_IS_FINITE(date))
-        return js_NaN;
-
-    JS_ASSERT(ToInteger(year) == year);
-    JS_ASSERT(ToInteger(month) == month);
-    JS_ASSERT(ToInteger(date) == date);
-
-    year += floor(month / 12);
-
-    month = fmod(month, 12.0);
-    if (month < 0)
-        month += 12;
-
-    bool leap = IsLeapYear((int) year);
-
-    double yearday = floor(TimeFromYear(year) / msPerDay);
-    double monthday = DayFromMonth(month, leap);
-
-    return yearday + monthday + date - 1;
-}
-
-/* ES5 15.9.1.13 (out of order to accommodate DaylightSavingTA). */
-inline double
-MakeDate(double day, double time)
-{
-    /* Step 1. */
-    if (!MOZ_DOUBLE_IS_FINITE(day) || !MOZ_DOUBLE_IS_FINITE(time))
-        return js_NaN;
-
-    /* Step 2. */
-    return day * msPerDay + time;
-}
-
 /* ES5 15.9.1.8. */
 static double
 DaylightSavingTA(double t, JSContext *cx)
 {
     if (!MOZ_DOUBLE_IS_FINITE(t))
         return js_NaN;
 
     /*
      * If earlier than 1970 or after 2038, potentially beyond the ken of
      * many OSes, map it to an equivalent year before asking.
      */
     if (t < 0.0 || t > 2145916800000.0) {
-        int year = EquivalentYearForDST(YearFromTime(t));
+        int year = EquivalentYearForDST(int(YearFromTime(t)));
         double day = MakeDay(year, MonthFromTime(t), DateFromTime(t));
         t = MakeDate(day, TimeWithinDay(t));
     }
 
     int64_t timeMilliseconds = static_cast<int64_t>(t);
     int64_t offsetMilliseconds = cx->dstOffsetCache.getDSTOffsetMilliseconds(timeMilliseconds, cx);
     return static_cast<double>(offsetMilliseconds);
 }
@@ -428,49 +452,49 @@ UTC(double t, JSContext *cx)
 /* ES5 15.9.1.10. */
 const double HoursPerDay = 24.0;
 const double MinutesPerHour = 60;
 const double SecondsPerMinute = 60;
 const double msPerSecond = 1000;
 const double msPerMinute = msPerSecond * SecondsPerMinute;
 const double msPerHour = msPerMinute * MinutesPerHour;
 
-static int
+static double
 HourFromTime(double t)
 {
-    int result = (int) fmod(floor(t/msPerHour), HoursPerDay);
+    double result = fmod(floor(t/msPerHour), HoursPerDay);
     if (result < 0)
-        result += int(HoursPerDay);
+        result += HoursPerDay;
     return result;
 }
 
-static int
+static double
 MinFromTime(double t)
 {
-    int result = (int) fmod(floor(t / msPerMinute), MinutesPerHour);
+    double result = fmod(floor(t / msPerMinute), MinutesPerHour);
     if (result < 0)
-        result += int(MinutesPerHour);
+        result += MinutesPerHour;
     return result;
 }
 
-static int
+static double
 SecFromTime(double t)
 {
-    int result = (int) fmod(floor(t / msPerSecond), SecondsPerMinute);
+    double result = fmod(floor(t / msPerSecond), SecondsPerMinute);
     if (result < 0)
-        result += int(SecondsPerMinute);
+        result += SecondsPerMinute;
     return result;
 }
 
-static int
+static double
 msFromTime(double t)
 {
-    int result = (int) fmod(t, msPerSecond);
+    double result = fmod(t, msPerSecond);
     if (result < 0)
-        result += int(msPerSecond);
+        result += msPerSecond;
     return result;
 }
 
 /* ES5 15.9.1.11. */
 static double
 MakeTime(double hour, double min, double sec, double ms)
 {
     /* Step 1. */
@@ -496,25 +520,16 @@ MakeTime(double hour, double min, double
 
     /* Steps 6-7. */
     return h * msPerHour + m * msPerMinute + s * msPerSecond + milli;
 }
 
 /* Additional quantities not mentioned in the spec. */
 const double SecondsPerDay = SecondsPerMinute * MinutesPerHour * HoursPerDay;
 
-/* Additional methods not mentioned in the spec. */
-static int
-DaysInMonth(int year, int month)
-{
-    bool leap = IsLeapYear(year);
-    int result = int(DayFromMonth(month, leap) - DayFromMonth(month - 1, leap));
-    return result;
-}
-
 /**
  * end of ECMA 'support' functions
  */
 
 static JSBool
 date_convert(JSContext *cx, HandleObject obj, JSType hint, Value *vp)
 {
     JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
@@ -595,24 +610,17 @@ date_regionMatches(const char* s1, int s
     return result;
 }
 
 /* find UTC time from given date... no 1900 correction! */
 static double
 date_msecFromDate(double year, double mon, double mday, double hour,
                   double min, double sec, double msec)
 {
-    double day;
-    double msec_time;
-    double result;
-
-    day = MakeDay(year, mon, mday);
-    msec_time = MakeTime(hour, min, sec, msec);
-    result = MakeDate(day, msec_time);
-    return result;
+    return MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, msec));
 }
 
 /* compute the time in msec (unclipped) from the given args */
 #define MAXARGS        7
 
 static JSBool
 date_msecFromArgs(JSContext *cx, CallArgs args, double *rval)
 {
@@ -726,16 +734,24 @@ ndigits(size_t n, size_t *result, const 
 
     if (digits(result, s, i, JS_MIN(limit, init+n)))
         return ((*i - init) == n);
 
     *i = init;
     return JS_FALSE;
 }
 
+static int
+DaysInMonth(int year, int month)
+{
+    bool leap = IsLeapYear(year);
+    int result = int(DayFromMonth(month, leap) - DayFromMonth(month - 1, leap));
+    return result;
+}
+
 /*
  * Parse a string in one of the date-time formats given by the W3C
  * "NOTE-datetime" specification. These formats make up a restricted
  * profile of the ISO 8601 format. Quoted here:
  *
  *   The formats are as follows. Exactly the components shown here
  *   must be present, with exactly this punctuation. Note that the "T"
  *   appears literally in the string, to indicate the beginning of the
@@ -1286,18 +1302,18 @@ FillLocalTimes(JSContext *cx, JSObject *
         }
         return true;
     }
 
     double localTime = LocalTime(utcTime, cx);
 
     obj->setSlot(JSObject::JSSLOT_DATE_LOCAL_TIME, DoubleValue(localTime));
 
-    int year = (int) floor(localTime /(msPerDay*365.2425)) + 1970;
-    double yearStartTime = (double) TimeFromYear(year);
+    int year = (int) floor(localTime /(msPerDay * 365.2425)) + 1970;
+    double yearStartTime = TimeFromYear(year);
 
     /* Adjust the year in case the approximation was wrong, as in YearFromTime. */
     int yearDays;
     if (yearStartTime > localTime) {
         year--;
         yearStartTime -= (msPerDay * DaysInYear(year));
         yearDays = DaysInYear(year);
     } else {
@@ -1531,21 +1547,18 @@ date_getUTCMonth(JSContext *cx, unsigned
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JSObject *thisObj;
     if (!NonGenericMethodGuard(cx, args, date_getUTCMonth, &DateClass, &thisObj))
         return false;
     if (!thisObj)
         return true;
 
-    double result = thisObj->getDateUTCTime().toNumber();
-    if (MOZ_DOUBLE_IS_FINITE(result))
-        result = MonthFromTime(result);
-
-    args.rval().setNumber(result);
+    double d = thisObj->getDateUTCTime().toNumber();
+    args.rval().setNumber(MonthFromTime(d));
     return true;
 }
 
 static JSBool
 date_getDate(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -2408,37 +2421,39 @@ static const char* months[] =
 };
 
 
 // Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
 // requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
 static void
 print_gmt_string(char* buf, size_t size, double utctime)
 {
+    JS_ASSERT(TimeClip(utctime) == utctime);
     JS_snprintf(buf, size, "%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
-                days[WeekDay(utctime)],
-                DateFromTime(utctime),
-                months[MonthFromTime(utctime)],
-                YearFromTime(utctime),
-                HourFromTime(utctime),
-                MinFromTime(utctime),
-                SecFromTime(utctime));
+                days[int(WeekDay(utctime))],
+                int(DateFromTime(utctime)),
+                months[int(MonthFromTime(utctime))],
+                int(YearFromTime(utctime)),
+                int(HourFromTime(utctime)),
+                int(MinFromTime(utctime)),
+                int(SecFromTime(utctime)));
 }
 
 static void
 print_iso_string(char* buf, size_t size, double utctime)
 {
+    JS_ASSERT(TimeClip(utctime) == utctime);
     JS_snprintf(buf, size, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d.%.3dZ",
-                YearFromTime(utctime),
-                MonthFromTime(utctime) + 1,
-                DateFromTime(utctime),
-                HourFromTime(utctime),
-                MinFromTime(utctime),
-                SecFromTime(utctime),
-                msFromTime(utctime));
+                int(YearFromTime(utctime)),
+                int(MonthFromTime(utctime)) + 1,
+                int(DateFromTime(utctime)),
+                int(HourFromTime(utctime)),
+                int(MinFromTime(utctime)),
+                int(SecFromTime(utctime)),
+                int(msFromTime(utctime)));
 }
 
 /* ES5 B.2.6. */
 static JSBool
 date_toGMTString(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -2539,17 +2554,17 @@ date_toJSON(JSContext *cx, unsigned argc
     return true;
 }
 
 /* for Date.toLocaleString; interface to PRMJTime date struct.
  */
 static void
 new_explode(double timeval, PRMJTime *split, JSContext *cx)
 {
-    int year = YearFromTime(timeval);
+    double year = YearFromTime(timeval);
 
     split->tm_usec = int32_t(msFromTime(timeval)) * 1000;
     split->tm_sec = int8_t(SecFromTime(timeval));
     split->tm_min = int8_t(MinFromTime(timeval));
     split->tm_hour = int8_t(HourFromTime(timeval));
     split->tm_mday = int8_t(DateFromTime(timeval));
     split->tm_mon = int8_t(MonthFromTime(timeval));
     split->tm_wday = int8_t(WeekDay(timeval));
@@ -2574,16 +2589,18 @@ date_format(JSContext *cx, double date, 
     char tzbuf[100];
     JSBool usetz;
     size_t i, tzlen;
     PRMJTime split;
 
     if (!MOZ_DOUBLE_IS_FINITE(date)) {
         JS_snprintf(buf, sizeof buf, js_NaN_date_str);
     } else {
+        JS_ASSERT(TimeClip(date) == date);
+
         double local = LocalTime(date, cx);
 
         /* offset from GMT in minutes.  The offset includes daylight savings,
            if it applies. */
         int minutes = (int) floor(AdjustTime(date, cx) / msPerMinute);
 
         /* map 510 minutes to 0830 hours */
         int offset = (minutes / 60) * 100 + minutes % 60;
@@ -2633,43 +2650,43 @@ date_format(JSContext *cx, double date, 
           case FORMATSPEC_FULL:
             /*
              * Avoid dependence on PRMJ_FormatTimeUSEnglish, because it
              * requires a PRMJTime... which only has 16-bit years.  Sub-ECMA.
              */
             /* Tue Oct 31 2000 09:41:40 GMT-0800 (PST) */
             JS_snprintf(buf, sizeof buf,
                         "%s %s %.2d %.4d %.2d:%.2d:%.2d GMT%+.4d%s%s",
-                        days[WeekDay(local)],
-                        months[MonthFromTime(local)],
-                        DateFromTime(local),
-                        YearFromTime(local),
-                        HourFromTime(local),
-                        MinFromTime(local),
-                        SecFromTime(local),
+                        days[int(WeekDay(local))],
+                        months[int(MonthFromTime(local))],
+                        int(DateFromTime(local)),
+                        int(YearFromTime(local)),
+                        int(HourFromTime(local)),
+                        int(MinFromTime(local)),
+                        int(SecFromTime(local)),
                         offset,
                         usetz ? " " : "",
                         usetz ? tzbuf : "");
             break;
           case FORMATSPEC_DATE:
             /* Tue Oct 31 2000 */
             JS_snprintf(buf, sizeof buf,
                         "%s %s %.2d %.4d",
-                        days[WeekDay(local)],
-                        months[MonthFromTime(local)],
-                        DateFromTime(local),
-                        YearFromTime(local));
+                        days[int(WeekDay(local))],
+                        months[int(MonthFromTime(local))],
+                        int(DateFromTime(local)),
+                        int(YearFromTime(local)));
             break;
           case FORMATSPEC_TIME:
             /* 09:41:40 GMT-0800 (PST) */
             JS_snprintf(buf, sizeof buf,
                         "%.2d:%.2d:%.2d GMT%+.4d%s%s",
-                        HourFromTime(local),
-                        MinFromTime(local),
-                        SecFromTime(local),
+                        int(HourFromTime(local)),
+                        int(MinFromTime(local)),
+                        int(SecFromTime(local)),
                         offset,
                         usetz ? " " : "",
                         usetz ? tzbuf : "");
             break;
         }
     }
 
     str = JS_NewStringCopyZ(cx, buf);
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -813,25 +813,16 @@ GetPropertyDesc(JSContext *cx, JSObject 
 
     if (wasThrowing)
         cx->setPendingException(lastException);
 
     pd->flags |= (shape->enumerable() ? JSPD_ENUMERATE : 0)
               |  (!shape->writable()  ? JSPD_READONLY  : 0)
               |  (!shape->configurable() ? JSPD_PERMANENT : 0);
     pd->spare = 0;
-    if (shape->setter() == CallObject::setArgOp) {
-        pd->slot = shape->shortid();
-        pd->flags |= JSPD_ARGUMENT;
-    } else if (shape->setter() == CallObject::setVarOp) {
-        pd->slot = shape->shortid();
-        pd->flags |= JSPD_VARIABLE;
-    } else {
-        pd->slot = 0;
-    }
     pd->alias = JSVAL_VOID;
 
     return JS_TRUE;
 }
 
 JS_PUBLIC_API(JSBool)
 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj_, JSPropertyDescArray *pda)
 {
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -331,26 +331,23 @@ JS_EvaluateInStackFrame(JSContext *cx, J
 
 /************************************************************************/
 
 typedef struct JSPropertyDesc {
     jsval           id;         /* primary id, atomized string, or int */
     jsval           value;      /* property value */
     uint8_t         flags;      /* flags, see below */
     uint8_t         spare;      /* unused */
-    uint16_t        slot;       /* argument/variable slot */
     jsval           alias;      /* alias id if JSPD_ALIAS flag */
 } JSPropertyDesc;
 
 #define JSPD_ENUMERATE  0x01    /* visible to for/in loop */
 #define JSPD_READONLY   0x02    /* assignment is error */
 #define JSPD_PERMANENT  0x04    /* property cannot be deleted */
 #define JSPD_ALIAS      0x08    /* property has an alias id */
-#define JSPD_ARGUMENT   0x10    /* argument to function */
-#define JSPD_VARIABLE   0x20    /* local variable in function */
 #define JSPD_EXCEPTION  0x40    /* exception occurred fetching the property, */
                                 /* value is exception */
 #define JSPD_ERROR      0x80    /* native getter returned JS_FALSE without */
                                 /* throwing an exception */
 
 typedef struct JSPropertyDescArray {
     uint32_t        length;     /* number of elements in array */
     JSPropertyDesc  *array;     /* alloc'd by Get, freed by Put */
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -556,16 +556,19 @@ Exception(JSContext *cx, unsigned argc, 
         args[0].setString(message);
     } else {
         message = NULL;
     }
 
     /* Find the scripted caller. */
     ScriptFrameIter iter(cx);
 
+    /* XXX StackIter should not point directly to scripts. */
+    SkipRoot skip(cx, &iter);
+
     /* Set the 'fileName' property. */
     RootedString filename(cx);
     if (args.length() > 1) {
         filename = ToString(cx, args[1]);
         if (!filename)
             return false;
         args[1].setString(filename);
     } else {
@@ -605,36 +608,36 @@ exn_toString(JSContext *cx, unsigned arg
 
     /* Step 2. */
     if (!args.thisv().isObject()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error");
         return false;
     }
 
     /* Step 1. */
-    JSObject &obj = args.thisv().toObject();
+    RootedObject obj(cx, &args.thisv().toObject());
 
     /* Step 3. */
     Value nameVal;
-    if (!obj.getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal))
+    if (!obj->getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal))
         return false;
 
     /* Step 4. */
     RootedString name(cx);
     if (nameVal.isUndefined()) {
         name = CLASS_NAME(cx, Error);
     } else {
         name = ToString(cx, nameVal);
         if (!name)
             return false;
     }
 
     /* Step 5. */
     Value msgVal;
-    if (!obj.getProperty(cx, cx->runtime->atomState.messageAtom, &msgVal))
+    if (!obj->getProperty(cx, cx->runtime->atomState.messageAtom, &msgVal))
         return false;
 
     /* Step 6. */
     JSString *message;
     if (msgVal.isUndefined()) {
         message = cx->runtime->emptyString;
     } else {
         message = ToString(cx, msgVal);
@@ -677,38 +680,38 @@ exn_toString(JSContext *cx, unsigned arg
  * Return a string that may eval to something similar to the original object.
  */
 static JSBool
 exn_toSource(JSContext *cx, unsigned argc, Value *vp)
 {
     JS_CHECK_RECURSION(cx, return false);
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    JSObject *obj = ToObject(cx, &args.thisv());
+    RootedObject obj(cx, ToObject(cx, &args.thisv()));
     if (!obj)
         return false;
 
     Value nameVal;
-    JSString *name;
+    RootedString name(cx);
     if (!obj->getProperty(cx, cx->runtime->atomState.nameAtom, &nameVal) ||
         !(name = ToString(cx, nameVal)))
     {
         return false;
     }
 
     Value messageVal;
-    JSString *message;
+    RootedString message(cx);
     if (!obj->getProperty(cx, cx->runtime->atomState.messageAtom, &messageVal) ||
         !(message = js_ValueToSource(cx, messageVal)))
     {
         return false;
     }
 
     Value filenameVal;
-    JSString *filename;
+    RootedString filename(cx);
     if (!obj->getProperty(cx, cx->runtime->atomState.fileNameAtom, &filenameVal) ||
         !(filename = js_ValueToSource(cx, filenameVal)))
     {
         return false;
     }
 
     Value linenoVal;
     uint32_t lineno;
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -334,24 +334,24 @@ fun_resolve(JSContext *cx, HandleObject 
     return true;
 }
 
 template<XDRMode mode>
 bool
 js::XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *parentScript)
 {
     /* NB: Keep this in sync with CloneInterpretedFunction. */
-    JSFunction *fun;
     JSAtom *atom;
     uint32_t firstword;           /* flag telling whether fun->atom is non-null,
                                    plus for fun->u.i.skipmin, fun->u.i.wrapper,
                                    and 14 bits reserved for future use */
     uint32_t flagsword;           /* word for argument count and fun->flags */
 
     JSContext *cx = xdr->cx();
+    RootedFunction fun(cx);
     JSScript *script;
     if (mode == XDR_ENCODE) {
         fun = (*objp)->toFunction();
         if (!fun->isInterpreted()) {
             JSAutoByteString funNameBytes;
             if (const char *name = GetFunctionNameBytes(cx, fun, &funNameBytes)) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_SCRIPTED_FUNCTION,
                                      name);
@@ -362,19 +362,19 @@ js::XDRInterpretedFunction(XDRState<mode
         flagsword = (fun->nargs << 16) | fun->flags;
         atom = fun->atom;
         script = fun->script();
     } else {
         RootedObject parent(cx, NULL);
         fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, parent, NULL);
         if (!fun)
             return false;
-        if (!fun->clearParent(cx))
+        if (!JSObject::clearParent(cx, fun))
             return false;
-        if (!fun->clearType(cx))
+        if (!JSObject::clearType(cx, fun))
             return false;
         atom = NULL;
         script = NULL;
     }
 
     if (!xdr->codeUint32(&firstword))
         return false;
     if ((firstword & 1U) && !XDRAtom(xdr, &atom))
@@ -404,27 +404,27 @@ js::XDRInterpretedFunction(XDRState<mode
 
 template bool
 js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *xdr, JSObject **objp, JSScript *parentScript);
 
 template bool
 js::XDRInterpretedFunction(XDRState<XDR_DECODE> *xdr, JSObject **objp, JSScript *parentScript);
 
 JSObject *
-js::CloneInterpretedFunction(JSContext *cx, JSFunction *srcFun)
+js::CloneInterpretedFunction(JSContext *cx, HandleFunction srcFun)
 {
     /* NB: Keep this in sync with XDRInterpretedFunction. */
 
     RootedObject parent(cx, NULL);
-    JSFunction *clone = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, parent, NULL);
+    RootedFunction clone(cx, js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, parent, NULL));
     if (!clone)
         return NULL;
-    if (!clone->clearParent(cx))
+    if (!JSObject::clearParent(cx, clone))
         return NULL;
-    if (!clone->clearType(cx))
+    if (!JSObject::clearType(cx, clone))
         return NULL;
 
     Rooted<JSScript*> srcScript(cx, srcFun->script());
     JSScript *clonedScript = CloneScript(cx, srcScript);
     if (!clonedScript)
         return NULL;
 
     clone->nargs = srcFun->nargs;
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -258,17 +258,17 @@ js_IsNamedLambda(JSFunction *fun) { retu
 
 namespace js {
 
 template<XDRMode mode>
 bool
 XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *parentScript);
 
 extern JSObject *
-CloneInterpretedFunction(JSContext *cx, JSFunction *fun);
+CloneInterpretedFunction(JSContext *cx, HandleFunction fun);
 
 } /* namespace js */
 
 extern JSBool
 js_fun_apply(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern JSBool
 js_fun_call(JSContext *cx, unsigned argc, js::Value *vp);
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -1512,16 +1512,18 @@ js::analyze::ScriptAnalysis::addPushedTy
 {
     js::types::TypeSet *pushed = pushedTypes(offset, which);
     pushed->addType(cx, type);
 }
 
 inline js::types::TypeObject *
 JSCompartment::getEmptyType(JSContext *cx)
 {
+    JS::MaybeCheckStackRoots(cx);
+
     if (!emptyTypeObject) {
         JS::RootedObject nullproto(cx, NULL);
         emptyTypeObject = types.newTypeObject(cx, NULL, JSProto_Object, nullproto, true);
     }
     return emptyTypeObject;
 }
 
 namespace JS {
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -2828,17 +2828,20 @@ BEGIN_CASE(JSOP_DEFVAR)
         attrs |= JSPROP_PERMANENT;
     if (op == JSOP_DEFCONST)
         attrs |= JSPROP_READONLY;
 
     /* Step 8b. */
     RootedObject &obj = rootObject0;
     obj = &regs.fp()->varObj();
 
-    if (!DefVarOrConstOperation(cx, obj, script->getName(regs.pc), attrs))
+    RootedPropertyName &name = rootName0;
+    name = script->getName(regs.pc);
+
+    if (!DefVarOrConstOperation(cx, obj, name, attrs))
         goto error;
 }
 END_CASE(JSOP_DEFVAR)
 
 BEGIN_CASE(JSOP_DEFFUN)
 {
     /*
      * A top-level function defined in Global or Eval code (see ECMA-262
@@ -2962,19 +2965,21 @@ BEGIN_CASE(JSOP_CALLEE)
     PUSH_COPY(regs.fp()->calleev());
 END_CASE(JSOP_CALLEE)
 
 BEGIN_CASE(JSOP_GETTER)
 BEGIN_CASE(JSOP_SETTER)
 {
     JSOp op2 = JSOp(*++regs.pc);
     RootedId &id = rootId0;
-    Value rval;
+    RootedValue &rval_ = rootValue0;
+    Value &rval = rval_.get();
     int i;
-    JSObject *obj;
+
+    RootedObject &obj = rootObject0;
     switch (op2) {
       case JSOP_SETNAME:
       case JSOP_SETPROP:
         id = NameToId(script->getName(regs.pc));
         rval = regs.sp[-1];
         i = -1;
         goto gs_pop_lval;
       case JSOP_SETELEM:
@@ -3116,30 +3121,31 @@ BEGIN_CASE(JSOP_ENDINIT)
     JS_ASSERT(regs.sp[-1].isObject());
 }
 END_CASE(JSOP_ENDINIT)
 
 BEGIN_CASE(JSOP_INITPROP)
 {
     /* Load the property's initial value into rval. */
     JS_ASSERT(regs.stackDepth() >= 2);
-    Value rval = regs.sp[-1];
+    RootedValue &rval = rootValue0;
+    rval = regs.sp[-1];
 
     /* Load the object being initialized into lval/obj. */
     RootedObject &obj = rootObject0;
     obj = &regs.sp[-2].toObject();
     JS_ASSERT(obj->isObject());
 
     PropertyName *name = script->getName(regs.pc);
 
     RootedId &id = rootId0;
     id = NameToId(name);
 
     if (JS_UNLIKELY(name == cx->runtime->atomState.protoAtom)
-        ? !baseops::SetPropertyHelper(cx, obj, obj, id, 0, &rval, script->strictModeCode)
+        ? !baseops::SetPropertyHelper(cx, obj, obj, id, 0, rval.address(), script->strictModeCode)
         : !DefineNativeProperty(cx, obj, id, rval, NULL, NULL,
                                 JSPROP_ENUMERATE, 0, 0, 0)) {
         goto error;
     }
 
     regs.sp--;
 }
 END_CASE(JSOP_INITPROP);
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -308,19 +308,19 @@ SetPropertyOperation(JSContext *cx, jsby
         Shape *shape = entry->prop;
         JS_ASSERT_IF(shape->isDataDescriptor(), shape->writable());
         JS_ASSERT_IF(shape->hasSlot(), entry->isOwnPropertyHit());
 
         if (entry->isOwnPropertyHit() ||
             ((obj2 = obj->getProto()) && obj2->lastProperty() == entry->pshape)) {
 #ifdef DEBUG
             if (entry->isOwnPropertyHit()) {
-                JS_ASSERT(obj->nativeLookupNoAllocation(cx, shape->propid()) == shape);
+                JS_ASSERT(obj->nativeLookupNoAllocation(shape->propid()) == shape);
             } else {
-                JS_ASSERT(obj2->nativeLookupNoAllocation(cx, shape->propid()) == shape);
+                JS_ASSERT(obj2->nativeLookupNoAllocation(shape->propid()) == shape);
                 JS_ASSERT(entry->isPrototypePropertyHit());
                 JS_ASSERT(entry->kshape != entry->pshape);
                 JS_ASSERT(!shape->hasSlot());
             }
 #endif
 
             if (shape->hasDefaultSetter() && shape->hasSlot()) {
                 /* Fast path for, e.g., plain Object instance properties. */
@@ -413,17 +413,17 @@ NameOperation(JSContext *cx, JSScript *s
         if (!NativeGet(cx, normalized, obj2, shape, 0, vp))
             return false;
     }
 
     return true;
 }
 
 inline bool
-DefVarOrConstOperation(JSContext *cx, HandleObject varobj, PropertyName *dn, unsigned attrs)
+DefVarOrConstOperation(JSContext *cx, HandleObject varobj, HandlePropertyName dn, unsigned attrs)
 {
     JS_ASSERT(varobj->isVarObj());
     JS_ASSERT(!varobj->getOps()->defineProperty || varobj->isDebugScope());
 
     RootedShape prop(cx);
     RootedObject obj2(cx);
     if (!varobj->lookupProperty(cx, dn, &obj2, &prop))
         return false;
@@ -542,52 +542,52 @@ AddOperation(JSContext *cx, const Value 
 
 static JS_ALWAYS_INLINE bool
 SubOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, Value *res)
 {
     double d1, d2;
     if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
         return false;
     double d = d1 - d2;
-    if (!res->setNumber(d) && !(lhs.get().isDouble() || rhs.get().isDouble()))
+    if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble()))
         types::TypeScript::MonitorOverflow(cx);
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
 MulOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, Value *res)
 {
     double d1, d2;
     if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
         return false;
     double d = d1 * d2;
-    if (!res->setNumber(d) && !(lhs.get().isDouble() || rhs.get().isDouble()))
+    if (!res->setNumber(d) && !(lhs.isDouble() || rhs.isDouble()))
         types::TypeScript::MonitorOverflow(cx);
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
 DivOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, Value *res)
 {
     double d1, d2;
     if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
         return false;
     res->setNumber(NumberDiv(d1, d2));
 
-    if (d2 == 0 || (res->isDouble() && !(lhs.get().isDouble() || rhs.get().isDouble())))
+    if (d2 == 0 || (res->isDouble() && !(lhs.isDouble() || rhs.isDouble())))
         types::TypeScript::MonitorOverflow(cx);
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
 ModOperation(JSContext *cx, HandleValue lhs, HandleValue rhs, Value *res)
 {
     int32_t l, r;
-    if (lhs.get().isInt32() && rhs.get().isInt32() &&
-        (l = lhs.get().toInt32()) >= 0 && (r = rhs.get().toInt32()) > 0) {
+    if (lhs.isInt32() && rhs.isInt32() &&
+        (l = lhs.toInt32()) >= 0 && (r = rhs.toInt32()) > 0) {
         int32_t mod = l % r;
         res->setInt32(mod);
         return true;
     }
 
     double d1, d2;
     if (!ToNumber(cx, lhs, &d1) || !ToNumber(cx, rhs, &d2))
         return false;
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -610,17 +610,17 @@ VectorToValueIterator(JSContext *cx, Han
     JS_ASSERT(flags & JSITER_FOREACH);
 
     if (obj) {
         if (obj->hasSingletonType() && !obj->setIteratedSingleton(cx))
             return false;
         types::MarkTypeObjectFlags(cx, obj, types::OBJECT_FLAG_ITERATED);
     }
 
-    JSObject *iterobj = NewIteratorObject(cx, flags);
+    RootedObject iterobj(cx, NewIteratorObject(cx, flags));
     if (!iterobj)
         return false;
 
     NativeIterator *ni = NativeIterator::allocateIterator(cx, 0, keys);
     if (!ni)
         return false;
     ni->init(obj, flags, 0, 0);
 
@@ -1593,102 +1593,132 @@ CloseGenerator(JSContext *cx, JSObject *
     }
 
     if (gen->state == JSGEN_CLOSED)
         return JS_TRUE;
 
     return SendToGenerator(cx, JSGENOP_CLOSE, obj, gen, UndefinedValue());
 }
 
-/*
- * Common subroutine of generator_(next|send|throw|close) methods.
- */
 static JSBool
-generator_op(JSContext *cx, Native native, JSGeneratorOp op, Value *vp, unsigned argc)
+generator_send(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JSObject *thisObj;
-    if (!NonGenericMethodGuard(cx, args, native, &GeneratorClass, &thisObj))
+    if (!NonGenericMethodGuard(cx, args, generator_send, &GeneratorClass, &thisObj))
         return false;
     if (!thisObj)
         return true;
 
     JSGenerator *gen = (JSGenerator *) thisObj->getPrivate();
-    if (!gen) {
+    if (!gen || gen->state == JSGEN_CLOSED) {
         /* This happens when obj is the generator prototype. See bug 352885. */
-        goto closed_generator;
+        return js_ThrowStopIteration(cx);
+    }
+
+    if (gen->state == JSGEN_NEWBORN && args.hasDefined(0)) {
+        js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
+                            JSDVG_SEARCH_STACK, args[0], NULL);
+        return false;
+    }
+
+    if (!SendToGenerator(cx, JSGENOP_SEND, thisObj, gen,
+                         args.length() > 0 ? args[0] : UndefinedValue()))
+    {
+        return false;
     }
 
-    if (gen->state == JSGEN_NEWBORN) {
-        switch (op) {
-          case JSGENOP_NEXT:
-          case JSGENOP_THROW:
-            break;
+    args.rval() = gen->fp->returnValue();
+    return true;
+
+}
 
-          case JSGENOP_SEND:
-            if (args.hasDefined(0)) {
-                js_ReportValueError(cx, JSMSG_BAD_GENERATOR_SEND,
-                                    JSDVG_SEARCH_STACK, args[0], NULL);
-                return false;
-            }
-            break;
+static JSBool
+generator_next(JSContext *cx, unsigned argc, Value *vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
 
-          default:
-            JS_ASSERT(op == JSGENOP_CLOSE);
-            SetGeneratorClosed(cx, gen);
-            args.rval().setUndefined();
-            return true;
-        }
-    } else if (gen->state == JSGEN_CLOSED) {
-      closed_generator:
-        switch (op) {
-          case JSGENOP_NEXT:
-          case JSGENOP_SEND:
-            return js_ThrowStopIteration(cx);
-          case JSGENOP_THROW:
-            cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue());
-            return false;
-          default:
-            JS_ASSERT(op == JSGENOP_CLOSE);
-            args.rval().setUndefined();
-            return true;
-        }
+    JSObject *thisObj;
+    if (!NonGenericMethodGuard(cx, args, generator_next, &GeneratorClass, &thisObj))
+        return false;
+    if (!thisObj)
+        return true;
+
+    JSGenerator *gen = (JSGenerator *) thisObj->getPrivate();
+    if (!gen || gen->state == JSGEN_CLOSED) {
+        /* This happens when obj is the generator prototype. See bug 352885. */
+        return js_ThrowStopIteration(cx);
     }
 
-    bool undef = ((op == JSGENOP_SEND || op == JSGENOP_THROW) && args.length() != 0);
-    if (!SendToGenerator(cx, op, thisObj, gen, undef ? args[0] : UndefinedValue()))
+    if (!SendToGenerator(cx, JSGENOP_NEXT, thisObj, gen, UndefinedValue()))
         return false;
 
     args.rval() = gen->fp->returnValue();
     return true;
 }
 
 static JSBool
-generator_send(JSContext *cx, unsigned argc, Value *vp)
-{
-    return generator_op(cx, generator_send, JSGENOP_SEND, vp, argc);
-}
-
-static JSBool
-generator_next(JSContext *cx, unsigned argc, Value *vp)
-{
-    return generator_op(cx, generator_next, JSGENOP_NEXT, vp, argc);
-}
-
-static JSBool
 generator_throw(JSContext *cx, unsigned argc, Value *vp)
 {
-    return generator_op(cx, generator_throw, JSGENOP_THROW, vp, argc);
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    JSObject *thisObj;
+    if (!NonGenericMethodGuard(cx, args, generator_throw, &GeneratorClass, &thisObj))
+        return false;
+    if (!thisObj)
+        return true;
+
+    JSGenerator *gen = (JSGenerator *) thisObj->getPrivate();
+    if (!gen || gen->state == JSGEN_CLOSED) {
+        /* This happens when obj is the generator prototype. See bug 352885. */
+        cx->setPendingException(args.length() >= 1 ? args[0] : UndefinedValue());
+        return false;
+    }
+
+    if (!SendToGenerator(cx, JSGENOP_THROW, thisObj, gen,
+                         args.length() > 0 ? args[0] : UndefinedValue()))
+    {
+        return false;
+    }
+
+    args.rval() = gen->fp->returnValue();
+    return true;
+
 }
 
 static JSBool
 generator_close(JSContext *cx, unsigned argc, Value *vp)
 {
-    return generator_op(cx, generator_close, JSGENOP_CLOSE, vp, argc);
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    JSObject *thisObj;
+    if (!NonGenericMethodGuard(cx, args, generator_close, &GeneratorClass, &thisObj))
+        return false;
+    if (!thisObj)
+        return true;
+
+    JSGenerator *gen = (JSGenerator *) thisObj->getPrivate();
+    if (!gen || gen->state == JSGEN_CLOSED) {
+        /* This happens when obj is the generator prototype. See bug 352885. */
+        args.rval().setUndefined();
+        return true;
+    }
+
+    if (gen->state == JSGEN_NEWBORN) {
+        SetGeneratorClosed(cx, gen);
+        args.rval().setUndefined();
+        return true;
+    }
+
+    if (!SendToGenerator(cx, JSGENOP_CLOSE, thisObj, gen, UndefinedValue()))
+        return false;
+
+    args.rval() = gen->fp->returnValue();
+    return true;
 }
 
 static JSFunctionSpec generator_methods[] = {
     JS_FN(js_next_str,      generator_next,     0,JSPROP_ROPERM),
     JS_FN(js_send_str,      generator_send,     1,JSPROP_ROPERM),
     JS_FN(js_throw_str,     generator_throw,    1,JSPROP_ROPERM),
     JS_FN(js_close_str,     generator_close,    0,JSPROP_ROPERM),
     JS_FS_END
--- a/js/src/jsnum.cpp
+++ b/js/src/jsnum.cpp
@@ -573,24 +573,24 @@ IntToCString(ToCStringBuf *cbuf, int i, 
         *--cp = '-';
 
     return cp.get();
 }
 
 static JSString * JS_FASTCALL
 js_NumberToStringWithBase(JSContext *cx, double d, int base);
 
-static JS_ALWAYS_INLINE bool
-num_toStringHelper(JSContext *cx, Native native, unsigned argc, Value *vp)
+static JSBool
+num_toString(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     double d;
     bool ok;
-    if (!BoxedPrimitiveMethodGuard(cx, args, native, &d, &ok))
+    if (!BoxedPrimitiveMethodGuard(cx, args, num_toString, &d, &ok))
         return ok;
 
     int32_t base = 10;
     if (args.hasDefined(0)) {
         double d2;
         if (!ToInteger(cx, args[0], &d2))
             return false;
 
@@ -606,97 +606,98 @@ num_toStringHelper(JSContext *cx, Native
         JS_ReportOutOfMemory(cx);
         return false;
     }
     args.rval().setString(str);
     return true;
 }
 
 static JSBool
-num_toString(JSContext *cx, unsigned argc, Value *vp)
-{
-    return num_toStringHelper(cx, num_toString, argc, vp);
-}
-
-static JSBool
 num_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
 {
-    size_t thousandsLength, decimalLength;
-    const char *numGrouping, *tmpGroup;
-    JSRuntime *rt;
-    JSString *str;
-    const char *num, *end, *tmpSrc;
-    char *buf, *tmpDest;
-    const char *nint;
-    int digits, buflen, remainder, nrepeat;
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    double d;
+    bool ok;
+    if (!BoxedPrimitiveMethodGuard(cx, args, num_toLocaleString, &d, &ok))
+        return ok;
+
+    Rooted<JSString*> str(cx, js_NumberToStringWithBase(cx, d, 10));
+    if (!str) {
+        JS_ReportOutOfMemory(cx);
+        return false;
+    }
 
     /*
      * Create the string, move back to bytes to make string twiddling
      * a bit easier and so we can insert platform charset seperators.
      */
-    if (!num_toStringHelper(cx, num_toLocaleString, 0, vp))
-        return JS_FALSE;
-    JS_ASSERT(vp->isString());
-    JSAutoByteString numBytes(cx, vp->toString());
+    JSAutoByteString numBytes(cx, str);
     if (!numBytes)
-        return JS_FALSE;
-    num = numBytes.ptr();
+        return false;
+    const char *num = numBytes.ptr();
     if (!num)
-        return JS_FALSE;
+        return false;
 
     /*
      * Find the first non-integer value, whether it be a letter as in
      * 'Infinity', a decimal point, or an 'e' from exponential notation.
      */
-    nint = num;
+    const char *nint = num;
     if (*nint == '-')
         nint++;
     while (*nint >= '0' && *nint <= '9')
         nint++;
-    digits = nint - num;
-    end = num + digits;
-    if (!digits)
-        return JS_TRUE;
+    int digits = nint - num;
+    const char *end = num + digits;
+    if (!digits) {
+        args.rval() = StringValue(str);
+        return true;
+    }
 
-    rt = cx->runtime;
-    thousandsLength = strlen(rt->thousandsSeparator);
-    decimalLength = strlen(rt->decimalSeparator);
+    JSRuntime *rt = cx->runtime;
+    size_t thousandsLength = strlen(rt->thousandsSeparator);
+    size_t decimalLength = strlen(rt->decimalSeparator);
 
     /* Figure out how long resulting string will be. */
-    buflen = strlen(num);
+    int buflen = strlen(num);
     if (*nint == '.')
         buflen += decimalLength - 1; /* -1 to account for existing '.' */
 
+    const char *numGrouping;
+    const char *tmpGroup;
     numGrouping = tmpGroup = rt->numGrouping;
-    remainder = digits;
+    int remainder = digits;
     if (*num == '-')
         remainder--;
 
     while (*tmpGroup != CHAR_MAX && *tmpGroup != '\0') {
         if (*tmpGroup >= remainder)
             break;
         buflen += thousandsLength;
         remainder -= *tmpGroup;
         tmpGroup++;
     }
+
+    int nrepeat;
     if (*tmpGroup == '\0' && *numGrouping != '\0') {
         nrepeat = (remainder - 1) / tmpGroup[-1];
         buflen += thousandsLength * nrepeat;
         remainder -= nrepeat * tmpGroup[-1];
     } else {
         nrepeat = 0;
     }
     tmpGroup--;
 
-    buf = (char *)cx->malloc_(buflen + 1);
+    char *buf = (char *)cx->malloc_(buflen + 1);
     if (!buf)
-        return JS_FALSE;
+        return false;
 
-    tmpDest = buf;
-    tmpSrc = num;
+    char *tmpDest = buf;
+    const char *tmpSrc = num;
 
     while (*tmpSrc == '-' || remainder--) {
         JS_ASSERT(tmpDest - buf < buflen);
         *tmpDest++ = *tmpSrc++;
     }
     while (tmpSrc < end) {
         JS_ASSERT(tmpDest - buf + ptrdiff_t(thousandsLength) <= buflen);
         strcpy(tmpDest, rt->thousandsSeparator);
@@ -716,28 +717,31 @@ num_toLocaleString(JSContext *cx, unsign
         JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint + 1)) <= buflen);
         strcpy(tmpDest, nint + 1);
     } else {
         JS_ASSERT(tmpDest - buf + ptrdiff_t(strlen(nint)) <= buflen);
         strcpy(tmpDest, nint);
     }
 
     if (cx->localeCallbacks && cx->localeCallbacks->localeToUnicode) {
-        JSBool ok = cx->localeCallbacks->localeToUnicode(cx, buf, vp);
+        Rooted<Value> v(cx, StringValue(str));
+        bool ok = !!cx->localeCallbacks->localeToUnicode(cx, buf, v.address());
+        if (ok)
+            args.rval() = v;
         cx->free_(buf);
         return ok;
     }
 
     str = js_NewStringCopyN(cx, buf, buflen);
     cx->free_(buf);
     if (!str)
-        return JS_FALSE;
+        return false;
 
-    vp->setString(str);
-    return JS_TRUE;
+    args.rval() = StringValue(str);
+    return true;
 }
 
 JSBool
 js_num_valueOf(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     double d;
@@ -745,87 +749,133 @@ js_num_valueOf(JSContext *cx, unsigned a
     if (!BoxedPrimitiveMethodGuard(cx, args, js_num_valueOf, &d, &ok))
         return ok;
 
     args.rval().setNumber(d);
     return true;
 }
 
 
-#define MAX_PRECISION 100
-
-static JSBool
-num_to(JSContext *cx, Native native, JSDToStrMode zeroArgMode, JSDToStrMode oneArgMode,
-       int precisionMin, int precisionMax, int precisionOffset,
-       CallArgs args)
-{
-    /* Use MAX_PRECISION+1 because precisionOffset can be 1. */
-    char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION+1)];
-    char *numStr;
-
-    double d;
-    bool ok;
-    if (!BoxedPrimitiveMethodGuard(cx, args, native, &d, &ok))
-        return ok;
+const unsigned MAX_PRECISION = 100;
 
-    double precision;
-    if (args.length() == 0) {
-        precision = 0.0;
-        oneArgMode = zeroArgMode;
-    } else {
-        if (!ToInteger(cx, args[0], &precision))
-            return false;
-        if (precision < precisionMin || precision > precisionMax) {
-            ToCStringBuf cbuf;
-            numStr = IntToCString(&cbuf, int(precision));
-            JS_ASSERT(numStr);
-            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr);
-            return JS_FALSE;
-        }
+static bool
+ComputePrecisionInRange(JSContext *cx, int minPrecision, int maxPrecision, const Value &v,
+                        int *precision)
+{
+    double prec;
+    if (!ToInteger(cx, v, &prec))
+        return false;
+    if (minPrecision <= prec && prec <= maxPrecision) {
+        *precision = int(prec);
+        return true;
     }
+    ToCStringBuf cbuf;
+    char *numStr = IntToCString(&cbuf, *precision);
+    JS_ASSERT(numStr);
+    JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PRECISION_RANGE, numStr);
+    return false;
+}
 
-    numStr = js_dtostr(cx->runtime->dtoaState, buf, sizeof buf,
-                       oneArgMode, (int)precision + precisionOffset, d);
+static bool
+DToStrResult(JSContext *cx, double d, JSDToStrMode mode, int precision, CallArgs args)
+{
+    char buf[DTOSTR_VARIABLE_BUFFER_SIZE(MAX_PRECISION + 1)];
+    char *numStr = js_dtostr(cx->runtime->dtoaState, buf, sizeof buf, mode, precision, d);
     if (!numStr) {
         JS_ReportOutOfMemory(cx);
-        return JS_FALSE;
+        return false;
     }
     JSString *str = js_NewStringCopyZ(cx, numStr);
     if (!str)
-        return JS_FALSE;
-    args.rval().setString(str);
-    return JS_TRUE;
+        return false;
+    args.rval() = StringValue(str);
+    return true;
 }
 
 /*
  * In the following three implementations, we allow a larger range of precision
  * than ECMA requires; this is permitted by ECMA-262.
  */
 static JSBool
 num_toFixed(JSContext *cx, unsigned argc, Value *vp)
 {
-    return num_to(cx, num_toFixed, DTOSTR_FIXED, DTOSTR_FIXED, -20, MAX_PRECISION, 0,
-                  CallArgsFromVp(argc, vp));
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    double d;
+    bool ok;
+    if (!BoxedPrimitiveMethodGuard(cx, args, num_toFixed, &d, &ok))
+        return ok;
+
+    int precision;
+    if (args.length() == 0) {
+        precision = 0;
+    } else {
+        if (!ComputePrecisionInRange(cx, -20, MAX_PRECISION, args[0], &precision))
+            return false;
+    }
+
+    return DToStrResult(cx, d, DTOSTR_FIXED, precision, args);
 }
 
 static JSBool
 num_toExponential(JSContext *cx, unsigned argc, Value *vp)
 {
-    return num_to(cx, num_toExponential, DTOSTR_STANDARD_EXPONENTIAL, DTOSTR_EXPONENTIAL, 0,
-                  MAX_PRECISION, 1, CallArgsFromVp(argc, vp));
+    CallArgs args = CallArgsFromVp(argc, vp);
+
+    double d;
+    bool ok;
+    if (!BoxedPrimitiveMethodGuard(cx, args, num_toExponential, &d, &ok))
+        return ok;
+
+    JSDToStrMode mode;
+    int precision;
+    if (args.length() == 0) {
+        mode = DTOSTR_STANDARD_EXPONENTIAL;
+        precision = 0;
+    } else {
+        mode = DTOSTR_EXPONENTIAL;
+        if (!ComputePrecisionInRange(cx, 0, MAX_PRECISION, args[0], &precision))
+            return false;
+    }
+
+    return DToStrResult(cx, d, mode, precision + 1, args);
 }
 
 static JSBool
 num_toPrecision(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    if (!args.hasDefined(0))
-        return num_toStringHelper(cx, num_toPrecision, 0, vp);
-    return num_to(cx, num_toPrecision, DTOSTR_STANDARD, DTOSTR_PRECISION, 1, MAX_PRECISION, 0,
-                  args);
+
+    double d;
+    bool ok;
+    if (!BoxedPrimitiveMethodGuard(cx, args, num_toPrecision, &d, &ok))
+        return ok;
+
+    if (!args.hasDefined(0)) {
+        JSString *str = js_NumberToStringWithBase(cx, d, 10);
+        if (!str) {
+            JS_ReportOutOfMemory(cx);
+            return false;
+        }
+        args.rval().setString(str);
+        return true;
+    }
+
+    JSDToStrMode mode;
+    int precision;
+    if (args.length() == 0) {
+        mode = DTOSTR_STANDARD;
+        precision = 0;
+    } else {
+        mode = DTOSTR_PRECISION;
+        if (!ComputePrecisionInRange(cx, 1, MAX_PRECISION, args[0], &precision))
+            return false;
+    }
+
+    return DToStrResult(cx, d, mode, precision, args);
 }
 
 static JSFunctionSpec number_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,       num_toSource,          0, 0),
 #endif
     JS_FN(js_toString_str,       num_toString,          1, 0),
     JS_FN(js_toLocaleString_str, num_toLocaleString,    0, 0),
--- a/js/src/jsnum.h
+++ b/js/src/jsnum.h
@@ -121,51 +121,73 @@ const double DOUBLE_INTEGRAL_PRECISION_L
 extern bool
 GetPrefixInteger(JSContext *cx, const jschar *start, const jschar *end, int base,
                  const jschar **endp, double *dp);
 
 /* ES5 9.3 ToNumber, overwriting *vp with the appropriate number value. */
 JS_ALWAYS_INLINE bool
 ToNumber(JSContext *cx, Value *vp)
 {
+#ifdef DEBUG
+    {
+        SkipRoot skip(cx, vp);
+        MaybeCheckStackRoots(cx);
+    }
+#endif
+
     if (vp->isNumber())
         return true;
     double d;
     extern bool ToNumberSlow(JSContext *cx, js::Value v, double *dp);
     if (!ToNumberSlow(cx, *vp, &d))
         return false;
+
     vp->setNumber(d);
     return true;
 }
 
 /*
  * Convert a value to a uint32_t, according to the ECMA rules for
  * ToUint32. Return converted value in *out on success, !ok on
  * failure.
  */
 
 JS_ALWAYS_INLINE bool
 ToUint32(JSContext *cx, const js::Value &v, uint32_t *out)
 {
+#ifdef DEBUG
+    {
+        SkipRoot skip(cx, &v);
+        MaybeCheckStackRoots(cx);
+    }
+#endif
+
     if (v.isInt32()) {
         *out = (uint32_t)v.toInt32();
         return true;
     }
     extern bool ToUint32Slow(JSContext *cx, const js::Value &v, uint32_t *ip);
     return ToUint32Slow(cx, v, out);
 }
 
 /*
  * Convert a value to a number, then to a uint16_t according to the ECMA rules
  * for ToUint16. Return converted value on success, !ok on failure. v must be a
  * copy of a rooted value.
  */
 JS_ALWAYS_INLINE bool
 ValueToUint16(JSContext *cx, const js::Value &v, uint16_t *out)
 {
+#ifdef DEBUG
+    {
+        SkipRoot skip(cx, &v);
+        MaybeCheckStackRoots(cx);
+    }
+#endif
+
     if (v.isInt32()) {
         *out = uint16_t(v.toInt32());
         return true;
     }
     extern bool ValueToUint16Slow(JSContext *cx, const js::Value &v, uint16_t *out);
     return ValueToUint16Slow(cx, v, out);
 }
 
@@ -228,16 +250,23 @@ IsDefinitelyIndex(const Value &v, uint32
 
     return false;
 }
 
 /* ES5 9.4 ToInteger. */
 static inline bool
 ToInteger(JSContext *cx, const js::Value &v, double *dp)
 {
+#ifdef DEBUG
+    {
+        SkipRoot skip(cx, &v);
+        MaybeCheckStackRoots(cx);
+    }
+#endif
+
     if (v.isInt32()) {
         *dp = v.toInt32();
         return true;
     }
     if (v.isDouble()) {
         *dp = v.toDouble();
     } else {
         extern bool ToNumberSlow(JSContext *cx, Value v, double *dp);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -452,17 +452,17 @@ obj_toSource(JSContext *cx, unsigned arg
      * the raw (unconverted, "uncooked") values.
      */
     val = localroot + 2;
 
     RootedId id(cx);
     for (int i = 0; i < ida->length; i++) {
         /* Get strings for id and value and GC-root them via vp. */
         id = ida->vector[i];
-        JSLinearString *idstr;
+        Rooted<JSLinearString*> idstr(cx);
 
         RootedObject obj2(cx);
         RootedShape prop(cx);
         if (!obj->lookupGeneric(cx, id, &obj2, &prop))
             return false;
 
         /*
          * Convert id to a value and then to a string.  Decide early whether we
@@ -698,19 +698,22 @@ obj_valueOf(JSContext *cx, unsigned argc
         return false;
     vp->setObject(*obj);
     return true;
 }
 
 #if JS_HAS_OBJ_WATCHPOINT
 
 static JSBool
-obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
+obj_watch_handler(JSContext *cx, JSObject *obj_, jsid id_, jsval old,
                   jsval *nvp, void *closure)
 {
+    RootedObject obj(cx, obj_);
+    RootedId id(cx, id_);
+
     JSObject *callable = (JSObject *) closure;
     if (JSSubsumePrincipalsOp subsume = cx->runtime->securityCallbacks->subsumePrincipals) {
         if (JSPrincipals *watcher = callable->principals(cx)) {
             if (JSObject *scopeChain = cx->stack.currentScriptedScopeChain()) {
                 if (JSPrincipals *subject = scopeChain->principals(cx)) {
                     if (!subsume(watcher, subject)) {
                         /* Silently don't call the watch handler. */
                         return true;
@@ -1901,17 +1904,17 @@ obj_defineProperty(JSContext *cx, unsign
 
     vp->setObject(*obj);
     return true;
 }
 
 namespace js {
 
 bool
-ReadPropertyDescriptors(JSContext *cx, JSObject *props, bool checkAccessors,
+ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors,
                         AutoIdVector *ids, AutoPropDescArrayRooter *descs)
 {
     if (!GetPropertyNames(cx, props, JSITER_OWNONLY, ids))
         return false;
 
     RootedId id(cx);
     for (size_t i = 0, len = ids->length(); i < len; i++) {
         id = (*ids)[i];
@@ -1921,34 +1924,34 @@ ReadPropertyDescriptors(JSContext *cx, J
             return false;
     }
     return true;
 }
 
 } /* namespace js */
 
 static bool
-DefineProperties(JSContext *cx, HandleObject obj, JSObject *props)
+DefineProperties(JSContext *cx, HandleObject obj, HandleObject props)
 {
     AutoIdVector ids(cx);
     AutoPropDescArrayRooter descs(cx);
     if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
         return false;
 
     bool dummy;
     for (size_t i = 0, len = ids.length(); i < len; i++) {
         if (!DefineProperty(cx, obj, Handle<jsid>::fromMarkedLocation(&ids[i]), descs[i], true, &dummy))
             return false;
     }
 
     return true;
 }
 
 extern JSBool
-js_PopulateObject(JSContext *cx, HandleObject newborn, JSObject *props)
+js_PopulateObject(JSContext *cx, HandleObject newborn, HandleObject props)
 {
     return DefineProperties(cx, newborn, props);
 }
 
 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
 static JSBool
 obj_defineProperties(JSContext *cx, unsigned argc, Value *vp)
 {
@@ -1959,17 +1962,17 @@ obj_defineProperties(JSContext *cx, unsi
     vp->setObject(*obj);
 
     /* Step 2. */
     if (argc < 2) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              "Object.defineProperties", "0", "s");
         return false;
     }
-    JSObject *props = ToObject(cx, &vp[3]);
+    RootedObject props(cx, ToObject(cx, &vp[3]));
     if (!props)
         return false;
 
     /* Steps 3-6. */
     return DefineProperties(cx, obj, props);
 }
 
 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
@@ -2015,17 +2018,18 @@ obj_create(JSContext *cx, unsigned argc,
 
     /* 15.2.3.5 step 4. */
     if (args.hasDefined(1)) {
         if (args[1].isPrimitive()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
             return false;
         }
 
-        if (!DefineProperties(cx, obj, &args[1].toObject()))
+        RootedObject props(cx, &args[1].toObject());
+        if (!DefineProperties(cx, obj, props))
             return false;
     }
 
     /* 5. Return obj. */
     args.rval().setObject(*obj);
     return true;
 }
 
@@ -2384,18 +2388,21 @@ js::NewObjectWithGivenProto(JSContext *c
 
     if (CanBeFinalizedInBackground(kind, clasp))
         kind = GetBackgroundAllocKind(kind);
 
     NewObjectCache &cache = cx->runtime->newObjectCache;
 
     NewObjectCache::EntryIndex entry = -1;
     if (proto && (!parent || parent == proto->getParent()) && !proto->isGlobal()) {
-        if (cache.lookupProto(clasp, proto, kind, &entry))
-            return cache.newObjectFromHit(cx, entry);
+        if (cache.lookupProto(clasp, proto, kind, &entry)) {
+            JSObject *obj = cache.newObjectFromHit(cx, entry);
+            if (obj)
+                return obj;
+        }
     }
 
     types::TypeObject *type = proto ? proto->getNewType(cx) : cx->compartment->getEmptyType(cx);
     if (!type)
         return NULL;
 
     /*
      * Default parent to the parent of the prototype, which was set from
@@ -2440,18 +2447,21 @@ js::NewObjectWithClassProto(JSContext *c
      * will flush the new object cache).
      */
     JSProtoKey protoKey = GetClassProtoKey(clasp);
 
     NewObjectCache &cache = cx->runtime->newObjectCache;
 
     NewObjectCache::EntryIndex entry = -1;
     if (parent->isGlobal() && protoKey != JSProto_Null) {
-        if (cache.lookupGlobal(clasp, &parent->asGlobal(), kind, &entry))
-            return cache.newObjectFromHit(cx, entry);
+        if (cache.lookupGlobal(clasp, &parent->asGlobal(), kind, &entry)) {
+            JSObject *obj = cache.newObjectFromHit(cx, entry);
+            if (obj)
+                return obj;
+        }
     }
 
     if (!FindProto(cx, clasp, parent, &proto))
         return NULL;
 
     types::TypeObject *type = proto->getNewType(cx);
     if (!type)
         return NULL;
@@ -2475,18 +2485,21 @@ js::NewObjectWithType(JSContext *cx, Han
     JS_ASSERT(kind <= gc::FINALIZE_OBJECT_LAST);
     if (CanBeFinalizedInBackground(kind, &ObjectClass))
         kind = GetBackgroundAllocKind(kind);
 
     NewObjectCache &cache = cx->runtime->newObjectCache;
 
     NewObjectCache::EntryIndex entry = -1;
     if (parent == type->proto->getParent()) {
-        if (cache.lookupType(&ObjectClass, type, kind, &entry))
-            return cache.newObjectFromHit(cx, entry);
+        if (cache.lookupType(&ObjectClass, type, kind, &entry)) {
+            JSObject *obj = cache.newObjectFromHit(cx, entry);
+            if (obj)
+                return obj;
+        }
     }
 
     JSObject *obj = NewObject(cx, &ObjectClass, type, parent, kind);
     if (!obj)
         return NULL;
 
     if (entry != -1 && !obj->hasDynamicSlots())
         cache.fillType(entry, &ObjectClass, type, kind, obj);
@@ -2527,17 +2540,17 @@ js::NewReshapedObject(JSContext *cx, Han
         }
     }
     JS_ASSERT(!res->inDictionaryMode());
 
     return res;
 }
 
 JSObject*
-js_CreateThis(JSContext *cx, Class *newclasp, JSObject *callee)
+js_CreateThis(JSContext *cx, Class *newclasp, HandleObject callee)
 {
     Value protov;
     if (!callee->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov))
         return NULL;
 
     JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL;
     JSObject *parent = callee->getParent();
     gc::AllocKind kind = NewObjectGCKind(cx, newclasp);
@@ -3818,17 +3831,18 @@ js_GetClassObject(JSContext *cx, HandleO
     }
 
     Value v = global->getReservedSlot(key);
     if (v.isObject()) {
         objp.set(&v.toObject());
         return true;
     }
 
-    AutoResolving resolving(cx, global, NameToId(cx->runtime->atomState.classAtoms[key]));
+    RootedId name(cx, NameToId(cx->runtime->atomState.classAtoms[key]));
+    AutoResolving resolving(cx, global, name);
     if (resolving.alreadyStarted()) {
         /* Already caching id in global -- suppress recursion. */
         objp.set(NULL);
         return true;
     }
 
     JSObject *cobj = NULL;
     if (JSClassInitializerOp init = lazy_prototype_init[key]) {
@@ -4245,28 +4259,30 @@ CallResolveOp(JSContext *cx, HandleObjec
         if (!obj2)
             return true;
 
         if (!obj2->isNative()) {
             /* Whoops, newresolve handed back a foreign obj2. */
             JS_ASSERT(obj2 != obj);
             return obj2->lookupGeneric(cx, id, objp, propp);
         }
-        obj = obj2;
+
+        objp.set(obj2);
     } else {
         if (!resolve(cx, obj, id))
             return false;
-    }
-
-    if (!obj->nativeEmpty()) {
-        if (Shape *shape = obj->nativeLookup(cx, id)) {
-            objp.set(obj);
-            propp.set(shape);
-        }
-    }
+
+        objp.set(obj);
+    }
+
+    Shape *shape;
+    if (!objp->nativeEmpty() && (shape = objp->nativeLookup(cx, id)))
+        propp.set(shape);
+    else
+        objp.set(NULL);
 
     return true;
 }
 
 static JS_ALWAYS_INLINE bool
 LookupPropertyWithFlagsInline(JSContext *cx, HandleObject obj, HandleId id, unsigned flags,
                               MutableHandleObject objp, MutableHandleShape propp)
 {
@@ -4291,17 +4307,17 @@ LookupPropertyWithFlagsInline(JSContext 
                 /*
                  * For stats we do not recalculate protoIndex even if it was
                  * resolved on some other object.
                  */
                 return true;
             }
         }
 
-        JSObject *proto = current->getProto();
+        RootedObject proto(cx, current->getProto());
         if (!proto)
             break;
         if (!proto->isNative()) {
             if (!proto->lookupGeneric(cx, id, objp, propp))
                 return false;
 #ifdef DEBUG
             /*
              * Non-native objects must have either non-native lookup results,
@@ -4547,17 +4563,17 @@ js_NativeGetInline(JSContext *cx, Handle
 
     Rooted<Shape*> shapeRoot(cx, shape);
     RootedObject pobjRoot(cx, pobj);
 
     if (!shape->get(cx, receiver, obj, pobj, vp))
         return false;
 
     /* Update slotful shapes according to the value produced by the getter. */
-    if (shapeRoot->hasSlot() && pobjRoot->nativeContains(cx, *shapeRoot))
+    if (shapeRoot->hasSlot() && pobjRoot->nativeContains(cx, shapeRoot))
         pobjRoot->nativeSetSlot(shapeRoot->slot(), *vp);
 
     return true;
 }
 
 JSBool
 js_NativeGet(JSContext *cx, Handle<JSObject*> obj, Handle<JSObject*> pobj, Shape *shape,
              unsigned getHow, Value *vp)
@@ -4599,17 +4615,17 @@ js_NativeSet(JSContext *cx, Handle<JSObj
         return false;
 
     /*
      * Update any slot for the shape with the value produced by the setter,
      * unless the setter deleted the shape.
      */
     if (shapeRoot->hasSlot() &&
         (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
-         obj->nativeContains(cx, *shapeRoot))) {
+         obj->nativeContains(cx, shapeRoot))) {
         obj->setSlot(shapeRoot->slot(), *vp);
     }
 
     return true;
 }
 
 static JS_ALWAYS_INLINE JSBool
 js_GetPropertyHelperInline(JSContext *cx, HandleObject obj, HandleObject receiver, jsid id_,
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -302,18 +302,21 @@ struct JSObject : public js::ObjectImpl
     inline bool canRemoveLastProperty();
 
     /*
      * Update the slot span directly for a dictionary object, and allocate
      * slots to cover the new span if necessary.
      */
     bool setSlotSpan(JSContext *cx, uint32_t span);
 
-    inline bool nativeContains(JSContext *cx, jsid id);
-    inline bool nativeContains(JSContext *cx, const js::Shape &shape);
+    inline bool nativeContains(JSContext *cx, js::HandleId id);
+    inline bool nativeContains(JSContext *cx, js::HandleShape shape);
+
+    inline bool nativeContainsNoAllocation(jsid id);
+    inline bool nativeContainsNoAllocation(const js::Shape &shape);
 
     /* Upper bound on the number of elements in an object. */
     static const uint32_t NELEMENTS_LIMIT = JS_BIT(28);
 
   public:
     inline bool setDelegate(JSContext *cx);
 
     inline bool isBoundFunction() const;
@@ -505,18 +508,18 @@ struct JSObject : public js::ObjectImpl
     inline JSObject *enclosingScope();
 
     inline js::GlobalObject &global() const;
 
     /* N.B. Infallible: NULL means 'no principal', not an error. */
     inline JSPrincipals *principals(JSContext *cx);
 
     /* Remove the type (and prototype) or parent from a new object. */
-    inline bool clearType(JSContext *cx);
-    bool clearParent(JSContext *cx);
+    static inline bool clearType(JSContext *cx, js::HandleObject obj);
+    static bool clearParent(JSContext *cx, js::HandleObject obj);
 
     /*
      * ES5 meta-object properties and operations.
      */
 
   private:
     enum ImmutabilityType { SEAL, FREEZE };
 
@@ -1084,17 +1087,17 @@ extern const char js_propertyIsEnumerabl
 #ifdef OLD_GETTER_SETTER_METHODS
 extern const char js_defineGetter_str[];
 extern const char js_defineSetter_str[];
 extern const char js_lookupGetter_str[];
 extern const char js_lookupSetter_str[];
 #endif
 
 extern JSBool
-js_PopulateObject(JSContext *cx, js::HandleObject newborn, JSObject *props);
+js_PopulateObject(JSContext *cx, js::HandleObject newborn, js::HandleObject props);
 
 /*
  * Fast access to immutable standard objects (constructors and prototypes).
  */
 extern bool
 js_GetClassObject(JSContext *cx, js::HandleObject obj, JSProtoKey key,
                   js::MutableHandleObject objp);
 
@@ -1112,17 +1115,17 @@ extern JSObject *
 js_CreateThisForFunctionWithProto(JSContext *cx, js::HandleObject callee, JSObject *proto);
 
 // Specialized call for constructing |this| with a known function callee.
 extern JSObject *
 js_CreateThisForFunction(JSContext *cx, js::HandleObject callee, bool newType);
 
 // Generic call for constructing |this|.
 extern JSObject *
-js_CreateThis(JSContext *cx, js::Class *clasp, JSObject *callee);
+js_CreateThis(JSContext *cx, js::Class *clasp, js::HandleObject callee);
 
 /*
  * Find or create a property named by id in obj's scope, with the given getter
  * and setter, slot, attributes, and other members.
  */
 extern js::Shape *
 js_AddNativeProperty(JSContext *cx, js::HandleObject obj, jsid id,
                      JSPropertyOp getter, JSStrictPropertyOp setter, uint32_t slot,
@@ -1190,17 +1193,17 @@ DefineProperty(JSContext *cx, js::Handle
                js::HandleId id, const PropDesc &desc, bool throwError,
                bool *rval);
 
 /*
  * Read property descriptors from props, as for Object.defineProperties. See
  * ES5 15.2.3.7 steps 3-5.
  */
 extern bool
-ReadPropertyDescriptors(JSContext *cx, JSObject *props, bool checkAccessors,
+ReadPropertyDescriptors(JSContext *cx, HandleObject props, bool checkAccessors,
                         AutoIdVector *ids, AutoPropDescArrayRooter *descs);
 
 /*
  * Constant to pass to js_LookupPropertyWithFlags to infer bits from current
  * bytecode.
  */
 static const unsigned RESOLVE_INFER = 0xffff;
 
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -686,26 +686,26 @@ inline js::types::TypeObject *
 JSObject::getType(JSContext *cx)
 {
     JS::RootedObject self(cx, this);
     if (self->hasLazyType())
         self->makeLazyType(cx);
     return self->type_;
 }
 
-inline bool
-JSObject::clearType(JSContext *cx)
+/* static */ inline bool
+JSObject::clearType(JSContext *cx, js::HandleObject obj)
 {
-    JS_ASSERT(!hasSingletonType());
+    JS_ASSERT(!obj->hasSingletonType());
 
     js::types::TypeObject *type = cx->compartment->getEmptyType(cx);
     if (!type)
         return false;
 
-    type_ = type;
+    obj->type_ = type;
     return true;
 }
 
 inline void
 JSObject::setType(js::types::TypeObject *newType)
 {
 #ifdef DEBUG
     JS_ASSERT(newType);
@@ -960,25 +960,37 @@ JSObject::nativeSetSlot(unsigned slot, c
 inline void
 JSObject::nativeSetSlotWithType(JSContext *cx, js::Shape *shape, const js::Value &value)
 {
     nativeSetSlot(shape->slot(), value);
     js::types::AddTypePropertyId(cx, this, shape->propid(), value);
 }
 
 inline bool
-JSObject::nativeContains(JSContext *cx, jsid id)
+JSObject::nativeContains(JSContext *cx, js::HandleId id)
 {
     return nativeLookup(cx, id) != NULL;
 }
 
 inline bool
-JSObject::nativeContains(JSContext *cx, const js::Shape &shape)
+JSObject::nativeContains(JSContext *cx, js::HandleShape shape)
+{
+    return nativeLookup(cx, shape->propid()) == shape;
+}
+
+inline bool
+JSObject::nativeContainsNoAllocation(jsid id)
 {
-    return nativeLookup(cx, shape.propid()) == &shape;
+    return nativeLookupNoAllocation(id) != NULL;
+}
+
+inline bool
+JSObject::nativeContainsNoAllocation(const js::Shape &shape)
+{
+    return nativeLookupNoAllocation(shape.propid()) == &shape;
 }
 
 inline bool
 JSObject::nativeEmpty() const
 {
     return lastProperty()->isEmptyShape();
 }
 
@@ -1599,17 +1611,17 @@ inline bool
 DefineConstructorAndPrototype(JSContext *cx, GlobalObject *global,
                               JSProtoKey key, JSObject *ctor, JSObject *proto)
 {
     JS_ASSERT(!global->nativeEmpty()); /* reserved slots already allocated */
     JS_ASSERT(ctor);
     JS_ASSERT(proto);
 
     jsid id = NameToId(cx->runtime->atomState.classAtoms[key]);
-    JS_ASSERT(!global->nativeLookupNoAllocation(cx, id));
+    JS_ASSERT(!global->nativeLookupNoAllocation(id));
 
     /* Set these first in case AddTypePropertyId looks for this class. */
     global->setSlot(key, ObjectValue(*ctor));
     global->setSlot(key + JSProto_LIMIT, ObjectValue(*proto));
     global->setSlot(key + JSProto_LIMIT * 2, ObjectValue(*ctor));
 
     types::AddTypePropertyId(cx, global, id, ObjectValue(*ctor));
     if (!global->addDataProperty(cx, id, key + JSProto_LIMIT * 2, 0)) {
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -75,24 +75,24 @@ js_json_parse(JSContext *cx, unsigned ar
     /* Steps 2-5. */
     return ParseJSONWithReviver(cx, linear->chars(), linear->length(), reviver, vp);
 }
 
 /* ES5 15.12.3. */
 JSBool
 js_json_stringify(JSContext *cx, unsigned argc, Value *vp)
 {
-    *vp = (argc >= 1) ? vp[2] : UndefinedValue();
-    JSObject *replacer = (argc >= 2 && vp[3].isObject())
-                         ? &vp[3].toObject()
-                         : NULL;
-    Value space = (argc >= 3) ? vp[4] : UndefinedValue();
+    RootedObject replacer(cx, (argc >= 2 && vp[3].isObject())
+                              ? &vp[3].toObject()
+                              : NULL);
+    RootedValue value(cx, (argc >= 1) ? vp[2] : UndefinedValue());
+    RootedValue space(cx, (argc >= 3) ? vp[4] : UndefinedValue());
 
     StringBuffer sb(cx);
-    if (!js_Stringify(cx, vp, replacer, space, sb))
+    if (!js_Stringify(cx, &value, replacer, space, sb))
         return false;
 
     // XXX This can never happen to nsJSON.cpp, but the JSON object
     // needs to support returning undefined. So this is a little awkward
     // for the API, because we want to support streaming writers.
     if (!sb.empty()) {
         JSString *str = sb.finishString();
         if (!str)
@@ -267,44 +267,44 @@ class KeyStringifier<jsid> {
 };
 
 /*
  * ES5 15.12.3 Str, steps 2-4, extracted to enable preprocessing of property
  * values when stringifying objects in JO.
  */
 template<typename KeyType>
 static bool
-PreprocessValue(JSContext *cx, JSObject *holder, KeyType key, Value *vp, StringifyContext *scx)
+PreprocessValue(JSContext *cx, JSObject *holder, KeyType key, MutableHandleValue vp, StringifyContext *scx)
 {
     JSString *keyStr = NULL;
 
     /* Step 2. */
-    if (vp->isObject()) {
+    if (vp.get().isObject()) {
         Value toJSON;
         RootedId id(cx, NameToId(cx->runtime->atomState.toJSONAtom));
-        Rooted<JSObject*> obj(cx, &vp->toObject());
+        Rooted<JSObject*> obj(cx, &vp.get().toObject());
         if (!GetMethod(cx, obj, id, 0, &toJSON))
             return false;
 
         if (js_IsCallable(toJSON)) {
             keyStr = KeyStringifier<KeyType>::toString(cx, key);
             if (!keyStr)
                 return false;
 
             InvokeArgsGuard args;
             if (!cx->stack.pushInvokeArgs(cx, 1, &args))
                 return false;
 
             args.calleev() = toJSON;
-            args.thisv() = *vp;
+            args.thisv() = vp;
             args[0] = StringValue(keyStr);
 
             if (!Invoke(cx, args))
                 return false;
-            *vp = args.rval();
+            vp.set(args.rval());
         }
     }
 
     /* Step 3. */
     if (scx->replacer && scx->replacer->isCallable()) {
         if (!keyStr) {
             keyStr = KeyStringifier<KeyType>::toString(cx, key);
             if (!keyStr)
@@ -313,40 +313,40 @@ PreprocessValue(JSContext *cx, JSObject 
 
         InvokeArgsGuard args;
         if (!cx->stack.pushInvokeArgs(cx, 2, &args))
             return false;
 
         args.calleev() = ObjectValue(*scx->replacer);
         args.thisv() = ObjectValue(*holder);
         args[0] = StringValue(keyStr);
-        args[1] = *vp;
+        args[1] = vp;
 
         if (!Invoke(cx, args))
             return false;
-        *vp = args.rval();
+        vp.set(args.rval());
     }
 
     /* Step 4. */
-    if (vp->isObject()) {
-        JSObject &obj = vp->toObject();
+    if (vp.get().isObject()) {
+        JSObject &obj = vp.get().toObject();
         if (ObjectClassIs(obj, ESClass_Number, cx)) {
             double d;
-            if (!ToNumber(cx, *vp, &d))
+            if (!ToNumber(cx, vp, &d))
                 return false;
-            vp->setNumber(d);
+            vp.set(NumberValue(d));
         } else if (ObjectClassIs(obj, ESClass_String, cx)) {
-            JSString *str = ToStringSlow(cx, *vp);
+            JSString *str = ToStringSlow(cx, vp);
             if (!str)
                 return false;
-            vp->setString(str);
+            vp.set(StringValue(str));
         } else if (ObjectClassIs(obj, ESClass_Boolean, cx)) {
-            if (!BooleanGetPrimitiveValue(cx, obj, vp))
+            if (!BooleanGetPrimitiveValue(cx, obj, vp.address()))
                 return false;
-            JS_ASSERT(vp->isBoolean());
+            JS_ASSERT(vp.get().isBoolean());
         }
     }
 
     return true;
 }
 
 /*
  * Determines whether a value which has passed by ES5 150.2.3 Str steps 1-4's
@@ -407,18 +407,18 @@ JO(JSContext *cx, HandleObject obj, Stri
         /*
          * Steps 8a-8b.  Note that the call to Str is broken up into 1) getting
          * the property; 2) processing for toJSON, calling the replacer, and
          * handling boxed Number/String/Boolean objects; 3) filtering out
          * values which process to |undefined|, and 4) stringifying all values
          * which pass the filter.
          */
         id = propertyList[i];
-        Value outputValue;
-        if (!obj->getGeneric(cx, id, &outputValue))
+        RootedValue outputValue(cx);
+        if (!obj->getGeneric(cx, id, outputValue.address()))
             return false;
         if (!PreprocessValue(cx, obj, id.get(), &outputValue, scx))
             return false;
         if (IsFilteredValue(outputValue))
             continue;
 
         /* Output a comma unless this is the first member to write. */
         if (wroteMember && !scx->sb.append(','))
@@ -476,25 +476,25 @@ JA(JSContext *cx, HandleObject obj, Stri
 
     /* Steps 7-10. */
     if (length != 0) {
         /* Steps 4, 10b(i). */
         if (!WriteIndent(cx, scx, scx->depth))
             return JS_FALSE;
 
         /* Steps 7-10. */
-        Value outputValue;
+        RootedValue outputValue(cx);
         for (uint32_t i = 0; i < length; i++) {
             /*
              * Steps 8a-8c.  Again note how the call to the spec's Str method
              * is broken up into getting the property, running it past toJSON
              * and the replacer and maybe unboxing, and interpreting some
              * values as |null| in separate steps.
              */
-            if (!obj->getElement(cx, i, &outputValue))
+            if (!obj->getElement(cx, i, outputValue.address()))
                 return JS_FALSE;
             if (!PreprocessValue(cx, obj, i, &outputValue, scx))
                 return JS_FALSE;
             if (IsFilteredValue(outputValue)) {
                 if (!scx->sb.append("null"))
                     return JS_FALSE;
             } else {
                 if (!Str(cx, outputValue, scx))
@@ -577,17 +577,18 @@ Str(JSContext *cx, const Value &v, Strin
         ok = JO(cx, obj, scx);
     scx->depth--;
 
     return ok;
 }
 
 /* ES5 15.12.3. */
 JSBool
-js_Stringify(JSContext *cx, Value *vp, JSObject *replacer_, Value space_, StringBuffer &sb)
+js_Stringify(JSContext *cx, MutableHandleValue vp, JSObject *replacer_, Value space_,
+             StringBuffer &sb)
 {
     RootedObject replacer(cx, replacer_);
     RootedValue spaceRoot(cx, space_);
     Value &space = spaceRoot.get();
 
     /* Step 4. */
     AutoIdVector propertyList(cx);
     if (replacer) {
@@ -718,33 +719,33 @@ js_Stringify(JSContext *cx, Value *vp, J
 
     /* Step 9. */
     RootedObject wrapper(cx, NewBuiltinClassInstance(cx, &ObjectClass));
     if (!wrapper)
         return false;
 
     /* Step 10. */
     RootedId emptyId(cx, NameToId(cx->runtime->atomState.emptyAtom));
-    if (!DefineNativeProperty(cx, wrapper, emptyId, *vp, JS_PropertyStub, JS_StrictPropertyStub,
+    if (!DefineNativeProperty(cx, wrapper, emptyId, vp, JS_PropertyStub, JS_StrictPropertyStub,
                               JSPROP_ENUMERATE, 0, 0))
     {
         return false;
     }
 
     /* Step 11. */
     StringifyContext scx(cx, sb, gap, replacer, propertyList);
     if (!scx.init())
         return false;
 
     if (!PreprocessValue(cx, wrapper, emptyId.get(), vp, &scx))
         return false;
-    if (IsFilteredValue(*vp))
+    if (IsFilteredValue(vp))
         return true;
 
-    return Str(cx, *vp, &scx);
+    return Str(cx, vp, &scx);
 }
 
 /* ES5 15.12.2 Walk. */
 static bool
 Walk(JSContext *cx, HandleObject holder, HandleId name, const Value &reviver, Value *vp)
 {
     JS_CHECK_RECURSION(cx, return false);
 
--- a/js/src/json.h
+++ b/js/src/json.h
@@ -12,17 +12,18 @@
 
 #define JSON_MAX_DEPTH  2048
 #define JSON_PARSER_BUFSIZE 1024
 
 extern JSObject *
 js_InitJSONClass(JSContext *cx, JSObject *obj);
 
 extern JSBool
-js_Stringify(JSContext *cx, js::Value *vp, JSObject *replacer, js::Value space,
+js_Stringify(JSContext *cx, js::MutableHandleValue vp,
+	     JSObject *replacer, js::Value space, 
              js::StringBuffer &sb);
 
 // Avoid build errors on certain platforms that define these names as constants
 #undef STRICT
 #undef LEGACY
 
 /*
  * The type of JSON decoding to perform.  Strict decoding is to-the-spec;
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -187,17 +187,17 @@ PropertyCache::fullTest(JSContext *cx, j
             break;
         pobj = tmp;
         protoIndex--;
     }
 
     if (pobj->lastProperty() == entry->pshape) {
 #ifdef DEBUG
         PropertyName *name = GetNameFromBytecode(cx, script, pc, op);
-        JS_ASSERT(pobj->nativeContains(cx, NameToId(name)));
+        JS_ASSERT(pobj->nativeContainsNoAllocation(NameToId(name)));
 #endif
         *pobjp = pobj;
         return NULL;
     }
 
     PCMETER(vcapmisses++);
     return GetNameFromBytecode(cx, script, pc, op);
 }
--- a/js/src/jspropertycacheinlines.h
+++ b/js/src/jspropertycacheinlines.h
@@ -26,16 +26,18 @@
  * We must lock pobj on a hit in order to close races with threads that might
  * be deleting a property from its scope, or otherwise invalidating property
  * caches (on all threads) by re-generating JSObject::shape().
  */
 JS_ALWAYS_INLINE void
 js::PropertyCache::test(JSContext *cx, jsbytecode *pc, JSObject *&obj,
                         JSObject *&pobj, PropertyCacheEntry *&entry, PropertyName *&name)
 {
+    AssertRootingUnnecessary assert(cx);
+
     JS_ASSERT(this == &JS_PROPERTY_CACHE(cx));
 
     Shape *kshape = obj->lastProperty();
     entry = &table[hash(pc, kshape)];
     PCMETER(pctestentry = entry);
     PCMETER(tests++);
     JS_ASSERT(&obj != &pobj);
     if (entry->kpc == pc && entry->kshape == kshape) {
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -140,20 +140,20 @@ BaseProxyHandler::getElementIfPresent(JS
         Debug_SetValueRangeToCrashOnTouch(vp, 1);
         return true;
     }
 
     return get(cx, proxy, receiver, id, vp);
 }
 
 bool
-BaseProxyHandler::set(JSContext *cx, JSObject *proxy, JSObject *receiver_, jsid id_, bool strict,
+BaseProxyHandler::set(JSContext *cx, JSObject *proxy_, JSObject *receiver_, jsid id_, bool strict,
                       Value *vp)
 {
-    RootedObject receiver(cx, receiver_);
+    RootedObject proxy(cx, proxy_), receiver(cx, receiver_);
     RootedId id(cx, id_);
 
     JS_ASSERT(OperationInProgress(cx, proxy));
     AutoPropertyDescriptorRooter desc(cx);
     if (!getOwnPropertyDescriptor(cx, proxy, id, true, &desc))
         return false;
     /* The control-flow here differs from ::get() because of the fall-through case below. */
     if (desc.obj) {
@@ -588,42 +588,41 @@ bool
 DirectProxyHandler::iterate(JSContext *cx, JSObject *proxy, unsigned flags,
                             Value *vp)
 {
     Rooted<JSObject*> target(cx, GetProxyTargetObject(proxy));
     return GetIterator(cx, target, flags, vp);
 }
 
 static bool
-GetTrap(JSContext *cx, JSObject *handler, PropertyName *name, Value *fvalp)
+GetTrap(JSContext *cx, HandleObject handler, HandlePropertyName name, Value *fvalp)
 {
     JS_CHECK_RECURSION(cx, return false);
 
-    Rooted<PropertyName*> propname(cx, name);
-    return handler->getProperty(cx, propname, fvalp);
+    return handler->getProperty(cx, name, fvalp);
 }
 
 static bool
-GetFundamentalTrap(JSContext *cx, JSObject *handler, PropertyName *name, Value *fvalp)
+GetFundamentalTrap(JSContext *cx, HandleObject handler, HandlePropertyName name, Value *fvalp)
 {
     if (!GetTrap(cx, handler, name, fvalp))
         return false;
 
     if (!js_IsCallable(*fvalp)) {
         JSAutoByteString bytes;
         if (js_AtomToPrintableString(cx, name, &bytes))
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_FUNCTION, bytes.ptr());
         return false;
     }
 
     return true;
 }
 
 static bool
-GetDerivedTrap(JSContext *cx, JSObject *handler, PropertyName *name, Value *fvalp)
+GetDerivedTrap(JSContext *cx, HandleObject handler, HandlePropertyName name, Value *fvalp)
 {
     JS_ASSERT(name == ATOM(has) ||
               name == ATOM(hasOwn) ||
               name == ATOM(get) ||
               name == ATOM(set) ||
               name == ATOM(keys) ||
               name == ATOM(iterate));
 
@@ -632,38 +631,40 @@ GetDerivedTrap(JSContext *cx, JSObject *
 
 static bool
 Trap(JSContext *cx, HandleObject handler, HandleValue fval, unsigned argc, Value* argv, Value *rval)
 {
     return Invoke(cx, ObjectValue(*handler), fval, argc, argv, rval);
 }
 
 static bool
-Trap1(JSContext *cx, HandleObject handler, HandleValue fval, jsid id, Value *rval)
+Trap1(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, Value *rval)
 {
     JSString *str = ToString(cx, IdToValue(id));
     if (!str)
         return false;
     rval->setString(str);
     return Trap(cx, handler, fval, 1, rval, rval);
 }
 
 static bool
-Trap2(JSContext *cx, HandleObject handler, HandleValue fval, jsid id, Value v, Value *rval)
+Trap2(JSContext *cx, HandleObject handler, HandleValue fval, HandleId id, Value v_, Value *rval)
 {
+    RootedValue v(cx, v_);
     JSString *str = ToString(cx, IdToValue(id));
     if (!str)
         return false;
     rval->setString(str);
     Value argv[2] = { *rval, v };
+    AutoValueArray ava(cx, argv, 2);
     return Trap(cx, handler, fval, 2, argv, rval);
 }
 
 static bool
-ParsePropertyDescriptorObject(JSContext *cx, JSObject *obj, jsid id, const Value &v,
+ParsePropertyDescriptorObject(JSContext *cx, HandleObject obj, const Value &v,
                               PropertyDescriptor *desc)
 {
     AutoPropDescArrayRooter descs(cx);
     PropDesc *d = descs.append();
     if (!d || !d->initialize(cx, v))
         return false;
     desc->obj = obj;
     desc->value = d->hasValue() ? d->value() : UndefinedValue();
@@ -788,32 +789,32 @@ ScriptedProxyHandler::getPropertyDescrip
     RootedId id(cx, id_);
     RootedObject proxy(cx, proxy_);
     RootedObject handler(cx, GetProxyHandlerObject(cx, proxy));
     RootedValue fval(cx), value(cx);
     return GetFundamentalTrap(cx, handler, ATOM(getPropertyDescriptor), fval.address()) &&
            Trap1(cx, handler, fval, id, value.address()) &&
            ((value.get().isUndefined() && IndicatePropertyNotFound(cx, desc)) ||
             (ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), value) &&
-             ParsePropertyDescriptorObject(cx, proxy, id, value, desc)));
+             ParsePropertyDescriptorObject(cx, proxy, value, desc)));
 }
 
 bool
 ScriptedProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy_, jsid id_, bool set,
                                                PropertyDescriptor *desc)
 {
     RootedId id(cx, id_);
     RootedObject proxy(cx, proxy_);
     RootedObject handler(cx, GetProxyHandlerObject(cx, proxy));
     RootedValue fval(cx), value(cx);
     return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyDescriptor), fval.address()) &&
            Trap1(cx, handler, fval, id, value.address()) &&
            ((value.get().isUndefined() && IndicatePropertyNotFound(cx, desc)) ||
             (ReturnedValueMustNotBePrimitive(cx, proxy, ATOM(getPropertyDescriptor), value) &&
-             ParsePropertyDescriptorObject(cx, proxy, id, value, desc)));
+             ParsePropertyDescriptorObject(cx, proxy, value, desc)));
 }
 
 bool
 ScriptedProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id_,
                                      PropertyDescriptor *desc)
 {
     RootedObject handler(cx, GetProxyHandlerObject(cx, proxy));
     RootedValue fval(cx), value(cx);
@@ -829,19 +830,20 @@ ScriptedProxyHandler::getOwnPropertyName
     RootedObject handler(cx, GetProxyHandlerObject(cx, proxy));
     RootedValue fval(cx), value(cx);
     return GetFundamentalTrap(cx, handler, ATOM(getOwnPropertyNames), fval.address()) &&
            Trap(cx, handler, fval, 0, NULL, value.address()) &&
            ArrayToIdVector(cx, value, props);
 }
 
 bool
-ScriptedProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
+ScriptedProxyHandler::delete_(JSContext *cx, JSObject *proxy, jsid id_, bool *bp)
 {
     RootedObject handler(cx, GetProxyHandlerObject(cx, proxy));
+    RootedId id(cx, id_);
     RootedValue fval(cx), value(cx);
     return GetFundamentalTrap(cx, handler, ATOM(delete), fval.address()) &&
            Trap1(cx, handler, fval, id, value.address()) &&
            ValueToBool(cx, value, bp);
 }
 
 bool
 ScriptedProxyHandler::enumerate(JSContext *cx, JSObject *proxy, AutoIdVector &props)
@@ -849,74 +851,78 @@ ScriptedProxyHandler::enumerate(JSContex
     RootedObject handler(cx, GetProxyHandlerObject(cx, proxy));
     RootedValue fval(cx), value(cx);
     return GetFundamentalTrap(cx, handler, ATOM(enumerate), fval.address()) &&
            Trap(cx, handler, fval, 0, NULL, value.address()) &&
            ArrayToIdVector(cx, value, props);
 }
 
 bool
-ScriptedProxyHandler::has(JSContext *cx, JSObject *proxy_, jsid id, bool *bp)
+ScriptedProxyHandler::has(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp)
 {
     RootedObject proxy(cx, proxy_);
+    RootedId id(cx, id_);
     RootedObject handler(cx, GetProxyHandlerObject(cx, proxy));
     RootedValue fval(cx), value(cx);
     if (!GetDerivedTrap(cx, handler, ATOM(has), fval.address()))
         return false;
     if (!js_IsCallable(fval))
         return BaseProxyHandler::has(cx, proxy, id, bp);
     return Trap1(cx, handler, fval, id, value.address()) &&
            ValueToBool(cx, value, bp);
 }
 
 bool
-ScriptedProxyHandler::hasOwn(JSContext *cx, JSObject *proxy_, jsid id, bool *bp)
+ScriptedProxyHandler::hasOwn(JSContext *cx, JSObject *proxy_, jsid id_, bool *bp)
 {
     RootedObject proxy(cx, proxy_);
+    RootedId id(cx, id_);
     RootedObject handler(cx, GetProxyHandlerObject(cx, proxy));
     RootedValue fval(cx), value(cx);
     if (!GetDerivedTrap(cx, handler, ATOM(hasOwn), fval.address()))
         return false;
     if (!js_IsCallable(fval))
         return BaseProxyHandler::hasOwn(cx, proxy, id, bp);
     return Trap1(cx, handler, fval, id, value.address()) &&
            ValueToBool(cx, value, bp);
 }
 
 bool
-ScriptedProxyHandler::get(JSContext *cx, JSObject *proxy_, JSObject *receiver, jsid id_, Value *vp)
+ScriptedProxyHandler::get(JSContext *cx, JSObject *proxy_, JSObject *receiver_, jsid id_, Value *vp)
 {
     RootedId id(cx, id_);
-    RootedObject proxy(cx, proxy_);
+    RootedObject proxy(cx, proxy_), receiver(cx, receiver_);
     RootedObject handler(cx, GetProxyHandlerObject(cx, proxy));
     JSString *str = ToString(cx, IdToValue(id));
     if (!str)
         return false;
     RootedValue value(cx, StringValue(str));
     Value argv[] = { ObjectOrNullValue(receiver), value };
+    AutoValueArray ava(cx, argv, 2);
     RootedValue fval(cx);
     if (!GetDerivedTrap(cx, handler, ATOM(get), fval.address()))
         return false;
     if (!js_IsCallable(fval))
         return BaseProxyHandler::get(cx, proxy, receiver, id, vp);
     return Trap(cx, handler, fval, 2, argv, vp);
 }
 
 bool
-ScriptedProxyHandler::set(JSContext *cx, JSObject *proxy_, JSObject *receiver, jsid id_, bool strict,
+ScriptedProxyHandler::set(JSContext *cx, JSObject *proxy_, JSObject *receiver_, jsid id_, bool strict,
                           Value *vp)
 {
     RootedId id(cx, id_);
-    RootedObject proxy(cx, proxy_);
+    RootedObject proxy(cx, proxy_), receiver(cx, receiver_);
     RootedObject handler(cx, GetProxyHandlerObject(cx, proxy));
     JSString *str = ToString(cx, IdToValue(id));
     if (!str)
         return false;
     RootedValue value(cx, StringValue(str));
     Value argv[] = { ObjectOrNullValue(receiver), value, *vp };
+    AutoValueArray ava(cx, argv, 3);
     RootedValue fval(cx);
     if (!GetDerivedTrap(cx, handler, ATOM(set), fval.address()))
         return false;
     if (!js_IsCallable(fval))
         return BaseProxyHandler::set(cx, proxy, receiver, id, strict, vp);
     return Trap(cx, handler, fval, 3, argv, value.address());
 }
 
@@ -1031,22 +1037,24 @@ bool
 Proxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, PropertyDescriptor *desc)
 {
     JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
     return GetProxyHandler(proxy)->defineProperty(cx, proxy, id, desc);
 }
 
 bool
-Proxy::defineProperty(JSContext *cx, JSObject *proxy, jsid id, const Value &v)
+Proxy::defineProperty(JSContext *cx, JSObject *proxy_, jsid id_, const Value &v)
 {
     JS_CHECK_RECURSION(cx, return false);
+    RootedObject proxy(cx, proxy_);
+    RootedId id(cx, id_);
     AutoPendingProxyOperation pending(cx, proxy);
     AutoPropertyDescriptorRooter desc(cx);
-    return ParsePropertyDescriptorObject(cx, proxy, id, v, &desc) &&
+    return ParsePropertyDescriptorObject(cx, proxy, v, &desc) &&
            Proxy::defineProperty(cx, proxy, id, &desc);
 }
 
 bool
 Proxy::getOwnPropertyNames(JSContext *cx, JSObject *proxy, AutoIdVector &props)
 {
     JS_CHECK_RECURSION(cx, return false);
     AutoPendingProxyOperation pending(cx, proxy);
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -755,17 +755,17 @@ JSObject::putProperty(JSContext *cx, jsi
 
     return shape;
 }
 
 Shape *
 JSObject::changeProperty(JSContext *cx, Shape *shape, unsigned attrs, unsigned mask,
                          PropertyOp getter, StrictPropertyOp setter)
 {
-    JS_ASSERT(nativeContains(cx, *shape));
+    JS_ASSERT(nativeContainsNoAllocation(*shape));
 
     attrs |= shape->attrs & mask;
 
     /* Allow only shared (slotless) => unshared (slotful) transition. */
     JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
               !(attrs & JSPROP_SHARED));
 
     types::MarkTypePropertyConfigured(cx, this, shape->propid());
@@ -873,17 +873,17 @@ JSObject::removeProperty(JSContext *cx, 
 #ifdef DEBUG
             /*
              * Check the consistency of the table but limit the number of
              * checks not to alter significantly the complexity of the
              * delete in debug builds, see bug 534493.
              */
             Shape *aprop = self->lastProperty();
             for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
-                JS_ASSERT_IF(aprop != shape, self->nativeContains(cx, *aprop));
+                JS_ASSERT_IF(aprop != shape, self->nativeContainsNoAllocation(*aprop));
 #endif
         }
 
         /* Remove shape from its non-circular doubly linked list. */
         Shape *oldLastProp = self->lastProperty();
         shape->removeFromDictionary(self);
 
         /* Hand off table from the old to new last property. */
@@ -947,17 +947,17 @@ JSObject::rollbackProperties(JSContext *
     }
 }
 
 Shape *
 JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *newShape)
 {
     JS_ASSERT_IF(oldShape != lastProperty(),
                  inDictionaryMode() &&
-                 nativeLookup(cx, oldShape->propidRef()) == oldShape);
+                 nativeLookupNoAllocation(oldShape->propidRef()) == oldShape);
 
     JSObject *self = this;
 
     if (!inDictionaryMode()) {
         RootedObject selfRoot(cx, self);
         RootedShape newRoot(cx, newShape);
         if (!toDictionaryMode(cx))
             return NULL;
@@ -1001,22 +1001,20 @@ JSObject::replaceWithNewEquivalentShape(
 }
 
 bool
 JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape)
 {
     return generateOwnShape(cx);
 }
 
-bool
-JSObject::clearParent(JSContext *cx)
+/* static */ bool
+JSObject::clearParent(JSContext *cx, HandleObject obj)
 {
-    Rooted<JSObject*> obj(cx, this);
-    Rooted<JSObject*> newParent(cx, NULL);
-    return setParent(cx, obj, newParent);
+    return setParent(cx, obj, NullPtr());
 }
 
 /* static */ bool
 JSObject::setParent(JSContext *cx, HandleObject obj, HandleObject parent)
 {
     if (parent && !parent->setDelegate(cx))
         return false;
 
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -501,19 +501,17 @@ struct Shape : public js::gc::Cell
                                    has a double-indirect back pointer,
                                    either to the next shape's parent if not
                                    last, else to obj->shape_ */
     };
 
     static inline Shape *search(JSContext *cx, Shape *start, jsid id,
                                 Shape ***pspp, bool adding = false);
 
-#ifdef DEBUG
-    static inline Shape *searchNoAllocation(JSContext *cx, Shape *start, jsid id);
-#endif
+    static inline Shape *searchNoAllocation(Shape *start, jsid id);
 
     inline void removeFromDictionary(JSObject *obj);
     inline void insertIntoDictionary(HeapPtrShape *dictp);
 
     inline void initDictionaryShape(const StackShape &child, uint32_t nfixed,
                                     HeapPtrShape *dictp);
 
     Shape *getChildBinding(JSContext *cx, const StackShape &child);
@@ -913,17 +911,20 @@ class AutoRooterGetterSetter
     class Inner : private AutoGCRooter
     {
       public:
         Inner(JSContext *cx, uint8_t attrs,
               PropertyOp *pgetter_, StrictPropertyOp *psetter_)
             : AutoGCRooter(cx, GETTERSETTER), attrs(attrs),
               pgetter(pgetter_), psetter(psetter_),
               getterRoot(cx, pgetter_), setterRoot(cx, psetter_)
-        {}
+        {
+            JS_ASSERT_IF(attrs & JSPROP_GETTER, !IsPoisonedPtr(*pgetter));
+            JS_ASSERT_IF(attrs & JSPROP_SETTER, !IsPoisonedPtr(*psetter));
+        }
 
         friend void AutoGCRooter::trace(JSTracer *trc);
 
       private:
         uint8_t attrs;
         PropertyOp *pgetter;
         StrictPropertyOp *psetter;
         SkipRoot getterRoot, setterRoot;
@@ -1099,16 +1100,24 @@ struct StackShape
 #define SHAPE_STORE_PRESERVING_COLLISION(spp, shape)                          \
     (*(spp) = (js::Shape *) (uintptr_t(shape) | SHAPE_HAD_COLLISION(*(spp))))
 
 namespace js {
 
 inline Shape *
 Shape::search(JSContext *cx, Shape *start, jsid id, Shape ***pspp, bool adding)
 {
+#ifdef DEBUG
+    {
+        SkipRoot skip0(cx, &start);
+        SkipRoot skip1(cx, &id);
+        MaybeCheckStackRoots(cx);
+    }
+#endif
+
     if (start->inDictionary()) {
         *pspp = start->table().search(id, adding);
         return SHAPE_FETCH(*pspp);
     }
 
     *pspp = NULL;
 
     if (start->hasTable()) {
@@ -1116,17 +1125,17 @@ Shape::search(JSContext *cx, Shape *star
         return SHAPE_FETCH(spp);
     }
 
     if (start->numLinearSearches() == LINEAR_SEARCHES_MAX) {
         if (start->isBigEnoughForAShapeTable()) {
             RootedShape startRoot(cx, start);
             RootedId idRoot(cx, id);
             if (startRoot->hashify(cx)) {
-                Shape **spp = startRoot->table().search(id, adding);
+                Shape **spp = startRoot->table().search(idRoot, adding);
                 return SHAPE_FETCH(spp);
             }
             start = startRoot;
             id = idRoot;
         }
         /*
          * No table built -- there weren't enough entries, or OOM occurred.
          * Don't increment numLinearSearches, to keep hasTable() false.
@@ -1139,33 +1148,31 @@ Shape::search(JSContext *cx, Shape *star
     for (Shape *shape = start; shape; shape = shape->parent) {
         if (shape->propidRef() == id)
             return shape;
     }
 
     return NULL;
 }
 
-#ifdef DEBUG
 /* static */ inline Shape *
-Shape::searchNoAllocation(JSContext *cx, Shape *start, jsid id)
+Shape::searchNoAllocation(Shape *start, jsid id)
 {
     if (start->hasTable()) {
         Shape **spp = start->table().search(id, false);
         return SHAPE_FETCH(spp);
     }
 
     for (Shape *shape = start; shape; shape = shape->parent) {
         if (shape->propidRef() == id)
             return shape;
     }
 
     return NULL;
 }
-#endif /* DEBUG */
 
 void
 MarkNonNativePropertyFound(HandleObject obj, MutableHandleShape propp);
 
 } // namespace js
 
 #ifdef _MSC_VER
 #pragma warning(pop)
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -117,20 +117,24 @@ StackBaseShape::StackBaseShape(Shape *sh
 }
 
 inline void
 StackBaseShape::updateGetterSetter(uint8_t attrs,
                                    PropertyOp rawGetter,
                                    StrictPropertyOp rawSetter)
 {
     flags &= ~(BaseShape::HAS_GETTER_OBJECT | BaseShape::HAS_SETTER_OBJECT);
-    if ((attrs & JSPROP_GETTER) && rawGetter)
+    if ((attrs & JSPROP_GETTER) && rawGetter) {
+        JS_ASSERT(!IsPoisonedPtr(rawGetter));
         flags |= BaseShape::HAS_GETTER_OBJECT;
-    if ((attrs & JSPROP_SETTER) && rawSetter)
+    }
+    if ((attrs & JSPROP_SETTER) && rawSetter) {
+        JS_ASSERT(!IsPoisonedPtr(rawSetter));
         flags |= BaseShape::HAS_SETTER_OBJECT;
+    }
 
     this->rawGetter = rawGetter;
     this->rawSetter = rawSetter;
 }
 
 inline void
 BaseShape::adoptUnowned(UnownedBaseShape *other)
 {
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -363,17 +363,17 @@ js::XDRScript(XDRState<mode> *xdr, JSScr
 
     uint32_t length, lineno, nslots;
     uint32_t natoms, nsrcnotes, ntrynotes, nobjects, nregexps, nconsts, nClosedArgs, nClosedVars, i;
     uint32_t prologLength, version;
     uint32_t nTypeSets = 0;
     uint32_t scriptBits = 0;
 
     JSContext *cx = xdr->cx();
-    JSScript *script;
+    Rooted<JSScript*> script(cx);
     nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = nClosedArgs = nClosedVars = 0;
     jssrcnote *notes = NULL;
 
     /* XDR arguments, var vars, and upvars. */
     uint16_t nargs, nvars;
 #if defined(DEBUG) || defined(__GNUC__) /* quell GCC overwarning */
     script = NULL;
     nargs = nvars = Bindings::BINDING_COUNT_LIMIT;
@@ -567,19 +567,20 @@ js::XDRScript(XDRState<mode> *xdr, JSScr
                                   !!(scriptBits & (1 << SavedCallerFun)),
                                   /* principals = */ NULL,
                                   /* originPrincipals = */ NULL,
                                   /* compileAndGo = */ false,
                                   !!(scriptBits & (1 << NoScriptRval)),
                                   /* globalObject = */ NULL,
                                   version_,
                                   /* staticLevel = */ 0);
-        if (!script || !script->partiallyInit(cx, length, nsrcnotes, natoms, nobjects,
-                                              nregexps, ntrynotes, nconsts, nClosedArgs,
-                                              nClosedVars, nTypeSets))
+        if (!script || !JSScript::partiallyInit(cx, script,
+                                                length, nsrcnotes, natoms, nobjects,
+                                                nregexps, ntrynotes, nconsts, nClosedArgs,
+                                                nClosedVars, nTypeSets))
             return JS_FALSE;
 
         script->bindings.transfer(&bindings);
         JS_ASSERT(!script->mainOffset);
         script->mainOffset = prologLength;
         script->nfixed = uint16_t(version >> 16);
 
         /* If we know nsrcnotes, we allocated space for notes in script. */
@@ -1124,34 +1125,33 @@ AllocScriptData(JSContext *cx, size_t si
     uint8_t *data = static_cast<uint8_t *>(cx->calloc_(JS_ROUNDUP(size, sizeof(Value))));
     if (!data)
         return NULL;
 
     JS_ASSERT(size_t(data) % sizeof(Value) == 0);
     return data;
 }
 
-bool
-JSScript::partiallyInit(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
+/* static */ bool
+JSScript::partiallyInit(JSContext *cx, Handle<JSScript*> script,
+                        uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
                         uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes, uint32_t nconsts,
                         uint16_t nClosedArgs, uint16_t nClosedVars, uint32_t nTypeSets)
 {
-    JSScript *script = this;
-
     size_t size = ScriptDataSize(length, nsrcnotes, natoms, nobjects, nregexps,
                                  ntrynotes, nconsts, nClosedArgs, nClosedVars);
     script->data = AllocScriptData(cx, size);
     if (!script->data)
         return false;
 
     script->length = length;
 
     new (&script->bindings) Bindings;
 
-    uint8_t *cursor = data;
+    uint8_t *cursor = script->data;
     if (nconsts != 0) {
         script->setHasArray(CONSTS);
         cursor += sizeof(ConstArray);
     }
     if (nobjects != 0) {
         script->setHasArray(OBJECTS);
         cursor += sizeof(ObjectArray);
     }
@@ -1218,60 +1218,56 @@ JSScript::partiallyInit(JSContext *cx, u
         script->closedVars()->vector = reinterpret_cast<uint32_t *>(cursor);
         cursor += nClosedVars * sizeof(script->closedVars()->vector[0]);
     }
 
     JS_ASSERT(nTypeSets <= UINT16_MAX);
     script->nTypeSets = uint16_t(nTypeSets);
 
     script->code = (jsbytecode *)cursor;
-    JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == data + size);
+    JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == script->data + size);
 
     return true;
 }
 
-bool
-JSScript::fullyInitTrivial(JSContext *cx)
+/* static */ bool
+JSScript::fullyInitTrivial(JSContext *cx, Handle<JSScript*> script)
 {
-    JSScript *script = this;
-
-    if (!script->partiallyInit(cx, /* length = */ 1, /* nsrcnotes = */ 1, 0, 0, 0, 0, 0, 0, 0, 0))
+    if (!partiallyInit(cx, script, /* length = */ 1, /* nsrcnotes = */ 1, 0, 0, 0, 0, 0, 0, 0, 0))
         return false;
 
     script->code[0] = JSOP_STOP;
     script->notes()[0] = SRC_NULL;
 
     return true;
 }
 
-bool
-JSScript::fullyInitFromEmitter(JSContext *cx, BytecodeEmitter *bce)
+/* static */ bool
+JSScript::fullyInitFromEmitter(JSContext *cx, Handle<JSScript*> script, BytecodeEmitter *bce)
 {
-    JSScript *script = this;
-
     /* The counts of indexed things must be checked during code generation. */
     JS_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT);
     JS_ASSERT(bce->objectList.length <= INDEX_LIMIT);
     JS_ASSERT(bce->regexpList.length <= INDEX_LIMIT);
 
     uint32_t mainLength = bce->offset();
     uint32_t prologLength = bce->prologOffset();
 
     if (!bce->sc->bindings.ensureShape(cx))
         return false;
 
     uint32_t nsrcnotes = uint32_t(bce->countFinalSourceNotes());
     uint16_t nClosedArgs = uint16_t(bce->closedArgs.length());
     JS_ASSERT(nClosedArgs == bce->closedArgs.length());
     uint16_t nClosedVars = uint16_t(bce->closedVars.length());
     JS_ASSERT(nClosedVars == bce->closedVars.length());
-    if (!script->partiallyInit(cx, prologLength + mainLength, nsrcnotes, bce->atomIndices->count(),
-                               bce->objectList.length, bce->regexpList.length, bce->ntrynotes,
-                               bce->constList.length(), nClosedArgs, nClosedVars,
-                               bce->typesetCount))
+    if (!partiallyInit(cx, script, prologLength + mainLength, nsrcnotes, bce->atomIndices->count(),
+                       bce->objectList.length, bce->regexpList.length, bce->ntrynotes,
+                       bce->constList.length(), nClosedArgs, nClosedVars,
+                       bce->typesetCount))
         return false;
 
     JS_ASSERT(script->mainOffset == 0);
     script->mainOffset = prologLength;
     PodCopy<jsbytecode>(script->code, bce->prologBase(), prologLength);
     PodCopy<jsbytecode>(script->main(), bce->base(), mainLength);
     uint32_t nfixed = bce->sc->inFunction() ? bce->sc->bindings.numVars() : 0;
     JS_ASSERT(nfixed < SLOTNO_LIMIT);
@@ -1698,19 +1694,24 @@ js::CloneScript(JSContext *cx, HandleScr
         return NULL;
 
     /* Objects */
 
     AutoObjectVector objects(cx);
     if (nobjects != 0) {
         HeapPtrObject *vector = src->objects()->vector;
         for (unsigned i = 0; i < nobjects; i++) {
-            JSObject *clone = vector[i]->isStaticBlock()
-                              ? CloneStaticBlockObject(cx, vector[i]->asStaticBlock(), objects, src)
-                              : CloneInterpretedFunction(cx, vector[i]->toFunction());
+            JSObject *clone;
+            if (vector[i]->isStaticBlock()) {
+                Rooted<StaticBlockObject*> block(cx, &vector[i]->asStaticBlock());
+                clone = CloneStaticBlockObject(cx, block, objects, src);
+            } else {
+                RootedFunction fun(cx, vector[i]->toFunction());
+                clone = CloneInterpretedFunction(cx, fun);
+            }
             if (!clone || !objects.append(clone))
                 return NULL;
         }
     }
 
     /* RegExps */
 
     AutoObjectVector regexps(cx);
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -547,21 +547,22 @@ struct JSScript : public js::gc::Cell
                             bool compileAndGo, bool noScriptRval,
                             js::GlobalObject *globalObject, JSVersion version,
                             unsigned staticLevel);
 
     // Three ways ways to initialize a JSScript.  Callers of partiallyInit()
     // and fullyInitTrivial() are responsible for notifying the debugger after
     // successfully creating any kind (function or other) of new JSScript.
     // However, callers of fullyInitFromEmitter() do not need to do this.
-    bool partiallyInit(JSContext *cx, uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
-                       uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes, uint32_t nconsts,
-                       uint16_t nClosedArgs, uint16_t nClosedVars, uint32_t nTypeSets);
-    bool fullyInitTrivial(JSContext *cx);  // inits a JSOP_STOP-only script
-    bool fullyInitFromEmitter(JSContext *cx, js::BytecodeEmitter *bce);
+    static bool partiallyInit(JSContext *cx, JS::Handle<JSScript*> script,
+                              uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
+                              uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes, uint32_t nconsts,
+                              uint16_t nClosedArgs, uint16_t nClosedVars, uint32_t nTypeSets);
+    static bool fullyInitTrivial(JSContext *cx, JS::Handle<JSScript*> script);  // inits a JSOP_STOP-only script
+    static bool fullyInitFromEmitter(JSContext *cx, JS::Handle<JSScript*> script, js::BytecodeEmitter *bce);
 
     void setVersion(JSVersion v) { version = v; }
 
     /* See ContextFlags::funArgumentsHasLocalBinding comment. */
     bool argumentsHasVarBinding() const { return argsHasVarBinding_; }
     jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; }
     void setArgumentsHasVarBinding();
 
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -716,17 +716,17 @@ str_toLocaleUpperCase(JSContext *cx, uns
 
     return ToUpperCaseHelper(cx, args);
 }
 
 static JSBool
 str_localeCompare(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JSString *str = ThisToStringForStringProto(cx, args);
+    RootedString str(cx, ThisToStringForStringProto(cx, args));
     if (!str)
         return false;
 
     if (args.length() == 0) {
         args.rval() = Int32Value(0);
     } else {
         JSString *thatStr = ToString(cx, args[0]);
         if (!thatStr)
@@ -752,17 +752,17 @@ str_localeCompare(JSContext *cx, unsigne
     return true;
 }
 
 JSBool
 js_str_charAt(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    JSString *str;
+    RootedString str(cx);
     size_t i;
     if (args.thisv().isString() && args.length() != 0 && args[0].isInt32()) {
         str = args.thisv().toString();
         i = size_t(args[0].toInt32());
         if (i >= str->length())
             goto out_of_range;
     } else {
         str = ThisToStringForStringProto(cx, args);
@@ -789,17 +789,17 @@ js_str_charAt(JSContext *cx, unsigned ar
     return true;
 }
 
 JSBool
 js_str_charCodeAt(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    JSString *str;
+    RootedString str(cx);
     size_t i;
     if (args.thisv().isString() && args.length() != 0 && args[0].isInt32()) {
         str = args.thisv().toString();
         i = size_t(args[0].toInt32());
         if (i >= str->length())
             goto out_of_range;
     } else {
         str = ThisToStringForStringProto(cx, args);
@@ -1101,17 +1101,17 @@ RopeMatch(JSContext *cx, JSString *texts
     *match = -1;
     return true;
 }
 
 static JSBool
 str_indexOf(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
-    JSString *str = ThisToStringForStringProto(cx, args);
+    RootedString str(cx, ThisToStringForStringProto(cx, args));
     if (!str)
         return false;
 
     JSLinearString *patstr = ArgToRootedString(cx, args, 0);
     if (!patstr)
         return false;
 
     uint32_t textlen = str->length();
@@ -2712,17 +2712,17 @@ str_slice(JSContext *cx, unsigned argc, 
                 if (!str)
                     return false;
             }
             args.rval() = StringValue(str);
             return true;
         }
     }
 
-    JSString *str = ThisToStringForStringProto(cx, args);
+    RootedString str(cx, ThisToStringForStringProto(cx, args));
     if (!str)
         return false;
 
     if (args.length() != 0) {
         double begin, end, length;
 
         if (!ToInteger(cx, args[0], &begin))
             return false;
@@ -3279,17 +3279,17 @@ js_ValueToSource(JSContext *cx, const Va
 
     Value rval = NullValue();
     Value fval;
     RootedId id(cx, NameToId(cx->runtime->atomState.toSourceAtom));
     Rooted<JSObject*> obj(cx, &v.toObject());
     if (!GetMethod(cx, obj, id, 0, &fval))
         return NULL;
     if (js_IsCallable(fval)) {
-        if (!Invoke(cx, v, fval, 0, NULL, &rval))
+        if (!Invoke(cx, ObjectValue(*obj), fval, 0, NULL, &rval))
             return NULL;
     }
 
     return ToString(cx, rval);
 }
 
 namespace js {
 
--- a/js/src/jsstr.h
+++ b/js/src/jsstr.h
@@ -118,16 +118,23 @@ ToStringSlow(JSContext *cx, const Value 
 /*
  * Convert the given value to a string.  This method includes an inline
  * fast-path for the case where the value is already a string; if the value is
  * known not to be a string, use ToStringSlow instead.
  */
 static JS_ALWAYS_INLINE JSString *
 ToString(JSContext *cx, const js::Value &v)
 {
+#ifdef DEBUG
+    {
+        SkipRoot skip(cx, &v);
+        MaybeCheckStackRoots(cx);
+    }
+#endif
+
     if (v.isString())
         return v.toString();
     return ToStringSlow(cx, v);
 }
 
 /*
  * This function implements E-262-3 section 9.8, toString. Convert the given
  * value to a string of jschars appended to the given buffer. On error, the
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -1105,17 +1105,17 @@ class TypedArrayTemplate
             // non-primitive assignments become NaN or 0 (for float/int arrays)
             *d = js_NaN;
         }
 
         return true;
     }
 
     static bool
-    setElementTail(JSContext *cx, JSObject *tarray, uint32_t index, Value *vp, JSBool strict)
+    setElementTail(JSContext *cx, HandleObject tarray, uint32_t index, Value *vp, JSBool strict)
     {
         JS_ASSERT(tarray);
         JS_ASSERT(index < length(tarray));
 
         if (vp->isInt32()) {
             setIndex(tarray, index, NativeType(vp->toInt32()));
             return true;
         }
@@ -2337,31 +2337,30 @@ DataViewObject::class_constructor(JSCont
             return false;
         args.rval() = proxyArgs.rval();
         return true;
     }
 
     return construct(cx, bufobj, args, NULL);
 }
 
-bool
-DataViewObject::getDataPointer(JSContext *cx, CallArgs args, size_t typeSize, uint8_t **data)
+/* static */ bool
+DataViewObject::getDataPointer(JSContext *cx, Handle<DataViewObject*> obj,
+                               CallArgs args, size_t typeSize, uint8_t **data)
 {
-    JS_ASSERT(isDataView());
-
     uint32_t offset;
     JS_ASSERT(args.length() > 0);
     if (!ToUint32(cx, args[0], &offset))
         return false;
-    if (offset > UINT32_MAX - typeSize || offset + typeSize > byteLength()) {
+    if (offset > UINT32_MAX - typeSize || offset + typeSize > obj->byteLength()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ARG_INDEX_OUT_OF_RANGE, "1");
         return false;
     }
 
-    *data = static_cast<uint8_t*>(dataPointer()) + offset;
+    *data = static_cast<uint8_t*>(obj->dataPointer()) + offset;
     return true;
 }
 
 static inline bool
 needToSwapBytes(bool littleEndian)
 {
 #if IS_LITTLE_ENDIAN
     return !littleEndian;
@@ -2430,27 +2429,28 @@ struct DataViewIO
         ReadWriteType