Merge the last PGO-green inbound changeset to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 12 Jul 2012 20:46:27 -0400
changeset 99136 6489be1890c058de61c4cac031e40a1902e3e9d9
parent 99135 6a640ca09064b687395e845ba2b3988ad971a600 (current diff)
parent 99095 1f4ad785cca82b5affcc79332a6dbd109d53338a (diff)
child 99137 e2daccb7d367a5a6bcb68d6a63c57bbb14c18c3a
child 99138 32c911bc25790a2b70a64b05972d3edfe3cc12a8
push id11839
push userryanvm@gmail.com
push dateFri, 13 Jul 2012 01:12:18 +0000
treeherdermozilla-inbound@e2daccb7d367 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone16.0a1
first release with
nightly linux32
6489be1890c0 / 16.0a1 / 20120713030548 / files
nightly linux64
6489be1890c0 / 16.0a1 / 20120713030548 / files
nightly mac
6489be1890c0 / 16.0a1 / 20120713030548 / files
nightly win32
6489be1890c0 / 16.0a1 / 20120713030548 / files
nightly win64
6489be1890c0 / 16.0a1 / 20120713030548 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge the last PGO-green inbound changeset to m-c.
ipc/glue/GeckoChildProcessHost.cpp
ipc/ipdl/ipdl/lower.py
js/src/tests/js1_8_1/regress/regress-452498-108.js
testing/marionette/client/marionette/www/test.xul
testing/marionette/client/marionette/www/test2.xul
testing/marionette/client/marionette/www/test_nested_iframe.xul
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -135,30 +135,37 @@ var gPluginHandler = {
       case "PluginBlocklisted":
       case "PluginOutdated":
 #ifdef XP_MACOSX
       case "npapi-carbon-event-model-failure":
 #endif
         self.pluginUnavailable(plugin, event.type);
         break;
 
+      case "PluginVulnerableUpdatable":
+        let updateLink = doc.getAnonymousElementByAttribute(plugin, "class", "checkForUpdatesLink");
+        self.addLinkClickCallback(updateLink, "openPluginUpdatePage");
+        /* FALLTHRU */
+
+      case "PluginVulnerableNoUpdate":
       case "PluginClickToPlay":
         self._handleClickToPlayEvent(plugin);
         break;
 
       case "PluginDisabled":
         let manageLink = doc.getAnonymousElementByAttribute(plugin, "class", "managePluginsLink");
         self.addLinkClickCallback(manageLink, "managePlugins");
         break;
     }
 
     // Hide the in-content UI if it's too big. The crashed plugin handler already did this.
-    if (event.type != "PluginCrashed" && event.type != "PluginClickToPlay") {
+    if (event.type != "PluginCrashed") {
       let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
-      if (self.isTooSmall(plugin, overlay))
+      /* overlay might be null, so only operate on it if it exists */
+      if (overlay != null && self.isTooSmall(plugin, overlay))
           overlay.style.visibility = "hidden";
     }
   },
 
   activatePlugins: function PH_activatePlugins(aContentWindow) {
     let browser = gBrowser.getBrowserForDocument(aContentWindow.document);
     browser._clickToPlayPluginsActivated = true;
     let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -219,16 +226,22 @@ var gPluginHandler = {
                "PFSWindow", "chrome,centerscreen,resizable=yes",
                {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
   },
 
   // Callback for user clicking on a disabled plugin
   managePlugins: function (aEvent) {
     BrowserOpenAddonsMgr("addons://list/plugin");
   },
+ 
+  // Callback for user clicking on the link in a click-to-play plugin
+  // (where the plugin has an update)
+  openPluginUpdatePage: function (aEvent) {
+    openURL(Services.urlFormatter.formatURLPref("plugins.update.url"));
+  },
 
 #ifdef MOZ_CRASHREPORTER
   // Callback for user clicking "submit a report" link
   submitReport : function(pluginDumpID, browserDumpID) {
     // The crash reporter wants a DOM element it can append an IFRAME to,
     // which it uses to submit a form. Let's just give it gBrowser.
     this.CrashSubmit.submit(pluginDumpID);
     if (browserDumpID)
@@ -253,25 +266,28 @@ var gPluginHandler = {
     let pluginsPermission = Services.perms.testPermission(browser.currentURI, "plugins");
     let overlay = doc.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
 
     if (browser._clickToPlayPluginsActivated) {
       let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
       objLoadingContent.playPlugin();
       return;
     } else if (pluginsPermission == Ci.nsIPermissionManager.DENY_ACTION) {
-      overlay.style.visibility = "hidden";
+      if (overlay)
+        overlay.style.visibility = "hidden";
       return;
     }
 
-    let overlay = doc.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
     // The overlay is null if the XBL binding is not attached (element is display:none).
     if (overlay) {
       overlay.addEventListener("click", function(aEvent) {
-        if (aEvent.button == 0 && aEvent.isTrusted)
+        // Have to check that the target is a XULElement and not the link
+        // to update the plugin
+        if (aEvent.target instanceof XULElement && 
+            aEvent.button == 0 && aEvent.isTrusted)
           gPluginHandler.activateSinglePlugin(aEvent.target.ownerDocument.defaultView.top, aPlugin);
       }, true);
     }
 
     if (!browser._clickToPlayDoorhangerShown)
       gPluginHandler._showClickToPlayNotification(browser);
   },
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -996,16 +996,18 @@ var gBrowserInit = {
     gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false);
 
     gBrowser.addEventListener("PluginNotFound",     gPluginHandler, true);
     gBrowser.addEventListener("PluginCrashed",      gPluginHandler, true);
     gBrowser.addEventListener("PluginBlocklisted",  gPluginHandler, true);
     gBrowser.addEventListener("PluginOutdated",     gPluginHandler, true);
     gBrowser.addEventListener("PluginDisabled",     gPluginHandler, true);
     gBrowser.addEventListener("PluginClickToPlay",  gPluginHandler, true);
+    gBrowser.addEventListener("PluginVulnerableUpdatable", gPluginHandler, true);
+    gBrowser.addEventListener("PluginVulnerableNoUpdate", gPluginHandler, true);
     gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true);
 #ifdef XP_MACOSX
     gBrowser.addEventListener("npapi-carbon-event-model-failure", gPluginHandler, true);
 #endif
 
     Services.obs.addObserver(gPluginHandler.pluginCrashed, "plugin-crashed", false);
 
     window.addEventListener("AppCommand", HandleAppCommandEvent, true);
--- a/browser/base/content/test/browser_pluginnotification.js
+++ b/browser/base/content/test/browser_pluginnotification.js
@@ -512,10 +512,108 @@ function test16d() {
 // Tests that mContentType is used for click-to-play plugins, and not the
 // inspected type.
 function test17() {
   var clickToPlayNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
   ok(clickToPlayNotification, "Test 17, Should have a click-to-play notification");
   var missingNotification = PopupNotifications.getNotification("missing-plugins", gTestBrowser);
   ok(!missingNotification, "Test 17, Should not have a missing plugin notification");
 
+  registerFakeBlocklistService(Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE);
+  prepareTest(test18a, gTestRoot + "plugin_test.html");
+}
+
+const Cr = Components.results;
+const Cm = Components.manager;
+const Cc = Components.classes;
+const gReg = Cm.QueryInterface(Ci.nsIComponentRegistrar);
+const gRealBlocklistServiceCID = Cc["@mozilla.org/extensions/blocklist;1"];
+const gFakeBlocklistServiceCID = Components.ID("{614b68a0-3c53-4ec0-8146-28cc1e25f8a1}");
+var gFactory = null;
+
+function registerFakeBlocklistService(blockState) {
+
+  var BlocklistService = {
+    getPluginBlocklistState: function(plugin, appVersion, toolkitVersion) {
+      return blockState;
+    },
+
+    classID: gFakeBlocklistServiceCID,
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsIBlocklistService])
+  };
+
+  gFactory = {
+    createInstance: function(outer, iid) {
+      if (outer != null)
+        throw Cr.NS_ERROR_NO_AGGREGATION;
+      return BlocklistService.QueryInterface(iid);
+    }
+  };
+
+  gReg.registerFactory(gFakeBlocklistServiceCID,
+                       "Fake Blocklist Service",
+                       "@mozilla.org/extensions/blocklist;1",
+                       gFactory);
+}
+
+function unregisterFakeBlocklistService() {
+  if (gFactory != null ) {
+    gReg.unregisterFactory(gFakeBlocklistServiceCID, gFactory);
+    gFactory = null;
+    // This should restore the original blocklist service:
+    gReg.registerFactory(gRealBlocklistServiceCID,
+                         "Blocklist Service",
+                         "@mozilla.org/extensions/blocklist;1",
+                         null);
+  }
+}
+
+// Tests a vulnerable, updatable plugin
+function test18a() {
+  var clickToPlayNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(clickToPlayNotification, "Test 18a, Should have a click-to-play notification");
+  var doc = gTestBrowser.contentDocument;
+  var plugin = doc.getElementById("test");
+  var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+  ok(!objLoadingContent.activated, "Test 18a, Plugin should not be activated");
+  var overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+  ok(overlay.style.visibility != "hidden", "Test 18a, Plugin overlay should exist, not be hidden");
+  var updateLink = doc.getAnonymousElementByAttribute(plugin, "class", "checkForUpdatesLink");
+  ok(updateLink.style.visibility != "hidden", "Test 18a, Plugin should have an update link");
+
+  var tabOpenListener = new TabOpenListener(Services.urlFormatter.formatURLPref("plugins.update.url"), false, false);
+  tabOpenListener.handleEvent = function(event) {
+    if (event.type == "TabOpen") {
+      gBrowser.tabContainer.removeEventListener("TabOpen", this, false);
+      this.tab = event.originalTarget;
+      ok(event.target.label == this.url, "Test 18a, Update link should open up the plugin check page");
+      gBrowser.removeTab(this.tab);
+      test18b();
+    }
+  };
+  EventUtils.synthesizeMouse(updateLink, 5, 5, {}, gTestBrowser.contentWindow);
+}
+
+function test18b() {
+  unregisterFakeBlocklistService();
+  registerFakeBlocklistService(Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
+  prepareTest(test18c, gTestRoot + "plugin_test.html");
+}
+
+// Tests a vulnerable plugin with no update
+function test18c() {
+  var clickToPlayNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
+  ok(clickToPlayNotification, "Test 18c, Should have a click-to-play notification");
+  var doc = gTestBrowser.contentDocument;
+  var plugin = doc.getElementById("test");
+  var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+  ok(!objLoadingContent.activated, "Test 18c, Plugin should not be activated");
+  var overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+  ok(overlay.style.visibility != "hidden", "Test 18c, Plugin overlay should exist, not be hidden");
+  var updateLink = doc.getAnonymousElementByAttribute(plugin, "class", "checkForUpdatesLink");
+  ok(updateLink.style.display != "block", "Test 18c, Plugin should not have an update link");
+
+  unregisterFakeBlocklistService();
+  var plugin = get_test_plugin();
+  plugin.clicktoplay = false;
+
   finishTest();
 }
--- a/build/mobile/devicemanager.py
+++ b/build/mobile/devicemanager.py
@@ -504,17 +504,38 @@ class DeviceManager:
   @abstractmethod
   def chmodDir(self, remoteDir):
     """
     external function
     returns:
     success: True
     failure: False
     """
-    
+
+  @staticmethod
+  def _escapedCommandLine(cmd):
+    """ Utility function to return escaped and quoted version of command line """
+    quotedCmd = []
+
+    for arg in cmd:
+      arg.replace('&', '\&')
+
+      needsQuoting = False
+      for char in [ ' ', '(', ')', '"', '&' ]:
+        if arg.find(char) >= 0:
+          needsQuoting = True
+          break
+      if needsQuoting:
+        arg = '\'%s\'' % arg
+
+      quotedCmd.append(arg)
+
+    return " ".join(quotedCmd)
+
+
 class NetworkTools:
   def __init__(self):
     pass
 
   # Utilities to get the local ip address
   def getInterfaceIp(self, ifname):
     if os.name != "nt":
       import fcntl
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -90,34 +90,23 @@ class DeviceManagerADB(DeviceManager):
     if self.host:
       self.disconnectRemoteADB()
 
   # external function: executes shell command on device
   # returns:
   # success: <return code>
   # failure: None
   def shell(self, cmd, outputfile, env=None, cwd=None):
-    # need to quote and escape special characters here
-    for (index, arg) in enumerate(cmd):
-      arg.replace('&', '\&')
-
-      needsQuoting = False
-      for char in [ ' ', '(', ')', '"', '&' ]:
-        if arg.find(char):
-          needsQuoting = True
-          break
-      if needsQuoting:
-        cmd[index] = '\'%s\'' % arg
-
-    # This is more complex than you'd think because adb doesn't actually
-    # return the return code from a process, so we have to capture the output
-    # to get it
     # FIXME: this function buffers all output of the command into memory,
     # always. :(
-    cmdline = " ".join(cmd) + "; echo $?"
+
+    # Getting the return code is more complex than you'd think because adb
+    # doesn't actually return the return code from a process, so we have to
+    # capture the output to get it
+    cmdline = "%s; echo $?" % self._escapedCommandLine(cmd)
 
     # prepend cwd and env to command if necessary
     if cwd:
       cmdline = "cd %s; %s" % (cwd, cmdline)
     if env:
       envstr = '; '.join(map(lambda x: 'export %s=%s' % (x[0], x[1]), env.iteritems()))
       cmdline = envstr + "; " + cmdline
 
--- a/build/mobile/devicemanagerSUT.py
+++ b/build/mobile/devicemanagerSUT.py
@@ -255,17 +255,17 @@ class DeviceManagerSUT(DeviceManager):
         self._sock = None
         raise AgentError("Error closing socket")
 
   # external function: executes shell command on device
   # returns:
   # success: <return code>
   # failure: None
   def shell(self, cmd, outputfile, env=None, cwd=None):
-    cmdline = subprocess.list2cmdline(cmd)
+    cmdline = self._escapedCommandLine(cmd)
     if env:
       cmdline = '%s %s' % (self.formatEnvString(env), cmdline)
 
     try:
       if cwd:
         self.sendCmds([{ 'cmd': 'execcwd %s %s' % (cwd, cmdline) }], outputfile)
       else:
         self.sendCmds([{ 'cmd': 'exec su -c "%s"' % cmdline }], outputfile)
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -75,30 +75,16 @@ static NS_DEFINE_CID(kAppShellCID, NS_AP
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc");
 #endif
 
 #define LOG(args) PR_LOG(gObjectLog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gObjectLog, PR_LOG_DEBUG)
 
-#include "mozilla/Preferences.h"
-
-static bool gClickToPlayPlugins = false;
-
-static void
-InitPrefCache()
-{
-  static bool initializedPrefCache = false;
-  if (!initializedPrefCache) {
-    mozilla::Preferences::AddBoolVarCache(&gClickToPlayPlugins, "plugins.click_to_play");
-  }
-  initializedPrefCache = true;
-}
-
 class nsAsyncInstantiateEvent : public nsRunnable {
 public:
   nsObjectLoadingContent *mContent;
   nsAsyncInstantiateEvent(nsObjectLoadingContent* aContent)
   : mContent(aContent)
   {
     static_cast<nsIObjectLoadingContent *>(mContent)->AddRef();
   }
@@ -176,16 +162,22 @@ nsPluginErrorEvent::Run()
 {
   LOG(("OBJLC []: Firing plugin not found event for content %p\n",
        mContent.get()));
   nsString type;
   switch (mState) {
     case ePluginClickToPlay:
       type = NS_LITERAL_STRING("PluginClickToPlay");
       break;
+    case ePluginVulnerableUpdatable:
+      type = NS_LITERAL_STRING("PluginVulnerableUpdatable");
+      break;
+    case ePluginVulnerableNoUpdate:
+      type = NS_LITERAL_STRING("PluginVulnerableNoUpdate");
+      break;
     case ePluginUnsupported:
       type = NS_LITERAL_STRING("PluginNotFound");
       break;
     case ePluginDisabled:
       type = NS_LITERAL_STRING("PluginDisabled");
       break;
     case ePluginBlocklisted:
       type = NS_LITERAL_STRING("PluginBlocklisted");
@@ -479,17 +471,21 @@ nsresult nsObjectLoadingContent::IsPlugi
 
   // Check to see if the plugin is disabled before deciding if it
   // should be in the "click to play" state, since we only want to
   // display "click to play" UI for enabled plugins.
   if (NS_FAILED(rv)) {
     return rv;
   }
 
-  if (!mShouldPlay) {
+  if (!pluginHost->IsPluginClickToPlayForType(aMIMEType.get())) {
+    mCTPPlayable = true;
+  }
+
+  if (!mCTPPlayable) {
     nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
     MOZ_ASSERT(thisContent);
     nsIDocument* ownerDoc = thisContent->OwnerDoc();
 
     nsCOMPtr<nsIDOMWindow> window = ownerDoc->GetWindow();
     if (!window) {
       return NS_ERROR_FAILURE;
     }
@@ -500,22 +496,27 @@ nsresult nsObjectLoadingContent::IsPlugi
     rv = topWindow->GetDocument(getter_AddRefs(topDocument));
     NS_ENSURE_SUCCESS(rv, rv);
     nsCOMPtr<nsIDocument> topDoc = do_QueryInterface(topDocument);
     nsIURI* topUri = topDoc->GetDocumentURI();
 
     nsCOMPtr<nsIPermissionManager> permissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
     PRUint32 permission;
-    rv = permissionManager->TestPermission(topUri,
-                                           "plugins",
-                                           &permission);
+    rv = permissionManager->TestPermission(topUri, "plugins", &permission);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRUint32 state;
+    rv = pluginHost->GetBlocklistStateForType(aMIMEType.get(), &state);
     NS_ENSURE_SUCCESS(rv, rv);
-    if (permission == nsIPermissionManager::ALLOW_ACTION) {
-      mShouldPlay = true;
+
+    if (permission == nsIPermissionManager::ALLOW_ACTION &&
+        state != nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE &&
+        state != nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
+      mCTPPlayable = true;
     } else {
       return NS_ERROR_PLUGIN_CLICKTOPLAY;
     }
   }
 
   return NS_OK;
 }
 
@@ -537,39 +538,45 @@ GetExtensionFromURI(nsIURI* uri, nsCStri
 }
 
 /**
  * Checks whether a plugin exists and is enabled for the extension
  * in the given URI. The MIME type is returned in the mimeType out parameter.
  */
 bool nsObjectLoadingContent::IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType)
 {
-  if (!mShouldPlay) {
-    return false;
-  }
-
   nsCAutoString ext;
   GetExtensionFromURI(uri, ext);
+  bool enabled = false;
 
   if (ext.IsEmpty()) {
     return false;
   }
 
   nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
   nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
   if (!pluginHost) {
     return false;
   }
 
   const char* typeFromExt;
   if (NS_SUCCEEDED(pluginHost->IsPluginEnabledForExtension(ext.get(), typeFromExt))) {
     mimeType = typeFromExt;
-    return true;
+    enabled = true;
+
+    if (!pluginHost->IsPluginClickToPlayForType(mimeType.get())) {
+      mCTPPlayable = true;
+    }
   }
-  return false;
+
+  if (!mCTPPlayable) {
+    return false;
+  } else {
+    return enabled;
+  }
 }
 
 nsresult
 nsObjectLoadingContent::BindToTree(nsIDocument* aDocument, nsIContent* /*aParent*/,
                                    nsIContent* /*aBindingParent*/,
                                    bool /*aCompileEventHandlers*/)
 {
   if (aDocument) {
@@ -593,39 +600,30 @@ nsObjectLoadingContent::nsObjectLoadingC
   , mType(eType_Loading)
   , mInstantiating(false)
   , mUserDisabled(false)
   , mSuppressed(false)
   , mNetworkCreated(true)
   , mIsStopping(false)
   , mSrcStreamLoading(false)
   , mFallbackReason(ePluginOtherState)
-{
-  InitPrefCache();
-  // If plugins.click_to_play is false, plugins should always play
-  mShouldPlay = !gClickToPlayPlugins;
-  // If plugins.click_to_play is true, track the activated state of plugins.
-  mActivated = !gClickToPlayPlugins;
-}
+  , mCTPPlayable(false)
+  , mActivated(false) {}
 
 nsObjectLoadingContent::~nsObjectLoadingContent()
 {
   DestroyImageLoadingContent();
   if (mFrameLoader) {
     mFrameLoader->Destroy();
   }
 }
 
 nsresult
 nsObjectLoadingContent::InstantiatePluginInstance(const char* aMimeType, nsIURI* aURI)
 {
-  if (!mShouldPlay) {
-    return NS_ERROR_PLUGIN_CLICKTOPLAY;
-  }
-
   // Don't do anything if we already have an active instance.
   if (mInstanceOwner) {
     return NS_OK;
   }
 
   // Don't allow re-entry into initialization code.
   if (mInstantiating) {
     return NS_OK;
@@ -660,16 +658,24 @@ nsObjectLoadingContent::InstantiatePlugi
 
   nsresult rv = NS_ERROR_FAILURE;
   nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &rv));
   nsPluginHost* pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
   if (NS_FAILED(rv)) {
     return rv;
   }
 
+  if (!pluginHost->IsPluginClickToPlayForType(aMimeType)) {
+    mCTPPlayable = true;
+  }
+
+  if (!mCTPPlayable) {
+    return NS_ERROR_PLUGIN_CLICKTOPLAY;
+  }
+
   // If you add early return(s), be sure to balance this call to
   // appShell->SuspendNative() with additional call(s) to
   // appShell->ReturnNative().
   nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
   if (appShell) {
     appShell->SuspendNative();
   }
 
@@ -1186,16 +1192,20 @@ nsObjectLoadingContent::ObjectState() co
       if (mUserDisabled)
         return NS_EVENT_STATE_USERDISABLED;
 
       // Otherwise, broken
       nsEventStates state = NS_EVENT_STATE_BROKEN;
       switch (mFallbackReason) {
         case ePluginClickToPlay:
           return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY;
+        case ePluginVulnerableUpdatable:
+          return NS_EVENT_STATE_VULNERABLE_UPDATABLE;
+        case ePluginVulnerableNoUpdate:
+          return NS_EVENT_STATE_VULNERABLE_NO_UPDATE;
         case ePluginDisabled:
           state |= NS_EVENT_STATE_HANDLER_DISABLED;
           break;
         case ePluginBlocklisted:
           state |= NS_EVENT_STATE_HANDLER_BLOCKED;
           break;
         case ePluginCrashed:
           state |= NS_EVENT_STATE_HANDLER_CRASHED;
@@ -1928,35 +1938,53 @@ nsObjectLoadingContent::GetPluginSupport
       }
     } else if (!hasAlternateContent) {
       hasAlternateContent =
         nsStyleUtil::IsSignificantChild(child, true, false);
     }
   }
 
   PluginSupportState pluginDisabledState = GetPluginDisabledState(aContentType);
-  if (pluginDisabledState == ePluginClickToPlay) {
-    return ePluginClickToPlay;
+  if (pluginDisabledState == ePluginClickToPlay ||
+      pluginDisabledState == ePluginVulnerableUpdatable ||
+      pluginDisabledState == ePluginVulnerableNoUpdate) {
+    return pluginDisabledState;
   } else if (hasAlternateContent) {
     return ePluginOtherState;
   } else {
     return pluginDisabledState;
   }
 }
 
 PluginSupportState
 nsObjectLoadingContent::GetPluginDisabledState(const nsCString& aContentType)
 {
   nsresult rv = IsPluginEnabledForType(aContentType);
-  if (rv == NS_ERROR_PLUGIN_DISABLED)
+  if (rv == NS_ERROR_PLUGIN_DISABLED) {
     return ePluginDisabled;
-  if (rv == NS_ERROR_PLUGIN_CLICKTOPLAY)
+  }
+  if (rv == NS_ERROR_PLUGIN_CLICKTOPLAY) {
+    PRUint32 state;
+    nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
+    nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
+    if (pluginHost) {
+      rv = pluginHost->GetBlocklistStateForType(aContentType.get(), &state);
+      if (NS_SUCCEEDED(rv)) {
+        if (state == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
+          return ePluginVulnerableUpdatable;
+        } else if (state == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
+          return ePluginVulnerableNoUpdate;
+        }
+      }
+    }
     return ePluginClickToPlay;
-  if (rv == NS_ERROR_PLUGIN_BLOCKLISTED)
+  }
+  if (rv == NS_ERROR_PLUGIN_BLOCKLISTED) {
     return ePluginBlocklisted;
+  }
   return ePluginUnsupported;
 }
 
 void
 nsObjectLoadingContent::CreateStaticClone(nsObjectLoadingContent* aDest) const
 {
   nsImageLoadingContent::CreateStaticImageClone(aDest);
 
@@ -2208,17 +2236,17 @@ nsObjectLoadingContent::NotifyContentObj
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::PlayPlugin()
 {
   if (!nsContentUtils::IsCallerChrome())
     return NS_OK;
 
-  mShouldPlay = true;
+  mCTPPlayable = true;
   return LoadObject(mURI, true, mContentType, true);
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::GetActivated(bool* aActivated)
 {
   *aActivated = mActivated;
   return NS_OK;
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -33,17 +33,19 @@ class nsObjectFrame;
 
 enum PluginSupportState {
   ePluginUnsupported,  // The plugin is not supported (e.g. not installed)
   ePluginDisabled,     // The plugin has been explicitly disabled by the user
   ePluginBlocklisted,  // The plugin is blocklisted and disabled
   ePluginOutdated,     // The plugin is considered outdated, but not disabled
   ePluginOtherState,   // Something else (e.g. uninitialized or not a plugin)
   ePluginCrashed,
-  ePluginClickToPlay   // The plugin is disabled until the user clicks on it
+  ePluginClickToPlay,  // The plugin is disabled until the user clicks on it
+  ePluginVulnerableUpdatable, // The plugin is vulnerable (update available)
+  ePluginVulnerableNoUpdate   // The plugin is vulnerable (no update available)
 };
 
 /**
  * INVARIANTS OF THIS CLASS
  * - mChannel is non-null between asyncOpen and onStopRequest (NOTE: Only needs
  *   to be valid until onStopRequest is called on mFinalListener, not
  *   necessarily until the channel calls onStopRequest on us)
  * - mChannel corresponds to the channel that gets passed to the
@@ -367,19 +369,21 @@ class nsObjectLoadingContent : public ns
     bool                        mUserDisabled  : 1;
     bool                        mSuppressed    : 1;
 
     // True when the object is created for an element which the parser has
     // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
     // it may lose the flag.
     bool                        mNetworkCreated : 1;
 
-    // Used to keep track of whether or not a plugin should be played.
-    // This is used for click-to-play plugins.
-    bool                        mShouldPlay : 1;
+    // Used to keep track of if a plugin is blocked by click-to-play.
+    // True indicates the plugin is not click-to-play or it has been clicked by
+    // the user.
+    // False indicates the plugin is click-to-play and has not yet been clicked.
+    bool                        mCTPPlayable    : 1;
 
     // Used to keep track of whether or not a plugin has been played.
     // This is used for click-to-play plugins.
     bool                        mActivated : 1;
 
     // Protects DoStopPlugin from reentry (bug 724781).
     bool                        mIsStopping : 1;
 
--- a/content/events/public/nsEventStates.h
+++ b/content/events/public/nsEventStates.h
@@ -237,16 +237,20 @@ private:
 // Handler for click to play plugin
 #define NS_EVENT_STATE_TYPE_CLICK_TO_PLAY NS_DEFINE_EVENT_STATE_MACRO(36)
 // Content is in the optimum region.
 #define NS_EVENT_STATE_OPTIMUM NS_DEFINE_EVENT_STATE_MACRO(37)
 // Content is in the suboptimal region.
 #define NS_EVENT_STATE_SUB_OPTIMUM NS_DEFINE_EVENT_STATE_MACRO(38)
 // Content is in the sub-suboptimal region.
 #define NS_EVENT_STATE_SUB_SUB_OPTIMUM NS_DEFINE_EVENT_STATE_MACRO(39)
+// Handler for click to play plugin (vulnerable w/update)
+#define NS_EVENT_STATE_VULNERABLE_UPDATABLE NS_DEFINE_EVENT_STATE_MACRO(40)
+// Handler for click to play plugin (vulnerable w/no update)
+#define NS_EVENT_STATE_VULNERABLE_NO_UPDATE NS_DEFINE_EVENT_STATE_MACRO(41)
 
 /**
  * NOTE: do not go over 63 without updating nsEventStates::InternalType!
  */
 
 #define ESM_MANAGED_STATES (NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS |     \
                             NS_EVENT_STATE_HOVER | NS_EVENT_STATE_DRAGOVER |   \
                             NS_EVENT_STATE_URLTARGET | NS_EVENT_STATE_FOCUSRING | \
--- a/dom/plugins/base/nsIPluginTag.idl
+++ b/dom/plugins/base/nsIPluginTag.idl
@@ -1,18 +1,19 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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(88e03453-a773-47ba-9d84-14f672ac99e2)]
+[scriptable, uuid(a361a7e7-7f8d-4b68-91e9-30ae096460d4)]
 interface nsIPluginTag : nsISupports
 {
   readonly attribute AUTF8String description;
   readonly attribute AUTF8String filename;
   readonly attribute AUTF8String fullpath;
   readonly attribute AUTF8String version;
   readonly attribute AUTF8String name;
            attribute boolean  disabled;
            attribute boolean  blocklisted;
+           attribute boolean  clicktoplay;
 };
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -337,18 +337,20 @@ nsPluginHost::nsPluginHost()
   // has a zeroing operator new.
 {
   // check to see if pref is set at startup to let plugins take over in
   // full page mode for certain image mime types that we handle internally
   mOverrideInternalTypes =
     Preferences::GetBool("plugin.override_internal_types", false);
 
   mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
+  mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
 
   Preferences::AddStrongObserver(this, "plugin.disable");
+  Preferences::AddStrongObserver(this, "plugins.click_to_play");
 
   nsCOMPtr<nsIObserverService> obsService =
     mozilla::services::GetObserverService();
   if (obsService) {
     obsService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 #ifdef MOZ_WIDGET_ANDROID
     obsService->AddObserver(this, "application-foreground", false);
     obsService->AddObserver(this, "application-background", false);
@@ -1289,16 +1291,46 @@ nsPluginHost::IsPluginEnabledForType(con
     if (plugin->HasFlag(NS_PLUGIN_FLAG_BLOCKLISTED))
       return NS_ERROR_PLUGIN_BLOCKLISTED;
     else
       return NS_ERROR_PLUGIN_DISABLED;
   }
 
   return NS_OK;
 }
+ 
+bool
+nsPluginHost::IsPluginClickToPlayForType(const char* aMimeType)
+{
+  nsPluginTag *plugin = FindPluginForType(aMimeType, true);
+  if (plugin && 
+      (plugin->HasFlag(NS_PLUGIN_FLAG_CLICKTOPLAY) || mPluginsClickToPlay)) {
+    return true;
+  }
+  else {
+    return false;
+  }
+}
+
+nsresult
+nsPluginHost::GetBlocklistStateForType(const char *aMimeType, PRUint32 *aState) 
+{
+  nsPluginTag *plugin = FindPluginForType(aMimeType, true);
+  if (plugin) {
+    nsCOMPtr<nsIBlocklistService> blocklist = do_GetService("@mozilla.org/extensions/blocklist;1");
+    if (blocklist) {
+      // The EmptyString()s are so we use the currently running application
+      // and toolkit versions
+      return blocklist->GetPluginBlocklistState(plugin, EmptyString(),
+                                                EmptyString(), aState);
+    }
+  }
+
+  return NS_ERROR_FAILURE;
+}
 
 // check comma delimitered extensions
 static int CompareExtensions(const char *aExtensionList, const char *aExtension)
 {
   if (!aExtensionList || !aExtension)
     return -1;
 
   const char *pExt = aExtensionList;
@@ -2069,29 +2101,42 @@ nsresult nsPluginHost::ScanPluginsDirect
 
       nsCOMPtr<nsIBlocklistService> blocklist = do_GetService("@mozilla.org/extensions/blocklist;1");
       if (blocklist) {
         PRUint32 state;
         rv = blocklist->GetPluginBlocklistState(pluginTag, EmptyString(),
                                                 EmptyString(), &state);
 
         if (NS_SUCCEEDED(rv)) {
-          // If the blocklist says so then block the plugin. If the blocklist says
-          // it is risky and we have never seen this plugin before then disable it
-          if (state == nsIBlocklistService::STATE_BLOCKED)
-            pluginTag->Mark(NS_PLUGIN_FLAG_BLOCKLISTED);
-          else if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore)
-            enabled = false;
-          else if (state == nsIBlocklistService::STATE_OUTDATED && !seenBefore)
-            warnOutdated = true;
+          // If the blocklist says so, block the plugin.
+          // If the blocklist says it is risky and we have never seen this
+          // plugin before, then disable it.
+          // If the blocklist says this is an outdated plugin, warn about
+          // outdated plugins.
+          // If the blocklist says the plugin is one of the click-to-play
+          // states, set the click-to-play flag.
+          if (state == nsIBlocklistService::STATE_BLOCKED) {
+             pluginTag->Mark(NS_PLUGIN_FLAG_BLOCKLISTED);
+          }
+          if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore) {
+             enabled = false;
+          }
+          if (state == nsIBlocklistService::STATE_OUTDATED && !seenBefore) {
+             warnOutdated = true;
+          }
+          if (state == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
+              state == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
+            pluginTag->Mark(NS_PLUGIN_FLAG_CLICKTOPLAY);
+          }
         }
       }
 
-      if (!enabled)
+      if (!enabled) {
         pluginTag->UnMark(NS_PLUGIN_FLAG_ENABLED);
+      }
 
       // Plugin unloading is tag-based. If we created a new tag and loaded
       // the library in the process then we want to attempt to unload it here.
       // Only do this if the pref is set for aggressive unloading.
       if (UnloadPluginsASAP()) {
         pluginTag->TryUnloadPlugin(false);
       }
     }
@@ -3297,16 +3342,17 @@ NS_IMETHODIMP nsPluginHost::Observe(nsIS
 {
   if (!nsCRT::strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic)) {
     OnShutdown();
     UnloadPlugins();
     sInst->Release();
   }
   if (!nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
     mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
+    mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
     // Unload or load plugins as needed
     if (mPluginsDisabled) {
       UnloadPlugins();
     } else {
       LoadPlugins();
     }
   }
 #ifdef MOZ_WIDGET_ANDROID
--- a/dom/plugins/base/nsPluginHost.h
+++ b/dom/plugins/base/nsPluginHost.h
@@ -80,16 +80,18 @@ public:
   nsresult LoadPlugins();
   nsresult UnloadPlugins();
 
   nsresult SetUpPluginInstance(const char *aMimeType,
                                nsIURI *aURL,
                                nsIPluginInstanceOwner *aOwner);
   nsresult IsPluginEnabledForType(const char* aMimeType);
   nsresult IsPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
+  bool     IsPluginClickToPlayForType(const char *aMimeType);
+  nsresult GetBlocklistStateForType(const char *aMimeType, PRUint32 *state);
 
   nsresult GetPluginCount(PRUint32* aPluginCount);
   nsresult GetPlugins(PRUint32 aPluginCount, nsIDOMPlugin** aPluginArray);
 
   nsresult GetURL(nsISupports* pluginInst,
                   const char* url,
                   const char* target,
                   nsNPAPIPluginStreamListener* streamListener,
@@ -276,16 +278,18 @@ private:
   bool mPluginsLoaded;
   bool mDontShowBadPluginMessage;
 
   // set by pref plugin.override_internal_types
   bool mOverrideInternalTypes;
 
   // set by pref plugin.disable
   bool mPluginsDisabled;
+  // set by pref plugins.click_to_play
+  bool mPluginsClickToPlay;
 
   // Any instances in this array will have valid plugin objects via GetPlugin().
   // When removing an instance it might not die - be sure to null out it's plugin.
   nsTArray< nsRefPtr<nsNPAPIPluginInstance> > mInstances;
 
   nsCOMPtr<nsIFile> mPluginRegFile;
 #ifdef XP_WIN
   nsRefPtr<nsPluginDirServiceProvider> mPrivateDirServiceProvider;
--- a/dom/plugins/base/nsPluginTags.cpp
+++ b/dom/plugins/base/nsPluginTags.cpp
@@ -315,16 +315,40 @@ nsPluginTag::SetBlocklisted(bool aBlockl
   if (aBlocklisted)
     Mark(NS_PLUGIN_FLAG_BLOCKLISTED);
   else
     UnMark(NS_PLUGIN_FLAG_BLOCKLISTED);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsPluginTag::GetClicktoplay(bool *aClicktoplay)
+{
+  *aClicktoplay = HasFlag(NS_PLUGIN_FLAG_CLICKTOPLAY);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPluginTag::SetClicktoplay(bool aClicktoplay)
+{
+  if (HasFlag(NS_PLUGIN_FLAG_CLICKTOPLAY) == aClicktoplay) {
+    return NS_OK;
+  }
+  
+  if (aClicktoplay) {
+    Mark(NS_PLUGIN_FLAG_CLICKTOPLAY);
+  } else {
+    UnMark(NS_PLUGIN_FLAG_CLICKTOPLAY);
+  }
+  
+  mPluginHost->UpdatePluginInfo(nsnull);
+  return NS_OK;
+}
+
 void nsPluginTag::Mark(PRUint32 mask)
 {
   bool wasEnabled = IsEnabled();
   mFlags |= mask;
 
   if (mPluginHost && wasEnabled != IsEnabled()) {
     mPluginHost->UpdatePluginInfo(this);
   }
--- a/dom/plugins/base/nsPluginTags.h
+++ b/dom/plugins/base/nsPluginTags.h
@@ -22,16 +22,17 @@ struct nsPluginInfo;
 
 // Remember that flags are written out to pluginreg.dat, be careful
 // changing their meaning.
 #define NS_PLUGIN_FLAG_ENABLED      0x0001    // is this plugin enabled?
 // no longer used                   0x0002    // reuse only if regenerating pluginreg.dat
 #define NS_PLUGIN_FLAG_FROMCACHE    0x0004    // this plugintag info was loaded from cache
 // no longer used                   0x0008    // reuse only if regenerating pluginreg.dat
 #define NS_PLUGIN_FLAG_BLOCKLISTED  0x0010    // this is a blocklisted plugin
+#define NS_PLUGIN_FLAG_CLICKTOPLAY  0x0020    // this is a click-to-play plugin
 
 // A linked-list of plugin information that is used for instantiating plugins
 // and reflecting plugin information into JavaScript.
 class nsPluginTag : public nsIPluginTag
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPLUGINTAG
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -14,16 +14,17 @@
 #include "nsArrayEnumerator.h"
 #include "nsTArray.h"
 #include "nsReadableUtils.h"
 #include "nsILineInputStream.h"
 #include "nsIIDNService.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "prprf.h"
 #include "mozilla/storage.h"
+#include "mozilla/Attributes.h"
 #include "nsXULAppAPI.h"
 #include "nsIPrincipal.h"
 
 static nsPermissionManager *gPermissionManager = nsnull;
 
 using mozilla::dom::ContentParent;
 using mozilla::dom::ContentChild;
 using mozilla::unused; // ha!
@@ -110,17 +111,17 @@ nsHostEntry::nsHostEntry(const nsHostEnt
  * the database is closed.
  *
  * Note: Beware that, if you hold onto a |CloseDatabaseListener| from a
  * |nsPermissionManager|, this will create a cycle.
  *
  * Note: Once the callback has been called this DeleteFromMozHostListener cannot
  * be reused.
  */
-class CloseDatabaseListener : public mozIStorageCompletionCallback
+class CloseDatabaseListener MOZ_FINAL : public mozIStorageCompletionCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
   /**
    * @param aManager The owning manager.
    * @param aRebuildOnSuccess If |true|, reinitialize the database once
    * it has been closed. Otherwise, do nothing such.
@@ -159,17 +160,17 @@ CloseDatabaseListener::Complete()
  * the database and reinitializing it.
  *
  * Note: Beware that, if you hold onto a |DeleteFromMozHostListener| from a
  * |nsPermissionManager|, this will create a cycle.
  *
  * Note: Once the callback has been called this DeleteFromMozHostListener cannot
  * be reused.
  */
-class DeleteFromMozHostListener : public mozIStorageStatementCallback
+class DeleteFromMozHostListener MOZ_FINAL : public mozIStorageStatementCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_MOZISTORAGESTATEMENTCALLBACK
 
   /**
    * @param aManager The owning manager.
    */
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -135,13 +135,21 @@ EXPORTS_mozilla/layers += ShadowLayerUti
 CPPSRCS += ShadowLayerUtilsX11.cpp
 endif #}
 
 ifdef MOZ_ENABLE_D3D10_LAYER
 EXPORTS_mozilla/layers += ShadowLayerUtilsD3D10.h
 DEFINES	+= -DMOZ_ENABLE_D3D10_LAYER
 endif
 
+# NB: Gralloc is available on other platforms that use the android GL
+# libraries, but only Gonk is able to use it reliably because Gecko
+# has full system permissions there.
+ifeq ($(MOZ_WIDGET_TOOLKIT),gonk)
+EXPORTS_mozilla/layers += ShadowLayerUtilsGralloc.h
+CPPSRCS += ShadowLayerUtilsGralloc.cpp
+endif
+
 include $(topsrcdir)/config/rules.mk
 
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
 CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(MOZ_PIXMAN_CFLAGS) $(TK_CFLAGS)
--- a/gfx/layers/ThebesLayerBuffer.h
+++ b/gfx/layers/ThebesLayerBuffer.h
@@ -167,16 +167,26 @@ protected:
     nsRefPtr<gfxASurface> tmp = mBuffer.forget();
     mBuffer = aBuffer;
     mBufferRect = aBufferRect;
     mBufferRotation = aBufferRotation;
     return tmp.forget();
   }
 
   /**
+   * Set the buffer only.  This is intended to be used with the
+   * shadow-layer Open/CloseDescriptor interface, to ensure we don't
+   * accidentally touch a buffer when it's not mapped.
+   */
+  void SetBuffer(gfxASurface* aBuffer)
+  {
+    mBuffer = aBuffer;
+  }
+
+  /**
    * Get a context at the specified resolution for updating |aBounds|,
    * which must be contained within a single quadrant.
    */
   already_AddRefed<gfxContext>
   GetContextForQuadrantUpdate(const nsIntRect& aBounds);
 
 private:
   bool BufferSizeOkFor(const nsIntSize& aSize)
--- a/gfx/layers/basic/BasicBuffers.cpp
+++ b/gfx/layers/basic/BasicBuffers.cpp
@@ -5,18 +5,18 @@
 
 #include "BasicThebesLayer.h"
 #include "BasicBuffers.h"
 #include "gfxUtils.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
-namespace layers {
-
+namespace layers {
+
 
 static bool
 IsClippingCheap(gfxContext* aTarget, const nsIntRegion& aRegion)
 {
   // Assume clipping is cheap if the context just has an integer
   // translation, and the visible region is simple.
   return !aTarget->CurrentMatrix().HasNonIntegerTranslation() &&
          aRegion.GetNumRects() <= 1; 
@@ -42,20 +42,20 @@ BasicThebesLayerBuffer::DrawTo(ThebesLay
     // Bug 599189 if there is a non-integer-translation transform in aTarget,
     // we might sample pixels outside GetEffectiveVisibleRegion(), which is wrong
     // and may cause gray lines.
     gfxUtils::ClipToRegionSnapped(aTarget, aLayer->GetEffectiveVisibleRegion());
   }
 
   // Pull out the mask surface and transform here, because the mask
   // is internal to basic layers
-  gfxMatrix maskTransform;
-  if (nsRefPtr<gfxASurface> maskSurface =
-        GetMaskSurfaceAndTransform(aMaskLayer, &maskTransform)) {
-    DrawBufferWithRotation(aTarget, aOpacity, maskSurface, &maskTransform);
+  AutoMaskData mask;
+  if (GetMaskData(aMaskLayer, &mask)) {
+    DrawBufferWithRotation(aTarget, aOpacity,
+                           mask.GetSurface(), &mask.GetTransform());
   } else {
     DrawBufferWithRotation(aTarget, aOpacity);
   }
   aTarget->Restore();
 }
 
 already_AddRefed<gfxASurface>
 BasicThebesLayerBuffer::CreateBuffer(ContentType aType, 
--- a/gfx/layers/basic/BasicBuffers.h
+++ b/gfx/layers/basic/BasicBuffers.h
@@ -53,16 +53,37 @@ public:
     oldBuffer = SetBuffer(aBuffer, aRect, aRotation);
   }
 
   void SetBackingBufferAndUpdateFrom(
     gfxASurface* aBuffer,
     gfxASurface* aSource, const nsIntRect& aRect, const nsIntPoint& aRotation,
     const nsIntRegion& aUpdateRegion);
 
+  /**
+   * When BasicThebesLayerBuffer is used with layers that hold
+   * SurfaceDescriptor, this buffer only has a valid gfxASurface in
+   * the scope of an AutoOpenSurface for that SurfaceDescriptor.  That
+   * is, it's sort of a "virtual buffer" that's only mapped an
+   * unmapped within the scope of AutoOpenSurface.  None of the
+   * underlying buffer attributes (rect, rotation) are affected by
+   * mapping/unmapping.
+   *
+   * These helpers just exist to provide more descriptive names of the
+   * map/unmap process.
+   */
+  void MapBuffer(gfxASurface* aBuffer)
+  {
+    SetBuffer(aBuffer);
+  }
+  void UnmapBuffer()
+  {
+    SetBuffer(nsnull);
+  }
+
 private:
   BasicThebesLayerBuffer(gfxASurface* aBuffer,
                          const nsIntRect& aRect, const nsIntPoint& aRotation)
     // The size policy doesn't really matter here; this constructor is
     // intended to be used for creating temporaries
     : ThebesLayerBuffer(ContainsVisibleBounds)
   {
     SetBuffer(aBuffer, aRect, aRotation);
@@ -82,35 +103,42 @@ public:
     MOZ_COUNT_CTOR(ShadowThebesLayerBuffer);
   }
 
   ~ShadowThebesLayerBuffer()
   {
     MOZ_COUNT_DTOR(ShadowThebesLayerBuffer);
   }
 
-  void Swap(gfxASurface* aNewBuffer,
-            const nsIntRect& aNewRect, const nsIntPoint& aNewRotation,
-            gfxASurface** aOldBuffer,
+  /**
+   * Swap in the old "virtual buffer" (see above) attributes in aNew*
+   * and return the old ones in aOld*.
+   *
+   * Swap() must only be called when the buffer is in its "unmapped"
+   * state, that is the underlying gfxASurface is not available.  It
+   * is expected that the owner of this buffer holds an unmapped
+   * SurfaceDescriptor as the backing storage for this buffer.  That's
+   * why no gfxASurface or SurfaceDescriptor parameters appear here.
+   */
+  void Swap(const nsIntRect& aNewRect, const nsIntPoint& aNewRotation,
             nsIntRect* aOldRect, nsIntPoint* aOldRotation)
   {
     *aOldRect = BufferRect();
     *aOldRotation = BufferRotation();
 
     nsRefPtr<gfxASurface> oldBuffer;
-    oldBuffer = SetBuffer(aNewBuffer,
-                          aNewRect, aNewRotation);
-    oldBuffer.forget(aOldBuffer);
+    oldBuffer = SetBuffer(nsnull, aNewRect, aNewRotation);
+    MOZ_ASSERT(!oldBuffer);
   }
 
 protected:
   virtual already_AddRefed<gfxASurface>
   CreateBuffer(ContentType, const nsIntSize&, PRUint32)
   {
     NS_RUNTIMEABORT("ShadowThebesLayer can't paint content");
     return nsnull;
   }
 };
 
 }
 }
 
-#endif
\ No newline at end of file
+#endif
--- a/gfx/layers/basic/BasicCanvasLayer.cpp
+++ b/gfx/layers/basic/BasicCanvasLayer.cpp
@@ -334,19 +334,18 @@ BasicShadowableCanvasLayer::Initialize(c
   BasicCanvasLayer::Initialize(aData);
   if (!HasShadow())
       return;
 
   // XXX won't get here currently; need to figure out what to do on
   // canvas resizes
 
   if (IsSurfaceDescriptorValid(mBackBuffer)) {
-    nsRefPtr<gfxASurface> backSurface =
-      BasicManager()->OpenDescriptor(mBackBuffer);
-    if (gfxIntSize(mBounds.width, mBounds.height) != backSurface->GetSize()) {
+    AutoOpenSurface backSurface(OPEN_READ_ONLY, mBackBuffer);
+    if (gfxIntSize(mBounds.width, mBounds.height) != backSurface.Size()) {
       DestroyBackBuffer();
     }
   }
 }
 
 void
 BasicShadowableCanvasLayer::Paint(gfxContext* aContext, Layer* aMaskLayer)
 {
@@ -363,25 +362,23 @@ BasicShadowableCanvasLayer::Paint(gfxCon
     if (!BasicManager()->AllocBuffer(
         gfxIntSize(mBounds.width, mBounds.height),
         isOpaque ?
           gfxASurface::CONTENT_COLOR : gfxASurface::CONTENT_COLOR_ALPHA,
         &mBackBuffer))
     NS_RUNTIMEABORT("creating CanvasLayer back buffer failed!");
   }
 
-  nsRefPtr<gfxASurface> backSurface =
-    BasicManager()->OpenDescriptor(mBackBuffer);
-
+  AutoOpenSurface autoBackSurface(OPEN_READ_WRITE, mBackBuffer);
 
   if (aMaskLayer) {
     static_cast<BasicImplData*>(aMaskLayer->ImplData())
       ->Paint(aContext, nsnull);
   }
-  UpdateSurface(backSurface, nsnull);
+  UpdateSurface(autoBackSurface.Get(), nsnull);
   FireDidTransactionCallback();
 
   BasicManager()->PaintedCanvas(BasicManager()->Hold(this),
                                 mNeedsYFlip ? true : false,
                                 mBackBuffer);
 }
 
 class BasicShadowCanvasLayer : public ShadowCanvasLayer,
@@ -432,54 +429,52 @@ BasicShadowCanvasLayer::Initialize(const
 {
   NS_RUNTIMEABORT("Incompatibe surface type");
 }
 
 void
 BasicShadowCanvasLayer::Swap(const CanvasSurface& aNewFront, bool needYFlip,
                              CanvasSurface* aNewBack)
 {
-  nsRefPtr<gfxASurface> surface =
-    BasicManager()->OpenDescriptor(aNewFront);
+  AutoOpenSurface autoSurface(OPEN_READ_ONLY, aNewFront);
   // Destroy mFrontBuffer if size different
-  gfxIntSize sz = surface->GetSize();
+  gfxIntSize sz = autoSurface.Size();
   bool surfaceConfigChanged = sz != gfxIntSize(mBounds.width, mBounds.height);
   if (IsSurfaceDescriptorValid(mFrontSurface)) {
-    nsRefPtr<gfxASurface> front = BasicManager()->OpenDescriptor(mFrontSurface);
+    AutoOpenSurface autoFront(OPEN_READ_ONLY, mFrontSurface);
     surfaceConfigChanged = surfaceConfigChanged ||
-                           surface->GetContentType() != front->GetContentType();
+                           autoSurface.ContentType() != autoFront.ContentType();
   }
   if (surfaceConfigChanged) {
     DestroyFrontBuffer();
     mBounds.SetRect(0, 0, sz.width, sz.height);
   }
 
   mNeedsYFlip = needYFlip;
   // If mFrontBuffer
   if (IsSurfaceDescriptorValid(mFrontSurface)) {
     *aNewBack = mFrontSurface;
   } else {
     *aNewBack = null_t();
   }
-  mFrontSurface = aNewFront.get_SurfaceDescriptor();
+  mFrontSurface = aNewFront;
 }
 
 void
 BasicShadowCanvasLayer::Paint(gfxContext* aContext, Layer* aMaskLayer)
 {
   NS_ASSERTION(BasicManager()->InDrawing(),
                "Can only draw in drawing phase");
 
   if (!IsSurfaceDescriptorValid(mFrontSurface)) {
     return;
   }
 
-  nsRefPtr<gfxASurface> surface =
-    BasicManager()->OpenDescriptor(mFrontSurface);
-  nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
+  AutoOpenSurface autoSurface(OPEN_READ_ONLY, mFrontSurface);
+  nsRefPtr<gfxPattern> pat = new gfxPattern(autoSurface.Get());
 
   pat->SetFilter(mFilter);
   pat->SetExtend(gfxPattern::EXTEND_PAD);
 
   gfxRect r(0, 0, mBounds.width, mBounds.height);
 
   gfxMatrix m;
   if (mNeedsYFlip) {
--- a/gfx/layers/basic/BasicColorLayer.cpp
+++ b/gfx/layers/basic/BasicColorLayer.cpp
@@ -4,18 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/layers/PLayersParent.h"
 #include "BasicLayersImpl.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
-namespace layers {
-
+namespace layers {
+
 class BasicColorLayer : public ColorLayer, public BasicImplData {
 public:
   BasicColorLayer(BasicLayerManager* aLayerManager) :
     ColorLayer(aLayerManager, static_cast<BasicImplData*>(this))
   {
     MOZ_COUNT_CTOR(BasicColorLayer);
   }
   virtual ~BasicColorLayer()
--- a/gfx/layers/basic/BasicContainerLayer.cpp
+++ b/gfx/layers/basic/BasicContainerLayer.cpp
@@ -4,18 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BasicLayersImpl.h"
 #include "BasicContainerLayer.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
-namespace layers {
-
+namespace layers {
+
 
 
 BasicContainerLayer::~BasicContainerLayer()
 {
   while (mFirstChild) {
     ContainerRemoveChild(mFirstChild, this);
   }
 
--- a/gfx/layers/basic/BasicImageLayer.cpp
+++ b/gfx/layers/basic/BasicImageLayer.cpp
@@ -6,17 +6,17 @@
 #include "mozilla/layers/PLayersParent.h"
 #include "BasicLayersImpl.h"
 #include "gfxUtils.h"
 #include "gfxSharedImageSurface.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
-namespace layers {
+namespace layers {
 
 class BasicImageLayer : public ImageLayer, public BasicImplData {
 public:
   BasicImageLayer(BasicLayerManager* aLayerManager) :
     ImageLayer(aLayerManager, static_cast<BasicImplData*>(this)),
     mSize(-1, -1)
   {
     MOZ_COUNT_CTOR(BasicImageLayer);
@@ -36,17 +36,18 @@ public:
   virtual void Paint(gfxContext* aContext, Layer* aMaskLayer);
 
   static void PaintContext(gfxPattern* aPattern,
                            const nsIntRegion& aVisible,
                            float aOpacity,
                            gfxContext* aContext,
                            Layer* aMaskLayer);
 
-  virtual already_AddRefed<gfxASurface> GetAsSurface();
+  virtual bool GetAsSurface(gfxASurface** aSurface,
+                            SurfaceDescriptor* aDescriptor);
 
 protected:
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
   }
 
   // only paints the image if aContext is non-null
@@ -81,19 +82,16 @@ BasicImageLayer::GetAndPaintCurrentImage
   AutoLockImage autoLock(mContainer, getter_AddRefs(surface));
   Image *image = autoLock.GetImage();
   gfxIntSize size = mSize = autoLock.GetSize();
 
   if (!surface || surface->CairoStatus()) {
     return nsnull;
   }
 
-  NS_ASSERTION(surface->GetContentType() != gfxASurface::CONTENT_ALPHA,
-               "Image layer has alpha image");
-
   nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
   if (!pat) {
     return nsnull;
   }
 
   pat->SetFilter(mFilter);
   gfxIntSize sourceSize = surface->GetSize();
   if (mScaleMode != SCALE_NONE) {
@@ -149,25 +147,28 @@ BasicImageLayer::PaintContext(gfxPattern
   aPattern->SetExtend(extend);
   aContext->SetPattern(aPattern);
   FillWithMask(aContext, aOpacity, aMaskLayer);
 
   // Reset extend mode for callers that need to reuse the pattern
   aPattern->SetExtend(extend);
 }
 
-already_AddRefed<gfxASurface>
-BasicImageLayer::GetAsSurface()
+bool
+BasicImageLayer::GetAsSurface(gfxASurface** aSurface,
+                              SurfaceDescriptor* aDescriptor)
 {
   if (!mContainer) {
-    return nsnull;
+    return false;
   }
 
   gfxIntSize dontCare;
-  return mContainer->GetCurrentAsSurface(&dontCare);
+  nsRefPtr<gfxASurface> surface = mContainer->GetCurrentAsSurface(&dontCare);
+  *aSurface = surface.forget().get();
+  return true;
 }
 
 class BasicShadowableImageLayer : public BasicImageLayer,
                                   public BasicShadowableLayer
 {
 public:
   BasicShadowableImageLayer(BasicShadowLayerManager* aManager) :
     BasicImageLayer(aManager),
@@ -191,57 +192,59 @@ public:
   virtual Layer* AsLayer() { return this; }
   virtual ShadowableLayer* AsShadowableLayer() { return this; }
 
   virtual void SetBackBuffer(const SurfaceDescriptor& aBuffer)
   {
     mBackBuffer = aBuffer;
   }
 
-  virtual void SetBackBufferYUVImage(gfxSharedImageSurface* aYBuffer,
-                                     gfxSharedImageSurface* aUBuffer,
-                                     gfxSharedImageSurface* aVBuffer)
+  virtual void SetBackBufferYUVImage(const SurfaceDescriptor& aYBuffer,
+                                     const SurfaceDescriptor& aUBuffer,
+                                     const SurfaceDescriptor& aVBuffer)
   {
     mBackBufferY = aYBuffer;
     mBackBufferU = aUBuffer;
     mBackBufferV = aVBuffer;
   }
 
   virtual void Disconnect()
   {
-    mBackBufferY = mBackBufferU = mBackBufferV = nsnull;
+    mBackBufferY = SurfaceDescriptor();
+    mBackBufferU = SurfaceDescriptor();
+    mBackBufferV = SurfaceDescriptor();
     mBackBuffer = SurfaceDescriptor();
     BasicShadowableLayer::Disconnect();
   }
 
   void DestroyBackBuffer()
   {
     if (IsSurfaceDescriptorValid(mBackBuffer)) {
       BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBuffer);
     }
-    if (mBackBufferY) {
-      BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBufferY);
-      BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBufferU);
-      BasicManager()->ShadowLayerForwarder::DestroySharedSurface(mBackBufferV);
-    }
+    if (IsSurfaceDescriptorValid(mBackBufferY)) {
+      BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBufferY);
+      BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBufferU);
+      BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBufferV);
+}
   }
 
 private:
   BasicShadowLayerManager* BasicManager()
   {
     return static_cast<BasicShadowLayerManager*>(mManager);
   }
 
   // For YUV Images these are the 3 planes (Y, Cb and Cr),
   // for RGB images only mBackSurface is used.
   SurfaceDescriptor mBackBuffer;
   bool mBufferIsOpaque;
-  nsRefPtr<gfxSharedImageSurface> mBackBufferY;
-  nsRefPtr<gfxSharedImageSurface> mBackBufferU;
-  nsRefPtr<gfxSharedImageSurface> mBackBufferV;
+  SurfaceDescriptor mBackBufferY;
+  SurfaceDescriptor mBackBufferU;
+  SurfaceDescriptor mBackBufferV;
   gfxIntSize mCbCrSize;
 };
  
 void
 BasicShadowableImageLayer::Paint(gfxContext* aContext, Layer* aMaskLayer)
 {
   if (!HasShadow()) {
     BasicImageLayer::Paint(aContext, aMaskLayer);
@@ -266,48 +269,62 @@ BasicShadowableImageLayer::Paint(gfxCont
       ->Paint(aContext, nsnull);
   }
 
   if (image->GetFormat() == Image::PLANAR_YCBCR && BasicManager()->IsCompositingCheap()) {
     PlanarYCbCrImage *YCbCrImage = static_cast<PlanarYCbCrImage*>(image);
     const PlanarYCbCrImage::Data *data = YCbCrImage->GetData();
     NS_ASSERTION(data, "Must be able to retrieve yuv data from image!");
 
-    if (mSize != data->mYSize || mCbCrSize != data->mCbCrSize || !mBackBufferY) {
+    if (mSize != data->mYSize || mCbCrSize != data->mCbCrSize || !IsSurfaceDescriptorValid(mBackBufferY)) {
       DestroyBackBuffer();
       mSize = data->mYSize;
       mCbCrSize = data->mCbCrSize;
 
-      if (!BasicManager()->AllocBuffer(mSize, gfxASurface::CONTENT_ALPHA,
-                                       getter_AddRefs(mBackBufferY)) ||
-          !BasicManager()->AllocBuffer(mCbCrSize, gfxASurface::CONTENT_ALPHA,
-                                       getter_AddRefs(mBackBufferU)) ||
-          !BasicManager()->AllocBuffer(mCbCrSize, gfxASurface::CONTENT_ALPHA,
-                                       getter_AddRefs(mBackBufferV))) {
+      // We either allocate all three planes or none.
+      if (!BasicManager()->AllocBufferWithCaps(mSize,
+                                               gfxASurface::CONTENT_ALPHA,
+                                               MAP_AS_IMAGE_SURFACE,
+                                               &mBackBufferY) ||
+          !BasicManager()->AllocBufferWithCaps(mCbCrSize,
+                                               gfxASurface::CONTENT_ALPHA,
+                                               MAP_AS_IMAGE_SURFACE,
+                                               &mBackBufferU) ||
+          !BasicManager()->AllocBufferWithCaps(mCbCrSize,
+                                               gfxASurface::CONTENT_ALPHA,
+                                               MAP_AS_IMAGE_SURFACE,
+                                               &mBackBufferV)) {
         NS_RUNTIMEABORT("creating ImageLayer 'front buffer' failed!");
       }
     }
 
+    AutoOpenSurface dyas(OPEN_READ_WRITE, mBackBufferY);
+    gfxImageSurface* dy = dyas.GetAsImage();
+
     for (int i = 0; i < data->mYSize.height; i++) {
-      memcpy(mBackBufferY->Data() + i * mBackBufferY->Stride(),
+      memcpy(dy->Data() + i * dy->Stride(),
              data->mYChannel + i * data->mYStride,
              data->mYSize.width);
     }
+
+    AutoOpenSurface duas(OPEN_READ_WRITE, mBackBufferU);
+    gfxImageSurface* du = duas.GetAsImage();
+    AutoOpenSurface dvas(OPEN_READ_WRITE, mBackBufferV);
+    gfxImageSurface* dv = dvas.GetAsImage();
+
     for (int i = 0; i < data->mCbCrSize.height; i++) {
-      memcpy(mBackBufferU->Data() + i * mBackBufferU->Stride(),
+      memcpy(du->Data() + i * du->Stride(),
              data->mCbChannel + i * data->mCbCrStride,
              data->mCbCrSize.width);
-      memcpy(mBackBufferV->Data() + i * mBackBufferV->Stride(),
+      memcpy(dv->Data() + i * dv->Stride(),
              data->mCrChannel + i * data->mCbCrStride,
              data->mCbCrSize.width);
     }
 
-    YUVImage yuv(mBackBufferY->GetShmem(),
-                 mBackBufferU->GetShmem(),
-                 mBackBufferV->GetShmem(),
+    YUVImage yuv(mBackBufferY, mBackBufferU, mBackBufferV,
                  data->GetPictureRect());
 
     BasicManager()->PaintedImage(BasicManager()->Hold(this),
                                  yuv);
     return;
   }
 
   gfxIntSize oldSize = mSize;
@@ -331,19 +348,18 @@ BasicShadowableImageLayer::Paint(gfxCont
         isOpaque) {
       type = gfxASurface::CONTENT_COLOR;
     }
 
     if (!BasicManager()->AllocBuffer(mSize, type, &mBackBuffer))
       NS_RUNTIMEABORT("creating ImageLayer 'front buffer' failed!");
   }
 
-  nsRefPtr<gfxASurface> backSurface =
-    BasicManager()->OpenDescriptor(mBackBuffer);
-  nsRefPtr<gfxContext> tmpCtx = new gfxContext(backSurface);
+  AutoOpenSurface backSurface(OPEN_READ_WRITE, mBackBuffer);
+  nsRefPtr<gfxContext> tmpCtx = new gfxContext(backSurface.Get());
   tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
   PaintContext(pat,
                nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height)),
                1.0, tmpCtx, nsnull);
 
   BasicManager()->PaintedImage(BasicManager()->Hold(this),
                                mBackBuffer);
 }
@@ -372,84 +388,85 @@ public:
   virtual void DestroyFrontBuffer()
   {
     if (mAllocator && IsSurfaceDescriptorValid(mFrontBuffer)) {
       mAllocator->DestroySharedSurface(&mFrontBuffer);
     }
   }
 
   virtual void Paint(gfxContext* aContext, Layer* aMaskLayer);
-  already_AddRefed<gfxASurface> GetAsSurface();
+  virtual bool GetAsSurface(gfxASurface** aSurface,
+                            SurfaceDescriptor* aDescriptor);
 
 protected:
   BasicShadowLayerManager* BasicManager()
   {
     return static_cast<BasicShadowLayerManager*>(mManager);
   }
 
   SurfaceDescriptor mFrontBuffer;
   gfxIntSize mSize;
 };
 
 void
 BasicShadowImageLayer::Swap(const SharedImage& aNewFront,
                             SharedImage* aNewBack)
 {
-  nsRefPtr<gfxASurface> surface =
-    BasicManager()->OpenDescriptor(aNewFront);
+  AutoOpenSurface autoSurface(OPEN_READ_ONLY, aNewFront);
   // Destroy mFrontBuffer if size different or image type is different
-  bool surfaceConfigChanged = surface->GetSize() != mSize;
+  bool surfaceConfigChanged = autoSurface.Size() != mSize;
   if (IsSurfaceDescriptorValid(mFrontBuffer)) {
-    nsRefPtr<gfxASurface> front = BasicManager()->OpenDescriptor(mFrontBuffer);
+    AutoOpenSurface autoFront(OPEN_READ_ONLY, mFrontBuffer);
     surfaceConfigChanged = surfaceConfigChanged ||
-                           surface->GetContentType() != front->GetContentType();
+                           autoSurface.ContentType() != autoFront.ContentType();
   }
   if (surfaceConfigChanged) {
     DestroyFrontBuffer();
-    mSize = surface->GetSize();
+    mSize = autoSurface.Size();
   }
 
   // If mFrontBuffer
   if (IsSurfaceDescriptorValid(mFrontBuffer)) {
     *aNewBack = mFrontBuffer;
   } else {
     *aNewBack = null_t();
   }
-  mFrontBuffer = aNewFront.get_SurfaceDescriptor();
+  mFrontBuffer = aNewFront;
 }
 
 void
 BasicShadowImageLayer::Paint(gfxContext* aContext, Layer* aMaskLayer)
 {
   if (!IsSurfaceDescriptorValid(mFrontBuffer)) {
     return;
   }
 
-  nsRefPtr<gfxASurface> surface =
-    BasicManager()->OpenDescriptor(mFrontBuffer);
-  nsRefPtr<gfxPattern> pat = new gfxPattern(surface);
+  AutoOpenSurface autoSurface(OPEN_READ_ONLY, mFrontBuffer);
+  nsRefPtr<gfxPattern> pat = new gfxPattern(autoSurface.Get());
   pat->SetFilter(mFilter);
 
   // The visible region can extend outside the image, so just draw
   // within the image bounds.
   AutoSetOperator setOperator(aContext, GetOperator());
   BasicImageLayer::PaintContext(pat,
                                 nsIntRegion(nsIntRect(0, 0, mSize.width, mSize.height)),
                                 GetEffectiveOpacity(), aContext,
                                 aMaskLayer);
 }
 
-already_AddRefed<gfxASurface>
-BasicShadowImageLayer::GetAsSurface()
+bool
+BasicShadowImageLayer::GetAsSurface(gfxASurface** aSurface,
+                                    SurfaceDescriptor* aDescriptor)
 {
   if (!IsSurfaceDescriptorValid(mFrontBuffer)) {
-    return nsnull;
+    return false;
   }
 
-  return BasicManager()->OpenDescriptor(mFrontBuffer);
+  *aDescriptor = mFrontBuffer;
+  return true;
  }
 
 already_AddRefed<ImageLayer>
 BasicLayerManager::CreateImageLayer()
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
   nsRefPtr<ImageLayer> layer = new BasicImageLayer(this);
   return layer.forget();
--- a/gfx/layers/basic/BasicImplData.h
+++ b/gfx/layers/basic/BasicImplData.h
@@ -96,21 +96,24 @@ public:
                  aOperator == gfxContext::OPERATOR_SOURCE,
                  "Bad composition operator");
     mOperator = aOperator;
   }
   gfxContext::GraphicsOperator GetOperator() const { return mOperator; }
 
   /**
    * Return a surface for this layer. Will use an existing surface, if
-   * possible, or may create a temporary surface.
-   * Implement this method for any layers that might be used as a mask.
-   * Should only return null if a surface cannor be created.
+   * possible, or may create a temporary surface.  Implement this
+   * method for any layers that might be used as a mask.  Should only
+   * return false if a surface cannot be created.  If true is
+   * returned, only one of |aSurface| or |aDescriptor| is valid.
    */
-  virtual already_AddRefed<gfxASurface> GetAsSurface() { return nsnull; }
+  virtual bool GetAsSurface(gfxASurface** aSurface,
+                            SurfaceDescriptor* aDescriptor)
+  { return false; }
 
   bool GetClipToVisibleRegion() { return mClipToVisibleRegion; }
   void SetClipToVisibleRegion(bool aClip) { mClipToVisibleRegion = aClip; }
 
   void SetDrawAtomically(bool aDrawAtomically) { mDrawAtomically = aDrawAtomically; }
 
 protected:
   bool mHidden;
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -19,17 +19,17 @@
 #include "BasicTiledThebesLayer.h"
 #include "BasicLayersImpl.h"
 #include "BasicThebesLayer.h"
 #include "BasicContainerLayer.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
-namespace layers {
+namespace layers {
 
 /**
  * Clips to the smallest device-pixel-aligned rectangle containing aRect
  * in user space.
  * Returns true if the clip is "perfect", i.e. we actually clipped exactly to
  * aRect.
  */
 static bool
@@ -834,17 +834,16 @@ BasicLayerManager::PaintLayer(gfxContext
     // intersect any other leaf layers, so if the transaction is not yet marked
     // incomplete, the contents of this container layer are the final contents
     // for the window.
     if (!mTransactionIncomplete && !blitComplete) {
       if (needsClipToVisibleRegion) {
         gfxUtils::ClipToRegion(aTarget, aLayer->GetEffectiveVisibleRegion());
       }
       AutoSetOperator setOperator(aTarget, container->GetOperator());
-      gfxMatrix temp = aTarget->CurrentMatrix();
       PaintWithMask(aTarget, aLayer->GetEffectiveOpacity(),
                     HasShadowManager() ? nsnull : aLayer->GetMaskLayer());
     }
   }
 
   if (pushedTargetOpaqueRect) {
     if (aTarget->IsCairo()) {
       currentSurface->SetOpaqueRect(gfxRect(0, 0, 0, 0));
@@ -1043,23 +1042,22 @@ BasicShadowLayerManager::ForwardTransact
         const OpImageSwap& ois = reply.get_OpImageSwap();
         BasicShadowableLayer* layer = GetBasicShadowable(ois);
         const SharedImage& newBack = ois.newBackImage();
 
         if (newBack.type() == SharedImage::TSurfaceDescriptor) {
           layer->SetBackBuffer(newBack.get_SurfaceDescriptor());
         } else if (newBack.type() == SharedImage::TYUVImage) {
           const YUVImage& yuv = newBack.get_YUVImage();
-          nsRefPtr<gfxSharedImageSurface> YSurf = gfxSharedImageSurface::Open(yuv.Ydata());
-          nsRefPtr<gfxSharedImageSurface> USurf = gfxSharedImageSurface::Open(yuv.Udata());
-          nsRefPtr<gfxSharedImageSurface> VSurf = gfxSharedImageSurface::Open(yuv.Vdata());
-          layer->SetBackBufferYUVImage(YSurf, USurf, VSurf);
+          layer->SetBackBufferYUVImage(yuv.Ydata(), yuv.Udata(), yuv.Vdata());
         } else {
           layer->SetBackBuffer(SurfaceDescriptor());
-          layer->SetBackBufferYUVImage(nsnull, nsnull, nsnull);
+          layer->SetBackBufferYUVImage(SurfaceDescriptor(),
+                                       SurfaceDescriptor(),
+                                       SurfaceDescriptor());
         }
 
         break;
       }
 
       default:
         NS_RUNTIMEABORT("not reached");
       }
@@ -1122,17 +1120,17 @@ BasicShadowLayerManager::CreateThebesLay
   {
     nsRefPtr<BasicShadowableThebesLayer> layer =
       new BasicShadowableThebesLayer(this);
     MAYBE_CREATE_SHADOW(Thebes);
     return layer.forget();
   }
 }
 
-
+
 BasicShadowableLayer::~BasicShadowableLayer()
 {
   if (HasShadow()) {
     PLayerChild::Send__delete__(GetShadow());
   }
   MOZ_COUNT_DTOR(BasicShadowableLayer);
 }
 
--- a/gfx/layers/basic/BasicLayers.h
+++ b/gfx/layers/basic/BasicLayers.h
@@ -274,21 +274,21 @@ public:
     mShadow = aShadow;
   }
 
   virtual void SetBackBuffer(const SurfaceDescriptor& aBuffer)
   {
     NS_RUNTIMEABORT("if this default impl is called, |aBuffer| leaks");
   }
   
-  virtual void SetBackBufferYUVImage(gfxSharedImageSurface* aYBuffer,
-                                     gfxSharedImageSurface* aUBuffer,
-                                     gfxSharedImageSurface* aVBuffer)
+  virtual void SetBackBufferYUVImage(const SurfaceDescriptor& aYBuffer,
+                                     const SurfaceDescriptor& aUBuffer,
+                                     const SurfaceDescriptor& aVBuffer)
   {
-    NS_RUNTIMEABORT("if this default impl is called, |aBuffer| leaks");
+    NS_RUNTIMEABORT("if this default impl is called, the buffers leak");
   }
 
   virtual void Disconnect()
   {
     // This is an "emergency Disconnect()", called when the compositing
     // process has died.  |mShadow| and our Shmem buffers are
     // automatically managed by IPDL, so we don't need to explicitly
     // free them here (it's hard to get that right on emergency
--- a/gfx/layers/basic/BasicLayersImpl.cpp
+++ b/gfx/layers/basic/BasicLayersImpl.cpp
@@ -1,83 +1,132 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "BasicLayersImpl.h"
+#include "mozilla/layers/PLayers.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace layers {
-
-already_AddRefed<gfxASurface>
-GetMaskSurfaceAndTransform(Layer* aMaskLayer, gfxMatrix* aMaskTransform)
+
+void
+AutoMaskData::Construct(const gfxMatrix& aTransform,
+                        gfxASurface* aSurface)
+{
+  MOZ_ASSERT(!IsConstructed());
+  mTransform = aTransform;
+  mSurface = aSurface;
+}
+
+void
+AutoMaskData::Construct(const gfxMatrix& aTransform,
+                        const SurfaceDescriptor& aSurface)
+{
+  MOZ_ASSERT(!IsConstructed());
+  mTransform = aTransform;
+  mSurfaceOpener.construct(OPEN_READ_ONLY, aSurface);
+}
+
+gfxASurface*
+AutoMaskData::GetSurface()
+{
+  MOZ_ASSERT(IsConstructed());
+  if (mSurface) {
+    return mSurface.get();
+  }
+  return mSurfaceOpener.ref().Get();
+}
+
+const gfxMatrix&
+AutoMaskData::GetTransform()
 {
-   if (aMaskLayer) {
-     nsRefPtr<gfxASurface> maskSurface =
-       static_cast<BasicImplData*>(aMaskLayer->ImplData())->GetAsSurface();
-     if (maskSurface) {
-       bool maskIs2D =
-         aMaskLayer->GetEffectiveTransform().CanDraw2D(aMaskTransform);
-       NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
-       return maskSurface.forget();
-     }
-   }
-   return nsnull;
+  MOZ_ASSERT(IsConstructed());
+  return mTransform;
+}
+
+bool
+AutoMaskData::IsConstructed()
+{
+  return !!mSurface || !mSurfaceOpener.empty();
 }
-
+
+
+bool
+GetMaskData(Layer* aMaskLayer, AutoMaskData* aMaskData)
+{
+  if (aMaskLayer) {
+    nsRefPtr<gfxASurface> surface;
+    SurfaceDescriptor descriptor;
+    if (static_cast<BasicImplData*>(aMaskLayer->ImplData())
+        ->GetAsSurface(getter_AddRefs(surface), &descriptor) &&
+        (surface || IsSurfaceDescriptorValid(descriptor))) {
+      gfxMatrix transform;
+      DebugOnly<bool> maskIs2D =
+        aMaskLayer->GetEffectiveTransform().CanDraw2D(&transform);
+      NS_ASSERTION(maskIs2D, "How did we end up with a 3D transform here?!");
+      if (surface) {
+        aMaskData->Construct(transform, surface);
+      } else {
+        aMaskData->Construct(transform, descriptor);
+      }
+      return true;
+    }
+  }
+  return false;
+}
+
 void
 PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer)
 {
-  gfxMatrix maskTransform;
-  if (nsRefPtr<gfxASurface> maskSurface =
-        GetMaskSurfaceAndTransform(aMaskLayer, &maskTransform)) {
+  AutoMaskData mask;
+  if (GetMaskData(aMaskLayer, &mask)) {
     if (aOpacity < 1.0) {
       aContext->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
       aContext->Paint(aOpacity);
       aContext->PopGroupToSource();
     }
-    aContext->SetMatrix(maskTransform);
-    aContext->Mask(maskSurface);
+    aContext->SetMatrix(mask.GetTransform());
+    aContext->Mask(mask.GetSurface());
     return;
   }
 
   // if there is no mask, just paint normally
   aContext->Paint(aOpacity);
-}
-
+}
+
 void
 FillWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer)
 {
-  gfxMatrix maskTransform;
-  if (nsRefPtr<gfxASurface> maskSurface =
-        GetMaskSurfaceAndTransform(aMaskLayer, &maskTransform)) {
+  AutoMaskData mask;
+  if (GetMaskData(aMaskLayer, &mask)) {
     if (aOpacity < 1.0) {
       aContext->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
       aContext->FillWithOpacity(aOpacity);
       aContext->PopGroupToSource();
-      aContext->SetMatrix(maskTransform);
-      aContext->Mask(maskSurface);
+      aContext->SetMatrix(mask.GetTransform());
+      aContext->Mask(mask.GetSurface());
     } else {
       aContext->Save();
       aContext->Clip();
-      aContext->SetMatrix(maskTransform);
-      aContext->Mask(maskSurface);
+      aContext->SetMatrix(mask.GetTransform());
+      aContext->Mask(mask.GetSurface());
       aContext->NewPath();
       aContext->Restore();
     }
     return;
   }
 
   // if there is no mask, just fill normally
   aContext->FillWithOpacity(aOpacity);
-}
-
+}
+
 BasicImplData*
 ToData(Layer* aLayer)
 {
   return static_cast<BasicImplData*>(aLayer->ImplData());
 }
 
 ShadowableLayer*
 ToShadowable(Layer* aLayer)
@@ -91,11 +140,11 @@ ShouldShadow(Layer* aLayer)
   if (!ToShadowable(aLayer)) {
     NS_ABORT_IF_FALSE(aLayer->GetType() == Layer::TYPE_READBACK,
                       "Only expect not to shadow ReadbackLayers");
     return false;
   }
   return true;
 }
 
-
-}
-}
\ No newline at end of file
+
+}
+}
--- a/gfx/layers/basic/BasicLayersImpl.h
+++ b/gfx/layers/basic/BasicLayersImpl.h
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 GFX_BASICLAYERSIMPL_H
 #define GFX_BASICLAYERSIMPL_H
 
+#include "ipc/AutoOpenSurface.h"
 #include "ipc/ShadowLayerChild.h"
 #include "BasicLayers.h"
 #include "BasicImplData.h"
 #include "ReadbackProcessor.h"
 
 namespace mozilla {
 namespace layers {
 
@@ -57,24 +58,68 @@ public:
 
 protected:
   BasicLayerManager* BasicManager()
   {
     return static_cast<BasicLayerManager*>(mManager);
   }
 };
 
+/**
+ * Drawing with a mask requires a mask surface and a transform.
+ * Sometimes the mask surface is a direct gfxASurface, but other times
+ * it's a SurfaceDescriptor.  For SurfaceDescriptor, we need to use a
+ * scoped AutoOpenSurface to get a gfxASurface for the
+ * SurfaceDescriptor.
+ *
+ * This helper class manages the gfxASurface-or-SurfaceDescriptor
+ * logic.
+ */
+class NS_STACK_CLASS AutoMaskData {
+public:
+  AutoMaskData() { }
+  ~AutoMaskData() { }
+
+  /**
+   * Construct this out of either a gfxASurface or a
+   * SurfaceDescriptor.  Construct() must only be called once.
+   * GetSurface() and GetTransform() must not be called until this has
+   * been constructed.
+   */
+
+  void Construct(const gfxMatrix& aTransform,
+                 gfxASurface* aSurface);
+
+  void Construct(const gfxMatrix& aTransform,
+                 const SurfaceDescriptor& aSurface);
+
+  /** The returned surface can't escape the scope of |this|. */
+  gfxASurface* GetSurface();
+  const gfxMatrix& GetTransform();
+
+private:
+  bool IsConstructed();
+
+  gfxMatrix mTransform;
+  nsRefPtr<gfxASurface> mSurface;
+  Maybe<AutoOpenSurface> mSurfaceOpener;
+
+  AutoMaskData(const AutoMaskData&) MOZ_DELETE;
+  AutoMaskData& operator=(const AutoMaskData&) MOZ_DELETE;
+};
+
 /*
  * Extract a mask surface for a mask layer
- * Returns a surface for the mask layer if a mask layer is present and has a
- * valid surface and transform; nsnull otherwise.
- * The transform for the layer will be put in aMaskTransform
+ * Returns true and through outparams a surface for the mask layer if
+ * a mask layer is present and has a valid surface and transform;
+ * false otherwise.
+ * The transform for the layer will be put in aMaskData
  */
-already_AddRefed<gfxASurface>
-GetMaskSurfaceAndTransform(Layer* aMaskLayer, gfxMatrix* aMaskTransform);
+bool
+GetMaskData(Layer* aMaskLayer, AutoMaskData* aMaskData);
 
 // Paint the current source to a context using a mask, if present
 void
 PaintWithMask(gfxContext* aContext, float aOpacity, Layer* aMaskLayer);
 
 // Fill the current path with the current source, using a
 // mask and opacity, if present
 void
@@ -112,17 +157,17 @@ MaybeCreateShadowFor(BasicShadowableLaye
 
   PLayerChild* shadow = aMgr->ConstructShadowFor(aLayer);
   // XXX error handling
   NS_ABORT_IF_FALSE(shadow, "failed to create shadow");
 
   aLayer->SetShadow(shadow);
   (aMgr->*aMethod)(aLayer);
   aMgr->Hold(aLayer->AsLayer());
-}
+}
 
 #define MAYBE_CREATE_SHADOW(_type)                                      \
   MaybeCreateShadowFor(layer, this,                                     \
                        &ShadowLayerForwarder::Created ## _type ## Layer)
 
 }
 }
 
--- a/gfx/layers/basic/BasicThebesLayer.cpp
+++ b/gfx/layers/basic/BasicThebesLayer.cpp
@@ -223,28 +223,82 @@ BasicThebesLayer::PaintThebes(gfxContext
       NS_ASSERTION(opacity == 1.0, "Should only read back opaque layers");
       ctx->Translate(gfxPoint(offset.x, offset.y));
       mBuffer.DrawTo(this, ctx, 1.0, aMaskLayer);
       update.mLayer->GetSink()->EndUpdate(ctx, update.mUpdateRect + offset);
     }
   }
 }
 
+/**
+ * AutoOpenBuffer is a helper that builds on top of AutoOpenSurface,
+ * which we need to get a gfxASurface from a SurfaceDescriptor.  For
+ * other layer types, simple lexical scoping of AutoOpenSurface is
+ * easy.  For ThebesLayers, the lifetime of buffer mappings doesn't
+ * exactly match simple lexical scopes, so naively putting
+ * AutoOpenSurfaces on the stack doesn't always work.  We use this
+ * helper to track openings instead.
+ *
+ * Any surface that's opened while painting this ThebesLayer will
+ * notify this helper and register itself for unmapping.
+ *
+ * We ignore buffer destruction here because the shadow layers
+ * protocol already ensures that destroyed buffers stay alive until
+ * end-of-transaction.
+ */
+struct NS_STACK_CLASS AutoBufferTracker {
+  AutoBufferTracker(BasicShadowableThebesLayer* aLayer)
+    : mLayer(aLayer)
+  {
+    MOZ_ASSERT(!mLayer->mBufferTracker);
+
+    mLayer->mBufferTracker = this;
+    if (IsSurfaceDescriptorValid(mLayer->mBackBuffer)) {
+      mInitialBuffer.construct(OPEN_READ_WRITE, mLayer->mBackBuffer);
+      mLayer->mBuffer.MapBuffer(mInitialBuffer.ref().Get());
+    }
+  }
+
+  ~AutoBufferTracker() {
+    mLayer->mBufferTracker = nsnull;
+    mLayer->mBuffer.UnmapBuffer();
+    // mInitialBuffer and mNewBuffer will clean up after themselves if
+    // they were constructed.
+  }
+
+  gfxASurface*
+  CreatedBuffer(const SurfaceDescriptor& aDescriptor) {
+    Maybe<AutoOpenSurface>* surface = mNewBuffers.AppendElement();
+    surface->construct(OPEN_READ_WRITE, aDescriptor);
+    return surface->ref().Get();
+  }
+
+  Maybe<AutoOpenSurface> mInitialBuffer;
+  nsAutoTArray<Maybe<AutoOpenSurface>, 2> mNewBuffers;
+  BasicShadowableThebesLayer* mLayer;
+
+private:
+  AutoBufferTracker(const AutoBufferTracker&) MOZ_DELETE;
+  AutoBufferTracker& operator=(const AutoBufferTracker&) MOZ_DELETE;
+};
+
 void
 BasicShadowableThebesLayer::PaintThebes(gfxContext* aContext,
                                         Layer* aMaskLayer,
                                         LayerManager::DrawThebesLayerCallback aCallback,
                                         void* aCallbackData,
                                         ReadbackProcessor* aReadback)
 {
   if (!HasShadow()) {
     BasicThebesLayer::PaintThebes(aContext, aMaskLayer, aCallback, aCallbackData, aReadback);
     return;
   }
 
+  AutoBufferTracker tracker(this);
+
   BasicThebesLayer::PaintThebes(aContext, nsnull, aCallback, aCallbackData, aReadback);
   if (aMaskLayer) {
     static_cast<BasicImplData*>(aMaskLayer->ImplData())
       ->Paint(aContext, nsnull);
   }
 }
 
 
@@ -275,28 +329,32 @@ BasicShadowableThebesLayer::SetBackBuffe
 
 void
 BasicShadowableThebesLayer::SyncFrontBufferToBackBuffer()
 {
   if (!mFrontAndBackBufferDiffer) {
     return;
   }
 
-  nsRefPtr<gfxASurface> backBuffer;
+  gfxASurface* backBuffer = mBuffer.GetBuffer();
   if (!IsSurfaceDescriptorValid(mBackBuffer)) {
-    NS_ABORT_IF_FALSE(mROFrontBuffer.type() == OptionalThebesBuffer::TThebesBuffer,
-                      "should have a front RO buffer by now");
+    MOZ_ASSERT(!backBuffer);
+    MOZ_ASSERT(mROFrontBuffer.type() == OptionalThebesBuffer::TThebesBuffer);
     const ThebesBuffer roFront = mROFrontBuffer.get_ThebesBuffer();
-    nsRefPtr<gfxASurface> roFrontBuffer = BasicManager()->OpenDescriptor(roFront.buffer());
-    backBuffer = CreateBuffer(roFrontBuffer->GetContentType(), roFrontBuffer->GetSize());
-  } else {
-    backBuffer = BasicManager()->OpenDescriptor(mBackBuffer);
+    AutoOpenSurface roFrontBuffer(OPEN_READ_ONLY, roFront.buffer());
+    AllocBackBuffer(roFrontBuffer.ContentType(), roFrontBuffer.Size());
   }
   mFrontAndBackBufferDiffer = false;
 
+  Maybe<AutoOpenSurface> autoBackBuffer;
+  if (!backBuffer) {
+    autoBackBuffer.construct(OPEN_READ_WRITE, mBackBuffer);
+    backBuffer = autoBackBuffer.ref().Get();
+  }
+
   if (OptionalThebesBuffer::Tnull_t == mROFrontBuffer.type()) {
     // We didn't get back a read-only ref to our old back buffer (the
     // parent's new front buffer).  If the parent is pushing updates
     // to a texture it owns, then we probably got back the same buffer
     // we pushed in the update and all is well.  If not, ...
     mValidRegion = mFrontValidRegion;
     mBuffer.SetBackingBuffer(backBuffer, mBackBufferRect, mBackBufferRectRotation);
     return;
@@ -305,20 +363,20 @@ BasicShadowableThebesLayer::SyncFrontBuf
   MOZ_LAYERS_LOG(("BasicShadowableThebes(%p): reading back <x=%d,y=%d,w=%d,h=%d>",
                   this,
                   mFrontUpdatedRegion.GetBounds().x,
                   mFrontUpdatedRegion.GetBounds().y,
                   mFrontUpdatedRegion.GetBounds().width,
                   mFrontUpdatedRegion.GetBounds().height));
 
   const ThebesBuffer roFront = mROFrontBuffer.get_ThebesBuffer();
-  nsRefPtr<gfxASurface> roFrontBuffer = BasicManager()->OpenDescriptor(roFront.buffer());
+  AutoOpenSurface autoROFront(OPEN_READ_ONLY, roFront.buffer());
   mBuffer.SetBackingBufferAndUpdateFrom(
     backBuffer,
-    roFrontBuffer, roFront.rect(), roFront.rotation(),
+    autoROFront.Get(), roFront.rect(), roFront.rotation(),
     mFrontUpdatedRegion);
   mIsNewBuffer = false;
   // Now the new back buffer has the same (interesting) pixels as the
   // new front buffer, and mValidRegion et al. are correct wrt the new
   // back buffer (i.e. as they were for the old back buffer)
 }
 
 void
@@ -359,16 +417,33 @@ BasicShadowableThebesLayer::PaintBuffer(
                     "should have a back buffer by now");
   BasicManager()->PaintedThebesBuffer(BasicManager()->Hold(this),
                                       updatedRegion,
                                       mBuffer.BufferRect(),
                                       mBuffer.BufferRotation(),
                                       mBackBuffer);
 }
 
+void
+BasicShadowableThebesLayer::AllocBackBuffer(Buffer::ContentType aType,
+                                            const nsIntSize& aSize)
+{
+  // This function may *not* open the buffer it allocates.
+  if (!BasicManager()->AllocBuffer(gfxIntSize(aSize.width, aSize.height),
+                                   aType,
+                                   &mBackBuffer)) {
+    enum { buflen = 256 };
+    char buf[buflen];
+    PR_snprintf(buf, buflen,
+                "creating ThebesLayer 'back buffer' failed! width=%d, height=%d, type=%x",
+                aSize.width, aSize.height, int(aType));
+    NS_RUNTIMEABORT(buf);
+  }
+}
+
 already_AddRefed<gfxASurface>
 BasicShadowableThebesLayer::CreateBuffer(Buffer::ContentType aType,
                                          const nsIntSize& aSize)
 {
   if (!HasShadow()) {
     return BasicThebesLayer::CreateBuffer(aType, aSize);
   }
 
@@ -377,34 +452,25 @@ BasicShadowableThebesLayer::CreateBuffer
                   aSize.width, aSize.height));
 
   if (IsSurfaceDescriptorValid(mBackBuffer)) {
     BasicManager()->DestroyedThebesBuffer(BasicManager()->Hold(this),
                                           mBackBuffer);
     mBackBuffer = SurfaceDescriptor();
   }
 
-  // XXX error handling
-  if (!BasicManager()->AllocBuffer(gfxIntSize(aSize.width, aSize.height),
-                                   aType,
-                                   &mBackBuffer)) {
-      enum { buflen = 256 };
-      char buf[buflen];
-      PR_snprintf(buf, buflen,
-                  "creating ThebesLayer 'back buffer' failed! width=%d, height=%d, type=%x",
-                  aSize.width, aSize.height, int(aType));
-      NS_RUNTIMEABORT(buf);
-  }
+  AllocBackBuffer(aType, aSize);
 
   NS_ABORT_IF_FALSE(!mIsNewBuffer,
                     "Bad! Did we create a buffer twice without painting?");
 
   mIsNewBuffer = true;
 
-  return BasicManager()->OpenDescriptor(mBackBuffer);
+  nsRefPtr<gfxASurface> buffer = mBufferTracker->CreatedBuffer(mBackBuffer);
+  return buffer.forget();
 }
 
 void
 BasicShadowableThebesLayer::Disconnect()
 {
   mBackBuffer = SurfaceDescriptor();
   BasicShadowableLayer::Disconnect();
 }
@@ -478,43 +544,40 @@ private:
 void
 BasicShadowThebesLayer::Swap(const ThebesBuffer& aNewFront,
                              const nsIntRegion& aUpdatedRegion,
                              OptionalThebesBuffer* aNewBack,
                              nsIntRegion* aNewBackValidRegion,
                              OptionalThebesBuffer* aReadOnlyFront,
                              nsIntRegion* aFrontUpdatedRegion)
 {
-  nsRefPtr<gfxASurface> newFrontBuffer =
-    BasicManager()->OpenDescriptor(aNewFront.buffer());
-
   if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
-    nsRefPtr<gfxASurface> currentFront = BasicManager()->OpenDescriptor(mFrontBufferDescriptor);
-    if (currentFront->GetSize() != newFrontBuffer->GetSize()) {
+    AutoOpenSurface autoNewFrontBuffer(OPEN_READ_ONLY, aNewFront.buffer());
+    AutoOpenSurface autoCurrentFront(OPEN_READ_ONLY, mFrontBufferDescriptor);
+    if (autoCurrentFront.Size() != autoNewFrontBuffer.Size()) {
       // Current front buffer is obsolete
       DestroyFrontBuffer();
     }
   }
   // This code relies on Swap() arriving *after* attribute mutations.
   if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
     *aNewBack = ThebesBuffer();
     aNewBack->get_ThebesBuffer().buffer() = mFrontBufferDescriptor;
   } else {
     *aNewBack = null_t();
   }
   // We have to invalidate the pixels painted into the new buffer.
   // They might overlap with our old pixels.
   aNewBackValidRegion->Sub(mOldValidRegion, aUpdatedRegion);
 
-  nsRefPtr<gfxASurface> unused;
   nsIntRect backRect;
   nsIntPoint backRotation;
   mFrontBuffer.Swap(
-    newFrontBuffer, aNewFront.rect(), aNewFront.rotation(),
-    getter_AddRefs(unused), &backRect, &backRotation);
+    aNewFront.rect(), aNewFront.rotation(),
+    &backRect, &backRotation);
 
   if (aNewBack->type() != OptionalThebesBuffer::Tnull_t) {
     aNewBack->get_ThebesBuffer().rect() = backRect;
     aNewBack->get_ThebesBuffer().rotation() = backRotation;
   }
 
   mFrontBufferDescriptor = aNewFront.buffer();
 
@@ -529,21 +592,26 @@ BasicShadowThebesLayer::PaintThebes(gfxC
                                     void* aCallbackData,
                                     ReadbackProcessor* aReadback)
 {
   NS_ASSERTION(BasicManager()->InDrawing(),
                "Can only draw in drawing phase");
   NS_ASSERTION(BasicManager()->IsRetained(),
                "ShadowThebesLayer makes no sense without retained mode");
 
-  if (!mFrontBuffer.GetBuffer()) {
+  if (!IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
     return;
   }
 
+  AutoOpenSurface autoFrontBuffer(OPEN_READ_ONLY, mFrontBufferDescriptor);
+  mFrontBuffer.MapBuffer(autoFrontBuffer.Get());
+
   mFrontBuffer.DrawTo(this, aContext, GetEffectiveOpacity(), aMaskLayer);
+
+  mFrontBuffer.UnmapBuffer();
 }
 
 already_AddRefed<ThebesLayer>
 BasicLayerManager::CreateThebesLayer()
 {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
   nsRefPtr<ThebesLayer> layer = new BasicThebesLayer(this);
   return layer.forget();
--- a/gfx/layers/basic/BasicThebesLayer.h
+++ b/gfx/layers/basic/BasicThebesLayer.h
@@ -98,24 +98,29 @@ protected:
     nsIntRegion tmp;
     tmp.Or(mVisibleRegion, aExtendedRegionToDraw);
     mValidRegion.Or(mValidRegion, tmp);
   }
 
   Buffer mBuffer;
 };
 
+struct AutoBufferTracker;
+
 class BasicShadowableThebesLayer : public BasicThebesLayer,
                                    public BasicShadowableLayer
 {
+  friend struct AutoBufferTracker;
+
   typedef BasicThebesLayer Base;
 
 public:
   BasicShadowableThebesLayer(BasicShadowLayerManager* aManager)
     : BasicThebesLayer(aManager)
+    , mBufferTracker(nsnull)
     , mIsNewBuffer(false)
     , mFrontAndBackBufferDiffer(false)
   {
     MOZ_COUNT_CTOR(BasicShadowableThebesLayer);
   }
   virtual ~BasicShadowableThebesLayer()
   {
     DestroyBackBuffer();
@@ -158,16 +163,20 @@ private:
   PaintBuffer(gfxContext* aContext,
               const nsIntRegion& aRegionToDraw,
               const nsIntRegion& aExtendedRegionToDraw,
               const nsIntRegion& aRegionToInvalidate,
               bool aDidSelfCopy,
               LayerManager::DrawThebesLayerCallback aCallback,
               void* aCallbackData) MOZ_OVERRIDE;
 
+  // This function may *not* open the buffer it allocates.
+  void
+  AllocBackBuffer(Buffer::ContentType aType, const nsIntSize& aSize);
+
   virtual already_AddRefed<gfxASurface>
   CreateBuffer(Buffer::ContentType aType, const nsIntSize& aSize) MOZ_OVERRIDE;
 
   void DestroyBackBuffer()
   {
     if (IsSurfaceDescriptorValid(mBackBuffer)) {
       BasicManager()->ShadowLayerForwarder::DestroySharedSurface(&mBackBuffer);
     }
@@ -175,16 +184,22 @@ private:
 
   // This describes the gfxASurface we hand to mBuffer.  We keep a
   // copy of the descriptor here so that we can call
   // DestroySharedSurface() on the descriptor.
   SurfaceDescriptor mBackBuffer;
   nsIntRect mBackBufferRect;
   nsIntPoint mBackBufferRectRotation;
 
+  // This helper object lives on the stack during its lifetime and
+  // keeps track of buffers we might have mapped and/or allocated.
+  // When it goes out of scope on the stack, it unmaps whichever
+  // buffers have been mapped (if any).
+  AutoBufferTracker* mBufferTracker;
+
   bool mIsNewBuffer;
   OptionalThebesBuffer mROFrontBuffer;
   nsIntRegion mFrontUpdatedRegion;
   nsIntRegion mFrontValidRegion;
   PRPackedBool mFrontAndBackBufferDiffer;
 };
 
 }
--- a/gfx/layers/d3d9/CanvasLayerD3D9.cpp
+++ b/gfx/layers/d3d9/CanvasLayerD3D9.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "ipc/AutoOpenSurface.h"
 #include "mozilla/layers/PLayers.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "ShadowBufferD3D9.h"
 
 #include "gfxImageSurface.h"
 #include "gfxWindowsSurface.h"
 #include "gfxWindowsPlatform.h"
 
@@ -306,22 +307,21 @@ ShadowCanvasLayerD3D9::Init(bool needYFl
 void
 ShadowCanvasLayerD3D9::Swap(const CanvasSurface& aNewFront,
                             bool needYFlip,
                             CanvasSurface* aNewBack)
 {
   NS_ASSERTION(aNewFront.type() == CanvasSurface::TSurfaceDescriptor, 
     "ShadowCanvasLayerD3D9::Swap expected CanvasSurface surface");
 
-  nsRefPtr<gfxASurface> surf = 
-    ShadowLayerForwarder::OpenDescriptor(aNewFront);
+  AutoOpenSurface surf(OPEN_READ_ONLY, aNewFront);
   if (!mBuffer) {
     Init(needYFlip);
   }
-  mBuffer->Upload(surf, GetVisibleRegion().GetBounds());
+  mBuffer->Upload(surf.Get(), GetVisibleRegion().GetBounds());
 
   *aNewBack = aNewFront;
 }
 
 void
 ShadowCanvasLayerD3D9::DestroyFrontBuffer()
 {
   Destroy();
--- a/gfx/layers/d3d9/ImageLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "ipc/AutoOpenSurface.h"
 #include "mozilla/layers/PLayers.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "ShadowBufferD3D9.h"
 #include "gfxSharedImageSurface.h"
 
 #include "ImageLayerD3D9.h"
 #include "ThebesLayerD3D9.h"
 #include "gfxPlatform.h"
@@ -546,29 +547,27 @@ ShadowImageLayerD3D9::~ShadowImageLayerD
 void
 ShadowImageLayerD3D9::Swap(const SharedImage& aNewFront,
                            SharedImage* aNewBack)
 {
   if (aNewFront.type() == SharedImage::TSurfaceDescriptor) {
     if (!mBuffer) {
       mBuffer = new ShadowBufferD3D9(this);
     }
-    nsRefPtr<gfxASurface> surf =
-      ShadowLayerForwarder::OpenDescriptor(aNewFront.get_SurfaceDescriptor());
-
-    mBuffer->Upload(surf, GetVisibleRegion().GetBounds());
+    AutoOpenSurface surf(OPEN_READ_ONLY, aNewFront.get_SurfaceDescriptor());
+    mBuffer->Upload(surf.Get(), GetVisibleRegion().GetBounds());
   } else {
     const YUVImage& yuv = aNewFront.get_YUVImage();
 
-    nsRefPtr<gfxSharedImageSurface> surfY =
-      gfxSharedImageSurface::Open(yuv.Ydata());
-    nsRefPtr<gfxSharedImageSurface> surfU =
-      gfxSharedImageSurface::Open(yuv.Udata());
-    nsRefPtr<gfxSharedImageSurface> surfV =
-      gfxSharedImageSurface::Open(yuv.Vdata());
+    AutoOpenSurface asurfY(OPEN_READ_ONLY, yuv.Ydata());
+    AutoOpenSurface asurfU(OPEN_READ_ONLY, yuv.Udata());
+    AutoOpenSurface asurfV(OPEN_READ_ONLY, yuv.Vdata());
+    gfxImageSurface* surfY = asurfY.GetAsImage();
+    gfxImageSurface* surfU = asurfU.GetAsImage();
+    gfxImageSurface* surfV = asurfV.GetAsImage();
 
     PlanarYCbCrImage::Data data;
     data.mYChannel = surfY->Data();
     data.mYStride = surfY->Stride();
     data.mYSize = surfY->GetSize();
     data.mCbChannel = surfU->Data();
     data.mCrChannel = surfV->Data();
     data.mCbCrStride = surfU->Stride();
--- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp
@@ -3,16 +3,17 @@
  * 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 "mozilla/layers/PLayers.h"
 
 /* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
 #include "mozilla/Util.h"
 
+#include "ipc/AutoOpenSurface.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "ShadowBufferD3D9.h"
 
 #include "ThebesLayerD3D9.h"
 #include "gfxPlatform.h"
 
 #include "gfxWindowsPlatform.h"
 #include "gfxTeeSurface.h"
@@ -612,18 +613,18 @@ ShadowThebesLayerD3D9::Swap(const Thebes
                            OptionalThebesBuffer* aReadOnlyFront,
                            nsIntRegion* aFrontUpdatedRegion)
 {
   if (!mBuffer) {
     mBuffer = new ShadowBufferD3D9(this);
   }
 
   if (mBuffer) {
-    nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(aNewFront.buffer());
-    mBuffer->Upload(surf, GetVisibleRegion().GetBounds());
+    AutoOpenSurface surf(OPEN_READ_ONLY, aNewFront.buffer());
+    mBuffer->Upload(surf.Get(), GetVisibleRegion().GetBounds());
   }
 
   *aNewBack = aNewFront;
   *aNewBackValidRegion = mValidRegion;
   *aReadOnlyFront = null_t();
   aFrontUpdatedRegion->SetEmpty();
 }
 
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/AutoOpenSurface.h
@@ -0,0 +1,73 @@
+/* -*- 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/. */
+
+#ifndef mozilla_layers_AutoOpenSurface_h
+#define mozilla_layers_AutoOpenSurface_h 1
+
+#include "base/basictypes.h"
+
+#include "gfxASurface.h"
+#include "mozilla/layers/PLayers.h"
+#include "ShadowLayers.h"
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * Some surface types can be fairly expensive to open.  This helper
+ * tries to put off opening surfaces as long as it can, until
+ * ahsolutely necessary.  And after being forced to open, it remembers
+ * the mapping so it doesn't need to be redone.
+ */
+class NS_STACK_CLASS AutoOpenSurface
+{
+public:
+  typedef gfxASurface::gfxContentType gfxContentType;
+
+  /** |aDescriptor| must be valid while AutoOpenSurface is
+   * in scope. */
+  AutoOpenSurface(OpenMode aMode, const SurfaceDescriptor& aDescriptor);
+
+  ~AutoOpenSurface();
+
+  /**
+   * These helpers do not necessarily need to open the descriptor to
+   * return an answer.
+   */
+  gfxContentType ContentType();
+  gfxIntSize Size();
+
+  /** This can't escape the scope of AutoOpenSurface. */
+  gfxASurface* Get();
+
+  /**
+   * This can't escape the scope of AutoOpenSurface.
+   *
+   * This method is currently just a convenience wrapper around
+   * gfxASurface::GetAsImageSurface() --- it returns a valid surface
+   * exactly when this->Get()->GetAsImageSurface() would.  Clients
+   * that need guaranteed (fast) ImageSurfaces should allocate the
+   * underlying descriptor with capability MAP_AS_IMAGE_SURFACE, in
+   * which case this helper is guaranteed to succeed.
+   */
+  gfxImageSurface* GetAsImage();
+
+
+private:
+  SurfaceDescriptor mDescriptor;
+  nsRefPtr<gfxASurface> mSurface;
+  nsRefPtr<gfxImageSurface> mSurfaceAsImage;
+  OpenMode mMode;
+
+  AutoOpenSurface(const AutoOpenSurface&) MOZ_DELETE;
+  AutoOpenSurface& operator=(const AutoOpenSurface&) MOZ_DELETE;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // ifndef mozilla_layers_AutoOpenSurface_h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/PGrallocBuffer.ipdl
@@ -0,0 +1,28 @@
+/* -*- 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 protocol PLayers;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * This is a trivial protocol that's used to track gralloc buffers
+ * across thread contexts.  A live PGrallocBuffer actor always
+ * corresponds 1:1 to a pre-shared gralloc buffer (sharing is done by
+ * the PGrallocBuffer constructor).
+ */
+async protocol PGrallocBuffer {
+  manager PLayers;
+
+  /** Gralloc buffers can be "owned" by either parent or child. */
+both:
+  async __delete__();
+};
+
+} // namespace layers
+} // namespace mozilla
--- a/gfx/layers/ipc/PLayers.ipdl
+++ b/gfx/layers/ipc/PLayers.ipdl
@@ -1,30 +1,34 @@
 /* -*- 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 protocol PCompositor;
+include protocol PGrallocBuffer;
 include protocol PLayer;
 include protocol PRenderFrame;
 
 include "gfxipc/ShadowLayerUtils.h";
 
 using gfx3DMatrix;
+using gfxIntSize;
 using gfxPoint;
 using gfxRGBA;
 using nsIntPoint;
 using nsIntRect;
 using nsIntRegion;
 using nsIntSize;
+using mozilla::gfxContentType;
 using mozilla::GraphicsFilterType;
 using mozilla::layers::FrameMetrics;
+using mozilla::layers::MagicGrallocBufferHandle;
 using mozilla::layers::SurfaceDescriptorX11;
 using mozilla::null_t;
 using mozilla::WindowsHandle;
 
 /**
  * The layers protocol is spoken between thread contexts that manage
  * layer (sub)trees.  The protocol comprises atomically publishing
  * layer subtrees to a "shadow" thread context (which grafts the
@@ -37,30 +41,40 @@ namespace layers {
 
 // Create a shadow layer for |layer|
 struct OpCreateThebesLayer     { PLayer layer; };
 struct OpCreateContainerLayer  { PLayer layer; };
 struct OpCreateImageLayer      { PLayer layer; };
 struct OpCreateColorLayer      { PLayer layer; };
 struct OpCreateCanvasLayer     { PLayer layer; };
 
+union MaybeMagicGrallocBufferHandle {
+  MagicGrallocBufferHandle;
+  null_t;
+};
+
 struct SurfaceDescriptorD3D10 {
   WindowsHandle handle;
 };
 
+struct SurfaceDescriptorGralloc {
+  PGrallocBuffer buffer;
+};
+
 union SurfaceDescriptor {
   Shmem;
   SurfaceDescriptorD3D10;
+  SurfaceDescriptorGralloc;
   SurfaceDescriptorX11;
 };
 
 struct YUVImage {
-  Shmem Ydata;
-  Shmem Udata;
-  Shmem Vdata;
+  SurfaceDescriptor Ydata;
+  SurfaceDescriptor Udata;
+  SurfaceDescriptor Vdata;
   nsIntRect picture;
 };
 
 union SharedImage {
   SurfaceDescriptor;
   YUVImage;
   null_t;
 };
@@ -201,19 +215,27 @@ union EditReply {
   OpBufferSwap;
   OpThebesBufferSwap;
   OpImageSwap;
 };
 
 
 sync protocol PLayers {
   manager PRenderFrame or PCompositor;
+  manages PGrallocBuffer;
   manages PLayer;
 
 parent:
+  /**
+   * Only the parent side has privileges to allocate the buffer.
+   * Allocation may fail (pmem is a scarce resource), and if so null_t
+   * is returned.
+   */
+  sync PGrallocBuffer(gfxIntSize size, gfxContentType content)
+    returns (MaybeMagicGrallocBufferHandle handle);
   async PLayer();
 
   // The isFirstPaint flag can be used to indicate that this is the first update
   // for a particular document.
   sync Update(Edit[] cset, bool isFirstPaint)
     returns (EditReply[] reply);
 
   // Composite the layer tree to the given surface, and return the surface.
--- a/gfx/layers/ipc/ShadowLayerUtils.h
+++ b/gfx/layers/ipc/ShadowLayerUtils.h
@@ -20,16 +20,26 @@
 #else
 namespace mozilla { namespace layers {
 struct SurfaceDescriptorX11 {
   bool operator==(const SurfaceDescriptorX11&) const { return false; }
 };
 } }
 #endif
 
+#if defined(MOZ_WIDGET_GONK)
+# include "mozilla/layers/ShadowLayerUtilsGralloc.h"
+#else
+namespace mozilla { namespace layers {
+struct MagicGrallocBufferHandle {
+  bool operator==(const MagicGrallocBufferHandle&) const { return false; }
+};
+} }
+#endif
+
 namespace IPC {
 
 template <>
 struct ParamTraits<mozilla::layers::FrameMetrics>
 {
   typedef mozilla::layers::FrameMetrics paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
@@ -57,13 +67,22 @@ struct ParamTraits<mozilla::layers::Fram
 
 #if !defined(MOZ_HAVE_SURFACEDESCRIPTORX11)
 template <>
 struct ParamTraits<mozilla::layers::SurfaceDescriptorX11> {
   typedef mozilla::layers::SurfaceDescriptorX11 paramType;
   static void Write(Message*, const paramType&) {}
   static bool Read(const Message*, void**, paramType*) { return false; }
 };
-#endif  // !defined(MOZ_HAVE_XSURFACEDESCRIPTOR)
+#endif  // !defined(MOZ_HAVE_XSURFACEDESCRIPTORX11)
+
+#if !defined(MOZ_HAVE_SURFACEDESCRIPTORGRALLOC)
+template <>
+struct ParamTraits<mozilla::layers::MagicGrallocBufferHandle> {
+  typedef mozilla::layers::MagicGrallocBufferHandle paramType;
+  static void Write(Message*, const paramType&) {}
+  static bool Read(const Message*, void**, paramType*) { return false; }
+};
+#endif  // !defined(MOZ_HAVE_XSURFACEDESCRIPTORGRALLOC)
 
 }
 
 #endif // IPC_ShadowLayerUtils_h
--- a/gfx/layers/ipc/ShadowLayerUtilsD3D10.cpp
+++ b/gfx/layers/ipc/ShadowLayerUtilsD3D10.cpp
@@ -12,38 +12,57 @@
 #include "ShadowLayers.h"
 
 namespace mozilla {
 namespace layers {
 
 // Platform-specific shadow-layers interfaces.  See ShadowLayers.h.
 // D3D10 doesn't need all these yet.
 bool
-ShadowLayerForwarder::PlatformAllocDoubleBuffer(const gfxIntSize&,
-                                                gfxASurface::gfxContentType,
-                                                SurfaceDescriptor*,
-                                                SurfaceDescriptor*)
-{
-  return false;
-}
-
-bool
 ShadowLayerForwarder::PlatformAllocBuffer(const gfxIntSize&,
                                           gfxASurface::gfxContentType,
+                                          uint32_t,
                                           SurfaceDescriptor*)
 {
   return false;
 }
 
 /*static*/ already_AddRefed<gfxASurface>
-ShadowLayerForwarder::PlatformOpenDescriptor(const SurfaceDescriptor&)
+ShadowLayerForwarder::PlatformOpenDescriptor(OpenMode,
+                                             const SurfaceDescriptor&)
 {
   return nsnull;
 }
 
+/*static*/ bool
+ShadowLayerForwarder::PlatformCloseDescriptor(const SurfaceDescriptor&)
+{
+  return false;
+}
+
+/*static*/ bool
+ShadowLayerForwarder::PlatformGetDescriptorSurfaceContentType(
+  const SurfaceDescriptor&,
+  OpenMode,
+  gfxContentType*,
+  gfxASurface**)
+{
+  return false;
+}
+
+/*static*/ bool
+ShadowLayerForwarder::PlatformGetDescriptorSurfaceSize(
+  const SurfaceDescriptor&,
+  OpenMode,
+  gfxIntSize*,
+  gfxASurface**)
+{
+  return false;
+}
+
 bool
 ShadowLayerForwarder::PlatformDestroySharedSurface(SurfaceDescriptor*)
 {
   return false;
 }
 
 /*static*/ void
 ShadowLayerForwarder::PlatformSyncBeforeUpdate()
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.cpp
@@ -0,0 +1,348 @@
+/* -*- 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 "mozilla/layers/PGrallocBufferChild.h"
+#include "mozilla/layers/PGrallocBufferParent.h"
+#include "mozilla/layers/PLayersChild.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/unused.h"
+#include "nsXULAppAPI.h"
+
+#include "ShadowLayerUtilsGralloc.h"
+
+#include "gfxImageSurface.h"
+
+using namespace android;
+using namespace base;
+using namespace mozilla::layers;
+
+namespace IPC {
+
+void
+ParamTraits<MagicGrallocBufferHandle>::Write(Message* aMsg,
+                                             const paramType& aParam)
+{
+  Flattenable *flattenable = aParam.mGraphicBuffer.get();
+  size_t nbytes = flattenable->getFlattenedSize();
+  size_t nfds = flattenable->getFdCount();
+
+  char data[nbytes];
+  int fds[nfds];
+  flattenable->flatten(data, nbytes, fds, nfds);
+
+  aMsg->WriteSize(nbytes);
+  aMsg->WriteSize(nfds);
+
+  aMsg->WriteBytes(data, nbytes);
+  for (size_t n = 0; n < nfds; ++n) {
+    // These buffers can't die in transit because they're created
+    // synchonously and the parent-side buffer can only be dropped if
+    // there's a crash.
+    aMsg->WriteFileDescriptor(FileDescriptor(fds[n], false));
+  }
+}
+
+bool
+ParamTraits<MagicGrallocBufferHandle>::Read(const Message* aMsg,
+                                            void** aIter, paramType* aResult)
+{
+  size_t nbytes;
+  size_t nfds;
+  const char* data;
+
+  if (!aMsg->ReadSize(aIter, &nbytes) ||
+      !aMsg->ReadSize(aIter, &nfds) ||
+      !aMsg->ReadBytes(aIter, &data, nbytes)) {
+    return false;
+  }
+  
+  int fds[nfds];
+
+  for (size_t n = 0; n < nfds; ++n) {
+    FileDescriptor fd;
+    if (!aMsg->ReadFileDescriptor(aIter, &fd)) {
+      return false;
+    }
+    // If the GraphicBuffer was shared cross-process, SCM_RIGHTS does
+    // the right thing and dup's the fd.  If it's shared cross-thread,
+    // SCM_RIGHTS doesn't dup the fd.  That's surprising, but we just
+    // deal with it here.  NB: only the "default" (master) process can
+    // alloc gralloc buffers.
+    bool sameProcess = (XRE_GetProcessType() == GeckoProcessType_Default);
+    int dupFd = sameProcess ? dup(fd.fd) : fd.fd;
+    fds[n] = dupFd;
+  }
+
+  sp<GraphicBuffer> buffer(new GraphicBuffer());
+  Flattenable *flattenable = buffer.get();
+
+  if (NO_ERROR == flattenable->unflatten(data, nbytes, fds, nfds)) {
+    aResult->mGraphicBuffer = buffer;
+    return true;
+  }
+  return false;
+}
+
+} // namespace IPC
+
+namespace mozilla {
+namespace layers {
+
+MagicGrallocBufferHandle::MagicGrallocBufferHandle(const sp<GraphicBuffer>& aGraphicBuffer)
+  : mGraphicBuffer(aGraphicBuffer)
+{
+}
+
+//-----------------------------------------------------------------------------
+// Parent process
+
+static gfxASurface::gfxImageFormat
+ImageFormatForPixelFormat(android::PixelFormat aFormat)
+{
+  switch (aFormat) {
+  case PIXEL_FORMAT_RGBA_8888:
+    return gfxASurface::ImageFormatARGB32;
+  case PIXEL_FORMAT_RGBX_8888:
+    return gfxASurface::ImageFormatRGB24;
+  case PIXEL_FORMAT_RGB_565:
+    return gfxASurface::ImageFormatRGB16_565;
+  case PIXEL_FORMAT_A_8:
+    return gfxASurface::ImageFormatA8;
+  default:
+    MOZ_NOT_REACHED("Unknown gralloc pixel format");
+  }
+  return gfxASurface::ImageFormatARGB32;
+}
+
+static android::PixelFormat
+PixelFormatForImageFormat(gfxASurface::gfxImageFormat aFormat)
+{
+  switch (aFormat) {
+  case gfxASurface::ImageFormatARGB32:
+    return android::PIXEL_FORMAT_RGBA_8888;
+  case gfxASurface::ImageFormatRGB24:
+    return android::PIXEL_FORMAT_RGBX_8888;
+  case gfxASurface::ImageFormatRGB16_565:
+    return android::PIXEL_FORMAT_RGB_565;
+  case gfxASurface::ImageFormatA8:
+    return android::PIXEL_FORMAT_A_8;
+  default:
+    MOZ_NOT_REACHED("Unknown gralloc pixel format");
+  }
+  return gfxASurface::ImageFormatARGB32;
+}
+
+static android::PixelFormat
+PixelFormatForContentType(gfxASurface::gfxContentType aContentType)
+{
+  return PixelFormatForImageFormat(
+    gfxPlatform::GetPlatform()->OptimalFormatForContent(aContentType));
+}
+
+static gfxASurface::gfxContentType
+ContentTypeFromPixelFormat(android::PixelFormat aFormat)
+{
+  return gfxASurface::ContentFromFormat(ImageFormatForPixelFormat(aFormat));
+}
+
+/*static*/ PGrallocBufferParent*
+GrallocBufferActor::Create(const gfxIntSize& aSize,
+                           const gfxContentType& aContent,
+                           MaybeMagicGrallocBufferHandle* aOutHandle)
+{
+  GrallocBufferActor* actor = new GrallocBufferActor();
+  *aOutHandle = null_t();
+  android::PixelFormat format = PixelFormatForContentType(aContent);
+  sp<GraphicBuffer> buffer(
+    new GraphicBuffer(aSize.width, aSize.height, format,
+                      GraphicBuffer::USAGE_SW_READ_OFTEN |
+                      GraphicBuffer::USAGE_SW_WRITE_OFTEN |
+                      GraphicBuffer::USAGE_HW_TEXTURE));
+  if (buffer->initCheck() != OK)
+    return actor;
+
+  actor->mGraphicBuffer = buffer;
+  *aOutHandle = MagicGrallocBufferHandle(buffer);
+  return actor;
+}
+
+bool
+ShadowLayerManager::PlatformDestroySharedSurface(SurfaceDescriptor* aSurface)
+{
+  if (SurfaceDescriptor::TSurfaceDescriptorGralloc != aSurface->type()) {
+    return false;
+  }
+
+  PGrallocBufferParent* gbp =
+    aSurface->get_SurfaceDescriptorGralloc().bufferParent();
+  unused << PGrallocBufferParent::Send__delete__(gbp);
+  *aSurface = SurfaceDescriptor();
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+// Child process
+
+/*static*/ PGrallocBufferChild*
+GrallocBufferActor::Create()
+{
+  return new GrallocBufferActor();
+}
+
+void
+GrallocBufferActor::InitFromHandle(const MagicGrallocBufferHandle& aHandle)
+{
+  MOZ_ASSERT(!mGraphicBuffer.get());
+  MOZ_ASSERT(aHandle.mGraphicBuffer.get());
+
+  mGraphicBuffer = aHandle.mGraphicBuffer;
+}
+
+bool
+ShadowLayerForwarder::PlatformAllocBuffer(const gfxIntSize& aSize,
+                                          gfxASurface::gfxContentType aContent,
+                                          uint32_t aCaps,
+                                          SurfaceDescriptor* aBuffer)
+{
+  // Gralloc buffers are efficiently mappable as gfxImageSurface, so
+  // no need to check |aCaps & MAP_AS_IMAGE_SURFACE|.
+  MaybeMagicGrallocBufferHandle handle;
+  PGrallocBufferChild* gc =
+    mShadowManager->SendPGrallocBufferConstructor(aSize, aContent, &handle);
+  if (handle.Tnull_t == handle.type()) {
+    PGrallocBufferChild::Send__delete__(gc);
+    return false;
+  }
+
+  GrallocBufferActor* gba = static_cast<GrallocBufferActor*>(gc);
+  gba->InitFromHandle(handle.get_MagicGrallocBufferHandle());
+
+  *aBuffer = SurfaceDescriptorGralloc(nsnull, gc);
+  return true;
+}
+
+//-----------------------------------------------------------------------------
+// Both processes
+
+/*static*/ sp<GraphicBuffer>
+GrallocBufferActor::GetFrom(const SurfaceDescriptorGralloc& aDescriptor)
+{
+  GrallocBufferActor* gba = nsnull;
+  if (PGrallocBufferChild* child = aDescriptor.bufferChild()) {
+    gba = static_cast<GrallocBufferActor*>(child);
+  } else if (PGrallocBufferParent* parent = aDescriptor.bufferParent()) {
+    gba = static_cast<GrallocBufferActor*>(parent);
+  }
+  return gba->mGraphicBuffer;
+}
+
+
+/*static*/ already_AddRefed<gfxASurface>
+ShadowLayerForwarder::PlatformOpenDescriptor(OpenMode aMode,
+                                             const SurfaceDescriptor& aSurface)
+{
+  if (SurfaceDescriptor::TSurfaceDescriptorGralloc != aSurface.type()) {
+    return nsnull;
+  }
+
+  sp<GraphicBuffer> buffer =
+    GrallocBufferActor::GetFrom(aSurface.get_SurfaceDescriptorGralloc());
+  uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN;
+  if (OPEN_READ_WRITE == aMode) {
+    usage |= GRALLOC_USAGE_SW_WRITE_OFTEN;
+  }
+  void *vaddr;
+  DebugOnly<status_t> status = buffer->lock(usage, &vaddr);
+  // If we fail to lock, we'll just end up aborting anyway.
+  MOZ_ASSERT(status == OK);
+
+  gfxIntSize size = gfxIntSize(buffer->getWidth(), buffer->getHeight());
+  gfxImageFormat format = ImageFormatForPixelFormat(buffer->getPixelFormat());
+  long pixelStride = buffer->getStride();
+  long byteStride = pixelStride * gfxASurface::BytePerPixelFromFormat(format);
+
+  nsRefPtr<gfxASurface> surf =
+    new gfxImageSurface((unsigned char*)vaddr, size, byteStride, format);
+  return surf->CairoStatus() ? nsnull : surf.forget();
+}
+
+/*static*/ bool
+ShadowLayerForwarder::PlatformGetDescriptorSurfaceContentType(
+  const SurfaceDescriptor& aDescriptor, OpenMode aMode,
+  gfxContentType* aContent,
+  gfxASurface** aSurface)
+{
+  if (SurfaceDescriptor::TSurfaceDescriptorGralloc != aDescriptor.type()) {
+    return false;
+  }
+
+  sp<GraphicBuffer> buffer =
+    GrallocBufferActor::GetFrom(aDescriptor.get_SurfaceDescriptorGralloc());
+  *aContent = ContentTypeFromPixelFormat(buffer->getPixelFormat());
+  return true;
+}
+
+/*static*/ bool
+ShadowLayerForwarder::PlatformGetDescriptorSurfaceSize(
+  const SurfaceDescriptor& aDescriptor, OpenMode aMode,
+  gfxIntSize* aSize,
+  gfxASurface** aSurface)
+{
+  if (SurfaceDescriptor::TSurfaceDescriptorGralloc != aDescriptor.type()) {
+    return false;
+  }
+
+  sp<GraphicBuffer> buffer =
+    GrallocBufferActor::GetFrom(aDescriptor.get_SurfaceDescriptorGralloc());
+  *aSize = gfxIntSize(buffer->getWidth(), buffer->getHeight());
+  return true;
+}
+
+/*static*/ bool
+ShadowLayerForwarder::PlatformDestroySharedSurface(SurfaceDescriptor* aSurface)
+{
+  if (SurfaceDescriptor::TSurfaceDescriptorGralloc != aSurface->type()) {
+    return false;
+  }
+
+  PGrallocBufferChild* gbp =
+    aSurface->get_SurfaceDescriptorGralloc().bufferChild();
+  PGrallocBufferChild::Send__delete__(gbp);
+  *aSurface = SurfaceDescriptor();
+  return true;
+}
+
+/*static*/ bool
+ShadowLayerForwarder::PlatformCloseDescriptor(const SurfaceDescriptor& aDescriptor)
+{
+  if (SurfaceDescriptor::TSurfaceDescriptorGralloc != aDescriptor.type()) {
+    return false;
+  }
+
+  sp<GraphicBuffer> buffer =
+    GrallocBufferActor::GetFrom(aDescriptor);
+  // If the buffer wasn't lock()d, this probably blows up.  But since
+  // PlatformCloseDescriptor() is private and only used by
+  // AutoOpenSurface, we want to know if the logic is wrong there.
+  buffer->unlock();
+  return true;
+}
+
+/*static*/ void
+ShadowLayerForwarder::PlatformSyncBeforeUpdate()
+{
+  // Nothing to be done for gralloc.
+}
+
+/*static*/ void
+ShadowLayerManager::PlatformSyncBeforeReplyUpdate()
+{
+  // Nothing to be done for gralloc.
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtilsGralloc.h
@@ -0,0 +1,101 @@
+/* -*- 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/. */
+
+#ifndef mozilla_layers_ShadowLayerUtilsGralloc_h
+#define mozilla_layers_ShadowLayerUtilsGralloc_h
+
+#include <unistd.h>
+#include <ui/GraphicBuffer.h>
+
+#include "IPC/IPCMessageUtils.h"
+#include "mozilla/layers/PGrallocBufferChild.h"
+#include "mozilla/layers/PGrallocBufferParent.h"
+
+#define MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+#define MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS
+
+class gfxASurface;
+
+namespace mozilla {
+namespace layers {
+
+class MaybeMagicGrallocBufferHandle;
+class SurfaceDescriptorGralloc;
+
+/**
+ * This class exists to share the underlying GraphicBuffer resources
+ * from one thread context to another.  This requires going through
+ * slow paths in the kernel so can be somewhat expensive.
+ *
+ * This is not just platform-specific, but also
+ * gralloc-implementation-specific.
+ */
+struct MagicGrallocBufferHandle {
+  typedef android::GraphicBuffer GraphicBuffer;
+
+  MagicGrallocBufferHandle()
+  { }
+
+  MagicGrallocBufferHandle(const android::sp<GraphicBuffer>& aGraphicBuffer);
+
+  // Default copy ctor and operator= are OK
+
+  bool operator==(const MagicGrallocBufferHandle& aOther) const {
+    return mGraphicBuffer == aOther.mGraphicBuffer;
+  }
+
+  android::sp<GraphicBuffer> mGraphicBuffer;
+};
+
+/**
+ * GrallocBufferActor is an "IPC wrapper" for an underlying
+ * GraphicBuffer (pmem region).  It allows us to cheaply and
+ * conveniently share gralloc handles between processes.
+ */
+class GrallocBufferActor : public PGrallocBufferChild
+                         , public PGrallocBufferParent
+{
+  friend class ShadowLayerForwarder;
+  typedef android::GraphicBuffer GraphicBuffer;
+
+public:
+  virtual ~GrallocBufferActor() {}
+
+  static PGrallocBufferParent*
+  Create(const gfxIntSize& aSize, const gfxContentType& aContent,
+         MaybeMagicGrallocBufferHandle* aOutHandle);
+
+  static PGrallocBufferChild*
+  Create();
+
+private:
+  GrallocBufferActor() {}
+
+  void InitFromHandle(const MagicGrallocBufferHandle& aHandle);
+
+  static android::sp<GraphicBuffer>
+  GetFrom(const SurfaceDescriptorGralloc& aDescriptor);
+
+  android::sp<GraphicBuffer> mGraphicBuffer;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::layers::MagicGrallocBufferHandle> {
+  typedef mozilla::layers::MagicGrallocBufferHandle paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam);
+  static bool Read(const Message* aMsg, void** aIter, paramType* aResult);
+};
+
+} // namespace IPC
+
+#endif  // mozilla_layers_ShadowLayerUtilsGralloc_h
--- a/gfx/layers/ipc/ShadowLayerUtilsX11.cpp
+++ b/gfx/layers/ipc/ShadowLayerUtilsX11.cpp
@@ -84,36 +84,32 @@ SurfaceDescriptorX11::OpenForeign() cons
       return nsnull;
 
     surf = new gfxXlibSurface(display, mId, visual, mSize);
   }
   return surf->CairoStatus() ? nsnull : surf.forget();
 }
 
 bool
-ShadowLayerForwarder::PlatformAllocDoubleBuffer(const gfxIntSize& aSize,
-                                                gfxASurface::gfxContentType aContent,
-                                                SurfaceDescriptor* aFrontBuffer,
-                                                SurfaceDescriptor* aBackBuffer)
-{
-  return (PlatformAllocBuffer(aSize, aContent, aFrontBuffer) &&
-          PlatformAllocBuffer(aSize, aContent, aBackBuffer));
-}
-
-bool
 ShadowLayerForwarder::PlatformAllocBuffer(const gfxIntSize& aSize,
                                           gfxASurface::gfxContentType aContent,
+                                          uint32_t aCaps,
                                           SurfaceDescriptor* aBuffer)
 {
   if (!UsingXCompositing()) {
     // If we're not using X compositing, we're probably compositing on
     // the client side, in which case X surfaces would just slow
     // things down.  Use Shmem instead.
     return false;
   }
+  if (MAP_AS_IMAGE_SURFACE & aCaps) {
+    // We can't efficiently map pixmaps as gfxImageSurface, in
+    // general.  Fall back on Shmem.
+    return false;
+  }
 
   gfxPlatform* platform = gfxPlatform::GetPlatform();
   nsRefPtr<gfxASurface> buffer = platform->CreateOffscreenSurface(aSize, aContent);
   if (!buffer ||
       buffer->GetType() != gfxASurface::SurfaceTypeXlib) {
     NS_ERROR("creating Xlib front/back surfaces failed!");
     return false;
   }
@@ -122,24 +118,50 @@ ShadowLayerForwarder::PlatformAllocBuffe
   // Release Pixmap ownership to the layers model
   bufferX->ReleasePixmap();
 
   *aBuffer = SurfaceDescriptorX11(bufferX);
   return true;
 }
 
 /*static*/ already_AddRefed<gfxASurface>
-ShadowLayerForwarder::PlatformOpenDescriptor(const SurfaceDescriptor& aSurface)
+ShadowLayerForwarder::PlatformOpenDescriptor(OpenMode aMode,
+                                             const SurfaceDescriptor& aSurface)
 {
   if (SurfaceDescriptor::TSurfaceDescriptorX11 != aSurface.type()) {
     return nsnull;
   }
   return aSurface.get_SurfaceDescriptorX11().OpenForeign();
 }
 
+/*static*/ bool
+ShadowLayerForwarder::PlatformCloseDescriptor(const SurfaceDescriptor& aDescriptor)
+{
+  // XIDs don't need to be "closed".
+  return false;
+}
+
+/*static*/ bool
+ShadowLayerForwarder::PlatformGetDescriptorSurfaceContentType(
+  const SurfaceDescriptor& aDescriptor, OpenMode aMode,
+  gfxContentType* aContent,
+  gfxASurface** aSurface)
+{
+  return false;
+}
+
+/*static*/ bool
+ShadowLayerForwarder::PlatformGetDescriptorSurfaceSize(
+  const SurfaceDescriptor& aDescriptor, OpenMode aMode,
+  gfxIntSize* aSize,
+  gfxASurface** aSurface)
+{
+  return false;
+}
+
 bool
 ShadowLayerForwarder::PlatformDestroySharedSurface(SurfaceDescriptor* aSurface)
 {
   if (SurfaceDescriptor::TSurfaceDescriptorX11 != aSurface->type()) {
     return false;
   }
   return TakeAndDestroyXlibSurface(aSurface);
 }
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -6,16 +6,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include <set>
 #include <vector>
 
 #include "gfxSharedImageSurface.h"
 #include "gfxPlatform.h"
 
+#include "AutoOpenSurface.h"
 #include "mozilla/ipc/SharedMemorySysV.h"
 #include "mozilla/layers/PLayerChild.h"
 #include "mozilla/layers/PLayersChild.h"
 #include "mozilla/layers/PLayersParent.h"
 #include "ShadowLayers.h"
 #include "ShadowLayerChild.h"
 #include "gfxipc/ShadowLayerUtils.h"
 #include "RenderTrace.h"
@@ -350,17 +351,17 @@ ShadowLayerForwarder::ShadowDrawToTarget
   SurfaceDescriptor descriptorIn, descriptorOut;
   AllocBuffer(aTarget->OriginalSurface()->GetSize(),
               aTarget->OriginalSurface()->GetContentType(),
               &descriptorIn);
   if (!mShadowManager->SendDrawToSurface(descriptorIn, &descriptorOut)) {
     return false;
   }
 
-  nsRefPtr<gfxASurface> surface = OpenDescriptor(descriptorOut);
+  nsRefPtr<gfxASurface> surface = OpenDescriptor(OPEN_READ_WRITE, descriptorOut);
   aTarget->SetOperator(gfxContext::OPERATOR_SOURCE);
   aTarget->DrawSurface(surface, surface->GetSize());
 
   surface = nsnull;
   DestroySharedSurface(&descriptorOut);
 
   return true;
 }
@@ -377,32 +378,16 @@ OptimalShmemType()
   // use for layers.
   return SharedMemory::TYPE_SYSV;
 #else
   return SharedMemory::TYPE_BASIC;
 #endif
 }
 
 bool
-ShadowLayerForwarder::AllocDoubleBuffer(const gfxIntSize& aSize,
-                                        gfxASurface::gfxContentType aContent,
-                                        gfxSharedImageSurface** aFrontBuffer,
-                                        gfxSharedImageSurface** aBackBuffer)
-{
-  return AllocBuffer(aSize, aContent, aFrontBuffer) &&
-         AllocBuffer(aSize, aContent, aBackBuffer);
-}
-
-void
-ShadowLayerForwarder::DestroySharedSurface(gfxSharedImageSurface* aSurface)
-{
-  mShadowManager->DeallocShmem(aSurface->GetShmem());
-}
-
-bool
 ShadowLayerForwarder::AllocBuffer(const gfxIntSize& aSize,
                                   gfxASurface::gfxContentType aContent,
                                   gfxSharedImageSurface** aBuffer)
 {
   NS_ABORT_IF_FALSE(HasShadowManager(), "no manager to forward to");
 
   SharedMemory::SharedMemoryType shmemType = OptimalShmemType();
   gfxASurface::gfxImageFormat format = gfxPlatform::GetPlatform()->OptimalFormatForContent(aContent);
@@ -413,84 +398,107 @@ ShadowLayerForwarder::AllocBuffer(const 
     return false;
 
   *aBuffer = nsnull;
   back.swap(*aBuffer);
   return true;
 }
 
 bool
-ShadowLayerForwarder::AllocDoubleBuffer(const gfxIntSize& aSize,
-                                        gfxASurface::gfxContentType aContent,
-                                        SurfaceDescriptor* aFrontBuffer,
-                                        SurfaceDescriptor* aBackBuffer)
+ShadowLayerForwarder::AllocBuffer(const gfxIntSize& aSize,
+                                  gfxASurface::gfxContentType aContent,
+                                  SurfaceDescriptor* aBuffer)
+{
+  return AllocBufferWithCaps(aSize, aContent, DEFAULT_BUFFER_CAPS, aBuffer);
+}
+
+bool
+ShadowLayerForwarder::AllocBufferWithCaps(const gfxIntSize& aSize,
+                                          gfxASurface::gfxContentType aContent,
+                                          uint32_t aCaps,
+                                          SurfaceDescriptor* aBuffer)
 {
   bool tryPlatformSurface = true;
 #ifdef DEBUG
   tryPlatformSurface = !PR_GetEnv("MOZ_LAYERS_FORCE_SHMEM_SURFACES");
 #endif
   if (tryPlatformSurface &&
-      PlatformAllocDoubleBuffer(aSize, aContent, aFrontBuffer, aBackBuffer)) {
-    return true;
-  }
-
-  nsRefPtr<gfxSharedImageSurface> front;
-  nsRefPtr<gfxSharedImageSurface> back;
-  if (!AllocDoubleBuffer(aSize, aContent,
-                         getter_AddRefs(front), getter_AddRefs(back))) {
-    return false;
-  }
-
-  *aFrontBuffer = front->GetShmem();
-  *aBackBuffer = back->GetShmem();
-  return true;
-}
-
-bool
-ShadowLayerForwarder::AllocBuffer(const gfxIntSize& aSize,
-                                  gfxASurface::gfxContentType aContent,
-                                  SurfaceDescriptor* aBuffer)
-{
-  bool tryPlatformSurface = true;
-#ifdef DEBUG
-  tryPlatformSurface = !PR_GetEnv("MOZ_LAYERS_FORCE_SHMEM_SURFACES");
-#endif
-  if (tryPlatformSurface &&
-      PlatformAllocBuffer(aSize, aContent, aBuffer)) {
+      PlatformAllocBuffer(aSize, aContent, aCaps, aBuffer)) {
     return true;
   }
 
   nsRefPtr<gfxSharedImageSurface> buffer;
   if (!AllocBuffer(aSize, aContent,
                    getter_AddRefs(buffer)))
     return false;
 
   *aBuffer = buffer->GetShmem();
   return true;
 }
 
 /*static*/ already_AddRefed<gfxASurface>
-ShadowLayerForwarder::OpenDescriptor(const SurfaceDescriptor& aSurface)
+ShadowLayerForwarder::OpenDescriptor(OpenMode aMode,
+                                     const SurfaceDescriptor& aSurface)
 {
-  nsRefPtr<gfxASurface> surf = PlatformOpenDescriptor(aSurface);
+  nsRefPtr<gfxASurface> surf = PlatformOpenDescriptor(aMode, aSurface);
   if (surf) {
     return surf.forget();
   }
 
   switch (aSurface.type()) {
   case SurfaceDescriptor::TShmem: {
     surf = gfxSharedImageSurface::Open(aSurface.get_Shmem());
     return surf.forget();
   }
   default:
     NS_RUNTIMEABORT("unexpected SurfaceDescriptor type!");
     return nsnull;
   }
 }
 
+/*static*/ gfxContentType
+ShadowLayerForwarder::GetDescriptorSurfaceContentType(
+  const SurfaceDescriptor& aDescriptor, OpenMode aMode,
+  gfxASurface** aSurface)
+{
+  gfxContentType content;
+  if (PlatformGetDescriptorSurfaceContentType(aDescriptor, aMode,
+                                              &content, aSurface)) {
+    return content;
+  }
+
+  nsRefPtr<gfxASurface> surface = OpenDescriptor(aMode, aDescriptor);
+  content = surface->GetContentType();
+  *aSurface = surface.forget().get();
+  return content;
+}
+
+/*static*/ gfxIntSize
+ShadowLayerForwarder::GetDescriptorSurfaceSize(
+  const SurfaceDescriptor& aDescriptor, OpenMode aMode,
+  gfxASurface** aSurface)
+{
+  gfxIntSize size;
+  if (PlatformGetDescriptorSurfaceSize(aDescriptor, aMode, &size, aSurface)) {
+    return size;
+  }
+
+  nsRefPtr<gfxASurface> surface = OpenDescriptor(aMode, aDescriptor);
+  size = surface->GetSize();
+  *aSurface = surface.forget().get();
+  return size;
+}
+
+/*static*/ void
+ShadowLayerForwarder::CloseDescriptor(const SurfaceDescriptor& aDescriptor)
+{
+  PlatformCloseDescriptor(aDescriptor);
+  // There's no "close" needed for Shmem surfaces.
+}
+
 // Destroy the Shmem SurfaceDescriptor |aSurface|.
 template<class ShmemDeallocator>
 static void
 DestroySharedShmemSurface(SurfaceDescriptor* aSurface,
                           ShmemDeallocator* aDeallocator)
 {
   switch (aSurface->type()) {
   case SurfaceDescriptor::TShmem: {
@@ -542,38 +550,57 @@ ShadowLayerManager::DestroySharedSurface
     DestroySharedShmemSurface(aSurface, aDeallocator);
   }
 }
 
 
 #if !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
 
 bool
-ShadowLayerForwarder::PlatformAllocDoubleBuffer(const gfxIntSize&,
-                                                gfxASurface::gfxContentType,
-                                                SurfaceDescriptor*,
-                                                SurfaceDescriptor*)
-{
-  return false;
-}
-
-bool
 ShadowLayerForwarder::PlatformAllocBuffer(const gfxIntSize&,
                                           gfxASurface::gfxContentType,
+                                          uint32_t,
                                           SurfaceDescriptor*)
 {
   return false;
 }
 
 /*static*/ already_AddRefed<gfxASurface>
-ShadowLayerForwarder::PlatformOpenDescriptor(const SurfaceDescriptor&)
+ShadowLayerForwarder::PlatformOpenDescriptor(OpenMode,
+                                             const SurfaceDescriptor&)
 {
   return nsnull;
 }
 
+/*static*/ bool
+ShadowLayerForwarder::PlatformCloseDescriptor(const SurfaceDescriptor&)
+{
+  return false;
+}
+
+/*static*/ bool
+ShadowLayerForwarder::PlatformGetDescriptorSurfaceContentType(
+  const SurfaceDescriptor&,
+  OpenMode,
+  gfxContentType*,
+  gfxASurface**)
+{
+  return false;
+}
+
+/*static*/ bool
+ShadowLayerForwarder::PlatformGetDescriptorSurfaceSize(
+  const SurfaceDescriptor&,
+  OpenMode,
+  gfxIntSize*,
+  gfxASurface**)
+{
+  return false;
+}
+
 bool
 ShadowLayerForwarder::PlatformDestroySharedSurface(SurfaceDescriptor*)
 {
   return false;
 }
 
 /*static*/ void
 ShadowLayerForwarder::PlatformSyncBeforeUpdate()
@@ -594,10 +621,65 @@ ShadowLayerManager::PlatformSyncBeforeRe
 #endif  // !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
 
 bool
 IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface)
 {
   return SurfaceDescriptor::T__None != aSurface.type();
 }
 
+AutoOpenSurface::AutoOpenSurface(OpenMode aMode,
+                                 const SurfaceDescriptor& aDescriptor)
+  : mDescriptor(aDescriptor)
+  , mMode(aMode)
+{
+  MOZ_ASSERT(IsSurfaceDescriptorValid(mDescriptor));
+}
+
+AutoOpenSurface::~AutoOpenSurface()
+{
+  if (mSurface) {
+    mSurface = nsnull;
+    ShadowLayerForwarder::CloseDescriptor(mDescriptor);
+  }
+}
+
+gfxContentType
+AutoOpenSurface::ContentType()
+{
+  if (mSurface) {
+    return mSurface->GetContentType();
+  }
+  return ShadowLayerForwarder::GetDescriptorSurfaceContentType(
+    mDescriptor, mMode, getter_AddRefs(mSurface));
+}
+
+gfxIntSize
+AutoOpenSurface::Size()
+{
+  if (mSurface) {
+    return mSurface->GetSize();
+  }
+  return ShadowLayerForwarder::GetDescriptorSurfaceSize(
+    mDescriptor, mMode, getter_AddRefs(mSurface));
+}
+
+gfxASurface*
+AutoOpenSurface::Get()
+{
+  if (!mSurface) {
+    mSurface = ShadowLayerForwarder::OpenDescriptor(mMode, mDescriptor);
+  }
+  return mSurface.get();
+}
+
+gfxImageSurface*
+AutoOpenSurface::GetAsImage()
+{
+  if (!mSurfaceAsImage) {
+    mSurfaceAsImage = Get()->GetAsImageSurface();
+  }
+  return mSurfaceAsImage.get();
+}
+
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -33,16 +33,30 @@ class ShadowCanvasLayer;
 class SurfaceDescriptor;
 class ThebesBuffer;
 class TiledLayerComposer;
 class Transaction;
 class SharedImage;
 class CanvasSurface;
 class BasicTiledLayerBuffer;
 
+enum BufferCapabilities {
+  DEFAULT_BUFFER_CAPS = 0,
+  /** 
+   * The allocated buffer must be efficiently mappable as a
+   * gfxImageSurface.
+   */
+  MAP_AS_IMAGE_SURFACE = 1 << 0
+};
+
+enum OpenMode {
+  OPEN_READ_ONLY,
+  OPEN_READ_WRITE
+};
+
 /**
  * We want to share layer trees across thread contexts and address
  * spaces for several reasons; chief among them
  *
  *  - a parent process can paint a child process's layer tree while
  *    the child process is blocked, say on content script.  This is
  *    important on mobile devices where UI responsiveness is key.
  *
@@ -76,17 +90,20 @@ class BasicTiledLayerBuffer;
  *
  * There are only shadow types for layers that have different shadow
  * vs. not-shadow behavior.  ColorLayers and ContainerLayers behave
  * the same way in both regimes (so far).
  */
 
 class ShadowLayerForwarder
 {
+  friend class AutoOpenSurface;
+
 public:
+  typedef gfxASurface::gfxContentType gfxContentType;
   typedef LayerManager::LayersBackend LayersBackend;
 
   virtual ~ShadowLayerForwarder();
 
   /**
    * Begin recording a transaction to be forwarded atomically to a
    * ShadowLayerManager.
    */
@@ -222,18 +239,18 @@ public:
    * The following Alloc/Open/Destroy interfaces abstract over the
    * details of working with surfaces that are shared across
    * processes.  They provide the glue between C++ Layers and the
    * ShadowLayer IPC system.
    *
    * The basic lifecycle is
    *
    *  - a Layer needs a buffer.  Its ShadowableLayer subclass calls
-   *    AllocDoubleBuffer(), then calls one of the Created*Buffer()
-   *    methods above to transfer the (temporary) front buffer to its
+   *    AllocBuffer(), then calls one of the Created*Buffer() methods
+   *    above to transfer the (temporary) front buffer to its
    *    ShadowLayer in the other process.  The Layer needs a
    *    gfxASurface to paint, so the ShadowableLayer uses
    *    OpenDescriptor(backBuffer) to get that surface, and hands it
    *    out to the Layer.
    *
    * - a Layer has painted new pixels.  Its ShadowableLayer calls one
    *   of the Painted*Buffer() methods above with the back buffer
    *   descriptor.  This notification is forwarded to the ShadowLayer,
@@ -250,44 +267,27 @@ public:
    *   ShadowLayer also calls DestroySharedSurface() on its front
    *   buffer, and the double-buffer pair is gone.
    */
 
   /**
    * Shmem (gfxSharedImageSurface) buffers are available on all
    * platforms, but they may not be optimal.
    *
-   * NB: this interface is being deprecated in favor of the
-   * SurfaceDescriptor variant below.
-   */
-  bool AllocDoubleBuffer(const gfxIntSize& aSize,
-                           gfxASurface::gfxContentType aContent,
-                           gfxSharedImageSurface** aFrontBuffer,
-                           gfxSharedImageSurface** aBackBuffer);
-  void DestroySharedSurface(gfxSharedImageSurface* aSurface);
-
-  bool AllocBuffer(const gfxIntSize& aSize,
-                     gfxASurface::gfxContentType aContent,
-                     gfxSharedImageSurface** aBuffer);
-
-  /**
    * In the absence of platform-specific buffers these fall back to
    * Shmem/gfxSharedImageSurface.
    */
-  bool AllocDoubleBuffer(const gfxIntSize& aSize,
-                           gfxASurface::gfxContentType aContent,
-                           SurfaceDescriptor* aFrontBuffer,
-                           SurfaceDescriptor* aBackBuffer);
+  bool AllocBuffer(const gfxIntSize& aSize,
+                   gfxASurface::gfxContentType aContent,
+                   SurfaceDescriptor* aBuffer);
 
-  bool AllocBuffer(const gfxIntSize& aSize,
-                     gfxASurface::gfxContentType aContent,
-                     SurfaceDescriptor* aBuffer);
-
-  static already_AddRefed<gfxASurface>
-  OpenDescriptor(const SurfaceDescriptor& aSurface);
+  bool AllocBufferWithCaps(const gfxIntSize& aSize,
+                           gfxASurface::gfxContentType aContent,
+                           uint32_t aCaps,
+                           SurfaceDescriptor* aBuffer);
 
   void DestroySharedSurface(SurfaceDescriptor* aSurface);
 
   /**
    * Construct a shadow of |aLayer| on the "other side", at the
    * ShadowLayerManager.
    */
   PLayerChild* ConstructShadowFor(ShadowableLayer* aLayer);
@@ -306,40 +306,79 @@ public:
   void SetMaxTextureSize(PRInt32 aMaxTextureSize) { mMaxTextureSize = aMaxTextureSize; }
 
 protected:
   ShadowLayerForwarder();
 
   PLayersChild* mShadowManager;
 
 private:
-  bool PlatformAllocDoubleBuffer(const gfxIntSize& aSize,
-                                   gfxASurface::gfxContentType aContent,
-                                   SurfaceDescriptor* aFrontBuffer,
-                                   SurfaceDescriptor* aBackBuffer);
+  bool AllocBuffer(const gfxIntSize& aSize,
+                   gfxASurface::gfxContentType aContent,
+                   gfxSharedImageSurface** aBuffer);
 
   bool PlatformAllocBuffer(const gfxIntSize& aSize,
-                             gfxASurface::gfxContentType aContent,
-                             SurfaceDescriptor* aBuffer);
+                           gfxASurface::gfxContentType aContent,
+                           uint32_t aCaps,
+                           SurfaceDescriptor* aBuffer);
+
+  /**
+   * Try to query the content type efficiently, but at worst map the
+   * surface and return it in *aSurface.
+   */
+  static gfxContentType
+  GetDescriptorSurfaceContentType(const SurfaceDescriptor& aDescriptor,
+                                  OpenMode aMode,
+                                  gfxASurface** aSurface);
+  /**
+   * It can be expensive to open a descriptor just to query its
+   * content type.  If the platform impl can do this cheaply, it will
+   * set *aContent and return true.
+   */
+  static bool
+  PlatformGetDescriptorSurfaceContentType(const SurfaceDescriptor& aDescriptor,
+                                          OpenMode aMode,
+                                          gfxContentType* aContent,
+                                          gfxASurface** aSurface);
+  // (Same as above, but for surface size.)
+  static gfxIntSize
+  GetDescriptorSurfaceSize(const SurfaceDescriptor& aDescriptor,
+                           OpenMode aMode,
+                           gfxASurface** aSurface);
+  static bool
+  PlatformGetDescriptorSurfaceSize(const SurfaceDescriptor& aDescriptor,
+                                   OpenMode aMode,
+                                   gfxIntSize* aSize,
+                                   gfxASurface** aSurface);
 
   static already_AddRefed<gfxASurface>
-  PlatformOpenDescriptor(const SurfaceDescriptor& aDescriptor);
+  OpenDescriptor(OpenMode aMode, const SurfaceDescriptor& aSurface);
+
+  static already_AddRefed<gfxASurface>
+  PlatformOpenDescriptor(OpenMode aMode, const SurfaceDescriptor& aDescriptor);
+
+  /** Make this descriptor unusable for gfxASurface clients.  A
+   * private interface with AutoOpenSurface. */
+  static void
+  CloseDescriptor(const SurfaceDescriptor& aDescriptor);
+
+  static bool
+  PlatformCloseDescriptor(const SurfaceDescriptor& aDescriptor);
 
   bool PlatformDestroySharedSurface(SurfaceDescriptor* aSurface);
 
   static void PlatformSyncBeforeUpdate();
 
   Transaction* mTxn;
   PRInt32 mMaxTextureSize;
   LayersBackend mParentBackend;
 
   bool mIsFirstPaint;
 };
 
-
 class ShadowLayerManager : public LayerManager
 {
 public:
   virtual ~ShadowLayerManager() {}
 
   virtual void GetBackendName(nsAString& name) { name.AssignLiteral("Shadow"); }
 
   void DestroySharedSurface(gfxSharedImageSurface* aSurface,
--- a/gfx/layers/ipc/ShadowLayersChild.cpp
+++ b/gfx/layers/ipc/ShadowLayersChild.cpp
@@ -2,29 +2,55 @@
  * 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 "ShadowLayerChild.h"
 #include "ShadowLayersChild.h"
+#include "ShadowLayerUtils.h"
 
 namespace mozilla {
 namespace layers {
 
 void
 ShadowLayersChild::Destroy()
 {
   NS_ABORT_IF_FALSE(0 == ManagedPLayerChild().Length(),
                     "layers should have been cleaned up by now");
   PLayersChild::Send__delete__(this);
   // WARNING: |this| has gone to the great heap in the sky
 }
 
+PGrallocBufferChild*
+ShadowLayersChild::AllocPGrallocBuffer(const gfxIntSize&,
+                                       const gfxContentType&,
+                                       MaybeMagicGrallocBufferHandle*)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+  return GrallocBufferActor::Create();
+#else
+  NS_RUNTIMEABORT("No gralloc buffers for you");
+  return nsnull;
+#endif
+}
+
+bool
+ShadowLayersChild::DeallocPGrallocBuffer(PGrallocBufferChild* actor)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+  delete actor;
+  return true;
+#else
+  NS_RUNTIMEABORT("Um, how did we get here?");
+  return false;
+#endif
+}
+
 PLayerChild*
 ShadowLayersChild::AllocPLayer()
 {
   // we always use the "power-user" ctor
   NS_RUNTIMEABORT("not reached");
   return NULL;
 }
 
--- a/gfx/layers/ipc/ShadowLayersChild.h
+++ b/gfx/layers/ipc/ShadowLayersChild.h
@@ -24,16 +24,21 @@ public:
    *
    * It is expected (checked with an assert) that all shadow layers
    * created by this have already been destroyed and
    * Send__delete__()d by the time this method is called.
    */
   void Destroy();
 
 protected:
-  NS_OVERRIDE virtual PLayerChild* AllocPLayer();
-  NS_OVERRIDE virtual bool DeallocPLayer(PLayerChild* actor);
+  virtual PGrallocBufferChild*
+  AllocPGrallocBuffer(const gfxIntSize&, const gfxContentType&,
+                      MaybeMagicGrallocBufferHandle*) MOZ_OVERRIDE;
+  virtual bool
+  DeallocPGrallocBuffer(PGrallocBufferChild* actor) MOZ_OVERRIDE;
+  virtual PLayerChild* AllocPLayer() MOZ_OVERRIDE;
+  virtual bool DeallocPLayer(PLayerChild* actor) MOZ_OVERRIDE;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // ifndef mozilla_layers_ShadowLayersChild_h
--- a/gfx/layers/ipc/ShadowLayersParent.cpp
+++ b/gfx/layers/ipc/ShadowLayersParent.cpp
@@ -2,30 +2,28 @@
  * 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 <vector>
 
-#include "ShadowLayersParent.h"
-#include "ShadowLayerParent.h"
-#include "ShadowLayers.h"
-#include "RenderTrace.h"
-
+#include "AutoOpenSurface.h"
+#include "CompositorParent.h"
+#include "gfxSharedImageSurface.h"
+#include "ImageLayers.h"
+#include "mozilla/layout/RenderFrameParent.h"
 #include "mozilla/unused.h"
-
-#include "mozilla/layout/RenderFrameParent.h"
-#include "CompositorParent.h"
-
-#include "gfxSharedImageSurface.h"
-
+#include "RenderTrace.h"
+#include "ShadowLayerParent.h"
+#include "ShadowLayersParent.h"
+#include "ShadowLayers.h"
+#include "ShadowLayerUtils.h"
 #include "TiledLayerBuffer.h"
-#include "ImageLayers.h"
 
 typedef std::vector<mozilla::layers::EditReply> EditReplyVector;
 
 using mozilla::layout::RenderFrameParent;
 
 namespace mozilla {
 namespace layers {
 
@@ -415,31 +413,56 @@ bool
 ShadowLayersParent::RecvDrawToSurface(const SurfaceDescriptor& surfaceIn,
                                       SurfaceDescriptor* surfaceOut)
 {
   *surfaceOut = surfaceIn;
   if (mDestroyed || layer_manager()->IsDestroyed()) {
     return true;
   }
 
-  nsRefPtr<gfxASurface> sharedSurface = ShadowLayerForwarder::OpenDescriptor(surfaceIn);
+  AutoOpenSurface sharedSurface(OPEN_READ_WRITE, surfaceIn);
 
   nsRefPtr<gfxASurface> localSurface =
-    gfxPlatform::GetPlatform()->CreateOffscreenSurface(sharedSurface->GetSize(),
-                                                       sharedSurface->GetContentType());
+    gfxPlatform::GetPlatform()->CreateOffscreenSurface(sharedSurface.Size(),
+                                                       sharedSurface.ContentType());
   nsRefPtr<gfxContext> context = new gfxContext(localSurface);
 
   layer_manager()->BeginTransactionWithTarget(context);
   layer_manager()->EndTransaction(NULL, NULL);
-  nsRefPtr<gfxContext> contextForCopy = new gfxContext(sharedSurface);
+  nsRefPtr<gfxContext> contextForCopy = new gfxContext(sharedSurface.Get());
   contextForCopy->SetOperator(gfxContext::OPERATOR_SOURCE);
   contextForCopy->DrawSurface(localSurface, localSurface->GetSize());
   return true;
 }
 
+PGrallocBufferParent*
+ShadowLayersParent::AllocPGrallocBuffer(const gfxIntSize& aSize,
+                                        const gfxContentType& aContent,
+                                        MaybeMagicGrallocBufferHandle* aOutHandle)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+  return GrallocBufferActor::Create(aSize, aContent, aOutHandle);
+#else
+  NS_RUNTIMEABORT("No gralloc buffers for you");
+  return nsnull;
+#endif
+}
+
+bool
+ShadowLayersParent::DeallocPGrallocBuffer(PGrallocBufferParent* actor)
+{
+#ifdef MOZ_HAVE_SURFACEDESCRIPTORGRALLOC
+  delete actor;
+  return true;
+#else
+  NS_RUNTIMEABORT("Um, how did we get here?");
+  return false;
+#endif
+}
+
 PLayerParent*
 ShadowLayersParent::AllocPLayer()
 {
   return new ShadowLayerParent();
 }
 
 bool
 ShadowLayersParent::DeallocPLayer(PLayerParent* actor)
--- a/gfx/layers/ipc/ShadowLayersParent.h
+++ b/gfx/layers/ipc/ShadowLayersParent.h
@@ -39,28 +39,34 @@ public:
   ShadowLayerManager* layer_manager() const { return mLayerManager; }
 
   ContainerLayer* GetRoot() const { return mRoot; }
 
   virtual void DestroySharedSurface(gfxSharedImageSurface* aSurface);
   virtual void DestroySharedSurface(SurfaceDescriptor* aSurface);
 
 protected:
-  NS_OVERRIDE virtual bool RecvUpdate(const EditArray& cset,
-                                      const bool& isFirstPaint,
-                                      EditReplyArray* reply);
+  virtual bool RecvUpdate(const EditArray& cset,
+                          const bool& isFirstPaint,
+                          EditReplyArray* reply) MOZ_OVERRIDE;
+
+  virtual bool RecvDrawToSurface(const SurfaceDescriptor& surfaceIn,
+                                 SurfaceDescriptor* surfaceOut) MOZ_OVERRIDE;
 
-  NS_OVERRIDE virtual bool RecvDrawToSurface(const SurfaceDescriptor& surfaceIn,
-                                             SurfaceDescriptor* surfaceOut);
+  virtual bool RecvUpdateNoSwap(const EditArray& cset,
+                                const bool& isFirstPaint) MOZ_OVERRIDE;
 
-  NS_OVERRIDE virtual bool RecvUpdateNoSwap(const EditArray& cset,
-                                            const bool& isFirstPaint);
+  virtual PGrallocBufferParent*
+  AllocPGrallocBuffer(const gfxIntSize& aSize, const gfxContentType& aContent,
+                      MaybeMagicGrallocBufferHandle* aOutHandle) MOZ_OVERRIDE;
+  virtual bool
+  DeallocPGrallocBuffer(PGrallocBufferParent* actor) MOZ_OVERRIDE;
 
-  NS_OVERRIDE virtual PLayerParent* AllocPLayer();
-  NS_OVERRIDE virtual bool DeallocPLayer(PLayerParent* actor);
+  virtual PLayerParent* AllocPLayer() MOZ_OVERRIDE;
+  virtual bool DeallocPLayer(PLayerParent* actor) MOZ_OVERRIDE;
 
 private:
   nsRefPtr<ShadowLayerManager> mLayerManager;
   ShadowLayersManager* mShadowLayersManager;
   // Hold the root because it might be grafted under various
   // containers in the "real" layer tree
   nsRefPtr<ContainerLayer> mRoot;
   // When the widget/frame/browser stuff in this process begins its
--- a/gfx/layers/ipc/ipdl.mk
+++ b/gfx/layers/ipc/ipdl.mk
@@ -1,9 +1,10 @@
 # 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/.
 
 IPDLSRCS = \
   PCompositor.ipdl \
+  PGrallocBuffer.ipdl \
   PLayer.ipdl \
   PLayers.ipdl \
   $(NULL)
--- a/gfx/layers/opengl/CanvasLayerOGL.cpp
+++ b/gfx/layers/opengl/CanvasLayerOGL.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "ipc/AutoOpenSurface.h"
 #include "mozilla/layers/PLayers.h"
 #include "mozilla/layers/ShadowLayers.h"
 
 #include "gfxSharedImageSurface.h"
 
 #include "CanvasLayerOGL.h"
 
 #include "gfxImageSurface.h"
@@ -300,40 +301,40 @@ void
 ShadowCanvasLayerOGL::Initialize(const Data& aData)
 {
   NS_RUNTIMEABORT("Incompatibe surface type");
 }
 
 void
 ShadowCanvasLayerOGL::Init(const CanvasSurface& aNewFront, bool needYFlip)
 {
-  nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(aNewFront);
+  AutoOpenSurface autoSurf(OPEN_READ_ONLY, aNewFront);
 
   mNeedsYFlip = needYFlip;
 
-  mTexImage = gl()->CreateTextureImage(surf->GetSize(),
-                                       surf->GetContentType(),
+  mTexImage = gl()->CreateTextureImage(autoSurf.Size(),
+                                       autoSurf.ContentType(),
                                        LOCAL_GL_CLAMP_TO_EDGE,
                                        mNeedsYFlip ? TextureImage::NeedsYFlip : TextureImage::NoFlags);
 }
 
 void
 ShadowCanvasLayerOGL::Swap(const CanvasSurface& aNewFront,
                            bool needYFlip,
                            CanvasSurface* aNewBack)
 {
   if (!mDestroyed) {
-    nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(aNewFront);
-    gfxIntSize sz = surf->GetSize();
+    AutoOpenSurface autoSurf(OPEN_READ_ONLY, aNewFront);
+    gfxIntSize sz = autoSurf.Size();
     if (!mTexImage || mTexImage->GetSize() != sz ||
-        mTexImage->GetContentType() != surf->GetContentType()) {
+        mTexImage->GetContentType() != autoSurf.ContentType()) {
       Init(aNewFront, needYFlip);
     }
     nsIntRegion updateRegion(nsIntRect(0, 0, sz.width, sz.height));
-    mTexImage->DirectUpdate(surf, updateRegion);
+    mTexImage->DirectUpdate(autoSurf.Get(), updateRegion);
   }
 
   *aNewBack = aNewFront;
 }
 
 void
 ShadowCanvasLayerOGL::DestroyFrontBuffer()
 {
@@ -360,16 +361,20 @@ ShadowCanvasLayerOGL::GetLayer()
 {
   return this;
 }
 
 void
 ShadowCanvasLayerOGL::RenderLayer(int aPreviousFrameBuffer,
                                   const nsIntPoint& aOffset)
 {
+  if (!mTexImage) {
+    return;
+  }
+
   mOGLManager->MakeCurrent();
 
   ShaderProgramOGL *program =
     mOGLManager->GetProgram(mTexImage->GetShaderProgramType(),
                             GetMaskLayer());
 
 
   gfx3DMatrix effectiveTransform = GetEffectiveTransform();
--- a/gfx/layers/opengl/ImageLayerOGL.cpp
+++ b/gfx/layers/opengl/ImageLayerOGL.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "gfxSharedImageSurface.h"
 
+#include "ipc/AutoOpenSurface.h"
 #include "ImageLayerOGL.h"
 #include "gfxImageSurface.h"
 #include "gfxUtils.h"
 #include "yuv_convert.h"
 #include "GLContextProvider.h"
 #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO)
 # include "GLXLibrary.h"
 # include "mozilla/X11Util.h"
@@ -655,39 +656,33 @@ ShadowImageLayerOGL::ShadowImageLayerOGL
 
 ShadowImageLayerOGL::~ShadowImageLayerOGL()
 {}
 
 bool
 ShadowImageLayerOGL::Init(const SharedImage& aFront)
 {
   if (aFront.type() == SharedImage::TSurfaceDescriptor) {
-    SurfaceDescriptor desc = aFront.get_SurfaceDescriptor();
-    nsRefPtr<gfxASurface> surf =
-      ShadowLayerForwarder::OpenDescriptor(desc);
-    mSize = surf->GetSize();
+    AutoOpenSurface autoSurf(OPEN_READ_ONLY, aFront.get_SurfaceDescriptor());
+    mSize = autoSurf.Size();
     mTexImage = gl()->CreateTextureImage(nsIntSize(mSize.width, mSize.height),
-                                         surf->GetContentType(),
+                                         autoSurf.ContentType(),
                                          LOCAL_GL_CLAMP_TO_EDGE,
                                          mForceSingleTile
                                           ? TextureImage::ForceSingleTile
                                           : TextureImage::NoFlags);
     return true;
   } else {
     YUVImage yuv = aFront.get_YUVImage();
 
-    nsRefPtr<gfxSharedImageSurface> surfY =
-      gfxSharedImageSurface::Open(yuv.Ydata());
-    nsRefPtr<gfxSharedImageSurface> surfU =
-      gfxSharedImageSurface::Open(yuv.Udata());
-    nsRefPtr<gfxSharedImageSurface> surfV =
-      gfxSharedImageSurface::Open(yuv.Vdata());
+    AutoOpenSurface surfY(OPEN_READ_ONLY, yuv.Ydata());
+    AutoOpenSurface surfU(OPEN_READ_ONLY, yuv.Udata());
 
-    mSize = surfY->GetSize();
-    mCbCrSize = surfU->GetSize();
+    mSize = surfY.Size();
+    mCbCrSize = surfU.Size();
 
     if (!mYUVTexture[0].IsAllocated()) {
       mYUVTexture[0].Allocate(gl());
       mYUVTexture[1].Allocate(gl());
       mYUVTexture[2].Allocate(gl());
     }
 
     NS_ASSERTION(mYUVTexture[0].IsAllocated() &&
@@ -705,35 +700,34 @@ ShadowImageLayerOGL::Init(const SharedIm
 }
 
 void
 ShadowImageLayerOGL::Swap(const SharedImage& aNewFront,
                           SharedImage* aNewBack)
 {
   if (!mDestroyed) {
     if (aNewFront.type() == SharedImage::TSurfaceDescriptor) {
-      nsRefPtr<gfxASurface> surf =
-        ShadowLayerForwarder::OpenDescriptor(aNewFront.get_SurfaceDescriptor());
-      gfxIntSize size = surf->GetSize();
+      AutoOpenSurface surf(OPEN_READ_ONLY, aNewFront.get_SurfaceDescriptor());
+      gfxIntSize size = surf.Size();
       if (mSize != size || !mTexImage ||
-          mTexImage->GetContentType() != surf->GetContentType()) {
+          mTexImage->GetContentType() != surf.ContentType()) {
         Init(aNewFront);
       }
       // XXX this is always just ridiculously slow
       nsIntRegion updateRegion(nsIntRect(0, 0, size.width, size.height));
-      mTexImage->DirectUpdate(surf, updateRegion);
+      mTexImage->DirectUpdate(surf.Get(), updateRegion);
     } else {
       const YUVImage& yuv = aNewFront.get_YUVImage();
 
-      nsRefPtr<gfxSharedImageSurface> surfY =
-        gfxSharedImageSurface::Open(yuv.Ydata());
-      nsRefPtr<gfxSharedImageSurface> surfU =
-        gfxSharedImageSurface::Open(yuv.Udata());
-      nsRefPtr<gfxSharedImageSurface> surfV =
-        gfxSharedImageSurface::Open(yuv.Vdata());
+      AutoOpenSurface asurfY(OPEN_READ_ONLY, yuv.Ydata());
+      AutoOpenSurface asurfU(OPEN_READ_ONLY, yuv.Udata());
+      AutoOpenSurface asurfV(OPEN_READ_ONLY, yuv.Vdata());
+      nsRefPtr<gfxImageSurface> surfY = asurfY.GetAsImage();
+      nsRefPtr<gfxImageSurface> surfU = asurfU.GetAsImage();
+      nsRefPtr<gfxImageSurface> surfV = asurfV.GetAsImage();
       mPictureRect = yuv.picture();
 
       gfxIntSize size = surfY->GetSize();
       gfxIntSize CbCrSize = surfU->GetSize();
       if (size != mSize || mCbCrSize != CbCrSize || !mYUVTexture[0].IsAllocated()) {
         Init(aNewFront);
       }
 
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/ThebesLayerOGL.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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 "ipc/AutoOpenSurface.h"
 #include "mozilla/layers/PLayers.h"
 #include "TiledLayerBuffer.h"
 
 /* This must occur *after* layers/PLayers.h to avoid typedefs conflicts. */
 #include "mozilla/Util.h"
 
 #include "mozilla/layers/ShadowLayers.h"
 
@@ -963,17 +964,17 @@ ShadowThebesLayerOGL::ShadowThebesLayerO
 }
 
 ShadowThebesLayerOGL::~ShadowThebesLayerOGL()
 {}
 
 bool
 ShadowThebesLayerOGL::ShouldDoubleBuffer()
 {
-#ifdef ANDROID
+#ifdef MOZ_JAVA_COMPOSITOR
   /* Enable double-buffering on Android so that we don't block for as long
    * when uploading textures. This is a work-around for the lack of an
    * asynchronous texture upload facility.
    *
    * As the progressive upload relies on tile size, doing this when large
    * tiles are in use is harder to justify.
    *
    * XXX When bug 730718 is fixed, we will likely want this only to apply for
@@ -987,17 +988,18 @@ ShadowThebesLayerOGL::ShouldDoubleBuffer
 }
 
 void
 ShadowThebesLayerOGL::EnsureTextureUpdated()
 {
   if (mRegionPendingUpload.IsEmpty() || !IsSurfaceDescriptorValid(mFrontBufferDescriptor))
     return;
 
-  mBuffer->DirectUpdate(mFrontBuffer.Buffer(), mRegionPendingUpload);
+  AutoOpenSurface frontSurface(OPEN_READ_ONLY, mFrontBuffer.Buffer());
+  mBuffer->DirectUpdate(frontSurface.Get(), mRegionPendingUpload);
   mRegionPendingUpload.SetEmpty();
 }
 
 static bool
 EnsureTextureUpdatedCallback(gl::TextureImage* aImage, int aTileNumber,
                              void *aData)
 {
   // If this tile intersects with the region we asked to update, it will be
@@ -1040,34 +1042,34 @@ ShadowThebesLayerOGL::EnsureTextureUpdat
 
     // Check if any part of the texture that's pending upload intersects with
     // this region.
     updateRegion.And(aRegion, mRegionPendingUpload);
 
     if (updateRegion.IsEmpty())
       continue;
 
+    AutoOpenSurface surface(OPEN_READ_ONLY, mFrontBuffer.Buffer());
     nsRefPtr<TextureImage> texImage;
     if (!gl()->CanUploadSubTextures()) {
       // When sub-textures are unsupported, TiledTextureImage expands the
       // boundaries of DirectUpdate to tile boundaries. So that we don't
       // re-upload texture data, use the tile iteration to monitor how much
       // of the texture was actually uploaded.
-      gfxASurface* surface = mFrontBuffer.Buffer();
-      gfxIntSize size = surface->GetSize();
-      mBuffer->EnsureTexture(size, surface->GetContentType());
+      gfxIntSize size = surface.Size();
+      mBuffer->EnsureTexture(size, surface.ContentType());
       texImage = mBuffer->GetTextureImage().get();
       if (texImage->GetTileCount() > 1)
         texImage->SetIterationCallback(EnsureTextureUpdatedCallback, (void *)&updateRegion);
       else
         updateRegion = nsIntRect(0, 0, size.width, size.height);
     }
 
     // Upload this quadrant of the region.
-    mBuffer->DirectUpdate(mFrontBuffer.Buffer(), updateRegion);
+    mBuffer->DirectUpdate(surface.Get(), updateRegion);
 
     if (!gl()->CanUploadSubTextures())
       texImage->SetIterationCallback(nsnull, nsnull);
 
     // Remove the updated region from the pending-upload region.
     mRegionPendingUpload.Sub(mRegionPendingUpload, updateRegion);
   }
 }
@@ -1097,26 +1099,27 @@ ShadowThebesLayerOGL::ProgressiveUpload(
   // Mark the task as completed
   mUploadTask = nsnull;
 
   if (mRegionPendingUpload.IsEmpty() || mBuffer == nsnull)
     return;
 
   // Set a tile iteration callback so we can cancel the upload after a tile
   // has been uploaded and subtract it from mRegionPendingUpload
-  mBuffer->EnsureTexture(mFrontBuffer.Buffer()->GetSize(),
-                         mFrontBuffer.Buffer()->GetContentType());
+  AutoOpenSurface frontSurface(OPEN_READ_ONLY, mFrontBuffer.Buffer());
+  mBuffer->EnsureTexture(frontSurface.Size(),
+                         frontSurface.ContentType());
   nsRefPtr<gl::TextureImage> tiledImage = mBuffer->GetTextureImage().get();
   if (tiledImage->GetTileCount() > 1)
     tiledImage->SetIterationCallback(ProgressiveUploadCallback, (void *)&mRegionPendingUpload);
   else
     mRegionPendingUpload.SetEmpty();
 
   // Upload a tile
-  mBuffer->DirectUpdate(mFrontBuffer.Buffer(), mRegionPendingUpload);
+  mBuffer->DirectUpdate(frontSurface.Get(), mRegionPendingUpload);
 
   // Remove the iteration callback
   tiledImage->SetIterationCallback(nsnull, nsnull);
 
   if (!mRegionPendingUpload.IsEmpty()) {
     // Schedule another upload task
     mUploadTask = NewRunnableMethod(this, &ShadowThebesLayerOGL::ProgressiveUpload);
     // Post a delayed task to complete more of the upload - give a reasonable delay to allow
@@ -1130,23 +1133,21 @@ ShadowThebesLayerOGL::Swap(const ThebesB
                            const nsIntRegion& aUpdatedRegion,
                            OptionalThebesBuffer* aNewBack,
                            nsIntRegion* aNewBackValidRegion,
                            OptionalThebesBuffer* aReadOnlyFront,
                            nsIntRegion* aFrontUpdatedRegion)
 {
   // The double-buffer path is copied and adapted from BasicLayers.cpp
   if (ShouldDoubleBuffer()) {
-    nsRefPtr<gfxASurface> newFrontBuffer =
-      ShadowLayerForwarder::OpenDescriptor(aNewFront.buffer());
+    AutoOpenSurface newFrontBuffer(OPEN_READ_ONLY, aNewFront.buffer());
 
     if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
-      nsRefPtr<gfxASurface> currentFront =
-        ShadowLayerForwarder::OpenDescriptor(mFrontBufferDescriptor);
-      if (currentFront->GetSize() != newFrontBuffer->GetSize()) {
+      AutoOpenSurface currentFront(OPEN_READ_ONLY, mFrontBufferDescriptor);
+      if (currentFront.Size() != newFrontBuffer.Size()) {
         // Current front buffer is obsolete
         DestroyFrontBuffer();
       }
     }
 
     // This code relies on Swap() arriving *after* attribute mutations.
     if (IsSurfaceDescriptorValid(mFrontBufferDescriptor)) {
       *aNewBack = ThebesBuffer();
@@ -1154,58 +1155,59 @@ ShadowThebesLayerOGL::Swap(const ThebesB
     } else {
       *aNewBack = null_t();
     }
 
     // We have to invalidate the pixels painted into the new buffer.
     // They might overlap with our old pixels.
     aNewBackValidRegion->Sub(mOldValidRegion, aUpdatedRegion);
 
-    nsRefPtr<gfxASurface> unused;
+    SurfaceDescriptor unused;
     nsIntRect backRect;
     nsIntPoint backRotation;
     mFrontBuffer.Swap(
-      newFrontBuffer, aNewFront.rect(), aNewFront.rotation(),
-      getter_AddRefs(unused), &backRect, &backRotation);
+      aNewFront.buffer(), aNewFront.rect(), aNewFront.rotation(),
+      &unused, &backRect, &backRotation);
 
     if (aNewBack->type() != OptionalThebesBuffer::Tnull_t) {
       aNewBack->get_ThebesBuffer().rect() = backRect;
       aNewBack->get_ThebesBuffer().rotation() = backRotation;
     }
 
     mFrontBufferDescriptor = aNewFront.buffer();
 
     // Upload new front-buffer to texture
     if (!mDestroyed) {
       if (!mBuffer) {
         mBuffer = new ShadowBufferOGL(this);
       }
-      nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(mFrontBufferDescriptor);
-      mBuffer->Upload(surf, aUpdatedRegion, aNewFront.rect(), aNewFront.rotation(), true, mRegionPendingUpload);
+      AutoOpenSurface frontSurface(OPEN_READ_ONLY, mFrontBufferDescriptor);
+      mBuffer->Upload(frontSurface.Get(), aUpdatedRegion, aNewFront.rect(), aNewFront.rotation(), true, mRegionPendingUpload);
 
       // Schedule a task to progressively upload the texture
       if (!mUploadTask) {
         mUploadTask = NewRunnableMethod(this, &ShadowThebesLayerOGL::ProgressiveUpload);
+        // XXX magic delay constant
         MessageLoop::current()->PostDelayedTask(FROM_HERE, mUploadTask, 5);
       }
     }
 
     *aReadOnlyFront = aNewFront;
     *aFrontUpdatedRegion = aUpdatedRegion;
 
     return;
   }
 
   // Single-buffer path
   if (!mDestroyed) {
     if (!mBuffer) {
       mBuffer = new ShadowBufferOGL(this);
     }
-    nsRefPtr<gfxASurface> surf = ShadowLayerForwarder::OpenDescriptor(aNewFront.buffer());
-    mBuffer->Upload(surf, aUpdatedRegion, aNewFront.rect(), aNewFront.rotation(), false, mRegionPendingUpload);
+    AutoOpenSurface frontSurface(OPEN_READ_ONLY, aNewFront.buffer());
+    mBuffer->Upload(frontSurface.Get(), aUpdatedRegion, aNewFront.rect(), aNewFront.rotation(), false, mRegionPendingUpload);
   }
 
   *aNewBack = aNewFront;
   *aNewBackValidRegion = mValidRegion;
   *aReadOnlyFront = null_t();
   aFrontUpdatedRegion->SetEmpty();
 }
 
--- a/gfx/layers/opengl/ThebesLayerOGL.h
+++ b/gfx/layers/opengl/ThebesLayerOGL.h
@@ -62,55 +62,53 @@ public:
     MOZ_COUNT_CTOR(ShadowThebesLayerBufferOGL);
   }
 
   ~ShadowThebesLayerBufferOGL()
   {
     MOZ_COUNT_DTOR(ShadowThebesLayerBufferOGL);
   }
 
-  void Swap(gfxASurface* aNewBuffer,
+  void Swap(const SurfaceDescriptor& aDescriptor,
             const nsIntRect& aNewRect, const nsIntPoint& aNewRotation,
-            gfxASurface** aOldBuffer,
+            SurfaceDescriptor* aOldDescriptor,
             nsIntRect* aOldRect, nsIntPoint* aOldRotation)
   {
+    *aOldDescriptor = mBuffer;
     *aOldRect = mBufferRect;
     *aOldRotation = mBufferRotation;
-    nsRefPtr<gfxASurface> oldBuffer = mBuffer;
 
+    mBuffer = aDescriptor;
     mBufferRect = aNewRect;
     mBufferRotation = aNewRotation;
-    mBuffer = aNewBuffer;
-    oldBuffer.forget(aOldBuffer);
   }
 
   nsIntRect Rect() {
     return mBufferRect;
   }
 
   nsIntPoint Rotation() {
     return mBufferRotation;
   }
 
-  gfxASurface* Buffer() {
+  SurfaceDescriptor Buffer() {
     return mBuffer;
   }
 
   /**
    * Wipe out all retained contents. Call this when the entire
    * buffer becomes invalid.
    */
   void Clear()
   {
-    mBuffer = nsnull;
     mBufferRect.SetEmpty();
   }
 
 protected:
-  nsRefPtr<gfxASurface> mBuffer;
+  SurfaceDescriptor mBuffer;
   nsIntRect mBufferRect;
   nsIntPoint mBufferRotation;
 };
 
 class ShadowThebesLayerOGL : public ShadowThebesLayer,
                              public LayerOGL
 {
 public:
--- a/gfx/thebes/gfxASurface.h
+++ b/gfx/thebes/gfxASurface.h
@@ -90,17 +90,18 @@ public:
         SurfaceTypeSubsurface,
         SurfaceTypeD2D,
         SurfaceTypeMax
     } gfxSurfaceType;
 
     typedef enum {
         CONTENT_COLOR       = 0x1000,
         CONTENT_ALPHA       = 0x2000,
-        CONTENT_COLOR_ALPHA = 0x3000
+        CONTENT_COLOR_ALPHA = 0x3000,
+        CONTENT_SENTINEL    = 0xffff
     } gfxContentType;
 
     /** Wrap the given cairo surface and return a gfxASurface for it.
      * This adds a reference to csurf (owned by the returned gfxASurface).
      */
     static already_AddRefed<gfxASurface> Wrap(cairo_surface_t *csurf);
 
     /*** this DOES NOT addref the surface */
--- a/gfx/thebes/gfxPattern.h
+++ b/gfx/thebes/gfxPattern.h
@@ -83,17 +83,18 @@ public:
     int CairoStatus();
 
     enum GraphicsFilter {
         FILTER_FAST,
         FILTER_GOOD,
         FILTER_BEST,
         FILTER_NEAREST,
         FILTER_BILINEAR,
-        FILTER_GAUSSIAN
+        FILTER_GAUSSIAN,
+        FILTER_SENTINEL
     };
 
     void SetFilter(GraphicsFilter filter);
     GraphicsFilter Filter() const;
 
     /* returns TRUE if it succeeded */
     bool GetSolidColor(gfxRGBA& aColor);
 
--- a/ipc/glue/GeckoChildProcessHost.cpp
+++ b/ipc/glue/GeckoChildProcessHost.cpp
@@ -39,17 +39,17 @@
 
 #ifdef MOZ_WIDGET_ANDROID
 #include "APKOpen.h"
 #endif
 
 using mozilla::MonitorAutoLock;
 using mozilla::ipc::GeckoChildProcessHost;
 
-#ifdef MOZ_WIDGET_ANDROID
+#ifdef ANDROID
 // Like its predecessor in nsExceptionHandler.cpp, this is
 // the magic number of a file descriptor remapping we must
 // preserve for the child process.
 static const int kMagicAndroidSystemPropFd = 5;
 #endif
 
 static bool
 ShouldHaveDirectoryService()
@@ -479,32 +479,34 @@ GeckoChildProcessHost::PerformAsyncLaunc
     mFileMap.push_back(std::pair<int,int>(cache->fd, cache->fd));
     cacheStr.Append(cache->name);
     cacheStr.AppendPrintf(":%d;", cache->fd);
     cache++;
   }
   // fill the last arg with something if there's no cache
   if (cacheStr.IsEmpty())
     cacheStr.AppendLiteral("-");
+#endif  // MOZ_WIDGET_ANDROID
 
+#ifdef ANDROID
   // Remap the Android property workspace to a well-known int,
   // and update the environment to reflect the new value for the
   // child process.
   const char *apws = getenv("ANDROID_PROPERTY_WORKSPACE");
   if (apws) {
     int fd = atoi(apws);
     mFileMap.push_back(std::pair<int, int>(fd, kMagicAndroidSystemPropFd));
 
     char buf[32];
     char *szptr = strchr(apws, ',');
 
     snprintf(buf, sizeof(buf), "%d%s", kMagicAndroidSystemPropFd, szptr);
     newEnvVars["ANDROID_PROPERTY_WORKSPACE"] = buf;
   }
-#endif  // MOZ_WIDGET_ANDROID
+#endif  // ANDROID
 
   // remap the IPC socket fd to a well-known int, as the OS does for
   // STDOUT_FILENO, for example
   int srcChannelFd, dstChannelFd;
   channel().GetClientFileDescriptorMapping(&srcChannelFd, &dstChannelFd);
   mFileMap.push_back(std::pair<int,int>(srcChannelFd, dstChannelFd));
 
   // no need for kProcessChannelID, the child process inherits the
--- a/ipc/glue/IPCMessageUtils.h
+++ b/ipc/glue/IPCMessageUtils.h
@@ -39,20 +39,21 @@
 // FileDescriptor fails to build)
 namespace base { struct FileDescriptor { }; }
 #endif
 
 using mozilla::layers::LayerManager;
 
 namespace mozilla {
 
-typedef gfxPattern::GraphicsFilter GraphicsFilterType;
+typedef gfxASurface::gfxContentType gfxContentType;
+typedef gfxASurface::gfxImageFormat PixelFormat;
 typedef gfxASurface::gfxSurfaceType gfxSurfaceType;
+typedef gfxPattern::GraphicsFilter GraphicsFilterType;
 typedef LayerManager::LayersBackend LayersBackend;
-typedef gfxASurface::gfxImageFormat PixelFormat;
 
 // This is a cross-platform approximation to HANDLE, which we expect
 // to be typedef'd to void* or thereabouts.
 typedef uintptr_t WindowsHandle;
 
 // XXX there are out of place and might be generally useful.  Could
 // move to nscore.h or something.
 struct void_t {
@@ -490,150 +491,50 @@ struct ParamTraits<gfx3DMatrix>
     return (Rd(_11) && Rd(_12) && Rd(_13) && Rd(_14) &&
             Rd(_21) && Rd(_22) && Rd(_23) && Rd(_24) &&
             Rd(_31) && Rd(_32) && Rd(_33) && Rd(_34) &&
             Rd(_41) && Rd(_42) && Rd(_43) && Rd(_44));
 #undef Rd
   }
 };
 
- template<>
-struct ParamTraits<mozilla::GraphicsFilterType>
-{
-  typedef mozilla::GraphicsFilterType paramType;
-
-  static void Write(Message* msg, const paramType& param)
-  {
-    switch (param) {
-    case gfxPattern::FILTER_FAST:
-    case gfxPattern::FILTER_GOOD:
-    case gfxPattern::FILTER_BEST:
-    case gfxPattern::FILTER_NEAREST:
-    case gfxPattern::FILTER_BILINEAR:
-    case gfxPattern::FILTER_GAUSSIAN:
-      WriteParam(msg, int32(param));
-      return;
-
-    }
-    NS_RUNTIMEABORT("not reached");
-  }
-
-  static bool Read(const Message* msg, void** iter, paramType* result)
-  {
-    int32 filter;
-    if (!ReadParam(msg, iter, &filter))
-      return false;
+template <>
+struct ParamTraits<mozilla::gfxContentType>
+  : public EnumSerializer<mozilla::gfxContentType,
+                          gfxASurface::CONTENT_COLOR,
+                          gfxASurface::CONTENT_SENTINEL>
+{};
 
-    switch (filter) {
-    case gfxPattern::FILTER_FAST:
-    case gfxPattern::FILTER_GOOD:
-    case gfxPattern::FILTER_BEST:
-    case gfxPattern::FILTER_NEAREST:
-    case gfxPattern::FILTER_BILINEAR:
-    case gfxPattern::FILTER_GAUSSIAN:
-      *result = paramType(filter);
-      return true;
-
-    default:
-      return false;
-    }
-  }
-};
-
- template<>
+template <>
 struct ParamTraits<mozilla::gfxSurfaceType>
-{
-  typedef mozilla::gfxSurfaceType paramType;
-
-  static void Write(Message* msg, const paramType& param)
-  {
-    if (gfxASurface::SurfaceTypeImage <= param &&
-        param < gfxASurface::SurfaceTypeMax) {
-      WriteParam(msg, int32(param));
-      return;
-    }
-    NS_RUNTIMEABORT("surface type not reached");
-  }
-
-  static bool Read(const Message* msg, void** iter, paramType* result)
-  {
-    int32 filter;
-    if (!ReadParam(msg, iter, &filter))
-      return false;
+  : public EnumSerializer<gfxASurface::gfxSurfaceType,
+                          gfxASurface::SurfaceTypeImage,
+                          gfxASurface::SurfaceTypeMax>
+{};
 
-    if (gfxASurface::SurfaceTypeImage <= filter &&
-        filter < gfxASurface::SurfaceTypeMax) {
-      *result = paramType(filter);
-      return true;
-    }
-    return false;
-  }
-};
-
-template<>
-struct ParamTraits<mozilla::LayersBackend>
-{
-  typedef mozilla::LayersBackend paramType;
-
-  static void Write(Message* msg, const paramType& param)
-  {
-    if (LayerManager::LAYERS_NONE <= param &&
-        param < LayerManager::LAYERS_LAST) {
-      WriteParam(msg, int32(param));
-      return;
-    }
-    NS_RUNTIMEABORT("backend type not reached");
-  }
-
-  static bool Read(const Message* msg, void** iter, paramType* result)
-  {
-    int32 type;
-    if (!ReadParam(msg, iter, &type))
-      return false;
+template <>
+struct ParamTraits<mozilla::GraphicsFilterType>
+  : public EnumSerializer<mozilla::GraphicsFilterType,
+                          gfxPattern::FILTER_FAST,
+                          gfxPattern::FILTER_SENTINEL>
+{};
 
-    if (LayerManager::LAYERS_NONE <= type &&
-        type < LayerManager::LAYERS_LAST) {
-      *result = paramType(type);
-      return true;
-    }
-    return false;
-  }
-};
-
-template<>
-struct ParamTraits<mozilla::PixelFormat>
-{
-  typedef mozilla::PixelFormat paramType;
-
-  static bool IsLegalPixelFormat(const paramType& format)
-  {
-    return (gfxASurface::ImageFormatARGB32 <= format &&
-            format < gfxASurface::ImageFormatUnknown);
-  }
+template <>
+struct ParamTraits<mozilla::LayersBackend>
+  : public EnumSerializer<mozilla::LayersBackend,
+                          LayerManager::LAYERS_NONE,
+                          LayerManager::LAYERS_LAST>
+{};
 
-  static void Write(Message* msg, const paramType& param)
-  {
-    if (!IsLegalPixelFormat(param)) {
-      NS_RUNTIMEABORT("Unknown pixel format");
-    }
-    WriteParam(msg, int32(param));
-    return;
-  }
-
-  static bool Read(const Message* msg, void** iter, paramType* result)
-  {
-    int32 format;
-    if (!ReadParam(msg, iter, &format) ||
-        !IsLegalPixelFormat(paramType(format))) {
-      return false;
-    }
-    *result = paramType(format);
-    return true;
-  }
-};
+template <>
+struct ParamTraits<mozilla::PixelFormat>
+  : public EnumSerializer<mozilla::PixelFormat,
+                          gfxASurface::ImageFormatARGB32,
+                          gfxASurface::ImageFormatUnknown>
+{};
 
 template<>
 struct ParamTraits<gfxRGBA>
 {
   typedef gfxRGBA paramType;
 
   static void Write(Message* msg, const paramType& param)
   {
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -661,16 +661,19 @@ class _StructField(_CompoundTypeComponen
 
     def memberVar(self):
         return ExprVar(self.name + '_')
 
     def initStmts(self):
         if self.recursive:
             return [ StmtExpr(ExprAssn(self.memberVar(),
                                        ExprNew(self.bareType()))) ]
+        elif self.ipdltype.isIPDL() and self.ipdltype.isActor():
+            return [ StmtExpr(ExprAssn(self.memberVar(),
+                                       ExprLiteral.NULL)) ]
         else:
             return []
 
     def destructStmts(self):
         if self.recursive:
             return [ StmtExpr(ExprDelete(self.memberVar())) ]
         else:
             return []
--- a/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
+++ b/ipc/ipdl/test/cxx/PTestDataStructures.ipdl
@@ -34,16 +34,19 @@ parent:
                IntDoubleArrays i3)
         returns (IntDoubleArrays o1,
                  IntDoubleArrays o2,
                  IntDoubleArrays o3);
 
     sync Test6(IntDoubleArrays[] i1)
         returns (IntDoubleArrays[] o1);
 
+    sync Test7_0(ActorWrapper a1)
+        returns (ActorWrapper o1);
+
     sync Test7(Actors i1,
                Actors i2,
                Actors i3)
         returns (Actors o1,
                  Actors o2,
                  Actors o3);
 
     sync Test8(Actors[] i1)
--- a/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh
+++ b/ipc/ipdl/test/cxx/PTestDataStructuresCommon.ipdlh
@@ -23,16 +23,20 @@ union IntDoubleArrays {
 };
 
 struct SIntDoubleArrays {
     int i;
     int[] ai;
     double[] ad;
 };
 
+struct ActorWrapper {
+    PTestDataStructuresSub actor;
+};
+
 union Actors {
     int;
     int[];
     PTestDataStructuresSub[];
 };
 
 struct SActors {
     int i;
--- a/ipc/ipdl/test/cxx/TestDataStructures.cpp
+++ b/ipc/ipdl/test/cxx/TestDataStructures.cpp
@@ -148,16 +148,33 @@ bool TestDataStructuresParent::RecvTest5
 
     *o1 = i1;
     *o2 = i2a;
     *o3 = i3a;
 
     return true;
 }
 
+bool
+TestDataStructuresParent::RecvTest7_0(const ActorWrapper& i1,
+                                      ActorWrapper* o1)
+{
+    if (i1.actorChild() != nsnull)
+        fail("child side actor should always be null");
+
+    if (i1.actorParent() != mKids[0])
+        fail("should have got back same actor on parent side");
+
+    o1->actorParent() = mKids[0];
+    // malicious behavior
+    o1->actorChild() =
+        reinterpret_cast<PTestDataStructuresSubChild*>(0xdeadbeef);
+    return true;
+}
+
 bool TestDataStructuresParent::RecvTest6(
         const InfallibleTArray<IntDoubleArrays>& i1,
         InfallibleTArray<IntDoubleArrays>* o1)
 {
     test_assert(3 == i1.Length(), "wrong length");
 
     IntDoubleArrays id1(i1[0]);
     test_assert(42 == id1.get_int(), "wrong value");
@@ -460,16 +477,17 @@ TestDataStructuresChild::RecvStart()
     puts("[TestDataStructuresChild] starting");
 
     Test1();
     Test2();
     Test3();
     Test4();
     Test5();
     Test6();
+    Test7_0();
     Test7();
     Test8();
     Test9();
     Test10();
     Test11();
     Test12();
     Test13();
     Test14();
@@ -606,16 +624,38 @@ TestDataStructuresChild::Test6()
     test_assert(42 == od1.get_int(), "wrong value");
     assert_arrays_equal(id2, od2);
     assert_arrays_equal(id3, od3);
 
     printf("  passed %s\n", __FUNCTION__);
 }
 
 void
+TestDataStructuresChild::Test7_0()
+{
+    ActorWrapper iaw;
+    if (iaw.actorChild() != nsnull || iaw.actorParent() != nsnull)
+        fail("actor members should be null initially");
+
+    iaw.actorChild() = mKids[0];
+    if (iaw.actorParent() != nsnull)
+        fail("parent should be null on child side after set");
+
+    ActorWrapper oaw;
+    if (!SendTest7_0(iaw, &oaw))
+        fail("sending Test7_0");
+
+    if (oaw.actorParent() != nsnull)
+        fail("parent accessor on actor-struct members should always be null in child");
+
+    if (oaw.actorChild() != mKids[0])
+        fail("should have got back same child-side actor");
+}
+
+void
 TestDataStructuresChild::Test7()
 {
     Actors i1(42);
     InfallibleTArray<int> i2a;
     i2a.AppendElement(1);  i2a.AppendElement(2);  i2a.AppendElement(3);
 
     Actors o1, o2, o3;
     if (!SendTest7(i1, Actors(i2a), Actors(mKids), &o1, &o2, &o3))
--- a/ipc/ipdl/test/cxx/TestDataStructures.h
+++ b/ipc/ipdl/test/cxx/TestDataStructures.h
@@ -86,16 +86,20 @@ protected:
             IntDoubleArrays* o3);
 
     NS_OVERRIDE
     virtual bool RecvTest6(
             const InfallibleTArray<IntDoubleArrays>& i1,
             InfallibleTArray<IntDoubleArrays>* o1);
 
     NS_OVERRIDE
+    virtual bool RecvTest7_0(const ActorWrapper& i1,
+                             ActorWrapper* o1);
+
+    NS_OVERRIDE
     virtual bool RecvTest7(
             const Actors& i1,
             const Actors& i2,
             const Actors& i3,
             Actors* o1,
             Actors* o2,
             Actors* o3);
 
@@ -221,16 +225,17 @@ protected:
 
 private:
     void Test1();
     void Test2();
     void Test3();
     void Test4();
     void Test5();
     void Test6();
+    void Test7_0();
     void Test7();
     void Test8();
     void Test9();
     void Test10();
     void Test11();
     void Test12();
     void Test13();
     void Test14();
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -672,16 +672,18 @@ EXTRA_LIBS += $(MOZ_ZLIB_LIBS)
 ifdef _MSC_VER
 # XXX We should add this to CXXFLAGS, too?
 CFLAGS += -fp:precise
 
 ifeq ($(CPU_ARCH),x86)
 # Workaround compiler bug on PGO (Bug 721284)
 MonoIC.$(OBJ_SUFFIX): CXXFLAGS += -GL-
 Compiler.$(OBJ_SUFFIX): CXXFLAGS += -GL-
+# Ditto (Bug 772303)
+RegExp.$(OBJ_SUFFIX): CXXFLAGS += -GL-
 endif
 endif # _MSC_VER
 
 ifeq ($(OS_ARCH),FreeBSD)
 EXTRA_LIBS	+= -pthread
 endif
 ifeq ($(OS_ARCH),Linux)
 EXTRA_LIBS	+= -ldl
--- a/js/src/builtin/Eval.cpp
+++ b/js/src/builtin/Eval.cpp
@@ -5,16 +5,17 @@
  * 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 "jscntxt.h"
 #include "jsonparser.h"
 
 #include "builtin/Eval.h"
 #include "frontend/BytecodeCompiler.h"
+#include "mozilla/HashFunctions.h"
 #include "vm/GlobalObject.h"
 
 #include "jsinterpinlines.h"
 
 using namespace js;
 
 // We should be able to assert this for *any* fp->scopeChain().
 static void
@@ -25,160 +26,100 @@ AssertInnerizedScopeChain(JSContext *cx,
         if (JSObjectOp op = o->getClass()->ext.innerObject) {
             Rooted<JSObject*> obj(cx, o);
             JS_ASSERT(op(cx, obj) == o);
         }
     }
 #endif
 }
 
-void
-EvalCache::purge()
+static bool
+IsEvalCacheCandidate(JSScript *script)
 {
-    // Purge all scripts from the eval cache. In addition to removing them from
-    // table_, null out the evalHashLink field of any script removed. Since
-    // evalHashLink is in a union with globalObject, this allows the GC to
-    // indiscriminately use the union as a nullable globalObject pointer.
-    for (size_t i = 0; i < ArrayLength(table_); ++i) {
-        for (JSScript **listHeadp = &table_[i]; *listHeadp; ) {
-            JSScript *script = *listHeadp;
-            JS_ASSERT(GetGCThingTraceKind(script) == JSTRACE_SCRIPT);
-            *listHeadp = script->evalHashLink();
-            script->evalHashLink() = NULL;
-        }
-    }
-}
-
-JSScript **
-EvalCache::bucket(JSLinearString *str)
-{
-    const jschar *s = str->chars();
-    size_t n = str->length();
-
-    if (n > 100)
-        n = 100;
-    uint32_t h;
-    for (h = 0; n; s++, n--)
-        h = JS_ROTATE_LEFT32(h, 4) ^ *s;
-
-    h *= JS_GOLDEN_RATIO;
-    h >>= 32 - SHIFT;
-    JS_ASSERT(h < ArrayLength(table_));
-    return &table_[h];
+    // Make sure there are no inner objects which might use the wrong parent
+    // and/or call scope by reusing the previous eval's script. Skip the
+    // script's first object, which entrains the eval's scope.
+    return script->savedCallerFun &&
+           !script->hasSingletons &&
+           script->objects()->length == 1 &&
+           !script->hasRegexps();
 }
 
-static JSScript *
-EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, unsigned staticLevel,
-                JSPrincipals *principals, JSObject &scopeobj, JSScript **bucket)
+/* static */ HashNumber
+EvalCacheHashPolicy::hash(const EvalCacheLookup &l)
 {
-    // Cache local eval scripts indexed by source qualified by scope.
-    // 
-    // An eval cache entry should never be considered a hit unless its
-    // strictness matches that of the new eval code. The existing code takes
-    // care of this, because hits are qualified by the function from which
-    // eval was called, whose strictness doesn't change. (We don't cache evals
-    // in eval code, so the calling function corresponds to the calling script,
-    // and its strictness never varies.) Scripts produced by calls to eval from
-    // global code aren't cached.
-    // 
-    // FIXME bug 620141: Qualify hits by calling script rather than function.
-    // Then we wouldn't need the unintuitive !isEvalFrame() hack in EvalKernel
-    // to avoid caching nested evals in functions (thus potentially mismatching
-    // on strict mode), and we could cache evals in global code if desired.
-    unsigned count = 0;
-    JSScript **scriptp = bucket;
+    return AddToHash(HashString(l.str->chars(), l.str->length()),
+                     l.caller,
+                     l.staticLevel,
+                     l.version,
+                     l.compartment);
+}
 
-    JSVersion version = cx->findVersion();
-    JSScript *script;
-    JSSubsumePrincipalsOp subsume = cx->runtime->securityCallbacks->subsumePrincipals;
-    while ((script = *scriptp) != NULL) {
-        if (script->savedCallerFun &&
-            script->staticLevel == staticLevel &&
-            script->getVersion() == version &&
-            !script->hasSingletons &&
-            (!subsume || script->principals == principals ||
-             (subsume(principals, script->principals) &&
-              subsume(script->principals, principals))))
-        {
-            // Get the prior (cache-filling) eval's saved caller function.
-            // See frontend::CompileScript.
-            JSFunction *fun = script->getCallerFunction();
-
-            if (fun == caller->fun()) {
-                /*
-                 * Get the source string passed for safekeeping in the atom map
-                 * by the prior eval to frontend::CompileScript.
-                 */
-                JSAtom *src = script->atoms[0];
+/* static */ bool
+EvalCacheHashPolicy::match(JSScript *script, const EvalCacheLookup &l)
+{
+    JS_ASSERT(IsEvalCacheCandidate(script));
 
-                if (src == str || EqualStrings(src, str)) {
-                    // Source matches. Make sure there are no inner objects
-                    // which might use the wrong parent and/or call scope by
-                    // reusing the previous eval's script. Skip the script's
-                    // first object, which entrains the eval's scope.
-                    JS_ASSERT(script->objects()->length >= 1);
-                    if (script->objects()->length == 1 &&
-                        !script->hasRegexps()) {
-                        JS_ASSERT(staticLevel == script->staticLevel);
-                        *scriptp = script->evalHashLink();
-                        script->evalHashLink() = NULL;
-                        return script;
-                    }
-                }
-            }
-        }
+    // Get the source string passed for safekeeping in the atom map
+    // by the prior eval to frontend::CompileScript.
+    JSAtom *keyStr = script->atoms[0];
 
-        static const unsigned EVAL_CACHE_CHAIN_LIMIT = 4;
-        if (++count == EVAL_CACHE_CHAIN_LIMIT)
-            return NULL;
-        scriptp = &script->evalHashLink();
-    }
-    return NULL;
+    return EqualStrings(keyStr, l.str) &&
+           script->getCallerFunction() == l.caller &&
+           script->staticLevel == l.staticLevel &&
+           script->getVersion() == l.version &&
+           script->compartment() == l.compartment;
 }
 
 // There are two things we want to do with each script executed in EvalKernel:
 //  1. notify jsdbgapi about script creation/destruction
 //  2. add the script to the eval cache when EvalKernel is finished
 //
 // NB: Although the eval cache keeps a script alive wrt to the JS engine, from
 // a jsdbgapi user's perspective, we want each eval() to create and destroy a
 // script. This hides implementation details and means we don't have to deal
-// with calls to JS_GetScriptObject for scripts in the eval cache (currently,
-// script->object aliases script->evalHashLink()).
+// with calls to JS_GetScriptObject for scripts in the eval cache.
 class EvalScriptGuard
 {
     JSContext *cx_;
-    JSLinearString *str_;
-    JSScript **bucket_;
     Rooted<JSScript*> script_;
 
+    /* These fields are only valid if lookup_.str is non-NULL. */
+    EvalCacheLookup lookup_;
+    EvalCache::AddPtr p_;
+
   public:
-    EvalScriptGuard(JSContext *cx, JSLinearString *str)
-      : cx_(cx),
-        str_(str),
-        script_(cx) {
-        bucket_ = cx->runtime->evalCache.bucket(str);
+    EvalScriptGuard(JSContext *cx)
+      : cx_(cx), script_(cx)
+    {
+        lookup_.str = NULL;
     }
 
     ~EvalScriptGuard() {
         if (script_) {
             CallDestroyScriptHook(cx_->runtime->defaultFreeOp(), script_);
             script_->isActiveEval = false;
             script_->isCachedEval = true;
-            script_->evalHashLink() = *bucket_;
-            *bucket_ = script_;
+            if (lookup_.str && IsEvalCacheCandidate(script_))
+                cx_->runtime->evalCache.relookupOrAdd(p_, lookup_, script_);
         }
     }
 
-    void lookupInEvalCache(StackFrame *caller, unsigned staticLevel,
-                           JSPrincipals *principals, JSObject &scopeobj) {
-        if (JSScript *found = EvalCacheLookup(cx_, str_, caller, staticLevel,
-                                              principals, scopeobj, bucket_)) {
-            js_CallNewScriptHook(cx_, found, NULL);
-            script_ = found;
+    void lookupInEvalCache(JSLinearString *str, JSFunction *caller, unsigned staticLevel)
+    {
+        lookup_.str = str;
+        lookup_.caller = caller;
+        lookup_.staticLevel = staticLevel;
+        lookup_.version = cx_->findVersion();
+        lookup_.compartment = cx_->compartment;
+        p_ = cx_->runtime->evalCache.lookupForAdd(lookup_);
+        if (p_) {
+            script_ = *p_;
+            cx_->runtime->evalCache.remove(p_);
+            js_CallNewScriptHook(cx_, script_, NULL);
             script_->isCachedEval = false;
             script_->isActiveEval = true;
         }
     }
 
     void setNewScript(JSScript *script) {
         // JSScript::initFromEmitter has already called js_CallNewScriptHook.
         JS_ASSERT(!script_ && script);
@@ -242,21 +183,16 @@ EvalKernel(JSContext *cx, const CallArgs
         staticLevel = caller->script()->staticLevel + 1;
 
         // Direct calls to eval are supposed to see the caller's |this|. If we
         // haven't wrapped that yet, do so now, before we make a copy of it for
         // the eval code to use.
         if (!ComputeThis(cx, caller))
             return false;
         thisv = caller->thisValue();
-
-#ifdef DEBUG
-        jsbytecode *callerPC = caller->pcQuadratic(cx);
-        JS_ASSERT(callerPC && JSOp(*callerPC) == JSOP_EVAL);
-#endif
     } else {
         JS_ASSERT(args.callee().global() == *scopeobj);
         staticLevel = 0;
 
         // Use the global as 'this', modulo outerization.
         JSObject *thisobj = scopeobj->thisObject(cx);
         if (!thisobj)
             return false;
@@ -305,37 +241,36 @@ EvalKernel(JSContext *cx, const CallArgs
                 if (tmp.isUndefined())
                     break;
                 args.rval() = tmp;
                 return true;
             }
         }
     }
 
-    EvalScriptGuard esg(cx, linearStr);
+    EvalScriptGuard esg(cx);
 
     JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
 
     if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame())
-        esg.lookupInEvalCache(caller, staticLevel, principals, *scopeobj);
+        esg.lookupInEvalCache(linearStr, caller->fun(), staticLevel);
 
     if (!esg.foundScript()) {
         unsigned lineno;
         const char *filename;
         JSPrincipals *originPrincipals;
         CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals,
                                     evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL
                                                             : NOT_CALLED_FROM_JSOP_EVAL);
 
         bool compileAndGo = true;
         bool noScriptRval = false;
-        bool needScriptGlobal = false;
         JSScript *compiled = frontend::CompileScript(cx, scopeobj, caller,
                                                      principals, originPrincipals,
-                                                     compileAndGo, noScriptRval, needScriptGlobal,
+                                                     compileAndGo, noScriptRval,
                                                      chars, length, filename,
                                                      lineno, cx->findVersion(), linearStr,
                                                      staticLevel);
         if (!compiled)
             return false;
 
         esg.setNewScript(compiled);
     }
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -571,29 +571,29 @@ ExecuteRegExp(JSContext *cx, Native nati
         return false;
 
     /* Step 3. */
     Rooted<JSLinearString*> linearInput(cx, input->ensureLinear(cx));
     if (!linearInput)
         return false;
 
     /* Step 4. */
-    const Value &lastIndex = reobj->getLastIndex();
+    Value lastIndex = reobj->getLastIndex();
 
     /* Step 5. */
     double i;
     if (!ToInteger(cx, lastIndex, &i))
         return false;
 
     /* Steps 6-7 (with sticky extension). */
     if (!re->global() && !re->sticky())
         i = 0;
 
     const jschar *chars = linearInput->chars();
-    size_t length = input->length();
+    size_t length = linearInput->length();
 
     /* Step 9a. */
     if (i < 0 || i > length) {
         reobj->zeroLastIndex();
         args.rval() = NullValue();
         return true;
     }
 
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -16,64 +16,20 @@
 
 #include "jsinferinlines.h"
 
 #include "frontend/TreeContext-inl.h"
 
 using namespace js;
 using namespace js::frontend;
 
-bool
-MarkInnerAndOuterFunctions(JSContext *cx, JSScript* script)
-{
-    AssertRootingUnnecessary safe(cx);
-
-    Vector<JSScript *, 16> worklist(cx);
-    if (!worklist.append(script))
-        return false;
-
-    while (worklist.length()) {
-        JSScript *outer = worklist.back();
-        worklist.popBack();
-
-        if (outer->hasObjects()) {
-            ObjectArray *arr = outer->objects();
-
-            /*
-             * If this is an eval script, don't treat the saved caller function
-             * stored in the first object slot as an inner function.
-             */
-            size_t start = outer->savedCallerFun ? 1 : 0;
-
-            for (size_t i = start; i < arr->length; i++) {
-                JSObject *obj = arr->vector[i];
-                if (!obj->isFunction())
-                    continue;
-                JSFunction *fun = obj->toFunction();
-                JS_ASSERT(fun->isInterpreted());
-                JSScript *inner = fun->script();
-                if (outer->function() && outer->function()->isHeavyweight()) {
-                    outer->isOuterFunction = true;
-                    inner->isInnerFunction = true;
-                }
-                if (!inner->hasObjects())
-                    continue;
-                if (!worklist.append(inner))
-                    return false;
-            }
-        }
-    }
-
-    return true;
-}
-
 JSScript *
 frontend::CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame,
                         JSPrincipals *principals, JSPrincipals *originPrincipals,
-                        bool compileAndGo, bool noScriptRval, bool needScriptGlobal,
+                        bool compileAndGo, bool noScriptRval,
                         const jschar *chars, size_t length,
                         const char *filename, unsigned lineno, JSVersion version,
                         JSString *source_ /* = NULL */,
                         unsigned staticLevel /* = 0 */)
 {
     RootedString source(cx, source_);
 
     class ProbesManager
@@ -103,24 +59,23 @@ frontend::CompileScript(JSContext *cx, H
 
     SharedContext sc(cx, scopeChain, /* fun = */ NULL, /* funbox = */ NULL, StrictModeFromContext(cx));
 
     TreeContext tc(&parser, &sc, staticLevel, /* bodyid = */ 0);
     if (!tc.init())
         return NULL;
 
     bool savedCallerFun = compileAndGo && callerFrame && callerFrame->isFunctionFrame();
-    GlobalObject *globalObject = needScriptGlobal ? GetCurrentGlobal(cx) : NULL;
     Rooted<JSScript*> script(cx, JSScript::Create(cx,
+                                                  /* enclosingScope = */ NullPtr(),
                                                   savedCallerFun,
                                                   principals,
                                                   originPrincipals,
                                                   compileAndGo,
                                                   noScriptRval,
-                                                  globalObject,
                                                   version,
                                                   staticLevel));
     if (!script)
         return NULL;
 
     // We can specialize a bit for the given scope chain if that scope chain is the global object.
     JSObject *globalScope = scopeChain && scopeChain == &scopeChain->global() ? (JSObject*) scopeChain : NULL;
     JS_ASSERT_IF(globalScope, globalScope->isNative());
@@ -244,19 +199,16 @@ frontend::CompileScript(JSContext *cx, H
     if (Emit1(cx, &bce, JSOP_STOP) < 0)
         return NULL;
 
     if (!JSScript::fullyInitFromEmitter(cx, script, &bce))
         return NULL;
 
     bce.tellDebuggerAboutCompiledScript(cx);
 
-    if (!MarkInnerAndOuterFunctions(cx, script))
-        return NULL;
-
     return script;
 }
 
 // Compile a JS function body, which might appear as the value of an event
 // handler attribute in an HTML <INPUT> tag, or in a Function() constructor.
 bool
 frontend::CompileFunctionBody(JSContext *cx, HandleFunction fun,
                               JSPrincipals *principals, JSPrincipals *originPrincipals,
@@ -274,24 +226,23 @@ frontend::CompileFunctionBody(JSContext 
     funsc.bindings.transfer(bindings);
     fun->setArgCount(funsc.bindings.numArgs());
 
     unsigned staticLevel = 0;
     TreeContext funtc(&parser, &funsc, staticLevel, /* bodyid = */ 0);
     if (!funtc.init())
         return false;
 
-    GlobalObject *globalObject = fun->getParent() ? &fun->getParent()->global() : NULL;
     Rooted<JSScript*> script(cx, JSScript::Create(cx,
+                                                  /* enclosingScope = */ NullPtr(),
                                                   /* savedCallerFun = */ false,
                                                   principals,
                                                   originPrincipals,
                                                   /* compileAndGo = */ false,
                                                   /* noScriptRval = */ false,
-                                                  globalObject,
                                                   version,
                                                   staticLevel));
     if (!script)
         return false;
 
     StackFrame *nullCallerFrame = NULL;
     BytecodeEmitter funbce(/* parent = */ NULL, &parser, &funsc, script, nullCallerFrame,
                            /* hasGlobalScope = */ false, lineno);
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -11,17 +11,17 @@
 #include "frontend/Parser.h"
 
 namespace js {
 namespace frontend {
 
 JSScript *
 CompileScript(JSContext *cx, HandleObject scopeChain, StackFrame *callerFrame,
               JSPrincipals *principals, JSPrincipals *originPrincipals,
-              bool compileAndGo, bool noScriptRval, bool needScriptGlobal,
+              bool compileAndGo, bool noScriptRval,
               const jschar *chars, size_t length,
               const char *filename, unsigned lineno, JSVersion version,
               JSString *source_ = NULL, unsigned staticLevel = 0);
 
 bool
 CompileFunctionBody(JSContext *cx, HandleFunction fun,
                     JSPrincipals *principals, JSPrincipals *originPrincipals,
                     Bindings *bindings, const jschar *chars, size_t length,
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -668,22 +668,41 @@ BackPatch(JSContext *cx, BytecodeEmitter
 
 static void
 PushStatementBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StmtType type, ptrdiff_t top)
 {
     SET_STATEMENT_TOP(stmt, top);
     PushStatement(bce, stmt, type);
 }
 
+/*
+ * Return the enclosing lexical scope, which is the innermost enclosing static
+ * block object or compiler created function.
+ */
+static JSObject *
+EnclosingStaticScope(BytecodeEmitter *bce)
+{
+    if (bce->blockChain)
+        return bce->blockChain;
+
+    if (!bce->sc->inFunction()) {
+        JS_ASSERT(!bce->parent);
+        return NULL;
+    }
+
+    return bce->sc->fun();
+}
+
 // Push a block scope statement and link blockObj into bce->blockChain.
 static void
 PushBlockScopeBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StaticBlockObject &blockObj,
                   ptrdiff_t top)
 {
     PushStatementBCE(bce, stmt, STMT_BLOCK, top);
+    blockObj.initEnclosingStaticScope(EnclosingStaticScope(bce));
     FinishPushBlockScope(bce, stmt, blockObj);
 }
 
 // Patches |breaks| and |continues| unless the top statement info record
 // represents a try-catch-finally suite. May fail if a jump offset overflows.
 static bool
 PopStatementBCE(JSContext *cx, BytecodeEmitter *bce)
 {
@@ -849,16 +868,17 @@ EmitAliasedVarOp(JSContext *cx, JSOp op,
         return false;
 
     jsbytecode *pc = bce->code(off);
     SET_UINT16(pc, sc.hops);
     pc += sizeof(uint16_t);
     SET_UINT16(pc, sc.slot);
     pc += sizeof(uint16_t);
     SET_UINT32_INDEX(pc, maybeBlockIndex);
+    CheckTypeSet(cx, bce, op);
     return true;
 }
 
 static unsigned
 ClonedBlockDepth(BytecodeEmitter *bce)
 {
     unsigned clonedBlockDepth = 0;
     for (StaticBlockObject *b = bce->blockChain; b; b = b->enclosingBlock()) {
@@ -867,60 +887,77 @@ ClonedBlockDepth(BytecodeEmitter *bce)
     }
 
     return clonedBlockDepth;
 }
 
 static bool
 EmitAliasedVarOp(JSContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *bce)
 {
-    /*
-     * The contents of the dynamic scope chain (fp->scopeChain) exactly reflect
-     * the needsClone-subset of the block chain. Use this to determine the
-     * number of ClonedBlockObjects on fp->scopeChain to skip to find the scope
-     * object containing the var to which pn is bound. ALIASEDVAR ops cannot
-     * reach across with scopes so ClonedBlockObjects is the only NestedScope
-     * on the scope chain.
-     */
+    unsigned skippedScopes = 0;
+    BytecodeEmitter *bceOfDef = bce;
+    if (pn->isUsed()) {
+        /*
+         * As explained in BindNameToSlot, the 'level' of a use indicates how
+         * many function scopes (i.e., BytecodeEmitters) to skip to find the
+         * enclosing function scope of the definition being accessed.
+         */
+        for (unsigned i = pn->pn_cookie.level(); i; i--) {
+            skippedScopes += ClonedBlockDepth(bceOfDef);
+            if (bceOfDef->sc->funIsHeavyweight()) {
+                skippedScopes++;
+                if (bceOfDef->sc->fun()->isNamedLambda())
+                    skippedScopes++;
+            }
+            bceOfDef = bceOfDef->parent;
+        }
+    } else {
+        JS_ASSERT(pn->isDefn());
+        JS_ASSERT(pn->pn_cookie.level() == bce->script->staticLevel);
+    }
+
     ScopeCoordinate sc;
     if (JOF_OPTYPE(pn->getOp()) == JOF_QARG) {
-        sc.hops = ClonedBlockDepth(bce);
-        sc.slot = bce->sc->bindings.formalIndexToSlot(pn->pn_cookie.slot());
+        sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
+        sc.slot = bceOfDef->sc->bindings.formalIndexToSlot(pn->pn_cookie.slot());
     } else {
         JS_ASSERT(JOF_OPTYPE(pn->getOp()) == JOF_LOCAL || pn->isKind(PNK_FUNCTION));
         unsigned local = pn->pn_cookie.slot();
-        if (local < bce->sc->bindings.numVars()) {
-            sc.hops = ClonedBlockDepth(bce);
-            sc.slot = bce->sc->bindings.varIndexToSlot(local);
+        if (local < bceOfDef->sc->bindings.numVars()) {
+            sc.hops = skippedScopes + ClonedBlockDepth(bceOfDef);
+            sc.slot = bceOfDef->sc->bindings.varIndexToSlot(local);
         } else {
-            unsigned depth = local - bce->sc->bindings.numVars();
-            unsigned hops = 0;
-            StaticBlockObject *b = bce->blockChain;
+            unsigned depth = local - bceOfDef->sc->bindings.numVars();
+            StaticBlockObject *b = bceOfDef->blockChain;
             while (!b->containsVarAtDepth(depth)) {
                 if (b->needsClone())
-                    hops++;
+                    skippedScopes++;
                 b = b->enclosingBlock();
             }
-            sc.hops = hops;
-            sc.slot = b->localIndexToSlot(bce->sc->bindings, local);
+            sc.hops = skippedScopes;
+            sc.slot = b->localIndexToSlot(bceOfDef->sc->bindings, local);
         }
     }
 
     return EmitAliasedVarOp(cx, op, sc, bce);
 }
 
 static bool
 EmitVarOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce)
 {
     JS_ASSERT(pn->isKind(PNK_FUNCTION) || pn->isKind(PNK_NAME));
     JS_ASSERT_IF(pn->isKind(PNK_NAME), JOF_OPTYPE(op) == JOF_QARG || JOF_OPTYPE(op) == JOF_LOCAL);
     JS_ASSERT(!pn->pn_cookie.isFree());
 
-    if (!bce->isAliasedName(pn))
+    if (!bce->isAliasedName(pn)) {
+        JS_ASSERT(pn->isUsed() || pn->isDefn());
+        JS_ASSERT_IF(pn->isUsed(), pn->pn_cookie.level() == 0);
+        JS_ASSERT_IF(pn->isDefn(), pn->pn_cookie.level() == bce->script->staticLevel);
         return EmitUnaliasedVarOp(cx, op, pn->pn_cookie.slot(), bce);
+    }
 
     switch (op) {
       case JSOP_GETARG: case JSOP_GETLOCAL: op = JSOP_GETALIASEDVAR; break;
       case JSOP_SETARG: case JSOP_SETLOCAL: op = JSOP_SETALIASEDVAR; break;
       case JSOP_CALLARG: case JSOP_CALLLOCAL: op = JSOP_CALLALIASEDVAR; break;
       default: JS_NOT_REACHED("unexpected var op");
     }
 
@@ -1166,93 +1203,83 @@ TryConvertToGname(BytecodeEmitter *bce, 
  *
  * NB: if you add more opcodes specialized from JSOP_NAME, etc., don't forget
  * to update the special cases in EmitFor (for-in) and EmitAssignment (= and
  * op=, e.g. +=).
  */
 static bool
 BindNameToSlot(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
-    Definition *dn;
-    JSOp op;
-    Definition::Kind dn_kind;
-
     JS_ASSERT(pn->isKind(PNK_NAME));
 
-    /* Idempotency tests come first, since we may be called more than once. */
-    if (pn->pn_dflags & PND_BOUND)
+    /* Don't attempt if 'pn' is already bound, deoptimized, or a nop. */
+    if ((pn->pn_dflags & PND_BOUND) || pn->isDeoptimized() || pn->getOp() == JSOP_NOP)
         return true;
 
-    /* No cookie initialized for callee; it is pre-bound by definition. */
+    /* JSOP_CALLEE is pre-bound by definition. */
     JS_ASSERT(!pn->isOp(JSOP_CALLEE));
 
     /*
-     * The parser linked all uses (including forward references) to their
-     * definitions, unless a with statement or direct eval intervened.
+     * The parser already linked name uses to definitions when (where not
+     * prevented by non-lexical constructs like 'with' and 'eval').
      */
+    Definition *dn;
     if (pn->isUsed()) {
         JS_ASSERT(pn->pn_cookie.isFree());
         dn = pn->pn_lexdef;
         JS_ASSERT(dn->isDefn());
-        if (pn->isDeoptimized())
-            return true;
         pn->pn_dflags |= (dn->pn_dflags & PND_CONST);
-    } else {
-        if (!pn->isDefn())
-            return true;
+    } else if (pn->isDefn()) {
         dn = (Definition *) pn;
-    }
-
-    op = pn->getOp();
-    if (op == JSOP_NOP)
+    } else {
         return true;
-
+    }
+
+    JSOp op = pn->getOp();
     JS_ASSERT(JOF_OPTYPE(op) == JOF_ATOM);
-    RootedAtom atom(cx, pn->pn_atom);
-    UpvarCookie cookie = dn->pn_cookie;
-    dn_kind = dn->kind();
+    JS_ASSERT_IF(dn->kind() == Definition::CONST, pn->pn_dflags & PND_CONST);
 
     /*
      * Turn attempts to mutate const-declared bindings into get ops (for
      * pre-increment and pre-decrement ops, our caller will have to emit
      * JSOP_POS, JSOP_ONE, and JSOP_ADD as well).
      *
      * Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared
      * bindings visible to the compiler are permanent in JS unless the
      * declaration originates at top level in eval code.
      */
     switch (op) {
       case JSOP_NAME:
       case JSOP_SETCONST:
         break;
       case JSOP_DELNAME:
-        if (dn_kind != Definition::UNKNOWN) {
+        if (dn->kind() != Definition::UNKNOWN) {
             if (bce->callerFrame && dn->isTopLevel())
                 JS_ASSERT(bce->script->compileAndGo);
             else
                 pn->setOp(JSOP_FALSE);
             pn->pn_dflags |= PND_BOUND;
             return true;
         }
         break;
       default:
         if (pn->isConst()) {
             if (bce->sc->needStrictChecks()) {
                 JSAutoByteString name;
-                if (!js_AtomToPrintableString(cx, atom, &name) ||
+                if (!js_AtomToPrintableString(cx, pn->pn_atom, &name) ||
                     !bce->reportStrictModeError(pn, JSMSG_READ_ONLY, name.ptr()))
                 {
                     return false;
                 }
             }
             pn->setOp(op = JSOP_NAME);
         }
     }
 
-    if (cookie.isFree()) {
+    if (dn->pn_cookie.isFree()) {
         StackFrame *caller = bce->callerFrame;
         if (caller) {
             JS_ASSERT(bce->script->compileAndGo);
 
             /*
              * Don't generate upvars on the left side of a for loop. See
              * bug 470758.
              */
@@ -1283,61 +1310,70 @@ BindNameToSlot(JSContext *cx, BytecodeEm
             return true;
 
         pn->setOp(op);
         pn->pn_dflags |= PND_BOUND;
 
         return true;
     }
 
-    uint16_t level = cookie.level();
-    JS_ASSERT(bce->script->staticLevel >= level);
-
-    const unsigned skip = bce->script->staticLevel - level;
-    if (skip != 0)
-        return true;
+    /*
+     * At this point, we are only dealing with uses that have already been
+     * bound to definitions via pn_lexdef. The rest of this routine converts
+     * the parse node of the use from its initial JSOP_*NAME* op to a LOCAL/ARG
+     * op. This requires setting the node's pn_cookie with a pair (level, slot)
+     * where 'level' is the number of function scopes between the use and the
+     * def and 'slot' is the index to emit as the immediate of the ARG/LOCAL
+     * op. For example, in this code:
+     *
+     *   function(a,b,x) { return x }
+     *   function(y) { function() { return y } }
+     *
+     * x will get (level = 0, slot = 2) and y will get (level = 1, slot = 0).
+     */
+    JS_ASSERT(!pn->isDefn());
+    JS_ASSERT(pn->isUsed());
+    JS_ASSERT(pn->pn_lexdef);
+    JS_ASSERT(pn->pn_cookie.isFree());
 
     /*
      * We are compiling a function body and may be able to optimize name
      * to stack slot. Look for an argument or variable in the function and
      * rewrite pn_op and update pn accordingly.
      */
-    switch (dn_kind) {
+    switch (dn->kind()) {
       case Definition::UNKNOWN:
         return true;
 
-      case Definition::LET:
-        switch (op) {
-          case JSOP_NAME:     op = JSOP_GETLOCAL; break;
-          case JSOP_SETNAME:  op = JSOP_SETLOCAL; break;
-          case JSOP_INCNAME:  op = JSOP_INCLOCAL; break;
-          case JSOP_NAMEINC:  op = JSOP_LOCALINC; break;
-          case JSOP_DECNAME:  op = JSOP_DECLOCAL; break;
-          case JSOP_NAMEDEC:  op = JSOP_LOCALDEC; break;
-          default: JS_NOT_REACHED("let");
-        }
-        break;
-
       case Definition::ARG:
         switch (op) {
           case JSOP_NAME:     op = JSOP_GETARG; break;
           case JSOP_SETNAME:  op = JSOP_SETARG; break;
           case JSOP_INCNAME:  op = JSOP_INCARG; break;
           case JSOP_NAMEINC:  op = JSOP_ARGINC; break;
           case JSOP_DECNAME:  op = JSOP_DECARG; break;
           case JSOP_NAMEDEC:  op = JSOP_ARGDEC; break;
           default: JS_NOT_REACHED("arg");
         }
         JS_ASSERT(!pn->isConst());
         break;
 
       case Definition::VAR:
         if (dn->isOp(JSOP_CALLEE)) {
             JS_ASSERT(op != JSOP_CALLEE);
-            JS_ASSERT((bce->sc->fun()->flags & JSFUN_LAMBDA) && atom == bce->sc->fun()->atom);
+
+            /*
+             * Currently, the ALIASEDVAR ops do not support accessing the
+             * callee of a DeclEnvObject, so use NAME.
+             */
+            if (dn->pn_cookie.level() != bce->script->staticLevel)
+                return true;
+
+            JS_ASSERT(bce->sc->fun()->flags & JSFUN_LAMBDA);
+            JS_ASSERT(pn->pn_atom == bce->sc->fun()->atom);
 
             /*
              * Leave pn->isOp(JSOP_NAME) if bce->fun is heavyweight to
              * address two cases: a new binding introduced by eval, and
              * assignment to the name in strict mode.
              *
              *   var fun = (function f(s) { eval(s); return f; });
              *   assertEq(fun("var f = 42"), 42);
@@ -1365,38 +1401,65 @@ BindNameToSlot(JSContext *cx, BytecodeEm
             }
 
             pn->setOp(op);
             pn->pn_dflags |= PND_BOUND;
             return true;
         }
         /* FALL THROUGH */
 
-      default:
-        JS_ASSERT_IF(dn_kind != Definition::FUNCTION,
-                     dn_kind == Definition::VAR ||
-                     dn_kind == Definition::CONST);
+      case Definition::FUNCTION:
+      case Definition::CONST:
+      case Definition::LET:
         switch (op) {
           case JSOP_NAME:     op = JSOP_GETLOCAL; break;
           case JSOP_SETNAME:  op = JSOP_SETLOCAL; break;
           case JSOP_SETCONST: op = JSOP_SETLOCAL; break;
           case JSOP_INCNAME:  op = JSOP_INCLOCAL; break;
           case JSOP_NAMEINC:  op = JSOP_LOCALINC; break;
           case JSOP_DECNAME:  op = JSOP_DECLOCAL; break;
           case JSOP_NAMEDEC:  op = JSOP_LOCALDEC; break;
           default: JS_NOT_REACHED("local");
         }
-        JS_ASSERT_IF(dn_kind == Definition::CONST, pn->pn_dflags & PND_CONST);
         break;
+
+      default:
+        JS_NOT_REACHED("unexpected dn->kind()");
+    }
+
+    /*
+     * The difference between the current static level and the static level of
+     * the definition is the number of function scopes between the current
+     * scope and dn's scope.
+     */
+    unsigned skip = bce->script->staticLevel - dn->pn_cookie.level();
+    JS_ASSERT_IF(skip, dn->isClosed());
+
+    /*
+     * Explicitly disallow accessing var/let bindings in global scope from
+     * nested functions. The reason for this limitation is that, since the
+     * global script is not included in the static scope chain (1. because it
+     * has no object to stand in the static scope chain, 2. to minimize memory
+     * bloat where a single live function keeps its whole global script
+     * alive.), ScopeCoordinateToTypeSet is not able to find the var/let's
+     * associated types::TypeSet.
+     */
+    if (skip) {
+        BytecodeEmitter *bceSkipped = bce;
+        for (unsigned i = 0; i < skip; i++)
+            bceSkipped = bceSkipped->parent;
+        if (!bceSkipped->sc->inFunction())
+            return true;
     }
 
     JS_ASSERT(!pn->isOp(op));
     pn->setOp(op);
-    if (!pn->pn_cookie.set(bce->sc->context, 0, cookie.slot()))
-        return false;
+    if (!pn->pn_cookie.set(bce->sc->context, skip, dn->pn_cookie.slot()))
+        return false;
+
     pn->pn_dflags |= PND_BOUND;
     return true;
 }
 
 /*
  * If pn contains a useful expression, return true with *answer set to true.
  * If pn contains a useless expression, return true with *answer set to false.
  * Return false on error.
@@ -1637,21 +1700,18 @@ BytecodeEmitter::needsImplicitThis()
 }
 
 void
 BytecodeEmitter::tellDebuggerAboutCompiledScript(JSContext *cx)
 {
     js_CallNewScriptHook(cx, script, script->function());
     if (!parent) {
         GlobalObject *compileAndGoGlobal = NULL;
-        if (script->compileAndGo) {
-            compileAndGoGlobal = script->globalObject;
-            if (!compileAndGoGlobal)
-                compileAndGoGlobal = &sc->scopeChain()->global();
-        }
+        if (script->compileAndGo)
+            compileAndGoGlobal = &script->global();
         Debugger::onNewScript(cx, script, compileAndGoGlobal);
     }
 }
 
 bool
 BytecodeEmitter::reportError(ParseNode *pn, unsigned errorNumber, ...)
 {
     va_list args;
@@ -3427,27 +3487,24 @@ EmitAssignment(JSContext *cx, BytecodeEm
 {
     ptrdiff_t top = bce->offset();
 
     /*
      * Check left operand type and generate specialized code for it.
      * Specialize to avoid ECMA "reference type" values on the operand
      * stack, which impose pervasive runtime "GetValue" costs.
      */
-    jsatomid atomIndex = (jsatomid) -1;              /* quell GCC overwarning */
+    jsatomid atomIndex = (jsatomid) -1;
     jsbytecode offset = 1;
 
     switch (lhs->getKind()) {
       case PNK_NAME:
         if (!BindNameToSlot(cx, bce, lhs))
             return false;
-        if (!lhs->pn_cookie.isFree()) {
-            JS_ASSERT(lhs->pn_cookie.level() == 0);
-            atomIndex = lhs->pn_cookie.slot();
-        } else {
+        if (lhs->pn_cookie.isFree()) {
             if (!bce->makeAtomIndex(lhs->pn_atom, &atomIndex))
                 return false;
             if (!lhs->isConst()) {
                 JSOp op = lhs->isOp(JSOP_SETGNAME) ? JSOP_BINDGNAME : JSOP_BINDNAME;
                 if (!EmitIndex32(cx, op, atomIndex, bce))
                     return false;
                 offset++;
             }
@@ -4212,17 +4269,16 @@ EmitIf(JSContext *cx, BytecodeEmitter *b
  *
  *  bytecode          stackDepth  srcnotes
  *  evaluate a        +1
  *  evaluate b        +1
  *  dup               +1          SRC_DESTRUCTLET + offset to enterlet0
  *  destructure y
  *  pick 1
  *  dup               +1          SRC_DESTRUCTLET + offset to enterlet0
- *  pick
  *  destructure z
  *  pick 1
  *  pop               -1
  *  enterlet0                     SRC_DECL + offset to leaveblockexpr
  *  evaluate e        +1
  *  leaveblockexpr    -3          SRC_PCBASE + offset to evaluate a
  *
  * Note that, since enterlet0 simply changes fp->blockChain and does not
@@ -4819,25 +4875,25 @@ EmitFunc(JSContext *cx, BytecodeEmitter 
         SharedContext sc(cx, /* scopeChain = */ NULL, fun, funbox, funbox->strictModeState);
         sc.cxFlags = funbox->cxFlags;
         if (bce->sc->funMightAliasLocals())
             sc.setFunMightAliasLocals();  // inherit funMightAliasLocals from parent
         sc.bindings.transfer(&funbox->bindings);
         JS_ASSERT_IF(bce->sc->inStrictMode(), sc.inStrictMode());
 
         // Inherit most things (principals, version, etc) from the parent.
-        GlobalObject *globalObject = fun->getParent() ? &fun->getParent()->global() : NULL;
         Rooted<JSScript*> parent(cx, bce->script);
+        Rooted<JSObject*> enclosingScope(cx, EnclosingStaticScope(bce));
         Rooted<JSScript*> script(cx, JSScript::Create(cx,
+                                                      enclosingScope,
                                                       /* savedCallerFun = */ false,
                                                       parent->principals,
                                                       parent->originPrincipals,
                                                       parent->compileAndGo,
                                                       /* noScriptRval = */ false,
-                                                      globalObject,
                                                       parent->getVersion(),
                                                       parent->staticLevel + 1));
         if (!script)
             return false;
 
         BytecodeEmitter bce2(bce, bce->parser, &sc, script, bce->callerFrame, bce->hasGlobalScope,
                              pn->pn_pos.begin.lineno);
         if (!bce2.init())
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -731,16 +731,20 @@ struct ParseNode {
 #define PND_BLOCKCHILD  0x20            /* use or def is direct block child */
 #define PND_PLACEHOLDER 0x40            /* placeholder definition for lexdep */
 #define PND_BOUND       0x80            /* bound to a stack or global slot */
 #define PND_DEOPTIMIZED 0x100           /* former pn_used name node, pn_lexdef
                                            still valid, but this use no longer
                                            optimizable via an upvar opcode */
 #define PND_CLOSED      0x200           /* variable is closed over */
 #define PND_DEFAULT     0x400           /* definition is an arg with a default */
+#define PND_IMPLICITARGUMENTS 0x800     /* the definition is a placeholder for
+                                           'arguments' that has been converted
+                                           into a definition after the function
+                                           body has been parsed. */
 
 /* Flags to propagate from uses to definition. */
 #define PND_USE2DEF_FLAGS (PND_ASSIGNED | PND_CLOSED)
 
 /* PN_LIST pn_xflags bits. */
 #define PNX_STRCAT      0x01            /* PNK_ADD list has string term */
 #define PNX_CANTFOLD    0x02            /* PNK_ADD list has unfoldable term */
 #define PNX_POPVAR      0x04            /* PNK_VAR or PNK_CONST last result
@@ -777,16 +781,17 @@ struct ParseNode {
     bool isLet() const          { return test(PND_LET); }
     bool isConst() const        { return test(PND_CONST); }
     bool isInitialized() const  { return test(PND_INITIALIZED); }
     bool isBlockChild() const   { return test(PND_BLOCKCHILD); }
     bool isPlaceholder() const  { return test(PND_PLACEHOLDER); }
     bool isDeoptimized() const  { return test(PND_DEOPTIMIZED); }
     bool isAssigned() const     { return test(PND_ASSIGNED); }
     bool isClosed() const       { return test(PND_CLOSED); }
+    bool isImplicitArguments() const { return test(PND_IMPLICITARGUMENTS); }
 
     /*
      * True iff this definition creates a top-level binding in the overall
      * script being compiled -- that is, it affects the whole program's
      * bindings, not bindings for a specific function (unless this definition
      * is in the outermost scope in eval code, executed within a function) or
      * the properties of a specific object (through the with statement).
      *
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -97,24 +97,16 @@ StrictModeGetter::setQueuedStrictModeErr
 static void
 PushStatementTC(TreeContext *tc, StmtInfoTC *stmt, StmtType type)
 {
     stmt->blockid = tc->blockid();
     PushStatement(tc, stmt, type);
     stmt->isFunctionBodyBlock = false;
 }
 
-// Push a block scope statement and link blockObj into tc->blockChain.
-static void
-PushBlockScopeTC(TreeContext *tc, StmtInfoTC *stmt, StaticBlockObject &blockObj)
-{
-    PushStatementTC(tc, stmt, STMT_BLOCK);
-    FinishPushBlockScope(tc, stmt, blockObj);
-}
-
 Parser::Parser(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin,
                const jschar *chars, size_t length, const char *fn, unsigned ln, JSVersion v,
                bool foldConstants, bool compileAndGo)
   : AutoGCRooter(cx, PARSER),
     context(cx),
     strictModeGetter(this),
     tokenStream(cx, prin, originPrin, chars, length, fn, ln, v, &strictModeGetter),
     tempPoolMark(NULL),
@@ -519,17 +511,20 @@ CheckStrictParameters(JSContext *cx, Par
     JSAtom *evalAtom = cx->runtime->atomState.evalAtom;
 
     /* name => whether we've warned about the name already */
     HashMap<JSAtom *, bool> parameters(cx);
     if (!parameters.init(sc->bindings.numArgs()))
         return false;
 
     // Start with lastVariable(), not the last argument, for destructuring.
-    for (Shape::Range r = sc->bindings.lastVariable(); !r.empty(); r.popFront()) {
+    Shape::Range r = sc->bindings.lastVariable();
+    Shape::Range::AutoRooter root(cx, &r);
+
+    for (; !r.empty(); r.popFront()) {
         jsid id = r.front().propid();
         if (!JSID_IS_ATOM(id))
             continue;
 
         JSAtom *name = JSID_TO_ATOM(id);
 
         if (name == argumentsAtom || name == evalAtom) {
             if (!ReportBadParameter(cx, parser, name, JSMSG_BAD_BINDING))
@@ -665,22 +660,25 @@ Parser::functionBody(FunctionBodyType ty
      */
     for (AtomDefnRange r = tc->lexdeps->all(); !r.empty(); r.popFront()) {
         JSAtom *atom = r.front().key();
         Definition *dn = r.front().value();
         JS_ASSERT(dn->isPlaceholder());
         if (atom == arguments) {
             /*
              * Turn 'dn' into a proper definition so uses will be bound as
-             * GETLOCAL in the emitter.
+             * GETLOCAL in the emitter. The PND_IMPLICITARGUMENTS flag informs
+             * CompExprTransplanter (and anyone else) that this definition node
+             * has no proper declaration in the parse tree.
              */
             if (!BindLocalVariable(context, tc, dn, VARIABLE))
                 return NULL;
             dn->setOp(JSOP_GETLOCAL);
             dn->pn_dflags &= ~PND_PLACEHOLDER;
+            dn->pn_dflags |= PND_IMPLICITARGUMENTS;
 
             /* NB: this leaves r invalid so we must break immediately. */
             tc->lexdeps->remove(arguments);
             break;
         }
     }
 
     bool hasRest = tc->sc->fun()->hasRest();
@@ -2136,22 +2134,26 @@ struct RemoveDecl {
         tc->decls.remove(atom);
         return true;
     }
 };
 
 static void
 PopStatementTC(TreeContext *tc)
 {
-    if (tc->topStmt->isBlockScope) {
-        StaticBlockObject &blockObj = *tc->topStmt->blockObj;
-        JS_ASSERT(!blockObj.inDictionaryMode());
-        ForEachLetDef(tc, blockObj, RemoveDecl());
+    StaticBlockObject *blockObj = tc->topStmt->blockObj;
+    JS_ASSERT(!!blockObj == (tc->topStmt->isBlockScope));
+
+    FinishPopStatement(tc);
+
+    if (blockObj) {
+        JS_ASSERT(!blockObj->inDictionaryMode());
+        ForEachLetDef(tc, *blockObj, RemoveDecl());
+        blockObj->resetPrevBlockChainFromParser();
     }
-    FinishPopStatement(tc);
 }
 
 static inline bool
 OuterLet(TreeContext *tc, StmtInfoTC *stmt, HandleAtom atom)
 {
     while (stmt->downScope) {
         stmt = LexicalLookup(tc, atom, NULL, stmt->downScope);
         if (!stmt)
@@ -2750,32 +2752,37 @@ Parser::returnOrYield(bool useAssignExpr
     {
         return NULL;
     }
 
     return pn;
 }
 
 static ParseNode *
-PushLexicalScope(JSContext *cx, Parser *parser, StaticBlockObject &obj, StmtInfoTC *stmt)
+PushLexicalScope(JSContext *cx, Parser *parser, StaticBlockObject &blockObj, StmtInfoTC *stmt)
 {
     ParseNode *pn = LexicalScopeNode::create(PNK_LEXICALSCOPE, parser);
     if (!pn)
         return NULL;
 
-    ObjectBox *blockbox = parser->newObjectBox(&obj);
+    ObjectBox *blockbox = parser->newObjectBox(&blockObj);
     if (!blockbox)
         return NULL;
 
-    PushBlockScopeTC(parser->tc, stmt, obj);
+    TreeContext *tc = parser->tc;
+
+    PushStatementTC(tc, stmt, STMT_BLOCK);
+    blockObj.initPrevBlockChainFromParser(tc->blockChain);
+    FinishPushBlockScope(tc, stmt, blockObj);
+
     pn->setOp(JSOP_LEAVEBLOCK);
     pn->pn_objbox = blockbox;
     pn->pn_cookie.makeFree();
     pn->pn_dflags = 0;
-    if (!GenerateBlockId(parser->tc, stmt->blockid))
+    if (!GenerateBlockId(tc, stmt->blockid))
         return NULL;
     pn->pn_blockid = stmt->blockid;
     return pn;
 }
 
 static ParseNode *
 PushLexicalScope(JSContext *cx, Parser *parser, StmtInfoTC *stmt)
 {
@@ -3767,17 +3774,17 @@ Parser::letStatement()
              * list stack, if it isn't already there.  If it is there, but it
              * lacks the SIF_SCOPE flag, it must be a try, catch, or finally
              * block.
              */
             stmt->isBlockScope = true;
             stmt->downScope = tc->topScopeStmt;
             tc->topScopeStmt = stmt;
 
-            blockObj->setEnclosingBlock(tc->blockChain);
+            blockObj->initPrevBlockChainFromParser(tc->blockChain);
             tc->blockChain = blockObj;
             stmt->blockObj = blockObj;
 
 #ifdef DEBUG
             ParseNode *tmp = tc->blockNode;
             JS_ASSERT(!tmp || !tmp->isKind(PNK_LEXICALSCOPE));
 #endif
 
@@ -4896,21 +4903,26 @@ Parser::unaryExpr()
  * the one or more bindings induced by V have not yet been created.
  */
 class CompExprTransplanter {
     ParseNode       *root;
     Parser          *parser;
     bool            genexp;
     unsigned        adjust;
     unsigned        funcLevel;
+    HashSet<Definition *> visitedImplicitArguments;
 
   public:
     CompExprTransplanter(ParseNode *pn, Parser *parser, bool ge, unsigned adj)
-      : root(pn), parser(parser), genexp(ge), adjust(adj), funcLevel(0)
-    {
+      : root(pn), parser(parser), genexp(ge), adjust(adj), funcLevel(0),
+        visitedImplicitArguments(parser->context)
+    {}
+
+    bool init() {
+        return visitedImplicitArguments.init();
     }
 
     bool transplant(ParseNode *pn);
 };
 
 /*
  * A helper for lazily checking for the presence of illegal |yield| or |arguments|
  * tokens inside of generator expressions. This must be done lazily since we don't
@@ -5175,16 +5187,31 @@ CompExprTransplanter::transplant(ParseNo
                     /*
                      * The variable first occurs free in the 'yield' expression;
                      * move the existing placeholder node (and all its uses)
                      * from the parent's lexdeps into the generator's lexdeps.
                      */
                     tc->parent->lexdeps->remove(atom);
                     if (!tc->lexdeps->put(atom, dn))
                         return false;
+                } else if (dn->isImplicitArguments()) {
+                    /*
+                     * Implicit 'arguments' Definition nodes (see
+                     * PND_IMPLICITARGUMENTS in Parser::functionBody) are only
+                     * reachable via the lexdefs of their uses. Unfortunately,
+                     * there may be multiple uses, so we need to maintain a set
+                     * to only bump the definition once.
+                     */
+                    if (genexp && !visitedImplicitArguments.has(dn)) {
+                        if (!BumpStaticLevel(dn, tc))
+                            return false;
+                        AdjustBlockId(dn, adjust, tc);
+                        if (!visitedImplicitArguments.put(dn))
+                            return false;
+                    }
                 }
             }
         }
 
         if (pn->pn_pos >= root->pn_pos)
             AdjustBlockId(pn, adjust, tc);
         break;
 
@@ -5257,16 +5284,19 @@ Parser::comprehensionTail(ParseNode *kid
         pn->pn_blockid = stmtInfo.blockid = blockid;
         JS_ASSERT(adjust < blockid);
         adjust = blockid - adjust;
     }
 
     pnp = &pn->pn_expr;
 
     CompExprTransplanter transplanter(kid, this, kind == PNK_SEMI, adjust);
+    if (!transplanter.init())
+        return NULL;
+
     transplanter.transplant(kid);
 
     JS_ASSERT(tc->blockChain && tc->blockChain == pn->pn_objbox->object);
     data.initLet(HoistVars, *tc->blockChain, JSMSG_ARRAY_INIT_TOO_BIG);
 
     do {
         /*
          * FOR node is binary, left is loop control and right is body.  Use
--- a/js/src/frontend/TreeContext-inl.h
+++ b/js/src/frontend/TreeContext-inl.h
@@ -140,17 +140,16 @@ frontend::PushStatement(ContextT *ct, ty
 }
 
 template <class ContextT>
 void
 frontend::FinishPushBlockScope(ContextT *ct, typename ContextT::StmtInfo *stmt,
                                StaticBlockObject &blockObj)
 {
     stmt->isBlockScope = true;
-    blockObj.setEnclosingBlock(ct->blockChain);
     stmt->downScope = ct->topScopeStmt;
     ct->topScopeStmt = stmt;
     ct->blockChain = &blockObj;
     stmt->blockObj = &blockObj;
 }
 
 template <class ContextT>
 void
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/testImplicitArgumentsInGenExprs.js
@@ -0,0 +1,5 @@
+assertEq(((function() arguments) for (x in [1])).next()(42)[0], 42);
+assertEq(((function() {return arguments}) for (x in [1])).next()(42)[0], 42);
+assertEq(((function() {return arguments[0] + arguments[1]}) for (x in [1])).next()(41,1), 42);
+assertEq(((function() {return arguments[0] + (function() { return arguments[0]})(arguments[1])}) for (x in [1])).next()(41,1), 42);
+assertEq(((function() { var arguments = 3; return arguments}) for (x in [1])).next()(42), 3);
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -309,51 +309,42 @@ ScriptAnalysis::analyzeBytecode(JSContex
           case JSOP_QNAMECONST:
             isJaegerCompileable = false;
             /* FALL THROUGH */
           case JSOP_NAME:
           case JSOP_CALLNAME:
           case JSOP_BINDNAME:
           case JSOP_SETNAME:
           case JSOP_DELNAME:
+          case JSOP_GETALIASEDVAR:
+          case JSOP_CALLALIASEDVAR:
+          case JSOP_SETALIASEDVAR:
             usesScopeChain_ = true;
             isInlineable = false;
             break;
 
-          case JSOP_GETALIASEDVAR:
-          case JSOP_CALLALIASEDVAR:
-          case JSOP_SETALIASEDVAR: {
-            JS_ASSERT(!isInlineable);
-            usesScopeChain_ = true;
-            break;
-          }
-
           case JSOP_DEFFUN:
           case JSOP_DEFVAR:
           case JSOP_DEFCONST:
           case JSOP_SETCONST:
-            extendsScope_ = true;
             isInlineable = canTrackVars = false;
             break;
 
           case JSOP_EVAL:
-            extendsScope_ = true;
             isInlineable = canTrackVars = false;
             break;
 
           case JSOP_ENTERWITH:
-            addsScopeObjects_ = true;
             isJaegerCompileable = isInlineable = canTrackVars = false;
             break;
 
           case JSOP_ENTERLET0:
           case JSOP_ENTERLET1:
           case JSOP_ENTERBLOCK:
           case JSOP_LEAVEBLOCK:
-            addsScopeObjects_ = true;
             isInlineable = false;
             break;
 
           case JSOP_THIS:
             usesThisValue_ = true;
             break;
 
           case JSOP_CALL:
--- a/js/src/jsanalyze.h
+++ b/js/src/jsanalyze.h
@@ -354,26 +354,16 @@ static inline uint32_t GetBytecodeSlot(J
       case JSOP_CALLLOCAL:
       case JSOP_SETLOCAL:
       case JSOP_INCLOCAL:
       case JSOP_DECLOCAL:
       case JSOP_LOCALINC:
       case JSOP_LOCALDEC:
         return LocalSlot(script, GET_SLOTNO(pc));
 
-      case JSOP_GETALIASEDVAR:
-      case JSOP_CALLALIASEDVAR:
-      case JSOP_SETALIASEDVAR:
-      {
-        unsigned index;
-        return ScopeCoordinateToFrameIndex(script, pc, &index) == FrameIndex_Local
-               ? LocalSlot(script, index)
-               : ArgSlot(index);
-      }
-
       case JSOP_THIS:
         return ThisSlot();
 
       default:
         JS_NOT_REACHED("Bad slot opcode");
         return 0;
     }
 }
@@ -838,18 +828,16 @@ class ScriptAnalysis
 
     /* --------- Bytecode analysis --------- */
 
     bool usesReturnValue_:1;
     bool usesScopeChain_:1;
     bool usesThisValue_:1;
     bool hasFunctionCalls_:1;
     bool modifiesArguments_:1;
-    bool extendsScope_:1;
-    bool addsScopeObjects_:1;
     bool localsAliasStack_:1;
     bool isInlineable:1;
     bool isJaegerCompileable:1;
     bool canTrackVars:1;
 
     uint32_t numReturnSites_;
 
     /* --------- Lifetime analysis --------- */
@@ -896,25 +884,16 @@ 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 the script may extend declarations in its top level scope with
-     * dynamic fun/var declarations or through eval.
-     */
-    bool extendsScope() { return extendsScope_; }
-
-    /* True if the script may add block or with objects to its scope chain. */
-    bool addsScopeObjects() { return addsScopeObjects_; }
-
-    /*
      * True if there are any LOCAL opcodes aliasing values on the stack (above
      * script->nfixed).
      */
     bool localsAliasStack() { return localsAliasStack_; }
 
     /* Accessors for bytecode information. */
 
     Bytecode& getCode(uint32_t offset) {
@@ -1112,35 +1091,16 @@ class ScriptAnalysis
     bool trackSlot(uint32_t slot) { return !slotEscapes(slot) && canTrackVars && slot < 1000; }
 
     const LifetimeVariable & liveness(uint32_t slot) {
         JS_ASSERT(script->compartment()->activeAnalysis);
         JS_ASSERT(!slotEscapes(slot));
         return lifetimes[slot];
     }
 
-    /*
-     * If a NAME or similar opcode is definitely accessing a particular slot
-     * of a script this one is nested in, get that script/slot.
-     */
-    struct NameAccess {
-        JSScript *script;
-        types::TypeScriptNesting *nesting;
-        uint32_t slot;
-
-        /* Decompose the slot above. */
-        bool arg;
-        uint32_t index;
-
-        const Value **basePointer() const {
-            return arg ? &nesting->argArray : &nesting->varArray;
-        }
-    };
-    NameAccess resolveNameAccess(JSContext *cx, jsid id, bool addDependency = false);
-
     void printSSA(JSContext *cx);
     void printTypes(JSContext *cx);
 
     void clearAllocations();
 
   private:
     void setOOM(JSContext *cx) {
         if (!outOfMemory)
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -871,16 +871,19 @@ JSRuntime::init(uint32_t maxbytes)
         return false;
 
     if (!stackSpace.init())
         return false;
 
     if (!scriptFilenameTable.init())
         return false;
 
+    if (!evalCache.init())
+        return false;
+
     debugScopes = this->new_<DebugScopes>(this);
     if (!debugScopes || !debugScopes->init()) {
         Foreground::delete_(debugScopes);
         return false;
     }
 
     nativeStackBase = GetNativeStackBase();
     return true;
@@ -4658,18 +4661,25 @@ JS_CloneFunctionObject(JSContext *cx, JS
         JS_ASSERT(parent);
     }
 
     if (!funobj->isFunction()) {
         ReportIsNotFunction(cx, ObjectValue(*funobj));
         return NULL;
     }
 
+    /*
+     * If a function was compiled as compile-and-go or was compiled to be
+     * lexically nested inside some other script, we cannot clone it without
+     * breaking the compiler's assumptions.
+     */
     RootedFunction fun(cx, funobj->toFunction());
-    if (fun->isInterpreted() && fun->script()->compileAndGo) {
+    if (fun->isInterpreted() &&
+        (fun->script()->compileAndGo || fun->script()->enclosingStaticScope()))
+    {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
         return NULL;
     }
 
     if (fun->isBoundFunction()) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_CANT_CLONE_OBJECT);
@@ -4897,20 +4907,19 @@ CompileUCScriptForPrincipalsCommon(JSCon
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, principals);
     AutoLastFrameCheck lfc(cx);
 
     bool compileAndGo = cx->hasRunOption(JSOPTION_COMPILE_N_GO);
     bool noScriptRval = cx->hasRunOption(JSOPTION_NO_SCRIPT_RVAL);
-    bool needScriptGlobal = true;
     return frontend::CompileScript(cx, obj, NULL, principals, originPrincipals,
-                                   compileAndGo, noScriptRval, needScriptGlobal,
-                                   chars, length, filename, lineno, version);
+                                   compileAndGo, noScriptRval, chars, length,
+                                   filename, lineno, version);
 }
 
 extern JS_PUBLIC_API(JSScript *)
 JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
                                        JSPrincipals *principals,
                                        const jschar *chars, size_t length,
                                        const char *filename, unsigned lineno,
                                        JSVersion version)
@@ -5103,20 +5112,19 @@ CompileUTF8FileHelper(JSContext *cx, JSO
 
     JS_ASSERT(i <= len);
     len = i;
     size_t decodelen = len;
     jschar *decodebuf = (jschar *)cx->malloc_(decodelen * sizeof(jschar));
     if (JS_DecodeUTF8(cx, buf, len, decodebuf, &decodelen)) {
         bool compileAndGo = cx->hasRunOption(JSOPTION_COMPILE_N_GO);
         bool noScriptRval = cx->hasRunOption(JSOPTION_NO_SCRIPT_RVAL);
-        bool needScriptGlobal = true;
         script = frontend::CompileScript(cx, obj, NULL, principals, NULL,
-                                         compileAndGo, noScriptRval, needScriptGlobal,
-                                         decodebuf, decodelen, filename, 1, cx->findVersion());
+                                         compileAndGo, noScriptRval, decodebuf, decodelen,
+                                         filename, 1, cx->findVersion());
     } else {
         script = NULL;
     }
     cx->free_(buf);
     cx->free_(decodebuf);
     return script;
 }
 
@@ -5176,19 +5184,17 @@ JS_CompileUTF8FileHandle(JSContext *cx, 
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
     return JS_CompileUTF8FileHandleForPrincipals(cx, obj, filename, file, NULL);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetGlobalFromScript(JSScript *script)
 {
     JS_ASSERT(!script->isCachedEval);
-    JS_ASSERT(script->globalObject);
-
-    return script->globalObject;
+    return &script->global();
 }
 
 static JSFunction *
 CompileUCFunctionForPrincipalsCommon(JSContext *cx, JSObject *obj_,
                                      JSPrincipals *principals, const char *name,
                                      unsigned nargs, const char **argnames,
                                      const jschar *chars, size_t length,
                                      const char *filename, unsigned lineno, JSVersion version)
@@ -5373,17 +5379,17 @@ JS_ExecuteScript(JSContext *cx, JSObject
      * Mozilla caches pre-compiled scripts (e.g., in the XUL prototype cache)
      * and runs them against multiple globals. With a compartment per global,
      * this requires cloning the pre-compiled script into each new global.
      * Since each script gets run once, there is no point in trying to cache
      * this clone. Ideally, this would be handled at some pinch point in
      * mozilla, but there doesn't seem to be one, so we handle it here.
      */
     if (scriptArg->compartment() != obj->compartment()) {
-        script = CloneScript(cx, scriptArg);
+        script = CloneScript(cx, NullPtr(), NullPtr(), scriptArg);
         if (!script.get())
             return false;
     } else {
         script = scriptArg;
     }
 
     return Execute(cx, script.get(), *obj, rval);
 }
@@ -5404,23 +5410,22 @@ EvaluateUCScriptForPrincipalsCommon(JSCo
                                     jsval *rval, JSVersion compileVersion)
 {
     JS_THREADSAFE_ASSERT(cx->compartment != cx->runtime->atomsCompartment);
 
     RootedObject obj(cx, obj_);
 
     bool compileAndGo = true;
     bool noScriptRval = !rval;
-    bool needScriptGlobal = true;
 
     CHECK_REQUEST(cx);
     AutoLastFrameCheck lfc(cx);
     JSScript *script = frontend::CompileScript(cx, obj, NULL, principals, originPrincipals,
-                                               compileAndGo, noScriptRval, needScriptGlobal,
-                                               chars, length, filename, lineno, compileVersion);
+                                               compileAndGo, noScriptRval, chars, length,
+                                               filename, lineno, compileVersion);
     if (!script)
         return false;
 
     JS_ASSERT(script->getVersion() == compileVersion);
 
     return Execute(cx, script, *obj, rval);
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4640,16 +4640,20 @@ extern JS_PUBLIC_API(JSFunction *)
 JS_DefineUCFunction(JSContext *cx, JSObject *obj,
                     const jschar *name, size_t namelen, JSNative call,
                     unsigned nargs, unsigned attrs);
 
 extern JS_PUBLIC_API(JSFunction *)
 JS_DefineFunctionById(JSContext *cx, JSObject *obj, jsid id, JSNative call,
                       unsigned nargs, unsigned attrs);
 
+/*
+ * Clone a top-level function into a new scope. This function will dynamically
+ * fail if funobj was lexically nested inside some other function.
+ */
 extern JS_PUBLIC_API(JSObject *)
 JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent);
 
 /*
  * Methods usually act upon |this| objects only from a single global object and
  * compartment.  Sometimes, however, a method must act upon |this| values from
  * multiple global objects or compartments.  In such cases the |this| value a
  * method might see will be wrapped, such that various access to the object --
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -168,28 +168,35 @@ class ToSourceCache
     Map *map_;
   public:
     ToSourceCache() : map_(NULL) {}
     JSString *lookup(JSFunction *fun);
     void put(JSFunction *fun, JSString *);
     void purge();
 };
 
-class EvalCache
+struct EvalCacheLookup
 {
-    static const unsigned SHIFT = 6;
-    static const unsigned LENGTH = 1 << SHIFT;
-    JSScript *table_[LENGTH];
+    JSLinearString *str;
+    JSFunction *caller;
+    unsigned staticLevel;
+    JSVersion version;
+    JSCompartment *compartment;
+};
 
-  public:
-    EvalCache() { PodArrayZero(table_); }
-    JSScript **bucket(JSLinearString *str);
-    void purge();
+struct EvalCacheHashPolicy
+{
+    typedef EvalCacheLookup Lookup;
+
+    static HashNumber hash(const Lookup &l);
+    static bool match(JSScript *script, const EvalCacheLookup &l);
 };
 
+typedef HashSet<JSScript *, EvalCacheHashPolicy, SystemAllocPolicy> EvalCache;
+
 class NativeIterCache
 {
     static const size_t SIZE = size_t(1) << 8;
 
     /* Cached native iterators. */
     JSObject            *data[SIZE];
 
     static size_t getIndex(uint32_t key) {
--- a/js/src/jscntxtinlines.h
+++ b/js/src/jscntxtinlines.h
@@ -288,21 +288,18 @@ class CompartmentChecker
             for (int i = 0; i < ida->length; i++) {
                 if (JSID_IS_OBJECT(ida->vector[i]))
                     check(ida->vector[i]);
             }
         }
     }
 
     void check(JSScript *script) {
-        if (script) {
+        if (script)
             check(script->compartment());
-            if (!script->isCachedEval && script->globalObject)
-                check(script->globalObject);
-        }
     }
 
     void check(StackFrame *fp) {
         if (fp)
             check(fp->scopeChain());
     }
 };
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -530,17 +530,16 @@ JSCompartment::sweep(FreeOp *fop, bool r
             for (CellIterUnderGC i(this, FINALIZE_SCRIPT); !i.done(); i.next()) {
                 JSScript *script = i.get<JSScript>();
                 if (script->types) {
                     types::TypeScript::Sweep(fop, script);
 
                     if (releaseTypes) {
                         script->types->destroy();
                         script->types = NULL;
-                        script->typesPurged = true;
                     }
                 }
             }
         }
 
         {
             gcstats::AutoPhase ap2(rt->gcStats, gcstats::PHASE_SWEEP_TYPES);
             types.sweep(fop);
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -123,17 +123,17 @@ struct JSCompartment
     // Nb: global_ might be NULL, if (a) it's the atoms compartment, or (b) the
     // compartment's global has been collected.  The latter can happen if e.g.
     // a string in a compartment is rooted but no object is, and thus the
     // global isn't rooted, and thus the global can be finalized while the
     // compartment lives on.
     //
     // In contrast, JSObject::global() is infallible because marking a JSObject
     // always marks its global as well.
-    // TODO: add infallible JSScript::global() and JSContext::global()
+    // TODO: add infallible JSScript::global()
     //
     js::GlobalObject *maybeGlobal() const {
         JS_ASSERT_IF(global_, global_->compartment() == this);
         return global_;
     }
 
     void initGlobal(js::GlobalObject &global) {
         JS_ASSERT(global.compartment() == this);
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -180,17 +180,17 @@ JS_SetSingleStepMode(JSContext *cx, JSSc
 JS_PUBLIC_API(JSBool)
 JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc, JSTrapHandler handler, jsval closure)
 {
     assertSameCompartment(cx, script, closure);
 
     if (!CheckDebugMode(cx))
         return false;
 
-    BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc, NULL);
+    BreakpointSite *site = script->getOrCreateBreakpointSite(cx, pc);
     if (!site)
         return false;
     site->setTrap(cx->runtime->defaultFreeOp(), handler, closure);
     return true;
 }
 
 JS_PUBLIC_API(void)
 JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -331,17 +331,18 @@ fun_resolve(JSContext *cx, HandleObject 
         }
     }
 
     return true;
 }
 
 template<XDRMode mode>
 bool
-js::XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *parentScript)
+js::XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
+                           JSObject **objp)
 {
     /* NB: Keep this in sync with CloneInterpretedFunction. */
     JSAtom *atom;
     uint32_t firstword;           /* flag telling whether fun->atom is non-null,
                                    plus for fun->u.i.skipmin, fun->u.i.wrapper,
                                    and 14 bits reserved for future use */
     uint32_t flagsword;           /* word for argument count and fun->flags */
 
@@ -377,17 +378,17 @@ js::XDRInterpretedFunction(XDRState<mode
 
     if (!xdr->codeUint32(&firstword))
         return false;
     if ((firstword & 1U) && !XDRAtom(xdr, &atom))
         return false;
     if (!xdr->codeUint32(&flagsword))
         return false;
 
-    if (!XDRScript(xdr, &script, parentScript))
+    if (!XDRScript(xdr, enclosingScope, enclosingScript, fun, &script))
         return false;
 
     if (mode == XDR_DECODE) {
         fun->nargs = flagsword >> 16;
         JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
         fun->flags = uint16_t(flagsword);
         fun->atom.init(atom);
         fun->initScript(script);
@@ -398,37 +399,37 @@ js::XDRInterpretedFunction(XDRState<mode
         js_CallNewScriptHook(cx, fun->script(), fun);
         *objp = fun;
     }
 
     return true;
 }
 
 template bool
-js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *xdr, JSObject **objp, JSScript *parentScript);
+js::XDRInterpretedFunction(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, JSObject **);
 
 template bool
-js::XDRInterpretedFunction(XDRState<XDR_DECODE> *xdr, JSObject **objp, JSScript *parentScript);
+js::XDRInterpretedFunction(XDRState<XDR_DECODE> *, HandleObject, HandleScript, JSObject **);
 
 JSObject *
-js::CloneInterpretedFunction(JSContext *cx, HandleFunction srcFun)
+js::CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction srcFun)
 {
     /* NB: Keep this in sync with XDRInterpretedFunction. */
 
     RootedObject parent(cx, NULL);
     RootedFunction clone(cx, js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, parent, NULL));
     if (!clone)
         return NULL;
     if (!JSObject::clearParent(cx, clone))
         return NULL;
     if (!JSObject::clearType(cx, clone))
         return NULL;
 
     Rooted<JSScript*> srcScript(cx, srcFun->script());
-    JSScript *clonedScript = CloneScript(cx, srcScript);
+    JSScript *clonedScript = CloneScript(cx, enclosingScope, clone, srcScript);
     if (!clonedScript)
         return NULL;
 
     clone->nargs = srcFun->nargs;
     clone->flags = srcFun->flags;
     clone->atom.init(srcFun->atom);
     clone->initScript(clonedScript);
     clonedScript->setFunction(clone);
@@ -1276,30 +1277,33 @@ js_CloneFunctionObject(JSContext *cx, Ha
          * will have been caught by CloneFunctionObject coming from function
          * definitions or read barriers, so will not get here.
          */
         if (fun->getProto() == proto && !fun->hasSingletonType())
             clone->setType(fun->type());
     } else {
         /*
          * Across compartments we have to clone the script for interpreted
-         * functions.
+         * functions. Cross-compartment cloning only happens via JSAPI
+         * (JS_CloneFunctionObject) which dynamically ensures that 'script' has
+         * no enclosing lexical scope (only the global scope).
          */
         if (clone->isInterpreted()) {
             RootedScript script(cx, clone->script());
             JS_ASSERT(script);
             JS_ASSERT(script->compartment() == fun->compartment());
             JS_ASSERT(script->compartment() != cx->compartment);
+            JS_ASSERT(!script->enclosingStaticScope());
 
             clone->mutableScript().init(NULL);
-            JSScript *cscript = CloneScript(cx, script);
+
+            JSScript *cscript = CloneScript(cx, NullPtr(), clone, script);
             if (!cscript)
                 return NULL;
 
-            cscript->globalObject = &clone->global();
             clone->setScript(cscript);
             cscript->setFunction(clone);
             if (!clone->setTypeForScriptedFunction(cx))
                 return NULL;
 
             js_CallNewScriptHook(cx, clone->script(), clone);
             Debugger::onNewScript(cx, clone->script(), NULL);
         }
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -67,16 +67,17 @@ struct JSFunction : public JSObject
     bool hasRest()           const { return flags & JSFUN_HAS_REST; }
     bool isInterpreted()     const { return kind() >= JSFUN_INTERPRETED; }
     bool isNative()          const { return !isInterpreted(); }
     bool isNativeConstructor() const { return flags & JSFUN_CONSTRUCTOR; }
     bool isHeavyweight()     const { return JSFUN_HEAVYWEIGHT_TEST(flags); }
     bool isNullClosure()     const { return kind() == JSFUN_NULL_CLOSURE; }
     bool isFunctionPrototype() const { return flags & JSFUN_PROTOTYPE; }
     bool isInterpretedConstructor() const { return isInterpreted() && !isFunctionPrototype(); }
+    bool isNamedLambda()     const { return (flags & JSFUN_LAMBDA) && atom; }
 
     uint16_t kind()          const { return flags & JSFUN_KINDMASK; }
     void setKind(uint16_t k) {
         JS_ASSERT(!(k & ~JSFUN_KINDMASK));
         flags = (flags & ~JSFUN_KINDMASK) | k;
     }
 
     /* Returns the strictness of this function, which must be interpreted. */
@@ -248,27 +249,25 @@ JSFunction::toExtended()
 
 inline const js::FunctionExtended *
 JSFunction::toExtended() const
 {
     JS_ASSERT(isExtended());
     return static_cast<const js::FunctionExtended *>(this);
 }
 
-inline bool
-js_IsNamedLambda(JSFunction *fun) { return (fun->flags & JSFUN_LAMBDA) && fun->atom; }
-
 namespace js {
 
 template<XDRMode mode>
 bool
-XDRInterpretedFunction(XDRState<mode> *xdr, JSObject **objp, JSScript *parentScript);
+XDRInterpretedFunction(XDRState<mode> *xdr, HandleObject enclosingScope,
+                       HandleScript enclosingScript, JSObject **objp);
 
 extern JSObject *
-CloneInterpretedFunction(JSContext *cx, HandleFunction fun);
+CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction fun);
 
 } /* namespace js */
 
 extern JSBool
 js_fun_apply(JSContext *cx, unsigned argc, js::Value *vp);
 
 extern JSBool
 js_fun_call(JSContext *cx, unsigned argc, js::Value *vp);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3051,17 +3051,17 @@ PurgeRuntime(JSTracer *trc)
 
     rt->tempLifoAlloc.freeUnused();
 
     rt->gsnCache.purge();
     rt->propertyCache.purge(rt);
     rt->newObjectCache.purge();
     rt->nativeIterCache.purge();
     rt->toSourceCache.purge();
-    rt->evalCache.purge();
+    rt->evalCache.clear();
 
     for (ContextIter acx(rt); !acx.done(); acx.next())
         acx->purge();
 }
 
 static bool
 ShouldPreserveJITCode(JSCompartment *c, int64_t currentTime)
 {
@@ -4322,16 +4322,28 @@ JS::CheckStackRoots(JSContext *cx)
     // should be modified to properly root any gcthings, and very possibly any
     // code calling that function should also be modified if it was improperly
     // assuming that GC could not happen at all within the called function.
     // (The latter may not apply if the AssertRootingUnnecessary only protected
     // a portion of a function, so the callers were already assuming that GC
     // could happen.)
     JS_ASSERT(!cx->rootingUnnecessary);
 
+    // GCs can't happen when analysis/inference/compilation are active.
+    if (cx->compartment->activeAnalysis)
+        return;
+
+    // Can switch to the atoms compartment during analysis.
+    if (IsAtomsCompartment(cx->compartment)) {
+        for (CompartmentsIter c(rt); !c.done(); c.next()) {
+            if (c.get()->activeAnalysis)
+                return;
+        }
+    }
+
     AutoCopyFreeListToArenas copy(rt);
 
     JSTracer checker;
     JS_TracerInit(&checker, rt, EmptyMarkCallback);
 
     ConservativeGCData *cgcd = &rt->conservativeGC;
     cgcd->recordStackTop();
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -1858,17 +1858,17 @@ TypeCompartment::newAllocationSiteTypeOb
             return NULL;
         }
     }
 
     AllocationSiteTable::AddPtr p = allocationSiteTable->lookupForAdd(key);
     JS_ASSERT(!p);
 
     RootedObject proto(cx);
-    RootedObject global(cx, key.script->global());
+    RootedObject global(cx, &key.script->global());
     if (!js_GetClassPrototype(cx, global, key.kind, &proto, NULL))
         return NULL;
 
     RootedScript keyScript(cx, key.script);
     TypeObject *res = newTypeObject(cx, key.script, key.kind, proto);
     if (!res) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return NULL;
@@ -1950,29 +1950,29 @@ types::UseNewTypeForInitializer(JSContex
     if (!cx->typeInferenceEnabled() || script->function())
         return false;
 
     if (key != JSProto_Object && !(key >= JSProto_Int8Array && key <= JSProto_Uint8ClampedArray))
         return false;
 
     AutoEnterTypeInference enter(cx);
 
-    if (!script->ensureRanAnalysis(cx, NULL))
+    if (!script->ensureRanAnalysis(cx))
         return false;
 
     return !script->analysis()->getCode(pc).inLoop;
 }
 
 bool
 types::ArrayPrototypeHasIndexedProperty(JSContext *cx, JSScript *script)
 {
     if (!cx->typeInferenceEnabled() || !script->hasGlobal())
         return true;
 
-    JSObject *proto = script->global()->getOrCreateArrayPrototype(cx);
+    JSObject *proto = script->global().getOrCreateArrayPrototype(cx);
     if (!proto)
         return true;
 
     do {
         TypeObject *type = proto->getType(cx);
         if (type->unknownProperties())
             return true;
         TypeSet *indexTypes = type->getProperty(cx, JSID_VOID, false);
@@ -2884,18 +2884,16 @@ TypeObject::setFlags(JSContext *cx, Type
         return;
 
     AutoEnterTypeInference enter(cx);
 
     if (singleton) {
         /* Make sure flags are consistent with persistent object state. */
         JS_ASSERT_IF(flags & OBJECT_FLAG_UNINLINEABLE,
                      interpretedFunction->script()->uninlineable);
-        JS_ASSERT_IF(flags & OBJECT_FLAG_REENTRANT_FUNCTION,
-                     interpretedFunction->script()->reentrantOuterFunction);
         JS_ASSERT_IF(flags & OBJECT_FLAG_ITERATED,
                      singleton->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON));
     }
 
     this->flags |= flags;
 
     InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags);
 
@@ -3137,115 +3135,16 @@ GetInitializerType(JSContext *cx, JSScri
     JSProtoKey key = isArray ? JSProto_Array : JSProto_Object;
 
     if (UseNewTypeForInitializer(cx, script, pc, key))
         return NULL;
 
     return TypeScript::InitObject(cx, script, pc, key);
 }
 
-/*
- * Detach nesting state for script from its parent, removing it entirely if it
- * has no children of its own. This happens when walking type information while
- * initially resolving NAME accesses, thus will not invalidate any compiler
- * dependencies.
- */
-static void
-DetachNestingParent(JSScript *script)
-{
-    TypeScriptNesting *nesting = script->nesting();
-
-    if (!nesting || !nesting->parent)
-        return;
-
-    /* Remove from parent's list of children. */
-    JSScript **pscript = &nesting->parent->nesting()->children;
-    while ((*pscript)->nesting() != nesting)
-        pscript = &(*pscript)->nesting()->next;
-    *pscript = nesting->next;
-
-    nesting->parent = NULL;
-
-    /* If this nesting can have no children of its own, destroy it. */
-    if (!script->isOuterFunction)
-        script->clearNesting();
-}
-
-ScriptAnalysis::NameAccess
-ScriptAnalysis::resolveNameAccess(JSContext *cx, jsid id, bool addDependency)
-{
-    JS_ASSERT(cx->typeInferenceEnabled());
-
-    NameAccess access;
-    PodZero(&access);
-
-    if (!JSID_IS_ATOM(id))
-        return access;
-    JSAtom *atom = JSID_TO_ATOM(id);
-
-    JSScript *script = this->script;
-    while (script->function() && script->nesting()) {
-        if (!script->ensureRanInference(cx))
-            return access;
-
-        /*
-         * Don't resolve names in scripts which use 'let' or 'with'. New names
-         * bound here can mask variables of the script itself.
-         *
-         * Also, don't resolve names in scripts which are generators. Frame
-         * balancing works differently for generators and we do not maintain
-         * active frame counts for such scripts.
-         */
-        if (script->analysis()->addsScopeObjects() || script->isGenerator)
-            return access;
-
-        /* Check if the script definitely binds the identifier. */
-        unsigned index;
-        BindingKind kind = script->bindings.lookup(cx, atom, &index);
-        if (kind == ARGUMENT || kind == VARIABLE) {
-            TypeObject *obj = script->function()->getType(cx);
-
-            if (addDependency) {
-                /*
-                 * Record the dependency which compiled code has on the outer
-                 * function being non-reentrant.
-                 */
-                if (TypeSet::HasObjectFlags(cx, obj, OBJECT_FLAG_REENTRANT_FUNCTION))
-                    return access;
-            }
-
-            if (!script->isOuterFunction)
-                return access;
-
-            access.script = script;
-            access.nesting = script->nesting();
-            access.slot = (kind == ARGUMENT) ? ArgSlot(index) : LocalSlot(script, index);
-            access.arg = (kind == ARGUMENT);
-            access.index = index;
-            return access;
-        } else if (kind != NONE) {
-            return access;
-        }
-
-        /*
-         * The script's bindings do not contain a name for the function itself,
-         * don't resolve name accesses on lambdas in DeclEnv objects on the
-         * scope chain.
-         */
-        if (atom == CallObjectLambdaName(*script->function()))
-            return access;
-
-        if (!script->nesting()->parent)
-            return access;
-        script = script->nesting()->parent;
-    }
-
-    return access;
-}
-
 /* Analyze type information for a single bytecode. */
 bool
 ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset,
                                      TypeInferenceState &state)
 {
     jsbytecode *pc = script->code + offset;
     JSOp op = (JSOp)*pc;
 
@@ -3461,91 +3360,61 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
         if (id == NameToId(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]))
             seen->addType(cx, Type::UndefinedType());
         if (id == NameToId(cx->runtime->atomState.NaNAtom))
             seen->addType(cx, Type::DoubleType());
         if (id == NameToId(cx->runtime->atomState.InfinityAtom))
             seen->addType(cx, Type::DoubleType());
 
         /* Handle as a property access. */
-        PropertyAccess(cx, script, pc, script->global()->getType(cx), false, seen, id);
+        PropertyAccess(cx, script, pc, script->global().getType(cx), false, seen, id);
 
         if (op == JSOP_CALLGNAME)
             pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
 
         if (CheckNextTest(pc))
             pushed[0].addType(cx, Type::UndefinedType());
         break;
       }
 
       case JSOP_NAME:
       case JSOP_CALLNAME: {
         TypeSet *seen = bytecodeTypes(pc);
+        addTypeBarrier(cx, pc, seen, Type::UnknownType());
         seen->addSubset(cx, &pushed[0]);
-
-        /*
-         * Try to resolve this name by walking the function's scope nesting.
-         * If we succeed but the accessed script has had its TypeScript purged
-         * in the past, we still must use a type barrier: the name access can
-         * be on a call object which predated the purge, and whose types might
-         * not be reflected in the reconstructed information.
-         */
-        jsid id = GetAtomId(cx, script, pc, 0);
-        NameAccess access = resolveNameAccess(cx, id);
-        if (access.script && !access.script->typesPurged) {
-            TypeSet *types = TypeScript::SlotTypes(access.script, access.slot);
-            types->addSubsetBarrier(cx, script, pc, seen);
-        } else {
-            addTypeBarrier(cx, pc, seen, Type::UnknownType());
-        }
-
         if (op == JSOP_CALLNAME)
             pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
         break;
       }
 
       case JSOP_BINDGNAME:
       case JSOP_BINDNAME:
         break;
 
       case JSOP_SETGNAME: {
         jsid id = GetAtomId(cx, script, pc, 0);
-        PropertyAccess(cx, script, pc, script->global()->getType(cx),
+        PropertyAccess(cx, script, pc, script->global().getType(cx),
                        true, poppedTypes(pc, 0), id);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
       }
 
-      case JSOP_SETNAME: {
-        jsid id = GetAtomId(cx, script, pc, 0);
-        NameAccess access = resolveNameAccess(cx, id);
-        if (access.script) {
-            TypeSet *types = TypeScript::SlotTypes(access.script, access.slot);
-            poppedTypes(pc, 0)->addSubset(cx, types);
-        } else {
-            cx->compartment->types.monitorBytecode(cx, script, offset);
-        }
-        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
-        break;
-      }
-
+      case JSOP_SETNAME:
       case JSOP_SETCONST:
         cx->compartment->types.monitorBytecode(cx, script, offset);
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_GETXPROP: {
         TypeSet *seen = bytecodeTypes(pc);
         addTypeBarrier(cx, pc, seen, Type::UnknownType());
         seen->addSubset(cx, &pushed[0]);
         break;
       }
 
-      case JSOP_GETALIASEDVAR:
-      case JSOP_CALLALIASEDVAR:
       case JSOP_GETARG:
       case JSOP_CALLARG:
       case JSOP_GETLOCAL:
       case JSOP_CALLLOCAL: {
         uint32_t slot = GetBytecodeSlot(script, pc);
         if (trackSlot(slot)) {
             /*
              * Normally these opcodes don't pop anything, but they are given
@@ -3555,22 +3424,21 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
             poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         } else if (slot < TotalSlots(script)) {
             TypeSet *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 || op == JSOP_CALLALIASEDVAR)
+        if (op == JSOP_CALLARG || op == JSOP_CALLLOCAL)
             pushed[0].addPropagateThis(cx, script, pc, Type::UndefinedType());
         break;
       }
 
-      case JSOP_SETALIASEDVAR:
       case JSOP_SETARG:
       case JSOP_SETLOCAL: {
         uint32_t slot = GetBytecodeSlot(script, pc);
         if (!trackSlot(slot) && slot < TotalSlots(script)) {
             TypeSet *types = TypeScript::SlotTypes(script, slot);
             poppedTypes(pc, 0)->addSubset(cx, types);
         }
 
@@ -3578,16 +3446,34 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
          * 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.
          */
         poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
         break;
       }
 
+      case JSOP_GETALIASEDVAR:
+      case JSOP_CALLALIASEDVAR:
+        /*
+         * Every aliased variable will contain 'undefined' in addition to the
+         * type of whatever value is written to it. Thus, a dynamic barrier is
+         * necessary. Since we don't expect the to observe more than 1 type,
+         * there is little benefit to maintaining a TypeSet for the aliased
+         * variable. Instead, we monitor/barrier all reads unconditionally.
+         */
+        bytecodeTypes(pc)->addSubset(cx, &pushed[0]);
+        if (op == JSOP_CALLALIASEDVAR)
+            pushed[0].addPropagateThis(cx, script, pc, Type::UnknownType());
+        break;
+
+      case JSOP_SETALIASEDVAR:
+        poppedTypes(pc, 0)->addSubset(cx, &pushed[0]);
+        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: {
@@ -3967,17 +3853,17 @@ ScriptAnalysis::analyzeTypesBytecode(JSC
 
       case JSOP_CASE:
         poppedTypes(pc, 1)->addSubset(cx, &pushed[0]);
         break;
 
       case JSOP_GENERATOR:
           if (script->function()) {
             if (script->hasGlobal()) {
-                JSObject *proto = script->global()->getOrCreateGeneratorPrototype(cx);
+                JSObject *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));
             } else {
                 TypeScript::ReturnTypes(script)->addType(cx, Type::UnknownType());
@@ -4070,55 +3956,16 @@ ScriptAnalysis::analyzeTypes(JSContext *
      * 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());
 
-    TypeScriptNesting *nesting = script->function() ? script->nesting() : NULL;
-    if (nesting && nesting->parent) {
-        /*
-         * Check whether NAME accesses can be resolved in parent scopes, and
-         * detach from the parent if so. Even if outdated activations of this
-         * function are live when the parent is called again, we do not need to
-         * consider this reentrance as no state in the parent will be used.
-         */
-        if (!nesting->parent->ensureRanInference(cx))
-            return;
-
-        bool detached = false;
-
-        /* Don't track for leaf scripts which have no free variables. */
-        if (!usesScopeChain() && !script->isOuterFunction) {
-            DetachNestingParent(script);
-            detached = true;
-        }
-
-        /*
-         * If the names bound by the script are extensible (DEFFUN, EVAL, ...),
-         * don't resolve NAME accesses into the parent.
-         */
-        if (!detached && extendsScope()) {
-            DetachNestingParent(script);
-            detached = true;
-        }
-
-
-        if (!detached) {
-            /*
-             * Don't track for parents which add call objects or are generators,
-             * don't resolve NAME accesses into the parent.
-             */
-            if (nesting->parent->analysis()->addsScopeObjects() || nesting->parent->isGenerator)
-                DetachNestingParent(script);
-        }
-    }
-
     TypeInferenceState state(cx);
 
     unsigned offset = 0;
     while (offset < script->length) {
         Bytecode *code = maybeCode(offset);
 
         jsbytecode *pc = script->code + offset;
 
@@ -4272,19 +4119,17 @@ AnalyzeNewScriptProperties(JSContext *cx
         /*
          * 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();
-    JS_ASSERT(!script->isInnerFunction);
-
-    if (!script->ensureRanAnalysis(cx, fun) || !script->ensureRanInference(cx)) {
+    if (!script->ensureRanAnalysis(cx) || !script->ensureRanInference(cx)) {
         *pbaseobj = NULL;
         cx->compartment->types.setPendingNukeTypes(cx);
         return false;
     }
 
     if (script->hasClearedGlobal())
         return false;
 
@@ -4510,17 +4355,16 @@ AnalyzePoppedThis(JSContext *cx, Vector<
             JSObject *funcallObj = funcallTypes->getSingleton(cx, false);
             JSObject *scriptObj = scriptTypes->getSingleton(cx, false);
             if (!funcallObj || !scriptObj || !scriptObj->isFunction() ||
                 !scriptObj->toFunction()->isInterpreted()) {
                 return false;
             }
 
             JSFunction *function = scriptObj->toFunction();
-            JS_ASSERT(!function->script()->isInnerFunction);
 
             /*
              * Generate constraints to clear definite properties from the type
              * should the Function.call or callee itself change in the future.
              */
             funcallTypes->add(cx,
                 cx->typeLifoAlloc().new_<TypeConstraintClearDefiniteSingle>(type));
             scriptTypes->add(cx,
@@ -4561,17 +4405,17 @@ AnalyzePoppedThis(JSContext *cx, Vector<
 /*
  * Either make the newScript information for type when it is constructed
  * by the specified script, or regenerate the constraints for an existing
  * newScript on the type after they were cleared by a GC.
  */
 static void
 CheckNewScriptProperties(JSContext *cx, HandleTypeObject type, JSFunction *fun)
 {
-    if (type->unknownProperties() || fun->script()->isInnerFunction)
+    if (type->unknownProperties())
         return;
 
     /* Strawman object to add properties to and watch for duplicates. */
     RootedObject baseobj(cx, NewBuiltinClassInstance(cx, &ObjectClass, gc::FINALIZE_OBJECT16));
     if (!baseobj) {
         if (type->newScript)
             type->clearNewScript(cx);
         return;
@@ -4603,18 +4447,18 @@ CheckNewScriptProperties(JSContext *cx, 
 
     TypeNewScript::Initializer done(TypeNewScript::Initializer::DONE, 0);
 
     /*
      * The base object may have been created with a different finalize kind
      * than we will use for subsequent new objects. Generate an object with the
      * appropriate final shape.
      */
-    baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind,
-                                baseobj->lastProperty());
+    RootedShape shape(cx, baseobj->lastProperty());
+    baseobj = NewReshapedObject(cx, type, baseobj->getParent(), kind, shape);
     if (!baseobj ||
         !type->addDefiniteProperties(cx, baseobj) ||
         !initializerList.append(done)) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
     size_t numBytes = sizeof(TypeNewScript)
@@ -4877,17 +4721,17 @@ IsAboutToBeFinalized(TypeObjectKey *key)
 void
 TypeDynamicResult(JSContext *cx, JSScript *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, NULL)) {
+        if (!script->ensureRanAnalysis(cx)) {
             cx->compartment->types.setPendingNukeTypes(cx);
             return;
         }
         TypeSet *types = script->analysis()->bytecodeTypes(pc);
         if (!types->hasType(type)) {
             InferSpew(ISpewOps, "externalType: monitorResult #%u:%05u: %s",
                       script->id(), pc - script->code, TypeString(type));
             types->addType(cx, type);
@@ -4983,280 +4827,31 @@ void
 TypeMonitorResult(JSContext *cx, JSScript *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, NULL)) {
+    if (!script->ensureRanAnalysis(cx)) {
         cx->compartment->types.setPendingNukeTypes(cx);
         return;
     }
 
     Type type = GetValueType(cx, rval);
     TypeSet *types = script->analysis()->bytecodeTypes(pc);
     if (types->hasType(type))
         return;
 
     InferSpew(ISpewOps, "bytecodeType: #%u:%05u: %s",
               script->id(), pc - script->code, TypeString(type));
     types->addType(cx, type);
 }
 
-bool
-TypeScript::SetScope(JSContext *cx, JSScript *script_, JSObject *scope_)
-{
-    Rooted<JSScript*> script(cx, script_);
-    RootedObject scope(cx, scope_);
-
-    JS_ASSERT(script->types && !script->types->hasScope());
-
-    JSFunction *fun = script->function();
-    bool nullClosure = fun && fun->isNullClosure();
-
-    JS_ASSERT_IF(!fun, !script->isOuterFunction && !script->isInnerFunction);
-    JS_ASSERT_IF(!scope, fun && !script->isInnerFunction);
-
-    /*
-     * The scope object must be the initial one for the script, before any call
-     * object has been created in the heavyweight case.
-     */
-    JS_ASSERT_IF(scope && scope->isCall() && !scope->asCall().isForEval(),
-                 &scope->asCall().callee() != fun);
-
-    if (!script->compileAndGo) {
-        script->types->global = NULL;
-        return true;
-    }
-
-    JS_ASSERT_IF(fun && scope, fun->global() == scope->global());
-    script->types->global = fun ? &fun->global() : &scope->global();
-
-    /*
-     * Update the parent in the script's bindings. The bindings are created
-     * with a NULL parent, and fixing the parent now avoids the need to reshape
-     * every time a call object is created from the bindings.
-     */
-    if (!script->bindings.setParent(cx, script->types->global))
-        return false;
-
-    if (!cx->typeInferenceEnabled())
-        return true;
-
-    if (!script->isInnerFunction || nullClosure) {
-        /*
-         * Outermost functions need nesting information if there are inner
-         * functions directly nested in them.
-         */
-        if (script->isOuterFunction) {
-            script->types->nesting = cx->new_<TypeScriptNesting>();
-            if (!script->types->nesting)
-                return false;
-        }
-        return true;
-    }
-
-    /*
-     * Walk the scope chain to the next call object, which will be the function
-     * the script is nested inside.
-     */
-    while (!scope->isCall())
-        scope = &scope->asScope().enclosingScope();
-
-    CallObject &call = scope->asCall();
-
-    /* The isInnerFunction test ensures there is no intervening strict eval call object. */
-    JS_ASSERT(!call.isForEval());
-
-    /* Don't track non-heavyweight parents, NAME ops won't reach into them. */
-    JSFunction *parentFun = &call.callee();
-    if (!parentFun || !parentFun->isHeavyweight())
-        return true;
-    JSScript *parent = parentFun->script();
-    JS_ASSERT(parent->isOuterFunction);
-
-    /*
-     * We only need the nesting in the child if it has NAME accesses going
-     * into the parent. We won't know for sure whether this is the case until
-     * analyzing the script's types, which we don't want to do yet. The nesting
-     * info we make here may get pruned if/when we eventually do such analysis.
-     */
-
-    /*
-     * Scopes are set when scripts first execute, and the parent script must
-     * have executed first. It is still possible for the parent script to not
-     * have a scope, however, as we occasionally purge all TypeScripts from the
-     * compartment and there may be inner function objects parented to an
-     * activation of the outer function sticking around. In such cases, treat
-     * the parent's call object as the most recent one, so that it is not
-     * marked as reentrant.
-     */
-    if (!parent->ensureHasTypes(cx))
-        return false;
-    if (!parent->types->hasScope()) {
-        if (!SetScope(cx, parent, &call.enclosingScope()))
-            return false;
-        parent->nesting()->activeCall = &call;
-        parent->nesting()->argArray = Valueify(call.argArray());
-        parent->nesting()->varArray = Valueify(call.varArray());
-    }
-
-    JS_ASSERT(!script->types->nesting);
-
-    /* Construct and link nesting information for the two functions. */
-
-    script->types->nesting = cx->new_<TypeScriptNesting>();
-    if (!script->types->nesting)
-        return false;
-
-    script->nesting()->parent = parent;
-    script->nesting()->next = parent->nesting()->children;
-    parent->nesting()->children = script;
-
-    return true;
-}
-
-TypeScriptNesting::~TypeScriptNesting()
-{
-    /*
-     * Unlink from any parent/child. Nesting info on a script does not keep
-     * either the parent or children live during GC.
-     */
-
-    if (parent) {
-        JSScript **pscript = &parent->nesting()->children;
-        while ((*pscript)->nesting() != this)
-            pscript = &(*pscript)->nesting()->next;
-        *pscript = next;
-    }
-
-    while (children) {
-        TypeScriptNesting *child = children->nesting();
-        children = child->next;
-        child->parent = NULL;
-        child->next = NULL;
-    }
-}
-
-bool
-ClearActiveNesting(JSScript *start)
-{
-    /*
-     * Clear active call information for script and any outer functions
-     * inner to it. Return false if an inner function has frames on the stack.
-     */
-
-    /* Traverse children, then parent, avoiding recursion. */
-    JSScript *script = start;
-    bool traverseChildren = true;
-    while (true) {
-        TypeScriptNesting *nesting = script->nesting();
-        if (nesting->children && traverseChildren) {
-            script = nesting->children;
-            continue;
-        }
-        if (nesting->activeFrames)
-            return false;
-        if (script->isOuterFunction) {
-            nesting->activeCall = NULL;
-            nesting->argArray = NULL;
-            nesting->varArray = NULL;
-        }
-        if (script == start)
-            break;
-        if (nesting->next) {
-            script = nesting->next;
-            traverseChildren = true;
-        } else {
-            script = nesting->parent;
-            traverseChildren = false;
-        }
-    }
-
-    return true;
-}
-
-/*
- * For the specified scope and script with an outer function, check if the
- * scope represents a reentrant activation on an inner function of the parent
- * or any of its transitive parents.
- */
-static void
-CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
-{
-  restart:
-    JSScript *parent = script->nesting()->parent;
-    JS_ASSERT(parent);
-
-    while (!scope->isCall() || scope->asCall().callee().script() != parent)
-        scope = &scope->asScope().enclosingScope();
-
-    if (scope != parent->nesting()->activeCall) {
-        parent->reentrantOuterFunction = true;
-        MarkTypeObjectFlags(cx, parent->function(), OBJECT_FLAG_REENTRANT_FUNCTION);
-
-        /*
-         * Continue checking parents to see if this is reentrant for them too.
-         * We don't need to check this in for non-reentrant calls on the outer
-         * function: when we entered any outer function to the immediate parent
-         * we cleared the active call for its transitive children, so a
-         * non-reentrant call on a child is also a non-reentrant call on the
-         * parent.
-         */
-        if (parent->nesting()->parent) {
-            scope = &scope->asScope().enclosingScope();
-            script = parent;
-            goto restart;
-        }
-    }
-}
-
-void
-NestingPrologue(JSContext *cx, StackFrame *fp)
-{
-    JSScript *script = fp->fun()->script();
-    TypeScriptNesting *nesting = script->nesting();
-
-    if (nesting->parent)
-        CheckNestingParent(cx, fp->scopeChain(), script);
-
-    if (script->isOuterFunction) {
-        /*
-         * Check the stack has no frames for this activation, any of its inner
-         * functions or any of their transitive inner functions.
-         *
-         * Also, if the script has an extensible scope, then the arg/var array
-         * can be moved unexpectedly, so abort the optimization.
-         */
-        if (!ClearActiveNesting(script) || script->funHasExtensibleScope) {
-            script->reentrantOuterFunction = true;
-            MarkTypeObjectFlags(cx, fp->fun(), OBJECT_FLAG_REENTRANT_FUNCTION);
-        }
-
-        nesting->activeCall = &fp->callObj();
-        nesting->argArray = Valueify(nesting->activeCall->argArray());
-        nesting->varArray = Valueify(nesting->activeCall->varArray());
-    }
-
-    /* Maintain stack frame count for the function. */
-    nesting->activeFrames++;
-}
-
-void
-NestingEpilogue(StackFrame *fp)
-{
-    JSScript *script = fp->fun()->script();
-    TypeScriptNesting *nesting = script->nesting();
-
-    JS_ASSERT(nesting->activeFrames != 0);
-    nesting->activeFrames--;
-}
-
 } } /* namespace js::types */
 
 /////////////////////////////////////////////////////////////////////
 // TypeScript
 /////////////////////////////////////////////////////////////////////
 
 /*
  * Returns true if we don't expect to compute the correct types for some value
@@ -5423,23 +5018,25 @@ JSFunction::setTypeForScriptedFunction(J
 
     if (!cx->typeInferenceEnabled())
         return true;
 
     if (singleton) {
         if (!setSingletonType(cx))
             return false;
     } else {
+        RootedFunction self(cx, this);
+
         TypeObject *type = cx->compartment->types.newTypeObject(cx, script(),
                                                                 JSProto_Function, getProto());
         if (!type)
             return false;
 
-        setType(type);
-        type->interpretedFunction = this;
+        self->setType(type);
+        type->interpretedFunction = self;
     }
 
     return true;
 }
 
 #ifdef DEBUG
 
 /* static */ void
@@ -5581,18 +5178,16 @@ JSObject::makeLazyType(JSContext *cx)
 
     type->singleton = self;
 
     if (self->isFunction() && self->toFunction()->isInterpreted()) {
         type->interpretedFunction = self->toFunction();
         JSScript *script = type->interpretedFunction->script();
         if (script->uninlineable)
             type->flags |= OBJECT_FLAG_UNINLINEABLE;
-        if (script->reentrantOuterFunction)
-            type->flags |= OBJECT_FLAG_REENTRANT_FUNCTION;
     }
 
     if (self->lastProperty()->hasObjectFlag(BaseShape::ITERATED_SINGLETON))
         type->flags |= OBJECT_FLAG_ITERATED;
 
 #if JS_HAS_XML_SUPPORT
     /*
      * XML objects do not have equality hooks but are treated special by EQ/NE
@@ -5605,20 +5200,20 @@ JSObject::makeLazyType(JSContext *cx)
     if (self->getClass()->ext.equality)
         type->flags |= OBJECT_FLAG_SPECIAL_EQUALITY;
 
     /*
      * Adjust flags for objects which will have the wrong flags set by just
      * looking at the class prototype key.
      */
 
-    if (isSlowArray())
+    if (self->isSlowArray())
         type->flags |= OBJECT_FLAG_NON_DENSE_ARRAY | OBJECT_FLAG_NON_PACKED_ARRAY;
 
-    if (IsTypedArrayProto(this))
+    if (IsTypedArrayProto(self))
         type->flags |= OBJECT_FLAG_NON_TYPED_ARRAY;
 
     self->type_ = type;
 }
 
 /* static */ inline HashNumber
 TypeObjectEntry::hash(JSObject *proto)
 {
@@ -5661,17 +5256,17 @@ JSObject::setNewTypeUnknown(JSContext *c
         if (TypeObjectSet::Ptr p = table.lookup(this))
             MarkTypeObjectUnknownProperties(cx, *p);
     }
 
     return true;
 }
 
 TypeObject *
-JSObject::getNewType(JSContext *cx, JSFunction *fun)
+JSObject::getNewType(JSContext *cx, JSFunction *fun_)
 {
     TypeObjectSet &table = cx->compartment->newTypeObjects;
 
     if (!table.initialized() && !table.init())
         return NULL;
 
     TypeObjectSet::AddPtr p = table.lookupForAdd(this);
     if (p) {
@@ -5683,23 +5278,24 @@ JSObject::getNewType(JSContext *cx, JSFu
          * in existence which are not created by calling 'new' on newScript,
          * we must clear the new script information from the type and will not
          * be able to assume any definite properties for instances of the type.
          * This case is rare, but can happen if, for example, two scripted
          * functions have the same value for their 'prototype' property, or if
          * Object.create is called with a prototype object that is also the
          * 'prototype' property of some scripted function.
          */
-        if (type->newScript && type->newScript->fun != fun)
+        if (type->newScript && type->newScript->fun != fun_)
             type->clearNewScript(cx);
 
         return type;
     }
 
     RootedObject self(cx, this);
+    RootedFunction fun(cx, fun_);
 
     if (!setDelegate(cx))
         return NULL;
 
     bool markUnknown = self->lastProperty()->hasObjectFlag(BaseShape::NEW_TYPE_UNKNOWN);
 
     RootedTypeObject type(cx);
     type = cx->compartment->types.newTypeObject(cx, NULL, JSProto_Object, self, markUnknown);
@@ -6083,39 +5679,27 @@ TypeScript::Sweep(FreeOp *fop, JSScript 
             IsAboutToBeFinalized(type.objectKey()))
         {
             *presult = result->next;
             fop->delete_(result);
         } else {
             presult = &result->next;
         }
     }
-
-    /*
-     * If the script has nesting state with a most recent activation, we do not
-     * need either to mark the call object or clear it if not live. Even with
-     * a dead pointer in the nesting, we can't get a spurious match while
-     * testing for reentrancy: if previous activations are still live, they
-     * cannot alias the most recent one, and future activations will overwrite
-     * activeCall on creation.
-     */
 }
 
 void
 TypeScript::destroy()
 {
     while (dynamicList) {
         TypeResult *next = dynamicList->next;
         Foreground::delete_(dynamicList);
         dynamicList = next;
     }
 
-    if (nesting)
-        Foreground::delete_(nesting);
-
     Foreground::free_(this);
 }
 
 inline size_t
 TypeSet::computedSizeOfExcludingThis()
 {
     /*
      * This memory is allocated within the temp pool (but accounted for
@@ -6161,18 +5745,16 @@ SizeOfScriptTypeInferenceData(JSScript *
         return;
 
     /* If TI is disabled, a single TypeScript is still present. */
     if (!script->compartment()->types.inferenceEnabled) {
         sizes->scripts += mallocSizeOf(typeScript);
         return;
     }
 
-    sizes->scripts += mallocSizeOf(typeScript->nesting);
-
     unsigned count = TypeScript::NumTypeSets(script);
     sizes->scripts += mallocSizeOf(typeScript);
 
     TypeResult *result = typeScript->dynamicList;
     while (result) {
         sizes->scripts += mallocSizeOf(result);
         result = result->next;
     }
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -281,24 +281,21 @@ enum {
     OBJECT_FLAG_UNINLINEABLE          = 0x00080000,
 
     /* Whether any objects have an equality hook. */
     OBJECT_FLAG_SPECIAL_EQUALITY      = 0x00100000,
 
     /* Whether any objects have been iterated over. */
     OBJECT_FLAG_ITERATED              = 0x00200000,
 
-    /* Outer function which has been marked reentrant. */
-    OBJECT_FLAG_REENTRANT_FUNCTION    = 0x00400000,
-
     /* For a global object, whether flags were set on the RegExpStatics. */
-    OBJECT_FLAG_REGEXP_FLAGS_SET      = 0x00800000,
+    OBJECT_FLAG_REGEXP_FLAGS_SET      = 0x00400000,
 
     /* Flags which indicate dynamic properties of represented objects. */
-    OBJECT_FLAG_DYNAMIC_MASK          = 0x00ff0000,
+    OBJECT_FLAG_DYNAMIC_MASK          = 0x007f0000,
 
     /*
      * Whether all properties of this object are considered unknown.
      * If set, all flags in DYNAMIC_MASK will also be set.
      */
     OBJECT_FLAG_UNKNOWN_PROPERTIES    = 0x80000000,
 
     /* Mask for objects created with unknown properties. */
@@ -797,17 +794,17 @@ struct TypeObject : gc::Cell
 
     /* Set flags on this object which are implied by the specified key. */
     inline void setFlagsFromKey(JSContext *cx, JSProtoKey kind);
 
     /*
      * Get the global object which all objects of this type are parented to,
      * or NULL if there is none known.
      */
-    inline JSObject *getGlobal();
+    //inline JSObject *getGlobal();
 
     /* Helpers */
 
     bool addProperty(JSContext *cx, jsid id, Property **pprop);
     bool addDefiniteProperties(JSContext *cx, JSObject *obj);
     bool matchDefiniteProperties(JSObject *obj);
     void addPrototype(JSContext *cx, TypeObject *proto);
     void addPropertyType(JSContext *cx, jsid id, Type type);
@@ -902,129 +899,28 @@ struct TypeCallsite
 
     /* Type set receiving the return value of this call. */
     TypeSet *returnTypes;
 
     inline TypeCallsite(JSContext *cx, JSScript *script, jsbytecode *pc,
                         bool isNew, unsigned argumentCount);
 };
 
-/*
- * Information attached to outer and inner function scripts nested in one
- * another for tracking the reentrance state for outer functions. This state is
- * used to generate fast accesses to the args and vars of the outer function.
- *
- * A function is non-reentrant if, at any point in time, only the most recent
- * activation (i.e. call object) is live. An activation is live if either the
- * activation is on the stack, or a transitive inner function parented to the
- * activation is on the stack.
- *
- * Because inner functions can be (and, quite often, are) stored in object
- * properties and it is difficult to build a fast and robust escape analysis
- * to cope with such flow, we detect reentrance dynamically. For the outer
- * function, we keep track of the call object for the most recent activation,
- * and the number of frames for the function and its inner functions which are
- * on the stack.
- *
- * If the outer function is called while frames associated with a previous
- * activation are on the stack, the outer function is reentrant. If an inner
- * function is called whose scope does not match the most recent activation,
- * the outer function is reentrant.
- *
- * The situation gets trickier when there are several levels of nesting.
- *
- * function foo() {
- *   var a;
- *   function bar() {
- *     var b;
- *     function baz() { return a + b; }
- *   }
- * }
- *
- * At calls to 'baz', we don't want to do the scope check for the activations
- * of both 'foo' and 'bar', but rather 'bar' only. For this to work, a call to
- * 'baz' which is a reentrant call on 'foo' must also be a reentrant call on
- * 'bar'. When 'foo' is called, we clear the most recent call object for 'bar'.
- */
-struct TypeScriptNesting
-{
-    /*
-     * If this is an inner function, the outer function. If non-NULL, this will
-     * be the immediate nested parent of the script (even if that parent has
-     * been marked reentrant). May be NULL even if the script has a nested
-     * parent, if NAME accesses cannot be tracked into the parent (either the
-     * script extends its scope with eval() etc., or the parent can make new
-     * scope chain objects with 'let' or 'with').
-     */
-    JSScript *parent;
-
-    /* If this is an outer function, list of inner functions. */
-    JSScript *children;
-
-    /* Link for children list of parent. */
-    JSScript *next;
-
-    /* If this is an outer function, the most recent activation. */
-    CallObject *activeCall;
-
-    /*
-     * If this is an outer function, pointers to the most recent activation's
-     * arguments and variables arrays. These could be referring either to stack
-     * values in activeCall's frame (if it has not finished yet) or to the
-     * internal slots of activeCall (if the frame has finished). Pointers to
-     * these fields can be embedded directly in JIT code (though remember to
-     * use 'addDependency == true' when calling resolveNameAccess).
-     */
-    const Value *argArray;
-    const Value *varArray;
-
-    /* Number of frames for this function on the stack. */
-    uint32_t activeFrames;
-
-    TypeScriptNesting() { PodZero(this); }
-    ~TypeScriptNesting();
-};
-
-/* Construct nesting information for script wrt its parent. */
-bool CheckScriptNesting(JSContext *cx, JSScript *script);
-
-/* Track nesting state when calling or finishing an outer/inner function. */
-void NestingPrologue(JSContext *cx, StackFrame *fp);
-void NestingEpilogue(StackFrame *fp);
-
 /* Persistent type information for a script, retained across GCs. */
 class TypeScript
 {
     friend struct ::JSScript;
 
     /* Analysis information for the script, cleared on each GC. */
     analyze::ScriptAnalysis *analysis;
 
-    /*
-     * Information about the scope in which a script executes. This information
-     * is not set until the script has executed at least once and SetScope
-     * called, before that 'global' will be poisoned per GLOBAL_MISSING_SCOPE.
-     */
-    static const size_t GLOBAL_MISSING_SCOPE = 0x1;
-
-    /* Global object for the script, if compileAndGo. */
-    HeapPtr<GlobalObject> global;
-
   public:
-
-    /* Nesting state for outer or inner function scripts. */
-    TypeScriptNesting *nesting;
-
     /* Dynamic types generated at points within this script. */
     TypeResult *dynamicList;
 
-    inline TypeScript();
-
-    bool hasScope() { return size_t(global.get()) != GLOBAL_MISSING_SCOPE; }
-
     /* 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 bool SetScope(JSContext *cx, JSScript *script, JSObject *scope);
 
     static inline TypeSet *ReturnTypes(JSScript *script);
@@ -1077,17 +973,16 @@ class TypeScript
     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 void Sweep(FreeOp *fop, JSScript *script);
-    inline void trace(JSTracer *trc);
     void destroy();
 };
 
 struct ArrayTableKey;
 typedef HashMap<ArrayTableKey,ReadBarriered<TypeObject>,ArrayTableKey,SystemAllocPolicy> ArrayTypeTable;
 
 struct ObjectTableKey;
 struct ObjectTableEntry;
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -7,17 +7,16 @@
 /* Inline members for javascript type inference. */
 
 #include "jsarray.h"
 #include "jsanalyze.h"
 #include "jscompartment.h"
 #include "jsinfer.h"
 #include "jsprf.h"
 
-#include "gc/Marking.h"
 #include "gc/Root.h"
 #include "vm/GlobalObject.h"
 
 #include "vm/Stack-inl.h"
 
 #ifndef jsinferinlines_h___
 #define jsinferinlines_h___
 
@@ -307,17 +306,17 @@ TypeMonitorCall(JSContext *cx, const js:
     extern void TypeMonitorCallSlow(JSContext *cx, JSObject *callee,
                                     const CallArgs &args, bool constructing);
 
     RootedObject callee(cx, &args.callee());
     if (callee->isFunction()) {
         JSFunction *fun = callee->toFunction();
         if (fun->isInterpreted()) {
             RootedScript script(cx, fun->script());
-            if (!script->ensureRanAnalysis(cx, fun->environment()))
+            if (!script->ensureRanAnalysis(cx))
                 return false;
             if (cx->typeInferenceEnabled())
                 TypeMonitorCallSlow(cx, callee, args, constructing);
         }
     }
 
     return true;
 }
@@ -444,22 +443,16 @@ UseNewTypeAtEntry(JSContext *cx, StackFr
            fp->prev() && fp->prev()->isScriptFrame() &&
            UseNewType(cx, fp->prev()->script(), fp->prevpc());
 }
 
 /////////////////////////////////////////////////////////////////////
 // Script interface functions
 /////////////////////////////////////////////////////////////////////
 
-inline
-TypeScript::TypeScript()
-{
-    this->global = (js::GlobalObject *) GLOBAL_MISSING_SCOPE;
-}
-
 /* static */ inline unsigned
 TypeScript::NumTypeSets(JSScript *script)
 {
     return script->nTypeSets + analyze::TotalSlots(script);
 }
 
 /* static */ inline TypeSet *
 TypeScript::ReturnTypes(JSScript *script)
@@ -499,17 +492,17 @@ TypeScript::SlotTypes(JSScript *script, 
     JS_ASSERT(slot < js::analyze::TotalSlots(script));
     return script->types->typeArray() + script->nTypeSets + slot;
 }
 
 /* static */ inline TypeObject *
 TypeScript::StandardType(JSContext *cx, JSScript *script, JSProtoKey key)
 {
     RootedObject proto(cx);
-    RootedObject global(cx, script->global());
+    RootedObject global(cx, &script->global());
     if (!js_GetClassPrototype(cx, global, key, &proto, NULL))
         return NULL;
     return proto->getNewType(cx);
 }
 
 struct AllocationSiteKey {
     JSScript *script;
 
@@ -689,17 +682,17 @@ TypeScript::SetThis(JSContext *cx, JSScr
 
     if (!ThisTypes(script)->hasType(type) || analyze) {
         AutoEnterTypeInference enter(cx);
 
         InferSpew(ISpewOps, "externalType: setThis #%u: %s",
                   script->id(), TypeString(type));
         ThisTypes(script)->addType(cx, type);
 
-        if (analyze && script->types->hasScope())
+        if (analyze)
             script->ensureRanInference(cx);
     }
 }
 
 /* static */ inline void
 TypeScript::SetThis(JSContext *cx, JSScript *script, const js::Value &value)
 {
     if (cx->typeInferenceEnabled())
@@ -751,25 +744,16 @@ TypeScript::SetArgument(JSContext *cx, J
 TypeScript::SetArgument(JSContext *cx, JSScript *script, unsigned arg, const js::Value &value)
 {
     if (cx->typeInferenceEnabled()) {
         Type type = GetValueType(cx, value);
         SetArgument(cx, script, arg, type);
     }
 }
 
-void
-TypeScript::trace(JSTracer *trc)
-{
-    if (hasScope() && global)
-        gc::MarkObject(trc, &global, "script_global");
-
-    /* Note: nesting does not keep anything alive. */
-}
-
 /////////////////////////////////////////////////////////////////////
 // TypeCompartment
 /////////////////////////////////////////////////////////////////////
 
 inline JSCompartment *
 TypeCompartment::compartment()
 {
     return (JSCompartment *)((char *)this - offsetof(JSCompartment, types));
@@ -1362,26 +1346,16 @@ TypeObject::setFlagsFromKey(JSContext *c
               | OBJECT_FLAG_NON_TYPED_ARRAY;
         break;
     }
 
     if (!hasAllFlags(flags))
         setFlags(cx, flags);
 }
 
-inline JSObject *
-TypeObject::getGlobal()
-{
-    if (singleton)
-        return &singleton->global();
-    if (interpretedFunction && interpretedFunction->script()->compileAndGo)
-        return &interpretedFunction->global();
-    return NULL;
-}
-
 inline void
 TypeObject::writeBarrierPre(TypeObject *type)
 {
 #ifdef JSGC_INCREMENTAL
     if (!type)
         return;
 
     JSCompartment *comp = type->compartment();
@@ -1447,41 +1421,35 @@ Property::Property(const Property &o)
 
 inline bool
 JSScript::ensureHasTypes(JSContext *cx)
 {
     return types || makeTypes(cx);
 }
 
 inline bool
-JSScript::ensureRanAnalysis(JSContext *cx, JSObject *scope)
+JSScript::ensureRanAnalysis(JSContext *cx)
 {
     js::analyze::AutoEnterAnalysis aea(cx->compartment);
     JSScript *self = this;
     JS::SkipRoot root(cx, &self);
 
     if (!self->ensureHasTypes(cx))
         return false;
-    if (!self->types->hasScope()) {
-        js::RootedObject scopeRoot(cx, scope);
-        if (!js::types::TypeScript::SetScope(cx, self, scope))
-            return false;
-        scope = scopeRoot;
-    }
     if (!self->hasAnalysis() && !self->makeAnalysis(cx))
         return false;
     JS_ASSERT(self->analysis()->ranBytecode());
     return true;
 }
 
 inline bool
 JSScript::ensureRanInference(JSContext *cx)
 {
     JS::RootedScript self(cx, this);
-    if (!ensureRanAnalysis(cx, NULL))
+    if (!ensureRanAnalysis(cx))
         return false;
     if (!self->analysis()->ranInference()) {
         js::types::AutoEnterTypeInference enter(cx);
         self->analysis()->analyzeTypes(cx);
     }
     return !self->analysis()->OOM() &&
         !cx->compartment->types.pendingNukeTypes;
 }
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -474,17 +474,17 @@ js::ExecuteKernel(JSContext *cx, JSScrip
             result->setUndefined();
         return true;
     }
 
     ExecuteFrameGuard efg;
     if (!cx->stack.pushExecuteFrame(cx, script, thisv, scopeChain, type, evalInFrame, &efg))
         return false;
 
-    if (!script->ensureRanAnalysis(cx, &scopeChain))
+    if (!script->ensureRanAnalysis(cx))
         return false;
     TypeScript::SetThis(cx, script, efg.fp()->thisValue());
 
     Probes::startExecution(cx, script);
     bool ok = RunScript(cx, script, efg.fp());
     Probes::stopExecution(cx, script);
 
     /* Propgate the return value out. */
@@ -834,17 +834,17 @@ TryNoteIter::settle()
 }
 
 /*
  * Increment/decrement the value 'v'. The resulting value is stored in *slot.
  * The result of the expression (taking into account prefix/postfix) is stored
  * in *expr.
  */
 static bool
-DoIncDec(JSContext *cx, JSScript *script, jsbytecode *pc, const Value &v, Value *slot, Value *expr)
+DoIncDec(JSContext *cx, HandleScript script, jsbytecode *pc, const Value &v, Value *slot, Value *expr)
 {
     const JSCodeSpec &cs = js_CodeSpec[*pc];
 
     if (v.isInt32()) {
         int32_t i = v.toInt32();
         if (i > JSVAL_INT_MIN && i < JSVAL_INT_MAX) {
             int32_t sum = i + (cs.format & JOF_INC ? 1 : -1);
             *slot = Int32Value(sum);
@@ -2760,16 +2760,17 @@ BEGIN_CASE(JSOP_REST)
 }
 END_CASE(JSOP_REST)
 
 BEGIN_CASE(JSOP_CALLALIASEDVAR)
 BEGIN_CASE(JSOP_GETALIASEDVAR)
 {
     ScopeCoordinate sc = ScopeCoordinate(regs.pc);
     PUSH_COPY(regs.fp()->aliasedVarScope(sc).aliasedVar(sc));
+    TypeScript::Monitor(cx, script, regs.pc, regs.sp[-1]);
 }
 END_CASE(JSOP_GETALIASEDVAR)
 
 BEGIN_CASE(JSOP_SETALIASEDVAR)
 {
     ScopeCoordinate sc = ScopeCoordinate(regs.pc);
     regs.fp()->aliasedVarScope(sc).setAliasedVar(sc, regs.sp[-1]);
 }
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2178,34 +2178,34 @@ JSObject::sealOrFreeze(JSContext *cx, Im
             if (!self->setGenericAttributes(cx, id, &attrs))
                 return false;
         }
     }
 
     return true;
 }
 
-bool
-JSObject::isSealedOrFrozen(JSContext *cx, ImmutabilityType it, bool *resultp)
-{
-    if (isExtensible()) {
+/* static */ bool
+JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it, bool *resultp)
+{
+    if (obj->isExtensible()) {
         *resultp = false;
         return true;
     }
 
     AutoIdVector props(cx);
-    if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props))
+    if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
         return false;
 
     RootedId id(cx);
     for (size_t i = 0, len = props.length(); i < len; i++) {
         id = props[i];
 
         unsigned attrs;
-        if (!getGenericAttributes(cx, id, &attrs))
+        if (!obj->getGenericAttributes(cx, id, &attrs))
             return false;
 
         /*
          * If the property is configurable, this object is neither sealed nor
          * frozen. If the property is a writable data property, this object is
          * not frozen.
          */
         if (!(attrs & JSPROP_PERMANENT) ||
@@ -2236,17 +2236,17 @@ obj_freeze(JSContext *cx, unsigned argc,
 static JSBool
 obj_isFrozen(JSContext *cx, unsigned argc, Value *vp)
 {
     RootedObject obj(cx);
     if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
         return false;
 
     bool frozen;
-    if (!obj->isFrozen(cx, &frozen))
+    if (!JSObject::isFrozen(cx, obj, &frozen))
         return false;
     vp->setBoolean(frozen);
     return true;
 }
 
 static JSBool
 obj_seal(JSContext *cx, unsigned argc, Value *vp)
 {
@@ -2262,17 +2262,17 @@ obj_seal(JSContext *cx, unsigned argc, V
 static JSBool
 obj_isSealed(JSContext *cx, unsigned argc, Value *vp)
 {
     RootedObject obj(cx);
     if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isSealed", &obj))
         return false;
 
     bool sealed;
-    if (!obj->isSealed(cx, &sealed))
+    if (!JSObject::isSealed(cx, obj, &sealed))
         return false;
     vp->setBoolean(sealed);
     return true;
 }
 
 JSFunctionSpec object_methods[] = {
 #if JS_HAS_TOSOURCE
     JS_FN(js_toSource_str,             obj_toSource,                0,0),
@@ -2504,17 +2504,17 @@ js::NewObjectWithType(JSContext *cx, Han
     if (entry != -1 && !obj->hasDynamicSlots())
         cache.fillType(entry, &ObjectClass, type, kind, obj);
 
     return obj;
 }
 
 JSObject *
 js::NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent,
-                      gc::AllocKind kind, Shape *shape)
+                      gc::AllocKind kind, HandleShape shape)
 {
     RootedObject res(cx, NewObjectWithType(cx, type, parent, kind));
     if (!res)
         return NULL;
 
     if (shape->isEmptyShape())
         return res;
 
@@ -2607,26 +2607,30 @@ js_CreateThisForFunction(JSContext *cx, 
     JSObject *proto;
     if (protov.isObject())
         proto = &protov.toObject();
     else
         proto = NULL;
     JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
 
     if (obj && newType) {
+        RootedObject nobj(cx, obj);
+
         /*
          * Reshape the object and give it a (lazily instantiated) singleton
          * type before passing it as the 'this' value for the call.
          */
-        obj->clear(cx);
-        if (!obj->setSingletonType(cx))
+        nobj->clear(cx);
+        if (!nobj->setSingletonType(cx))
             return NULL;
 
         JSScript *calleeScript = callee->toFunction()->script();
-        TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(obj));
+        TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(nobj));
+
+        return nobj;
     }
 
     return obj;
 }
 
 /*
  * Given pc pointing after a property accessing bytecode, return true if the
  * access is "object-detecting" in the sense used by web scripts, e.g., when
@@ -3522,20 +3526,22 @@ JSObject::growSlots(JSContext *cx, uint3
      * by calling 'new' on a particular script, bump the GC kind for that
      * type to give these objects a larger number of fixed slots when future
      * objects are constructed.
      */
     if (!hasLazyType() && !oldCount && type()->newScript) {
         gc::AllocKind kind = type()->newScript->allocKind;
         unsigned newScriptSlots = gc::GetGCKindSlots(kind);
         if (newScriptSlots == numFixedSlots() && gc::TryIncrementAllocKind(&kind)) {
+            AutoEnterTypeInference enter(cx);
+
             Rooted<TypeObject*> typeObj(cx, type());
+            RootedShape shape(cx, typeObj->newScript->shape);
             JSObject *obj = NewReshapedObject(cx, typeObj,
-                                              getParent(), kind,
-                                              typeObj->newScript->shape);
+                                              getParent(), kind, shape);
             if (!obj)
                 return false;
 
             typeObj->newScript->allocKind = kind;
             typeObj->newScript->shape = obj->lastProperty();
             typeObj->markStateChange(cx);
         }
     }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -526,30 +526,34 @@ struct JSObject : public js::ObjectImpl
     /*
      * The guts of Object.seal (ES5 15.2.3.8) and Object.freeze (ES5 15.2.3.9): mark the
      * object as non-extensible, and adjust each property's attributes appropriately: each
      * property becomes non-configurable, and if |freeze|, data properties become
      * read-only as well.
      */
     bool sealOrFreeze(JSContext *cx, ImmutabilityType it);
 
-    bool isSealedOrFrozen(JSContext *cx, ImmutabilityType it, bool *resultp);
+    static bool isSealedOrFrozen(JSContext *cx, js::HandleObject obj, ImmutabilityType it, bool *resultp);
 
     static inline unsigned getSealedOrFrozenAttributes(unsigned attrs, ImmutabilityType it);
 
   public:
     bool preventExtensions(JSContext *cx);
 
     /* ES5 15.2.3.8: non-extensible, all props non-configurable */
     inline bool seal(JSContext *cx) { return sealOrFreeze(cx, SEAL); }
     /* ES5 15.2.3.9: non-extensible, all properties non-configurable, all data props read-only */
     bool freeze(JSContext *cx) { return sealOrFreeze(cx, FREEZE); }
 
-    bool isSealed(JSContext *cx, bool *resultp) { return isSealedOrFrozen(cx, SEAL, resultp); }
-    bool isFrozen(JSContext *cx, bool *resultp) { return isSealedOrFrozen(cx, FREEZE, resultp); }
+    static inline bool isSealed(JSContext *cx, js::HandleObject obj, bool *resultp) {
+        return isSealedOrFrozen(cx, obj, SEAL, resultp);
+    }
+    static inline bool isFrozen(JSContext *cx, js::HandleObject obj, bool *resultp) {
+        return isSealedOrFrozen(cx, obj, FREEZE, resultp);
+    }
 
     /* Accessors for elements. */
 
     inline bool ensureElements(JSContext *cx, unsigned cap);
     bool growElements(JSContext *cx, unsigned cap);
     void shrinkElements(JSContext *cx, unsigned cap);
 
     inline js::ElementIteratorObject *asElementIterator();
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -1547,17 +1547,17 @@ CopyInitializerObject(JSContext *cx, Han
     if (!obj->setLastProperty(cx, baseobj->lastProperty()))
         return NULL;
 
     return obj;
 }
 
 JSObject *
 NewReshapedObject(JSContext *cx, HandleTypeObject type, JSObject *parent,
-                  gc::AllocKind kind, Shape *shape);
+                  gc::AllocKind kind, HandleShape shape);
 
 /*
  * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
  * the object, zero if the final size is unknown. This should only be used for
  * objects that do not require any fixed slots.
  */
 static inline gc::AllocKind
 GuessObjectGCKind(size_t numSlots)
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -6374,17 +6374,17 @@ GetPCCountScriptContents(JSContext *cx, 
 
     StringBuffer buf(cx);
 
     if (!script->function() && !script->compileAndGo)
         return buf.finishString();
 
     {
         JSAutoEnterCompartment ac;
-        if (!ac.enter(cx, script->function() ? (JSObject *) script->function() : script->global()))
+        if (!ac.enter(cx, &script->global()))
             return NULL;
 
         if (!GetPCCountJSON(cx, sac, buf))
             return NULL;
     }
 
     return buf.finishString();
 }
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -337,18 +337,18 @@ OPDEF(JSOP_FINALLY,     135,"finally",  
  * An ALIASEDVAR opcode contains the following immediates:
  *  uint16 hops:  the number of scope objects to skip to find the ScopeObject
  *                containing the variable being accessed
  *  uint16 slot:  the slot containing the variable in the ScopeObject (this
  *                'slot' does not include RESERVED_SLOTS).
  *  uint32 block: the index (into the script object table) of the block chain
  *                at the point of the variable access.
  */
-OPDEF(JSOP_GETALIASEDVAR, 136,"getaliasedvar",NULL,   9,  0,  1, 19,  JOF_SCOPECOORD|JOF_NAME)
-OPDEF(JSOP_CALLALIASEDVAR,137,"callaliasedvar",NULL,  9,  0,  1, 19,  JOF_SCOPECOORD|JOF_NAME)
+OPDEF(JSOP_GETALIASEDVAR, 136,"getaliasedvar",NULL,   9,  0,  1, 19,  JOF_SCOPECOORD|JOF_NAME|JOF_TYPESET)
+OPDEF(JSOP_CALLALIASEDVAR,137,"callaliasedvar",NULL,  9,  0,  1, 19,  JOF_SCOPECOORD|JOF_NAME|JOF_TYPESET)
 OPDEF(JSOP_SETALIASEDVAR, 138,"setaliasedvar",NULL,   9,  1,  1,  3,  JOF_SCOPECOORD|JOF_NAME|JOF_SET|JOF_DETECTING)
 OPDEF(JSOP_INCALIASEDVAR, 139,"incaliasedvar",NULL,   10, 0,  1, 15,  JOF_SCOPECOORD|JOF_NAME|JOF_INC|JOF_TMPSLOT3|JOF_DECOMPOSE)
 OPDEF(JSOP_DECALIASEDVAR, 140,"decaliasedvar",NULL,   10, 0,  1, 15,  JOF_SCOPECOORD|JOF_NAME|JOF_DEC|JOF_TMPSLOT3|JOF_DECOMPOSE)
 OPDEF(JSOP_ALIASEDVARINC, 141,"aliasedvarinc",NULL,   10, 0,  1, 15,  JOF_SCOPECOORD|JOF_NAME|JOF_INC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
 OPDEF(JSOP_ALIASEDVARDEC, 142,"aliasedvardec",NULL,   10, 0,  1, 15,  JOF_SCOPECOORD|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_DECOMPOSE)
 
 /* Unused. */
 OPDEF(JSOP_UNUSED8,       143,"unused8",  NULL,       1,  0,  0,  0,  JOF_BYTE)
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -362,17 +362,17 @@ IndirectProxyHandler::getPropertyDescrip
                                             jsid id, bool set,
                                             PropertyDescriptor *desc)
 {
     return JS_GetPropertyDescriptorById(cx, GetProxyTargetObject(proxy), id,
                                         JSRESOLVE_QUALIFIED, desc);
 }
 
 static bool
-GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
+GetOwnPropertyDescriptor(JSContext *cx, HandleObject obj, jsid id, unsigned flags,
                          JSPropertyDescriptor *desc)
 {
     // If obj is a proxy, we can do better than just guessing. This is
     // important for certain types of wrappers that wrap other wrappers.
     if (obj->isProxy())
         return Proxy::getOwnPropertyDescriptor(cx, obj, id,
                                                flags & JSRESOLVE_ASSIGNING,
                                                desc);
@@ -384,17 +384,18 @@ GetOwnPropertyDescriptor(JSContext *cx, 
     return true;
 }
 
 bool
 IndirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy,
                                                jsid id, bool set,
                                                PropertyDescriptor *desc)
 {
-    return GetOwnPropertyDescriptor(cx, GetProxyTargetObject(proxy), id,
+    RootedObject target(cx, GetProxyTargetObject(proxy));
+    return GetOwnPropertyDescriptor(cx, target, id,
                                     JSRESOLVE_QUALIFIED, desc);
 }
 
 bool
 IndirectProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id_,
                                      PropertyDescriptor *desc)
 {
     RootedObject obj(cx, GetProxyTargetObject(proxy));
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -273,45 +273,22 @@ ShapeTable::grow(JSContext *cx)
     return true;
 }
 
 Shape *
 Shape::getChildBinding(JSContext *cx, const StackShape &child)
 {
     JS_ASSERT(!inDictionary());
 
-    Shape *shape = cx->propertyTree().getChild(cx, this, numFixedSlots(), child);
-    if (shape) {
-        //JS_ASSERT(shape->parent == this); // XXX 'this' is not rooted here
-
-        /*
-         * Update the number of fixed slots which bindings of this shape will
-         * have. Bindings are constructed as new properties come in, so the
-         * call object allocation class is not known ahead of time. Compute
-         * the fixed slot count here, which will feed into call objects created
-         * off of the bindings.
-         */
-        uint32_t slots = child.slotSpan();
-        gc::AllocKind kind = gc::GetGCObjectKind(slots);
+    /* Try to allocate all slots inline. */
+    uint32_t slots = child.slotSpan();
+    gc::AllocKind kind = gc::GetGCObjectKind(slots);
+    uint32_t nfixed = gc::GetGCKindSlots(kind);
 
-        /*
-         * Make sure that the arguments and variables in the call object all
-         * end up in a contiguous range of slots. We need this to be able to
-         * embed the args/vars arrays in the TypeScriptNesting for the function
-         * after the call object's frame has finished.
-         */
-        uint32_t nfixed = gc::GetGCKindSlots(kind);
-        if (nfixed < slots) {
-            nfixed = CallObject::RESERVED_SLOTS;
-            JS_ASSERT(gc::GetGCKindSlots(gc::GetGCObjectKind(nfixed)) == CallObject::RESERVED_SLOTS);
-        }
-
-        shape->setNumFixedSlots(nfixed);
-    }
-    return shape;
+    return cx->propertyTree().getChild(cx, this, nfixed, child);
 }
 
 /* static */ Shape *
 Shape::replaceLastProperty(JSContext *cx, const StackBaseShape &base, JSObject *proto, Shape *shape_)
 {
     RootedShape shape(cx, shape_);
 
     JS_ASSERT(!shape->inDictionary());
@@ -1202,41 +1179,16 @@ Bindings::setExtensibleParents(JSContext
         return false;
     Shape *newShape = Shape::setExtensibleParents(cx, lastBinding);
     if (!newShape)
         return false;
     lastBinding = newShape;
     return true;
 }
 
-bool
-Bindings::setParent(JSContext *cx, JSObject *obj_)
-{
-    RootedObject obj(cx, obj_);
-
-    /*
-     * This may be invoked on GC heap allocated bindings, in which case this
-     * is pointing to an internal value of a JSScript that can't itself be
-     * relocated. The script itself will be rooted, and will not be moved, so
-     * mark the stack value as non-relocatable for the stack root analysis.
-     */
-    Bindings *self = this;
-    SkipRoot root(cx, &self);
-
-    if (!ensureShape(cx))
-        return false;
-
-    /* This is only used for Block objects, which have a NULL proto. */
-    Shape *newShape = Shape::setObjectParent(cx, obj, NULL, self->lastBinding);
-    if (!newShape)
-        return false;
-    self->lastBinding = newShape;
-    return true;
-}
-
 inline
 InitialShapeEntry::InitialShapeEntry() : shape(NULL), proto(NULL)
 {
 }
 
 inline
 InitialShapeEntry::InitialShapeEntry(const ReadBarriered<Shape> &shape, JSObject *proto)
   : shape(shape), proto(proto)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -46,18 +46,16 @@
 #include "vm/RegExpObject-inl.h"
 
 #include "frontend/TreeContext-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::frontend;
 
-namespace js {
-
 BindingKind
 Bindings::lookup(JSContext *cx, JSAtom *name, unsigned *indexp) const
 {
     if (!lastBinding)
         return NONE;
 
     Shape **spp;
     Shape *shape = Shape::search(cx, lastBinding, AtomToId(name), &spp);
@@ -119,17 +117,17 @@ Bindings::add(JSContext *cx, HandleAtom 
     RootedId id(cx);
     if (!name) {
         JS_ASSERT(kind == ARGUMENT); /* destructuring */
         id = INT_TO_JSID(nargs);
     } else {
         id = AtomToId(name);
     }
 
-    StackBaseShape base(&CallClass, NULL, BaseShape::VAROBJ);
+    StackBaseShape base(&CallClass, cx->global(), BaseShape::VAROBJ);
     base.updateGetterSetter(attrs, getter, setter);
 
     UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
     if (!nbase)
         return false;
 
     StackShape child(nbase, id, slot, 0, attrs, Shape::HAS_SHORTID, *indexp);
 
@@ -242,18 +240,16 @@ Bindings::lastVariable() const
 
 void
 Bindings::trace(JSTracer *trc)
 {
     if (lastBinding)
         MarkShape(trc, &lastBinding, "shape");
 }
 
-} /* namespace js */
-
 template<XDRMode mode>
 static bool
 XDRScriptConst(XDRState<mode> *xdr, HeapValue *vp)
 {
     /*
      * A script constant can be an arbitrary primitive value as they are used
      * to implement JSOP_LOOKUPSWITCH. But they cannot be objects, see
      * bug 407186.
@@ -337,19 +333,35 @@ XDRScriptConst(XDRState<mode> *xdr, Heap
       case SCRIPT_VOID:
         if (mode == XDR_DECODE)
             vp->init(UndefinedValue());
         break;
     }
     return true;
 }
 
+static inline uint32_t
+FindBlockIndex(JSScript *script, StaticBlockObject &block)
+{
+    ObjectArray *objects = script->objects();
+    HeapPtrObject *vector = objects->vector;
+    unsigned length = objects->length;
+    for (unsigned i = 0; i < length; ++i) {
+        if (vector[i] == &block)
+            return i;
+    }
+
+    JS_NOT_REACHED("Block not found");
+    return UINT32_MAX;
+}
+
 template<XDRMode mode>
 bool
-js::XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript)
+js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
+              HandleFunction fun, JSScript **scriptp)
 {
     /* NB: Keep this in sync with CloneScript. */
 
     enum ScriptBits {
         NoScriptRval,
         SavedCallerFun,
         StrictModeCode,
         ContainsDynamicNameAccess,
@@ -367,26 +379,22 @@ js::XDRScript(XDRState<mode> *xdr, JSScr
     uint32_t nTypeSets = 0;
     uint32_t scriptBits = 0;
 
     JSContext *cx = xdr->cx();
     Rooted<JSScript*> script(cx);
     nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = nClosedArgs = nClosedVars = 0;
     jssrcnote *notes = NULL;
 
-    /* XDR arguments, var vars, and upvars. */
-    uint16_t nargs, nvars;
-#if defined(DEBUG) || defined(__GNUC__) /* quell GCC overwarning */
-    script = NULL;
-    nargs = nvars = Bindings::BINDING_COUNT_LIMIT;
-#endif
-    uint32_t argsVars;
+    /* XDR arguments and vars. */
+    uint16_t nargs = 0, nvars = 0;
+    uint32_t argsVars = 0;
     if (mode == XDR_ENCODE) {
         script = *scriptp;
-        JS_ASSERT_IF(parentScript, parentScript->compartment() == script->compartment());
+        JS_ASSERT_IF(enclosingScript, enclosingScript->compartment() == script->compartment());
 
         nargs = script->bindings.numArgs();
         nvars = script->bindings.numVars();
         argsVars = (nargs << 16) | nvars;
     }
     if (!xdr->codeUint32(&argsVars))
         return false;
     if (mode == XDR_DECODE) {
@@ -510,17 +518,17 @@ js::XDRScript(XDRState<mode> *xdr, JSScr
             scriptBits |= (1 << ContainsDynamicNameAccess);
         if (script->funHasExtensibleScope)
             scriptBits |= (1 << FunHasExtensibleScope);
         if (script->argumentsHasVarBinding())
             scriptBits |= (1 << ArgumentsHasVarBinding);
         if (script->analyzedArgsUsage() && script->needsArgsObj())
             scriptBits |= (1 << NeedsArgsObj);
         if (script->filename) {
-            scriptBits |= (parentScript && parentScript->filename == script->filename)
+            scriptBits |= (enclosingScript && enclosingScript->filename == script->filename)
                           ? (1 << ParentFilename)
                           : (1 << OwnFilename);
         }
         if (script->isGenerator)
             scriptBits |= (1 << IsGenerator);
 
         JS_ASSERT(!script->compileAndGo);
         JS_ASSERT(!script->hasSingletons);
@@ -559,22 +567,22 @@ js::XDRScript(XDRState<mode> *xdr, JSScr
     if (mode == XDR_DECODE) {
         /* Note: version is packed into the 32b space with another 16b value. */
         JSVersion version_ = JSVersion(version & JS_BITMASK(16));
         JS_ASSERT((version_ & VersionFlags::FULL_MASK) == unsigned(version_));
 
         // principals and originPrincipals are set with xdr->initScriptPrincipals(script) below.
         // staticLevel is set below.
         script = JSScript::Create(cx,
+                                  enclosingScope,
                                   !!(scriptBits & (1 << SavedCallerFun)),
                                   /* principals = */ NULL,
                                   /* originPrincipals = */ NULL,
                                   /* compileAndGo = */ false,
                                   !!(scriptBits & (1 << NoScriptRval)),
-                                  /* globalObject = */ NULL,
                                   version_,
                                   /* staticLevel = */ 0);
         if (!script || !JSScript::partiallyInit(cx, script,
                                                 length, nsrcnotes, natoms, nobjects,
                                                 nregexps, ntrynotes, nconsts, nClosedArgs,
                                                 nClosedVars, nTypeSets))
             return JS_FALSE;
 
@@ -617,19 +625,19 @@ js::XDRScript(XDRState<mode> *xdr, JSScr
         if (!xdr->codeCString(&filename))
             return false;
         if (mode == XDR_DECODE) {
             script->filename = SaveScriptFilename(cx, filename);
             if (!script->filename)
                 return false;
         }
     } else if (scriptBits & (1 << ParentFilename)) {
-        JS_ASSERT(parentScript);
+        JS_ASSERT(enclosingScript);
         if (mode == XDR_DECODE)
-            script->filename = parentScript->filename;
+            script->filename = enclosingScript->filename;
     }
 
     if (mode == XDR_DECODE) {
         script->lineno = lineno;
         script->nslots = uint16_t(nslots);
         script->staticLevel = uint16_t(nslots >> 16);
         xdr->initScriptPrincipals(script);
     }
@@ -643,40 +651,83 @@ js::XDRScript(XDRState<mode> *xdr, JSScr
         } else {
             JSAtom *tmp = script->atoms[i];
             if (!XDRAtom(xdr, &tmp))
                 return false;
         }
     }
 
     /*
-     * Here looping from 0-to-length to xdr objects is essential. It ensures
-     * that block objects from the script->objects array will be written and
-     * restored in the outer-to-inner order. js_XDRBlockObject relies on this
-     * to restore the parent chain.
+     * Here looping from 0-to-length to xdr objects is essential to ensure that
+     * all references to enclosing blocks (via FindBlockIndex below) happen
+     * after the enclosing block has been XDR'd.
      */
     for (i = 0; i != nobjects; ++i) {
         HeapPtr<JSObject> *objp = &script->objects()->vector[i];
         uint32_t isBlock;
         if (mode == XDR_ENCODE) {
             JSObject *obj = *objp;
             JS_ASSERT(obj->isFunction() || obj->isStaticBlock());
             isBlock = obj->isBlock() ? 1 : 0;
         }
         if (!xdr->codeUint32(&isBlock))
             return false;
         if (isBlock == 0) {
+            /* Code the nested function's enclosing scope. */
+            uint32_t funEnclosingScopeIndex = 0;
+            if (mode == XDR_ENCODE) {
+                StaticScopeIter ssi((*objp)->toFunction()->script()->enclosingStaticScope());
+                if (ssi.done() || ssi.type() == StaticScopeIter::FUNCTION) {
+                    JS_ASSERT(ssi.done() == !fun);
+                    funEnclosingScopeIndex = UINT32_MAX;
+                } else {
+                    funEnclosingScopeIndex = FindBlockIndex(script, ssi.block());
+                    JS_ASSERT(funEnclosingScopeIndex < i);
+                }
+            }
+            if (!xdr->codeUint32(&funEnclosingScopeIndex))
+                return false;
+            Rooted<JSObject*> funEnclosingScope(cx);
+            if (mode == XDR_DECODE) {
+                if (funEnclosingScopeIndex == UINT32_MAX) {
+                    funEnclosingScope = fun;
+                } else {
+                    JS_ASSERT(funEnclosingScopeIndex < i);
+                    funEnclosingScope = script->objects()->vector[funEnclosingScopeIndex];
+                }
+            }
+
             JSObject *tmp = *objp;
-            if (!XDRInterpretedFunction(xdr, &tmp, parentScript))
+            if (!XDRInterpretedFunction(xdr, funEnclosingScope, script, &tmp))
                 return false;
             *objp = tmp;
         } else {
+            /* Code the nested block's enclosing scope. */
             JS_ASSERT(isBlock == 1);
+            uint32_t blockEnclosingScopeIndex = 0;
+            if (mode == XDR_ENCODE) {
+                if (StaticBlockObject *block = (*objp)->asStaticBlock().enclosingBlock())
+                    blockEnclosingScopeIndex = FindBlockIndex(script, *block);
+                else
+                    blockEnclosingScopeIndex = UINT32_MAX;
+            }
+            if (!xdr->codeUint32(&blockEnclosingScopeIndex))
+                return false;
+            Rooted<JSObject*> blockEnclosingScope(cx);
+            if (mode == XDR_DECODE) {
+                if (blockEnclosingScopeIndex != UINT32_MAX) {
+                    JS_ASSERT(blockEnclosingScopeIndex < i);
+                    blockEnclosingScope = script->objects()->vector[blockEnclosingScopeIndex];
+                } else {
+                    blockEnclosingScope = fun;
+                }
+            }
+
             StaticBlockObject *tmp = static_cast<StaticBlockObject *>(objp->get());
-            if (!XDRStaticBlockObject(xdr, script, &tmp))
+            if (!XDRStaticBlockObject(xdr, blockEnclosingScope, script, &tmp))
                 return false;
             *objp = tmp;
         }
     }
     for (i = 0; i != nregexps; ++i) {
         if (!XDRScriptRegExpObject(xdr, &script->regexps()->vector[i]))
             return false;
     }
@@ -733,20 +784,20 @@ js::XDRScript(XDRState<mode> *xdr, JSScr
             (void) script->initScriptCounts(cx);
         *scriptp = script;
     }
 
     return true;
 }
 
 template bool
-js::XDRScript(XDRState<XDR_ENCODE> *xdr, JSScript **scriptp, JSScript *parentScript);
+js::XDRScript(XDRState<XDR_ENCODE> *, HandleObject, HandleScript, HandleFunction, JSScript **);
 
 template bool
-js::XDRScript(XDRState<XDR_DECODE> *xdr, JSScript **scriptp, JSScript *parentScript);
+js::XDRScript(XDRState<XDR_DECODE> *, HandleObject, HandleScript, HandleFunction, JSScript **);
 
 bool
 JSScript::initScriptCounts(JSContext *cx)
 {
     JS_ASSERT(!hasScriptCounts);
 
     size_t n = 0;
 
@@ -1069,44 +1120,42 @@ ScriptDataSize(uint32_t length, uint32_t
         size += sizeof(ClosedSlotArray) + nClosedVars * sizeof(uint32_t);
 
     size += length * sizeof(jsbytecode);
     size += nsrcnotes * sizeof(jssrcnote);
     return size;
 }
 
 JSScript *
-JSScript::Create(JSContext *cx, bool savedCallerFun, JSPrincipals *principals,
-                 JSPrincipals *originPrincipals, bool compileAndGo, bool noScriptRval,
-                 GlobalObject *globalObject_, JSVersion version, unsigned staticLevel)
+JSScript::Create(JSContext *cx, HandleObject enclosingScope, bool savedCallerFun,
+                 JSPrincipals *principals, JSPrincipals *originPrincipals,
+                 bool compileAndGo, bool noScriptRval, JSVersion version, unsigned staticLevel)
 {
-    Rooted<GlobalObject*> globalObject(cx, globalObject_);
     JSScript *script = js_NewGCScript(cx);
     if (!script)
         return NULL;
 
     PodZero(script);
 
+    script->enclosingScope_ = enclosingScope;
     script->savedCallerFun = savedCallerFun;
 
     /* Establish invariant: principals implies originPrincipals. */
     if (principals) {
         script->principals = principals;
         script->originPrincipals = originPrincipals ? originPrincipals : principals;
         JS_HoldPrincipals(script->principals);
         JS_HoldPrincipals(script->originPrincipals);
     } else if (originPrincipals) {
         script->originPrincipals = originPrincipals;
         JS_HoldPrincipals(script->originPrincipals);
     }
 
     script->compileAndGo = compileAndGo;
     script->noScriptRval = noScriptRval;
- 
-    script->globalObject = globalObject;
 
     script->version = version;
     JS_ASSERT(script->getVersion() == version);     // assert that no overflow occurred
 
     // This is an unsigned-to-uint16_t conversion, test for too-high values.
     // In practice, recursion in Parser and/or BytecodeEmitter will blow the
     // stack if we nest functions more than a few hundred deep, so this will
     // never trigger.  Oh well.
@@ -1313,17 +1362,17 @@ JSScript::fullyInitFromEmitter(JSContext
     if (cx->compartment->debugMode())
         script->debugMode = true;
 #endif
 
     if (bce->sc->inFunction()) {
         if (bce->sc->funArgumentsHasLocalBinding()) {
             // This must precede the script->bindings.transfer() call below
             script->setArgumentsHasVarBinding();
-            if (bce->sc->funDefinitelyNeedsArgsObj())        
+            if (bce->sc->funDefinitelyNeedsArgsObj())
                 script->setNeedsArgsObj(true);
         } else {
             JS_ASSERT(!bce->sc->funDefinitelyNeedsArgsObj());
         }
     }
 
     if (nClosedArgs)
         PodCopy<uint32_t>(script->closedArgs()->vector, &bce->closedArgs[0], nClosedArgs);
@@ -1645,17 +1694,17 @@ template <class T>
 static inline T *
 Rebase(JSScript *dst, JSScript *src, T *srcp)
 {
     size_t off = reinterpret_cast<uint8_t *>(srcp) - src->data;
     return reinterpret_cast<T *>(dst->data + off);
 }
 
 JSScript *
-js::CloneScript(JSContext *cx, HandleScript src)
+js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript src)
 {
     /* NB: Keep this in sync with XDRScript. */
 
     uint32_t nconsts   = src->hasConsts()   ? src->consts()->length   : 0;
     uint32_t nobjects  = src->hasObjects()  ? src->objects()->length  : 0;
     uint32_t nregexps  = src->hasRegexps()  ? src->regexps()->length  : 0;
     uint32_t ntrynotes = src->hasTrynotes() ? src->trynotes()->length : 0;
     uint32_t nClosedArgs = src->numClosedArgs();
@@ -1694,23 +1743,39 @@ js::CloneScript(JSContext *cx, HandleScr
         return NULL;
 
     /* Objects */
 
     AutoObjectVector objects(cx);
     if (nobjects != 0) {
         HeapPtrObject *vector = src->objects()->vector;
         for (unsigned i = 0; i < nobjects; i++) {
+            JSObject &obj = *vector[i];
             JSObject *clone;
-            if (vector[i]->isStaticBlock()) {
-                Rooted<StaticBlockObject*> block(cx, &vector[i]->asStaticBlock());
-                clone = CloneStaticBlockObject(cx, block, objects, src);
+            if (obj.isStaticBlock()) {
+                Rooted<StaticBlockObject*> innerBlock(cx, &obj.asStaticBlock());
+
+                Rooted<JSObject*> enclosingScope(cx);
+                if (StaticBlockObject *enclosingBlock = innerBlock->enclosingBlock())
+                    enclosingScope = objects[FindBlockIndex(src, *enclosingBlock)];
+                else
+                    enclosingScope = fun;
+
+                clone = CloneStaticBlockObject(cx, enclosingScope, innerBlock);
             } else {
-                RootedFunction fun(cx, vector[i]->toFunction());
-                clone = CloneInterpretedFunction(cx, fun);
+                Rooted<JSFunction*> innerFun(cx, obj.toFunction());
+
+                StaticScopeIter ssi(innerFun->script()->enclosingStaticScope());
+                Rooted<JSObject*> enclosingScope(cx);
+                if (!ssi.done() && ssi.type() == StaticScopeIter::BLOCK)
+                    enclosingScope = objects[FindBlockIndex(src, ssi.block())];
+                else
+                    enclosingScope = fun;
+
+                clone = CloneInterpretedFunction(cx, enclosingScope, innerFun);
             }
             if (!clone || !objects.append(clone))
                 return NULL;
         }
     }
 
     /* RegExps */
 
@@ -1721,21 +1786,20 @@ js::CloneScript(JSContext *cx, HandleScr
             JSObject *clone = CloneScriptRegExpObject(cx, vector[i]->asRegExp());
             if (!clone || !regexps.append(clone))
                 return NULL;
         }
     }
 
     /* Now that all fallible allocation is complete, create the GC thing. */
 
-    JSScript *dst = JSScript::Create(cx, src->savedCallerFun,
+    JSScript *dst = JSScript::Create(cx, enclosingScope, src->savedCallerFun,
                                      cx->compartment->principals, src->originPrincipals,
                                      src->compileAndGo, src->noScriptRval,
-                                     /* globalObject = */ NULL, src->getVersion(),
-                                     src->staticLevel);
+                                     src->getVersion(), src->staticLevel);
     if (!dst) {
         Foreground::free_(data);
         return NULL;
     }
 
     new (&dst->bindings) Bindings;
     dst->bindings.transfer(&bindings);
 
@@ -1944,18 +2008,17 @@ JSScript::changeStepModeCount(JSContext 
     uint32_t count = debug->stepMode & stepCountMask;
     JS_ASSERT(((count + delta) & stepCountMask) == count + delta);
     return tryNewStepMode(cx,
                           (debug->stepMode & stepFlagMask) |
                           ((count + delta) & stepCountMask));
 }
 
 BreakpointSite *
-JSScript::getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc,
-                                    GlobalObject *scriptGlobal)
+JSScript::getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc)
 {
     JS_ASSERT(size_t(pc - code) < length);
 
     if (!ensureHasDebugScript(cx))
         return NULL;
 
     DebugScript *debug = debugScript();
     BreakpointSite *&site = debug->breakpoints[pc - code];
@@ -1964,21 +2027,16 @@ JSScript::getOrCreateBreakpointSite(JSCo
         site = cx->runtime->new_<BreakpointSite>(this, pc);
         if (!site) {
             js_ReportOutOfMemory(cx);
             return NULL;
         }
         debug->numSites++;
     }
 
-    if (site->scriptGlobal)
-        JS_ASSERT_IF(scriptGlobal, site->scriptGlobal == scriptGlobal);
-    else
-        site->scriptGlobal = scriptGlobal;
-
     return site;
 }
 
 void
 JSScript::destroyBreakpointSite(FreeOp *fop, jsbytecode *pc)
 {
     JS_ASSERT(unsigned(pc - code) < length);
 
@@ -2055,27 +2113,24 @@ JSScript::markChildren(JSTracer *trc)
     if (hasConsts()) {
         ConstArray *constarray = consts();
         MarkValueRange(trc, constarray->length, constarray->vector, "consts");
     }
 
     if (function())
         MarkObject(trc, &function_, "function");
 
-    if (!isCachedEval && globalObject)
-        MarkObject(trc, &globalObject, "object");
+    if (enclosingScope_)
+        MarkObject(trc, &enclosingScope_, "enclosing");
 
     if (IS_GC_MARKING_TRACER(trc) && filename)
         MarkScriptFilename(trc->runtime, filename);
 
     bindings.trace(trc);
 
-    if (types)
-        types->trace(trc);
-
 #ifdef JS_METHODJIT
     for (int constructing = 0; constructing <= 1; constructing++) {
         for (int barriers = 0; barriers <= 1; barriers++) {
             mjit::JITScript *jit = getJIT((bool) constructing, (bool) barriers);
             if (jit)
                 jit->trace(trc);
         }
     }
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -98,16 +98,19 @@ class Bindings
      * not be used again.
      */
     inline void transfer(Bindings *bindings);
 
     uint16_t numArgs() const { return nargs; }
     uint16_t numVars() const { return nvars; }
     unsigned count() const { return nargs + nvars; }
 
+    /* Convert a CallObject slot to either a formal or local variable index. */
+    inline BindingKind slotToFrameIndex(unsigned slot, unsigned *index);
+
     /*
      * The VM's StackFrame allocates a Value for each formal and variable.
      * A (formal|var)Index is the index passed to fp->unaliasedFormal/Var to
      * access this variable. These two functions convert between formal/var
      * indices and the corresponding slot in the CallObject.
      */
     inline uint16_t formalIndexToSlot(uint16_t i);
     inline uint16_t varIndexToSlot(uint16_t i);
@@ -123,18 +126,16 @@ class Bindings
      * The result is guaranteed not to have duplicate property names.
      */
     Shape *callObjectShape(JSContext *cx) const;
 
     /* See Scope::extensibleParents */
     inline bool extensibleParents();
     bool setExtensibleParents(JSContext *cx);
 
-    bool setParent(JSContext *cx, JSObject *obj);
-
     enum {
         /* A script may have no more than this many arguments or variables. */
         BINDING_COUNT_LIMIT = 0xFFFF
     };
 
     /*
      * Add a local binding for the given name, of the given type, for the code
      * being compiled.  If fun is non-null, this binding set is being created
@@ -404,38 +405,25 @@ struct JSScript : public js::gc::Cell
                                    comment above Create() for details) */
 
     const char      *filename;  /* source filename or null */
     js::HeapPtrAtom *atoms;     /* maps immediate index to literal struct */
 
     JSPrincipals    *principals;/* principals for this script */
     JSPrincipals    *originPrincipals; /* see jsapi.h 'originPrincipals' comment */
 
-    /*
-     * A global object for the script.
-     * - All scripts returned by JSAPI functions (JS_CompileScript,
-     *   JS_CompileUTF8File, etc.) have a non-null globalObject.
-     * - A function script has a globalObject if the function comes from a
-     *   compile-and-go script.
-     * - Temporary scripts created by obj_eval, JS_EvaluateScript, and
-     *   similar functions never have the globalObject field set; for such
-     *   scripts the global should be extracted from the JS frame that
-     *   execute scripts.
-     */
-    js::HeapPtr<js::GlobalObject, JSScript*> globalObject;
-
     /* Persistent type information retained across GCs. */
     js::types::TypeScript *types;
 
   private:
 #ifdef JS_METHODJIT
     JITScriptSet *jitInfo;
 #endif
-
     js::HeapPtrFunction function_;
+    js::HeapPtrObject   enclosingScope_;
 
     // 32-bit fields.
 
   public:
     uint32_t        length;     /* length of code vector */
 
     uint32_t        lineno;     /* base line number of script */
 
@@ -505,24 +493,19 @@ struct JSScript : public js::gc::Cell
     bool            funHasExtensibleScope:1;       /* see ContextFlags' field of the same name */
     bool            warnedAboutTwoArgumentEval:1; /* have warned about use of
                                                      obsolete eval(s, o) in
                                                      this script */
     bool            warnedAboutUndefinedProp:1; /* have warned about uses of
                                                    undefined properties in this
                                                    script */
     bool            hasSingletons:1;  /* script has singleton objects */
-    bool            isOuterFunction:1; /* function is heavyweight, with inner functions */
-    bool            isInnerFunction:1; /* function is directly nested in a heavyweight
-                                        * outer function */
     bool            isActiveEval:1;   /* script came from eval(), and is still active */
     bool            isCachedEval:1;   /* script came from eval(), and is in eval cache */
     bool            uninlineable:1;   /* script is considered uninlineable by analysis */
-    bool            reentrantOuterFunction:1; /* outer function marked reentrant */
-    bool            typesPurged:1;    /* TypeScript has been purged at some point */
 #ifdef JS_METHODJIT
     bool            debugMode:1;      /* script was compiled in debug mode */
     bool            failedBoundsCheck:1; /* script has had hoisted bounds checks fail */
 #endif
     bool            callDestroyHook:1;/* need to call destroy hook */
     bool            isGenerator:1;    /* is a generator */
     bool            hasScriptCounts:1;/* script has an entry in
                                          JSCompartment::scriptCountsMap */
@@ -537,21 +520,20 @@ struct JSScript : public js::gc::Cell
     bool            needsArgsAnalysis_:1;
     bool            needsArgsObj_:1;
 
     //
     // End of fields.  Start methods.
     //
 
   public:
-    static JSScript *Create(JSContext *cx, bool savedCallerFun,
+    static JSScript *Create(JSContext *cx, js::HandleObject enclosingScope, bool savedCallerFun,
                             JSPrincipals *principals, JSPrincipals *originPrincipals,
                             bool compileAndGo, bool noScriptRval,
-                            js::GlobalObject *globalObject, JSVersion version,
-                            unsigned staticLevel);
+                            JSVersion version, unsigned staticLevel);
 
     // Three ways ways to initialize a JSScript.  Callers of partiallyInit()
     // and fullyInitTrivial() are responsible for notifying the debugger after
     // successfully creating any kind (function or other) of new JSScript.
     // However, callers of fullyInitFromEmitter() do not need to do this.
     static bool partiallyInit(JSContext *cx, JS::Handle<JSScript*> script,
                               uint32_t length, uint32_t nsrcnotes, uint32_t natoms,
                               uint32_t nobjects, uint32_t nregexps, uint32_t ntrynotes, uint32_t nconsts,
@@ -589,62 +571,55 @@ struct JSScript : public js::gc::Cell
      * canonical location for the arguments. Note: if a formal is aliased
      * through the scope chain, then script->argLivesInCallObject and
      * JSOP_*ARG* opcodes won't be emitted at all.
      */
     bool argsObjAliasesFormals() const {
         return needsArgsObj() && !strictModeCode;
     }
 
-    /* Hash table chaining for JSCompartment::evalCache. */
-    JSScript *&evalHashLink() { return *globalObject.unsafeGetUnioned(); }
-
     /*
      * Original compiled function for the script, if it has a function.
      * NULL for global and eval scripts.
      */
     JSFunction *function() const { return function_; }
     void setFunction(JSFunction *fun);
 
+    /* Return whether this script was compiled for 'eval' */
+    bool isForEval() { return isCachedEval || isActiveEval; }
+
 #ifdef DEBUG
     unsigned id();
 #else
     unsigned id() { return 0; }
 #endif
 
     /* Ensure the script has a TypeScript. */
     inline bool ensureHasTypes(JSContext *cx);
 
     /*
-     * Ensure the script has scope and bytecode analysis information.
-     * Performed when the script first runs, or first runs after a TypeScript
-     * GC purge. If scope is NULL then the script must already have types with
-     * scope information.
+     * Ensure the script has bytecode analysis information. Performed when the
+     * script first runs, or first runs after a TypeScript GC purge.
      */
-    inline bool ensureRanAnalysis(JSContext *cx, JSObject *scope);
+    inline bool ensureRanAnalysis(JSContext *cx);
 
     /* Ensure the script has type inference analysis information. */
     inline bool ensureRanInference(JSContext *cx);
 
     inline bool hasAnalysis();
     inline void clearAnalysis();
     inline js::analyze::ScriptAnalysis *analysis();
 
     inline bool hasGlobal() const;
     inline bool hasClearedGlobal() const;
 
-    inline js::GlobalObject * global() const;
-    inline js::types::TypeScriptNesting *nesting() const;
-
-    inline void clearNesting();
+    inline js::GlobalObject &global() const;
 
-    /* Return creation time global or null. */
-    js::GlobalObject *getGlobalObjectOrNull() const {
-        return (isCachedEval || isActiveEval) ? NULL : globalObject.get();
-    }
+    /* See StaticScopeIter comment. */
+    JSObject *enclosingStaticScope() const { return enclosingScope_; }
 
   private:
     bool makeTypes(JSContext *cx);
     bool makeAnalysis(JSContext *cx);
 
 #ifdef JS_METHODJIT
   private:
     // CallCompiler must be a friend because it generates code that directly
@@ -870,18 +845,17 @@ struct JSScript : public js::gc::Cell
     bool hasAnyBreakpointsOrStepMode() { return hasDebugScript; }
 
     js::BreakpointSite *getBreakpointSite(jsbytecode *pc)
     {
         JS_ASSERT(size_t(pc - code) < length);
         return hasDebugScript ? debugScript()->breakpoints[pc - code] : NULL;
     }
 
-    js::BreakpointSite *getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc,
-                                                  js::GlobalObject *scriptGlobal);
+    js::BreakpointSite *getOrCreateBreakpointSite(JSContext *cx, jsbytecode *pc);
 
     void destroyBreakpointSite(js::FreeOp *fop, jsbytecode *pc);
 
     void clearBreakpointsIn(js::FreeOp *fop, js::Debugger *dbg, JSObject *handler);
     void clearTraps(js::FreeOp *fop);
 
     void markTrapClosures(JSTracer *trc);
 
@@ -1030,22 +1004,23 @@ enum LineOption {
     CALLED_FROM_JSOP_EVAL,
     NOT_CALLED_FROM_JSOP_EVAL
 };
 
 inline void
 CurrentScriptFileLineOrigin(JSContext *cx, unsigned *linenop, LineOption = NOT_CALLED_FROM_JSOP_EVAL);
 
 extern JSScript *
-CloneScript(JSContext *cx, HandleScript script);
+CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun, HandleScript script);
 
 /*
  * NB: after a successful XDR_DECODE, XDRScript callers must do any required
  * subsequent set-up of owning function or script object and then call
  * js_CallNewScriptHook.
  */
 template<XDRMode mode>
 bool
-XDRScript(XDRState<mode> *xdr, JSScript **scriptp, JSScript *parentScript);
+XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enclosingScript,
+          HandleFunction fun, JSScript **scriptp);
 
 } /* namespace js */
 
 #endif /* jsscript_h___ */
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -10,29 +10,42 @@
 
 #include "jsautooplen.h"
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsopcode.h"
 #include "jsscript.h"
 #include "jsscope.h"
 
-#include "vm/ScopeObject.h"
 #include "vm/GlobalObject.h"
 #include "vm/RegExpObject.h"
 
 #include "jsscopeinlines.h"
 
 namespace js {
 
 inline
 Bindings::Bindings()
     : lastBinding(NULL), nargs(0), nvars(0), hasDup_(false)
 {}
 
+inline BindingKind
+Bindings::slotToFrameIndex(unsigned slot, unsigned *index)
+{
+    slot -= CallObject::RESERVED_SLOTS;
+    if (slot < numArgs()) {
+        *index = slot;
+        return ARGUMENT;
+    }
+
+    *index = slot - numArgs();
+    JS_ASSERT(*index < numVars());
+    return VARIABLE;
+}
+
 inline void
 Bindings::transfer(Bindings *bindings)
 {
     JS_ASSERT(!lastBinding);
     JS_ASSERT(!bindings->lastBinding || !bindings->lastBinding->inDictionary());
 
     *this = *bindings;
 #ifdef DEBUG
@@ -50,18 +63,18 @@ Bindings::lastShape() const
 
 Shape *
 Bindings::initialShape(JSContext *cx) const
 {
     /* Get an allocation kind to match an empty call object. */
     gc::AllocKind kind = gc::FINALIZE_OBJECT2_BACKGROUND;
     JS_ASSERT(gc::GetGCKindSlots(kind) == CallObject::RESERVED_SLOTS);
 
-    return EmptyShape::getInitialShape(cx, &CallClass, NULL, NULL, kind,
-                                       BaseShape::VAROBJ);
+    return EmptyShape::getInitialShape(cx, &CallClass, NULL, cx->global(),
+                                       kind, BaseShape::VAROBJ);
 }
 
 bool
 Bindings::ensureShape(JSContext *cx)
 {
     if (!lastBinding) {
         lastBinding = initialShape(cx);
         if (!lastBinding)
@@ -186,51 +199,34 @@ JSScript::isEmpty() const
 inline bool
 JSScript::hasGlobal() const
 {
     /*
      * Make sure that we don't try to query information about global objects
      * which have had their scopes cleared. compileAndGo code should not run
      * anymore against such globals.
      */
-    JS_ASSERT(types && types->hasScope());
-    js::GlobalObject *obj = types->global;
-    return obj && !obj->isCleared();
+    return compileAndGo && !global().isCleared();
 }
 
-inline js::GlobalObject *
+inline js::GlobalObject &
 JSScript::global() const
 {
-    JS_ASSERT(hasGlobal());
-    return types->global;
+    /*
+     * A JSScript always marks its compartment's global (via bindings) so we
+     * can assert that maybeGlobal is non-null here.
+     */
+    return *compartment()->maybeGlobal();
 }
 
 inline bool
 JSScript::hasClearedGlobal() const
 {
-    JS_ASSERT(types && types->hasScope());
-    js::GlobalObject *obj = types->global;
-    return obj && obj->isCleared();
-}
-
-inline js::types::TypeScriptNesting *
-JSScript::nesting() const
-{
-    JS_ASSERT(function() && types && types->hasScope());
-    return types->nesting;
-}
-
-inline void
-JSScript::clearNesting()
-{
-    js::types::TypeScriptNesting *nesting = this->nesting();
-    if (nesting) {
-        js::Foreground::delete_(nesting);
-        types->nesting = NULL;
-    }
+    JS_ASSERT(types);
+    return global().isCleared();
 }
 
 #ifdef JS_METHODJIT
 inline bool
 JSScript::ensureHasJITInfo(JSContext *cx)
 {
     if (jitInfo)
         return true;
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -1712,17 +1712,17 @@ static JSObject *
 GetCurrentScopeChain(JSContext *cx)
 {
     if (cx->hasfp())
         return cx->fp()->scopeChain();
     return JS_ObjectToInnerObject(cx, cx->globalObject);
 }
 
 static JSXML *
-ParseXMLSource(JSContext *cx, JSString *src)
+ParseXMLSource(JSContext *cx, HandleString src)
 {
     jsval nsval;
     JSLinearString *uri;
     size_t urilen, srclen, length, offset, dstlen;
     jschar *chars;
     const jschar *srcp, *endp;
     JSXML *xml;
     const char *filename;
@@ -1851,17 +1851,17 @@ OrphanXMLChild(JSContext *cx, JSXML *xml
 }
 
 static JSObject *
 ToXML(JSContext *cx, jsval v)
 {
     JSObject *obj;
     JSXML *xml;
     Class *clasp;
-    JSString *str;
+    RootedString str(cx);
     uint32_t length;
 
     if (JSVAL_IS_PRIMITIVE(v)) {
         if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
             goto bad;
     } else {
         obj = JSVAL_TO_OBJECT(v);
         if (obj->isXML()) {
@@ -1932,17 +1932,17 @@ static JSBool
 Append(JSContext *cx, JSXML *list, JSXML *kid);
 
 static JSObject *
 ToXMLList(JSContext *cx, jsval v)
 {
     JSObject *obj, *listobj;
     JSXML *xml, *list, *kid;
     Class *clasp;
-    JSString *str;
+    RootedString str(cx);
     uint32_t i, length;
 
     if (JSVAL_IS_PRIMITIVE(v)) {
         if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
             goto bad;
     } else {
         obj = JSVAL_TO_OBJECT(v);
         if (obj->isXML()) {
--- a/js/src/methodjit/BaseAssembler.h
+++ b/js/src/methodjit/BaseAssembler.h
@@ -1370,17 +1370,17 @@ static const JSC::MacroAssembler::Regist
             bumpCount(count, scratch);
         }
     }
 
     static const double oneDouble;
 };
 
 /* Return f<true> if the script is strict mode code, f<false> otherwise. */
-#define STRICT_VARIANT(f)                                                     \
+#define STRICT_VARIANT(script, f)                                             \
     (FunctionTemplateConditional(script->strictModeCode,                      \
                                  f<true>, f<false>))
 
 /* Save some typing. */
 static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = Assembler::JSReturnReg_Type;
 static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = Assembler::JSReturnReg_Data;
 static const JSC::MacroAssembler::RegisterID JSParamReg_Argc  = Assembler::JSParamReg_Argc;
 
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -54,17 +54,17 @@ static const size_t USES_BEFORE_INLINING
 mjit::Compiler::Compiler(JSContext *cx, JSScript *outerScript,
                          unsigned chunkIndex, bool isConstructing)
   : BaseCompiler(cx),
     outerScript(cx, outerScript),
     chunkIndex(chunkIndex),
     isConstructing(isConstructing),
     outerChunk(outerJIT()->chunkDescriptor(chunkIndex)),
     ssa(cx, outerScript),
-    globalObj(cx, outerScript->hasGlobal() ? outerScript->global() : NULL),
+    globalObj(cx, outerScript->hasGlobal() ? &outerScript->global() : NULL),
     globalSlots(globalObj ? globalObj->getRawSlots() : NULL),
     frame(cx, *thisFromCtor(), masm, stubcc),
     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())),
@@ -130,17 +130,17 @@ mjit::Compiler::compile()
 CompileStatus
 mjit::Compiler::checkAnalysis(HandleScript script)
 {
     if (script->hasClearedGlobal()) {
         JaegerSpew(JSpew_Abort, "script has a cleared global\n");
         return Compile_Abort;
     }
 
-    if (!script->ensureRanAnalysis(cx, NULL))
+    if (!script->ensureRanAnalysis(cx))
         return Compile_Error;
 
     if (!script->analysis()->jaegerCompileable()) {
         JaegerSpew(JSpew_Abort, "script has uncompileable opcodes\n");
         return Compile_Abort;
     }
 
     if (cx->typeInferenceEnabled() && !script->ensureRanInference(cx))
@@ -185,17 +185,17 @@ mjit::Compiler::scanInlineCalls(uint32_t
     if (isConstructing)
         return Compile_Okay;
 
     JSScript *script = ssa.getFrame(index).script;
     ScriptAnalysis *analysis = script->analysis();
 
     /* Don't inline from functions which could have a non-global scope object. */
     if (!script->hasGlobal() ||
-        script->global() != globalObj ||
+        &script->global() != globalObj ||
         (script->function() && script->function()->getParent() != globalObj) ||
         (script->function() && script->function()->isHeavyweight()) ||
         script->isActiveEval) {
         return Compile_Okay;
     }
 
     uint32_t nextOffset = 0;
     uint32_t lastOffset = script->length;
@@ -311,17 +311,17 @@ mjit::Compiler::scanInlineCalls(uint32_t
                 break;
 
             /* Watch for excessively deep nesting of inlined frames. */
             if (nextDepth + script->nslots >= stackLimit) {
                 okay = false;
                 break;
             }
 
-            if (!script->types || !script->types->hasScope()) {
+            if (!script->types) {
                 okay = false;
                 break;
             }
 
             CompileStatus status = checkAnalysis(script);
             if (status != Compile_Okay)
                 return status;
 
@@ -628,17 +628,17 @@ mjit::SetChunkLimit(uint32_t limit)
 {
     if (limit)
         CHUNK_LIMIT = limit;
 }
 
 JITScript *
 MakeJITScript(JSContext *cx, JSScript *script)
 {
-    if (!script->ensureRanAnalysis(cx, NULL))
+    if (!script->ensureRanAnalysis(cx))
         return NULL;
 
     ScriptAnalysis *analysis = script->analysis();
 
     Vector<ChunkDescriptor> chunks(cx);
     Vector<CrossChunkEdge> edges(cx);
 
     if (script->length < CHUNK_LIMIT || !cx->typeInferenceEnabled()) {
@@ -1092,19 +1092,17 @@ 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() || script->nesting()))
-        {
+        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.loadPtr(Address(t0, JSFunction::offsetOfEnvironment()), t0);
             masm.storePtr(t0, Address(JSFrameReg, StackFrame::offsetOfScopeChain()));
             hasScope.linkTo(masm.label(), &masm);
         }
@@ -1140,52 +1138,20 @@ mjit::Compiler::generatePrologue()
     /* Inline StackFrame::prologue. */
     if (script->isActiveEval && script->strictModeCode) {
         prepareStubCall(Uses(0));
         INLINE_STUBCALL(stubs::StrictEvalPrologue, REJOIN_EVAL_PROLOGUE);
     } else if (script->function()) {
         if (script->function()->isHeavyweight()) {
             prepareStubCall(Uses(0));
             INLINE_STUBCALL(stubs::HeavyweightFunctionPrologue, REJOIN_FUNCTION_PROLOGUE);
-        } else if (types::TypeScriptNesting *nesting = script->nesting()) {
-            /*
-             * Inline the common case for the nesting prologue: the
-             * function is a non-heavyweight inner function with no
-             * children of its own. We ensure during inference that the
-             * outer function does not add scope objects for 'let' or
-             * 'with', so that the frame's scope chain will be
-             * the parent's call object, and if it differs from the
-             * parent's current activation then the parent is reentrant.
-             */
-            JSScript *parent = nesting->parent;
-            JS_ASSERT(parent);
-            JS_ASSERT_IF(parent->hasAnalysis() && parent->analysis()->ranBytecode(),
-                         !parent->analysis()->addsScopeObjects());
-
-            RegisterID t0 = Registers::ReturnReg;
-            masm.move(ImmPtr(&parent->nesting()->activeCall), t0);
-            masm.loadPtr(Address(t0), t0);
-
-            Address scopeChain(JSFrameReg, StackFrame::offsetOfScopeChain());
-            Jump mismatch = masm.branchPtr(Assembler::NotEqual, t0, scopeChain);
-            masm.add32(Imm32(1), AbsoluteAddress(&nesting->activeFrames));
-
-            masm.load32(FrameFlagsAddress(), t0);
-            masm.or32(Imm32(StackFrame::HAS_NESTING), t0);
-            masm.store32(t0, FrameFlagsAddress());
-
-            stubcc.linkExitDirect(mismatch, stubcc.masm.label());
-            OOL_STUBCALL(stubs::TypeNestingPrologue, REJOIN_FUNCTION_PROLOGUE);
-            stubcc.crossJump(stubcc.masm.jump(), masm.label());
         }
 
-        if (isConstructing) {
-            if (!constructThis())
-                return Compile_Error;
-        }
+        if (isConstructing && !constructThis())
+            return Compile_Error;
     }
 
     CompileStatus status = methodEntryHelper();
     if (status == Compile_Okay)
         recompileCheckHelper();
 
     return status;
 }
@@ -2552,26 +2518,26 @@ mjit::Compiler::generateMethod()
 
           BEGIN_CASE(JSOP_DELPROP)
           {
             uint32_t index = GET_UINT32_INDEX(PC);
             PropertyName *name = script->getName(index);
 
             prepareStubCall(Uses(1));
             masm.move(ImmPtr(name), Registers::ArgReg1);
-            INLINE_STUBCALL(STRICT_VARIANT(stubs::DelProp), REJOIN_FALLTHROUGH);
+            INLINE_STUBCALL(STRICT_VARIANT(script, stubs::DelProp), REJOIN_FALLTHROUGH);
             frame.pop();
             pushSyncedEntry(0);
           }
           END_CASE(JSOP_DELPROP)
 
           BEGIN_CASE(JSOP_DELELEM)
           {
             prepareStubCall(Uses(2));
-            INLINE_STUBCALL(STRICT_VARIANT(stubs::DelElem), REJOIN_FALLTHROUGH);
+            INLINE_STUBCALL(STRICT_VARIANT(script, stubs::DelElem), REJOIN_FALLTHROUGH);
             frame.popn(2);
             pushSyncedEntry(0);
           }
           END_CASE(JSOP_DELELEM)
 
           BEGIN_CASE(JSOP_TYPEOF)
           BEGIN_CASE(JSOP_TYPEOFEXPR)
             jsop_typeof();
@@ -2855,49 +2821,48 @@ mjit::Compiler::generateMethod()
                 PC += JSOP_SETARG_LENGTH + JSOP_POP_LENGTH;
                 break;
             }
           }
           END_CASE(JSOP_SETARG)
 
           BEGIN_CASE(JSOP_GETLOCAL)
           BEGIN_CASE(JSOP_CALLLOCAL)
-          BEGIN_CASE(JSOP_GETALIASEDVAR)
-          BEGIN_CASE(JSOP_CALLALIASEDVAR)
           {
             /*
              * Update the var type unless we are about to pop the variable.
              * Sync is not guaranteed for types of dead locals, and GETLOCAL
              * followed by POP is not regarded as a use of the variable.
              */
             jsbytecode *next = &PC[JSOP_GETLOCAL_LENGTH];
             if (JSOp(*next) != JSOP_POP || analysis->jumpTarget(next))
                 restoreVarType();
             if (JSObject *singleton = pushedSingleton(0))
                 frame.push(ObjectValue(*singleton));
-            else if (JOF_OPTYPE(*PC) == JOF_SCOPECOORD)
-                jsop_aliasedVar(ScopeCoordinate(PC), /* get = */ true);
             else
                 frame.pushLocal(GET_SLOTNO(PC));
-
-            PC += GetBytecodeLength(PC);
-            break;
           }
           END_CASE(JSOP_GETLOCAL)
 
+          BEGIN_CASE(JSOP_GETALIASEDVAR)
+          BEGIN_CASE(JSOP_CALLALIASEDVAR)
+            jsop_aliasedVar(ScopeCoordinate(PC), /* get = */ true);
+          END_CASE(JSOP_GETALIASEDVAR);
+
           BEGIN_CASE(JSOP_SETLOCAL)
           BEGIN_CASE(JSOP_SETALIASEDVAR)
           {
             jsbytecode *next = &PC[GetBytecodeLength(PC)];
             bool pop = JSOp(*next) == JSOP_POP && !analysis->jumpTarget(next);
-            if (JOF_OPTYPE(*PC) == JOF_SCOPECOORD)
+            if (JOF_OPTYPE(*PC) == JOF_SCOPECOORD) {
                 jsop_aliasedVar(ScopeCoordinate(PC), /* get = */ false, pop);
-            else
+            } else {
                 frame.storeLocal(GET_SLOTNO(PC), pop);
-            updateVarType();
+                updateVarType();
+            }
 
             if (pop) {
                 frame.pop();
                 PC = next + JSOP_POP_LENGTH;
                 break;
             }
 
             PC = next;
@@ -3062,17 +3027,17 @@ mjit::Compiler::generateMethod()
           END_CASE(JSOP_LABEL)
 
           BEGIN_CASE(JSOP_DEFFUN)
           {
             JSFunction *innerFun = script->getFunction(GET_UINT32_INDEX(PC));
 
             prepareStubCall(Uses(0));
             masm.move(ImmPtr(innerFun), Registers::ArgReg1);
-            INLINE_STUBCALL(STRICT_VARIANT(stubs::DefFun), REJOIN_FALLTHROUGH);
+            INLINE_STUBCALL(STRICT_VARIANT(script, stubs::DefFun), REJOIN_FALLTHROUGH);
           }
           END_CASE(JSOP_DEFFUN)
 
           BEGIN_CASE(JSOP_DEFVAR)
           BEGIN_CASE(JSOP_DEFCONST)
           {
             PropertyName *name = script->getName(GET_UINT32_INDEX(PC));
 
@@ -3803,20 +3768,16 @@ mjit::Compiler::emitReturn(FrameEntry *f
     }
 
     /* Inline StackFrame::epilogue. */
     if (debugMode()) {
         prepareStubCall(Uses(0));
         INLINE_STUBCALL(stubs::Epilogue, REJOIN_NONE);
     } else {
         profilingPopHelper();
-
-        if (script->function() && script->nesting()) {
-            masm.sub32(Imm32(1), AbsoluteAddress(&script->nesting()->activeFrames));
-        }
     }
 
     emitReturnValue(&masm, fe);
     emitFinalReturn(masm);
 
     /*
      * After we've placed the call object, all tracked state can be
      * thrown away. This will happen anyway because the next live opcode (if
@@ -4234,28 +4195,18 @@ mjit::Compiler::inlineCallHelper(uint32_
 
     callIC.typeMonitored = monitored(PC) || hasTypeBarriers(PC);
 
     /* Test the type if necessary. Failing this always takes a really slow path. */
     MaybeJump notObjectJump;
     if (icCalleeType.isSet())
         notObjectJump = masm.testObject(Assembler::NotEqual, icCalleeType.reg());
 
-    /*
-     * For an optimized apply, keep icCalleeData in a callee-saved register for
-     * the subsequent ic::SplatApplyArgs call.
-     */
     Registers tempRegs(Registers::AvailRegs);
-    if (callIC.frameSize.isDynamic() && !Registers::isSaved(icCalleeData)) {
-        RegisterID x = tempRegs.takeAnyReg(Registers::SavedRegs).reg();
-        masm.move(icCalleeData, x);
-        icCalleeData = x;
-    } else {
-        tempRegs.takeReg(icCalleeData);
-    }
+    tempRegs.takeReg(icCalleeData);
 
     /* Reserve space just before initialization of funGuard. */
     RESERVE_IC_SPACE(masm);
 
     /*
      * Guard on the callee identity. This misses on the first run. If the
      * callee is scripted, compiled/compilable, and argc == nargs, then this
      * guard is patched, and the compiled code address is baked in.
@@ -4286,19 +4237,26 @@ mjit::Compiler::inlineCallHelper(uint32_
         Jump isNative = stubcc.masm.branch32(Assembler::Below, tmp, Imm32(JSFUN_INTERPRETED));
         tempRegs.putReg(tmp);
 
         /*
          * N.B. After this call, the frame will have a dynamic frame size.
          * Check after the function is known not to be a native so that the
          * catch-all/native path has a static depth.
          */
-        if (callIC.frameSize.isDynamic())
+        if (callIC.frameSize.isDynamic()) {
             OOL_STUBCALL(ic::SplatApplyArgs, REJOIN_CALL_SPLAT);
 
+            /*
+             * Restore identity of callee after SplatApplyArgs, which may
+             * have been clobbered (not callee save reg or changed by moving GC).
+             */
+            stubcc.masm.loadPayload(frame.addressOf(origThis), icCalleeData);
+        }
+
         /*
          * No-op jump that gets patched by ic::New/Call to the stub generated
          * by generateFullCallStub.
          */
         Jump toPatch = stubcc.masm.jump();
         toPatch.linkTo(stubcc.masm.label(), &stubcc.masm);
         callIC.oolJump = toPatch;
         callIC.icCall = stubcc.masm.label();
@@ -4731,17 +4689,17 @@ mjit::Compiler::emitStubCmpOp(BoolStub s
     return jumpAndRun(j, target);
 }
 
 void
 mjit::Compiler::jsop_setprop_slow(PropertyName *name)
 {
     prepareStubCall(Uses(2));
     masm.move(ImmPtr(name), Registers::ArgReg1);
-    INLINE_STUBCALL(STRICT_VARIANT(stubs::SetName), REJOIN_FALLTHROUGH);
+    INLINE_STUBCALL(STRICT_VARIANT(script, stubs::SetName), REJOIN_FALLTHROUGH);
     JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
     frame.shimmy(1);
     if (script->hasScriptCounts)
         bumpPropCount(PC, PCCounts::PROP_OTHER);
 }
 
 void
 mjit::Compiler::jsop_getprop_slow(PropertyName *name, bool forPrototype)
@@ -5393,52 +5351,16 @@ mjit::Compiler::jsop_setprop(PropertyNam
 
     /* If the incoming type will never PIC, take slow path. */
     if (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_OBJECT) {
         jsop_setprop_slow(name);
         return true;
     }
 
     /*
-     * If this is a SETNAME to a variable of a non-reentrant outer function,
-     * set the variable's slot directly for the active call object.
-     */
-    if (cx->typeInferenceEnabled() && js_CodeSpec[*PC].format & JOF_NAME) {
-        ScriptAnalysis::NameAccess access =
-            analysis->resolveNameAccess(cx, NameToId(name), true);
-        if (access.nesting) {
-            /* Use a SavedReg so it isn't clobbered by the stub call. */
-            RegisterID nameReg = frame.allocReg(Registers::SavedRegs).reg();
-            Address address = frame.loadNameAddress(access, nameReg);
-
-#ifdef JSGC_INCREMENTAL_MJ
-            /* Write barrier. */
-            if (cx->compartment->needsBarrier()) {
-                stubcc.linkExit(masm.jump(), Uses(0));
-                stubcc.leave();
-
-                /* sync() may have overwritten nameReg, so we reload its data. */
-                JS_ASSERT(address.base == nameReg);
-                stubcc.masm.move(ImmPtr(access.basePointer()), nameReg);
-                stubcc.masm.loadPtr(Address(nameReg), nameReg);
-                stubcc.masm.addPtr(Imm32(address.offset), nameReg, Registers::ArgReg1);
-
-                OOL_STUBCALL(stubs::WriteBarrier, REJOIN_NONE);
-                stubcc.rejoin(Changes(0));
-            }
-#endif
-
-            frame.storeTo(rhs, address, popGuaranteed);
-            frame.shimmy(1);
-            frame.freeReg(address.base);
-            return true;
-        }
-    }
-
-    /*
      * Set the property directly if we are accessing a known object which
      * always has the property in a particular inline slot.
      */
     jsid id = NameToId(name);
     types::TypeSet *types = frame.extra(lhs).types;
     if (JSOp(*PC) == JSOP_SETPROP && id == types::MakeTypeId(cx, id) &&
         types && !types->unknownObject() &&
         types->getObjectCount() == 1 &&
@@ -5469,17 +5391,17 @@ mjit::Compiler::jsop_setprop(PropertyNam
                 OOL_STUBCALL(stubs::GCThingWriteBarrier, REJOIN_NONE);
                 stubcc.rejoin(Changes(0));
             }
 #endif
             if (!isObject) {
                 stubcc.linkExit(notObject.get(), Uses(2));
                 stubcc.leave();
                 stubcc.masm.move(ImmPtr(name), Registers::ArgReg1);
-                OOL_STUBCALL(STRICT_VARIANT(stubs::SetName), REJOIN_FALLTHROUGH);
+                OOL_STUBCALL(STRICT_VARIANT(script, stubs::SetName), REJOIN_FALLTHROUGH);
             }
             frame.storeTo(rhs, Address(reg, JSObject::getFixedSlotOffset(slot)), popGuaranteed);
             frame.unpinReg(reg);
             frame.shimmy(1);
             if (!isObject)
                 stubcc.rejoin(Changes(1));
             if (script->hasScriptCounts)
                 bumpPropCount(PC, PCCounts::PROP_DEFINITE);
@@ -5533,17 +5455,17 @@ mjit::Compiler::jsop_setprop(PropertyNam
         /* Start the hot path where it's easy to patch it. */
         pic.fastPathStart = masm.label();
         Jump j = masm.testObject(Assembler::NotEqual, reg);
 
         pic.typeCheck = stubcc.linkExit(j, Uses(2));
         stubcc.leave();
 
         stubcc.masm.move(ImmPtr(name), Registers::ArgReg1);
-        OOL_STUBCALL(STRICT_VARIANT(stubs::SetName), REJOIN_FALLTHROUGH);
+        OOL_STUBCALL(STRICT_VARIANT(script, stubs::SetName), REJOIN_FALLTHROUGH);
 
         typeCheck = stubcc.masm.jump();
         pic.hasTypeCheck = true;
     } else {
         pic.fastPathStart = masm.label();
         pic.hasTypeCheck = false;
         pic.typeReg = Registers::ReturnReg;
     }
@@ -5614,34 +5536,16 @@ mjit::Compiler::jsop_setprop(PropertyNam
 
     pics.append(pic);
     return true;
 }
 
 void
 mjit::Compiler::jsop_name(PropertyName *name, JSValueType type)
 {
-    /*
-     * If this is a NAME for a variable of a non-reentrant outer function, get
-     * the variable's slot directly for the active call object. We always need
-     * to check for undefined, however.
-     */
-    if (cx->typeInferenceEnabled()) {
-        ScriptAnalysis::NameAccess access =
-            analysis->resolveNameAccess(cx, NameToId(name), true);
-        if (access.nesting) {
-            Address address = frame.loadNameAddress(access);
-            JSValueType type = knownPushedType(0);
-            BarrierState barrier = pushAddressMaybeBarrier(address, type, true,
-                                                           /* testUndefined = */ true);
-            finishBarrier(barrier, REJOIN_GETTER, 0);
-            return;
-        }
-    }
-
     PICGenInfo pic(ic::PICInfo::NAME, JSOp(*PC));
 
     RESERVE_IC_SPACE(masm);
 
     pic.shapeReg = frame.allocReg();
     pic.objReg = frame.allocReg();
     pic.typeReg = Registers::ReturnReg;
     pic.name = name;
@@ -5688,34 +5592,16 @@ mjit::Compiler::jsop_name(PropertyName *
     pics.append(pic);
 
     finishBarrier(barrier, REJOIN_GETTER, 0);
 }
 
 bool
 mjit::Compiler::jsop_xname(PropertyName *name)
 {
-    /*
-     * If this is a GETXPROP for a variable of a non-reentrant outer function,
-     * treat in the same way as a NAME.
-     */
-    if (cx->typeInferenceEnabled()) {
-        ScriptAnalysis::NameAccess access =
-            analysis->resolveNameAccess(cx, NameToId(name), true);
-        if (access.nesting) {
-            frame.pop();
-            Address address = frame.loadNameAddress(access);
-            JSValueType type = knownPushedType(0);
-            BarrierState barrier = pushAddressMaybeBarrier(address, type, true,
-                                                           /* testUndefined = */ true);
-            finishBarrier(barrier, REJOIN_GETTER, 0);
-            return true;
-        }
-    }
-
     PICGenInfo pic(ic::PICInfo::XNAME, JSOp(*PC));
 
     FrameEntry *fe = frame.peek(-1);
     if (fe->isNotType(JSVAL_TYPE_OBJECT)) {
         return jsop_getprop(name, knownPushedType(0));
     }
 
     if (!fe->isTypeKnown()) {
@@ -5768,33 +5654,16 @@ mjit::Compiler::jsop_xname(PropertyName 
 
     finishBarrier(barrier, REJOIN_FALLTHROUGH, 0);
     return true;
 }
 
 void
 mjit::Compiler::jsop_bindname(PropertyName *name)
 {
-    /*
-     * If this is a BINDNAME for a variable of a non-reentrant outer function,
-     * the object is definitely the outer function's active call object.
-     */
-    if (cx->typeInferenceEnabled()) {
-        ScriptAnalysis::NameAccess access =
-            analysis->resolveNameAccess(cx, NameToId(name), true);
-        if (access.nesting) {
-            RegisterID reg = frame.allocReg();
-            CallObject **pobj = &access.nesting->activeCall;
-            masm.move(ImmPtr(pobj), reg);
-            masm.loadPtr(Address(reg), reg);
-            frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg);
-            return;
-        }
-    }
-
     PICGenInfo pic(ic::PICInfo::BIND, JSOp(*PC));
 
     // This code does not check the frame flags to see if scopeChain has been
     // set. Rather, it relies on the up-front analysis statically determining
     // whether BINDNAME can be used, which reifies the scope chain at the
     // prologue.
     JS_ASSERT(analysis->usesScopeChain());
 
@@ -5925,57 +5794,35 @@ mjit::Compiler::jsop_aliasedArg(unsigned
 void
 mjit::Compiler::jsop_aliasedVar(ScopeCoordinate sc, bool get, bool poppedAfter)
 {
     RegisterID reg = frame.allocReg(Registers::SavedRegs).reg();
     masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), reg);
     for (unsigned i = 0; i < sc.hops; i++)
         masm.loadPayload(Address(reg, ScopeObject::offsetOfEnclosingScope()), reg);
 
-    /*
-     * TODO bug 753158: Call and Block objects should use the same layout
-     * strategy: up to the maximum numFixedSlots and overflow (if any) in
-     * dynamic slots. For now, we special case for different layouts:
-     */
+    Shape *shape = ScopeCoordinateToStaticScope(script, PC).scopeShape();
     Address addr;
-    StaticBlockObject *block = ScopeCoordinateBlockChain(script, PC);
-    if (block) {
-        /*
-         * Block objects use a fixed AllocKind which means an invariant number
-         * of fixed slots. Any slot below the fixed slot count is inline, any
-         * slot over is in the dynamic slots.
-         */
-        uint32_t nfixed = gc::GetGCKindSlots(BlockObject::FINALIZE_KIND);
-        if (nfixed <= sc.slot) {
-            masm.loadPtr(Address(reg, JSObject::offsetOfSlots()), reg);
-            addr = Address(reg, (sc.slot - nfixed) * sizeof(Value));
-        } else {
-            addr = Address(reg, JSObject::getFixedSlotOffset(sc.slot));
-        }
+    if (shape->numFixedSlots() <= sc.slot) {
+        masm.loadPtr(Address(reg, JSObject::offsetOfSlots()), reg);
+        addr = Address(reg, (sc.slot - shape->numFixedSlots()) * sizeof(Value));
     } else {
-        /*
-         * Using special-case hackery in Shape::getChildBinding, CallObject