Bug 581222. Draw outer box-shadows for native-themed elements using the alpha mask of the theme background as the border-shape, and disable inner box-shadows. r=dbaron

--HG--
extra : rebase_source : a9b81a5d3e09cca41a8b479a4b62b3e2df6c4354
This commit is contained in:
Robert O'Callahan 2010-08-23 21:30:08 +12:00
Родитель 5de92d7b23
Коммит cac5ffc3c7
12 изменённых файлов: 153 добавлений и 105 удалений

Просмотреть файл

@ -1176,31 +1176,61 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
const nsRect& aFrameArea,
const nsRect& aDirtyRect)
{
nsCSSShadowArray* shadows = aForFrame->GetStyleBorder()->mBoxShadow;
const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder();
nsCSSShadowArray* shadows = styleBorder->mBoxShadow;
if (!shadows)
return;
const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder();
PRIntn sidesToSkip = aForFrame->GetSkipSides();
// Get any border radius, since box-shadow must also have rounded corners if the frame does
nscoord twipsRadii[8];
PRBool hasBorderRadius = GetBorderRadiusTwips(styleBorder->mBorderRadius,
aFrameArea.width, twipsRadii);
nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
PRBool hasBorderRadius;
PRBool nativeTheme; // mutually exclusive with hasBorderRadius
gfxCornerSizes borderRadii;
ComputePixelRadii(twipsRadii, aFrameArea, sidesToSkip,
twipsPerPixel, &borderRadii);
gfxRect frameGfxRect = RectToGfxRect(aFrameArea, twipsPerPixel);
// Get any border radius, since box-shadow must also have rounded corners if the frame does
const nsStyleDisplay* styleDisplay = aForFrame->GetStyleDisplay();
nsITheme::Transparency transparency;
if (aForFrame->IsThemed(styleDisplay, &transparency)) {
// We don't respect border-radius for native-themed widgets
hasBorderRadius = PR_FALSE;
// For opaque (rectangular) theme widgets we can take the generic
// border-box path with border-radius disabled.
nativeTheme = transparency != nsITheme::eOpaque;
} else {
nativeTheme = PR_FALSE;
nscoord twipsRadii[8];
hasBorderRadius =
GetBorderRadiusTwips(styleBorder->mBorderRadius,
aFrameArea.width, twipsRadii);
if (hasBorderRadius) {
PRIntn sidesToSkip = aForFrame->GetSkipSides();
ComputePixelRadii(twipsRadii, aFrameArea, sidesToSkip, twipsPerPixel,
&borderRadii);
}
}
nsRect frameRect =
nativeTheme ? aForFrame->GetOverflowRectRelativeToSelf() + aFrameArea.TopLeft() : aFrameArea;
gfxRect frameGfxRect = RectToGfxRect(frameRect, twipsPerPixel);
frameGfxRect.Round();
// We don't show anything that intersects with the frame we're blurring on. So tell the
// blurrer not to do unnecessary work there.
gfxRect skipGfxRect = frameGfxRect;
if (hasBorderRadius) {
skipGfxRect.Inset(PR_MAX(borderRadii[C_TL].height, borderRadii[C_TR].height), 0,
PR_MAX(borderRadii[C_BL].height, borderRadii[C_BR].height), 0);
PRBool useSkipGfxRect = PR_TRUE;
if (nativeTheme) {
// Optimize non-leaf native-themed frames by skipping computing pixels
// in the padding-box. We assume the padding-box is going to be painted
// opaquely for non-leaf frames.
// XXX this may not be a safe assumption; we should make this go away
// by optimizing box-shadow drawing more for the cases where we don't have a skip-rect.
useSkipGfxRect = !aForFrame->IsLeaf();
nsRect paddingRect =
aForFrame->GetPaddingRect() - aForFrame->GetPosition() + aFrameArea.TopLeft();
skipGfxRect = RectToGfxRect(paddingRect, twipsPerPixel);
} else if (hasBorderRadius) {
skipGfxRect.Inset(
PR_MAX(borderRadii[C_TL].height, borderRadii[C_TR].height), 0,
PR_MAX(borderRadii[C_BL].height, borderRadii[C_BR].height), 0);
}
for (PRUint32 i = shadows->Length(); i > 0; --i) {
@ -1208,9 +1238,15 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
if (shadowItem->mInset)
continue;
nsRect shadowRect = aFrameArea;
nsRect shadowRect = frameRect;
shadowRect.MoveBy(shadowItem->mXOffset, shadowItem->mYOffset);
shadowRect.Inflate(shadowItem->mSpread, shadowItem->mSpread);
nscoord pixelSpreadRadius;
if (nativeTheme) {
pixelSpreadRadius = shadowItem->mSpread;
} else {
shadowRect.Inflate(shadowItem->mSpread, shadowItem->mSpread);
pixelSpreadRadius = 0;
}
// shadowRect won't include the blur, so make an extra rect here that includes the blur
// for use in the even-odd rule below.
@ -1227,8 +1263,15 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
nsRefPtr<gfxContext> shadowContext;
nsContextBoxBlur blurringArea;
shadowContext = blurringArea.Init(shadowRect, 0, blurRadius, twipsPerPixel, renderContext,
aDirtyRect, &skipGfxRect);
// When getting the widget shape from the native theme, we're going
// to draw the widget into the shadow surface to create a mask.
// We need to ensure that there actually *is* a shadow surface
// and that we're not going to draw directly into renderContext.
shadowContext =
blurringArea.Init(shadowRect, pixelSpreadRadius,
blurRadius, twipsPerPixel, renderContext, aDirtyRect,
useSkipGfxRect ? &skipGfxRect : nsnull,
nativeTheme ? nsContextBoxBlur::FORCE_MASK : 0);
if (!shadowContext)
continue;
@ -1242,55 +1285,72 @@ nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
renderContext->Save();
renderContext->SetColor(gfxRGBA(shadowColor));
// Clip out the area of the actual frame so the shadow is not shown within
// the frame
renderContext->NewPath();
renderContext->Rectangle(shadowGfxRectPlusBlur);
if (hasBorderRadius)
renderContext->RoundedRectangle(frameGfxRect, borderRadii);
else
renderContext->Rectangle(frameGfxRect);
renderContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
renderContext->Clip();
// Draw the shape of the frame so it can be blurred. Recall how nsContextBoxBlur
// doesn't make any temporary surfaces if blur is 0 and it just returns the original
// surface? If we have no blur, we're painting this fill on the actual content surface
// (renderContext == shadowContext) which is why we set up the color and clip
// before doing this.
shadowContext->NewPath();
if (hasBorderRadius) {
gfxCornerSizes clipRectRadii;
gfxFloat spreadDistance = -shadowItem->mSpread / twipsPerPixel;
gfxFloat borderSizes[4] = {0, 0, 0, 0};
if (nativeTheme) {
// We don't clip the border-box from the shadow, nor any other box.
// We assume that the native theme is going to paint over the shadow.
// We only give the spread radius to corners with a radius on them, otherwise we'll
// give a rounded shadow corner to a frame corner with 0 border radius, should
// the author use non-uniform border radii sizes (-moz-border-radius-topleft etc)
// (bug 514670)
if (borderRadii[C_TL].width > 0 || borderRadii[C_BL].width > 0) {
borderSizes[NS_SIDE_LEFT] = spreadDistance;
}
if (borderRadii[C_TL].height > 0 || borderRadii[C_TR].height > 0) {
borderSizes[NS_SIDE_TOP] = spreadDistance;
}
if (borderRadii[C_TR].width > 0 || borderRadii[C_BR].width > 0) {
borderSizes[NS_SIDE_RIGHT] = spreadDistance;
}
if (borderRadii[C_BL].height > 0 || borderRadii[C_BR].height > 0) {
borderSizes[NS_SIDE_BOTTOM] = spreadDistance;
}
nsCSSBorderRenderer::ComputeInnerRadii(borderRadii, borderSizes,
&clipRectRadii);
shadowContext->RoundedRectangle(shadowGfxRect, clipRectRadii);
// Draw the widget shape
gfxContextMatrixAutoSaveRestore save(shadowContext);
nsIDeviceContext* devCtx = aPresContext->DeviceContext();
nsCOMPtr<nsIRenderingContext> wrapperCtx;
devCtx->CreateRenderingContextInstance(*getter_AddRefs(wrapperCtx));
wrapperCtx->Init(devCtx, shadowContext);
wrapperCtx->Translate(shadowItem->mXOffset, shadowItem->mYOffset);
aPresContext->GetTheme()->DrawWidgetBackground(wrapperCtx, aForFrame,
styleDisplay->mAppearance, aFrameArea, frameRect);
} else {
shadowContext->Rectangle(shadowGfxRect);
// Clip out the area of the actual frame so the shadow is not shown within
// the frame
renderContext->NewPath();
renderContext->Rectangle(shadowGfxRectPlusBlur);
if (hasBorderRadius) {
renderContext->RoundedRectangle(frameGfxRect, borderRadii);
} else {
renderContext->Rectangle(frameGfxRect);
}
renderContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
renderContext->Clip();
shadowContext->NewPath();
if (hasBorderRadius) {
gfxCornerSizes clipRectRadii;
gfxFloat spreadDistance = -shadowItem->mSpread / twipsPerPixel;
gfxFloat borderSizes[4] = { 0, 0, 0, 0 };
// We only give the spread radius to corners with a radius on them, otherwise we'll
// give a rounded shadow corner to a frame corner with 0 border radius, should
// the author use non-uniform border radii sizes (-moz-border-radius-topleft etc)
// (bug 514670)
if (borderRadii[C_TL].width > 0 || borderRadii[C_BL].width > 0) {
borderSizes[NS_SIDE_LEFT] = spreadDistance;
}
if (borderRadii[C_TL].height > 0 || borderRadii[C_TR].height > 0) {
borderSizes[NS_SIDE_TOP] = spreadDistance;
}
if (borderRadii[C_TR].width > 0 || borderRadii[C_BR].width > 0) {
borderSizes[NS_SIDE_RIGHT] = spreadDistance;
}
if (borderRadii[C_BL].height > 0 || borderRadii[C_BR].height > 0) {
borderSizes[NS_SIDE_BOTTOM] = spreadDistance;
}
nsCSSBorderRenderer::ComputeInnerRadii(borderRadii, borderSizes,
&clipRectRadii);
shadowContext->RoundedRectangle(shadowGfxRect, clipRectRadii);
} else {
shadowContext->Rectangle(shadowGfxRect);
}
shadowContext->Fill();
}
shadowContext->Fill();
blurringArea.DoPaint();
renderContext->Restore();
@ -1304,10 +1364,18 @@ nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext,
const nsRect& aFrameArea,
const nsRect& aDirtyRect)
{
nsCSSShadowArray* shadows = aForFrame->GetStyleBorder()->mBoxShadow;
const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder();
nsCSSShadowArray* shadows = styleBorder->mBoxShadow;
if (!shadows)
return;
const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder();
if (aForFrame->IsThemed() && aForFrame->GetContent() &&
!nsContentUtils::IsChromeDoc(aForFrame->GetContent()->GetCurrentDoc())) {
// There's no way of getting hold of a shape corresponding to a
// "padding-box" for native-themed widgets, so just don't draw
// inner box-shadows for them. But we allow chrome to paint inner
// box shadows since chrome can be aware of the platform theme.
return;
}
// Get any border radius, since box-shadow must also have rounded corners if the frame does
nscoord twipsRadii[8];

Просмотреть файл

@ -0,0 +1 @@
<button>Hello</button>

Просмотреть файл

@ -0,0 +1 @@
<button style="-moz-box-shadow: 10px 10px gold;">Hello</button>

Просмотреть файл

@ -0,0 +1,4 @@
<select size="1">
<option>Hello</option>
<option>Kitty</option>
</select>

Просмотреть файл

@ -0,0 +1,4 @@
<select size="1" style="-moz-box-shadow: 10px 10px gold;">
<option>Hello</option>
<option>Kitty</option>
</select>

Просмотреть файл

@ -0,0 +1 @@
<input>

Просмотреть файл

@ -0,0 +1 @@
<input style="-moz-box-shadow: 10px 10px gold;">

Просмотреть файл

@ -0,0 +1,4 @@
<select size="2">
<option>Hello</option>
<option>Kitty</option>
</select>

Просмотреть файл

@ -0,0 +1,4 @@
<select size="2" style="-moz-box-shadow: 10px 10px gold;">
<option>Hello</option>
<option>Kitty</option>
</select>

Просмотреть файл

@ -1,18 +0,0 @@
<!DOCTYPE HTML>
<html>
<body>
<p><div style="-moz-appearance:button; width:100px; height:20px;"></div>
<p><input>
<p><input type=submit value=submit>
<p><input type=text value=text >
<p><input type=password value=password>
<p><input type=reset value=reset>
<p><input type=button value=button>
<p><button> &lt;button> </button>
<p><button type=button> &lt;button type=button> </button>
<p><button type=submit> &lt;button type=submit> </button>
<p><button type=reset> &lt;button type=reset> </button>
<p><select size="3"><option>a</option><option>b</option></select>
<p><select size="1"><option>a</option><option>b</option></select>
</body>
</html>

Просмотреть файл

@ -1,25 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<style>
input, button, select, div {
-moz-box-shadow: 10px 10px gold, 10px 1000px blue;
}
</style>
</head>
<body>
<p><div style="-moz-appearance:button; width:100px; height:20px;"></div>
<p><input>
<p><input type=submit value=submit>
<p><input type=text value=text >
<p><input type=password value=password>
<p><input type=reset value=reset>
<p><input type=button value=button>
<p><button> &lt;button> </button>
<p><button type=button> &lt;button type=button> </button>
<p><button type=submit> &lt;button type=submit> </button>
<p><button type=reset> &lt;button type=reset> </button>
<p><select size="3"><option>a</option><option>b</option></select>
<p><select size="1"><option>a</option><option>b</option></select>
</body>
</html>

Просмотреть файл

@ -41,7 +41,10 @@ skip-if(!winWidget) != 403458-winmenu-ltr.xul 403458-winmenu-rtl.xul
== 492155-3.html about:blank
!= 492155-4.html about:blank
fails-if(!nativeThemePref) == box-shadow-styling.html box-shadow-styling-ref.html
!= box-shadow-input.html box-shadow-input-ref.html
!= box-shadow-button.html box-shadow-button-ref.html
!= box-shadow-listbox.html box-shadow-listbox-ref.html
!= box-shadow-combobox.html box-shadow-combobox-ref.html
# RTL mirroring tests
== checkbox-not-mirrored-when-rtl.html checkbox-not-mirrored-when-rtl-ref.html