Bug 508495. Let CSS borders and padding apply to plugin elements, and fix layout, painting and event handling to work with them. r=dbaron

--HG--
extra : rebase_source : c42240c5a88dc05579b658f8cc29ff289422986b
This commit is contained in:
Robert O'Callahan 2009-09-11 13:44:20 +12:00
parent d56dfa741e
commit 0661a95762
11 changed files with 138 additions and 38 deletions

View File

@ -39,7 +39,7 @@
#define NSMARGIN_H
#include "nsCoord.h"
#include "nsMargin.h"
#include "nsPoint.h"
#include "gfxCore.h"
struct nsMargin {
@ -62,6 +62,8 @@ struct nsMargin {
nscoord LeftRight() const { return left + right; }
nscoord TopBottom() const { return top + bottom; }
nsPoint TopLeft() const { return nsPoint(left, top); }
#if (NS_SIDE_TOP == 0) && (NS_SIDE_RIGHT == 1) && (NS_SIDE_BOTTOM == 2) && (NS_SIDE_LEFT == 3)
nscoord& side(PRUint8 aSide) {
NS_PRECONDITION(aSide <= NS_SIDE_LEFT, "Out of range side");
@ -120,6 +122,12 @@ struct nsIntMargin {
void SizeTo(PRInt32 aLeft, PRInt32 aTop,
PRInt32 aRight, PRInt32 aBottom) {left = aLeft; top = aTop;
right = aRight; bottom = aBottom;}
PRInt32 LeftRight() const { return left + right; }
PRInt32 TopBottom() const { return top + bottom; }
nsPoint TopLeft() const { return nsPoint(left, top); }
PRInt32& side(PRUint8 aSide) {
NS_PRECONDITION(aSide <= NS_SIDE_LEFT, "Out of range side");
return *(&top + aSide);
@ -129,8 +137,6 @@ struct nsIntMargin {
NS_PRECONDITION(aSide <= NS_SIDE_LEFT, "Out of range side");
return *(&top + aSide);
}
PRInt32 LeftRight() const { return left + right; }
PRInt32 TopBottom() const { return top + bottom; }
PRBool operator!=(const nsIntMargin& aMargin) const {
return (PRBool) ((left != aMargin.left) || (top != aMargin.top) ||

View File

@ -619,7 +619,7 @@ nsObjectFrame::Destroy()
// StopPluginInternal might have disowned the widget; if it has,
// mWidget will be null.
if (mWidget) {
GetView()->DetachWidgetEventHandler(mWidget);
mInnerView->DetachWidgetEventHandler(mWidget);
mWidget->Destroy();
}
@ -701,6 +701,13 @@ nsObjectFrame::CreateWidget(nscoord aWidth,
viewMan->MoveViewTo(view, origin.x, origin.y);
if (!aViewOnly && !mWidget && usewidgets) {
mInnerView = viewMan->CreateView(GetContentRect() - GetPosition(), view);
if (!mInnerView) {
NS_ERROR("Could not create inner view");
return NS_ERROR_OUT_OF_MEMORY;
}
viewMan->InsertChild(view, mInnerView, nsnull, PR_TRUE);
nsresult rv;
mWidget = do_CreateInstance(kWidgetCID, &rv);
if (NS_FAILED(rv))
@ -720,7 +727,7 @@ nsObjectFrame::CreateWidget(nscoord aWidth,
// Mac where events to the plugin are routed through Gecko. So we
// allow the view to attach its event handler to mWidget even though
// mWidget isn't the view's designated widget.
EVENT_CALLBACK eventHandler = view->AttachWidgetEventHandler(mWidget);
EVENT_CALLBACK eventHandler = mInnerView->AttachWidgetEventHandler(mWidget);
mWidget->Create(parentWidget, nsnull, nsIntRect(0,0,0,0),
eventHandler, dx, nsnull, nsnull, &initData);
@ -872,7 +879,16 @@ nsObjectFrame::Reflow(nsPresContext* aPresContext,
return NS_OK;
}
FixupWindow(nsSize(aMetrics.width, aMetrics.height));
nsRect r(0, 0, aMetrics.width, aMetrics.height);
r.Deflate(aReflowState.mComputedBorderPadding);
if (mInnerView) {
nsIViewManager* vm = mInnerView->GetViewManager();
vm->MoveViewTo(mInnerView, r.x, r.y);
vm->ResizeView(mInnerView, nsRect(nsPoint(0, 0), r.Size()), PR_TRUE);
}
FixupWindow(r.Size());
aStatus = NS_FRAME_COMPLETE;
@ -1060,6 +1076,9 @@ nsIntPoint nsObjectFrame::GetWindowOriginInPixels(PRBool aWindowless)
parentWithView->GetNearestWidget(&offsetToWidget);
origin += offsetToWidget;
}
// It's OK to use GetUsedBorderAndPadding here (and below) since
// GetSkipSides always returns 0; we don't split nsObjectFrames
origin += GetUsedBorderAndPadding().TopLeft();
return nsIntPoint(PresContext()->AppUnitsToDevPixels(origin.x),
PresContext()->AppUnitsToDevPixels(origin.y));
@ -1103,8 +1122,9 @@ nsObjectFrame::DidReflow(nsPresContext* aPresContext,
nsObjectFrame::PaintPrintPlugin(nsIFrame* aFrame, nsIRenderingContext* aCtx,
const nsRect& aDirtyRect, nsPoint aPt)
{
nsPoint pt = aPt + aFrame->GetUsedBorderAndPadding().TopLeft();
nsIRenderingContext::AutoPushTranslation translate(aCtx, pt.x, pt.y);
// FIXME - Bug 385435: Doesn't aDirtyRect need translating too?
nsIRenderingContext::AutoPushTranslation translate(aCtx, aPt.x, aPt.y);
static_cast<nsObjectFrame*>(aFrame)->PrintPlugin(*aCtx, aDirtyRect);
}
@ -1120,7 +1140,7 @@ nsDisplayPlugin::Paint(nsDisplayListBuilder* aBuilder,
nsIRenderingContext* aCtx)
{
nsObjectFrame* f = static_cast<nsObjectFrame*>(mFrame);
f->PaintPlugin(*aCtx, mVisibleRect, GetBounds(aBuilder).TopLeft());
f->PaintPlugin(*aCtx, mVisibleRect, GetBounds(aBuilder));
}
PRBool
@ -1143,8 +1163,9 @@ nsDisplayPlugin::GetWidgetConfiguration(nsDisplayListBuilder* aBuilder,
nsTArray<nsIWidget::Configuration>* aConfigurations)
{
nsObjectFrame* f = static_cast<nsObjectFrame*>(mFrame);
f->ComputeWidgetGeometry(mVisibleRegion, aBuilder->ToReferenceFrame(mFrame),
aConfigurations);
nsPoint pluginOrigin = mFrame->GetUsedBorderAndPadding().TopLeft() +
aBuilder->ToReferenceFrame(mFrame);
f->ComputeWidgetGeometry(mVisibleRegion, pluginOrigin, aConfigurations);
}
void
@ -1536,7 +1557,7 @@ nsObjectFrame::PrintPlugin(nsIRenderingContext& aRenderingContext,
void
nsObjectFrame::PaintPlugin(nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect, const nsPoint& aFramePt)
const nsRect& aDirtyRect, const nsRect& aPluginRect)
{
// Screen painting code
#if defined(XP_MACOSX)
@ -1546,8 +1567,7 @@ nsObjectFrame::PaintPlugin(nsIRenderingContext& aRenderingContext,
PRInt32 appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
// Clip to the content area where the plugin should be drawn. If
// we don't do this, the plugin can draw outside its bounds.
nsRect content = GetContentRect() - GetPosition() + aFramePt;
nsIntRect contentPixels = content.ToNearestPixels(appUnitsPerDevPixel);
nsIntRect contentPixels = aPluginRect.ToNearestPixels(appUnitsPerDevPixel);
nsIntRect dirtyPixels = aDirtyRect.ToOutsidePixels(appUnitsPerDevPixel);
nsIntRect clipPixels;
clipPixels.IntersectRect(contentPixels, dirtyPixels);
@ -1618,7 +1638,7 @@ nsObjectFrame::PaintPlugin(nsIRenderingContext& aRenderingContext,
} else {
// FIXME - Bug 385435: Doesn't aDirtyRect need translating too?
nsIRenderingContext::AutoPushTranslation
translate(&aRenderingContext, aFramePt.x, aFramePt.y);
translate(&aRenderingContext, aPluginRect.x, aPluginRect.y);
// this rect is used only in the CoreGraphics drawing model
gfxRect tmpRect(0, 0, 0, 0);
@ -1635,7 +1655,7 @@ nsObjectFrame::PaintPlugin(nsIRenderingContext& aRenderingContext,
if (window->type == NPWindowTypeDrawable) {
#endif
gfxRect frameGfxRect =
PresContext()->AppUnitsToGfxUnits(nsRect(aFramePt, GetSize()));
PresContext()->AppUnitsToGfxUnits(aPluginRect);
gfxRect dirtyGfxRect =
PresContext()->AppUnitsToGfxUnits(aDirtyRect);
gfxContext* ctx = aRenderingContext.ThebesContext();
@ -1648,7 +1668,7 @@ nsObjectFrame::PaintPlugin(nsIRenderingContext& aRenderingContext,
GetPluginInstance(*getter_AddRefs(inst));
if (inst) {
gfxRect frameGfxRect =
PresContext()->AppUnitsToGfxUnits(nsRect(aFramePt, GetSize()));
PresContext()->AppUnitsToGfxUnits(aPluginRect);
gfxRect dirtyGfxRect =
PresContext()->AppUnitsToGfxUnits(aDirtyRect);
gfxContext *ctx = aRenderingContext.ThebesContext();
@ -1781,7 +1801,7 @@ nsObjectFrame::PaintPlugin(nsIRenderingContext& aRenderingContext,
if (window->type == NPWindowTypeDrawable) {
// FIXME - Bug 385435: Doesn't aDirtyRect need translating too?
nsIRenderingContext::AutoPushTranslation
translate(&aRenderingContext, aFramePt.x, aFramePt.y);
translate(&aRenderingContext, aPluginRect.x, aPluginRect.y);
// check if we need to call SetWindow with updated parameters
PRBool doupdatewindow = PR_FALSE;
@ -1977,7 +1997,7 @@ nsObjectFrame::Instantiate(nsIChannel* aChannel, nsIStreamListener** aStreamList
mInstanceOwner->SetPluginHost(pluginHost);
// This must be done before instantiating the plugin
FixupWindow(mRect.Size());
FixupWindow(GetContentRect().Size());
nsWeakFrame weakFrame(this);
@ -2017,7 +2037,7 @@ nsObjectFrame::Instantiate(const char* aMimeType, nsIURI* aURI)
nsWeakFrame weakFrame(this);
// This must be done before instantiating the plugin
FixupWindow(mRect.Size());
FixupWindow(GetContentRect().Size());
// get the nsIPluginHost service
nsCOMPtr<nsIPluginHost> pluginHost(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID, &rv));
@ -3913,7 +3933,8 @@ nsEventStatus nsPluginInstanceOwner::ProcessEventX11Composited(const nsGUIEvent&
// Get reference point relative to plugin origin.
const nsPresContext* presContext = mOwner->PresContext();
nsPoint appPoint =
nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mOwner);
nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mOwner) -
mOwner->GetUsedBorderAndPadding().TopLeft();
nsIntPoint pluginPoint(presContext->AppUnitsToDevPixels(appPoint.x),
presContext->AppUnitsToDevPixels(appPoint.y));
mLastPoint = pluginPoint;
@ -4162,7 +4183,8 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent)
void* event = anEvent.nativeMsg;
if (!event) {
nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mOwner);
nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mOwner)
- mOwner->GetUsedBorderAndPadding().TopLeft();
nsPresContext* presContext = mOwner->PresContext();
nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x),
presContext->AppUnitsToDevPixels(pt.y));
@ -4323,7 +4345,9 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent)
anEvent.message == NS_MOUSE_EXIT_SYNTH ||
anEvent.message == NS_MOUSE_MOVE,
"Incorrect event type for coordinate translation");
nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mOwner);
nsPoint pt =
nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mOwner) -
mOwner->GetUsedBorderAndPadding().TopLeft();
nsPresContext* presContext = mOwner->PresContext();
nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x),
presContext->AppUnitsToDevPixels(pt.y));
@ -4377,7 +4401,8 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent)
// Get reference point relative to plugin origin.
const nsPresContext* presContext = mOwner->PresContext();
nsPoint appPoint =
nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mOwner);
nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mOwner) -
mOwner->GetUsedBorderAndPadding().TopLeft();
nsIntPoint pluginPoint(presContext->AppUnitsToDevPixels(appPoint.x),
presContext->AppUnitsToDevPixels(appPoint.y));
const nsMouseEvent& mouseEvent =

View File

@ -195,13 +195,10 @@ protected:
static void PaintPrintPlugin(nsIFrame* aFrame,
nsIRenderingContext* aRenderingContext,
const nsRect& aDirtyRect, nsPoint aPt);
static void PaintPlugin(nsIFrame* aFrame,
nsIRenderingContext* aRenderingContext,
const nsRect& aDirtyRect, nsPoint aPt);
void PrintPlugin(nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
void PaintPlugin(nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect, const nsPoint& aFramePt);
const nsRect& aDirtyRect, const nsRect& aPluginRect);
/**
* Makes sure that mInstanceOwner is valid and without a current plugin
@ -212,9 +209,9 @@ protected:
/**
* Get the widget geometry for the plugin. aRegion is in some appunits
* coordinate system whose origin is device-pixel-aligned (if possible),
* and aPluginOrigin gives the top-left of the plugin in that coordinate
* system. It doesn't matter what that coordinate system actually is,
* as long as aRegion and aPluginOrigin are consistent.
* and aPluginOrigin gives the top-left of the plugin frame's content-rect
* in that coordinate system. It doesn't matter what that coordinate
* system actually is, as long as aRegion and aPluginOrigin are consistent.
* This will append a Configuration object to aConfigurations
* containing the widget, its desired position, size and clip region.
*/
@ -229,6 +226,7 @@ protected:
private:
nsRefPtr<nsPluginInstanceOwner> mInstanceOwner;
nsIView* mInnerView;
nsCOMPtr<nsIWidget> mWidget;
nsIntRect mWindowlessRect;

View File

@ -15,9 +15,14 @@
<p id="display"></p>
<div id="content" style="display: block">
<embed id="p1" type="application/x-test"
style="position:absolute; left:300px; top:10px;"></embed></div>
style="position:absolute; left:300px; top:10px;"></embed>
<iframe id="f1" style="position:absolute; left:0; top:250px;"
src="data:text/html,&lt;embed id='p2' type='application/x-test' style='position:absolute; left:10px; top:10px'&gt;"></iframe>
<embed id="p3" type="application/x-test"
style="position:absolute; left:320px; top:250px;
outline:5px solid blue;
border:solid black; border-width:4px 8px 4px 8px;
padding:3px 1px;"></embed>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
@ -46,6 +51,19 @@ function doTest() {
is(p2.getLastMouseX(), 15, "p2 mouse up X");
is(p2.getLastMouseY(), 28, "p2 mouse up Y");
var p3 = document.getElementById("p3");
// The synthesized coordinates are relative to the border-box, but the plugin
// is at offset (9,7) relative to the border-box
synthesizeMouse(p3, 15, 18, { type:"mousedown" });
is(p3.getLastMouseX(), 6, "p3 mouse down X");
is(p3.getLastMouseY(), 11, "p3 mouse down Y");
synthesizeMouse(p3, 15, 38, { type:"mousemove" });
is(p3.getLastMouseX(), 6, "p3 mouse move X");
is(p3.getLastMouseY(), 31, "p3 mouse move Y");
synthesizeMouse(p3, 15, 28, { type:"mouseup" });
is(p3.getLastMouseX(), 6, "p3 mouse up X");
is(p3.getLastMouseY(), 21, "p3 mouse up Y");
SimpleTest.finish();
}
// Need to run 'doTest' after painting is unsuppressed, or we'll set clip

View File

@ -14,13 +14,18 @@
</hbox>
<embed id="p" type="application/x-test" width="200" height="200" wmode="window"></embed>
<embed id="p2" type="application/x-test" wmode="window"
style="outline:5px solid blue; width:200px; height:200px;
border:solid black; border-width:4px 8px 4px 8px;
padding:3px 1px;">
</embed>
<script class="testbody" type="application/javascript">
<![CDATA[
var windowFrameX, windowFrameY;
function checkGeometry(id) {
function checkGeometry(id, x, y, w, h) {
var p = document.getElementById(id);
var bounds = p.getBoundingClientRect();
var pX = p.getEdge(0);
@ -28,10 +33,10 @@ function checkGeometry(id) {
var pWidth = p.getEdge(2) - pX;
var pHeight = p.getEdge(3) - pY;
is(pX, windowFrameX + bounds.left, id + " plugin X");
is(pY, windowFrameY + bounds.top, id + " plugin Y");
is(pWidth, bounds.width, id + " plugin width");
is(pHeight, bounds.height, id + " plugin height");
is(pX, windowFrameX + bounds.left + x, id + " plugin X");
is(pY, windowFrameY + bounds.top + y, id + " plugin Y");
is(pWidth, w, id + " plugin width");
is(pHeight, h, id + " plugin height");
}
function runTests() {
@ -55,7 +60,9 @@ function runTests() {
windowFrameX = h1.boxObject.screenX - bounds.left - window.screenX;
windowFrameY = h1.boxObject.screenY - bounds.top - window.screenY;
checkGeometry("p");
checkGeometry("p", 0, 0, 200, 200);
// This one tests widget positioning in the presence of borders and padding
checkGeometry("p2", 9, 7, 182, 186);
SimpleTest.finish();
}

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<body style="margin:0">
<div style="width:184px; height:192px; margin:90px 80px; outline:5px dashed blue;
border:dotted black; border-width:4px 8px 4px 8px;
background:cyan;">
<div style="margin:3px 1px; height:186px; background:lime;"></div>
</div>
</body>
</html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body style="margin:0">
<object type="application/x-test" drawmode="solid" color="ff00ff00"
style="width:200px; height:200px; display:block; margin:90px 80px;
outline:5px dashed blue;
background:cyan;
border:dotted black; border-width:4px 8px 4px 8px;
padding:3px 1px;">
</object>
</body>
</html>

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<body style="margin:0">
<div style="width:184px; height:192px; margin:90px 80px; outline:5px dashed blue;
border:dotted black; border-width:4px 8px 4px 8px;
background:cyan;">
<object style="margin:3px 1px; height:186px; width:182px; display:block;" type="application/x-test"></object>
</div>
</body>
</html>

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<body style="margin:0">
<object type="application/x-test"
style="width:200px; height:200px; display:block; margin:90px 80px;
outline:5px dashed blue;
background:cyan;
border:dotted black; border-width:4px 8px 4px 8px;
padding:3px 1px;">
</object>
</body>
</html>

View File

@ -3,3 +3,5 @@ random-if(!haveTestPlugin) != plugin-sanity.html about:blank
fails-if(!haveTestPlugin) == plugin-sanity.html div-sanity.html
fails-if(!haveTestPlugin) == plugin-alpha-zindex.html div-alpha-zindex.html
fails-if(!haveTestPlugin) == windowless-clipping-1.html windowless-clipping-1-ref.html
fails-if(!haveTestPlugin) == border-padding-1.html border-padding-1-ref.html
fails-if(!haveTestPlugin) == border-padding-2.html border-padding-2-ref.html

View File

@ -273,10 +273,10 @@ nsViewManager::CreateView(const nsRect& aBounds,
{
nsView *v = new nsView(this, aVisibilityFlag);
if (v) {
v->SetParent(static_cast<nsView*>(const_cast<nsIView*>(aParent)));
v->SetPosition(aBounds.x, aBounds.y);
nsRect dim(0, 0, aBounds.width, aBounds.height);
v->SetDimensions(dim, PR_FALSE);
v->SetParent(static_cast<nsView*>(const_cast<nsIView*>(aParent)));
}
return v;
}