Bug 811315 - Use process-per-mozbrowser by default and make scrolling behavior selectable by content. r=jlebar, sr=roc, a=blocking-basecamp
authorChris Jones <jones.chris.g@gmail.com>
Tue, 27 Nov 2012 12:43:52 -0800
changeset 121782 1fb4d36d2ca466a5b69b643c585c8cc7efcbb2d0
parent 121781 f7d72ef613c081931878a6c9a03b41a6ae36e0bd
child 121783 306c951213c65132f5b93f6943ee3d153d5d2ad5
push id1997
push userakeybl@mozilla.com
push dateMon, 07 Jan 2013 21:25:26 +0000
treeherdermozilla-beta@4baf45cdcf21 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjlebar, roc, blocking-basecamp
bugs811315
milestone19.0a2
Bug 811315 - Use process-per-mozbrowser by default and make scrolling behavior selectable by content. r=jlebar, sr=roc, a=blocking-basecamp This is a rollup of the following patches Bug 811315, part 0.9: Fix pre-existing race condition that allows dying ContentParents to accidentally try to load new TabParents. r=jlebar Bug 811315, part 0.91: Fix another pre-existing race condition where FirstIdle might run after ContentChild::ActorDestroy(). r=jlebar Bug 811315, part 1: Make "browser" process limit infinite and unload the processes when all tabs are closed. Firefox with process-per-tab! r=jlebar Bug 811315, part 2: Pass scrolling-behavior hint down into TabParent instead of inferring it from app/browser. r=jlebar Bug 811315, part 3: Add a mozasyncpanzoom attribute for iframes and honor it when constructing a remote frame. r=jlebar sr=roc
b2g/app/b2g.js
content/base/src/nsFrameLoader.cpp
content/base/src/nsGkAtomList.h
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabContext.cpp
dom/ipc/TabContext.h
dom/ipc/TabParent.cpp
dom/ipc/TabParent.h
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -370,18 +370,21 @@ pref("browser.link.open_newwindow", 3);
 // 1: don't divert window.open at all
 // 2: don't divert window.open with features
 pref("browser.link.open_newwindow.restriction", 0);
 
 // Enable browser frames (including OOP, except on Windows, where it doesn't
 // work), but make in-process browser frames the default.
 pref("dom.mozBrowserFramesEnabled", true);
 
+// Enable a (virtually) unlimited number of mozbrowser processes.
+// We'll run out of PIDs on UNIX-y systems before we hit this limit.
+pref("dom.ipc.processCount", 100000);
+
 pref("dom.ipc.tabs.disabled", false);
-
 pref("dom.ipc.browser_frames.oop_by_default", false);
 
 // Temporary permission hack for WebSMS
 pref("dom.sms.enabled", true);
 pref("dom.sms.strict7BitEncoding", false); // Disabled by default.
 
 // Temporary permission hack for WebContacts
 pref("dom.mozContacts.enabled", true);
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -2031,21 +2031,28 @@ nsFrameLoader::TryRemoteBrowser()
   }
   if (NS_FAILED(window->GetChromeFlags(&chromeFlags))) {
     return false;
   }
 
   MutableTabContext context;
   nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
   nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
+  ScrollingBehavior scrollingBehavior = DEFAULT_SCROLLING;
+  if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
+                                 nsGkAtoms::mozasyncpanzoom,
+                                 nsGkAtoms::_true,
+                                 eCaseMatters)) {
+    scrollingBehavior = ASYNC_PAN_ZOOM;
+  }
   if (ownApp) {
-    context.SetTabContextForAppFrame(ownApp, containingApp);
+    context.SetTabContextForAppFrame(ownApp, containingApp, scrollingBehavior);
   } else if (OwnerIsBrowserFrame()) {
     // The |else| above is unnecessary; OwnerIsBrowserFrame() implies !ownApp.
-    context.SetTabContextForBrowserFrame(containingApp);
+    context.SetTabContextForBrowserFrame(containingApp, scrollingBehavior);
   }
 
   mRemoteBrowser = ContentParent::CreateBrowserOrApp(context);
   if (mRemoteBrowser) {
     nsCOMPtr<nsIDOMElement> element = do_QueryInterface(mOwnerContent);
     mRemoteBrowser->SetOwnerElement(element);
 
     nsCOMPtr<nsIDocShellTreeItem> rootItem;
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -563,16 +563,17 @@ GK_ATOM(mode, "mode")
 GK_ATOM(modifiers, "modifiers")
 GK_ATOM(monochrome, "monochrome")
 GK_ATOM(mousedown, "mousedown")
 GK_ATOM(mousemove, "mousemove")
 GK_ATOM(mouseout, "mouseout")
 GK_ATOM(mouseover, "mouseover")
 GK_ATOM(mousethrough, "mousethrough")
 GK_ATOM(mouseup, "mouseup")
+GK_ATOM(mozasyncpanzoom, "mozasyncpanzoom")
 GK_ATOM(mozfullscreenchange, "mozfullscreenchange")
 GK_ATOM(mozfullscreenerror, "mozfullscreenerror")
 GK_ATOM(mozpasspointerevents, "mozpasspointerevents")
 GK_ATOM(mozpointerlockchange, "mozpointerlockchange")
 GK_ATOM(mozpointerlockerror, "mozpointerlockerror")
 GK_ATOM(moz_opaque, "moz-opaque")
 GK_ATOM(moz_action_hint, "mozactionhint")
 GK_ATOM(x_moz_errormessage, "x-moz-errormessage")
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -476,28 +476,34 @@ ContentChild::AllocPCompositor(mozilla::
 
 PImageBridgeChild*
 ContentChild::AllocPImageBridge(mozilla::ipc::Transport* aTransport,
                                 base::ProcessId aOtherProcess)
 {
     return ImageBridgeChild::StartUpInChildProcess(aTransport, aOtherProcess);
 }
 
+static CancelableTask* sFirstIdleTask;
+
 static void FirstIdle(void)
 {
+    MOZ_ASSERT(sFirstIdleTask);
+    sFirstIdleTask = nullptr;
     ContentChild::GetSingleton()->SendFirstIdle();
 }
 
 PBrowserChild*
 ContentChild::AllocPBrowser(const IPCTabContext& aContext,
                             const uint32_t& aChromeFlags)
 {
     static bool firstIdleTaskPosted = false;
     if (!firstIdleTaskPosted) {
-        MessageLoop::current()->PostIdleTask(FROM_HERE, NewRunnableFunction(FirstIdle));
+        MOZ_ASSERT(!sFirstIdleTask);
+        sFirstIdleTask = NewRunnableFunction(FirstIdle);
+        MessageLoop::current()->PostIdleTask(FROM_HERE, sFirstIdleTask);
         firstIdleTaskPosted = true;
     }
 
     // We'll happily accept any kind of IPCTabContext here; we don't need to
     // check that it's of a certain type for security purposes, because we
     // believe whatever the parent process tells us.
 
     nsRefPtr<TabChild> child = TabChild::Create(TabContext(aContext), aChromeFlags);
@@ -818,16 +824,20 @@ ContentChild::ActorDestroy(ActorDestroyR
 
 #ifndef DEBUG
     // In release builds, there's no point in the content process
     // going through the full XPCOM shutdown path, because it doesn't
     // keep persistent state.
     QuickExit();
 #endif
 
+    if (sFirstIdleTask) {
+        sFirstIdleTask->Cancel();
+    }
+
     mAlertObservers.Clear();
 
     nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
     if (svc) {
         svc->UnregisterListener(mConsoleListener);
         mConsoleListener->mChild = nullptr;
     }
 
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -449,26 +449,27 @@ ContentParent::SetManifestFromPreallocat
     // Clients should think of mAppManifestURL as const ... we're
     // bending the rules here just for the preallocation hack.
     const_cast<nsString&>(mAppManifestURL) = aAppManifestURL;
 }
 
 void
 ContentParent::ShutDownProcess()
 {
-  if (mIsAlive) {
+  if (!mIsDestroyed) {
     const InfallibleTArray<PIndexedDBParent*>& idbParents =
       ManagedPIndexedDBParent();
     for (uint32_t i = 0; i < idbParents.Length(); ++i) {
       static_cast<IndexedDBParent*>(idbParents[i])->Disconnect();
     }
 
     // Close() can only be called once.  It kicks off the
     // destruction sequence.
     Close();
+    mIsDestroyed = true;
   }
   // NB: must MarkAsDead() here so that this isn't accidentally
   // returned from Get*() while in the midst of shutdown.
   MarkAsDead();
 }
 
 void
 ContentParent::MarkAsDead()
@@ -666,17 +667,20 @@ ContentParent::ActorDestroy(ActorDestroy
 }
 
 void
 ContentParent::NotifyTabDestroyed(PBrowserParent* aTab)
 {
     // There can be more than one PBrowser for a given app process
     // because of popup windows.  When the last one closes, shut
     // us down.
-    if (IsForApp() && ManagedPBrowserParent().Length() == 1) {
+    if (ManagedPBrowserParent().Length() == 1) {
+        // Prevent this content process from being recycled, since
+        // it's dying.
+        MarkAsDead();
         MessageLoop::current()->PostTask(
             FROM_HERE,
             NewRunnableMethod(this, &ContentParent::ShutDownProcess));
     }
 }
 
 TestShellParent*
 ContentParent::CreateTestShell()
@@ -704,16 +708,17 @@ ContentParent::ContentParent(const nsASt
     : mSubprocess(nullptr)
     , mOSPrivileges(aOSPrivileges)
     , mChildID(-1)
     , mGeolocationWatchID(-1)
     , mRunToCompletionDepth(0)
     , mShouldCallUnblockChild(false)
     , mAppManifestURL(aAppManifestURL)
     , mIsAlive(true)
+    , mIsDestroyed(false)
     , mSendPermissionUpdates(false)
     , mIsForBrowser(aIsForBrowser)
 {
     // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
     // PID along with the warning.
     nsDebugImpl::SetMultiprocessMode("Parent");
 
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -1016,17 +1021,17 @@ NS_IMPL_THREADSAFE_ISUPPORTS3(ContentPar
                               nsIDOMGeoPositionCallback)
 
 NS_IMETHODIMP
 ContentParent::Observe(nsISupports* aSubject,
                        const char* aTopic,
                        const PRUnichar* aData)
 {
     if (!strcmp(aTopic, "xpcom-shutdown") && mSubprocess) {
-        Close();
+        ShutDownProcess();
         NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess");
     }
 
     if (!mIsAlive || !mSubprocess)
         return NS_OK;
 
     // listening for memory pressure event
     if (!strcmp(aTopic, "memory-pressure") &&
@@ -1148,26 +1153,28 @@ ContentParent::RecvGetXPCOMProcessAttrib
 }
 
 PBrowserParent*
 ContentParent::AllocPBrowser(const IPCTabContext& aContext,
                              const uint32_t &aChromeFlags)
 {
     unused << aChromeFlags;
 
+    const IPCTabAppBrowserContext& appBrowser = aContext.appBrowserContext();
+
     // We don't trust the IPCTabContext we receive from the child, so we'll bail
     // if we receive an IPCTabContext that's not a PopupIPCTabContext.
     // (PopupIPCTabContext lets the child process prove that it has access to
     // the app it's trying to open.)
-    if (aContext.type() != IPCTabContext::TPopupIPCTabContext) {
+    if (appBrowser.type() != IPCTabAppBrowserContext::TPopupIPCTabContext) {
         NS_ERROR("Unexpected IPCTabContext type.  Aborting AllocPBrowser.");
         return nullptr;
     }
 
-    const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
+    const PopupIPCTabContext& popupContext = appBrowser.get_PopupIPCTabContext();
     TabParent* opener = static_cast<TabParent*>(popupContext.openerParent());
     if (!opener) {
         NS_ERROR("Got null opener from child; aborting AllocPBrowser.");
         return nullptr;
     }
 
     // Popup windows of isBrowser frames must be isBrowser if the parent
     // isBrowser.  Allocating a !isBrowser frame with same app ID would allow
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -320,17 +320,25 @@ private:
     // registered in the child process.  To update this, one
     // can broadcast the topic "child-memory-reporter-request" using
     // the nsIObserverService.
     nsCOMArray<nsIMemoryReporter> mMemoryReporters;
 
     const nsString mAppManifestURL;
     nsRefPtr<nsFrameMessageManager> mMessageManager;
 
+    // True only while this is ready to be used to host remote tabs.
+    // This must not be used for new purposes after mIsAlive goes to
+    // false, but some previously scheduled IPC traffic may still pass
+    // through.
     bool mIsAlive;
+    // True after the OS-level shutdown sequence has been initiated.
+    // After going true, any use of this at all, including lingering
+    // IPC traffic passing through, will cause assertions to fail.
+    bool mIsDestroyed;
     bool mSendPermissionUpdates;
     bool mIsForBrowser;
 
     friend class CrashReporterParent;
 
     nsRefPtr<nsConsoleService>  mConsoleService;
     nsConsoleService* GetConsoleService();
 };
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -19,31 +19,33 @@ include protocol PMemoryReportRequest;
 include protocol PNecko;
 include protocol PSms;
 include protocol PStorage;
 include protocol PTestShell;
 include DOMTypes;
 include URIParams;
 
 include "mozilla/chrome/RegistryMessageUtils.h";
+include "mozilla/dom/TabMessageUtils.h";
+include "mozilla/layout/RenderFrameUtils.h";
 include "mozilla/net/NeckoMessageUtils.h";
-include "mozilla/dom/TabMessageUtils.h";
 
 include "nsGeoPositionIPCSerialiser.h";
 
 using GeoPosition;
 using PrefTuple;
 
 using ChromePackage;
 using ResourceMapping;
 using OverrideMapping;
 using IPC::Permission;
 using mozilla::null_t;
 using mozilla::void_t;
 using mozilla::dom::NativeThreadId;
+using mozilla::layout::ScrollingBehavior;
 using gfxIntSize;
 
 namespace mozilla {
 namespace dom {
 
 // Data required to clone an existing DOMStorageImpl in the parent
 struct StorageClone
 {
@@ -178,24 +180,29 @@ struct VanillaFrameIPCTabContext
 // IPCTabContext is an analog to mozilla::dom::TabContext.  Both specify an
 // iframe/PBrowser's own and containing app-ids and tell you whether the
 // iframe/PBrowser is a browser frame.  But only IPCTabContext is allowed to
 // travel over IPC.
 //
 // We need IPCTabContext (specifically, PopupIPCTabContext) to prevent a
 // privilege escalation attack by a compromised child process.  See the comment
 // on AllocPBrowser for details.
-union IPCTabContext
+union IPCTabAppBrowserContext
 {
   PopupIPCTabContext;
   AppFrameIPCTabContext;
   BrowserFrameIPCTabContext;
   VanillaFrameIPCTabContext;
 };
 
+struct IPCTabContext {
+  IPCTabAppBrowserContext appBrowserContext;
+  ScrollingBehavior scrollingBehavior;
+};
+
 union PrefValue {
   nsCString;
   int32_t;
   bool;
 };
 
 union MaybePrefValue {
   PrefValue;
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -845,17 +845,17 @@ TabChild::BrowserFrameProvideWindow(nsID
   // reasons.
   PopupIPCTabContext context;
   context.openerChild() = this;
   context.isBrowserElement() = IsBrowserElement();
 
   unused << Manager()->SendPBrowserConstructor(
       // We release this ref in DeallocPBrowserChild
       nsRefPtr<TabChild>(newChild).forget().get(),
-      context, /* chromeFlags */ 0);
+      IPCTabContext(context, mScrolling), /* chromeFlags */ 0);
 
   nsAutoCString spec;
   if (aURI) {
     aURI->GetSpec(spec);
   }
 
   NS_ConvertUTF8toUTF16 url(spec);
   nsString name(aName);
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -148,18 +148,19 @@ class TabChild : public PBrowserChild,
                  public nsIWebProgressListener,
                  public nsSupportsWeakReference,
                  public nsIDialogCreator,
                  public nsITabChild,
                  public nsIObserver,
                  public ipc::MessageManagerCallback,
                  public TabContext
 {
+    typedef mozilla::dom::ClonedMessageData ClonedMessageData;
     typedef mozilla::layout::RenderFrameChild RenderFrameChild;
-    typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+    typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
 
 public:
     /** 
      * This is expected to be called off the critical path to content
      * startup.  This is an opportunity to load things that are slow
      * on the critical path.
      */
     static void PreloadSlowThings();
--- a/dom/ipc/TabContext.cpp
+++ b/dom/ipc/TabContext.cpp
@@ -5,34 +5,37 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/TabChild.h"
 #include "nsIAppsService.h"
 
 using namespace mozilla::dom::ipc;
+using namespace mozilla::layout;
 
 namespace mozilla {
 namespace dom {
 
 TabContext::TabContext()
   : mInitialized(false)
   , mOwnAppId(nsIScriptSecurityManager::NO_APP_ID)
   , mContainingAppId(nsIScriptSecurityManager::NO_APP_ID)
+  , mScrollingBehavior(DEFAULT_SCROLLING)
   , mIsBrowser(false)
 {
 }
 
 TabContext::TabContext(const IPCTabContext& aParams)
   : mInitialized(true)
 {
-  switch(aParams.type()) {
-    case IPCTabContext::TPopupIPCTabContext: {
-      const PopupIPCTabContext &ipcContext = aParams.get_PopupIPCTabContext();
+  const IPCTabAppBrowserContext& appBrowser = aParams.appBrowserContext();
+  switch(appBrowser.type()) {
+    case IPCTabAppBrowserContext::TPopupIPCTabContext: {
+      const PopupIPCTabContext &ipcContext = appBrowser.get_PopupIPCTabContext();
 
       TabContext *context;
       if (ipcContext.openerParent()) {
         context = static_cast<TabParent*>(ipcContext.openerParent());
         if (context->IsBrowserElement() && !ipcContext.isBrowserElement()) {
           // If the TabParent corresponds to a browser element, then it can only
           // open other browser elements, for security reasons.  We should have
           // checked this before calling the TabContext constructor, so this is
@@ -59,44 +62,46 @@ TabContext::TabContext(const IPCTabConte
       }
       else {
         mIsBrowser = false;
         mOwnAppId = context->mOwnAppId;
         mContainingAppId = context->mContainingAppId;
       }
       break;
     }
-    case IPCTabContext::TAppFrameIPCTabContext: {
+    case IPCTabAppBrowserContext::TAppFrameIPCTabContext: {
       const AppFrameIPCTabContext &ipcContext =
-        aParams.get_AppFrameIPCTabContext();
+        appBrowser.get_AppFrameIPCTabContext();
 
       mIsBrowser = false;
       mOwnAppId = ipcContext.ownAppId();
       mContainingAppId = ipcContext.appFrameOwnerAppId();
       break;
     }
-    case IPCTabContext::TBrowserFrameIPCTabContext: {
+    case IPCTabAppBrowserContext::TBrowserFrameIPCTabContext: {
       const BrowserFrameIPCTabContext &ipcContext =
-        aParams.get_BrowserFrameIPCTabContext();
+        appBrowser.get_BrowserFrameIPCTabContext();
 
       mIsBrowser = true;
       mOwnAppId = nsIScriptSecurityManager::NO_APP_ID;
       mContainingAppId = ipcContext.browserFrameOwnerAppId();
       break;
     }
-    case IPCTabContext::TVanillaFrameIPCTabContext: {
+    case IPCTabAppBrowserContext::TVanillaFrameIPCTabContext: {
       mIsBrowser = false;
       mOwnAppId = nsIScriptSecurityManager::NO_APP_ID;
       mContainingAppId = nsIScriptSecurityManager::NO_APP_ID;
       break;
     }
     default: {
       MOZ_CRASH();
     }
   }
+
+  mScrollingBehavior = aParams.scrollingBehavior();
 }
 
 bool
 TabContext::IsBrowserElement() const
 {
   return mIsBrowser;
 }
 
@@ -209,21 +214,23 @@ TabContext::SetTabContext(const TabConte
     nsCOMPtr<mozIApplication> app = GetAppForId(aContext.mContainingAppId);
     NS_ENSURE_TRUE(app, false);
   }
 
   mInitialized = true;
   mIsBrowser = aContext.mIsBrowser;
   mOwnAppId = aContext.mOwnAppId;
   mContainingAppId = aContext.mContainingAppId;
+  mScrollingBehavior = aContext.mScrollingBehavior;
   return true;
 }
 
 bool
-TabContext::SetTabContextForAppFrame(mozIApplication* aOwnApp, mozIApplication* aAppFrameOwnerApp)
+TabContext::SetTabContextForAppFrame(mozIApplication* aOwnApp, mozIApplication* aAppFrameOwnerApp,
+                                     ScrollingBehavior aRequestedBehavior)
 {
   NS_ENSURE_FALSE(mInitialized, false);
 
   // Get ids for both apps and only write to our member variables after we've
   // verified that this worked.
   uint32_t ownAppId = nsIScriptSecurityManager::NO_APP_ID;
   if (aOwnApp) {
     nsresult rv = aOwnApp->GetLocalId(&ownAppId);
@@ -235,45 +242,50 @@ TabContext::SetTabContextForAppFrame(moz
     nsresult rv = aOwnApp->GetLocalId(&containingAppId);
     NS_ENSURE_SUCCESS(rv, false);
   }
 
   mInitialized = true;
   mIsBrowser = false;
   mOwnAppId = ownAppId;
   mContainingAppId = containingAppId;
+  mScrollingBehavior = aRequestedBehavior;
   return true;
 }
 
 bool
-TabContext::SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp)
+TabContext::SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp,
+                                         ScrollingBehavior aRequestedBehavior)
 {
   NS_ENSURE_FALSE(mInitialized, false);
 
   uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID;
   if (aBrowserFrameOwnerApp) {
     nsresult rv = aBrowserFrameOwnerApp->GetLocalId(&containingAppId);
     NS_ENSURE_SUCCESS(rv, false);
   }
 
   mInitialized = true;
   mIsBrowser = true;
   mOwnAppId = nsIScriptSecurityManager::NO_APP_ID;
   mContainingAppId = containingAppId;
+  mScrollingBehavior = aRequestedBehavior;
   return true;
 }
 
 IPCTabContext
 TabContext::AsIPCTabContext() const
 {
   if (mIsBrowser) {
-    return BrowserFrameIPCTabContext(mContainingAppId);
+    return IPCTabContext(BrowserFrameIPCTabContext(mContainingAppId),
+                         mScrollingBehavior);
   }
 
-  return AppFrameIPCTabContext(mOwnAppId, mContainingAppId);
+  return IPCTabContext(AppFrameIPCTabContext(mOwnAppId, mContainingAppId),
+                       mScrollingBehavior);
 }
 
 already_AddRefed<mozIApplication>
 TabContext::GetAppForId(uint32_t aAppId) const
 {
   if (aAppId == nsIScriptSecurityManager::NO_APP_ID) {
     return nullptr;
   }
--- a/dom/ipc/TabContext.h
+++ b/dom/ipc/TabContext.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_TabContext_h
 #define mozilla_dom_TabContext_h
 
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/dom/PBrowser.h"
+#include "mozilla/layout/RenderFrameUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "mozIApplication.h"
 
 namespace mozilla {
 namespace dom {
 
 /**
  * TabContext encapsulates information about an iframe that may be a mozbrowser
@@ -25,16 +26,19 @@ namespace dom {
  * standalone TabContext objects.
  *
  * This class is immutable except by calling one of the protected
  * SetTabContext*() methods (and those methods can only be called once).  See
  * also MutableTabContext.
  */
 class TabContext
 {
+protected:
+  typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
+
 public:
   /**
    * This constructor sets is-browser to false, and sets all relevant apps to
    * NO_APP_ID.  If you inherit from TabContext, you can mutate this object
    * exactly once by calling one of the protected SetTabContext*() methods.
    */
   TabContext();
 
@@ -113,16 +117,21 @@ public:
    * OwnOrContainingAppId() gets the ID of this frame, if HasOwnApp().  If this
    * frame does not have its own app, it gets the ID of the app which contains
    * this frame (i.e., the result of {Browser,App}OwnerAppId(), as applicable).
    */
   uint32_t OwnOrContainingAppId() const;
   already_AddRefed<mozIApplication> GetOwnOrContainingApp() const;
   bool HasOwnOrContainingApp() const;
 
+  /**
+   * Return the requested scrolling behavior for this frame.
+   */
+  ScrollingBehavior GetScrollingBehavior() const { return mScrollingBehavior; }
+
 protected:
   /**
    * These protected mutator methods let you modify a TabContext once.  Further
    * attempts to modify a given TabContext will fail (the method will return
    * false).
    *
    * These mutators will also fail if the TabContext was created with anything
    * other than the no-args constructor.
@@ -133,23 +142,25 @@ protected:
    */
   bool SetTabContext(const TabContext& aContext);
 
   /**
    * Set this TabContext to be an app frame (with the given own app) inside the
    * given app.  Either or both apps may be null.
    */
   bool SetTabContextForAppFrame(mozIApplication* aOwnApp,
-                                mozIApplication* aAppFrameOwnerApp);
+                                mozIApplication* aAppFrameOwnerApp,
+                                ScrollingBehavior aRequestedBehavior);
 
   /**
    * Set this TabContext to be a browser frame inside the given app (which may
    * be null).
    */
-  bool SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp);
+  bool SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp,
+                                    ScrollingBehavior aRequestedBehavior);
 
 private:
   /**
    * Translate an appId into a mozIApplication.
    */
   already_AddRefed<mozIApplication> GetAppForId(uint32_t aAppId) const;
 
   /**
@@ -167,16 +178,21 @@ private:
    * The id of the app which contains this TabContext's frame.  If mIsBrowser,
    * this corresponds to the ID of the app which contains the browser frame;
    * otherwise, this correspodns to the ID of the app which contains the app
    * frame.
    */
   uint32_t mContainingAppId;
 
   /**
+   * The requested scrolling behavior for this frame.
+   */
+  ScrollingBehavior mScrollingBehavior;
+
+  /**
    * Does this TabContext correspond to a browser element?
    *
    * If this is true, mOwnAppId must be NO_APP_ID.
    */
   bool mIsBrowser;
 };
 
 /**
@@ -187,23 +203,27 @@ private:
 class MutableTabContext : public TabContext
 {
 public:
   bool SetTabContext(const TabContext& aContext)
   {
     return TabContext::SetTabContext(aContext);
   }
 
-  bool SetTabContextForAppFrame(mozIApplication* aOwnApp, mozIApplication* aAppFrameOwnerApp)
+  bool SetTabContextForAppFrame(mozIApplication* aOwnApp, mozIApplication* aAppFrameOwnerApp,
+                                ScrollingBehavior aRequestedBehavior)
   {
-    return TabContext::SetTabContextForAppFrame(aOwnApp, aAppFrameOwnerApp);
+    return TabContext::SetTabContextForAppFrame(aOwnApp, aAppFrameOwnerApp,
+                                                aRequestedBehavior);
   }
 
-  bool SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp)
+  bool SetTabContextForBrowserFrame(mozIApplication* aBrowserFrameOwnerApp,
+                                    ScrollingBehavior aRequestedBehavior)
   {
-    return TabContext::SetTabContextForBrowserFrame(aBrowserFrameOwnerApp);
+    return TabContext::SetTabContextForBrowserFrame(aBrowserFrameOwnerApp,
+                                                    aRequestedBehavior);
   }
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -1218,18 +1218,18 @@ TabParent::GetWidget() const
 }
 
 bool
 TabParent::UseAsyncPanZoom()
 {
   bool usingOffMainThreadCompositing = !!CompositorParent::CompositorLoop();
   bool asyncPanZoomEnabled =
     Preferences::GetBool("layers.async-pan-zoom.enabled", false);
-  return (usingOffMainThreadCompositing &&
-          IsBrowserElement() && asyncPanZoomEnabled);
+  return (usingOffMainThreadCompositing && asyncPanZoomEnabled &&
+          GetScrollingBehavior() == ASYNC_PAN_ZOOM);
 }
 
 void
 TabParent::MaybeForwardEventToRenderFrame(const nsInputEvent& aEvent,
                                           nsInputEvent* aOutEvent)
 {
   if (RenderFrameParent* rfp = GetRenderFrame()) {
     rfp->NotifyInputEvent(aEvent, aOutEvent);
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -49,16 +49,17 @@ class ContentDialogParent : public PCont
 
 class TabParent : public PBrowserParent 
                 , public nsITabParent 
                 , public nsIAuthPromptProvider
                 , public nsISecureBrowserUI
                 , public TabContext
 {
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+    typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
 
 public:
     TabParent(const TabContext& aContext);
     virtual ~TabParent();
     nsIDOMElement* GetOwnerElement() { return mFrameElement; }
     void SetOwnerElement(nsIDOMElement* aElement);
     nsIBrowserDOMWindow *GetBrowserDOMWindow() { return mBrowserDOMWindow; }
     void SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserDOMWindow) {