Merge m-c to inbound.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 30 Jul 2013 18:08:18 -0400
changeset 152922 0d8409268f42fb2d9847ac7e874eb3036a284816
parent 152921 7aaee10b08e115a3e92d4c90912cecd572162246 (current diff)
parent 152834 60bf7ceba8906a32783d79c78e917fce83b7d72a (diff)
child 152923 c2b375f3a909fed4dd66947b88bd63e414c8d97e
push id2859
push userakeybl@mozilla.com
push dateMon, 16 Sep 2013 19:14:59 +0000
treeherdermozilla-beta@87d3c51cd2bf [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone25.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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;
 };