merge m-c to fx-team
authorTim Taubert <ttaubert@mozilla.com>
Thu, 27 Sep 2012 18:58:19 +0200
changeset 108498 d206fb69f55b4ef3217344abacad66585de6f479
parent 108497 4b4d5aff92964b49ae604e1ca026116cc8f547f9 (current diff)
parent 108358 c7b8d71aa25d7136dacfcf548456f86a1dddc2dc (diff)
child 108499 f6a071d71458722174576917416953e926034bbb
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
milestone18.0a1
merge m-c to fx-team
content/base/src/nsBlobProtocolHandler.h
--- a/accessible/src/base/FocusManager.cpp
+++ b/accessible/src/base/FocusManager.cpp
@@ -109,18 +109,20 @@ FocusManager::IsInOrContainsFocus(const 
   }
 
   return eNone;
 }
 
 void
 FocusManager::NotifyOfDOMFocus(nsISupports* aTarget)
 {
-  A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET("DOM focus", "DOM focus target",
-                                              aTarget)
+#ifdef DEBUG
+  if (logging::IsEnabled(logging::eFocus))
+    logging::FocusNotificationTarget("DOM focus", "Target", aTarget);
+#endif
 
   mActiveItem = nullptr;
 
   nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
   if (targetNode) {
     DocAccessible* document =
       GetAccService()->GetDocAccessible(targetNode->OwnerDoc());
     if (document) {
@@ -135,18 +137,20 @@ FocusManager::NotifyOfDOMFocus(nsISuppor
         (this, &FocusManager::ProcessDOMFocus, targetNode);
     }
   }
 }
 
 void
 FocusManager::NotifyOfDOMBlur(nsISupports* aTarget)
 {
-  A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET("DOM blur", "DOM blur target",
-                                              aTarget)
+#ifdef DEBUG
+  if (logging::IsEnabled(logging::eFocus))
+    logging::FocusNotificationTarget("DOM blur", "Target", aTarget);
+#endif
 
   mActiveItem = nullptr;
 
   // If DOM document stays focused then fire accessible focus event to process
   // the case when no element within this DOM document will be focused.
   nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));
   if (targetNode && targetNode->OwnerDoc() == FocusedDOMDocument()) {
     nsIDocument* DOMDoc = targetNode->OwnerDoc();
@@ -157,28 +161,33 @@ FocusManager::NotifyOfDOMBlur(nsISupport
         (this, &FocusManager::ProcessDOMFocus, DOMDoc);
     }
   }
 }
 
 void
 FocusManager::ActiveItemChanged(Accessible* aItem, bool aCheckIfActive)
 {
-  A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET("active item changed",
-                                         "Active item", aItem)
+#ifdef DEBUG
+  if (logging::IsEnabled(logging::eFocus))
+    logging::FocusNotificationTarget("active item changed", "Item", aItem);
+#endif
 
   // Nothing changed, happens for XUL trees and HTML selects.
   if (aItem && aItem == mActiveItem)
     return;
 
   mActiveItem = nullptr;
 
   if (aItem && aCheckIfActive) {
     Accessible* widget = aItem->ContainerWidget();
-    A11YDEBUG_FOCUS_LOG_WIDGET("Active item widget", widget)
+#ifdef DEBUG
+    if (logging::IsEnabled(logging::eFocus))
+      logging::ActiveWidget(widget);
+#endif
     if (!widget || !widget->IsActiveWidget() || !widget->AreItemsOperable())
       return;
   }
   mActiveItem = aItem;
 
   // If active item is changed then fire accessible focus event on it, otherwise
   // if there's no an active item then fire focus event to accessible having
   // DOM focus.
@@ -207,25 +216,30 @@ FocusManager::DispatchFocusEvent(DocAcce
 {
   NS_PRECONDITION(aDocument, "No document for focused accessible!");
   if (aDocument) {
     nsRefPtr<AccEvent> event =
       new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, aTarget,
                    eAutoDetect, AccEvent::eCoalesceOfSameType);
     aDocument->FireDelayedAccessibleEvent(event);
 
-    A11YDEBUG_FOCUS_LOG_ACCTARGET("Focus notification", aTarget)
+#ifdef DEBUG
+    if (logging::IsEnabled(logging::eFocus))
+      logging::FocusDispatched(aTarget);
+#endif
   }
 }
 
 void
 FocusManager::ProcessDOMFocus(nsINode* aTarget)
 {
-  A11YDEBUG_FOCUS_NOTIFICATION_DOMTARGET("Process DOM focus",
-                                         "Notification target", aTarget)
+#ifdef DEBUG
+  if (logging::IsEnabled(logging::eFocus))
+    logging::FocusNotificationTarget("process DOM focus", "Target", aTarget);
+#endif
 
   DocAccessible* document =
     GetAccService()->GetDocAccessible(aTarget->OwnerDoc());
 
   Accessible* target = document->GetAccessibleOrContainer(aTarget);
   if (target && document) {
     // Check if still focused. Otherwise we can end up with storing the active
     // item for control that isn't focused anymore.
@@ -303,18 +317,20 @@ FocusManager::ProcessFocusEvent(AccEvent
     nsRefPtr<AccEvent> menuEndEvent =
       new AccEvent(nsIAccessibleEvent::EVENT_MENU_END, mActiveARIAMenubar,
                    fromUserInputFlag);
     nsEventShell::FireEvent(menuEndEvent);
 
     mActiveARIAMenubar = nullptr;
   }
 
-  A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET("FIRE FOCUS EVENT", "Focus target",
-                                         target)
+#ifdef DEBUG
+  if (logging::IsEnabled(logging::eFocus))
+    logging::FocusNotificationTarget("fire focus event", "Target", target);
+#endif
 
   nsRefPtr<AccEvent> focusEvent =
     new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, fromUserInputFlag);
   nsEventShell::FireEvent(focusEvent);
 
   // Fire scrolling_start event when the document receives the focus if it has
   // an anchor jump. If an accessible within the document receive the focus
   // then null out the anchor jump because it no longer applies.
--- a/accessible/src/base/FocusManager.h
+++ b/accessible/src/base/FocusManager.h
@@ -123,131 +123,9 @@ private:
 private:
   nsRefPtr<Accessible> mActiveItem;
   nsRefPtr<Accessible> mActiveARIAMenubar;
 };
 
 } // namespace a11y
 } // namespace mozilla
 
-
-//#define A11YDEBUG_FOCUS
-
-#ifdef A11YDEBUG_FOCUS
-
-// Util macros (don't use them directly)
-#define A11YDEBUG_FOCUS_STARTBLOCK                                             \
-  printf("  {\n    ");
-
-#define A11YDEBUG_FOCUS_ENDBLOCK                                               \
-  printf("\n  }\n");
-
-#define A11YDEBUG_FOCUS_BLOCKOFFSET                                            \
-  printf("    ");
-
-#define A11YDEBUG_FOCUS_LOG_TIME                                               \
-  {                                                                            \
-    PRIntervalTime time = PR_IntervalNow();                                    \
-    uint32_t mins = (PR_IntervalToSeconds(time) / 60) % 60;                    \
-    uint32_t secs = PR_IntervalToSeconds(time) % 60;                           \
-    uint32_t msecs = PR_IntervalToMilliseconds(time) % 1000;                   \
-    printf("Time: %2d:%2d.%3d\n", mins, secs, msecs);                          \
-  }
-
-#define A11YDEBUG_FOCUS_LOG_DOMNODE(aNode)                                     \
-  if (aNode) {                                                                 \
-    if (aNode->IsElement()) {                                                  \
-      dom::Element* targetElm = aNode->AsElement();                            \
-      nsAutoCString tag;                                                       \
-      targetElm->Tag()->ToUTF8String(tag);                                     \
-      nsAutoCString id;                                                        \
-      nsIAtom* atomid = targetElm->GetID();                                    \
-      if (atomid)                                                              \
-        atomid->ToUTF8String(id);                                              \
-      printf("element %s@id='%s': %p", tag.get(), id.get(), (void*)aNode);     \
-    } else if (aNode->IsNodeOfType(nsINode::eDOCUMENT)) {                      \
-      nsCOMPtr<nsIDocument> document = do_QueryInterface(aNode);               \
-      nsIURI* uri = document->GetDocumentURI();                                \
-      nsAutoCString spec;                                                      \
-      uri->GetSpec(spec);                                                      \
-      printf("document: %p; uri: %s", (void*)aNode, spec.get());               \
-    }                                                                          \
-  }
-
-#define A11YDEBUG_FOCUS_LOG_ACCESSIBLE(aAccessible)                            \
-  printf("accessible: %p; ", (void*)aAccessible);                              \
-  if (aAccessible) {                                                           \
-    nsAutoString role;                                                         \
-    GetAccService()->GetStringRole(aAccessible->Role(), role);                 \
-    nsAutoString name;                                                         \
-    aAccessible->Name(name);                                                   \
-    printf(" role: %s, name: %s; ", NS_ConvertUTF16toUTF8(role).get(),         \
-           NS_ConvertUTF16toUTF8(name).get());                                 \
-    A11YDEBUG_FOCUS_LOG_DOMNODE(aAccessible->GetNode())                        \
-  }
-
-// Public macros
-#define A11YDEBUG_FOCUS_LOG_DOMTARGET(aMsg, aTarget)                           \
-  A11YDEBUG_FOCUS_STARTBLOCK                                                   \
-  printf(aMsg "\n");                                                           \
-  if (aTarget) {                                                               \
-    A11YDEBUG_FOCUS_BLOCKOFFSET                                                \
-    A11YDEBUG_FOCUS_LOG_DOMNODE(aTarget)                                       \
-  }                                                                            \
-  A11YDEBUG_FOCUS_ENDBLOCK
-
-#define A11YDEBUG_FOCUS_LOG_ACCTARGET(aMsg, aTarget)                           \
-  A11YDEBUG_FOCUS_STARTBLOCK                                                   \
-  printf(aMsg "\n");                                                           \
-  A11YDEBUG_FOCUS_BLOCKOFFSET                                                  \
-  A11YDEBUG_FOCUS_LOG_ACCESSIBLE(aTarget)                                      \
-  A11YDEBUG_FOCUS_ENDBLOCK
-
-#define A11YDEBUG_FOCUS_LOG_WIDGET(aMsg, aWidget)                              \
-  A11YDEBUG_FOCUS_STARTBLOCK                                                   \
-  printf(aMsg "\n");                                                           \
-  A11YDEBUG_FOCUS_BLOCKOFFSET                                                  \
-  A11YDEBUG_FOCUS_LOG_ACCESSIBLE(aWidget)                                      \
-  printf("; widget is active: %s, has operable items: %s",                     \
-         (aWidget && aWidget->IsActiveWidget() ? "true" : "false"),            \
-         (aWidget && aWidget->AreItemsOperable() ? "true" : "false"));         \
-  A11YDEBUG_FOCUS_ENDBLOCK
-
-#define A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET(aMsg, aTargetMsg, aTarget) \
-  printf("\nA11Y FOCUS: " aMsg ". ");                                          \
-  A11YDEBUG_FOCUS_LOG_TIME                                                     \
-  if (aTarget) {                                                               \
-    A11YDEBUG_FOCUS_STARTBLOCK                                                 \
-    printf(aTargetMsg "\n");                                                   \
-    A11YDEBUG_FOCUS_BLOCKOFFSET                                                \
-    nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTarget));                  \
-    if (targetNode) {                                                          \
-      A11YDEBUG_FOCUS_LOG_DOMNODE(targetNode)                                  \
-    } else {                                                                   \
-      printf("window: %p", (void*)aTarget);                                    \
-    }                                                                          \
-    A11YDEBUG_FOCUS_ENDBLOCK                                                   \
-  }
-
-#define A11YDEBUG_FOCUS_NOTIFICATION_DOMTARGET(aMsg, aTargetMsg, aTarget)      \
-  printf("\nA11Y FOCUS: " aMsg ". ");                                          \
-  A11YDEBUG_FOCUS_LOG_TIME                                                     \
-  A11YDEBUG_FOCUS_LOG_DOMTARGET(aTargetMsg, aTarget)
-
-#define A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET(aMsg, aTargetMsg, aTarget)      \
-  printf("\nA11Y FOCUS: " aMsg ". ");                                          \
-  A11YDEBUG_FOCUS_LOG_TIME                                                     \
-  A11YDEBUG_FOCUS_LOG_ACCTARGET(aTargetMsg, aTarget)
-
-#define A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE(aMsg, aTarget)                  \
-  A11YDEBUG_FOCUS_LOG_ACCTARGET("Caused by: " aMsg, aTarget)
-
-#else
-#define A11YDEBUG_FOCUS_LOG_DOMTARGET(aMsg, aTarget)
-#define A11YDEBUG_FOCUS_LOG_ACCTARGET(aMsg, aTarget)
-#define A11YDEBUG_FOCUS_LOG_WIDGET(aMsg, aWidget)
-#define A11YDEBUG_FOCUS_NOTIFICATION_SUPPORTSTARGET(aMsg, aTargetMsg, aTarget)
-#define A11YDEBUG_FOCUS_NOTIFICATION_DOMTARGET(aMsg, aTargetMsg, aTarget)
-#define A11YDEBUG_FOCUS_NOTIFICATION_ACCTARGET(aMsg, aTargetMsg, aTarget)
-#define A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE(aMsg, aTarget)
 #endif
-
-#endif
--- a/accessible/src/base/Logging.cpp
+++ b/accessible/src/base/Logging.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Logging.h"
 
+#include "Accessible-inl.h"
 #include "AccEvent.h"
 #include "DocAccessible.h"
 #include "nsAccessibilityService.h"
 #include "nsCoreUtils.h"
 #include "OuterDocAccessible.h"
 
 #include "nsDocShellLoadTypes.h"
 #include "nsIChannel.h"
@@ -345,16 +346,17 @@ GetDocLoadEventType(AccEvent* aEvent, ns
 
 ////////////////////////////////////////////////////////////////////////////////
 // namespace logging:: document life cycle logging methods
 
 static const char* sDocLoadTitle = "DOCLOAD";
 static const char* sDocCreateTitle = "DOCCREATE";
 static const char* sDocDestroyTitle = "DOCDESTROY";
 static const char* sDocEventTitle = "DOCEVENT";
+static const char* sFocusTitle = "FOCUS";
 
 void
 logging::DocLoad(const char* aMsg, nsIWebProgress* aWebProgress,
                  nsIRequest* aRequest, uint32_t aStateFlags)
 {
   MsgBegin(sDocLoadTitle, aMsg);
 
   nsCOMPtr<nsIDOMWindow> DOMWindow;
@@ -461,16 +463,82 @@ void
 logging::OuterDocDestroy(OuterDocAccessible* aOuterDoc)
 {
   MsgBegin(sDocDestroyTitle, "outerdoc shutdown");
   logging::Address("outerdoc", aOuterDoc);
   MsgEnd();
 }
 
 void
+logging::FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
+                                 Accessible* aTarget)
+{
+  MsgBegin(sFocusTitle, aMsg);
+  AccessibleNNode(aTargetDescr, aTarget);
+  MsgEnd();
+}
+
+void
+logging::FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
+                                 nsINode* aTargetNode)
+{
+  MsgBegin(sFocusTitle, aMsg);
+  Node(aTargetDescr, aTargetNode);
+  MsgEnd();
+}
+
+void
+logging::FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
+                                 nsISupports* aTargetThing)
+{
+  MsgBegin(sFocusTitle, aMsg);
+
+  if (aTargetThing) {
+    nsCOMPtr<nsINode> targetNode(do_QueryInterface(aTargetThing));
+    if (targetNode)
+      Node(aTargetDescr, targetNode);
+    else
+      printf("    %s: %p, window\n", aTargetDescr,
+             static_cast<void*>(aTargetThing));
+  }
+
+  MsgEnd();
+}
+
+void
+logging::ActiveItemChangeCausedBy(const char* aCause, Accessible* aTarget)
+{
+  SubMsgBegin();
+  printf("    Caused by: %s\n", aCause);
+  AccessibleNNode("Item", aTarget);
+  SubMsgEnd();
+}
+
+void
+logging::ActiveWidget(Accessible* aWidget)
+{
+  SubMsgBegin();
+
+  AccessibleNNode("Widget", aWidget);
+  printf("    Widget is active: %s, has operable items: %s\n",
+         (aWidget && aWidget->IsActiveWidget() ? "true" : "false"),
+         (aWidget && aWidget->AreItemsOperable() ? "true" : "false"));
+
+  SubMsgEnd();
+}
+
+void
+logging::FocusDispatched(Accessible* aTarget)
+{
+  SubMsgBegin();
+  AccessibleNNode("A11y target", aTarget);
+  SubMsgEnd();
+}
+
+void
 logging::SelChange(nsISelection* aSelection, DocAccessible* aDocument)
 {
   nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(aSelection));
 
   int16_t type = 0;
   privSel->GetType(&type);
 
   const char* strType = 0;
@@ -491,26 +559,44 @@ logging::MsgBegin(const char* aTitle, co
 {
   printf("\nA11Y %s: ", aTitle);
 
   va_list argptr;
   va_start(argptr, aMsgText);
   vprintf(aMsgText, argptr);
   va_end(argptr);
 
+  PRIntervalTime time = PR_IntervalNow();
+  uint32_t mins = (PR_IntervalToSeconds(time) / 60) % 60;
+  uint32_t secs = PR_IntervalToSeconds(time) % 60;
+  uint32_t msecs = PR_IntervalToMilliseconds(time) % 1000;
+  printf("; %02d:%02d.%03d", mins, secs, msecs);
+
   printf("\n  {\n");
 }
 
 void
 logging::MsgEnd()
 {
   printf("  }\n");
 }
 
 void
+logging::SubMsgBegin()
+{
+  printf("  {\n");
+}
+
+void
+logging::SubMsgEnd()
+{
+  printf("  }\n");
+}
+
+void
 logging::MsgEntry(const char* aEntryText, ...)
 {
   printf("    ");
 
   va_list argptr;
   va_start(argptr, aEntryText);
   vprintf(aEntryText, argptr);
   va_end(argptr);
@@ -582,16 +668,44 @@ logging::Node(const char* aDescr, nsINod
   if (idAtom)
     idAtom->ToUTF8String(id);
 
   printf("%s: %p, %s@id='%s', idx in parent: %d\n",
          aDescr, static_cast<void*>(elm), tag.get(), id.get(), idxInParent);
 }
 
 void
+logging::AccessibleNNode(const char* aDescr, Accessible* aAccessible)
+{
+  printf("    %s: %p; ", aDescr, static_cast<void*>(aAccessible));
+  if (!aAccessible)
+    return;
+
+  nsAutoString role;
+  GetAccService()->GetStringRole(aAccessible->Role(), role);
+  nsAutoString name;
+  aAccessible->Name(name);
+
+  printf("role: %s, name: '%s';\n", NS_ConvertUTF16toUTF8(role).get(),
+         NS_ConvertUTF16toUTF8(name).get());
+
+  nsAutoCString nodeDescr(aDescr);
+  nodeDescr.AppendLiteral(" node");
+  Node(nodeDescr.get(), aAccessible->GetNode());
+
+  printf("    Document: %p, document node: %p\n",
+         static_cast<void*>(aAccessible->Document()),
+         static_cast<void*>(aAccessible->GetDocumentNode()));
+
+  printf("    Document");
+  LogDocURI(static_cast<nsIDocument*>(aAccessible->GetDocumentNode()));
+  printf("\n");
+}
+
+void
 logging::Stack()
 {
   if (IsEnabled(eStack)) {
     printf("  stack: \n");
     nsTraceRefcntImpl::WalkTheStack(stdout);
   }
 }
 
--- a/accessible/src/base/Logging.h
+++ b/accessible/src/base/Logging.h
@@ -80,29 +80,61 @@ void DocDestroy(const char* aMsg, nsIDoc
                 DocAccessible* aDocument = nullptr);
 
 /**
  * Log the outer document was destroyed.
  */
 void OuterDocDestroy(OuterDocAccessible* OuterDoc);
 
 /**
+ * Log the focus notification target.
+ */
+void FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
+                             Accessible* aTarget);
+void FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
+                             nsINode* aTargetNode);
+void FocusNotificationTarget(const char* aMsg, const char* aTargetDescr,
+                             nsISupports* aTargetThing);
+
+/**
+ * Log a cause of active item descendant change (submessage).
+ */
+void ActiveItemChangeCausedBy(const char* aMsg, Accessible* aTarget);
+
+/**
+ * Log the active widget (submessage).
+ */
+void ActiveWidget(Accessible* aWidget);
+
+/**
+ * Log the focus event was dispatched (submessage).
+ */
+void FocusDispatched(Accessible* aTarget);
+
+/**
  * Log the selection change.
  */
 void SelChange(nsISelection* aSelection, DocAccessible* aDocument);
 
 /**
  * Log the message ('title: text' format) on new line. Print the start and end
  * boundaries of the message body designated by '{' and '}' (2 spaces indent for
  * body).
  */
 void MsgBegin(const char* aTitle, const char* aMsgText, ...);
 void MsgEnd();
 
 /**
+ * Print start and end boundaries of the message body designated by '{' and '}'
+ * (2 spaces indent for body).
+ */
+void SubMsgBegin();
+void SubMsgEnd();
+
+/**
  * Log the entry into message body (4 spaces indent).
  */
 void MsgEntry(const char* aEntryText, ...);
 
 /**
  * Log the text, two spaces offset is used.
  */
 void Text(const char* aText);
@@ -113,16 +145,21 @@ void Text(const char* aText);
 void Address(const char* aDescr, Accessible* aAcc);
 
 /**
  * Log the DOM node info as message entry.
  */
 void Node(const char* aDescr, nsINode* aNode);
 
 /**
+ * Log the accessible and its DOM node as a message entry.
+ */
+void AccessibleNNode(const char* aDescr, Accessible* aAccessible);
+
+/**
  * Log the call stack, two spaces offset is used.
  */
 void Stack();
 
 /**
  * Enable logging of the specified modules, all other modules aren't logged.
  */
 void Enable(const nsAFlatCString& aModules);
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -612,27 +612,43 @@ Accessible::VisibilityState()
     return states::INVISIBLE;
 
   // Walk the parent frame chain to see if there's invisible parent or the frame
   // is in background tab.
   if (!frame->GetStyleVisibility()->IsVisible())
     return states::INVISIBLE;
 
   nsIFrame* curFrame = frame;
+  nsPoint framePos(0, 0);
   do {
     nsIView* view = curFrame->GetView();
     if (view && view->GetVisibility() == nsViewVisibility_kHide)
       return states::INVISIBLE;
 
     // Offscreen state for background tab content.
     nsIFrame* parentFrame = curFrame->GetParent();
     nsDeckFrame* deckFrame = do_QueryFrame(parentFrame);
     if (deckFrame && deckFrame->GetSelectedBox() != curFrame)
       return states::OFFSCREEN;
 
+    // If contained by scrollable frame then check that at least 12 pixels
+    // around the object is visible, otherwise the object is offscreen.
+    framePos += curFrame->GetPosition();
+    nsIScrollableFrame* scrollableFrame = do_QueryFrame(parentFrame);
+    if (scrollableFrame) {
+      nsRect scrollPortRect = scrollableFrame->GetScrollPortRect();
+      nsRect frameRect(framePos, frame->GetSize());
+      if (!scrollPortRect.Contains(frameRect)) {
+        const nscoord kMinPixels = nsPresContext::CSSPixelsToAppUnits(12);
+        scrollPortRect.Deflate(kMinPixels, kMinPixels);
+        if (!scrollPortRect.Intersects(frameRect))
+          return states::OFFSCREEN;
+      }
+    }
+
     if (!parentFrame) {
       parentFrame = nsLayoutUtils::GetCrossDocParentFrame(curFrame);
       if (parentFrame && !parentFrame->GetStyleVisibility()->IsVisible())
         return states::INVISIBLE;
     }
 
     curFrame = parentFrame;
   } while (curFrame);
@@ -646,27 +662,16 @@ Accessible::VisibilityState()
       !(frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
       frame->GetRect().IsEmpty()) {
     nsAutoString renderedText;
     frame->GetRenderedText(&renderedText, nullptr, nullptr, 0, 1);
     if (renderedText.IsEmpty())
       return states::INVISIBLE;
   }
 
-  // We need to know if at least a kMinPixels around the object is visible,
-  // otherwise it will be marked states::OFFSCREEN.
-  const uint16_t kMinPixels  = 12;
-  const nsSize frameSize = frame->GetSize();
-  const nsRectVisibility rectVisibility =
-    mDoc->PresShell()->GetRectVisibility(frame, nsRect(nsPoint(0,0), frameSize),
-                                         nsPresContext::CSSPixelsToAppUnits(kMinPixels));
-
-  if (rectVisibility != nsRectVisibility_kVisible)
-    return states::OFFSCREEN;
-
   return 0;
 }
 
 uint64_t
 Accessible::NativeState()
 {
   uint64_t state = 0;
 
--- a/accessible/src/generic/DocAccessible.cpp
+++ b/accessible/src/generic/DocAccessible.cpp
@@ -1225,18 +1225,21 @@ DocAccessible::ARIAActiveDescendantChang
   if (widget && widget->IsActiveWidget()) {
     nsAutoString id;
     if (aElm->GetAttr(kNameSpaceID_None, nsGkAtoms::aria_activedescendant, id)) {
       dom::Element* activeDescendantElm = aElm->OwnerDoc()->GetElementById(id);
       if (activeDescendantElm) {
         Accessible* activeDescendant = GetAccessible(activeDescendantElm);
         if (activeDescendant) {
           FocusMgr()->ActiveItemChanged(activeDescendant, false);
-          A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("ARIA activedescedant changed",
-                                                 activeDescendant)
+#ifdef DEBUG
+          if (logging::IsEnabled(logging::eFocus))
+            logging::ActiveItemChangeCausedBy("ARIA activedescedant changed",
+                                              activeDescendant);
+#endif
         }
       }
     }
   }
 }
 
 void
 DocAccessible::ContentAppended(nsIDocument* aDocument,
@@ -1408,17 +1411,20 @@ DocAccessible::UnbindFromDocument(Access
 {
   NS_ASSERTION(mAccessibleCache.GetWeak(aAccessible->UniqueID()),
                "Unbinding the unbound accessible!");
 
   // Fire focus event on accessible having DOM focus if active item was removed
   // from the tree.
   if (FocusMgr()->IsActiveItem(aAccessible)) {
     FocusMgr()->ActiveItemChanged(nullptr);
-    A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("tree shutdown", aAccessible)
+#ifdef DEBUG
+          if (logging::IsEnabled(logging::eFocus))
+            logging::ActiveItemChangeCausedBy("tree shutdown", aAccessible);
+#endif
   }
 
   // Remove an accessible from node-to-accessible map if it exists there.
   if (aAccessible->IsPrimaryForNode() &&
       mNodeToAccessibleMap.Get(aAccessible->GetNode()) == aAccessible)
     mNodeToAccessibleMap.Remove(aAccessible->GetNode());
 
   void* uniqueID = aAccessible->UniqueID();
--- a/accessible/src/generic/RootAccessible.cpp
+++ b/accessible/src/generic/RootAccessible.cpp
@@ -337,17 +337,20 @@ RootAccessible::ProcessDOMEvent(nsIDOMEv
     bool isEnabled = (state & (states::CHECKED | states::SELECTED)) != 0;
 
     nsRefPtr<AccEvent> accEvent =
       new AccStateChangeEvent(accessible, states::CHECKED, isEnabled);
     nsEventShell::FireEvent(accEvent);
 
     if (isEnabled) {
       FocusMgr()->ActiveItemChanged(accessible);
-      A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("RadioStateChange", accessible)
+#ifdef DEBUG
+      if (logging::IsEnabled(logging::eFocus))
+        logging::ActiveItemChangeCausedBy("RadioStateChange", accessible);
+#endif
     }
 
     return;
   }
 
   if (eventType.EqualsLiteral("CheckboxStateChange")) {
     uint64_t state = accessible->State();
 
@@ -416,52 +419,64 @@ RootAccessible::ProcessDOMEvent(nsIDOMEv
   else if (eventType.EqualsLiteral("DOMMenuInactive")) {
     if (accessible->Role() == roles::MENUPOPUP) {
       nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
                               accessible);
     }
   }
   else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
     FocusMgr()->ActiveItemChanged(accessible);
-    A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuItemActive", accessible)
+#ifdef DEBUG
+    if (logging::IsEnabled(logging::eFocus))
+      logging::ActiveItemChangeCausedBy("DOMMenuItemActive", accessible);
+#endif
   }
   else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
     // Process DOMMenuItemInactive event for autocomplete only because this is
     // unique widget that may acquire focus from autocomplete popup while popup
     // stays open and has no active item. In case of XUL tree autocomplete
     // popup this event is fired for tree accessible.
     Accessible* widget =
       accessible->IsWidget() ? accessible : accessible->ContainerWidget();
     if (widget && widget->IsAutoCompletePopup()) {
       FocusMgr()->ActiveItemChanged(nullptr);
-      A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuItemInactive", accessible)
+#ifdef DEBUG
+      if (logging::IsEnabled(logging::eFocus))
+        logging::ActiveItemChangeCausedBy("DOMMenuItemInactive", accessible);
+#endif
     }
   }
   else if (eventType.EqualsLiteral("DOMMenuBarActive")) {  // Always from user input
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
                             accessible, eFromUserInput);
 
     // Notify of active item change when menubar gets active and if it has
     // current item. This is a case of mouseover (set current menuitem) and
     // mouse click (activate the menubar). If menubar doesn't have current item
     // (can be a case of menubar activation from keyboard) then ignore this
     // notification because later we'll receive DOMMenuItemActive event after
     // current menuitem is set.
     Accessible* activeItem = accessible->CurrentItem();
     if (activeItem) {
       FocusMgr()->ActiveItemChanged(activeItem);
-      A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuBarActive", accessible)
+#ifdef DEBUG
+      if (logging::IsEnabled(logging::eFocus))
+        logging::ActiveItemChangeCausedBy("DOMMenuBarActive", accessible);
+#endif
     }
   }
   else if (eventType.EqualsLiteral("DOMMenuBarInactive")) {  // Always from user input
     nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
                             accessible, eFromUserInput);
 
     FocusMgr()->ActiveItemChanged(nullptr);
-    A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("DOMMenuBarInactive", accessible)
+#ifdef DEBUG
+    if (logging::IsEnabled(logging::eFocus))
+      logging::ActiveItemChangeCausedBy("DOMMenuBarInactive", accessible);
+#endif
   }
   else if (eventType.EqualsLiteral("ValueChange")) {
     targetDocument->
       FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_VALUE_CHANGE,
                                  targetNode, AccEvent::eRemoveDupes);
   }
 #ifdef DEBUG_DRAGDROPSTART
   else if (eventType.EqualsLiteral("mouseover")) {
@@ -642,17 +657,20 @@ RootAccessible::HandlePopupHidingEvent(n
     // For menubars processing we listen DOMMenubarActive/Inactive
     // notifications.
     notifyOf = kNotifyOfFocus;
   }
 
   // Restore focus to where it was.
   if (notifyOf & kNotifyOfFocus) {
     FocusMgr()->ActiveItemChanged(nullptr);
-    A11YDEBUG_FOCUS_ACTIVEITEMCHANGE_CAUSE("popuphiding", popup)
+#ifdef DEBUG
+    if (logging::IsEnabled(logging::eFocus))
+      logging::ActiveItemChangeCausedBy("popuphiding", popup);
+#endif
   }
 
   // Fire expanded state change event.
   if (notifyOf & kNotifyOfState) {
     nsRefPtr<AccEvent> event =
       new AccStateChangeEvent(widget, states::EXPANDED, false);
     document->FireDelayedAccessibleEvent(event);
   }
--- a/accessible/tests/mochitest/events/test_focus_contextmenu.xul
+++ b/accessible/tests/mochitest/events/test_focus_contextmenu.xul
@@ -45,16 +45,19 @@
       gQueue.push(new synthContextMenu("button",
                                        new invokerChecker(EVENT_MENUPOPUP_START, "contextmenu")));
       gQueue.push(new synthDownKey("contextmenu", new focusChecker("item1")));
       gQueue.push(new synthDownKey("item1", new focusChecker("item2")));
       gQueue.push(new synthRightKey("item2", new focusChecker("item2.1")));
       gQueue.push(new synthEscapeKey("item2.1", new focusChecker("item2")));
       gQueue.push(new synthEscapeKey("item2", new focusChecker("button")));
 
+      enableLogging("focus");
+      gQueue.onFinish = function() { disableLogging(); }
+
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 
   <hbox flex="1" style="overflow: auto;">
--- a/accessible/tests/mochitest/events/test_focus_menu.xul
+++ b/accessible/tests/mochitest/events/test_focus_menu.xul
@@ -66,16 +66,19 @@
       // move to first menu in cycle, DOMMenuItemActive is fired for fruit,
       // cycle and apple menuitems (bug 685191)
       todo(false, "focus is fired for 'cycle' menuitem");
       //gQueue.push(new synthRightKey("vehicle", new focusChecker("apple")));
 
       // click menuitem to close menu, focus gets back to document
       gQueue.push(new synthClick("tricycle", new focusChecker(document)));
 
+      enableLogging("focus");
+      gQueue.onFinish = function() { disableLogging(); }
+
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTests);
   </script>
 
   <hbox flex="1" style="overflow: auto;">
--- a/accessible/tests/mochitest/states/test_visibility.html
+++ b/accessible/tests/mochitest/states/test_visibility.html
@@ -17,71 +17,142 @@
   <script type="application/javascript"
           src="../states.js"></script>
   <script type="application/javascript"
           src="../events.js"></script>
   <script type="application/javascript"
           src="../browser.js"></script>
 
   <script type="application/javascript">
-    function addTab(aURL)
+    ////////////////////////////////////////////////////////////////////////////
+    // Invokers
+
+    function loadURIInvoker(aURI, aFunc)
+    {
+      this.invoke = function loadURIInvoker_invoke()
+      {
+        tabBrowser().loadURI(aURI);
+      }
+
+      this.eventSeq = [
+        new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument)
+      ];
+
+      this.finalCheck = function loadURIInvoker_finalCheck()
+      {
+        aFunc.call();
+      }
+
+      this.getID = function loadURIInvoker_getID()
+      {
+        return "load uri " + aURI;
+      }
+    }
+
+    function addTabInvoker(aURL, aFunc)
     {
       this.eventSeq = [
         new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, tabDocumentAt, 1)
       ];
 
-      this.invoke = function addTab_invoke()
+      this.invoke = function addTabInvoker_invoke()
       {
         tabBrowser().loadOneTab(aURL, null, "", null, false);
       }
 
-      this.finalCheck = function addTab_finalCheck()
+      this.finalCheck = function addTabInvoker_finalCheck()
       {
-        var tabDoc = tabDocumentAt(0);
-        var input = getAccessible(tabDoc.getElementById("input"));
-        testStates(input, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+        aFunc.call();
       }
 
-      this.getID = function addTab_getID()
+      this.getID = function addTabInvoker_getID()
       {
         return "add tab: " + aURL;
       }
     }
 
-    var gInputDocURI = "data:text/html,<html><input id='input'></html>";
+    ////////////////////////////////////////////////////////////////////////////
+    // Tests
+
+    function testBackgroundTab()
+    {
+      // Accessibles in background tab should have offscreen state and no
+      // invisible state.
+      var tabDoc = tabDocumentAt(0);
+      var input = getAccessible(tabDoc.getElementById("input"));
+      testStates(input, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+    }
+
+    function testScrolledOff()
+    {
+      var tabDoc = tabDocumentAt(1);
+
+      // scrolled off
+      input = getAccessible(tabDoc.getElementById("input_scrolledoff"));
+      testStates(input, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+
+      // scrolled off item (twice)
+      var lastLiNode = tabDoc.getElementById("li_last");
+      var lastLi = getAccessible(lastLiNode);
+      testStates(lastLi, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+
+      // scroll into view the item
+      lastLiNode.scrollIntoView(true);
+      testStates(lastLi, 0, 0, STATE_OFFSCREEN | STATE_INVISIBLE);
+
+      // first item is scrolled off now (testcase for bug 768786)
+      var firstLi = getAccessible(tabDoc.getElementById("li_first"));
+      testStates(firstLi, STATE_OFFSCREEN, 0, STATE_INVISIBLE);
+    }
+
+    var gInputDocURI = "data:text/html,<html><body>";
+    gInputDocURI += "<input id='input'></body></html>";
+
+    var gDocURI = "data:text/html,<html><body>";
+    gDocURI += "<div style='border:2px solid blue; width: 500px; height: 600px;'></div>";
+    gDocURI += "<input id='input_scrolledoff'>";
+    gDocURI += "<ul style='border:2px solid red; width: 100px; height: 50px; overflow: auto;'>";
+    gDocURI += "  <li id='li_first'>item1</li><li>item2</li><li>item3</li>";
+    gDocURI += "  <li>item4</li><li>item5</li><li id='li_last'>item6</li>";
+    gDocURI += "</ul>";
+    gDocURI += "</body></html>";
 
     function doTests()
     {
       testStates("div", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
       testStates("div_off", STATE_OFFSCREEN, 0, STATE_INVISIBLE);
       testStates("div_abschild", 0, 0, STATE_INVISIBLE | STATE_OFFSCREEN);
 
       gQueue = new eventQueue();
 
-      // Accessibles in background tab should have offscreen state and no
-      // invisible state.
-      gQueue.push(new addTab("about:blank"));
+      gQueue.push(new addTabInvoker("about:blank", testBackgroundTab));
+      gQueue.push(new loadURIInvoker(gDocURI, testScrolledOff));
 
       gQueue.onFinish = function() { closeBrowserWindow(); }
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
-    openBrowserWindow(doTests, gInputDocURI);
+    openBrowserWindow(doTests, gInputDocURI, { width: 600, height: 600 });
   </script>
 
 </head>
 
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=591363"
      title="(in)visible state is not always correct?">
     Mozilla Bug 591363
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=768786"
+     title="Offscreen state is not exposed under certain circumstances">
+    Mozilla Bug 768786
+  </a>
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <div id="outer_div">
 
     <!-- trivial cases -->
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -72,21 +72,16 @@ var shell = {
     let crashID;
     try {
       crashID = Cc["@mozilla.org/xre/app-info;1"]
                 .getService(Ci.nsIXULRuntime).lastRunCrashID;
     } catch(e) { }
     if (Services.prefs.getBoolPref('app.reportCrashes') &&
         crashID) {
 
-      if (!Services.io.offline) {
-        this.CrashSubmit.submit(crashID);
-        return;
-      }
-
       Services.obs.addObserver(function observer(subject, topic, state) {
           if (topic != "network:offline-status-changed")
             return;
           if (state == 'online') {
             shell.CrashSubmit.submit(crashID);
             Services.obs.removeObserver(observer, topic);
           }
         }
@@ -109,16 +104,29 @@ var shell = {
     return Services.prefs.getCharPref('browser.homescreenURL');
   },
 
   get manifestURL() {
     return Services.prefs.getCharPref('browser.manifestURL');
    },
 
   start: function shell_start() {
+
+    // Dogfood id. We might want to remove it in the future.
+    // see bug 789466
+    try {
+      let dogfoodId = Services.prefs.getCharPref('prerelease.dogfood.id');
+      if (dogfoodId != "") {
+        let cr = Cc["@mozilla.org/xre/app-info;1"]
+                   .getService(Ci.nsICrashReporter);
+        cr.annotateCrashReport("Email", dogfoodId);
+      }
+    }
+    catch (e) { }
+
     let homeURL = this.homeURL;
     if (!homeURL) {
       let msg = 'Fatal error during startup: No homescreen found: try setting B2G_HOMESCREEN';
       alert(msg);
       return;
     }
 
     let manifestURL = this.manifestURL;
@@ -427,16 +435,26 @@ Services.obs.addObserver(function(aSubje
   shell.sendChromeEvent({ type: "fullscreenoriginchange",
                           fullscreenorigin: aData });
 }, "fullscreen-origin-change", false);
 
 Services.obs.addObserver(function onWebappsReady(subject, topic, data) {
   shell.sendChromeEvent({ type: 'webapps-registry-ready' });
 }, 'webapps-registry-ready', false);
 
+Services.obs.addObserver(function onBluetoothVolumeChange(subject, topic, data) {
+  if (data == 'up') {
+    shell.sendChromeEvent({ type: 'volume-up-button-press' });
+    shell.sendChromeEvent({ type: 'volume-up-button-release' });
+  } else if (data == 'down') {
+    shell.sendChromeEvent({ type: 'volume-down-button-press' });
+    shell.sendChromeEvent({ type: 'volume-down-button-release' });
+  }
+}, 'bluetooth-volume-change', false);
+
 (function Repl() {
   if (!Services.prefs.getBoolPref('b2g.remote-js.enabled')) {
     return;
   }
   const prompt = 'JS> ';
   let output;
   let reader = {
     onInputStreamReady : function repl_readInput(input) {
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -579,17 +579,16 @@ pref("accessibility.typeaheadfind.flashB
 // plugin finder service url
 pref("pfs.datasource.url", "https://pfs.mozilla.org/plugins/PluginFinderService.php?mimetype=%PLUGIN_MIMETYPE%&appID=%APP_ID%&appVersion=%APP_VERSION%&clientOS=%CLIENT_OS%&chromeLocale=%CHROME_LOCALE%&appRelease=%APP_RELEASE%");
 
 // by default we show an infobar message when pages require plugins the user has not installed, or are outdated
 pref("plugins.hide_infobar_for_missing_plugin", false);
 pref("plugins.hide_infobar_for_outdated_plugin", false);
 
 #ifdef XP_MACOSX
-pref("plugins.use_layers", true);
 pref("plugins.hide_infobar_for_carbon_failure_plugin", false);
 #endif
 
 pref("plugins.update.url", "https://www.mozilla.org/%LOCALE%/plugincheck/");
 pref("plugins.update.notifyUser", false);
 
 pref("plugins.click_to_play", false);
 
@@ -1178,10 +1177,11 @@ pref("image.mem.max_decoded_image_kb", 2
 
 // Example social provider
 pref("social.manifest.motown", "{\"origin\":\"https://motown-dev.mozillalabs.com\",\"name\":\"MoTown\",\"workerURL\":\"https://motown-dev.mozillalabs.com/social/worker.js\",\"iconURL\":\"https://motown-dev.mozillalabs.com/images/motown-icon.png\",\"sidebarURL\":\"https://motown-dev.mozillalabs.com/social/sidebar\"}");
 // Comma-separated list of nsIURI::prePaths that are allowed to activate
 // built-in social functionality.
 pref("social.activation.whitelist", "https://motown-dev.mozillalabs.com");
 pref("social.sidebar.open", true);
 pref("social.active", false);
+pref("social.toast-notifications.enabled", true);
 
 pref("dom.identity.enabled", false);
--- a/browser/base/content/abouthome/aboutHome.xhtml
+++ b/browser/base/content/abouthome/aboutHome.xhtml
@@ -49,17 +49,17 @@
           <span id="defaultSnippet1">&abouthome.defaultSnippet1.v1;</span>
           <span id="defaultSnippet2">&abouthome.defaultSnippet2.v1;</span>
         </div>
         <div id="snippets"/>
       </div>
     </div>
     <div class="spacer"/>
 
-    <div id="launcher" session="true">
+    <div id="launcher">
       <button class="launchButton" id="downloads">&abouthome.downloadsButton.label;</button>
       <button class="launchButton" id="bookmarks">&abouthome.bookmarksButton.label;</button>
       <button class="launchButton" id="history">&abouthome.historyButton.label;</button>
       <button class="launchButton" id="apps" hidden="true">&abouthome.appsButton.label;</button>
       <button class="launchButton" id="addons">&abouthome.addonsButton.label;</button>
       <button class="launchButton" id="sync">&abouthome.syncButton.label;</button>
       <button class="launchButton" id="settings">&abouthome.settingsButton.label;</button>
       <div id="restorePreviousSessionSeparator"/>
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -104,16 +104,17 @@
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing" oncommand="gPrivateBrowsingUI.toggleMode();"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
     <command id="Social:SharePage" oncommand="SocialShareButton.sharePage();"/>
     <command id="Social:UnsharePage" oncommand="SocialShareButton.unsharePage();"/>
     <command id="Social:ToggleSidebar" oncommand="Social.toggleSidebar();"/>
+    <command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();"/>
     <command id="Social:Toggle" oncommand="Social.toggle();" hidden="true"/>
   </commandset>
 
   <commandset id="placesCommands">
     <command id="Browser:ShowAllBookmarks"
              oncommand="PlacesCommandHook.showPlacesOrganizer('AllBookmarks');"/>
     <command id="Browser:ShowAllHistory"
              oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -5,29 +5,31 @@
 let SocialUI = {
   // Called on delayed startup to initialize UI
   init: function SocialUI_init() {
     Services.obs.addObserver(this, "social:pref-changed", false);
     Services.obs.addObserver(this, "social:ambient-notification-changed", false);
     Services.obs.addObserver(this, "social:profile-changed", false);
 
     Services.prefs.addObserver("social.sidebar.open", this, false);
+    Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
 
     gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler, true, true);
 
     Social.init(this._providerReady.bind(this));
   },
 
   // Called on window unload
   uninit: function SocialUI_uninit() {
     Services.obs.removeObserver(this, "social:pref-changed");
     Services.obs.removeObserver(this, "social:ambient-notification-changed");
     Services.obs.removeObserver(this, "social:profile-changed");
 
     Services.prefs.removeObserver("social.sidebar.open", this);
+    Services.prefs.removeObserver("social.toast-notifications.enabled", this);
   },
 
   showProfile: function SocialUI_showProfile() {
     if (Social.provider)
       openUILinkIn(Social.provider.profile.profileURL, "tab");
   },
 
   observe: function SocialUI_observe(subject, topic, data) {
@@ -52,16 +54,17 @@ let SocialUI = {
         break;
       case "social:profile-changed":
         SocialToolbar.updateProfile();
         SocialShareButton.updateProfileInfo();
         SocialChatBar.update();
         break;
       case "nsPref:changed":
         SocialSidebar.updateSidebar();
+        SocialToolbar.updateButton();
     }
   },
 
   get toggleCommand() {
     return document.getElementById("Social:Toggle");
   },
 
   // Called once Social.jsm's provider has been set
@@ -584,16 +587,19 @@ var SocialToolbar = {
     let iconNames = Object.keys(provider.ambientNotificationIcons);
     let iconBox = document.getElementById("social-status-iconbox");
     let notifBox = document.getElementById("social-notification-box");
     let panel = document.getElementById("social-notification-panel");
     panel.hidden = false;
     let notificationFrames = document.createDocumentFragment();
     let iconContainers = document.createDocumentFragment();
 
+    let command = document.getElementById("Social:ToggleNotifications");
+    command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
+
     for each(let name in iconNames) {
       let icon = provider.ambientNotificationIcons[name];
 
       let notificationFrameId = "social-status-" + icon.name;
       let notificationFrame = document.getElementById(notificationFrameId);
       if (!notificationFrame) {
         notificationFrame = document.createElement("iframe");
         notificationFrame.setAttribute("type", "content");
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2488,18 +2488,18 @@ function BrowserOnAboutPageLoad(document
   if (/^about:home$/i.test(document.documentURI)) {
     // XXX bug 738646 - when Marketplace is launched, remove this statement and
     // the hidden attribute set on the apps button in aboutHome.xhtml
     if (getBoolPref("browser.aboutHome.apps", false))
       document.getElementById("apps").removeAttribute("hidden");
 
     let ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
              getService(Components.interfaces.nsISessionStore);
-    if (!ss.canRestoreLastSession)
-      document.getElementById("launcher").removeAttribute("session");
+    if (ss.canRestoreLastSession)
+      document.getElementById("launcher").setAttribute("session", "true");
 
     // Inject search engine and snippets URL.
     let docElt = document.documentElement;
     docElt.setAttribute("snippetsURL", AboutHomeUtils.snippetsURL);
     docElt.setAttribute("searchEngineName",
                         AboutHomeUtils.defaultSearchEngine.name);
     docElt.setAttribute("searchEngineURL",
                         AboutHomeUtils.defaultSearchEngine.searchURL);
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -679,16 +679,22 @@
                 </vbox>
               </hbox>
               <menuitem id="social-toggle-sidebar-menuitem"
                         type="checkbox"
                         autocheck="false"
                         command="Social:ToggleSidebar"
                         label="&social.toggleSidebar.label;"
                         accesskey="&social.toggleSidebar.accesskey;"/>
+              <menuitem id="social-toggle-notifications-menuitem"
+                        type="checkbox"
+                        autocheck="false"
+                        command="Social:ToggleNotifications"
+                        label="&social.toggleNotifications.label;"
+                        accesskey="&social.toggleNotifications.accesskey;"/>
             </menupopup>
           </button>
           <hbox id="social-status-iconbox" flex="1">
           </hbox>
         </hbox>
       </toolbaritem>
 
       <toolbaritem id="bookmarks-menu-button-container"
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -2,17 +2,17 @@
 
 <bindings id="socialChatBindings"
     xmlns="http://www.mozilla.org/xbl"
     xmlns:xbl="http://www.mozilla.org/xbl"
     xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <binding id="chatbox">
     <content orient="vertical" mousethrough="never">
-      <xul:hbox class="chat-titlebar" xbl:inherits="minimized,selected"
+      <xul:hbox class="chat-titlebar" xbl:inherits="minimized,selected,activity"
                 onclick="document.getBindingParent(this).toggle();" align="baseline">
         <xul:image class="chat-status-icon" xbl:inherits="src=image"/>
         <xul:label class="chat-title" flex="1" xbl:inherits="value=label,crop"/>
         <xul:toolbarbutton class="chat-close-button chat-toolbarbutton"
                            oncommand="document.getBindingParent(this).close();"/>
       </xul:hbox>
       <xul:iframe anonid="iframe" class="chat-frame" flex="1"
                   xbl:inherits="src,origin,collapsed=minimized" type="content"/>
@@ -73,37 +73,52 @@
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="focus" phase="capturing">
         this.parentNode.selectedChat = this;
       </handler>
-      <handler event="load"><![CDATA[
+      <handler event="DOMContentLoaded"><![CDATA[
         this.isActive = !this.minimized;
         if (this._callback) this._callback(this.iframe.contentWindow);
+        let chatbox = this;
+        function chatActivity() {
+          chatbox.setAttribute("activity", true);
+          chatbox.parentNode.updateTitlebar(chatbox);
+        };
+        let iframeWindow = this.iframe.contentWindow;
+        iframeWindow.addEventListener("socialChatActivity", chatActivity);
+        iframeWindow.addEventListener("unload", function unload() {
+          iframeWindow.removeEventListener("unload", unload);
+          iframeWindow.removeEventListener("socialChatActivity", chatActivity);
+        });
       ]]></handler>
-      <handler event="DOMTitleChanged" action="this.setAttribute('label', this.iframe.contentDocument.title);"/>
+      <handler event="DOMTitleChanged"><![CDATA[
+        this.setAttribute('label', this.iframe.contentDocument.title);
+        this.parentNode.updateTitlebar(this);
+      ]]></handler>
       <handler event="DOMLinkAdded"><![CDATA[
         // much of this logic is from DOMLinkHandler in browser.js
         // this sets the presence icon for a chat user, we simply use favicon style updating
         let link = event.originalTarget;
         let rel = link.rel && link.rel.toLowerCase();
         if (!link || !link.ownerDocument || !rel || !link.href)
           return;
         if (link.rel.indexOf("icon") < 0)
           return;
 
         let uri = DOMLinkHandler.getLinkIconURI(link);
         if (!uri)
           return;
 
         // we made it this far, use it
         this.setAttribute('image', uri.spec);
+        this.parentNode.updateTitlebar(this);
       ]]></handler>
     </handlers>
   </binding>
 
   <binding id="chatbar">
     <content>
       <xul:hbox align="end" pack="end" anonid="innerbox" class="chatbar-innerbox" mousethrough="always" flex="1">
         <xul:toolbarbutton anonid="nub" class="chatbar-button" type="menu" collapsed="true" mousethrough="never">
@@ -119,32 +134,37 @@
       <field name="innerbox" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "innerbox");
       </field>
 
       <field name="menupopup" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "nubMenu");
       </field>
 
+      <field name="nub" readonly="true">
+        document.getAnonymousElementByAttribute(this, "anonid", "nub");
+      </field>
+
       <property name="emptyWidth">
         <getter>
           return document.getAnonymousElementByAttribute(this, "anonid", "spacer").boxObject.width;
         </getter>
       </property>
 
       <property name="selectedChat">
         <getter><![CDATA[
           return this._selectedChat;
         ]]></getter>
         <setter><![CDATA[
           if (this._selectedChat)
             this._selectedChat.removeAttribute("selected");
           this._selectedChat = val;
           if (val) {
             this._selectedChat.setAttribute("selected", "true");
+            this._selectedChat.removeAttribute("activity");
           }
         ]]></setter>
       </property>
 
       <field name="menuitemMap">new WeakMap()</field>
       <field name="chatboxForURL">new Map();</field>
 
       <property name="firstCollapsedChild">
@@ -191,16 +211,31 @@
           }
           if (!this.firstCollapsedChild) {
             window.removeEventListener("resize", this);
             this.menupopup.parentNode.collapsed = true;
           }
         ]]></body>
       </method>
 
+      <method name="updateTitlebar">
+        <parameter name="aChatbox"/>
+        <body><![CDATA[
+          if (aChatbox.collapsed) {
+            let menuitem = this.menuitemMap.get(aChatbox);
+            if (aChatbox.getAttribute("activity")) {
+              menuitem.setAttribute("activity", true);
+              this.nub.setAttribute("activity", true);
+            }
+            menuitem.setAttribute("label", aChatbox.getAttribute("label"));
+            menuitem.setAttribute("image", aChatbox.getAttribute("image"));
+          }
+        ]]></body>
+      </method>
+
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
           if (aEvent.type == "resize") {
             this.resize();
           }
         ]]></body>
       </method>
@@ -220,17 +255,19 @@
 
       <method name="collapseChat">
         <parameter name="aChatbox"/>
         <body><![CDATA[
           aChatbox.viewWidth = aChatbox.getBoundingClientRect().width;
           aChatbox.collapsed = true;
           aChatbox.isActive = false;
           let menu = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "menuitem");
+          menu.setAttribute("class", "menuitem-iconic");
           menu.setAttribute("label", aChatbox.iframe.contentDocument.title);
+          menu.setAttribute("image", aChatbox.getAttribute("image"));
           menu.chat = aChatbox;
           this.menuitemMap.set(aChatbox, menu);
           this.menupopup.appendChild(menu);
           this.menupopup.parentNode.collapsed = false;
         ]]></body>
       </method>
 
       <method name="showChat">
@@ -291,16 +328,19 @@
           this.insertBefore(cb, this.firstChild);
           cb.init(aProvider, aURL, aCallback);
           this.chatboxForURL.set(aURL, Cu.getWeakReference(cb));
         ]]></body>
       </method>
 
     </implementation>
     <handlers>
+      <handler event="popupshown"><![CDATA[
+        this.nub.removeAttribute("activity");
+      ]]></handler>
       <handler event="overflow"><![CDATA[
         // make sure we're not getting an overflow from content
         if (event.originalTarget != this.innerbox)
           return;
 
         let hasHidden = this.firstCollapsedChild;
         let child = this.firstRemovableChild;
         if (child)
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -646,10 +646,13 @@ toolbar button -->
 <!ENTITY social.notLoggedIn.label   "Not logged in">
 
 <!ENTITY social.ok.label       "OK">
 <!ENTITY social.ok.accesskey   "O">
 
 <!ENTITY social.toggleSidebar.label "Show sidebar">
 <!ENTITY social.toggleSidebar.accesskey "s">
 
+<!ENTITY social.toggleNotifications.label "Show desktop notifications">
+<!ENTITY social.toggleNotifications.accesskey "n">
+
 <!ENTITY social.activated.undobutton.label "Undo">
 <!ENTITY social.activated.undobutton.accesskey "U">
--- a/browser/modules/Social.jsm
+++ b/browser/modules/Social.jsm
@@ -57,16 +57,21 @@ let Social = {
     this.enabled = !this.enabled;
   },
 
   toggleSidebar: function SocialSidebar_toggle() {
     let prefValue = Services.prefs.getBoolPref("social.sidebar.open");
     Services.prefs.setBoolPref("social.sidebar.open", !prefValue);
   },
 
+  toggleNotifications: function SocialNotifications_toggle() {
+    let prefValue = Services.prefs.getBoolPref("social.toast-notifications.enabled");
+    Services.prefs.setBoolPref("social.toast-notifications.enabled", !prefValue);
+  },
+
   // Sharing functionality
   _getShareablePageUrl: function Social_getShareablePageUrl(aURI) {
     let uri = aURI.clone();
     try {
       // Setting userPass on about:config throws.
       uri.userPass = "";
     } catch (e) {}
     return uri.spec;
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -2775,16 +2775,20 @@ html|*#gcli-output-frame {
 .chat-titlebar[minimized="true"] {
   border-bottom: none;
 }
 
 .chat-titlebar[selected] {
   background-color: #f0f0f0;
 }
 
+.chat-titlebar[activity] {
+  background-color: #ceeaff;
+}
+
 .chat-frame {
   padding: 0;
   margin: 0;
   overflow: hidden;
 }
 
 .chatbar-button {
   background-color: #d9d9d9;
@@ -2804,16 +2808,24 @@ html|*#gcli-output-frame {
   box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
 }
 
 .chatbar-button > .toolbarbutton-text,
 .chatbar-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
+.chatbar-button[activity] {
+  background-color: #ceeaff;
+}
+
+.chatbar-button > menupopup > menuitem[activity] {
+  font-weight: bold;
+}
+
 .chatbar-innerbox {
   background: transparent;
   margin: -285px -1px 0 -1px;
   overflow: hidden;
 }
 
 chatbar {
   -moz-margin-end: 20px;
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -3457,16 +3457,20 @@ html|*#gcli-output-frame {
   border-bottom: 1px solid #404040;
   cursor: pointer;
 }
 
 .chat-titlebar[minimized="true"] {
   border-bottom: none;
 }
 
+.chat-titlebar[activity] {
+  background-color: #ceeaff;
+}
+
 .chat-titlebar[selected] {
   background-color: #f0f0f0;
 }
 
 .chat-frame {
   padding: 0;
   margin: 0;
   overflow: hidden;
@@ -3490,16 +3494,24 @@ html|*#gcli-output-frame {
   box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
 }
 
 .chatbar-button > .toolbarbutton-text,
 .chatbar-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
+.chatbar-button[activity] {
+  background-color: #ceeaff;
+}
+
+.chatbar-button > menupopup > menuitem[activity] {
+  font-weight: bold;
+}
+
 .chatbar-innerbox {
   background: transparent;
   margin: -285px -1px 0 -1px;
   overflow: hidden;
 }
 
 chatbar {
   -moz-margin-end: 20px;
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -3479,16 +3479,20 @@ html|*#gcli-output-frame {
 .chat-titlebar[minimized="true"] {
   border-bottom: none;
 }
 
 .chat-titlebar[selected] {
   background-color: #dae3f0;
 }
 
+.chat-titlebar[activity] {
+  background-color: #ceeaff;
+}
+
 .chat-frame {
   padding: 0;
   margin: 0;
   overflow: hidden;
 }
 
 .chatbar-button {
   /* XXX get a real image for this */
@@ -3517,16 +3521,24 @@ html|*#gcli-output-frame {
   box-shadow: inset 0 2px 5px rgba(0,0,0,0.6), 0 1px rgba(255,255,255,0.2);
 }
 
 .chatbar-button > .toolbarbutton-text,
 .chatbar-button > .toolbarbutton-menu-dropmarker {
   display: none;
 }
 
+.chatbar-button[activity] {
+  background-color: #ceeaff;
+}
+
+.chatbar-button > menupopup > menuitem[activity] {
+  font-weight: bold;
+}
+
 .chatbar-innerbox {
   background: transparent;
   margin: -285px -1px 0 -1px;
   overflow: hidden;
 }
 
 chatbar {
   -moz-margin-end: 20px;
--- a/configure.in
+++ b/configure.in
@@ -60,17 +60,17 @@ NSS_VERSION=3
 dnl Set the minimum version of toolkit libs used by mozilla
 dnl ========================================================
 GLIB_VERSION=1.2.0
 PERL_VERSION=5.006
 PYTHON_VERSION_MAJOR=2
 PYTHON_VERSION_MINOR=5
 CAIRO_VERSION=1.10
 PANGO_VERSION=1.14.0
-GTK2_VERSION=2.18.0
+GTK2_VERSION=2.10.0
 WINDRES_VERSION=2.14.90
 W32API_VERSION=3.14
 GNOMEVFS_VERSION=2.0
 GNOMEUI_VERSION=2.2.0
 GCONF_VERSION=1.2.1
 GIO_VERSION=2.18
 STARTUP_NOTIFICATION_VERSION=0.8
 DBUS_VERSION=0.60
deleted file mode 100644
--- a/content/base/src/nsBlobProtocolHandler.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* 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/. */
-
-#ifndef nsBlobProtocolHandler_h
-#define nsBlobProtocolHandler_h
-
-#include "nsIProtocolHandler.h"
-#include "nsIURI.h"
-#include "nsCOMPtr.h"
-
-#define BLOBURI_SCHEME "blob"
-
-class nsIDOMBlob;
-class nsIPrincipal;
-class nsIInputStream;
-
-inline bool IsBlobURI(nsIURI* aUri)
-{
-  bool isBlob;
-  return NS_SUCCEEDED(aUri->SchemeIs(BLOBURI_SCHEME, &isBlob)) && isBlob;
-}
-
-extern nsresult
-NS_GetStreamForBlobURI(nsIURI* aURI, nsIInputStream** aStream);
-
-class nsBlobProtocolHandler : public nsIProtocolHandler
-{
-public:
-  NS_DECL_ISUPPORTS
-
-  // nsIProtocolHandler methods:
-  NS_DECL_NSIPROTOCOLHANDLER
-
-  // nsBlobProtocolHandler methods:
-  nsBlobProtocolHandler() {}
-  virtual ~nsBlobProtocolHandler() {}
-
-  // Methods for managing uri->file mapping
-  static void AddFileDataEntry(nsACString& aUri,
-                               nsIDOMBlob* aFile,
-                               nsIPrincipal* aPrincipal);
-  static void RemoveFileDataEntry(nsACString& aUri);
-  static nsIPrincipal* GetFileDataEntryPrincipal(nsACString& aUri);
-};
-
-#define NS_BLOBPROTOCOLHANDLER_CID \
-{ 0xb43964aa, 0xa078, 0x44b2, \
-  { 0xb0, 0x6b, 0xfd, 0x4d, 0x1b, 0x17, 0x2e, 0x66 } }
-
-#endif /* nsBlobProtocolHandler_h */
--- a/dom/apps/src/AppsUtils.jsm
+++ b/dom/apps/src/AppsUtils.jsm
@@ -29,17 +29,23 @@ let AppsUtils = {
       origin: aApp.origin,
       receipts: aApp.receipts ? JSON.parse(JSON.stringify(aApp.receipts)) : null,
       installTime: aApp.installTime,
       manifestURL: aApp.manifestURL,
       appStatus: aApp.appStatus,
       removable: aApp.removable,
       localId: aApp.localId,
       progress: aApp.progress || 0.0,
-      status: aApp.status || "installed"
+      installState: aApp.installState || "installed",
+      downloadAvailable: aApp.downloadAvailable,
+      downloading: aApp.downloading,
+      readyToApplyDownload: aApp.readyToApplyDownload,
+      downloadSize: aApp.downloadSize || 0,
+      lastUpdateCheck: aApp.lastUpdateCheck,
+      etag: aApp.etag
     };
   },
 
   cloneAsMozIApplication: function cloneAsMozIApplication(aApp) {
     let res = this.cloneAppObject(aApp);
     res.hasPermission = function(aPermission) {
       let uri = Services.io.newURI(this.origin, null, null);
       let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"]
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -7,16 +7,17 @@ const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
 Cu.import("resource://gre/modules/ObjectWrapper.jsm");
 Cu.import("resource://gre/modules/AppsUtils.jsm");
+Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
 function convertAppsArray(aApps, aWindow) {
   let apps = Cu.createArrayIn(aWindow);
   for (let i = 0; i < aApps.length; i++) {
@@ -49,17 +50,16 @@ WebappsRegistry.prototype = {
     if (!req)
       return;
     let app = msg.app;
     switch (aMessage.name) {
       case "Webapps:Install:Return:OK":
         Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app));
         break;
       case "Webapps:Install:Return:KO":
-      dump("XxXxX Webapps:Install:Return:KO\n");
         Services.DOMRequest.fireError(req, msg.error || "DENIED");
         break;
       case "Webapps:GetSelf:Return:OK":
         if (msg.apps.length) {
           app = msg.apps[0];
           Services.DOMRequest.fireSuccess(req, createApplicationObject(this._window, app));
         } else {
           Services.DOMRequest.fireSuccess(req, null);
@@ -115,20 +115,22 @@ WebappsRegistry.prototype = {
 
         if (!AppsUtils.checkManifest(manifest, installOrigin)) {
           Services.DOMRequest.fireError(request, "INVALID_MANIFEST");
         } else if (!this.checkAppStatus(manifest)) {
           Services.DOMRequest.fireError(request, "INVALID_SECURITY_LEVEL");
         } else {
           let receipts = (aParams && aParams.receipts && Array.isArray(aParams.receipts)) ? aParams.receipts : [];
           let categories = (aParams && aParams.categories && Array.isArray(aParams.categories)) ? aParams.categories : [];
+          let etag = xhr.getResponseHeader("Etag");
           cpmm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin,
                                                             origin: this._getOrigin(aURL),
                                                             manifestURL: aURL,
                                                             manifest: manifest,
+                                                            etag: etag,
                                                             receipts: receipts,
                                                             categories: categories },
                                                             from: installURL,
                                                             oid: this._id,
                                                             requestID: requestID });
         }
       } else {
         Services.DOMRequest.fireError(request, "MANIFEST_URL_ERROR");
@@ -254,16 +256,46 @@ WebappsRegistry.prototype = {
                                                  Ci.mozIDOMApplicationRegistry2,
 #endif
                                                  ],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Registry"})
 }
 
 /**
+  * nsIDOMDOMError object
+  */
+function createDOMError(aError) {
+  let error = Cc["@mozilla.org/dom-error;1"]
+                .createInstance(Ci.nsIDOMDOMError);
+  error.wrappedJSObject.init(aError);
+  return error;
+}
+
+function DOMError() {
+  this.wrappedJSObject = this;
+}
+
+DOMError.prototype = {
+  init: function domerror_init(aError) {
+    this.name = aError;
+  },
+
+  classID: Components.ID("{dcc1d5b7-43d8-4740-9244-b3d8db0f503d}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMDOMError]),
+
+  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{dcc1d5b7-43d8-4740-9244-b3d8db0f503d}"),
+                                    contractID: "@mozilla.org/dom-error;1",
+                                    interfaces: [Ci.nsIDOMDOMError],
+                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
+                                    classDescription: "DOMError object"})
+}
+
+/**
   * mozIDOMApplication object
   */
 
 function createApplicationObject(aWindow, aApp) {
   let app = Cc["@mozilla.org/webapps/application;1"].createInstance(Ci.mozIDOMApplication);
   app.wrappedJSObject.init(aWindow, aApp);
   return app;
 }
@@ -273,39 +305,111 @@ function WebappsApplication() {
 }
 
 WebappsApplication.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
 
   init: function(aWindow, aApp) {
     this.origin = aApp.origin;
     this.manifest = ObjectWrapper.wrap(aApp.manifest, aWindow);
+    this.updateManifest = aApp.updateManifest ? ObjectWrapper.wrap(aApp.updateManifest, aWindow)
+                                              : null;
     this.manifestURL = aApp.manifestURL;
     this.receipts = aApp.receipts;
     this.installOrigin = aApp.installOrigin;
     this.installTime = aApp.installTime;
-    this.status = "installed";
+    this.installState = aApp.installState || "installed";
     this.removable = aApp.removable;
+    this.lastUpdateCheck = aApp.lastUpdateCheck ? aApp.lastUpdateCheck
+                                                : Date.now();
     this.progress = NaN;
+    this.downloadAvailable = aApp.downloadAvailable;
+    this.downloading = aApp.downloading;
+    this.readyToApplyDownload = aApp.readyToApplyDownload;
+    this.downloadSize = aApp.downloadSize || 0;
+
     this._onprogress = null;
+    this._ondownloadsuccess = null;
+    this._ondownloaderror = null;
+    this._ondownloadavailable = null;
+    this._ondownloadapplied = null;
+
+    this._downloadError = null;
+
     this.initHelper(aWindow, ["Webapps:Uninstall:Return:OK",
                               "Webapps:Uninstall:Return:KO",
-                              "Webapps:OfflineCache"]);
+                              "Webapps:OfflineCache",
+                              "Webapps:CheckForUpdate:Return:OK",
+                              "Webapps:CheckForUpdate:Return:KO"]);
     cpmm.sendAsyncMessage("Webapps:RegisterForMessages",
                           ["Webapps:Uninstall:Return:OK", "Webapps:OfflineCache"]);
   },
 
   set onprogress(aCallback) {
     this._onprogress = aCallback;
   },
 
   get onprogress() {
     return this._onprogress;
   },
 
+  set ondownloadsuccess(aCallback) {
+    this._ondownloadsuccess = aCallback;
+  },
+
+  get ondownloadsuccess() {
+    return this._ondownloadsuccess;
+  },
+
+  set ondownloaderror(aCallback) {
+    this._ondownloaderror = aCallback;
+  },
+
+  get ondownloaderror() {
+    return this._ondownloaderror;
+  },
+
+  set ondownloadavailable(aCallback) {
+    this._ondownloadavailable = aCallback;
+  },
+
+  get ondownloadavailable() {
+    return this._ondownloadavailable;
+  },
+
+  set ondownloadapplied(aCallback) {
+    this._ondownloadapplied = aCallback;
+  },
+
+  get ondownloadapplied() {
+    return this._ondownloadapplied;
+  },
+
+  get downloadError() {
+    return createDOMError(this._downloadError);
+  },
+
+  download: function() {
+    throw Cr.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
+  cancelDownload: function() {
+    cpmm.sendAsyncMessage("Webapps:CancelDownload",
+                          { manifestURL: this.manifestURL });
+  },
+
+  checkForUpdate: function() {
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:CheckForUpdate",
+                          { manifestURL: this.manifestURL,
+                            oid: this._id,
+                            requestID: this.getRequestId(request) });
+    return request;
+  },
+
   launch: function(aStartPoint) {
     let request = this.createRequest();
     cpmm.sendAsyncMessage("Webapps:Launch", { origin: this.origin,
                                               manifestURL: this.manifestURL,
                                               startPoint: aStartPoint || "",
                                               oid: this._id,
                                               requestID: this.getRequestId(request) });
     return request;
@@ -314,44 +418,84 @@ WebappsApplication.prototype = {
   uninstall: function() {
     let request = this.createRequest();
     cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: this.origin,
                                                  oid: this._id,
                                                  requestID: this.getRequestId(request) });
     return request;
   },
 
+  clearBrowserData: function() {
+    let browserChild =
+      BrowserElementPromptService.getBrowserElementChildForWindow(this._window);
+    if (browserChild) {
+      browserChild.messageManager.sendAsyncMessage("Webapps:ClearBrowserData");
+    }
+  },
+
   uninit: function() {
     this._onprogress = null;
     cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
                           ["Webapps:Uninstall:Return:OK", "Webapps:OfflineCache"]);
   },
 
+  _fireEvent: function(aName, aHandler) {
+    if (aHandler) {
+      let event = new this._window.MozApplicationEvent(aName, { application: this });
+      aHandler.handleEvent(event);
+    }
+  },
+
   receiveMessage: function(aMessage) {
     var msg = aMessage.json;
     let req = this.takeRequest(msg.requestID);
     if ((msg.oid != this._id || !req) && aMessage.name !== "Webapps:OfflineCache")
       return;
     switch (aMessage.name) {
       case "Webapps:Uninstall:Return:OK":
         Services.DOMRequest.fireSuccess(req, msg.origin);
         break;
       case "Webapps:Uninstall:Return:KO":
         Services.DOMRequest.fireError(req, "NOT_INSTALLED");
         break;
+      case "Webapps:Launch:Return:KO":
+        Services.DOMRequest.fireError(req, "APP_INSTALL_PENDING");
+        break;
+      case "Webapps:Uninstall:Return:KO":
+        Services.DOMRequest.fireError(req, "NOT_INSTALLED");
+        break;
       case "Webapps:OfflineCache":
         if (msg.manifest != this.manifestURL)
           return;
 
-        this.status = msg.status;
-        if (this._onprogress) {
-          let event = new this._window.MozApplicationEvent("applicationinstall", { application: this });
-          this._onprogress.handleEvent(event);
+        if (installState in msg) {
+          this.installState = msg.installState;
+          if (this.installState == "installed") {
+            this._fireEvent("downloadsuccess", this._ondownloadsuccess);
+            this._fireEvent("downloadapplied", this._ondownloadapplied);
+          } else {
+            this._fireEvent("downloadprogress", this._onprogress);
+          }
+        } else if (msg.error) {
+          this._downloadError = msg.error;
+          this._fireEvent("downloaderror", this._ondownloaderror);
         }
         break;
+        case "Webapps:CheckForUpdate:Return:OK":
+          for (let prop in msg.app) {
+            this[prop] = msg.app[prop];
+            if (msg.event == "downloadapplied") {
+              Services.DOMRequest.fireSuccess(req, this.manifestURL);
+              this._fireEvent("downloadapplied", this._ondownloadapplied);
+            }
+          }
+          break;
+        case "Webapps:CheckForUpdate:Return:KO":
+          Services.DOMRequest.fireError(req, msg.error);
+          break;
     }
   },
 
   classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication]),
 
   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
@@ -384,29 +528,34 @@ function WebappsApplicationMgmt(aWindow)
 
   this._oninstall = null;
   this._onuninstall = null;
 }
 
 WebappsApplicationMgmt.prototype = {
   __proto__: DOMRequestIpcHelper.prototype,
   __exposedProps__: {
-                      getAll: 'r',
-                      getNotInstalled: 'r',
-                      oninstall: 'rw',
-                      onuninstall: 'rw'
+                      applyDownload: "r",
+                      getAll: "r",
+                      getNotInstalled: "r",
+                      oninstall: "rw",
+                      onuninstall: "rw"
                      },
 
   uninit: function() {
     this._oninstall = null;
     this._onuninstall = null;
     cpmm.sendAsyncMessage("Webapps:UnregisterForMessages",
                           ["Webapps:Install:Return:OK", "Webapps:Uninstall:Return:OK"]);
   },
 
+  applyDownload: function(aApp) {
+    return Cr.NS_ERROR_NOT_IMPLEMENTED;
+  },
+
   getAll: function() {
     let request = this.createRequest();
     cpmm.sendAsyncMessage("Webapps:GetAll", { oid: this._id,
                                               requestID: this.getRequestId(request),
                                               hasPrivileges: this.hasPrivileges });
     return request;
   },
 
@@ -482,9 +631,11 @@ WebappsApplicationMgmt.prototype = {
 
   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
                                     contractID: "@mozilla.org/webapps/application-mgmt;1",
                                     interfaces: [Ci.mozIDOMApplicationMgmt],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Application Mgmt"})
 }
 
-const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication]);
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry,
+                                                      WebappsApplication,
+                                                      DOMError]);
--- a/dom/apps/src/Webapps.jsm
+++ b/dom/apps/src/Webapps.jsm
@@ -12,16 +12,20 @@ const Cr = Components.results;
 let EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "DOMApplicationManifest"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import('resource://gre/modules/ActivitiesService.jsm');
 Cu.import("resource://gre/modules/AppsUtils.jsm");
 
+function debug(aMsg) {
+  //dump("-*-*- Webapps.jsm : " + aMsg + "\n");
+}
+
 const WEBAPP_RUNTIME = Services.appinfo.ID == "webapprt@mozilla.org";
 
 XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
   Cu.import("resource://gre/modules/NetUtil.jsm");
   return NetUtil;
 });
 
 XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
@@ -56,17 +60,20 @@ let DOMApplicationRegistry = {
 
   init: function() {
     this.messages = ["Webapps:Install", "Webapps:Uninstall",
                      "Webapps:GetSelf", "Webapps:IsInstalled",
                      "Webapps:GetInstalled", "Webapps:GetNotInstalled",
                      "Webapps:Launch", "Webapps:GetAll",
                      "Webapps:InstallPackage", "Webapps:GetBasePath",
                      "Webapps:GetList", "Webapps:RegisterForMessages",
-                     "Webapps:UnregisterForMessages"];
+                     "Webapps:UnregisterForMessages",
+                     "Webapps:CancelDownload", "Webapps:CheckForUpdate"];
+
+    this.frameMessages = ["Webapps:ClearBrowserData"];
 
     this.messages.forEach((function(msgName) {
       ppmm.addMessageListener(msgName, this);
     }).bind(this));
 
     cpmm.addMessageListener("Activities:Register:OK", this);
 
     Services.obs.addObserver(this, "xpcom-shutdown", false);
@@ -440,17 +447,17 @@ let DOMApplicationRegistry = {
       case "Webapps:GetSelf":
         this.getSelf(msg, mm);
         break;
       case "Webapps:Uninstall":
         Services.obs.notifyObservers(mm, "webapps-uninstall", JSON.stringify(msg));
         this.uninstall(msg);
         break;
       case "Webapps:Launch":
-        Services.obs.notifyObservers(mm, "webapps-launch", JSON.stringify(msg));
+        this.launchApp(msg, mm);
         break;
       case "Webapps:IsInstalled":
         this.isInstalled(msg, mm);
         break;
       case "Webapps:GetInstalled":
         this.getInstalled(msg, mm);
         break;
       case "Webapps:GetNotInstalled":
@@ -472,16 +479,22 @@ let DOMApplicationRegistry = {
         this.addMessageListener(msg, mm);
         break;
       case "Webapps:UnregisterForMessages":
         this.removeMessageListener(msg, mm);
         break;
       case "Webapps:GetList":
         this.addMessageListener(["Webapps:AddApp", "Webapps:RemoveApp"], mm);
         return this.webapps;
+      case "Webapps:CancelDownload":
+        this.cancelDowload(msg.manifestURL);
+        break;
+      case "Webapps:CheckForUpdate":
+        this.checkForUpdate(msg, mm);
+        break;
       case "Activities:Register:OK":
         this.activitiesRegistered++;
         if (this.allActivitiesSent &&
             this.activitiesRegistered === this.activitiesToRegister) {
           this.onInitDone();
         }
         break;
     }
@@ -529,16 +542,167 @@ let DOMApplicationRegistry = {
     // Asynchronously copy the data to the file.
     let istream = converter.convertToInputStream(aData);
     NetUtil.asyncCopy(istream, ostream, function(rc) {
       if (aCallbak)
         aCallbak();
     });
   },
 
+  launchApp: function launchApp(aData, aMm) {
+    let app = this.getAppByManifestURL(aData.manifestURL);
+    if (!app) {
+      aMm.sendAsyncMessage("Webapps:Launch:Return:KO", aData);
+      return;
+    }
+
+    // Fire an error when trying to launch an app that is not
+    // yet fully installed.
+    if (app.installState == "pending") {
+      aMm.sendAsyncMessage("Webapps:Launch:Return:KO", aData);
+      return;
+    }
+
+    Services.obs.notifyObservers(aMm, "webapps-launch", JSON.stringify(aData));
+  },
+
+  cancelDownload: function cancelDowload(aManifestURL) {
+    // We can't cancel appcache dowloads for now.
+  },
+
+  startOfflineCacheDownload: function startOfflineCacheDownload(aManifest, aApp, aProfileDir) {
+    // if the manifest has an appcache_path property, use it to populate the appcache
+    if (aManifest.appcache_path) {
+      let appcacheURI = Services.io.newURI(aManifest.fullAppcachePath(), null, null);
+      let updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"]
+                            .getService(Ci.nsIOfflineCacheUpdateService);
+      let docURI = Services.io.newURI(aManifest.fullLaunchPath(), null, null);
+      let cacheUpdate = aProfileDir ? updateService.scheduleCustomProfileUpdate(appcacheURI, docURI, aProfileDir)
+                                    : updateService.scheduleUpdate(appcacheURI, docURI, null);
+      cacheUpdate.addObserver(new AppcacheObserver(aApp), false);
+      if (aOfflineCacheObserver) {
+        cacheUpdate.addObserver(aOfflineCacheObserver, false);
+      }
+    }
+  },
+
+  checkForUpdate: function(aData, aMm) {
+    let app = this.getAppByManifestURL(aData.manifestURL);
+    if (!app) {
+      aData.error = "NO_SUCH_APP";
+      aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
+      return;
+    }
+
+    function sendError(aError) {
+      aData.error = aError;
+      aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:KO", aData);
+    }
+
+    function updatePackagedApp(aManifest) {
+      debug("updatePackagedApp");
+    }
+
+    function updateHostedApp(aManifest) {
+      debug("updateHostedApp");
+      let id = this._appId(app.origin);
+
+#ifdef MOZ_SYS_MSG
+      // Update the Web Activities
+      this._readManifests([{ id: id }], (function unregisterManifest(aResult) {
+        this._unregisterActivities(aResult[0].manifest, app);
+        this._registerSystemMessages(aManifest, app);
+        this._registerActivities(aManifest, app);
+      }).bind(this));
+#endif
+
+      // Store the new manifest.
+      let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
+      let manFile = dir.clone();
+      manFile.append("manifest.webapp");
+      this._writeFile(manFile, JSON.stringify(aManifest), function() { });
+
+      let manifest = new DOMApplicationManifest(aManifest, app.origin);
+
+      if (manifest.appcache_path) {
+        app.installState = "updating";
+        app.downloadAvailable = true;
+        app.downloading = true;
+        app.downloadsize = 0;
+        app.readyToApplyDownload = false;
+      } else {
+        app.installState = "installed";
+        app.downloadAvailable = false;
+        app.downloading = false;
+        app.readyToApplyDownload = false;
+      }
+
+      app.name = aManifest.name;
+
+      // Update the registry.
+      this.webapps[id] = app;
+
+      this._saveApps((function() {
+        // XXX Should we fire notifications ?
+      }).bind(this));
+
+      // Preload the appcache if needed.
+      this.startOfflineCacheDownload(manifest, app);
+    }
+
+    // First, we download the manifest.
+    let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+                .createInstance(Ci.nsIXMLHttpRequest);
+    xhr.open("GET", aData.manifestURL, true);
+    if (aData.etag) {
+      xhr.setRequestHeader("If-None-Match", aData.etag);
+    }
+
+    xhr.addEventListener("load", (function() {
+      if (xhr.status == 200) {
+        let manifest;
+        try {
+          JSON.parse(xhr.responseText, installOrigin);
+        } catch(e) {
+          sendError("MANIFEST_PARSE_ERROR");
+          return;
+        }
+        if (!AppsUtils.checkManifest(manifest, installOrigin)) {
+          sendError("INVALID_MANIFEST");
+        } else {
+          app.etag = xhr.getResponseHeader("Etag");
+          app.lastCheckedUpdate = Date.now();
+          if (package_path in manifest) {
+            updatePackagedApp(manifest);
+          } else {
+            updateHostedApp(manifest);
+          }
+        }
+        this._saveApps();
+      } else if (xhr.status == 304) {
+        // The manifest has not changed. We just update lastCheckedUpdate.
+        app.lastCheckedUpdate = Date.now();
+        aData.event = "downloadapplied";
+        aData.app = {
+          lastCheckedUpdate: app.lastCheckedUpdate
+        }
+        aMm.sendAsyncMessage("Webapps:CheckForUpdate:Return:OK", aData);
+        this._saveApps();
+      } else {
+        sendError("MANIFEST_URL_ERROR");
+      }
+    }).bind(this), false);
+
+    xhr.addEventListener("error", (function() {
+      sendError(request, "NETWORK_ERROR");
+    }).bind(this), false);
+
+    xhr.send(null);
+  },
+
   denyInstall: function(aData) {
     let packageId = aData.app.packageId;
     if (packageId) {
       let dir = FileUtils.getDir("TmpD", ["webapps", packageId],
                                  true, true);
       try {
         dir.remove(true);
       } catch(e) {
@@ -568,16 +732,17 @@ let DOMApplicationRegistry = {
     if (app.packageId) {
       // Override the origin with the correct id.
       app.origin = "app://" + id;
     }
 
     let appObject = AppsUtils.cloneAppObject(app);
     appObject.appStatus = app.appStatus || Ci.nsIPrincipal.APP_STATUS_INSTALLED;
     appObject.installTime = app.installTime = Date.now();
+    appObject.lastUpdateCheck = app.lastUpdateCheck = Date.now();
     let appNote = JSON.stringify(appObject);
     appNote.id = id;
 
     appObject.localId = localId;
     appObject.basePath = FileUtils.getDir(DIRECTORY_NAME, ["webapps"], true, true).path;
 
     let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true);
     let manFile = dir.clone();
@@ -592,48 +757,49 @@ let DOMApplicationRegistry = {
         let tmpDir = FileUtils.getDir("TmpD", ["webapps", app.packageId],
                                         true, true);
         try {
           tmpDir.remove(true);
         } catch(e) {
         }
       }
     });
-    this.webapps[id] = appObject;
+
+    let manifest = new DOMApplicationManifest(app.manifest, app.origin);
 
-    appObject.status = "installed";
+    if (manifest.appcache_path) {
+      appObject.installState = "pending";
+      appObject.downloadAvailable = true;
+      appObject.downloading = true;
+      appObject.downloadsize = 0;
+      appObject.readyToApplyDownload = false;
+    } else {
+      appObject.installState = "installed";
+      appObject.downloadAvailable = false;
+      appObject.downloading = false;
+      appObject.readyToApplyDownload = false;
+    }
+
     appObject.name = app.manifest.name;
 
-    let manifest = new DOMApplicationManifest(app.manifest, app.origin);
+    this.webapps[id] = appObject;
 
     if (!aFromSync)
       this._saveApps((function() {
         this.broadcastMessage("Webapps:Install:Return:OK", aData);
         Services.obs.notifyObservers(this, "webapps-sync-install", appNote);
         this.broadcastMessage("Webapps:AddApp", { id: id, app: appObject });
       }).bind(this));
 
 #ifdef MOZ_SYS_MSG
     this._registerSystemMessages(app.manifest, app);
     this._registerActivities(app.manifest, app);
 #endif
 
-    // if the manifest has an appcache_path property, use it to populate the appcache
-    if (manifest.appcache_path) {
-      let appcacheURI = Services.io.newURI(manifest.fullAppcachePath(), null, null);
-      let updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"]
-                            .getService(Ci.nsIOfflineCacheUpdateService);
-      let docURI = Services.io.newURI(manifest.fullLaunchPath(), null, null);
-      let cacheUpdate = aProfileDir ? updateService.scheduleCustomProfileUpdate(appcacheURI, docURI, aProfileDir)
-                                    : updateService.scheduleUpdate(appcacheURI, docURI, null);
-      cacheUpdate.addObserver(new AppcacheObserver(appObject), false);
-      if (aOfflineCacheObserver) {
-        cacheUpdate.addObserver(aOfflineCacheObserver, false);
-      }
-    }
+    this.startOfflineCacheDownload(manifest, appObject, aProfileDir);
   },
 
   _nextLocalId: function() {
     let id = Services.prefs.getIntPref("dom.mozApps.maxLocalId") + 1;
     Services.prefs.setIntPref("dom.mozApps.maxLocalId", id);
     return id;
   },
 
@@ -646,17 +812,17 @@ let DOMApplicationRegistry = {
   },
 
   makeAppId: function() {
     let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
     return uuidGenerator.generateUUID().toString();
   },
 
   _saveApps: function(aCallback) {
-    this._writeFile(this.appsFile, JSON.stringify(this.webapps), function() {
+    this._writeFile(this.appsFile, JSON.stringify(this.webapps, null, 2), function() {
       if (aCallback)
         aCallback();
     });
   },
 
   /**
     * Asynchronously reads a list of manifests
     */
@@ -837,21 +1003,21 @@ let DOMApplicationRegistry = {
 
       if (!this.webapps[id].removable)
         return;
 
       found = true;
       let appNote = JSON.stringify(AppsUtils.cloneAppObject(app));
       appNote.id = id;
 
+#ifdef MOZ_SYS_MSG
       this._readManifests([{ id: id }], (function unregisterManifest(aResult) {
-#ifdef MOZ_SYS_MSG
         this._unregisterActivities(aResult[0].manifest, app);
+      }).bind(this));
 #endif
-      }).bind(this));
 
       let dir = this._getAppDir(id);
       try {
         dir.remove(true);
       } catch (e) {}
 
       delete this.webapps[id];
 
@@ -1131,54 +1297,134 @@ let DOMApplicationRegistry = {
 
     desktopINI.append("owa-" + uniqueName + ".desktop");
 
     return desktopINI.exists();
 #else
     return true;
 #endif
 
+  },
+
+  _notifyCategoryAndObservers: function(subject, topic, data) {
+    const serviceMarker = "service,";
+
+    // First create observers from the category manager.
+    let cm =
+      Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
+    let enumerator = cm.enumerateCategory(topic);
+
+    let observers = [];
+
+    while (enumerator.hasMoreElements()) {
+      let entry =
+        enumerator.getNext().QueryInterface(Ci.nsISupportsCString).data;
+      let contractID = cm.getCategoryEntry(topic, entry);
+
+      let factoryFunction;
+      if (contractID.substring(0, serviceMarker.length) == serviceMarker) {
+        contractID = contractID.substring(serviceMarker.length);
+        factoryFunction = "getService";
+      }
+      else {
+        factoryFunction = "createInstance";
+      }
+
+      try {
+        let handler = Cc[contractID][factoryFunction]();
+        if (handler) {
+          let observer = handler.QueryInterface(Ci.nsIObserver);
+          observers.push(observer);
+        }
+      } catch(e) { }
+    }
+
+    // Next enumerate the registered observers.
+    enumerator = Services.obs.enumerateObservers(topic);
+    while (enumerator.hasMoreElements()) {
+      let observer = enumerator.getNext();
+      if (observers.indexOf(observer) == -1) {
+        observers.push(observer);
+      }
+    }
+
+    observers.forEach(function (observer) {
+      try {
+        observer.observe(subject, topic, data);
+      } catch(e) { }
+    });
+  },
+
+  registerBrowserElementParentForApp: function(bep, appId) {
+    let mm = bep._mm;
+
+    // Make a listener function that holds on to this appId.
+    let listener = this.receiveAppMessage.bind(this, appId);
+
+    this.frameMessages.forEach(function(msgName) {
+      mm.addMessageListener(msgName, listener);
+    });
+  },
+
+  receiveAppMessage: function(appId, message) {
+    switch (message.name) {
+      case "Webapps:ClearBrowserData":
+        let subject = {
+          appId: appId,
+          browserOnly: true,
+          QueryInterface: XPCOMUtils.generateQI([Ci.mozIApplicationClearPrivateDataParams])
+        };
+        this._notifyCategoryAndObservers(subject, "webapps-clear-data", null);
+        break;
+    }
   }
 };
 
 /**
  * Appcache download observer
  */
 let AppcacheObserver = function(aApp) {
   this.app = aApp;
+  this.startStatus = aApp.installState;
 };
 
 AppcacheObserver.prototype = {
   // nsIOfflineCacheUpdateObserver implementation
   updateStateChanged: function appObs_Update(aUpdate, aState) {
     let mustSave = false;
     let app = this.app;
 
     let setStatus = function appObs_setStatus(aStatus) {
-      mustSave = (app.status != aStatus);
-      app.status = aStatus;
+      mustSave = (app.installState != aStatus);
+      app.installState = aStatus;
       DOMApplicationRegistry.broadcastMessage("Webapps:OfflineCache",
                                               { manifest: app.manifestURL,
-                                                status: aStatus });
+                                                installState: app.installState });
+    }
+
+    let setError = function appObs_setError(aError) {
+      DOMApplicationRegistry.broadcastMessage("Webapps:OfflineCache",
+                                              { manifest: app.manifestURL,
+                                                error: aError });
     }
 
     switch (aState) {
       case Ci.nsIOfflineCacheUpdateObserver.STATE_ERROR:
         aUpdate.removeObserver(this);
-        setStatus("cache-error");
+        setError("APP_CACHE_DOWNLOAD_ERROR");
         break;
       case Ci.nsIOfflineCacheUpdateObserver.STATE_NOUPDATE:
       case Ci.nsIOfflineCacheUpdateObserver.STATE_FINISHED:
         aUpdate.removeObserver(this);
-        setStatus("cached");
+        setStatus("installed");
         break;
       case Ci.nsIOfflineCacheUpdateObserver.STATE_DOWNLOADING:
       case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMSTARTED:
       case Ci.nsIOfflineCacheUpdateObserver.STATE_ITEMPROGRESS:
-        setStatus("downloading")
+        setStatus(this.startStatus);
         break;
     }
 
     // Status changed, update the stored version.
     if (mustSave) {
       DOMApplicationRegistry._saveApps();
     }
   },
--- a/dom/apps/src/Webapps.manifest
+++ b/dom/apps/src/Webapps.manifest
@@ -1,7 +1,10 @@
 # Webapps.js
 component {fff440b3-fae2-45c1-bf03-3b5a2e432270} Webapps.js
 contract @mozilla.org/webapps;1 {fff440b3-fae2-45c1-bf03-3b5a2e432270}
 category JavaScript-navigator-property mozApps @mozilla.org/webapps;1
 
 component {723ed303-7757-4fb0-b261-4f78b1f6bd22} Webapps.js
 contract @mozilla.org/webapps/application;1 {723ed303-7757-4fb0-b261-4f78b1f6bd22}
+
+component {dcc1d5b7-43d8-4740-9244-b3d8db0f503d} Webapps.js
+contract @mozilla.org/dom-error;1 {dcc1d5b7-43d8-4740-9244-b3d8db0f503d}
--- a/dom/base/nsIDOMDOMError.idl
+++ b/dom/base/nsIDOMDOMError.idl
@@ -1,13 +1,13 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-[scriptable, builtinclass, uuid(e4e28307-d409-4cf7-93cd-6ea8e889f87a)]
+[scriptable, uuid(e4e28307-d409-4cf7-93cd-6ea8e889f87a)]
 interface nsIDOMDOMError : nsISupports
 {
   readonly attribute DOMString name;
 };
--- a/dom/bluetooth/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/BluetoothHfpManager.cpp
@@ -5,22 +5,25 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BluetoothHfpManager.h"
 
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothService.h"
 #include "BluetoothServiceUuid.h"
 
+#include "mozilla/Services.h"
+#include "nsIObserverService.h"
+
 USING_BLUETOOTH_NAMESPACE
 using namespace mozilla::ipc;
 
 static nsRefPtr<BluetoothHfpManager> sInstance = nullptr;
 
-BluetoothHfpManager::BluetoothHfpManager()
+BluetoothHfpManager::BluetoothHfpManager() : mCurrentVgs(-1)
 {
 }
 
 BluetoothHfpManager::~BluetoothHfpManager()
 {
 }
 
 //static
@@ -36,16 +39,89 @@ BluetoothHfpManager::Get()
   return sInstance;
 }
 
 // Virtual function of class SocketConsumer
 void
 BluetoothHfpManager::ReceiveSocketData(UnixSocketRawData* aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread());
+
+  const char* msg = (const char*)aMessage->mData;
+
+  // For more information, please refer to 4.34.1 "Bluetooth Defined AT
+  // Capabilities" in Bluetooth hands-free profile 1.6
+  if (!strncmp(msg, "AT+BRSF=", 8)) {
+    SendLine("+BRSF: 23");
+    SendLine("OK");
+  } else if (!strncmp(msg, "AT+CIND=?", 9)) {
+    nsAutoCString cindRange;
+
+    cindRange += "+CIND: ";
+    cindRange += "(\"battchg\",(0-5)),";
+    cindRange += "(\"signal\",(0-5)),";
+    cindRange += "(\"service\",(0,1)),";
+    cindRange += "(\"call\",(0,1)),";
+    cindRange += "(\"callsetup\",(0-3)),";
+    cindRange += "(\"callheld\",(0-2)),";
+    cindRange += "(\"roam\",(0,1))";
+
+    SendLine(cindRange.get());
+    SendLine("OK");
+  } else if (!strncmp(msg, "AT+CIND", 7)) {
+    // FIXME - Bug 794349
+    // This value reflects current status of telephony, roaming, battery ...,
+    // so obviously fixed value must be wrong if there is an ongoing call. 
+    // Need a patch for this, but currently just using fixed value for basic 
+    // SLC establishment.
+    SendLine("+CIND: 5,5,1,0,0,0,0");
+    SendLine("OK");
+  } else if (!strncmp(msg, "AT+CMER=", 8)) {
+    SendLine("OK");
+  } else if (!strncmp(msg, "AT+CHLD=?", 9)) {
+    SendLine("+CHLD: (0,1,2,3)");
+    SendLine("OK");
+  } else if (!strncmp(msg, "AT+CHLD=", 8)) {
+    SendLine("OK");
+  } else if (!strncmp(msg, "AT+VGS=", 7)) {
+    // VGS range: [0, 15]
+    int newVgs = msg[7] - '0';
+
+    if (strlen(msg) > 8) {
+      newVgs *= 10;
+      newVgs += (msg[8] - '0');
+    }
+
+#ifdef DEBUG
+    NS_ASSERTION(newVgs >= 0 && newVgs <= 15, "Received invalid VGS value");
+#endif
+
+    // Currently, we send volume up/down commands to represent that
+    // volume has been changed by Bluetooth headset, and that will affect
+    // the main stream volume of our device. In the future, we may want to
+    // be able to set volume by stream.
+    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+    if (newVgs > mCurrentVgs) {
+      os->NotifyObservers(nullptr, "bluetooth-volume-change", NS_LITERAL_STRING("up").get());
+    } else if (newVgs < mCurrentVgs) {
+      os->NotifyObservers(nullptr, "bluetooth-volume-change", NS_LITERAL_STRING("down").get());
+    }
+
+    mCurrentVgs = newVgs;
+
+    SendLine("OK");
+  } else {
+#ifdef DEBUG
+    nsCString warningMsg;
+    warningMsg.AssignLiteral("Not handling HFP message, reply ok: ");
+    warningMsg.Append(msg);
+    NS_WARNING(warningMsg.get());
+#endif
+    SendLine("OK");
+  }
 }
 
 bool
 BluetoothHfpManager::Connect(const nsAString& aDeviceObjectPath,
                              BluetoothReplyRunnable* aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
@@ -73,8 +149,21 @@ BluetoothHfpManager::Connect(const nsASt
 }
 
 void
 BluetoothHfpManager::Disconnect()
 {
   CloseSocket();
 }
 
+bool
+BluetoothHfpManager::SendLine(const char* aMessage)
+{
+  const char* kHfpCrlf = "\xd\xa";
+  nsAutoCString msg;
+
+  msg += kHfpCrlf;
+  msg += aMessage;
+  msg += kHfpCrlf;
+
+  return SendSocketData(msg);
+}
+
--- a/dom/bluetooth/BluetoothHfpManager.h
+++ b/dom/bluetooth/BluetoothHfpManager.h
@@ -20,16 +20,19 @@ public:
   ~BluetoothHfpManager();
 
   static BluetoothHfpManager* Get();
   void ReceiveSocketData(mozilla::ipc::UnixSocketRawData* aMessage);
 
   bool Connect(const nsAString& aDeviceObjectPath,
                BluetoothReplyRunnable* aRunnable);
   void Disconnect();
+  bool SendLine(const char* aMessage);
 
 private:
   BluetoothHfpManager();
+
+  int mCurrentVgs;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/browser-element/BrowserElementChild.js
+++ b/dom/browser-element/BrowserElementChild.js
@@ -670,16 +670,28 @@ BrowserElementChild.prototype = {
 
       sendAsyncMsg('securitychange', {state: stateDesc, extendedValidation: isEV});
     },
 
     onStatusChange: function(webProgress, request, status, message) {},
     onProgressChange: function(webProgress, request, curSelfProgress,
                                maxSelfProgress, curTotalProgress, maxTotalProgress) {},
   },
+
+  // Expose the message manager for WebApps and others.
+  _messageManagerPublic: {
+    sendAsyncMessage: global.sendAsyncMessage.bind(global),
+    sendSyncMessage: global.sendSyncMessage.bind(global),
+    addMessageListener: global.addMessageListener.bind(global),
+    removeMessageListener: global.removeMessageListener.bind(global)
+  },
+
+  get messageManager() {
+    return this._messageManagerPublic;
+  }
 };
 
 var api = new BrowserElementChild();
 
 // FIXME/bug 775438: use a JSM?
 //
 // The code in this included file depends on the |addEventListener|,
 // |addMessageListener|, |content|, |Geometry| and |Services| symbols
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -8,16 +8,21 @@ let Cu = Components.utils;
 let Ci = Components.interfaces;
 let Cc = Components.classes;
 let Cr = Components.results;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
 
+XPCOMUtils.defineLazyGetter(this, "DOMApplicationRegistry", function () {
+  Cu.import("resource://gre/modules/Webapps.jsm");
+  return DOMApplicationRegistry;
+});
+
 const NS_PREFBRANCH_PREFCHANGE_TOPIC_ID = "nsPref:changed";
 const BROWSER_FRAMES_ENABLED_PREF = "dom.mozBrowserFramesEnabled";
 const TOUCH_EVENTS_ENABLED_PREF = "dom.w3c_touch_events.enabled";
 
 function debug(msg) {
   //dump("BrowserElementParent - " + msg + "\n");
 }
 
@@ -245,16 +250,28 @@ function BrowserElementParent(frameLoade
   // down to the child.
   this._window.addEventListener('mozvisibilitychange',
                                 this._ownerVisibilityChange.bind(this),
                                 /* useCapture = */ false,
                                 /* wantsUntrusted = */ false);
 
   // Insert ourself into the prompt service.
   BrowserElementPromptService.mapFrameToBrowserElementParent(this._frameElement, this);
+
+  // If this browser represents an app then let the Webapps module register for
+  // any messages that it needs.
+  let appManifestURL =
+    this._frameElement.QueryInterface(Ci.nsIMozBrowserFrame).appManifestURL;
+  if (appManifestURL) {
+    let appId =
+      DOMApplicationRegistry.getAppLocalIdByManifestURL(appManifestURL);
+    if (appId != Ci.nsIScriptSecurityManager.NO_APP_ID) {
+      DOMApplicationRegistry.registerBrowserElementParentForApp(this, appId);
+    }
+  }
 }
 
 BrowserElementParent.prototype = {
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference]),
 
   /**
--- a/dom/interfaces/apps/Makefile.in
+++ b/dom/interfaces/apps/Makefile.in
@@ -11,16 +11,17 @@ VPATH          = @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 MODULE         = dom
 XPIDL_MODULE   = dom_apps
 GRE_MODULE     = 1
 
 XPIDLSRCS =                               \
             mozIApplication.idl \
+            mozIApplicationClearPrivateDataParams.idl \
             nsIDOMApplicationRegistry.idl \
             nsIDOMApplicationRegistry2.idl \
             nsIAppsService.idl \
             nsIDOMMozApplicationEvent.idl \
             $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/dom/interfaces/apps/mozIApplicationClearPrivateDataParams.idl
@@ -0,0 +1,15 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(ba0e6c8e-8c03-4b9b-8f9b-4fb14216f56e)]
+interface mozIApplicationClearPrivateDataParams : nsISupports
+{
+  readonly attribute unsigned long appId;
+  readonly attribute boolean browserOnly;
+};
--- a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
+++ b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
@@ -1,54 +1,96 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 #include "nsIDOMEventTarget.idl"
 
 interface nsIDOMDOMRequest;
-interface nsIArray;
+interface nsIDOMDOMError;
 
-[scriptable, uuid(e3649c1d-c950-495e-b0ed-6ce40be9743b)]
+[scriptable, uuid(b00a5908-1228-46bf-a42b-091dce3abde1)]
 interface mozIDOMApplication  : nsISupports
 {
   readonly attribute jsval manifest;
+  readonly attribute jsval updateManifest;
   readonly attribute DOMString manifestURL;
   readonly attribute jsval receipts; /* an array of strings */
   readonly attribute DOMString origin;
   readonly attribute DOMString installOrigin;
   readonly attribute unsigned long long installTime;
   readonly attribute boolean removable;
 
-  /* 
+  /**
    * The current progress when downloading an offline cache.
    */
   readonly attribute double progress;
 
-  /*
-   * The application status :
-   * "installed"   : The app is in the registry, but we have no offline cache.
-   * "downlading"  : We are downloading the offline cache.
-   * "cached"      : We are done with the offline cache download.
-   * "cache-error" : An error occured while downloading the offline-cache.
+  /**
+   * The application installation state :
+   * "pending"   : The application is being installed (eg, we're downloading the
+   *               offline cache or the package).
+   * "installed" : The application is installed and ready to be launched.
+   * "updating"  : We are updating the offline-cache or the package.
    */
-  readonly attribute DOMString status;
+  readonly attribute DOMString installState;
 
-  /*
-   * fires a nsIDOMApplicationEvent when a change in appcache download or status happens
+  /**
+   * fires a nsIDOMApplicationEvent when a change in appcache download or
+   * package download happens.
    */
   attribute nsIDOMEventListener onprogress;
 
+  /**
+   * The date of the last update.
+   */
+  readonly attribute unsigned long long lastUpdateCheck;
+
+  /**
+   * Starts the process of looking for an update.
+   */
+  nsIDOMDOMRequest checkForUpdate();
+
+  readonly attribute boolean downloadAvailable;
+  readonly attribute boolean downloading;
+  readonly attribute boolean readyToApplyDownload;
+  readonly attribute long downloadSize;
+
+  readonly attribute nsIDOMDOMError downloadError;
+
+  attribute nsIDOMEventListener ondownloadsuccess;
+  attribute nsIDOMEventListener ondownloaderror;
+  attribute nsIDOMEventListener ondownloadavailable;
+
+  /**
+   * Will fire once the mgmt.applyDownload() call succeeds.
+   */
+  attribute nsIDOMEventListener ondownloadapplied;
+
+  /**
+   * Starts to download an update. If |downloading| is true, this
+   * is a no-op.
+   */
+  void download();
+
+  /**
+   * Cancels an ongoing update download.
+   */
+  void cancelDownload();
+
   /* startPoint will be used when several launch_path exists for an app */
   nsIDOMDOMRequest launch([optional] in DOMString startPoint);
   nsIDOMDOMRequest uninstall();
+
+  /* Clear data that has been collected through mozbrowser elements. */
+  void clearBrowserData();
 };
 
-[scriptable, uuid(4c36ca48-841e-4d5a-8c46-dda14ac633ca)]
+[scriptable, uuid(0015d114-70c1-44ae-a8a3-fb6c107fe0e1)]
 interface mozIDOMApplicationMgmt : nsISupports
 {
   /**
    * the request will return the all the applications installed. Only accessible
    * to privileged callers.
    */
   nsIDOMDOMRequest getAll();
 
@@ -66,16 +108,23 @@ interface mozIDOMApplicationMgmt : nsISu
   attribute nsIDOMEventListener oninstall;
 
   /**
    * event listener to get notified of application uninstalls. Only settable by
    * privileged callers.
    * the event will be a mozIDOMApplicationEvent
    */
   attribute nsIDOMEventListener onuninstall;
+
+  /**
+   * Applies a downloaded update.
+   * This function is a no-op if it's passed an app object which doesn't have
+   * |readyToApplyDownload| set to true.
+   */
+  void applyDownload(in mozIDOMApplication app);
 };
 
 [scriptable, uuid(7ca34d3e-d855-4d0a-a3b3-58c0acad9ec3)]
 interface mozIDOMApplicationRegistry : nsISupports
 {
   /**
    * Install a web app.
    *
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -166,32 +166,24 @@ nsNPAPIPluginInstance::nsNPAPIPluginInst
     mWindowless(false),
     mTransparent(false),
     mCached(false),
     mUsesDOMForCursor(false),
     mInPluginInitCall(false),
     mPlugin(nullptr),
     mMIMEType(nullptr),
     mOwner(nullptr),
-    mCurrentPluginEvent(nullptr),
-#if defined(MOZ_X11) || defined(XP_WIN) || defined(XP_MACOSX)
-    mUsePluginLayersPref(true)
-#else
-    mUsePluginLayersPref(false)
-#endif
+    mCurrentPluginEvent(nullptr)
 #ifdef MOZ_WIDGET_ANDROID
   , mOnScreen(true)
 #endif
 {
   mNPP.pdata = NULL;
   mNPP.ndata = this;
 
-  mUsePluginLayersPref =
-    Preferences::GetBool("plugins.use_layers", mUsePluginLayersPref);
-
   PLUGIN_LOG(PLUGIN_LOG_BASIC, ("nsNPAPIPluginInstance ctor: this=%p\n",this));
 }
 
 nsNPAPIPluginInstance::~nsNPAPIPluginInstance()
 {
   PLUGIN_LOG(PLUGIN_LOG_BASIC, ("nsNPAPIPluginInstance dtor: this=%p\n",this));
 
   if (mMIMEType) {
@@ -1207,23 +1199,18 @@ nsNPAPIPluginInstance::GetImageSize(nsIn
 nsresult
 nsNPAPIPluginInstance::NotifyPainted(void)
 {
   NS_NOTREACHED("Dead code, shouldn't be called.");
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 nsresult
-nsNPAPIPluginInstance::UseAsyncPainting(bool* aIsAsync)
+nsNPAPIPluginInstance::GetIsOOP(bool* aIsAsync)
 {
-  if (!mUsePluginLayersPref) {
-    *aIsAsync = mUsePluginLayersPref;
-    return NS_OK;
-  }
-
   AutoPluginLibraryCall library(this);
   if (!library)
     return NS_ERROR_FAILURE;
 
   *aIsAsync = library->IsOOP();
   return NS_OK;
 }
 
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -79,17 +79,17 @@ public:
   nsresult IsRemoteDrawingCoreAnimation(bool* aDrawing);
   nsresult GetJSObject(JSContext *cx, JSObject** outObject);
   bool ShouldCache();
   nsresult IsWindowless(bool* isWindowless);
   nsresult AsyncSetWindow(NPWindow* window);
   nsresult GetImageContainer(ImageContainer **aContainer);
   nsresult GetImageSize(nsIntSize* aSize);
   nsresult NotifyPainted(void);
-  nsresult UseAsyncPainting(bool* aIsAsync);
+  nsresult GetIsOOP(bool* aIsOOP);
   nsresult SetBackgroundUnknown();
   nsresult BeginUpdateBackground(nsIntRect* aRect, gfxContext** aContext);
   nsresult EndUpdateBackground(gfxContext* aContext, nsIntRect* aRect);
   nsresult IsTransparent(bool* isTransparent);
   nsresult GetFormValue(nsAString& aValue);
   nsresult PushPopupsEnabledState(bool aEnabled);
   nsresult PopPopupsEnabledState();
   nsresult GetPluginAPIVersion(uint16_t* version);
@@ -338,17 +338,16 @@ private:
 
   // non-null during a HandleEvent call
   void* mCurrentPluginEvent;
 
   // Timestamp for the last time this plugin was stopped.
   // This is only valid when the plugin is actually stopped!
   mozilla::TimeStamp mStopTime;
 
-  bool mUsePluginLayersPref;
 #ifdef MOZ_WIDGET_ANDROID
   void EnsureSharedTexture();
   nsSurfaceTexture* CreateSurfaceTexture();
 
   std::map<void*, VideoInfo*> mVideos;
   bool mOnScreen;
 
   nsIntSize mCurrentSize;
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -258,20 +258,19 @@ bool
 nsPluginInstanceOwner::UseAsyncRendering()
 {
 #ifdef XP_MACOSX
   if (mUseAsyncRendering) {
     return true;
   }
 #endif
 
-  bool useAsyncRendering;
+  bool isOOP;
   bool result = (mInstance &&
-          NS_SUCCEEDED(mInstance->UseAsyncPainting(&useAsyncRendering)) &&
-          useAsyncRendering
+          NS_SUCCEEDED(mInstance->GetIsOOP(&isOOP)) && isOOP
 #ifndef XP_MACOSX
           && (!mPluginWindow ||
            mPluginWindow->type == NPWindowTypeDrawable)
 #endif
           );
 
 #ifdef XP_MACOSX
   if (result) {
--- a/dom/plugins/ipc/PPluginInstance.ipdl
+++ b/dom/plugins/ipc/PPluginInstance.ipdl
@@ -141,20 +141,16 @@ child:
     returns (bool handled);
 
   async NPP_DidComposite();
 
   rpc NPP_Destroy()
     returns (NPError rv);
 
 parent:
-  rpc NPN_GetValue_NPNVjavascriptEnabledBool()
-    returns (bool value, NPError result);
-  rpc NPN_GetValue_NPNVisOfflineBool()
-    returns (bool value, NPError result);
   rpc NPN_GetValue_NPNVWindowNPObject()
     returns (nullable PPluginScriptableObject value, NPError result);
   rpc NPN_GetValue_NPNVPluginElementNPObject()
     returns (nullable PPluginScriptableObject value, NPError result);
   rpc NPN_GetValue_NPNVprivateModeBool()
     returns (bool value, NPError result);
   rpc NPN_GetValue_NPNVnetscapeWindow()
     returns (NativeWindowHandle value, NPError result);
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -282,41 +282,29 @@ NPError
 PluginInstanceChild::NPN_GetValue(NPNVariable aVar,
                                   void* aValue)
 {
     PLUGIN_LOG_DEBUG(("%s (aVar=%i)", FULLFUNCTION, (int) aVar));
     AssertPluginThread();
 
     switch(aVar) {
 
-    case NPNVSupportsWindowless:
-#if defined(OS_LINUX) || defined(MOZ_X11) || defined(OS_WIN)
-        *((NPBool*)aValue) = true;
-#else
-        *((NPBool*)aValue) = false;
-#endif
-        return NPERR_NO_ERROR;
-
 #if (MOZ_PLATFORM_MAEMO == 5) || (MOZ_PLATFORM_MAEMO == 6)
     case NPNVSupportsWindowlessLocal: {
 #ifdef MOZ_WIDGET_QT
         const char *graphicsSystem = PR_GetEnv("MOZ_QT_GRAPHICSSYSTEM");
         // we should set local rendering to false in order to render X-Plugin
         // there is no possibility to change it later on maemo5 platform
         mMaemoImageRendering = (!(graphicsSystem && !strcmp(graphicsSystem, "native")));
 #endif
         *((NPBool*)aValue) = mMaemoImageRendering;
         return NPERR_NO_ERROR;
     }
 #endif
 #if defined(MOZ_X11)
-    case NPNVSupportsXEmbedBool:
-        *((NPBool*)aValue) = true;
-        return NPERR_NO_ERROR;
-
     case NPNVToolkit:
         *((NPNToolkitType*)aValue) = NPNVGtk2;
         return NPERR_NO_ERROR;
 
     case NPNVxDisplay:
         if (!mWsInfo.display) {
             // We are called before Initialize() so we have to call it now.
            Initialize();
@@ -324,36 +312,16 @@ PluginInstanceChild::NPN_GetValue(NPNVar
         }
         *(void **)aValue = mWsInfo.display;
         return NPERR_NO_ERROR;
     
 #elif defined(OS_WIN)
     case NPNVToolkit:
         return NPERR_GENERIC_ERROR;
 #endif
-    case NPNVjavascriptEnabledBool: {
-        bool v = false;
-        NPError result;
-        if (!CallNPN_GetValue_NPNVjavascriptEnabledBool(&v, &result)) {
-            return NPERR_GENERIC_ERROR;
-        }
-        *static_cast<NPBool*>(aValue) = v;
-        return result;
-    }
-
-    case NPNVisOfflineBool: {
-        bool v = false;
-        NPError result;
-        if (!CallNPN_GetValue_NPNVisOfflineBool(&v, &result)) {
-            return NPERR_GENERIC_ERROR;
-        }
-        *static_cast<NPBool*>(aValue) = v;
-        return result;
-    }
-
     case NPNVprivateModeBool: {
         bool v = false;
         NPError result;
         if (!CallNPN_GetValue_NPNVprivateModeBool(&v, &result)) {
             return NPERR_GENERIC_ERROR;
         }
         *static_cast<NPBool*>(aValue) = v;
         return result;
@@ -463,16 +431,25 @@ PluginInstanceChild::NPN_GetValue(NPNVar
 #ifndef NP_NO_QUICKDRAW
     case NPNVsupportsQuickDrawBool: {
         *((NPBool*)aValue) = false;
         return NPERR_NO_ERROR;
     }
 #endif /* NP_NO_QUICKDRAW */
 #endif /* XP_MACOSX */
 
+#ifdef DEBUG
+    case NPNVjavascriptEnabledBool:
+    case NPNVasdEnabledBool:
+    case NPNVisOfflineBool:
+    case NPNVSupportsXEmbedBool:
+    case NPNVSupportsWindowless:
+        NS_NOTREACHED("NPNVariable should be handled in PluginModuleChild.");
+#endif
+
     default:
         PR_LOG(gPluginLog, PR_LOG_WARNING,
                ("In PluginInstanceChild::NPN_GetValue: Unhandled NPNVariable %i (%s)",
                 (int) aVar, NPNVariableToString(aVar)));
         return NPERR_GENERIC_ERROR;
     }
 
 }
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -187,37 +187,16 @@ PluginInstanceParent::AllocPPluginStream
 bool
 PluginInstanceParent::DeallocPPluginStream(PPluginStreamParent* stream)
 {
     delete stream;
     return true;
 }
 
 bool
-PluginInstanceParent::AnswerNPN_GetValue_NPNVjavascriptEnabledBool(
-                                                       bool* value,
-                                                       NPError* result)
-{
-    NPBool v;
-    *result = mNPNIface->getvalue(mNPP, NPNVjavascriptEnabledBool, &v);
-    *value = v;
-    return true;
-}
-
-bool
-PluginInstanceParent::AnswerNPN_GetValue_NPNVisOfflineBool(bool* value,
-                                                           NPError* result)
-{
-    NPBool v;
-    *result = mNPNIface->getvalue(mNPP, NPNVisOfflineBool, &v);
-    *value = v;
-    return true;
-}
-
-bool
 PluginInstanceParent::AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle* value,
                                                             NPError* result)
 {
 #ifdef XP_WIN
     HWND id;
 #elif defined(MOZ_X11)
     XID id;
 #elif defined(XP_MACOSX)
--- a/dom/plugins/ipc/PluginInstanceParent.h
+++ b/dom/plugins/ipc/PluginInstanceParent.h
@@ -84,20 +84,16 @@ public:
     virtual PPluginStreamParent*
     AllocPPluginStream(const nsCString& mimeType,
                        const nsCString& target,
                        NPError* result);
     virtual bool
     DeallocPPluginStream(PPluginStreamParent* stream);
 
     virtual bool
-    AnswerNPN_GetValue_NPNVjavascriptEnabledBool(bool* value, NPError* result);
-    virtual bool
-    AnswerNPN_GetValue_NPNVisOfflineBool(bool* value, NPError* result);
-    virtual bool
     AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle* value,
                                           NPError* result);
     virtual bool
     AnswerNPN_GetValue_NPNVWindowNPObject(
                                        PPluginScriptableObjectParent** value,
                                        NPError* result);
     virtual bool
     AnswerNPN_GetValue_NPNVPluginElementNPObject(
--- a/dom/src/storage/nsDOMStorage.cpp
+++ b/dom/src/storage/nsDOMStorage.cpp
@@ -32,16 +32,18 @@ using mozilla::dom::ContentChild;
 #include "nsIJSContextStack.h"
 #include "nsDOMString.h"
 #include "nsNetCID.h"
 #include "mozilla/Preferences.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Telemetry.h"
 #include "DictionaryHelpers.h"
 #include "GeneratedEvents.h"
+#include "nsIAppsService.h"
+#include "mozIApplication.h"
 
 // calls FlushAndDeleteTemporaryTables(false)
 #define NS_DOMSTORAGE_FLUSH_TIMER_TOPIC "domstorage-flush-timer"
 
 // calls FlushAndDeleteTemporaryTables(true)
 #define NS_DOMSTORAGE_FLUSH_FORCE_TOPIC "domstorage-flush-force"
 
 using namespace mozilla;
@@ -157,16 +159,18 @@ nsDOMStorageManager::Initialize()
   NS_ENSURE_SUCCESS(rv, rv);
   // Used for temporary table flushing
   rv = os->AddObserver(gStorageManager, "profile-before-change", true);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = os->AddObserver(gStorageManager, NS_DOMSTORAGE_FLUSH_TIMER_TOPIC, true);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = os->AddObserver(gStorageManager, "last-pb-context-exited", true);
   NS_ENSURE_SUCCESS(rv, rv);
+  rv = os->AddObserver(gStorageManager, "webapps-uninstall", true);
+  NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 //static
 nsDOMStorageManager*
 nsDOMStorageManager::GetInstance()
 {
@@ -302,16 +306,33 @@ nsDOMStorageManager::Observe(nsISupports
         DOMStorageImpl::gStorageDB->FlushAndDeleteTemporaryTables(true);
       NS_WARN_IF_FALSE(NS_SUCCEEDED(rv),
                        "DOMStorage: temporary table commit failed");
     }
   } else if (!strcmp(aTopic, "last-pb-context-exited")) {
     if (DOMStorageImpl::gStorageDB) {
       return DOMStorageImpl::gStorageDB->DropPrivateBrowsingStorages();
     }
+  } else if (!strcmp(aTopic, "webapps-uninstall")) {
+    if (!DOMStorageImpl::gStorageDB) {
+      return NS_OK;
+    }
+
+    nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
+    nsCOMPtr<mozIApplication> app;
+
+    appsService->GetAppFromObserverMessage(nsAutoString(aData), getter_AddRefs(app));
+    NS_ENSURE_TRUE(app, NS_ERROR_UNEXPECTED);
+
+    uint32_t appId;
+    app->GetLocalId(&appId);
+    MOZ_ASSERT(appId != nsIScriptSecurityManager::NO_APP_ID);
+
+    return DOMStorageImpl::gStorageDB->RemoveAllForApp(appId,
+                                                       /* onlyBrowserElements */ false);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMStorageManager::GetUsage(const nsAString& aDomain,
                               int32_t *aUsage)
--- a/dom/src/storage/nsDOMStorageDBWrapper.cpp
+++ b/dom/src/storage/nsDOMStorageDBWrapper.cpp
@@ -198,16 +198,25 @@ nsDOMStorageDBWrapper::RemoveAll()
 
   rv = mPersistentDB.RemoveAll();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return rv;
 }
 
 nsresult
+nsDOMStorageDBWrapper::RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement)
+{
+  // We only care about removing the permament storage. Temporary storage such
+  // as session storage or private browsing storage will not be re-used anyway
+  // and will be automatically deleted at some point.
+  return mPersistentDB.RemoveAllForApp(aAppId, aOnlyBrowserElement);
+}
+
+nsresult
 nsDOMStorageDBWrapper::GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage)
 {
   IMPL_FORWARDER(GetUsage(aStorage, aUsage));
 }
 
 nsresult
 nsDOMStorageDBWrapper::GetUsage(const nsACString& aDomain,
                                 int32_t *aUsage, bool aPrivate)
--- a/dom/src/storage/nsDOMStorageDBWrapper.h
+++ b/dom/src/storage/nsDOMStorageDBWrapper.h
@@ -133,16 +133,25 @@ public:
 
   /**
    * Removes all keys from storage. Used when clearing storage.
    */
   nsresult
   RemoveAll();
 
   /**
+   * Removes all keys from storage for a specific app.
+   * If aOnlyBrowserElement is true, it will remove only keys with the
+   * browserElement flag set.
+   * aAppId has to be a valid app id. It can't be NO_APP_ID or UNKNOWN_APP_ID.
+   */
+  nsresult
+  RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement);
+
+  /**
     * Returns usage for a storage using its GetQuotaDBKey() as a key.
     */
   nsresult
   GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage);
 
   /**
     * Returns usage of the domain and optionaly by any subdomain.
     */
--- a/dom/src/storage/nsDOMStoragePersistentDB.cpp
+++ b/dom/src/storage/nsDOMStoragePersistentDB.cpp
@@ -660,16 +660,49 @@ nsDOMStoragePersistentDB::RemoveAll()
   NS_ENSURE_SUCCESS(rv, rv);
 
   MarkAllScopesDirty();
 
   return NS_OK;
 }
 
 nsresult
+nsDOMStoragePersistentDB::RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement)
+{
+  nsresult rv;
+
+  rv = MaybeCommitInsertTransaction();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<mozIStorageStatement> stmt = mStatements.GetCachedStatement(
+    "DELETE FROM webappsstore2_view "
+    "WHERE scope LIKE :scope"
+  );
+  NS_ENSURE_STATE(stmt);
+  mozStorageStatementScoper scopeStmt(stmt);
+
+  nsAutoCString scope;
+  scope.AppendInt(aAppId);
+  if (aOnlyBrowserElement) {
+    scope.Append(NS_LITERAL_CSTRING(":t:%"));
+  } else {
+    scope.Append(NS_LITERAL_CSTRING(":_:%"));
+  }
+  rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("scope"), scope);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = stmt->Execute();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  MarkAllScopesDirty();
+
+  return NS_OK;
+}
+
+nsresult
 nsDOMStoragePersistentDB::GetUsage(DOMStorageImpl* aStorage,
                                    int32_t *aUsage)
 {
   return GetUsageInternal(aStorage->GetQuotaDBKey(), aUsage);
 }
 
 nsresult
 nsDOMStoragePersistentDB::GetUsage(const nsACString& aDomain,
--- a/dom/src/storage/nsDOMStoragePersistentDB.h
+++ b/dom/src/storage/nsDOMStoragePersistentDB.h
@@ -94,16 +94,25 @@ public:
 
   /**
    * Removes all keys from storage. Used when clearing storage.
    */
   nsresult
   RemoveAll();
 
   /**
+   * Removes all keys from storage for a specific app.
+   * If aOnlyBrowserElement is true, it will remove only keys with the
+   * browserElement flag set.
+   * aAppId has to be a valid app id. It can't be NO_APP_ID or UNKNOWN_APP_ID.
+   */
+  nsresult
+  RemoveAllForApp(uint32_t aAppId, bool aOnlyBrowserElement);
+
+  /**
     * Returns usage for a storage using its GetQuotaDBKey() as a key.
     */
   nsresult
   GetUsage(DOMStorageImpl* aStorage, int32_t *aUsage);
 
   /**
     * Returns usage of the domain and optionaly by any subdomain.
     */
--- a/dom/tests/mochitest/localstorage/Makefile.in
+++ b/dom/tests/mochitest/localstorage/Makefile.in
@@ -54,11 +54,12 @@ MOCHITEST_FILES	= \
     test_localStorageQuotaSessionOnly.html \
     test_localStorageQuotaSessionOnly2.html \
     test_localStorageKeyOrder.html \
     test_storageConstructor.html \
     $(NULL)
 
 MOCHITEST_CHROME_FILES = \
     test_localStorageFromChrome.xhtml \
+    test_app_uninstall.html \
     $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/localstorage/test_app_uninstall.html
@@ -0,0 +1,236 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=786301
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Tests that uninstalling app removes the localStorage data</title>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=786296">Mozilla Bug 786301</a>
+<p id="display"></p>
+<div id="content">
+  <iframe src="http://example.com/tests/error404"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+/** Test for Bug 786301 **/
+
+/*
+ * This test will check that localStorage data are correctly deleted when an app
+ * is uninstalled.
+ * Here is the big picture of what the test does:
+ * 1. Setup permissions and preferences.
+ * 2. Install a dummy application and embed it in an iframe.
+ * 3. Load a mozbrowser iframe from this application.
+ * 4. Fill storages for the app and the mozbrowser iframe.
+ * 5. Uninstall the application.
+ *
+ * Expected result: all localStorage data from the app and mozbrowser have been
+ * deleted but sessionStorage stays untouched such as non-app/browser data.
+ *
+ * This test is asynchronous and methods are called in a reading order.
+ * Each method has a deeper explanation of what it does
+ */
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+SimpleTest.waitForExplicitFinish();
+
+var permManager = Cc["@mozilla.org/permissionmanager;1"]
+                    .getService(Ci.nsIPermissionManager);
+var appsService = Cc['@mozilla.org/AppsService;1']
+                    .getService(Ci.nsIAppsService);
+
+/**
+ * This function will make sure that the next applications we try to install
+ * will be installed. That means it will behave like if the user allowed the app
+ * to be installed in the door hanger.
+ */
+function confirmNextInstall() {
+  var panel = window.top.QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIWebNavigation)
+                        .QueryInterface(Ci.nsIDocShell)
+                        .chromeEventHandler.ownerDocument.defaultView
+                        .PopupNotifications.panel
+
+  panel.addEventListener("popupshown", function() {
+    panel.removeEventListener("popupshown", arguments.callee, false);
+    this.childNodes[0].button.doCommand();
+  }, false);
+}
+
+/**
+ * Initialize the |storage| that has been given with "foo" => "bar".
+ * Checks that the storage wasn't initialized and checks that the initialization
+ * was successful.
+ */
+function setupStorage(storage) {
+  is(storage.getItem("foo"), null, "no data");
+
+  storage.setItem("foo", "bar");
+  is(storage.getItem("foo"), "bar", "data written");
+}
+
+permManager.addFromPrincipal(window.document.nodePrincipal, "webapps-manage",
+                             Ci.nsIPermissionManager.ALLOW_ACTION);
+permManager.addFromPrincipal(window.document.nodePrincipal, "browser",
+                             Ci.nsIPermissionManager.ALLOW_ACTION);
+
+var previousPrefs = {
+  mozBrowserFramesEnabled: null,
+  installerDryRun: null,
+};
+
+// Save the prefs we want to change (so we can re-set them later) and set them
+// to the needed value.
+try {
+  previousPrefs.mozBrowserFramesEnabled = SpecialPowers.getBoolPref('dom.mozBrowserFramesEnabled');
+} catch(e)
+{
+}
+SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', true);
+
+try {
+  previousPrefs.installerDryRun = SpecialPowers.getBoolPref('browser.mozApps.installer.dry_run');
+} catch(e) {
+}
+SpecialPowers.setBoolPref('browser.mozApps.installer.dry_run', true);
+
+// URL of the manifest of the app we want to install.
+const gManifestURL = "http://www.example.com:80/chrome/dom/tests/mochitest/webapps/apps/basic.webapp";
+// ID of the installed app.
+var gTestAppId = 0;
+// Cookies currently in the system.
+var gCurrentCookiesCount = 0;
+// Storages from a non-app to make sure we do not remove cookies from everywhere.
+var gWitnessStorage = {};
+// Storages for the app.
+var gAppStorage = {};
+// Storage for a mozbrowser inside the app.
+var gBrowserStorage = {};
+
+addLoadEvent(function() {
+  /*
+   * We are setuping the witness storage (non-app) and will install the
+   * application.
+   * When the application is installed, we will insert it in an iframe and wait
+   * for the load event. to be fired.
+   */
+
+  confirmNextInstall();
+
+  gWitnessStorage.localStorage = window.frames[0].localStorage;
+  gWitnessStorage.sessionStorage = window.frames[0].sessionStorage;
+
+  setupStorage(gWitnessStorage.localStorage);
+  setupStorage(gWitnessStorage.sessionStorage);
+
+  navigator.mozApps.install(gManifestURL, null).onsuccess = function() {
+    gTestAppId = appsService.getAppLocalIdByManifestURL(gManifestURL);
+
+    var frame = document.createElement('iframe');
+    frame.setAttribute('mozbrowser', '');
+    frame.setAttribute('mozapp', gManifestURL);
+    frame.src = 'http://example.com/tests/error404';
+    frame.name = 'app';
+
+    frame.addEventListener('load', appFrameLoadEvent);
+
+    document.body.appendChild(frame);
+  };
+});
+
+function appFrameLoadEvent() {
+  /*
+   * The app frame has been loaded. We can now add permissions for the app to
+   * create browsers and we will load a page in this browser and wait for the
+   * load event.
+   */
+  permManager.addFromPrincipal(window.frames[1].document.nodePrincipal, "browser",
+                               Ci.nsIPermissionManager.ALLOW_ACTION);
+
+  var frame = document.createElement('iframe');
+  frame.setAttribute('mozbrowser', '');
+  frame.src = 'http://example.com/tests/error404_2';
+
+  frame.addEventListener('load', browserLoadEvent);
+
+  document.getElementsByName('app')[0].contentDocument.body.appendChild(frame);
+}
+
+function browserLoadEvent() {
+  /*
+   * The browser inside the app has loaded.
+   * We can now setup the app and browser storages and uninstall the app.
+   */
+
+  gAppStorage.localStorage = window.frames[1].localStorage;
+  gAppStorage.sessionStorage = window.frames[1].sessionStorage;
+
+  gBrowserStorage.localStorage = window.frames[1].frames[0].localStorage;
+  gBrowserStorage.sessionStorage = window.frames[1].frames[0].sessionStorage;
+
+  setupStorage(gAppStorage.localStorage);
+  setupStorage(gAppStorage.sessionStorage);
+  setupStorage(gBrowserStorage.localStorage);
+  setupStorage(gBrowserStorage.sessionStorage);
+
+  navigator.mozApps.mgmt.getNotInstalled().onsuccess = function() {
+    for (i in this.result) {
+      var app = this.result[i];
+      if (app.manifestURL == gManifestURL) {
+        app.uninstall().onsuccess = function() {
+          /*
+           * Now that the app is uninstalled, we should not find any more
+           * localStorage data from the app or its browsers. However,
+           * sessionStorage is expected to stay.
+           * The witness storage (non-app) should not have changed.
+           */
+          is(gAppStorage.localStorage.getItem("foo"), null, "localstorage data have been deleted");
+          is(gBrowserStorage.localStorage.getItem("foo"), null, "localstorage data have been deleted");
+
+          is(gAppStorage.sessionStorage.getItem("foo"), "bar", "sessionstorage data have not been deleted");
+          is(gBrowserStorage.sessionStorage.getItem("foo"), "bar", "sessionstorage data have not been deleted");
+
+          is(gWitnessStorage.localStorage.getItem("foo"), "bar", "data are still there");
+          is(gWitnessStorage.sessionStorage.getItem("foo"), "bar", "data are still there");
+
+          finish();
+
+          return;
+        };
+      }
+    }
+  };
+}
+
+/**
+ * This method will be called when the test will be done. It is going to clear
+ * all storage data, permissions, etc.
+ */
+function finish() {
+  gWitnessStorage.localStorage.clear();
+  gWitnessStorage.sessionStorage.clear();
+
+  permManager.removeFromPrincipal(window.document.nodePrincipal, "webapps-manage",
+                                  Ci.nsIPermissionManager.ALLOW_ACTION);
+  permManager.removeFromPrincipal(window.document.nodePrincipal, "browser",
+                                  Ci.nsIPermissionManager.ALLOW_ACTION);
+
+  SpecialPowers.setBoolPref('dom.mozBrowserFramesEnabled', previousPrefs.mozBrowserFramesEnabled);
+  SpecialPowers.setBoolPref('browser.mozApps.installer.dry_run', previousPrefs.installerDryRun);
+
+  SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/tests/mochitest/webapps/test_list_api.xul
+++ b/dom/tests/mochitest/webapps/test_list_api.xul
@@ -31,16 +31,17 @@ isDeeply([p for (p in navigator.mozApps)
          "navigator.mozApps has only the expected properties");
 
 for (var p in props) {
   is(typeof navigator.mozApps[p], props[p], "typeof " + p);
 }
 
 var mgmtProps = {
   QueryInterface: "function",
+  applyDownload: "function",
   getAll: "function",
   getNotInstalled: "function",
   oninstall: "object",
   onuninstall: "object",
 };
 
 isDeeply([p for (p in navigator.mozApps.mgmt)].sort(),
          Object.keys(mgmtProps).sort(),
--- a/gfx/layers/ipc/ShmemYCbCrImage.h
+++ b/gfx/layers/ipc/ShmemYCbCrImage.h
@@ -31,17 +31,18 @@ namespace layers {
 class ShmemYCbCrImage
 {
 public:
   typedef mozilla::ipc::Shmem Shmem;
 
   ShmemYCbCrImage() : mOffset(0) {}
 
   ShmemYCbCrImage(Shmem& shm, size_t offset = 0) {
-    NS_ABORT_IF_FALSE(Open(shm,offset), "Invalid data in Shmem.");
+    DebugOnly<bool> status = Open(shm,offset);
+    NS_ASSERTION(status, "Invalid data in the shmem");
   }
 
   /**
    * This function is meant as a helper to know how much shared memory we need
    * to allocate in a shmem in order to place a shared YCbCr image blob of 
    * given dimensions.
    */
   static size_t ComputeMinBufferSize(const gfxIntSize& aYSize,
--- a/js/src/ion/Bailouts.cpp
+++ b/js/src/ion/Bailouts.cpp
@@ -438,17 +438,17 @@ ion::InvalidationBailout(InvalidationBai
     return BAILOUT_RETURN_FATAL_ERROR;
 }
 
 static void
 ReflowArgTypes(JSContext *cx)
 {
     StackFrame *fp = cx->fp();
     unsigned nargs = fp->fun()->nargs;
-    JSScript *script = fp->script();
+    RootedScript script(cx, fp->script());
 
     types::AutoEnterTypeInference enter(cx);
 
     if (!fp->isConstructing())
         types::TypeScript::SetThis(cx, script, fp->thisValue());
     for (unsigned i = 0; i < nargs; ++i)
         types::TypeScript::SetArgument(cx, script, i, fp->unaliasedFormal(i, DONT_CHECK_ALIASING));
 }
@@ -462,17 +462,17 @@ ion::ReflowTypeInfo(uint32 bailoutResult
     IonSpew(IonSpew_Bailouts, "reflowing type info");
 
     if (bailoutResult == BAILOUT_RETURN_ARGUMENT_CHECK) {
         IonSpew(IonSpew_Bailouts, "reflowing type info at argument-checked entry");
         ReflowArgTypes(cx);
         return true;
     }
 
-    JSScript *script = cx->fp()->script();
+    RootedScript script(cx, cx->fp()->script());
     jsbytecode *pc = activation->bailout()->bailoutPc();
 
     JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
 
     IonSpew(IonSpew_Bailouts, "reflowing type info at %s:%d pcoff %d", script->filename,
             script->lineno, pc - script->code);
 
     types::AutoEnterTypeInference enter(cx);
--- a/js/src/ion/Ion.cpp
+++ b/js/src/ion/Ion.cpp
@@ -145,20 +145,20 @@ IonCompartment::initialize(JSContext *cx
         return false;
 
     return true;
 }
 
 void
 ion::FinishOffThreadBuilder(IonBuilder *builder)
 {
-    if (builder->script->isIonCompilingOffThread()) {
-        types::TypeCompartment &types = builder->script->compartment()->types;
+    if (builder->script()->isIonCompilingOffThread()) {
+        types::TypeCompartment &types = builder->script()->compartment()->types;
         builder->recompileInfo.compilerOutput(types)->invalidate();
-        builder->script->ion = NULL;
+        builder->script()->ion = NULL;
     }
     js_delete(builder->temp().lifoAlloc());
 }
 
 static inline void
 FinishAllOffThreadCompilations(IonCompartment *ion)
 {
     OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
@@ -898,27 +898,27 @@ class AutoDestroyAllocator
         if (alloc)
             js_delete(alloc);
     }
 };
 
 bool
 TestCompiler(IonBuilder *builder, MIRGraph *graph, AutoDestroyAllocator &autoDestroy)
 {
-    JS_ASSERT(!builder->script->ion);
+    JS_ASSERT(!builder->script()->ion);
     JSContext *cx = GetIonContext()->cx;
 
-    IonSpewNewFunction(graph, builder->script);
+    IonSpewNewFunction(graph, builder->script());
 
     if (!builder->build())
         return false;
     builder->clearForBackEnd();
 
     if (js_IonOptions.parallelCompilation) {
-        builder->script->ion = ION_COMPILING_SCRIPT;
+        builder->script()->ion = ION_COMPILING_SCRIPT;
 
         if (!StartOffThreadIonCompile(cx, builder))
             return false;
 
         // The allocator and associated data will be destroyed after being
         // processed in the finishedOffThreadCompilations list.
         autoDestroy.cancel();
 
@@ -950,17 +950,17 @@ AttachFinishedCompilations(JSContext *cx
 
     // Incorporate any off thread compilations which have finished, failed or
     // have been cancelled, and destroy JM jitcode for any compilations which
     // succeeded, to allow entering the Ion code from the interpreter.
     while (!compilations.empty()) {
         IonBuilder *builder = compilations.popCopy();
 
         if (builder->lir) {
-            JSScript *script = builder->script;
+            JSScript *script = builder->script();
             IonContext ictx(cx, cx->compartment, &builder->temp());
 
             CodeGenerator codegen(builder, *builder->lir);
 
             types::AutoEnterCompilation enterCompiler(cx, types::AutoEnterCompilation::Ion);
             enterCompiler.initExisting(builder->recompileInfo);
 
             bool success;
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -21,28 +21,28 @@
 #endif
 
 using namespace js;
 using namespace js::ion;
 
 IonBuilder::IonBuilder(JSContext *cx, TempAllocator *temp, MIRGraph *graph,
                        TypeOracle *oracle, CompileInfo *info, size_t inliningDepth, uint32 loopDepth)
   : MIRGenerator(cx->compartment, temp, graph, info),
-    script(info->script()),
     recompileInfo(cx->compartment->types.compiledInfo),
     lir(NULL),
     cx(cx),
     loopDepth_(loopDepth),
     callerResumePoint_(NULL),
     callerBuilder_(NULL),
     oracle(oracle),
     inliningDepth(inliningDepth),
-    failedBoundsCheck_(script->failedBoundsCheck),
+    failedBoundsCheck_(info->script()->failedBoundsCheck),
     lazyArguments_(NULL)
 {
+    script_.init(info->script());
     pc = info->startPC();
 }
 
 void
 IonBuilder::clearForBackEnd()
 {
     cx = NULL;
     oracle = NULL;
@@ -52,27 +52,27 @@ bool
 IonBuilder::abort(const char *message, ...)
 {
     // Don't call PCToLineNumber in release builds.
 #ifdef DEBUG
     va_list ap;
     va_start(ap, message);
     abortFmt(message, ap);
     va_end(ap);
-    IonSpew(IonSpew_Abort, "aborted @ %s:%d", script->filename, PCToLineNumber(script, pc));
+    IonSpew(IonSpew_Abort, "aborted @ %s:%d", script_->filename, PCToLineNumber(script_, pc));
 #endif
     return false;
 }
 
 void
 IonBuilder::spew(const char *message)
 {
     // Don't call PCToLineNumber in release builds.
 #ifdef DEBUG
-    IonSpew(IonSpew_MIR, "%s @ %s:%d", message, script->filename, PCToLineNumber(script, pc));
+    IonSpew(IonSpew_MIR, "%s @ %s:%d", message, script_->filename, PCToLineNumber(script_, pc));
 #endif
 }
 
 static inline int32
 GetJumpOffset(jsbytecode *pc)
 {
     JS_ASSERT(js_CodeSpec[JSOp(*pc)].type() == JOF_JUMP);
     return GET_JUMP_OFFSET(pc);
@@ -141,32 +141,32 @@ IonBuilder::CFGState::LookupSwitch(jsbyt
         (FixedList<MBasicBlock *> *)GetIonContext()->temp->allocate(sizeof(FixedList<MBasicBlock *>));
     state.lookupswitch.currentBlock = 0;
     return state;
 }
 
 JSFunction *
 IonBuilder::getSingleCallTarget(uint32 argc, jsbytecode *pc)
 {
-    types::StackTypeSet *calleeTypes = oracle->getCallTarget(script, argc, pc);
+    types::StackTypeSet *calleeTypes = oracle->getCallTarget(script(), argc, pc);
     if (!calleeTypes)
         return NULL;
 
     JSObject *obj = calleeTypes->getSingleton();
     if (!obj || !obj->isFunction())
         return NULL;
 
     return obj->toFunction();
 }
 
 uint32_t
 IonBuilder::getPolyCallTargets(uint32 argc, jsbytecode *pc,
                                AutoObjectVector &targets, uint32_t maxTargets)
 {
-    types::TypeSet *calleeTypes = oracle->getCallTarget(script, argc, pc);
+    types::TypeSet *calleeTypes = oracle->getCallTarget(script_, argc, pc);
     if (!calleeTypes)
         return 0;
 
     if (calleeTypes->baseFlags() != 0)
         return 0;
 
     unsigned objCount = calleeTypes->getObjectCount();
 
@@ -186,42 +186,42 @@ IonBuilder::getPolyCallTargets(uint32 ar
 bool
 IonBuilder::canInlineTarget(JSFunction *target)
 {
     if (!target->isInterpreted()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to non-interpreted");
         return false;
     }
 
-    if (target->getParent() != &script->global()) {
+    if (target->getParent() != &script_->global()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to scope mismatch");
         return false;
     }
 
     JSScript *inlineScript = target->script();
 
     if (!inlineScript->canIonCompile()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to disable Ion compilation");
         return false;
     }
 
     // Allow inlining of recursive calls, but only one level deep.
     IonBuilder *builder = callerBuilder_;
     while (builder) {
-        if (builder->script == inlineScript) {
+        if (builder->script() == inlineScript) {
             IonSpew(IonSpew_Inlining, "Not inlining recursive call");
             return false;
         }
         builder = builder->callerBuilder_;
     }
 
     bool canInline = oracle->canEnterInlinedFunction(target);
 
     if (!canInline) {
-        IonSpew(IonSpew_Inlining, "Cannot inline due to oracle veto %d", script->lineno);
+        IonSpew(IonSpew_Inlining, "Cannot inline due to oracle veto %d", script_->lineno);
         return false;
     }
 
     IonSpew(IonSpew_Inlining, "Inlining good to go!");
     return true;
 }
 
 void
@@ -260,20 +260,20 @@ IonBuilder::pushLoop(CFGState::State ini
 bool
 IonBuilder::build()
 {
     current = newBlock(pc);
     if (!current)
         return false;
 
     IonSpew(IonSpew_Scripts, "Analyzing script %s:%d (%p) (usecount=%d) (maxloopcount=%d)",
-            script->filename, script->lineno, (void *)script, (int)script->getUseCount(),
-            (int)script->getMaxLoopCount());
-
-    if (!graph().addScript(script))
+            script_->filename, script_->lineno, (void *)script_, (int)script_->getUseCount(),
+            (int)script_->getMaxLoopCount());
+
+    if (!graph().addScript(script_))
         return false;
 
     if (!initParameters())
         return false;
 
     // Initialize local variables.
     for (uint32 i = 0; i < info().nlocals(); i++) {
         MConstant *undef = MConstant::New(UndefinedValue());
@@ -289,17 +289,17 @@ IonBuilder::build()
         MInstruction *scope = MConstant::New(UndefinedValue());
         current->add(scope);
         current->initSlot(info().scopeChainSlot(), scope);
     }
 
     // Emit the start instruction, so we can begin real instructions.
     current->makeStart(MStart::New(MStart::StartType_Default));
     if (instrumentedProfiling())
-        current->add(MFunctionBoundary::New(script, MFunctionBoundary::Enter));
+        current->add(MFunctionBoundary::New(script_, MFunctionBoundary::Enter));
 
     // Parameters have been checked to correspond to the typeset, now we unbox
     // what we can in an infallible manner.
     rewriteParameters();
 
     // It's safe to start emitting actual IR, so now build the scope chain.
     if (!initScopeChain())
         return false;
@@ -332,17 +332,17 @@ IonBuilder::build()
         MInstruction *ins = current->getEntrySlot(i)->toInstruction();
         if (ins->type() == MIRType_Value)
             ins->setResumePoint(current->entryResumePoint());
     }
 
     // Recompile to inline calls if this function is hot.
     insertRecompileCheck();
 
-    if (script->argumentsHasVarBinding()) {
+    if (script_->argumentsHasVarBinding()) {
         lazyArguments_ = MConstant::New(MagicValue(JS_OPTIMIZED_ARGUMENTS));
         current->add(lazyArguments_);
     }
 
     if (!traverseBytecode())
         return false;
 
     if (!processIterators())
@@ -386,19 +386,19 @@ IonBuilder::processIterators()
     return true;
 }
 
 bool
 IonBuilder::buildInline(IonBuilder *callerBuilder, MResumePoint *callerResumePoint,
                         MDefinition *thisDefn, MDefinitionVector &argv)
 {
     IonSpew(IonSpew_Scripts, "Inlining script %s:%d (%p)",
-            script->filename, script->lineno, (void *)script);
-
-    if (!graph().addScript(script))
+            script_->filename, script_->lineno, (void *)script_);
+
+    if (!graph().addScript(script_))
         return false;
 
     callerBuilder_ = callerBuilder;
     callerResumePoint_ = callerResumePoint;
 
     if (callerBuilder->failedBoundsCheck_)
         failedBoundsCheck_ = true;
 
@@ -413,17 +413,17 @@ IonBuilder::buildInline(IonBuilder *call
     MBasicBlock *predecessor = callerBuilder->current;
     JS_ASSERT(predecessor == callerResumePoint->block());
 
     // All further instructions generated in from this scope should be
     // considered as part of the function that we're inlining. We also need to
     // keep track of the inlining depth because all scripts inlined on the same
     // level contiguously have only one Inline_Exit node.
     if (instrumentedProfiling())
-        predecessor->add(MFunctionBoundary::New(script,
+        predecessor->add(MFunctionBoundary::New(script_,
                                                 MFunctionBoundary::Inline_Enter,
                                                 inliningDepth));
 
     predecessor->end(MGoto::New(current));
     if (!current->addPredecessorWithoutPhis(predecessor))
         return false;
 
     // Explicitly pass Undefined for missing arguments.
@@ -437,17 +437,17 @@ IonBuilder::buildInline(IonBuilder *call
             MConstant *undef = MConstant::New(UndefinedValue());
             current->add(undef);
             if (!argv.append(undef))
                 return false;
         }
     }
 
     // The Oracle ensures that the inlined script does not use the scope chain.
-    JS_ASSERT(!script->analysis()->usesScopeChain());
+    JS_ASSERT(!script_->analysis()->usesScopeChain());
     MInstruction *scope = MConstant::New(UndefinedValue());
     current->add(scope);
     current->initSlot(info().scopeChainSlot(), scope);
 
     current->initSlot(info().thisSlot(), thisDefn);
 
     IonSpew(IonSpew_Inlining, "Initializing %u arg slots", nargs);
 
@@ -528,44 +528,44 @@ IonBuilder::rewriteParameters()
 
 bool
 IonBuilder::initParameters()
 {
     if (!info().fun())
         return true;
 
     MParameter *param = MParameter::New(MParameter::THIS_SLOT,
-                                        oracle->thisTypeSet(script));
+                                        oracle->thisTypeSet(script_));
     current->add(param);
     current->initSlot(info().thisSlot(), param);
 
     for (uint32 i = 0; i < info().nargs(); i++) {
-        param = MParameter::New(i, oracle->parameterTypeSet(script, i));
+        param = MParameter::New(i, oracle->parameterTypeSet(script_, i));
         current->add(param);
         current->initSlot(info().argSlot(i), param);
     }
 
     return true;
 }
 
 bool
 IonBuilder::initScopeChain()
 {
     MInstruction *scope = NULL;
 
     // If the script doesn't use the scopechain, then it's already initialized
     // from earlier.
-    if (!script->analysis()->usesScopeChain())
+    if (!script_->analysis()->usesScopeChain())
         return true;
 
     // The scope chain is only tracked in scripts that have NAME opcodes which
     // will try to access the scope. For other scripts, the scope instructions
     // will be held live by resume points and code will still be generated for
     // them, so just use a constant undefined value.
-    if (!script->compileAndGo)
+    if (!script_->compileAndGo)
         return abort("non-CNG global scripts are not supported");
 
     if (JSFunction *fun = info().fun()) {
         MCallee *callee = MCallee::New();
         current->add(callee);
 
         scope = MFunctionEnvironment::New(callee);
         current->add(scope);
@@ -575,17 +575,17 @@ IonBuilder::initScopeChain()
             if (fun->isNamedLambda())
                 return abort("DeclEnv scope objects are not yet supported");
 
             scope = createCallObject(callee, scope);
             if (!scope)
                 return false;
         }
     } else {
-        scope = MConstant::New(ObjectValue(script->global()));
+        scope = MConstant::New(ObjectValue(script_->global()));
         current->add(scope);
     }
 
     current->setScopeChain(scope);
     return true;
 }
 
 // We try to build a control-flow graph in the order that it would be built as
@@ -738,17 +738,17 @@ IonBuilder::snoopControlFlow(JSOp op)
         break;
     }
     return ControlStatus_None;
 }
 
 void
 IonBuilder::markPhiBytecodeUses(jsbytecode *pc)
 {
-    unsigned nuses = analyze::GetUseCount(script, pc - script->code);
+    unsigned nuses = analyze::GetUseCount(script_, pc - script_->code);
     for (unsigned i = 0; i < nuses; i++) {
         MDefinition *def = current->peek(-(i + 1));
         if (def->isPassArg())
             def = def->toPassArg()->getArgument();
         if (def->isPhi())
             def->toPhi()->setHasBytecodeUses();
     }
 }
@@ -942,17 +942,17 @@ IonBuilder::inspectOpcode(JSOp op)
       case JSOP_GETGNAME:
       case JSOP_CALLGNAME:
       {
         RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName());
         return jsop_getgname(name);
       }
 
       case JSOP_BINDGNAME:
-        return pushConstant(ObjectValue(script->global()));
+        return pushConstant(ObjectValue(script_->global()));
 
       case JSOP_SETGNAME:
       {
         RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName());
         return jsop_setgname(name);
       }
 
       case JSOP_NAME:
@@ -2285,17 +2285,17 @@ IonBuilder::lookupSwitch(JSOp op, jssrcn
     bool defaultShared = false;
 
     MBasicBlock *prevCond = NULL;
     MCompare *prevCmpIns = NULL;
     MBasicBlock *prevBody = NULL;
     bool prevShared = false;
     jsbytecode *prevpc = NULL;
     for (unsigned int i = 0; i < ncases; i++) {
-        Value rval = script->getConst(GET_UINT32_INDEX(pc2));
+        Value rval = script_->getConst(GET_UINT32_INDEX(pc2));
         pc2 += UINT32_INDEX_LEN;
         jsbytecode *casepc = pc + GET_JUMP_OFFSET(pc2);
         pc2 += JUMP_OFFSET_LEN;
         JS_ASSERT(casepc > pc && casepc <= exitpc);
         JS_ASSERT_IF(i > 0, prevpc <= casepc);
 
         // Create case block
         MBasicBlock *cond = newBlock(((i == 0) ? current : prevCond), casepc);
@@ -2565,17 +2565,17 @@ IonBuilder::processReturn(JSOp op)
 
       default:
         def = NULL;
         JS_NOT_REACHED("unknown return op");
         break;
     }
 
     if (instrumentedProfiling())
-        current->add(MFunctionBoundary::New(script, MFunctionBoundary::Exit));
+        current->add(MFunctionBoundary::New(script_, MFunctionBoundary::Exit));
     MReturn *ret = MReturn::New(def);
     current->end(ret);
 
     if (!graph().addExit(current))
         return ControlStatus_Error;
 
     // Make sure no one tries to use this block now.
     current = NULL;
@@ -2609,17 +2609,17 @@ IonBuilder::pushConstant(const Value &v)
 
 bool
 IonBuilder::jsop_bitnot()
 {
     MDefinition *input = current->pop();
     MBitNot *ins = MBitNot::New(input);
 
     current->add(ins);
-    ins->infer(oracle->unaryTypes(script, pc));
+    ins->infer(oracle->unaryTypes(script_, pc));
 
     current->push(ins);
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
     return true;
 }
 bool
 IonBuilder::jsop_bitop(JSOp op)
@@ -2655,29 +2655,29 @@ IonBuilder::jsop_bitop(JSOp op)
         break;
 
       default:
         JS_NOT_REACHED("unexpected bitop");
         return false;
     }
 
     current->add(ins);
-    ins->infer(oracle->binaryTypes(script, pc));
+    ins->infer(oracle->binaryTypes(script_, pc));
 
     current->push(ins);
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
 
     return true;
 }
 
 bool
 IonBuilder::jsop_binary(JSOp op, MDefinition *left, MDefinition *right)
 {
-    TypeOracle::Binary b = oracle->binaryOp(script, pc);
+    TypeOracle::Binary b = oracle->binaryOp(script_, pc);
 
     if (op == JSOP_ADD && b.rval == MIRType_String &&
         (b.lhs == MIRType_String || b.lhs == MIRType_Int32) &&
         (b.rhs == MIRType_String || b.rhs == MIRType_Int32))
     {
         MConcat *ins = MConcat::New(left, right);
         current->add(ins);
         current->push(ins);
@@ -2706,17 +2706,17 @@ IonBuilder::jsop_binary(JSOp op, MDefini
         ins = MMod::New(left, right);
         break;
 
       default:
         JS_NOT_REACHED("unexpected binary opcode");
         return false;
     }
 
-    TypeOracle::BinaryTypes types = oracle->binaryTypes(script, pc);
+    TypeOracle::BinaryTypes types = oracle->binaryTypes(script_, pc);
     current->add(ins);
     ins->infer(cx, types);
     current->push(ins);
 
     if (ins->isEffectful())
         return resumeAfter(ins);
     return true;
 }
@@ -2728,17 +2728,17 @@ IonBuilder::jsop_binary(JSOp op)
     MDefinition *left = current->pop();
 
     return jsop_binary(op, left, right);
 }
 
 bool
 IonBuilder::jsop_pos()
 {
-    TypeOracle::Unary types = oracle->unaryOp(script, pc);
+    TypeOracle::Unary types = oracle->unaryOp(script_, pc);
     if (IsNumberType(types.ival)) {
         // Already int32 or double.
         JS_ASSERT(IsNumberType(types.rval));
         return true;
     }
 
     // Compile +x as x * 1.
     MDefinition *value = current->pop();
@@ -2913,22 +2913,22 @@ IonBuilder::makeInliningDecision(AutoObj
             return false;
 
         if (script->length > js_IonOptions.smallFunctionMaxBytecodeLength)
             allFunctionsAreSmall = false;
     }
     if (allFunctionsAreSmall)
         checkUses = js_IonOptions.smallFunctionUsesBeforeInlining;
 
-    if (script->getUseCount() < checkUses) {
+    if (script_->getUseCount() < checkUses) {
         IonSpew(IonSpew_Inlining, "Not inlining, caller is not hot");
         return false;
     }
 
-    if (!oracle->canInlineCall(script, pc)) {
+    if (!oracle->canInlineCall(script_, pc)) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to uninlineable call site");
         return false;
     }
 
     for (size_t i = 0; i < targets.length(); i++) {
         if (!canInlineTarget(targets[i]->toFunction())) {
             IonSpew(IonSpew_Inlining, "Decided not to inline");
             return false;
@@ -3418,17 +3418,17 @@ IonBuilder::inlineScriptedCall(AutoObjec
 }
 
 MInstruction *
 IonBuilder::createCallObject(MDefinition *callee, MDefinition *scope)
 {
     // Create a template CallObject that we'll use to generate inline object
     // creation.
 
-    RootedObject templateObj(cx, CallObject::createTemplateObject(cx, script));
+    RootedObject templateObj(cx, CallObject::createTemplateObject(cx, script_));
     if (!templateObj)
         return NULL;
 
     // If the CallObject needs dynamic slots, allocate those now.
     MInstruction *slots;
     if (templateObj->hasDynamicSlots()) {
         size_t nslots = JSObject::dynamicSlotsCount(templateObj->numFixedSlots(),
                                                     templateObj->slotSpan());
@@ -3444,17 +3444,17 @@ IonBuilder::createCallObject(MDefinition
     MInstruction *callObj = MNewCallObject::New(templateObj, slots);
     current->add(callObj);
 
     // Initialize the object's reserved slots.
     current->add(MStoreFixedSlot::New(callObj, CallObject::calleeSlot(), callee));
     current->add(MStoreFixedSlot::New(callObj, CallObject::enclosingScopeSlot(), scope));
 
     // Initialize argument slots.
-    for (AliasedFormalIter i(script); i; i++) {
+    for (AliasedFormalIter i(script_); i; i++) {
         unsigned slot = i.scopeSlot();
         unsigned formal = i.frameIndex();
         MDefinition *param = current->getSlot(info().argSlot(formal));
         if (slot >= templateObj->numFixedSlots())
             current->add(MStoreSlot::New(slots, slot - templateObj->numFixedSlots(), param));
         else
             current->add(MStoreFixedSlot::New(callObj, slot, param));
     }
@@ -3568,17 +3568,17 @@ IonBuilder::jsop_funcall(uint32 argc)
     // argc+2: The native 'call' function.
 
     // If |Function.prototype.call| may be overridden, don't optimize callsite.
     RootedFunction native(cx, getSingleCallTarget(argc, pc));
     if (!native || !native->isNative() || native->native() != &js_fun_call)
         return makeCall(native, argc, false);
 
     // Extract call target.
-    types::StackTypeSet *funTypes = oracle->getCallArg(script, argc, 0, pc);
+    types::StackTypeSet *funTypes = oracle->getCallArg(script_, argc, 0, pc);
     RootedObject funobj(cx, (funTypes) ? funTypes->getSingleton() : NULL);
     RootedFunction target(cx, (funobj && funobj->isFunction()) ? funobj->toFunction() : NULL);
 
     // Unwrap the (JSFunction *) parameter.
     int funcDepth = -((int)argc + 1);
     MPassArg *passFunc = current->peek(funcDepth)->toPassArg();
     current->rewriteAtDepth(funcDepth, passFunc->getArgument());
 
@@ -3610,17 +3610,17 @@ bool
 IonBuilder::jsop_funapply(uint32 argc)
 {
     RootedFunction native(cx, getSingleCallTarget(argc, pc));
     if (argc != 2)
         return makeCall(native, argc, false);
 
     // Disable compilation if the second argument to |apply| cannot be guaranteed
     // to be either definitely |arguments| or definitely not |arguments|.
-    types::StackTypeSet *argObjTypes = oracle->getCallArg(script, argc, 2, pc);
+    types::StackTypeSet *argObjTypes = oracle->getCallArg(script_, argc, 2, pc);
     LazyArgumentsType isArgObj = oracle->isArgumentObject(argObjTypes);
     if (isArgObj == MaybeArguments)
         return abort("fun.apply with MaybeArguments");
 
     // Fallback to regular call if arg 2 is not definitely |arguments|.
     if (isArgObj != DefinitelyArguments)
         return makeCall(native, argc, false);
 
@@ -3633,17 +3633,17 @@ IonBuilder::jsop_funapply(uint32 argc)
 
     // Stack for JSOP_FUNAPPLY:
     // 1:      MPassArg(Vp)
     // 2:      MPassArg(This)
     // argc+1: MPassArg(JSFunction *), the 'f' in |f.call()|, in |this| position.
     // argc+2: The native 'apply' function.
 
     // Extract call target.
-    types::StackTypeSet *funTypes = oracle->getCallArg(script, argc, 0, pc);
+    types::StackTypeSet *funTypes = oracle->getCallArg(script_, argc, 0, pc);
     RootedObject funobj(cx, (funTypes) ? funTypes->getSingleton() : NULL);
     RootedFunction target(cx, (funobj && funobj->isFunction()) ? funobj->toFunction() : NULL);
 
     // Vp
     MPassArg *passVp = current->pop()->toPassArg();
     passVp->replaceAllUsesWith(passVp->getArgument());
     passVp->block()->discard(passVp);
 
@@ -3667,17 +3667,17 @@ IonBuilder::jsop_funapply(uint32 argc)
 
     MApplyArgs *apply = MApplyArgs::New(target, argFunc, numArgs, argThis);
     current->add(apply);
     current->push(apply);
     if (!resumeAfter(apply))
         return false;
 
     types::StackTypeSet *barrier;
-    types::StackTypeSet *types = oracle->returnTypeSet(script, pc, &barrier);
+    types::StackTypeSet *types = oracle->returnTypeSet(script_, pc, &barrier);
     return pushTypeBarrier(apply, types, barrier);
 }
 
 // Get the builtin RegExp.prototype.test function.
 static bool
 GetBuiltinRegExpTest(JSContext *cx, JSScript *script, JSFunction **result)
 {
     JS_ASSERT(*result == NULL);
@@ -3713,17 +3713,17 @@ GetBuiltinRegExpTest(JSContext *cx, JSSc
 
 bool
 IonBuilder::jsop_call(uint32 argc, bool constructing)
 {
     // Acquire known call target if existent.
     AutoObjectVector targets(cx);
     uint32_t numTargets = getPolyCallTargets(argc, pc, targets, 4);
     types::StackTypeSet *barrier;
-    types::StackTypeSet *types = oracle->returnTypeSet(script, pc, &barrier);
+    types::StackTypeSet *types = oracle->returnTypeSet(script_, pc, &barrier);
 
     // Attempt to inline native and scripted functions.
     if (inliningEnabled()) {
         // Inline a single native call if possible.
         if (numTargets == 1 && targets[0]->toFunction()->isNative()) {
             RootedFunction target(cx, targets[0]->toFunction());
             switch (inlineNativeCall(target->native(), argc, constructing)) {
               case InliningStatus_Inlined:
@@ -3742,17 +3742,17 @@ IonBuilder::jsop_call(uint32 argc, bool 
     RootedFunction target(cx, NULL);
     if (numTargets == 1) {
         target = targets[0]->toFunction();
 
         // Call RegExp.test instead of RegExp.exec if the result will not be used
         // or will only be used to test for existence.
         if (target->maybeNative() == regexp_exec && !CallResultEscapes(pc)) {
             JSFunction *newTarget = NULL;
-            if (!GetBuiltinRegExpTest(cx, script, &newTarget))
+            if (!GetBuiltinRegExpTest(cx, script_, &newTarget))
                 return false;
             if (newTarget)
                 target = newTarget;
         }
     }
 
     return makeCallBarrier(target, argc, constructing, types, barrier);
 }
@@ -3843,26 +3843,26 @@ IonBuilder::makeCallBarrier(HandleFuncti
 
     return pushTypeBarrier(call, types, barrier);
 }
 
 bool
 IonBuilder::makeCall(HandleFunction target, uint32 argc, bool constructing)
 {
     types::StackTypeSet *barrier;
-    types::StackTypeSet *types = oracle->returnTypeSet(script, pc, &barrier);
+    types::StackTypeSet *types = oracle->returnTypeSet(script_, pc, &barrier);
     return makeCallBarrier(target, argc, constructing, types, barrier);
 }
 
 bool
 IonBuilder::jsop_incslot(JSOp op, uint32 slot)
 {
     int32 amt = (js_CodeSpec[op].format & JOF_INC) ? 1 : -1;
     bool post = !!(js_CodeSpec[op].format & JOF_POST);
-    TypeOracle::BinaryTypes types = oracle->incslot(script, pc);
+    TypeOracle::BinaryTypes types = oracle->incslot(script_, pc);
 
     // Grab the value at the local slot, and convert it to a number. Currently,
     // we use ToInt32 or ToNumber which are fallible but idempotent. This whole
     // operation must be idempotent because we cannot resume in the middle of
     // an INC op.
     current->pushSlot(slot);
     MDefinition *value = current->pop();
     MInstruction *lhs;
@@ -3913,47 +3913,48 @@ IonBuilder::jsop_compare(JSOp op)
 {
     MDefinition *right = current->pop();
     MDefinition *left = current->pop();
 
     MCompare *ins = MCompare::New(left, right, op);
     current->add(ins);
     current->push(ins);
 
-    ins->infer(cx, oracle->binaryTypes(script, pc));
+    ins->infer(cx, oracle->binaryTypes(script_, pc));
 
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
     return true;
 }
 
 JSObject *
 IonBuilder::getNewArrayTemplateObject(uint32 count)
 {
     RootedObject templateObject(cx, NewDenseUnallocatedArray(cx, count));
     if (!templateObject)
         return NULL;
 
+    RootedScript script(cx, script_);
     if (types::UseNewTypeForInitializer(cx, script, pc, JSProto_Array)) {
         if (!JSObject::setSingletonType(cx, templateObject))
             return NULL;
     } else {
         types::TypeObject *type = types::TypeScript::InitObject(cx, script, pc, JSProto_Array);
         if (!type)
             return NULL;
         templateObject->setType(type);
     }
 
     return templateObject;
 }
 
 bool
 IonBuilder::jsop_newarray(uint32 count)
 {
-    JS_ASSERT(script->compileAndGo);
+    JS_ASSERT(script_->compileAndGo);
 
     JSObject *templateObject = getNewArrayTemplateObject(count);
     if (!templateObject)
         return false;
 
     MNewArray *ins = new MNewArray(count, templateObject, MNewArray::NewArray_Allocating);
 
     current->add(ins);
@@ -3961,30 +3962,31 @@ IonBuilder::jsop_newarray(uint32 count)
 
     return true;
 }
 
 bool
 IonBuilder::jsop_newobject(HandleObject baseObj)
 {
     // Don't bake in the TypeObject for non-CNG scripts.
-    JS_ASSERT(script->compileAndGo);
+    JS_ASSERT(script_->compileAndGo);
 
     RootedObject templateObject(cx);
 
     if (baseObj) {
         templateObject = CopyInitializerObject(cx, baseObj);
     } else {
         gc::AllocKind kind = GuessObjectGCKind(0);
         templateObject = NewBuiltinClassInstance(cx, &ObjectClass, kind);
     }
 
     if (!templateObject)
         return false;
 
+    RootedScript script(cx, script_);
     if (types::UseNewTypeForInitializer(cx, script, pc, JSProto_Object)) {
         if (!JSObject::setSingletonType(cx, templateObject))
             return false;
     } else {
         types::TypeObject *type = types::TypeScript::InitObject(cx, script, pc, JSProto_Object);
         if (!type)
             return false;
         templateObject->setType(type);
@@ -3996,18 +3998,18 @@ IonBuilder::jsop_newobject(HandleObject 
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::jsop_initelem()
 {
-    if (oracle->propertyWriteCanSpecialize(script, pc)) {
-        if (oracle->elementWriteIsDenseArray(script, pc))
+    if (oracle->propertyWriteCanSpecialize(script_, pc)) {
+        if (oracle->elementWriteIsDenseArray(script_, pc))
             return jsop_initelem_dense();
     }
 
     return abort("NYI: JSOP_INITELEM supports for non dense objects/arrays.");
 }
 
 bool
 IonBuilder::jsop_initelem_dense()
@@ -4050,17 +4052,17 @@ CanEffectlesslyCallLookupGenericOnObject
 bool
 IonBuilder::jsop_initprop(HandlePropertyName name)
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->peek(-1);
 
     RootedObject templateObject(cx, obj->toNewObject()->templateObject());
 
-    if (!oracle->propertyWriteCanSpecialize(script, pc)) {
+    if (!oracle->propertyWriteCanSpecialize(script_, pc)) {
         // This should only happen for a few names like __proto__.
         return abort("INITPROP Monitored initprop");
     }
 
     if (!CanEffectlesslyCallLookupGenericOnObject(templateObject))
         return abort("INITPROP template object is special");
 
     RootedObject holder(cx);
@@ -4074,17 +4076,17 @@ IonBuilder::jsop_initprop(HandleProperty
     if (!shape || holder != templateObject) {
         // JSOP_NEWINIT becomes an MNewObject without preconfigured properties.
         MInitProp *init = MInitProp::New(obj, name, value);
         current->add(init);
         return resumeAfter(init);
     }
 
     bool needsBarrier = true;
-    TypeOracle::BinaryTypes b = oracle->binaryTypes(script, pc);
+    TypeOracle::BinaryTypes b = oracle->binaryTypes(script_, pc);
     if (b.lhsTypes &&
         ((jsid)id == types::MakeTypeId(cx, id)) &&
         !b.lhsTypes->propertyNeedsBarrier(cx, id))
     {
         needsBarrier = false;
     }
 
     if (templateObject->isFixedSlot(shape->slot())) {
@@ -4368,25 +4370,25 @@ IonBuilder::insertRecompileCheck()
 {
     if (!inliningEnabled())
         return;
 
     if (inliningDepth > 0)
         return;
 
     // Don't recompile if we are already inlining.
-    if (script->getUseCount() >= js_IonOptions.usesBeforeInlining)
+    if (script_->getUseCount() >= js_IonOptions.usesBeforeInlining)
         return;
 
     // Don't recompile if the oracle cannot provide inlining information
     // or if the script has no calls.
     if (!oracle->canInlineCalls())
         return;
 
-    uint32_t minUses = UsesBeforeIonRecompile(script, pc);
+    uint32_t minUses = UsesBeforeIonRecompile(script_, pc);
     MRecompileCheck *check = MRecompileCheck::New(minUses);
     current->add(check);
 }
 
 static inline bool
 TestSingletonProperty(JSContext *cx, HandleObject obj, HandleId id, bool *isKnownConstant)
 {
     // We would like to completely no-op property/global accesses which can
@@ -4627,39 +4629,39 @@ IonBuilder::jsop_getgname(HandleProperty
     // Optimize undefined, NaN, and Infinity.
     if (name == cx->names().undefined)
         return pushConstant(UndefinedValue());
     if (name == cx->names().NaN)
         return pushConstant(cx->runtime->NaNValue);
     if (name == cx->names().Infinity)
         return pushConstant(cx->runtime->positiveInfinityValue);
 
-    RootedObject globalObj(cx, &script->global());
+    RootedObject globalObj(cx, &script_->global());
     JS_ASSERT(globalObj->isNative());
 
     RootedId id(cx, NameToId(name));
 
     // For the fastest path, the property must be found, and it must be found
     // as a normal data property on exactly the global object.
     const js::Shape *shape = globalObj->nativeLookup(cx, id);
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return jsop_getname(name);
 
-    types::HeapTypeSet *propertyTypes = oracle->globalPropertyTypeSet(script, pc, id);
+    types::HeapTypeSet *propertyTypes = oracle->globalPropertyTypeSet(script_, pc, id);
     if (propertyTypes && propertyTypes->isOwnProperty(cx, globalObj->getType(cx), true)) {
         // The property has been reconfigured as non-configurable, non-enumerable
         // or non-writable.
         return jsop_getname(name);
     }
 
     // If the property is permanent, a shape guard isn't necessary.
     JSValueType knownType = JSVAL_TYPE_UNKNOWN;
 
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script, pc);
+    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script_, pc);
+    types::StackTypeSet *types = oracle->propertyRead(script_, pc);
     if (types) {
         JSObject *singleton = types->getSingleton();
 
         knownType = types->getKnownTypeTag();
         if (!barrier) {
             if (singleton) {
                 // Try to inline a known constant value.
                 bool isKnownConstant;
@@ -4698,23 +4700,23 @@ IonBuilder::jsop_getgname(HandleProperty
 
     current->push(load);
     return pushTypeBarrier(load, types, barrier);
 }
 
 bool
 IonBuilder::jsop_setgname(HandlePropertyName name)
 {
-    RootedObject globalObj(cx, &script->global());
+    RootedObject globalObj(cx, &script_->global());
     RootedId id(cx, NameToId(name));
 
     JS_ASSERT(globalObj->isNative());
 
     bool canSpecialize;
-    types::HeapTypeSet *propertyTypes = oracle->globalPropertyWrite(script, pc, id, &canSpecialize);
+    types::HeapTypeSet *propertyTypes = oracle->globalPropertyWrite(script_, pc, id, &canSpecialize);
 
     // This should only happen for a few names like __proto__.
     if (!canSpecialize || globalObj->watched())
         return jsop_setprop(name);
 
     // For the fastest path, the property must be found, and it must be found
     // as a normal data property on exactly the global object.
     const js::Shape *shape = globalObj->nativeLookup(cx, id);
@@ -4771,17 +4773,17 @@ IonBuilder::jsop_setgname(HandleProperty
     return resumeAfter(store);
 }
 
 bool
 IonBuilder::jsop_getname(HandlePropertyName name)
 {
     MDefinition *object;
     if (js_CodeSpec[*pc].format & JOF_GNAME) {
-        MInstruction *global = MConstant::New(ObjectValue(script->global()));
+        MInstruction *global = MConstant::New(ObjectValue(script_->global()));
         current->add(global);
         object = global;
     } else {
         current->push(current->scopeChain());
         object = current->pop();
     }
 
     MGetNameCache *ins;
@@ -4791,51 +4793,51 @@ IonBuilder::jsop_getname(HandlePropertyN
         ins = MGetNameCache::New(object, name, MGetNameCache::NAME);
 
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return false;
 
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script, pc);
+    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script_, pc);
+    types::StackTypeSet *types = oracle->propertyRead(script_, pc);
 
     monitorResult(ins, types);
     return pushTypeBarrier(ins, types, barrier);
 }
 
 bool
 IonBuilder::jsop_bindname(PropertyName *name)
 {
-    JS_ASSERT(script->analysis()->usesScopeChain());
+    JS_ASSERT(script_->analysis()->usesScopeChain());
 
     MDefinition *scopeChain = current->scopeChain();
-    MBindNameCache *ins = MBindNameCache::New(scopeChain, name, script, pc);
+    MBindNameCache *ins = MBindNameCache::New(scopeChain, name, script_, pc);
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::jsop_getelem()
 {
-    if (oracle->elementReadIsDenseArray(script, pc))
+    if (oracle->elementReadIsDenseArray(script_, pc))
         return jsop_getelem_dense();
 
     int arrayType = TypedArray::TYPE_MAX;
-    if (oracle->elementReadIsTypedArray(script, pc, &arrayType))
+    if (oracle->elementReadIsTypedArray(script_, pc, &arrayType))
         return jsop_getelem_typed(arrayType);
 
-    if (oracle->elementReadIsString(script, pc))
+    if (oracle->elementReadIsString(script_, pc))
         return jsop_getelem_string();
 
-    LazyArgumentsType isArguments = oracle->elementReadMagicArguments(script, pc);
+    LazyArgumentsType isArguments = oracle->elementReadMagicArguments(script_, pc);
     if (isArguments == MaybeArguments)
         return abort("Type is not definitely lazy arguments.");
     if (isArguments == DefinitelyArguments)
         return jsop_arguments_getelem();
 
     MDefinition *rhs = current->pop();
     MDefinition *lhs = current->pop();
 
@@ -4843,46 +4845,46 @@ IonBuilder::jsop_getelem()
 
     // TI does not account for GETELEM with string indexes, so we have to monitor
     // the result of MGetElementCache if it's expected to access string properties.
     // If the result of MGetElementCache is not monitored, we won't generate any
     // getprop stubs.
     bool mustMonitorResult = false;
     bool cacheable = false;
 
-    oracle->elementReadGeneric(script, pc, &cacheable, &mustMonitorResult);
+    oracle->elementReadGeneric(script_, pc, &cacheable, &mustMonitorResult);
 
     if (cacheable)
         ins = MGetElementCache::New(lhs, rhs, mustMonitorResult);
     else
         ins = MCallGetElement::New(lhs, rhs);
 
     current->add(ins);
     current->push(ins);
 
     if (!resumeAfter(ins))
         return false;
 
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script, pc);
+    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script_, pc);
+    types::StackTypeSet *types = oracle->propertyRead(script_, pc);
 
     if (mustMonitorResult)
         monitorResult(ins, types);
     return pushTypeBarrier(ins, types, barrier);
 }
 
 bool
 IonBuilder::jsop_getelem_dense()
 {
     if (oracle->arrayPrototypeHasIndexedProperty())
         return abort("GETELEM Array proto has indexed properties");
 
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script, pc);
-    bool needsHoleCheck = !oracle->elementReadIsPacked(script, pc);
+    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script_, pc);
+    types::StackTypeSet *types = oracle->propertyRead(script_, pc);
+    bool needsHoleCheck = !oracle->elementReadIsPacked(script_, pc);
     bool maybeUndefined = types->hasType(types::Type::UndefinedType());
 
     MDefinition *id = current->pop();
     MDefinition *obj = current->pop();
 
     JSValueType knownType = JSVAL_TYPE_UNKNOWN;
     if (!needsHoleCheck && !barrier) {
         knownType = types->getKnownTypeTag();
@@ -4959,18 +4961,18 @@ GetTypedArrayElements(MDefinition *obj)
         return MConstantElements::New(data);
     }
     return MTypedArrayElements::New(obj);
 }
 
 bool
 IonBuilder::jsop_getelem_typed(int arrayType)
 {
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script, pc);
+    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script_, pc);
+    types::StackTypeSet *types = oracle->propertyRead(script_, pc);
 
     MDefinition *id = current->pop();
     MDefinition *obj = current->pop();
 
     bool maybeUndefined = types->hasType(types::Type::UndefinedType());
 
     // Reading from an Uint32Array will result in a double for values
     // that don't fit in an int32. We have to bailout if this happens
@@ -5069,26 +5071,26 @@ IonBuilder::jsop_getelem_string()
     current->add(result);
     current->push(result);
     return true;
 }
 
 bool
 IonBuilder::jsop_setelem()
 {
-    if (oracle->propertyWriteCanSpecialize(script, pc)) {
-        if (oracle->elementWriteIsDenseArray(script, pc))
+    if (oracle->propertyWriteCanSpecialize(script_, pc)) {
+        if (oracle->elementWriteIsDenseArray(script_, pc))
             return jsop_setelem_dense();
 
         int arrayType = TypedArray::TYPE_MAX;
-        if (oracle->elementWriteIsTypedArray(script, pc, &arrayType))
+        if (oracle->elementWriteIsTypedArray(script_, pc, &arrayType))
             return jsop_setelem_typed(arrayType);
     }
 
-    LazyArgumentsType isArguments = oracle->elementWriteMagicArguments(script, pc);
+    LazyArgumentsType isArguments = oracle->elementWriteMagicArguments(script_, pc);
     if (isArguments == MaybeArguments)
         return abort("Type is not definitely lazy arguments.");
     if (isArguments == DefinitelyArguments)
         return jsop_arguments_setelem();
 
     MDefinition *value = current->pop();
     MDefinition *index = current->pop();
     MDefinition *object = current->pop();
@@ -5101,18 +5103,18 @@ IonBuilder::jsop_setelem()
 }
 
 bool
 IonBuilder::jsop_setelem_dense()
 {
     if (oracle->arrayPrototypeHasIndexedProperty())
         return abort("SETELEM Array proto has indexed properties");
 
-    MIRType elementType = oracle->elementWrite(script, pc);
-    bool packed = oracle->elementWriteIsPacked(script, pc);
+    MIRType elementType = oracle->elementWrite(script_, pc);
+    bool packed = oracle->elementWriteIsPacked(script_, pc);
 
     MDefinition *value = current->pop();
     MDefinition *id = current->pop();
     MDefinition *obj = current->pop();
 
     // Ensure id is an integer.
     MInstruction *idInt32 = MToInt32::New(id);
     current->add(idInt32);
@@ -5121,17 +5123,17 @@ IonBuilder::jsop_setelem_dense()
     // Get the elements vector.
     MElements *elements = MElements::New(obj);
     current->add(elements);
 
     // Use MStoreElementHole if this SETELEM has written to out-of-bounds
     // indexes in the past. Otherwise, use MStoreElement so that we can hoist
     // the initialized length and bounds check.
     MStoreElementCommon *store;
-    if (oracle->setElementHasWrittenHoles(script, pc)) {
+    if (oracle->setElementHasWrittenHoles(script_, pc)) {
         MStoreElementHole *ins = MStoreElementHole::New(obj, elements, id, value);
         store = ins;
 
         current->add(ins);
         current->push(value);
 
         if (!resumeAfter(ins))
             return false;
@@ -5147,17 +5149,17 @@ IonBuilder::jsop_setelem_dense()
         current->add(ins);
         current->push(value);
 
         if (!resumeAfter(ins))
             return false;
     }
 
     // Determine whether a write barrier is required.
-    if (oracle->elementWriteNeedsBarrier(script, pc))
+    if (oracle->elementWriteNeedsBarrier(script_, pc))
         store->setNeedsBarrier();
 
     if (elementType != MIRType_None && packed)
         store->setElementType(elementType);
 
     return true;
 }
 
@@ -5207,17 +5209,17 @@ IonBuilder::jsop_length()
 
     RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName());
     return jsop_getprop(name);
 }
 
 bool
 IonBuilder::jsop_length_fastPath()
 {
-    TypeOracle::UnaryTypes sig = oracle->unaryTypes(script, pc);
+    TypeOracle::UnaryTypes sig = oracle->unaryTypes(script_, pc);
     if (!sig.inTypes || !sig.outTypes)
         return false;
 
     if (sig.outTypes->getKnownTypeTag() != JSVAL_TYPE_INT32)
         return false;
 
     switch (sig.inTypes->getKnownTypeTag()) {
       case JSVAL_TYPE_STRING: {
@@ -5277,18 +5279,18 @@ IonBuilder::jsop_arguments_length()
     current->add(ins);
     current->push(ins);
     return true;
 }
 
 bool
 IonBuilder::jsop_arguments_getelem()
 {
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script, pc);
+    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script_, pc);
+    types::StackTypeSet *types = oracle->propertyRead(script_, pc);
 
     MDefinition *idx = current->pop();
 
     // Type Inference has guaranteed this is an optimized arguments object.
     current->pop();
 
     // To ensure that we are not looking above the number of actual arguments.
     MArgumentsLength *length = MArgumentsLength::New();
@@ -5384,17 +5386,17 @@ IonBuilder::TestCommonPropFunc(JSContext
                 return true;
 
             // If the type has an own property, we can't be sure we don't shadow
             // the chain.
             jsid typeId = types::MakeTypeId(cx, id);
             types::HeapTypeSet *propSet = typeObj->getProperty(cx, typeId, false);
             if (!propSet)
                 return false;
-            if (propSet->isOwnProperty(cx, typeObj, false))
+            if (propSet->ownProperty(false))
                 return true;
 
             // Check the DOM status of the instance type
             thinkDOM = thinkDOM && !typeObj->hasAnyFlags(types::OBJECT_FLAG_NON_DOM);
 
             // Otherwise try using the prototype.
             curObj = typeObj->proto;
         } else {
@@ -5447,31 +5449,42 @@ IonBuilder::TestCommonPropFunc(JSContext
         // We only support cases with a single prototype shared. This is
         // overwhelmingly more likely than having multiple different prototype
         // chains with the same custom property function.
         if (!foundProto)
             foundProto = proto;
         else if (foundProto != proto)
             return true;
 
-        // Check here to make sure that everyone has Type Objects which known
+        // Check here to make sure that everyone has Type Objects with known
         // properties between them and the proto we found the accessor on. We
         // need those to add freezes safely. NOTE: We do not do this above, as
         // we may be able to freeze all the types up to where we found the
         // property, even if there are unknown types higher in the prototype
         // chain.
         while (curObj != foundProto) {
-            if (curObj->getType(cx)->unknownProperties())
+            types::TypeObject *typeObj = curObj->getType(cx);
+
+            if (typeObj->unknownProperties())
                 return true;
 
-            // If anyone on the chain is watched, TI thinks they have an own
-            // property, which means if they were to actually overwrite the
-            // property accessors, we would never know, since we are freezing on
-            // setting that flag.
-            if (curObj->watched())
+            // Check here to make sure that nobody on the prototype chain is
+            // marked as having the property as an "own property". This can
+            // happen in cases of |delete| having been used, or cases with
+            // watched objects. If TI ever decides to be more accurate about
+            // |delete| handling, this should go back to curObj->watched().
+
+            // Even though we are not directly accessing the properties on the whole
+            // prototype chain, we need to fault in the sets anyway, as we need
+            // to freeze on them.
+            jsid typeId = types::MakeTypeId(cx, id);
+            types::HeapTypeSet *propSet = typeObj->getProperty(cx, typeId, false);
+            if (!propSet)
+                return false;
+            if (propSet->ownProperty(false))
                 return true;
 
             curObj = curObj->getProto();
         }
     }
 
     // No need to add a freeze if we didn't find anything
     if (!found)
@@ -5513,16 +5526,18 @@ IonBuilder::TestCommonPropFunc(JSContext
         // If we found a Singleton object's own-property, there's nothing to
         // freeze.
         if (obj != foundProto) {
             // Walk the prototype chain. Everyone has to have the property, since we
             // just checked, so propSet cannot be NULL.
             jsid typeId = types::MakeTypeId(cx, id);
             while (true) {
                 types::HeapTypeSet *propSet = curType->getProperty(cx, typeId, false);
+                // This assert is now assured, since we have faulted them in
+                // above.
                 JS_ASSERT(propSet);
                 // Asking, freeze by asking.
                 DebugOnly<bool> isOwn = propSet->isOwnProperty(cx, curType, false);
                 JS_ASSERT(!isOwn);
                 // Don't mark the proto. It will be held down by the shape
                 // guard. This allows us tp use properties found on prototypes
                 // with properties unknown to TI.
                 if (curType->proto == foundProto)
@@ -5729,32 +5744,32 @@ IonBuilder::annotateGetPropertyCache(JSC
 
 // Returns true if an idempotent cache has ever invalidated this script
 // or an outer script.
 bool
 IonBuilder::invalidatedIdempotentCache()
 {
     IonBuilder *builder = this;
     do {
-        if (builder->script->invalidatedIdempotentCache)
+        if (builder->script()->invalidatedIdempotentCache)
             return true;
         builder = builder->callerBuilder_;
     } while (builder);
 
     return false;
 }
 
 bool
 IonBuilder::loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType)
 {
     JS_ASSERT(shape->hasDefaultGetter());
     JS_ASSERT(shape->hasSlot());
 
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script, pc);
+    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script_, pc);
+    types::StackTypeSet *types = oracle->propertyRead(script_, pc);
 
     if (shape->slot() < shape->numFixedSlots()) {
         MLoadFixedSlot *load = MLoadFixedSlot::New(obj, shape->slot());
         current->add(load);
         current->push(load);
 
         load->setResultType(rvalType);
         return pushTypeBarrier(load, types, barrier);
@@ -5796,40 +5811,40 @@ IonBuilder::storeSlot(MDefinition *obj, 
     if (needsBarrier)
         store->setNeedsBarrier();
     return resumeAfter(store);
 }
 
 bool
 IonBuilder::jsop_getprop(HandlePropertyName name)
 {
-    LazyArgumentsType isArguments = oracle->propertyReadMagicArguments(script, pc);
+    LazyArgumentsType isArguments = oracle->propertyReadMagicArguments(script_, pc);
     if (isArguments == MaybeArguments)
         return abort("Type is not definitely lazy arguments.");
     if (isArguments == DefinitelyArguments) {
         if (JSOp(*pc) == JSOP_LENGTH)
             return jsop_arguments_length();
         // Can also be a callee.
     }
 
     MDefinition *obj = current->pop();
     MInstruction *ins;
 
-    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script, pc);
-    types::StackTypeSet *types = oracle->propertyRead(script, pc);
-
-    TypeOracle::Unary unary = oracle->unaryOp(script, pc);
-    TypeOracle::UnaryTypes unaryTypes = oracle->unaryTypes(script, pc);
+    types::StackTypeSet *barrier = oracle->propertyReadBarrier(script_, pc);
+    types::StackTypeSet *types = oracle->propertyRead(script_, pc);
+
+    TypeOracle::Unary unary = oracle->unaryOp(script_, pc);
+    TypeOracle::UnaryTypes unaryTypes = oracle->unaryTypes(script_, pc);
 
     RootedId id(cx, NameToId(name));
 
     JSObject *singleton = types ? types->getSingleton() : NULL;
     if (singleton && !barrier) {
         bool isKnownConstant, testObject;
-        RootedObject global(cx, &script->global());
+        RootedObject global(cx, &script_->global());
         if (!TestSingletonPropertyTypes(cx, unaryTypes.inTypes,
                                         global, id,
                                         &isKnownConstant, &testObject))
         {
             return false;
         }
 
         if (isKnownConstant) {
@@ -5900,17 +5915,17 @@ IonBuilder::jsop_getprop(HandlePropertyN
     }
 
     if (unary.ival == MIRType_Object) {
         MIRType rvalType = MIRType_Value;
         if (!barrier && !IsNullOrUndefined(unary.rval))
             rvalType = unary.rval;
 
         Shape *objShape;
-        if ((objShape = mjit::GetPICSingleShape(cx, script, pc, info().constructing())) &&
+        if ((objShape = mjit::GetPICSingleShape(cx, script_, pc, info().constructing())) &&
             !objShape->inDictionary())
         {
             // The JM IC was monomorphic, so we inline the property access as
             // long as the shape is not in dictionary mode. We cannot be sure
             // that the shape is still a lastProperty, and calling
             // Shape::search() on dictionary mode shapes that aren't
             // lastProperty is invalid.
             MGuardShape *guard = MGuardShape::New(obj, objShape, Bailout_CachedShapeGuard);
@@ -5930,17 +5945,17 @@ IonBuilder::jsop_getprop(HandlePropertyN
         load->setResultType(rvalType);
 
         // Try to mark the cache as idempotent. We only do this if JM is enabled
         // (its ICs are used to mark property reads as likely non-idempotent) or
         // if we are compiling eagerly (to improve test coverage).
         if ((cx->methodJitEnabled || js_IonOptions.eagerCompilation) &&
             !invalidatedIdempotentCache())
         {
-            if (oracle->propertyReadIdempotent(script, pc, id))
+            if (oracle->propertyReadIdempotent(script_, pc, id))
                 load->setIdempotent();
         }
 
         ins = load;
         if (JSOp(*pc) == JSOP_CALLPROP) {
             if (!annotateGetPropertyCache(cx, obj, load, unaryTypes.inTypes, types))
                 return false;
         }
@@ -5960,19 +5975,19 @@ IonBuilder::jsop_getprop(HandlePropertyN
 }
 
 bool
 IonBuilder::jsop_setprop(HandlePropertyName name)
 {
     MDefinition *value = current->pop();
     MDefinition *obj = current->pop();
 
-    bool monitored = !oracle->propertyWriteCanSpecialize(script, pc);
-
-    TypeOracle::BinaryTypes binaryTypes = oracle->binaryTypes(script, pc);
+    bool monitored = !oracle->propertyWriteCanSpecialize(script_, pc);
+
+    TypeOracle::BinaryTypes binaryTypes = oracle->binaryTypes(script_, pc);
 
     if (!monitored) {
         if (types::HeapTypeSet *propTypes = GetDefiniteSlot(cx, binaryTypes.lhsTypes, name)) {
             MStoreFixedSlot *fixed = MStoreFixedSlot::New(obj, propTypes->definiteSlot(), value);
             current->add(fixed);
             current->push(value);
             if (propTypes->needsBarrier(cx))
                 fixed->setNeedsBarrier();
@@ -6016,47 +6031,47 @@ IonBuilder::jsop_setprop(HandlePropertyN
         MCall *call = makeCallHelper(setter, 1, false);
         if (!call)
             return false;
 
         current->push(value);
         return resumeAfter(call);
     }
 
-    oracle->binaryOp(script, pc);
+    oracle->binaryOp(script_, pc);
 
     MSetPropertyInstruction *ins;
     if (monitored) {
-        ins = MCallSetProperty::New(obj, value, name, script->strictModeCode);
+        ins = MCallSetProperty::New(obj, value, name, script_->strictModeCode);
     } else {
         Shape *objShape;
-        if ((objShape = mjit::GetPICSingleShape(cx, script, pc, info().constructing())) &&
+        if ((objShape = mjit::GetPICSingleShape(cx, script_, pc, info().constructing())) &&
             !objShape->inDictionary())
         {
             // The JM IC was monomorphic, so we inline the property access as
             // long as the shape is not in dictionary mode. We cannot be sure
             // that the shape is still a lastProperty, and calling Shape::search
             // on dictionary mode shapes that aren't lastProperty is invalid.
             MGuardShape *guard = MGuardShape::New(obj, objShape, Bailout_CachedShapeGuard);
             current->add(guard);
 
             Shape *shape = objShape->search(cx, NameToId(name));
             JS_ASSERT(shape);
 
             spew("Inlining monomorphic SETPROP");
 
             jsid typeId = types::MakeTypeId(cx, id);
-            bool needsBarrier = oracle->propertyWriteNeedsBarrier(script, pc, typeId);
+            bool needsBarrier = oracle->propertyWriteNeedsBarrier(script_, pc, typeId);
 
             return storeSlot(obj, shape, value, needsBarrier);
         }
 
         spew("SETPROP not monomorphic");
 
-        ins = MSetPropertyCache::New(obj, value, name, script->strictModeCode);
+        ins = MSetPropertyCache::New(obj, value, name, script_->strictModeCode);
 
         if (!binaryTypes.lhsTypes || binaryTypes.lhsTypes->propertyNeedsBarrier(cx, id))
             ins->setNeedsBarrier();
     }
 
     current->add(ins);
     current->push(value);
 
@@ -6074,17 +6089,17 @@ IonBuilder::jsop_delprop(HandlePropertyN
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::jsop_regexp(RegExpObject *reobj)
 {
-    JSObject *prototype = script->global().getOrCreateRegExpPrototype(cx);
+    JSObject *prototype = script_->global().getOrCreateRegExpPrototype(cx);
     if (!prototype)
         return false;
 
     MRegExp *ins = MRegExp::New(reobj, prototype, MRegExp::MustClone);
     current->add(ins);
     current->push(ins);
 
     return true;
@@ -6098,105 +6113,105 @@ IonBuilder::jsop_object(JSObject *obj)
     current->push(ins);
 
     return true;
 }
 
 bool
 IonBuilder::jsop_lambda(JSFunction *fun)
 {
-    JS_ASSERT(script->analysis()->usesScopeChain());
+    JS_ASSERT(script_->analysis()->usesScopeChain());
     MLambda *ins = MLambda::New(current->scopeChain(), fun);
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::jsop_deflocalfun(uint32 local, JSFunction *fun)
 {
-    JS_ASSERT(script->analysis()->usesScopeChain());
+    JS_ASSERT(script_->analysis()->usesScopeChain());
 
     MLambda *ins = MLambda::New(current->scopeChain(), fun);
     current->add(ins);
     current->push(ins);
 
     current->setLocal(local);
     current->pop();
 
     return resumeAfter(ins);
 }
 
 bool
 IonBuilder::jsop_defvar(uint32 index)
 {
     JS_ASSERT(JSOp(*pc) == JSOP_DEFVAR || JSOp(*pc) == JSOP_DEFCONST);
 
-    PropertyName *name = script->getName(index);
+    PropertyName *name = script_->getName(index);
 
     // Bake in attrs.
     unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT;
     if (JSOp(*pc) == JSOP_DEFCONST)
         attrs |= JSPROP_READONLY;
 
     // Pass the ScopeChain.
-    JS_ASSERT(script->analysis()->usesScopeChain());
+    JS_ASSERT(script_->analysis()->usesScopeChain());
 
     // Bake the name pointer into the MDefVar.
     MDefVar *defvar = MDefVar::New(name, attrs, current->scopeChain());
     current->add(defvar);
 
     return resumeAfter(defvar);
 }
 
 bool
 IonBuilder::jsop_this()
 {
     if (!info().fun())
         return abort("JSOP_THIS outside of a JSFunction.");
 
-    if (script->strictModeCode) {
+    if (script_->strictModeCode) {
         current->pushSlot(info().thisSlot());
         return true;
     }
 
-    types::StackTypeSet *types = oracle->thisTypeSet(script);
+    types::StackTypeSet *types = oracle->thisTypeSet(script_);
     if (types && types->getKnownTypeTag() == JSVAL_TYPE_OBJECT) {
         // This is safe, because if the entry type of |this| is an object, it
         // will necessarily be an object throughout the entire function. OSR
         // can introduce a phi, but this phi will be specialized.
         current->pushSlot(info().thisSlot());
         return true;
     }
 
     return abort("JSOP_THIS hard case not yet handled");
 }
 
 bool
 IonBuilder::jsop_typeof()
 {
-    TypeOracle::Unary unary = oracle->unaryOp(script, pc);
+    TypeOracle::Unary unary = oracle->unaryOp(script_, pc);
 
     MDefinition *input = current->pop();
     MTypeOf *ins = MTypeOf::New(input, unary.ival);
 
     current->add(ins);
     current->push(ins);
 
     if (ins->isEffectful() && !resumeAfter(ins))
         return false;
     return true;
 }
 
 bool
 IonBuilder::jsop_toid()
 {
     // No-op if the index is an integer.
-    TypeOracle::Unary unary = oracle->unaryOp(script, pc);
+    TypeOracle::Unary unary = oracle->unaryOp(script_, pc);
     if (unary.ival == MIRType_Int32)
         return true;
 
     MDefinition *index = current->pop();
     MToId *ins = MToId::New(current->peek(-1), index);
 
     current->add(ins);
     current->push(ins);
@@ -6267,21 +6282,21 @@ IonBuilder::walkScopeChain(unsigned hops
 
     return scope;
 }
 
 bool
 IonBuilder::jsop_getaliasedvar(ScopeCoordinate sc)
 {
     types::StackTypeSet *barrier;
-    types::StackTypeSet *actual = oracle->aliasedVarBarrier(script, pc, &barrier);
+    types::StackTypeSet *actual = oracle->aliasedVarBarrier(script_, pc, &barrier);
 
     MDefinition *obj = walkScopeChain(sc.hops);
 
-    RootedShape shape(cx, ScopeCoordinateToStaticScope(script, pc).scopeShape());
+    RootedShape shape(cx, ScopeCoordinateToStaticScope(script_, pc).scopeShape());
 
     MInstruction *load;
     if (shape->numFixedSlots() <= sc.slot) {
         MInstruction *slots = MSlots::New(obj);
         current->add(slots);
 
         load = MLoadSlot::New(slots, sc.slot - shape->numFixedSlots());
     } else {
@@ -6305,17 +6320,17 @@ IonBuilder::jsop_getaliasedvar(ScopeCoor
 }
 
 bool
 IonBuilder::jsop_setaliasedvar(ScopeCoordinate sc)
 {
     MDefinition *rval = current->peek(-1);
     MDefinition *obj = walkScopeChain(sc.hops);
 
-    RootedShape shape(cx, ScopeCoordinateToStaticScope(script, pc).scopeShape());
+    RootedShape shape(cx, ScopeCoordinateToStaticScope(script_, pc).scopeShape());
 
     MInstruction *store;
     if (shape->numFixedSlots() <= sc.slot) {
         MInstruction *slots = MSlots::New(obj);
         current->add(slots);
 
         store = MStoreSlot::NewBarriered(slots, sc.slot - shape->numFixedSlots(), rval);
     } else {
--- a/js/src/ion/IonBuilder.h
+++ b/js/src/ion/IonBuilder.h
@@ -410,28 +410,30 @@ class IonBuilder : public MIRGenerator
 
     MPolyInlineDispatch *
     makePolyInlineDispatch(JSContext *cx, AutoObjectVector &targets, int argc,
                            MGetPropertyCache *getPropCache,
                            types::StackTypeSet *types, types::StackTypeSet *barrier,
                            MBasicBlock *bottom,
                            Vector<MDefinition *, 8, IonAllocPolicy> &retvalDefns);
 
+    // A builder is inextricably tied to a particular script.
+    HeapPtrScript script_;
+
   public:
-    // A builder is inextricably tied to a particular script.
-    JSScript * const script;
-
     // Compilation index for this attempt.
     types::RecompileInfo const recompileInfo;
 
     // If off thread compilation is successful, final LIR is attached here.
     LIRGraph *lir;
 
     void clearForBackEnd();
 
+    JSScript *script() const { return script_; }
+
   private:
     JSContext *cx;
 
     jsbytecode *pc;
     MBasicBlock *current;
     uint32 loopDepth_;
 
     /* Information used for inline-call builders. */
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -309,17 +309,17 @@ js::ion::GetPropertyCache(JSContext *cx,
     JSScript *topScript = GetTopIonJSScript(cx);
     IonScript *ion = topScript->ionScript();
 
     IonCacheGetProperty &cache = ion->getCache(cacheIndex).toGetProperty();
     RootedPropertyName name(cx, cache.name());
 
     RootedScript script(cx);
     jsbytecode *pc;
-    cache.getScriptedLocation(script.address(), &pc);
+    cache.getScriptedLocation(&script, &pc);
 
     // Override the return value if we are invalidated (bug 728188).
     AutoDetectInvalidation adi(cx, vp.address(), ion);
 
     // If the cache is idempotent, we will redo the op in the interpreter.
     if (cache.idempotent())
         adi.disable();
 
@@ -826,17 +826,17 @@ js::ion::GetElementCache(JSContext *cx, 
             // Generate at most one dense array stub.
             cache.incrementStubCount();
 
             if (!cache.attachDenseArray(cx, obj, idval))
                 return false;
         }
     }
 
-    JSScript *script;
+    RootedScript script(cx);
     jsbytecode *pc;
     cache.getScriptedLocation(&script, &pc);
 
     RootedValue lval(cx, ObjectValue(*obj));
     if (!GetElementOperation(cx, JSOp(*pc), lval, idval, res))
         return false;
 
     types::TypeScript::Monitor(cx, script, pc, res);
@@ -1150,17 +1150,17 @@ js::ion::GetNameCache(JSContext *cx, siz
 {
     AutoFlushCache afc ("GetNameCache");
 
     IonScript *ion = GetTopIonJSScript(cx)->ionScript();
 
     IonCacheName &cache = ion->getCache(cacheIndex).toName();
     RootedPropertyName name(cx, cache.name());
 
-    JSScript *script;
+    RootedScript script(cx);
     jsbytecode *pc;
     cache.getScriptedLocation(&script, &pc);
 
     RootedObject obj(cx);
     RootedObject holder(cx);
     RootedShape shape(cx);
     if (!LookupName(cx, name, scopeChain, &obj, &holder, &shape))
         return false;
--- a/js/src/ion/IonCaches.h
+++ b/js/src/ion/IonCaches.h
@@ -228,18 +228,18 @@ class IonCache
     }
 
     void setScriptedLocation(JSScript *script, jsbytecode *pc) {
         JS_ASSERT(!idempotent_);
         this->script = script;
         this->pc = pc;
     }
 
-    void getScriptedLocation(JSScript **pscript, jsbytecode **ppc) {
-        *pscript = script;
+    void getScriptedLocation(MutableHandleScript pscript, jsbytecode **ppc) {
+        pscript.set(script);
         *ppc = pc;
     }
 };
 
 inline IonCache &
 IonScript::getCache(size_t index) {
     JS_ASSERT(index < numCaches());
     return cacheList()[index];
--- a/js/src/ion/IonFrames.cpp
+++ b/js/src/ion/IonFrames.cpp
@@ -652,28 +652,28 @@ ion::MarkIonActivations(JSRuntime *rt, J
 void
 ion::AutoTempAllocatorRooter::trace(JSTracer *trc)
 {
     for (CompilerRootNode *root = temp->rootList(); root != NULL; root = root->next)
         gc::MarkGCThingRoot(trc, root->address(), "ion-compiler-root");
 }
 
 void
-ion::GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes)
+ion::GetPcScript(JSContext *cx, MutableHandleScript scriptRes, jsbytecode **pcRes)
 {
     JS_ASSERT(cx->fp()->beginsIonActivation());
     IonSpew(IonSpew_Snapshots, "Recover PC & Script from the last frame.");
 
     // Recover the innermost inlined frame.
     IonFrameIterator it(cx->runtime->ionTop);
     ++it;
     InlineFrameIterator ifi(&it);
 
     // Set the result.
-    *scriptRes = ifi.script();
+    scriptRes.set(ifi.script());
     if (pcRes)
         *pcRes = ifi.pc();
 }
 
 void
 OsiIndex::fixUpOffset(MacroAssembler &masm)
 {
     callPointDisplacement_ = masm.actualOffset(callPointDisplacement_);
--- a/js/src/ion/IonFrames.h
+++ b/js/src/ion/IonFrames.h
@@ -257,17 +257,17 @@ namespace js {
 namespace ion {
 
 JSScript *
 GetTopIonJSScript(JSContext *cx,
                   const SafepointIndex **safepointIndexOut = NULL,
                   void **returnAddrOut = NULL);
 
 void
-GetPcScript(JSContext *cx, JSScript **scriptRes, jsbytecode **pcRes);
+GetPcScript(JSContext *cx, MutableHandleScript scriptRes, jsbytecode **pcRes);
 
 // Given a slot index, returns the offset, in bytes, of that slot from an
 // IonJSFrameLayout. Slot distances are uniform across architectures, however,
 // the distance does depend on the size of the frame header.
 static inline int32
 OffsetOfFrameSlot(int32 slot)
 {
     if (slot <= 0)
--- a/js/src/ion/MCallOptimize.cpp
+++ b/js/src/ion/MCallOptimize.cpp
@@ -94,33 +94,33 @@ IonBuilder::discardCall(uint32 argc, MDe
     bb->pop();
     return true;
 }
 
 types::StackTypeSet *
 IonBuilder::getInlineReturnTypeSet()
 {
     types::StackTypeSet *barrier;
-    types::StackTypeSet *returnTypes = oracle->returnTypeSet(script, pc, &barrier);
+    types::StackTypeSet *returnTypes = oracle->returnTypeSet(script_, pc, &barrier);
 
     JS_ASSERT(returnTypes);
     return returnTypes;
 }
 
 MIRType
 IonBuilder::getInlineReturnType()
 {
     types::StackTypeSet *returnTypes = getInlineReturnTypeSet();
     return MIRTypeFromValueType(returnTypes->getKnownTypeTag());
 }
 
 types::StackTypeSet *
 IonBuilder::getInlineArgTypeSet(uint32 argc, uint32 arg)
 {
-    types::StackTypeSet *argTypes = oracle->getCallArg(script, argc, arg, pc);
+    types::StackTypeSet *argTypes = oracle->getCallArg(script_, argc, arg, pc);
     JS_ASSERT(argTypes);
     return argTypes;
 }
 
 MIRType
 IonBuilder::getInlineArgType(uint32 argc, uint32 arg)
 {
     types::StackTypeSet *argTypes = getInlineArgTypeSet(argc, arg);
@@ -240,16 +240,17 @@ IonBuilder::inlineArrayPopShift(MArrayPo
     // Inference's TypeConstraintCall generates the constraints that propagate
     // properties directly into the result type set.
     types::TypeObjectFlags unhandledFlags =
         types::OBJECT_FLAG_NON_DENSE_ARRAY | types::OBJECT_FLAG_ITERATED;
 
     types::StackTypeSet *thisTypes = getInlineArgTypeSet(argc, 0);
     if (thisTypes->hasObjectFlags(cx, unhandledFlags))
         return InliningStatus_NotInlined;
+    RootedScript script(cx, script_);
     if (types::ArrayPrototypeHasIndexedProperty(cx, script))
         return InliningStatus_NotInlined;
 
     MDefinitionVector argv;
     if (!discardCall(argc, argv, current))
         return InliningStatus_Error;
 
     types::StackTypeSet *returnTypes = getInlineReturnTypeSet();
@@ -277,16 +278,17 @@ IonBuilder::inlineArrayPush(uint32 argc,
     if (getInlineArgType(argc, 0) != MIRType_Object)
         return InliningStatus_NotInlined;
 
     // Inference's TypeConstraintCall generates the constraints that propagate
     // properties directly into the result type set.
     types::StackTypeSet *thisTypes = getInlineArgTypeSet(argc, 0);
     if (thisTypes->hasObjectFlags(cx, types::OBJECT_FLAG_NON_DENSE_ARRAY))
         return InliningStatus_NotInlined;
+    RootedScript script(cx, script_);
     if (types::ArrayPrototypeHasIndexedProperty(cx, script))
         return InliningStatus_NotInlined;
 
     MDefinitionVector argv;
     if (!discardCall(argc, argv, current))
         return InliningStatus_Error;
 
     MArrayPush *ins = MArrayPush::New(argv[0], argv[1]);
--- a/js/src/ion/TypeOracle.cpp
+++ b/js/src/ion/TypeOracle.cpp
@@ -16,17 +16,17 @@ using namespace js;
 using namespace js::ion;
 using namespace js::types;
 using namespace js::analyze;
 
 bool
 TypeInferenceOracle::init(JSContext *cx, JSScript *script)
 {
     this->cx = cx;
-    this->script = script;
+    this->script_.init(script);
     return script->ensureRanInference(cx);
 }
 
 MIRType
 GetMIRType(JSValueType type)
 {
     /* Get the suggested representation to use for values in a given type set. */
     switch (type) {
@@ -61,28 +61,28 @@ MIRType
 TypeInferenceOracle::getMIRType(HeapTypeSet *types)
 {
     return GetMIRType(types->getKnownTypeTag(cx));
 }
 
 TypeOracle::UnaryTypes
 TypeInferenceOracle::unaryTypes(JSScript *script, jsbytecode *pc)
 {
-    JS_ASSERT(script == this->script);
+    JS_ASSERT(script == this->script_);
 
     UnaryTypes res;
     res.inTypes = script->analysis()->poppedTypes(pc, 0);
     res.outTypes = script->analysis()->pushedTypes(pc, 0);
     return res;
 }
 
 TypeOracle::BinaryTypes
 TypeInferenceOracle::binaryTypes(JSScript *script, jsbytecode *pc)
 {
-    JS_ASSERT(script == this->script);
+    JS_ASSERT(script == this->script_);
 
     JSOp op = (JSOp)*pc;
 
     BinaryTypes res;
     if ((js_CodeSpec[op].format & JOF_INCDEC) || op == JSOP_NEG || op == JSOP_POS) {
         res.lhsTypes = script->analysis()->poppedTypes(pc, 0);
         res.rhsTypes = NULL;
         res.outTypes = script->analysis()->pushedTypes(pc, 0);
@@ -116,28 +116,28 @@ TypeInferenceOracle::incslot(JSScript *s
     b.rhsTypes = NULL;
     b.outTypes = script->analysis()->pushedTypes(pc, 0);
     return b;
 }
 
 TypeOracle::Unary
 TypeInferenceOracle::unaryOp(JSScript *script, jsbytecode *pc)
 {
-    JS_ASSERT(script == this->script);
+    JS_ASSERT(script == this->script_);
 
     Unary res;
     res.ival = getMIRType(script->analysis()->poppedTypes(pc, 0));
     res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0));
     return res;
 }
 
 TypeOracle::Binary
 TypeInferenceOracle::binaryOp(JSScript *script, jsbytecode *pc)
 {
-    JS_ASSERT(script == this->script);
+    JS_ASSERT(script == this->script_);
 
     JSOp op = (JSOp)*pc;
 
     Binary res;
     if ((js_CodeSpec[op].format & JOF_INCDEC) || op == JSOP_NEG || op == JSOP_POS) {
         res.lhs = getMIRType(script->analysis()->poppedTypes(pc, 0));
         res.rhs = MIRType_Int32;
         res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0));
@@ -147,55 +147,55 @@ TypeInferenceOracle::binaryOp(JSScript *
         res.rval = getMIRType(script->analysis()->pushedTypes(pc, 0));
     }
     return res;
 }
 
 StackTypeSet *
 TypeInferenceOracle::thisTypeSet(JSScript *script)
 {
-    JS_ASSERT(script == this->script);
+    JS_ASSERT(script == this->script_);
     return TypeScript::ThisTypes(script);
 }
 
 bool
 TypeInferenceOracle::getOsrTypes(jsbytecode *osrPc, Vector<MIRType> &slotTypes)
 {
     JS_ASSERT(JSOp(*osrPc) == JSOP_LOOPENTRY);
-    JS_ASSERT(script->code < osrPc);
-    JS_ASSERT(osrPc < script->code + script->length);
+    JS_ASSERT(script_->code < osrPc);
+    JS_ASSERT(osrPc < script_->code + script_->length);
 
     Vector<types::StackTypeSet *> slotTypeSets(cx);
-    if (!slotTypeSets.resize(TotalSlots(script)))
+    if (!slotTypeSets.resize(TotalSlots(script_)))
         return false;
 
-    for (uint32_t slot = ThisSlot(); slot < TotalSlots(script); slot++)
-        slotTypeSets[slot] = TypeScript::SlotTypes(script, slot);
+    for (uint32_t slot = ThisSlot(); slot < TotalSlots(script_); slot++)
+        slotTypeSets[slot] = TypeScript::SlotTypes(script_, slot);
 
-    jsbytecode *pc = script->code;
-    ScriptAnalysis *analysis = script->analysis();
+    jsbytecode *pc = script_->code;
+    ScriptAnalysis *analysis = script_->analysis();
 
     // To determine the slot types at the OSR pc, we have to do a forward walk
     // over the bytecode to reconstruct the types.
     for (;;) {
         Bytecode *opinfo = analysis->maybeCode(pc);
         if (opinfo) {
             if (opinfo->jumpTarget) {
                 // Update variable types for all new values at this bytecode.
                 if (const SlotValue *newv = analysis->newValues(pc)) {
                     while (newv->slot) {
-                        if (newv->slot < TotalSlots(script))
+                        if (newv->slot < TotalSlots(script_))
                             slotTypeSets[newv->slot] = analysis->getValueTypes(newv->value);
                         newv++;
                     }
                 }
             }
 
             if (BytecodeUpdatesSlot(JSOp(*pc))) {
-                uint32_t slot = GetBytecodeSlot(script, pc);
+                uint32_t slot = GetBytecodeSlot(script_, pc);
                 if (analysis->trackSlot(slot))
                     slotTypeSets[slot] = analysis->pushedTypes(pc, 0);
             }
         }
 
         if (pc == osrPc)
             break;
 
@@ -208,35 +208,35 @@ TypeInferenceOracle::getOsrTypes(jsbytec
     // scripts. This means we have to subtract 1 for global/eval scripts.
     JS_ASSERT(ThisSlot() == 1);
     JS_ASSERT(ArgSlot(0) == 2);
 
 #ifdef DEBUG
     uint32_t stackDepth = analysis->getCode(osrPc).stackDepth;
 #endif
 
-    if (script->function()) {
-        JS_ASSERT(slotTypes.length() == TotalSlots(script) + stackDepth);
+    if (script_->function()) {
+        JS_ASSERT(slotTypes.length() == TotalSlots(script_) + stackDepth);
 
-        for (size_t i = ThisSlot(); i < TotalSlots(script); i++)
+        for (size_t i = ThisSlot(); i < TotalSlots(script_); i++)
             slotTypes[i] = getMIRType(slotTypeSets[i]);
     } else {
-        JS_ASSERT(slotTypes.length() == TotalSlots(script) + stackDepth - 1);
+        JS_ASSERT(slotTypes.length() == TotalSlots(script_) + stackDepth - 1);
 
-        for (size_t i = ArgSlot(0); i < TotalSlots(script); i++)
+        for (size_t i = ArgSlot(0); i < TotalSlots(script_); i++)
             slotTypes[i - 1] = getMIRType(slotTypeSets[i]);
     }
 
     return true;
 }
 
 StackTypeSet *
 TypeInferenceOracle::parameterTypeSet(JSScript *script, size_t index)
 {
-    JS_ASSERT(script == this->script);
+    JS_ASSERT(script == this->script_);
     return TypeScript::ArgTypes(script, index);
 }
 
 StackTypeSet *
 TypeInferenceOracle::propertyRead(JSScript *script, jsbytecode *pc)
 {
     return script->analysis()->pushedTypes(pc, 0);
 }
@@ -463,23 +463,24 @@ TypeInferenceOracle::elementWrite(JSScri
     }
 
     return elementType;
 }
 
 bool
 TypeInferenceOracle::arrayPrototypeHasIndexedProperty()
 {
+    RootedScript script(cx, script_);
     return ArrayPrototypeHasIndexedProperty(cx, script);
 }
 
 bool
 TypeInferenceOracle::canInlineCalls()
 {
-    return script->analysis()->hasFunctionCalls();
+    return script_->analysis()->hasFunctionCalls();
 }
 
 bool
 TypeInferenceOracle::propertyWriteCanSpecialize(JSScript *script, jsbytecode *pc)
 {
     return !script->analysis()->getCode(pc).monitoredTypes;
 }
 
@@ -497,20 +498,20 @@ TypeInferenceOracle::elementWriteNeedsBa
     // a property. The object is the third value popped by SETELEM.
     StackTypeSet *types = script->analysis()->poppedTypes(pc, 2);
     return types->propertyNeedsBarrier(cx, JSID_VOID);
 }
 
 StackTypeSet *
 TypeInferenceOracle::getCallTarget(JSScript *caller, uint32 argc, jsbytecode *pc)
 {
-    JS_ASSERT(caller == this->script);
+    JS_ASSERT(caller == this->script_);
     JS_ASSERT(js_CodeSpec[*pc].format & JOF_INVOKE && JSOp(*pc) != JSOP_EVAL);
 
-    ScriptAnalysis *analysis = script->analysis();
+    ScriptAnalysis *analysis = script_->analysis();
     return analysis->poppedTypes(pc, argc + 1);
 }
 
 StackTypeSet *
 TypeInferenceOracle::getCallArg(JSScript *script, uint32 argc, uint32 arg, jsbytecode *pc)
 {
     JS_ASSERT(argc >= arg);
     // Bytecode order: Function, This, Arg0, Arg1, ..., ArgN, Call.
--- a/js/src/ion/TypeOracle.h
+++ b/js/src/ion/TypeOracle.h
@@ -208,23 +208,23 @@ class DummyOracle : public TypeOracle
         b.rval = MIRType_Int32;
         return b;
     }
 };
 
 class TypeInferenceOracle : public TypeOracle
 {
     JSContext *cx;
-    JSScript *script;
+    HeapPtrScript script_;
 
     MIRType getMIRType(types::StackTypeSet *types);
     MIRType getMIRType(types::HeapTypeSet *types);
 
   public:
-    TypeInferenceOracle() : cx(NULL), script(NULL) {}
+    TypeInferenceOracle() : cx(NULL), script_(NULL) {}
 
     bool init(JSContext *cx, JSScript *script);
 
     UnaryTypes unaryTypes(JSScript *script, jsbytecode *pc);
     BinaryTypes binaryTypes(JSScript *script, jsbytecode *pc);
     Unary unaryOp(JSScript *script, jsbytecode *pc);
     Binary binaryOp(JSScript *script, jsbytecode *pc);
     types::StackTypeSet *thisTypeSet(JSScript *script);
--- a/js/src/ion/shared/Assembler-x86-shared.cpp
+++ b/js/src/ion/shared/Assembler-x86-shared.cpp
@@ -3,16 +3,18 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ion/IonMacroAssembler.h"
 #include "gc/Marking.h"
 
+#include "jsscriptinlines.h"
+
 using namespace js;
 using namespace js::ion;
 
 void
 AssemblerX86Shared::copyJumpRelocationTable(uint8 *dest)
 {
     if (jumpRelocations_.length())
         memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length());
--- a/js/src/ion/shared/MoveEmitter-x86-shared.cpp
+++ b/js/src/ion/shared/MoveEmitter-x86-shared.cpp
@@ -2,16 +2,18 @@
  * vim: set ts=4 sw=4 et tw=99:
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MoveEmitter-x86-shared.h"
 
+#include "jsscriptinlines.h"
+
 using namespace js;
 using namespace js::ion;
 
 MoveEmitterX86::MoveEmitterX86(MacroAssemblerSpecific &masm)
   : inCycle_(false),
     masm(masm),
     pushedAtCycle_(-1),
     pushedAtSpill_(-1),
--- a/js/src/ion/x64/Assembler-x64.cpp
+++ b/js/src/ion/x64/Assembler-x64.cpp
@@ -3,16 +3,18 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Assembler-x64.h"
 #include "gc/Marking.h"
 
+#include "jsscriptinlines.h"
+
 using namespace js;
 using namespace js::ion;
 
 void
 Assembler::writeRelocation(JmpSrc src, Relocation::Kind reloc)
 {
     if (!jumpRelocations_.length()) {
         // The jump relocation table starts with a fixed-width integer pointing
--- a/js/src/ion/x64/MacroAssembler-x64.cpp
+++ b/js/src/ion/x64/MacroAssembler-x64.cpp
@@ -4,16 +4,18 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MacroAssembler-x64.h"
 #include "ion/MoveEmitter.h"
 #include "ion/IonFrames.h"
 
+#include "jsscriptinlines.h"
+
 using namespace js;
 using namespace js::ion;
 
 void
 MacroAssemblerX64::setupABICall(uint32 args)
 {
     JS_ASSERT(!inCall_);
     inCall_ = true;
--- a/js/src/ion/x64/Trampoline-x64.cpp
+++ b/js/src/ion/x64/Trampoline-x64.cpp
@@ -9,16 +9,18 @@
 #include "assembler/assembler/MacroAssembler.h"
 #include "ion/IonCompartment.h"
 #include "ion/IonLinker.h"
 #include "ion/IonFrames.h"
 #include "ion/Bailouts.h"
 #include "ion/VMFunctions.h"
 #include "ion/IonSpewer.h"
 
+#include "jsscriptinlines.h"
+
 using namespace js;
 using namespace js::ion;
 
 /* This method generates a trampoline on x64 for a c++ function with
  * the following signature:
  *   JSBool blah(void *code, int argc, Value *argv, Value *vp)
  *   ...using standard x64 fastcall calling convention
  */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug792166-1.js
@@ -0,0 +1,8 @@
+Object.defineProperty(Object.prototype, 'x', {
+    set: function() {}
+});
+var obj = {};
+for (var i = 0; i < 100 ; ++i) {
+    obj.x = 1;
+    delete obj.x;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug792166-2.js
@@ -0,0 +1,8 @@
+try {
+  __defineGetter__("eval", function() {
+    this["__proto__"]
+  })
+  delete this["__proto__"]
+  this["__proto__"]
+} catch (e) {}
+eval
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -50,17 +50,17 @@ IsJumpOpcode(JSOp op)
 // Bytecode Analysis
 /////////////////////////////////////////////////////////////////////
 
 inline bool
 ScriptAnalysis::addJump(JSContext *cx, unsigned offset,
                         unsigned *currentOffset, unsigned *forwardJump, unsigned *forwardLoop,
                         unsigned stackDepth)
 {
-    JS_ASSERT(offset < script->length);
+    JS_ASSERT(offset < script_->length);
 
     Bytecode *&code = codeArray[offset];
     if (!code) {
         code = cx->analysisLifoAlloc().new_<Bytecode>();
         if (!code) {
             setOOM(cx);
             return false;
         }
@@ -106,19 +106,19 @@ ScriptAnalysis::addJump(JSContext *cx, u
 
 void
 ScriptAnalysis::analyzeBytecode(JSContext *cx)
 {
     JS_ASSERT(cx->compartment->activeAnalysis);
     JS_ASSERT(!ranBytecode());
     LifoAlloc &alloc = cx->analysisLifoAlloc();
 
-    numSlots = TotalSlots(script);
+    numSlots = TotalSlots(script_);
 
-    unsigned length = script->length;
+    unsigned length = script_->length;
     codeArray = alloc.newArray<Bytecode*>(length);
     escapedSlots = alloc.newArray<bool>(numSlots);
 
     if (!codeArray || !escapedSlots) {
         setOOM(cx);
         return;
     }
 
@@ -133,39 +133,39 @@ ScriptAnalysis::analyzeBytecode(JSContex
      * (needsArgsObj requires SSA which requires escapedSlots). Lastly, the
      * debugger can access any local at any time. Even though debugger
      * reads/writes are monitored by the DebugScopeProxy, this monitoring
      * updates the flow-insensitive type sets, so we cannot use SSA.
      */
 
     PodZero(escapedSlots, numSlots);
 
-    bool allVarsAliased = script->compartment()->debugMode();
-    bool allArgsAliased = allVarsAliased || script->argumentsHasVarBinding();
+    bool allVarsAliased = script_->compartment()->debugMode();
+    bool allArgsAliased = allVarsAliased || script_->argumentsHasVarBinding();
 
-    for (BindingIter bi(script->bindings); bi; bi++) {
+    for (BindingIter bi(script_->bindings); bi; bi++) {
         if (bi->kind() == ARGUMENT)
             escapedSlots[ArgSlot(bi.frameIndex())] = allArgsAliased || bi->aliased();
         else
-            escapedSlots[LocalSlot(script, bi.frameIndex())] = allVarsAliased || bi->aliased();
+            escapedSlots[LocalSlot(script_, bi.frameIndex())] = allVarsAliased || bi->aliased();
     }
 
     /*
      * If the script is in debug mode, JS_SetFrameReturnValue can be called at
      * any safe point.
      */
     if (cx->compartment->debugMode())
         usesReturnValue_ = true;
 
-    bool heavyweight = script->function() && script->function()->isHeavyweight();
+    bool heavyweight = script_->function() && script_->function()->isHeavyweight();
 
     isJaegerCompileable = true;
 
     isInlineable = true;
-    if (heavyweight || script->argumentsHasVarBinding() || cx->compartment->debugMode())
+    if (heavyweight || script_->argumentsHasVarBinding() || cx->compartment->debugMode())
         isInlineable = false;
 
     modifiesArguments_ = false;
     if (heavyweight)
         modifiesArguments_ = true;
 
     canTrackVars = true;
 
@@ -192,32 +192,32 @@ ScriptAnalysis::analyzeBytecode(JSContex
         return;
     }
 
     startcode->stackDepth = 0;
     codeArray[0] = startcode;
 
     /* Number of JOF_TYPESET opcodes we have encountered. */
     unsigned nTypeSets = 0;
-    types::TypeSet *typeArray = script->types->typeArray();
+    types::TypeSet *typeArray = script_->types->typeArray();
 
     unsigned offset, nextOffset = 0;
     while (nextOffset < length) {
         offset = nextOffset;
 
         JS_ASSERT(forwardCatch <= forwardJump);
 
         /* Check if the current forward jump/try-block has finished. */
         if (forwardJump && forwardJump == offset)
             forwardJump = 0;
         if (forwardCatch && forwardCatch == offset)
             forwardCatch = 0;
 
         Bytecode *code = maybeCode(offset);
-        jsbytecode *pc = script->code + offset;
+        jsbytecode *pc = script_->code + offset;
 
         JSOp op = (JSOp)*pc;
         JS_ASSERT(op < JSOP_LIMIT);
 
         /* Immediate successor of this bytecode. */
         unsigned successorOffset = offset + GetBytecodeLength(pc);
 
         /*
@@ -246,48 +246,48 @@ ScriptAnalysis::analyzeBytecode(JSContex
             continue;
         }
 
         code->analyzed = true;
 
         if (forwardCatch)
             code->inTryBlock = true;
 
-        if (script->hasBreakpointsAt(pc)) {
+        if (script_->hasBreakpointsAt(pc)) {
             code->safePoint = true;
             isInlineable = canTrackVars = false;
         }
 
         unsigned stackDepth = code->stackDepth;
 
         if (!forwardJump)
             code->unconditional = true;
 
         /*
          * Treat decompose ops as no-ops which do not adjust the stack. We will
          * pick up the stack depths as we go through the decomposed version.
          */
         if (!(js_CodeSpec[op].format & JOF_DECOMPOSE)) {
-            unsigned nuses = GetUseCount(script, offset);
-            unsigned ndefs = GetDefCount(script, offset);
+            unsigned nuses = GetUseCount(script_, offset);
+            unsigned ndefs = GetDefCount(script_, offset);
 
             JS_ASSERT(stackDepth >= nuses);
             stackDepth -= nuses;
             stackDepth += ndefs;
         }
 
         /*
          * Assign an observed type set to each reachable JOF_TYPESET opcode.
          * This may be less than the number of type sets in the script if some
          * are unreachable, and may be greater in case the number of type sets
          * overflows a uint16. In the latter case a single type set will be
          * used for the observed types of all ops after the overflow.
          */
         if ((js_CodeSpec[op].format & JOF_TYPESET) && cx->typeInferenceEnabled()) {
-            if (nTypeSets < script->nTypeSets) {
+            if (nTypeSets < script_->nTypeSets) {
                 code->observedTypes = typeArray[nTypeSets++].toStackTypeSet();
             } else {
                 JS_ASSERT(nTypeSets == UINT16_MAX);
                 code->observedTypes = typeArray[nTypeSets - 1].toStackTypeSet();
             }
         }
 
         switch (op) {
@@ -408,20 +408,20 @@ ScriptAnalysis::analyzeBytecode(JSContex
           case JSOP_TRY: {
             /*
              * Everything between a try and corresponding catch or finally is conditional.
              * Note that there is no problem with code which is skipped by a thrown
              * exception but is not caught by a later handler in the same function:
              * no more code will execute, and it does not matter what is defined.
              */
             isInlineable = false;
-            JSTryNote *tn = script->trynotes()->vector;
-            JSTryNote *tnlimit = tn + script->trynotes()->length;
+            JSTryNote *tn = script_->trynotes()->vector;
+            JSTryNote *tnlimit = tn + script_->trynotes()->length;
             for (; tn < tnlimit; tn++) {
-                unsigned startOffset = script->mainOffset + tn->start;
+                unsigned startOffset = script_->mainOffset + tn->start;
                 if (startOffset == offset + 1) {
                     unsigned catchOffset = startOffset + tn->length;
 
                     /* This will overestimate try block code, for multiple catch/finally. */
                     if (catchOffset > forwardCatch)
                         forwardCatch = catchOffset;
 
                     if (tn->kind != JSTRY_ITER) {
@@ -439,32 +439,32 @@ ScriptAnalysis::analyzeBytecode(JSContex
             /*
              * Watch for uses of variables not known to be defined, and mark
              * them as having possible uses before definitions.  Ignore GETLOCAL
              * followed by a POP, these are generated for, e.g. 'var x;'
              */
             jsbytecode *next = pc + JSOP_GETLOCAL_LENGTH;
             if (JSOp(*next) != JSOP_POP || jumpTarget(next)) {
                 uint32_t local = GET_SLOTNO(pc);
-                if (local >= script->nfixed) {
+                if (local >= script_->nfixed) {
                     localsAliasStack_ = true;
                     break;
                 }
             }
             break;
           }
 
           case JSOP_CALLLOCAL:
           case JSOP_INCLOCAL:
           case JSOP_DECLOCAL:
           case JSOP_LOCALINC:
           case JSOP_LOCALDEC:
           case JSOP_SETLOCAL: {
             uint32_t local = GET_SLOTNO(pc);
-            if (local >= script->nfixed) {
+            if (local >= script_->nfixed) {
                 localsAliasStack_ = true;
                 break;
             }
             break;
           }
 
           case JSOP_SETARG:
           case JSOP_INCARG:
@@ -609,17 +609,17 @@ ScriptAnalysis::analyzeBytecode(JSContex
 
             unsigned targetOffset = offset + GET_JUMP_OFFSET(pc);
             if (!addJump(cx, targetOffset, &nextOffset, &forwardJump, &forwardLoop, newStackDepth))
                 return;
         }
 
         /* Handle any fallthrough from this opcode. */
         if (!BytecodeNoFallThrough(op)) {
-            JS_ASSERT(successorOffset < script->length);
+            JS_ASSERT(successorOffset < script_->length);
 
             Bytecode *&nextcode = codeArray[successorOffset];
 
             if (!nextcode) {
                 nextcode = alloc.new_<Bytecode>();
                 if (!nextcode) {
                     setOOM(cx);
                     return;
@@ -644,30 +644,30 @@ ScriptAnalysis::analyzeBytecode(JSContex
 
     ranBytecode_ = true;
 
     /*
      * Always ensure that a script's arguments usage has been analyzed before
      * entering the script. This allows the functionPrologue to ensure that
      * arguments are always created eagerly which simplifies interp logic.
      */
-    if (!script->analyzedArgsUsage())
+    if (!script_->analyzedArgsUsage())
         analyzeSSA(cx);
 
     /*
      * If the script has JIT information (we are reanalyzing the script after
      * a purge), add safepoints for the targets of any cross chunk edges in
      * the script. These safepoints are normally added when the JITScript is
      * constructed, but will have been lost during the purge.
      */
 #ifdef JS_METHODJIT
     mjit::JITScript *jit = NULL;
     for (int constructing = 0; constructing <= 1 && !jit; constructing++) {
         for (int barriers = 0; barriers <= 1 && !jit; barriers++)
-            jit = script->getJIT((bool) constructing, (bool) barriers);
+            jit = script_->getJIT((bool) constructing, (bool) barriers);
     }
     if (jit) {
         mjit::CrossChunkEdge *edges = jit->edges();
         for (size_t i = 0; i < jit->nedges; i++)
             getCode(edges[i].target).safePoint = true;
     }
 #endif
 }
@@ -704,28 +704,28 @@ ScriptAnalysis::analyzeLifetimes(JSConte
     if (!saved) {
         setOOM(cx);
         return;
     }
     unsigned savedCount = 0;
 
     LoopAnalysis *loop = NULL;
 
-    uint32_t offset = script->length - 1;
-    while (offset < script->length) {
+    uint32_t offset = script_->length - 1;
+    while (offset < script_->length) {
         Bytecode *code = maybeCode(offset);
         if (!code) {
             offset--;
             continue;
         }
 
         if (loop && code->safePoint)
             loop->hasSafePoints = true;
 
-        jsbytecode *pc = script->code + offset;
+        jsbytecode *pc = script_->code + offset;
 
         JSOp op = (JSOp) *pc;
 
         if (op == JSOP_LOOPHEAD && code->loop) {
             /*
              * This is the head of a loop, we need to go and make sure that any
              * variables live at the head are live at the backedge and points prior.
              * For each such variable, look for the last lifetime segment in the body
@@ -743,20 +743,20 @@ ScriptAnalysis::analyzeLifetimes(JSConte
         }
 
         /* Find the last jump target in the loop, other than the initial entry point. */
         if (loop && code->jumpTarget && offset != loop->entry && offset > loop->lastBlock)
             loop->lastBlock = offset;
 
         if (code->exceptionEntry) {
             DebugOnly<bool> found = false;
-            JSTryNote *tn = script->trynotes()->vector;
-            JSTryNote *tnlimit = tn + script->trynotes()->length;
+            JSTryNote *tn = script_->trynotes()->vector;
+            JSTryNote *tnlimit = tn + script_->trynotes()->length;
             for (; tn < tnlimit; tn++) {
-                unsigned startOffset = script->mainOffset + tn->start;
+                unsigned startOffset = script_->mainOffset + tn->start;
                 if (startOffset + tn->length == offset) {
                     /*
                      * Extend all live variables at exception entry to the start of
                      * the try block.
                      */
                     for (unsigned i = 0; i < numSlots; i++) {
                         if (lifetimes[i].lifetime)
                             ensureVariable(lifetimes[i], startOffset - 1);
@@ -770,39 +770,39 @@ ScriptAnalysis::analyzeLifetimes(JSConte
         }
 
         switch (op) {
           case JSOP_GETARG:
           case JSOP_CALLARG:
           case JSOP_GETLOCAL:
           case JSOP_CALLLOCAL:
           case JSOP_THIS: {
-            uint32_t slot = GetBytecodeSlot(script, pc);
+            uint32_t slot = GetBytecodeSlot(script_, pc);
             if (!slotEscapes(slot))
                 addVariable(cx, lifetimes[slot], offset, saved, savedCount);
             break;
           }
 
           case JSOP_SETARG:
           case JSOP_SETLOCAL: {
-            uint32_t slot = GetBytecodeSlot(script, pc);
+            uint32_t slot = GetBytecodeSlot(script_, pc);
             if (!slotEscapes(slot))
                 killVariable(cx, lifetimes[slot], offset, saved, savedCount);
             break;
           }
 
           case JSOP_INCARG:
           case JSOP_DECARG:
           case JSOP_ARGINC:
           case JSOP_ARGDEC:
           case JSOP_INCLOCAL:
           case JSOP_DECLOCAL:
           case JSOP_LOCALINC:
           case JSOP_LOCALDEC: {
-            uint32_t slot = GetBytecodeSlot(script, pc);
+            uint32_t slot = GetBytecodeSlot(script_, pc);
             if (!slotEscapes(slot)) {
                 killVariable(cx, lifetimes[slot], offset, saved, savedCount);
                 addVariable(cx, lifetimes[slot], offset, saved, savedCount);
             }
             break;
           }
 
           case JSOP_LOOKUPSWITCH:
@@ -850,30 +850,30 @@ ScriptAnalysis::analyzeLifetimes(JSConte
         }
 
         if (IsJumpOpcode(op)) {
             /*
              * Forward jumps need to pull in all variables which are live at
              * their target offset --- the variables live before the jump are
              * the union of those live at the fallthrough and at the target.
              */
-            uint32_t targetOffset = FollowBranch(cx, script, offset);
+            uint32_t targetOffset = FollowBranch(cx, script_, offset);
 
             /*
              * Watch for 'continue' statements in the loop body, which are
              * jumps to the entry offset separate from the initial jump.
              */
             if (loop && loop->entry == targetOffset && loop->entry > loop->lastBlock)
                 loop->lastBlock = loop->entry;
 
             if (targetOffset < offset) {
                 /* This is a loop back edge, no lifetime to pull in yet. */
 
 #ifdef DEBUG
-                JSOp nop = JSOp(script->code[targetOffset]);
+                JSOp nop = JSOp(script_->code[targetOffset]);
                 JS_ASSERT(nop == JSOP_LOOPHEAD);
 #endif
 
                 /*
                  * If we already have a loop, it is an outer loop and we
                  * need to prune the last block in the loop --- we do not
                  * track 'continue' statements for outer loops.
                  */
@@ -906,28 +906,28 @@ ScriptAnalysis::analyzeLifetimes(JSConte
                  * 'while' loops or a fallthrough for 'do while' loops.
                  */
                 uint32_t entry = targetOffset;
                 if (entry) {
                     do {
                         entry--;
                     } while (!maybeCode(entry));
 
-                    jsbytecode *entrypc = script->code + entry;
+                    jsbytecode *entrypc = script_->code + entry;
 
                     if (JSOp(*entrypc) == JSOP_GOTO || JSOp(*entrypc) == JSOP_FILTER)
                         loop->entry = entry + GET_JUMP_OFFSET(entrypc);
                     else
                         loop->entry = targetOffset;
                 } else {
                     /* Do-while loop at the start of the script. */
                     loop->entry = targetOffset;
                 }
-                JS_ASSERT(script->code[loop->entry] == JSOP_LOOPHEAD ||
-                          script->code[loop->entry] == JSOP_LOOPENTRY);
+                JS_ASSERT(script_->code[loop->entry] == JSOP_LOOPHEAD ||
+                          script_->code[loop->entry] == JSOP_LOOPENTRY);
             } else {
                 for (unsigned i = 0; i < savedCount; i++) {
                     LifetimeVariable &var = *saved[i];
                     JS_ASSERT(!var.lifetime && var.saved);
                     if (var.live(targetOffset)) {
                         /*
                          * Jumping to a place where this variable is live. Make a new
                          * lifetime segment for the variable.
@@ -1181,17 +1181,17 @@ ScriptAnalysis::ensureVariable(LifetimeV
 void
 ScriptAnalysis::clearAllocations()
 {
     /*
      * Clear out storage used for register allocations in a compilation once
      * that compilation has finished. Register allocations are only used for
      * a single compilation.
      */
-    for (unsigned i = 0; i < script->length; i++) {
+    for (unsigned i = 0; i < script_->length; i++) {
         Bytecode *code = maybeCode(i);
         if (code)
             code->allocation = NULL;
     }
 }
 
 /////////////////////////////////////////////////////////////////////
 // SSA Analysis
@@ -1204,17 +1204,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
 
     if (!ranLifetimes()) {
         analyzeLifetimes(cx);
         if (failed())
             return;
     }
 
     LifoAlloc &alloc = cx->analysisLifoAlloc();
-    unsigned maxDepth = script->nslots - script->nfixed;
+    unsigned maxDepth = script_->nslots - script_->nfixed;
 
     /*
      * Current value of each variable and stack value. Empty for missing or
      * untracked entries, i.e. escaping locals and arguments.
      */
     SSAValueInfo *values = cx->pod_calloc<SSAValueInfo>(numSlots + maxDepth);
     if (!values) {
         setOOM(cx);
@@ -1246,18 +1246,18 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
     /*
      * Subset of branchTargets which are exception handlers at future offsets.
      * Any new value of a variable modified before the target is reached is a
      * potential value at that target, along with the lazy original value.
      */
     Vector<uint32_t> exceptionTargets(cx);
 
     uint32_t offset = 0;
-    while (offset < script->length) {
-        jsbytecode *pc = script->code + offset;
+    while (offset < script_->length) {
+        jsbytecode *pc = script_->code + offset;
         JSOp op = (JSOp)*pc;
 
         uint32_t successorOffset = offset + GetBytecodeLength(pc);
 
         Bytecode *code = maybeCode(pc);
         if (!code) {
             offset = successorOffset;
             continue;
@@ -1379,18 +1379,18 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
             freezeNewValues(cx, offset);
         }
 
         if (js_CodeSpec[op].format & JOF_DECOMPOSE) {
             offset = successorOffset;
             continue;
         }
 
-        unsigned nuses = GetUseCount(script, offset);
-        unsigned ndefs = GetDefCount(script, offset);
+        unsigned nuses = GetUseCount(script_, offset);
+        unsigned ndefs = GetDefCount(script_, offset);
         JS_ASSERT(stackDepth >= nuses);
 
         unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses;
 
         if (xuses) {
             code->poppedValues = alloc.newArray<SSAValue>(xuses);
             if (!code->poppedValues) {
                 setOOM(cx);
@@ -1401,17 +1401,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
                 code->poppedValues[i] = v;
                 v.clear();
             }
             if (xuses > nuses) {
                 /*
                  * For SETLOCAL, INCLOCAL, etc. opcodes, add an extra popped
                  * value holding the value of the local before the op.
                  */
-                uint32_t slot = GetBytecodeSlot(script, pc);
+                uint32_t slot = GetBytecodeSlot(script_, pc);
                 if (trackSlot(slot))
                     code->poppedValues[nuses] = values[slot].v;
                 else
                     code->poppedValues[nuses].clear();
             }
 
             if (xuses) {
                 SSAUseChain *useChains = alloc.newArray<SSAUseChain>(xuses);
@@ -1447,28 +1447,28 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
                 return;
             }
             PodZero(code->pushedUses, xdefs);
         }
 
         stackDepth += ndefs;
 
         if (BytecodeUpdatesSlot(op)) {
-            uint32_t slot = GetBytecodeSlot(script, pc);
+            uint32_t slot = GetBytecodeSlot(script_, pc);
             if (trackSlot(slot)) {
                 mergeBranchTarget(cx, values[slot], slot, branchTargets, offset);
                 mergeExceptionTarget(cx, values[slot].v, slot, exceptionTargets);
                 values[slot].v.initWritten(slot, offset);
             }
         }
 
         switch (op) {
           case JSOP_GETARG:
           case JSOP_GETLOCAL: {
-            uint32_t slot = GetBytecodeSlot(script, pc);
+            uint32_t slot = GetBytecodeSlot(script_, pc);
             if (trackSlot(slot)) {
                 /*
                  * Propagate the current value of the local to the pushed value,
                  * and remember it with an extended use on the opcode.
                  */
                 stack[stackDepth - 1].v = code->poppedValues[0] = values[slot].v;
             }
             break;
@@ -1550,20 +1550,20 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
                 npairs--;
             }
 
             checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth);
             break;
           }
 
           case JSOP_TRY: {
-            JSTryNote *tn = script->trynotes()->vector;
-            JSTryNote *tnlimit = tn + script->trynotes()->length;
+            JSTryNote *tn = script_->trynotes()->vector;
+            JSTryNote *tnlimit = tn + script_->trynotes()->length;
             for (; tn < tnlimit; tn++) {
-                unsigned startOffset = script->mainOffset + tn->start;
+                unsigned startOffset = script_->mainOffset + tn->start;
                 if (startOffset == offset + 1) {
                     unsigned catchOffset = startOffset + tn->length;
 
                     if (tn->kind != JSTRY_ITER) {
                         checkBranchTarget(cx, catchOffset, branchTargets, values, stackDepth);
                         checkExceptionTarget(cx, catchOffset, exceptionTargets);
                     }
                 }
@@ -1577,17 +1577,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
           case JSOP_RETRVAL:
             mergeAllExceptionTargets(cx, values, exceptionTargets);
             break;
 
           default:;
         }
 
         if (IsJumpOpcode(op)) {
-            unsigned targetOffset = FollowBranch(cx, script, offset);
+            unsigned targetOffset = FollowBranch(cx, script_, offset);
             checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth);
 
             /*
              * If this is a back edge, we're done with the loop and can freeze
              * the phi values at the head now.
              */
             if (targetOffset < offset)
                 freezeNewValues(cx, targetOffset);
@@ -1597,18 +1597,18 @@ ScriptAnalysis::analyzeSSA(JSContext *cx
     }
 
     ranSSA_ = true;
 
     /*
      * Now that we have full SSA information for the script, analyze whether
      * we can avoid creating the arguments object.
      */
-    if (!script->analyzedArgsUsage())
-        script->setNeedsArgsObj(needsArgsObj(cx));
+    if (!script_->analyzedArgsUsage())
+        script_->setNeedsArgsObj(needsArgsObj(cx));
 }
 
 /* Get a phi node's capacity for a given length. */
 static inline unsigned
 PhiNodeCapacity(unsigned length)
 {
     if (length <= 4)
         return 4;
@@ -1750,17 +1750,17 @@ ScriptAnalysis::checkBranchTarget(JSCont
 
     /*
      * Make sure there is a pending entry for each value on the stack.
      * The number of stack entries at join points is usually zero, and
      * we don't want to look at the active branches while popping and
      * pushing values in each opcode.
      */
     for (unsigned i = 0; i < targetDepth; i++) {
-        uint32_t slot = StackSlot(script, i);
+        uint32_t slot = StackSlot(script_, i);
         checkPendingValue(cx, values[slot].v, slot, pending);
     }
 }
 
 void
 ScriptAnalysis::checkExceptionTarget(JSContext *cx, uint32_t catchOffset,
                                      Vector<uint32_t> &exceptionTargets)
 {
@@ -1917,17 +1917,17 @@ ScriptAnalysis::needsArgsObj(JSContext *
 }
 
 bool
 ScriptAnalysis::needsArgsObj(JSContext *cx, SeenVector &seen, SSAUseChain *use)
 {
     if (!use->popped)
         return needsArgsObj(cx, seen, SSAValue::PhiValue(use->offset, use->u.phi));
 
-    jsbytecode *pc = script->code + use->offset;
+    jsbytecode *pc = script_->code + use->offset;
     JSOp op = JSOp(*pc);
 
     if (op == JSOP_POP || op == JSOP_POPN)
         return false;
 
     /* SplatApplyArgs can read fp->canonicalActualArg(i) directly. */
     if (op == JSOP_FUNAPPLY && GET_ARGC(pc) == 2 && use->u.which == 0) {
 #ifdef JS_METHODJIT
@@ -1942,46 +1942,46 @@ ScriptAnalysis::needsArgsObj(JSContext *
 
     /* arguments.length length can read fp->numActualArgs() directly. */
     if (op == JSOP_LENGTH)
         return false;
 
     /* Allow assignments to non-closed locals (but not arguments). */
 
     if (op == JSOP_SETLOCAL) {
-        uint32_t slot = GetBytecodeSlot(script, pc);
+        uint32_t slot = GetBytecodeSlot(script_, pc);
         if (!trackSlot(slot))
             return true;
         return needsArgsObj(cx, seen, SSAValue::PushedValue(use->offset, 0)) ||
                needsArgsObj(cx, seen, SSAValue::WrittenVar(slot, use->offset));
     }
 
     if (op == JSOP_GETLOCAL)
         return needsArgsObj(cx, seen, SSAValue::PushedValue(use->offset, 0));
 
     return true;
 }
 
 bool
 ScriptAnalysis::needsArgsObj(JSContext *cx)
 {
-    JS_ASSERT(script->argumentsHasVarBinding());
+    JS_ASSERT(script_->argumentsHasVarBinding());
 
     /*
      * Since let variables and dynamic name access are not tracked, we cannot
      * soundly perform this analysis in their presence. Generators can be
      * suspended when the speculation fails, so disallow it also.
      */
-    if (script->bindingsAccessedDynamically || script->funHasAnyAliasedFormal ||
-        localsAliasStack() || cx->compartment->debugMode() || script->isGenerator)
+    if (script_->bindingsAccessedDynamically || script_->funHasAnyAliasedFormal ||
+        localsAliasStack() || cx->compartment->debugMode() || script_->isGenerator)
     {
         return true;
     }
 
-    unsigned pcOff = script->argumentsBytecode() - script->code;
+    unsigned pcOff = script_->argumentsBytecode() - script_->code;
 
     SeenVector seen(cx);
     return needsArgsObj(cx, seen, SSAValue::PushedValue(pcOff, 0));
 }
 
 CrossSSAValue
 CrossScriptSSA::foldValue(const CrossSSAValue &cv)
 {
@@ -2068,24 +2068,24 @@ CrossScriptSSA::foldValue(const CrossSSA
 
 void
 ScriptAnalysis::printSSA(JSContext *cx)
 {
     AutoEnterAnalysis enter(cx);
 
     printf("\n");
 
-    for (unsigned offset = 0; offset < script->length; offset++) {
+    for (unsigned offset = 0; offset < script_->length; offset++) {
         Bytecode *code = maybeCode(offset);
         if (!code)
             continue;
 
-        jsbytecode *pc = script->code + offset;
+        jsbytecode *pc = script_->code + offset;
 
-        PrintBytecode(cx, script, pc);
+        PrintBytecode(cx, script_, pc);
 
         SlotValue *newv = code->newValues;
         if (newv) {
             while (newv->slot) {
                 if (newv->value.kind() != SSAValue::PHI || newv->value.phiOffset() != offset) {
                     newv++;
                     continue;
                 }
@@ -2097,17 +2097,17 @@ ScriptAnalysis::printSSA(JSContext *cx)
                         printf(",");
                     newv->value.phiValue(i).print();
                 }
                 printf("]\n");
                 newv++;
             }
         }
 
-        unsigned nuses = GetUseCount(script, offset);
+        unsigned nuses = GetUseCount(script_, offset);
         unsigned xuses = ExtendedUse(pc) ? nuses + 1 : nuses;
 
         for (unsigned i = 0; i < xuses; i++) {
             printf("  popped%d: ", i);
             code->poppedValues[i].print();
             printf("\n");
         }
     }
@@ -2142,15 +2142,15 @@ SSAValue::print() const
       default:
         JS_NOT_REACHED("Bad kind");
     }
 }
 
 void
 ScriptAnalysis::assertMatchingDebugMode()
 {
-    JS_ASSERT(!!script->compartment()->debugMode() == !!originalDebugMode_);
+    JS_ASSERT(!!script_->compartment()->debugMode() == !!originalDebugMode_);
 }
 
 #endif  /* DEBUG */
 
 } /* namespace analyze */
 } /* namespace js */
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -834,17 +834,17 @@ class SlotValue
 
 struct NeedsArgsObjState;
 
 /* Analysis information about a script. */
 class ScriptAnalysis
 {
     friend class Bytecode;
 
-    JSScript *script;
+    JSScript *script_;
 
     Bytecode **codeArray;
 
     uint32_t numSlots;
     uint32_t numPropertyReads_;
 
     bool outOfMemory;
     bool hadFailure;
@@ -880,19 +880,19 @@ class ScriptAnalysis
     /* --------- Lifetime analysis --------- */
 
     LifetimeVariable *lifetimes;
 
   public:
 
     ScriptAnalysis(JSScript *script) {
         PodZero(this);
-        this->script = script;
+        this->script_ = script;
 #ifdef DEBUG
-        this->originalDebugMode_ = script->compartment()->debugMode();
+        this->originalDebugMode_ = script_->compartment()->debugMode();
 #endif
     }
 
     bool ranBytecode() { return ranBytecode_; }
     bool ranSSA() { return ranSSA_; }
     bool ranLifetimes() { return ranLifetimes_; }
     bool ranInference() { return ranInference_; }
 
@@ -902,17 +902,17 @@ class ScriptAnalysis
     void analyzeTypes(JSContext *cx);
 
     /* Analyze the effect of invoking 'new' on script. */
     void analyzeTypesNew(JSContext *cx);
 
     bool OOM() const { return outOfMemory; }
     bool failed() const { return hadFailure; }
     bool inlineable() const { return isInlineable; }
-    bool inlineable(uint32_t argc) const { return isInlineable && argc == script->function()->nargs; }
+    bool inlineable(uint32_t argc) const { return isInlineable && argc == script_->function()->nargs; }
     bool jaegerCompileable() { return isJaegerCompileable; }
 
     /* Number of property read opcodes in the script. */
     uint32_t numPropertyReads() const { return numPropertyReads_; }
 
     /* Whether there are POPV/SETRVAL bytecodes which can write to the frame's rval. */
     bool usesReturnValue() const { return usesReturnValue_; }
 
@@ -928,40 +928,40 @@ class ScriptAnalysis
     /*
      * True if all named formal arguments are not modified. If the arguments
      * object cannot escape, the arguments are never modified within the script.
      */
     bool modifiesArguments() { return modifiesArguments_; }
 
     /*
      * True if there are any LOCAL opcodes aliasing values on the stack (above
-     * script->nfixed).
+     * script_->nfixed).
      */
     bool localsAliasStack() { return localsAliasStack_; }
 
     /* Accessors for bytecode information. */
 
     Bytecode& getCode(uint32_t offset) {
-        JS_ASSERT(offset < script->length);
+        JS_ASSERT(offset < script_->length);
         JS_ASSERT(codeArray[offset]);
         return *codeArray[offset];
     }
-    Bytecode& getCode(const jsbytecode *pc) { return getCode(pc - script->code); }
+    Bytecode& getCode(const jsbytecode *pc) { return getCode(pc - script_->code); }
 
     Bytecode* maybeCode(uint32_t offset) {
-        JS_ASSERT(offset < script->length);
+        JS_ASSERT(offset < script_->length);
         return codeArray[offset];
     }
-    Bytecode* maybeCode(const jsbytecode *pc) { return maybeCode(pc - script->code); }
+    Bytecode* maybeCode(const jsbytecode *pc) { return maybeCode(pc - script_->code); }
 
     bool jumpTarget(uint32_t offset) {
-        JS_ASSERT(offset < script->length);
+        JS_ASSERT(offset < script_->length);
         return codeArray[offset] && codeArray[offset]->jumpTarget;
     }
-    bool jumpTarget(const jsbytecode *pc) { return jumpTarget(pc - script->code); }
+    bool jumpTarget(const jsbytecode *pc) { return jumpTarget(pc - script_->code); }
 
     bool popGuaranteed(jsbytecode *pc) {
         jsbytecode *next = pc + GetBytecodeLength(pc);
         return JSOp(*next) == JSOP_POP && !jumpTarget(next);
     }
 
     bool incrementInitialValueObserved(jsbytecode *pc) {
         const JSCodeSpec *cs = &js_CodeSpec[*pc];
@@ -969,52 +969,52 @@ class ScriptAnalysis
     }
 
     types::StackTypeSet *bytecodeTypes(const jsbytecode *pc) {
         JS_ASSERT(js_CodeSpec[*pc].format & JOF_TYPESET);
         return getCode(pc).observedTypes;
     }
 
     const SSAValue &poppedValue(uint32_t offset, uint32_t which) {
-        JS_ASSERT(offset < script->length);
-        JS_ASSERT(which < GetUseCount(script, offset) +
-                  (ExtendedUse(script->code + offset) ? 1 : 0));
+        JS_ASSERT(offset < script_->length);
+        JS_ASSERT(which < GetUseCount(script_, offset) +
+                  (ExtendedUse(script_->code + offset) ? 1 : 0));
         return getCode(offset).poppedValues[which];
     }
     const SSAValue &poppedValue(const jsbytecode *pc, uint32_t which) {
-        return poppedValue(pc - script->code, which);
+        return poppedValue(pc - script_->code, which);
     }
 
     const SlotValue *newValues(uint32_t offset) {
-        JS_ASSERT(offset < script->length);
+        JS_ASSERT(offset < script_->length);
         return getCode(offset).newValues;
     }
-    const SlotValue *newValues(const jsbytecode *pc) { return newValues(pc - script->code); }
+    const SlotValue *newValues(const jsbytecode *pc) { return newValues(pc - script_->code); }
 
     types::StackTypeSet *pushedTypes(uint32_t offset, uint32_t which = 0) {
-        JS_ASSERT(offset < script->length);
-        JS_ASSERT(which < GetDefCount(script, offset) +
-                  (ExtendedDef(script->code + offset) ? 1 : 0));
+        JS_ASSERT(offset < script_->length);
+        JS_ASSERT(which < GetDefCount(script_, offset) +
+                  (ExtendedDef(script_->code + offset) ? 1 : 0));
         types::StackTypeSet *array = getCode(offset).pushedTypes;
         JS_ASSERT(array);
         return array + which;
     }
     types::StackTypeSet *pushedTypes(const jsbytecode *pc, uint32_t which) {
-        return pushedTypes(pc - script->code, which);
+        return pushedTypes(pc - script_->code, which);
     }
 
     bool hasPushedTypes(const jsbytecode *pc) { return getCode(pc).pushedTypes != NULL; }
 
     types::TypeBarrier *typeBarriers(JSContext *cx, uint32_t offset) {
         if (getCode(offset).typeBarriers)
             pruneTypeBarriers(cx, offset);
         return getCode(offset).typeBarriers;
     }
     types::TypeBarrier *typeBarriers(JSContext *cx, const jsbytecode *pc) {
-        return typeBarriers(cx, pc - script->code);
+        return typeBarriers(cx, pc - script_->code);
     }
     void addTypeBarrier(JSContext *cx, const jsbytecode *pc,
                         types::TypeSet *target, types::Type type);
     void addSingletonTypeBarrier(JSContext *cx, const jsbytecode *pc,
                                  types::TypeSet *target, HandleObject singleton, jsid singletonId);
 
     /* Remove obsolete type barriers at the given offset. */
     void pruneTypeBarriers(JSContext *cx, uint32_t offset);
@@ -1033,17 +1033,17 @@ class ScriptAnalysis
 
     types::StackTypeSet *getValueTypes(const SSAValue &v) {
         switch (v.kind()) {
           case SSAValue::PUSHED:
             return pushedTypes(v.pushedOffset(), v.pushedIndex());
           case SSAValue::VAR:
             JS_ASSERT(!slotEscapes(v.varSlot()));
             if (v.varInitial()) {
-                return types::TypeScript::SlotTypes(script, v.varSlot());
+                return types::TypeScript::SlotTypes(script_, v.varSlot());
             } else {
                 /*
                  * Results of intermediate assignments have the same type as
                  * the first type pushed by the assignment op. Note that this
                  * may not be the exact same value as was pushed, due to
                  * post-inc/dec ops.
                  */
                 return pushedTypes(v.varOffset(), 0);
@@ -1077,69 +1077,69 @@ class ScriptAnalysis
      * Get the use chain for an SSA value. May be invalid for some opcodes in
      * scripts where localsAliasStack(). You have been warned!
      */
     SSAUseChain *& useChain(const SSAValue &v) {
         JS_ASSERT(trackUseChain(v));
         if (v.kind() == SSAValue::PUSHED)
             return getCode(v.pushedOffset()).pushedUses[v.pushedIndex()];
         if (v.kind() == SSAValue::VAR)
-            return getCode(v.varOffset()).pushedUses[GetDefCount(script, v.varOffset())];
+            return getCode(v.varOffset()).pushedUses[GetDefCount(script_, v.varOffset())];
         return v.phiNode()->uses;
     }
 
     mjit::RegisterAllocation *&getAllocation(uint32_t offset) {
-        JS_ASSERT(offset < script->length);
+        JS_ASSERT(offset < script_->length);
         return getCode(offset).allocation;
     }
     mjit::RegisterAllocation *&getAllocation(const jsbytecode *pc) {
-        return getAllocation(pc - script->code);
+        return getAllocation(pc - script_->code);
     }
 
     LoopAnalysis *getLoop(uint32_t offset) {
-        JS_ASSERT(offset < script->length);
+        JS_ASSERT(offset < script_->length);
         return getCode(offset).loop;
     }
-    LoopAnalysis *getLoop(const jsbytecode *pc) { return getLoop(pc - script->code); }
+    LoopAnalysis *getLoop(const jsbytecode *pc) { return getLoop(pc - script_->code); }
 
     /* For a JSOP_CALL* op, get the pc of the corresponding JSOP_CALL/NEW/etc. */
     jsbytecode *getCallPC(jsbytecode *pc)
     {
-        SSAUseChain *uses = useChain(SSAValue::PushedValue(pc - script->code, 0));
+        SSAUseChain *uses = useChain(SSAValue::PushedValue(pc - script_->code, 0));
         JS_ASSERT(uses && uses->popped);
-        JS_ASSERT(js_CodeSpec[script->code[uses->offset]].format & JOF_INVOKE);
-        return script->code + uses->offset;
+        JS_ASSERT(js_CodeSpec[script_->code[uses->offset]].format & JOF_INVOKE);
+        return script_->code + uses->offset;
     }
 
     /* Accessors for local variable information. */
 
     /*
      * Escaping slots include all slots that can be accessed in ways other than
      * through the corresponding LOCAL/ARG opcode. This includes all closed
      * slots in the script, all slots in scripts which use eval or are in debug
      * mode, and slots which are aliased by NAME or similar opcodes in the
      * containing script (which does not imply the variable is closed).
      */
     bool slotEscapes(uint32_t slot) {
-        JS_ASSERT(script->compartment()->activeAnalysis);
+        JS_ASSERT(script_->compartment()->activeAnalysis);
         if (slot >= numSlots)
             return true;
         return escapedSlots[slot];
     }
 
     /*
      * Whether we distinguish different writes of this variable while doing
      * SSA analysis. Escaping locals can be written in other scripts, and the
      * presence of NAME opcodes which could alias local variables or arguments
      * keeps us from tracking variable values at each point.
      */
     bool trackSlot(uint32_t slot) { return !slotEscapes(slot) && canTrackVars && slot < 1000; }
 
     const LifetimeVariable & liveness(uint32_t slot) {
-        JS_ASSERT(script->compartment()->activeAnalysis);
+        JS_ASSERT(script_->compartment()->activeAnalysis);
         JS_ASSERT(!slotEscapes(slot));
         return lifetimes[slot];
     }
 
     void printSSA(JSContext *cx);
     void printTypes(JSContext *cx);
 
     void clearAllocations();
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -575,17 +575,17 @@ JSCompartment::sweep(FreeOp *fop, bool r
          * Sweep analysis information and everything depending on it from the
          * compartment, including all remaining mjit code if inference is
          * enabled in the compartment.
          */
         if (types.inferenceEnabled) {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_DISCARD_TI);
 
             for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
-                JSScript *script = i.get<JSScript>();
+                RawScript script = i.get<JSScript>();
                 if (script->types) {
                     types::TypeScript::Sweep(fop, script);
 
                     if (releaseTypes) {
                         script->types->destroy();
                         script->types = NULL;
                     }
                 }
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -954,54 +954,57 @@ InFreeList(ArenaHeader *aheader, uintptr
          * The last possible empty span is an the end of the arena. Here
          * span->end < thing < thingsEnd and so we must have more spans.
          */
         span = span->nextSpan();
     }
 }
 
 #ifdef JSGC_USE_EXACT_ROOTING
+static inline void
+MarkExactStackRooter(JSTracer *trc, Rooted<void*> rooter, ThingRootKind kind)
+{
+    void **addr = (void **)rooter->address();
+    if (!*addr)
+        return;
+
+    switch (kind) {
+      case THING_ROOT_OBJECT:      MarkObjectRoot(trc, (JSObject **)addr, "exact-object"); break;
+      case THING_ROOT_STRING:      MarkStringRoot(trc, (JSSTring **)addr, "exact-string"); break;
+      case THING_ROOT_SCRIPT:      MarkScriptRoot(trc, (JSScript **)addr, "exact-script"); break;
+      case THING_ROOT_SHAPE:       MarkShapeRoot(trc, (Shape **)addr, "exact-shape"); break;
+      case THING_ROOT_BASE_SHAPE:  MarkBaseShapeRoot(trc, (BaseShape **)addr, "exact-baseshape"); break;
+      case THING_ROOT_TYPE:        MarkTypeRoot(trc, (types::Type *)addr, "exact-type"); break;
+      case THING_ROOT_TYPE_OBJECT: MarkTypeObjectRoot(trc, (types::TypeObject **)addr, "exact-typeobject"); break;
+      case THING_ROOT_VALUE:       MarkValueRoot(trc, (Value *)addr, "exact-value"); break;
+      case THING_ROOT_ID:          MarkIdRoot(trc, (jsid *)addr, "exact-id"); break;
+      case THING_ROOT_PROPERTY_ID: MarkIdRoot(trc, &((js::PropertyId *)addr)->asId(), "exact-propertyid"); break;
+      case THING_ROOT_BINDINGS:    ((Bindings *)addr)->trace(trc); break;
+      default: JS_NOT_REACHED("Invalid THING_ROOT kind"); break;
+    }
+}
+
+static inline void
+MarkExactStackRooters(JSTracer *trc, Rooted<void*> rooter, ThingRootKind kind)
+{
+    Rooted<void*> *rooter = cx->thingGCRooters[i];
+    while (rooter) {
+        MarkExactStackRoot(trc, rooter, ThingRootKind(i));
+        rooter = rooter->previous();
+    }
+}
+
 static void
 MarkExactStackRoots(JSTracer *trc)
 {
-    for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) {
-        for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
-            Rooted<void*> *rooter = cx->thingGCRooters[i];
-            while (rooter) {
-                void **addr = (void **)rooter->address();
-                if (*addr) {
-                    if (i == THING_ROOT_OBJECT) {
-                        MarkObjectRoot(trc, (JSObject **)addr, "exact stackroot object");
-                    } else if (i == THING_ROOT_STRING) {
-                        MarkStringRoot(trc, (JSString **)addr, "exact stackroot string");
-                    } else if (i == THING_ROOT_ID) {
-                        MarkIdRoot(trc, (jsid *)addr, "exact stackroot id");
-                    } else if (i == THING_ROOT_PROPERTY_ID) {
-                        MarkIdRoot(trc, &((PropertyId *)addr)->asId(), "exact stackroot property id");
-                    } else if (i == THING_ROOT_VALUE) {
-                        MarkValueRoot(trc, (Value *)addr, "exact stackroot value");
-                    } else if (i == THING_ROOT_TYPE) {
-                        MarkTypeRoot(trc, (types::Type *)addr, "exact stackroot type");
-                    } else if (i == THING_ROOT_SHAPE) {
-                        MarkShapeRoot(trc, (Shape **)addr, "exact stackroot shape");
-                    } else if (i == THING_ROOT_BASE_SHAPE) {
-                        MarkBaseShapeRoot(trc, (BaseShape **)addr, "exact stackroot baseshape");
-                    } else if (i == THING_ROOT_TYPE_OBJECT) {
-                        MarkTypeObjectRoot(trc, (types::TypeObject **)addr, "exact stackroot typeobject");
-                    } else if (i == THING_ROOT_SCRIPT) {
-                        MarkScriptRoot(trc, (JSScript **)addr, "exact stackroot script");
-                    } else if (i == THING_ROOT_XML) {
-                        MarkXMLRoot(trc, (JSXML **)addr, "exact stackroot xml");
-                    } else {
-                        JS_NOT_REACHED("Invalid thing root kind.");
-                    }
-                }
-                rooter = rooter->previous();
-            }
+    for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
+        for (ContextIter cx(trc->runtime); !cx.done(); cx.next()) {
+            MarkExactStackRooters(trc, cx->thingGCRooters[i], ThingRootKind(i));
         }
+        MarkExactStackRooters(trc, rt->thingGCRooters[i], ThingRootKind(i));
     }
 }
 #endif /* JSGC_USE_EXACT_ROOTING */
 
 enum ConservativeGCTest
 {
     CGCT_VALID,
     CGCT_LOWBITSET, /* excluded because one of the low bits was set */
@@ -4965,21 +4968,31 @@ SetValidateGC(JSContext *cx, bool enable
     rt->gcValidate = enabled;
 }
 
 } /* namespace gc */
 } /* namespace js */
 
 #if defined(DEBUG) && defined(JS_GC_ZEAL) && defined(JSGC_ROOT_ANALYSIS) && !defined(JS_THREADSAFE)
 
+JS_ALWAYS_INLINE bool
+CheckStackRootThing(uintptr_t *w, void *address, ThingRootKind kind)
+{
+    if (kind != THING_ROOT_BINDINGS)
+        return address == static_cast<void*>(w);
+
+    Bindings *bp = static_cast<Bindings*>(address);
+    return w >= (uintptr_t*)bp && w < (uintptr_t*)(bp + 1);
+}
+
 JS_ALWAYS_INLINE void
-CheckStackRootThings(uintptr_t *w, Rooted<void*> *rooter, bool *matched)
+CheckStackRootThings(uintptr_t *w, Rooted<void*> *rooter, ThingRootKind kind, bool *matched)
 {
     while (rooter) {
-        if (rooter->address() == static_cast<void*>(w))
+        if (CheckStackRootThing(w, rooter->address(), kind))
             *matched = true;
         rooter = rooter->previous();
     }
 }
 
 static void
 CheckStackRoot(JSTracer *trc, uintptr_t *w)
 {
@@ -4989,19 +5002,19 @@ CheckStackRoot(JSTracer *trc, uintptr_t 
 #endif
 
     ConservativeGCTest test = MarkIfGCThingWord(trc, *w);
 
     if (test == CGCT_VALID) {
         bool matched = false;
         JSRuntime *rt = trc->runtime;
         for (unsigned i = 0; i < THING_ROOT_LIMIT; i++) {
-            CheckStackRootThings(w, rt->thingGCRooters[i], &matched);
+            CheckStackRootThings(w, rt->thingGCRooters[i], ThingRootKind(i), &matched);
             for (ContextIter cx(rt); !cx.done(); cx.next()) {
-                CheckStackRootThings(w, cx->thingGCRooters[i], &matched);
+                CheckStackRootThings(w, cx->thingGCRooters[i], ThingRootKind(i), &matched);
                 SkipRoot *skip = cx->skipGCRooters;
                 while (skip) {
                     if (skip->contains(reinterpret_cast<uint8_t*>(w), sizeof(w)))
                         matched = true;
                     skip = skip->previous();
                 }
             }
         }
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -471,17 +471,17 @@ StackTypeSet::make(JSContext *cx, const 
 
 /////////////////////////////////////////////////////////////////////
 // TypeSet constraints
 /////////////////////////////////////////////////////////////////////
 
 /* Standard subset constraint, propagate all types from one set to another. */
 class TypeConstraintSubset : public TypeConstraint
 {
-public:
+  public:
     TypeSet *target;
 
     TypeConstraintSubset(TypeSet *target)
         : target(target)
     {
         JS_ASSERT(target);
     }
 
@@ -512,105 +512,107 @@ enum PropertyAccessKind {
     PROPERTY_READ,
     PROPERTY_READ_EXISTING
 };
 
 /* Constraints for reads/writes on object properties. */
 template <PropertyAccessKind access>
 class TypeConstraintProp : public TypeConstraint
 {
-public:
-    JSScript *script;
+    JSScript *script_;
+
+  public:
     jsbytecode *pc;
 
     /*
      * If assign is true, the target is used to update a property of the object.
      * If assign is false, the target is assigned the value of the property.
      */
     StackTypeSet *target;
 
     /* Property being accessed. */
     jsid id;
 
     TypeConstraintProp(JSScript *script, jsbytecode *pc, StackTypeSet *target, jsid id)
-        : script(script), pc(pc), target(target), id(id)
+        : script_(script), pc(pc), target(target), id(id)
     {
         JS_ASSERT(script && pc && target);
     }
 
     const char *kind() { return "prop"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 typedef TypeConstraintProp<PROPERTY_WRITE> TypeConstraintSetProperty;
 typedef TypeConstraintProp<PROPERTY_READ>  TypeConstraintGetProperty;
 typedef TypeConstraintProp<PROPERTY_READ_EXISTING> TypeConstraintGetPropertyExisting;
 
 void
-StackTypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
+StackTypeSet::addGetProperty(JSContext *cx, HandleScript script, jsbytecode *pc,
                              StackTypeSet *target, jsid id)
 {
     /*
      * GetProperty constraints are normally used with property read input type
      * sets, except for array_pop/array_shift special casing.
      */
     JS_ASSERT(js_CodeSpec[*pc].format & JOF_INVOKE);
 
     add(cx, cx->analysisLifoAlloc().new_<TypeConstraintGetProperty>(script, pc, target, id));
 }
 
 void
-StackTypeSet::addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
+StackTypeSet::addSetProperty(JSContext *cx, HandleScript script, jsbytecode *pc,
                              StackTypeSet *target, jsid id)
 {
     add(cx, cx->analysisLifoAlloc().new_<TypeConstraintSetProperty>(script, pc, target, id));
 }
 
 void
-HeapTypeSet::addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
+HeapTypeSet::addGetProperty(JSContext *cx, HandleScript script, jsbytecode *pc,
                             StackTypeSet *target, jsid id)
 {
     JS_ASSERT(!target->purged());
     add(cx, cx->typeLifoAlloc().new_<TypeConstraintGetProperty>(script, pc, target, id));
 }
 
 /*
  * Constraints for updating the 'this' types of callees on CALLPROP/CALLELEM.
  * These are derived from the types on the properties themselves, rather than
  * those pushed in the 'this' slot at the call site, which allows us to retain
  * correlations between the type of the 'this' object and the associated
  * callee scripts at polymorphic call sites.
  */
 template <PropertyAccessKind access>
 class TypeConstraintCallProp : public TypeConstraint
 {
-public:
-    JSScript *script;
+    JSScript *script_;
+
+  public:
     jsbytecode *callpc;
 
     /* Property being accessed. */
     jsid id;
 
     TypeConstraintCallProp(JSScript *script, jsbytecode *callpc, jsid id)
-        : script(script), callpc(callpc), id(id)
+        : script_(script), callpc(callpc), id(id)
     {
         JS_ASSERT(script && callpc);
     }
 
     const char *kind() { return "callprop"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 typedef TypeConstraintCallProp<PROPERTY_READ> TypeConstraintCallProperty;
 typedef TypeConstraintCallProp<PROPERTY_READ_EXISTING> TypeConstraintCallPropertyExisting;
 
 void
-HeapTypeSet::addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id)
+HeapTypeSet::addCallProperty(JSContext *cx, HandleScript script, jsbytecode *pc, jsid id)
 {
     /*
      * For calls which will go through JSOP_NEW, don't add any constraints to
      * modify the 'this' types of callees. The initial 'this' value will be
      * outright ignored.
      */
     jsbytecode *callpc = script->analysis()->getCallPC(pc);
     if (JSOp(*callpc) == JSOP_NEW)
@@ -622,52 +624,53 @@ HeapTypeSet::addCallProperty(JSContext *
 /*
  * Constraints for generating 'set' property constraints on a SETELEM only if
  * the element type may be a number. For SETELEM we only account for integer
  * indexes, and if the element cannot be an integer (e.g. it must be a string)
  * then we lose precision by treating it like one.
  */
 class TypeConstraintSetElement : public TypeConstraint
 {
-public:
-    JSScript *script;
+    JSScript *script_;
+
+  public:
     jsbytecode *pc;
 
     StackTypeSet *objectTypes;
     StackTypeSet *valueTypes;
 
     TypeConstraintSetElement(JSScript *script, jsbytecode *pc,
                              StackTypeSet *objectTypes, StackTypeSet *valueTypes)
-        : script(script), pc(pc),
+        : script_(script), pc(pc),
           objectTypes(objectTypes), valueTypes(valueTypes)
     {
         JS_ASSERT(script && pc);
     }
 
     const char *kind() { return "setelement"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-StackTypeSet::addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
+StackTypeSet::addSetElement(JSContext *cx, HandleScript script, jsbytecode *pc,
                             StackTypeSet *objectTypes, StackTypeSet *valueTypes)
 {
     add(cx, cx->analysisLifoAlloc().new_<TypeConstraintSetElement>(script, pc, objectTypes,
                                                                    valueTypes));
 }
 
 /*
  * Constraints for watching call edges as they are discovered and invoking native
  * function handlers, adding constraints for arguments, receiver objects and the
  * return value, and updating script foundOffsets.
  */
 class TypeConstraintCall : public TypeConstraint
 {
-public:
+  public:
     /* Call site being tracked. */
     TypeCallsite *callsite;
 
     TypeConstraintCall(TypeCallsite *callsite)
         : callsite(callsite)
     {}
 
     const char *kind() { return "call"; }
@@ -679,102 +682,106 @@ void
 StackTypeSet::addCall(JSContext *cx, TypeCallsite *site)
 {
     add(cx, cx->analysisLifoAlloc().new_<TypeConstraintCall>(site));
 }
 
 /* Constraints for arithmetic operations. */
 class TypeConstraintArith : public TypeConstraint
 {
-public:
-    JSScript *script;
+    JSScript *script_;
+
+  public:
     jsbytecode *pc;
 
     /* Type set receiving the result of the arithmetic. */
     TypeSet *target;
 
     /* For addition operations, the other operand. */
     TypeSet *other;
 
     TypeConstraintArith(JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other)
-        : script(script), pc(pc), target(target), other(other)
+        : script_(script), pc(pc), target(target), other(other)
     {
         JS_ASSERT(target);
     }
 
     const char *kind() { return "arith"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-StackTypeSet::addArith(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target, TypeSet *other)
+StackTypeSet::addArith(JSContext *cx, HandleScript script, jsbytecode *pc, TypeSet *target,
+                       TypeSet *other)
 {
     add(cx, cx->analysisLifoAlloc().new_<TypeConstraintArith>(script, pc, target, other));
 }
 
 /* Subset constraint which transforms primitive values into appropriate objects. */
 class TypeConstraintTransformThis : public TypeConstraint
 {
-public:
-    JSScript *script;
+    JSScript *script_;
+
+  public:
     TypeSet *target;
 
     TypeConstraintTransformThis(JSScript *script, TypeSet *target)
-        : script(script), target(target)
+        : script_(script), target(target)
     {}
 
     const char *kind() { return "transformthis"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-StackTypeSet::addTransformThis(JSContext *cx, JSScript *script, TypeSet *target)
+StackTypeSet::addTransformThis(JSContext *cx, HandleScript script, TypeSet *target)
 {
     add(cx, cx->analysisLifoAlloc().new_<TypeConstraintTransformThis>(script, target));
 }
 
 /*
  * Constraint which adds a particular type to the 'this' types of all
  * discovered scripted functions.
  */
 class TypeConstraintPropagateThis : public TypeConstraint
 {
-public:
-    JSScript *script;
+    JSScript *script_;
+
+  public:
     jsbytecode *callpc;
     Type type;
     StackTypeSet *types;
 
     TypeConstraintPropagateThis(JSScript *script, jsbytecode *callpc, Type type, StackTypeSet *types)
-        : script(script), callpc(callpc), type(type), types(types)
+        : script_(script), callpc(callpc), type(type), types(types)
     {}
 
     const char *kind() { return "propagatethis"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type);
 };
 
 void
-StackTypeSet::addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc,
+StackTypeSet::addPropagateThis(JSContext *cx, HandleScript script, jsbytecode *pc,
                                Type type, StackTypeSet *types)
 {
     /* Don't add constraints when the call will be 'new' (see addCallProperty). */
     jsbytecode *callpc = script->analysis()->getCallPC(pc);
     if (JSOp(*callpc) == JSOP_NEW)
         return;
 
     add(cx, cx->analysisLifoAlloc().new_<TypeConstraintPropagateThis>(script, callpc, type, types));
 }
 
 /* Subset constraint which filters out primitive types. */
 class TypeConstraintFilterPrimitive : public TypeConstraint
 {
-public:
+  public:
     TypeSet *target;
 
     TypeConstraintFilterPrimitive(TypeSet *target)
         : target(target)
     {}
 
     const char *kind() { return "filter"; }
 
@@ -886,29 +893,29 @@ void ScriptAnalysis::breakTypeBarriers(J
 }
 
 void ScriptAnalysis::breakTypeBarriersSSA(JSContext *cx, const SSAValue &v)
 {
     if (v.kind() != SSAValue::PUSHED)
         return;
 
     uint32_t offset = v.pushedOffset();
-    if (JSOp(script->code[offset]) == JSOP_GETPROP)
+    if (JSOp(script_->code[offset]) == JSOP_GETPROP)
         breakTypeBarriersSSA(cx, poppedValue(offset, 0));
 
     breakTypeBarriers(cx, offset, true);
 }
 
 /*
  * Subset constraint for property reads and argument passing which can add type
  * barriers on the read instead of passing types along.
  */
 class TypeConstraintSubsetBarrier : public TypeConstraint
 {
-public:
+  public:
     JSScript *script;
     jsbytecode *pc;
     TypeSet *target;
 
     TypeConstraintSubsetBarrier(JSScript *script, jsbytecode *pc, TypeSet *target)
         : script(script), pc(pc), target(target)
     {}
 
@@ -920,35 +927,35 @@ public:
             if (!script->ensureRanAnalysis(cx))
                 return;
             script->analysis()->addTypeBarrier(cx, pc, target, type);
         }
     }
 };
 
 void
-StackTypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
+StackTypeSet::addSubsetBarrier(JSContext *cx, HandleScript script, jsbytecode *pc, TypeSet *target)
 {
     add(cx, cx->analysisLifoAlloc().new_<TypeConstraintSubsetBarrier>(script, pc, target));
 }
 
 void
-HeapTypeSet::addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
+HeapTypeSet::addSubsetBarrier(JSContext *cx, HandleScript script, jsbytecode *pc, TypeSet *target)
 {
     JS_ASSERT(!target->purged());
     add(cx, cx->typeLifoAlloc().new_<TypeConstraintSubsetBarrier>(script, pc, target));
 }
 
 /////////////////////////////////////////////////////////////////////
 // TypeConstraint
 /////////////////////////////////////////////////////////////////////
 
 /* Get the object to use for a property access on type. */
 static inline TypeObject *
-GetPropertyObject(JSContext *cx, JSScript *script, Type type)
+GetPropertyObject(JSContext *cx, HandleScript script, Type type)
 {
     if (type.isTypeObject())
         return type.typeObject();
 
     /* Force instantiation of lazy types for singleton objects. */
     if (type.isSingleObject())
         return type.singleObject()->getType(cx);
 
@@ -989,17 +996,17 @@ UsePropertyTypeBarrier(jsbytecode *pc)
      * At call opcodes, type barriers can only be added for the call bindings,
      * which TypeConstraintCall will add barrier constraints for directly.
      */
     uint32_t format = js_CodeSpec[*pc].format;
     return (format & JOF_TYPESET) && !(format & JOF_INVOKE);
 }
 
 static inline void
-MarkPropertyAccessUnknown(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target)
+MarkPropertyAccessUnknown(JSContext *cx, HandleScript script, jsbytecode *pc, TypeSet *target)
 {
     if (UsePropertyTypeBarrier(pc))
         script->analysis()->addTypeBarrier(cx, pc, target, Type::UnknownType());
     else
         target->addType(cx, Type::UnknownType());
 }
 
 /*
@@ -1041,17 +1048,17 @@ GetSingletonPropertyType(JSContext *cx, 
 }
 
 /*
  * Handle a property access on a specific object. All property accesses go through
  * here, whether via x.f, x[f], or global name accesses.
  */
 template <PropertyAccessKind access>
 static inline void
-PropertyAccess(JSContext *cx, JSScript *script, jsbytecode *pc, TypeObject *object,
+PropertyAccess(JSContext *cx, HandleScript script, jsbytecode *pc, TypeObject *object,
                StackTypeSet *target, jsid id)
 {
     /* Reads from objects with unknown properties are unknown, writes to such objects are ignored. */
     if (object->unknownProperties()) {
         if (access != PROPERTY_WRITE)
             MarkPropertyAccessUnknown(cx, script, pc, target);
         return;
     }
@@ -1142,27 +1149,29 @@ PropertyAccess(JSContext *cx, JSScript *
             JS_ASSERT(access == PROPERTY_READ);
             types->addSubset(cx, target);
         }
     }
 }
 
 /* Whether the JSObject/TypeObject referent of an access on type cannot be determined. */
 static inline bool
-UnknownPropertyAccess(JSScript *script, Type type)
+UnknownPropertyAccess(HandleScript script, Type type)
 {
     return type.isUnknown()
         || type.isAnyObject()
         || (!type.isObject() && !script->compileAndGo);
 }
 
 template <PropertyAccessKind access>
 void
 TypeConstraintProp<access>::newType(JSContext *cx, TypeSet *source, Type type)
 {
+    RootedScript script(cx, script_);
+
     if (UnknownPropertyAccess(script, type)) {
         /*
          * Access on an unknown object. Reads produce an unknown result, writes
          * need to be monitored.
          */
         if (access == PROPERTY_WRITE)
             cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
         else
@@ -1186,16 +1195,18 @@ TypeConstraintProp<access>::newType(JSCo
     if (object)
         PropertyAccess<access>(cx, script, pc, object, target, id);
 }
 
 template <PropertyAccessKind access>
 void
 TypeConstraintCallProp<access>::newType(JSContext *cx, TypeSet *source, Type type)
 {
+    RootedScript script(cx, script_);
+
     /*
      * For CALLPROP, we need to update not just the pushed types but also the
      * 'this' types of possible callees. If we can't figure out that set of
      * callees, monitor the call to make sure discovered callees get their
      * 'this' types updated.
      */
 
     if (UnknownPropertyAccess(script, type)) {
@@ -1211,39 +1222,40 @@ TypeConstraintCallProp<access>::newType(
             TypeSet *types = object->getProperty(cx, id, false);
             if (!types)
                 return;
             if (!types->hasPropagatedProperty())
                 object->getFromPrototypes(cx, id, types);
             /* Bypass addPropagateThis, we already have the callpc. */
             if (access == PROPERTY_READ) {
                 types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintPropagateThis>(
-                                script, callpc, type, (StackTypeSet *) NULL));
+                                script_, callpc, type, (StackTypeSet *) NULL));
             } else {
                 TypeConstraintPropagateThis constraint(script, callpc, type, NULL);
                 types->addTypesToConstraint(cx, &constraint);
             }
         }
     }
 }
 
 void
 TypeConstraintSetElement::newType(JSContext *cx, TypeSet *source, Type type)
 {
+    RootedScript script(cx, script_);
     if (type.isUnknown() ||
         type.isPrimitive(JSVAL_TYPE_INT32) ||
         type.isPrimitive(JSVAL_TYPE_DOUBLE)) {
         objectTypes->addSetProperty(cx, script, pc, valueTypes, JSID_VOID);
     }
 }
 
 void
 TypeConstraintCall::newType(JSContext *cx, TypeSet *source, Type type)
 {
-    JSScript *script = callsite->script;
+    RootedScript script(cx, callsite->script);
     jsbytecode *pc = callsite->pc;
 
     JS_ASSERT_IF(script->hasAnalysis(),
                  callsite->returnTypes == script->analysis()->bytecodeTypes(pc));
 
     if (type.isUnknown() || type.isAnyObject()) {
         /* Monitor calls on unknown functions. */
         cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
@@ -1364,16 +1376,17 @@ TypeConstraintPropagateThis::newType(JSC
 {
     if (type.isUnknown() || type.isAnyObject()) {
         /*
          * The callee is unknown, make sure the call is monitored so we pick up
          * possible this/callee correlations. This only comes into play for
          * CALLPROP, for other calls we are past the type barrier and a
          * TypeConstraintCall will also monitor the call.
          */
+        RootedScript script(cx, script_);
         cx->compartment->types.monitorBytecode(cx, script, callpc - script->code);
         return;
     }
 
     /* Ignore calls to natives, these will be handled by TypeConstraintCall. */
     JSFunction *callee = NULL;
 
     if (type.isSingleObject()) {
@@ -1407,16 +1420,17 @@ TypeConstraintArith::newType(JSContext *
     /*
      * We only model a subset of the arithmetic behavior that is actually
      * possible. The following need to be watched for at runtime:
      *
      * 1. Operations producing a double where no operand was a double.
      * 2. Operations producing a string where no operand was a string (addition only).
      * 3. Operations producing a value other than int/double/string.
      */
+    RootedScript script(cx, script_);
     if (other) {
         /*
          * Addition operation, consider these cases:
          *   {int,bool} x {int,bool} -> int
          *   double x {int,bool,double} -> double
          *   string x any -> string
          */
         if (type.isUnknown() || other->unknown()) {
@@ -1450,21 +1464,23 @@ TypeConstraintArith::newType(JSContext *
         else
             target->addType(cx, Type::Int32Type());
     }
 }
 
 void
 TypeConstraintTransformThis::newType(JSContext *cx, TypeSet *source, Type type)
 {
-    if (type.isUnknown() || type.isAnyObject() || type.isObject() || script->strictModeCode) {
+    if (type.isUnknown() || type.isAnyObject() || type.isObject() || script_->strictModeCode) {
         target->addType(cx, type);
         return;
     }
 
+    RootedScript script(cx, script_);
+
     /*
      * Note: if |this| is null or undefined, the pushed value is the outer window. We
      * can't use script->getGlobalType() here because it refers to the inner window.
      */
     if (!script->compileAndGo ||
         type.isPrimitive(JSVAL_TYPE_NULL) ||
         type.isPrimitive(JSVAL_TYPE_UNDEFINED)) {
         target->addType(cx, Type::UnknownType());
@@ -1497,17 +1513,17 @@ TypeConstraintTransformThis::newType(JSC
 
 /////////////////////////////////////////////////////////////////////
 // Freeze constraints
 /////////////////////////////////////////////////////////////////////
 
 /* Constraint which triggers recompilation of a script if any type is added to a type set. */
 class TypeConstraintFreeze : public TypeConstraint
 {
-public:
+  public:
     RecompileInfo info;
 
     /* Whether a new type has already been added, triggering recompilation. */
     bool typeAdded;
 
     TypeConstraintFreeze(RecompileInfo info)
         : info(info), typeAdded(false)
     {}
@@ -1605,17 +1621,17 @@ HeapTypeSet::getKnownTypeTag(JSContext *
     JS_ASSERT_IF(empty, type == JSVAL_TYPE_UNKNOWN);
 
     return type;
 }
 
 /* Constraint which triggers recompilation if an object acquires particular flags. */
 class TypeConstraintFreezeObjectFlags : public TypeConstraint
 {
-public:
+  public:
     RecompileInfo info;
 
     /* Flags we are watching for on this object. */
     TypeObjectFlags flags;
 
     /* Whether the object has already been marked as having one of the flags. */
     bool marked;
 
@@ -1726,17 +1742,17 @@ HeapTypeSet::WatchObjectStateChange(JSCo
      */
     types->add(cx, cx->typeLifoAlloc().new_<TypeConstraintFreezeObjectFlags>(
                      cx->compartment->types.compiledInfo,
                      0));
 }
 
 class TypeConstraintFreezeOwnProperty : public TypeConstraint
 {
-public:
+  public:
     RecompileInfo info;
 
     bool updated;
     bool configurable;
 
     TypeConstraintFreezeOwnProperty(RecompileInfo info, bool configurable)
         : info(info), updated(false), configurable(configurable)
     {}
@@ -1950,17 +1966,17 @@ enum RecompileKind {
 /*
  * Whether all jitcode for a given pc was compiled with monitoring or barriers.
  * If we reanalyze the script after generating jitcode, new monitoring and
  * barriers will be added which may be duplicating information available when
  * the script was originally compiled, and which should not invalidate that
  * compilation.
  */
 static inline bool
-JITCodeHasCheck(JSScript *script, jsbytecode *pc, RecompileKind kind)
+JITCodeHasCheck(HandleScript script, jsbytecode *pc, RecompileKind kind)
 {
     if (kind == RECOMPILE_NONE)
         return false;
 
 #ifdef JS_METHODJIT
     for (int constructing = 0; constructing <= 1; constructing++) {
         for (int barriers = 0; barriers <= 1; barriers++) {
             mjit::JITScript *jit = script->getJIT((bool) constructing, (bool) barriers);
@@ -1992,17 +2008,17 @@ JITCodeHasCheck(JSScript *script, jsbyte
     return true;
 }
 
 /*
  * Force recompilation of any jitcode for script at pc, or of any other script
  * which this script was inlined into.
  */
 static inline void
-AddPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc,
+AddPendingRecompile(JSContext *cx, HandleScript script, jsbytecode *pc,
                     RecompileKind kind = RECOMPILE_NONE)
 {
     /*
      * Trigger recompilation of the script itself, if code was not previously
      * compiled with the specified information.
      */
     if (!JITCodeHasCheck(script, pc, kind))
         cx->compartment->types.addPendingRecompile(cx, script, pc);
@@ -2018,31 +2034,32 @@ AddPendingRecompile(JSContext *cx, JSScr
 
 /*
  * As for TypeConstraintFreeze, but describes an implicit freeze constraint
  * added for stack types within a script. Applies to all compilations of the
  * script, not just a single one.
  */
 class TypeConstraintFreezeStack : public TypeConstraint
 {
-public:
-    JSScript *script;
-
+    JSScript *script_;
+
+  public:
     TypeConstraintFreezeStack(JSScript *script)
-        : script(script)
+        : script_(script)
     {}
 
     const char *kind() { return "freezeStack"; }
 
     void newType(JSContext *cx, TypeSet *source, Type type)
     {
         /*
          * Unlike TypeConstraintFreeze, triggering this constraint once does
          * not disable it on future changes to the type set.
          */
+        RootedScript script(cx, script_);
         AddPendingRecompile(cx, script, NULL);
     }
 };
 
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
@@ -2084,17 +2101,17 @@ TypeCompartment::newTypeObject(JSContext
             object->setFlagsFromKey(cx, key);
         }
     }
 
     return object;
 }
 
 static inline jsbytecode *
-PreviousOpcode(JSScript *script, jsbytecode *pc)
+PreviousOpcode(HandleScript script, jsbytecode *pc)
 {
     ScriptAnalysis *analysis = script->analysis();
     JS_ASSERT(analysis->maybeCode(pc));
 
     if (pc == script->code)
         return NULL;
 
     for (pc--;; pc--) {
@@ -2105,17 +2122,17 @@ PreviousOpcode(JSScript *script, jsbytec
     return pc;
 }
 
 /*
  * If pc is an array initializer within an outer multidimensional array
  * initializer, find the opcode of the previous newarray. NULL otherwise.
  */
 static inline jsbytecode *
-FindPreviousInnerInitializer(JSScript *script, jsbytecode *initpc)
+FindPreviousInnerInitializer(HandleScript script, jsbytecode *initpc)
 {
     if (!script->hasAnalysis())
         return NULL;
 
     /*
      * Pattern match the following bytecode, which will appear between
      * adjacent initializer elements:
      *
@@ -2196,17 +2213,18 @@ TypeCompartment::addAllocationSiteTypeOb
     TypeObject *res = NULL;
 
     /*
      * If this is an array initializer nested in another array initializer,
      * try to reuse the type objects from earlier elements to avoid
      * distinguishing elements of the outer array unnecessarily.
      */
     jsbytecode *pc = key.script->code + key.offset;
-    jsbytecode *prev = FindPreviousInnerInitializer(key.script, pc);
+    RootedScript keyScript(cx, key.script);
+    jsbytecode *prev = FindPreviousInnerInitializer(keyScript, pc);
     if (prev) {
         AllocationSiteKey nkey;
         nkey.script = key.script;
         nkey.offset = prev - key.script->code;
         nkey.kind = JSProto_Array;
 
         AllocationSiteTable::Ptr p = cx->compartment->types.allocationSiteTable->lookup(nkey);
         if (p)
@@ -2244,24 +2262,24 @@ TypeCompartment::addAllocationSiteTypeOb
         cx->compartment->types.setPendingNukeTypes(cx);
         return NULL;
     }
 
     return res;
 }
 
 static inline jsid
-GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset)
+GetAtomId(JSContext *cx, HandleScript script, const jsbytecode *pc, unsigned offset)
 {
     PropertyName *name = script->getName(GET_UINT32_INDEX(pc + offset));
     return MakeTypeId(cx, NameToId(name));
 }
 
 bool
-types::UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc)
+types::UseNewType(JSContext *cx, HandleScript script, jsbytecode *pc)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
 
     /*
      * Make a heuristic guess at a use of JSOP_NEW that the constructed object
      * should have a fresh type object. We do this when the NEW is immediately
      * followed by a simple assignment to an object's .prototype field.
      * This is designed to catch common patterns for subclassing in JS:
@@ -2285,17 +2303,17 @@ types::UseNewType(JSContext *cx, JSScrip
         if (id == id_prototype(cx))
             return true;
     }
 
     return false;
 }
 
 bool
-types::UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey key)
+types::UseNewTypeForInitializer(JSContext *cx, HandleScript script, jsbytecode *pc, JSProtoKey key)
 {
     /*
      * Objects created outside loops in global and eval scripts should have
      * singleton types. For now this is only done for plain objects and typed
      * arrays, but not normal arrays.
      */
 
     if (!cx->typeInferenceEnabled() || script->function())
@@ -2308,17 +2326,17 @@ types::UseNewTypeForInitializer(JSContex
 
     if (!script->ensureRanAnalysis(cx))
         return false;
 
     return !script->analysis()->getCode(pc).inLoop;
 }
 
 bool
-types::ArrayPrototypeHasIndexedProperty(JSContext *cx, JSScript *script)
+types::ArrayPrototypeHasIndexedProperty(JSContext *cx, HandleScript script)
 {
     if (!cx->typeInferenceEnabled() || !script->compileAndGo)
         return true;
 
     RootedObject proto(cx, script->global().getOrCreateArrayPrototype(cx));
     if (!proto)
         return true;
 
@@ -2435,17 +2453,17 @@ TypeCompartment::nukeTypes(FreeOp *fop)
     mjit::ClearAllFrames(compartment);
 # ifdef JS_ION
     ion::InvalidateAll(fop, compartment);
 # endif
 
     /* Throw away all JIT code in the compartment, but leave everything else alone. */
 
     for (gc::CellIter i(compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
-        JSScript *script = i.get<JSScript>();
+        RawScript script = i.get<JSScript>();
         mjit::ReleaseScriptCode(fop, script);
 # ifdef JS_ION
         ion::FinishInvalidation(fop, script);
 # endif
     }
 #endif /* JS_METHODJIT */
 }
 
@@ -2498,17 +2516,17 @@ TypeCompartment::addPendingRecompile(JSC
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
     co->setPendingRecompilation();
 }
 
 void
-TypeCompartment::addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc)
+TypeCompartment::addPendingRecompile(JSContext *cx, HandleScript script, jsbytecode *pc)
 {
     JS_ASSERT(script);
     if (!constrainedOutputs)
         return;
 
 #ifdef JS_METHODJIT
     for (int constructing = 0; constructing <= 1; constructing++) {
         for (int barriers = 0; barriers <= 1; barriers++) {
@@ -2536,17 +2554,17 @@ TypeCompartment::addPendingRecompile(JSC
 
     if (script->hasIonScript())
         addPendingRecompile(cx, script->ionScript()->recompileInfo());
 # endif
 #endif
 }
 
 void
-TypeCompartment::monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,
+TypeCompartment::monitorBytecode(JSContext *cx, HandleScript script, uint32_t offset,
                                  bool returnOnly)
 {
     if (!script->ensureRanInference(cx))
         return;
 
     ScriptAnalysis *analysis = script->analysis();
     jsbytecode *pc = script->code + offset;
 
@@ -2606,17 +2624,17 @@ TypeCompartment::markSetsUnknown(JSConte
             }
         }
     }
 
     for (unsigned i = 0; i < pending.length(); i++)
         pending[i]->addType(cx, Type::AnyObjectType());
 
     for (gc::CellIter i(cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
-        JSScript *script = i.get<JSScript>();
+        RootedScript script(cx, i.get<JSScript>());
         if (script->types) {
             unsigned count = TypeScript::NumTypeSets(script);
             TypeSet *typeArray = script->types->typeArray();
             for (unsigned i = 0; i < count; i++) {
                 if (typeArray[i].hasType(Type::ObjectType(target)))
                     typeArray[i].addType(cx, Type::AnyObjectType());
             }
         }
@@ -2654,29 +2672,30 @@ ScriptAnalysis::addTypeBarrier(JSContext
 
     if (!code.typeBarriers) {
         /*
          * Adding type barriers at a bytecode which did not have them before
          * will trigger recompilation. If there were already type barriers,
          * however, do not trigger recompilation (the script will be recompiled
          * if any of the barriers is ever violated).
          */
+        RootedScript script(cx, script_);
         AddPendingRecompile(cx, script, const_cast<jsbytecode*>(pc), RECOMPILE_CHECK_BARRIERS);
     }
 
     /* Ignore duplicate barriers. */
     TypeBarrier *barrier = code.typeBarriers;
     while (barrier) {
         if (barrier->target == target && barrier->type == type && !barrier->singleton)
             return;
         barrier = barrier->next;
     }
 
     InferSpew(ISpewOps, "typeBarrier: #%u:%05u: %sT%p%s %s",
-              script->id(), pc - script->code,
+              script_->id(), pc - script_->code,
               InferSpewColor(target), target, InferSpewColorReset(),
               TypeString(type));
 
     barrier = cx->analysisLifoAlloc().new_<TypeBarrier>(target, type, (JSObject *) NULL, JSID_VOID);
 
     if (!barrier) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
@@ -2691,21 +2710,22 @@ ScriptAnalysis::addSingletonTypeBarrier(
                                         HandleObject singleton, jsid singletonId)
 {
     JS_ASSERT(singletonId == MakeTypeId(cx, singletonId) && !JSID_IS_VOID(singletonId));
 
     Bytecode &code = getCode(pc);
 
     if (!code.typeBarriers) {
         /* Trigger recompilation as for normal type barriers. */
+        RootedScript script(cx, script_);
         AddPendingRecompile(cx, script, const_cast<jsbytecode*>(pc), RECOMPILE_CHECK_BARRIERS);
     }
 
     InferSpew(ISpewOps, "singletonTypeBarrier: #%u:%05u: %sT%p%s %p %s",
-              script->id(), pc - script->code,
+              script_->id(), pc - script_->code,
               InferSpewColor(target), target, InferSpewColorReset(),
               (void *) singleton.get(), TypeIdString(singletonId));
 
     TypeBarrier *barrier = cx->analysisLifoAlloc().new_<TypeBarrier>(target, Type::UndefinedType(),
                               singleton, singletonId);
 
     if (!barrier) {
         cx->compartment->types.setPendingNukeTypes(cx);
@@ -2721,17 +2741,17 @@ TypeCompartment::print(JSContext *cx, bo
 {
     JSCompartment *compartment = this->compartment();
     AutoEnterAnalysis enter(compartment);
 
     if (!force && !InferSpewActive(ISpewResult))
         return;
 
     for (gc::CellIter i(compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
-        JSScript *script = i.get<JSScript>();
+        RootedScript script(cx, i.get<JSScript>());
         if (script->hasAnalysis() && script->analysis()->ranInference())
             script->analysis()->printTypes(cx);
     }
 
 #ifdef DEBUG
     for (gc::CellIter i(compartment, gc::FINALIZE_TYPE_OBJECT); !i.done(); i.next()) {
         TypeObject *object = i.get<TypeObject>();
         object->print(cx);
@@ -3494,17 +3514,17 @@ TypeObject::print(JSContext *cx)
     printf("\n}\n");
 }
 
 /////////////////////////////////////////////////////////////////////
 // Type Analysis
 /////////////////////////////////////////////////////////////////////
 
 static inline TypeObject *
-GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc)
+GetInitializerType(JSContext *cx, HandleScript script, jsbytecode *pc)
 {
     if (!script->compileAndGo)
         return NULL;
 
     JSOp op = JSOp(*pc);
     JS_ASSERT(op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT || op == JSOP_NEWINIT);
 
     bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array));
@@ -3523,28 +3543,29 @@ GetCalleeThisType(jsbytecode *pc)
     if (*pc == JSOP_UNDEFINED)
         return Type::UndefinedType();
     JS_ASSERT(*pc == JSOP_IMPLICITTHIS);
     return Type::UnknownType();
 }
 
 /* Analyze type information for a single bytecode. */
 bool
-ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
-                                     TypeInferenceState &state)
-{
-    jsbytecode *pc = script->code + offset;
+ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, TypeInferenceState &state)
+{
+    RootedScript script(cx, script_);
+
+    jsbytecode *pc = script_->code + offset;
     JSOp op = (JSOp)*pc;
 
     Bytecode &code = getCode(offset);
     JS_ASSERT(!code.pushedTypes);
 
-    InferSpew(ISpewOps, "analyze: #%u:%05u", script->id(), offset);
-
-    unsigned defCount = GetDefCount(script, offset);
+    InferSpew(ISpewOps, "analyze: #%u:%05u", script_->id(), offset);
+
+    unsigned defCount = GetDefCount(script_, offset);
     if (ExtendedDef(pc))
         defCount++;
 
     StackTypeSet *pushed = cx->analysisLifoAlloc().newArrayUninitialized<StackTypeSet>(defCount);
     if (!pushed)
         return false;
     PodZero(pushed, defCount);
     code.pushedTypes = pushed;
@@ -3567,34 +3588,34 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
              * The phi nodes at join points should all be unique, and every phi
              * node created should be in the phiValues list on some bytecode.
              */
             if (!state.phiNodes.append(newv->value.phiNode()))
                 return false;
             TypeSet &types = newv->value.phiNode()->types;
             InferSpew(ISpewOps, "typeSet: %sT%p%s phi #%u:%05u:%u",
                       InferSpewColor(&types), &types, InferSpewColorReset(),
-                      script->id(), offset, newv->slot);
+                      script_->id(), offset, newv->slot);
             types.setPurged();
 
             newv++;
         }
     }
 
     /*
      * Treat decomposed ops as no-ops, we will analyze the decomposed version
      * instead. (We do, however, need to look at introduced phi nodes).
      */
     if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
         return true;
 
     for (unsigned i = 0; i < defCount; i++) {
         InferSpew(ISpewOps, "typeSet: %sT%p%s pushed%u #%u:%05u",
                   InferSpewColor(&pushed[i]), &pushed[i], InferSpewColorReset(),
-                  i, script->id(), offset);
+                  i, script_->id(), offset);
         pushed[i].setPurged();
     }
 
     /* Add type constraints for the various opcodes. */
     switch (op) {
 
         /* Nop bytecodes. */
       case JSOP_POP:
@@ -3679,34 +3700,34 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_XMLELTEXPR:
         pushed[0].addType(cx, Type::StringType());
         break;
       case JSOP_NULL:
         pushed[0].addType(cx, Type::NullType());
         break;
 
       case JSOP_REGEXP:
-        if (script->compileAndGo) {
+        if (script_->compileAndGo) {
             TypeObject *object = TypeScript::StandardType(cx, script, JSProto_RegExp);
             if (!object)
                 return false;
             pushed[0].addType(cx, Type::ObjectType(object));
         } else {
             pushed[0].addType(cx, Type::UnknownType());
         }
         break;
 
       case JSOP_OBJECT:
-        pushed[0].addType(cx, Type::ObjectType(script->getObject(GET_UINT32_INDEX(pc))));
+        pushed[0].addType(cx, Type::ObjectType(script_->getObject(GET_UINT32_INDEX(pc))));
         break;
 
       case JSOP_STOP:
         /* If a stop is reachable then the return type may be void. */
-          if (script->function())
-            TypeScript::ReturnTypes(script)->addType(cx, Type::UndefinedType());
+          if (script_->function())
+            TypeScript::ReturnTypes(script_)->addType(cx, Type::UndefinedType());
         break;
 
       case JSOP_OR:
       case JSOP_AND:
         /* OR/AND push whichever operand determined the result. */
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
@@ -3747,17 +3768,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
          */
         if (id == NameToId(cx->names().undefined))
             seen->addType(cx, Type::UndefinedType());
         if (id == NameToId(cx->names().NaN))
             seen->addType(cx, Type::DoubleType());
         if (id == NameToId(cx->names().Infinity))
             seen->addType(cx, Type::DoubleType());
 
-        TypeObject *global = script->global().getType(cx);
+        TypeObject *global = script_->global().getType(cx);
 
         /* Handle as a property access. */
         if (state.hasPropertyReadTypes)
             PropertyAccess<PROPERTY_READ_EXISTING>(cx, script, pc, global, seen, id);
         else
             PropertyAccess<PROPERTY_READ>(cx, script, pc, global, seen, id);
 
         if (op == JSOP_CALLGNAME)
@@ -3778,17 +3799,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       }
 
       case JSOP_BINDGNAME:
       case JSOP_BINDNAME:
         break;
 
       case JSOP_SETGNAME: {
         jsid id = GetAtomId(cx, script, pc, 0);
-        TypeObject *global = script->global().getType(cx);
+        TypeObject *global = script_->global().getType(cx);
         PropertyAccess<PROPERTY_WRITE>(cx, script, pc, global, poppedTypes(pc, 0), id);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_SETNAME:
       case JSOP_SETCONST:
         cx->compartment->types.monitorBytecode(cx, script, offset);
@@ -3801,41 +3822,41 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         seen->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_GETARG:
       case JSOP_CALLARG:
       case JSOP_GETLOCAL:
       case JSOP_CALLLOCAL: {
-        uint32_t slot = GetBytecodeSlot(script, pc);
+        uint32_t slot = GetBytecodeSlot(script_, pc);
         if (trackSlot(slot)) {
             /*
              * Normally these opcodes don't pop anything, but they are given
              * an extended use holding the variable's SSA value before the
              * access. Use the types from here.
              */
             poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
-        } else if (slot < TotalSlots(script)) {
-            StackTypeSet *types = TypeScript::SlotTypes(script, slot);
+        } else if (slot < TotalSlots(script_)) {
+            StackTypeSet *types = TypeScript::SlotTypes(script_, slot);
             types->addSubset(cx, &pushed[0]);
         } else {
             /* Local 'let' variable. Punt on types for these, for now. */
             pushed[0].addType(cx, Type::UnknownType());
         }
         if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL)
             pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
         break;
       }
 
       case JSOP_SETARG:
       case JSOP_SETLOCAL: {
-        uint32_t slot = GetBytecodeSlot(script, pc);
-        if (!trackSlot(slot) && slot < TotalSlots(script)) {
-            TypeSet *types = TypeScript::SlotTypes(script, slot);
+        uint32_t slot = GetBytecodeSlot(script_, pc);
+        if (!trackSlot(slot) && slot < TotalSlots(script_)) {
+            TypeSet *types = TypeScript::SlotTypes(script_, slot);
             poppedTypes(pc, 0)->addSubset(cx, types);
         }
 
         /*
          * For assignments to non-escaping locals/args, we don't need to update
          * the possible types of the var, as for each read of the var SSA gives
          * us the writes that could have produced that read.
          */
@@ -3864,40 +3885,40 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_INCARG:
       case JSOP_DECARG:
       case JSOP_ARGINC:
       case JSOP_ARGDEC:
       case JSOP_INCLOCAL:
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC: {
-        uint32_t slot = GetBytecodeSlot(script, pc);
+        uint32_t slot = GetBytecodeSlot(script_, pc);
         if (trackSlot(slot)) {
             poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
-        } else if (slot < TotalSlots(script)) {
-            StackTypeSet *types = TypeScript::SlotTypes(script, slot);
+        } else if (slot < TotalSlots(script_)) {
+            StackTypeSet *types = TypeScript::SlotTypes(script_, slot);
             types->addArith(cx, script, pc, types);
             types->addSubset(cx, &pushed[0]);
         } else {
             pushed[0].addType(cx, Type::UnknownType());
         }
         break;
       }
 
       case JSOP_ARGUMENTS:
         /* Compute a precise type only when we know the arguments won't escape. */
-        if (script->needsArgsObj())
+        if (script_->needsArgsObj())
             pushed[0].addType(cx, Type::UnknownType());
         else
             pushed[0].addType(cx, Type::MagicArgType());
         break;
 
       case JSOP_REST: {
-        StackTypeSet *types = script->analysis()->bytecodeTypes(pc);
-        if (script->compileAndGo) {
+        StackTypeSet *types = script_->analysis()->bytecodeTypes(pc);
+        if (script_->compileAndGo) {
             TypeObject *rest = TypeScript::InitObject(cx, script, pc, JSProto_Array);
             if (!rest)
                 return false;
 
             // Simulate setting a element.
             if (!rest->unknownProperties()) {
                 HeapTypeSet *propTypes = rest->getProperty(cx, JSID_VOID, true);
                 if (!propTypes)
@@ -3920,26 +3941,26 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
       }
 
       case JSOP_LENGTH:
       case JSOP_GETPROP:
       case JSOP_CALLPROP: {
         jsid id = GetAtomId(cx, script, pc, 0);
-        StackTypeSet *seen = script->analysis()->bytecodeTypes(pc);
-
-        HeapTypeSet *input = &script->types->propertyReadTypes[state.propertyReadIndex++];
+        StackTypeSet *seen = script_->analysis()->bytecodeTypes(pc);
+
+        HeapTypeSet *input = &script_->types->propertyReadTypes[state.propertyReadIndex++];
         poppedTypes(pc, 0)->addSubset(cx, input);
 
         if (state.hasPropertyReadTypes) {
-            TypeConstraintGetPropertyExisting getProp(script, pc, seen, id);
+            TypeConstraintGetPropertyExisting getProp(script_, pc, seen, id);
             input->addTypesToConstraint(cx, &getProp);
             if (op == JSOP_CALLPROP) {
-                TypeConstraintCallPropertyExisting callProp(script, pc, id);
+                TypeConstraintCallPropertyExisting callProp(script_, pc, id);
                 input->addTypesToConstraint(cx, &callProp);
             }
         } else {
             input->addGetProperty(cx, script, pc, seen, id);
             if (op == JSOP_CALLPROP)
                 input->addCallProperty(cx, script, pc, id);
         }
 
@@ -3949,27 +3970,27 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
       /*
        * We only consider ELEM accesses on integers below. Any element access
        * which is accessing a non-integer property must be monitored.
        */
 
       case JSOP_GETELEM:
       case JSOP_CALLELEM: {
-        StackTypeSet *seen = script->analysis()->bytecodeTypes(pc);
+        StackTypeSet *seen = script_->analysis()->bytecodeTypes(pc);
 
         /* Don't try to compute a precise callee for CALLELEM. */
         if (op == JSOP_CALLELEM)
             seen->addType(cx, Type::AnyObjectType());
 
-        HeapTypeSet *input = &script->types->propertyReadTypes[state.propertyReadIndex++];
+        HeapTypeSet *input = &script_->types->propertyReadTypes[state.propertyReadIndex++];
         poppedTypes(pc, 1)->addSubset(cx, input);
 
         if (state.hasPropertyReadTypes) {
-            TypeConstraintGetPropertyExisting getProp(script, pc, seen, JSID_VOID);
+            TypeConstraintGetPropertyExisting getProp(script_, pc, seen, JSID_VOID);
             input->addTypesToConstraint(cx, &getProp);
         } else {
             input->addGetProperty(cx, script, pc, seen, JSID_VOID);
         }
 
         seen->addSubset(cx, &pushed[0]);
         if (op == JSOP_CALLELEM)
             pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType(), poppedTypes(pc, 1));
@@ -3985,23 +4006,23 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         /*
          * This is only used for element inc/dec ops; any id produced which
          * is not an integer must be monitored.
          */
         pushed[0].addType(cx, Type::Int32Type());
         break;
 
       case JSOP_THIS:
-        TypeScript::ThisTypes(script)->addTransformThis(cx, script, &pushed[0]);
+        TypeScript::ThisTypes(script_)->addTransformThis(cx, script, &pushed[0]);
         break;
 
       case JSOP_RETURN:
       case JSOP_SETRVAL:
-          if (script->function())
-            poppedTypes(pc, 0)->addSubset(cx, TypeScript::ReturnTypes(script));
+          if (script_->function())
+            poppedTypes(pc, 0)->addSubset(cx, TypeScript::ReturnTypes(script_));
         break;
 
       case JSOP_ADD:
         poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0], poppedTypes(pc, 1));
         poppedTypes(pc, 1)->addArith(cx, script, pc, &pushed[0], poppedTypes(pc, 0));
         break;
 
       case JSOP_SUB:
@@ -4014,24 +4035,24 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
       case JSOP_NEG:
       case JSOP_POS:
         poppedTypes(pc, 0)->addArith(cx, script, pc, &pushed[0]);
         break;
 
       case JSOP_LAMBDA:
       case JSOP_DEFFUN: {
-        RootedObject obj(cx, script->getObject(GET_UINT32_INDEX(pc)));
+        RootedObject obj(cx, script_->getObject(GET_UINT32_INDEX(pc)));
 
         TypeSet *res = NULL;
         if (op == JSOP_LAMBDA)
             res = &pushed[0];
 
         if (res) {
-            if (script->compileAndGo && !UseNewTypeForClone(obj->toFunction()))
+            if (script_->compileAndGo && !UseNewTypeForClone(obj->toFunction()))
                 res->addType(cx, Type::ObjectType(obj));
             else
                 res->addType(cx, Type::UnknownType());
         } else {
             cx->compartment->types.monitorBytecode(cx, script, offset);
         }
         break;
       }
@@ -4039,61 +4060,61 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_DEFVAR:
         break;
 
       case JSOP_CALL:
       case JSOP_EVAL:
       case JSOP_FUNCALL:
       case JSOP_FUNAPPLY:
       case JSOP_NEW: {
-        StackTypeSet *seen = script->analysis()->bytecodeTypes(pc);
+        StackTypeSet *seen = script_->analysis()->bytecodeTypes(pc);
         seen->addSubset(cx, &pushed[0]);
 
         /* Construct the base call information about this site. */
-        unsigned argCount = GetUseCount(script, offset) - 2;
+        unsigned argCount = GetUseCount(script_, offset) - 2;
         TypeCallsite *callsite = cx->analysisLifoAlloc().new_<TypeCallsite>(
-                                                        cx, script, pc, op == JSOP_NEW, argCount);
+                                                        cx, script_, pc, op == JSOP_NEW, argCount);
         if (!callsite || (argCount && !callsite->argumentTypes)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             break;
         }
         callsite->thisTypes = poppedTypes(pc, argCount);
         callsite->returnTypes = seen;
 
         for (unsigned i = 0; i < argCount; i++)
             callsite->argumentTypes[i] = poppedTypes(pc, argCount - 1 - i);
 
         /*
          * Mark FUNCALL and FUNAPPLY sites as monitored. The method JIT may
          * lower these into normal calls, and we need to make sure the
          * callee's argument types are checked on entry.
          */
         if (op == JSOP_FUNCALL || op == JSOP_FUNAPPLY)
-            cx->compartment->types.monitorBytecode(cx, script, pc - script->code);
+            cx->compartment->types.monitorBytecode(cx, script, pc - script_->code);
 
         poppedTypes(pc, argCount + 1)->addCall(cx, callsite);
         break;
       }
 
       case JSOP_NEWINIT:
       case JSOP_NEWARRAY:
       case JSOP_NEWOBJECT: {
-        StackTypeSet *types = script->analysis()->bytecodeTypes(pc);
+        StackTypeSet *types = script_->analysis()->bytecodeTypes(pc);
         types->addSubset(cx, &pushed[0]);
 
         bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array));
         JSProtoKey key = isArray ? JSProto_Array : JSProto_Object;
 
         if (UseNewTypeForInitializer(cx, script, pc, key)) {
             /* Defer types pushed by this bytecode until runtime. */
             break;
         }
 
         TypeObject *initializer = GetInitializerType(cx, script, pc);
-        if (script->compileAndGo) {
+        if (script_->compileAndGo) {
             if (!initializer)
                 return false;
             types->addType(cx, Type::ObjectType(initializer));
         } else {
             JS_ASSERT(!initializer);
             types->addType(cx, Type::UnknownType());
         }
         break;
@@ -4101,17 +4122,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
       case JSOP_ENDINIT:
         break;
 
       case JSOP_INITELEM:
       case JSOP_INITELEM_INC:
       case JSOP_SPREAD: {
         const SSAValue &objv = poppedValue(pc, 2);
-        jsbytecode *initpc = script->code + objv.pushedOffset();
+        jsbytecode *initpc = script_->code + objv.pushedOffset();
         TypeObject *initializer = GetInitializerType(cx, script, initpc);
 
         if (initializer) {
             pushed[0].addType(cx, Type::ObjectType(initializer));
             if (!initializer->unknownProperties()) {
                 /*
                  * Assume the initialized element is an integer. INITELEM can be used
                  * for doubles which don't map to the JSID_VOID property, which must
@@ -4154,17 +4175,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         break;
 
       case JSOP_HOLE:
         state.hasHole = true;
         break;
 
       case JSOP_INITPROP: {
         const SSAValue &objv = poppedValue(pc, 1);
-        jsbytecode *initpc = script->code + objv.pushedOffset();
+        jsbytecode *initpc = script_->code + objv.pushedOffset();
         TypeObject *initializer = GetInitializerType(cx, script, initpc);
 
         if (initializer) {
             pushed[0].addType(cx, Type::ObjectType(initializer));
             if (!initializer->unknownProperties()) {
                 jsid id = GetAtomId(cx, script, pc, 0);
                 TypeSet *types = initializer->getProperty(cx, id, true);
                 if (!types)
@@ -4266,27 +4287,27 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_LEAVEFORLETIN:
         break;
 
       case JSOP_CASE:
         poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_GENERATOR:
-          if (script->function()) {
-            if (script->compileAndGo) {
-                RawObject proto = script->global().getOrCreateGeneratorPrototype(cx);
+          if (script_->function()) {
+            if (script_->compileAndGo) {
+                RawObject proto = script_->global().getOrCreateGeneratorPrototype(cx);
                 if (!proto)
                     return false;
                 TypeObject *object = proto->getNewType(cx);
                 if (!object)
                     return false;
-                TypeScript::ReturnTypes(script)->addType(cx, Type::ObjectType(object));
+                TypeScript::ReturnTypes(script_)->addType(cx, Type::ObjectType(object));
             } else {
-                TypeScript::ReturnTypes(script)->addType(cx, Type::UnknownType());
+                TypeScript::ReturnTypes(script_)->addType(cx, Type::UnknownType());
             }
         }
         break;
 
       case JSOP_YIELD:
         pushed[0].addType(cx, Type::UnknownType());
         break;
 
@@ -4319,27 +4340,27 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
       case JSOP_GETFUNNS:
       case JSOP_FILTER:
         /* Note: the second value pushed by filter is a hole, and not modelled. */
       case JSOP_ENDFILTER:
         pushed[0].addType(cx, Type::UnknownType());
         break;
 
       case JSOP_CALLEE: {
-        JSFunction *fun = script->function();
-        if (script->compileAndGo && !UseNewTypeForClone(fun))
+        JSFunction *fun = script_->function();
+        if (script_->compileAndGo && !UseNewTypeForClone(fun))
             pushed[0].addType(cx, Type::ObjectType(fun));
         else
             pushed[0].addType(cx, Type::UnknownType());
         break;
       }
 
       default:
         /* Display fine-grained debug information first */
-        fprintf(stderr, "Unknown bytecode %02x at #%u:%05u\n", op, script->id(), offset);
+        fprintf(stderr, "Unknown bytecode %02x at #%u:%05u\n", op, script_->id(), offset);
         TypeFailure(cx, "Unknown bytecode %02x", op);
     }
 
     return true;
 }
 
 void
 ScriptAnalysis::analyzeTypes(JSContext *cx)
@@ -4359,54 +4380,54 @@ ScriptAnalysis::analyzeTypes(JSContext *
 
     /*
      * Set this early to avoid reentrance. Any failures are OOMs, and will nuke
      * all types in the compartment.
      */
     ranInference_ = true;
 
     /* Make sure the initial type set of all local vars includes void. */
-    for (unsigned i = 0; i < script->nfixed; i++)
-        TypeScript::LocalTypes(script, i)->addType(cx, Type::UndefinedType());
+    for (unsigned i = 0; i < script_->nfixed; i++)
+        TypeScript::LocalTypes(script_, i)->addType(cx, Type::UndefinedType());
 
     TypeInferenceState state(cx);
 
     /*
      * Generate type sets for the inputs to property reads in the script,
      * unless it already has them. If we purge analysis information and end up
      * reanalyzing types in the script, we don't want to regenerate constraints
      * on these property inputs as they will be duplicating information on the
      * property type sets previously added.
      */
-    if (script->types->propertyReadTypes) {
+    if (script_->types->propertyReadTypes) {
         state.hasPropertyReadTypes = true;
     } else {
         HeapTypeSet *typeArray =
             (HeapTypeSet*) cx->typeLifoAlloc().alloc(sizeof(HeapTypeSet) * numPropertyReads());
         if (!typeArray) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
-        script->types->propertyReadTypes = typeArray;
+        script_->types->propertyReadTypes = typeArray;
         PodZero(typeArray, numPropertyReads());
 
 #ifdef DEBUG
         for (unsigned i = 0; i < numPropertyReads(); i++) {
             InferSpew(ISpewOps, "typeSet: %sT%p%s propertyRead%u #%u",
                       InferSpewColor(&typeArray[i]), &typeArray[i], InferSpewColorReset(),
-                      i, script->id());
+                      i, script_->id());
         }
 #endif
     }
 
     unsigned offset = 0;
-    while (offset < script->length) {
+    while (offset < script_->length) {
         Bytecode *code = maybeCode(offset);
 
-        jsbytecode *pc = script->code + offset;
+        jsbytecode *pc = script_->code + offset;
 
         if (code && !analyzeTypesBytecode(cx, offset, state)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
 
         offset += GetBytecodeLength(pc);
     }
@@ -4421,51 +4442,52 @@ ScriptAnalysis::analyzeTypes(JSContext *
         }
     }
 
     /*
      * Replay any dynamic type results which have been generated for the script
      * either because we ran the interpreter some before analyzing or because
      * we are reanalyzing after a GC.
      */
-    TypeResult *result = script->types->dynamicList;
+    TypeResult *result = script_->types->dynamicList;
     while (result) {
         if (result->offset != UINT32_MAX) {
             pushedTypes(result->offset)->addType(cx, result->type);
         } else {
             /* Custom for-in loop iteration has happened in this script. */
             state.forTypes->addType(cx, Type::UnknownType());
         }
         result = result->next;
     }
 
-    if (!script->hasFreezeConstraints) {
+    if (!script_->hasFreezeConstraints) {
+        RootedScript script(cx, script_);
         TypeScript::AddFreezeConstraints(cx, script);
-        script->hasFreezeConstraints = true;
+        script_->hasFreezeConstraints = true;
     }
 }
 
 bool
 ScriptAnalysis::integerOperation(JSContext *cx, jsbytecode *pc)
 {
-    JS_ASSERT(uint32_t(pc - script->code) < script->length);
+    JS_ASSERT(uint32_t(pc - script_->code) < script_->length);
 
     switch (JSOp(*pc)) {
 
       case JSOP_INCARG:
       case JSOP_DECARG:
       case JSOP_ARGINC:
       case JSOP_ARGDEC:
       case JSOP_INCLOCAL:
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC: {
         if (pushedTypes(pc, 0)->getKnownTypeTag() != JSVAL_TYPE_INT32)
             return false;
-        uint32_t slot = GetBytecodeSlot(script, pc);
+        uint32_t slot = GetBytecodeSlot(script_, pc);
         if (trackSlot(slot)) {
             if (poppedTypes(pc, 0)->getKnownTypeTag() != JSVAL_TYPE_INT32)
                 return false;
         }
         return true;
       }
 
       case JSOP_ADD:
@@ -4486,17 +4508,17 @@ ScriptAnalysis::integerOperation(JSConte
 }
 
 /*
  * Persistent constraint clearing out newScript and definite properties from
  * an object should a property on another object get a setter.
  */
 class TypeConstraintClearDefiniteSetter : public TypeConstraint
 {
-public:
+  public:
     TypeObject *object;
 
     TypeConstraintClearDefiniteSetter(TypeObject *object)
         : object(object)
     {}
 
     const char *kind() { return "clearDefiniteSetter"; }
 
@@ -4518,17 +4540,17 @@ public:
 };
 
 /*
  * Constraint which clears definite properties on an object should a type set
  * contain any types other than a single object.
  */
 class TypeConstraintClearDefiniteSingle : public TypeConstraint
 {
-public:
+  public:
     TypeObject *object;
 
     TypeConstraintClearDefiniteSingle(TypeObject *object)
         : object(object)
     {}
 
     const char *kind() { return "clearDefiniteSingle"; }
 
@@ -4564,17 +4586,17 @@ AnalyzeNewScriptProperties(JSContext *cx
     if (initializerList->length() > 50) {
         /*
          * Bail out on really long initializer lists (far longer than maximum
          * number of properties we can track), we may be recursing.
          */
         return false;
     }
 
-    JSScript *script = fun->script();
+    RootedScript script(cx, fun->script());
     if (!script->ensureRanAnalysis(cx) || !script->ensureRanInference(cx)) {
         pbaseobj.set(NULL);
         cx->compartment->types.setPendingNukeTypes(cx);
         return false;
     }
 
     ScriptAnalysis *analysis = script->analysis();
 
@@ -4702,17 +4724,17 @@ AnalyzeNewScriptProperties(JSContext *cx
     return entirelyAnalyzed;
 }
 
 static bool
 AnalyzePoppedThis(JSContext *cx, Vector<SSAUseChain *> *pendingPoppedThis,
                   TypeObject *type, JSFunction *fun, MutableHandleObject pbaseobj,
                   Vector<TypeNewScript::Initializer> *initializerList)
 {
-    JSScript *script = fun->script();
+    RootedScript script(cx, fun->script());
     ScriptAnalysis *analysis = script->analysis();
 
     while (!pendingPoppedThis->empty()) {
         SSAUseChain *uses = pendingPoppedThis->back();
         pendingPoppedThis->popBack();
 
         jsbytecode *pc = script->code + uses->offset;
         JSOp op = JSOp(*pc);
@@ -4938,33 +4960,33 @@ CheckNewScriptProperties(JSContext *cx, 
 
 /////////////////////////////////////////////////////////////////////
 // Printing
 /////////////////////////////////////////////////////////////////////
 
 void
 ScriptAnalysis::printTypes(JSContext *cx)
 {
-    AutoEnterAnalysis enter(script->compartment());
-    TypeCompartment *compartment = &script->compartment()->types;
+    AutoEnterAnalysis enter(script_->compartment());
+    TypeCompartment *compartment = &script_->compartment()->types;
 
     /*
      * Check if there are warnings for used values with unknown types, and build
      * statistics about the size of type sets found for stack values.
      */
-    for (unsigned offset = 0; offset < script->length; offset++) {
+    for (unsigned offset = 0; offset < script_->length; offset++) {
         if (!maybeCode(offset))
             continue;
 
-        jsbytecode *pc = script->code + offset;
+        jsbytecode *pc = script_->code + offset;
 
         if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
             continue;
 
-        unsigned defCount = GetDefCount(script, offset);
+        unsigned defCount = GetDefCount(script_, offset);
         if (!defCount)
             continue;
 
         for (unsigned i = 0; i < defCount; i++) {
             TypeSet *types = pushedTypes(offset, i);
 
             if (types->unknown()) {
                 compartment->typeCountOver++;
@@ -4997,61 +5019,61 @@ ScriptAnalysis::printTypes(JSContext *cx
             } else {
                 compartment->typeCounts[typeCount-1]++;
             }
         }
     }
 
 #ifdef DEBUG
 
-    if (script->function())
+    if (script_->function())
         printf("Function");
-    else if (script->isCachedEval)
+    else if (script_->isCachedEval)
         printf("Eval");
     else
         printf("Main");
-    printf(" #%u %s (line %d):\n", script->id(), script->filename, script->lineno);
+    printf(" #%u %s (line %d):\n", script_->id(), script_->filename, script_->lineno);
 
     printf("locals:");
     printf("\n    return:");
-    TypeScript::ReturnTypes(script)->print(cx);
+    TypeScript::ReturnTypes(script_)->print(cx);
     printf("\n    this:");
-    TypeScript::ThisTypes(script)->print(cx);
-
-    for (unsigned i = 0; script->function() && i < script->function()->nargs; i++) {
+    TypeScript::ThisTypes(script_)->print(cx);
+
+    for (unsigned i = 0; script_->function() && i < script_->function()->nargs; i++) {
         printf("\n    arg%u:", i);
-        TypeScript::ArgTypes(script, i)->print(cx);
-    }
-    for (unsigned i = 0; i < script->nfixed; i++) {
-        if (!trackSlot(LocalSlot(script, i))) {
+        TypeScript::ArgTypes(script_, i)->print(cx);
+    }
+    for (unsigned i = 0; i < script_->nfixed; i++) {
+        if (!trackSlot(LocalSlot(script_, i))) {
             printf("\n    local%u:", i);
-            TypeScript::LocalTypes(script, i)->print(cx);
+            TypeScript::LocalTypes(script_, i)->print(cx);
         }
     }
     printf("\n");
 
-    for (unsigned offset = 0; offset < script->length; offset++) {
+    for (unsigned offset = 0; offset < script_->length; offset++) {
         if (!maybeCode(offset))
             continue;
 
-        jsbytecode *pc = script->code + offset;
-
-        PrintBytecode(cx, script, pc);
+        jsbytecode *pc = script_->code + offset;
+
+        PrintBytecode(cx, script_, pc);
 
         if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
             continue;
 
         if (js_CodeSpec[*pc].format & JOF_TYPESET) {
-            TypeSet *types = script->analysis()->bytecodeTypes(pc);
-            printf("  typeset %d:", (int) (types - script->types->typeArray()));
+            TypeSet *types = script_->analysis()->bytecodeTypes(pc);
+            printf("  typeset %d:", (int) (types - script_->types->typeArray()));
             types->print(cx);
             printf("\n");
         }
 
-        unsigned defCount = GetDefCount(script, offset);
+        unsigned defCount = GetDefCount(script_, offset);
         for (unsigned i = 0; i < defCount; i++) {
             printf("  type %d:", i);
             pushedTypes(offset, i)->print(cx);
             printf("\n");
         }
 
         if (getCode(offset).monitoredTypes)
             printf("  monitored\n");
@@ -5081,17 +5103,17 @@ namespace js {
 namespace types {
 
 void
 MarkIteratorUnknownSlow(JSContext *cx)
 {
     /* Check whether we are actually at an ITER opcode. */
 
     jsbytecode *pc;
-    JSScript *script = cx->stack.currentScript(&pc);
+    RootedScript script(cx, cx->stack.currentScript(&pc));
     if (!script || !pc)
         return;
 
     if (JSOp(*pc) != JSOP_ITER)
         return;
 
     AutoEnterTypeInference enter(cx);
 
@@ -5139,17 +5161,17 @@ MarkIteratorUnknownSlow(JSContext *cx)
             analysis->pushedTypes(pc, 0)->addType(cx, Type::UnknownType());
     }
 }
 
 void
 TypeMonitorCallSlow(JSContext *cx, HandleObject callee, const CallArgs &args, bool constructing)
 {
     unsigned nargs = callee->toFunction()->nargs;
-    JSScript *script = callee->toFunction()->script();
+    RootedScript script(cx, callee->toFunction()->script());
 
     if (!constructing)
         TypeScript::SetThis(cx, script, args.thisv());
 
     /*
      * Add constraints going up to the minimum of the actual and formal count.
      * If there are more actuals than formals the later values can only be
      * accessed through the arguments object, which is monitored.
@@ -5169,17 +5191,17 @@ IsAboutToBeFinalized(TypeObjectKey *key)
     /* Mask out the low bit indicating whether this is a type or JS object. */
     gc::Cell *tmp = reinterpret_cast<gc::Cell *>(uintptr_t(key) & ~1);
     bool isMarked = IsCellMarked(&tmp);
     JS_ASSERT(tmp == reinterpret_cast<gc::Cell *>(uintptr_t(key) & ~1));
     return !isMarked;
 }
 
 void
-TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, Type type)
+TypeDynamicResult(JSContext *cx, HandleScript script, jsbytecode *pc, Type type)
 {
     JS_ASSERT(cx->typeInferenceEnabled());
     AutoEnterTypeInference enter(cx);
 
     /* Directly update associated type sets for applicable bytecodes. */
     if (js_CodeSpec[*pc].format & JOF_TYPESET) {
         if (!script->ensureRanAnalysis(cx)) {
             cx->compartment->types.setPendingNukeTypes(cx);
@@ -5307,17 +5329,17 @@ TypeDynamicResult(JSContext *cx, JSScrip
 
     if (script->hasAnalysis() && script->analysis()->ranInference()) {
         TypeSet *pushed = script->analysis()->pushedTypes(pc, 0);
         pushed->addType(cx, type);
     }
 }
 
 void
-TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
+TypeMonitorResult(JSContext *cx, HandleScript script, jsbytecode *pc, const js::Value &rval)
 {
     /* Allow the non-TYPESET scenario to simplify stubs used in compound opcodes. */
     if (!(js_CodeSpec[*pc].format & JOF_TYPESET))
         return;
 
     AutoEnterTypeInference enter(cx);
 
     if (!script->ensureRanAnalysis(cx)) {
@@ -5488,19 +5510,19 @@ JSScript::makeAnalysis(JSContext *cx)
 
     AutoEnterAnalysis enter(cx);
 
     types->analysis = cx->analysisLifoAlloc().new_<ScriptAnalysis>(this);
 
     if (!types->analysis)
         return false;
 
-    Rooted<JSScript*> self(cx, this);
-
-    types->analysis->analyzeBytecode(cx);
+    RootedScript self(cx, this);
+
+    self->types->analysis->analyzeBytecode(cx);
 
     if (self->types->analysis->OOM()) {
         self->types->analysis = NULL;
         return false;
     }
 
     return true;
 }
@@ -5533,17 +5555,17 @@ JSFunction::setTypeForScriptedFunction(J
     }
 
     return true;
 }
 
 #ifdef DEBUG
 
 /* static */ void
-TypeScript::CheckBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value *sp)
+TypeScript::CheckBytecode(JSContext *cx, HandleScript script, jsbytecode *pc, const js::Value *sp)
 {
     AutoEnterTypeInference enter(cx);
 
     if (js_CodeSpec[*pc].format & JOF_DECOMPOSE)
         return;
 
     if (!script->hasAnalysis() || !script->analysis()->ranInference())
         return;
@@ -5676,18 +5698,17 @@ JSObject::makeLazyType(JSContext *cx)
     AutoEnterTypeInference enter(cx);
 
     /* Fill in the type according to the state of this object. */
 
     type->singleton = self;
 
     if (self->isFunction() && self->toFunction()->isInterpreted()) {
         type->interpretedFunction = self->toFunction();
-        JSScript *script = type->interpretedFunction->script();
-        if (script->uninlineable)
+        if (type->interpretedFunction->script()->uninlineable)
             type->flags |= OBJECT_FLAG_UNINLINEABLE;
     }
 
     if (self->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
         type->flags |= OBJECT_FLAG_ITERATED;
 
 #if JS_HAS_XML_SUPPORT
     /*
@@ -6211,17 +6232,17 @@ TypeCompartment::~TypeCompartment()
     if (objectTypeTable)
         js_delete(objectTypeTable);
 
     if (allocationSiteTable)
         js_delete(allocationSiteTable);
 }
 
 /* static */ void
-TypeScript::Sweep(FreeOp *fop, JSScript *script)
+TypeScript::Sweep(FreeOp *fop, RawScript script)
 {
     JSCompartment *compartment = script->compartment();
     JS_ASSERT(compartment->types.inferenceEnabled);
 
     unsigned num = NumTypeSets(script);
     TypeSet *typeArray = script->types->typeArray();
 
     /* Remove constraints and references to dead objects from the persistent type sets. */
@@ -6258,17 +6279,17 @@ TypeScript::destroy()
         js_delete(dynamicList);
         dynamicList = next;
     }
 
     js_free(this);
 }
 
 /* static */ void
-TypeScript::AddFreezeConstraints(JSContext *cx, JSScript *script)
+TypeScript::AddFreezeConstraints(JSContext *cx, HandleScript script)
 {
     /*
      * Adding freeze constraints to a script ensures that code for the script
      * will be recompiled any time any type set for stack values in the script
      * change: these type sets are implicitly frozen during compilation.
      *
      * To ensure this occurs, we don't need to add freeze constraints to the
      * type sets for every stack value, but rather only the input type sets
@@ -6291,17 +6312,17 @@ TypeScript::AddFreezeConstraints(JSConte
         if (types == returnTypes)
             continue;
         JS_ASSERT(types->constraintsPurged());
         types->add(cx, cx->analysisLifoAlloc().new_<TypeConstraintFreezeStack>(script), false);
     }
 }
 
 /* static */ void
-TypeScript::Purge(JSContext *cx, JSScript *script)
+TypeScript::Purge(JSContext *cx, HandleScript script)
 {
     if (!script->types)
         return;
 
     unsigned num = NumTypeSets(script);
     TypeSet *typeArray = script->types->typeArray();
     TypeSet *returnTypes = ReturnTypes(script);
 
@@ -6357,17 +6378,17 @@ TypeCompartment::maybePurgeAnalysis(JSCo
     AutoEnterTypeInference enter(cx);
 
     /* Reset the analysis pool, making its memory available for reuse. */
     cx->compartment->analysisLifoAlloc.releaseAll();
 
     uint64_t start = PRMJ_Now();
 
     for (gc::CellIter i(cx->compartment, gc::FINALIZE_SCRIPT); !i.done(); i.next()) {
-        JSScript *script = i.get<JSScript>();
+        RootedScript script(cx, i.get<JSScript>());
         TypeScript::Purge(cx, script);
     }
 
     uint64_t done = PRMJ_Now();
 
     if (cx->runtime->analysisPurgeCallback) {
         size_t afterUsed = cx->compartment->analysisLifoAlloc.used();
         size_t typeUsed = cx->compartment->typeLifoAlloc.used();
@@ -6423,17 +6444,17 @@ TypeObject::computedSizeOfExcludingThis(
         if (prop)
             bytes += sizeof(Property) + prop->types.computedSizeOfExcludingThis();
     }
 
     return bytes;
 }
 
 static void
-SizeOfScriptTypeInferenceData(JSScript *script, TypeInferenceSizes *sizes,
+SizeOfScriptTypeInferenceData(RawScript script, TypeInferenceSizes *sizes,
                               JSMallocSizeOfFun mallocSizeOf)
 {
     TypeScript *typeScript = script->types;
     if (!typeScript)
         return;
 
     /* If TI is disabled, a single TypeScript is still present. */
     if (!script->compartment()->types.inferenceEnabled) {
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -535,29 +535,29 @@ class StackTypeSet : public TypeSet
      * Make a type set with the specified debugging name, not embedded in
      * another structure.
      */
     static StackTypeSet *make(JSContext *cx, const char *name);
 
     /* Constraints for type inference. */
 
     void addSubset(JSContext *cx, TypeSet *target);
-    void addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
+    void addGetProperty(JSContext *cx, HandleScript script, jsbytecode *pc,
                         StackTypeSet *target, jsid id);
-    void addSetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
+    void addSetProperty(JSContext *cx, HandleScript script, jsbytecode *pc,
                         StackTypeSet *target, jsid id);
-    void addSetElement(JSContext *cx, JSScript *script, jsbytecode *pc,
+    void addSetElement(JSContext *cx, HandleScript script, jsbytecode *pc,
                        StackTypeSet *objectTypes, StackTypeSet *valueTypes);
     void addCall(JSContext *cx, TypeCallsite *site);
-    void addArith(JSContext *cx, JSScript *script, jsbytecode *pc,
+    void addArith(JSContext *cx, HandleScript script, jsbytecode *pc,
                   TypeSet *target, TypeSet *other = NULL);
-    void addTransformThis(JSContext *cx, JSScript *script, TypeSet *target);
-    void addPropagateThis(JSContext *cx, JSScript *script, jsbytecode *pc,
+    void addTransformThis(JSContext *cx, HandleScript script, TypeSet *target);
+    void addPropagateThis(JSContext *cx, HandleScript script, jsbytecode *pc,
                           Type type, StackTypeSet *types = NULL);
-    void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
+    void addSubsetBarrier(JSContext *cx, HandleScript script, jsbytecode *pc, TypeSet *target);
 
     /*
      * Constraints for JIT compilation.
      *
      * Methods for JIT compilation. These must be used when a script is
      * currently being compiled (see AutoEnterCompilation) and will add
      * constraints ensuring that if the return value change in the future due
      * to new type information, the script's jitcode will be discarded.
@@ -601,28 +601,27 @@ class StackTypeSet : public TypeSet
  */
 class HeapTypeSet : public TypeSet
 {
   public:
 
     /* Constraints for type inference. */
 
     void addSubset(JSContext *cx, TypeSet *target);
-    void addGetProperty(JSContext *cx, JSScript *script, jsbytecode *pc,
+    void addGetProperty(JSContext *cx, HandleScript script, jsbytecode *pc,
                         StackTypeSet *target, jsid id);
-    void addCallProperty(JSContext *cx, JSScript *script, jsbytecode *pc, jsid id);
+    void addCallProperty(JSContext *cx, HandleScript script, jsbytecode *pc, jsid id);
     void addFilterPrimitives(JSContext *cx, TypeSet *target);
-    void addSubsetBarrier(JSContext *cx, JSScript *script, jsbytecode *pc, TypeSet *target);
+    void addSubsetBarrier(JSContext *cx, HandleScript script, jsbytecode *pc, TypeSet *target);
 
     /* Constraints for JIT compilation. */
 
     /* Completely freeze the contents of this type set. */
     void addFreeze(JSContext *cx);
 
-
     /*
      * Watch for a generic object state change on a type object. This currently
      * includes reallocations of slot pointers for global objects, and changes
      * to newScript data on types.
      */
     static void WatchObjectStateChange(JSContext *cx, TypeObject *object);
 
     /* Whether an object has any of a set of flags. */
@@ -1049,28 +1048,28 @@ struct TypeObjectEntry
 
     static inline HashNumber hash(TaggedProto base);
     static inline bool match(TypeObject *key, TaggedProto lookup);
 };
 typedef HashSet<ReadBarriered<TypeObject>, TypeObjectEntry, SystemAllocPolicy> TypeObjectSet;
 
 /* Whether to use a new type object when calling 'new' at script/pc. */
 bool
-UseNewType(JSContext *cx, JSScript *script, jsbytecode *pc);
+UseNewType(JSContext *cx, HandleScript script, jsbytecode *pc);
 
 /* Whether to use a new type object for an initializer opcode at script/pc. */
 bool
-UseNewTypeForInitializer(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey key);
+UseNewTypeForInitializer(JSContext *cx, HandleScript script, jsbytecode *pc, JSProtoKey key);
 
 /*
  * Whether Array.prototype, or an object on its proto chain, has an
  * indexed property.
  */
 bool
-ArrayPrototypeHasIndexedProperty(JSContext *cx, JSScript *script);
+ArrayPrototypeHasIndexedProperty(JSContext *cx, HandleScript script);
 
 /*
  * Type information about a callsite. this is separated from the bytecode
  * information itself so we can handle higher order functions not called
  * directly via a bytecode.
  */
 struct TypeCallsite
 {
@@ -1111,76 +1110,80 @@ class TypeScript
      * Generated the first time the script is analyzed by inference and kept
      * after analysis purges.
      */
     HeapTypeSet *propertyReadTypes;
 
     /* Array of type type sets for variables and JOF_TYPESET ops. */
     TypeSet *typeArray() { return (TypeSet *) (uintptr_t(this) + sizeof(TypeScript)); }
 
-    static inline unsigned NumTypeSets(JSScript *script);
+    static inline unsigned NumTypeSets(RawScript script);
 
-    static inline HeapTypeSet  *ReturnTypes(JSScript *script);
-    static inline StackTypeSet *ThisTypes(JSScript *script);
-    static inline StackTypeSet *ArgTypes(JSScript *script, unsigned i);
-    static inline StackTypeSet *LocalTypes(JSScript *script, unsigned i);
+    static inline HeapTypeSet  *ReturnTypes(RawScript script);
+    static inline StackTypeSet *ThisTypes(RawScript script);
+    static inline StackTypeSet *ArgTypes(RawScript script, unsigned i);
+    static inline StackTypeSet *LocalTypes(RawScript script, unsigned i);
 
     /* Follows slot layout in jsanalyze.h, can get this/arg/local type sets. */
-    static inline StackTypeSet *SlotTypes(JSScript *script, unsigned slot);
+    static inline StackTypeSet *SlotTypes(RawScript script, unsigned slot);
 
 #ifdef DEBUG
     /* Check that correct types were inferred for the values pushed by this bytecode. */
-    static void CheckBytecode(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value *sp);
+    static void CheckBytecode(JSContext *cx, HandleScript script, jsbytecode *pc,
+                              const js::Value *sp);
 #endif
 
     /* Get the default 'new' object for a given standard class, per the script's global. */
-    static inline TypeObject *StandardType(JSContext *cx, JSScript *script, JSProtoKey kind);
+    static inline TypeObject *StandardType(JSContext *cx, HandleScript script, JSProtoKey kind);
 
     /* Get a type object for an allocation site in this script. */
-    static inline TypeObject *InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind);
+    static inline TypeObject *InitObject(JSContext *cx, HandleScript script, jsbytecode *pc,
+                                         JSProtoKey kind);
 
     /*
      * Monitor a bytecode pushing a value which is not accounted for by the
      * inference type constraints, such as integer overflow.
      */
-    static inline void MonitorOverflow(JSContext *cx, JSScript *script, jsbytecode *pc);
-    static inline void MonitorString(JSContext *cx, JSScript *script, jsbytecode *pc);
-    static inline void MonitorUnknown(JSContext *cx, JSScript *script, jsbytecode *pc);
+    static inline void MonitorOverflow(JSContext *cx, HandleScript script, jsbytecode *pc);
+    static inline void MonitorString(JSContext *cx, HandleScript script, jsbytecode *pc);
+    static inline void MonitorUnknown(JSContext *cx, HandleScript script, jsbytecode *pc);
 
-    static inline void GetPcScript(JSContext *cx, JSScript **script, jsbytecode **pc);
+    static inline void GetPcScript(JSContext *cx, MutableHandleScript script, jsbytecode **pc);
     static inline void MonitorOverflow(JSContext *cx);
     static inline void MonitorString(JSContext *cx);
     static inline void MonitorUnknown(JSContext *cx);
 
     /*
      * Monitor a bytecode pushing any value. This must be called for any opcode
      * which is JOF_TYPESET, and where either the script has not been analyzed
      * by type inference or where the pc has type barriers. For simplicity, we
      * always monitor JOF_TYPESET opcodes in the interpreter and stub calls,
      * and only look at barriers when generating JIT code for the script.
      */
-    static inline void Monitor(JSContext *cx, JSScript *script, jsbytecode *pc,
+    static inline void Monitor(JSContext *cx, HandleScript script, jsbytecode *pc,
                                const js::Value &val);
     static inline void Monitor(JSContext *cx, const js::Value &rval);
 
     /* Monitor an assignment at a SETELEM on a non-integer identifier. */
     static inline void MonitorAssign(JSContext *cx, HandleObject obj, jsid id);
 
     /* Add a type for a variable in a script. */
-    static inline void SetThis(JSContext *cx, JSScript *script, Type type);
-    static inline void SetThis(JSContext *cx, JSScript *script, const js::Value &value);
-    static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type);
-    static inline void SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::Value &value);
-    static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type);
-    static inline void SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value);
+    static inline void SetThis(JSContext *cx, HandleScript script, Type type);
+    static inline void SetThis(JSContext *cx, HandleScript script, const js::Value &value);
+    static inline void SetLocal(JSContext *cx, HandleScript script, unsigned local, Type type);
+    static inline void SetLocal(JSContext *cx, HandleScript script, unsigned local,
+                                const js::Value &value);
+    static inline void SetArgument(JSContext *cx, HandleScript script, unsigned arg, Type type);
+    static inline void SetArgument(JSContext *cx, HandleScript script, unsigned arg,
+                                   const js::Value &value);
 
-    static void AddFreezeConstraints(JSContext *cx, JSScript *script);
-    static void Purge(JSContext *cx, JSScript *script);
+    static void AddFreezeConstraints(JSContext *cx, HandleScript script);
+    static void Purge(JSContext *cx, HandleScript script);
 
-    static void Sweep(FreeOp *fop, JSScript *script);
+    static void Sweep(FreeOp *fop, RawScript script);
     void destroy();
 };
 
 struct ArrayTableKey;
 typedef HashMap<ArrayTableKey,ReadBarriered<TypeObject>,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
 
 struct ObjectTableKey;
 struct ObjectTableEntry;
@@ -1343,20 +1346,20 @@ struct TypeCompartment
     void processPendingRecompiles(FreeOp *fop);
 
     /* Mark all types as needing destruction once inference has 'finished'. */
     void setPendingNukeTypes(JSContext *cx);
     void setPendingNukeTypesNoReport();
 
     /* Mark a script as needing recompilation once inference has finished. */
     void addPendingRecompile(JSContext *cx, const RecompileInfo &info);
-    void addPendingRecompile(JSContext *cx, JSScript *script, jsbytecode *pc);
+    void addPendingRecompile(JSContext *cx, HandleScript script, jsbytecode *pc);
 
     /* Monitor future effects on a bytecode. */
-    void monitorBytecode(JSContext *cx, JSScript *script, uint32_t offset,
+    void monitorBytecode(JSContext *cx, HandleScript script, uint32_t offset,
                          bool returnOnly = false);
 
     /* Mark any type set containing obj as having a generic object type. */
     void markSetsUnknown(JSContext *cx, TypeObject *obj);
 
     void sweep(FreeOp *fop);
     void sweepCompilerOutputs(FreeOp *fop, bool discardConstraints);
 
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -474,17 +474,17 @@ GetTypeNewObject(JSContext *cx, JSProtoK
 }
 
 /* Get a type object for the immediate allocation site within a native. */
 inline TypeObject *
 GetTypeCallerInitObject(JSContext *cx, JSProtoKey key)
 {
     if (cx->typeInferenceEnabled()) {
         jsbytecode *pc;
-        JSScript *script = cx->stack.currentScript(&pc);
+        RootedScript script(cx, cx->stack.currentScript(&pc));
         if (script)
             return TypeScript::InitObject(cx, script, pc, key);
     }
     return GetTypeNewObject(cx, key);
 }
 
 /*
  * When using a custom iterator within the initialization of a 'for in' loop,
@@ -631,24 +631,30 @@ FixArrayType(JSContext *cx, HandleObject
 inline void
 FixObjectType(JSContext *cx, HandleObject obj)
 {
     if (cx->typeInferenceEnabled())
         cx->compartment->types.fixObjectType(cx, obj);
 }
 
 /* Interface helpers for JSScript */
-extern void TypeMonitorResult(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval);
-extern void TypeDynamicResult(JSContext *cx, JSScript *script, jsbytecode *pc, js::types::Type type);
+extern void TypeMonitorResult(JSContext *cx, HandleScript script, jsbytecode *pc,
+                              const js::Value &rval);
+extern void TypeDynamicResult(JSContext *cx, HandleScript script, jsbytecode *pc,
+                              js::types::Type type);
 
 inline bool
 UseNewTypeAtEntry(JSContext *cx, StackFrame *fp)
 {
-    return fp->isConstructing() && cx->typeInferenceEnabled() &&
-           fp->prev() && UseNewType(cx, fp->prev()->script(), fp->prevpc());
+
+    if (!fp->isConstructing() || !cx->typeInferenceEnabled() || !fp->prev())
+        return false;
+
+    RootedScript prevScript(cx, fp->prev()->script());
+    return UseNewType(cx, prevScript, fp->prevpc());
 }
 
 inline bool
 UseNewTypeForClone(JSFunction *fun)
 {
     if (fun->hasSingletonType() || !fun->isInterpreted())
         return false;
 
@@ -671,17 +677,17 @@ UseNewTypeForClone(JSFunction *fun)
      *
      * Each instance of the innermost function will have a different wrapped
      * initialize method. We capture this, along with similar cases, by looking
      * for short scripts which use both .apply and arguments. For such scripts,
      * whenever creating a new instance of the function we both give that
      * instance a singleton type and clone the underlying script.
      */
 
-    JSScript *script = fun->script();
+    RawScript script = fun->script();
 
     if (script->length >= 50)
         return false;
 
     if (script->hasConsts() || script->hasObjects() || script->hasRegexps() || fun->isHeavyweight())
         return false;
 
     bool hasArguments = false;
@@ -700,67 +706,67 @@ UseNewTypeForClone(JSFunction *fun)
     return hasArguments && hasApply;
 }
 
 /////////////////////////////////////////////////////////////////////
 // Script interface functions
 /////////////////////////////////////////////////////////////////////
 
 /* static */ inline unsigned
-TypeScript::NumTypeSets(JSScript *script)
+TypeScript::NumTypeSets(RawScript script)
 {
     return script->nTypeSets + analyze::TotalSlots(script);
 }
 
 /* static */ inline HeapTypeSet *
-TypeScript::ReturnTypes(JSScript *script)
+TypeScript::ReturnTypes(RawScript script)
 {
     TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::CalleeSlot();
     return types->toHeapTypeSet();
 }
 
 /* static */ inline StackTypeSet *
-TypeScript::ThisTypes(JSScript *script)
+TypeScript::ThisTypes(RawScript script)
 {
     TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ThisSlot();
     return types->toStackTypeSet();
 }
 
 /*
  * Note: for non-escaping arguments and locals, argTypes/localTypes reflect
  * only the initial type of the variable (e.g. passed values for argTypes,
  * or undefined for localTypes) and not types from subsequent assignments.
  */
 
 /* static */ inline StackTypeSet *
-TypeScript::ArgTypes(JSScript *script, unsigned i)
+TypeScript::ArgTypes(RawScript script, unsigned i)
 {
     JS_ASSERT(i < script->function()->nargs);
     TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::ArgSlot(i);
     return types->toStackTypeSet();
 }
 
 /* static */ inline StackTypeSet *
-TypeScript::LocalTypes(JSScript *script, unsigned i)
+TypeScript::LocalTypes(RawScript script, unsigned i)
 {
     JS_ASSERT(i < script->nfixed);
     TypeSet *types = script->types->typeArray() + script->nTypeSets + js::analyze::LocalSlot(script, i);
     return types->toStackTypeSet();
 }
 
 /* static */ inline StackTypeSet *
-TypeScript::SlotTypes(JSScript *script, unsigned slot)
+TypeScript::SlotTypes(RawScript script, unsigned slot)
 {
     JS_ASSERT(slot < js::analyze::TotalSlots(script));
     TypeSet *types = script->types->typeArray() + script->nTypeSets + slot;
     return types->toStackTypeSet();
 }
 
 /* static */ inline TypeObject *
-TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key)
+TypeScript::StandardType(JSContext *cx, HandleScript script, JSProtoKey key)
 {
     js::RootedObject proto(cx);
     if (!js_GetClassPrototype(cx, key, &proto, NULL))
         return NULL;
     return proto->getNewType(cx);
 }
 
 struct AllocationSiteKey {
@@ -780,17 +786,17 @@ struct AllocationSiteKey {
     }
 
     static inline bool match(const AllocationSiteKey &a, const AllocationSiteKey &b) {
         return a.script == b.script && a.offset == b.offset && a.kind == b.kind;
     }
 };
 
 /* static */ inline TypeObject *
-TypeScript::InitObject(JSContext *cx, JSScript *script, jsbytecode *pc, JSProtoKey kind)
+TypeScript::InitObject(JSContext *cx, HandleScript script, jsbytecode *pc, JSProtoKey kind)
 {
     JS_ASSERT(!UseNewTypeForInitializer(cx, script, pc, kind));
 
     /* :XXX: Limit script->length so we don't need to check the offset up front? */
     uint32_t offset = pc - script->code;
 
     if (!cx->typeInferenceEnabled() || !script->compileAndGo || offset >= AllocationSiteKey::OFFSET_LIMIT)
         return GetTypeNewObject(cx, kind);
@@ -836,87 +842,87 @@ SetInitializerObjectType(JSContext *cx, 
             return false;
         obj->setType(type);
     }
 
     return true;
 }
 
 /* static */ inline void
-TypeScript::Monitor(JSContext *cx, JSScript *script, jsbytecode *pc, const js::Value &rval)
+TypeScript::Monitor(JSContext *cx, HandleScript script, jsbytecode *pc, const js::Value &rval)
 {
     if (cx->typeInferenceEnabled())
         TypeMonitorResult(cx, script, pc, rval);
 }
 
 /* static */ inline void
-TypeScript::MonitorOverflow(JSContext *cx, JSScript *script, jsbytecode *pc)
+TypeScript::MonitorOverflow(JSContext *cx, HandleScript script, jsbytecode *pc)
 {
     if (cx->typeInferenceEnabled())
         TypeDynamicResult(cx, script, pc, Type::DoubleType());
 }
 
 /* static */ inline void
-TypeScript::MonitorString(JSContext *cx, JSScript *script, jsbytecode *pc)
+TypeScript::MonitorString(JSContext *cx, HandleScript script, jsbytecode *pc)
 {
     if (cx->typeInferenceEnabled())
         TypeDynamicResult(cx, script, pc, Type::StringType());
 }
 
 /* static */ inline void
-TypeScript::MonitorUnknown(JSContext *cx, JSScript *script, jsbytecode *pc)
+TypeScript::MonitorUnknown(JSContext *cx, HandleScript script, jsbytecode *pc)
 {
     if (cx->typeInferenceEnabled())
         TypeDynamicResult(cx, script, pc, Type::UnknownType());
 }
 
 /* static */ inline void
-TypeScript::GetPcScript(JSContext *cx, JSScript **script, jsbytecode **pc)
+TypeScript::GetPcScript(JSContext *cx, MutableHandleScript script, jsbytecode **pc)
 {
 #ifdef JS_ION
     if (cx->fp()->beginsIonActivation()) {
         ion::GetPcScript(cx, script, pc);
         return;
     }
 #endif
-    *script = cx->fp()->script();
+    script.set(cx->fp()->script());
     *pc = cx->regs().pc;
 }
 
 /* static */ inline void
 TypeScript::MonitorOverflow(JSContext *cx)
 {
-    JSScript *script;
+    RootedScript script(cx);
     jsbytecode *pc;
     GetPcScript(cx, &script, &pc);
     MonitorOverflow(cx, script, pc);
 }
 
 /* static */ inline void
 TypeScript::MonitorString(JSContext *cx)
 {
-    JSScript *script;
+    RootedScript script(cx);
     jsbytecode *pc;
     GetPcScript(cx, &script, &pc);
     MonitorString(cx, script, pc);
 }
 
 /* static */ inline void
 TypeScript::MonitorUnknown(JSContext *cx)
 {
-    JSScript *script;
+    RootedScript script(cx);
     jsbytecode *pc;
     GetPcScript(cx, &script, &pc);
     MonitorUnknown(cx, script, pc);
 }
 
 /* static */ inline void
 TypeScript::Monitor(JSContext *cx, const js::Value &rval)
 {
-    JSScript *script;
+    RootedScript script(cx);
     jsbytecode *pc;
     GetPcScript(cx, &script, &pc);
     Monitor(cx, script, pc, rval);
 }
 
 /* static */ inline void
 TypeScript::MonitorAssign(JSContext *cx, HandleObject obj, jsid id)
 {
@@ -932,17 +938,17 @@ TypeScript::MonitorAssign(JSContext *cx,
         uint32_t i;
         if (js_IdIsIndex(id, &i))
             return;
         MarkTypeObjectUnknownProperties(cx, obj->type());
     }
 }
 
 /* static */ inline void
-TypeScript::SetThis(JSContext *cx, JSScript *script, Type type)
+TypeScript::SetThis(JSContext *cx, HandleScript script, Type type)
 {
     if (!cx->typeInferenceEnabled())
         return;
     JS_ASSERT(script->types);
 
     /* Analyze the script regardless if -a was used. */
     bool analyze = cx->hasRunOption(JSOPTION_METHODJIT_ALWAYS);
 
@@ -954,65 +960,65 @@ TypeScript::SetThis(JSContext *cx, JSScr
         ThisTypes(script)->addType(cx, type);
 
         if (analyze)
             script->ensureRanInference(cx);
     }
 }
 
 /* static */ inline void
-TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value)
+TypeScript::SetThis(JSContext *cx, HandleScript script, const js::Value &value)
 {
     if (cx->typeInferenceEnabled())
         SetThis(cx, script, GetValueType(cx, value));
 }
 
 /* static */ inline void
-TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, Type type)
+TypeScript::SetLocal(JSContext *cx, HandleScript script, unsigned local, Type type)
 {
     if (!cx->typeInferenceEnabled())
         return;
     JS_ASSERT(script->types);
 
     if (!LocalTypes(script, local)->hasType(type)) {
         AutoEnterTypeInference enter(cx);
 
         InferSpew(ISpewOps, "externalType: setLocal #%u %u: %s",
                   script->id(), local, TypeString(type));
         LocalTypes(script, local)->addType(cx, type);
     }
 }
 
 /* static */ inline void
-TypeScript::SetLocal(JSContext *cx, JSScript *script, unsigned local, const js::Value &value)
+TypeScript::SetLocal(JSContext *cx, HandleScript script, unsigned local, const js::Value &value)
 {
     if (cx->typeInferenceEnabled()) {
         Type type = GetValueType(cx, value);
         SetLocal(cx, script, local, type);
     }
 }
 
 /* static */ inline void
-TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, Type type)
+TypeScript::SetArgument(JSContext *cx, HandleScript script, unsigned arg, Type type)
 {
     if (!cx->typeInferenceEnabled())
         return;
     JS_ASSERT(script->types);
 
     if (!ArgTypes(script, arg)->hasType(type)) {
         AutoEnterTypeInference enter(cx);
 
         InferSpew(ISpewOps, "externalType: setArg #%u %u: %s",
                   script->id(), arg, TypeString(type));
         ArgTypes(script, arg)->addType(cx, type);
     }
 }
 
 /* static */ inline void
-TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value)
+TypeScript::SetArgument(JSContext *cx, HandleScript script, unsigned arg, const js::Value &value)
 {
     if (cx->typeInferenceEnabled()) {
         Type type = GetValueType(cx, value);
         SetArgument(cx, script, arg, type);
     }
 }
 
 /////////////////////////////////////////////////////////////////////
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -1043,21 +1043,23 @@ IteratorNext(JSContext *cx, HandleObject
     return js_IteratorNext(cx, iterobj, rval);
 }
 
 /*
  * For bytecodes which push values and then fall through, make sure the
  * types of the pushed values are consistent with type inference information.
  */
 static inline void
-TypeCheckNextBytecode(JSContext *cx, JSScript *script, unsigned n, const FrameRegs &regs)
+TypeCheckNextBytecode(JSContext *cx, JSScript *script_, unsigned n, const FrameRegs &regs)
 {
 #ifdef DEBUG
     if (cx->typeInferenceEnabled() &&
-        n == GetBytecodeLength(regs.pc)) {
+        n == GetBytecodeLength(regs.pc))
+    {
+        RootedScript script(cx, script_);
         TypeScript::CheckBytecode(cx, script, regs.pc, regs.sp);
     }
 #endif
 }
 
 JS_NEVER_INLINE InterpretStatus
 js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode)
 {
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -701,17 +701,17 @@ GetObjectElementOperation(JSContext *cx,
             }
             if (!JSObject::getElement(cx, obj, obj, index, res))
                 return false;
         } while(0);
     } else {
         if (!cx->fp()->beginsIonActivation()) {
             // Don't update getStringElement if called from Ion code, since
             // ion::GetPcScript is expensive.
-            JSScript *script;
+            RootedScript script(cx);
             jsbytecode *pc;
             types::TypeScript::GetPcScript(cx, &script, &pc);
 
             if (script->hasAnalysis())
                 script->analysis()->getCode(pc).getStringElement = true;
         }
 
         SpecialId special;
@@ -804,17 +804,17 @@ SetObjectElementOperation(JSContext *cx,
                         break;
                     if ((uint32_t)i >= obj->getArrayLength())
                         JSObject::setArrayLength(cx, obj, i + 1);
                 }
                 JSObject::setDenseArrayElementWithType(cx, obj, i, value);
                 return true;
             } else {
                 if (!cx->fp()->beginsIonActivation()) {
-                    JSScript *script;
+                    RootedScript script(cx);
                     jsbytecode *pc;
                     types::TypeScript::GetPcScript(cx, &script, &pc);
 
                     if (script->hasAnalysis())
                         script->analysis()->getCode(pc).arrayWriteHole = true;
                 }
             }
         }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2363,18 +2363,20 @@ js_CreateThisForFunctionWithProto(JSCont
         if (!type)
             return NULL;
         res = CreateThisForFunctionWithType(cx, type, callee->getParent());
     } else {
         gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass);
         res = NewObjectWithClassProto(cx, &ObjectClass, proto, callee->getParent(), kind);
     }
 
-    if (res && cx->typeInferenceEnabled())
-        TypeScript::SetThis(cx, callee->toFunction()->script(), types::Type::ObjectType(res));
+    if (res && cx->typeInferenceEnabled()) {
+        RootedScript script(cx, callee->toFunction()->script());
+        TypeScript::SetThis(cx, script, types::Type::ObjectType(res));
+    }
 
     return res;
 }
 
 JSObject *
 js_CreateThisForFunction(JSContext *cx, HandleObject callee, bool newType)
 {
     RootedValue protov(cx);
@@ -2393,17 +2395,17 @@ js_CreateThisForFunction(JSContext *cx, 
         /*
          * Reshape the object and give it a (lazily instantiated) singleton
          * type before passing it as the 'this' value for the call.
          */
         JSObject::clear(cx, nobj);
         if (!JSObject::setSingletonType(cx, nobj))
             return NULL;
 
-        JSScript *calleeScript = callee->toFunction()->script();
+        RootedScript calleeScript(cx, callee->toFunction()->script());
         TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj));
 
         return nobj;
     }
 
     return obj;
 }
 
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -235,16 +235,17 @@ enum ThingRootKind
     THING_ROOT_TYPE_OBJECT,
     THING_ROOT_STRING,
     THING_ROOT_SCRIPT,
     THING_ROOT_XML,
     THING_ROOT_ID,
     THING_ROOT_PROPERTY_ID,
     THING_ROOT_VALUE,
     THING_ROOT_TYPE,
+    THING_ROOT_BINDINGS,
     THING_ROOT_LIMIT
 };
 
 template <typename T>
 struct RootKind;
 
 /*
  * Specifically mark the ThingRootKind of externally visible types, so that
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -88,19 +88,22 @@ Bindings::initWithTemporaryStorage(JSCon
      * aliased variables. While the debugger may observe any scope object at
      * any time, such accesses are mediated by DebugScopeProxy (see
      * DebugScopeProxy::handleUnaliasedAccess).
      */
 
     JS_STATIC_ASSERT(CallObject::RESERVED_SLOTS == 2);
     gc::AllocKind allocKind = gc::FINALIZE_OBJECT2_BACKGROUND;
     JS_ASSERT(gc::GetGCKindSlots(allocKind) == CallObject::RESERVED_SLOTS);
-    self->callObjShape_ =
+    RootedShape initial(cx,
         EmptyShape::getInitialShape(cx, &CallClass, NULL, cx->global(),
-                                    allocKind, BaseShape::VAROBJ | BaseShape::DELEGATE);
+                                    allocKind, BaseShape::VAROBJ | BaseShape::DELEGATE));
+    if (!initial)
+        return false;
+    self->callObjShape_.init(initial);
 
 #ifdef DEBUG
     HashSet<PropertyName *> added(cx);
     if (!added.init())
         return false;
 #endif
 
     BindingIter bi(*self);
@@ -164,16 +167,22 @@ Bindings::clone(JSContext *cx, InternalH
      * the source's bindingArray directly.
      */
     if (!initWithTemporaryStorage(cx, self, src.numArgs(), src.numVars(), src.bindingArray()))
         return false;
     self->switchToScriptStorage(dstPackedBindings);
     return true;
 }
 
+/* static */ Bindings
+RootMethods<Bindings>::initial()
+{
+    return Bindings();
+}
+
 template<XDRMode mode>
 static bool
 XDRScriptBindings(XDRState<mode> *xdr, LifoAllocScope &las, unsigned numArgs, unsigned numVars,
                   HandleScript script)
 {
     JSContext *cx = xdr->cx();
 
     if (mode == XDR_ENCODE) {
@@ -2085,19 +2094,19 @@ js::CloneScript(JSContext *cx, HandleObj
                                  nobjects, nregexps, ntrynotes, nconsts);
 
     uint8_t *data = AllocScriptData(cx, size);
     if (!data)
         return NULL;
 
     /* Bindings */
 
-    Bindings bindings;
+    Rooted<Bindings> bindings(cx);
     InternalHandle<Bindings*> bindingsHandle =
-        InternalHandle<Bindings*>::fromMarkedLocation(&bindings);
+        InternalHandle<Bindings*>::fromMarkedLocation(bindings.address());
     if (!Bindings::clone(cx, bindingsHandle, data, src))
         return NULL;
 
     /* Objects */
 
     AutoObjectVector objects(cx);
     if (nobjects != 0) {
         HeapPtrObject *vector = src->objects()->vector;
@@ -2605,8 +2614,9 @@ JSScript::formalIsAliased(unsigned argSl
     return bindings.bindingIsAliased(argSlot);
 }
 
 bool
 JSScript::formalLivesInArgumentsObject(unsigned argSlot)
 {
     return argsObjAliasesFormals() && !formalIsAliased(argSlot);
 }
+
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -194,16 +194,25 @@ class Bindings
     bool bindingIsAliased(unsigned bindingIndex);
 
     /* Return whether this scope has any aliased bindings. */
     bool hasAnyAliasedBindings() const { return !callObjShape_->isEmptyShape(); }
 
     void trace(JSTracer *trc);
 };
 
+template <>
+struct RootMethods<Bindings> {
+    static Bindings initial();
+    static ThingRootKind kind() { return THING_ROOT_BINDINGS; }
+    static bool poisoned(const Bindings &bindings) {
+        return IsPoisonedPtr(bindings.callObjShape());
+    }
+};
+
 class ScriptCounts
 {
     friend struct ::JSScript;
     friend struct ScriptAndCounts;
     /*
      * This points to a single block that holds an array of PCCounts followed
      * by an array of doubles.  Each element in the PCCounts array has a
      * pointer into the array of doubles.
--- a/js/src/jstypedarray.cpp
+++ b/js/src/jstypedarray.cpp
@@ -546,31 +546,32 @@ ArrayBufferObject::obj_trace(JSTracer *t
 void
 ArrayBufferObject::sweepAll(JSRuntime *rt)
 {
     JSObject *buffer = rt->liveArrayBuffers;
     while (buffer) {
         JSObject **views = GetViewList(&buffer->asArrayBuffer());
         JS_ASSERT(*views);
         JSObject *nextBuffer = BufferLink(*views);
-        SetBufferLink(*views, UNSET_BUFFER_LINK);
 
         // Rebuild the list of views of the ArrayBuffer, discarding dead views
         JSObject *prevLiveView = NULL;
         JSObject *view = *views;
         while (view) {
             JSObject *nextView =
                 static_cast<JSObject*>(view->getFixedSlot(BufferView::NEXT_VIEW_SLOT).toPrivate());
             if (!JS_IsAboutToBeFinalized(view)) {
                 view->setFixedSlot(BufferView::NEXT_VIEW_SLOT, PrivateValue(prevLiveView));
                 prevLiveView = view;
             }
             view = nextView;
         }
         *views = prevLiveView;
+        if (*views)
+            SetBufferLink(*views, UNSET_BUFFER_LINK);
 
         buffer = nextBuffer;
     }
 
     rt->liveArrayBuffers = NULL;
 }
 
 JSBool
--- a/js/src/jsworkers.cpp
+++ b/js/src/jsworkers.cpp
@@ -34,17 +34,17 @@ js::StartOffThreadIonCompile(JSContext *
 /*
  * Move an IonBuilder for which compilation has either finished, failed, or
  * been cancelled into the Ion compartment's finished compilations list.
  * All off thread compilations which are started must eventually be finished.
  */
 static void
 FinishOffThreadIonCompile(ion::IonBuilder *builder)
 {
-    JSCompartment *compartment = builder->script->compartment();
+    JSCompartment *compartment = builder->script()->compartment();
     JS_ASSERT(compartment->rt->workerThreadState->isLocked());
 
     compartment->ionCompartment()->finishedOffThreadCompilations().append(builder);
 }
 
 static inline bool
 CompiledScriptMatches(JSCompartment *compartment, JSScript *script, JSScript *target)
 {
@@ -62,17 +62,17 @@ js::CancelOffThreadIonCompile(JSCompartm
     if (!ion)
         return;
 
     AutoLockWorkerThreadState lock(compartment->rt);
 
     /* Cancel any pending entries for which processing hasn't started. */
     for (size_t i = 0; i < state.ionWorklist.length(); i++) {
         ion::IonBuilder *builder = state.ionWorklist[i];
-        if (CompiledScriptMatches(compartment, script, builder->script)) {
+        if (CompiledScriptMatches(compartment, script, builder->script())) {
             FinishOffThreadIonCompile(builder);
             state.ionWorklist[i--] = state.ionWorklist.back();
             state.ionWorklist.popBack();
         }
     }
 
     /* Wait for in progress entries to finish up. */
     for (size_t i = 0; i < state.numThreads; i++) {
@@ -81,17 +81,17 @@ js::CancelOffThreadIonCompile(JSCompartm
             state.wait(WorkerThreadState::MAIN);
     }
 
     ion::OffThreadCompilationVector &compilations = ion->finishedOffThreadCompilations();
 
     /* Cancel code generation for any completed entries. */
     for (size_t i = 0; i < compilations.length(); i++) {
         ion::IonBuilder *builder = compilations[i];
-        if (CompiledScriptMatches(compartment, script, builder->script)) {
+        if (CompiledScriptMatches(compartment, script, builder->script())) {
             ion::FinishOffThreadBuilder(builder);
             compilations[i--] = compilations.back();
             compilations.popBack();
         }
     }
 }
 
 bool
@@ -256,17 +256,17 @@ WorkerThread::threadLoop()
             if (terminate) {
                 state.unlock();
                 return;
             }
             state.wait(WorkerThreadState::WORKER);
         }
 
         ion::IonBuilder *builder = state.ionWorklist.popCopy();
-        ionScript = builder->script;
+        ionScript = builder->script();
 
         JS_ASSERT(ionScript->ion == ION_COMPILING_SCRIPT);
 
         state.unlock();
 
         {
             ion::IonContext ictx(NULL, ionScript->compartment(), &builder->temp());
             ion::CompileBackEnd(builder);
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -74,17 +74,17 @@ mjit::Compiler::Compiler(JSContext *cx, 
     isConstructing(isConstructing),
     outerChunk(outerJIT()->chunkDescriptor(chunkIndex)),
     ssa(cx, outerScript),
     globalObj(cx, outerScript->compileAndGo ? &outerScript->global() : NULL),
     globalSlots(globalObj ? globalObj->getRawSlots() : NULL),
     sps(&cx->runtime->spsProfiler),
     masm(&sps, &PC),
     frame(cx, *thisFromCtor(), masm, stubcc),
-    a(NULL), outer(NULL), script(NULL), PC(NULL), loop(NULL),
+    a(NULL), outer(NULL), script_(NULL), PC(NULL), loop(NULL),
     inlineFrames(CompilerAllocPolicy(cx, *thisFromCtor())),
     branchPatches(CompilerAllocPolicy(cx, *thisFromCtor())),
 #if defined JS_MONOIC
     getGlobalNames(CompilerAllocPolicy(cx, *thisFromCtor())),
     setGlobalNames(CompilerAllocPolicy(cx, *thisFromCtor())),
     callICs(CompilerAllocPolicy(cx, *thisFromCtor())),
     equalityICs(CompilerAllocPolicy(cx, *thisFromCtor())),
 #endif
@@ -473,34 +473,34 @@ mjit::Compiler::pushActiveFrame(JSScript
         CompileStatus status = prepareInferenceTypes(script, newa);
         if (status != Compile_Okay)
             return status;
     }
 
     if (script != outerScript && !sps.enterInlineFrame())
         return Compile_Error;
 
-    this->script = script;
+    this->script_ = script;
     this->analysis = newAnalysis;
     this->PC = script->code;
     this->a = newa;
 
     return Compile_Okay;
 }
 
 void
 mjit::Compiler::popActiveFrame()
 {
     JS_ASSERT(a->parent);
     a->mainCodeEnd = masm.size();
     a->stubCodeEnd = stubcc.size();
     this->PC = a->parentPC;
     this->a = (ActiveFrame *) a->parent;
-    this->script = a->script;
-    this->analysis = this->script->analysis();
+    this->script_ = a->script;
+    this->analysis = this->script_->analysis();
 
     frame.popActiveFrame();
     sps.leaveInlineFrame();
 }
 
 #define CHECK_STATUS(expr)                                           \
     JS_BEGIN_MACRO                                                   \
         CompileStatus status_ = (expr);                              \
@@ -552,17 +552,17 @@ mjit::Compiler::performCompilation()
             pcLengths = js_pod_calloc<PCLengthEntry>(length);
             if (!pcLengths)
                 return Compile_Error;
         }
 
         if (chunkIndex == 0)
             CHECK_STATUS(generatePrologue());
         else
-            sps.setPushed(script);
+            sps.setPushed(script_);
         CHECK_STATUS(generateMethod());
         if (outerJIT() && chunkIndex == outerJIT()->nchunks - 1)
             CHECK_STATUS(generateEpilogue());
         CHECK_STATUS(finishThisUp());
     }
 
 #ifdef JS_METHODJIT_SPEW
     prof.stop();
@@ -1119,77 +1119,77 @@ CompileStatus
 mjit::Compiler::generatePrologue()
 {
     fastEntryLabel = masm.label();
 
     /*
      * If there is no function, then this can only be called via JaegerShot(),
      * which expects an existing frame to be initialized like the interpreter.
      */
-    if (script->function()) {
+    if (script_->function()) {
         Jump j = masm.jump();
 
         /*
          * Entry point #2: The caller has partially constructed a frame, and
          * either argc >= nargs or the arity check has corrected the frame.
          */
         fastEntryLabel = masm.label();
 
         /* Store this early on so slow paths can access it. */
-        masm.storePtr(ImmPtr(script->function()),
+        masm.storePtr(ImmPtr(script_->function()),
                       Address(JSFrameReg, StackFrame::offsetOfExec()));
 
         {
             /*
              * Entry point #3: The caller has partially constructed a frame,
              * but argc might be != nargs, so an arity check might be called.
              *
              * This loops back to entry point #2.
              */
             arityLabel = stubcc.masm.label();
 
             Jump argMatch = stubcc.masm.branch32(Assembler::Equal, JSParamReg_Argc,
-                                                 Imm32(script->function()->nargs));
+                                                 Imm32(script_->function()->nargs));
 
             if (JSParamReg_Argc != Registers::ArgReg1)
                 stubcc.masm.move(JSParamReg_Argc, Registers::ArgReg1);
 
             /* Slow path - call the arity check function. Returns new fp. */
-            stubcc.masm.storePtr(ImmPtr(script->function()),
+            stubcc.masm.storePtr(ImmPtr(script_->function()),
                                  Address(JSFrameReg, StackFrame::offsetOfExec()));
             OOL_STUBCALL(stubs::FixupArity, REJOIN_NONE);
             stubcc.masm.move(Registers::ReturnReg, JSFrameReg);
             argMatch.linkTo(stubcc.masm.label(), &stubcc.masm);
 
             argsCheckLabel = stubcc.masm.label();
 
             /* Type check the arguments as well. */
             if (cx->typeInferenceEnabled()) {
 #ifdef JS_MONOIC
                 this->argsCheckJump = stubcc.masm.jump();
                 this->argsCheckStub = stubcc.masm.label();
                 this->argsCheckJump.linkTo(this->argsCheckStub, &stubcc.masm);
 #endif
-                stubcc.masm.storePtr(ImmPtr(script->function()),
+                stubcc.masm.storePtr(ImmPtr(script_->function()),
                                      Address(JSFrameReg, StackFrame::offsetOfExec()));
                 OOL_STUBCALL(stubs::CheckArgumentTypes, REJOIN_CHECK_ARGUMENTS);
 #ifdef JS_MONOIC
                 this->argsCheckFallthrough = stubcc.masm.label();
 #endif
             }
 
             stubcc.crossJump(stubcc.masm.jump(), fastEntryLabel);
         }
 
         /*
          * Guard that there is enough stack space. Note we reserve space for
          * any inline frames we end up generating, or a callee's stack frame
          * we write to before the callee checks the stack.
          */
-        uint32_t nvals = VALUES_PER_STACK_FRAME + script->nslots + StackSpace::STACK_JIT_EXTRA;
+        uint32_t nvals = VALUES_PER_STACK_FRAME + script_->nslots + StackSpace::STACK_JIT_EXTRA;
         masm.addPtr(Imm32(nvals * sizeof(Value)), JSFrameReg, Registers::ReturnReg);
         Jump stackCheck = masm.branchPtr(Assembler::AboveOrEqual, Registers::ReturnReg,
                                          FrameAddress(offsetof(VMFrame, stackLimit)));
 
         /*
          * If the stack check fails then we need to either commit more of the
          * reserved stack space or throw an error. Specify that the number of
          * local slots is 0 (instead of the default script->nfixed) since the
@@ -1205,60 +1205,60 @@ mjit::Compiler::generatePrologue()
         markUndefinedLocals();
 
         /*
          * Load the scope chain into the frame if it will be needed by NAME
          * opcodes or by the nesting prologue below. The scope chain is always
          * set for global and eval frames, and will have been set by
          * HeavyweightFunctionPrologue for heavyweight function frames.
          */
-        if (!script->function()->isHeavyweight() && analysis->usesScopeChain()) {
+        if (!script_->function()->isHeavyweight() && analysis->usesScopeChain()) {
             RegisterID t0 = Registers::ReturnReg;
             Jump hasScope = masm.branchTest32(Assembler::NonZero,
                                               FrameFlagsAddress(), Imm32(StackFrame::HAS_SCOPECHAIN));
-            masm.loadPayload(Address(JSFrameReg, StackFrame::offsetOfCallee(script->function())), t0);
+            masm.loadPayload(Address(JSFrameReg, StackFrame::offsetOfCallee(script_->function())), t0);
             masm.loadPtr(Address(t0, JSFunction::offsetOfEnvironment()), t0);
             masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain()));
             hasScope.linkTo(masm.label(), &masm);
         }
 
         /*
          * When 'arguments' is used in the script, it may be optimized away
          * which involves reading from the stack frame directly, including
          * fp->u.nactual. fp->u.nactual is only set when numActual != numFormal,
          * so store 'fp->u.nactual = numFormal' when there is no over/underflow.
          */
-        if (script->argumentsHasVarBinding()) {
+        if (script_->argumentsHasVarBinding()) {
             Jump hasArgs = masm.branchTest32(Assembler::NonZero, FrameFlagsAddress(),
                                              Imm32(StackFrame::UNDERFLOW_ARGS |
                                                    StackFrame::OVERFLOW_ARGS));
-            masm.storePtr(ImmPtr((void *)(size_t) script->function()->nargs),
+            masm.storePtr(ImmPtr((void *)(size_t) script_->function()->nargs),
                           Address(JSFrameReg, StackFrame::offsetOfNumActual()));
             hasArgs.linkTo(masm.label(), &masm);
         }
 
         j.linkTo(masm.label(), &masm);
     }
 
     if (cx->typeInferenceEnabled()) {
 #ifdef DEBUG
-        if (script->function()) {
+        if (script_->function()) {
             prepareStubCall(Uses(0));
             INLINE_STUBCALL(stubs::AssertArgumentTypes, REJOIN_NONE);
         }
 #endif
         ensureDoubleArguments();
     }
 
     /* Inline StackFrame::prologue. */
-    if (script->isActiveEval && script->strictModeCode) {
+    if (script_->isActiveEval && script_->strictModeCode) {
         prepareStubCall(Uses(0));
         INLINE_STUBCALL(stubs::StrictEvalPrologue, REJOIN_EVAL_PROLOGUE);
-    } else if (script->function()) {
-        if (script->function()->isHeavyweight()) {
+    } else if (script_->function()) {
+        if (script_->function()->isHeavyweight()) {
             prepareStubCall(Uses(0));
             INLINE_STUBCALL(stubs::HeavyweightFunctionPrologue, REJOIN_FUNCTION_PROLOGUE);
         }
 
         if (isConstructing && !constructThis())
             return Compile_Error;
     }
 
@@ -1272,28 +1272,28 @@ mjit::Compiler::generatePrologue()
 
     return status;
 }
 
 void
 mjit::Compiler::ensureDoubleArguments()
 {
     /* Convert integer arguments which were inferred as (int|double) to doubles. */
-    for (uint32_t i = 0; script->function() && i < script->function()->nargs; i++) {
+    for (uint32_t i = 0; script_->function() && i < script_->function()->nargs; i++) {
         uint32_t slot = ArgSlot(i);
         if (a->varTypes[slot].getTypeTag() == JSVAL_TYPE_DOUBLE && analysis->trackSlot(slot))
             frame.ensureDouble(frame.getArg(i));
     }
 }
 
 void
 mjit::Compiler::markUndefinedLocal(uint32_t offset, uint32_t i)
 {
     uint32_t depth = ssa.getFrame(a->inlineIndex).depth;
-    uint32_t slot = LocalSlot(script, i);
+    uint32_t slot = LocalSlot(script_, i);
     Address local(JSFrameReg, sizeof(StackFrame) + (depth + i) * sizeof(Value));
     if (!cx->typeInferenceEnabled() || !analysis->trackSlot(slot)) {
         masm.storeValue(UndefinedValue(), local);
     } else {
         Lifetime *lifetime = analysis->liveness(slot).live(offset);
         if (lifetime)
             masm.storeValue(UndefinedValue(), local);
 #ifdef DEBUG
@@ -1305,22 +1305,22 @@ mjit::Compiler::markUndefinedLocal(uint3
 
 void
 mjit::Compiler::markUndefinedLocals()
 {
     /*
      * Set locals to undefined. Skip locals which aren't closed and are known
      * to be defined before used,
      */
-    for (uint32_t i = 0; i < script->nfixed; i++)
+    for (uint32_t i = 0; i < script_->nfixed; i++)
         markUndefinedLocal(0, i);
 
 #ifdef DEBUG
     uint32_t depth = ssa.getFrame(a->inlineIndex).depth;
-    for (uint32_t i = script->nfixed; i < script->nslots; i++) {
+    for (uint32_t i = script_->nfixed; i < script_->nslots; i++) {
         Address local(JSFrameReg, sizeof(StackFrame) + (depth + i) * sizeof(Value));
         masm.storeValue(ObjectValueCrashOnTouch(), local);
     }
 #endif
 }
 
 CompileStatus
 mjit::Compiler::generateEpilogue()
@@ -1447,24 +1447,24 @@ mjit::Compiler::finishThisUp()
         execPool->release();
         js_ReportOutOfMemory(cx);
         return Compile_Error;
     }
 
     JITChunk *chunk = new(cursor) JITChunk;
     cursor += sizeof(JITChunk);
 
-    JS_ASSERT(outerScript == script);
+    JS_ASSERT(outerScript == script_);
 
     chunk->code = JSC::MacroAssemblerCodeRef(result, execPool, masm.size() + stubcc.size());
     chunk->pcLengths = pcLengths;
 
     if (chunkIndex == 0) {
         jit->invokeEntry = result;
-        if (script->function()) {
+        if (script_->function()) {
             jit->arityCheckEntry = stubCode.locationOf(arityLabel).executableAddress();
             jit->argsCheckEntry = stubCode.locationOf(argsCheckLabel).executableAddress();
             jit->fastEntry = fullCode.locationOf(fastEntryLabel).executableAddress();
         }
     }
 
     /*
      * WARNING: mics(), callICs() et al depend on the ordering of these
@@ -1581,17 +1581,17 @@ mjit::Compiler::finishThisUp()
 
     uint32_t *jitTypeBarrierBytecodes = (uint32_t *)cursor;
     chunk->nTypeBarrierBytecodes = typeBarrierBytecodes.length();
     cursor += sizeof(uint32_t) * chunk->nTypeBarrierBytecodes;
     for (size_t i = 0; i < chunk->nTypeBarrierBytecodes; i++)
         jitTypeBarrierBytecodes[i] = typeBarrierBytecodes[i];
 
 #if defined JS_MONOIC
-    if (chunkIndex == 0 && script->function()) {
+    if (chunkIndex == 0 && script_->function()) {
         JS_ASSERT(jit->argsCheckPool == NULL);
         if (cx->typeInferenceEnabled()) {
             jit->argsCheckStub = stubCode.locationOf(argsCheckStub);
             jit->argsCheckFallthrough = stubCode.locationOf(argsCheckFallthrough);
             jit->argsCheckJump = stubCode.locationOf(argsCheckJump);
         }
     }
 
@@ -1707,17 +1707,17 @@ mjit::Compiler::finishThisUp()
 
     ic::EqualityICInfo *jitEqualityICs = (ic::EqualityICInfo *)cursor;
     chunk->nEqualityICs = equalityICs.length();
     cursor += sizeof(ic::EqualityICInfo) * chunk->nEqualityICs;
     for (size_t i = 0; i < chunk->nEqualityICs; i++) {
         if (equalityICs[i].trampoline) {
             jitEqualityICs[i].target = stubCode.locationOf(equalityICs[i].trampolineStart);
         } else {
-            uint32_t offs = uint32_t(equalityICs[i].jumpTarget - script->code);
+            uint32_t offs = uint32_t(equalityICs[i].jumpTarget - script_->code);
             JS_ASSERT(jumpMap[offs].isSet());
             jitEqualityICs[i].target = fullCode.locationOf(jumpMap[offs]);
         }
         jitEqualityICs[i].stubEntry = stubCode.locationOf(equalityICs[i].stubEntry);
         jitEqualityICs[i].stubCall = stubCode.locationOf(equalityICs[i].stubCall);
         jitEqualityICs[i].stub = equalityICs[i].stub;
         jitEqualityICs[i].lvr = equalityICs[i].lvr;
         jitEqualityICs[i].rvr = equalityICs[i].rvr;
@@ -1778,17 +1778,17 @@ mjit::Compiler::finishThisUp()
     cursor += sizeof(ic::SetElementIC) * chunk->nSetElems;
     for (size_t i = 0; i < chunk->nSetElems; i++) {
         ic::SetElementIC &to = jitSetElems[i];
         SetElementICInfo &from = setElemICs[i];
 
         new (&to) ic::SetElementIC();
         from.copyTo(to, fullCode, stubCode);
 
-        to.strictMode = script->strictModeCode;
+        to.strictMode = script_->strictModeCode;
         to.vr = from.vr;
         to.objReg = from.objReg;
         to.objRemat = from.objRemat.toInt32();
         JS_ASSERT(to.objRemat == from.objRemat.toInt32());
 
         to.hasConstantKey = from.key.isConstant();
         if (from.key.isConstant())
             to.keyValue = from.key.index();
@@ -1863,17 +1863,17 @@ mjit::Compiler::finishThisUp()
     double *oolDoubles = (double*) (result + doubleOffset +
                                     masm.numDoubles() * sizeof(double));
 
     /* Generate jump tables. */
     void **jumpVec = (void **)(oolDoubles + stubcc.masm.numDoubles());
 
     for (size_t i = 0; i < jumpTableEdges.length(); i++) {
         JumpTableEdge edge = jumpTableEdges[i];
-        if (bytecodeInChunk(script->code + edge.target)) {
+        if (bytecodeInChunk(script_->code + edge.target)) {
             JS_ASSERT(jumpMap[edge.target].isSet());
             jumpVec[i] = (void *)(result + masm.distanceOf(jumpMap[edge.target]));
         } else {
             ChunkJumpTableEdge nedge;
             nedge.edge = edge;
             nedge.jumpTableEntry = &jumpVec[i];
             chunkJumps.infallibleAppend(nedge);
             jumpVec[i] = NULL;
@@ -1910,17 +1910,17 @@ mjit::Compiler::finishThisUp()
     /* Patch all incoming and outgoing cross-chunk jumps. */
     CrossChunkEdge *crossEdges = jit->edges();
     for (unsigned i = 0; i < jit->nedges; i++) {
         CrossChunkEdge &edge = crossEdges[i];
         if (bytecodeInChunk(outerScript->code + edge.source)) {
             JS_ASSERT(!edge.sourceJump1 && !edge.sourceJump2);
             void *label = edge.targetLabel ? edge.targetLabel : edge.shimLabel;
             CodeLocationLabel targetLabel(label);
-            JSOp op = JSOp(script->code[edge.source]);
+            JSOp op = JSOp(script_->code[edge.source]);
             if (op == JSOP_TABLESWITCH) {
                 if (edge.jumpTableEntries)
                     js_free(edge.jumpTableEntries);
                 CrossChunkEdge::JumpTableEntryVector *jumpTableEntries = NULL;
                 bool failed = false;
                 for (unsigned j = 0; j < chunkJumps.length(); j++) {
                     ChunkJumpTableEdge nedge = chunkJumps[j];
                     if (nedge.edge.source == edge.source && nedge.edge.target == edge.target) {
@@ -1978,18 +1978,18 @@ mjit::Compiler::finishThisUp()
 }
 
 #ifdef DEBUG
 #define SPEW_OPCODE()                                                         \
     JS_BEGIN_MACRO                                                            \
         if (IsJaegerSpewChannelActive(JSpew_JSOps)) {                         \
             Sprinter sprinter(cx);                                            \
             sprinter.init();                                                  \
-            RootedScript script_(cx, script);                                 \
-            js_Disassemble1(cx, script_, PC, PC - script->code,               \
+            RootedScript script(cx, script_);                                 \
+            js_Disassemble1(cx, script, PC, PC - script_->code,               \
                             JS_TRUE, &sprinter);                              \
             JaegerSpew(JSpew_JSOps, "    %2d %s",                             \
                        frame.stackDepth(), sprinter.string());                \
         }                                                                     \
     JS_END_MACRO;
 #else
 #define SPEW_OPCODE()
 #endif /* DEBUG */
@@ -2012,62 +2012,62 @@ inline bool
 mjit::Compiler::shouldStartLoop(jsbytecode *head)
 {
     /*
      * Don't do loop based optimizations or register allocation for loops which
      * span multiple chunks.
      */
     if (*head == JSOP_LOOPHEAD && analysis->getLoop(head)) {
         uint32_t backedge = analysis->getLoop(head)->backedge;
-        if (!bytecodeInChunk(script->code + backedge))
+        if (!bytecodeInChunk(script_->code + backedge))
             return false;
         return true;
     }
     return false;
 }
 
 CompileStatus
 mjit::Compiler::generateMethod()
 {
-    SrcNoteLineScanner scanner(script->notes(), script->lineno);
+    SrcNoteLineScanner scanner(script_->notes(), script_->lineno);
 
     /* For join points, whether there was fallthrough from the previous opcode. */
     bool fallthrough = true;
 
     /* Last bytecode processed. */
     jsbytecode *lastPC = NULL;
 
     if (!outerJIT())
         return Compile_Retry;
 
-    uint32_t chunkBegin = 0, chunkEnd = script->length;
+    uint32_t chunkBegin = 0, chunkEnd = script_->length;
     if (!a->parent) {
         const ChunkDescriptor &desc =
             outerJIT()->chunkDescriptor(chunkIndex);
         chunkBegin = desc.begin;
         chunkEnd = desc.end;
 
-        while (PC != script->code + chunkBegin) {
+        while (PC != script_->code + chunkBegin) {
             Bytecode *opinfo = analysis->maybeCode(PC);
             if (opinfo) {
                 if (opinfo->jumpTarget) {
                     /* Update variable types for all new values at this bytecode. */
                     const SlotValue *newv = analysis->newValues(PC);
                     if (newv) {
                         while (newv->slot) {
-                            if (newv->slot < TotalSlots(script)) {
+                            if (newv->slot < TotalSlots(script_)) {
                                 VarType &vt = a->varTypes[newv->slot];
                                 vt.setTypes(analysis->getValueTypes(newv->value));
                             }
                             newv++;
                         }
                     }
                 }
                 if (analyze::BytecodeUpdatesSlot(JSOp(*PC))) {
-                    uint32_t slot = GetBytecodeSlot(script, PC);
+                    uint32_t slot = GetBytecodeSlot(script_, PC);
                     if (analysis->trackSlot(slot)) {
                         VarType &vt = a->varTypes[slot];
                         vt.setTypes(analysis->pushedTypes(PC, 0));
                     }
                 }
             }
 
             PC += GetBytecodeLength(PC);
@@ -2079,36 +2079,36 @@ mjit::Compiler::generateMethod()
                 fram