Bug 476738. Implement 'inset' box-shadows. r+sr=roc, r+a191=dbaron
authorMichael Ventnor <ventnor.bugzilla@gmail.com>
Tue, 07 Apr 2009 13:39:50 +0200
changeset 24246 cfea33167281
parent 24245 9d06e2be1e19
child 24247 33ce0269a939
push id1099
push usermstange@themasta.com
push date2009-04-07 11:40 +0000
bugs476738
milestone1.9.1b4pre
Bug 476738. Implement 'inset' box-shadows. r+sr=roc, r+a191=dbaron
layout/base/nsCSSRendering.cpp
layout/base/nsCSSRendering.h
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsStyleConsts.h
layout/forms/nsButtonFrameRenderer.cpp
layout/forms/nsFieldSetFrame.cpp
layout/forms/nsFileControlFrame.cpp
layout/forms/nsHTMLButtonControlFrame.cpp
layout/generic/nsFrame.cpp
layout/reftests/box-shadow/boxshadow-inner-basic-ref.html
layout/reftests/box-shadow/boxshadow-inner-basic.html
layout/reftests/box-shadow/boxshadow-mixed-ref.html
layout/reftests/box-shadow/boxshadow-mixed.html
layout/reftests/box-shadow/reftest.list
layout/style/nsCSSParser.cpp
layout/style/nsCSSPropList.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsRuleNode.cpp
layout/style/nsRuleNode.h
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
layout/tables/nsTableCellFrame.cpp
layout/tables/nsTableFrame.cpp
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -1047,47 +1047,48 @@ GetBorderRadiusTwips(const nsStyleCorner
 
     if (aTwipsRadii[i])
       result = PR_TRUE;
   }
   return result;
 }
 
 void
-nsCSSRendering::PaintBoxShadow(nsPresContext* aPresContext,
-                               nsIRenderingContext& aRenderingContext,
-                               nsIFrame* aForFrame,
-                               const nsPoint& aForFramePt,
-                               const nsRect& aDirtyRect)
+nsCSSRendering::PaintBoxShadowOuter(nsPresContext* aPresContext,
+                                    nsIRenderingContext& aRenderingContext,
+                                    nsIFrame* aForFrame,
+                                    const nsRect& aFrameArea,
+                                    const nsRect& aDirtyRect)
 {
   const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder();
   if (!styleBorder->mBoxShadow)
     return;
 
-  nsMargin borderValues = styleBorder->GetActualBorder();
   PRIntn sidesToSkip = aForFrame->GetSkipSides();
-  nsRect frameRect = nsRect(aForFramePt, aForFrame->GetSize());
 
   // Get any border radius, since box-shadow must also have rounded corners if the frame does
   nscoord twipsRadii[8];
   PRBool hasBorderRadius = GetBorderRadiusTwips(styleBorder->mBorderRadius,
-                                                frameRect.width, twipsRadii);
+                                                aFrameArea.width, twipsRadii);
   nscoord twipsPerPixel = aPresContext->DevPixelsToAppUnits(1);
 
   gfxCornerSizes borderRadii;
-  ComputePixelRadii(twipsRadii, frameRect, sidesToSkip,
+  ComputePixelRadii(twipsRadii, aFrameArea, sidesToSkip,
                     twipsPerPixel, &borderRadii);
 
-  gfxRect frameGfxRect = RectToGfxRect(frameRect, twipsPerPixel);
+  gfxRect frameGfxRect = RectToGfxRect(aFrameArea, twipsPerPixel);
   frameGfxRect.Round();
   gfxRect dirtyGfxRect = RectToGfxRect(aDirtyRect, twipsPerPixel);
 
   for (PRUint32 i = styleBorder->mBoxShadow->Length(); i > 0; --i) {
     nsCSSShadowItem* shadowItem = styleBorder->mBoxShadow->ShadowAt(i - 1);
-    gfxRect shadowRect(frameRect.x, frameRect.y, frameRect.width, frameRect.height);
+    if (shadowItem->mInset)
+      continue;
+
+    gfxRect shadowRect(aFrameArea.x, aFrameArea.y, aFrameArea.width, aFrameArea.height);
     shadowRect.MoveBy(gfxPoint(shadowItem->mXOffset, shadowItem->mYOffset));
     shadowRect.Outset(shadowItem->mSpread);
 
     gfxRect shadowRectPlusBlur = shadowRect;
     shadowRect.ScaleInverse(twipsPerPixel);
     shadowRect.Round();
 
     // shadowRect won't include the blur, so make an extra rect here that includes the blur
@@ -1141,16 +1142,130 @@ nsCSSRendering::PaintBoxShadow(nsPresCon
     shadowContext->Fill();
 
     blurringArea.DoPaint();
     renderContext->Restore();
   }
 }
 
 void
+nsCSSRendering::PaintBoxShadowInner(nsPresContext* aPresContext,
+                                    nsIRenderingContext& aRenderingContext,
+                                    nsIFrame* aForFrame,
+                                    const nsRect& aFrameArea,
+                                    const nsRect& aDirtyRect)
+{
+  const nsStyleBorder* styleBorder = aForFrame->GetStyleBorder();
+  if (!styleBorder->mBoxShadow)
+    return;
+
+  // 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);
+
+  nsRect paddingRect = aFrameArea;
+  nsMargin border = aForFrame->GetUsedBorder();
+  aForFrame->ApplySkipSides(border);
+  paddingRect.Deflate(border);
+
+  gfxCornerSizes innerRadii;
+  if (hasBorderRadius) {
+    gfxCornerSizes borderRadii;
+    PRIntn sidesToSkip = aForFrame->GetSkipSides();
+
+    ComputePixelRadii(twipsRadii, aFrameArea, sidesToSkip,
+                      twipsPerPixel, &borderRadii);
+    gfxFloat borderSizes[4] = {
+      border.top / twipsPerPixel, border.right / twipsPerPixel,
+      border.bottom / twipsPerPixel, border.left / twipsPerPixel
+    };
+    nsCSSBorderRenderer::ComputeInnerRadii(borderRadii, borderSizes,
+                                           &innerRadii);
+  }
+
+  gfxRect frameGfxRect = RectToGfxRect(paddingRect, twipsPerPixel);
+  frameGfxRect.Round();
+  gfxRect dirtyGfxRect = RectToGfxRect(aDirtyRect, twipsPerPixel);
+
+  for (PRUint32 i = styleBorder->mBoxShadow->Length(); i > 0; --i) {
+    nsCSSShadowItem* shadowItem = styleBorder->mBoxShadow->ShadowAt(i - 1);
+    if (!shadowItem->mInset)
+      continue;
+
+    /*
+     * shadowRect: the frame's padding rect
+     * shadowPaintRect: the area to paint on the temp surface, larger than shadowRect
+     *                  so that blurs still happen properly near the edges
+     * shadowClipRect: the area on the temporary surface within shadowPaintRect
+     *                 that we will NOT paint in
+     */
+    nscoord blurRadius = shadowItem->mRadius;
+    gfxRect shadowRect(paddingRect.x, paddingRect.y, paddingRect.width, paddingRect.height);
+    gfxRect shadowPaintRect = shadowRect;
+    shadowPaintRect.Outset(blurRadius);
+
+    gfxRect shadowClipRect = shadowRect;
+    shadowClipRect.MoveBy(gfxPoint(shadowItem->mXOffset, shadowItem->mYOffset));
+    shadowClipRect.Inset(shadowItem->mSpread);
+
+    shadowRect.ScaleInverse(twipsPerPixel);
+    shadowRect.Round();
+    shadowPaintRect.ScaleInverse(twipsPerPixel);
+    shadowPaintRect.RoundOut();
+    shadowClipRect.ScaleInverse(twipsPerPixel);
+    shadowClipRect.Round();
+
+    gfxContext* renderContext = aRenderingContext.ThebesContext();
+    nsRefPtr<gfxContext> shadowContext;
+    nsContextBoxBlur blurringArea;
+
+    // shadowPaintRect is already in device pixels, pass 1 as the appunits/pixel value
+    blurRadius /= twipsPerPixel;
+    shadowContext = blurringArea.Init(shadowPaintRect, blurRadius, 1, renderContext, dirtyGfxRect);
+    if (!shadowContext)
+      continue;
+
+    // Set the shadow color; if not specified, use the foreground color
+    nscolor shadowColor;
+    if (shadowItem->mHasColor)
+      shadowColor = shadowItem->mColor;
+    else
+      shadowColor = aForFrame->GetStyleColor()->mColor;
+
+    renderContext->Save();
+    renderContext->SetColor(gfxRGBA(shadowColor));
+
+    // Clip the context to the area of the frame's padding rect, so no part of the
+    // shadow is painted outside
+    renderContext->NewPath();
+    if (hasBorderRadius)
+      renderContext->RoundedRectangle(shadowRect, innerRadii, PR_FALSE);
+    else
+      renderContext->Rectangle(shadowRect);
+    renderContext->Clip();
+
+    // Fill the temporary surface minus the area within the frame that we should
+    // not paint in, and blur and apply it
+    shadowContext->NewPath();
+    shadowContext->Rectangle(shadowPaintRect);
+    if (hasBorderRadius)
+      shadowContext->RoundedRectangle(shadowClipRect, innerRadii, PR_FALSE);
+    else
+      shadowContext->Rectangle(shadowClipRect);
+    shadowContext->SetFillRule(gfxContext::FILL_RULE_EVEN_ODD);
+    shadowContext->Fill();
+
+    blurringArea.DoPaint();
+    renderContext->Restore();
+  }
+}
+
+void
 nsCSSRendering::PaintBackground(nsPresContext* aPresContext,
                                 nsIRenderingContext& aRenderingContext,
                                 nsIFrame* aForFrame,
                                 const nsRect& aDirtyRect,
                                 const nsRect& aBorderArea,
                                 PRBool aUsePrintSettings,
                                 nsRect* aBGClipRect)
 {
--- a/layout/base/nsCSSRendering.h
+++ b/layout/base/nsCSSRendering.h
@@ -56,21 +56,27 @@ struct nsCSSRendering {
    */
   static nsresult Init();
   
   /**
    * Clean up any static variables used by nsCSSRendering.
    */
   static void Shutdown();
   
-  static void PaintBoxShadow(nsPresContext* aPresContext,
-                             nsIRenderingContext& aRenderingContext,
-                             nsIFrame* aForFrame,
-                             const nsPoint& aForFramePt,
-                             const nsRect& aDirtyRect);
+  static void PaintBoxShadowInner(nsPresContext* aPresContext,
+                                  nsIRenderingContext& aRenderingContext,
+                                  nsIFrame* aForFrame,
+                                  const nsRect& aFrameArea,
+                                  const nsRect& aDirtyRect);
+
+  static void PaintBoxShadowOuter(nsPresContext* aPresContext,
+                                  nsIRenderingContext& aRenderingContext,
+                                  nsIFrame* aForFrame,
+                                  const nsRect& aFrameArea,
+                                  const nsRect& aDirtyRect);
 
   /**
    * Render the border for an element using css rendering rules
    * for borders. aSkipSides is a bitmask of the sides to skip
    * when rendering. If 0 then no sides are skipped.
    *
    * Both aDirtyRect and aBorderArea are in the local coordinate space
    * of aForFrame
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -638,46 +638,54 @@ nsDisplayBorder::Paint(nsDisplayListBuil
   nsCSSRendering::PaintBorder(mFrame->PresContext(), *aCtx, mFrame,
                               aDirtyRect, nsRect(offset, mFrame->GetSize()),
                               *mFrame->GetStyleBorder(),
                               mFrame->GetStyleContext(),
                               mFrame->GetSkipSides());
 }
 
 void
-nsDisplayBoxShadow::Paint(nsDisplayListBuilder* aBuilder,
+nsDisplayBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
      nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
   nsPoint offset = aBuilder->ToReferenceFrame(mFrame);
-  nsCSSRendering::PaintBoxShadow(mFrame->PresContext(), *aCtx,
-                                 mFrame, offset, aDirtyRect);
+  nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame,
+                                      nsRect(offset, mFrame->GetSize()), aDirtyRect);
 }
 
 nsRect
-nsDisplayBoxShadow::GetBounds(nsDisplayListBuilder* aBuilder) {
+nsDisplayBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder) {
   return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
 }
 
 PRBool
-nsDisplayBoxShadow::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
-                                       nsRegion* aVisibleRegion) {
+nsDisplayBoxShadowOuter::OptimizeVisibility(nsDisplayListBuilder* aBuilder,
+                                            nsRegion* aVisibleRegion) {
   if (!nsDisplayItem::OptimizeVisibility(aBuilder, aVisibleRegion))
     return PR_FALSE;
 
   const nsStyleBorder* border = mFrame->GetStyleBorder();
   nsPoint origin = aBuilder->ToReferenceFrame(mFrame);
   if (nsRect(origin, mFrame->GetSize()).Contains(aVisibleRegion->GetBounds()) &&
       !nsLayoutUtils::HasNonZeroCorner(border->mBorderRadius)) {
     // the visible region is entirely inside the border-rect, and box shadows
     // never render within the border-rect (unless there's a border radius).
     return PR_FALSE;
   }
 
   return PR_TRUE;
 }
 
+void
+nsDisplayBoxShadowInner::Paint(nsDisplayListBuilder* aBuilder,
+     nsIRenderingContext* aCtx, const nsRect& aDirtyRect) {
+  nsPoint offset = aBuilder->ToReferenceFrame(mFrame);
+  nsCSSRendering::PaintBoxShadowInner(mFrame->PresContext(), *aCtx, mFrame,
+                                      nsRect(offset, mFrame->GetSize()), aDirtyRect);
+}
+
 nsDisplayWrapList::nsDisplayWrapList(nsIFrame* aFrame, nsDisplayList* aList)
   : nsDisplayItem(aFrame) {
   mList.AppendToTop(aList);
 }
 
 nsDisplayWrapList::nsDisplayWrapList(nsIFrame* aFrame, nsDisplayItem* aItem)
   : nsDisplayItem(aFrame) {
   mList.AppendToTop(aItem);
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1032,34 +1032,53 @@ public:
      const nsRect& aDirtyRect);
   NS_DISPLAY_DECL_NAME("Background")
 private:
     /* Used to cache mFrame->IsThemed() since it isn't a cheap call */
     PRPackedBool mIsThemed;
 };
 
 /**
- * The standard display item to paint the CSS box-shadow of a frame.
+ * The standard display item to paint the outer CSS box-shadows of a frame.
  */
-class nsDisplayBoxShadow : public nsDisplayItem {
+class nsDisplayBoxShadowOuter : public nsDisplayItem {
 public:
-  nsDisplayBoxShadow(nsIFrame* aFrame) : nsDisplayItem(aFrame) {
-    MOZ_COUNT_CTOR(nsDisplayBoxShadow);
+  nsDisplayBoxShadowOuter(nsIFrame* aFrame) : nsDisplayItem(aFrame) {
+    MOZ_COUNT_CTOR(nsDisplayBoxShadowOuter);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
-  virtual ~nsDisplayBoxShadow() {
-    MOZ_COUNT_DTOR(nsDisplayBoxShadow);
+  virtual ~nsDisplayBoxShadowOuter() {
+    MOZ_COUNT_DTOR(nsDisplayBoxShadowOuter);
   }
 #endif
 
   virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
      const nsRect& aDirtyRect);
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
   virtual PRBool OptimizeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion);
-  NS_DISPLAY_DECL_NAME("BoxShadow")
+  NS_DISPLAY_DECL_NAME("BoxShadowOuter")
+};
+
+/**
+ * The standard display item to paint the inner CSS box-shadows of a frame.
+ */
+class nsDisplayBoxShadowInner : public nsDisplayItem {
+public:
+  nsDisplayBoxShadowInner(nsIFrame* aFrame) : nsDisplayItem(aFrame) {
+    MOZ_COUNT_CTOR(nsDisplayBoxShadowInner);
+  }
+#ifdef NS_BUILD_REFCNT_LOGGING
+  virtual ~nsDisplayBoxShadowInner() {
+    MOZ_COUNT_DTOR(nsDisplayBoxShadowInner);
+  }
+#endif
+
+  virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
+     const nsRect& aDirtyRect);
+  NS_DISPLAY_DECL_NAME("BoxShadowInner")
 };
 
 /**
  * The standard display item to paint the CSS outline of a frame.
  */
 class nsDisplayOutline : public nsDisplayItem {
 public:
   nsDisplayOutline(nsIFrame* aFrame) : nsDisplayItem(aFrame) {
--- a/layout/base/nsStyleConsts.h
+++ b/layout/base/nsStyleConsts.h
@@ -95,16 +95,19 @@
 #define NS_BOXPROP_SOURCE_PHYSICAL 0
 #define NS_BOXPROP_SOURCE_LOGICAL  1
 
 // box-sizing
 #define NS_STYLE_BOX_SIZING_CONTENT       0
 #define NS_STYLE_BOX_SIZING_PADDING       1
 #define NS_STYLE_BOX_SIZING_BORDER        2
 
+// box-shadow
+#define NS_STYLE_BOX_SHADOW_INSET         0
+
 // float-edge
 #define NS_STYLE_FLOAT_EDGE_CONTENT       0
 #define NS_STYLE_FLOAT_EDGE_MARGIN        1
 
 // key-equivalent
 #define NS_STYLE_KEY_EQUIVALENT_NONE      0
 
 // user-focus
--- a/layout/forms/nsButtonFrameRenderer.cpp
+++ b/layout/forms/nsButtonFrameRenderer.cpp
@@ -86,16 +86,54 @@ nsButtonFrameRenderer::SetDisabled(PRBoo
 PRBool
 nsButtonFrameRenderer::isDisabled() 
 {
   // get the content
   return mFrame->GetContent()->HasAttr(kNameSpaceID_None,
                                        nsGkAtoms::disabled);
 }
 
+class nsDisplayButtonBoxShadowOuter : public nsDisplayItem {
+public:
+  nsDisplayButtonBoxShadowOuter(nsButtonFrameRenderer* aRenderer)
+    : nsDisplayItem(aRenderer->GetFrame()), mBFR(aRenderer) {
+    MOZ_COUNT_CTOR(nsDisplayButtonBoxShadowOuter);
+  }
+#ifdef NS_BUILD_REFCNT_LOGGING
+  virtual ~nsDisplayButtonBoxShadowOuter() {
+    MOZ_COUNT_DTOR(nsDisplayButtonBoxShadowOuter);
+  }
+#endif  
+  
+  virtual void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
+     const nsRect& aDirtyRect);
+  virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder);
+  NS_DISPLAY_DECL_NAME("ButtonBoxShadowOuter")
+private:
+  nsButtonFrameRenderer* mBFR;
+};
+
+nsRect
+nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder) {
+  return mFrame->GetOverflowRect() + aBuilder->ToReferenceFrame(mFrame);
+}
+
+void
+nsDisplayButtonBoxShadowOuter::Paint(nsDisplayListBuilder* aBuilder,
+                                     nsIRenderingContext* aCtx,
+                                     const nsRect& aDirtyRect) {
+  nsRect frameRect = nsRect(aBuilder->ToReferenceFrame(mFrame), mFrame->GetSize());
+
+  nsRect buttonRect;
+  mBFR->GetButtonRect(frameRect, buttonRect);
+
+  nsCSSRendering::PaintBoxShadowOuter(mFrame->PresContext(), *aCtx, mFrame,
+                                      buttonRect, aDirtyRect);
+}
+
 class nsDisplayButtonBorderBackground : public nsDisplayItem {
 public:
   nsDisplayButtonBorderBackground(nsButtonFrameRenderer* aRenderer)
     : nsDisplayItem(aRenderer->GetFrame()), mBFR(aRenderer) {
     MOZ_COUNT_CTOR(nsDisplayButtonBorderBackground);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayButtonBorderBackground() {
@@ -159,16 +197,22 @@ void nsDisplayButtonForeground::Paint(ns
   }
 }
 
 nsresult
 nsButtonFrameRenderer::DisplayButton(nsDisplayListBuilder* aBuilder,
                                      nsDisplayList* aBackground,
                                      nsDisplayList* aForeground)
 {
+  if (mFrame->GetStyleBorder()->mBoxShadow) {
+    nsresult rv = aBackground->AppendNewToTop(new (aBuilder)
+        nsDisplayButtonBoxShadowOuter(this));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   nsresult rv = aBackground->AppendNewToTop(new (aBuilder)
       nsDisplayButtonBorderBackground(this));
   NS_ENSURE_SUCCESS(rv, rv);
 
   return aForeground->AppendNewToTop(new (aBuilder)
       nsDisplayButtonForeground(this));
 }
 
@@ -221,16 +265,18 @@ nsButtonFrameRenderer::PaintBorderAndBac
   GetButtonRect(aRect, buttonRect);
 
   nsStyleContext* context = mFrame->GetStyleContext();
 
   const nsStyleBorder* border = context->GetStyleBorder();
 
   nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, mFrame,
                                   aDirtyRect, buttonRect, PR_FALSE);
+  nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext,
+                                      mFrame, buttonRect, aDirtyRect);
   nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
                               aDirtyRect, buttonRect, *border, context);
 }
 
 
 void
 nsButtonFrameRenderer::GetButtonOuterFocusRect(const nsRect& aRect, nsRect& focusRect)
 {
--- a/layout/forms/nsFieldSetFrame.cpp
+++ b/layout/forms/nsFieldSetFrame.cpp
@@ -210,23 +210,25 @@ NS_IMETHODIMP
 nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                   const nsRect&           aDirtyRect,
                                   const nsDisplayListSet& aLists) {
   // Paint our background and border in a special way.
   // REVIEW: We don't really need to check frame emptiness here; if it's empty,
   // the background/border display item won't do anything, and if it isn't empty,
   // we need to paint the outline
   if (IsVisibleForPainting(aBuilder)) {
-    nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
-        nsDisplayBoxShadow(this));
-    NS_ENSURE_SUCCESS(rv, rv);
+    if (GetStyleBorder()->mBoxShadow) {
+      nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
+          nsDisplayBoxShadowOuter(this));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
 
     // don't bother checking to see if we really have a border or background.
     // we usually will have a border.
-    rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
+    nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
         nsDisplayFieldSetBorderBackground(this));
     NS_ENSURE_SUCCESS(rv, rv);
   
     rv = DisplayOutlineUnconditional(aBuilder, aLists);
     NS_ENSURE_SUCCESS(rv, rv);
 
     DO_GLOBAL_REFLOW_COUNT_DSP("nsFieldSetFrame");
   }
@@ -274,16 +276,19 @@ nsFieldSetFrame::PaintBorderBackground(n
   if (topBorder < mLegendRect.height)
     yoff = (mLegendRect.height - topBorder)/2;
       
   nsRect rect(aPt.x, aPt.y + yoff, mRect.width, mRect.height - yoff);
 
   nsCSSRendering::PaintBackground(presContext, aRenderingContext, this,
                                   aDirtyRect, rect, PR_TRUE);
 
+  nsCSSRendering::PaintBoxShadowInner(presContext, aRenderingContext,
+                                      this, rect, aDirtyRect);
+
    if (mLegendFrame) {
 
     // Use the rect of the legend frame, not mLegendRect, so we draw our
     // border under the legend's left and right margins.
     nsRect legendRect = mLegendFrame->GetRect() + aPt;
     
     // we should probably use PaintBorderEdges to do this but for now just use clipping
     // to achieve the same effect.
--- a/layout/forms/nsFileControlFrame.cpp
+++ b/layout/forms/nsFileControlFrame.cpp
@@ -569,17 +569,17 @@ nsFileControlFrame::GetFormProperty(nsIA
 NS_IMETHODIMP
 nsFileControlFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                      const nsRect&           aDirtyRect,
                                      const nsDisplayListSet& aLists)
 {
   // box-shadow
   if (GetStyleBorder()->mBoxShadow) {
     nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
-        nsDisplayBoxShadow(this));
+        nsDisplayBoxShadowOuter(this));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Our background is inherited to the text input, and we don't really want to
   // paint it or out padding and borders (which we never have anyway, per
   // styles in forms.css) -- doing it just makes us look ugly in some cases and
   // has no effect in others.
   nsDisplayListCollection tempList;
--- a/layout/forms/nsHTMLButtonControlFrame.cpp
+++ b/layout/forms/nsHTMLButtonControlFrame.cpp
@@ -199,21 +199,17 @@ nsHTMLButtonControlFrame::HandleEvent(ns
 
 NS_IMETHODIMP
 nsHTMLButtonControlFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                            const nsRect&           aDirtyRect,
                                            const nsDisplayListSet& aLists)
 {
   nsDisplayList onTop;
   if (IsVisibleForPainting(aBuilder)) {
-    nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
-        nsDisplayBoxShadow(this));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mRenderer.DisplayButton(aBuilder, aLists.BorderBackground(), &onTop);
+    nsresult rv = mRenderer.DisplayButton(aBuilder, aLists.BorderBackground(), &onTop);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   
   nsDisplayListCollection set;
   // Do not allow the child subtree to receive events.
   if (!aBuilder->IsForEventDelivery()) {
     nsresult rv =
       BuildDisplayListForChild(aBuilder, mFrames.FirstChild(), aDirtyRect, set,
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -945,31 +945,38 @@ nsFrame::DisplayBorderBackgroundOutline(
                                         PRBool                  aForceBackground)
 {
   // The visibility check belongs here since child elements have the
   // opportunity to override the visibility property and display even if
   // their parent is hidden.
   if (!IsVisibleForPainting(aBuilder))
     return NS_OK;
 
-  if (GetStyleBorder()->mBoxShadow) {
+  PRBool hasBoxShadow = !!(GetStyleBorder()->mBoxShadow);
+  if (hasBoxShadow) {
     nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
-        nsDisplayBoxShadow(this));
+        nsDisplayBoxShadowOuter(this));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Here we don't try to detect background propagation. Frames that might
   // receive a propagated background should just set aForceBackground to
   // PR_TRUE.
   if (aBuilder->IsForEventDelivery() || aForceBackground ||
       !GetStyleBackground()->IsTransparent() || GetStyleDisplay()->mAppearance) {
     nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
         nsDisplayBackground(this));
     NS_ENSURE_SUCCESS(rv, rv);
   }
+
+  if (hasBoxShadow) {
+    nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
+        nsDisplayBoxShadowInner(this));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
   
   if (HasBorder()) {
     nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
         nsDisplayBorder(this));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return DisplayOutlineUnconditional(aBuilder, aLists);
@@ -3851,16 +3858,20 @@ ComputeOutlineAndEffectsRect(nsIFrame* a
 
   // box-shadow
   nsCSSShadowArray* boxShadows = aFrame->GetStyleBorder()->mBoxShadow;
   if (boxShadows) {
     nsRect shadows;
     for (PRUint32 i = 0; i < boxShadows->Length(); ++i) {
       nsRect tmpRect = r;
       nsCSSShadowItem* shadow = boxShadows->ShadowAt(i);
+
+      // inset shadows are never painted outside the frame
+      if (shadow->mInset)
+        continue;
       nscoord outsetRadius = shadow->mRadius + shadow->mSpread;
 
       tmpRect.MoveBy(nsPoint(shadow->mXOffset, shadow->mYOffset));
       tmpRect.Inflate(outsetRadius, outsetRadius);
 
       shadows.UnionRect(shadows, tmpRect);
     }
     r.UnionRect(r, shadows);
new file mode 100644
--- /dev/null
+++ b/layout/reftests/box-shadow/boxshadow-inner-basic-ref.html
@@ -0,0 +1,1 @@
+<div style="background-color: grey; width: 300px; height: 300px; -moz-border-radius: 5px; position: absolute; top: 20px; left: 20px;">inset</div><div style="-moz-border-radius: 5px; width: 300px; height: 300px; background-color: white; position: absolute; top: 40px; left: 40px;">&nbsp;</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/box-shadow/boxshadow-inner-basic.html
@@ -0,0 +1,1 @@
+<div style="-moz-box-shadow: 20px 20px grey inset; width: 300px; height: 300px; -moz-border-radius: 5px; position: absolute; top: 20px; left: 20px;">inset</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/box-shadow/boxshadow-mixed-ref.html
@@ -0,0 +1,1 @@
+<div style="background-color: blue; border: 2px solid red; -moz-border-radius: 10px; width: 300px; height: 300px; position: absolute; top: 10px; left: 10px;">inset and outset</div><div style="-moz-border-radius: 10px; background-color: green; width: 304px; height: 304px; position: absolute; top: 10px; left: 360px;">&nbsp;</div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/box-shadow/boxshadow-mixed.html
@@ -0,0 +1,1 @@
+<div style="border: 2px red solid; -moz-border-radius: 10px; width: 300px; height: 300px; -moz-box-shadow: 500px 500px 0px 20px blue inset, 350px 0px green; position: absolute; top: 10px; left: 10px;">inset and outset</div>
--- a/layout/reftests/box-shadow/reftest.list
+++ b/layout/reftests/box-shadow/reftest.list
@@ -3,8 +3,10 @@
 == boxshadow-multiple.html boxshadow-multiple-ref.html
 == boxshadow-spread.html boxshadow-spread-ref.html
 == tableboxshadow-basic.html tableboxshadow-basic-ref.html
 == tableboxshadow-trshadow.html tableboxshadow-trshadow-ref.html
 == tableboxshadow-tdshadow.html tableboxshadow-tdshadow-ref.html
 == boxshadow-rounding.html boxshadow-rounding-ref.html
 == boxshadow-button.html boxshadow-button-ref.html
 == boxshadow-fileupload.html boxshadow-fileupload-ref.html
+== boxshadow-inner-basic.html boxshadow-inner-basic-ref.html
+== boxshadow-mixed.html boxshadow-mixed-ref.html
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -429,17 +429,17 @@ protected:
   PRBool ParseOutline();
   PRBool ParseOverflow();
   PRBool ParsePadding();
   PRBool ParsePause();
   PRBool ParseQuotes();
   PRBool ParseSize();
   PRBool ParseTextDecoration(nsCSSValue& aValue);
 
-  nsCSSValueList* ParseCSSShadowList(PRBool aUsesSpread);
+  nsCSSValueList* ParseCSSShadowList(PRBool aIsBoxShadow);
   PRBool ParseTextShadow();
   PRBool ParseBoxShadow();
 
 #ifdef MOZ_SVG
   PRBool ParsePaint(nsCSSValuePair* aResult,
                     nsCSSProperty aPropID);
   PRBool ParseDasharray();
   PRBool ParseMarker();
@@ -7639,51 +7639,61 @@ CSSParserImpl::ParseTextDecoration(nsCSS
       aValue.SetIntValue(intValue, eCSSUnit_Enumerated);
     }
     return PR_TRUE;
   }
   return PR_FALSE;
 }
 
 nsCSSValueList*
-CSSParserImpl::ParseCSSShadowList(PRBool aUsesSpread)
+CSSParserImpl::ParseCSSShadowList(PRBool aIsBoxShadow)
 {
   nsAutoParseCompoundProperty compound(this);
 
   // Parses x, y, radius, color (in two possible orders)
   // This parses the input into a list. Either it contains just a "none" or
   // "inherit" value, or a list of arrays.
   // The resulting arrays will always contain the above order, with color and
   // radius as null values as needed
   enum {
     IndexX,
     IndexY,
     IndexRadius,
     IndexSpread,
-    IndexColor
+    IndexColor,
+    IndexInset
   };
 
   nsCSSValueList *list = nsnull;
   for (nsCSSValueList **curp = &list, *cur; ; curp = &cur->mNext) {
     cur = *curp = new nsCSSValueList();
     if (!cur) {
       mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
       break;
     }
+
+    nsCSSValue isInset;
+    if (aIsBoxShadow) {
+      // Optional inset keyword (ignore errors)
+      ParseVariant(isInset, VARIANT_KEYWORD,
+                   nsCSSProps::kBoxShadowTypeKTable);
+    }
+
+    PRBool isFirstToken = (cur == list && isInset.GetUnit() == eCSSUnit_Null);
     if (!ParseVariant(cur->mValue,
-                      (cur == list) ? VARIANT_HC | VARIANT_LENGTH | VARIANT_NONE
-                                    : VARIANT_COLOR | VARIANT_LENGTH,
+                      isFirstToken ? VARIANT_HC | VARIANT_LENGTH | VARIANT_NONE
+                                   : VARIANT_COLOR | VARIANT_LENGTH,
                       nsnull)) {
       break;
     }
 
     nsCSSUnit unit = cur->mValue.GetUnit();
     if (unit != eCSSUnit_None && unit != eCSSUnit_Inherit &&
         unit != eCSSUnit_Initial) {
-      nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(5);
+      nsRefPtr<nsCSSValue::Array> val = nsCSSValue::Array::Create(6);
       if (!val) {
         mScanner.SetLowLevelError(NS_ERROR_OUT_OF_MEMORY);
         break;
       }
       PRBool haveColor = PR_FALSE;
       if (cur->mValue.IsLengthUnit()) {
         val->Item(IndexX) = cur->mValue;
       } else {
@@ -7712,28 +7722,36 @@ CSSParserImpl::ParseCSSShadowList(PRBool
       // value which we must reject. If we use ParsePositiveVariant we can't
       // tell the difference between an unspecified radius and a negative
       // radius, so that's why we don't use it.
       if (ParseVariant(val->Item(IndexRadius), VARIANT_LENGTH, nsnull) &&
           val->Item(IndexRadius).GetFloatValue() < 0) {
         break;
       }
 
-      if (aUsesSpread) {
+      if (aIsBoxShadow) {
         // Optional spread (ignore errors)
         ParseVariant(val->Item(IndexSpread), VARIANT_LENGTH,
                      nsnull);
       }
 
       if (!haveColor) {
         // Optional color (ignore errors)
         ParseVariant(val->Item(IndexColor), VARIANT_COLOR,
                      nsnull);
       }
 
+      if (aIsBoxShadow && isInset.GetUnit() == eCSSUnit_Null) {
+        // Optional inset keyword (ignore errors)
+        ParseVariant(val->Item(IndexInset), VARIANT_KEYWORD,
+                     nsCSSProps::kBoxShadowTypeKTable);
+      } else if (isInset.GetUnit() == eCSSUnit_Enumerated) {
+        val->Item(IndexInset) = isInset;
+      }
+
       // Might be at a comma now
       if (ExpectSymbol(',', PR_TRUE)) {
         // Go to next value
         continue;
       }
     }
 
     if (!ExpectEndProperty()) {
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -385,17 +385,17 @@ CSS_PROP_BORDER(border-start-width-value
 CSS_PROP_SHORTHAND(border-style, border_style, BorderStyle, 0)  // on/off will need reflow
 CSS_PROP_SHORTHAND(border-top, border_top, BorderTop, 0)
 CSS_PROP_BORDER(border-top-color, border_top_color, BorderTopColor, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, Margin, mBorderColor.mTop, eCSSType_Value, kBorderColorKTable)
 CSS_PROP_BORDER(-moz-border-top-colors, border_top_colors, MozBorderTopColors, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, Margin, mBorderColors.mTop, eCSSType_ValueList, nsnull)
 CSS_PROP_BORDER(border-top-style, border_top_style, BorderTopStyle, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, Margin, mBorderStyle.mTop, eCSSType_Value, kBorderStyleKTable)  // on/off will need reflow
 CSS_PROP_BORDER(border-top-width, border_top_width, BorderTopWidth, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER, Margin, mBorderWidth.mTop, eCSSType_Value, kBorderWidthKTable)
 CSS_PROP_SHORTHAND(border-width, border_width, BorderWidth, 0)
 CSS_PROP_POSITION(bottom, bottom, Bottom, 0, Position, mOffset.mBottom, eCSSType_Value, nsnull)
-CSS_PROP_BORDER(-moz-box-shadow, box_shadow, MozBoxShadow, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | CSS_PROPERTY_VALUE_LIST_USES_COMMAS, Margin, mBoxShadow, eCSSType_ValueList, nsnull)
+CSS_PROP_BORDER(-moz-box-shadow, box_shadow, MozBoxShadow, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER | CSS_PROPERTY_VALUE_LIST_USES_COMMAS, Margin, mBoxShadow, eCSSType_ValueList, kBoxShadowTypeKTable)
 CSS_PROP_POSITION(-moz-box-sizing, box_sizing, MozBoxSizing, 0, Position, mBoxSizing, eCSSType_Value, kBoxSizingKTable) // XXX bug 3935
 CSS_PROP_TABLEBORDER(caption-side, caption_side, CaptionSide, 0, Table, mCaptionSide, eCSSType_Value, kCaptionSideKTable)
 CSS_PROP_DISPLAY(clear, clear, Clear, 0, Display, mClear, eCSSType_Value, kClearKTable)
 CSS_PROP_DISPLAY(clip, clip, Clip, 0, Display, mClip, eCSSType_Rect, nsnull)
 CSS_PROP_COLOR(color, color, Color, CSS_PROPERTY_APPLIES_TO_FIRST_LETTER_AND_FIRST_LINE, Color, mColor, eCSSType_Value, nsnull)
 CSS_PROP_COLUMN(-moz-column-count, _moz_column_count, MozColumnCount, 0, Column, mColumnCount, eCSSType_Value, nsnull)
 CSS_PROP_COLUMN(-moz-column-width, _moz_column_width, MozColumnWidth, 0, Column, mColumnWidth, eCSSType_Value, nsnull)
 CSS_PROP_COLUMN(-moz-column-gap, _moz_column_gap, MozColumnGap, 0, Column, mColumnGap, eCSSType_Value, nsnull)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -432,16 +432,21 @@ const PRInt32 nsCSSProps::kBorderWidthKT
 };
 
 const PRInt32 nsCSSProps::kBoxPropSourceKTable[] = {
   eCSSKeyword_physical,     NS_BOXPROP_SOURCE_PHYSICAL,
   eCSSKeyword_logical,      NS_BOXPROP_SOURCE_LOGICAL,
   eCSSKeyword_UNKNOWN,-1
 };
 
+const PRInt32 nsCSSProps::kBoxShadowTypeKTable[] = {
+  eCSSKeyword_inset, NS_STYLE_BOX_SHADOW_INSET,
+  eCSSKeyword_UNKNOWN,-1
+};
+
 const PRInt32 nsCSSProps::kBoxSizingKTable[] = {
   eCSSKeyword_content_box,  NS_STYLE_BOX_SIZING_CONTENT,
   eCSSKeyword_border_box,   NS_STYLE_BOX_SIZING_BORDER,
   eCSSKeyword_padding_box,  NS_STYLE_BOX_SIZING_PADDING,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const PRInt32 nsCSSProps::kCaptionSideKTable[] = {
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -157,16 +157,17 @@ public:
   static const PRInt32 kShapeRenderingKTable[];
   static const PRInt32 kStrokeLinecapKTable[];
   static const PRInt32 kStrokeLinejoinKTable[];
   static const PRInt32 kTextAnchorKTable[];
   static const PRInt32 kTextRenderingKTable[];
   static const PRInt32 kColorInterpolationKTable[];
 #endif
   static const PRInt32 kBoxPropSourceKTable[];
+  static const PRInt32 kBoxShadowTypeKTable[];
   static const PRInt32 kBoxSizingKTable[];
   static const PRInt32 kCaptionSideKTable[];
   static const PRInt32 kClearKTable[];
   static const PRInt32 kColorKTable[];
   static const PRInt32 kContentKTable[];
   static const PRInt32 kCursorKTable[];
   static const PRInt32 kDirectionKTable[];
   static const PRInt32 kDisplayKTable[];
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -1783,17 +1783,17 @@ nsComputedDOMStyle::GetEllipseRadii(cons
 
     return CallQueryInterface(valueList, aValue);
   }
 }
 
 nsresult
 nsComputedDOMStyle::GetCSSShadowArray(nsCSSShadowArray* aArray,
                                       const nscolor& aDefaultColor,
-                                      PRBool aUsesSpread,
+                                      PRBool aIsBoxShadow,
                                       nsIDOMCSSValue** aValue)
 {
   if (!aArray) {
     nsROCSSPrimitiveValue *val = GetROCSSPrimitiveValue();
     val->SetIdent(nsGkAtoms::none);
     return CallQueryInterface(val, aValue);
   }
 
@@ -1807,17 +1807,17 @@ nsComputedDOMStyle::GetCSSShadowArray(ns
     &nsCSSShadowItem::mXOffset,
     &nsCSSShadowItem::mYOffset,
     &nsCSSShadowItem::mRadius,
     &nsCSSShadowItem::mSpread
   };
 
   nscoord nsCSSShadowItem::* const * shadowValues;
   PRUint32 shadowValuesLength;
-  if (aUsesSpread) {
+  if (aIsBoxShadow) {
     shadowValues = shadowValuesWithSpread;
     shadowValuesLength = NS_ARRAY_LENGTH(shadowValuesWithSpread);
   } else {
     shadowValues = shadowValuesNoSpread;
     shadowValuesLength = NS_ARRAY_LENGTH(shadowValuesNoSpread);
   }
 
   nsDOMCSSValueList *valueList = GetROCSSValueList(PR_TRUE);
@@ -1853,16 +1853,29 @@ nsComputedDOMStyle::GetCSSShadowArray(ns
       val = GetROCSSPrimitiveValue();
       if (!val || !itemList->AppendCSSValue(val)) {
         delete val;
         delete valueList;
         return NS_ERROR_OUT_OF_MEMORY;
       }
       val->SetAppUnits(item->*(shadowValues[i]));
     }
+
+    if (item->mInset && aIsBoxShadow) {
+      // This is an inset box-shadow
+      val = GetROCSSPrimitiveValue();
+      if (!val || !itemList->AppendCSSValue(val)) {
+        delete val;
+        delete valueList;
+        return NS_ERROR_OUT_OF_MEMORY;
+      }
+      val->SetIdent(
+        nsCSSProps::ValueToKeywordEnum(NS_STYLE_BOX_SHADOW_INSET,
+                                       nsCSSProps::kBoxShadowTypeKTable));
+    }
   }
 
   return CallQueryInterface(valueList, aValue);
 }
 
 nsresult
 nsComputedDOMStyle::GetBoxShadow(nsIDOMCSSValue** aValue)
 {
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -111,17 +111,17 @@ private:
   nsresult GetBorderColorFor(PRUint8 aSide, nsIDOMCSSValue** aValue);
 
   nsresult GetMarginWidthFor(PRUint8 aSide, nsIDOMCSSValue** aValue);
 
   PRBool GetLineHeightCoord(nscoord& aCoord);
 
   nsresult GetCSSShadowArray(nsCSSShadowArray* aArray,
                              const nscolor& aDefaultColor,
-                             PRBool aUsesSpread,
+                             PRBool aIsBoxShadow,
                              nsIDOMCSSValue** aValue);
 
   /* Properties Queryable as CSSValues */
 
   nsresult GetAppearance(nsIDOMCSSValue** aValue);
 
   /* Box properties */
   nsresult GetBoxAlign(nsIDOMCSSValue** aValue);
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -2848,17 +2848,17 @@ nsRuleNode::ComputeFontData(void* aStart
   }
 
   COMPUTE_END_INHERITED(Font, font)
 }
 
 already_AddRefed<nsCSSShadowArray>
 nsRuleNode::GetShadowData(nsCSSValueList* aList,
                           nsStyleContext* aContext,
-                          PRBool aUsesSpread,
+                          PRBool aIsBoxShadow,
                           PRBool& inherited)
 {
   PRUint32 arrayLength = 0;
   for (nsCSSValueList *list2 = aList; list2; list2 = list2->mNext)
     ++arrayLength;
 
   NS_ASSERTION(arrayLength > 0, "Non-null text-shadow list, yet we counted 0 items.");
   nsCSSShadowArray* shadowList = new(arrayLength) nsCSSShadowArray(arrayLength);
@@ -2889,32 +2889,40 @@ nsRuleNode::GetShadowData(nsCSSValueList
                         SETCOORD_LENGTH, aContext, mPresContext, inherited);
       NS_ASSERTION(unitOK, "unexpected unit");
       item->mRadius = tempCoord.GetCoordValue();
     } else {
       item->mRadius = 0;
     }
 
     // Find the spread radius
-    if (aUsesSpread && arr->Item(3).GetUnit() != eCSSUnit_Null) {
+    if (aIsBoxShadow && arr->Item(3).GetUnit() != eCSSUnit_Null) {
       unitOK = SetCoord(arr->Item(3), tempCoord, nsStyleCoord(),
                         SETCOORD_LENGTH, aContext, mPresContext, inherited);
       NS_ASSERTION(unitOK, "unexpected unit");
       item->mSpread = tempCoord.GetCoordValue();
     } else {
       item->mSpread = 0;
     }
 
     if (arr->Item(4).GetUnit() != eCSSUnit_Null) {
       item->mHasColor = PR_TRUE;
       // 2nd argument can be bogus since inherit is not a valid color
       unitOK = SetColor(arr->Item(4), 0, mPresContext, aContext, item->mColor,
                         inherited);
       NS_ASSERTION(unitOK, "unexpected unit");
     }
+
+    if (aIsBoxShadow && arr->Item(5).GetUnit() == eCSSUnit_Enumerated) {
+      NS_ASSERTION(arr->Item(5).GetIntValue() == NS_STYLE_BOX_SHADOW_INSET,
+                   "invalid keyword type for box shadow");
+      item->mInset = PR_TRUE;
+    } else {
+      item->mInset = PR_FALSE;
+    }
   }
 
   NS_ADDREF(shadowList);
   return shadowList;
 }
 
 const void*
 nsRuleNode::ComputeTextData(void* aStartStruct,
--- a/layout/style/nsRuleNode.h
+++ b/layout/style/nsRuleNode.h
@@ -682,17 +682,17 @@ protected:
 #ifdef MOZ_SVG
   NS_HIDDEN_(const void*) GetSVGData(nsStyleContext* aContext);
   NS_HIDDEN_(const void*) GetSVGResetData(nsStyleContext* aContext);
 #endif
 
   NS_HIDDEN_(already_AddRefed<nsCSSShadowArray>)
                           GetShadowData(nsCSSValueList* aList,
                                         nsStyleContext* aContext,
-                                        PRBool aUsesSpread,
+                                        PRBool aIsBoxShadow,
                                         PRBool& inherited);
 
 private:
   nsRuleNode(nsPresContext* aPresContext, nsRuleNode* aParent,
              nsIStyleRule* aRule, PRUint8 aLevel, PRBool aIsImportant)
     NS_HIDDEN;
   ~nsRuleNode() NS_HIDDEN;
 
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -303,30 +303,32 @@ private:
 struct nsCSSShadowItem {
   nscoord mXOffset;
   nscoord mYOffset;
   nscoord mRadius;
   nscoord mSpread;
 
   nscolor      mColor;
   PRPackedBool mHasColor; // Whether mColor should be used
+  PRPackedBool mInset;
 
   nsCSSShadowItem() : mHasColor(PR_FALSE) {
     MOZ_COUNT_CTOR(nsCSSShadowItem);
   }
   ~nsCSSShadowItem() {
     MOZ_COUNT_DTOR(nsCSSShadowItem);
   }
 
   PRBool operator==(const nsCSSShadowItem& aOther) {
     return (mXOffset == aOther.mXOffset &&
             mYOffset == aOther.mYOffset &&
             mRadius == aOther.mRadius &&
             mHasColor == aOther.mHasColor &&
             mSpread == aOther.mSpread &&
+            mInset == aOther.mInset &&
             (!mHasColor || mColor == aOther.mColor));
   }
   PRBool operator!=(const nsCSSShadowItem& aOther) {
     return !(*this == aOther);
   }
 };
 
 class nsCSSShadowArray {
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -328,18 +328,18 @@ var gCSSProperties = {
 		invalid_values: []
 	},
 	"-moz-box-shadow": {
 		domProp: "MozBoxShadow",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "none" ],
 		prerequisites: { "color": "blue" },
-		other_values: [ "2px 2px", "2px 2px 1px", "2px 2px 2px 2px", "blue 3px 2px", "2px 2px 1px 5px green", "2px 2px red", "green 2px 2px 1px", "green 2px 2px, blue 1px 3px 4px", "currentColor 3px 3px", "blue 2px 2px, currentColor 1px 2px, 1px 2px 3px 2px orange", "3px 0 0 0" ],
-		invalid_values: [ "3% 3%", "1px 1px 1px 1px 1px", "2px 2px, none", "red 2px 2px blue", "inherit, 2px 2px", "2px 2px, inherit", "2px 2px -5px" ]
+		other_values: [ "2px 2px", "2px 2px 1px", "2px 2px 2px 2px", "blue 3px 2px", "2px 2px 1px 5px green", "2px 2px red", "green 2px 2px 1px", "green 2px 2px, blue 1px 3px 4px", "currentColor 3px 3px", "blue 2px 2px, currentColor 1px 2px, 1px 2px 3px 2px orange", "3px 0 0 0", "inset 2px 2px 3px 4px black", "2px -2px green inset, 4px 4px 3px blue, inset 2px 2px" ],
+		invalid_values: [ "3% 3%", "1px 1px 1px 1px 1px", "2px 2px, none", "red 2px 2px blue", "inherit, 2px 2px", "2px 2px, inherit", "2px 2px -5px", "inset 4px 4px black inset", "inset inherit", "inset none" ]
 	},
 	"-moz-box-sizing": {
 		domProp: "MozBoxSizing",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "content-box" ],
 		other_values: [ "border-box", "padding-box" ],
 		invalid_values: [ "margin-box", "content", "padding", "border", "margin" ]
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -441,34 +441,43 @@ nsTableCellFrame::BuildDisplayList(nsDis
 
     PRBool isRoot = aBuilder->IsAtRootOfPseudoStackingContext();
     if (!isRoot) {
       nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
       NS_ASSERTION(currentItem, "No current table item???");
       currentItem->UpdateForFrameBackground(this);
     }
 
-    if (GetStyleBorder()->mBoxShadow) {
-      nsDisplayItem* item = new (aBuilder) nsDisplayBoxShadow(this);
+    // display outset box-shadows if we need to.
+    PRBool hasBoxShadow = !!(GetStyleBorder()->mBoxShadow);
+    if (hasBoxShadow) {
+      nsDisplayItem* item = new (aBuilder) nsDisplayBoxShadowOuter(this);
       nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // display background if we need to.
     if (aBuilder->IsForEventDelivery() ||
         (((!tableFrame->IsBorderCollapse() || isRoot) &&
         (!GetStyleBackground()->IsTransparent() || GetStyleDisplay()->mAppearance)))) {
       // The cell background was not painted by the nsTablePainter,
       // so we need to do it. We have special background processing here
       // so we need to duplicate some code from nsFrame::DisplayBorderBackgroundOutline
       nsDisplayTableItem* item = new (aBuilder) nsDisplayTableCellBackground(this);
       nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
       NS_ENSURE_SUCCESS(rv, rv);
       item->UpdateForFrameBackground(this);
     }
+
+    // display inset box-shadows if we need to.
+    if (hasBoxShadow) {
+      nsDisplayItem* item = new (aBuilder) nsDisplayBoxShadowInner(this);
+      nsresult rv = aLists.BorderBackground()->AppendNewToTop(item);
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
     
     // display borders if we need to
     if (!tableFrame->IsBorderCollapse() && HasBorder() &&
         emptyCellStyle == NS_STYLE_TABLE_EMPTY_CELLS_SHOW) {
       nsresult rv = aLists.BorderBackground()->AppendNewToTop(new (aBuilder)
           nsDisplayBorder(this));
       NS_ENSURE_SUCCESS(rv, rv);
     }
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1375,34 +1375,42 @@ nsTableFrame::DisplayGenericTablePart(ns
   nsAutoPushCurrentTableItem pushTableItem;
   if (aDisplayItem) {
     pushTableItem.Push(aBuilder, aDisplayItem);
   }
   nsDisplayTableItem* currentItem = aBuilder->GetCurrentTableItem();
   NS_ASSERTION(currentItem, "No current table item!");
   currentItem->UpdateForFrameBackground(aFrame);
   
-  // Paint the box-shadow for the table frames
-  if (aFrame->IsVisibleForPainting(aBuilder) &&
-      aFrame->GetStyleBorder()->mBoxShadow) {
-    nsDisplayItem* item = new (aBuilder) nsDisplayBoxShadow(aFrame);
+  // Paint the outset box-shadows for the table frames
+  PRBool hasBoxShadow = aFrame->IsVisibleForPainting(aBuilder) &&
+                        aFrame->GetStyleBorder()->mBoxShadow;
+  if (hasBoxShadow) {
+    nsDisplayItem* item = new (aBuilder) nsDisplayBoxShadowOuter(aFrame);
     nsresult rv = lists->BorderBackground()->AppendNewToTop(item);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Create dedicated background display items per-frame when we're
   // handling events.
   // XXX how to handle collapsed borders?
   if (aBuilder->IsForEventDelivery() &&
       aFrame->IsVisibleForPainting(aBuilder)) {
     nsresult rv = lists->BorderBackground()->AppendNewToTop(new (aBuilder)
         nsDisplayBackground(aFrame));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
+  // Paint the inset box-shadows for the table frames
+  if (hasBoxShadow) {
+    nsDisplayItem* item = new (aBuilder) nsDisplayBoxShadowInner(aFrame);
+    nsresult rv = lists->BorderBackground()->AppendNewToTop(item);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   nsresult rv = aTraversal(aBuilder, aFrame, aDirtyRect, *lists);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (sortEventBackgrounds) {
     // Ensure that the table frame event background goes before the
     // table rowgroups event backgrounds, before the table row event backgrounds,
     // before everything else (cells and their blocks)
     separatedCollection.BorderBackground()->Sort(aBuilder, CompareByTablePartRank, nsnull);