Bug 726817 - Add nsIFrameLoaderOwner.clampScrollPosition. r=
authorChris Lord <chrislord.net@gmail.com>
Fri, 17 Feb 2012 23:41:13 +0000
changeset 92481 b7340b9a251436249c1e938aea4af5fcc4fcd551
parent 92480 2d9224f8734c58c028f8572d28444cdb6bc9163f
child 92482 922f460d0c7e64ff90246a549cde4f2e18594c63
push idunknown
push userunknown
push dateunknown
bugs726817
milestone13.0a1
Bug 726817 - Add nsIFrameLoaderOwner.clampScrollPosition. r= Add an attribute to nsIFrameLoaderOwner that allows the clamping of set scroll positions to be toggled.
content/base/public/nsIFrameLoader.idl
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameLoader.h
layout/generic/nsGfxScrollFrame.cpp
layout/generic/nsGfxScrollFrame.h
layout/generic/nsSubDocumentFrame.h
--- a/content/base/public/nsIFrameLoader.idl
+++ b/content/base/public/nsIFrameLoader.idl
@@ -260,16 +260,23 @@ interface nsIFrameLoader : nsISupports
   attribute unsigned long eventMode;
 
   /**
    * If false, then the subdocument is not clipped to its CSS viewport, and the
    * subdocument's viewport scrollbar(s) are not rendered.
    * Defaults to true.
    */
   attribute boolean clipSubdocument;
+
+  /**
+   * If false, then the subdocument's scroll coordinates will not be clamped
+   * to their scroll boundaries.
+   * Defaults to true.
+   */
+  attribute boolean clampScrollPosition;
 };
 
 native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);
 
 [scriptable, uuid(5879040e-83e9-40e3-b2bb-5ddf43b76e47)]
 interface nsIFrameLoaderOwner : nsISupports
 {
   /**
--- a/content/base/src/nsFrameLoader.cpp
+++ b/content/base/src/nsFrameLoader.cpp
@@ -71,16 +71,17 @@
 #include "nsIJSContextStack.h"
 #include "nsUnicharUtils.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIScrollable.h"
 #include "nsFrameLoader.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIFrame.h"
+#include "nsIScrollableFrame.h"
 #include "nsSubDocumentFrame.h"
 #include "nsDOMError.h"
 #include "nsGUIEvent.h"
 #include "nsEventDispatcher.h"
 #include "nsISHistory.h"
 #include "nsISHistoryInternal.h"
 #include "nsIDocShellHistory.h"
 #include "nsIDOMHTMLDocument.h"
@@ -325,16 +326,17 @@ nsFrameLoader::nsFrameLoader(Element* aO
   , mInSwap(false)
   , mInShow(false)
   , mHideCalled(false)
   , mNetworkCreated(aNetworkCreated)
   , mDelayRemoteDialogs(false)
   , mRemoteBrowserShown(false)
   , mRemoteFrame(false)
   , mClipSubdocument(true)
+  , mClampScrollPosition(true)
   , mCurrentRemoteFrame(nsnull)
   , mRemoteBrowser(nsnull)
   , mRenderMode(RENDER_MODE_DEFAULT)
   , mEventMode(EVENT_MODE_NORMAL_DISPATCH)
 {
 }
 
 nsFrameLoader*
@@ -1750,16 +1752,48 @@ nsFrameLoader::SetClipSubdocument(bool a
             FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
         }
       }
     }
   }
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsFrameLoader::GetClampScrollPosition(bool* aResult)
+{
+  *aResult = mClampScrollPosition;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFrameLoader::SetClampScrollPosition(bool aClamp)
+{
+  mClampScrollPosition = aClamp;
+
+  // When turning clamping on, make sure the current position is clamped.
+  if (aClamp) {
+    nsIFrame* frame = GetPrimaryFrameOfOwningContent();
+    if (frame) {
+      nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame);
+      if (subdocFrame) {
+        nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame();
+        if (subdocRootFrame) {
+          nsIScrollableFrame* subdocRootScrollFrame = subdocRootFrame->PresContext()->PresShell()->
+            GetRootScrollFrameAsScrollable();
+          if (subdocRootScrollFrame) {
+            subdocRootScrollFrame->ScrollTo(subdocRootScrollFrame->GetScrollPosition(), nsIScrollableFrame::INSTANT);
+          }
+        }
+      }
+    }
+  }
+  return NS_OK;
+}
+
 nsIntSize
 nsFrameLoader::GetSubDocumentSize(const nsIFrame *aIFrame)
 {
   nsSize docSizeAppUnits;
   nsPresContext* presContext = aIFrame->PresContext();
   nsCOMPtr<nsIDOMHTMLFrameElement> frameElem = 
     do_QueryInterface(aIFrame->GetContent());
   if (frameElem) {
--- a/content/base/src/nsFrameLoader.h
+++ b/content/base/src/nsFrameLoader.h
@@ -284,16 +284,18 @@ public:
   }
   nsFrameMessageManager* GetFrameMessageManager() { return mMessageManager; }
 
   mozilla::dom::Element* GetOwnerContent() { return mOwnerContent; }
   void SetOwnerContent(mozilla::dom::Element* aContent);
 
   bool ShouldClipSubdocument() { return mClipSubdocument; }
 
+  bool ShouldClampScrollPosition() { return mClampScrollPosition; }
+
 private:
 
   bool ShouldUseRemoteProcess();
 
   /**
    * If we are an IPC frame, set mRemoteFrame. Otherwise, create and
    * initialize mDocShell.
    */
@@ -337,16 +339,17 @@ private:
   // created using NS_FROM_PARSER_NETWORK flag. If the element is modified,
   // it may lose the flag.
   bool mNetworkCreated : 1;
 
   bool mDelayRemoteDialogs : 1;
   bool mRemoteBrowserShown : 1;
   bool mRemoteFrame : 1;
   bool mClipSubdocument : 1;
+  bool mClampScrollPosition : 1;
 
   // XXX leaking
   nsCOMPtr<nsIObserver> mChildHost;
   RenderFrameParent* mCurrentRemoteFrame;
   TabParent* mRemoteBrowser;
 
   // See nsIFrameLoader.idl.  Short story, if !(mRenderMode &
   // RENDER_MODE_ASYNC_SCROLL), all the fields below are ignored in
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -1541,17 +1541,22 @@ nsGfxScrollFrameInner::AsyncScrollCallba
 /*
  * this method wraps calls to ScrollToImpl(), either in one shot or incrementally,
  *  based on the setting of the smooth scroll pref
  */
 void
 nsGfxScrollFrameInner::ScrollTo(nsPoint aScrollPosition,
                                 nsIScrollableFrame::ScrollMode aMode)
 {
-  mDestination = ClampScrollPosition(aScrollPosition);
+  nsSubDocumentFrame* subdocFrame = static_cast<nsSubDocumentFrame*>
+    (nsLayoutUtils::GetCrossDocParentFrame(mOuter->PresContext()->PresShell()->GetRootFrame()));
+  if (!subdocFrame || subdocFrame->ShouldClampScrollPosition())
+    mDestination = ClampScrollPosition(aScrollPosition);
+  else
+    mDestination = aScrollPosition;
 
   if (aMode == nsIScrollableFrame::INSTANT) {
     // Asynchronous scrolling is not allowed, so we'll kill any existing
     // async-scrolling process and do an instant scroll
     delete mAsyncScroll;
     mAsyncScroll = nsnull;
     ScrollToImpl(mDestination);
     return;
@@ -1803,27 +1808,33 @@ ClampInt(nscoord aLower, nscoord aVal, n
   if (v < low)
     return low;
   if (v > high)
     return high;
   return v;
 }
 
 nsPoint
-nsGfxScrollFrameInner::ClampAndRestrictToDevPixels(const nsPoint& aPt,
-                                                   nsIntPoint* aPtDevPx) const
+nsGfxScrollFrameInner::RestrictToDevPixels(const nsPoint& aPt,
+                                           nsIntPoint* aPtDevPx,
+                                           bool aShouldClamp) const
 {
   nsPresContext* presContext = mOuter->PresContext();
   nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
   // Convert to device pixels so we scroll to an integer offset of device
   // pixels. But we also need to make sure that our position remains
   // inside the allowed region.
-  nsRect scrollRange = GetScrollRange();
-  *aPtDevPx = nsIntPoint(ClampInt(scrollRange.x, aPt.x, scrollRange.XMost(), appUnitsPerDevPixel),
-                         ClampInt(scrollRange.y, aPt.y, scrollRange.YMost(), appUnitsPerDevPixel));
+  if (aShouldClamp) {
+    nsRect scrollRange = GetScrollRange();
+    *aPtDevPx = nsIntPoint(ClampInt(scrollRange.x, aPt.x, scrollRange.XMost(), appUnitsPerDevPixel),
+                           ClampInt(scrollRange.y, aPt.y, scrollRange.YMost(), appUnitsPerDevPixel));
+  } else {
+    *aPtDevPx = nsIntPoint(NSAppUnitsToIntPixels(aPt.x, appUnitsPerDevPixel),
+                           NSAppUnitsToIntPixels(aPt.y, appUnitsPerDevPixel));
+  }
   return nsPoint(NSIntPixelsToAppUnits(aPtDevPx->x, appUnitsPerDevPixel),
                  NSIntPixelsToAppUnits(aPtDevPx->y, appUnitsPerDevPixel));
 }
 
 /* static */ void
 nsGfxScrollFrameInner::ScrollActivityCallback(nsITimer *aTimer, void* anInstance)
 {
   nsGfxScrollFrameInner* self = static_cast<nsGfxScrollFrameInner*>(anInstance);
@@ -1849,17 +1860,20 @@ nsGfxScrollFrameInner::ScheduleSynthetic
 }
 
 void
 nsGfxScrollFrameInner::ScrollToImpl(nsPoint aPt)
 {
   nsPresContext* presContext = mOuter->PresContext();
   nscoord appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel();
   nsIntPoint ptDevPx;
-  nsPoint pt = ClampAndRestrictToDevPixels(aPt, &ptDevPx);
+
+  nsSubDocumentFrame* subdocFrame = static_cast<nsSubDocumentFrame*>
+    (nsLayoutUtils::GetCrossDocParentFrame(presContext->PresShell()->GetRootFrame()));
+  nsPoint pt = RestrictToDevPixels(aPt, &ptDevPx, !subdocFrame || subdocFrame->ShouldClampScrollPosition());
 
   nsPoint curPos = GetScrollPosition();
   if (pt == curPos) {
     return;
   }
   nsIntPoint curPosDevPx(NSAppUnitsToIntPixels(curPos.x, appUnitsPerDevPixel),
                          NSAppUnitsToIntPixels(curPos.y, appUnitsPerDevPixel));
   // We maintain the invariant that the scroll position is a multiple of device
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -172,17 +172,17 @@ public:
     pt.x = IsLTR() ?
       mScrollPort.x - mScrolledFrame->GetPosition().x :
       mScrollPort.XMost() - mScrolledFrame->GetRect().XMost();
     pt.y = mScrollPort.y - mScrolledFrame->GetPosition().y;
     return pt;
   }
   nsRect GetScrollRange() const;
 
-  nsPoint ClampAndRestrictToDevPixels(const nsPoint& aPt, nsIntPoint* aPtDevPx) const;
+  nsPoint RestrictToDevPixels(const nsPoint& aPt, nsIntPoint* aPtDevPx, bool aShouldClamp) const;
   nsPoint ClampScrollPosition(const nsPoint& aPt) const;
   static void AsyncScrollCallback(nsITimer *aTimer, void* anInstance);
   void ScrollTo(nsPoint aScrollPosition, nsIScrollableFrame::ScrollMode aMode);
   void ScrollToImpl(nsPoint aScrollPosition);
   void ScrollVisual(nsPoint aOldScrolledFramePosition);
   void ScrollBy(nsIntPoint aDelta, nsIScrollableFrame::ScrollUnit aUnit,
                 nsIScrollableFrame::ScrollMode aMode, nsIntPoint* aOverflow);
   void ScrollToRestoredPosition();
--- a/layout/generic/nsSubDocumentFrame.h
+++ b/layout/generic/nsSubDocumentFrame.h
@@ -125,16 +125,22 @@ public:
   virtual void ReflowCallbackCanceled();
 
   bool ShouldClipSubdocument()
   {
     nsFrameLoader* frameLoader = FrameLoader();
     return !frameLoader || frameLoader->ShouldClipSubdocument();
   }
 
+  bool ShouldClampScrollPosition()
+  {
+    nsFrameLoader* frameLoader = FrameLoader();
+    return !frameLoader || frameLoader->ShouldClampScrollPosition();
+  }
+
 protected:
   friend class AsyncFrameInit;
 
   // Helper method to look up the HTML marginwidth & marginheight attributes
   nsIntSize GetMarginAttributes();
 
   nsFrameLoader* FrameLoader();