Bug 898580 - Metro mechanism for content to inform APZC about scroll events. Handles multi-APZC. r=bbondy, r=kats
authorBotond Ballo <botond@mozilla.com>
Mon, 12 Aug 2013 16:31:49 -0400
changeset 143671 3078301c9b34bf238c0330f7bf8c1db198ed9eab
parent 143670 842ac636819c9f2cc3d2c79a2071d8dd01e6a7ef
child 143672 1383705460abe24263f075a603c2f610b0ac9a0d
push id32724
push userryanvm@gmail.com
push dateWed, 21 Aug 2013 15:07:17 +0000
treeherdermozilla-inbound@2654e3d27d41 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbondy, kats
bugs898580
milestone26.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 898580 - Metro mechanism for content to inform APZC about scroll events. Handles multi-APZC. r=bbondy, r=kats
browser/metro/base/content/apzc.js
browser/metro/base/content/bindings/browser.js
browser/metro/base/content/bindings/browser.xml
widget/windows/winrt/MetroWidget.cpp
widget/windows/winrt/MetroWidget.h
--- a/browser/metro/base/content/apzc.js
+++ b/browser/metro/base/content/apzc.js
@@ -41,21 +41,23 @@ var APZCObserver = {
                           getInterface(Ci.nsIDOMWindowUtils);
         windowUtils.setDisplayPortForElement(0, 0, ContentAreaObserver.width,
                                              ContentAreaObserver.height,
                                              windowUtils.findElementWithViewId(ROOT_ID));
         break;
       case 'TabOpen': {
         let browser = aEvent.originalTarget.linkedBrowser;
         browser.addEventListener("pageshow", this, true);
+        browser.messageManager.addMessageListener("scroll", this);
         break;
       }
       case 'TabClose': {
         let browser = aEvent.originalTarget.linkedBrowser;
-        browser.removeEventListener("pageshow", this);
+        browser.removeEventListener("pageshow", this, true);
+        browser.messageManager.removeMessageListener("scroll", this);
         break;
       }
     }
   },
   shutdown: function shutdown() {
     if (!this._enabled) {
       return;
     }
@@ -108,10 +110,21 @@ var APZCObserver = {
       Util.dumpLn("APZC pan-begin");
       if (InputSourceHelper.isPrecise) {
         InputSourceHelper._imprecise();
       }
 
     } else if (aTopic == "apzc-handle-pan-end") {
       Util.dumpLn("APZC pan-end");
     }
+  },
+
+  receiveMessage: function(aMessage) {
+    let json = aMessage.json;
+    switch (aMessage.name) {
+      case "scroll": {
+        let data = json.viewId + " " + json.presShellId + " (" + json.scrollOffset.x + ", " + json.scrollOffset.y + ")";
+        Services.obs.notifyObservers(null, "scroll-offset-changed", data);
+        break;
+      }
+    }
   }
 };
--- a/browser/metro/base/content/bindings/browser.js
+++ b/browser/metro/base/content/bindings/browser.js
@@ -647,21 +647,17 @@ let ContentScroll =  {
 
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
       case "pagehide":
         this._scrollOffset = { x: 0, y: 0 };
         break;
 
       case "scroll": {
-        let doc = aEvent.target;
-        if (doc != content.document)
-          break;
-
-        this.sendScroll();
+        this.sendScroll(aEvent.target);
         break;
       }
 
       case "MozScrolledAreaChanged": {
         let doc = aEvent.originalTarget;
         if (content != doc.defaultView) // We are only interested in root scroll pane changes
           return;
 
@@ -678,23 +674,45 @@ let ContentScroll =  {
           sendAsyncMessage("Content:UpdateDisplayPort");
         }, false);
 
         break;
       }
     }
   },
 
-  sendScroll: function sendScroll() {
-    let scrollOffset = this.getScrollOffset(content);
-    if (this._scrollOffset.x == scrollOffset.x && this._scrollOffset.y == scrollOffset.y)
-      return;
+  sendScroll: function sendScroll(target) {
+    let isRoot = false;
+    if (target instanceof Ci.nsIDOMDocument) {
+      var window = target.defaultView;
+      var scrollOffset = this.getScrollOffset(window);
+      var element = target.documentElement;
 
-    this._scrollOffset = scrollOffset;
-    sendAsyncMessage("scroll", scrollOffset);
+      if (target == content.document) {
+        if (this._scrollOffset.x == scrollOffset.x && this._scrollOffset.y == scrollOffset.y) {
+          return;
+        }
+        this._scrollOffset = scrollOffset;
+        isRoot = true;
+      }
+    } else {
+      var window = target.currentDoc.defaultView;
+      var scrollOffset = this.getScrollOffsetForElement(target);
+      var element = target;
+    }
+
+    let utils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+    let presShellId = {};
+    utils.getPresShellId(presShellId);
+    let viewId = utils.getViewId(element);
+
+    sendAsyncMessage("scroll", { presShellId: presShellId.value,
+                                 viewId: viewId,
+                                 scrollOffset: scrollOffset,
+                                 isRoot: isRoot });
   }
 };
 
 ContentScroll.init();
 
 let ContentActive =  {
   init: function() {
     addMessageListener("Content:Activate", this);
--- a/browser/metro/base/content/bindings/browser.xml
+++ b/browser/metro/base/content/bindings/browser.xml
@@ -854,19 +854,21 @@
         ({
           self: this,
           receiveMessage: function receiveMessage(aMessage) {
             let self = this.self;
             let json = aMessage.json;
 
             switch (aMessage.name) {
               case "scroll":
+	        if (!json.isRoot)
+		  return;
                 if (!self.scrollSync)
                   return;
-                this.doScroll(json.x, json.y, 0);
+                this.doScroll(json.scrollOffset.x, json.scrollOffset.y, 0);
                 break;
            }
          },
 
          doScroll: function doScroll(aX, aY, aCount) {
             let self = this.self;
 
             // Use floor so that we always guarantee top-left corner of content is visible.
--- a/widget/windows/winrt/MetroWidget.cpp
+++ b/widget/windows/winrt/MetroWidget.cpp
@@ -158,16 +158,17 @@ NS_IMPL_ISUPPORTS_INHERITED0(MetroWidget
 
 nsRefPtr<mozilla::layers::APZCTreeManager> MetroWidget::sAPZC;
 
 MetroWidget::MetroWidget() :
   mTransparencyMode(eTransparencyOpaque),
   mWnd(NULL),
   mMetroWndProc(NULL),
   mTempBasicLayerInUse(false),
+  mRootLayerTreeId(),
   nsWindowBase()
 {
   // Global initialization
   if (!gInstanceCount) {
     UserActivity();
     nsTextStore::Initialize();
     MouseScrollHandler::Initialize();
     KeyboardLayout::GetInstance()->OnLayoutChange(::GetKeyboardLayout(0));
@@ -266,16 +267,24 @@ MetroWidget::Destroy()
 {
   if (mOnDestroyCalled)
     return NS_OK;
   Log("[%X] %s mWnd=%X type=%d", this, __FUNCTION__, mWnd, mWindowType);
   mOnDestroyCalled = true;
 
   nsCOMPtr<nsIWidget> kungFuDeathGrip(this);
 
+  if (ShouldUseAPZC()) {
+    nsresult rv;
+    nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1", &rv);
+    if (NS_SUCCEEDED(rv)) {
+      observerService->RemoveObserver(this, "scroll-offset-changed");
+    }
+  }
+
   RemoveSubclass();
   NotifyWindowDestroyed();
 
   // Prevent the widget from sending additional events.
   mWidgetListener = nullptr;
   mAttachedWidgetListener = nullptr;
 
   // Release references to children, device context, toolkit, and app shell.
@@ -824,16 +833,23 @@ MetroWidget::ShouldUseAPZC()
 
 CompositorParent* MetroWidget::NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight)
 {
   CompositorParent *compositor = nsBaseWidget::NewCompositorParent(aSurfaceWidth, aSurfaceHeight);
 
   if (ShouldUseAPZC()) {
     CompositorParent::SetControllerForLayerTree(compositor->RootLayerTreeId(), this);
     MetroWidget::sAPZC = CompositorParent::GetAPZCTreeManager(compositor->RootLayerTreeId());
+    mRootLayerTreeId = compositor->RootLayerTreeId();
+
+    nsresult rv;
+    nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1", &rv);
+    if (NS_SUCCEEDED(rv)) {
+      observerService->AddObserver(this, "scroll-offset-changed", false);
+    }
   }
 
   return compositor;
 }
 
 LayerManager*
 MetroWidget::GetLayerManager(PLayerTransactionChild* aShadowManager,
                              LayersBackend aBackendHint,
@@ -1441,8 +1457,35 @@ MetroWidget::HandlePanBegin()
 }
 
 void
 MetroWidget::HandlePanEnd()
 {
   LogFunction();
   MetroUtils::FireObserver("apzc-handle-pan-end", L"");
 }
+
+NS_IMETHODIMP
+MetroWidget::Observe(nsISupports *subject, const char *topic, const PRUnichar *data)
+{
+  NS_ENSURE_ARG_POINTER(topic);
+  if (!strcmp(topic, "scroll-offset-changed")) {
+    uint64_t scrollId;
+    int32_t presShellId;
+    CSSIntPoint scrollOffset;
+    int matched = sscanf(NS_LossyConvertUTF16toASCII(data).get(),
+                         "%llu %d (%d, %d)",
+                         &scrollId,
+                         &presShellId,
+                         &scrollOffset.x,
+                         &scrollOffset.y);
+    if (matched != 4) {
+      NS_WARNING("Malformed scroll-offset-changed message");
+      return NS_ERROR_UNEXPECTED;
+    }
+    if (MetroWidget::sAPZC) {
+      MetroWidget::sAPZC->UpdateScrollOffset(
+          ScrollableLayerGuid(mRootLayerTreeId, presShellId, scrollId),
+          scrollOffset);
+    }
+  }
+  return NS_OK;
+}
\ No newline at end of file
--- a/widget/windows/winrt/MetroWidget.h
+++ b/widget/windows/winrt/MetroWidget.h
@@ -41,35 +41,38 @@ namespace mozilla {
 namespace widget {
 namespace winrt {
 
 class FrameworkView;
 
 } } }
 
 class MetroWidget : public nsWindowBase,
-                    public mozilla::layers::GeckoContentController
+                    public mozilla::layers::GeckoContentController,
+                    public nsIObserver
 {
   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;
+  typedef mozilla::layers::FrameMetrics FrameMetrics;
 
   static LRESULT CALLBACK
   StaticWindowProcedure(HWND aWnd, UINT aMsg, WPARAM aWParan, LPARAM aLParam);
   LRESULT WindowProcedure(HWND aWnd, UINT aMsg, WPARAM aWParan, LPARAM aLParam);
 
 public:
   MetroWidget();
   virtual ~MetroWidget();
 
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_NSIOBSERVER
 
   static HWND GetICoreWindowHWND() { return sICoreHwnd; }
 
   // nsWindowBase
   virtual bool DispatchWindowEvent(nsGUIEvent* aEvent) MOZ_OVERRIDE;
   virtual bool IsTopLevelWidget() MOZ_OVERRIDE { return true; }
   virtual nsWindowBase* GetParentWindowBase(bool aIncludeOwner) MOZ_OVERRIDE { return nullptr; }
   // InitEvent assumes physical coordinates and is used by shared win32 code. Do
@@ -189,21 +192,21 @@ public:
   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 RequestContentRepaint(const 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(mozilla::layers::FrameMetrics::ViewID aScrollId, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize);
+  virtual void SendAsyncScrollDOMEvent(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;
   }
@@ -238,12 +241,13 @@ protected:
   nsIntRegion mInvalidatedRegion;
   nsCOMPtr<nsIdleService> mIdleService;
   HWND mWnd;
   static HWND sICoreHwnd;
   WNDPROC mMetroWndProc;
   bool mTempBasicLayerInUse;
   Microsoft::WRL::ComPtr<mozilla::widget::winrt::MetroInput> mMetroInput;
   mozilla::layers::FrameMetrics mFrameMetrics;
+  uint64_t mRootLayerTreeId;
 
 public:
   static nsRefPtr<mozilla::layers::APZCTreeManager> sAPZC;
 };