Bug 775448: Disable async scrolling when we detect a scrollable subframe r=cjones
authorDoug Sherk <dsherk2@mozilla.com>
Wed, 08 Aug 2012 13:38:06 -0700
changeset 107894 282bfd3e56e029cc430ac372a7cdcaa660586e1f
parent 107893 986a9cafa237ab8b46ea3cc6e5bff270f4ff64fa
child 107895 174a57bbcb225b08864d9284a0ffe86d54a25346
push id214
push userakeybl@mozilla.com
push dateWed, 14 Nov 2012 20:38:59 +0000
treeherdermozilla-release@c8b08ec8e1aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscjones
bugs775448
milestone17.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 775448: Disable async scrolling when we detect a scrollable subframe r=cjones
dom/browser-element/BrowserElementScrolling.js
dom/ipc/TabChild.cpp
gfx/layers/ipc/AsyncPanZoomController.cpp
gfx/layers/ipc/AsyncPanZoomController.h
layout/ipc/PRenderFrame.ipdl
layout/ipc/RenderFrameChild.cpp
layout/ipc/RenderFrameChild.h
layout/ipc/RenderFrameParent.cpp
layout/ipc/RenderFrameParent.h
--- a/dom/browser-element/BrowserElementScrolling.js
+++ b/dom/browser-element/BrowserElementScrolling.js
@@ -38,16 +38,26 @@ const ContentPanning = {
 
   onTouchStart: function cp_onTouchStart(evt) {
     this.dragging = true;
     this.panning = false;
 
     let oldTarget = this.target;
     [this.target, this.scrollCallback] = this.getPannable(evt.target);
 
+    // If we found a target, that means we have found a scrollable subframe. In
+    // this case, and if we are using async panning and zooming on the parent
+    // frame, inform the pan/zoom controller that it should not attempt to
+    // handle any touch events it gets until the next batch (meaning the next
+    // time we get a touch end).
+    if (this.target != null && ContentPanning._asyncPanZoomForViewportFrame) {
+      var os = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+      os.notifyObservers(docShell, 'cancel-default-pan-zoom', null);
+    }
+
     // If there is a pan animation running (from a previous pan gesture) and
     // the user touch back the screen, stop this animation immediatly and
     // prevent the possible click action if the touch happens on the same
     // target.
     this.preventNextClick = false;
     if (KineticPanning.active) {
       KineticPanning.stop();
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -115,16 +115,22 @@ TabChild::Observe(nsISupports *aSubject,
   if (!strcmp(aTopic, "dom-touch-listener-added")) {
     nsCOMPtr<nsIDOMWindow> subject(do_QueryInterface(aSubject));
     nsCOMPtr<nsIDOMWindow> win(do_GetInterface(mWebNav));
     nsCOMPtr<nsIDOMWindow> topSubject;
     subject->GetTop(getter_AddRefs(topSubject));
     if (win == topSubject) {
       SendNotifyDOMTouchListenerAdded();
     }
+  } else if (!strcmp(aTopic, "cancel-default-pan-zoom")) {
+    nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aSubject));
+    nsCOMPtr<nsITabChild> tabChild(GetTabChildFrom(docShell));
+    if (tabChild == this) {
+      mRemoteFrame->CancelDefaultPanZoom();
+    }
   }
 
   return NS_OK;
 }
 
 nsresult
 TabChild::Init()
 {
@@ -143,16 +149,19 @@ TabChild::Init()
 
   nsCOMPtr<nsIObserverService> observerService =
     do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
 
   if (observerService) {
     observerService->AddObserver(this,
                                  "dom-touch-listener-added",
                                  false);
+    observerService->AddObserver(this,
+                                 "cancel-default-pan-zoom",
+                                 false);
   }
 
   return NS_OK;
 }
 
 NS_INTERFACE_MAP_BEGIN(TabChild)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIWebBrowserChrome)
   NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
--- a/gfx/layers/ipc/AsyncPanZoomController.cpp
+++ b/gfx/layers/ipc/AsyncPanZoomController.cpp
@@ -49,17 +49,18 @@ AsyncPanZoomController::AsyncPanZoomCont
   :  mGeckoContentController(aGeckoContentController),
      mX(this),
      mY(this),
      mMonitor("AsyncPanZoomController"),
      mLastSampleTime(TimeStamp::Now()),
      mState(NOTHING),
      mDPI(72),
      mContentPainterStatus(CONTENT_IDLE),
-     mMayHaveTouchListeners(false)
+     mMayHaveTouchListeners(false),
+     mDisableNextTouchBatch(false)
 {
   if (aGestures == USE_GESTURE_DETECTOR) {
     mGestureEventListener = new GestureEventListener(this);
   }
 
   SetDPI(mDPI);
 }
 
@@ -218,16 +219,20 @@ nsEventStatus AsyncPanZoomController::On
       NS_WARNING("Unhandled case in OnTouchStart");
       break;
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnTouchMove(const MultiTouchInput& aEvent) {
+  if (mDisableNextTouchBatch) {
+    return nsEventStatus_eIgnore;
+  }
+
   switch (mState) {
     case FLING:
     case NOTHING:
       // May happen if the user double-taps and drags without lifting after the
       // second tap. Ignore the move if this happens.
       return nsEventStatus_eIgnore;
 
     case TOUCHING: {
@@ -249,16 +254,21 @@ nsEventStatus AsyncPanZoomController::On
       NS_WARNING("Gesture listener should have handled pinching in OnTouchMove.");
       return nsEventStatus_eIgnore;
   }
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent) {
+  if (mDisableNextTouchBatch) {
+    mDisableNextTouchBatch = false;
+    return nsEventStatus_eIgnore;
+  }
+
   switch (mState) {
   case FLING:
     // Should never happen.
     NS_WARNING("Received impossible touch end in OnTouchEnd.");
     // Fall through.
   case NOTHING:
     // May happen if the user double-taps and drags without lifting after the
     // second tap. Ignore if this happens.
@@ -806,10 +816,14 @@ void AsyncPanZoomController::UpdateViewp
   metrics.mViewport = nsIntRect(0, 0, aWidth, aHeight);
   mFrameMetrics = metrics;
 }
 
 void AsyncPanZoomController::NotifyDOMTouchListenerAdded() {
   mMayHaveTouchListeners = true;
 }
 
+void AsyncPanZoomController::CancelDefaultPanZoom() {
+  mDisableNextTouchBatch = true;
+}
+
 }
 }
--- a/gfx/layers/ipc/AsyncPanZoomController.h
+++ b/gfx/layers/ipc/AsyncPanZoomController.h
@@ -104,16 +104,26 @@ public:
    * that allows touch listeners to preventDefault any touch inputs. This should
    * not be called unless there are actually touch listeners as it introduces
    * potentially unbounded lag because it causes a round-trip through content.
    * Usually, if content is responding in a timely fashion, this only introduces
    * a nearly constant few hundred ms of lag.
    */
   void NotifyDOMTouchListenerAdded();
 
+  /**
+   * We have found a scrollable 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();
+
   // --------------------------------------------------------------------------
   // These methods must only be called on the compositor thread.
   //
 
   /**
    * 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
@@ -403,15 +413,19 @@ private:
   // this status will not be updated. It is only changed when this class
   // requests a repaint.
   ContentPainterStatus mContentPainterStatus;
 
   // Whether or not we might have touch listeners. This is a conservative
   // approximation and may not be accurate.
   bool mMayHaveTouchListeners;
 
+  // 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;
+
   friend class Axis;
 };
 
 }
 }
 
 #endif // mozilla_layers_PanZoomController_h
--- a/layout/ipc/PRenderFrame.ipdl
+++ b/layout/ipc/PRenderFrame.ipdl
@@ -36,16 +36,18 @@ parent:
      *
      * |id| is set to 0 in the "direct" case, and to a whole number
      * in the "indirect" case.
      */
     async PLayers();
 
     async NotifyCompositorTransaction();
 
+    async CancelDefaultPanZoom();
+
     async __delete__();
 
 state EMPTY_OR_DIRECT_COMPOSITOR:
     recv PLayers goto HAVE_CONTENT;
     recv NotifyCompositorTransaction goto EMPTY_OR_DIRECT_COMPOSITOR;
     recv __delete__;
 
 state HAVE_CONTENT:
--- a/layout/ipc/RenderFrameChild.cpp
+++ b/layout/ipc/RenderFrameChild.cpp
@@ -27,16 +27,22 @@ RenderFrameChild::Destroy()
     layers->Destroy();
     // |layers| was just deleted, take care
   }
 
   Send__delete__(this);
   // WARNING: |this| is dead, hands off
 }
 
+void
+RenderFrameChild::CancelDefaultPanZoom()
+{
+  SendCancelDefaultPanZoom();
+}
+
 PLayersChild*
 RenderFrameChild::AllocPLayers()
 {
   return new ShadowLayersChild();
 }
 
 bool
 RenderFrameChild::DeallocPLayers(PLayersChild* aLayers)
--- a/layout/ipc/RenderFrameChild.h
+++ b/layout/ipc/RenderFrameChild.h
@@ -14,16 +14,18 @@ namespace mozilla {
 namespace layout {
 
 class RenderFrameChild : public PRenderFrameChild
 {
 public:
   RenderFrameChild() {}
   virtual ~RenderFrameChild() {}
 
+  void CancelDefaultPanZoom();
+
   void Destroy();
 
 protected:
   virtual PLayersChild* AllocPLayers() MOZ_OVERRIDE;
   virtual bool DeallocPLayers(PLayersChild* aLayers) MOZ_OVERRIDE;
 };
 
 } // namespace layout
--- a/layout/ipc/RenderFrameParent.cpp
+++ b/layout/ipc/RenderFrameParent.cpp
@@ -715,16 +715,25 @@ RenderFrameParent::ActorDestroy(ActorDes
 
 bool
 RenderFrameParent::RecvNotifyCompositorTransaction()
 {
   TriggerRepaint();
   return true;
 }
 
+bool
+RenderFrameParent::RecvCancelDefaultPanZoom()
+{
+  if (mPanZoomController) {
+    mPanZoomController->CancelDefaultPanZoom();
+  }
+  return true;
+}
+
 PLayersParent*
 RenderFrameParent::AllocPLayers()
 {
   if (!mFrameLoader || mFrameLoaderDestroyed) {
     return nullptr;
   }
   nsRefPtr<LayerManager> lm = GetFrom(mFrameLoader);
   return new ShadowLayersParent(lm->AsShadowManager(), this, 0);
--- a/layout/ipc/RenderFrameParent.h
+++ b/layout/ipc/RenderFrameParent.h
@@ -95,16 +95,18 @@ public:
 
   void NotifyDOMTouchListenerAdded();
 
 protected:
   void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
   virtual bool RecvNotifyCompositorTransaction() MOZ_OVERRIDE;
 
+  virtual bool RecvCancelDefaultPanZoom() MOZ_OVERRIDE;
+
   virtual PLayersParent* AllocPLayers() MOZ_OVERRIDE;
   virtual bool DeallocPLayers(PLayersParent* aLayers) MOZ_OVERRIDE;
 
 private:
   void BuildViewMap();
   void TriggerRepaint();
   void DispatchEventForPanZoomController(const InputEvent& aEvent);