Bug 866232 - Update TabChild to better handle scrolling subframes. r=BenWa, mattwoodrow
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 30 Jul 2013 14:03:42 -0400
changeset 152825 6323f687fa3c3eb5862b20e776ab58ed7bed6821
parent 152824 a960ee129d9114b295c87596c552d0dcac74cb62
child 152826 4751c26f302ae881f2a1d556e9e5d07e527ebac1
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)
reviewersBenWa, mattwoodrow
bugs866232
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 866232 - Update TabChild to better handle scrolling subframes. r=BenWa, mattwoodrow
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
--- 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/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);