Bug 869940 - APZC Metro winrt implementation. r=jimm
authorBrian R. Bondy <netzen@gmail.com>
Thu, 25 Jul 2013 13:15:16 -0400
changeset 140011 6a8926881a44ace1739d0e873e5b2b948a434e0c
parent 140010 d8c0cdc0bc22829d9cde1de0a38dd873c0256377
child 140012 692b48b7c022b69a5a215b2bc3df04dd0f334ff8
push id31557
push userbbondy@mozilla.com
push dateThu, 25 Jul 2013 17:20:24 +0000
treeherdermozilla-inbound@692b48b7c022 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimm
bugs869940
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
Bug 869940 - APZC Metro winrt implementation. r=jimm
widget/windows/winrt/FrameworkView.h
widget/windows/winrt/MetroInput.cpp
widget/windows/winrt/MetroInput.h
widget/windows/winrt/MetroWidget.cpp
widget/windows/winrt/MetroWidget.h
--- a/widget/windows/winrt/FrameworkView.h
+++ b/widget/windows/winrt/FrameworkView.h
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 4; 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/. */
 
 #pragma once
 
 #include "nsGUIEvent.h"
-#include "MetroInput.h"
 #include "mozilla/TimeStamp.h"
 #include "MetroWidget.h"
 #include "gfxWindowsPlatform.h"
 #include "gfxD2DSurface.h"
 #include "nsDataHashtable.h"
 
 #include "mozwrlbase.h"
 
--- a/widget/windows/winrt/MetroInput.cpp
+++ b/widget/windows/winrt/MetroInput.cpp
@@ -1,31 +1,31 @@
 /* -*- Mode: C++; tab-width: 4; 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 "MetroInput.h"
-
 // Moz headers (alphabetical)
 #include "MetroUtils.h" // Logging, POINT_CEIL_*, ActivateGenericInstance, etc
 #include "MetroWidget.h" // MetroInput::mWidget
 #include "mozilla/dom/Touch.h"  // Touch
 #include "nsTArray.h" // Touch lists
 #include "nsIDOMSimpleGestureEvent.h" // Constants for gesture events
+#include "InputData.h"
 
 // System headers (alphabetical)
 #include <windows.ui.core.h> // ABI::Window::UI::Core namespace
 #include <windows.ui.input.h> // ABI::Window::UI::Input namespace
 
 //#define DEBUG_INPUT
 
 // Using declarations
 using namespace ABI::Windows; // UI, System, Foundation namespaces
 using namespace Microsoft; // WRL namespace (ComPtr, possibly others)
+using namespace mozilla;
 using namespace mozilla::widget::winrt;
 using namespace mozilla::dom;
 
 // File-scoped statics (unnamed namespace)
 namespace {
   // XXX: Set these min values appropriately
   const double SWIPE_MIN_DISTANCE = 5.0;
   const double SWIPE_MIN_VELOCITY = 5.0;
@@ -160,16 +160,18 @@ MetroInput::MetroInput(MetroWidget* aWid
                 mWindow(aWindow),
                 mDispatcher(aDispatcher),
                 mTouchEvent(true, NS_TOUCH_MOVE, aWidget)
 {
   LogFunction();
   NS_ASSERTION(aWidget, "Attempted to create MetroInput for null widget!");
   NS_ASSERTION(aWindow, "Attempted to create MetroInput for null window!");
 
+  mWidget->SetMetroInput(this);
+
   mTokenPointerPressed.value = 0;
   mTokenPointerReleased.value = 0;
   mTokenPointerMoved.value = 0;
   mTokenPointerEntered.value = 0;
   mTokenPointerExited.value = 0;
   mTokenPointerWheelChanged.value = 0;
   mTokenEdgeStarted.value = 0;
   mTokenEdgeCanceled.value = 0;
@@ -457,17 +459,17 @@ MetroInput::OnPointerPressed(UI::Core::I
   touch->mChanged = true;
   mTouches.Put(pointerId, touch);
   mTouchEvent.message = NS_TOUCH_START;
 
   // If this is the first touchstart of a touch session,
   // dispatch it now so we can see if preventDefault gets called on it.
   if (mTouches.Count() == 1) {
     nsEventStatus status;
-    DispatchPendingTouchEvent(status);
+    DispatchPendingTouchEvent(status, true);
     mTouchStartDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status);
     // If the first touchstart event has preventDefault called on it, then
     // we will not perform any default actions associated with any touch
     // events for this session, including touchmove events.
     // Thus, mTouchStartDefaultPrevented implies mTouchMoveDefaultPrevented.
     mTouchMoveDefaultPrevented = mTouchStartDefaultPrevented;
     mIsFirstTouchMove = !mTouchStartDefaultPrevented;
   }
@@ -513,28 +515,37 @@ MetroInput::OnPointerReleased(UI::Core::
   uint32_t pointerId;
   currentPoint->get_PointerId(&pointerId);
   nsRefPtr<Touch> touch = mTouches.Get(pointerId);
 
   // We are about to dispatch a touchend.  Before we do that, we should make
   // sure that we don't have a touchmove or touchstart sitting around for this
   // point.
   if (touch->mChanged) {
-    DispatchPendingTouchEvent();
+    DispatchPendingTouchEvent(true);
   }
   mTouches.Remove(pointerId);
 
   // touchend events only have a single touch; the touch that has been removed
   mTouchEvent.message = NS_TOUCH_END;
   mTouchEvent.touches.Clear();
   mTouchEvent.touches.AppendElement(CreateDOMTouch(currentPoint.Get()));
   mTouchEvent.time = ::GetMessageTime();
   mModifierKeyState.Update();
   mModifierKeyState.InitInputEvent(mTouchEvent);
-  DispatchEventIgnoreStatus(&mTouchEvent);
+
+  nsEventStatus status;
+  mWidget->DispatchEvent(&mTouchEvent, status);
+  if (status != nsEventStatus_eConsumeNoDefault) {
+    MultiTouchInput inputData(mTouchEvent);
+    if (MetroWidget::sAPZC) {
+      status = MetroWidget::sAPZC->ReceiveInputEvent(inputData);
+    }
+  }
+  
   // mTouchEvent.message should always be set to NS_TOUCH_MOVE
   mTouchEvent.message = NS_TOUCH_MOVE;
 
   // If the first touchstart of this touch session had its preventDefault
   // called on it, we will not perform any default actions for any of the
   // touches in this touch session.  Note that we don't check
   // mTouchMoveDefaultPrevented here.  The reason is that, even if
   // preventDefault was called on the first touchmove event, we might still
@@ -589,28 +600,28 @@ MetroInput::OnPointerMoved(UI::Core::ICo
   // for touchIds that we are not currently tracking.  See bug 819223.
   if (!touch) {
     return S_OK;
   }
 
   // If we're modifying a touch entry that has a pending update, go through
   // with the update.
   if (touch->mChanged) {
-    DispatchPendingTouchEvent();
+    DispatchPendingTouchEvent(true);
   }
 
   touch = CreateDOMTouch(currentPoint.Get());
   touch->mChanged = true;
   mTouches.Put(pointerId, touch);
 
   // If this is the first touch move of our session, we should dispatch it
   // and store our mTouchMoveDefaultPrevented value
   if (mIsFirstTouchMove) {
     nsEventStatus status;
-    DispatchPendingTouchEvent(status);
+    DispatchPendingTouchEvent(status, true);
     mTouchMoveDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status);
     mIsFirstTouchMove = false;
   }
 
   // We will perform default actions for touchmove events only if
   // preventDefault was not called on the first touchmove event and
   // preventDefault was not called on the first touchstart event.  Checking
   // mTouchMoveDefaultPrevented is enough here because it will be set if
@@ -942,105 +953,151 @@ HRESULT
 MetroInput::OnTapped(UI::Input::IGestureRecognizer* aSender,
                      UI::Input::ITappedEventArgs* aArgs)
 {
 #ifdef DEBUG_INPUT
   LogFunction();
 #endif
 
   Devices::Input::PointerDeviceType deviceType;
-
   aArgs->get_PointerDeviceType(&deviceType);
 
   // For mouse and pen input, we send mousedown/mouseup/mousemove
   // events as soon as we detect the input event.  For touch input, a set of
   // mousedown/mouseup events will be sent only once a tap has been detected.
-  if (deviceType ==
-              Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
-    Foundation::Point position;
-    aArgs->get_Position(&position);
-
-    // Set up the mouse event that we'll reuse for mousemove, mousedown, and
-    // mouseup
-    nsMouseEvent mouseEvent(true,
-                            NS_MOUSE_MOVE,
-                            mWidget.Get(),
-                            nsMouseEvent::eReal,
-                            nsMouseEvent::eNormal);
-    mModifierKeyState.Update();
-    mModifierKeyState.InitInputEvent(mouseEvent);
-    mouseEvent.refPoint = MetroUtils::LogToPhys(position);
-    mouseEvent.time = ::GetMessageTime();
-    aArgs->get_TapCount(&mouseEvent.clickCount);
-    mouseEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
-
-    // Send the mousemove
-    DispatchEventIgnoreStatus(&mouseEvent);
-
-    // Send the mousedown
-    mouseEvent.message = NS_MOUSE_BUTTON_DOWN;
-    mouseEvent.button = nsMouseEvent::buttonType::eLeftButton;
-    DispatchEventIgnoreStatus(&mouseEvent);
-
-    // Send the mouseup
-    mouseEvent.message = NS_MOUSE_BUTTON_UP;
-    DispatchEventIgnoreStatus(&mouseEvent);
-
-    // Send one more mousemove to avoid getting a hover state.
-    // In the Metro environment for any application, a tap does not imply a
-    // mouse cursor move.  In desktop environment for any application a tap
-    // does imply a cursor move.
-    POINT point;
-    if (GetCursorPos(&point)) {
-      ScreenToClient((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW), &point);
-      Foundation::Point oldMousePosition;
-      oldMousePosition.X = static_cast<FLOAT>(point.x);
-      oldMousePosition.Y = static_cast<FLOAT>(point.y);
-      mouseEvent.refPoint = MetroUtils::LogToPhys(oldMousePosition);
-      mouseEvent.message = NS_MOUSE_MOVE;
-      mouseEvent.button = 0;
-
-      DispatchEventIgnoreStatus(&mouseEvent);
-    }
+  if (deviceType != Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
+    return S_OK;
   }
 
+  Foundation::Point position;
+  aArgs->get_Position(&position);
+  nsIntPoint pt = MetroUtils::LogToPhys(position);
+
+  CSSIntPoint point(pt.x, pt.y);
+  HandleSingleTap(point);
   return S_OK;
 }
 
 // This event is raised when a sequence of pointer events has been
 // interpreted by the GestureRecognizer as a right tap.
 // This could be a mouse right-click, a right-click on a pen, or
 // a tap-and-hold on a touch surface.
 HRESULT
 MetroInput::OnRightTapped(UI::Input::IGestureRecognizer* aSender,
                           UI::Input::IRightTappedEventArgs* aArgs)
 {
 #ifdef DEBUG_INPUT
   LogFunction();
 #endif
 
   Devices::Input::PointerDeviceType deviceType;
+  aArgs->get_PointerDeviceType(&deviceType);
+
   Foundation::Point position;
+  aArgs->get_Position(&position);
+  nsIntPoint pt = MetroUtils::LogToPhys(position);
+
+  CSSIntPoint point(pt.x, pt.y);
+  HandleLongTap(point);
+
+  return S_OK;
+}
+
+// Used by MetroWidget GeckoContentController callbacks
+void
+MetroInput::HandleDoubleTap(const mozilla::CSSIntPoint& aPoint)
+{
+#ifdef DEBUG_INPUT
+  LogFunction();
+#endif
+  nsSimpleGestureEvent geckoEvent(true, NS_SIMPLE_GESTURE_TAP, mWidget.Get(), 0, 0.0);
+  mModifierKeyState.Update();
+  mModifierKeyState.InitInputEvent(geckoEvent);
+  geckoEvent.time = ::GetMessageTime();
+  geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+  geckoEvent.refPoint.x = aPoint.x;
+  geckoEvent.refPoint.y = aPoint.y;
+  geckoEvent.clickCount = 2;
+  geckoEvent.pressure = 1;
+  DispatchEventIgnoreStatus(&geckoEvent);
+}
+
+void
+MetroInput::HandleSingleTap(const mozilla::CSSIntPoint& aPoint)
+{
+#ifdef DEBUG_INPUT
+  LogFunction();
+#endif
 
-  aArgs->get_PointerDeviceType(&deviceType);
-  aArgs->get_Position(&position);
+  // Set up the mouse event that we'll reuse for mousemove, mousedown, and
+  // mouseup
+  nsMouseEvent mouseEvent(true,
+                          NS_MOUSE_MOVE,
+                          mWidget.Get(),
+                          nsMouseEvent::eReal,
+                          nsMouseEvent::eNormal);
+  mModifierKeyState.Update();
+  mModifierKeyState.InitInputEvent(mouseEvent);
+  mouseEvent.refPoint.x = aPoint.x;
+  mouseEvent.refPoint.y = aPoint.y;
+  mouseEvent.time = ::GetMessageTime();
+  mouseEvent.clickCount = 1;
+  mouseEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+
+  // Send the mousemove
+  DispatchEventIgnoreStatus(&mouseEvent);
+
+  // Send the mousedown
+  mouseEvent.message = NS_MOUSE_BUTTON_DOWN;
+  mouseEvent.button = nsMouseEvent::buttonType::eLeftButton;
+  DispatchEventIgnoreStatus(&mouseEvent);
+
+  // Send the mouseup
+  mouseEvent.message = NS_MOUSE_BUTTON_UP;
+  DispatchEventIgnoreStatus(&mouseEvent);
+
+  // Send one more mousemove to avoid getting a hover state.
+  // In the Metro environment for any application, a tap does not imply a
+  // mouse cursor move.  In desktop environment for any application a tap
+  // does imply a cursor move.
+  POINT point;
+  if (GetCursorPos(&point)) {
+    ScreenToClient((HWND)mWidget->GetNativeData(NS_NATIVE_WINDOW), &point);
+    Foundation::Point oldMousePosition;
+    oldMousePosition.X = static_cast<FLOAT>(point.x);
+    oldMousePosition.Y = static_cast<FLOAT>(point.y);
+    mouseEvent.refPoint.x = aPoint.x;
+    mouseEvent.refPoint.y = aPoint.y;
+    mouseEvent.message = NS_MOUSE_MOVE;
+    mouseEvent.button = 0;
+
+    DispatchEventIgnoreStatus(&mouseEvent);
+  }
+
+}
+
+void
+MetroInput::HandleLongTap(const mozilla::CSSIntPoint& aPoint)
+{
+#ifdef DEBUG_INPUT
+  LogFunction();
+#endif
 
   nsMouseEvent contextMenu(true,
                            NS_CONTEXTMENU,
                            mWidget.Get(),
                            nsMouseEvent::eReal,
                            nsMouseEvent::eNormal);
   mModifierKeyState.Update();
   mModifierKeyState.InitInputEvent(contextMenu);
-  contextMenu.refPoint = MetroUtils::LogToPhys(position);
+  contextMenu.refPoint.x = aPoint.x;
+  contextMenu.refPoint.y = aPoint.y;
   contextMenu.time = ::GetMessageTime();
-  MozInputSourceFromDeviceType(deviceType, contextMenu.inputSource);
+  contextMenu.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
   DispatchEventIgnoreStatus(&contextMenu);
-
-  return S_OK;
 }
 
 /**
  * Implementation Details
  */
 nsEventStatus MetroInput::sThrowawayStatus;
 
 // This function allows us to call MetroWidget's DispatchEvent function
@@ -1048,32 +1105,37 @@ nsEventStatus MetroInput::sThrowawayStat
 // is never read.  This allows us to avoid the (admittedly small) overhead
 // of creating a new nsEventStatus every time we dispatch an event.
 void
 MetroInput::DispatchEventIgnoreStatus(nsGUIEvent *aEvent) {
   mWidget->DispatchEvent(aEvent, sThrowawayStatus);
 }
 
 void
-MetroInput::DispatchPendingTouchEvent(nsEventStatus& aStatus) {
+MetroInput::DispatchPendingTouchEvent(nsEventStatus& aStatus, bool aDispatchToAPZC) {
   mTouchEvent.touches.Clear();
   mTouches.Enumerate(&AppendToTouchList,
                      static_cast<void*>(&mTouchEvent.touches));
   mTouchEvent.time = ::GetMessageTime();
   mModifierKeyState.Update();
   mModifierKeyState.InitInputEvent(mTouchEvent);
+
   mWidget->DispatchEvent(&mTouchEvent, aStatus);
+  if (aStatus != nsEventStatus_eConsumeNoDefault && aDispatchToAPZC && MetroWidget::sAPZC) {
+    MultiTouchInput inputData(mTouchEvent);
+    aStatus = MetroWidget::sAPZC->ReceiveInputEvent(inputData);
+  }
 
   // mTouchEvent.message should always be set to NS_TOUCH_MOVE
   mTouchEvent.message = NS_TOUCH_MOVE;
 }
 
 void
-MetroInput::DispatchPendingTouchEvent() {
-  DispatchPendingTouchEvent(sThrowawayStatus);
+MetroInput::DispatchPendingTouchEvent(bool aDispatchToAPZC) {
+  DispatchPendingTouchEvent(sThrowawayStatus, aDispatchToAPZC);
 }
 
 void
 MetroInput::UnregisterInputEvents() {
   // Unregister ourselves for the edge swipe event
   WRL::ComPtr<UI::Input::IEdgeGestureStatics> edgeStatics;
   if (SUCCEEDED(Foundation::GetActivationFactory(
         WRL::Wrappers::HStringReference(
--- a/widget/windows/winrt/MetroInput.h
+++ b/widget/windows/winrt/MetroInput.h
@@ -146,16 +146,21 @@ public:
   HRESULT OnManipulationUpdated(IGestureRecognizer* aSender,
                                 IManipulationUpdatedEventArgs* aArgs);
   HRESULT OnManipulationCompleted(IGestureRecognizer* aSender,
                                   IManipulationCompletedEventArgs* aArgs);
   HRESULT OnTapped(IGestureRecognizer* aSender, ITappedEventArgs* aArgs);
   HRESULT OnRightTapped(IGestureRecognizer* aSender,
                         IRightTappedEventArgs* aArgs);
 
+  // Used by MetroWidget GeckoContentController callbacks
+  void HandleDoubleTap(const mozilla::CSSIntPoint& aPoint);
+  void HandleSingleTap(const mozilla::CSSIntPoint& aPoint);
+  void HandleLongTap(const mozilla::CSSIntPoint& aPoint);
+
 private:
   Microsoft::WRL::ComPtr<ICoreWindow> mWindow;
   Microsoft::WRL::ComPtr<MetroWidget> mWidget;
   Microsoft::WRL::ComPtr<ICoreDispatcher> mDispatcher;
   Microsoft::WRL::ComPtr<IGestureRecognizer> mGestureRecognizer;
 
   ModifierKeyState mModifierKeyState;
 
@@ -218,18 +223,18 @@ private:
   // events.  This caused performance to visibly degrade on modestly-powered
   // machines.  In response, we no longer send touch events immediately
   // upon receiving PointerPressed or PointerMoved.  Instead, we store
   // the updated touchpoint info and record the fact that the touchpoint
   // has changed.  If ever we try to update a touchpoint has already
   // changed, we dispatch a touch event containing all the changed touches.
   nsEventStatus mTouchEventStatus;
   nsTouchEvent mTouchEvent;
-  void DispatchPendingTouchEvent();
-  void DispatchPendingTouchEvent(nsEventStatus& status);
+  void DispatchPendingTouchEvent(bool aDispatchToAPZC);
+  void DispatchPendingTouchEvent(nsEventStatus& status, bool aDispatchToAPZC);
   nsBaseHashtable<nsUint32HashKey,
                   nsRefPtr<mozilla::dom::Touch>,
                   nsRefPtr<mozilla::dom::Touch> > mTouches;
 
   // These registration tokens are set when we register ourselves to receive
   // events from our window.  We must hold on to them for the entire duration
   // that we want to receive these events.  When we are done, we must
   // unregister ourself with the window using these tokens.
--- a/widget/windows/winrt/MetroWidget.cpp
+++ b/widget/windows/winrt/MetroWidget.cpp
@@ -18,16 +18,18 @@
 #include "nsIPresShell.h"
 #include "nsPrintfCString.h"
 #include "nsWindowDefs.h"
 #include "FrameworkView.h"
 #include "nsTextStore.h"
 #include "Layers.h"
 #include "ClientLayerManager.h"
 #include "BasicLayers.h"
+#include "FrameMetrics.h"
+#include "nsIObserver.h"
 #include "Windows.Graphics.Display.h"
 #ifdef MOZ_CRASHREPORTER
 #include "nsExceptionHandler.h"
 #endif
 
 using namespace Microsoft::WRL;
 using namespace Microsoft::WRL::Wrappers;
 
@@ -132,16 +134,19 @@ namespace {
       Log("    Dispatched 0x%x 0x%x 0x%x", msg.message, msg.wParam, msg.lParam);
     }
     Log("  No more input messages");
   }
 }
 
 NS_IMPL_ISUPPORTS_INHERITED0(MetroWidget, nsBaseWidget)
 
+
+nsRefPtr<mozilla::layers::AsyncPanZoomController> MetroWidget::sAPZC;
+
 MetroWidget::MetroWidget() :
   mTransparencyMode(eTransparencyOpaque),
   mWnd(NULL),
   mMetroWndProc(NULL),
   mTempBasicLayerInUse(false),
   nsWindowBase()
 {
   // Global initialization
@@ -156,16 +161,18 @@ 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,
                     nsNativeWidget aNativeParent,
@@ -776,16 +783,97 @@ MetroWidget::ShouldUseMainThreadD3D10Man
 
 bool
 MetroWidget::ShouldUseBasicManager()
 {
   // toolkit or test widgets fall back on empty shadow layers
   return (mWindowType != eWindowType_toplevel);
 }
 
+bool
+MetroWidget::ShouldUseAPZC()
+{
+  const char* kPrefName = "layers.async-pan-zoom.enabled";
+  return ShouldUseOffMainThreadCompositing() &&
+         Preferences::GetBool(kPrefName, false);
+}
+
+class MetroCompositorParent : public CompositorParent,
+                              public nsIObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  MetroCompositorParent(MetroWidget* aMetroWidget, bool aRenderToEGLSurface,
+                        int aSurfaceWidth, int aSurfaceHeight) :
+    CompositorParent(aMetroWidget, aRenderToEGLSurface,
+                     aSurfaceHeight, aSurfaceHeight),
+    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);
+    }
+  }
+
+  NS_IMETHODIMP Observe(nsISupports *subject, const char *topic, const PRUnichar *data)
+  {
+    LogFunction();
+
+    NS_ENSURE_ARG_POINTER(topic);
+    if (!strcmp(topic, "viewport-needs-updating")) {
+      Layer* targetLayer = GetLayerManager()->GetPrimaryScrollableLayer();
+      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;
+};
+
+NS_IMPL_ISUPPORTS1(MetroCompositorParent, nsIObserver)
+
+
+CompositorParent* MetroWidget::NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight)
+{
+  if (ShouldUseAPZC()) {
+    return new MetroCompositorParent(this, true, aSurfaceWidth, aSurfaceHeight);
+  } else {
+    return nsBaseWidget::NewCompositorParent(aSurfaceWidth, aSurfaceHeight);
+  }
+}
+
 LayerManager*
 MetroWidget::GetLayerManager(PLayerTransactionChild* aShadowManager,
                              LayersBackend aBackendHint,
                              LayerManagerPersistence aPersistence,
                              bool* aAllowRetaining)
 {
   bool retaining = true;
 
@@ -817,16 +905,20 @@ 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();
@@ -1261,8 +1353,132 @@ MetroWidget::PickerClosed()
 
 bool
 MetroWidget::HasPendingInputEvent()
 {
   if (HIWORD(GetQueueStatus(QS_INPUT)))
     return true;
   return false;
 }
+
+// GeckoContentController interface impl
+
+#include "nsIBrowserDOMWindow.h"
+#include "nsIWebNavigation.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsIWindowMediator.h"
+#include "nsIInterfaceRequestorUtils.h"
+
+class RequestContentRepaintEvent : public nsRunnable
+{
+public:
+    RequestContentRepaintEvent(const FrameMetrics& aFrameMetrics) : mFrameMetrics(aFrameMetrics)
+    {
+    }
+
+    NS_IMETHOD Run() {
+        // This event shuts down the worker thread and so must be main thread.
+        MOZ_ASSERT(NS_IsMainThread());
+
+        CSSToScreenScale resolution = mFrameMetrics.CalculateResolution();
+        CSSRect compositedRect = mFrameMetrics.CalculateCompositedRectInCssPixels();
+
+        NS_ConvertASCIItoUTF16 data(nsPrintfCString("{ " \
+                                                    "  \"resolution\": %.2f, " \
+                                                    "  \"compositedRect\": { \"width\": %d, \"height\": %d }, " \
+                                                    "  \"displayPort\":    { \"x\": %d, \"y\": %d, \"width\": %d, \"height\": %d }, " \
+                                                    "  \"scrollTo\":       { \"x\": %d, \"y\": %d }" \
+                                                    "}",
+                                                    (float)(resolution.scale / mFrameMetrics.mDevPixelsPerCSSPixel.scale),
+                                                    (int)compositedRect.width,
+                                                    (int)compositedRect.height,
+                                                    (int)mFrameMetrics.mDisplayPort.x,
+                                                    (int)mFrameMetrics.mDisplayPort.y,
+                                                    (int)mFrameMetrics.mDisplayPort.width,
+                                                    (int)mFrameMetrics.mDisplayPort.height,
+                                                    (int)mFrameMetrics.mScrollOffset.x,
+                                                    (int)mFrameMetrics.mScrollOffset.y));
+
+        MetroUtils::FireObserver("apzc-request-content-repaint", data.get());
+        return NS_OK;
+    }
+protected:
+    const FrameMetrics mFrameMetrics;
+};
+
+void
+MetroWidget::RequestContentRepaint(const FrameMetrics& aFrameMetrics)
+{
+  LogFunction();
+
+  // Send the result back to the main thread so that it can shutdown
+  nsCOMPtr<nsIRunnable> r1 = new RequestContentRepaintEvent(aFrameMetrics);
+  if (!NS_IsMainThread()) {
+    NS_DispatchToMainThread(r1);
+  } else {
+    r1->Run();
+  }
+}
+
+void
+MetroWidget::HandleDoubleTap(const CSSIntPoint& aPoint)
+{
+  LogFunction();
+
+  if (!mMetroInput) {
+    return;
+  }
+
+  mMetroInput->HandleDoubleTap(aPoint);
+}
+
+void
+MetroWidget::HandleSingleTap(const CSSIntPoint& aPoint)
+{
+  LogFunction();
+
+  if (!mMetroInput) {
+    return;
+  }
+
+  mMetroInput->HandleSingleTap(aPoint);
+}
+
+void
+MetroWidget::HandleLongTap(const CSSIntPoint& aPoint)
+{
+  LogFunction();
+
+  if (!mMetroInput) {
+    return;
+  }
+
+  mMetroInput->HandleLongTap(aPoint);
+}
+
+void
+MetroWidget::SendAsyncScrollDOMEvent(const CSSRect &aContentRect, const CSSSize &aScrollableSize)
+{
+  LogFunction();
+}
+
+void
+MetroWidget::PostDelayedTask(Task* aTask, int aDelayMs)
+{
+  LogFunction();
+  MessageLoop::current()->PostDelayedTask(FROM_HERE, aTask, aDelayMs);
+}
+
+void
+MetroWidget::HandlePanBegin()
+{
+  LogFunction();
+  MetroUtils::FireObserver("apzc-handle-pan-begin", L"");
+}
+
+void
+MetroWidget::HandlePanEnd()
+{
+  LogFunction();
+  MetroUtils::FireObserver("apzc-handle-pan-end", L"");
+}
--- a/widget/windows/winrt/MetroWidget.h
+++ b/widget/windows/winrt/MetroWidget.h
@@ -17,16 +17,21 @@
 #include "nsWindowDbg.h"
 #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/LayerManagerComposite.h"
+#include "Units.h"
+#include "MetroInput.h"
 
 #include "mozwrlbase.h"
 
 #include <windows.system.h>
 #include <windows.ui.core.h>
 #include <Windows.ApplicationModel.core.h>
 #include <Windows.ApplicationModel.h>
 #include <Windows.Applicationmodel.Activation.h>
@@ -35,17 +40,18 @@
 namespace mozilla {
 namespace widget {
 namespace winrt {
 
 class FrameworkView;
 
 } } }
 
-class MetroWidget : public nsWindowBase
+class MetroWidget : public nsWindowBase,
+                    public mozilla::layers::GeckoContentController
 {
   typedef mozilla::widget::WindowHook WindowHook;
   typedef mozilla::widget::TaskbarWindowPreview TaskbarWindowPreview;
   typedef ABI::Windows::UI::Input::IPointerPoint IPointerPoint;
   typedef ABI::Windows::UI::Core::IPointerEventArgs IPointerEventArgs;
   typedef ABI::Windows::UI::Core::IKeyEventArgs IKeyEventArgs;
   typedef ABI::Windows::UI::Core::ICharacterReceivedEventArgs ICharacterReceivedEventArgs;
   typedef mozilla::widget::winrt::FrameworkView FrameworkView;
@@ -59,16 +65,19 @@ public:
   virtual ~MetroWidget();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsWindowBase
   virtual void InitEvent(nsGUIEvent& aEvent, nsIntPoint* aPoint = nullptr) MOZ_OVERRIDE;
   virtual bool DispatchWindowEvent(nsGUIEvent* aEvent) MOZ_OVERRIDE;
 
+  // nsBaseWidget
+  virtual CompositorParent* NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight);
+
   // nsIWidget interface
   NS_IMETHOD    Create(nsIWidget *aParent,
                        nsNativeWidget aNativeParent,
                        const nsIntRect &aRect,
                        nsDeviceContext *aContext,
                        nsWidgetInitData *aInitData = nullptr);
   NS_IMETHOD    Destroy();
   NS_IMETHOD    SetParent(nsIWidget *aNewParent);
@@ -114,16 +123,17 @@ public:
   float         GetDPI();
   void          ChangedDPI();
   virtual bool  IsVisible() const;
   virtual bool  IsEnabled() const;
   // ShouldUseOffMainThreadCompositing is defined in base widget
   virtual bool  ShouldUseOffMainThreadCompositing();
   bool          ShouldUseMainThreadD3D10Manager();
   bool          ShouldUseBasicManager();
+  bool          ShouldUseAPZC();
   virtual LayerManager* GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
                                         LayersBackend aBackendHint = mozilla::layers::LAYERS_NONE,
                                         LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
                                         bool* aAllowRetaining = nullptr);
   virtual mozilla::layers::LayersBackend GetPreferredCompositorBackend() { return mozilla::layers::LAYERS_D3D11; }
 
   // IME related interfaces
   NS_IMETHOD_(void) SetInputContext(const InputContext& aContext,
@@ -168,16 +178,34 @@ public:
   void SetTaskbarPreview(nsITaskbarWindowPreview *preview) { }
   WindowHook& GetWindowHook() { return mWindowHook; }
 
   void SetView(FrameworkView* aView);
   void FindMetroWindow();
   virtual void SetTransparencyMode(nsTransparencyMode aMode);
   virtual nsTransparencyMode GetTransparencyMode();
 
+  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 PostDelayedTask(Task* aTask, int aDelayMs);
+  virtual void HandlePanBegin();
+  virtual void HandlePanEnd();
+
+  void SetMetroInput(mozilla::widget::winrt::MetroInput* aMetroInput)
+  {
+    mMetroInput = aMetroInput;
+  }
+
 protected:
   friend class FrameworkView;
 
   struct OleInitializeWrapper {
     HRESULT const hr;
 
     OleInitializeWrapper()
       : hr(::OleInitialize(NULL))
@@ -200,9 +228,14 @@ protected:
   WindowHook mWindowHook;
   Microsoft::WRL::ComPtr<FrameworkView> mView;
   nsTransparencyMode mTransparencyMode;
   nsIntRegion mInvalidatedRegion;
   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;
 };