Bug 590294, part 9: Add API to set a <browser> expectation of the content window's viewport, and to draw content pixels in the <browser> at particular scale factors. sr=smaug
authorChris Jones <jones.chris.g@gmail.com>
Fri, 03 Sep 2010 15:10:46 -0500
changeset 54084 a1526ab475c9a7f5c5e484e622bbbd2abfe43828
parent 54083 8321155e8dad2fb39353be91023c23f85600f215
child 54085 15302bb0843b7108a2cb3e522fc01ac8441884c2
push idunknown
push userunknown
push dateunknown
reviewerssmaug
bugs590294
milestone2.0b6pre
Bug 590294, part 9: Add API to set a <browser> expectation of the content window's viewport, and to draw content pixels in the <browser> at particular scale factors. sr=smaug
content/base/public/nsIFrameLoader.idl
content/base/src/nsFrameLoader.cpp
content/base/src/nsFrameLoader.h
--- a/content/base/public/nsIFrameLoader.idl
+++ b/content/base/public/nsIFrameLoader.idl
@@ -41,17 +41,17 @@
 
 interface nsIDocShell;
 interface nsIURI;
 interface nsIWebProgress;
 interface nsIFrame;
 interface nsIChromeFrameMessageManager;
 interface nsIVariant;
 
-[scriptable, uuid(65d2c9e2-852c-48cf-a95d-9b82f1273c15)]
+[scriptable, uuid(afb86369-d183-4bc4-9fce-26cd314a4ac0)]
 interface nsIFrameLoader : nsISupports
 {
   /**
    * Get the docshell from the frame loader.
    */
   readonly attribute nsIDocShell docShell;
 
   /**
@@ -122,16 +122,46 @@ interface nsIFrameLoader : nsISupports
   void sendCrossProcessKeyEvent(in AString aType,
                                 in long aKeyCode,
                                 in long aCharCode,
                                 in long aModifiers,
                                 [optional] in boolean aPreventDefault);
 
   attribute boolean delayRemoteDialogs;
 
+  /**
+   * Implement viewport scrolling/scaling, used to support
+   * asynchronous re-paints of content pixels.  These interfaces are
+   * only meaningful for <browser>.
+   *
+   * These interfaces do *not* scroll or scale the content document;
+   * instead they set a "goal" scroll/scale wrt the current content
+   * viewport.  When the content document is painted, the viewport*
+   * attributes are used to set a compensating transform.  If the
+   * metrics of the content document's current pixels don't match the
+   * viewport* config, the transform matrix may need to translate
+   * content pixels and/or perform a "fuzzy-scale" that doesn't
+   * re-rasterize fonts or intelligently resample images.
+   *
+   * The viewport* attrs are allowed to transform content pixels in
+   * such a way that the <browser>'s visible rect encloses pixels that
+   * the content document does not (yet) define.
+   *
+   * The viewport scroll values are in units of content-document CSS
+   * pixels.
+   *
+   * These APIs are designed to be used with nsIDOMWindowUtils
+   * setDisplayPort() and setResolution().
+   */
+  void scrollViewportTo(in float xPx, in float yPx);
+  void scrollViewportBy(in float dxPx, in float dyPx);
+  void setViewportScale(in float xScale, in float yScale);
+
+  readonly attribute float viewportScrollX;
+  readonly attribute float viewportScrollY;
 };
 
 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
@@ -1524,16 +1524,87 @@ nsFrameLoader::UpdateBaseWindowPositionA
     nsIntSize size = GetSubDocumentSize(aIFrame);
 
     baseWindow->SetPositionAndSize(x, y, size.width, size.height, PR_FALSE);
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsFrameLoader::ScrollViewportTo(float aXpx, float aYpx)
+{
+  ViewportConfig config(mViewportConfig);
+  config.mScrollOffset = nsPoint(nsPresContext::CSSPixelsToAppUnits(aXpx),
+                                nsPresContext::CSSPixelsToAppUnits(aYpx));
+  return UpdateViewportConfig(config);
+}
+
+NS_IMETHODIMP
+nsFrameLoader::ScrollViewportBy(float aDXpx, float aDYpx)
+{
+  ViewportConfig config(mViewportConfig);
+  config.mScrollOffset.MoveBy(nsPresContext::CSSPixelsToAppUnits(aDXpx),
+                             nsPresContext::CSSPixelsToAppUnits(aDYpx));
+  return UpdateViewportConfig(config);
+}
+
+NS_IMETHODIMP
+nsFrameLoader::SetViewportScale(float aXScale, float aYScale)
+{
+  ViewportConfig config(mViewportConfig);
+  config.mXScale = aXScale;
+  config.mYScale = aYScale;
+  return UpdateViewportConfig(config);
+}
+
+NS_IMETHODIMP
+nsFrameLoader::GetViewportScrollX(float* aViewportScrollX)
+{
+  *aViewportScrollX =
+    nsPresContext::AppUnitsToFloatCSSPixels(mViewportConfig.mScrollOffset.x);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFrameLoader::GetViewportScrollY(float* aViewportScrollY)
+{
+  *aViewportScrollY =
+    nsPresContext::AppUnitsToFloatCSSPixels(mViewportConfig.mScrollOffset.y);
+  return NS_OK;
+}
+
+nsresult
+nsFrameLoader::UpdateViewportConfig(const ViewportConfig& aNewConfig)
+{
+  if (aNewConfig == mViewportConfig) {
+    return NS_OK;
+  }
+  mViewportConfig = aNewConfig;
+
+  // Viewport changed.  Try to locate our subdoc frame and invalidate
+  // it if found.
+  nsIFrame* frame = GetPrimaryFrameOfOwningContent();
+  if (!frame) {
+    // XXX should this be a silent failure?
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // XXX could be clever here and compute a smaller invalidation
+  // rect
+  nsRect rect = nsRect(nsPoint(0, 0), frame->GetRect().Size());
+  // NB: we pass INVALIDATE_NO_THEBES_LAYERS here to keep viewport
+  // semantics the same for both in-process and out-of-process
+  // <browser>.  This is just a transform of the layer subtree in
+  // both.
+  frame->InvalidateWithFlags(rect, nsIFrame::INVALIDATE_NO_THEBES_LAYERS);
+
+  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
@@ -42,16 +42,17 @@
  */
 
 #ifndef nsFrameLoader_h_
 #define nsFrameLoader_h_
 
 #include "nsIDocShell.h"
 #include "nsStringFwd.h"
 #include "nsIFrameLoader.h"
+#include "nsPoint.h"
 #include "nsSize.h"
 #include "nsIURI.h"
 #include "nsAutoPtr.h"
 #include "nsFrameMessageManager.h"
 
 class nsIContent;
 class nsIURI;
 class nsSubDocumentFrame;
@@ -104,16 +105,57 @@ protected:
     , mRemoteBrowserShown(PR_FALSE)
     , mRemoteFrame(false)
     , mCurrentRemoteFrame(nsnull)
     , mRemoteBrowser(nsnull)
 #endif
   {}
 
 public:
+  /**
+   * Defines a target configuration for this <browser>'s content
+   * document's viewport.  If the content document's actual viewport
+   * doesn't match a desired ViewportConfig, then on paints its pixels
+   * are transformed to compensate for the difference.
+   *
+   * Used to support asynchronous re-paints of content pixels; see
+   * nsIFrameLoader.scrollViewport* and viewportScale.
+   */
+  struct ViewportConfig {
+    ViewportConfig()
+      : mScrollOffset(0, 0)
+      , mXScale(1.0)
+      , mYScale(1.0)
+    {}
+
+    // Default copy ctor and operator= are fine
+
+    PRBool operator==(const ViewportConfig& aOther) const
+    {
+      return (mScrollOffset == aOther.mScrollOffset &&
+              mXScale == aOther.mXScale &&
+              mYScale == aOther.mYScale);
+    }
+
+    // This is the scroll offset the <browser> user wishes or expects
+    // its enclosed content document to have.  "Scroll offset" here
+    // means the document pixel at pixel (0,0) within the CSS
+    // viewport.  If the content document's actual scroll offset
+    // doesn't match |mScrollOffset|, the difference is used to define
+    // a translation transform when painting the content document.
+    nsPoint mScrollOffset;
+    // The scale at which the <browser> user wishes to paint its
+    // enclosed content document.  If content-document layers have a
+    // lower or higher resolution than the desired scale, then the
+    // ratio is used to define a scale transform when painting the
+    // content document.
+    float mXScale;
+    float mYScale;
+  };
+
   ~nsFrameLoader() {
     mNeedsAsyncDestroy = PR_TRUE;
     if (mMessageManager) {
       mMessageManager->Disconnect();
     }
     nsFrameLoader::Destroy();
   }
 
@@ -201,16 +243,18 @@ public:
    */
   void SetCurrentRemoteFrame(RenderFrameParent* aFrame)
   {
     mCurrentRemoteFrame = aFrame;
   }
 #endif
   nsFrameMessageManager* GetFrameMessageManager() { return mMessageManager; }
 
+  const ViewportConfig& GetViewportConfig() { return mViewportConfig; }
+
 private:
 
 #ifdef MOZ_IPC
   bool ShouldUseRemoteProcess();
 #endif
 
   /**
    * If we are an IPC frame, set mRemoteFrame. Otherwise, create and
@@ -233,16 +277,18 @@ private:
 #ifdef MOZ_IPC
   // Return true if remote browser created; nothing else to do
   bool TryRemoteBrowser();
 
   // Tell the remote browser that it's now "virtually visible"
   bool ShowRemoteFrame(const nsIntSize& size);
 #endif
 
+  nsresult UpdateViewportConfig(const ViewportConfig& aNewConfig);
+
   nsCOMPtr<nsIDocShell> mDocShell;
   nsCOMPtr<nsIURI> mURIToLoad;
   nsIContent *mOwnerContent; // WEAK
 public:
   // public because a callback needs these.
   nsRefPtr<nsFrameMessageManager> mMessageManager;
   nsCOMPtr<nsIInProcessContentFrameMessageManager> mChildMessageManager;
 private:
@@ -263,11 +309,12 @@ private:
   PRPackedBool mRemoteBrowserShown : 1;
   bool mRemoteFrame;
   // XXX leaking
   nsCOMPtr<nsIObserver> mChildHost;
   RenderFrameParent* mCurrentRemoteFrame;
   TabParent* mRemoteBrowser;
 #endif
 
+  ViewportConfig mViewportConfig;
 };
 
 #endif