Bug 811315: Use process-per-mozbrowser by default and make scrolling behavior selectable by content. r=jlebar sr=roc
authorChris Jones <jones.chris.g@gmail.com>
Tue, 27 Nov 2012 12:43:52 -0800
changeset 114282 8aa207f87eb6e4055c649ffcd2f6ceee7cc296c3
parent 114281 0c2011091748a5b5c1d9a4dd8705727aff6525f2
child 114283 5158d648702ecfe12338765a1c5de00d993189c9
push id23913
push useremorley@mozilla.com
push dateWed, 28 Nov 2012 17:11:31 +0000
treeherdermozilla-central@17c267a881cf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjlebar, roc
bugs811315
milestone20.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 811315: Use process-per-mozbrowser by default and make scrolling behavior selectable by content. r=jlebar sr=roc 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
@@ -564,16 +564,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
@@ -470,28 +470,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);
@@ -789,16 +795,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
@@ -445,26 +445,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()
@@ -662,17 +663,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()
@@ -700,16 +704,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!");
@@ -1012,17 +1017,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") &&
@@ -1144,26 +1149,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
@@ -316,17 +316,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
@@ -18,31 +18,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
 {
@@ -177,24 +179,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) {