Bug 598470. Treat all chrome display items as opaque when we're computing plugin visibility regions; this ensures translucent chrome content is visible above windowed plugins. r=tnikkel,a=blocker

This commit is contained in:
Robert O'Callahan 2010-11-08 22:06:14 +13:00
parent 26e032892f
commit 50f59c86bf
11 changed files with 153 additions and 13 deletions

View File

@ -68,12 +68,12 @@ using namespace mozilla;
using namespace mozilla::layers;
nsDisplayListBuilder::nsDisplayListBuilder(nsIFrame* aReferenceFrame,
PRBool aIsForEvents, PRBool aBuildCaret)
Mode aMode, PRBool aBuildCaret)
: mReferenceFrame(aReferenceFrame),
mIgnoreScrollFrame(nsnull),
mCurrentTableItem(nsnull),
mBuildCaret(aBuildCaret),
mEventDelivery(aIsForEvents),
mMode(aMode),
mIgnoreSuppression(PR_FALSE),
mHadToIgnoreSuppression(PR_FALSE),
mIsAtRootOfPseudoStackingContext(PR_FALSE),
@ -307,6 +307,21 @@ nsDisplayList::ComputeVisibilityForRoot(nsDisplayListBuilder* aBuilder,
return ComputeVisibilityForSublist(aBuilder, aVisibleRegion, r.GetBounds());
}
static PRBool
TreatAsOpaque(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder,
PRBool* aTransparentBackground)
{
if (aItem->IsOpaque(aBuilder, aTransparentBackground))
return PR_TRUE;
if (aBuilder->IsForPluginGeometry()) {
// Treat all chrome items as opaque
nsIFrame* f = aItem->GetUnderlyingFrame();
if (f && f->PresContext()->IsChrome())
return PR_TRUE;
}
return PR_FALSE;
}
PRBool
nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
nsRegion* aVisibleRegion,
@ -345,7 +360,7 @@ nsDisplayList::ComputeVisibilityForSublist(nsDisplayListBuilder* aBuilder,
anyVisible = PR_TRUE;
nsIFrame* f = item->GetUnderlyingFrame();
PRBool transparentBackground = PR_FALSE;
if (item->IsOpaque(aBuilder, &transparentBackground) && f) {
if (TreatAsOpaque(item, aBuilder, &transparentBackground) && f) {
// Subtract opaque item from the visible region
aBuilder->SubtractFromVisibleRegion(aVisibleRegion, nsRegion(bounds));
}
@ -650,7 +665,8 @@ PRBool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
if (!ComputeVisibility(aBuilder, aVisibleRegion))
return PR_FALSE;
if (IsOpaque(aBuilder)) {
PRBool forceTransparentBackground;
if (TreatAsOpaque(this, aBuilder, &forceTransparentBackground)) {
aVisibleRegion->Sub(*aVisibleRegion, bounds);
}
return PR_TRUE;

View File

@ -136,15 +136,25 @@ public:
* @param aBuildCaret whether or not we should include the caret in any
* display lists that we make.
*/
nsDisplayListBuilder(nsIFrame* aReferenceFrame, PRBool aIsForEvents,
PRBool aBuildCaret);
enum Mode {
PAINTING,
EVENT_DELIVERY,
PLUGIN_GEOMETRY,
OTHER
};
nsDisplayListBuilder(nsIFrame* aReferenceFrame, Mode aMode, PRBool aBuildCaret);
~nsDisplayListBuilder();
/**
* @return PR_TRUE if the display is being built in order to determine which
* frame is under the mouse position.
*/
PRBool IsForEventDelivery() { return mEventDelivery; }
PRBool IsForEventDelivery() { return mMode == EVENT_DELIVERY; }
/**
* @return PR_TRUE if the display list is being build to compute geometry
* for plugins.
*/
PRBool IsForPluginGeometry() { return mMode == PLUGIN_GEOMETRY; }
/**
* @return PR_TRUE if "painting is suppressed" during page load and we
* should paint only the background of the document.
@ -393,8 +403,8 @@ private:
nsAutoTArray<PresShellState,8> mPresShellStates;
nsAutoTArray<nsIFrame*,100> mFramesMarkedForDisplay;
nsDisplayTableItem* mCurrentTableItem;
Mode mMode;
PRPackedBool mBuildCaret;
PRPackedBool mEventDelivery;
PRPackedBool mIgnoreSuppression;
PRPackedBool mHadToIgnoreSuppression;
PRPackedBool mIsAtRootOfPseudoStackingContext;

View File

@ -1093,7 +1093,8 @@ nsLayoutUtils::GetFramesForArea(nsIFrame* aFrame, const nsRect& aRect,
PRBool aShouldIgnoreSuppression,
PRBool aIgnoreRootScrollFrame)
{
nsDisplayListBuilder builder(aFrame, PR_TRUE, PR_FALSE);
nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::EVENT_DELIVERY,
PR_FALSE);
nsDisplayList list;
nsRect target(aRect);
@ -1266,7 +1267,8 @@ nsLayoutUtils::PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFra
// *and after* we draw.
PRBool willFlushRetainedLayers = (aFlags & PAINT_HIDE_CARET) != 0;
nsDisplayListBuilder builder(aFrame, PR_FALSE, !(aFlags & PAINT_HIDE_CARET));
nsDisplayListBuilder builder(aFrame, nsDisplayListBuilder::PAINTING,
!(aFlags & PAINT_HIDE_CARET));
nsDisplayList list;
if (aFlags & PAINT_IN_TRANSFORM) {
builder.SetInTransform(PR_TRUE);

View File

@ -2520,7 +2520,8 @@ nsRootPresContext::GetPluginGeometryUpdates(nsIFrame* aChangedSubtree,
nsRect bounds;
if (bounds.IntersectRect(closure.mAffectedPluginBounds,
closure.mRootFrame->GetRect())) {
nsDisplayListBuilder builder(closure.mRootFrame, PR_FALSE, PR_FALSE);
nsDisplayListBuilder builder(closure.mRootFrame,
nsDisplayListBuilder::PLUGIN_GEOMETRY, PR_FALSE);
builder.SetAccurateVisibleRegions();
nsDisplayList list;

View File

@ -271,7 +271,7 @@ struct RangePaintInfo {
nsPoint mRootOffset;
RangePaintInfo(nsIRange* aRange, nsIFrame* aFrame)
: mRange(aRange), mBuilder(aFrame, PR_FALSE, PR_FALSE)
: mRange(aRange), mBuilder(aFrame, nsDisplayListBuilder::PAINTING, PR_FALSE)
{
MOZ_COUNT_CTOR(RangePaintInfo);
}

View File

@ -53,6 +53,8 @@ _CHROME_FILES = \
bug551434_childframe.html \
test_chrome_content_integration.xul \
chrome_content_integration_window.xul \
test_chrome_over_plugin.xul \
chrome_over_plugin_window.xul \
test_default_background.xul \
default_background_window.xul \
test_printpreview.xul \

View File

@ -0,0 +1,62 @@
<?xml version="1.0"?>
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
<window title="Content/chrome integration subwindow"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
onload="runTests()">\
<!-- We're mainly testing that a) translucent chrome elements cause the plugin to be clipped away and
b) translucent content elements do NOT cause the plugin to be clipped away -->
<stack style="height:100px; width:150px;">
<iframe type="content" style="border:none;" id="f"
src="data:text/html,&lt;embed id='e' type='application/x-test' wmode='window'
style='position:absolute;left:0;top:0;width:100px;height:100px'&gt;&lt;/embed&gt;
&lt;div style='position:absolute;left:0;top:80px;width:100px;height:10px;background:rgba(0,0,128,0.5)'&gt;&lt;/div&gt;
&lt;div style='position:absolute;left:0;top:90px;width:100px;height:10px;background:blue'&gt;&lt;/div&gt;
"/>
<vbox>
<vbox style="height:25px; background:yellow;"/> <!-- plugin should be covered here -->
<vbox style="height:25px; background:rgba(0,128,0,0.5);"/> <!-- plugin should be covered here -->
<vbox style="height:50px;"/> <!-- plugin should be visible here -->
</vbox>
</stack>
<script type="application/javascript">
<![CDATA[
var imports = [ "SimpleTest", "is", "isnot", "ok", "todo" ];
for each (var import in imports) {
window[import] = window.opener.wrappedJSObject[import];
}
var plugin;
function waitForPaint() {
if (plugin.getPaintCount() < 1) {
setTimeout(waitForPaint, 0);
return;
}
if (plugin.hasWidget()) {
is(plugin.getClipRegionRectCount(), 1, "plugin clip rect count");
var left = plugin.getEdge(0);
var top = plugin.getEdge(1);
is(plugin.getClipRegionRectEdge(0,0) - left, 0, "plugin clip rect left");
// our two vboxes with backgrounds should cause the top of the plugin to be clipped
is(plugin.getClipRegionRectEdge(0,1) - top, 50, "plugin clip rect top");
is(plugin.getClipRegionRectEdge(0,2) - left, 100, "plugin clip rect right");
// of the two content DIVs, the first one should not cause the plugin to be clipped because
// it's transparent. The second one should cause the plugin to be clipped.
is(plugin.getClipRegionRectEdge(0,3) - top, 90, "plugin clip rect bottom");
} else {
todo(false, "Test only tests windowed plugins");
}
var tester = window.SimpleTest;
window.close();
tester.finish();
}
function runTests() {
plugin = document.getElementById("f").contentDocument.getElementById("e").wrappedJSObject;
waitForPaint();
}
]]>
</script>
</window>

View File

@ -0,0 +1,25 @@
<?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/MochiKit/packed.js"></script>
<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
var w = window.open("chrome_over_plugin_window.xul", "chrome_over_plugin",
"chrome,width=200,height=300");
]]>
</script>
</window>

View File

@ -1553,7 +1553,7 @@ InvalidateFixedBackgroundFrames(nsIFrame* aRootFrame,
"The root frame shouldn't be the one that's moving, that makes no sense");
// Build the 'after' display list over the whole area of interest.
nsDisplayListBuilder builder(aRootFrame, PR_FALSE, PR_TRUE);
nsDisplayListBuilder builder(aRootFrame, nsDisplayListBuilder::OTHER, PR_TRUE);
builder.EnterPresShell(aRootFrame, aUpdateRect);
nsDisplayList list;
nsresult rv =

View File

@ -1273,6 +1273,22 @@ nsDisplayPlugin::IsOpaque(nsDisplayListBuilder* aBuilder,
*aForceTransparentSurface = PR_FALSE;
}
nsObjectFrame* f = static_cast<nsObjectFrame*>(mFrame);
if (!aBuilder->IsForPluginGeometry()) {
nsIWidget* widget = f->GetWidget();
if (widget) {
nsTArray<nsIntRect> clip;
widget->GetWindowClipRegion(&clip);
nsTArray<nsIWidget::Configuration> configuration;
GetWidgetConfiguration(aBuilder, &configuration);
NS_ASSERTION(configuration.Length() == 1, "No configuration found");
if (clip != configuration[0].mClipRegion) {
// Something has clipped us unexpectedly. Perhaps there is a translucent
// chrome element overlaying us that forced us to be clipped away. Treat
// us as non-opaque since we may have holes.
return PR_FALSE;
}
}
}
return f->IsOpaque();
}

View File

@ -313,6 +313,12 @@ class nsTArray : public nsTArray_base {
return true;
}
// Return true if this array does not have the same length and the same
// elements as |other|.
bool operator!=(const self_type& other) const {
return !operator==(other);
}
//
// Accessor methods
//