Skip to content

Commit

Permalink
Bug 618975 Pan overflow elements in parent process r=cjones r=tn sr=r…
Browse files Browse the repository at this point in the history
…oc a=blocking-fennec
  • Loading branch information
Benjamin Stover committed Mar 15, 2011
1 parent 5de0701 commit e9b7771
Show file tree
Hide file tree
Showing 15 changed files with 272 additions and 140 deletions.
1 change: 1 addition & 0 deletions content/base/src/nsGkAtomList.h
Expand Up @@ -1749,6 +1749,7 @@ GK_ATOM(DeleteTxnName, "Deleting")
// IPC stuff
GK_ATOM(Remote, "remote")
GK_ATOM(RemoteId, "_remote_id")
GK_ATOM(DisplayPort, "_displayport")

// Names for system metrics
GK_ATOM(scrollbar_start_backward, "scrollbar-start-backward")
Expand Down
71 changes: 69 additions & 2 deletions dom/base/nsDOMWindowUtils.cpp
Expand Up @@ -48,6 +48,7 @@
#include "nsFocusManager.h"
#include "nsIEventStateManager.h"
#include "nsEventStateManager.h"
#include "nsFrameManager.h"

#include "nsIScrollableFrame.h"

Expand Down Expand Up @@ -258,9 +259,25 @@ nsDOMWindowUtils::SetCSSViewport(float aWidthPx, float aHeightPx)
return NS_OK;
}

static void DestroyNsRect(void* aObject, nsIAtom* aPropertyName,
void* aPropertyValue, void* aData)
{
nsRect* rect = static_cast<nsRect*>(aPropertyValue);
delete rect;
}

NS_IMETHODIMP
nsDOMWindowUtils::SetDisplayPort(float aXPx, float aYPx,
float aWidthPx, float aHeightPx)
{
NS_ABORT_IF_FALSE(false, "This interface is deprecated.");
return NS_ERROR_FAILURE;
}

NS_IMETHODIMP
nsDOMWindowUtils::SetDisplayPortForElement(float aXPx, float aYPx,
float aWidthPx, float aHeightPx,
nsIDOMElement* aElement)
{
if (!IsUniversalXPConnectCapable()) {
return NS_ERROR_DOM_SECURITY_ERR;
Expand All @@ -275,9 +292,59 @@ nsDOMWindowUtils::SetDisplayPort(float aXPx, float aYPx,
nsPresContext::CSSPixelsToAppUnits(aYPx),
nsPresContext::CSSPixelsToAppUnits(aWidthPx),
nsPresContext::CSSPixelsToAppUnits(aHeightPx));
presShell->SetDisplayPort(displayport);

presShell->SetIgnoreViewportScrolling(PR_TRUE);
if (!aElement) {
return NS_ERROR_INVALID_ARG;
}

nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);

if (!content) {
return NS_ERROR_INVALID_ARG;
}

nsRect lastDisplayPort;
if (nsLayoutUtils::GetDisplayPort(content, &lastDisplayPort) &&
displayport == lastDisplayPort) {
return NS_OK;
}

content->SetProperty(nsGkAtoms::DisplayPort, new nsRect(displayport),
DestroyNsRect);

nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
if (rootScrollFrame) {
if (content == rootScrollFrame->GetContent()) {
// We are setting the root displayport. The pres context needs a special
// flag to be set.
presShell->SetIgnoreViewportScrolling(PR_TRUE);
}
}

// FIXME (Bug 593243 should fix this.)
//
// Invalidated content does not pay any attention to the displayport, so
// invalidating the subdocument's root frame could end up not repainting
// visible content.
//
// For instance, imagine the iframe is located at y=1000. Even though the
// displayport may intersect the iframe's viewport, the visual overflow
// rect of the root content could be (0, 0, 800, 500). Since the dirty region
// does not intersect the visible overflow rect, the display list for the
// iframe will not even be generated.
//
// Here, we find the very top presShell and use its root frame for
// invalidation instead.
//
nsPresContext* rootPresContext = GetPresContext()->GetRootPresContext();
if (rootPresContext) {
nsIPresShell* rootPresShell = rootPresContext->GetPresShell();
nsIFrame* rootFrame = rootPresShell->FrameManager()->GetRootFrame();
if (rootFrame) {
rootFrame->InvalidateWithFlags(rootFrame->GetVisualOverflowRectRelativeToSelf(),
nsIFrame::INVALIDATE_NO_THEBES_LAYERS);
}
}

return NS_OK;
}
Expand Down
57 changes: 32 additions & 25 deletions dom/interfaces/base/nsIDOMWindowUtils.idl
Expand Up @@ -129,30 +129,7 @@ interface nsIDOMWindowUtils : nsISupports {
void setCSSViewport(in float aWidthPx, in float aHeightPx);

/**
* Set the "displayport" to be <xPx, yPx, widthPx, heightPx> in
* units of CSS pixels, regardless of the size of the enclosing
* widget/view. This will *not* trigger reflow.
*
* <x, y> is relative to the top-left of the CSS viewport. This
* means that the pixels rendered to the displayport take scrolling
* into account, for example.
*
* The displayport will be used as the window's visible region for
* the purposes of invalidation and painting. The displayport can
* approximately be thought of as a "persistent" drawWindow()
* (albeit with coordinates relative to the CSS viewport): the
* bounds are remembered by the platform, and layer pixels are
* retained and updated inside the viewport bounds.
*
* It's legal to set a displayport that extends beyond the CSS
* viewport in any direction (left/right/top/bottom).
*
* It's also legal to set a displayport that extends beyond the
* document's bounds. The value of the pixels rendered outside the
* document bounds is not yet defined.
*
* The caller of this method must have UniversalXPConnect
* privileges.
* @DEPRECATED See nsIDOMWindowUtils_MOZILLA_2_0_BRANCH.
*/
void setDisplayPort(in float aXPx, in float aYPx,
in float aWidthPx, in float aHeightPx);
Expand Down Expand Up @@ -824,7 +801,7 @@ interface nsIDOMWindowUtils : nsISupports {

typedef unsigned long long nsViewID;

[scriptable, uuid(be2e28c8-64f8-4100-906d-8a451ddd6835)]
[scriptable, uuid(7ad49829-e631-4cdd-a237-95847d9bcbe6)]
interface nsIDOMWindowUtils_MOZILLA_2_0_BRANCH : nsISupports {
/**
* Get the type of the currently focused html input, if any.
Expand All @@ -838,6 +815,36 @@ interface nsIDOMWindowUtils_MOZILLA_2_0_BRANCH : nsISupports {
*/
nsIDOMElement findElementWithViewId(in nsViewID aId);

/**
* For any scrollable element, this allows you to override the
* visible region and draw more than what is visible, which is
* useful for asynchronous drawing. The "displayport" will be
* <xPx, yPx, widthPx, heightPx> in units of CSS pixels,
* regardless of the size of the enclosing container. This
* will *not* trigger reflow.
*
* For the root scroll area, pass in the root document element.
* For scrollable elements, pass in the container element (for
* instance, the element with overflow: scroll).
*
* <x, y> is relative to the top-left of what would normally be
* the visible area of the element. This means that the pixels
* rendered to the displayport take scrolling into account,
* for example.
*
* It's legal to set a displayport that extends beyond the overflow
* area in any direction (left/right/top/bottom).
*
* It's also legal to set a displayport that extends beyond the
* area's bounds. No pixels are rendered outside the area bounds.
*
* The caller of this method must have UniversalXPConnect
* privileges.
*/
void setDisplayPortForElement(in float aXPx, in float aYPx,
in float aWidthPx, in float aHeightPx,
in nsIDOMElement aElement);

/**
* Same as enterModalState, but returns the window associated with the
* current JS context.
Expand Down
3 changes: 2 additions & 1 deletion layout/base/FrameLayerBuilder.cpp
Expand Up @@ -1295,7 +1295,8 @@ ContainerState::ProcessDisplayItems(const nsDisplayList& aList,
item->GetLayerState(mBuilder, mManager);

// Assign the item to a layer
if (layerState == LAYER_ACTIVE && (aClip.mRoundedClipRects.IsEmpty() ||
if (layerState == LAYER_ACTIVE_FORCE ||
layerState == LAYER_ACTIVE && (aClip.mRoundedClipRects.IsEmpty() ||
// We can use the visible rect here only because the item has its own
// layer, like the comment below.
!aClip.IsRectClippedByRoundedCorner(item->GetVisibleRect()))) {
Expand Down
5 changes: 4 additions & 1 deletion layout/base/FrameLayerBuilder.h
Expand Up @@ -56,7 +56,10 @@ namespace mozilla {
enum LayerState {
LAYER_NONE,
LAYER_INACTIVE,
LAYER_ACTIVE
LAYER_ACTIVE,
// Force an active layer even if it causes incorrect rendering, e.g.
// when the layer has rounded rect clips.
LAYER_ACTIVE_FORCE
};

/**
Expand Down
54 changes: 36 additions & 18 deletions layout/base/nsDisplayList.cpp
Expand Up @@ -149,12 +149,13 @@ static void UnmarkFrameForDisplay(nsIFrame* aFrame) {
}

static void RecordFrameMetrics(nsIFrame* aForFrame,
nsIFrame* aViewportFrame,
ContainerLayer* aRoot,
nsRect aVisibleRect,
nsRect aViewport,
nsRect aDisplayPort,
ViewID aScrollId) {
nsPresContext* presContext = aForFrame->PresContext();
nsIPresShell* presShell = presContext->GetPresShell();

nsIntRect visible = aVisibleRect.ToNearestPixels(presContext->AppUnitsPerDevPixel());
aRoot->SetVisibleRegion(nsIntRegion(visible));
Expand All @@ -163,23 +164,23 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,

PRInt32 auPerDevPixel = presContext->AppUnitsPerDevPixel();
metrics.mViewport = aViewport.ToNearestPixels(auPerDevPixel);
if (presShell->UsingDisplayPort()) {
metrics.mDisplayPort =
presShell->GetDisplayPort().ToNearestPixels(auPerDevPixel);
if (aViewport != aDisplayPort) {
metrics.mDisplayPort = aDisplayPort.ToNearestPixels(auPerDevPixel);
}

nsIScrollableFrame* rootScrollableFrame =
presShell->GetRootScrollFrameAsScrollable();
if (rootScrollableFrame) {
nsSize contentSize =
rootScrollableFrame->GetScrollRange().Size() +
rootScrollableFrame->GetScrollPortRect().Size();
nsIScrollableFrame* scrollableFrame = NULL;
if (aViewportFrame)
scrollableFrame = aViewportFrame->GetScrollTargetFrame();

if (scrollableFrame) {
nsSize contentSize =
scrollableFrame->GetScrollRange().Size() +
scrollableFrame->GetScrollPortRect().Size();
metrics.mContentSize = nsIntSize(NSAppUnitsToIntPixels(contentSize.width, auPerDevPixel),
NSAppUnitsToIntPixels(contentSize.height, auPerDevPixel));

metrics.mViewportScrollOffset =
rootScrollableFrame->GetScrollPosition().ToNearestPixels(auPerDevPixel);

scrollableFrame->GetScrollPosition().ToNearestPixels(auPerDevPixel);
}
else {
nsSize contentSize = aForFrame->GetSize();
Expand Down Expand Up @@ -523,7 +524,18 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
ViewID id = presContext->IsRootContentDocument() ? FrameMetrics::ROOT_SCROLL_ID
: FrameMetrics::NULL_SCROLL_ID;

RecordFrameMetrics(aForFrame, root, mVisibleRect, mVisibleRect, id);
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
nsRect visibleRect = mVisibleRect;
if (rootScrollFrame) {
nsIContent* content = rootScrollFrame->GetContent();
if (content) {
// If there is a displayport defined for the root content element,
// it will be stored in visibleRect.
nsLayoutUtils::GetDisplayPort(content, &visibleRect);
}
}
RecordFrameMetrics(aForFrame, presShell->GetRootScrollFrame(),
root, mVisibleRect, mVisibleRect, visibleRect, id);

// If the layer manager supports resolution scaling, set that up
if (LayerManager::LAYERS_BASIC == layerManager->GetBackendType()) {
Expand Down Expand Up @@ -1694,9 +1706,11 @@ nsDisplayOwnLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
nsDisplayScrollLayer::nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder,
nsDisplayList* aList,
nsIFrame* aForFrame,
nsIFrame* aViewportFrame)
nsIFrame* aViewportFrame,
const nsRect& aDisplayPort)
: nsDisplayOwnLayer(aBuilder, aForFrame, aList)
, mViewportFrame(aViewportFrame)
, mDisplayPort(aDisplayPort)
{
#ifdef NS_BUILD_REFCNT_LOGGING
MOZ_COUNT_CTOR(nsDisplayScrollLayer);
Expand All @@ -1721,7 +1735,8 @@ nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
mViewportFrame->GetPosition() +
aBuilder->ToReferenceFrame(mViewportFrame);

RecordFrameMetrics(mFrame, layer, mVisibleRect, viewport, scrollId);
RecordFrameMetrics(mFrame, mViewportFrame, layer, mVisibleRect, viewport,
mDisplayPort, scrollId);

return layer.forget();
}
Expand All @@ -1733,14 +1748,17 @@ nsDisplayScrollLayer::ComputeVisibility(nsDisplayListBuilder* aBuilder,
PRBool& aContainsRootContentDocBG)
{
nsPresContext* presContext = mFrame->PresContext();
nsIPresShell* presShell = presContext->GetPresShell();

if (presShell->UsingDisplayPort()) {
nsRect viewport = mViewportFrame->GetRect() -
mViewportFrame->GetPosition() +
aBuilder->ToReferenceFrame(mViewportFrame);

if (mDisplayPort != viewport) {
// The visible region for the children may be much bigger than the hole we
// are viewing the children from, so that the compositor process has enough
// content to asynchronously pan while content is being refreshed.

nsRegion childVisibleRegion = presShell->GetDisplayPort() + aBuilder->ToReferenceFrame(mViewportFrame);
nsRegion childVisibleRegion = mDisplayPort + aBuilder->ToReferenceFrame(mViewportFrame);

nsRect boundedRect;
boundedRect.IntersectRect(childVisibleRegion.GetBounds(), mList.GetBounds(aBuilder));
Expand Down
13 changes: 12 additions & 1 deletion layout/base/nsDisplayList.h
Expand Up @@ -1790,9 +1790,12 @@ class nsDisplayScrollLayer : public nsDisplayOwnLayer
* @param aForFrame This will determine what the displayport is. It should be
* the root content frame of the scrolled area.
* @param aViewportFrame The viewport frame you see this content through.
* @param aDisplayPort Overrides the visibility of the child items if it
* is not equal to the visible area.
*/
nsDisplayScrollLayer(nsDisplayListBuilder* aBuilder, nsDisplayList* aList,
nsIFrame* aForFrame, nsIFrame* aViewportFrame);
nsIFrame* aForFrame, nsIFrame* aViewportFrame,
const nsRect& aDisplayPort);
NS_DISPLAY_DECL_NAME("ScrollLayer", TYPE_SCROLL_LAYER)

#ifdef NS_BUILD_REFCNT_LOGGING
Expand All @@ -1807,8 +1810,16 @@ class nsDisplayScrollLayer : public nsDisplayOwnLayer
const nsRect& aAllowVisibleRegionExpansion,
PRBool& aContainsRootContentDocBG);

virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder,
LayerManager* aManager)
{
// Force this as a layer so we can scroll asynchronously.
// This causes incorrect rendering for rounded clips!
return mozilla::LAYER_ACTIVE_FORCE;
}
private:
nsIFrame* mViewportFrame;
nsRect mDisplayPort;
};
#endif

Expand Down
8 changes: 5 additions & 3 deletions layout/base/nsIPresShell.h
Expand Up @@ -1154,19 +1154,21 @@ class nsIPresShell : public nsIPresShell_base
* Set up a "displayport", which overrides what everything else thinks
* is the visible region of this document with the specified
* displayport rect.
* @DEPRECATED Use nsLayoutUtils displayport methods
*/
virtual void SetDisplayPort(const nsRect& aDisplayPort) = 0;
PRBool UsingDisplayPort() const
{ return mRenderFlags & STATE_USING_DISPLAYPORT; }
{ NS_ABORT_IF_FALSE(false, "UsingDisplayPort is deprecated"); return false; }

/**
* Return the displayport being used. |UsingDisplayPort()| must be
* true.
* @DEPRECATED Use nsLayoutUtils displayport methods
*/
nsRect GetDisplayPort()
{
NS_ABORT_IF_FALSE(UsingDisplayPort(), "no displayport defined!");
return mDisplayPort;
NS_ABORT_IF_FALSE(false, "GetDisplayPort is deprecated");
return nsRect();
}

/**
Expand Down

0 comments on commit e9b7771

Please sign in to comment.