Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 30 Jul 2013 18:08:18 -0400
changeset 141006 0d8409268f42fb2d9847ac7e874eb3036a284816
parent 140967 7aaee10b08e115a3e92d4c90912cecd572162246 (current diff)
parent 141005 60bf7ceba8906a32783d79c78e917fce83b7d72a (diff)
child 141007 c2b375f3a909fed4dd66947b88bd63e414c8d97e
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
milestone25.0a1
Merge m-c to inbound.
configure.in
gfx/layers/composite/AsyncCompositionManager.cpp
gfx/layers/ipc/CompositorParent.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
widget/gonk/nsAppShell.cpp
--- a/b2g/app/Makefile.in
+++ b/b2g/app/Makefile.in
@@ -25,17 +25,17 @@ LIBS += \
   -lEGL \
   -lhardware_legacy \
   -lhardware \
   -lcutils \
   $(DEPTH)/media/libpng/$(LIB_PREFIX)mozpng.$(LIB_SUFFIX) \
   $(DEPTH)/widget/gonk/libdisplay/$(LIB_PREFIX)display.$(LIB_SUFFIX) \
   $(MOZ_ZLIB_LIBS) \
   $(NULL)
-ifeq (17,$(ANDROID_VERSION))
+ifeq (18,$(ANDROID_VERSION))
 LIBS += \
   -lgui \
   -lsuspend \
   $(NULL)
 endif
 OS_LDFLAGS += -Wl,--export-dynamic
 LOCAL_INCLUDES += -I$(topsrcdir)/widget/gonk/libdisplay
 endif
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
 {
-    "revision": "10f16f6a15f8a1c4b910548b4d0968baaac21c91", 
+    "revision": "7e8284bea36c2d9307f919090922d3885f67eafc", 
     "repo_path": "/integration/gaia-central"
 }
--- a/configure.in
+++ b/configure.in
@@ -209,17 +209,17 @@ if test -n "$gonkdir" ; then
     15)
         GONK_INCLUDES="-I$gonkdir/frameworks/base/opengl/include -I$gonkdir/frameworks/base/native/include -I$gonkdir/frameworks/base/include -I$gonkdir/frameworks/base/services/camera -I$gonkdir/frameworks/base/include/media/stagefright -I$gonkdir/frameworks/base/include/media/stagefright/openmax -I$gonkdir/frameworks/base/media/libstagefright/rtsp -I$gonkdir/frameworks/base/media/libstagefright/include -I$gonkdir/external/dbus -I$gonkdir/external/bluetooth/bluez/lib -I$gonkdir/dalvik/libnativehelper/include/nativehelper"
         MOZ_B2G_BT=1
         MOZ_B2G_CAMERA=1
         MOZ_OMX_DECODER=1
         AC_DEFINE(MOZ_OMX_DECODER)
         AC_SUBST(MOZ_OMX_DECODER)
         ;;
-    17)
+    18)
         GONK_INCLUDES="-I$gonkdir/frameworks/native/include"
         ;;
     *)
         AC_MSG_ERROR([Unsupported platform version: $ANDROID_VERSION])
         ;;
     esac
     CPPFLAGS="-DANDROID -isystem $gonkdir/bionic/libc/$ARCH_DIR/include -isystem $gonkdir/bionic/libc/include/ -isystem $gonkdir/bionic/libc/kernel/common -isystem $gonkdir/bionic/libc/kernel/$ARCH_DIR -isystem $gonkdir/bionic/libm/include -I$gonkdir/system -I$gonkdir/system/core/include -isystem $gonkdir/bionic -I$gonkdir/hardware/libhardware/include -I$gonkdir/external/valgrind/fxos-include $GONK_INCLUDES $CPPFLAGS"
     CFLAGS="-mandroid -fno-short-enums -fno-exceptions $CFLAGS"
--- a/dom/browser-element/BrowserElementPanning.js
+++ b/dom/browser-element/BrowserElementPanning.js
@@ -40,26 +40,31 @@ const ContentPanning = {
       this.hybridEvents = isParentProcess;
 #endif
     } catch(e) {
       // Touch events aren't supported, so fall back on mouse.
       events = ['mousedown', 'mouseup', 'mousemove'];
       this.watchedEventsType = 'mouse';
     }
 
-    let els = Cc["@mozilla.org/eventlistenerservice;1"]
-                .getService(Ci.nsIEventListenerService);
+    // If we are using an AsyncPanZoomController for the parent frame,
+    // it will handle subframe scrolling too. We don't need to listen for
+    // these events.
+    if (!this._asyncPanZoomForViewportFrame) {
+      let els = Cc["@mozilla.org/eventlistenerservice;1"]
+                  .getService(Ci.nsIEventListenerService);
 
-    events.forEach(function(type) {
-      // Using the system group for mouse/touch events to avoid
-      // missing events if .stopPropagation() has been called.
-      els.addSystemEventListener(global, type,
-                                 this.handleEvent.bind(this),
-                                 /* useCapture = */ false);
-    }.bind(this));
+      events.forEach(function(type) {
+        // Using the system group for mouse/touch events to avoid
+        // missing events if .stopPropagation() has been called.
+        els.addSystemEventListener(global, type,
+                                   this.handleEvent.bind(this),
+                                   /* useCapture = */ false);
+      }.bind(this));
+    }
 
     addMessageListener("Viewport:Change", this._recvViewportChange.bind(this));
     addMessageListener("Gesture:DoubleTap", this._recvDoubleTap.bind(this));
   },
 
   handleEvent: function cp_handleEvent(evt) {
     if (evt.defaultPrevented || evt.multipleActionsPrevented) {
       // clean up panning state even if touchend/mouseup has been preventDefault.
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -900,26 +900,35 @@ ContactDB.prototype = {
         do {
           while (xIndex < sortBy.length && !x) {
             x = a.properties[sortBy[xIndex]];
             if (x) {
               x = x.join("").toLowerCase();
             }
             xIndex++;
           }
-          if (!x) {
-            return sortOrder == "descending" ? 1 : -1;
-          }
           while (yIndex < sortBy.length && !y) {
             y = b.properties[sortBy[yIndex]];
             if (y) {
               y = y.join("").toLowerCase();
             }
             yIndex++;
           }
+          if (!x) {
+            if (!y) {
+              let px, py;
+              px = JSON.stringify(a.published);
+              py = JSON.stringify(b.published);
+              if (px && py) {
+                return px.localeCompare(py);
+              }
+            } else {
+              return sortOrder == 'descending' ? 1 : -1;
+            }
+          }
           if (!y) {
             return sortOrder == "ascending" ? 1 : -1;
           }
 
           result = x.localeCompare(y);
           x = null;
           y = null;
         } while (result == 0);
--- a/dom/contacts/tests/test_contacts_basics.html
+++ b/dom/contacts/tests/test_contacts_basics.html
@@ -56,16 +56,34 @@ var c4 = {
   familyName: ["c","a","c"],
   givenName: ["c","a","c"],
 };
 
 var c5 = {
   nickname: "empty"
 };
 
+var c6 = {
+  name: "e",
+  familyName: ["e","e","e"],
+  givenName: ["e","e","e"],
+};
+
+var c7 = {
+  name: "e",
+  familyName: ["e","e","e"],
+  givenName: ["e","e","e"],
+};
+
+var c8 = {
+  name: "e",
+  familyName: ["e","e","e"],
+  givenName: ["e","e","e"],
+};
+
 var adr1 = {
   type: "work",
   streetAddress: "street 1",
   locality: "locality 1",
   region: "region 1",
   postalCode: "postal code 1",
   countryName: "country 1"
 };
@@ -1334,16 +1352,74 @@ var steps = [
     req = mozContacts.clear()
     req.onsuccess = function () {
       ok(true, "Deleted the database");
       next();
     }
     req.onerror = onFailure;
   },
   function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact();
+    createResult1.init(c7);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c7, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact();
+    createResult1.init(c6);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c6, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact();
+    createResult1.init(c8);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c8, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting with published");
+    var options = {sortBy: "familyName",
+                   sortOrder: "descending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 3, "3 results");
+      ok(req.result[0].published < req.result[1].published, "Right sorting order");
+      ok(req.result[1].published < req.result[2].published, "Right sorting order");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Deleting database");
+    req = mozContacts.clear()
+    req.onsuccess = function () {
+      ok(true, "Deleted the database");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
     ok(true, "Adding a new contact with properties2");
     createResult2 = new mozContact();
     createResult2.init(properties2);
     req = mozContacts.save(createResult2);
     req.onsuccess = function () {
       ok(createResult2.id, "The contact now has an ID.");
       sample_id2 = createResult2.id;
       next();
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1027,16 +1027,30 @@ TabChild::BrowserFrameProvideWindow(nsID
 already_AddRefed<nsIDOMWindowUtils>
 TabChild::GetDOMWindowUtils()
 {
   nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
   nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
   return utils.forget();
 }
 
+already_AddRefed<nsIDOMWindowUtils>
+TabChild::GetDOMWindowUtils(nsIContent* content)
+{
+  nsCOMPtr<nsIDOMWindowUtils> utils;
+  nsIDocument* doc = content->GetCurrentDoc();
+  if (doc) {
+    nsCOMPtr<nsIDOMWindow> window = doc->GetDefaultView();
+    if (window) {
+      utils = do_GetInterface(window);
+    }
+  }
+  return utils.forget();
+}
+
 static nsInterfaceHashtable<nsPtrHashKey<PContentDialogChild>, nsIDialogParamBlock> gActiveDialogs;
 
 NS_IMETHODIMP
 TabChild::OpenDialog(uint32_t aType, const nsACString& aName,
                      const nsACString& aFeatures,
                      nsIDialogParamBlock* aArguments,
                      nsIDOMElement* aFrameElement)
 {
@@ -1454,42 +1468,44 @@ TabChild::DispatchMessageManagerMessage(
     // Let the BrowserElementScrolling helper (if it exists) for this
     // content manipulate the frame state.
     nsRefPtr<nsFrameMessageManager> mm =
       static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
     mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal),
                        aMessageName, false, &cloneData, nullptr, nullptr);
 }
 
-static void
-ScrollWindowTo(nsIDOMWindow* aWindow, const CSSPoint& aPoint)
-{
-    nsGlobalWindow* window = static_cast<nsGlobalWindow*>(aWindow);
-    nsIScrollableFrame* sf = window->GetScrollFrame();
-
-    if (sf) {
-        sf->ScrollToCSSPixelsApproximate(aPoint);
-    }
-}
-
 bool
 TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
 {
-    nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
+  MOZ_ASSERT(aFrameMetrics.mScrollId != FrameMetrics::NULL_SCROLL_ID);
 
+  if (aFrameMetrics.mScrollId == FrameMetrics::ROOT_SCROLL_ID) {
     uint32_t presShellId;
+    nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
     nsresult rv = utils->GetPresShellId(&presShellId);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
-    if (NS_SUCCEEDED(rv) && aFrameMetrics.mPresShellId != presShellId) {
-        // We've recieved a message that is out of date and we want to ignore.
-        // However we can't reply without painting so we reply by painting the
-        // exact same thing as we did before.
-        return ProcessUpdateFrame(mLastMetrics);
+
+    if (NS_SUCCEEDED(rv) && aFrameMetrics.mPresShellId == presShellId) {
+      return ProcessUpdateFrame(aFrameMetrics);
     }
-    return ProcessUpdateFrame(aFrameMetrics);
+  } else {
+    // aFrameMetrics.mScrollId is not FrameMetrics::ROOT_SCROLL_ID,
+    // so we are trying to update a subframe. This requires special handling.
+    nsCOMPtr<nsIContent> content = nsLayoutUtils::FindContentFor(
+                                      aFrameMetrics.mScrollId);
+    if (content) {
+      return ProcessUpdateSubframe(content, aFrameMetrics);
+    }
+  }
+
+  // We've recieved a message that is out of date and we want to ignore.
+  // However we can't reply without painting so we reply by painting the
+  // exact same thing as we did before.
+  return ProcessUpdateFrame(mLastMetrics);
 }
 
 bool
 TabChild::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics)
   {
     if (!mGlobal || !mTabChildGlobal) {
         return true;
     }
@@ -1528,61 +1544,70 @@ TabChild::ProcessUpdateFrame(const Frame
             data += nsPrintfCString(" }");
     data += nsPrintfCString(" }");
 
     DispatchMessageManagerMessage(NS_LITERAL_STRING("Viewport:Change"), data);
 
     nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
     nsCOMPtr<nsIDOMWindow> window = do_GetInterface(mWebNav);
 
+    // set the scroll port size, which determines the scroll range
     utils->SetScrollPositionClampingScrollPortSize(
       cssCompositedRect.width, cssCompositedRect.height);
-    ScrollWindowTo(window, aFrameMetrics.mScrollOffset);
-    LayoutDeviceToLayerScale resolution =
-      aFrameMetrics.CalculateResolution()
-      / aFrameMetrics.mDevPixelsPerCSSPixel
-      * ScreenToLayerScale(1);
+
+    // scroll the window to the desired spot
+    nsIScrollableFrame* sf = static_cast<nsGlobalWindow*>(window.get())->GetScrollFrame();
+    if (sf) {
+        sf->ScrollToCSSPixelsApproximate(aFrameMetrics.mScrollOffset);
+    }
+
+    // set the resolution
+    LayoutDeviceToLayerScale resolution = aFrameMetrics.CalculateResolution()
+      / aFrameMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1);
     utils->SetResolution(resolution.scale, resolution.scale);
 
-    if (aFrameMetrics.mScrollId != FrameMetrics::NULL_SCROLL_ID) {
-      SetDisplayPort(aFrameMetrics);
+    // and set the display port
+    nsCOMPtr<nsIDOMDocument> domDoc;
+    mWebNav->GetDocument(getter_AddRefs(domDoc));
+    if (domDoc) {
+      nsCOMPtr<nsIDOMElement> element;
+      domDoc->GetDocumentElement(getter_AddRefs(element));
+      if (element) {
+        utils->SetDisplayPortForElement(
+          aFrameMetrics.mDisplayPort.x, aFrameMetrics.mDisplayPort.y,
+          aFrameMetrics.mDisplayPort.width, aFrameMetrics.mDisplayPort.height,
+          element);
+      }
     }
 
     mLastMetrics = aFrameMetrics;
-
     return true;
 }
 
-void
-TabChild::SetDisplayPort(const FrameMetrics& aFrameMetrics)
+bool
+TabChild::ProcessUpdateSubframe(nsIContent* aContent,
+                                const FrameMetrics& aMetrics)
 {
-  nsCOMPtr<nsIDOMDocument> domDoc;
-  mWebNav->GetDocument(getter_AddRefs(domDoc));
-  if (!domDoc) {
-    return;
+  // scroll the frame to the desired spot
+  nsIScrollableFrame* scrollFrame = nsLayoutUtils::FindScrollableFrameFor(aMetrics.mScrollId);
+  if (scrollFrame) {
+    scrollFrame->ScrollToCSSPixelsApproximate(aMetrics.mScrollOffset);
   }
 
-  // nsLayoutUtils::FindContentFor() doesn't provide a look-up for the root
-  // scroll idea. This is because the root scroll ID could refer to a different
-  // element in the DOM when navigating to a new document.
-  nsCOMPtr<nsIDOMElement> element;
-  if (aFrameMetrics.mScrollId == FrameMetrics::ROOT_SCROLL_ID) {
-    domDoc->GetDocumentElement(getter_AddRefs(element));
-  } else {
-    element = do_QueryInterface(nsLayoutUtils::FindContentFor(
-      aFrameMetrics.mScrollId));
+  nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils(aContent));
+  nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aContent);
+  if (utils && element) {
+    // and set the display port
+    utils->SetDisplayPortForElement(
+      aMetrics.mDisplayPort.x, aMetrics.mDisplayPort.y,
+      aMetrics.mDisplayPort.width, aMetrics.mDisplayPort.height,
+      element);
   }
 
-  if (element) {
-    nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
-    utils->SetDisplayPortForElement(
-      aFrameMetrics.mDisplayPort.x, aFrameMetrics.mDisplayPort.y,
-      aFrameMetrics.mDisplayPort.width, aFrameMetrics.mDisplayPort.height,
-      element);
-  }
+  return true;
 }
 
 bool
 TabChild::RecvHandleDoubleTap(const CSSIntPoint& aPoint)
 {
     if (!mGlobal || !mTabChildGlobal) {
         return true;
     }
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -373,20 +373,17 @@ private:
     void ActorDestroy(ActorDestroyReason why);
 
     enum FrameScriptLoading { DONT_LOAD_SCRIPTS, DEFAULT_LOAD_SCRIPTS };
     bool InitTabChildGlobal(FrameScriptLoading aScriptLoading = DEFAULT_LOAD_SCRIPTS);
     bool InitRenderingState();
     void DestroyWindow();
     void SetProcessNameToAppName();
     bool ProcessUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics);
-
-    // Update the DOM with the given display port. Finds the element based on
-    // the aFrameMetrics.mScrollId.
-    void SetDisplayPort(const FrameMetrics& aFrameMetrics);
+    bool ProcessUpdateSubframe(nsIContent* aContent, const FrameMetrics& aMetrics);
 
     // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
     void DoFakeShow();
 
     // Wrapper for nsIDOMWindowUtils.setCSSViewport(). This updates some state
     // variables local to this class before setting it.
     void SetCSSViewport(const CSSSize& aSize);
 
@@ -421,17 +418,21 @@ private:
     nsresult
     BrowserFrameProvideWindow(nsIDOMWindow* aOpener,
                               nsIURI* aURI,
                               const nsAString& aName,
                               const nsACString& aFeatures,
                               bool* aWindowIsNew,
                               nsIDOMWindow** aReturn);
 
+    // Get the DOMWindowUtils for the top-level window in this tab.
     already_AddRefed<nsIDOMWindowUtils> GetDOMWindowUtils();
+    // Get the DOMWindowUtils for the window corresponding to the givent content
+    // element. This might be an iframe inside the tab, for instance.
+    already_AddRefed<nsIDOMWindowUtils> GetDOMWindowUtils(nsIContent* aContent);
 
     class CachedFileDescriptorInfo;
     class CachedFileDescriptorCallbackRunnable;
 
     TextureFactoryIdentifier mTextureFactoryIdentifier;
     nsCOMPtr<nsIWebNavigation> mWebNav;
     nsCOMPtr<nsIWidget> mWidget;
     nsCOMPtr<nsIURI> mLastURI;
--- a/dom/network/tests/marionette/manifest.ini
+++ b/dom/network/tests/marionette/manifest.ini
@@ -8,10 +8,11 @@ disabled = Bug 808783
 [test_mobile_voice_state.js]
 [test_mobile_iccinfo.js]
 [test_mobile_operator_names.js]
 [test_mobile_preferred_network_type.js]
 disabled = Bug 808783
 [test_mobile_data_location.js]
 [test_mobile_data_state.js]
 [test_mobile_mmi.js]
+[test_mobile_roaming_preference.js]
 [test_call_barring_get_option.js]
 [test_call_barring_set_error.js]
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/marionette/test_mobile_roaming_preference.js
@@ -0,0 +1,98 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 60000;
+
+SpecialPowers.addPermission("mobileconnection", true, document);
+
+let connection = navigator.mozMobileConnection;
+ok(connection instanceof MozMobileConnection,
+   "connection is instanceof " + connection.constructor);
+
+function failedToSetRoamingPreference(mode, expectedErrorMessage, callback) {
+  let request = connection.setRoamingPreference(mode);
+
+  ok(request instanceof DOMRequest,
+     "request instanceof " + request.constructor);
+
+  request.onsuccess = function onsuccess() {
+    ok(false, "Should not be here !!");
+
+    callback();
+  }
+
+  request.onerror = function onerror() {
+    is(request.error.name, expectedErrorMessage);
+
+    callback();
+  }
+}
+
+function testSetRoamingPreferenceWithNullValue() {
+  log("test setRoamingPreference(null)");
+
+  failedToSetRoamingPreference(null, "InvalidParameter", runNextTest);
+}
+
+function testSetRoamingPreferenceWithInvalidValue() {
+  log("test setRoamingPreference(\"InvalidValue\")");
+
+  failedToSetRoamingPreference("InvalidValue", "InvalidParameter", runNextTest);
+}
+
+function testSetRoamingPreferenceToHome() {
+  log("test setRoamingPreference(\"home\")");
+
+  // TODO: Bug 896394.
+  // Currently emulator run as GSM mode by default. So we expect to get a
+  // 'RequestNotSupported' error here.
+  failedToSetRoamingPreference("home", "RequestNotSupported", runNextTest);
+}
+
+function testGetRoamingPreference() {
+  log("test getRoamingPreference()");
+
+  // TODO: Bug 896394.
+  // Currently emulator run as GSM mode by default. So we expect to get a
+  // 'RequestNotSupported' error here.
+  let request = connection.getRoamingPreference();
+
+  ok(request instanceof DOMRequest,
+     "request instanceof " + request.constructor);
+
+  request.onsuccess = function onsuccess() {
+    ok(false, "Should not be here !!");
+
+    runNextTest();
+  }
+
+  request.onerror = function onerror() {
+    is(request.error.name, "RequestNotSupported");
+
+    runNextTest();
+  }
+}
+
+let tests = [
+  testSetRoamingPreferenceWithNullValue,
+  testSetRoamingPreferenceWithInvalidValue,
+  testSetRoamingPreferenceToHome,
+  testGetRoamingPreference
+];
+
+function runNextTest() {
+  let test = tests.shift();
+  if (!test) {
+    cleanUp();
+    return;
+  }
+
+  test();
+}
+
+function cleanUp() {
+  SpecialPowers.removePermission("mobileconnection", document);
+  finish();
+}
+
+runNextTest();
\ No newline at end of file
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -1611,17 +1611,17 @@ let RIL = {
   /**
    * Set the roaming preference mode
    */
   setRoamingPreference: function setRoamingPreference(options) {
     let roamingMode = CDMA_ROAMING_PREFERENCE_TO_GECKO.indexOf(options.mode);
 
     if (roamingMode === -1) {
       options.errorMsg = GECKO_ERROR_INVALID_PARAMETER;
-      this.sendDOMMessage(options);
+      this.sendChromeMessage(options);
       return;
     }
 
     Buf.newParcel(REQUEST_CDMA_SET_ROAMING_PREFERENCE, options);
     Buf.writeUint32(1);
     Buf.writeUint32(roamingMode);
     Buf.sendParcel();
   },
@@ -5644,32 +5644,32 @@ RIL[REQUEST_GET_PREFERRED_NETWORK_TYPE] 
   });
 };
 RIL[REQUEST_GET_NEIGHBORING_CELL_IDS] = null;
 RIL[REQUEST_SET_LOCATION_UPDATES] = null;
 RIL[REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE] = null;
 RIL[REQUEST_CDMA_SET_ROAMING_PREFERENCE] = function REQUEST_CDMA_SET_ROAMING_PREFERENCE(length, options) {
   if (options.rilRequestError) {
     options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
-    this.sendDOMMessage(options);
+    this.sendChromeMessage(options);
     return;
   }
 
-  this.sendDOMMessage(options);
+  this.sendChromeMessage(options);
 };
 RIL[REQUEST_CDMA_QUERY_ROAMING_PREFERENCE] = function REQUEST_CDMA_QUERY_ROAMING_PREFERENCE(length, options) {
   if (options.rilRequestError) {
     options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError];
-    this.sendDOMMessage(options);
+    this.sendChromeMessage(options);
     return;
   }
 
   let mode = Buf.readUint32List();
   options.mode = CDMA_ROAMING_PREFERENCE_TO_GECKO[mode[0]];
-  this.sendDOMMessage(options);
+  this.sendChromeMessage(options);
 };
 RIL[REQUEST_SET_TTY_MODE] = null;
 RIL[REQUEST_QUERY_TTY_MODE] = null;
 RIL[REQUEST_CDMA_SET_PREFERRED_VOICE_PRIVACY_MODE] = null;
 RIL[REQUEST_CDMA_QUERY_PREFERRED_VOICE_PRIVACY_MODE] = null;
 RIL[REQUEST_CDMA_FLASH] = null;
 RIL[REQUEST_CDMA_BURST_DTMF] = null;
 RIL[REQUEST_CDMA_VALIDATE_AND_WRITE_AKEY] = null;
--- a/dom/wappush/src/gonk/CpPduHelper.jsm
+++ b/dom/wappush/src/gonk/CpPduHelper.jsm
@@ -81,187 +81,261 @@ this.PduHelper = {
     return msg;
 
   }
 };
 
 /**
   * Tag tokens
   *
-  * @see WAP-183-ProvCont-20010724-A, clause 8.1
+  * @see WAP-183-ProvCont-20010724-A, clause 8.1 for code page 0
+  * @see OMA-WAP-TS-ProvCont-V1_1-20090421-C, clause 7.1 for code page 1
   */
 const CP_TAG_FIELDS = (function () {
   let names = {};
-  function add(name, number) {
+  function add(name, codepage, number) {
     let entry = {
       name: name,
       number: number,
     };
-    names[number] = entry;
+    if (!names[codepage]) {
+      names[codepage] = {};
+    }
+    names[codepage][number] = entry;
   }
 
-  add("wap-provisioningdoc", 0x05);
-  add("characteristic",      0x06);
-  add("parm",                0x07);
+  // Code page 0
+  add("wap-provisioningdoc", 0, 0x05);
+  add("characteristic",      0, 0x06);
+  add("parm",                0, 0x07);
+  // Code page 1
+  add("characteristic",      1, 0x06);
+  add("parm",                1, 0x07);
 
   return names;
 })();
 
 /**
   * Attribute Tokens
   *
-  * @see WAP-183-ProvCont-20010724-A, clause 8.2
+  * @see WAP-183-ProvCont-20010724-A, clause 8.2 for code page 0
+  * @see OMA-WAP-TS-ProvCont-V1_1-20090421-C, clause 7.2 for code page 1
   */
 const CP_ATTRIBUTE_FIELDS = (function () {
   let names = {};
-  function add(name, value, number) {
+  function add(name, value, codepage, number) {
     let entry = {
       name: name,
       value: value,
       number: number,
     };
-    names[number] = entry;
+    if (!names[codepage]) {
+      names[codepage] = {};
+    }
+    names[codepage][number] = entry;
   }
 
-  add("name",     "",                                 0x05);
-  add("value",    "",                                 0x06);
-  add("name",     "NAME",                             0x07);
-  add("name",     "NAP-ADDRESS",                      0x08);
-  add("name",     "NAP-ADDRTYPE",                     0x09);
-  add("name",     "CALLTYPE",                         0x0A);
-  add("name",     "VALIDUNTIL",                       0x0B);
-  add("name",     "AUTHTYPE",                         0x0C);
-  add("name",     "AUTHNAME",                         0x0D);
-  add("name",     "AUTHSECRET",                       0x0E);
-  add("name",     "LINGER",                           0x0F);
-  add("name",     "BEARER",                           0x10);
-  add("name",     "NAPID",                            0x11);
-  add("name",     "COUNTRY",                          0x12);
-  add("name",     "NETWORK",                          0x13);
-  add("name",     "INTERNET",                         0x14);
-  add("name",     "PROXY-ID",                         0x15);
-  add("name",     "PROXY-PROVIDER-ID",                0x16);
-  add("name",     "DOMAIN",                           0x17);
-  add("name",     "PROVURL",                          0x18);
-  add("name",     "PXAUTH-TYPE",                      0x19);
-  add("name",     "PXAUTH-ID",                        0x1A);
-  add("name",     "PXAUTH-PW",                        0x1B);
-  add("name",     "STARTPAGE",                        0x1C);
-  add("name",     "BASAUTH-ID",                       0x1D);
-  add("name",     "BASAUTH-PW",                       0x1E);
-  add("name",     "PUSHENABLED",                      0x1F);
-  add("name",     "PXADDR",                           0x20);
-  add("name",     "PXADDRTYPE",                       0x21);
-  add("name",     "TO-NAPID",                         0x22);
-  add("name",     "PORTNBR",                          0x23);
-  add("name",     "SERVICE",                          0x24);
-  add("name",     "LINKSPEED",                        0x25);
-  add("name",     "DNLINKSPEED",                      0x26);
-  add("name",     "LOCAL-ADDR",                       0x27);
-  add("name",     "LOCAL-ADDRTYPE",                   0x28);
-  add("name",     "CONTEXT-ALLOW",                    0x29);
-  add("name",     "TRUST",                            0x2A);
-  add("name",     "MASTER",                           0x2B);
-  add("name",     "SID",                              0x2C);
-  add("name",     "SOC",                              0x2D);
-  add("name",     "WSP-VERSION",                      0x2E);
-  add("name",     "PHYSICAL-PROXY-ID",                0x2F);
-  add("name",     "CLIENT-ID",                        0x30);
-  add("name",     "DELIVERY-ERR-PDU",                 0x31);
-  add("name",     "DELIVERY-ORDER",                   0x32);
-  add("name",     "TRAFFIC-CLASS",                    0x33);
-  add("name",     "MAX-SDU-SIZE",                     0x34);
-  add("name",     "MAX-BITRATE-UPLINK",               0x35);
-  add("name",     "MAX-BITRATE-DNLINK",               0x36);
-  add("name",     "RESIDUAL-BER",                     0x37);
-  add("name",     "SDU-ERROR-RATIO",                  0x38);
-  add("name",     "TRAFFIC-HANDL-PRIO",               0x39);
-  add("name",     "TRANSFER-DELAY",                   0x3A);
-  add("name",     "GUARANTEED-BITRATE-UPLINK",        0x3B);
-  add("name",     "GUARANTEED-BITRATE-DNLINK",        0x3C);
-  add("version",  "",                                 0x45);
-  add("version",  "1.0",                              0x46);
-  add("type",     "",                                 0x50);
-  add("type",     "PXLOGICAL",                        0x51);
-  add("type",     "PXPHYSICAL",                       0x52);
-  add("type",     "PORT",                             0x53);
-  add("type",     "VALIDITY",                         0x54);
-  add("type",     "NAPDEF",                           0x55);
-  add("type",     "BOOTSTRAP",                        0x56);
+  // Code page 0
+  add("name",     "",                             0,  0x05);
+  add("value",    "",                             0,  0x06);
+  add("name",     "NAME",                         0,  0x07);
+  add("name",     "NAP-ADDRESS",                  0,  0x08);
+  add("name",     "NAP-ADDRTYPE",                 0,  0x09);
+  add("name",     "CALLTYPE",                     0,  0x0A);
+  add("name",     "VALIDUNTIL",                   0,  0x0B);
+  add("name",     "AUTHTYPE",                     0,  0x0C);
+  add("name",     "AUTHNAME",                     0,  0x0D);
+  add("name",     "AUTHSECRET",                   0,  0x0E);
+  add("name",     "LINGER",                       0,  0x0F);
+  add("name",     "BEARER",                       0,  0x10);
+  add("name",     "NAPID",                        0,  0x11);
+  add("name",     "COUNTRY",                      0,  0x12);
+  add("name",     "NETWORK",                      0,  0x13);
+  add("name",     "INTERNET",                     0,  0x14);
+  add("name",     "PROXY-ID",                     0,  0x15);
+  add("name",     "PROXY-PROVIDER-ID",            0,  0x16);
+  add("name",     "DOMAIN",                       0,  0x17);
+  add("name",     "PROVURL",                      0,  0x18);
+  add("name",     "PXAUTH-TYPE",                  0,  0x19);
+  add("name",     "PXAUTH-ID",                    0,  0x1A);
+  add("name",     "PXAUTH-PW",                    0,  0x1B);
+  add("name",     "STARTPAGE",                    0,  0x1C);
+  add("name",     "BASAUTH-ID",                   0,  0x1D);
+  add("name",     "BASAUTH-PW",                   0,  0x1E);
+  add("name",     "PUSHENABLED",                  0,  0x1F);
+  add("name",     "PXADDR",                       0,  0x20);
+  add("name",     "PXADDRTYPE",                   0,  0x21);
+  add("name",     "TO-NAPID",                     0,  0x22);
+  add("name",     "PORTNBR",                      0,  0x23);
+  add("name",     "SERVICE",                      0,  0x24);
+  add("name",     "LINKSPEED",                    0,  0x25);
+  add("name",     "DNLINKSPEED",                  0,  0x26);
+  add("name",     "LOCAL-ADDR",                   0,  0x27);
+  add("name",     "LOCAL-ADDRTYPE",               0,  0x28);
+  add("name",     "CONTEXT-ALLOW",                0,  0x29);
+  add("name",     "TRUST",                        0,  0x2A);
+  add("name",     "MASTER",                       0,  0x2B);
+  add("name",     "SID",                          0,  0x2C);
+  add("name",     "SOC",                          0,  0x2D);
+  add("name",     "WSP-VERSION",                  0,  0x2E);
+  add("name",     "PHYSICAL-PROXY-ID",            0,  0x2F);
+  add("name",     "CLIENT-ID",                    0,  0x30);
+  add("name",     "DELIVERY-ERR-PDU",             0,  0x31);
+  add("name",     "DELIVERY-ORDER",               0,  0x32);
+  add("name",     "TRAFFIC-CLASS",                0,  0x33);
+  add("name",     "MAX-SDU-SIZE",                 0,  0x34);
+  add("name",     "MAX-BITRATE-UPLINK",           0,  0x35);
+  add("name",     "MAX-BITRATE-DNLINK",           0,  0x36);
+  add("name",     "RESIDUAL-BER",                 0,  0x37);
+  add("name",     "SDU-ERROR-RATIO",              0,  0x38);
+  add("name",     "TRAFFIC-HANDL-PRIO",           0,  0x39);
+  add("name",     "TRANSFER-DELAY",               0,  0x3A);
+  add("name",     "GUARANTEED-BITRATE-UPLINK",    0,  0x3B);
+  add("name",     "GUARANTEED-BITRATE-DNLINK",    0,  0x3C);
+  add("version",  "",                             0,  0x45);
+  add("version",  "1.0",                          0,  0x46);
+  add("type",     "",                             0,  0x50);
+  add("type",     "PXLOGICAL",                    0,  0x51);
+  add("type",     "PXPHYSICAL",                   0,  0x52);
+  add("type",     "PORT",                         0,  0x53);
+  add("type",     "VALIDITY",                     0,  0x54);
+  add("type",     "NAPDEF",                       0,  0x55);
+  add("type",     "BOOTSTRAP",                    0,  0x56);
 /*
  *  Mark out VENDORCONFIG so if it is contained in message, parse
  *  will failed and raw data is returned.
  */
-//  add("type",     "VENDORCONFIG",                     0x57);
-  add("type",     "CLIENTIDENTITY",                   0x58);
-  add("type",     "PXAUTHINFO",                       0x59);
-  add("type",     "NAPAUTHINFO",                      0x5A);
+//  add("type",     "VENDORCONFIG",                 0,  0x57);
+  add("type",     "CLIENTIDENTITY",               0,  0x58);
+  add("type",     "PXAUTHINFO",                   0,  0x59);
+  add("type",     "NAPAUTHINFO",                  0,  0x5A);
+
+  // Code page 1
+  add("name",     "",                             1,  0x05);
+  add("value",    "",                             1,  0x06);
+  add("name",     "NAME",                         1,  0x07);
+  add("name",     "INTERNET",                     1,  0x14);
+  add("name",     "STARTPAGE",                    1,  0x1C);
+  add("name",     "TO-NAPID",                     1,  0x22);
+  add("name",     "PORTNBR",                      1,  0x23);
+  add("name",     "SERVICE",                      1,  0x24);
+  add("name",     "AACCEPT",                      1,  0x2E);
+  add("name",     "AAUTHDATA",                    1,  0x2F);
+  add("name",     "AAUTHLEVEL",                   1,  0x30);
+  add("name",     "AAUTHNAME",                    1,  0x31);
+  add("name",     "AAUTHSECRET",                  1,  0x32);
+  add("name",     "AAUTHTYPE",                    1,  0x33);
+  add("name",     "ADDR",                         1,  0x34);
+  add("name",     "ADDRTYPE",                     1,  0x35);
+  add("name",     "APPID",                        1,  0x36);
+  add("name",     "APROTOCOL",                    1,  0x37);
+  add("name",     "PROVIDER-ID",                  1,  0x38);
+  add("name",     "TO-PROXY",                     1,  0x39);
+  add("name",     "URI",                          1,  0x3A);
+  add("name",     "RULE",                         1,  0x3B);
+  add("type",     "",                             1,  0x50);
+  add("type",     "PORT",                         1,  0x53);
+  add("type",     "APPLICATION",                  1,  0x55);
+  add("type",     "APPADDR",                      1,  0x56);
+  add("type",     "APPAUTH",                      1,  0x57);
+  add("type",     "CLIENTIDENTITY",               1,  0x58);
+  add("type",     "RESOURCE",                     1,  0x59);
 
   return names;
 })();
 
+/**
+  * Value Tokens
+  *
+  * @see WAP-183-ProvCont-20010724-A, clause 8.3 for code page 0
+  * @see OMA-WAP-TS-ProvCont-V1_1-20090421-C, clause 7.3 for code page 1
+  */
 const CP_VALUE_FIELDS = (function () {
   let names = {};
-  function add(value, number) {
+  function add(value, codepage, number) {
     let entry = {
       value: value,
       number: number,
     };
-    names[number] = entry;
+    if (!names[codepage]) {
+      names[codepage] = {};
+    }
+    names[codepage][number] = entry;
   }
 
-  add("IPV4",                             0x85);
-  add("IPV6",                             0x86);
-  add("E164",                             0x87);
-  add("ALPHA",                            0x88);
-  add("APN",                              0x89);
-  add("SCODE",                            0x8A);
-  add("TETRA-ITSI",                       0x8B);
-  add("MAN",                              0x8C);
-  add("ANALOG-MODEM",                     0x90);
-  add("V.120",                            0x91);
-  add("V.110",                            0x92);
-  add("X.31",                             0x93);
-  add("BIT-TRANSPARENT",                  0x94);
-  add("DIRECT-ASYNCHRONOUS-DATA-SERVICE", 0x95);
-  add("PAP",                              0x9A);
-  add("CHAP",                             0x9B);
-  add("HTTP-BASIC",                       0x9C);
-  add("HTTP-DIGEST",                      0x9D);
-  add("WTLS-SS",                          0x9E);
-  add("GSM-USSD",                         0xA2);
-  add("GSM-SMS",                          0xA3);
-  add("ANSI-136-GUTS",                    0xA4);
-  add("IS-95-CDMA-SMS",                   0xA5);
-  add("IS-95-CDMA-CSD",                   0xA6);
-  add("IS-95-CDMA-PAC",                   0xA7);
-  add("ANSI-136-CSD",                     0xA8);
-  add("ANSI-136-GPRS",                    0xA9);
-  add("GSM-CSD",                          0xAA);
-  add("GSM-GPRS",                         0xAB);
-  add("AMPS-CDPD",                        0xAC);
-  add("PDC-CSD",                          0xAD);
-  add("PDC-PACKET",                       0xAE);
-  add("IDEN-SMS",                         0xAF);
-  add("IDEN-CSD",                         0xB0);
-  add("IDEN-PACKET",                      0xB1);
-  add("FLEX/REFLEX",                      0xB2);
-  add("PHS-SMS",                          0xB3);
-  add("PHS-CSD",                          0xB4);
-  add("TETRA-SDS",                        0xB5);
-  add("TETRA-PACKET",                     0xB6);
-  add("ANSI-136-GHOST",                   0xB7);
-  add("MOBITEX-MPAK",                     0xB8);
-  add("AUTOBOUDING",                      0xC5);
-  add("CL-WSP",                           0xCA);
-  add("CO-WSP",                           0xCB);
-  add("CL-SEC-WSP",                       0xCC);
-  add("CO-SEC-WSP",                       0xCD);
-  add("CL-SEC-WTA",                       0xCE);
-  add("CO-SEC-WTA",                       0xCF);
+  // Code page 0
+  add("IPV4",                             0,  0x85);
+  add("IPV6",                             0,  0x86);
+  add("E164",                             0,  0x87);
+  add("ALPHA",                            0,  0x88);
+  add("APN",                              0,  0x89);
+  add("SCODE",                            0,  0x8A);
+  add("TETRA-ITSI",                       0,  0x8B);
+  add("MAN",                              0,  0x8C);
+  add("ANALOG-MODEM",                     0,  0x90);
+  add("V.120",                            0,  0x91);
+  add("V.110",                            0,  0x92);
+  add("X.31",                             0,  0x93);
+  add("BIT-TRANSPARENT",                  0,  0x94);
+  add("DIRECT-ASYNCHRONOUS-DATA-SERVICE", 0,  0x95);
+  add("PAP",                              0,  0x9A);
+  add("CHAP",                             0,  0x9B);
+  add("HTTP-BASIC",                       0,  0x9C);
+  add("HTTP-DIGEST",                      0,  0x9D);
+  add("WTLS-SS",                          0,  0x9E);
+  add("MD5",                              0,  0x9F);  // Added in OMA, 7.3.3
+  add("GSM-USSD",                         0,  0xA2);
+  add("GSM-SMS",                          0,  0xA3);
+  add("ANSI-136-GUTS",                    0,  0xA4);
+  add("IS-95-CDMA-SMS",                   0,  0xA5);
+  add("IS-95-CDMA-CSD",                   0,  0xA6);
+  add("IS-95-CDMA-PAC",                   0,  0xA7);
+  add("ANSI-136-CSD",                     0,  0xA8);
+  add("ANSI-136-GPRS",                    0,  0xA9);
+  add("GSM-CSD",                          0,  0xAA);
+  add("GSM-GPRS",                         0,  0xAB);
+  add("AMPS-CDPD",                        0,  0xAC);
+  add("PDC-CSD",                          0,  0xAD);
+  add("PDC-PACKET",                       0,  0xAE);
+  add("IDEN-SMS",                         0,  0xAF);
+  add("IDEN-CSD",                         0,  0xB0);
+  add("IDEN-PACKET",                      0,  0xB1);
+  add("FLEX/REFLEX",                      0,  0xB2);
+  add("PHS-SMS",                          0,  0xB3);
+  add("PHS-CSD",                          0,  0xB4);
+  add("TETRA-SDS",                        0,  0xB5);
+  add("TETRA-PACKET",                     0,  0xB6);
+  add("ANSI-136-GHOST",                   0,  0xB7);
+  add("MOBITEX-MPAK",                     0,  0xB8);
+  add("CDMA2000-1X-SIMPLE-IP",            0,  0xB9);  // Added in OMA, 7.3.4
+  add("CDMA2000-1X-MOBILE-IP",            0,  0xBA);  // Added in OMA, 7.3.4
+  add("AUTOBOUDING",                      0,  0xC5);
+  add("CL-WSP",                           0,  0xCA);
+  add("CO-WSP",                           0,  0xCB);
+  add("CL-SEC-WSP",                       0,  0xCC);
+  add("CO-SEC-WSP",                       0,  0xCD);
+  add("CL-SEC-WTA",                       0,  0xCE);
+  add("CO-SEC-WTA",                       0,  0xCF);
+  add("OTA-HTTP-TO",                      0,  0xD0);  // Added in OMA, 7.3.6
+  add("OTA-HTTP-TLS-TO",                  0,  0xD1);  // Added in OMA, 7.3.6
+  add("OTA-HTTP-PO",                      0,  0xD2);  // Added in OMA, 7.3.6
+  add("OTA-HTTP-TLS-PO",                  0,  0xD3);  // Added in OMA, 7.3.6
+  add("AAA",                              0,  0xE0);  // Added in OMA, 7.3.8
+  add("HA",                               0,  0xE1);  // Added in OMA, 7.3.8
+
+  // Code page 1
+  add("IPV6",                             1,  0x86);
+  add("E164",                             1,  0x87);
+  add("ALPHA",                            1,  0x88);
+  add("APPSRV",                           1,  0x8D);
+  add("OBEX",                             1,  0x8E);
+  add(",",                                1,  0x90);
+  add("HTTP-",                            1,  0x91);
+  add("BASIC",                            1,  0x92);
+  add("DIGEST",                           1,  0x93);
 
   return names;
 })();
 
 let debug;
 if (DEBUG) {
   debug = function (s) {
     dump("-$- CpPduHelper: " + s + "\n");
--- a/dom/wappush/src/gonk/SiPduHelper.jsm
+++ b/dom/wappush/src/gonk/SiPduHelper.jsm
@@ -129,80 +129,89 @@ this.PduHelper = {
 
 /**
  * Tag tokens
  *
  * @see WAP-167-SERVICEIND-20010731-A, clause 8.3.1
  */
 const SI_TAG_FIELDS = (function () {
   let names = {};
-  function add(name, number) {
+  function add(name, codepage, number) {
     let entry = {
       name: name,
       number: number,
     };
-    names[number] = entry;
+    if (!names[codepage]) {
+      names[codepage] = {};
+    }
+    names[codepage][number] = entry;
   }
 
-  add("si",           0x05);
-  add("indication",   0x06);
-  add("info",         0x07);
-  add("item",         0x08);
+  add("si",           0,  0x05);
+  add("indication",   0,  0x06);
+  add("info",         0,  0x07);
+  add("item",         0,  0x08);
 
   return names;
 })();
 
 /**
  * Attribute Tokens
  *
  * @see WAP-167-SERVICEIND-20010731-A, clause 8.3.2
  */
 const SI_ATTRIBUTE_FIELDS = (function () {
   let names = {};
-  function add(name, value, number) {
+  function add(name, value, codepage, number) {
     let entry = {
       name: name,
       value: value,
       number: number,
     };
-    names[number] = entry;
+    if (!names[codepage]) {
+      names[codepage] = {};
+    }
+    names[codepage][number] = entry;
   }
 
-  add("action",       "signal-none",    0x05);
-  add("action",       "signal-low",     0x06);
-  add("action",       "signal-medium",  0x07);
-  add("action",       "signal-high",    0x08);
-  add("action",       "delete",         0x09);
-  add("created",      "",               0x0A);
-  add("href",         "",               0x0B);
-  add("href",         "http://",        0x0C);
-  add("href",         "http://www.",    0x0D);
-  add("href",         "https://",       0x0E);
-  add("href",         "https://www.",   0x0F);
-  add("si-expires",   "",               0x10);
-  add("si-id",        "",               0x11);
-  add("class",        "",               0x12);
+  add("action",       "signal-none",    0,  0x05);
+  add("action",       "signal-low",     0,  0x06);
+  add("action",       "signal-medium",  0,  0x07);
+  add("action",       "signal-high",    0,  0x08);
+  add("action",       "delete",         0,  0x09);
+  add("created",      "",               0,  0x0A);
+  add("href",         "",               0,  0x0B);
+  add("href",         "http://",        0,  0x0C);
+  add("href",         "http://www.",    0,  0x0D);
+  add("href",         "https://",       0,  0x0E);
+  add("href",         "https://www.",   0,  0x0F);
+  add("si-expires",   "",               0,  0x10);
+  add("si-id",        "",               0,  0x11);
+  add("class",        "",               0,  0x12);
 
   return names;
 })();
 
 const SI_VALUE_FIELDS = (function () {
   let names = {};
-  function add(value, number) {
+  function add(value, codepage, number) {
     let entry = {
       value: value,
       number: number,
     };
-    names[number] = entry;
+    if (!names[codepage]) {
+      names[codepage] = {};
+    }
+    names[codepage][number] = entry;
   }
 
-  add(".com/",          0x85);
-  add(".edu/",          0x86);
-  add(".net/",          0x87);
-  add(".org/",          0x88);
+  add(".com/",    0,    0x85);
+  add(".edu/",    0,    0x86);
+  add(".net/",    0,    0x87);
+  add(".org/",    0,    0x88);
 
   return names;
 })();
 
 this.EXPORTED_SYMBOLS = [
   // Parser
   "PduHelper",
 ];
--- a/dom/wappush/src/gonk/SlPduHelper.jsm
+++ b/dom/wappush/src/gonk/SlPduHelper.jsm
@@ -85,71 +85,80 @@ this.PduHelper = {
 
 /**
  * Tag tokens
  *
  * @see WAP-168-SERVICELOAD-20010731-A, clause 9.3.1
  */
 const SL_TAG_FIELDS = (function () {
   let names = {};
-  function add(name, number) {
+  function add(name, codepage, number) {
     let entry = {
       name: name,
       number: number,
     };
-    names[number] = entry;
+    if (!names[codepage]) {
+      names[codepage] = {};
+    }
+    names[codepage][number] = entry;
   }
 
-  add("sl",           0x05);
+  add("sl",       0,  0x05);
 
   return names;
 })();
 
 /**
  * Attribute Tokens
  *
  * @see WAP-168-SERVICELOAD-20010731-A, clause 9.3.2
  */
 const SL_ATTRIBUTE_FIELDS = (function () {
   let names = {};
-  function add(name, value, number) {
+  function add(name, value, codepage, number) {
     let entry = {
       name: name,
       value: value,
       number: number,
     };
-    names[number] = entry;
+    if (!names[codepage]) {
+      names[codepage] = {};
+    }
+    names[codepage][number] = entry;
   }
 
-  add("action",       "execute-low",    0x05);
-  add("action",       "execute-high",   0x06);
-  add("action",       "cache",          0x07);
-  add("href",         "",               0x08);
-  add("href",         "http://",        0x09);
-  add("href",         "http://www.",    0x0A);
-  add("href",         "https://",       0x0B);
-  add("href",         "https://www.",   0x0C);
+  add("action",       "execute-low",    0,  0x05);
+  add("action",       "execute-high",   0,  0x06);
+  add("action",       "cache",          0,  0x07);
+  add("href",         "",               0,  0x08);
+  add("href",         "http://",        0,  0x09);
+  add("href",         "http://www.",    0,  0x0A);
+  add("href",         "https://",       0,  0x0B);
+  add("href",         "https://www.",   0,  0x0C);
 
   return names;
 })();
 
 const SL_VALUE_FIELDS = (function () {
   let names = {};
-  function add(value, number) {
+  function add(value, codepage, number) {
     let entry = {
       value: value,
       number: number,
     };
-    names[number] = entry;
+    if (!names[codepage]) {
+      names[codepage] = {};
+    }
+    names[codepage][number] = entry;
   }
 
-  add(".com/",          0x85);
-  add(".edu/",          0x86);
-  add(".net/",          0x87);
-  add(".org/",          0x88);
+  add(".com/",      0,  0x85);
+  add(".edu/",      0,  0x86);
+  add(".net/",      0,  0x87);
+  add(".org/",      0,  0x88);
 
   return names;
 })();
 
 this.EXPORTED_SYMBOLS = [
   // Parser
   "PduHelper",
 ];
--- a/dom/wappush/src/gonk/WapPushManager.js
+++ b/dom/wappush/src/gonk/WapPushManager.js
@@ -57,18 +57,18 @@ this.WapPushManager = {
       debug("options: " + JSON.stringify(options));
     } catch (ex) {
       debug("Failed to parse sessionless WSP PDU: " + ex.message);
       return;
     }
 
     let appid = options.headers["x-wap-application-id"];
     if (!appid) {
+      // Assume message without applicatioin ID is WAP Push
       debug("Push message doesn't contains X-Wap-Application-Id.");
-      return;
     }
 
     // MMS
     if (appid == "x-wap-application:mms.ua") {
       let mmsService = Cc["@mozilla.org/mms/rilmmsservice;1"]
                        .getService(Ci.nsIMmsService);
       mmsService.QueryInterface(Ci.nsIWapPushApplication)
                 .receiveWapPush(data.array, data.array.length, data.offset, options);
--- a/dom/wappush/src/gonk/WbxmlPduHelper.jsm
+++ b/dom/wappush/src/gonk/WbxmlPduHelper.jsm
@@ -18,32 +18,75 @@ const TAG_TOKEN_ATTR_MASK     = 0x80;
 const TAG_TOKEN_CONTENT_MASK  = 0x40;
 const TAG_TOKEN_VALUE_MASK    = 0x3F;
 
 /**
  * Global tokens
  *
  * @see WAP-192-WBXML-20010725-A, clause 7.1
  */
+const CODE_PAGE_SWITCH_TOKEN  = 0x00;
 const TAG_END_TOKEN           = 0x01;
 const INLINE_STRING_TOKEN     = 0x03;
 const STRING_TABLE_TOKEN      = 0x83;
 const OPAQUE_TOKEN            = 0xC3;
 
 // Set to true to enable debug message on all WBXML decoders.
 this.DEBUG_ALL = false;
 
 // Enable debug message for WBXML decoder core.
 this.DEBUG = DEBUG_ALL | false;
 
 /**
+ * Handle WBXML code page switch.
+ *
+ * @param data
+ *        A wrapped object containing raw PDU data.
+ * @param decodeInfo
+ *        Internal information for decode process.
+ *
+ * @see WAP-192-WBXML-20010725-A, clause 5.8.4.7.2 and 5.8.1
+ */
+this.WbxmlCodePageSwitch = {
+  decode: function decode_wbxml_code_page_switch(data, decodeInfo) {
+    let codePage = WSP.Octet.decode(data);
+
+    if (decodeInfo.currentState === "tag") {
+      decodeInfo.currentTokenList.tag = decodeInfo.tokenList.tag[codePage];
+
+      if (!decodeInfo.currentTokenList.tag) {
+        throw new Error("Invalid tag code page: " + codePage + ".");
+      }
+
+      return "";
+    }
+
+    if (decodeInfo.currentState === "attr") {
+      decodeInfo.currentTokenList.attr = decodeInfo.tokenList.attr[codePage];
+      decodeInfo.currentTokenList.value = decodeInfo.tokenList.value[codePage];
+
+      if (!decodeInfo.currentTokenList.attr ||
+          !decodeInfo.currentTokenList.value) {
+        throw new Error("Invalid attr code page: " + codePage + ".");
+      }
+
+      return "";
+    }
+
+    throw new Error("Invalid decoder state: " + decodeInfo.currentState + ".");
+  },
+};
+
+/**
  * Parse end WBXML encoded message.
  *
  * @param data
  *        A wrapped object containing raw PDU data.
+ * @param decodeInfo
+ *        Internal information for decode process.
  *
  * @see WAP-192-WBXML-20010725-A, clause 5.8.4.7.1
  *
  */
 this.WbxmlEnd = {
   decode: function decode_wbxml_end(data, decodeInfo) {
     let tagInfo = decodeInfo.tagStack.pop();
     return "</" + tagInfo.name + ">";
@@ -79,16 +122,18 @@ this.WbxmlStringTable = {
   }
 };
 
 /**
  * Parse inline string in WBXML encoded message.
  *
  * @param data
  *        A wrapped object containing raw PDU data.
+ * @param decodeInfo
+ *        Internal information for decode process.
  *
  * @see WAP-192-WBXML-20010725-A, clause 5.8.4.1
  *
  */
 this.WbxmlInlineString = {
   decode: function decode_wbxml_inline_string(data, decodeInfo) {
     let charCode = WSP.Octet.decode(data);
     let stringData = [];
@@ -101,16 +146,18 @@ this.WbxmlInlineString = {
   },
 };
 
 /**
  * Parse inline Opaque data in WBXML encoded message.
  *
  * @param data
  *        A wrapped object containing raw PDU data.
+ * @param decodeInfo
+ *        Internal information for decode process.
  *
  * @see WAP-192-WBXML-20010725-A, clause 5.8.4.6
  *
  */
 this.WbxmlOpaque = {
   decode: function decode_wbxml_inline_opaque(data) {
     // Inline OPAQUE must be decoded based on application definition,
     // so it's illegal to run into OPAQUE type in general decoder.
@@ -130,84 +177,99 @@ this.PduHelper = {
    * @param appToken
    *        Application-specific token difinition.
    *
    * @return Decoded WBXML message string.
    */
   parseWbxml: function parseWbxml_wbxml(data, decodeInfo, appToken) {
 
     // Parse token definition to my structure.
-    let tagTokenList = appToken.tagTokenList;
-    let attrTokenList = appToken.attrTokenList;
-    let valueTokenList = appToken.valueTokenList;
-
-    decodeInfo.tagStack = [];    // tag decode stack
+    decodeInfo.tokenList = {
+      tag: appToken.tagTokenList,
+      attr: appToken.attrTokenList,
+      value: appToken.valueTokenList
+    };
+    decodeInfo.tagStack = [];   // tag decode stack
+    decodeInfo.currentTokenList = {
+      tag: decodeInfo.tokenList.tag[0],
+      attr: decodeInfo.tokenList.attr[0],
+      value: decodeInfo.tokenList.value[0]
+    };
+    decodeInfo.currentState = "tag";  // Current decoding state, "tag" or "attr"
+                                      // Used to read corresponding code page
+                                      // initial state : "tag"
 
     // Merge global tag tokens into single list, so we don't have
     // to search two lists every time.
     let globalTagTokenList = Object.create(WBXML_GLOBAL_TOKENS);
     if (appToken.globalTokenOverride) {
       let globalTokenOverrideList = appToken.globalTokenOverride;
       for (let token in globalTokenOverrideList) {
         globalTagTokenList[token] = globalTokenOverrideList[token];
       }
     }
 
     let content = "";
     while (data.offset < data.array.length) {
       // Decode content, might be a new tag token, an end of tag token, or an
       // inline string.
 
+      // Switch to tag domain
+      decodeInfo.currentState = "tag";
+
       let tagToken = WSP.Octet.decode(data);
       let tagTokenValue = tagToken & TAG_TOKEN_VALUE_MASK;
 
       // Try global tag first, tagToken of string table is 0x83, and will be 0x03
       // in tagTokenValue, which is collision with inline string.
       // So tagToken need to be searched before tagTokenValue.
       let tagInfo = globalTagTokenList[tagToken] ||
                     globalTagTokenList[tagTokenValue];
       if (tagInfo) {
         content += tagInfo.coder.decode(data, decodeInfo);
         continue;
       }
 
       // Check if application tag token is valid
-      tagInfo = tagTokenList[tagTokenValue];
+      tagInfo = decodeInfo.currentTokenList.tag[tagTokenValue];
       if (!tagInfo) {
         throw new Error("Unsupported WBXML token: " + tagTokenValue + ".");
       }
 
       content += "<" + tagInfo.name;
 
       if (tagToken & TAG_TOKEN_ATTR_MASK) {
         // Decode attributes, might be a new attribute token, a value token,
         // or an inline string
 
+        // Switch to attr/value domain
+        decodeInfo.currentState = "attr";
+
         let attrSeperator = "";
         while (data.offset < data.array.length) {
           let attrToken = WSP.Octet.decode(data);
           if (attrToken === TAG_END_TOKEN) {
             break;
           }
 
           let attrInfo = globalTagTokenList[attrToken];
           if (attrInfo) {
             content += attrInfo.coder.decode(data, decodeInfo);
             continue;
           }
 
           // Check if attribute token is valid
-          attrInfo = attrTokenList[attrToken];
+          attrInfo = decodeInfo.currentTokenList.attr[attrToken];
           if (attrInfo) {
             content += attrSeperator + " " + attrInfo.name + "=\"" + attrInfo.value;
             attrSeperator = "\"";
             continue;
           }
 
-          attrInfo = valueTokenList[attrToken];
+          attrInfo = decodeInfo.currentTokenList.value[attrToken];
           if (attrInfo) {
             content += attrInfo.value;
             continue;
           }
 
           throw new Error("Unsupported WBXML token: " + attrToken + ".");
         }
 
@@ -320,20 +382,21 @@ const WBXML_GLOBAL_TOKENS = (function ()
   function add(number, coder) {
     let entry = {
       number: number,
       coder: coder,
     };
     names[number] = entry;
   }
 
-  add(TAG_END_TOKEN,        WbxmlEnd);
-  add(INLINE_STRING_TOKEN,  WbxmlInlineString);
-  add(STRING_TABLE_TOKEN,   WbxmlStringTable);
-  add(OPAQUE_TOKEN,         WbxmlOpaque);
+  add(CODE_PAGE_SWITCH_TOKEN, WbxmlCodePageSwitch);
+  add(TAG_END_TOKEN,          WbxmlEnd);
+  add(INLINE_STRING_TOKEN,    WbxmlInlineString);
+  add(STRING_TABLE_TOKEN,     WbxmlStringTable);
+  add(OPAQUE_TOKEN,           WbxmlOpaque);
 
   return names;
 })();
 
 /**
  *  Pre-defined public IDs
  *
  * @see http://technical.openmobilealliance.org/tech/omna/omna-wbxml-public-docid.aspx
--- a/dom/wappush/tests/test_cp_pdu_helper.js
+++ b/dom/wappush/tests/test_cp_pdu_helper.js
@@ -352,8 +352,148 @@ add_test(function test_cp_parse_wbxml() 
 
   test_parser(wbxml_vendor_config_data_array, "application/vnd.wap.connectivity-wbxml", {
                 contentType: "application/vnd.wap.connectivity-wbxml",
                 content: wbxml_vendor_config_data_array
               });
 
   run_next_test();
 });
+
+/**
+ * CP compressed by WBXML with code page switch
+ */
+add_test(function test_cp_parse_wbxml_code_page() {
+  let wbxml_code_page_data_array = new Uint8Array([
+    0x03, 0x0B, 0x6A, 0x00, 0x45, 0xC6, 0x56, 0x01,
+    0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
+    0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x01,
+    0xC6, 0x00, 0x01, 0x55, 0x01, 0x87, 0x36, 0x00,
+    0x00, 0x06, 0x03, 0x77, 0x32, 0x00, 0x01, 0x87,
+    0x00, 0x01, 0x39, 0x00, 0x00, 0x06, 0x03, 0x57,
+    0x50, 0x52, 0x4F, 0x58, 0x59, 0x00, 0x01, 0x87,
+    0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, 0x65,
+    0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0xC6, 0x00,
+    0x01, 0x59, 0x01, 0x87, 0x3A, 0x00, 0x00, 0x06,
+    0x03, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F,
+    0x77, 0x61, 0x70, 0x2E, 0x65, 0x6D, 0x6F, 0x6D,
+    0x65, 0x2E, 0x6E, 0x65, 0x74, 0x2F, 0x00, 0x01,
+    0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
+    0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x87,
+    0x1C, 0x01, 0x01, 0x01, 0xC6, 0x00, 0x01, 0x55,
+    0x01, 0x87, 0x36, 0x00, 0x00, 0x06, 0x03, 0x77,
+    0x34, 0x00, 0x01, 0x87, 0x00, 0x01, 0x39, 0x00,
+    0x00, 0x06, 0x03, 0x4D, 0x50, 0x52, 0x4F, 0x58,
+    0x59, 0x00, 0x01, 0x87, 0x00, 0x01, 0x34, 0x00,
+    0x00, 0x06, 0x03, 0x68, 0x74, 0x74, 0x70, 0x3A,
+    0x2F, 0x2F, 0x6D, 0x6D, 0x73, 0x3A, 0x38, 0x30,
+    0x30, 0x32, 0x00, 0x01, 0x01, 0xC6, 0x51, 0x01,
+    0x87, 0x15, 0x06, 0x03, 0x57, 0x50, 0x52, 0x4F,
+    0x58, 0x59, 0x00, 0x01, 0x87, 0x07, 0x06, 0x03,
+    0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F, 0x6D,
+    0x65, 0x00, 0x01, 0x87, 0x1C, 0x06, 0x03, 0x68,
+    0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x61,
+    0x70, 0x2E, 0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x2E,
+    0x6E, 0x65, 0x74, 0x2F, 0x00, 0x01, 0xC6, 0x52,
+    0x01, 0x87, 0x2F, 0x06, 0x03, 0x50, 0x52, 0x4F,
+    0x58, 0x59, 0x31, 0x00, 0x01, 0x87, 0x20, 0x06,
+    0x03, 0x31, 0x30, 0x2E, 0x31, 0x2E, 0x31, 0x2E,
+    0x31, 0x00, 0x01, 0x87, 0x21, 0x06, 0x85, 0x01,
+    0x87, 0x22, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F,
+    0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0xC6,
+    0x53, 0x01, 0x87, 0x23, 0x06, 0x03, 0x38, 0x30,
+    0x38, 0x30, 0x00, 0x01, 0x01, 0x01, 0x01, 0xC6,
+    0x51, 0x01, 0x87, 0x15, 0x06, 0x03, 0x4D, 0x50,
+    0x52, 0x4F, 0x58, 0x59, 0x00, 0x01, 0x87, 0x07,
+    0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D,
+    0x53, 0x00, 0x01, 0xC6, 0x52, 0x01, 0x87, 0x2F,
+    0x06, 0x03, 0x50, 0x52, 0x4F, 0x58, 0x59, 0x32,
+    0x00, 0x01, 0x87, 0x20, 0x06, 0x03, 0x31, 0x30,
+    0x2E, 0x31, 0x2E, 0x31, 0x2E, 0x31, 0x00, 0x01,
+    0x87, 0x21, 0x06, 0x85, 0x01, 0x87, 0x22, 0x06,
+    0x03, 0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D, 0x53,
+    0x00, 0x01, 0xC6, 0x53, 0x01, 0x87, 0x23, 0x06,
+    0x03, 0x38, 0x30, 0x38, 0x30, 0x00, 0x01, 0x01,
+    0x01, 0x01, 0xC6, 0x55, 0x01, 0x87, 0x11, 0x06,
+    0x03, 0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F,
+    0x6D, 0x65, 0x00, 0x01, 0x87, 0x07, 0x06, 0x03,
+    0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F, 0x6D,
+    0x65, 0x00, 0x01, 0x87, 0x10, 0x06, 0xAB, 0x01,
+    0x87, 0x08, 0x06, 0x03, 0x65, 0x6D, 0x6F, 0x6D,
+    0x65, 0x00, 0x01, 0x87, 0x09, 0x06, 0x89, 0x01,
+    0x01, 0xC6, 0x55, 0x01, 0x87, 0x11, 0x06, 0x03,
+    0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D, 0x53, 0x00,
+    0x01, 0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54,
+    0x5F, 0x4D, 0x4D, 0x53, 0x00, 0x01, 0x87, 0x10,
+    0x06, 0xAB, 0x01, 0x87, 0x08, 0x06, 0x03, 0x65,
+    0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x87, 0x09,
+    0x06, 0x89, 0x01, 0x01, 0x01
+  ]);
+  let wbxml_content =
+    "<wap-provisioningdoc>" +
+    "<characteristic type=\"BOOTSTRAP\">" +
+      "<parm name=\"NAME\" value=\"CHT_emome\"/>" +
+    "</characteristic>" +
+    "<characteristic type=\"APPLICATION\">" +
+      "<parm name=\"APPID\" value=\"w2\"/>" +
+      "<parm name=\"TO-PROXY\" value=\"WPROXY\"/>" +
+      "<parm name=\"NAME\" value=\"CHT_emome\"/>" +
+      "<characteristic type=\"RESOURCE\">" +
+        "<parm name=\"URI\" value=\"http://wap.emome.net/\"/>" +
+        "<parm name=\"NAME\" value=\"CHT_emome\"/>" +
+        "<parm name=\"STARTPAGE\"/>" +
+      "</characteristic>" +
+    "</characteristic>" +
+    "<characteristic type=\"APPLICATION\">" +
+      "<parm name=\"APPID\" value=\"w4\"/>" +
+      "<parm name=\"TO-PROXY\" value=\"MPROXY\"/>" +
+      "<parm name=\"ADDR\" value=\"http://mms:8002\"/>" +
+    "</characteristic>" +
+    "<characteristic type=\"PXLOGICAL\">" +
+      "<parm name=\"PROXY-ID\" value=\"WPROXY\"/>" +
+      "<parm name=\"NAME\" value=\"CHT_emome\"/>" +
+      "<parm name=\"STARTPAGE\" value=\"http://wap.emome.net/\"/>" +
+      "<characteristic type=\"PXPHYSICAL\">" +
+        "<parm name=\"PHYSICAL-PROXY-ID\" value=\"PROXY1\"/>" +
+        "<parm name=\"PXADDR\" value=\"10.1.1.1\"/>" +
+        "<parm name=\"PXADDRTYPE\" value=\"IPV4\"/>" +
+        "<parm name=\"TO-NAPID\" value=\"CHT_emome\"/>" +
+        "<characteristic type=\"PORT\">" +
+          "<parm name=\"PORTNBR\" value=\"8080\"/>" +
+        "</characteristic>" +
+      "</characteristic>" +
+    "</characteristic>" +
+    "<characteristic type=\"PXLOGICAL\">" +
+      "<parm name=\"PROXY-ID\" value=\"MPROXY\"/>" +
+      "<parm name=\"NAME\" value=\"CHT_MMS\"/>" +
+      "<characteristic type=\"PXPHYSICAL\">" +
+        "<parm name=\"PHYSICAL-PROXY-ID\" value=\"PROXY2\"/>" +
+        "<parm name=\"PXADDR\" value=\"10.1.1.1\"/>" +
+        "<parm name=\"PXADDRTYPE\" value=\"IPV4\"/>" +
+        "<parm name=\"TO-NAPID\" value=\"CHT_MMS\"/>" +
+        "<characteristic type=\"PORT\">" +
+          "<parm name=\"PORTNBR\" value=\"8080\"/>" +
+        "</characteristic>" +
+      "</characteristic>" +
+    "</characteristic>" +
+    "<characteristic type=\"NAPDEF\">" +
+      "<parm name=\"NAPID\" value=\"CHT_emome\"/>" +
+      "<parm name=\"NAME\" value=\"CHT_emome\"/>" +
+      "<parm name=\"BEARER\" value=\"GSM-GPRS\"/>" +
+      "<parm name=\"NAP-ADDRESS\" value=\"emome\"/>" +
+      "<parm name=\"NAP-ADDRTYPE\" value=\"APN\"/>" +
+    "</characteristic>" +
+    "<characteristic type=\"NAPDEF\">" +
+      "<parm name=\"NAPID\" value=\"CHT_MMS\"/>" +
+      "<parm name=\"NAME\" value=\"CHT_MMS\"/>" +
+      "<parm name=\"BEARER\" value=\"GSM-GPRS\"/>" +
+      "<parm name=\"NAP-ADDRESS\" value=\"emome\"/>" +
+      "<parm name=\"NAP-ADDRTYPE\" value=\"APN\"/>" +
+    "</characteristic>" +
+    "</wap-provisioningdoc>";
+
+  test_parser(wbxml_code_page_data_array, "application/vnd.wap.connectivity-wbxml", {
+                contentType: "text/vnd.wap.connectivity-xml",
+                content: wbxml_content
+              });
+
+  run_next_test();
+});
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -9,20 +9,26 @@
 
 namespace mozilla {
 namespace layers {
 
 /* static */ LayersBackend Compositor::sBackend = LAYERS_NONE;
 /* static */ LayersBackend
 Compositor::GetBackend()
 {
+  AssertOnCompositorThread();
+  return sBackend;
+}
+
+/* static */ void
+Compositor::AssertOnCompositorThread()
+{
   MOZ_ASSERT(CompositorParent::CompositorLoop() ==
              MessageLoop::current(),
              "Can only call this from the compositor thread!");
-  return sBackend;
 }
 
 void
 Compositor::DrawDiagnostics(const gfx::Color& aColor,
                             const gfx::Rect& rect,
                             const gfx::Rect& aClipRect,
                             const gfx::Matrix4x4& aTransform,
                             const gfx::Point& aOffset)
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -394,16 +394,22 @@ public:
   virtual bool Resume() { return true; }
 
   // XXX I expect we will want to move mWidget into this class and implement
   // these methods properly.
   virtual nsIWidget* GetWidget() const { return nullptr; }
   virtual const nsIntSize& GetWidgetSize() = 0;
 
   /**
+   * Debug-build assertion that can be called to ensure code is running on the
+   * compositor thread.
+   */
+  static void AssertOnCompositorThread();
+
+  /**
    * We enforce that there can only be one Compositor backend type off the main
    * thread at the same time. The backend type in use can be checked with this
    * static method. We need this for creating texture clients/hosts etc. when we
    * don't have a reference to a Compositor.
    *
    * This can only be used from the compositor thread!
    */
   static LayersBackend GetBackend();
new file mode 100644
--- /dev/null
+++ b/gfx/layers/composite/APZCTreeManager.cpp
@@ -0,0 +1,386 @@
+/* -*- Mode: C++; tab-width: 8; 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 "APZCTreeManager.h"
+#include "AsyncCompositionManager.h"    // for ViewTransform
+#include "LayerManagerComposite.h"      // for AsyncCompositionManager.h
+#include "Compositor.h"
+
+#define APZC_LOG(...)
+// #define APZC_LOG(args...) printf_stderr(args)
+
+namespace mozilla {
+namespace layers {
+
+APZCTreeManager::APZCTreeManager()
+    : mTreeLock("APZCTreeLock")
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  AsyncPanZoomController::InitializeGlobalState();
+}
+
+void
+APZCTreeManager::AssertOnCompositorThread()
+{
+  Compositor::AssertOnCompositorThread();
+}
+
+/* Flatten the tree of APZC instances into the given nsTArray */
+static void
+Collect(AsyncPanZoomController* aApzc, nsTArray< nsRefPtr<AsyncPanZoomController> >* aCollection)
+{
+  if (aApzc) {
+    aCollection->AppendElement(aApzc);
+    Collect(aApzc->GetLastChild(), aCollection);
+    Collect(aApzc->GetPrevSibling(), aCollection);
+  }
+}
+
+void
+APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor, Layer* aRoot,
+                                             bool aIsFirstPaint, uint64_t aFirstPaintLayersId)
+{
+  AssertOnCompositorThread();
+
+  MonitorAutoLock lock(mTreeLock);
+
+  // We do this business with collecting the entire tree into an array because otherwise
+  // it's very hard to determine which APZC instances need to be destroyed. In the worst
+  // case, there are two scenarios: (a) a layer with an APZC is removed from the layer
+  // tree and (b) a layer with an APZC is moved in the layer tree from one place to a
+  // completely different place. In scenario (a) we would want to destroy the APZC while
+  // walking the layer tree and noticing that the layer/APZC is no longer there. But if
+  // we do that then we run into a problem in scenario (b) because we might encounter that
+  // layer later during the walk. To handle both of these we have to 'remember' that the
+  // layer was not found, and then do the destroy only at the end of the tree walk after
+  // we are sure that the layer was removed and not just transplanted elsewhere. Doing that
+  // as part of a recursive tree walk is hard and so maintaining a list and removing
+  // APZCs that are still alive is much simpler.
+  nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
+  Collect(mRootApzc, &apzcsToDestroy);
+  mRootApzc = nullptr;
+
+  if (aRoot) {
+    UpdatePanZoomControllerTree(aCompositor,
+                                aRoot,
+                                // aCompositor is null in gtest scenarios
+                                aCompositor ? aCompositor->RootLayerTreeId() : 0,
+                                nullptr, nullptr,
+                                aIsFirstPaint, aFirstPaintLayersId,
+                                &apzcsToDestroy);
+  }
+
+  for (int i = apzcsToDestroy.Length() - 1; i >= 0; i--) {
+    APZC_LOG("Destroying APZC at %p\n", apzcsToDestroy[i].get());
+    apzcsToDestroy[i]->Destroy();
+  }
+}
+
+AsyncPanZoomController*
+APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor,
+                                             Layer* aLayer, uint64_t aLayersId,
+                                             AsyncPanZoomController* aParent,
+                                             AsyncPanZoomController* aNextSibling,
+                                             bool aIsFirstPaint, uint64_t aFirstPaintLayersId,
+                                             nsTArray< nsRefPtr<AsyncPanZoomController> >* aApzcsToDestroy)
+{
+  ContainerLayer* container = aLayer->AsContainerLayer();
+  AsyncPanZoomController* controller = nullptr;
+  if (container) {
+    if (container->GetFrameMetrics().IsScrollable()) {
+      const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
+      if (state && state->mController.get()) {
+        // If we get here, aLayer is a scrollable container layer and somebody
+        // has registered a GeckoContentController for it, so we need to ensure
+        // it has an APZC instance to manage its scrolling.
+
+        controller = container->GetAsyncPanZoomController();
+        if (!controller) {
+          controller = new AsyncPanZoomController(aLayersId, state->mController,
+                                                  AsyncPanZoomController::USE_GESTURE_DETECTOR);
+          controller->SetCompositorParent(aCompositor);
+        } else {
+          // If there was already an APZC for the layer clear the tree pointers
+          // so that it doesn't continue pointing to APZCs that should no longer
+          // be in the tree. These pointers will get reset properly as we continue
+          // building the tree. Also remove it from the set of APZCs that are going
+          // to be destroyed, because it's going to remain active.
+          aApzcsToDestroy->RemoveElement(controller);
+          controller->SetPrevSibling(nullptr);
+          controller->SetLastChild(nullptr);
+        }
+        APZC_LOG("Using APZC %p for layer %p with identifiers %lld %lld\n", controller, aLayer, aLayersId, container->GetFrameMetrics().mScrollId);
+
+        controller->NotifyLayersUpdated(container->GetFrameMetrics(),
+                                        aIsFirstPaint && (aLayersId == aFirstPaintLayersId));
+
+        gfx3DMatrix transform = container->GetEffectiveTransform();
+        LayerRect visible = container->GetFrameMetrics().mViewport * container->GetFrameMetrics().LayersPixelsPerCSSPixel();
+        gfxRect transformed = transform.TransformBounds(gfxRect(visible.x, visible.y, visible.width, visible.height));
+        controller->SetVisibleRegion(transformed);
+        APZC_LOG("Setting rect(%f %f %f %f) as visible region for %p\n", transformed.x, transformed.y,
+                                                                         transformed.width, transformed.height,
+                                                                         controller);
+
+        // Bind the APZC instance into the tree of APZCs
+        if (aNextSibling) {
+          aNextSibling->SetPrevSibling(controller);
+        } else if (aParent) {
+          aParent->SetLastChild(controller);
+        } else {
+          mRootApzc = controller;
+        }
+
+        // Let this controller be the parent of other controllers when we recurse downwards
+        aParent = controller;
+      }
+    }
+
+    container->SetAsyncPanZoomController(controller);
+  }
+
+  uint64_t childLayersId = (aLayer->AsRefLayer() ? aLayer->AsRefLayer()->GetReferentId() : aLayersId);
+  AsyncPanZoomController* next = nullptr;
+  for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) {
+    next = UpdatePanZoomControllerTree(aCompositor, child, childLayersId, aParent, next,
+                                       aIsFirstPaint, aFirstPaintLayersId, aApzcsToDestroy);
+  }
+
+  // Return the APZC that should be the sibling of other APZCs as we continue
+  // moving towards the first child at this depth in the layer tree.
+  // If this layer doesn't have a controller, we promote any APZCs in the subtree
+  // upwards. Otherwise we fall back to the aNextSibling that was passed in.
+  if (controller) {
+    return controller;
+  }
+  if (next) {
+    return next;
+  }
+  return aNextSibling;
+}
+
+nsEventStatus
+APZCTreeManager::ReceiveInputEvent(const InputData& aEvent)
+{
+  nsRefPtr<AsyncPanZoomController> apzc;
+  switch (aEvent.mInputType) {
+    case MULTITOUCH_INPUT: {
+      const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
+      apzc = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[0].mScreenPoint));
+      break;
+    } case PINCHGESTURE_INPUT: {
+      const PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput();
+      apzc = GetTargetAPZC(pinchInput.mFocusPoint);
+      break;
+    } case TAPGESTURE_INPUT: {
+      const TapGestureInput& tapInput = aEvent.AsTapGestureInput();
+      apzc = GetTargetAPZC(ScreenPoint(tapInput.mPoint));
+      break;
+    } default: {
+      // leave apzc as nullptr
+      break;
+    }
+  }
+  if (apzc) {
+    return apzc->ReceiveInputEvent(aEvent);
+  }
+  return nsEventStatus_eIgnore;
+}
+
+nsEventStatus
+APZCTreeManager::ReceiveInputEvent(const nsInputEvent& aEvent,
+                                   nsInputEvent* aOutEvent)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsRefPtr<AsyncPanZoomController> apzc;
+  switch (aEvent.eventStructType) {
+    case NS_TOUCH_EVENT: {
+      const nsTouchEvent& touchEvent = static_cast<const nsTouchEvent&>(aEvent);
+      if (touchEvent.touches.Length() > 0) {
+        nsIntPoint point = touchEvent.touches[0]->mRefPoint;
+        apzc = GetTargetAPZC(ScreenPoint::FromUnknownPoint(gfx::Point(point.x, point.y)));
+      }
+      break;
+    } case NS_MOUSE_EVENT: {
+      const nsMouseEvent& mouseEvent = static_cast<const nsMouseEvent&>(aEvent);
+      apzc = GetTargetAPZC(ScreenPoint::FromUnknownPoint(gfx::Point(mouseEvent.refPoint.x,
+                                                                    mouseEvent.refPoint.y)));
+      break;
+    } default: {
+      // leave apzc as nullptr
+      break;
+    }
+  }
+  if (apzc) {
+    return apzc->ReceiveInputEvent(aEvent, aOutEvent);
+  }
+  return nsEventStatus_eIgnore;
+}
+
+void
+APZCTreeManager::UpdateCompositionBounds(const ScrollableLayerGuid& aGuid,
+                                         const ScreenIntRect& aCompositionBounds)
+{
+  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+  if (apzc) {
+    apzc->UpdateCompositionBounds(aCompositionBounds);
+  }
+}
+
+void
+APZCTreeManager::CancelDefaultPanZoom(const ScrollableLayerGuid& aGuid)
+{
+  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+  if (apzc) {
+    apzc->CancelDefaultPanZoom();
+  }
+}
+
+void
+APZCTreeManager::DetectScrollableSubframe(const ScrollableLayerGuid& aGuid)
+{
+  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+  if (apzc) {
+    apzc->DetectScrollableSubframe();
+  }
+}
+
+void
+APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
+                            const CSSRect& aRect)
+{
+  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+  if (apzc) {
+    apzc->ZoomToRect(aRect);
+  }
+}
+
+void
+APZCTreeManager::ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
+                                      bool aPreventDefault)
+{
+  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+  if (apzc) {
+    apzc->ContentReceivedTouch(aPreventDefault);
+  }
+}
+
+void
+APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
+                                       bool aAllowZoom,
+                                       float aMinScale,
+                                       float aMaxScale)
+{
+  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+  if (apzc) {
+    apzc->UpdateZoomConstraints(aAllowZoom, aMinScale, aMaxScale);
+  }
+}
+
+void
+APZCTreeManager::UpdateScrollOffset(const ScrollableLayerGuid& aGuid,
+                                    const CSSPoint& aScrollOffset)
+{
+  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+  if (apzc) {
+    apzc->UpdateScrollOffset(aScrollOffset);
+  }
+}
+
+void
+APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid)
+{
+  nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+  if (apzc) {
+    apzc->CancelAnimation();
+  }
+}
+
+void
+APZCTreeManager::ClearTree()
+{
+  MonitorAutoLock lock(mTreeLock);
+
+  // This can be done as part of a tree walk but it's easier to
+  // just re-use the Collect method that we need in other places.
+  // If this is too slow feel free to change it to a recursive walk.
+  nsTArray< nsRefPtr<AsyncPanZoomController> > apzcsToDestroy;
+  Collect(mRootApzc, &apzcsToDestroy);
+  for (int i = apzcsToDestroy.Length() - 1; i >= 0; i--) {
+    apzcsToDestroy[i]->Destroy();
+  }
+  mRootApzc = nullptr;
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid)
+{
+  MonitorAutoLock lock(mTreeLock);
+  nsRefPtr<AsyncPanZoomController> target;
+  // The root may have siblings, check those too
+  for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
+    target = FindTargetAPZC(apzc, aGuid);
+    if (target) {
+      break;
+    }
+  }
+  return target.forget();
+}
+
+already_AddRefed<AsyncPanZoomController>
+APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint)
+{
+  MonitorAutoLock lock(mTreeLock);
+  nsRefPtr<AsyncPanZoomController> target;
+  // The root may have siblings, so check those too
+  gfxPoint point(aPoint.x, aPoint.y);
+  for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) {
+    target = GetAPZCAtPoint(apzc, point);
+    if (target) {
+      break;
+    }
+  }
+  return target.forget();
+}
+
+AsyncPanZoomController*
+APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid) {
+  // This walks the tree in depth-first, reverse order, so that it encounters
+  // APZCs front-to-back on the screen.
+  for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
+    AsyncPanZoomController* match = FindTargetAPZC(child, aGuid);
+    if (match) {
+      return match;
+    }
+  }
+
+  if (aApzc->Matches(aGuid)) {
+    return aApzc;
+  }
+  return nullptr;
+}
+
+AsyncPanZoomController*
+APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc, gfxPoint aHitTestPoint)
+{
+  // This walks the tree in depth-first, reverse order, so that it encounters
+  // APZCs front-to-back on the screen.
+  ViewTransform apzcTransform = aApzc->GetCurrentAsyncTransform();
+  gfxPoint untransformed = gfx3DMatrix(apzcTransform).Inverse().ProjectPoint(aHitTestPoint);
+  for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) {
+    AsyncPanZoomController* match = GetAPZCAtPoint(child, untransformed);
+    if (match) {
+      return match;
+    }
+  }
+  if (aApzc->VisibleRegionContains(untransformed)) {
+    return aApzc;
+  }
+  return nullptr;
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/composite/APZCTreeManager.h
@@ -0,0 +1,275 @@
+/* -*- Mode: C++; tab-width: 8; 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 mozilla_layers_APZCTreeManager_h
+#define mozilla_layers_APZCTreeManager_h
+
+#include "mozilla/layers/AsyncPanZoomController.h"
+#include "Layers.h"
+#include "CompositorParent.h"
+
+namespace mozilla {
+namespace layers {
+
+class AsyncPanZoomController;
+class CompositorParent;
+
+/**
+ * This class allows us to uniquely identify a scrollable layer. The
+ * mLayersId identifies the layer tree (corresponding to a child process
+ * and/or tab) that the scrollable layer belongs to. The mPresShellId
+ * is a temporal identifier (corresponding to the document loaded that
+ * contains the scrollable layer, which may change over time). The
+ * mScrollId corresponds to the actual frame that is scrollable.
+ */
+struct ScrollableLayerGuid {
+  uint64_t mLayersId;
+  uint32_t mPresShellId;
+  FrameMetrics::ViewID mScrollId;
+
+  ScrollableLayerGuid(uint64_t aLayersId, uint32_t aPresShellId,
+                      FrameMetrics::ViewID aScrollId)
+    : mLayersId(aLayersId)
+    , mPresShellId(aPresShellId)
+    , mScrollId(aScrollId)
+  {
+    MOZ_COUNT_CTOR(ScrollableLayerGuid);
+  }
+
+  ScrollableLayerGuid(uint64_t aLayersId, const FrameMetrics& aMetrics)
+    : mLayersId(aLayersId)
+    , mPresShellId(aMetrics.mPresShellId)
+    , mScrollId(aMetrics.mScrollId)
+  {
+    MOZ_COUNT_CTOR(ScrollableLayerGuid);
+  }
+
+  ScrollableLayerGuid(uint64_t aLayersId)
+    : mLayersId(aLayersId)
+    , mPresShellId(0)
+    , mScrollId(FrameMetrics::ROOT_SCROLL_ID)
+  {
+    MOZ_COUNT_CTOR(ScrollableLayerGuid);
+    // TODO: get rid of this constructor once all callers know their
+    // presShellId and scrollId
+  }
+
+  ~ScrollableLayerGuid()
+  {
+    MOZ_COUNT_DTOR(ScrollableLayerGuid);
+  }
+
+  bool operator==(const ScrollableLayerGuid& other) const
+  {
+    return mLayersId == other.mLayersId
+        && mPresShellId == other.mPresShellId
+        && mScrollId == other.mScrollId;
+  }
+
+  bool operator!=(const ScrollableLayerGuid& other) const
+  {
+    return !(*this == other);
+  }
+};
+
+/**
+ * This class manages the tree of AsyncPanZoomController instances. There is one
+ * instance of this class owned by each CompositorParent, and it contains as
+ * many AsyncPanZoomController instances as there are scrollable container layers.
+ * This class generally lives on the compositor thread, although some functions
+ * may be called from other threads as noted; thread safety is ensured internally.
+ *
+ * The bulk of the work of this class happens as part of the UpdatePanZoomControllerTree
+ * function, which is when a layer tree update is received by the compositor.
+ * This function walks through the layer tree and creates a tree of APZC instances
+ * to match the scrollable container layers. APZC instances may be preserved across
+ * calls to this function if the corresponding layers are still present in the layer
+ * tree.
+ *
+ * The other functions on this class are used by various pieces of client code to
+ * notify the APZC instances of events relevant to them. This includes, for example,
+ * user input events that drive panning and zooming, changes to the scroll viewport
+ * area, and changes to pan/zoom constraints.
+ *
+ * Note that the ClearTree function MUST be called when this class is no longer needed;
+ * see the method documentation for details.
+ */
+class APZCTreeManager {
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZCTreeManager)
+
+public:
+  APZCTreeManager();
+  virtual ~APZCTreeManager() {}
+
+  /**
+   * Rebuild the APZC tree based on the layer update that just came up. Preserve
+   * APZC instances where possible, but retire those whose layers are no longer
+   * in the layer tree.
+   *
+   * This must be called on the compositor thread as it walks the layer tree.
+   *
+   * @param aCompositor A pointer to the compositor parent instance that owns
+   *                    this APZCTreeManager
+   * @param aRoot The root of the (full) layer tree
+   * @param aFirstPaintLayersId The layers id of the subtree to which aIsFirstPaint
+   *                            applies.
+   * @param aIsFirstPaint True if the layers update that this is called in response
+   *                      to included a first-paint. If this is true, the part of
+   *                      the tree that is affected by the first-paint flag is
+   *                      indicated by the aFirstPaintLayersId parameter.
+   */
+  void UpdatePanZoomControllerTree(CompositorParent* aCompositor, Layer* aRoot,
+                                   bool aIsFirstPaint, uint64_t aFirstPaintLayersId);
+
+  /**
+   * General handler for incoming input events. Manipulates the frame metrics
+   * based on what type of input it is. For example, a PinchGestureEvent will
+   * cause scaling. This should only be called externally to this class.
+   * HandleInputEvent() should be used internally.
+   */
+  nsEventStatus ReceiveInputEvent(const InputData& aEvent);
+
+  /**
+   * Special handler for nsInputEvents. Also sets |aOutEvent| (which is assumed
+   * to be an already-existing instance of an nsInputEvent which may be an
+   * nsTouchEvent) to have its touch points in DOM space. This is so that the
+   * touches can be passed through the DOM and content can handle them.
+   *
+   * NOTE: Be careful of invoking the nsInputEvent variant. This can only be
+   * called on the main thread. See widget/InputData.h for more information on
+   * why we have InputData and nsInputEvent separated.
+   */
+  nsEventStatus ReceiveInputEvent(const nsInputEvent& aEvent,
+                                  nsInputEvent* aOutEvent);
+
+  /**
+   * Updates the composition bounds, i.e. the dimensions of the final size of
+   * the frame this is tied to during composition onto, in device pixels. In
+   * general, this will just be:
+   * { x = 0, y = 0, width = surface.width, height = surface.height }, however
+   * there is no hard requirement for this.
+   */
+  void UpdateCompositionBounds(const ScrollableLayerGuid& aGuid,
+                               const ScreenIntRect& aCompositionBounds);
+
+  /**
+   * We are scrolling a subframe, so disable our machinery until we hit
+   * a touch end or a new touch start. This prevents us from accidentally
+   * panning both the subframe and the parent frame.
+   *
+   * XXX/bug 775452: We should eventually be supporting async scrollable
+   * subframes.
+   */
+  void CancelDefaultPanZoom(const ScrollableLayerGuid& aGuid);
+
+  /**
+   * We have found a scrollable subframe, so we need to delay the scrolling
+   * gesture executed and let subframe do the scrolling first.
+   */
+  void DetectScrollableSubframe(const ScrollableLayerGuid& aGuid);
+
+  /**
+   * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
+   * in. The actual animation is done on the compositor thread after being set
+   * up. |aRect| must be given in CSS pixels, relative to the document.
+   */
+  void ZoomToRect(const ScrollableLayerGuid& aGuid,
+                  const CSSRect& aRect);
+
+  /**
+   * If we have touch listeners, this should always be called when we know
+   * definitively whether or not content has preventDefaulted any touch events
+   * that have come in. If |aPreventDefault| is true, any touch events in the
+   * queue will be discarded.
+   */
+  void ContentReceivedTouch(const ScrollableLayerGuid& aGuid,
+                            bool aPreventDefault);
+
+  /**
+   * Updates any zoom constraints contained in the <meta name="viewport"> tag.
+   * We try to obey everything it asks us elsewhere, but here we only handle
+   * minimum-scale, maximum-scale, and user-scalable.
+   */
+  void UpdateZoomConstraints(const ScrollableLayerGuid& aGuid,
+                             bool aAllowZoom,
+                             float aMinScale,
+                             float aMaxScale);
+
+  /**
+   * Update mFrameMetrics.mScrollOffset to the given offset.
+   * This is necessary in cases where a scroll is not caused by user
+   * input (for example, a content scrollTo()).
+   */
+  void UpdateScrollOffset(const ScrollableLayerGuid& aGuid,
+                          const CSSPoint& aScrollOffset);
+
+  /**
+   * Cancels any currently running animation. Note that all this does is set the
+   * state of the AsyncPanZoomController back to NOTHING, but it is the
+   * animation's responsibility to check this before advancing.
+   */
+  void CancelAnimation(const ScrollableLayerGuid &aGuid);
+
+  /**
+   * Calls Destroy() on all APZC instances attached to the tree, and resets the
+   * tree back to empty. This function may be called multiple times during the
+   * lifetime of this APZCTreeManager, but it must always be called at least once
+   * when this APZCTreeManager is no longer needed. Failing to call this function
+   * may prevent objects from being freed properly.
+   */
+  void ClearTree();
+
+protected:
+  /**
+   * Debug-build assertion that can be called to ensure code is running on the
+   * compositor thread.
+   */
+  virtual void AssertOnCompositorThread();
+
+public:
+  /* Some helper functions to find an APZC given some identifying input. These functions
+     lock the tree of APZCs while they find the right one, and then return an addref'd
+     pointer to it. This allows caller code to just use the target APZC without worrying
+     about it going away. These are public for testing code and generally should not be
+     used by other production code.
+  */
+  already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScrollableLayerGuid& aGuid);
+  already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const ScreenPoint& aPoint);
+private:
+  /* Recursive helpers */
+  AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid);
+  AsyncPanZoomController* GetAPZCAtPoint(AsyncPanZoomController* aApzc, gfxPoint aHitTestPoint);
+
+  /**
+   * Recursive helper function to build the APZC tree. The tree of APZC instances has
+   * the same shape as the layer tree, but excludes all the layers that are not scrollable.
+   * Note that this means APZCs corresponding to layers at different depths in the tree
+   * may end up becoming siblings. It also means that the "root" APZC may have siblings.
+   * This function walks the layer tree backwards through siblings and constructs the APZC
+   * tree also as a last-child-prev-sibling tree because that simplifies the hit detection
+   * code.
+   */
+  AsyncPanZoomController* UpdatePanZoomControllerTree(CompositorParent* aCompositor,
+                                                      Layer* aLayer, uint64_t aLayersId,
+                                                      AsyncPanZoomController* aParent,
+                                                      AsyncPanZoomController* aNextSibling,
+                                                      bool aIsFirstPaint,
+                                                      uint64_t aFirstPaintLayersId,
+                                                      nsTArray< nsRefPtr<AsyncPanZoomController> >* aApzcsToDestroy);
+
+private:
+  /* Whenever walking or mutating the tree rooted at mRootApzc, mTreeLock must be held.
+   * This lock does not need to be held while manipulating a single APZC instance in
+   * isolation (that is, if its tree pointers are not being accessed or mutated). The
+   * lock also needs to be held when accessing the mRootApzc instance variable, as that
+   * is considered part of the APZC tree management state. */
+  mozilla::Monitor mTreeLock;
+  nsRefPtr<AsyncPanZoomController> mRootApzc;
+};
+
+}
+}
+
+#endif // mozilla_layers_PanZoomController_h
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -46,38 +46,29 @@ template<Op OP>
 static void
 WalkTheTree(Layer* aLayer,
             bool& aReady,
             const TargetConfig& aTargetConfig)
 {
   if (RefLayer* ref = aLayer->AsRefLayer()) {
     if (const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(ref->GetReferentId())) {
       if (Layer* referent = state->mRoot) {
-        ContainerLayer *referentAsContainer = referent->AsContainerLayer();
         if (!ref->GetVisibleRegion().IsEmpty()) {
           ScreenOrientation chromeOrientation = aTargetConfig.orientation();
           ScreenOrientation contentOrientation = state->mTargetConfig.orientation();
           if (!IsSameDimension(chromeOrientation, contentOrientation) &&
               ContentMightReflowOnOrientationChange(aTargetConfig.clientBounds())) {
             aReady = false;
           }
         }
 
         if (OP == Resolve) {
           ref->ConnectReferentLayer(referent);
-          if (referentAsContainer) {
-            if (AsyncPanZoomController* apzc = state->mController) {
-              referentAsContainer->SetAsyncPanZoomController(apzc);
-            }
-          }
         } else {
           ref->DetachReferentLayer(referent);
-          if (referentAsContainer) {
-            referentAsContainer->SetAsyncPanZoomController(nullptr);
-          }
         }
       }
     }
   }
   for (Layer* child = aLayer->GetFirstChild();
        child; child = child->GetNextSibling()) {
     WalkTheTree<OP>(child, aReady, aTargetConfig);
   }
@@ -432,17 +423,16 @@ AsyncCompositionManager::ApplyAsyncConte
   if (AsyncPanZoomController* controller = container->GetAsyncPanZoomController()) {
     LayerComposite* layerComposite = aLayer->AsLayerComposite();
     gfx3DMatrix oldTransform = aLayer->GetTransform();
 
     ViewTransform treeTransform;
     ScreenPoint scrollOffset;
     *aWantNextFrame |=
       controller->SampleContentTransformForFrame(aCurrentFrame,
-                                                 container,
                                                  &treeTransform,
                                                  scrollOffset);
 
     const gfx3DMatrix& rootTransform = mLayerManager->GetRoot()->GetTransform();
     const FrameMetrics& metrics = container->GetFrameMetrics();
     // XXX We use rootTransform instead of metrics.mResolution here because on
     // Fennec the resolution is set on the root layer rather than the scrollable layer.
     // The SyncFrameMetrics call and the paintScale variable are used on Fennec only
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -101,125 +101,134 @@ static float gYSkateSizeMultiplier = 3.5
 /** The multiplier we apply to a dimension's length if it is stationary. We
  * prefer to increase the size of the Y axis because it is more natural in the
  * case that a user is reading a page that scrolls up/down. Note that one,
  * both or neither of these may be used at any instant.
  */
 static float gXStationarySizeMultiplier = 1.5f;
 static float gYStationarySizeMultiplier = 2.5f;
 
+/**
+ * The time period in ms that throttles mozbrowserasyncscroll event.
+ * Default is 100ms if there is no "apzc.asyncscroll.throttle" in preference.
+ */
+
+static int gAsyncScrollThrottleTime = 100;
+/**
+ * The timeout in ms for mAsyncScrollTimeoutTask delay task.
+ * Default is 300ms if there is no "apzc.asyncscroll.timeout" in preference.
+ */
+static int gAsyncScrollTimeout = 300;
+
 static TimeStamp sFrameTime;
 
 static TimeStamp
 GetFrameTime() {
   if (sFrameTime.IsNull()) {
     return TimeStamp::Now();
   }
   return sFrameTime;
 }
 
 void
 AsyncPanZoomController::SetFrameTime(const TimeStamp& aTime) {
   sFrameTime = aTime;
 }
 
-static void ReadAZPCPrefs()
+/*static*/ void
+AsyncPanZoomController::InitializeGlobalState()
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  static bool sInitialized = false;
+  if (sInitialized)
+    return;
+  sInitialized = true;
+
   Preferences::AddIntVarCache(&gPanRepaintInterval, "gfx.azpc.pan_repaint_interval", gPanRepaintInterval);
   Preferences::AddIntVarCache(&gFlingRepaintInterval, "gfx.azpc.fling_repaint_interval", gFlingRepaintInterval);
   Preferences::AddFloatVarCache(&gMinSkateSpeed, "gfx.azpc.min_skate_speed", gMinSkateSpeed);
   Preferences::AddIntVarCache(&gTouchListenerTimeout, "gfx.azpc.touch_listener_timeout", gTouchListenerTimeout);
   Preferences::AddIntVarCache(&gNumPaintDurationSamples, "gfx.azpc.num_paint_duration_samples", gNumPaintDurationSamples);
   Preferences::AddFloatVarCache(&gTouchStartTolerance, "gfx.azpc.touch_start_tolerance", gTouchStartTolerance);
   Preferences::AddFloatVarCache(&gXSkateSizeMultiplier, "gfx.azpc.x_skate_size_multiplier", gXSkateSizeMultiplier);
   Preferences::AddFloatVarCache(&gYSkateSizeMultiplier, "gfx.azpc.y_skate_size_multiplier", gYSkateSizeMultiplier);
   Preferences::AddFloatVarCache(&gXStationarySizeMultiplier, "gfx.azpc.x_stationary_size_multiplier", gXStationarySizeMultiplier);
   Preferences::AddFloatVarCache(&gYStationarySizeMultiplier, "gfx.azpc.y_stationary_size_multiplier", gYStationarySizeMultiplier);
+  Preferences::AddIntVarCache(&gAsyncScrollThrottleTime, "apzc.asyncscroll.throttle", gAsyncScrollThrottleTime);
+  Preferences::AddIntVarCache(&gAsyncScrollTimeout, "apzc.asyncscroll.timeout", gAsyncScrollTimeout);
+
+  gComputedTimingFunction = new ComputedTimingFunction();
+  gComputedTimingFunction->Init(
+    nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
+  ClearOnShutdown(&gComputedTimingFunction);
 }
 
-class ReadAZPCPref MOZ_FINAL : public nsRunnable {
-public:
-  NS_IMETHOD Run()
-  {
-    ReadAZPCPrefs();
-    return NS_OK;
-  }
-};
-
-static void InitAZPCPrefs()
-{
-  static bool sInitialized = false;
-  if (sInitialized)
-    return;
-
-  sInitialized = true;
-  if (NS_IsMainThread()) {
-    ReadAZPCPrefs();
-  } else {
-    // We have to dispatch an event to the main thread to read the pref.
-    NS_DispatchToMainThread(new ReadAZPCPref());
-  }
-}
-
-AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoContentController,
+AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId,
+                                               GeckoContentController* aGeckoContentController,
                                                GestureBehavior aGestures)
-  :  mPaintThrottler(GetFrameTime()),
+  :  mLayersId(aLayersId),
+     mPaintThrottler(GetFrameTime()),
      mGeckoContentController(aGeckoContentController),
+     mRefPtrMonitor("RefPtrMonitor"),
      mTouchListenerTimeoutTask(nullptr),
      mX(this),
      mY(this),
      mAllowZoom(true),
      mMinZoom(MIN_ZOOM),
      mMaxZoom(MAX_ZOOM),
      mMonitor("AsyncPanZoomController"),
      mLastSampleTime(GetFrameTime()),
      mState(NOTHING),
      mLastAsyncScrollTime(GetFrameTime()),
      mLastAsyncScrollOffset(0, 0),
      mCurrentAsyncScrollOffset(0, 0),
      mAsyncScrollTimeoutTask(nullptr),
-     mAsyncScrollThrottleTime(100),
-     mAsyncScrollTimeout(300),
      mDPI(72),
      mDisableNextTouchBatch(false),
      mHandlingTouchQueue(false),
      mDelayPanning(false)
 {
-  MOZ_ASSERT(NS_IsMainThread());
   MOZ_COUNT_CTOR(AsyncPanZoomController);
 
-  InitAZPCPrefs();
-
   if (aGestures == USE_GESTURE_DETECTOR) {
     mGestureEventListener = new GestureEventListener(this);
   }
 
   SetDPI(mDPI);
-
-  if (!gComputedTimingFunction) {
-    gComputedTimingFunction = new ComputedTimingFunction();
-    gComputedTimingFunction->Init(
-      nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
-    ClearOnShutdown(&gComputedTimingFunction);
-  }
-
-  Preferences::GetUint("apzc.asyncscroll.throttle", &mAsyncScrollThrottleTime);
-  Preferences::GetUint("apzc.asyncscroll.timeout", &mAsyncScrollTimeout);
 }
 
 AsyncPanZoomController::~AsyncPanZoomController() {
   MOZ_COUNT_DTOR(AsyncPanZoomController);
 }
 
+already_AddRefed<GeckoContentController>
+AsyncPanZoomController::GetGeckoContentController() {
+  MonitorAutoLock lock(mRefPtrMonitor);
+  nsRefPtr<GeckoContentController> controller = mGeckoContentController;
+  return controller.forget();
+}
+
+already_AddRefed<GestureEventListener>
+AsyncPanZoomController::GetGestureEventListener() {
+  MonitorAutoLock lock(mRefPtrMonitor);
+  nsRefPtr<GestureEventListener> listener = mGestureEventListener;
+  return listener.forget();
+}
+
 void
 AsyncPanZoomController::Destroy()
 {
-  // These memebrs can only be used on the controller/UI thread.
-  mGeckoContentController = nullptr;
-  mGestureEventListener = nullptr;
+  { // scope the lock
+    MonitorAutoLock lock(mRefPtrMonitor);
+    mGeckoContentController = nullptr;
+    mGestureEventListener = nullptr;
+  }
+  mPrevSibling = nullptr;
+  mLastChild = nullptr;
 }
 
 /* static */float
 AsyncPanZoomController::GetTouchStartTolerance()
 {
   return gTouchStartTolerance;
 }
 
@@ -327,18 +336,19 @@ nsEventStatus AsyncPanZoomController::Re
   }
 
   return HandleInputEvent(aEvent);
 }
 
 nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) {
   nsEventStatus rv = nsEventStatus_eIgnore;
 
-  if (mGestureEventListener && !mDisableNextTouchBatch) {
-    rv = mGestureEventListener->HandleInputEvent(aEvent);
+  nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
+  if (listener && !mDisableNextTouchBatch) {
+    rv = listener->HandleInputEvent(aEvent);
     if (rv == nsEventStatus_eConsumeNoDefault)
       return rv;
   }
 
   if (mDelayPanning && aEvent.mInputType == MULTITOUCH_INPUT) {
     const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput();
     if (multiTouchInput.mType == MultiTouchInput::MULTITOUCH_MOVE) {
       // Let BrowserElementScrolling perform panning gesture first.
@@ -654,51 +664,54 @@ nsEventStatus AsyncPanZoomController::On
     ScheduleComposite();
     RequestContentRepaint();
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) {
-  if (mGeckoContentController) {
+  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
+  if (controller) {
     MonitorAutoLock monitor(mMonitor);
 
     CSSToScreenScale resolution = mFrameMetrics.CalculateResolution();
     CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, resolution);
-    mGeckoContentController->HandleLongTap(gfx::RoundedToInt(point));
+    controller->HandleLongTap(gfx::RoundedToInt(point));
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEvent) {
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) {
-  if (mGeckoContentController) {
+  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
+  if (controller) {
     MonitorAutoLock monitor(mMonitor);
 
     CSSToScreenScale resolution = mFrameMetrics.CalculateResolution();
     CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, resolution);
-    mGeckoContentController->HandleSingleTap(gfx::RoundedToInt(point));
+    controller->HandleSingleTap(gfx::RoundedToInt(point));
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) {
-  if (mGeckoContentController) {
+  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
+  if (controller) {
     MonitorAutoLock monitor(mMonitor);
 
     if (mAllowZoom) {
       CSSToScreenScale resolution = mFrameMetrics.CalculateResolution();
       CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, resolution);
-      mGeckoContentController->HandleDoubleTap(gfx::RoundedToInt(point));
+      controller->HandleDoubleTap(gfx::RoundedToInt(point));
     }
 
     return nsEventStatus_eConsumeNoDefault;
   }
   return nsEventStatus_eIgnore;
 }
 
 nsEventStatus AsyncPanZoomController::OnCancelTap(const TapGestureInput& aEvent) {
@@ -1014,22 +1027,25 @@ void AsyncPanZoomController::RequestCont
     clamped(std::max(mX.GetAccelerationFactor(), mY.GetAccelerationFactor()),
             MIN_ZOOM / 2.0f, MAX_ZOOM);
   // Scale down the resolution a bit based on acceleration.
   mFrameMetrics.mZoom.scale /= accelerationFactor;
 
   // This message is compressed, so fire whether or not we already have a paint
   // queued up. We need to know whether or not a paint was requested anyways,
   // for the purposes of content calling window.scrollTo().
-  mPaintThrottler.PostTask(
-    FROM_HERE,
-    NewRunnableMethod(mGeckoContentController.get(),
-                      &GeckoContentController::RequestContentRepaint,
-                      mFrameMetrics),
-    GetFrameTime());
+  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
+  if (controller) {
+    mPaintThrottler.PostTask(
+      FROM_HERE,
+      NewRunnableMethod(controller.get(),
+                        &GeckoContentController::RequestContentRepaint,
+                        mFrameMetrics),
+      GetFrameTime());
+  }
   mFrameMetrics.mPresShellId = mLastContentPaintMetrics.mPresShellId;
   mLastPaintRequestMetrics = mFrameMetrics;
 
   // Set the zoom back to what it was for the purpose of logic control.
   mFrameMetrics.mZoom = actualZoom;
 }
 
 void
@@ -1038,30 +1054,25 @@ AsyncPanZoomController::FireAsyncScrollO
   if (mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
     MonitorAutoLock monitor(mMonitor);
     SendAsyncScrollEvent();
   }
   mAsyncScrollTimeoutTask = nullptr;
 }
 
 bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime,
-                                                            ContainerLayer* aLayer,
                                                             ViewTransform* aNewTransform,
                                                             ScreenPoint& aScrollOffset) {
   // The eventual return value of this function. The compositor needs to know
   // whether or not to advance by a frame as soon as it can. For example, if a
   // fling is happening, it has to keep compositing so that the animation is
   // smooth. If an animation frame is requested, it is the compositor's
   // responsibility to schedule a composite.
   bool requestAnimationFrame = false;
 
-  LayerPoint metricsScrollOffset;
-  CSSPoint scrollOffset;
-  CSSToScreenScale localScale;
-  const FrameMetrics& frame = aLayer->GetFrameMetrics();
   {
     MonitorAutoLock mon(mMonitor);
 
     switch (mState) {
     case FLING:
       // If a fling is currently happening, apply it now. We can pull
       // the updated metrics afterwards.
       requestAnimationFrame |= DoFling(aSampleTime - mLastSampleTime);
@@ -1098,122 +1109,128 @@ bool AsyncPanZoomController::SampleConte
       }
 
       break;
     }
     default:
       break;
     }
 
-    // Current local transform; this is not what's painted but rather
-    // what PZC has transformed due to touches like panning or
-    // pinching. Eventually, the root layer transform will become this
-    // during runtime, but we must wait for Gecko to repaint.
-    localScale = mFrameMetrics.CalculateResolution();
+    aScrollOffset = mFrameMetrics.mScrollOffset * mFrameMetrics.CalculateResolution();
+    *aNewTransform = GetCurrentAsyncTransformInternal();
 
-    if (frame.IsScrollable()) {
-      metricsScrollOffset = frame.GetScrollOffsetInLayerPixels();
-    }
-
-    scrollOffset = mFrameMetrics.mScrollOffset;
     mCurrentAsyncScrollOffset = mFrameMetrics.mScrollOffset;
   }
 
   // Cancel the mAsyncScrollTimeoutTask because we will fire a
   // mozbrowserasyncscroll event or renew the mAsyncScrollTimeoutTask again.
   if (mAsyncScrollTimeoutTask) {
     mAsyncScrollTimeoutTask->Cancel();
     mAsyncScrollTimeoutTask = nullptr;
   }
   // Fire the mozbrowserasyncscroll event immediately if it's been
   // sAsyncScrollThrottleTime ms since the last time we fired the event and the
   // current scroll offset is different than the mLastAsyncScrollOffset we sent
   // with the last event.
   // Otherwise, start a timer to fire the event sAsyncScrollTimeout ms from now.
   TimeDuration delta = aSampleTime - mLastAsyncScrollTime;
-  if (delta.ToMilliseconds() > mAsyncScrollThrottleTime &&
+  if (delta.ToMilliseconds() > gAsyncScrollThrottleTime &&
       mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
     MonitorAutoLock monitor(mMonitor);
     mLastAsyncScrollTime = aSampleTime;
     mLastAsyncScrollOffset = mCurrentAsyncScrollOffset;
     SendAsyncScrollEvent();
   }
   else {
     mAsyncScrollTimeoutTask =
       NewRunnableMethod(this, &AsyncPanZoomController::FireAsyncScrollOnTimeout);
     MessageLoop::current()->PostDelayedTask(FROM_HERE,
                                             mAsyncScrollTimeoutTask,
-                                            mAsyncScrollTimeout);
+                                            gAsyncScrollTimeout);
   }
 
-  CSSToLayerScale paintedScale = frame.mDevPixelsPerCSSPixel * frame.mResolution;
-  LayerPoint translation = (scrollOffset * paintedScale) - metricsScrollOffset;
-  *aNewTransform = ViewTransform(-translation, localScale / frame.mDevPixelsPerCSSPixel);
-  aScrollOffset = scrollOffset * localScale;
-
   mLastSampleTime = aSampleTime;
 
   return requestAnimationFrame;
 }
 
-void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFrame, bool aIsFirstPaint) {
+ViewTransform AsyncPanZoomController::GetCurrentAsyncTransform() {
+  MonitorAutoLock mon(mMonitor);
+  return GetCurrentAsyncTransformInternal();
+}
+
+ViewTransform AsyncPanZoomController::GetCurrentAsyncTransformInternal() {
+  LayerPoint metricsScrollOffset;
+  if (mLastContentPaintMetrics.IsScrollable()) {
+    metricsScrollOffset = mLastContentPaintMetrics.GetScrollOffsetInLayerPixels();
+  }
+  CSSToScreenScale localScale = mFrameMetrics.CalculateResolution();
+  LayerPoint translation = mFrameMetrics.GetScrollOffsetInLayerPixels() - metricsScrollOffset;
+  return ViewTransform(-translation, localScale / mLastContentPaintMetrics.mDevPixelsPerCSSPixel);
+}
+
+void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
   MonitorAutoLock monitor(mMonitor);
 
-  mLastContentPaintMetrics = aViewportFrame;
+  mLastContentPaintMetrics = aLayerMetrics;
 
-  mFrameMetrics.mMayHaveTouchListeners = aViewportFrame.mMayHaveTouchListeners;
+  mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
 
   // TODO: Once a mechanism for calling UpdateScrollOffset() when content does
   //       a scrollTo() is implemented for B2G (bug 895905), this block can be removed.
 #ifndef MOZ_WIDGET_ANDROID
   if (!mPaintThrottler.IsOutstanding()) {
     // No paint was requested, but we got one anyways. One possible cause of this
     // is that content could have fired a scrollTo(). In this case, we should take
     // the new scroll offset. Document/viewport changes are handled elsewhere.
     // Also note that, since NotifyLayersUpdated() is called whenever there's a
     // layers update, we didn't necessarily get a new scroll offset, but we're
     // updating our local copy of it anyways just in case.
     switch (mState) {
     case NOTHING:
     case FLING:
     case TOUCHING:
     case WAITING_LISTENERS:
-      mFrameMetrics.mScrollOffset = aViewportFrame.mScrollOffset;
+      mFrameMetrics.mScrollOffset = aLayerMetrics.mScrollOffset;
       break;
     // Don't clobber if we're in other states.
     default:
       break;
     }
   }
 #endif
 
   mPaintThrottler.TaskComplete(GetFrameTime());
   bool needContentRepaint = false;
-  if (aViewportFrame.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width &&
-      aViewportFrame.mCompositionBounds.height == mFrameMetrics.mCompositionBounds.height) {
+  if (aLayerMetrics.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width &&
+      aLayerMetrics.mCompositionBounds.height == mFrameMetrics.mCompositionBounds.height) {
     // Remote content has sync'd up to the composition geometry
     // change, so we can accept the viewport it's calculated.
     CSSToScreenScale previousResolution = mFrameMetrics.CalculateResolution();
-    mFrameMetrics.mViewport = aViewportFrame.mViewport;
+    mFrameMetrics.mViewport = aLayerMetrics.mViewport;
     CSSToScreenScale newResolution = mFrameMetrics.CalculateResolution();
     needContentRepaint |= (previousResolution != newResolution);
   }
 
   if (aIsFirstPaint || mFrameMetrics.IsDefault()) {
     mPaintThrottler.ClearHistory();
     mPaintThrottler.SetMaxDurations(gNumPaintDurationSamples);
 
     mX.CancelTouch();
     mY.CancelTouch();
 
-    mFrameMetrics = aViewportFrame;
+    // XXX If this is the very first time we're getting a layers update we need to
+    // trigger another repaint, or the B2G browser shows stale content. This needs
+    // to be investigated and fixed.
+    needContentRepaint |= (mFrameMetrics.IsDefault() && !aLayerMetrics.IsDefault());
 
+    mFrameMetrics = aLayerMetrics;
     mState = NOTHING;
-  } else if (!mFrameMetrics.mScrollableRect.IsEqualEdges(aViewportFrame.mScrollableRect)) {
-    mFrameMetrics.mScrollableRect = aViewportFrame.mScrollableRect;
+  } else if (!mFrameMetrics.mScrollableRect.IsEqualEdges(aLayerMetrics.mScrollableRect)) {
+    mFrameMetrics.mScrollableRect = aLayerMetrics.mScrollableRect;
   }
 
   if (needContentRepaint) {
     RequestContentRepaint();
   }
 }
 
 const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() {
@@ -1237,18 +1254,19 @@ void AsyncPanZoomController::UpdateCompo
 
     // Repaint on a rotation so that our new resolution gets properly updated.
     RequestContentRepaint();
   }
 }
 
 void AsyncPanZoomController::CancelDefaultPanZoom() {
   mDisableNextTouchBatch = true;
-  if (mGestureEventListener) {
-    mGestureEventListener->CancelGesture();
+  nsRefPtr<GestureEventListener> listener = GetGestureEventListener();
+  if (listener) {
+    listener->CancelGesture();
   }
 }
 
 void AsyncPanZoomController::DetectScrollableSubframe() {
   mDelayPanning = true;
 }
 
 void AsyncPanZoomController::ZoomToRect(CSSRect aRect) {
@@ -1419,88 +1437,49 @@ void AsyncPanZoomController::UpdateZoomC
                                                    float aMinZoom,
                                                    float aMaxZoom) {
   mAllowZoom = aAllowZoom;
   mMinZoom = std::max(MIN_ZOOM, aMinZoom);
   mMaxZoom = std::min(MAX_ZOOM, aMaxZoom);
 }
 
 void AsyncPanZoomController::PostDelayedTask(Task* aTask, int aDelayMs) {
-  if (!mGeckoContentController) {
-    return;
+  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
+  if (controller) {
+    controller->PostDelayedTask(aTask, aDelayMs);
   }
-
-  mGeckoContentController->PostDelayedTask(aTask, aDelayMs);
 }
 
 void AsyncPanZoomController::SendAsyncScrollEvent() {
-  if (!mGeckoContentController) {
+  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
+  if (!controller) {
     return;
   }
 
+  FrameMetrics::ViewID scrollId;
   CSSRect contentRect;
   CSSSize scrollableSize;
   {
+    // XXX bug 890932 - there should be a lock here. but it causes a deadlock.
+    scrollId = mFrameMetrics.mScrollId;
     scrollableSize = mFrameMetrics.mScrollableRect.Size();
     contentRect = mFrameMetrics.CalculateCompositedRectInCssPixels();
     contentRect.MoveTo(mCurrentAsyncScrollOffset);
   }
 
-  mGeckoContentController->SendAsyncScrollDOMEvent(contentRect, scrollableSize);
+  controller->SendAsyncScrollDOMEvent(scrollId, contentRect, scrollableSize);
 }
 
-static void GetAPZCAtPointOnSubtree(const ContainerLayer& aLayerIn,
-                    const gfxPoint& aPoint,
-                    AsyncPanZoomController** aApzcOut,
-                    LayerIntPoint* aRelativePointOut)
-{
-  // Making layers const correct is very slow because it requires
-  // a near clobber of the tree. Once const correct is further along
-  // remove this cast.
-  ContainerLayer& aLayer = const_cast<ContainerLayer&>(aLayerIn);
-  gfx3DMatrix transform = aLayer.GetLocalTransform().Inverse();
-  gfxPoint layerPoint = transform.Transform(aPoint);
-
-  // iterate over the children first. They are better match then the parent
-  Layer* currLayer = aLayer.GetLastChild();
-  while (currLayer) {
-    if (currLayer->AsContainerLayer()) {
-      GetAPZCAtPointOnSubtree(*currLayer->AsContainerLayer(), layerPoint, aApzcOut, aRelativePointOut);
-    }
-    if (*aApzcOut) {
-        return;
-    }
-    currLayer = currLayer->GetPrevSibling();
-  }
-
-  if (aLayer.GetFrameMetrics().IsScrollable()) {
-    const FrameMetrics& frame = aLayer.GetFrameMetrics();
-    LayerRect layerViewport = frame.mViewport * frame.LayersPixelsPerCSSPixel();
-    bool intersect = layerViewport.Contains(layerPoint.x, layerPoint.y);
-
-    if (intersect) {
-      *aApzcOut = aLayer.GetAsyncPanZoomController();
-      *aRelativePointOut = LayerIntPoint(NS_lround(layerPoint.x), NS_lround(layerPoint.y));
-    }
-  }
-
-}
-
-void AsyncPanZoomController::GetAPZCAtPoint(const ContainerLayer& aLayerTree,
-                    const ScreenIntPoint& aPoint,
-                    AsyncPanZoomController** aApzcOut,
-                    LayerIntPoint* aRelativePointOut)
-{
-  *aApzcOut = nullptr;
-
-  gfxPoint point(aPoint.x, aPoint.y);
-
-  GetAPZCAtPointOnSubtree(aLayerTree, point, aApzcOut, aRelativePointOut);
-}
-
-void AsyncPanZoomController::UpdateScrollOffset(CSSPoint aScrollOffset)
+void AsyncPanZoomController::UpdateScrollOffset(const CSSPoint& aScrollOffset)
 {
   MonitorAutoLock monitor(mMonitor);
   mFrameMetrics.mScrollOffset = aScrollOffset;
 }
 
+bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid)
+{
+  // TODO: also check the presShellId and mScrollId, once those are
+  // fully propagated everywhere in RenderFrameParent and AndroidJNI.
+  return aGuid.mLayersId == mLayersId;
+}
+
 }
 }
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -9,22 +9,24 @@
 
 #include "GeckoContentController.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/RefPtr.h"
 #include "InputData.h"
 #include "Axis.h"
 #include "TaskThrottler.h"
+#include "mozilla/layers/APZCTreeManager.h"
 
 #include "base/message_loop.h"
 
 namespace mozilla {
 namespace layers {
 
+struct ScrollableLayerGuid;
 class CompositorParent;
 class GestureEventListener;
 class ContainerLayer;
 class ViewTransform;
 
 /**
  * Controller for all panning and zooming logic. Any time a user input is
  * detected and it must be processed in some way to affect what the user sees,
@@ -40,17 +42,17 @@ class ViewTransform;
  * class by locking it and querying it for the current transform matrix based on
  * the panning and zooming logic that was invoked on the UI thread.
  *
  * Currently, each outer DOM window (i.e. a website in a tab, but not any
  * subframes) has its own AsyncPanZoomController. In the future, to support
  * asynchronously scrolled subframes, we want to have one AsyncPanZoomController
  * per frame.
  */
-class AsyncPanZoomController MOZ_FINAL {
+class AsyncPanZoomController {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomController)
 
   typedef mozilla::MonitorAutoLock MonitorAutoLock;
 
 public:
   enum GestureBehavior {
     // The platform code is responsible for forwarding gesture events here. We
     // will not attempt to generate gesture events from MultiTouchInputs.
@@ -63,31 +65,37 @@ public:
   /**
    * Constant describing the tolerance in distance we use, multiplied by the
    * device DPI, before we start panning the screen. This is to prevent us from
    * accidentally processing taps as touch moves, and from very short/accidental
    * touches moving the screen.
    */
   static float GetTouchStartTolerance();
 
-  AsyncPanZoomController(GeckoContentController* aController,
+  AsyncPanZoomController(uint64_t aLayersId,
+                         GeckoContentController* aController,
                          GestureBehavior aGestures = DEFAULT_GESTURES);
   ~AsyncPanZoomController();
 
   // --------------------------------------------------------------------------
+  // These methods must only be called on the gecko thread.
+  //
+
+  /**
+   * Read the various prefs and do any global initialization for all APZC instances.
+   * This must be run on the gecko thread before any APZC instances are actually
+   * used for anything meaningful.
+   */
+  static void InitializeGlobalState();
+
+  // --------------------------------------------------------------------------
   // These methods must only be called on the controller/UI thread.
   //
 
   /**
-   * Shut down the controller/UI thread state and prepare to be
-   * deleted (which may happen from any thread).
-   */
-  void Destroy();
-
-  /**
    * General handler for incoming input events. Manipulates the frame metrics
    * based on what type of input it is. For example, a PinchGestureEvent will
    * cause scaling. This should only be called externally to this class.
    * HandleInputEvent() should be used internally.
    */
   nsEventStatus ReceiveInputEvent(const InputData& aEvent);
 
   /**
@@ -161,48 +169,65 @@ public:
   //
 
   /**
    * The compositor calls this when it's about to draw pannable/zoomable content
    * and is setting up transforms for compositing the layer tree. This is not
    * idempotent. For example, a fling transform can be applied each time this is
    * called (though not necessarily). |aSampleTime| is the time that this is
    * sampled at; this is used for interpolating animations. Calling this sets a
-   * new transform in |aNewTransform| which should be applied directly to the
-   * shadow layer of the frame (do not multiply it in as the code already does
-   * this internally with |aLayer|'s transform).
+   * new transform in |aNewTransform| which should be multiplied to the transform
+   * in the shadow layer corresponding to this APZC.
    *
    * Return value indicates whether or not any currently running animation
    * should continue. That is, if true, the compositor should schedule another
    * composite.
    */
   bool SampleContentTransformForFrame(const TimeStamp& aSampleTime,
-                                      ContainerLayer* aLayer,
                                       ViewTransform* aNewTransform,
                                       ScreenPoint& aScrollOffset);
 
   /**
-   * A shadow layer update has arrived. |aViewportFrame| is the new FrameMetrics
-   * for the top-level frame. |aIsFirstPaint| is a flag passed from the shadow
+   * A shadow layer update has arrived. |aLayerMetrics| is the new FrameMetrics
+   * for the container layer corresponding to this APZC.
+   * |aIsFirstPaint| is a flag passed from the shadow
    * layers code indicating that the frame metrics being sent with this call are
    * the initial metrics and the initial paint of the frame has just happened.
    */
-  void NotifyLayersUpdated(const FrameMetrics& aViewportFrame, bool aIsFirstPaint);
+  void NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint);
 
   /**
    * The platform implementation must set the compositor parent so that we can
    * request composites.
    */
   void SetCompositorParent(CompositorParent* aCompositorParent);
 
   // --------------------------------------------------------------------------
   // These methods can be called from any thread.
   //
 
   /**
+   * Shut down the controller/UI thread state and prepare to be
+   * deleted (which may happen from any thread).
+   */
+  void Destroy();
+
+  /**
+   * Returns the incremental transformation corresponding to the async pan/zoom
+   * in progress. That is, when this transform is multiplied with the layer's
+   * existing transform, it will make the layer appear with the desired pan/zoom
+   * amount.
+   */
+  ViewTransform GetCurrentAsyncTransform();
+private:
+  /* Internal method of above. Callers to this MUST hold the monitor. */
+  ViewTransform GetCurrentAsyncTransformInternal();
+public:
+
+  /**
    * Sets the DPI of the device for use within panning and zooming logic. It is
    * a platform responsibility to set this on initialization of this class and
    * whenever it changes.
    */
   void SetDPI(int aDPI);
 
   /**
    * Gets the DPI of the device for use outside the panning and zooming logic.
@@ -230,39 +255,33 @@ public:
 
   /**
    * Handler for events which should not be intercepted by the touch listener.
    * Does the work for ReceiveInputEvent().
    */
   nsEventStatus HandleInputEvent(const InputData& aEvent);
 
   /**
+   * Returns true if this APZC instance is for the layer identified by the guid.
+   */
+  bool Matches(const ScrollableLayerGuid& aGuid);
+
+  /**
    * Sync panning and zooming animation using a fixed frame time.
    * This will ensure that we animate the APZC correctly with other external
    * animations to the same timestamp.
    */
   static void SetFrameTime(const TimeStamp& aMilliseconds);
 
   /**
-   * Transform and intersect aPoint with the layer tree returning the appropriate
-   * AsyncPanZoomController for this point.
-   * aRelativePointOut Return the point transformed into the layer coordinates
-   * relative to the scroll origin for this layer.
-   */
-  static void GetAPZCAtPoint(const ContainerLayer& aLayerTree,
-                             const ScreenIntPoint& aPoint,
-                             AsyncPanZoomController** aApzcOut,
-                             LayerIntPoint* aRelativePointOut);
-
-  /**
    * Update mFrameMetrics.mScrollOffset to the given offset.
    * This is necessary in cases where a scroll is not caused by user
    * input (for example, a content scrollTo()).
    */
-  void UpdateScrollOffset(CSSPoint aScrollOffset);
+  void UpdateScrollOffset(const CSSPoint& aScrollOffset);
 
   /**
    * Cancels any currently running animation. Note that all this does is set the
    * state of the AsyncPanZoomController back to NOTHING, but it is the
    * animation's responsibility to check this before advancing.
    */
   void CancelAnimation();
 
@@ -487,26 +506,49 @@ private:
 
   /**
    * Helper to set the current state. Holds the monitor before actually setting
    * it. If the monitor is already held by the current thread, it is safe to
    * instead use: |mState = NEWSTATE;|
    */
   void SetState(PanZoomState aState);
 
+  uint64_t mLayersId;
   nsRefPtr<CompositorParent> mCompositorParent;
   TaskThrottler mPaintThrottler;
+
+  /* Access to the following two fields is protected by the mRefPtrMonitor,
+     since they are accessed on the UI thread but can be cleared on the
+     compositor thread. */
   nsRefPtr<GeckoContentController> mGeckoContentController;
   nsRefPtr<GestureEventListener> mGestureEventListener;
+  Monitor mRefPtrMonitor;
 
+  /* Utility functions that return a addrefed pointer to the corresponding fields. */
+  already_AddRefed<GeckoContentController> GetGeckoContentController();
+  already_AddRefed<GestureEventListener> GetGestureEventListener();
+
+protected:
   // Both |mFrameMetrics| and |mLastContentPaintMetrics| are protected by the
   // monitor. Do not read from or modify either of them without locking.
   FrameMetrics mFrameMetrics;
+
+  // Protects |mFrameMetrics|, |mLastContentPaintMetrics|, and |mState|.
+  // Before manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the
+  // monitor should be held. When setting |mState|, either the SetState()
+  // function can be used, or the monitor can be held and then |mState| updated.
+  Monitor mMonitor;
+
+private:
+  // Metrics of the container layer corresponding to this APZC. This is
+  // stored here so that it is accessible from the UI/controller thread.
   // These are the metrics at last content paint, the most recent
-  // values we were notified of in NotifyLayersUpdate().
+  // values we were notified of in NotifyLayersUpdate(). Since it represents
+  // the Gecko state, it should be used as a basis for untransformation when
+  // sending messages back to Gecko.
   FrameMetrics mLastContentPaintMetrics;
   // The last metrics that we requested a paint for. These are used to make sure
   // that we're not requesting a paint of the same thing that's already drawn.
   // If we don't do this check, we don't get a ShadowLayersUpdated back.
   FrameMetrics mLastPaintRequestMetrics;
 
   // Old metrics from before we started a zoom animation. This is only valid
   // when we are in the "ANIMATED_ZOOM" state. This is used so that we can
@@ -527,24 +569,16 @@ private:
 
   // Most up-to-date constraints on zooming. These should always be reasonable
   // values; for example, allowing a min zoom of 0.0 can cause very bad things
   // to happen.
   bool mAllowZoom;
   float mMinZoom;
   float mMaxZoom;
 
-  // Protects |mFrameMetrics|, |mLastContentPaintMetrics|, |mState| and
-  // |mMetaViewportInfo|. Before manipulating |mFrameMetrics| or
-  // |mLastContentPaintMetrics|, the monitor should be held. When setting
-  // |mState|, either the SetState() function can be used, or the monitor can be
-  // held and then |mState| updated.  |mMetaViewportInfo| should be updated
-  // using UpdateMetaViewport().
-  Monitor mMonitor;
-
   // The last time the compositor has sampled the content transform for this
   // frame.
   TimeStamp mLastSampleTime;
   // The last time a touch event came through on the UI thread.
   uint32_t mLastEventTime;
 
   // Start time of an animation. This is used for a zoom to animation to mark
   // the beginning.
@@ -566,24 +600,16 @@ private:
   // The current offset drawn on the screen, it may not be sent since we have
   // throttling policy for mozbrowserasyncscroll event.
   CSSPoint mCurrentAsyncScrollOffset;
 
   // The delay task triggered by the throttling mozbrowserasyncscroll event
   // ensures the last mozbrowserasyncscroll event is always been fired.
   CancelableTask* mAsyncScrollTimeoutTask;
 
-  // The time period in ms that throttles mozbrowserasyncscroll event.
-  // Default is 100ms if there is no "apzc.asyncscroll.throttle" in preference.
-  uint32_t mAsyncScrollThrottleTime;
-
-  // The timeout in ms for mAsyncScrollTimeoutTask delay task.
-  // Default is 300ms if there is no "apzc.asyncscroll.timeout" in preference.
-  uint32_t mAsyncScrollTimeout;
-
   int mDPI;
 
   // Flag used to determine whether or not we should disable handling of the
   // next batch of touch events. This is used for sync scrolling of subframes.
   bool mDisableNextTouchBatch;
 
   // Flag used to determine whether or not we should try to enter the
   // WAITING_LISTENERS state. This is used in the case that we are processing a
@@ -593,14 +619,45 @@ private:
 
   // Flag used to determine whether or not we should try scrolling by
   // BrowserElementScrolling first.  If set, we delay delivering
   // touchmove events to GestureListener until BrowserElementScrolling
   // decides whether it wants to handle panning for this touch series.
   bool mDelayPanning;
 
   friend class Axis;
+
+  /* The functions and members in this section are used to build a tree
+   * structure out of APZC instances. This tree can only be walked or
+   * manipulated while holding the lock in the associated APZCTreeManager
+   * instance.
+   */
+public:
+  void SetLastChild(AsyncPanZoomController* child) { mLastChild = child; }
+  void SetPrevSibling(AsyncPanZoomController* sibling) { mPrevSibling = sibling; }
+  AsyncPanZoomController* GetLastChild() const { return mLastChild; }
+  AsyncPanZoomController* GetPrevSibling() const { return mPrevSibling; }
+private:
+  nsRefPtr<AsyncPanZoomController> mLastChild;
+  nsRefPtr<AsyncPanZoomController> mPrevSibling;
+
+  /* The functions and members in this section are used to maintain the
+   * area that this APZC instance is responsible for. This is used when
+   * hit-testing to see which APZC instance should handle touch events.
+   */
+public:
+  void SetVisibleRegion(gfxRect rect) { mVisibleRegion = rect; }
+
+  bool VisibleRegionContains(const gfxPoint& aPoint) const {
+    return mVisibleRegion.Contains(aPoint.x, aPoint.y);
+  }
+
+private:
+  /* This is the viewport of the layer that this APZC corresponds to, but
+   * post-transform. In other words, it is in the coordinate space of its
+   * parent layer. */
+  gfxRect mVisibleRegion;
 };
 
 }
 }
 
 #endif // mozilla_layers_PanZoomController_h
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -3,17 +3,16 @@
 /* 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 <map>
 
 #include "mozilla/DebugOnly.h"
 
-#include "AsyncPanZoomController.h"
 #include "AutoOpenSurface.h"
 #include "CompositorParent.h"
 #include "mozilla/layers/CompositorOGL.h"
 #include "mozilla/layers/BasicCompositor.h"
 #ifdef XP_WIN
 #include "mozilla/layers/CompositorD3D11.h"
 #endif
 #include "LayerTransactionParent.h"
@@ -150,47 +149,60 @@ CompositorParent::CompositorParent(nsIWi
   MOZ_COUNT_CTOR(CompositorParent);
   mCompositorID = 0;
   // FIXME: This holds on the the fact that right now the only thing that
   // can destroy this instance is initialized on the compositor thread after
   // this task has been processed.
   CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(&AddCompositor,
                                                           this, &mCompositorID));
 
+  mRootLayerTreeID = AllocateLayerTreeId();
+  sIndirectLayerTrees[mRootLayerTreeID].mParent = this;
+
+  mApzcTreeManager = new APZCTreeManager();
   ++sCompositorThreadRefCount;
 }
 
 PlatformThreadId
 CompositorParent::CompositorThreadID()
 {
   return sCompositorThread ? sCompositorThread->thread_id() : sCompositorThreadID;
 }
 
 bool
 CompositorParent::IsInCompositorThread()
 {
   return CompositorThreadID() == PlatformThread::CurrentId();
 }
 
+uint64_t
+CompositorParent::RootLayerTreeId()
+{
+  return mRootLayerTreeID;
+}
+
 CompositorParent::~CompositorParent()
 {
   MOZ_COUNT_DTOR(CompositorParent);
 
   ReleaseCompositorThread();
 }
 
 void
 CompositorParent::Destroy()
 {
   NS_ABORT_IF_FALSE(ManagedPLayerTransactionParent().Length() == 0,
                     "CompositorParent destroyed before managed PLayerTransactionParent");
 
   // Ensure that the layer manager is destructed on the compositor thread.
   mLayerManager = nullptr;
   mCompositionManager = nullptr;
+  mApzcTreeManager->ClearTree();
+  mApzcTreeManager = nullptr;
+  sIndirectLayerTrees.erase(mRootLayerTreeID);
 }
 
 void
 CompositorParent::ForceIsFirstPaint()
 {
   mCompositionManager->ForceIsFirstPaint();
 }
 
@@ -399,18 +411,23 @@ CompositorParent::ScheduleTask(Cancelabl
   if (time == 0) {
     MessageLoop::current()->PostTask(FROM_HERE, task);
   } else {
     MessageLoop::current()->PostDelayedTask(FROM_HERE, task, time);
   }
 }
 
 void
-CompositorParent::NotifyShadowTreeTransaction()
+CompositorParent::NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint)
 {
+  if (mApzcTreeManager) {
+    AutoResolveRefLayers resolve(mCompositionManager);
+    mApzcTreeManager->UpdatePanZoomControllerTree(this, mLayerManager->GetRoot(), aIsFirstPaint, aId);
+  }
+
   if (mLayerManager) {
     LayerManagerComposite* managerComposite = mLayerManager->AsLayerManagerComposite();
     if (managerComposite) {
       managerComposite->NotifyShadowTreeTransaction();
     }
   }
   ScheduleComposition();
 }
@@ -554,16 +571,22 @@ CompositorParent::ShadowLayersUpdated(La
   // Instruct the LayerManager to update its render bounds now. Since all the orientation
   // change, dimension change would be done at the stage, update the size here is free of
   // race condition.
   mLayerManager->UpdateRenderBounds(aTargetConfig.clientBounds());
 
   mCompositionManager->Updated(isFirstPaint, aTargetConfig);
   Layer* root = aLayerTree->GetRoot();
   mLayerManager->SetRoot(root);
+
+  if (mApzcTreeManager) {
+    AutoResolveRefLayers resolve(mCompositionManager);
+    mApzcTreeManager->UpdatePanZoomControllerTree(this, root, isFirstPaint, mRootLayerTreeID);
+  }
+
   if (root) {
     SetShadowProperties(root);
     if (mIsTesting) {
       mCompositionManager->TransformShadowTree(mTestTime);
     }
   }
   ScheduleComposition();
   LayerManagerComposite *layerComposite = mLayerManager->AsLayerManagerComposite();
@@ -699,17 +722,17 @@ CompositorParent::NotifyChildCreated(uin
   sIndirectLayerTrees[aChild].mParent = this;
 }
 
 /*static*/ uint64_t
 CompositorParent::AllocateLayerTreeId()
 {
   MOZ_ASSERT(CompositorLoop());
   MOZ_ASSERT(NS_IsMainThread());
-  static uint64_t ids;
+  static uint64_t ids = 0;
   return ++ids;
 }
 
 static void
 EraseLayerState(uint64_t aId)
 {
   sIndirectLayerTrees.erase(aId);
 }
@@ -719,39 +742,59 @@ CompositorParent::DeallocateLayerTreeId(
 {
   MOZ_ASSERT(NS_IsMainThread());
   CompositorLoop()->PostTask(FROM_HERE,
                              NewRunnableFunction(&EraseLayerState, aId));
 }
 
 static void
 UpdateControllerForLayersId(uint64_t aLayersId,
-                            AsyncPanZoomController* aController)
+                            GeckoContentController* aController)
 {
-  // Adopt ref given to us by SetPanZoomControllerForLayerTree()
+  // Adopt ref given to us by SetControllerForLayerTree()
   sIndirectLayerTrees[aLayersId].mController =
-    already_AddRefed<AsyncPanZoomController>(aController);
+    already_AddRefed<GeckoContentController>(aController);
+}
 
-  // Notify the AsyncPanZoomController about the current compositor so that it
-  // can request composites off the compositor thread.
-  aController->SetCompositorParent(sIndirectLayerTrees[aLayersId].mParent);
+ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(uint64_t aLayersId,
+                                                         Layer* aRoot,
+                                                         GeckoContentController* aController)
+    : mLayersId(aLayersId)
+{
+  sIndirectLayerTrees[aLayersId].mRoot = aRoot;
+  sIndirectLayerTrees[aLayersId].mController = aController;
+}
+
+ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration()
+{
+  sIndirectLayerTrees.erase(mLayersId);
 }
 
 /*static*/ void
-CompositorParent::SetPanZoomControllerForLayerTree(uint64_t aLayersId,
-                                                   AsyncPanZoomController* aController)
+CompositorParent::SetControllerForLayerTree(uint64_t aLayersId,
+                                            GeckoContentController* aController)
 {
   // This ref is adopted by UpdateControllerForLayersId().
   aController->AddRef();
   CompositorLoop()->PostTask(FROM_HERE,
                              NewRunnableFunction(&UpdateControllerForLayersId,
                                                  aLayersId,
                                                  aController));
 }
 
+/*static*/ APZCTreeManager*
+CompositorParent::GetAPZCTreeManager(uint64_t aLayersId)
+{
+  const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId);
+  if (state && state->mParent) {
+    return state->mParent->mApzcTreeManager;
+  }
+  return nullptr;
+}
+
 /**
  * This class handles layer updates pushed directly from child
  * processes to the compositor thread.  It's associated with a
  * CompositorParent on the compositor thread.  While it uses the
  * PCompositor protocol to manage these updates, it doesn't actually
  * drive compositing itself.  For that it hands off work to the
  * CompositorParent it's associated with.
  */
@@ -826,26 +869,20 @@ CompositorParent::Create(Transport* aTra
     NewRunnableFunction(OpenCompositor, cpcp.get(),
                         aTransport, handle, XRE_GetIOMessageLoop()));
   // The return value is just compared to null for success checking,
   // we're not sharing a ref.
   return cpcp.get();
 }
 
 static void
-UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig, bool isFirstPaint)
+UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig)
 {
   sIndirectLayerTrees[aId].mRoot = aRoot;
   sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig;
-  ContainerLayer* rootContainer = aRoot->AsContainerLayer();
-  if (rootContainer) {
-    if (AsyncPanZoomController* apzc = sIndirectLayerTrees[aId].mController) {
-      apzc->NotifyLayersUpdated(rootContainer->GetFrameMetrics(), isFirstPaint);
-    }
-  }
 }
 
 /* static */ const CompositorParent::LayerTreeState*
 CompositorParent::GetIndirectShadowTree(uint64_t aId)
 {
   LayerTreeMap::const_iterator cit = sIndirectLayerTrees.find(aId);
   if (sIndirectLayerTrees.end() == cit) {
     return nullptr;
@@ -907,19 +944,19 @@ CrossProcessCompositorParent::ShadowLaye
   bool isFirstPaint)
 {
   uint64_t id = aLayerTree->GetId();
   MOZ_ASSERT(id != 0);
   Layer* shadowRoot = aLayerTree->GetRoot();
   if (shadowRoot) {
     SetShadowProperties(shadowRoot);
   }
-  UpdateIndirectTree(id, shadowRoot, aTargetConfig, isFirstPaint);
+  UpdateIndirectTree(id, shadowRoot, aTargetConfig);
 
-  sIndirectLayerTrees[id].mParent->NotifyShadowTreeTransaction();
+  sIndirectLayerTrees[id].mParent->NotifyShadowTreeTransaction(id, isFirstPaint);
 }
 
 void
 CrossProcessCompositorParent::DeferredDestroy()
 {
   mSelfRef = nullptr;
   // |this| was just destroyed, hands off
 }
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -12,36 +12,48 @@
 // its responsiveness objectives:
 //    1) Compose a frame within 15ms of receiving a ScheduleCompositeCall
 //    2) Unless a frame was composited within the throttle threshold in
 //       which the deadline will be 15ms + throttle threshold
 //#define COMPOSITOR_PERFORMANCE_WARNING
 
 #include "mozilla/layers/PCompositorParent.h"
 #include "mozilla/layers/PLayerTransactionParent.h"
+#include "mozilla/layers/APZCTreeManager.h"
 #include "base/thread.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/TimeStamp.h"
 #include "ShadowLayersManager.h"
 
 class nsIWidget;
 
 namespace base {
 class Thread;
 }
 
 namespace mozilla {
 namespace layers {
 
-class AsyncPanZoomController;
+class APZCTreeManager;
 class Layer;
 class LayerManagerComposite;
 class AsyncCompositionManager;
 struct TextureFactoryIdentifier;
 
+struct ScopedLayerTreeRegistration
+{
+  ScopedLayerTreeRegistration(uint64_t aLayersId,
+                              Layer* aRoot,
+                              GeckoContentController* aController);
+  ~ScopedLayerTreeRegistration();
+
+private:
+  uint64_t mLayersId;
+};
+
 class CompositorParent : public PCompositorParent,
                          public ShadowLayersManager
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorParent)
 
 public:
   CompositorParent(nsIWidget* aWidget,
                    bool aUseExternalSurfaceSize = false,
@@ -84,17 +96,23 @@ public:
   void SchedulePauseOnCompositorThread();
   /**
    * Returns true if a surface was obtained and the resume succeeded; false
    * otherwise.
    */
   bool ScheduleResumeOnCompositorThread(int width, int height);
 
   virtual void ScheduleComposition();
-  void NotifyShadowTreeTransaction();
+  void NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint);
+
+  /**
+   * Returns the unique layer tree identifier that corresponds to the root
+   * tree of this compositor.
+   */
+  uint64_t RootLayerTreeId();
 
   /**
    * Returns a pointer to the compositor corresponding to the given ID.
    */
   static CompositorParent* GetCompositor(uint64_t id);
 
   /**
    * Returns the compositor thread's message loop.
@@ -123,23 +141,29 @@ public:
   /**
    * Release compositor-thread resources referred to by |aID|.
    *
    * Must run on the content main thread.
    */
   static void DeallocateLayerTreeId(uint64_t aId);
 
   /**
-   * Set aController as the pan/zoom controller for the tree referred
+   * Set aController as the pan/zoom callback for the subtree referred
    * to by aLayersId.
    *
    * Must run on content main thread.
    */
-  static void SetPanZoomControllerForLayerTree(uint64_t aLayersId,
-                                               AsyncPanZoomController* aController);
+  static void SetControllerForLayerTree(uint64_t aLayersId,
+                                        GeckoContentController* aController);
+
+  /**
+   * This returns a reference to the APZCTreeManager to which
+   * pan/zoom-related events can be sent.
+   */
+  static APZCTreeManager* GetAPZCTreeManager(uint64_t aLayersId);
 
   /**
    * A new child process has been configured to push transactions
    * directly to us.  Transport is to its thread context.
    */
   static PCompositorParent*
   Create(Transport* aTransport, ProcessId aOtherProcess);
 
@@ -149,17 +173,17 @@ public:
    * for example moving Compositor into native toolkit main thread will allow to avoid
    * extra synchronization and call ::Composite() right from toolkit::Paint event
    */
   static void StartUpWithExistingThread(MessageLoop* aMsgLoop,
                                         PlatformThreadId aThreadID);
 
   struct LayerTreeState {
     nsRefPtr<Layer> mRoot;
-    nsRefPtr<AsyncPanZoomController> mController;
+    nsRefPtr<GeckoContentController> mController;
     CompositorParent *mParent;
     TargetConfig mTargetConfig;
   };
 
   /**
    * Lookup the indirect shadow tree for |aId| and return it if it
    * exists.  Otherwise null is returned.  This must only be called on
    * the compositor thread.
@@ -259,19 +283,22 @@ private:
 
   bool mUseExternalSurfaceSize;
   nsIntSize mEGLSurfaceSize;
 
   mozilla::Monitor mPauseCompositionMonitor;
   mozilla::Monitor mResumeCompositionMonitor;
 
   uint64_t mCompositorID;
+  uint64_t mRootLayerTreeID;
 
   bool mOverrideComposeReadiness;
   CancelableTask* mForceCompositionTask;
 
+  nsRefPtr<APZCTreeManager> mApzcTreeManager;
+
   DISALLOW_EVIL_CONSTRUCTORS(CompositorParent);
 };
 
 } // layers
 } // mozilla
 
 #endif // mozilla_layers_CompositorParent_h
--- a/gfx/layers/ipc/GeckoContentController.h
+++ b/gfx/layers/ipc/GeckoContentController.h
@@ -46,17 +46,18 @@ public:
    */
   virtual void HandleLongTap(const CSSIntPoint& aPoint) = 0;
 
   /**
    * Requests sending a mozbrowserasyncscroll domevent to embedder.
    * |aContentRect| is in CSS pixels, relative to the current cssPage.
    * |aScrollableSize| is the current content width/height in CSS pixels.
    */
-  virtual void SendAsyncScrollDOMEvent(const CSSRect &aContentRect,
+  virtual void SendAsyncScrollDOMEvent(FrameMetrics::ViewID aScrollId,
+                                       const CSSRect &aContentRect,
                                        const CSSSize &aScrollableSize) = 0;
 
   /**
    * Schedules a runnable to run on the controller/UI thread at some time
    * in the future.
    */
   virtual void PostDelayedTask(Task* aTask, int aDelayMs) = 0;
 
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -100,16 +100,17 @@ EXPORTS.mozilla.layers += [
     'YCbCrImageDataSerializer.h',
     'basic/BasicCompositor.h',
     'client/CanvasClient.h',
     'client/CompositableClient.h',
     'client/ContentClient.h',
     'client/ImageClient.h',
     'client/TextureClient.h',
     'client/TiledContentClient.h',
+    'composite/APZCTreeManager.h',
     'composite/AsyncCompositionManager.h',
     'composite/CanvasLayerComposite.h',
     'composite/ColorLayerComposite.h',
     'composite/ContainerLayerComposite.h',
     'composite/ContentHost.h',
     'composite/ImageHost.h',
     'composite/ImageLayerComposite.h',
     'composite/LayerManagerComposite.h',
@@ -165,16 +166,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
         'ipc/ShadowLayerUtilsGralloc.h',
     ]
     CPP_SOURCES += [
         'ShadowLayerUtilsGralloc.cpp',
         'GrallocImages.cpp',
     ]
 
 CPP_SOURCES += [
+    'APZCTreeManager.cpp',
     'AsyncCompositionManager.cpp',
     'AsyncPanZoomController.cpp',
     'Axis.cpp',
     'BasicCanvasLayer.cpp',
     'BasicColorLayer.cpp',
     'BasicCompositor.cpp',
     'BasicContainerLayer.cpp',
     'BasicImageLayer.cpp',
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -5,45 +5,64 @@
 
 #include "gtest/gtest.h"
 #include "gmock/gmock.h"
 
 #include "mozilla/layers/AsyncCompositionManager.h" // for ViewTransform
 #include "mozilla/layers/AsyncPanZoomController.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "mozilla/layers/GeckoContentController.h"
+#include "mozilla/layers/CompositorParent.h"
+#include "mozilla/layers/APZCTreeManager.h"
 #include "Layers.h"
 #include "TestLayers.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using ::testing::_;
 
 class MockContentController : public GeckoContentController {
 public:
   MOCK_METHOD1(RequestContentRepaint, void(const FrameMetrics&));
   MOCK_METHOD1(HandleDoubleTap, void(const CSSIntPoint&));
   MOCK_METHOD1(HandleSingleTap, void(const CSSIntPoint&));
   MOCK_METHOD1(HandleLongTap, void(const CSSIntPoint&));
-  MOCK_METHOD2(SendAsyncScrollDOMEvent, void(const CSSRect &aContentRect, const CSSSize &aScrollableSize));
+  MOCK_METHOD3(SendAsyncScrollDOMEvent, void(FrameMetrics::ViewID aScrollId, const CSSRect &aContentRect, const CSSSize &aScrollableSize));
   MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs));
 };
 
 class TestAPZCContainerLayer : public ContainerLayer {
   public:
     TestAPZCContainerLayer()
       : ContainerLayer(nullptr, nullptr)
     {}
   void RemoveChild(Layer* aChild) {}
   void InsertAfter(Layer* aChild, Layer* aAfter) {}
   void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface) {}
   void RepositionChild(Layer* aChild, Layer* aAfter) {}
 };
 
+class TestAsyncPanZoomController : public AsyncPanZoomController {
+public:
+  TestAsyncPanZoomController(uint64_t aLayersId, MockContentController* mcc)
+    : AsyncPanZoomController(aLayersId, mcc)
+  {}
+
+  void SetFrameMetrics(const FrameMetrics& metrics) {
+    MonitorAutoLock lock(mMonitor);
+    mFrameMetrics = metrics;
+  }
+};
+
+class TestAPZCTreeManager : public APZCTreeManager {
+protected:
+  void AssertOnCompositorThread() MOZ_OVERRIDE { /* no-op */ }
+};
+
 static
 FrameMetrics TestFrameMetrics() {
   FrameMetrics fm;
 
   fm.mDisplayPort = CSSRect(0, 0, 10, 10);
   fm.mCompositionBounds = ScreenIntRect(0, 0, 10, 10);
   fm.mCriticalDisplayPort = CSSRect(0, 0, 10, 10);
   fm.mScrollableRect = CSSRect(0, 0, 100, 100);
@@ -85,31 +104,30 @@ void ApzcPan(AsyncPanZoomController* apz
   aTime += TIME_BETWEEN_TOUCH_EVENT;
   mti.mTouches.AppendElement(SingleTouchData(0, ScreenIntPoint(10, aTouchEndY), ScreenSize(0, 0), 0, 0));
   status = apzc->HandleInputEvent(mti);
 }
 
 TEST(AsyncPanZoomController, Constructor) {
   // RefCounted class can't live in the stack
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<AsyncPanZoomController> apzc = new AsyncPanZoomController(mcc);
-  apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
+  apzc->SetFrameMetrics(TestFrameMetrics());
 }
 
 TEST(AsyncPanZoomController, SimpleTransform) {
   TimeStamp testStartTime = TimeStamp::Now();
   // RefCounted class can't live in the stack
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<AsyncPanZoomController> apzc = new AsyncPanZoomController(mcc);
-  apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
+  apzc->SetFrameMetrics(TestFrameMetrics());
 
-  TestAPZCContainerLayer layer;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
-  apzc->SampleContentTransformForFrame(testStartTime, &layer, &viewTransformOut, pointOut);
+  apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
 
   EXPECT_EQ(pointOut, ScreenPoint());
   EXPECT_EQ(viewTransformOut, ViewTransform());
 }
 
 
 TEST(AsyncPanZoomController, ComplexTransform) {
   TimeStamp testStartTime = TimeStamp::Now();
@@ -126,17 +144,18 @@ TEST(AsyncPanZoomController, ComplexTran
   // CSS transforms, the two layers are the same size in screen
   // pixels.
   //
   // The screen itself is 24x24 in screen pixels (therefore 4x4 in
   // CSS pixels). The displayport is 1 extra CSS pixel on all
   // sides.
 
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<AsyncPanZoomController> apzc = new AsyncPanZoomController(mcc);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
+  nsRefPtr<TestAsyncPanZoomController> childApzc = new TestAsyncPanZoomController(0, mcc);
 
   const char* layerTreeSyntax = "c(c)";
   // LayerID                     0 1
   nsIntRegion layerVisibleRegion[] = {
     nsIntRegion(nsIntRect(0, 0, 300, 300)),
     nsIntRegion(nsIntRect(0, 0, 150, 300)),
   };
   gfx3DMatrix transforms[] = {
@@ -169,134 +188,139 @@ TEST(AsyncPanZoomController, ComplexTran
 
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
   // Both the parent and child layer should behave exactly the same here, because
   // the CSS transform on the child layer does not affect the SampleContentTransformForFrame code
 
   // initial transform
+  apzc->SetFrameMetrics(metrics);
   apzc->NotifyLayersUpdated(metrics, true);
-  apzc->SampleContentTransformForFrame(testStartTime, layers[0]->AsContainerLayer(), &viewTransformOut, pointOut);
+  apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(ViewTransform(LayerPoint(), LayoutDeviceToScreenScale(2)), viewTransformOut);
   EXPECT_EQ(ScreenPoint(60, 60), pointOut);
 
-  apzc->NotifyLayersUpdated(childMetrics, true);
-  apzc->SampleContentTransformForFrame(testStartTime, layers[1]->AsContainerLayer(), &viewTransformOut, pointOut);
+  childApzc->SetFrameMetrics(childMetrics);
+  childApzc->NotifyLayersUpdated(childMetrics, true);
+  childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(ViewTransform(LayerPoint(), LayoutDeviceToScreenScale(2)), viewTransformOut);
   EXPECT_EQ(ScreenPoint(60, 60), pointOut);
 
   // do an async scroll by 5 pixels and check the transform
   metrics.mScrollOffset += CSSPoint(5, 0);
-  apzc->NotifyLayersUpdated(metrics, true);
-  apzc->SampleContentTransformForFrame(testStartTime, layers[0]->AsContainerLayer(), &viewTransformOut, pointOut);
+  apzc->SetFrameMetrics(metrics);
+  apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(2)), viewTransformOut);
   EXPECT_EQ(ScreenPoint(90, 60), pointOut);
 
   childMetrics.mScrollOffset += CSSPoint(5, 0);
-  apzc->NotifyLayersUpdated(childMetrics, true);
-  apzc->SampleContentTransformForFrame(testStartTime, layers[1]->AsContainerLayer(), &viewTransformOut, pointOut);
+  childApzc->SetFrameMetrics(childMetrics);
+  childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(2)), viewTransformOut);
   EXPECT_EQ(ScreenPoint(90, 60), pointOut);
 
   // do an async zoom of 1.5x and check the transform
   metrics.mZoom.scale *= 1.5f;
-  apzc->NotifyLayersUpdated(metrics, true);
-  apzc->SampleContentTransformForFrame(testStartTime, layers[0]->AsContainerLayer(), &viewTransformOut, pointOut);
+  apzc->SetFrameMetrics(metrics);
+  apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(3)), viewTransformOut);
   EXPECT_EQ(ScreenPoint(135, 90), pointOut);
 
   childMetrics.mZoom.scale *= 1.5f;
-  apzc->NotifyLayersUpdated(childMetrics, true);
-  apzc->SampleContentTransformForFrame(testStartTime, layers[0]->AsContainerLayer(), &viewTransformOut, pointOut);
+  childApzc->SetFrameMetrics(childMetrics);
+  childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(3)), viewTransformOut);
   EXPECT_EQ(ScreenPoint(135, 90), pointOut);
 }
 
 TEST(AsyncPanZoomController, Pan) {
   TimeStamp testStartTime = TimeStamp::Now();
   AsyncPanZoomController::SetFrameTime(testStartTime);
 
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<AsyncPanZoomController> apzc = new AsyncPanZoomController(mcc);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
+
+  apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
 
-  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_)).Times(4);
+  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(4);
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   int time = 0;
   int touchStart = 50;
   int touchEnd = 10;
-  TestAPZCContainerLayer layer;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
   // Pan down
   ApzcPan(apzc, time, touchStart, touchEnd);
-  apzc->SampleContentTransformForFrame(testStartTime, &layer, &viewTransformOut, pointOut);
+  apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(pointOut, ScreenPoint(0, -(touchEnd-touchStart)));
   EXPECT_NE(viewTransformOut, ViewTransform());
 
   // Pan back
   ApzcPan(apzc, time, touchEnd, touchStart);
-  apzc->SampleContentTransformForFrame(testStartTime, &layer, &viewTransformOut, pointOut);
+  apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut);
   EXPECT_EQ(pointOut, ScreenPoint());
   EXPECT_EQ(viewTransformOut, ViewTransform());
 }
 
 TEST(AsyncPanZoomController, Fling) {
   TimeStamp testStartTime = TimeStamp::Now();
   AsyncPanZoomController::SetFrameTime(testStartTime);
 
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<AsyncPanZoomController> apzc = new AsyncPanZoomController(mcc);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
+
+  apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
 
-  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_)).Times(2);
+  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(2);
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   int time = 0;
   int touchStart = 50;
   int touchEnd = 10;
-  TestAPZCContainerLayer layer;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
   // Fling down. Each step scroll further down
   ApzcPan(apzc, time, touchStart, touchEnd);
   ScreenPoint lastPoint;
   for (int i = 1; i < 50; i+=1) {
-    apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(i), &layer, &viewTransformOut, pointOut);
+    apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(i), &viewTransformOut, pointOut);
     EXPECT_GT(pointOut.y, lastPoint.y);
     lastPoint = pointOut;
   }
 }
 
 TEST(AsyncPanZoomController, OverScrollPanning) {
   TimeStamp testStartTime = TimeStamp::Now();
   AsyncPanZoomController::SetFrameTime(testStartTime);
 
   nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<AsyncPanZoomController> apzc = new AsyncPanZoomController(mcc);
+  nsRefPtr<TestAsyncPanZoomController> apzc = new TestAsyncPanZoomController(0, mcc);
+
+  apzc->SetFrameMetrics(TestFrameMetrics());
   apzc->NotifyLayersUpdated(TestFrameMetrics(), true);
 
-  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_)).Times(3);
+  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(3);
   EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
 
   // Pan sufficiently to hit overscroll behavior
   int time = 0;
   int touchStart = 500;
   int touchEnd = 10;
-  TestAPZCContainerLayer layer;
   ScreenPoint pointOut;
   ViewTransform viewTransformOut;
 
   // Pan down
   ApzcPan(apzc, time, touchStart, touchEnd);
-  apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(1000), &layer, &viewTransformOut, pointOut);
+  apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(1000), &viewTransformOut, pointOut);
   EXPECT_EQ(pointOut, ScreenPoint(0, 90));
 }
 
 static already_AddRefed<mozilla::layers::Layer>
 CreateTestLayerTree(nsRefPtr<LayerManager>& aLayerManager, nsTArray<nsRefPtr<Layer> >& aLayers) {
   const char* layerTreeSyntax = "c(ttccc(c(c)))";
   // LayerID                     0 12345 6 7
   nsIntRegion layerVisibleRegion[] = {
@@ -317,148 +341,128 @@ CreateTestLayerTree(nsRefPtr<LayerManage
     gfx3DMatrix(),
     gfx3DMatrix(),
     gfx3DMatrix(),
     gfx3DMatrix(),
   };
   return CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, aLayerManager, aLayers);
 }
 
-TEST(AsyncPanZoomController, GetAPZCAtPoint) {
+static void
+SetScrollableFrameMetrics(Layer* aLayer, FrameMetrics::ViewID aScrollId, MockContentController* mcc)
+{
+  ContainerLayer* container = aLayer->AsContainerLayer();
+  FrameMetrics metrics;
+  metrics.mScrollId = aScrollId;
+  nsIntRect layerBound = aLayer->GetVisibleRegion().GetBounds();
+  metrics.mCompositionBounds = ScreenIntRect(layerBound.x, layerBound.y,
+                                             layerBound.width, layerBound.height);
+  metrics.mViewport = CSSRect(layerBound.x, layerBound.y,
+                              layerBound.width, layerBound.height);
+  container->SetFrameMetrics(metrics);
+
+  // when we do the next tree update, a new APZC will be created for this layer,
+  // and that will invoke these functions once.
+  EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(1);
+  EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1);
+}
+
+TEST(APZCTreeManager, GetAPZCAtPoint) {
   nsTArray<nsRefPtr<Layer> > layers;
   nsRefPtr<LayerManager> lm;
   nsRefPtr<Layer> root = CreateTestLayerTree(lm, layers);
 
   TimeStamp testStartTime = TimeStamp::Now();
   AsyncPanZoomController::SetFrameTime(testStartTime);
+  nsRefPtr<MockContentController> mcc = new MockContentController();
+  ScopedLayerTreeRegistration controller(0, root, mcc);
 
-  nsRefPtr<MockContentController> mcc = new MockContentController();
-  nsRefPtr<AsyncPanZoomController> apzcMain = new AsyncPanZoomController(mcc);
-  nsRefPtr<AsyncPanZoomController> apzcSub3 = new AsyncPanZoomController(mcc);
-  nsRefPtr<AsyncPanZoomController> apzcSub4 = new AsyncPanZoomController(mcc);
-  nsRefPtr<AsyncPanZoomController> apzcSub7 = new AsyncPanZoomController(mcc);
-  apzcMain->NotifyLayersUpdated(TestFrameMetrics(), true);
-
-  nsIntRect layerBound;
-  ScreenIntPoint touchPoint(20, 20);
-  AsyncPanZoomController* apzcOut;
-  LayerIntPoint relativePointOut;
-
-  FrameMetrics scrollable;
+  nsRefPtr<APZCTreeManager> manager = new TestAPZCTreeManager();
 
   // No APZC attached so hit testing will return no APZC at (20,20)
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-
+  nsRefPtr<AsyncPanZoomController> hit = manager->GetTargetAPZC(ScreenPoint(20, 20));
   AsyncPanZoomController* nullAPZC = nullptr;
-  EXPECT_EQ(apzcOut, nullAPZC);
+  EXPECT_EQ(nullAPZC, hit.get());
 
   // Now we have a root APZC that will match the page
-  scrollable.mScrollId = FrameMetrics::ROOT_SCROLL_ID;
-  layerBound = root->GetVisibleRegion().GetBounds();
-  scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
-                                 layerBound.width, layerBound.height);
-  root->AsContainerLayer()->SetFrameMetrics(scrollable);
-  root->AsContainerLayer()->SetAsyncPanZoomController(apzcMain);
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-  EXPECT_EQ(apzcOut, apzcMain.get());
-  EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
+  SetScrollableFrameMetrics(root, FrameMetrics::ROOT_SCROLL_ID, mcc);
+  manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
+  hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
+  EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerIntPoint(15, 15)
 
   // Now we have a sub APZC with a better fit
-  scrollable.mScrollId = FrameMetrics::START_SCROLL_ID;
-  layerBound = layers[3]->GetVisibleRegion().GetBounds();
-  scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
-                                 layerBound.width, layerBound.height);
-  layers[3]->AsContainerLayer()->SetFrameMetrics(scrollable);
-  layers[3]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub3);
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-  EXPECT_EQ(apzcOut, apzcSub3.get());
-  EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
+  SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID, mcc);
+  manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
+  EXPECT_NE(root->AsContainerLayer()->GetAsyncPanZoomController(), layers[3]->AsContainerLayer()->GetAsyncPanZoomController());
+  hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
+  EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerIntPoint(15, 15)
 
   // Now test hit testing when we have two scrollable layers
-  touchPoint = ScreenIntPoint(15,15);
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-  EXPECT_EQ(apzcOut, apzcSub3.get()); // We haven't bound apzcSub4 yet
-  scrollable.mScrollId++;
-  layerBound = layers[4]->GetVisibleRegion().GetBounds();
-  scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
-                                 layerBound.width, layerBound.height);
-  layers[4]->AsContainerLayer()->SetFrameMetrics(scrollable);
-  layers[4]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub4);
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-  EXPECT_EQ(apzcOut, apzcSub4.get());
-  EXPECT_EQ(LayerIntPoint(15, 15), relativePointOut);
+  hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
+  EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 1, mcc);
+  manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
+  hit = manager->GetTargetAPZC(ScreenPoint(15, 15));
+  EXPECT_EQ(layers[4]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerIntPoint(15, 15)
 
-  // Hit test ouside the reach of apzc3/4 but inside apzcMain
-  touchPoint = ScreenIntPoint(90,90);
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-  EXPECT_EQ(apzcOut, apzcMain.get());
-  EXPECT_EQ(LayerIntPoint(90, 90), relativePointOut);
+  // Hit test ouside the reach of layer[3,4] but inside root
+  hit = manager->GetTargetAPZC(ScreenPoint(90, 90));
+  EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerIntPoint(90, 90)
 
   // Hit test ouside the reach of any layer
-  touchPoint = ScreenIntPoint(1000,10);
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-  EXPECT_EQ(apzcOut, nullAPZC);
-  touchPoint = ScreenIntPoint(-1000,10);
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-  EXPECT_EQ(apzcOut, nullAPZC);
+  hit = manager->GetTargetAPZC(ScreenPoint(1000, 10));
+  EXPECT_EQ(nullAPZC, hit.get());
+  hit = manager->GetTargetAPZC(ScreenPoint(-1000, 10));
+  EXPECT_EQ(nullAPZC, hit.get());
 
   // Test layer transform
   gfx3DMatrix transform;
   transform.ScalePost(0.1, 0.1, 1);
   root->SetBaseTransform(transform);
-
-  touchPoint = ScreenIntPoint(50,50); // This point is now outside the root layer
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-  EXPECT_EQ(apzcOut, nullAPZC);
+  root->ComputeEffectiveTransforms(gfx3DMatrix());
+  manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
+  hit = manager->GetTargetAPZC(ScreenPoint(50, 50)); // This point is now outside the root layer
+  EXPECT_EQ(nullAPZC, hit.get());
 
-  touchPoint = ScreenIntPoint(2,2);
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-  EXPECT_EQ(apzcOut, apzcSub4.get());
-  EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
+  hit = manager->GetTargetAPZC(ScreenPoint(2, 2));
+  EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerPoint(20, 20)
 
   // Scale layer[4] outside the range
   layers[4]->SetBaseTransform(transform);
   // layer 4 effective visible screenrect: (0.05, 0.05, 0.2, 0.2)
   // Does not contain (2, 2)
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-  EXPECT_EQ(apzcOut, apzcSub3.get());
-  EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut);
+  root->ComputeEffectiveTransforms(gfx3DMatrix());
+  manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
+  hit = manager->GetTargetAPZC(ScreenPoint(2, 2));
+  EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerPoint(20, 20)
 
   // Transformation chain to layer 7
-  scrollable.mScrollId++;
-  layerBound = layers[7]->GetVisibleRegion().GetBounds();
-  scrollable.mViewport = CSSRect(layerBound.x, layerBound.y,
-                                 layerBound.width, layerBound.height);
-  layers[7]->AsContainerLayer()->SetFrameMetrics(scrollable);
-  layers[7]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub7);
+  SetScrollableFrameMetrics(layers[7], FrameMetrics::START_SCROLL_ID + 2, mcc);
 
   gfx3DMatrix translateTransform;
   translateTransform.Translate(gfxPoint3D(10, 10, 0));
   layers[5]->SetBaseTransform(translateTransform);
 
   gfx3DMatrix translateTransform2;
   translateTransform2.Translate(gfxPoint3D(-20, 0, 0));
   layers[6]->SetBaseTransform(translateTransform2);
 
   gfx3DMatrix translateTransform3;
   translateTransform3.ScalePost(1,15,1);
   layers[7]->SetBaseTransform(translateTransform3);
 
-  touchPoint = ScreenIntPoint(1,45);
+  root->ComputeEffectiveTransforms(gfx3DMatrix());
+  manager->UpdatePanZoomControllerTree(nullptr, root, 0, false);
   // layer 7 effective visible screenrect (0,16,4,60) but clipped by parent layers
-  AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint,
-                 &apzcOut, &relativePointOut);
-  EXPECT_EQ(apzcOut, apzcSub7.get());
-  EXPECT_EQ(LayerIntPoint(20, 29), relativePointOut);
+  hit = manager->GetTargetAPZC(ScreenPoint(1, 45));
+  EXPECT_EQ(layers[7]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get());
+  // expect hit point at LayerPoint(20, 29)
+
+  manager->ClearTree();
 }
 
 
--- a/gfx/tests/gtest/TestLayers.cpp
+++ b/gfx/tests/gtest/TestLayers.cpp
@@ -43,16 +43,17 @@ public:
     return "TestContainerLayer";
   }
 
   virtual LayerType GetType() const {
     return TYPE_CONTAINER;
   }
 
   virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface) {
+    DefaultComputeEffectiveTransforms(aTransformToSurface);
   }
 
   virtual void RepositionChild(Layer* aChild, Layer* aAfter) {
     MOZ_CRASH();
   }
 
   virtual void InsertAfter(Layer* aChild, Layer* aAfter) {
     // Bad implementation but it should be fine for testing
@@ -87,20 +88,16 @@ public:
   virtual const char* Name() const {
     return "TestThebesLayer";
   }
 
   virtual LayerType GetType() const {
     return TYPE_THEBES;
   }
 
-  virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface) {
-    MOZ_CRASH();
-  }
-
   virtual void InvalidateRegion(const nsIntRegion& aRegion) {
     MOZ_CRASH();
   }
 };
 
 class TestUserData: public LayerUserData {
 public:
   MOCK_METHOD0(Die, void());
@@ -231,16 +228,19 @@ already_AddRefed<Layer> CreateLayerTree(
       }
       if (parentContainerLayer) {
         parentContainerLayer->InsertAfter(layer, nullptr);
         layer->SetParent(parentContainerLayer);
       }
       lastLayer = layer;
     }
   }
+  if (rootLayer) {
+    rootLayer->ComputeEffectiveTransforms(gfx3DMatrix());
+  }
   return rootLayer.forget();
 }
 
 TEST(Layers, LayerTree) {
   const char* layerTreeSyntax = "c(c(tt))";
   nsIntRegion layerVisibleRegion[] = {
     nsIntRegion(nsIntRect(0,0,100,100)),
     nsIntRegion(nsIntRect(0,0,100,100)),
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -464,16 +464,33 @@ nsLayoutUtils::FindContentFor(ViewID aId
 
   if (exists) {
     return content;
   } else {
     return nullptr;
   }
 }
 
+nsIScrollableFrame*
+nsLayoutUtils::FindScrollableFrameFor(ViewID aId)
+{
+  nsIContent* content = FindContentFor(aId);
+  if (!content) {
+    return nullptr;
+  }
+
+  nsIFrame* scrolledFrame = content->GetPrimaryFrame();
+  if (scrolledFrame && content->OwnerDoc()->GetRootElement() == content) {
+    // The content is the root element of a subdocument, so return the root scrollable
+    // for the subdocument.
+    scrolledFrame = scrolledFrame->PresContext()->PresShell()->GetRootScrollFrame();
+  }
+  return scrolledFrame ? scrolledFrame->GetScrollTargetFrame() : nullptr;
+}
+
 bool
 nsLayoutUtils::GetDisplayPort(nsIContent* aContent, nsRect *aResult)
 {
   void* property = aContent->GetProperty(nsGkAtoms::DisplayPort);
   if (!property) {
     return false;
   }
 
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -72,16 +72,21 @@ public:
   static ViewID FindIDFor(nsIContent* aContent);
 
   /**
    * Find content for given ID.
    */
   static nsIContent* FindContentFor(ViewID aId);
 
   /**
+   * Find the scrollable frame for a given ID.
+   */
+  static nsIScrollableFrame* FindScrollableFrameFor(ViewID aId);
+
+  /**
    * Get display port for the given element.
    */
   static bool GetDisplayPort(nsIContent* aContent, nsRect *aResult);
 
   /**
    * Get the critical display port for the given element.
    */
   static bool GetCriticalDisplayPort(nsIContent* aContent, nsRect* aResult);
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -10,17 +10,17 @@
 #include "BasicLayers.h"
 #include "gfx3DMatrix.h"
 #include "LayerManagerOGL.h"
 #ifdef MOZ_ENABLE_D3D9_LAYER
 # include "LayerManagerD3D9.h"
 #endif //MOZ_ENABLE_D3D9_LAYER
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/dom/TabParent.h"
-#include "mozilla/layers/AsyncPanZoomController.h"
+#include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/LayerTransactionParent.h"
 #include "nsContentUtils.h"
 #include "nsFrameLoader.h"
 #include "nsIObserver.h"
 #include "nsSubDocumentFrame.h"
 #include "nsView.h"
 #include "nsViewportFrame.h"
@@ -547,28 +547,29 @@ public:
     if (mRenderFrame) {
       TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
       browser->HandleLongTap(aPoint);
     }
   }
 
   void ClearRenderFrame() { mRenderFrame = nullptr; }
 
-  virtual void SendAsyncScrollDOMEvent(const CSSRect& aContentRect,
+  virtual void SendAsyncScrollDOMEvent(FrameMetrics::ViewID aScrollId,
+                                       const CSSRect& aContentRect,
                                        const CSSSize& aContentSize) MOZ_OVERRIDE
   {
     if (MessageLoop::current() != mUILoop) {
       mUILoop->PostTask(
         FROM_HERE,
         NewRunnableMethod(this,
                           &RemoteContentController::SendAsyncScrollDOMEvent,
-                          aContentRect, aContentSize));
+                          aScrollId, aContentRect, aContentSize));
       return;
     }
-    if (mRenderFrame) {
+    if (mRenderFrame && aScrollId == FrameMetrics::ROOT_SCROLL_ID) {
       TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
       BrowserElementParent::DispatchAsyncScrollEvent(browser, aContentRect,
                                                      aContentSize);
     }
   }
 
   virtual void PostDelayedTask(Task* aTask, int aDelayMs) MOZ_OVERRIDE
   {
@@ -615,24 +616,34 @@ RenderFrameParent::RenderFrameParent(nsF
     // and we'll keep an indirect reference to that tree.
     *aId = mLayersId = CompositorParent::AllocateLayerTreeId();
     if (lm && lm->GetBackendType() == LAYERS_CLIENT) {
       ClientLayerManager *clientManager = static_cast<ClientLayerManager*>(lm.get());
       clientManager->GetRemoteRenderer()->SendNotifyChildCreated(mLayersId);
     }
     if (aScrollingBehavior == ASYNC_PAN_ZOOM) {
       mContentController = new RemoteContentController(this);
-      mPanZoomController = new AsyncPanZoomController(
-        mContentController, AsyncPanZoomController::USE_GESTURE_DETECTOR);
-      CompositorParent::SetPanZoomControllerForLayerTree(mLayersId,
-                                                         mPanZoomController);
+      CompositorParent::SetControllerForLayerTree(mLayersId, mContentController);
     }
   }
 }
 
+APZCTreeManager*
+RenderFrameParent::GetApzcTreeManager()
+{
+  // We can't get a ref to the APZCTreeManager until after the child is
+  // created and the static getter knows which CompositorParent is
+  // instantiated with this layers ID. That's why try to fetch it when
+  // we first need it and cache the result.
+  if (!mApzcTreeManager) {
+    mApzcTreeManager = CompositorParent::GetAPZCTreeManager(mLayersId);
+  }
+  return mApzcTreeManager.get();
+}
+
 RenderFrameParent::~RenderFrameParent()
 {}
 
 void
 RenderFrameParent::Destroy()
 {
   size_t numChildren = ManagedPLayerTransactionParent().Length();
   NS_ABORT_IF_FALSE(0 == numChildren || 1 == numChildren,
@@ -782,40 +793,40 @@ RenderFrameParent::OwnerContentChanged(n
                     "Don't build new map if owner is same!");
   BuildViewMap();
 }
 
 void
 RenderFrameParent::NotifyInputEvent(const nsInputEvent& aEvent,
                                     nsInputEvent* aOutEvent)
 {
-  if (mPanZoomController) {
-    mPanZoomController->ReceiveInputEvent(aEvent, aOutEvent);
+  if (GetApzcTreeManager()) {
+    GetApzcTreeManager()->ReceiveInputEvent(aEvent, aOutEvent);
   }
 }
 
 void
 RenderFrameParent::NotifyDimensionsChanged(ScreenIntSize size)
 {
-  if (mPanZoomController) {
-    mPanZoomController->UpdateCompositionBounds(
-      ScreenIntRect(ScreenIntPoint(), size));
+  if (GetApzcTreeManager()) {
+    GetApzcTreeManager()->UpdateCompositionBounds(ScrollableLayerGuid(mLayersId),
+                                                  ScreenIntRect(ScreenIntPoint(), size));
   }
 }
 
 void
 RenderFrameParent::ActorDestroy(ActorDestroyReason why)
 {
   if (mLayersId != 0) {
     CompositorParent::DeallocateLayerTreeId(mLayersId);
     if (mContentController) {
       // Stop our content controller from requesting repaints of our
       // content.
       mContentController->ClearRenderFrame();
-      mPanZoomController->Destroy();
+      // TODO: notify the compositor?
     }
   }
 
   if (mFrameLoader && mFrameLoader->GetCurrentRemoteFrame() == this) {
     // XXX this might cause some weird issues ... we'll just not
     // redraw the part of the window covered by this until the "next"
     // remote frame has a layer-tree transaction.  For
     // why==NormalShutdown, we'll definitely want to do something
@@ -831,27 +842,27 @@ RenderFrameParent::RecvNotifyCompositorT
 {
   TriggerRepaint();
   return true;
 }
 
 bool
 RenderFrameParent::RecvCancelDefaultPanZoom()
 {
-  if (mPanZoomController) {
-    mPanZoomController->CancelDefaultPanZoom();
+  if (GetApzcTreeManager()) {
+    GetApzcTreeManager()->CancelDefaultPanZoom(ScrollableLayerGuid(mLayersId));
   }
   return true;
 }
 
 bool
 RenderFrameParent::RecvDetectScrollableSubframe()
 {
-  if (mPanZoomController) {
-    mPanZoomController->DetectScrollableSubframe();
+  if (GetApzcTreeManager()) {
+    GetApzcTreeManager()->DetectScrollableSubframe(ScrollableLayerGuid(mLayersId));
   }
   return true;
 }
 
 PLayerTransactionParent*
 RenderFrameParent::AllocPLayerTransactionParent()
 {
   if (!mFrameLoader || mFrameLoaderDestroyed) {
@@ -967,34 +978,37 @@ RenderFrameParent::BuildDisplayList(nsDi
     aLists.Content()->AppendToTop(
       new (aBuilder) nsDisplayRemote(aBuilder, aFrame, this));
   }
 }
 
 void
 RenderFrameParent::ZoomToRect(const CSSRect& aRect)
 {
-  if (mPanZoomController) {
-    mPanZoomController->ZoomToRect(aRect);
+  if (GetApzcTreeManager()) {
+    GetApzcTreeManager()->ZoomToRect(ScrollableLayerGuid(mLayersId),
+                                     aRect);
   }
 }
 
 void
 RenderFrameParent::ContentReceivedTouch(bool aPreventDefault)
 {
-  if (mPanZoomController) {
-    mPanZoomController->ContentReceivedTouch(aPreventDefault);
+  if (GetApzcTreeManager()) {
+    GetApzcTreeManager()->ContentReceivedTouch(ScrollableLayerGuid(mLayersId),
+                                               aPreventDefault);
   }
 }
 
 void
 RenderFrameParent::UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom)
 {
-  if (mPanZoomController) {
-    mPanZoomController->UpdateZoomConstraints(aAllowZoom, aMinZoom, aMaxZoom);
+  if (GetApzcTreeManager()) {
+    GetApzcTreeManager()->UpdateZoomConstraints(ScrollableLayerGuid(mLayersId),
+                                                aAllowZoom, aMinZoom, aMaxZoom);
   }
 }
 
 }  // namespace layout
 }  // namespace mozilla
 
 already_AddRefed<Layer>
 nsDisplayRemote::BuildLayer(nsDisplayListBuilder* aBuilder,
--- a/layout/ipc/RenderFrameParent.h
+++ b/layout/ipc/RenderFrameParent.h
@@ -20,17 +20,17 @@ class nsContentView;
 class nsFrameLoader;
 class nsSubDocumentFrame;
 
 namespace mozilla {
 
 class InputEvent;
 
 namespace layers {
-class AsyncPanZoomController;
+class APZCTreeManager;
 class GestureEventListener;
 class TargetConfig;
 class LayerTransactionParent;
 struct TextureFactoryIdentifier;
 }
 
 namespace layout {
 
@@ -127,21 +127,23 @@ private:
   // When our child frame is pushing transactions directly to the
   // compositor, this is the ID of its layer tree in the compositor's
   // context.
   uint64_t mLayersId;
 
   nsRefPtr<nsFrameLoader> mFrameLoader;
   nsRefPtr<ContainerLayer> mContainer;
   // When our scrolling behavior is ASYNC_PAN_ZOOM, we have a nonnull
-  // AsyncPanZoomController.  It's associated with the shadow layer
-  // tree on the compositor thread.
-  nsRefPtr<layers::AsyncPanZoomController> mPanZoomController;
+  // APZCTreeManager. It's used to manipulate the shadow layer tree
+  // on the compositor thread.
+  nsRefPtr<layers::APZCTreeManager> mApzcTreeManager;
   nsRefPtr<RemoteContentController> mContentController;
 
+  layers::APZCTreeManager* GetApzcTreeManager();
+
   // This contains the views for all the scrollable frames currently in the
   // painted region of our remote content.
   ViewMap mContentViews;
 
   // True after Destroy() has been called, which is triggered
   // originally by nsFrameLoader::Destroy().  After this point, we can
   // no longer safely ask the frame loader to find its nearest layer
   // manager, because it may have been disconnected from the DOM.
--- a/toolkit/library/Makefile.in
+++ b/toolkit/library/Makefile.in
@@ -91,16 +91,19 @@ STATIC_LIBS += \
   $(NULL)
 
 ifdef MOZ_B2G_RIL #{
 STATIC_LIBS += mozril_s
 endif #}
 
 ifdef MOZ_B2G_BT #{
 STATIC_LIBS += mozdbus_s
+ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
+OS_LIBS += -ldbus
+endif
 endif #}
 
 ifneq ($(strip $(MOZ_B2G_RIL)$(MOZ_B2G_BT)),) #{
 STATIC_LIBS += mozipcunixsocket_s
 endif #}
 
 ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
 STATIC_LIBS += moznetd_s
@@ -441,17 +444,16 @@ OS_LIBS += \
   -lmedia \
   -lhardware_legacy \
   -lhardware \
   -lutils \
   -lcutils \
   -lsysutils \
   -lcamera_client \
   -lsensorservice \
-  -ldbus \
   -lstagefright \
   -lstagefright_foundation \
   -lstagefright_omx \
   -lbinder \
   -lgui \
   $(NULL)
 endif
 
--- a/widget/android/AndroidBridge.cpp
+++ b/widget/android/AndroidBridge.cpp
@@ -2845,17 +2845,19 @@ void
 AndroidBridge::HandleLongTap(const CSSIntPoint& aPoint)
 {
     nsCString data = nsPrintfCString("{ \"x\": %d, \"y\": %d }", aPoint.x, aPoint.y);
     nsAppShell::gAppShell->PostEvent(AndroidGeckoEvent::MakeBroadcastEvent(
             NS_LITERAL_CSTRING("Gesture:LongPress"), data));
 }
 
 void
-AndroidBridge::SendAsyncScrollDOMEvent(const CSSRect& aContentRect, const CSSSize& aScrollableSize)
+AndroidBridge::SendAsyncScrollDOMEvent(mozilla::layers::FrameMetrics::ViewID aScrollId,
+                                       const CSSRect& aContentRect,
+                                       const CSSSize& aScrollableSize)
 {
     // FIXME implement this
 }
 
 void
 AndroidBridge::PostDelayedTask(Task* aTask, int aDelayMs)
 {
     JNIEnv* env = GetJNIForThread();
--- a/widget/android/AndroidBridge.h
+++ b/widget/android/AndroidBridge.h
@@ -596,17 +596,19 @@ private:
 
 public:
     jobject SetNativePanZoomController(jobject obj);
     // GeckoContentController methods
     void RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrameMetrics) MOZ_OVERRIDE;
     void HandleDoubleTap(const CSSIntPoint& aPoint) MOZ_OVERRIDE;
     void HandleSingleTap(const CSSIntPoint& aPoint) MOZ_OVERRIDE;
     void HandleLongTap(const CSSIntPoint& aPoint) MOZ_OVERRIDE;
-    void SendAsyncScrollDOMEvent(const CSSRect& aContentRect, const CSSSize& aScrollableSize) MOZ_OVERRIDE;
+    void SendAsyncScrollDOMEvent(mozilla::layers::FrameMetrics::ViewID aScrollId,
+                                 const CSSRect& aContentRect,
+                                 const CSSSize& aScrollableSize) MOZ_OVERRIDE;
     void PostDelayedTask(Task* aTask, int aDelayMs) MOZ_OVERRIDE;
     int64_t RunDelayedTasks();
 };
 
 class AutoJObject {
 public:
     AutoJObject(JNIEnv* aJNIEnv = NULL) : mObject(NULL)
     {
--- a/widget/android/AndroidJNI.cpp
+++ b/widget/android/AndroidJNI.cpp
@@ -30,17 +30,17 @@
 
 #include "mozilla/unused.h"
 
 #include "mozilla/dom/SmsMessage.h"
 #include "mozilla/dom/mobilemessage/Constants.h"
 #include "mozilla/dom/mobilemessage/Types.h"
 #include "mozilla/dom/mobilemessage/PSms.h"
 #include "mozilla/dom/mobilemessage/SmsParent.h"
-#include "mozilla/layers/AsyncPanZoomController.h"
+#include "mozilla/layers/APZCTreeManager.h"
 #include "nsIMobileMessageDatabaseService.h"
 #include "nsPluginInstanceOwner.h"
 #include "nsSurfaceTexture.h"
 #include "GeckoProfiler.h"
 
 #include "GeckoProfiler.h"
 
 using namespace mozilla;
@@ -842,41 +842,40 @@ NS_EXPORT jdouble JNICALL
 Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(JNIEnv *jenv, jclass jc)
 {
   return profiler_time();
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_abortAnimation(JNIEnv* env, jobject instance)
 {
-  AsyncPanZoomController* controller = nsWindow::GetPanZoomController();
-  if (controller) {
-      controller->CancelAnimation();
-  }
+    APZCTreeManager *controller = nsWindow::GetAPZCTreeManager();
+    if (controller) {
+        controller->CancelAnimation(ScrollableLayerGuid(nsWindow::RootLayerTreeId()));
+    }
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_init(JNIEnv* env, jobject instance)
 {
     if (!AndroidBridge::Bridge()) {
         return;
     }
 
     jobject oldRef = AndroidBridge::Bridge()->SetNativePanZoomController(env->NewGlobalRef(instance));
     if (oldRef) {
         MOZ_ASSERT(false, "Registering a new NPZC when we already have one");
         env->DeleteGlobalRef(oldRef);
     }
-    nsWindow::SetPanZoomController(new AsyncPanZoomController(AndroidBridge::Bridge(), AsyncPanZoomController::USE_GESTURE_DETECTOR));
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_handleTouchEvent(JNIEnv* env, jobject instance, jobject event)
 {
-    AsyncPanZoomController *controller = nsWindow::GetPanZoomController();
+    APZCTreeManager *controller = nsWindow::GetAPZCTreeManager();
     if (controller) {
         AndroidGeckoEvent* wrapper = AndroidGeckoEvent::MakeFromJavaObject(env, event);
         const MultiTouchInput& input = wrapper->MakeMultiTouchInput(nsWindow::TopWindow());
         delete wrapper;
         if (input.mType >= 0) {
             controller->ReceiveInputEvent(input);
         }
     }
@@ -900,31 +899,30 @@ Java_org_mozilla_gecko_gfx_NativePanZoom
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_destroy(JNIEnv* env, jobject instance)
 {
     if (!AndroidBridge::Bridge()) {
         return;
     }
 
-    nsWindow::SetPanZoomController(nullptr);
     jobject oldRef = AndroidBridge::Bridge()->SetNativePanZoomController(NULL);
     if (!oldRef) {
         MOZ_ASSERT(false, "Clearing a non-existent NPZC");
     } else {
         env->DeleteGlobalRef(oldRef);
     }
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_notifyDefaultActionPrevented(JNIEnv* env, jobject instance, jboolean prevented)
 {
-    AsyncPanZoomController *controller = nsWindow::GetPanZoomController();
+    APZCTreeManager *controller = nsWindow::GetAPZCTreeManager();
     if (controller) {
-        controller->ContentReceivedTouch(prevented);
+        controller->ContentReceivedTouch(ScrollableLayerGuid(nsWindow::RootLayerTreeId()), prevented);
     }
 }
 
 NS_EXPORT jboolean JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_getRedrawHint(JNIEnv* env, jobject instance)
 {
     // FIXME implement this
     return true;
@@ -941,19 +939,19 @@ Java_org_mozilla_gecko_gfx_NativePanZoom
 {
     // FIXME implement this
     return 0;
 }
 
 NS_EXPORT void JNICALL
 Java_org_mozilla_gecko_gfx_NativePanZoomController_updateScrollOffset(JNIEnv* env, jobject instance, jfloat cssX, jfloat cssY)
 {
-    AsyncPanZoomController* controller = nsWindow::GetPanZoomController();
+    APZCTreeManager *controller = nsWindow::GetAPZCTreeManager();
     if (controller) {
-        controller->UpdateScrollOffset(CSSPoint(cssX, cssY));
+        controller->UpdateScrollOffset(ScrollableLayerGuid(nsWindow::RootLayerTreeId()), CSSPoint(cssX, cssY));
     }
 }
 
 NS_EXPORT jboolean JNICALL
 Java_org_mozilla_gecko_ANRReporter_requestNativeStack(JNIEnv*, jclass)
 {
     if (profiler_is_active()) {
         // Don't proceed if profiler is already running
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -2412,21 +2412,21 @@ nsWindow::DrawWindowOverlay(LayerManager
     if (!mLayerRendererFrame.DrawForeground(&jniFrame)) return;
     if (!mLayerRendererFrame.EndDrawing(&jniFrame)) return;
     if (!client.DeactivateProgram(&jniFrame)) return;
     mLayerRendererFrame.Dispose(env);
 }
 
 // off-main-thread compositor fields and functions
 
+nsRefPtr<mozilla::layers::APZCTreeManager> nsWindow::sApzcTreeManager = 0;
 nsRefPtr<mozilla::layers::LayerManager> nsWindow::sLayerManager = 0;
 nsRefPtr<mozilla::layers::CompositorParent> nsWindow::sCompositorParent = 0;
 nsRefPtr<mozilla::layers::CompositorChild> nsWindow::sCompositorChild = 0;
 bool nsWindow::sCompositorPaused = true;
-nsRefPtr<mozilla::layers::AsyncPanZoomController> nsWindow::sApzc = 0;
 
 void
 nsWindow::SetCompositor(mozilla::layers::LayerManager* aLayerManager,
                         mozilla::layers::CompositorParent* aCompositorParent,
                         mozilla::layers::CompositorChild* aCompositorChild)
 {
     sLayerManager = aLayerManager;
     sCompositorParent = aCompositorParent;
@@ -2491,57 +2491,35 @@ bool
 nsWindow::NeedsPaint()
 {
   if (sCompositorPaused || FindTopLevel() != nsWindow::TopWindow() || !GetLayerManager(nullptr)) {
     return false;
   }
   return nsIWidget::NeedsPaint();
 }
 
-class AndroidCompositorParent : public CompositorParent {
-public:
-    AndroidCompositorParent(nsIWidget* aWidget, bool aRenderToEGLSurface,
-                            int aSurfaceWidth, int aSurfaceHeight)
-        : CompositorParent(aWidget, aRenderToEGLSurface, aSurfaceWidth, aSurfaceHeight)
-    {
-        if (nsWindow::GetPanZoomController()) {
-            nsWindow::GetPanZoomController()->SetCompositorParent(this);
-        }
-    }
-
-    virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, const TargetConfig& aTargetConfig,
-                                     bool isFirstPaint) MOZ_OVERRIDE
-    {
-        CompositorParent::ShadowLayersUpdated(aLayerTree, aTargetConfig, isFirstPaint);
-        Layer* targetLayer = GetLayerManager()->GetPrimaryScrollableLayer();
-        AsyncPanZoomController* controller = nsWindow::GetPanZoomController();
-        if (targetLayer && targetLayer->AsContainerLayer() && controller) {
-            targetLayer->AsContainerLayer()->SetAsyncPanZoomController(controller);
-            controller->NotifyLayersUpdated(targetLayer->AsContainerLayer()->GetFrameMetrics(), isFirstPaint);
-        }
-    }
-};
-
 CompositorParent*
 nsWindow::NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight)
 {
-    return new AndroidCompositorParent(this, true, aSurfaceWidth, aSurfaceHeight);
+    return new CompositorParent(this, true, aSurfaceWidth, aSurfaceHeight);
 }
 
-void
-nsWindow::SetPanZoomController(AsyncPanZoomController* apzc)
+mozilla::layers::APZCTreeManager*
+nsWindow::GetAPZCTreeManager()
 {
-    if (sApzc) {
-        sApzc->SetCompositorParent(nullptr);
-        sApzc = nullptr;
+    if (!sApzcTreeManager) {
+        CompositorParent* compositor = sCompositorParent;
+        if (!compositor) {
+            return nullptr;
+        }
+        uint64_t rootLayerTreeId = compositor->RootLayerTreeId();
+        CompositorParent::SetControllerForLayerTree(rootLayerTreeId, AndroidBridge::Bridge());
+        sApzcTreeManager = CompositorParent::GetAPZCTreeManager(rootLayerTreeId);
     }
-    if (apzc) {
-        sApzc = apzc;
-        sApzc->SetCompositorParent(sCompositorParent);
-    }
+    return sApzcTreeManager;
 }
 
-AsyncPanZoomController*
-nsWindow::GetPanZoomController()
+uint64_t
+nsWindow::RootLayerTreeId()
 {
-    return sApzc;
+    MOZ_ASSERT(sCompositorParent);
+    return sCompositorParent->RootLayerTreeId();
 }
-
--- a/widget/android/nsWindow.h
+++ b/widget/android/nsWindow.h
@@ -18,17 +18,17 @@ struct ANPEvent;
 
 namespace mozilla {
     class AndroidGeckoEvent;
 
     namespace layers {
         class CompositorParent;
         class CompositorChild;
         class LayerManager;
-        class AsyncPanZoomController;
+        class APZCTreeManager;
     }
 }
 
 class nsWindow :
     public nsBaseWidget
 {
 public:
     using nsBaseWidget::GetLayerManager;
@@ -150,18 +150,19 @@ public:
 
     static void SetCompositor(mozilla::layers::LayerManager* aLayerManager,
                               mozilla::layers::CompositorParent* aCompositorParent,
                               mozilla::layers::CompositorChild* aCompositorChild);
     static void ScheduleComposite();
     static void ScheduleResumeComposition(int width, int height);
     static void ForceIsFirstPaint();
     static float ComputeRenderIntegrity();
-    static void SetPanZoomController(mozilla::layers::AsyncPanZoomController* apzc);
-    static mozilla::layers::AsyncPanZoomController* GetPanZoomController();
+    static mozilla::layers::APZCTreeManager* GetAPZCTreeManager();
+    /* RootLayerTreeId() can only be called when GetAPZCTreeManager() returns non-null */
+    static uint64_t RootLayerTreeId();
 
     virtual bool WidgetPaintsBackground();
 
 protected:
     void BringToFront();
     nsWindow *FindTopLevel();
     bool DrawTo(gfxASurface *targetSurface);
     bool DrawTo(gfxASurface *targetSurface, const nsIntRect &aRect);
@@ -226,16 +227,16 @@ private:
     void DispatchGestureEvent(uint32_t msg, uint32_t direction, double delta,
                               const nsIntPoint &refPoint, uint64_t time);
     void HandleSpecialKey(mozilla::AndroidGeckoEvent *ae);
     void CreateLayerManager(int aCompositorWidth, int aCompositorHeight);
     void RedrawAll();
 
     mozilla::AndroidLayerRendererFrame mLayerRendererFrame;
 
+    static nsRefPtr<mozilla::layers::APZCTreeManager> sApzcTreeManager;
     static nsRefPtr<mozilla::layers::LayerManager> sLayerManager;
     static nsRefPtr<mozilla::layers::CompositorParent> sCompositorParent;
     static nsRefPtr<mozilla::layers::CompositorChild> sCompositorChild;
     static bool sCompositorPaused;
-    static nsRefPtr<mozilla::layers::AsyncPanZoomController> sApzc;
 };
 
 #endif /* NSWINDOW_H_ */
--- a/widget/gonk/libdisplay/FramebufferSurface.cpp
+++ b/widget/gonk/libdisplay/FramebufferSurface.cpp
@@ -24,17 +24,16 @@
 
 #include <utils/String8.h>
 
 #include <ui/Rect.h>
 
 #include <EGL/egl.h>
 
 #include <hardware/hardware.h>
-#include <gui/SurfaceTextureClient.h>
 #include <ui/GraphicBuffer.h>
 
 #include "FramebufferSurface.h"
 #include "GraphicBufferAlloc.h"
 
 #ifndef NUM_FRAMEBUFFER_SURFACE_BUFFERS
 #define NUM_FRAMEBUFFER_SURFACE_BUFFERS (2)
 #endif
--- a/widget/gonk/libdisplay/GonkDisplayJB.cpp
+++ b/widget/gonk/libdisplay/GonkDisplayJB.cpp
@@ -9,17 +9,17 @@
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "GonkDisplayJB.h"
-#include <gui/SurfaceTextureClient.h>
+#include <gui/Surface.h>
 
 #include <hardware/hardware.h>
 #include <hardware/hwcomposer.h>
 #include <hardware/power.h>
 #include <suspend/autosuspend.h>
 
 #include "GraphicBufferAlloc.h"
 #include "BootAnimation.h"
@@ -94,17 +94,17 @@ GonkDisplayJB::GonkDisplayJB()
                                            (hw_module_t const**)&mPowerModule);
     if (!err)
         mPowerModule->init(mPowerModule);
     ALOGW_IF(err, "Couldn't load %s module (%s)", POWER_HARDWARE_MODULE_ID, strerror(-err));
 
     mAlloc = new GraphicBufferAlloc();
     mFBSurface = new FramebufferSurface(0, mWidth, mHeight, surfaceformat, mAlloc);
 
-    sp<SurfaceTextureClient> stc = new SurfaceTextureClient(static_cast<sp<ISurfaceTexture> >(mFBSurface->getBufferQueue()));
+    sp<Surface> stc = new Surface(static_cast<sp<IGraphicBufferProducer> >(mFBSurface->getBufferQueue()));
     mSTClient = stc;
 
     mList = (hwc_display_contents_1_t *)malloc(sizeof(*mList) + (sizeof(hwc_layer_1_t)*2));
     if (mHwc)
         mHwc->blank(mHwc, HWC_DISPLAY_PRIMARY, 0);
 
     status_t error;
     mBootAnimBuffer = mAlloc->createGraphicBuffer(mWidth, mHeight, surfaceformat, GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_COMPOSER, &error);
@@ -161,18 +161,18 @@ GonkDisplayJB::GetHWCDevice()
 }
 
 bool
 GonkDisplayJB::SwapBuffers(EGLDisplay dpy, EGLSurface sur)
 {
     StopBootAnimation();
     mBootAnimBuffer = nullptr;
 
-    mList->dpy = dpy;
-    mList->sur = sur;
+    mList->outbuf = nullptr;
+    mList->outbufAcquireFenceFd = -1;
     eglSwapBuffers(dpy, sur);
     return Post(mFBSurface->lastHandle, mFBSurface->lastFenceFD);
 }
 
 bool
 GonkDisplayJB::Post(buffer_handle_t buf, int fence)
 {
     if (!mHwc) {
@@ -184,30 +184,34 @@ GonkDisplayJB::Post(buffer_handle_t buf,
     hwc_display_contents_1_t *displays[HWC_NUM_DISPLAY_TYPES] = {NULL};
     const hwc_rect_t r = { 0, 0, mWidth, mHeight };
     displays[HWC_DISPLAY_PRIMARY] = mList;
     mList->retireFenceFd = -1;
     mList->numHwLayers = 2;
     mList->flags = HWC_GEOMETRY_CHANGED;
     mList->hwLayers[0].compositionType = HWC_BACKGROUND;
     mList->hwLayers[0].hints = 0;
-    mList->hwLayers[0].flags = 0;
+    /* Skip this layer so the hwc module doesn't complain about null handles */
+    mList->hwLayers[0].flags = HWC_SKIP_LAYER;
     mList->hwLayers[0].backgroundColor = {0};
+    /* hwc module checks displayFrame even though it shouldn't */
+    mList->hwLayers[0].displayFrame = r;
     mList->hwLayers[1].compositionType = HWC_FRAMEBUFFER_TARGET;
     mList->hwLayers[1].hints = 0;
     mList->hwLayers[1].flags = 0;
     mList->hwLayers[1].handle = buf;
     mList->hwLayers[1].transform = 0;
     mList->hwLayers[1].blending = HWC_BLENDING_PREMULT;
     mList->hwLayers[1].sourceCrop = r;
     mList->hwLayers[1].displayFrame = r;
     mList->hwLayers[1].visibleRegionScreen.numRects = 1;
-    mList->hwLayers[1].visibleRegionScreen.rects = &mList->hwLayers[0].sourceCrop;
+    mList->hwLayers[1].visibleRegionScreen.rects = &mList->hwLayers[1].sourceCrop;
     mList->hwLayers[1].acquireFenceFd = fence;
     mList->hwLayers[1].releaseFenceFd = -1;
+    mList->hwLayers[1].planeAlpha = 0xFF;
     mHwc->prepare(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
     int err = mHwc->set(mHwc, HWC_NUM_DISPLAY_TYPES, displays);
     mFBSurface->setReleaseFenceFd(mList->hwLayers[1].releaseFenceFd);
     if (mList->retireFenceFd >= 0)
         close(mList->retireFenceFd);
     return !err;
 }
 
--- a/widget/gonk/libdisplay/moz.build
+++ b/widget/gonk/libdisplay/moz.build
@@ -15,17 +15,17 @@
 # limitations under the License.
 
 MODULE = 'display'
 
 CPP_SOURCES += [
     'BootAnimation.cpp',
 ]
 
-if CONFIG['ANDROID_VERSION'] == '17':
+if CONFIG['ANDROID_VERSION'] == '18':
     CPP_SOURCES += [
         'FramebufferSurface.cpp',
         'GraphicBufferAlloc.cpp',
         'GonkDisplayJB.cpp',
     ]
 elif CONFIG['ANDROID_VERSION'] == '15':
     CPP_SOURCES += [
         'GonkDisplayICS.cpp'
--- a/widget/gonk/nsAppShell.cpp
+++ b/widget/gonk/nsAppShell.cpp
@@ -47,16 +47,17 @@
 #include "nsWindow.h"
 #include "OrientationObserver.h"
 #include "GonkMemoryPressureMonitoring.h"
 
 #include "android/log.h"
 #include "libui/EventHub.h"
 #include "libui/InputReader.h"
 #include "libui/InputDispatcher.h"
+#include "cutils/properties.h"
 
 #include "GeckoProfiler.h"
 
 // Defines kKeyMapping and GetKeyNameIndex()
 #include "GonkKeyMapping.h"
 
 #define LOG(args...)                                            \
     __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
@@ -73,16 +74,19 @@ using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::services;
 using namespace mozilla::widget;
 
 bool gDrawRequest = false;
 static nsAppShell *gAppShell = NULL;
 static int epollfd = 0;
 static int signalfds[2] = {0};
+static bool sDevInputAudioJack;
+static int32_t sHeadphoneState;
+static int32_t sMicrophoneState;
 
 NS_IMPL_ISUPPORTS_INHERITED1(nsAppShell, nsBaseAppShell, nsIObserver)
 
 namespace mozilla {
 
 bool ProcessNextEvent()
 {
     return gAppShell->ProcessNextNativeEvent(true);
@@ -240,16 +244,51 @@ maybeSendKeyEvent(int keyCode, bool pres
     if (DOMKeyCode || DOMKeyNameIndex != KEY_NAME_INDEX_Unidentified) {
         sendKeyEvent(DOMKeyCode, DOMKeyNameIndex, pressed, timeMs);
     } else {
         VERBOSE_LOG("Got unknown key event code. type 0x%04x code 0x%04x value %d",
                     keyCode, pressed);
     }
 }
 
+class SwitchEventRunnable : public nsRunnable {
+public:
+    SwitchEventRunnable(hal::SwitchEvent& aEvent) : mEvent(aEvent)
+    {}
+
+    NS_IMETHOD Run()
+    {
+        hal::NotifySwitchChange(mEvent);
+        return NS_OK;
+    }
+private:
+    hal::SwitchEvent mEvent;
+};
+
+static void
+updateHeadphoneSwitch()
+{
+    hal::SwitchEvent event;
+
+    switch (sHeadphoneState) {
+    case AKEY_STATE_UP:
+        event.status() = hal::SWITCH_STATE_OFF;
+        break;
+    case AKEY_STATE_DOWN:
+        event.status() = sMicrophoneState == AKEY_STATE_DOWN ?
+            hal::SWITCH_STATE_HEADPHONE : hal::SWITCH_STATE_HEADSET;
+        break;
+    default:
+        return;
+    }
+
+    event.device() = hal::SWITCH_HEADPHONES;
+    NS_DispatchToMainThread(new SwitchEventRunnable(event));
+}
+
 class GeckoPointerController : public PointerControllerInterface {
     float mX;
     float mY;
     int32_t mButtonState;
     InputReaderConfiguration* mConfig;
 public:
     GeckoPointerController(InputReaderConfiguration* config)
         : mX(0)
@@ -548,16 +587,29 @@ GeckoInputDispatcher::notifyMotion(const
     }
     gAppShell->NotifyNativeEvent();
 }
 
 
 
 void GeckoInputDispatcher::notifySwitch(const NotifySwitchArgs* args)
 {
+    if (!sDevInputAudioJack)
+        return;
+
+    switch (args->switchCode) {
+    case SW_HEADPHONE_INSERT:
+        sHeadphoneState = args->switchValue;
+        updateHeadphoneSwitch();
+        break;
+    case SW_MICROPHONE_INSERT:
+        sMicrophoneState = args->switchValue;
+        updateHeadphoneSwitch();
+        break;
+    }
 }
 
 void GeckoInputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args)
 {
 }
 
 int32_t GeckoInputDispatcher::injectInputEvent(
     const InputEvent* event,
@@ -656,16 +708,22 @@ NS_IMETHODIMP
 nsAppShell::Observe(nsISupports* aSubject,
                     const char* aTopic,
                     const PRUnichar* aData)
 {
     if (strcmp(aTopic, "browser-ui-startup-complete")) {
         return nsBaseAppShell::Observe(aSubject, aTopic, aData);
     }
 
+    if (sDevInputAudioJack) {
+        sHeadphoneState  = mReader->getSwitchState(-1, AINPUT_SOURCE_SWITCH, SW_HEADPHONE_INSERT);
+        sMicrophoneState = mReader->getSwitchState(-1, AINPUT_SOURCE_SWITCH, SW_MICROPHONE_INSERT);
+        updateHeadphoneSwitch();
+    }
+
     mEnableDraw = true;
     NotifyEvent();
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsAppShell::Exit()
 {
@@ -675,16 +733,22 @@ nsAppShell::Exit()
         obsServ->RemoveObserver(this, "browser-ui-startup-complete");
     }
     return nsBaseAppShell::Exit();
 }
 
 void
 nsAppShell::InitInputDevices()
 {
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.moz.devinputjack", value, "0");
+    sDevInputAudioJack = !strcmp(value, "1");
+    sHeadphoneState = AKEY_STATE_UNKNOWN;
+    sMicrophoneState = AKEY_STATE_UNKNOWN;
+
     mEventHub = new EventHub();
     mReaderPolicy = new GeckoInputReaderPolicy();
     mReaderPolicy->setDisplayInfo();
     mDispatcher = new GeckoInputDispatcher();
 
     mReader = new InputReader(mEventHub, mReaderPolicy, mDispatcher);
     mReaderThread = new InputReaderThread(mReader);
 
--- a/widget/windows/winrt/MetroWidget.cpp
+++ b/widget/windows/winrt/MetroWidget.cpp
@@ -135,17 +135,17 @@ namespace {
     }
     Log("  No more input messages");
   }
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(MetroWidget, nsBaseWidget)
 
 
-nsRefPtr<mozilla::layers::AsyncPanZoomController> MetroWidget::sAPZC;
+nsRefPtr<mozilla::layers::APZCTreeManager> MetroWidget::sAPZC;
 
 MetroWidget::MetroWidget() :
   mTransparencyMode(eTransparencyOpaque),
   mWnd(NULL),
   mMetroWndProc(NULL),
   mTempBasicLayerInUse(false),
   nsWindowBase()
 {
@@ -161,17 +161,16 @@ MetroWidget::MetroWidget() :
 MetroWidget::~MetroWidget()
 {
   LogThis();
 
   gInstanceCount--;
 
   // Global shutdown
   if (!gInstanceCount) {
-    MetroWidget::sAPZC->Destroy();
     MetroWidget::sAPZC = nullptr;
     nsTextStore::Terminate();
   } // !gInstanceCount
 }
 
 static bool gTopLevelAssigned = false;
 NS_IMETHODIMP
 MetroWidget::Create(nsIWidget *aParent,
@@ -808,32 +807,18 @@ public:
     mMetroWidget(aMetroWidget)
   {
     nsresult rv;
     nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1", &rv);
     if (NS_SUCCEEDED(rv)) {
       observerService->AddObserver(this, "viewport-needs-updating", false);
     }
 
-    if (MetroWidget::sAPZC) {
-        MetroWidget::sAPZC->SetCompositorParent(this);
-    }
-  }
-
-  virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, const TargetConfig& aTargetConfig,
-                                   bool isFirstPaint) MOZ_OVERRIDE
-  {
-    CompositorParent::ShadowLayersUpdated(aLayerTree, aTargetConfig, isFirstPaint);
-    Layer* targetLayer = GetLayerManager()->GetPrimaryScrollableLayer();
-    if (targetLayer && targetLayer->AsContainerLayer() && MetroWidget::sAPZC &&
-        targetLayer->AsContainerLayer()->GetFrameMetrics().IsScrollable()) {
-      targetLayer->AsContainerLayer()->SetAsyncPanZoomController(MetroWidget::sAPZC);
-      MetroWidget::sAPZC->NotifyLayersUpdated(targetLayer->AsContainerLayer()->GetFrameMetrics(),
-                                              isFirstPaint);
-    }
+    CompositorParent::SetControllerForLayerTree(RootLayerTreeId(), aMetroWidget);
+    MetroWidget::sAPZC = CompositorParent::GetAPZCTreeManager(RootLayerTreeId());
   }
 
   NS_IMETHODIMP Observe(nsISupports *subject, const char *topic, const PRUnichar *data)
   {
     LogFunction();
 
     NS_ENSURE_ARG_POINTER(topic);
     if (!strcmp(topic, "viewport-needs-updating")) {
@@ -841,17 +826,16 @@ public:
       if (targetLayer && targetLayer->AsContainerLayer() && MetroWidget::sAPZC) {
         FrameMetrics frameMetrics =
           targetLayer->AsContainerLayer()->GetFrameMetrics();
         frameMetrics.mDisplayPort =
           AsyncPanZoomController::CalculatePendingDisplayPort(frameMetrics,
                                                               mozilla::gfx::Point(0.0f, 0.0f),
                                                               mozilla::gfx::Point(0.0f, 0.0f),
                                                               0.0);
-        MetroWidget::sAPZC->NotifyLayersUpdated(frameMetrics, true);
         mMetroWidget->RequestContentRepaint(frameMetrics);
       }
     }
     return NS_OK;
   }
 
 protected:
   nsCOMPtr<MetroWidget> mMetroWidget;
@@ -905,20 +889,16 @@ MetroWidget::GetLayerManager(PLayerTrans
   HRESULT hr = S_OK;
 
   // Create a layer manager: try to use an async compositor first, if enabled.
   // Otherwise fall back on the main thread d3d manager.
   if (!mLayerManager) {
     if (ShouldUseOffMainThreadCompositing()) {
       NS_ASSERTION(aShadowManager == nullptr, "Async Compositor not supported with e10s");
       CreateCompositor();
-      if (ShouldUseAPZC()) {
-        sAPZC = new AsyncPanZoomController(this, AsyncPanZoomController::USE_GESTURE_DETECTOR);
-        sAPZC->SetCompositorParent(mCompositorParent);
-      }
     } else if (ShouldUseMainThreadD3D10Manager()) {
       nsRefPtr<mozilla::layers::LayerManagerD3D10> layerManager =
         new mozilla::layers::LayerManagerD3D10(this);
       if (layerManager->Initialize(true, &hr)) {
         mLayerManager = layerManager;
       }
     } else if (ShouldUseBasicManager()) {
       mLayerManager = CreateBasicLayerManager();
@@ -1452,17 +1432,17 @@ MetroWidget::HandleLongTap(const CSSIntP
   if (!mMetroInput) {
     return;
   }
 
   mMetroInput->HandleLongTap(aPoint);
 }
 
 void
-MetroWidget::SendAsyncScrollDOMEvent(const CSSRect &aContentRect, const CSSSize &aScrollableSize)
+MetroWidget::SendAsyncScrollDOMEvent(FrameMetrics::ViewID aScrollId, const CSSRect &aContentRect, const CSSSize &aScrollableSize)
 {
   LogFunction();
 }
 
 void
 MetroWidget::PostDelayedTask(Task* aTask, int aDelayMs)
 {
   LogFunction();
--- a/widget/windows/winrt/MetroWidget.h
+++ b/widget/windows/winrt/MetroWidget.h
@@ -18,17 +18,17 @@
 #include "WindowHook.h"
 #include "TaskbarWindowPreview.h"
 #include "nsIdleService.h"
 #ifdef ACCESSIBILITY
 #include "mozilla/a11y/Accessible.h"
 #endif
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/GeckoContentController.h"
-#include "mozilla/layers/AsyncPanZoomController.h"
+#include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/LayerManagerComposite.h"
 #include "Units.h"
 #include "MetroInput.h"
 
 #include "mozwrlbase.h"
 
 #include <windows.system.h>
 #include <windows.ui.core.h>
@@ -186,17 +186,17 @@ public:
   nsresult RequestContentScroll();
   void RequestContentRepaintImplMainThread();
 
   // GeckoContentController interface impl
   virtual void RequestContentRepaint(const mozilla::layers::FrameMetrics& aFrameMetrics);
   virtual void HandleDoubleTap(const mozilla::CSSIntPoint& aPoint);
   virtual void HandleSingleTap(const mozilla::CSSIntPoint& aPoint);
   virtual void HandleLongTap(const mozilla::CSSIntPoint& aPoint);
-  virtual void SendAsyncScrollDOMEvent(const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize);
+  virtual void SendAsyncScrollDOMEvent(mozilla::layers::FrameMetrics::ViewID aScrollId, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize);
   virtual void PostDelayedTask(Task* aTask, int aDelayMs);
   virtual void HandlePanBegin();
   virtual void HandlePanEnd();
 
   void SetMetroInput(mozilla::widget::winrt::MetroInput* aMetroInput)
   {
     mMetroInput = aMetroInput;
   }
@@ -232,10 +232,10 @@ protected:
   nsCOMPtr<nsIdleService> mIdleService;
   HWND mWnd;
   WNDPROC mMetroWndProc;
   bool mTempBasicLayerInUse;
   Microsoft::WRL::ComPtr<mozilla::widget::winrt::MetroInput> mMetroInput;
   mozilla::layers::FrameMetrics mFrameMetrics;
 
 public:
-  static nsRefPtr<mozilla::layers::AsyncPanZoomController> sAPZC;
+  static nsRefPtr<mozilla::layers::APZCTreeManager> sAPZC;
 };