Bug 605618 Part 3: Viewport API for frontend r=cjones

This commit is contained in:
Benjamin Stover 2011-01-13 09:45:14 -08:00
parent c5f2f76798
commit 1b69189147
4 changed files with 327 additions and 142 deletions

View File

@ -45,6 +45,90 @@ interface nsIFrame;
interface nsIChromeFrameMessageManager;
interface nsIVariant;
typedef unsigned long long nsContentViewId;
/**
* These interfaces do *not* scroll or scale the content document;
* instead they set a "goal" scroll/scale wrt the current content
* view. When the content document is painted, the scroll*
* attributes are used to set a compensating transform. If the
* metrics of the content document's current pixels don't match the
* view 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 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 view scroll values are in units of chrome-document CSS
* pixels.
*
* These APIs are designed to be used with nsIDOMWindowUtils
* setDisplayPort() and setResolution().
*/
[scriptable, uuid(fbd25468-d2cf-487b-bc58-a0e105398b47)]
interface nsIContentView : nsISupports
{
/**
* Scroll view to or by the given chrome-document CSS pixels.
* Fails if the view is no longer valid.
*/
void scrollTo(in float xPx, in float yPx);
void scrollBy(in float dxPx, in float dyPx);
void setScale(in float xScale, in float yScale);
/**
* Scroll offset in chrome-document CSS pixels.
*
* When this view is active (i.e. it is being painted because it's in the
* visible region of the screen), this value is at first lined up with the
* content's scroll offset.
*
* Note that when this view becomes inactive, the new content view will have
* scroll values that are reset to the default!
*/
readonly attribute float scrollX;
readonly attribute float scrollY;
/**
* ID that can be used in conjunction with nsIDOMWindowUtils to change
* the actual document, instead of just how it is transformed.
*/
readonly attribute nsContentViewId id;
};
[scriptable, uuid(ba5af90d-ece5-40b2-9a1d-a0154128db1c)]
interface nsIContentViewManager : nsISupports
{
/**
* Retrieve view scrolling/scaling interfaces in a given area,
* used to support asynchronous re-paints of content pixels.
* These interfaces are only meaningful for <browser>.
*
* Pixels are in chrome device pixels and are relative to the browser
* element.
*
* @param aX x coordinate that will be in target rectangle
* @param aY y coordinate that will be in target rectangle
* @param aTopSize How much to expand up the rectangle
* @param aRightSize How much to expand right the rectangle
* @param aBottomSize How much to expand down the rectangle
* @param aLeftSize How much to expand left the rectangle
*/
void getContentViewsIn(in float aXPx, in float aYPx,
in float aTopSize, in float aRightSize,
in float aBottomSize, in float aLeftSize,
[optional] out unsigned long aLength,
[retval, array, size_is(aLength)] out nsIContentView aResult);
/**
* The root content view.
*/
readonly attribute nsIContentView rootContentView;
};
[scriptable, uuid(50a67436-bb44-11df-8d9a-001e37d2764a)]
interface nsIFrameLoader : nsISupports
{
@ -121,34 +205,14 @@ interface nsIFrameLoader : nsISupports
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 chrome-document CSS
* pixels.
*
* These APIs are designed to be used with nsIDOMWindowUtils
* setDisplayPort() and setResolution().
* DEPRECATED. Please QI to nsIContentViewManager.
* FIXME 615368
*/
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;
};

View File

@ -110,6 +110,8 @@
#include "mozilla/AutoRestore.h"
#include "mozilla/unused.h"
#include "Layers.h"
#ifdef MOZ_IPC
#include "ContentParent.h"
#include "TabParent.h"
@ -118,6 +120,8 @@ using namespace mozilla;
using namespace mozilla::dom;
#endif
using namespace mozilla::layers;
#include "jsapi.h"
class nsAsyncDocShellDestroyer : public nsRunnable
@ -139,6 +143,106 @@ public:
nsRefPtr<nsIDocShell> mDocShell;
};
static void InvalidateFrame(nsIFrame* aFrame)
{
nsRect rect = nsRect(nsPoint(0, 0), aFrame->GetRect().Size());
// NB: we pass INVALIDATE_NO_THEBES_LAYERS here to keep view
// semantics the same for both in-process and out-of-process
// <browser>. This is just a transform of the layer subtree in
// both.
aFrame->InvalidateWithFlags(rect, nsIFrame::INVALIDATE_NO_THEBES_LAYERS);
}
NS_IMPL_ISUPPORTS1(nsContentView, nsIContentView)
bool
nsContentView::IsRoot() const
{
return mScrollId == FrameMetrics::ROOT_SCROLL_ID;
}
nsresult
nsContentView::Update(const ViewConfig& aConfig)
{
if (aConfig == mConfig) {
return NS_OK;
}
mConfig = aConfig;
// View changed. Try to locate our subdoc frame and invalidate
// it if found.
if (!mOwnerContent) {
if (IsRoot()) {
// Oops, don't have a frame right now. That's OK; the view
// config persists and will apply to the next frame we get, if we
// ever get one.
return NS_OK;
} else {
// This view is no longer valid.
return NS_ERROR_NOT_AVAILABLE;
}
}
nsIFrame* frame = mOwnerContent->GetPrimaryFrame();
// XXX could be clever here and compute a smaller invalidation
// rect
InvalidateFrame(frame);
return NS_OK;
}
NS_IMETHODIMP
nsContentView::ScrollTo(float aXpx, float aYpx)
{
ViewConfig config(mConfig);
config.mScrollOffset = nsPoint(nsPresContext::CSSPixelsToAppUnits(aXpx),
nsPresContext::CSSPixelsToAppUnits(aYpx));
return Update(config);
}
NS_IMETHODIMP
nsContentView::ScrollBy(float aDXpx, float aDYpx)
{
ViewConfig config(mConfig);
config.mScrollOffset.MoveBy(nsPresContext::CSSPixelsToAppUnits(aDXpx),
nsPresContext::CSSPixelsToAppUnits(aDYpx));
return Update(config);
}
NS_IMETHODIMP
nsContentView::SetScale(float aXScale, float aYScale)
{
ViewConfig config(mConfig);
config.mXScale = aXScale;
config.mYScale = aYScale;
return Update(config);
}
NS_IMETHODIMP
nsContentView::GetScrollX(float* aViewScrollX)
{
*aViewScrollX = nsPresContext::AppUnitsToFloatCSSPixels(
mConfig.mScrollOffset.x);
return NS_OK;
}
NS_IMETHODIMP
nsContentView::GetScrollY(float* aViewScrollY)
{
*aViewScrollY = nsPresContext::AppUnitsToFloatCSSPixels(
mConfig.mScrollOffset.y);
return NS_OK;
}
NS_IMETHODIMP
nsContentView::GetId(nsContentViewId* aId)
{
NS_ASSERTION(sizeof(nsContentViewId) == sizeof(ViewID),
"ID size for XPCOM ID and internal ID type are not the same!");
*aId = mScrollId;
return NS_OK;
}
// Bug 136580: Limit to the number of nested content frames that can have the
// same URL. This is to stop content that is recursively loading
// itself. Note that "#foo" on the end of URL doesn't affect
@ -177,9 +281,31 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsFrameLoader, nsIFrameLoader)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
NS_INTERFACE_MAP_ENTRY(nsIFrameLoader)
NS_INTERFACE_MAP_ENTRY(nsIFrameLoader_MOZILLA_2_0_BRANCH)
NS_INTERFACE_MAP_ENTRY(nsIContentViewManager)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFrameLoader)
NS_INTERFACE_MAP_END
nsFrameLoader::nsFrameLoader(nsIContent *aOwner, PRBool aNetworkCreated)
: mOwnerContent(aOwner)
, mDepthTooGreat(PR_FALSE)
, mIsTopLevelContent(PR_FALSE)
, mDestroyCalled(PR_FALSE)
, mNeedsAsyncDestroy(PR_FALSE)
, mInSwap(PR_FALSE)
, mInShow(PR_FALSE)
, mHideCalled(PR_FALSE)
, mNetworkCreated(aNetworkCreated)
#ifdef MOZ_IPC
, mDelayRemoteDialogs(PR_FALSE)
, mRemoteBrowserShown(PR_FALSE)
, mRemoteFrame(false)
, mCurrentRemoteFrame(nsnull)
, mRemoteBrowser(nsnull)
#endif
, mContentView(new nsContentView(aOwner, FrameMetrics::ROOT_SCROLL_ID))
{
}
nsFrameLoader*
nsFrameLoader::Create(nsIContent* aOwner, PRBool aNetworkCreated)
{
@ -997,8 +1123,8 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
ourWindow->SetFrameElementInternal(otherFrameElement);
otherWindow->SetFrameElementInternal(ourFrameElement);
mOwnerContent = otherContent;
aOther->mOwnerContent = ourContent;
SetOwnerContent(otherContent);
aOther->SetOwnerContent(ourContent);
nsRefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
nsRefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
@ -1099,7 +1225,7 @@ nsFrameLoader::Destroy()
doc->SetSubDocumentFor(mOwnerContent, nsnull);
}
mOwnerContent = nsnull;
SetOwnerContent(nsnull);
}
DestroyChild();
@ -1154,6 +1280,12 @@ nsFrameLoader::GetDepthTooGreat(PRBool* aDepthTooGreat)
return NS_OK;
}
void
nsFrameLoader::SetOwnerContent(nsIContent* aContent)
{
mOwnerContent = aContent;
}
#ifdef MOZ_IPC
bool
nsFrameLoader::ShouldUseRemoteProcess()
@ -1505,95 +1637,49 @@ nsFrameLoader::UpdateBaseWindowPositionAndSize(nsIFrame *aIFrame)
NS_IMETHODIMP
nsFrameLoader::ScrollViewportTo(float aXpx, float aYpx)
{
ViewportConfig config(mViewportConfig);
config.mScrollOffset = nsPoint(nsPresContext::CSSPixelsToAppUnits(aXpx),
nsPresContext::CSSPixelsToAppUnits(aYpx));
return UpdateViewportConfig(config);
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsFrameLoader::ScrollViewportBy(float aDXpx, float aDYpx)
{
ViewportConfig config(mViewportConfig);
config.mScrollOffset.MoveBy(nsPresContext::CSSPixelsToAppUnits(aDXpx),
nsPresContext::CSSPixelsToAppUnits(aDYpx));
return UpdateViewportConfig(config);
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsFrameLoader::SetViewportScale(float aXScale, float aYScale)
{
ViewportConfig config(mViewportConfig);
config.mXScale = aXScale;
config.mYScale = aYScale;
return UpdateViewportConfig(config);
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsFrameLoader::GetViewportScrollX(float* aViewportScrollX)
{
*aViewportScrollX =
nsPresContext::AppUnitsToFloatCSSPixels(mViewportConfig.mScrollOffset.x);
return NS_OK;
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsFrameLoader::GetViewportScrollY(float* aViewportScrollY)
{
*aViewportScrollY =
nsPresContext::AppUnitsToFloatCSSPixels(mViewportConfig.mScrollOffset.y);
return NS_OK;
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsFrameLoader::GetRenderMode(PRUint32* aRenderMode)
{
*aRenderMode = mViewportConfig.mRenderMode;
*aRenderMode = mRenderMode;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::SetRenderMode(PRUint32 aRenderMode)
{
ViewportConfig config(mViewportConfig);
config.mRenderMode = aRenderMode;
return UpdateViewportConfig(config);
}
nsresult
nsFrameLoader::UpdateViewportConfig(const ViewportConfig& aNewConfig)
{
if (aNewConfig == mViewportConfig) {
return NS_OK;
} else if (!mViewportConfig.AsyncScrollEnabled() &&
!aNewConfig.AsyncScrollEnabled()) {
// The target viewport can't be set in synchronous mode
return NS_ERROR_NOT_AVAILABLE;
}
// XXX if we go from disabled->enabled, should we clear out the old
// config? Or what?
mViewportConfig = aNewConfig;
// Viewport changed. Try to locate our subdoc frame and invalidate
// it if found.
nsIFrame* frame = GetPrimaryFrameOfOwningContent();
if (!frame) {
// Oops, don't have a frame right now. That's OK; the viewport
// config persists and will apply to the next frame we get, if we
// ever get one.
if (aRenderMode == mRenderMode) {
return NS_OK;
}
// 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);
mRenderMode = aRenderMode;
InvalidateFrame(GetPrimaryFrameOfOwningContent());
return NS_OK;
}
@ -1882,6 +1968,25 @@ nsFrameLoader::GetMessageManager(nsIChromeFrameMessageManager** aManager)
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetContentViewsIn(float aXPx, float aYPx,
float aTopSize, float aRightSize,
float aBottomSize, float aLeftSize,
PRUint32* aLength,
nsIContentView*** aResult)
{
*aResult = nsnull;
*aLength = 0;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetRootContentView(nsIContentView** aContentView)
{
nsRefPtr<nsIContentView>(GetContentView()).forget(aContentView);
return NS_OK;
}
nsresult
nsFrameLoader::EnsureMessageManager()
{

View File

@ -52,6 +52,7 @@
#include "nsIURI.h"
#include "nsAutoPtr.h"
#include "nsFrameMessageManager.h"
#include "Layers.h"
class nsIContent;
class nsIURI;
@ -80,73 +81,38 @@ class QX11EmbedContainer;
#endif
#endif
class nsFrameLoader : public nsIFrameLoader,
public nsIFrameLoader_MOZILLA_2_0_BRANCH
/**
* Defines a target configuration for this <browser>'s content
* document's view. If the content document's actual view
* doesn't match this nsIContentView, then on paints its pixels
* are transformed to compensate for the difference.
*
* Used to support asynchronous re-paints of content pixels; see
* nsIContentView.
*/
class nsContentView : public nsIContentView
{
friend class AutoResetInShow;
#ifdef MOZ_IPC
typedef mozilla::dom::PBrowserParent PBrowserParent;
typedef mozilla::dom::TabParent TabParent;
typedef mozilla::layout::RenderFrameParent RenderFrameParent;
#endif
protected:
nsFrameLoader(nsIContent *aOwner, PRBool aNetworkCreated) :
mOwnerContent(aOwner),
mDepthTooGreat(PR_FALSE),
mIsTopLevelContent(PR_FALSE),
mDestroyCalled(PR_FALSE),
mNeedsAsyncDestroy(PR_FALSE),
mInSwap(PR_FALSE),
mInShow(PR_FALSE),
mHideCalled(PR_FALSE),
mNetworkCreated(aNetworkCreated)
#ifdef MOZ_IPC
, mDelayRemoteDialogs(PR_FALSE)
, 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()
: mRenderMode(nsIFrameLoader_MOZILLA_2_0_BRANCH::RENDER_MODE_DEFAULT)
, mScrollOffset(0, 0)
typedef mozilla::layers::FrameMetrics::ViewID ViewID;
NS_DECL_ISUPPORTS
NS_DECL_NSICONTENTVIEW
struct ViewConfig {
ViewConfig()
: mScrollOffset(0, 0)
, mXScale(1.0)
, mYScale(1.0)
{}
// Default copy ctor and operator= are fine
PRBool operator==(const ViewportConfig& aOther) const
PRBool operator==(const ViewConfig& aOther) const
{
return (mRenderMode == aOther.mRenderMode &&
mScrollOffset == aOther.mScrollOffset &&
return (mScrollOffset == aOther.mScrollOffset &&
mXScale == aOther.mXScale &&
mYScale == aOther.mYScale);
}
PRBool AsyncScrollEnabled() const
{
return !!(mRenderMode & RENDER_MODE_ASYNC_SCROLL);
}
// See nsIFrameLoader.idl. Short story, if !(mRenderMode &
// RENDER_MODE_ASYNC_SCROLL), all the fields below are ignored in
// favor of what content tells.
PRUint32 mRenderMode;
// 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
@ -163,6 +129,50 @@ public:
float mYScale;
};
nsContentView(nsIContent* aOwnerContent, ViewID aScrollId,
ViewConfig aConfig = ViewConfig())
: mOwnerContent(aOwnerContent)
, mScrollId(aScrollId)
, mConfig(aConfig)
{}
bool IsRoot() const;
ViewID GetId() const
{
return mScrollId;
}
ViewConfig GetViewConfig() const
{
return mConfig;
}
nsIContent *mOwnerContent; // WEAK
private:
nsresult Update(const ViewConfig& aConfig);
ViewID mScrollId;
ViewConfig mConfig;
};
class nsFrameLoader : public nsIFrameLoader,
public nsIFrameLoader_MOZILLA_2_0_BRANCH,
public nsIContentViewManager
{
friend class AutoResetInShow;
#ifdef MOZ_IPC
typedef mozilla::dom::PBrowserParent PBrowserParent;
typedef mozilla::dom::TabParent TabParent;
typedef mozilla::layout::RenderFrameParent RenderFrameParent;
#endif
protected:
nsFrameLoader(nsIContent *aOwner, PRBool aNetworkCreated);
public:
~nsFrameLoader() {
mNeedsAsyncDestroy = PR_TRUE;
if (mMessageManager) {
@ -177,6 +187,7 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsFrameLoader, nsIFrameLoader)
NS_DECL_NSIFRAMELOADER
NS_DECL_NSIFRAMELOADER_MOZILLA_2_0_BRANCH
NS_DECL_NSICONTENTVIEWMANAGER
NS_HIDDEN_(nsresult) CheckForRecursiveLoad(nsIURI* aURI);
nsresult ReallyStartLoading();
void Finalize();
@ -261,7 +272,9 @@ public:
#endif
nsFrameMessageManager* GetFrameMessageManager() { return mMessageManager; }
const ViewportConfig& GetViewportConfig() { return mViewportConfig; }
nsContentView* GetContentView() { return mContentView; }
void SetOwnerContent(nsIContent* aContent);
private:
@ -295,8 +308,6 @@ private:
bool ShowRemoteFrame(const nsIntSize& size);
#endif
nsresult UpdateViewportConfig(const ViewportConfig& aNewConfig);
nsCOMPtr<nsIDocShell> mDocShell;
nsCOMPtr<nsIURI> mURIToLoad;
nsIContent *mOwnerContent; // WEAK
@ -327,7 +338,12 @@ private:
TabParent* mRemoteBrowser;
#endif
ViewportConfig mViewportConfig;
nsRefPtr<nsContentView> mContentView;
// See nsIFrameLoader.idl. Short story, if !(mRenderMode &
// RENDER_MODE_ASYNC_SCROLL), all the fields below are ignored in
// favor of what content tells.
PRUint32 mRenderMode;
};
#endif

View File

@ -49,7 +49,7 @@
#include "nsViewportFrame.h"
#include "nsSubDocumentFrame.h"
typedef nsFrameLoader::ViewportConfig ViewportConfig;
typedef nsContentView::ViewConfig ViewConfig;
using namespace mozilla::layers;
namespace mozilla {
@ -85,7 +85,7 @@ AssertValidContainerOfShadowTree(ContainerLayer* aContainer,
static void
ComputeShadowTreeTransform(nsIFrame* aContainerFrame,
const FrameMetrics& aMetrics,
const ViewportConfig& aConfig,
const ViewConfig& aConfig,
nsDisplayListBuilder* aBuilder,
nsIntPoint* aShadowTranslation,
float* aShadowXScale,
@ -286,7 +286,7 @@ RenderFrameParent::BuildLayer(nsDisplayListBuilder* aBuilder,
float shadowXScale, shadowYScale;
ComputeShadowTreeTransform(aFrame,
shadowRoot->GetFrameMetrics(),
mFrameLoader->GetViewportConfig(),
mFrameLoader->GetContentView()->GetViewConfig(),
aBuilder,
&shadowTranslation,
&shadowXScale, &shadowYScale);