Bug 699351. Add nsIFrameLoader::clipSubdocument API to allow XUL <iframe> and <browser> to not clip the subdocument. r=tnikkel,sr=bzbarsky

This commit is contained in:
Robert O'Callahan 2011-12-06 01:38:46 +13:00
parent c2911d257e
commit 854ef91454
15 changed files with 376 additions and 70 deletions

View File

@ -141,7 +141,7 @@ interface nsIContentViewManager : nsISupports
readonly attribute nsIContentView rootContentView;
};
[scriptable, uuid(12905a29-4246-475a-81d4-fc389197df02)]
[scriptable, uuid(efc0b731-45dc-4189-8ffa-d3eeeb850977)]
interface nsIFrameLoader : nsISupports
{
/**
@ -258,6 +258,13 @@ interface nsIFrameLoader : nsISupports
const unsigned long EVENT_MODE_DONT_FORWARD_TO_CHILD = 0x00000001;
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;
};
native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);

View File

@ -143,14 +143,12 @@ public:
nsRefPtr<nsIDocShell> mDocShell;
};
static void InvalidateFrame(nsIFrame* aFrame)
static void InvalidateFrame(nsIFrame* aFrame, PRUint32 aFlags)
{
if (!aFrame)
return;
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);
aFrame->InvalidateWithFlags(rect, aFlags);
}
NS_IMPL_ISUPPORTS1(nsContentView, nsIContentView)
@ -189,8 +187,11 @@ nsContentView::Update(const ViewConfig& aConfig)
// XXX could be clever here and compute a smaller invalidation
// rect
nsIFrame* frame = mFrameLoader->GetPrimaryFrameOfOwningContent();
InvalidateFrame(frame);
// 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.
InvalidateFrame(mFrameLoader->GetPrimaryFrameOfOwningContent(), nsIFrame::INVALIDATE_NO_THEBES_LAYERS);
return NS_OK;
}
@ -328,6 +329,7 @@ nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated)
, mDelayRemoteDialogs(false)
, mRemoteBrowserShown(false)
, mRemoteFrame(false)
, mClipSubdocument(true)
, mCurrentRemoteFrame(nsnull)
, mRemoteBrowser(nsnull)
, mRenderMode(RENDER_MODE_DEFAULT)
@ -1693,7 +1695,11 @@ nsFrameLoader::SetRenderMode(PRUint32 aRenderMode)
}
mRenderMode = aRenderMode;
InvalidateFrame(GetPrimaryFrameOfOwningContent());
// 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.
InvalidateFrame(GetPrimaryFrameOfOwningContent(), nsIFrame::INVALIDATE_NO_THEBES_LAYERS);
return NS_OK;
}
@ -1711,6 +1717,38 @@ nsFrameLoader::SetEventMode(PRUint32 aEventMode)
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::GetClipSubdocument(bool* aResult)
{
*aResult = mClipSubdocument;
return NS_OK;
}
NS_IMETHODIMP
nsFrameLoader::SetClipSubdocument(bool aClip)
{
mClipSubdocument = aClip;
nsIFrame* frame = GetPrimaryFrameOfOwningContent();
if (frame) {
InvalidateFrame(frame, 0);
frame->PresContext()->PresShell()->
FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame);
if (subdocFrame) {
nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame();
if (subdocRootFrame) {
nsIFrame* subdocRootScrollFrame = subdocRootFrame->PresContext()->PresShell()->
GetRootScrollFrame();
if (subdocRootScrollFrame) {
frame->PresContext()->PresShell()->
FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
}
}
}
}
return NS_OK;
}
nsIntSize
nsFrameLoader::GetSubDocumentSize(const nsIFrame *aIFrame)
{

View File

@ -287,6 +287,8 @@ public:
mozilla::dom::Element* GetOwnerContent() { return mOwnerContent; }
void SetOwnerContent(mozilla::dom::Element* aContent);
bool ShouldClipSubdocument() { return mClipSubdocument; }
private:
bool ShouldUseRemoteProcess();
@ -338,7 +340,9 @@ private:
bool mDelayRemoteDialogs : 1;
bool mRemoteBrowserShown : 1;
bool mRemoteFrame;
bool mRemoteFrame : 1;
bool mClipSubdocument : 1;
// XXX leaking
nsCOMPtr<nsIObserver> mChildHost;
RenderFrameParent* mCurrentRemoteFrame;

View File

@ -7295,14 +7295,14 @@ PresShell::DoReflow(nsIFrame* target, bool aInterruptible)
desiredSize.height == size.height),
"non-root frame's desired size changed during an "
"incremental reflow");
NS_ASSERTION(desiredSize.VisualOverflow().IsEqualInterior(
NS_ASSERTION(target == rootFrame || desiredSize.VisualOverflow().IsEqualInterior(
nsRect(nsPoint(0, 0),
nsSize(desiredSize.width, desiredSize.height))),
"reflow roots must not have visible overflow");
NS_ASSERTION(desiredSize.ScrollableOverflow().IsEqualEdges(
"non-root reflow roots must not have visible overflow");
NS_ASSERTION(target == rootFrame || desiredSize.ScrollableOverflow().IsEqualEdges(
nsRect(nsPoint(0, 0),
nsSize(desiredSize.width, desiredSize.height))),
"reflow roots must not have scrollable overflow");
"non-root reflow roots must not have scrollable overflow");
NS_ASSERTION(status == NS_FRAME_COMPLETE,
"reflow roots should never split");

View File

@ -62,6 +62,9 @@ _CHROME_FILES = \
test_default_background.xul \
default_background_window.xul \
test_leaf_layers_partition_browser_window.xul \
test_no_clip_iframe.xul \
no_clip_iframe_window.xul \
no_clip_iframe_subdoc.html \
test_printpreview.xul \
printpreview_helper.xul \
test_printpreview_bug396024.xul \

View File

@ -0,0 +1,7 @@
<!DOCTYPE HTML>
<html>
<body style="margin:0; background:lime;">
<div id="d" style="position:relative; top:-50px; width:150px; height:250px; background:yellow;"></div>
<div id="p" style="margin-top:-50px; width:150px; height:50px;"></div>
</body>
</html>

View File

@ -0,0 +1,137 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="runTests()">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript"
src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
<div id="container" xmlns="http://www.w3.org/1999/xhtml" style="height:400px; overflow:auto; background:gray">
<div style="height:0">
<iframe type="content" id="f" src="no_clip_iframe_subdoc.html"
style="margin-top:50px; border:1px solid black; width:100px; height:100px;"/>
</div>
<div id="ref" style="background:gray;">
<div style="border:1px solid black; margin-top:50px; width:100px; height:100px;">
<div id="ref-d" style="background:lime; height:250px; width:150px;">
<div style="position:relative; top:-50px; width:150px; height:100%; background:yellow;"/>
</div>
</div>
</div>
</div>
<vbox flex="1"/>
<script type="application/javascript">
<![CDATA[
var imports = [ "SimpleTest", "is", "isnot", "ok", "onerror" ];
for each (var name in imports) {
window[name] = window.opener.wrappedJSObject[name];
}
SimpleTest.waitForExplicitFinish();
var accumulatedRect = null;
var onpaint = function() {};
function paintListener(event) {
if (event.target != window)
return;
dump("got MozAfterPaint: " + event.boundingClientRect.left + "," + event.boundingClientRect.top + "," +
event.boundingClientRect.right + "," + event.boundingClientRect.bottom + "\n");
if (accumulatedRect) {
accumulatedRect[0] = Math.min(accumulatedRect[0], event.boundingClientRect.left);
accumulatedRect[1] = Math.min(accumulatedRect[1], event.boundingClientRect.top);
accumulatedRect[2] = Math.max(accumulatedRect[2], event.boundingClientRect.right);
accumulatedRect[3] = Math.max(accumulatedRect[3], event.boundingClientRect.bottom);
} else {
accumulatedRect = [event.boundingClientRect.left, event.boundingClientRect.top,
event.boundingClientRect.right, event.boundingClientRect.bottom];
}
onpaint();
}
window.addEventListener("MozAfterPaint", paintListener, false);
function waitForAllPaintsFlushed(callback) {
document.documentElement.getBoundingClientRect();
var CI = Components.interfaces;
var utils = window.QueryInterface(CI.nsIInterfaceRequestor)
.getInterface(CI.nsIDOMWindowUtils);
if (!utils.isMozAfterPaintPending) {
dump("done...\n");
var result = accumulatedRect;
accumulatedRect = null;
onpaint = function() {};
if (!result) {
result = [0,0,0,0];
}
callback(result[0], result[1], result[2], result[3]);
return;
}
dump("waiting for paint...\n");
onpaint = function() { waitForAllPaintsFlushed(callback); };
}
var Ci = Components.interfaces;
var frame = document.getElementById("f");
var fl = frame.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
is(fl.clipSubdocument, true, "clipSubdocument should default to true");
fl.clipSubdocument = false;
is(fl.clipSubdocument, false, "clipSubdocument should have been set to false");
function runTests() {
var ref = document.getElementById("ref");
frame.contentWindow.scrollTo(0,0);
ref.style.visibility = "hidden";
var testCanvas = snapshotWindow(window);
ref.style.visibility = "";
var refCanvas = snapshotWindow(window);
var comparison = compareSnapshots(testCanvas, refCanvas, true);
ok(comparison[0], "Basic overflow drawing; got " + comparison[1] + ", expected " + comparison[2]);
document.getElementById("container").style.height = "200px";
ref.style.visibility = "hidden";
testCanvas = snapshotWindow(window);
ref.style.visibility = "";
refCanvas = snapshotWindow(window);
comparison = compareSnapshots(testCanvas, refCanvas, true);
ok(comparison[0], "Drawing with vertical scrollbar to show overflow area computation; got " +
comparison[1] + ", expected " + comparison[2]);
frame.contentDocument.getElementById("d").style.height = "350px";
document.getElementById("ref-d").style.height = "350px";
ref.style.visibility = "hidden";
testCanvas = snapshotWindow(window);
ref.style.visibility = "";
refCanvas = snapshotWindow(window);
comparison = compareSnapshots(testCanvas, refCanvas, true);
ok(comparison[0], "testing dynamic overflow area change affecting scrollbar; got " +
comparison[1] + ", expected " + comparison[2]);
// Now do invalidation tests
ref.style.visibility = "hidden";
document.getElementById("container").style.height = "400px";
waitForAllPaintsFlushed(function() {
dump("Scrolling\n");
frame.contentWindow.scrollTo(0,80);
waitForAllPaintsFlushed(function(x1, y1, x2, y2) {
ok(x1 <= 1 && x2 >= 151 && y1 <= 0 && y2 >= 400,
"Entire scrolled region is painted: " + x1 + "," + y1 + "," + x2 + "," + y2);
frame.contentDocument.getElementById("p").style.background = "cyan";
waitForAllPaintsFlushed(function(x1, y1, x2, y2) {
ok(x1 <= 1 && x2 >= 151 && y1 <= 271 && y2 >= 320,
"Entire updated region is painted: " + x1 + "," + y1 + "," + x2 + "," + y2);
var tester = window.SimpleTest;
window.close();
tester.finish();
});
});
});
}
]]>
</script>
</window>

View File

@ -0,0 +1,23 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<!-- test results are displayed in the html:body -->
<body xmlns="http://www.w3.org/1999/xhtml">
</body>
<!-- test code goes here -->
<script type="application/javascript">
<![CDATA[
SimpleTest.waitForExplicitFinish();
// Run the test in a separate window so that the test runs as a chrome
// window
window.open("no_clip_iframe_window.xul", "no_clip_iframe",
"chrome,width=200,height=400");
]]>
</script>
</window>

View File

@ -6439,13 +6439,17 @@ nsFrame::CreateAccessible()
NS_DECLARE_FRAME_PROPERTY(OverflowAreasProperty,
nsIFrame::DestroyOverflowAreas)
void
bool
nsIFrame::ClearOverflowRects()
{
if (mOverflow.mType == NS_FRAME_OVERFLOW_NONE) {
return false;
}
if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
Properties().Delete(OverflowAreasProperty());
}
mOverflow.mType = NS_FRAME_OVERFLOW_NONE;
return true;
}
/** Create or retrieve the previously stored overflow area, if the frame does
@ -6473,17 +6477,18 @@ nsIFrame::GetOverflowAreasProperty()
/** Set the overflowArea rect, storing it as deltas or a separate rect
* depending on its size in relation to the primary frame rect.
*/
void
bool
nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
{
if (mOverflow.mType == NS_FRAME_OVERFLOW_LARGE) {
nsOverflowAreas *overflow =
static_cast<nsOverflowAreas*>(Properties().Get(OverflowAreasProperty()));
bool changed = *overflow != aOverflowAreas;
*overflow = aOverflowAreas;
// Don't bother with converting to the deltas form if we already
// have a property.
return;
return changed;
}
const nsRect& vis = aOverflowAreas.VisualOverflow();
@ -6505,6 +6510,7 @@ nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
// so that our eventual SetRect/SetSize will know that it has to
// reset our overflow areas.
(l | t | r | b) != 0) {
VisualDeltas oldDeltas = mOverflow.mVisualDeltas;
// It's a "small" overflow area so we store the deltas for each edge
// directly in the frame, rather than allocating a separate rect.
// If they're all zero, that's fine; we're setting things to
@ -6513,12 +6519,18 @@ nsIFrame::SetOverflowAreas(const nsOverflowAreas& aOverflowAreas)
mOverflow.mVisualDeltas.mTop = t;
mOverflow.mVisualDeltas.mRight = r;
mOverflow.mVisualDeltas.mBottom = b;
// There was no scrollable overflow before, and there isn't now.
return oldDeltas != mOverflow.mVisualDeltas;
} else {
bool changed = !aOverflowAreas.ScrollableOverflow().IsEqualEdges(nsRect(nsPoint(0, 0), GetSize())) ||
!aOverflowAreas.VisualOverflow().IsEqualEdges(GetVisualOverflowFromDeltas());
// it's a large overflow area that we need to store as a property
mOverflow.mType = NS_FRAME_OVERFLOW_LARGE;
nsOverflowAreas* overflow = GetOverflowAreasProperty();
NS_ASSERTION(overflow, "should have created areas");
*overflow = aOverflowAreas;
return changed;
}
}
@ -6529,7 +6541,7 @@ IsInlineFrame(nsIFrame *aFrame)
return type == nsGkAtoms::inlineFrame;
}
void
bool
nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
nsSize aNewSize)
{
@ -6638,11 +6650,11 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
bool visualOverflowChanged =
!GetVisualOverflowRect().IsEqualInterior(aOverflowAreas.VisualOverflow());
bool anyOverflowChanged;
if (aOverflowAreas != nsOverflowAreas(bounds, bounds)) {
SetOverflowAreas(aOverflowAreas);
anyOverflowChanged = SetOverflowAreas(aOverflowAreas);
} else {
ClearOverflowRects();
anyOverflowChanged = ClearOverflowRects();
}
if (visualOverflowChanged) {
@ -6687,6 +6699,8 @@ nsIFrame::FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
nsDisplayItem::TYPE_TRANSFORM);
}
}
return anyOverflowChanged;
}
/* The overflow rects for leaf nodes in a preserve-3d hierarchy depends on

View File

@ -82,6 +82,7 @@
#include "mozilla/dom/Element.h"
#include "FrameLayerBuilder.h"
#include "nsSMILKeySpline.h"
#include "nsSubDocumentFrame.h"
using namespace mozilla;
using namespace mozilla::dom;
@ -196,16 +197,20 @@ nsHTMLScrollFrame::InvalidateInternal(const nsRect& aDamageRect,
nsRect damage = aDamageRect + nsPoint(aX, aY);
// This is the damage rect that we're going to pass up to our parent.
nsRect parentDamage;
// If we're using a displayport, we might be displaying an area
// different than our scroll port and the damage needs to be
// clipped to that instead.
nsRect displayport;
bool usingDisplayport = nsLayoutUtils::GetDisplayPort(GetContent(),
&displayport);
if (usingDisplayport) {
parentDamage.IntersectRect(damage, displayport);
if (mInner.IsIgnoringViewportClipping()) {
parentDamage = damage;
} else {
parentDamage.IntersectRect(damage, mInner.mScrollPort);
// If we're using a displayport, we might be displaying an area
// different than our scroll port and the damage needs to be
// clipped to that instead.
nsRect displayport;
bool usingDisplayport = nsLayoutUtils::GetDisplayPort(GetContent(),
&displayport);
if (usingDisplayport) {
parentDamage.IntersectRect(damage, displayport);
} else {
parentDamage.IntersectRect(damage, mInner.mScrollPort);
}
}
if (IsScrollingActive()) {
@ -283,7 +288,7 @@ struct ScrollReflowState {
nsMargin mComputedBorder;
// === Filled in by ReflowScrolledFrame ===
nsRect mContentsOverflowArea;
nsOverflowAreas mContentsOverflowAreas;
bool mReflowedContentsWithHScrollbar;
bool mReflowedContentsWithVScrollbar;
@ -427,7 +432,8 @@ nsHTMLScrollFrame::TryLayout(ScrollReflowState* aState,
if (!aForce) {
nsRect scrolledRect =
mInner.GetScrolledRectInternal(aState->mContentsOverflowArea, scrollPortSize);
mInner.GetScrolledRectInternal(aState->mContentsOverflowAreas.ScrollableOverflow(),
scrollPortSize);
nscoord oneDevPixel = aState->mBoxState.PresContext()->DevPixelsToAppUnits(1);
// If the style is HIDDEN then we already know that aAssumeHScroll is false
@ -566,7 +572,7 @@ nsHTMLScrollFrame::ReflowScrolledFrame(ScrollReflowState* aState,
// overflow area doesn't include the frame bounds.
aMetrics->UnionOverflowAreasWithDesiredBounds();
aState->mContentsOverflowArea = aMetrics->ScrollableOverflow();
aState->mContentsOverflowAreas = aMetrics->mOverflowAreas;
aState->mReflowedContentsWithHScrollbar = aAssumeHScroll;
aState->mReflowedContentsWithVScrollbar = aAssumeVScroll;
@ -718,7 +724,9 @@ nsHTMLScrollFrame::PlaceScrollArea(const ScrollReflowState& aState,
nsRect scrolledArea;
// Preserve the width or height of empty rects
nsSize portSize = mInner.mScrollPort.Size();
nsRect scrolledRect = mInner.GetScrolledRectInternal(aState.mContentsOverflowArea, portSize);
nsRect scrolledRect =
mInner.GetScrolledRectInternal(aState.mContentsOverflowAreas.ScrollableOverflow(),
portSize);
scrolledArea.UnionRectEdges(scrolledRect,
nsRect(nsPoint(0,0), portSize));
@ -925,6 +933,10 @@ nsHTMLScrollFrame::Reflow(nsPresContext* aPresContext,
state.mComputedBorder.TopBottom();
aDesiredSize.SetOverflowAreasToDesiredBounds();
if (mInner.IsIgnoringViewportClipping()) {
aDesiredSize.mOverflowAreas.UnionWith(
state.mContentsOverflowAreas + mInner.mScrolledFrame->GetPosition());
}
CheckInvalidateSizeChange(aDesiredSize);
@ -1687,6 +1699,15 @@ InvalidateFixedBackgroundFrames(nsIFrame* aRootFrame,
list.DeleteAll();
}
bool nsGfxScrollFrameInner::IsIgnoringViewportClipping() const
{
if (!mIsRoot)
return false;
nsSubDocumentFrame* subdocFrame = static_cast<nsSubDocumentFrame*>
(nsLayoutUtils::GetCrossDocParentFrame(mOuter->PresContext()->PresShell()->GetRootFrame()));
return subdocFrame && !subdocFrame->ShouldClipSubdocument();
}
bool nsGfxScrollFrameInner::IsAlwaysActive() const
{
// The root scrollframe for a non-chrome document which is the direct
@ -1721,7 +1742,7 @@ void nsGfxScrollFrameInner::MarkActive()
}
}
void nsGfxScrollFrameInner::ScrollVisual()
void nsGfxScrollFrameInner::ScrollVisual(nsPoint aOldScrolledFramePos)
{
nsRootPresContext* rootPresContext = mOuter->PresContext()->GetRootPresContext();
if (!rootPresContext) {
@ -1748,9 +1769,15 @@ void nsGfxScrollFrameInner::ScrollVisual()
}
nsRect invalidateRect, displayport;
invalidateRect =
(nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayport)) ?
displayport : mScrollPort;
if (IsIgnoringViewportClipping()) {
nsRect visualOverflow = mScrolledFrame->GetVisualOverflowRect();
invalidateRect.UnionRect(visualOverflow + mScrolledFrame->GetPosition(),
visualOverflow + aOldScrolledFramePos);
} else {
invalidateRect =
(nsLayoutUtils::GetDisplayPort(mOuter->GetContent(), &displayport)) ?
displayport : mScrollPort;
}
mOuter->InvalidateWithFlags(invalidateRect, flags);
@ -1820,12 +1847,13 @@ nsGfxScrollFrameInner::ScrollToImpl(nsPoint aPt)
for (PRUint32 i = 0; i < mListeners.Length(); i++) {
mListeners[i]->ScrollPositionWillChange(pt.x, pt.y);
}
nsPoint oldScrollFramePos = mScrolledFrame->GetPosition();
// Update frame position for scrolling
mScrolledFrame->SetPosition(mScrollPort.TopLeft() - pt);
// We pass in the amount to move visually
ScrollVisual();
ScrollVisual(oldScrollFramePos);
presContext->PresShell()->SynthesizeMouseMove(true);
UpdateScrollbarPosition();
@ -1937,7 +1965,7 @@ nsGfxScrollFrameInner::BuildDisplayList(nsDisplayListBuilder* aBuilder,
}
}
if (aBuilder->GetIgnoreScrollFrame() == mOuter) {
if (aBuilder->GetIgnoreScrollFrame() == mOuter || IsIgnoringViewportClipping()) {
// Don't clip the scrolled child, and don't paint scrollbars/scrollcorner.
// The scrolled frame shouldn't have its own background/border, so we
// can just pass aLists directly.

View File

@ -182,7 +182,7 @@ public:
static void AsyncScrollCallback(nsITimer *aTimer, void* anInstance);
void ScrollTo(nsPoint aScrollPosition, nsIScrollableFrame::ScrollMode aMode);
void ScrollToImpl(nsPoint aScrollPosition);
void ScrollVisual();
void ScrollVisual(nsPoint aOldScrolledFramePosition);
void ScrollBy(nsIntPoint aDelta, nsIScrollableFrame::ScrollUnit aUnit,
nsIScrollableFrame::ScrollMode aMode, nsIntPoint* aOverflow);
void ScrollToRestoredPosition();
@ -256,6 +256,8 @@ public:
const nsRect& aContentArea,
const nsRect& aOldScrollArea);
bool IsIgnoringViewportClipping() const;
bool IsAlwaysActive() const;
void MarkActive();
void MarkInactive();

View File

@ -2274,14 +2274,15 @@ public:
/**
* Store the overflow area in the frame's mOverflow.mVisualDeltas
* fields or as a frame property in the frame manager so that it can
* be retrieved later without reflowing the frame.
* be retrieved later without reflowing the frame. Returns true if either of
* the overflow areas changed.
*/
void FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
bool FinishAndStoreOverflow(nsOverflowAreas& aOverflowAreas,
nsSize aNewSize);
void FinishAndStoreOverflow(nsHTMLReflowMetrics* aMetrics) {
FinishAndStoreOverflow(aMetrics->mOverflowAreas,
nsSize(aMetrics->width, aMetrics->height));
bool FinishAndStoreOverflow(nsHTMLReflowMetrics* aMetrics) {
return FinishAndStoreOverflow(aMetrics->mOverflowAreas,
nsSize(aMetrics->width, aMetrics->height));
}
/**
@ -2294,8 +2295,9 @@ public:
/**
* Removes any stored overflow rects (visual and scrollable) from the frame.
* Returns true if the overflow changed.
*/
void ClearOverflowRects();
bool ClearOverflowRects();
/**
* Determine whether borders should not be painted on certain sides of the
@ -2822,14 +2824,24 @@ protected:
// If mOverflow.mType == NS_FRAME_OVERFLOW_LARGE, then the
// delta values are not meaningful and the overflow area is stored
// as a separate rect property.
struct VisualDeltas {
PRUint8 mLeft;
PRUint8 mTop;
PRUint8 mRight;
PRUint8 mBottom;
bool operator==(const VisualDeltas& aOther) const
{
return mLeft == aOther.mLeft && mTop == aOther.mTop &&
mRight == aOther.mRight && mBottom == aOther.mBottom;
}
bool operator!=(const VisualDeltas& aOther) const
{
return !(*this == aOther);
}
};
union {
PRUint32 mType;
struct {
PRUint8 mLeft;
PRUint8 mTop;
PRUint8 mRight;
PRUint8 mBottom;
} mVisualDeltas;
PRUint32 mType;
VisualDeltas mVisualDeltas;
} mOverflow;
// Helpers
@ -2944,7 +2956,10 @@ private:
mRect.height + mOverflow.mVisualDeltas.mBottom +
mOverflow.mVisualDeltas.mTop);
}
void SetOverflowAreas(const nsOverflowAreas& aOverflowAreas);
/**
* Returns true if any overflow changed.
*/
bool SetOverflowAreas(const nsOverflowAreas& aOverflowAreas);
nsPoint GetOffsetToCrossDoc(const nsIFrame* aOther, const PRInt32 aAPD) const;
#ifdef NS_DEBUG

View File

@ -411,18 +411,21 @@ nsSubDocumentFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
childItems.AppendToTop(layerItem);
}
nsDisplayList list;
// Clip children to the child root frame's rectangle
rv = list.AppendNewToTop(
if (ShouldClipSubdocument()) {
nsDisplayClip* item =
new (aBuilder) nsDisplayClip(aBuilder, this, &childItems,
subdocBoundsInParentUnits));
subdocBoundsInParentUnits);
// Clip children to the child root frame's rectangle
childItems.AppendToTop(item);
}
if (mIsInline) {
WrapReplacedContentForBorderRadius(aBuilder, &list, aLists);
WrapReplacedContentForBorderRadius(aBuilder, &childItems, aLists);
} else {
aLists.Content()->AppendToTop(&list);
aLists.Content()->AppendToTop(&childItems);
}
}
// delete childItems in case of OOM
childItems.DeleteAll();
@ -617,6 +620,14 @@ nsSubDocumentFrame::Reflow(nsPresContext* aPresContext,
vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), innerSize), true);
}
aDesiredSize.SetOverflowAreasToDesiredBounds();
if (!ShouldClipSubdocument()) {
nsIFrame* subdocRootFrame = GetSubdocumentRootFrame();
if (subdocRootFrame) {
aDesiredSize.mOverflowAreas.UnionWith(subdocRootFrame->GetOverflowAreas() + offset);
}
}
// Determine if we need to repaint our border, background or outline
CheckInvalidateSizeChange(aDesiredSize);

View File

@ -124,6 +124,12 @@ public:
virtual bool ReflowFinished();
virtual void ReflowCallbackCanceled();
bool ShouldClipSubdocument()
{
nsFrameLoader* frameLoader = FrameLoader();
return !frameLoader || frameLoader->ShouldClipSubdocument();
}
protected:
friend class AsyncFrameInit;

View File

@ -47,6 +47,7 @@
#include "nsIScrollableFrame.h"
#include "nsDisplayList.h"
#include "FrameLayerBuilder.h"
#include "nsSubDocumentFrame.h"
#include "nsAbsoluteContainingBlock.h"
using namespace mozilla;
@ -210,6 +211,8 @@ ViewportFrame::Reflow(nsPresContext* aPresContext,
nsresult rv = NS_OK;
aDesiredSize.SetOverflowAreasToDesiredBounds();
if (mFrames.NotEmpty()) {
// Deal with a non-incremental reflow or an incremental reflow
// targeted at our one-and-only principal child frame.
@ -234,6 +237,7 @@ ViewportFrame::Reflow(nsPresContext* aPresContext,
} else {
kidHeight = mFrames.FirstChild()->GetSize().height;
}
ConsiderChildOverflow(aDesiredSize.mOverflowAreas, mFrames.FirstChild());
}
NS_ASSERTION(aReflowState.availableWidth != NS_UNCONSTRAINEDSIZE,
@ -252,22 +256,18 @@ ViewportFrame::Reflow(nsPresContext* aPresContext,
nsHTMLReflowState reflowState(aReflowState);
nsPoint offset = AdjustReflowStateForScrollbars(&reflowState);
#ifdef DEBUG
if (IsAbsoluteContainer()) {
NS_ASSERTION(GetAbsoluteContainingBlock()->GetChildList().IsEmpty() ||
(offset.x == 0 && offset.y == 0),
"We don't handle correct positioning of fixed frames with "
"scrollbars in odd positions");
}
#endif
if (IsAbsoluteContainer()) {
// Just reflow all the fixed-pos frames.
rv = GetAbsoluteContainingBlock()->Reflow(this, aPresContext, reflowState, aStatus,
reflowState.ComputedWidth(),
reflowState.ComputedHeight(),
false, true, true, // XXX could be optimized
nsnull /* ignore overflow */);
&aDesiredSize.mOverflowAreas);
}
// If we were dirty then do a repaint
@ -276,8 +276,19 @@ ViewportFrame::Reflow(nsPresContext* aPresContext,
Invalidate(damageRect);
}
// XXX Should we do something to clip our children to this?
aDesiredSize.SetOverflowAreasToDesiredBounds();
// Clipping is handled by the document container (e.g., nsSubDocumentFrame),
// so we don't need to change our overflow areas.
bool overflowChanged = FinishAndStoreOverflow(&aDesiredSize);
if (overflowChanged) {
// We may need to alert our container to get it to pick up the
// overflow change.
nsSubDocumentFrame* container = static_cast<nsSubDocumentFrame*>
(nsLayoutUtils::GetCrossDocParentFrame(this));
if (container && !container->ShouldClipSubdocument()) {
container->PresContext()->PresShell()->
FrameNeedsReflow(container, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
}
}
NS_FRAME_TRACE_REFLOW_OUT("ViewportFrame::Reflow", aStatus);
NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);