Bug 1127066 - Extract SendSetTargetAPZCNotification and its helpers into APZCCallbackHelper. r=kats
authorBotond Ballo <botond@mozilla.com>
Fri, 06 Feb 2015 18:11:19 -0500
changeset 228800 4bd0d2b7b41db99880fe56e306711fe594bdcce7
parent 228799 cc71f20bede1aedd7385e4a9a5c04f89509e25b1
child 228801 c55657471570a3a4d8afe11768d5e74670b1f6bd
push idunknown
push userunknown
push dateunknown
reviewerskats
bugs1127066
milestone38.0a1
Bug 1127066 - Extract SendSetTargetAPZCNotification and its helpers into APZCCallbackHelper. r=kats
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
gfx/layers/apz/util/APZCCallbackHelper.cpp
gfx/layers/apz/util/APZCCallbackHelper.h
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -844,16 +844,32 @@ TabChild::Create(nsIContentChild* aManag
         return child.forget();
     }
 
     nsRefPtr<TabChild> iframe = new TabChild(aManager, aTabId,
                                              aContext, aChromeFlags);
     return NS_SUCCEEDED(iframe->Init()) ? iframe.forget() : nullptr;
 }
 
+class TabChildSetTargetAPZCCallback : public SetTargetAPZCCallback {
+public:
+  explicit TabChildSetTargetAPZCCallback(TabChild* aTabChild)
+    : mTabChild(do_GetWeakReference(static_cast<nsITabChild*>(aTabChild)))
+  {}
+
+  void Run(uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) const MOZ_OVERRIDE {
+    if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(mTabChild)) {
+      static_cast<TabChild*>(tabChild.get())->SendSetTargetAPZC(aInputBlockId, aTargets);
+    }
+  }
+
+private:
+  nsWeakPtr mTabChild;
+};
+
 
 TabChild::TabChild(nsIContentChild* aManager,
                    const TabId& aTabId,
                    const TabContext& aContext,
                    uint32_t aChromeFlags)
   : TabContext(aContext)
   , mRemoteFrame(nullptr)
   , mManager(aManager)
@@ -869,16 +885,17 @@ TabChild::TabChild(nsIContentChild* aMan
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mPendingTouchPreventedResponse(false)
   , mPendingTouchPreventedBlockId(0)
   , mTouchEndCancelled(false)
   , mEndTouchIsClick(false)
   , mIgnoreKeyPressEvent(false)
   , mActiveElementManager(new ActiveElementManager())
+  , mSetTargetAPZCCallback(new TabChildSetTargetAPZCCallback(this))
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
   , mDPI(0)
   , mDefaultScale(0)
   , mIPCOpen(true)
   , mParentIsActive(false)
 {
@@ -2317,25 +2334,18 @@ TabChild::RecvRealMouseEvent(const Widge
 
 bool
 TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
                               const ScrollableLayerGuid& aGuid,
                               const uint64_t& aInputBlockId)
 {
   if (IsAsyncPanZoomEnabled()) {
     nsCOMPtr<nsIDocument> document(GetDocument());
-    if (nsIPresShell* shell = document->GetShell()) {
-      if (nsIFrame* rootFrame = shell->GetRootFrame()) {
-        nsTArray<ScrollableLayerGuid> targets;
-        bool waitForRefresh =
-          PrepareForSetTargetAPZCNotification(aGuid, aInputBlockId, rootFrame, aEvent.refPoint, &targets);
-
-        SendSetTargetAPZCNotification(shell, aInputBlockId, targets, waitForRefresh);
-      }
-    }
+    APZCCallbackHelper::SendSetTargetAPZCNotification(WebWidget(), document, aEvent, aGuid,
+        aInputBlockId, mSetTargetAPZCCallback);
   }
 
   WidgetWheelEvent event(aEvent);
   event.widget = mWidget;
   APZCCallbackHelper::DispatchWidgetEvent(event);
 
   if (IsAsyncPanZoomEnabled()) {
     SendContentReceivedInputBlock(aGuid, aInputBlockId, event.mFlags.mDefaultPrevented);
@@ -2480,170 +2490,16 @@ TabChild::CancelTapTracking()
 {
   mActivePointerId = -1;
   if (mTapHoldTimer) {
     mTapHoldTimer->Cancel();
   }
   mTapHoldTimer = nullptr;
 }
 
-static nsIScrollableFrame*
-GetScrollableAncestorFrame(nsIFrame* aTarget)
-{
-  if (!aTarget) {
-    return nullptr;
-  }
-  uint32_t flags = nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT
-                 | nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE;
-  return nsLayoutUtils::GetNearestScrollableFrame(aTarget, flags);
-}
-
-static dom::Element*
-GetDisplayportElementFor(nsIScrollableFrame* aScrollableFrame)
-{
-  if (!aScrollableFrame) {
-    return nullptr;
-  }
-  nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
-  if (!scrolledFrame) {
-    return nullptr;
-  }
-  // |scrolledFrame| should at this point be the root content frame of the
-  // nearest ancestor scrollable frame. The element corresponding to this
-  // frame should be the one with the displayport set on it, so find that
-  // element and return it.
-  nsIContent* content = scrolledFrame->GetContent();
-  MOZ_ASSERT(content->IsElement()); // roc says this must be true
-  return content->AsElement();
-}
-
-class DisplayportSetListener : public nsAPostRefreshObserver {
-public:
-  DisplayportSetListener(TabChild* aTabChild,
-                         nsIPresShell* aPresShell,
-                         const uint64_t& aInputBlockId,
-                         const nsTArray<ScrollableLayerGuid>& aTargets)
-    : mTabChild(aTabChild)
-    , mPresShell(aPresShell)
-    , mInputBlockId(aInputBlockId)
-    , mTargets(aTargets)
-  {
-  }
-
-  virtual ~DisplayportSetListener()
-  {
-  }
-
-  void DidRefresh() MOZ_OVERRIDE {
-    if (!mTabChild) {
-      MOZ_ASSERT_UNREACHABLE("Post-refresh observer fired again after failed attempt at unregistering it");
-      return;
-    }
-
-    TABC_LOG("Got refresh, sending target APZCs for input block %" PRIu64 "\n", mInputBlockId);
-    mTabChild->SendSetTargetAPZC(mInputBlockId, mTargets);
-
-    if (!mPresShell->RemovePostRefreshObserver(this)) {
-      MOZ_ASSERT_UNREACHABLE("Unable to unregister post-refresh observer! Leaking it instead of leaving garbage registered");
-      // Graceful handling, just in case...
-      mTabChild = nullptr;
-      mPresShell = nullptr;
-      return;
-    }
-
-    delete this;
-  }
-
-private:
-  nsRefPtr<TabChild> mTabChild;
-  nsRefPtr<nsIPresShell> mPresShell;
-  uint64_t mInputBlockId;
-  nsTArray<ScrollableLayerGuid> mTargets;
-};
-
-bool
-TabChild::PrepareForSetTargetAPZCNotification(const ScrollableLayerGuid& aGuid,
-                                              const uint64_t& aInputBlockId,
-                                              nsIFrame* aRootFrame,
-                                              const LayoutDeviceIntPoint& aRefPoint,
-                                              nsTArray<ScrollableLayerGuid>* aTargets)
-{
-  ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
-  nsPoint point =
-    nsLayoutUtils::GetEventCoordinatesRelativeTo(WebWidget(), aRefPoint, aRootFrame);
-  nsIFrame* target =
-    nsLayoutUtils::GetFrameForPoint(aRootFrame, point, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
-  nsIScrollableFrame* scrollAncestor = GetScrollableAncestorFrame(target);
-  nsCOMPtr<dom::Element> dpElement = GetDisplayportElementFor(scrollAncestor);
-
-  nsAutoString dpElementDesc;
-  if (dpElement) {
-    dpElement->Describe(dpElementDesc);
-  }
-  TABC_LOG("For input block %" PRIu64 " found scrollable element %p (%s)\n",
-      aInputBlockId, dpElement.get(),
-      NS_LossyConvertUTF16toASCII(dpElementDesc).get());
-
-  bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
-    dpElement, &(guid.mPresShellId), &(guid.mScrollId));
-  aTargets->AppendElement(guid);
-
-  if (!guidIsValid || nsLayoutUtils::GetDisplayPort(dpElement, nullptr)) {
-    return false;
-  }
-
-  TABC_LOG("%p didn't have a displayport, so setting one...\n", dpElement.get());
-  return nsLayoutUtils::CalculateAndSetDisplayPortMargins(
-      scrollAncestor, nsLayoutUtils::RepaintMode::Repaint);
-}
-
-void
-TabChild::SendSetTargetAPZCNotification(nsIPresShell* aShell,
-                                        const uint64_t& aInputBlockId,
-                                        const nsTArray<ScrollableLayerGuid>& aTargets,
-                                        bool aWaitForRefresh)
-{
-  bool waitForRefresh = aWaitForRefresh;
-  if (waitForRefresh) {
-    TABC_LOG("At least one target got a new displayport, need to wait for refresh\n");
-    waitForRefresh = aShell->AddPostRefreshObserver(
-      new DisplayportSetListener(this, aShell, aInputBlockId, aTargets));
-  }
-  if (!waitForRefresh) {
-    TABC_LOG("Sending target APZCs for input block %" PRIu64 "\n", aInputBlockId);
-    SendSetTargetAPZC(aInputBlockId, aTargets);
-  } else {
-    TABC_LOG("Successfully registered post-refresh observer\n");
-  }
-}
-
-void
-TabChild::SendSetTargetAPZCNotification(const WidgetTouchEvent& aEvent,
-                                        const ScrollableLayerGuid& aGuid,
-                                        const uint64_t& aInputBlockId)
-{
-  nsCOMPtr<nsIDocument> document(GetDocument());
-  nsIPresShell* shell = document->GetShell();
-  if (!shell) {
-    return;
-  }
-  nsIFrame* rootFrame = shell->GetRootFrame();
-  if (!rootFrame) {
-    return;
-  }
-
-  bool waitForRefresh = false;
-  nsTArray<ScrollableLayerGuid> targets;
-  for (size_t i = 0; i < aEvent.touches.Length(); i++) {
-    waitForRefresh |= PrepareForSetTargetAPZCNotification(aGuid, aInputBlockId,
-        rootFrame, aEvent.touches[i]->mRefPoint, &targets);
-  }
-  SendSetTargetAPZCNotification(shell, aInputBlockId, targets, waitForRefresh);
-}
-
 float
 TabChild::GetPresShellResolution() const
 {
   nsCOMPtr<nsIDocument> document(GetDocument());
   nsIPresShell* shell = document->GetShell();
   if (!shell) {
     return 1.0f;
   }
@@ -2659,17 +2515,19 @@ TabChild::RecvRealTouchEvent(const Widge
 
   WidgetTouchEvent localEvent(aEvent);
   localEvent.widget = mWidget;
 
   APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
       mWidget->GetDefaultScale(), GetPresShellResolution());
 
   if (localEvent.message == NS_TOUCH_START && IsAsyncPanZoomEnabled()) {
-    SendSetTargetAPZCNotification(localEvent, aGuid, aInputBlockId);
+    nsCOMPtr<nsIDocument> document = GetDocument();
+    APZCCallbackHelper::SendSetTargetAPZCNotification(WebWidget(), document,
+        localEvent, aGuid, aInputBlockId, mSetTargetAPZCCallback);
   }
 
   // Dispatch event to content (potentially a long-running operation)
   nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
 
   if (!IsAsyncPanZoomEnabled()) {
     UpdateTapState(localEvent, status);
     return true;
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -41,16 +41,17 @@ class nsIDOMWindowUtils;
 
 namespace mozilla {
 namespace layout {
 class RenderFrameChild;
 }
 
 namespace layers {
 class ActiveElementManager;
+struct SetTargetAPZCCallback;
 }
 
 namespace widget {
 struct AutoCacheNativeKeyCommands;
 }
 
 namespace plugins {
 class PluginWidgetChild;
@@ -247,16 +248,17 @@ class TabChild MOZ_FINAL : public TabChi
                            public nsIObserver,
                            public TabContext,
                            public nsITooltipListener
 {
     typedef mozilla::dom::ClonedMessageData ClonedMessageData;
     typedef mozilla::layout::RenderFrameChild RenderFrameChild;
     typedef mozilla::layout::ScrollingBehavior ScrollingBehavior;
     typedef mozilla::layers::ActiveElementManager ActiveElementManager;
+    typedef mozilla::layers::SetTargetAPZCCallback SetTargetAPZCCallback;
 
 public:
     /**
      * Find TabChild of aTabId in the same content process of the
      * caller.
      */
     static already_AddRefed<TabChild> FindTabChild(const TabId& aTabId);
 
@@ -592,36 +594,16 @@ private:
                         bool* aWindowIsNew,
                         nsIDOMWindow** aReturn);
 
     bool HasValidInnerSize();
 
     void SendPendingTouchPreventedResponse(bool aPreventDefault,
                                            const ScrollableLayerGuid& aGuid);
 
-    // Adds the scrollable layer target to the target list, and returns whether
-    // or not the caller should wait for a refresh to send a target
-    // notification.
-    bool PrepareForSetTargetAPZCNotification(const ScrollableLayerGuid& aGuid,
-                                             const uint64_t& aInputBlockId,
-                                             nsIFrame* aRootFrame,
-                                             const LayoutDeviceIntPoint& aRefPoint,
-                                             nsTArray<ScrollableLayerGuid>* aTargets);
-
-    // Sends a SetTarget notification for APZC, given one or more previous
-    // calls to PrepareForAPZCSetTargetNotification().
-    void SendSetTargetAPZCNotification(nsIPresShell* aShell,
-                                       const uint64_t& aInputBlockId,
-                                       const nsTArray<ScrollableLayerGuid>& aTargets,
-                                       bool aWaitForRefresh);
-
-    void SendSetTargetAPZCNotification(const WidgetTouchEvent& aEvent,
-                                       const mozilla::layers::ScrollableLayerGuid& aGuid,
-                                       const uint64_t& aInputBlockId);
-
     // Get the pres shell resolution of the document in this tab.
     float GetPresShellResolution() const;
 
     void SetTabId(const TabId& aTabId);
 
     class CachedFileDescriptorInfo;
     class CachedFileDescriptorCallbackRunnable;
     class DelayedDeleteRunnable;
@@ -659,16 +641,17 @@ private:
     ScrollableLayerGuid mPendingTouchPreventedGuid;
     uint64_t mPendingTouchPreventedBlockId;
 
     bool mTouchEndCancelled;
     bool mEndTouchIsClick;
 
     bool mIgnoreKeyPressEvent;
     nsRefPtr<ActiveElementManager> mActiveElementManager;
+    nsRefPtr<SetTargetAPZCCallback> mSetTargetAPZCCallback;
     bool mHasValidInnerSize;
     bool mDestroyed;
     // Position of tab, relative to parent widget (typically the window)
     nsIntPoint mChromeDisp;
     TabId mUniqueId;
     float mDPI;
     double mDefaultScale;
     bool mIPCOpen;
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -429,10 +429,179 @@ APZCCallbackHelper::FireSingleTapEvent(c
   APZCCH_LOG("Dispatching single-tap component events to %s\n",
     Stringify(aPoint).c_str());
   int time = 0;
   DispatchSynthesizedMouseEvent(NS_MOUSE_MOVE, time, aPoint, aWidget);
   DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_DOWN, time, aPoint, aWidget);
   DispatchSynthesizedMouseEvent(NS_MOUSE_BUTTON_UP, time, aPoint, aWidget);
 }
 
+static nsIScrollableFrame*
+GetScrollableAncestorFrame(nsIFrame* aTarget)
+{
+  if (!aTarget) {
+    return nullptr;
+  }
+  uint32_t flags = nsLayoutUtils::SCROLLABLE_ALWAYS_MATCH_ROOT
+                 | nsLayoutUtils::SCROLLABLE_ONLY_ASYNC_SCROLLABLE;
+  return nsLayoutUtils::GetNearestScrollableFrame(aTarget, flags);
+}
+
+static dom::Element*
+GetDisplayportElementFor(nsIScrollableFrame* aScrollableFrame)
+{
+  if (!aScrollableFrame) {
+    return nullptr;
+  }
+  nsIFrame* scrolledFrame = aScrollableFrame->GetScrolledFrame();
+  if (!scrolledFrame) {
+    return nullptr;
+  }
+  // |scrolledFrame| should at this point be the root content frame of the
+  // nearest ancestor scrollable frame. The element corresponding to this
+  // frame should be the one with the displayport set on it, so find that
+  // element and return it.
+  nsIContent* content = scrolledFrame->GetContent();
+  MOZ_ASSERT(content->IsElement()); // roc says this must be true
+  return content->AsElement();
+}
+
+// Determine the scrollable target frame for the given point and add it to
+// the target list. If the frame doesn't have a displayport, set one.
+// Return whether or not a displayport was set.
+static bool
+PrepareForSetTargetAPZCNotification(nsIWidget* aWidget,
+                                    const ScrollableLayerGuid& aGuid,
+                                    nsIFrame* aRootFrame,
+                                    const LayoutDeviceIntPoint& aRefPoint,
+                                    nsTArray<ScrollableLayerGuid>* aTargets)
+{
+  ScrollableLayerGuid guid(aGuid.mLayersId, 0, FrameMetrics::NULL_SCROLL_ID);
+  nsPoint point =
+    nsLayoutUtils::GetEventCoordinatesRelativeTo(aWidget, aRefPoint, aRootFrame);
+  nsIFrame* target =
+    nsLayoutUtils::GetFrameForPoint(aRootFrame, point, nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME);
+  nsIScrollableFrame* scrollAncestor = GetScrollableAncestorFrame(target);
+  nsCOMPtr<dom::Element> dpElement = GetDisplayportElementFor(scrollAncestor);
+
+  nsAutoString dpElementDesc;
+  if (dpElement) {
+    dpElement->Describe(dpElementDesc);
+  }
+  APZCCH_LOG("For input block %" PRIu64 " found scrollable element %p (%s)\n",
+      aInputBlockId, dpElement.get(),
+      NS_LossyConvertUTF16toASCII(dpElementDesc).get());
+
+  bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
+    dpElement, &(guid.mPresShellId), &(guid.mScrollId));
+  aTargets->AppendElement(guid);
+
+  if (!guidIsValid || nsLayoutUtils::GetDisplayPort(dpElement, nullptr)) {
+    return false;
+  }
+
+  APZCCH_LOG("%p didn't have a displayport, so setting one...\n", dpElement.get());
+  return nsLayoutUtils::CalculateAndSetDisplayPortMargins(
+      scrollAncestor, nsLayoutUtils::RepaintMode::Repaint);
+}
+
+class DisplayportSetListener : public nsAPostRefreshObserver {
+public:
+  DisplayportSetListener(const nsRefPtr<SetTargetAPZCCallback>& aCallback,
+                         nsIPresShell* aPresShell,
+                         const uint64_t& aInputBlockId,
+                         const nsTArray<ScrollableLayerGuid>& aTargets)
+    : mCallback(aCallback)
+    , mPresShell(aPresShell)
+    , mInputBlockId(aInputBlockId)
+    , mTargets(aTargets)
+  {
+  }
+
+  virtual ~DisplayportSetListener()
+  {
+  }
+
+  void DidRefresh() MOZ_OVERRIDE {
+    if (!mCallback) {
+      MOZ_ASSERT_UNREACHABLE("Post-refresh observer fired again after failed attempt at unregistering it");
+      return;
+    }
+
+    APZCCH_LOG("Got refresh, sending target APZCs for input block %" PRIu64 "\n", mInputBlockId);
+    mCallback->Run(mInputBlockId, mTargets);
+
+    if (!mPresShell->RemovePostRefreshObserver(this)) {
+      MOZ_ASSERT_UNREACHABLE("Unable to unregister post-refresh observer! Leaking it instead of leaving garbage registered");
+      // Graceful handling, just in case...
+      mCallback = nullptr;
+      mPresShell = nullptr;
+      return;
+    }
+
+    delete this;
+  }
+
+private:
+  nsRefPtr<SetTargetAPZCCallback> mCallback;
+  nsRefPtr<nsIPresShell> mPresShell;
+  uint64_t mInputBlockId;
+  nsTArray<ScrollableLayerGuid> mTargets;
+};
+
+// Sends a SetTarget notification for APZC, given one or more previous
+// calls to PrepareForAPZCSetTargetNotification().
+static void
+SendSetTargetAPZCNotificationHelper(nsIPresShell* aShell,
+                                    const uint64_t& aInputBlockId,
+                                    const nsTArray<ScrollableLayerGuid>& aTargets,
+                                    bool aWaitForRefresh,
+                                    const nsRefPtr<SetTargetAPZCCallback>& aCallback)
+{
+  bool waitForRefresh = aWaitForRefresh;
+  if (waitForRefresh) {
+    APZCCH_LOG("At least one target got a new displayport, need to wait for refresh\n");
+    waitForRefresh = aShell->AddPostRefreshObserver(
+      new DisplayportSetListener(aCallback, aShell, aInputBlockId, aTargets));
+  }
+  if (!waitForRefresh) {
+    APZCCH_LOG("Sending target APZCs for input block %" PRIu64 "\n", aInputBlockId);
+    aCallback->Run(aInputBlockId, aTargets);
+  } else {
+    APZCCH_LOG("Successfully registered post-refresh observer\n");
+  }
+}
+
+void
+APZCCallbackHelper::SendSetTargetAPZCNotification(nsIWidget* aWidget,
+                                                  nsIDocument* aDocument,
+                                                  const WidgetGUIEvent& aEvent,
+                                                  const ScrollableLayerGuid& aGuid,
+                                                  uint64_t aInputBlockId,
+                                                  const nsRefPtr<SetTargetAPZCCallback>& aCallback)
+{
+  if (nsIPresShell* shell = aDocument->GetShell()) {
+    if (nsIFrame* rootFrame = shell->GetRootFrame()) {
+      bool waitForRefresh = false;
+      nsTArray<ScrollableLayerGuid> targets;
+
+      if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) {
+        for (size_t i = 0; i < touchEvent->touches.Length(); i++) {
+          waitForRefresh |= PrepareForSetTargetAPZCNotification(aWidget, aGuid,
+              rootFrame, touchEvent->touches[i]->mRefPoint, &targets);
+        }
+      } else if (const WidgetWheelEvent* wheelEvent = aEvent.AsWheelEvent()) {
+        waitForRefresh = PrepareForSetTargetAPZCNotification(aWidget, aGuid,
+            rootFrame, wheelEvent->refPoint, &targets);
+      }
+      // TODO: Do other types of events need to be handled?
+
+      if (!targets.IsEmpty()) {
+        SendSetTargetAPZCNotificationHelper(shell, aInputBlockId, targets,
+            waitForRefresh, aCallback);
+      }
+    }
+  }
+}
+
 }
 }
+
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -13,16 +13,27 @@
 class nsIContent;
 class nsIDocument;
 class nsIWidget;
 template<class T> struct already_AddRefed;
 
 namespace mozilla {
 namespace layers {
 
+/* A base class for callbacks to be passed to APZCCallbackHelper::SendSetTargetAPZCNotification.
+ * If we had something like std::function, we could just use
+ * std::function<void(uint64_t, const nsTArray<ScrollableLayerGuid>&)>. */
+struct SetTargetAPZCCallback {
+public:
+  NS_INLINE_DECL_REFCOUNTING(SetTargetAPZCCallback)
+  virtual void Run(uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) const = 0;
+protected:
+  virtual ~SetTargetAPZCCallback() {}
+};
+
 /* This class contains some helper methods that facilitate implementing the
    GeckoContentController callback interface required by the AsyncPanZoomController.
    Since different platforms need to implement this interface in similar-but-
    not-quite-the-same ways, this utility class provides some helpful methods
    to hold code that can be shared across the different platform implementations.
  */
 class APZCCallbackHelper
 {
@@ -113,14 +124,27 @@ public:
                                                        uint64_t aTime,
                                                        const LayoutDevicePoint& aRefPoint,
                                                        nsIWidget* aWidget);
 
     /* Fire a single-tap event at the given point. The event is dispatched
      * via the given widget. */
     static void FireSingleTapEvent(const LayoutDevicePoint& aPoint,
                                    nsIWidget* aWidget);
+
+    /* Perform hit-testing on the touch points of |aEvent| to determine
+     * which scrollable frames they target. If any of these frames don't have
+     * a displayport, set one. Finally, invoke the provided callback with
+     * the guids of the target frames. If any displayports needed to be set,
+     * the callback is invoked after the next refresh, otherwise it's invoked
+     * right away. */
+    static void SendSetTargetAPZCNotification(nsIWidget* aWidget,
+                                              nsIDocument* aDocument,
+                                              const WidgetGUIEvent& aEvent,
+                                              const ScrollableLayerGuid& aGuid,
+                                              uint64_t aInputBlockId,
+                                              const nsRefPtr<SetTargetAPZCCallback>& aCallback);
 };
 
 }
 }
 
 #endif /* mozilla_layers_APZCCallbackHelper_h */