Merge inbound to mozilla-central a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Tue, 23 Dec 2014 16:47:42 -0800
changeset 248705 b17e7747d3fb6ea39a3f8b9de2d2b71bbda26764
parent 248686 865d06511e992baca21c3fb007e10f9a58ae7222 (current diff)
parent 248704 b1817e0cdc3004347193d893066236eb48e396e0 (diff)
child 248714 ab8d887c049c9632bff6959cf1b22337cd75a0be
child 248732 dc5cdad7a70ba8fee6044d42ec71ebfdbcd42b83
child 248790 c5d1be9b095a12db61f75d7b80f8cf5e6370cb3e
push id698
push userjlund@mozilla.com
push dateMon, 23 Mar 2015 22:08:11 +0000
treeherdermozilla-release@b0c0ae7b02a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone37.0a1
first release with
nightly linux32
b17e7747d3fb / 37.0a1 / 20141224030205 / files
nightly linux64
b17e7747d3fb / 37.0a1 / 20141224030205 / files
nightly mac
b17e7747d3fb / 37.0a1 / 20141224030205 / files
nightly win32
b17e7747d3fb / 37.0a1 / 20141224030205 / files
nightly win64
b17e7747d3fb / 37.0a1 / 20141224030205 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to mozilla-central a=merge CLOSED TREE
gfx/layers/apz/src/AsyncPanZoomController.cpp
--- a/configure.in
+++ b/configure.in
@@ -1295,16 +1295,30 @@ MOZ_ARG_ENABLE_BOOL(memory-sanitizer,
     MOZ_MSAN= )
 if test -n "$MOZ_MSAN"; then
     MOZ_LLVM_HACKS=1
     AC_DEFINE(MOZ_MSAN)
     MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
 fi
 AC_SUBST(MOZ_MSAN)
 
+dnl ========================================================
+dnl = Use Thread Sanitizer
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(thread-sanitizer,
+[  --enable-thread-sanitizer       Enable Thread Sanitizer (default=no)],
+   MOZ_TSAN=1,
+   MOZ_TSAN= )
+if test -n "$MOZ_TSAN"; then
+    MOZ_LLVM_HACKS=1
+    AC_DEFINE(MOZ_TSAN)
+    MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
+fi
+AC_SUBST(MOZ_TSAN)
+
 # The LLVM symbolizer is used by all sanitizers
 AC_SUBST(LLVM_SYMBOLIZER)
 
 dnl ========================================================
 dnl = Enable hacks required for LLVM instrumentations
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(llvm-hacks,
 [  --enable-llvm-hacks       Enable workarounds required for several LLVM instrumentations (default=no)],
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -2305,49 +2305,52 @@ CanvasRenderingContext2D::SetFilter(cons
     }
   }
 }
 
 class CanvasUserSpaceMetrics : public UserSpaceMetricsWithSize
 {
 public:
   CanvasUserSpaceMetrics(const gfx::IntSize& aSize, const nsFont& aFont,
-                         nsIAtom* aFontLanguage, nsPresContext* aPresContext)
+                         nsIAtom* aFontLanguage, bool aExplicitLanguage,
+                         nsPresContext* aPresContext)
     : mSize(aSize)
     , mFont(aFont)
     , mFontLanguage(aFontLanguage)
+    , mExplicitLanguage(aExplicitLanguage)
     , mPresContext(aPresContext)
   {
   }
 
   virtual float GetEmLength() const MOZ_OVERRIDE
   {
     return NSAppUnitsToFloatPixels(mFont.size,
                                    nsPresContext::AppUnitsPerCSSPixel());
   }
 
   virtual float GetExLength() const MOZ_OVERRIDE
   {
     gfxTextPerfMetrics* tp = mPresContext->GetTextPerfMetrics();
     nsRefPtr<nsFontMetrics> fontMetrics;
     nsDeviceContext* dc = mPresContext->DeviceContext();
-    dc->GetMetricsFor(mFont, mFontLanguage, gfxFont::eHorizontal,
-                      nullptr, tp,
+    dc->GetMetricsFor(mFont, mFontLanguage, mExplicitLanguage,
+                      gfxFont::eHorizontal, nullptr, tp,
                       *getter_AddRefs(fontMetrics));
     return NSAppUnitsToFloatPixels(fontMetrics->XHeight(),
                                    nsPresContext::AppUnitsPerCSSPixel());
   }
 
   virtual gfx::Size GetSize() const MOZ_OVERRIDE
   { return Size(mSize); }
 
 private:
   gfx::IntSize mSize;
   const nsFont& mFont;
   nsIAtom* mFontLanguage;
+  bool mExplicitLanguage;
   nsPresContext* mPresContext;
 };
 
 void
 CanvasRenderingContext2D::UpdateFilter()
 {
   nsIPresShell* presShell = GetPresShell();
   if (!presShell || presShell->IsDestroying()) {
@@ -2355,16 +2358,17 @@ CanvasRenderingContext2D::UpdateFilter()
   }
 
   CurrentState().filter =
     nsFilterInstance::GetFilterDescription(mCanvasElement,
       CurrentState().filterChain,
       CanvasUserSpaceMetrics(IntSize(mWidth, mHeight),
                              CurrentState().fontFont,
                              CurrentState().fontLanguage,
+                             CurrentState().fontExplicitLanguage,
                              presShell->GetPresContext()),
       gfxRect(0, 0, mWidth, mHeight),
       CurrentState().filterAdditionalImages);
 }
 
 //
 // rects
 //
@@ -2986,17 +2990,17 @@ CanvasRenderingContext2D::SetFont(const 
   nsRefPtr<nsStyleContext> sc =
     GetFontStyleContext(mCanvasElement, font, presShell, usedFont, error);
   if (!sc) {
     return;
   }
 
   const nsStyleFont* fontStyle = sc->StyleFont();
 
-  nsIAtom* language = sc->StyleFont()->mLanguage;
+  nsIAtom* language = fontStyle->mLanguage;
   if (!language) {
     language = presShell->GetPresContext()->GetLanguageFromCharset();
   }
 
   // use CSS pixels instead of dev pixels to avoid being affected by page zoom
   const uint32_t aupcp = nsPresContext::AppUnitsPerCSSPixel();
 
   bool printerFont = (presShell->GetPresContext()->Type() == nsPresContext::eContext_PrintPreview ||
@@ -3008,16 +3012,17 @@ CanvasRenderingContext2D::SetFont(const 
   // https://bugzilla.mozilla.org/show_bug.cgi?id=698652.
   MOZ_ASSERT(!fontStyle->mAllowZoom,
              "expected text zoom to be disabled on this nsStyleFont");
   gfxFontStyle style(fontStyle->mFont.style,
                      fontStyle->mFont.weight,
                      fontStyle->mFont.stretch,
                      NSAppUnitsToFloatPixels(fontStyle->mSize, float(aupcp)),
                      language,
+                     fontStyle->mExplicitLanguage,
                      fontStyle->mFont.sizeAdjust,
                      fontStyle->mFont.systemFont,
                      printerFont,
                      fontStyle->mFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT,
                      fontStyle->mFont.synthesis & NS_FONT_SYNTHESIS_STYLE,
                      fontStyle->mFont.languageOverride);
 
   fontStyle->mFont.AddFontFeaturesToStyle(&style);
@@ -3028,16 +3033,17 @@ CanvasRenderingContext2D::SetFont(const 
                                                   &style,
                                                   c->GetUserFontSet());
   NS_ASSERTION(CurrentState().fontGroup, "Could not get font group");
   CurrentState().fontGroup->SetTextPerfMetrics(c->GetTextPerfMetrics());
   CurrentState().font = usedFont;
   CurrentState().fontFont = fontStyle->mFont;
   CurrentState().fontFont.size = fontStyle->mSize;
   CurrentState().fontLanguage = fontStyle->mLanguage;
+  CurrentState().fontExplicitLanguage = fontStyle->mExplicitLanguage;
 }
 
 void
 CanvasRenderingContext2D::SetTextAlign(const nsAString& ta)
 {
   if (ta.EqualsLiteral("start"))
     CurrentState().textAlign = TextAlign::START;
   else if (ta.EqualsLiteral("end"))
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -941,17 +941,18 @@ protected:
                      miterLimit(10.0f),
                      globalAlpha(1.0f),
                      shadowBlur(0.0),
                      dashOffset(0.0f),
                      op(mozilla::gfx::CompositionOp::OP_OVER),
                      fillRule(mozilla::gfx::FillRule::FILL_WINDING),
                      lineCap(mozilla::gfx::CapStyle::BUTT),
                      lineJoin(mozilla::gfx::JoinStyle::MITER_OR_BEVEL),
-                     imageSmoothingEnabled(true)
+                     imageSmoothingEnabled(true),
+                     fontExplicitLanguage(false)
     { }
 
     ContextState(const ContextState& other)
         : fontGroup(other.fontGroup),
           fontLanguage(other.fontLanguage),
           fontFont(other.fontFont),
           gradientStyles(other.gradientStyles),
           patternStyles(other.patternStyles),
@@ -972,17 +973,18 @@ protected:
           fillRule(other.fillRule),
           lineCap(other.lineCap),
           lineJoin(other.lineJoin),
           filterString(other.filterString),
           filterChain(other.filterChain),
           filterChainObserver(other.filterChainObserver),
           filter(other.filter),
           filterAdditionalImages(other.filterAdditionalImages),
-          imageSmoothingEnabled(other.imageSmoothingEnabled)
+          imageSmoothingEnabled(other.imageSmoothingEnabled),
+          fontExplicitLanguage(other.fontExplicitLanguage)
     { }
 
     void SetColorStyle(Style whichStyle, nscolor color)
     {
       colorStyles[whichStyle] = color;
       gradientStyles[whichStyle] = nullptr;
       patternStyles[whichStyle] = nullptr;
     }
@@ -1050,16 +1052,17 @@ protected:
 
     nsString filterString;
     nsTArray<nsStyleFilter> filterChain;
     nsRefPtr<nsSVGFilterChainObserver> filterChainObserver;
     mozilla::gfx::FilterDescription filter;
     nsTArray<mozilla::RefPtr<mozilla::gfx::SourceSurface>> filterAdditionalImages;
 
     bool imageSmoothingEnabled;
+    bool fontExplicitLanguage;
   };
 
   nsAutoTArray<ContextState, 3> mStyleStack;
 
   inline ContextState& CurrentState() {
     return mStyleStack[mStyleStack.Length() - 1];
   }
 
--- a/gfx/2d/DrawTargetCairo.cpp
+++ b/gfx/2d/DrawTargetCairo.cpp
@@ -1041,16 +1041,22 @@ DrawTargetCairo::CopyRect(const IntRect 
   }
 }
 
 void
 DrawTargetCairo::ClearRect(const Rect& aRect)
 {
   AutoPrepareForDrawing prep(this, mContext);
 
+  if (!mContext || aRect.Width() <= 0 || aRect.Height() <= 0 ||
+      !IsFinite(aRect.X()) || !IsFinite(aRect.Width()) ||
+      !IsFinite(aRect.Y()) || !IsFinite(aRect.Height())) {
+    gfxCriticalError(CriticalLog::DefaultOptions(false)) << "ClearRect with invalid argument " << gfx::hexa(mContext) << " with " << aRect.Width() << "x" << aRect.Height() << " [" << aRect.X() << ", " << aRect.Y() << "]";
+  }
+
   cairo_set_antialias(mContext, CAIRO_ANTIALIAS_NONE);
   cairo_new_path(mContext);
   cairo_set_operator(mContext, CAIRO_OPERATOR_CLEAR);
   cairo_rectangle(mContext, aRect.X(), aRect.Y(),
                   aRect.Width(), aRect.Height());
   cairo_fill(mContext);
 }
 
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -35,20 +35,20 @@ public:
   typedef uint64_t ViewID;
   static const ViewID NULL_SCROLL_ID;   // This container layer does not scroll.
   static const ViewID START_SCROLL_ID = 2;  // This is the ID that scrolling subframes
                                         // will begin at.
   static const FrameMetrics sNullMetrics;   // We often need an empty metrics
 
   FrameMetrics()
     : mCompositionBounds(0, 0, 0, 0)
-    , mDisplayPort(0, 0, 0, 0)
     , mCriticalDisplayPort(0, 0, 0, 0)
     , mScrollableRect(0, 0, 0, 0)
     , mPresShellResolution(1)
+    , mDisplayPort(0, 0, 0, 0)
     , mCumulativeResolution(1)
     , mDevPixelsPerCSSPixel(1)
     , mMayHaveTouchListeners(false)
     , mMayHaveTouchCaret(false)
     , mIsRoot(false)
     , mHasScrollgrab(false)
     , mScrollId(NULL_SCROLL_ID)
     , mScrollParentId(NULL_SCROLL_ID)
@@ -251,29 +251,16 @@ public:
   // layout/paint time.
   ParentLayerRect mCompositionBounds;
 
   // ---------------------------------------------------------------------------
   // The following metrics are all in CSS pixels. They are not in any uniform
   // space, so each is explained separately.
   //
 
-  // The area of a frame's contents that has been painted, relative to
-  // mCompositionBounds.
-  //
-  // Note that this is structured in such a way that it doesn't depend on the
-  // method layout uses to scroll content.
-  //
-  // May be larger or smaller than |mScrollableRect|.
-  //
-  // To pre-render a margin of 100 CSS pixels around the window,
-  // { x = -100, y = - 100,
-  //   width = window.innerWidth + 200, height = window.innerHeight + 200 }
-  CSSRect mDisplayPort;
-
   // If non-empty, the area of a frame's contents that is considered critical
   // to paint. Area outside of this area (i.e. area inside mDisplayPort, but
   // outside of mCriticalDisplayPort) is considered low-priority, and may be
   // painted with lower precision, or not painted at all.
   //
   // The same restrictions for mDisplayPort apply here.
   CSSRect mCriticalDisplayPort;
 
@@ -299,16 +286,26 @@ public:
   // user action, or choosing an initial zoom level on page load). This can
   // only be different from 1.0 for frames that are zoomable, which currently
   // is just the root content document's root scroll frame (mIsRoot = true).
   // This is a plain float rather than a ScaleFactor because in and of itself
   // it does not convert between any coordinate spaces for which we have names.
   float mPresShellResolution;
 
 public:
+  void SetDisplayPort(const CSSRect& aDisplayPort)
+  {
+    mDisplayPort = aDisplayPort;
+  }
+
+  CSSRect GetDisplayPort() const
+  {
+    return mDisplayPort;
+  }
+
   void SetCumulativeResolution(const LayoutDeviceToLayerScale& aCumulativeResolution)
   {
     mCumulativeResolution = aCumulativeResolution;
   }
 
   LayoutDeviceToLayerScale GetCumulativeResolution() const
   {
     return mCumulativeResolution;
@@ -527,16 +524,29 @@ public:
   }
 
   void SetLineScrollAmount(const LayoutDeviceIntSize& size)
   {
     mLineScrollAmount = size;
   }
 
 private:
+  // The area of a frame's contents that has been painted, relative to
+  // mCompositionBounds.
+  //
+  // Note that this is structured in such a way that it doesn't depend on the
+  // method layout uses to scroll content.
+  //
+  // May be larger or smaller than |mScrollableRect|.
+  //
+  // To pre-render a margin of 100 CSS pixels around the window,
+  // { x = -100, y = - 100,
+  //   width = window.innerWidth + 200, height = window.innerHeight + 200 }
+  CSSRect mDisplayPort;
+
   // The cumulative resolution that the current frame has been painted at.
   // This is the product of the pres-shell resolutions of the document
   // containing this scroll frame and its ancestors, and any css-driven
   // resolution. This information is provided by Gecko at layout/paint time.
   LayoutDeviceToLayerScale mCumulativeResolution;
 
   // New fields from now on should be made private and old fields should
   // be refactored to be private.
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -170,17 +170,17 @@ AppendToString(std::stringstream& aStrea
 {
   aStream << pfx;
   AppendToString(aStream, m.mCompositionBounds, "{ [cb=");
   AppendToString(aStream, m.mScrollableRect, "] [sr=");
   AppendToString(aStream, m.GetScrollOffset(), "] [s=");
   if (m.GetDoSmoothScroll()) {
     AppendToString(aStream, m.GetSmoothScrollOffset(), "] [ss=");
   }
-  AppendToString(aStream, m.mDisplayPort, "] [dp=");
+  AppendToString(aStream, m.GetDisplayPort(), "] [dp=");
   AppendToString(aStream, m.mCriticalDisplayPort, "] [cdp=");
   AppendToString(aStream, m.GetBackgroundColor(), "] [color=");
   if (!detailed) {
     AppendToString(aStream, m.GetScrollId(), "] [scrollId=");
     if (m.GetScrollParentId() != FrameMetrics::NULL_SCROLL_ID) {
       AppendToString(aStream, m.GetScrollParentId(), "] [scrollParent=");
     }
     aStream << nsPrintfCString("] [z=%.3f] }", m.GetZoom().scale).get();
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2619,22 +2619,22 @@ ViewTransform AsyncPanZoomController::Ge
   }
 
   CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() +
     mTestAsyncScrollOffset;
 
   // If checkerboarding has been disallowed, clamp the scroll position to stay
   // within rendered content.
   if (!gfxPrefs::APZAllowCheckerboarding() &&
-      !mLastContentPaintMetrics.mDisplayPort.IsEmpty()) {
+      !mLastContentPaintMetrics.GetDisplayPort().IsEmpty()) {
     CSSSize compositedSize = mLastContentPaintMetrics.CalculateCompositedSizeInCssPixels();
     CSSPoint maxScrollOffset = lastPaintScrollOffset +
-      CSSPoint(mLastContentPaintMetrics.mDisplayPort.XMost() - compositedSize.width,
-               mLastContentPaintMetrics.mDisplayPort.YMost() - compositedSize.height);
-    CSSPoint minScrollOffset = lastPaintScrollOffset + mLastContentPaintMetrics.mDisplayPort.TopLeft();
+      CSSPoint(mLastContentPaintMetrics.GetDisplayPort().XMost() - compositedSize.width,
+               mLastContentPaintMetrics.GetDisplayPort().YMost() - compositedSize.height);
+    CSSPoint minScrollOffset = lastPaintScrollOffset + mLastContentPaintMetrics.GetDisplayPort().TopLeft();
 
     if (minScrollOffset.x < maxScrollOffset.x) {
       currentScrollOffset.x = clamped(currentScrollOffset.x, minScrollOffset.x, maxScrollOffset.x);
     }
     if (minScrollOffset.y < maxScrollOffset.y) {
       currentScrollOffset.y = clamped(currentScrollOffset.y, minScrollOffset.y, maxScrollOffset.y);
     }
   }
@@ -2680,17 +2680,17 @@ Matrix4x4 AsyncPanZoomController::GetTra
 bool AsyncPanZoomController::IsCurrentlyCheckerboarding() const {
   ReentrantMonitorAutoEnter lock(mMonitor);
 
   if (!gfxPrefs::APZAllowCheckerboarding()) {
     return false;
   }
 
   CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset;
-  CSSRect painted = mLastContentPaintMetrics.mDisplayPort + mLastContentPaintMetrics.GetScrollOffset();
+  CSSRect painted = mLastContentPaintMetrics.GetDisplayPort() + mLastContentPaintMetrics.GetScrollOffset();
   painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));   // fuzz for rounding error
   CSSRect visible = CSSRect(currentScrollOffset, mFrameMetrics.CalculateCompositedSizeInCssPixels());
   return !painted.Contains(visible);
 }
 
 void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) {
   AssertOnCompositorThread();
 
@@ -2701,17 +2701,17 @@ void AsyncPanZoomController::NotifyLayer
 
   mFrameMetrics.SetMayHaveTouchListeners(aLayerMetrics.GetMayHaveTouchListeners());
   mFrameMetrics.SetMayHaveTouchCaret(aLayerMetrics.GetMayHaveTouchCaret());
   mFrameMetrics.SetScrollParentId(aLayerMetrics.GetScrollParentId());
   APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d", this, aIsFirstPaint);
 
   LogRendertraceRect(GetGuid(), "page", "brown", aLayerMetrics.mScrollableRect);
   LogRendertraceRect(GetGuid(), "painted displayport", "lightgreen",
-    aLayerMetrics.mDisplayPort + aLayerMetrics.GetScrollOffset());
+    aLayerMetrics.GetDisplayPort() + aLayerMetrics.GetScrollOffset());
   if (!aLayerMetrics.mCriticalDisplayPort.IsEmpty()) {
     LogRendertraceRect(GetGuid(), "painted critical displayport", "darkgreen",
       aLayerMetrics.mCriticalDisplayPort + aLayerMetrics.GetScrollOffset());
   }
 
   mPaintThrottler.TaskComplete(GetFrameTime());
   bool needContentRepaint = false;
   if (FuzzyEqualsAdditive(aLayerMetrics.mCompositionBounds.width, mFrameMetrics.mCompositionBounds.width) &&
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -748,17 +748,17 @@ ClientLayerManager::ProgressiveUpdateCal
 {
 #ifdef MOZ_WIDGET_ANDROID
   MOZ_ASSERT(aMetrics.IsScrollable());
   // This is derived from the code in
   // gfx/layers/ipc/CompositorParent.cpp::TransformShadowTree.
   CSSToLayerScale paintScale = aMetrics.LayersPixelsPerCSSPixel();
   const CSSRect& metricsDisplayPort =
     (aDrawingCritical && !aMetrics.mCriticalDisplayPort.IsEmpty()) ?
-      aMetrics.mCriticalDisplayPort : aMetrics.mDisplayPort;
+      aMetrics.mCriticalDisplayPort : aMetrics.GetDisplayPort();
   LayerRect displayPort = (metricsDisplayPort + aMetrics.GetScrollOffset()) * paintScale;
 
   ParentLayerPoint scrollOffset;
   CSSToParentLayerScale zoom;
   bool ret = AndroidBridge::Bridge()->ProgressiveUpdateCallback(
     aHasPendingNewThebesContent, displayPort, paintScale.scale, aDrawingCritical,
     scrollOffset, zoom);
   aMetrics.SetScrollOffset(scrollOffset / zoom);
--- a/gfx/layers/client/ClientTiledPaintedLayer.cpp
+++ b/gfx/layers/client/ClientTiledPaintedLayer.cpp
@@ -86,17 +86,17 @@ ClientTiledPaintedLayer::GetAncestorLaye
 {
   LayerMetricsWrapper scrollAncestor;
   LayerMetricsWrapper displayPortAncestor;
   for (LayerMetricsWrapper ancestor(this, LayerMetricsWrapper::StartAt::BOTTOM); ancestor; ancestor = ancestor.GetParent()) {
     const FrameMetrics& metrics = ancestor.Metrics();
     if (!scrollAncestor && metrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID) {
       scrollAncestor = ancestor;
     }
-    if (!metrics.mDisplayPort.IsEmpty()) {
+    if (!metrics.GetDisplayPort().IsEmpty()) {
       displayPortAncestor = ancestor;
       // Any layer that has a displayport must be scrollable, so we can break
       // here.
       break;
     }
   }
   if (aOutScrollAncestor) {
     *aOutScrollAncestor = scrollAncestor;
@@ -186,17 +186,17 @@ ClientTiledPaintedLayer::UseFastPath()
     return true;
   }
   const FrameMetrics& parentMetrics = scrollAncestor.Metrics();
 
   bool multipleTransactionsNeeded = gfxPlatform::GetPlatform()->UseProgressivePaint()
                                  || gfxPrefs::UseLowPrecisionBuffer()
                                  || !parentMetrics.mCriticalDisplayPort.IsEmpty();
   bool isFixed = GetIsFixedPosition() || GetParent()->GetIsFixedPosition();
-  return !multipleTransactionsNeeded || isFixed || parentMetrics.mDisplayPort.IsEmpty();
+  return !multipleTransactionsNeeded || isFixed || parentMetrics.GetDisplayPort().IsEmpty();
 }
 
 bool
 ClientTiledPaintedLayer::RenderHighPrecision(nsIntRegion& aInvalidRegion,
                                             const nsIntRegion& aVisibleRegion,
                                             LayerManager::DrawPaintedLayerCallback aCallback,
                                             void* aCallbackData)
 {
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -208,20 +208,20 @@ SharedFrameMetricsHelper::UpdateFromComp
   }
 
   // Never abort drawing if we can't be sure we've sent a more recent
   // display-port. If we abort updating when we shouldn't, we can end up
   // with blank regions on the screen and we open up the risk of entering
   // an endless updating cycle.
   if (fabsf(contentMetrics.GetScrollOffset().x - compositorMetrics.GetScrollOffset().x) <= 2 &&
       fabsf(contentMetrics.GetScrollOffset().y - compositorMetrics.GetScrollOffset().y) <= 2 &&
-      fabsf(contentMetrics.mDisplayPort.x - compositorMetrics.mDisplayPort.x) <= 2 &&
-      fabsf(contentMetrics.mDisplayPort.y - compositorMetrics.mDisplayPort.y) <= 2 &&
-      fabsf(contentMetrics.mDisplayPort.width - compositorMetrics.mDisplayPort.width) <= 2 &&
-      fabsf(contentMetrics.mDisplayPort.height - compositorMetrics.mDisplayPort.height) <= 2) {
+      fabsf(contentMetrics.GetDisplayPort().x - compositorMetrics.GetDisplayPort().x) <= 2 &&
+      fabsf(contentMetrics.GetDisplayPort().y - compositorMetrics.GetDisplayPort().y) <= 2 &&
+      fabsf(contentMetrics.GetDisplayPort().width - compositorMetrics.GetDisplayPort().width) <= 2 &&
+      fabsf(contentMetrics.GetDisplayPort().height - compositorMetrics.GetDisplayPort().height) <= 2) {
     return false;
   }
 
   // When not a low precision pass and the page is in danger of checker boarding
   // abort update.
   if (!aLowPrecision && !mProgressiveUpdateWasInDanger) {
     bool scrollUpdatePending = contentMetrics.GetScrollOffsetUpdated() &&
         contentMetrics.GetScrollGeneration() != compositorMetrics.GetScrollGeneration();
@@ -253,17 +253,17 @@ bool
 SharedFrameMetricsHelper::AboutToCheckerboard(const FrameMetrics& aContentMetrics,
                                               const FrameMetrics& aCompositorMetrics)
 {
   // The size of the painted area is originally computed in layer pixels in layout, but then
   // converted to app units and then back to CSS pixels before being put in the FrameMetrics.
   // This process can introduce some rounding error, so we inflate the rect by one app unit
   // to account for that.
   CSSRect painted = (aContentMetrics.mCriticalDisplayPort.IsEmpty()
-                      ? aContentMetrics.mDisplayPort
+                      ? aContentMetrics.GetDisplayPort()
                       : aContentMetrics.mCriticalDisplayPort)
                     + aContentMetrics.GetScrollOffset();
   painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1)));
 
   // Inflate the rect by the danger zone. See the description of the danger zone prefs
   // in AsyncPanZoomController.cpp for an explanation of this.
   CSSRect showing = CSSRect(aCompositorMetrics.GetScrollOffset(),
                             aCompositorMetrics.CalculateBoundedCompositedSizeInCssPixels());
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -600,17 +600,17 @@ AsyncCompositionManager::ApplyAsyncConte
 
     if (!aLayer->IsScrollInfoLayer()) {
       controller->MarkAsyncTransformAppliedToContent();
     }
 
     const FrameMetrics& metrics = aLayer->GetFrameMetrics(i);
     CSSToLayerScale paintScale = metrics.LayersPixelsPerCSSPixel();
     CSSRect displayPort(metrics.mCriticalDisplayPort.IsEmpty() ?
-                        metrics.mDisplayPort : metrics.mCriticalDisplayPort);
+                        metrics.GetDisplayPort() : metrics.mCriticalDisplayPort);
     ScreenPoint offset(0, 0);
     // XXX this call to SyncFrameMetrics is not currently being used. It will be cleaned
     // up as part of bug 776030 or one of its dependencies.
     SyncFrameMetrics(scrollOffset, asyncTransformWithoutOverscroll.mScale.scale,
                      metrics.mScrollableRect, mLayersUpdated, displayPort,
                      paintScale, mIsFirstPaint, fixedLayerMargins, offset);
 
     mIsFirstPaint = false;
@@ -855,17 +855,17 @@ AsyncCompositionManager::TransformScroll
     SetPageRect(mContentRect);
   }
 
   // We synchronise the viewport information with Java after sending the above
   // notifications, so that Java can take these into account in its response.
   // Calculate the absolute display port to send to Java
   LayerIntRect displayPort = RoundedToInt(
     (metrics.mCriticalDisplayPort.IsEmpty()
-      ? metrics.mDisplayPort
+      ? metrics.GetDisplayPort()
       : metrics.mCriticalDisplayPort
     ) * geckoZoom);
   displayPort += scrollOffsetLayerPixels;
 
   LayerMargin fixedLayerMargins(0, 0, 0, 0);
   ScreenPoint offset(0, 0);
 
   // Ideally we would initialize userZoom to AsyncPanZoomController::CalculateResolution(metrics)
--- a/gfx/layers/composite/LayerManagerComposite.cpp
+++ b/gfx/layers/composite/LayerManagerComposite.cpp
@@ -930,23 +930,23 @@ LayerManagerComposite::ComputeRenderInte
     bool hasLowPrecision = false;
     if (!metrics.mCriticalDisplayPort.IsEmpty()) {
       hasLowPrecision = true;
       highPrecisionMultiplier =
         GetDisplayportCoverage(metrics.mCriticalDisplayPort, transform, screenRect);
     }
 
     // Work out how much of the display-port covers the screen
-    if (!metrics.mDisplayPort.IsEmpty()) {
+    if (!metrics.GetDisplayPort().IsEmpty()) {
       if (hasLowPrecision) {
         lowPrecisionMultiplier =
-          GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect);
+          GetDisplayportCoverage(metrics.GetDisplayPort(), transform, screenRect);
       } else {
         lowPrecisionMultiplier = highPrecisionMultiplier =
-          GetDisplayportCoverage(metrics.mDisplayPort, transform, screenRect);
+          GetDisplayportCoverage(metrics.GetDisplayPort(), transform, screenRect);
       }
     }
   }
 
   // If none of the screen is covered, we have zero integrity.
   if (highPrecisionMultiplier <= 0.0f && lowPrecisionMultiplier <= 0.0f) {
     return 0.0f;
   }
--- a/gfx/src/nsDeviceContext.cpp
+++ b/gfx/src/nsDeviceContext.cpp
@@ -58,17 +58,18 @@ public:
     nsFontCache()   { MOZ_COUNT_CTOR(nsFontCache); }
 
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
 
     void Init(nsDeviceContext* aContext);
     void Destroy();
 
-    nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
+    nsresult GetMetricsFor(const nsFont& aFont,
+                           nsIAtom* aLanguage, bool aExplicitLanguage,
                            gfxFont::Orientation aOrientation,
                            gfxUserFontSet* aUserFontSet,
                            gfxTextPerfMetrics* aTextPerf,
                            nsFontMetrics*& aMetrics);
 
     void FontMetricsDeleted(const nsFontMetrics* aFontMetrics);
     void Compact();
     void Flush();
@@ -119,17 +120,18 @@ NS_IMETHODIMP
 nsFontCache::Observe(nsISupports*, const char* aTopic, const char16_t*)
 {
     if (!nsCRT::strcmp(aTopic, "memory-pressure"))
         Compact();
     return NS_OK;
 }
 
 nsresult
-nsFontCache::GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
+nsFontCache::GetMetricsFor(const nsFont& aFont,
+                           nsIAtom* aLanguage, bool aExplicitLanguage,
                            gfxFont::Orientation aOrientation,
                            gfxUserFontSet* aUserFontSet,
                            gfxTextPerfMetrics* aTextPerf,
                            nsFontMetrics*& aMetrics)
 {
     if (!aLanguage)
         aLanguage = mLocaleLanguage;
 
@@ -152,18 +154,18 @@ nsFontCache::GetMetricsFor(const nsFont&
             return NS_OK;
         }
     }
 
     // It's not in the cache. Get font metrics and then cache them.
 
     fm = new nsFontMetrics();
     NS_ADDREF(fm);
-    nsresult rv = fm->Init(aFont, aLanguage, aOrientation, mContext,
-                           aUserFontSet, aTextPerf);
+    nsresult rv = fm->Init(aFont, aLanguage, aExplicitLanguage, aOrientation,
+                           mContext, aUserFontSet, aTextPerf);
     if (NS_SUCCEEDED(rv)) {
         // the mFontMetrics list has the "head" at the end, because append
         // is cheaper than insert
         mFontMetrics.AppendElement(fm);
         aMetrics = fm;
         NS_ADDREF(aMetrics);
         return NS_OK;
     }
@@ -172,18 +174,18 @@ nsFontCache::GetMetricsFor(const nsFont&
 
     // One reason why Init() fails is because the system is running out of
     // resources. e.g., on Win95/98 only a very limited number of GDI
     // objects are available. Compact the cache and try again.
 
     Compact();
     fm = new nsFontMetrics();
     NS_ADDREF(fm);
-    rv = fm->Init(aFont, aLanguage, aOrientation, mContext, aUserFontSet,
-                  aTextPerf);
+    rv = fm->Init(aFont, aLanguage, aExplicitLanguage, aOrientation, mContext,
+                  aUserFontSet, aTextPerf);
     if (NS_SUCCEEDED(rv)) {
         mFontMetrics.AppendElement(fm);
         aMetrics = fm;
         return NS_OK;
     }
     fm->Destroy();
     NS_RELEASE(fm);
 
@@ -261,29 +263,31 @@ nsDeviceContext::~nsDeviceContext()
         mFontCache->Destroy();
         NS_RELEASE(mFontCache);
     }
 }
 
 nsresult
 nsDeviceContext::GetMetricsFor(const nsFont& aFont,
                                nsIAtom* aLanguage,
+                               bool aExplicitLanguage,
                                gfxFont::Orientation aOrientation,
                                gfxUserFontSet* aUserFontSet,
                                gfxTextPerfMetrics* aTextPerf,
                                nsFontMetrics*& aMetrics)
 {
     if (!mFontCache) {
         mFontCache = new nsFontCache();
         NS_ADDREF(mFontCache);
         mFontCache->Init(this);
     }
 
-    return mFontCache->GetMetricsFor(aFont, aLanguage, aOrientation,
-                                     aUserFontSet, aTextPerf, aMetrics);
+    return mFontCache->GetMetricsFor(aFont, aLanguage, aExplicitLanguage,
+                                     aOrientation, aUserFontSet, aTextPerf,
+                                     aMetrics);
 }
 
 nsresult
 nsDeviceContext::FlushFontCache(void)
 {
     if (mFontCache)
         mFontCache->Flush();
     return NS_OK;
--- a/gfx/src/nsDeviceContext.h
+++ b/gfx/src/nsDeviceContext.h
@@ -113,17 +113,18 @@ public:
      * Get the nsFontMetrics that describe the properties of
      * an nsFont.
      * @param aFont font description to obtain metrics for
      * @param aLanguage the language of the document
      * @param aMetrics out parameter for font metrics
      * @param aUserFontSet user font set
      * @return error status
      */
-    nsresult GetMetricsFor(const nsFont& aFont, nsIAtom* aLanguage,
+    nsresult GetMetricsFor(const nsFont& aFont,
+                           nsIAtom* aLanguage, bool aExplicitLanguage,
                            gfxFont::Orientation aOrientation,
                            gfxUserFontSet* aUserFontSet,
                            gfxTextPerfMetrics* aTextPerf,
                            nsFontMetrics*& aMetrics);
 
     /**
      * Notification when a font metrics instance created for this device is
      * about to be deleted
--- a/gfx/src/nsFontMetrics.cpp
+++ b/gfx/src/nsFontMetrics.cpp
@@ -98,17 +98,18 @@ nsFontMetrics::nsFontMetrics()
 
 nsFontMetrics::~nsFontMetrics()
 {
     if (mDeviceContext)
         mDeviceContext->FontMetricsDeleted(this);
 }
 
 nsresult
-nsFontMetrics::Init(const nsFont& aFont, nsIAtom* aLanguage,
+nsFontMetrics::Init(const nsFont& aFont,
+                    nsIAtom* aLanguage, bool aExplicitLanguage,
                     gfxFont::Orientation aOrientation,
                     nsDeviceContext *aContext,
                     gfxUserFontSet *aUserFontSet,
                     gfxTextPerfMetrics *aTextPerf)
 {
     NS_ABORT_IF_FALSE(mP2A == 0, "already initialized");
 
     mFont = aFont;
@@ -117,16 +118,17 @@ nsFontMetrics::Init(const nsFont& aFont,
     mDeviceContext = aContext;
     mP2A = mDeviceContext->AppUnitsPerDevPixel();
 
     gfxFontStyle style(aFont.style,
                        aFont.weight,
                        aFont.stretch,
                        gfxFloat(aFont.size) / mP2A,
                        aLanguage,
+                       aExplicitLanguage,
                        aFont.sizeAdjust,
                        aFont.systemFont,
                        mDeviceContext->IsPrinterSurface(),
                        aFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT,
                        aFont.synthesis & NS_FONT_SYNTHESIS_STYLE,
                        aFont.languageOverride);
 
     aFont.AddFontFeaturesToStyle(&style);
--- a/gfx/src/nsFontMetrics.h
+++ b/gfx/src/nsFontMetrics.h
@@ -51,17 +51,18 @@ public:
     NS_INLINE_DECL_REFCOUNTING(nsFontMetrics)
 
     /**
      * Initialize the font metrics. Call this after creating the font metrics.
      * Font metrics you get from the font cache do NOT need to be initialized
      *
      * @see nsDeviceContext#GetMetricsFor()
      */
-    nsresult Init(const nsFont& aFont, nsIAtom* aLanguage,
+    nsresult Init(const nsFont& aFont,
+                  nsIAtom* aLanguage, bool aExplicitLanguage,
                   gfxFont::Orientation aOrientation,
                   nsDeviceContext *aContext,
                   gfxUserFontSet *aUserFontSet,
                   gfxTextPerfMetrics *aTextPerf);
 
     /**
      * Destroy this font metrics. This breaks the association between
      * the font metrics and the device context.
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -218,17 +218,17 @@ TestAPZCTreeManager::MakeAPZCInstance(ui
   return new TestAsyncPanZoomController(aLayersId, aController, this, AsyncPanZoomController::USE_GESTURE_DETECTOR);
 }
 
 static FrameMetrics
 TestFrameMetrics()
 {
   FrameMetrics fm;
 
-  fm.mDisplayPort = CSSRect(0, 0, 10, 10);
+  fm.SetDisplayPort(CSSRect(0, 0, 10, 10));
   fm.mCompositionBounds = ParentLayerRect(0, 0, 10, 10);
   fm.mCriticalDisplayPort = CSSRect(0, 0, 10, 10);
   fm.mScrollableRect = CSSRect(0, 0, 100, 100);
 
   return fm;
 }
 
 class APZCBasicTester : public ::testing::Test {
@@ -811,17 +811,17 @@ TEST_F(APZCBasicTester, ComplexTransform
   transforms[1].PostScale(2.0f, 1.0f, 1.0f); // this is the 2.0 x-axis CSS transform on the child layer
 
   nsTArray<nsRefPtr<Layer> > layers;
   nsRefPtr<LayerManager> lm;
   nsRefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion, transforms, lm, layers);
 
   FrameMetrics metrics;
   metrics.mCompositionBounds = ParentLayerRect(0, 0, 24, 24);
-  metrics.mDisplayPort = CSSRect(-1, -1, 6, 6);
+  metrics.SetDisplayPort(CSSRect(-1, -1, 6, 6));
   metrics.SetScrollOffset(CSSPoint(10, 10));
   metrics.mScrollableRect = CSSRect(0, 0, 50, 50);
   metrics.SetCumulativeResolution(LayoutDeviceToLayerScale(2));
   metrics.mPresShellResolution = 2.0f;
   metrics.SetZoom(CSSToParentLayerScale(6));
   metrics.SetDevPixelsPerCSSPixel(CSSToLayoutDeviceScale(3));
   metrics.SetScrollId(FrameMetrics::START_SCROLL_ID);
 
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -2891,17 +2891,18 @@ gfxFont::InitFakeSmallCapsRun(gfxContext
                 chAction = runAction;
             } else {
                 if (ch != ToUpperCase(ch) || SpecialUpper(ch)) {
                     // ch is lower case
                     chAction = (aSyntheticLower ? kUppercaseReduce : kNoChange);
                 } else if (ch != ToLowerCase(ch)) {
                     // ch is upper case
                     chAction = (aSyntheticUpper ? kUppercaseReduce : kNoChange);
-                    if (mStyle.language == nsGkAtoms::el) {
+                    if (mStyle.explicitLanguage &&
+                        mStyle.language == nsGkAtoms::el) {
                         // In Greek, check for characters that will be modified by
                         // the GreekUpperCase mapping - this catches accented
                         // capitals where the accent is to be removed (bug 307039).
                         // These are handled by using the full-size font with the
                         // uppercasing transform.
                         mozilla::GreekCasing::State state;
                         uint32_t ch2 = mozilla::GreekCasing::UpperCase(ch, state);
                         if (ch != ch2 && !aSyntheticUpper) {
@@ -2943,17 +2944,18 @@ gfxFont::InitFakeSmallCapsRun(gfxContext
                 nsAutoString convertedString;
                 nsAutoTArray<bool,50> charsToMergeArray;
                 nsAutoTArray<bool,50> deletedCharsArray;
 
                 bool mergeNeeded = nsCaseTransformTextRunFactory::
                     TransformString(origString,
                                     convertedString,
                                     true,
-                                    mStyle.language,
+                                    mStyle.explicitLanguage
+                                      ? mStyle.language : nullptr,
                                     charsToMergeArray,
                                     deletedCharsArray);
 
                 if (mergeNeeded) {
                     // This is the hard case: the transformation caused chars
                     // to be inserted or deleted, so we can't shape directly
                     // into the destination textrun but have to handle the
                     // mismatch of character positions.
@@ -3579,38 +3581,41 @@ gfxFontStyle::gfxFontStyle() :
     language(nsGkAtoms::x_western),
     size(DEFAULT_PIXEL_FONT_SIZE), sizeAdjust(0.0f), baselineOffset(0.0f),
     languageOverride(NO_FONT_LANGUAGE_OVERRIDE),
     weight(NS_FONT_WEIGHT_NORMAL), stretch(NS_FONT_STRETCH_NORMAL),
     systemFont(true), printerFont(false), useGrayscaleAntialiasing(false),
     style(NS_FONT_STYLE_NORMAL),
     allowSyntheticWeight(true), allowSyntheticStyle(true),
     noFallbackVariantFeatures(true),
+    explicitLanguage(false),
     variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
     variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL)
 {
 }
 
 gfxFontStyle::gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
-                           gfxFloat aSize, nsIAtom *aLanguage,
+                           gfxFloat aSize,
+                           nsIAtom *aLanguage, bool aExplicitLanguage,
                            float aSizeAdjust, bool aSystemFont,
                            bool aPrinterFont,
                            bool aAllowWeightSynthesis,
                            bool aAllowStyleSynthesis,
                            const nsString& aLanguageOverride):
     language(aLanguage),
     size(aSize), sizeAdjust(aSizeAdjust), baselineOffset(0.0f),
     languageOverride(ParseFontLanguageOverride(aLanguageOverride)),
     weight(aWeight), stretch(aStretch),
     systemFont(aSystemFont), printerFont(aPrinterFont),
     useGrayscaleAntialiasing(false),
     style(aStyle),
     allowSyntheticWeight(aAllowWeightSynthesis),
     allowSyntheticStyle(aAllowStyleSynthesis),
     noFallbackVariantFeatures(true),
+    explicitLanguage(aExplicitLanguage),
     variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
     variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL)
 {
     MOZ_ASSERT(!mozilla::IsNaN(size));
     MOZ_ASSERT(!mozilla::IsNaN(sizeAdjust));
 
     if (weight > 900)
         weight = 900;
@@ -3639,16 +3644,17 @@ gfxFontStyle::gfxFontStyle(const gfxFont
     languageOverride(aStyle.languageOverride),
     weight(aStyle.weight), stretch(aStyle.stretch),
     systemFont(aStyle.systemFont), printerFont(aStyle.printerFont),
     useGrayscaleAntialiasing(aStyle.useGrayscaleAntialiasing),
     style(aStyle.style),
     allowSyntheticWeight(aStyle.allowSyntheticWeight),
     allowSyntheticStyle(aStyle.allowSyntheticStyle),
     noFallbackVariantFeatures(aStyle.noFallbackVariantFeatures),
+    explicitLanguage(aStyle.explicitLanguage),
     variantCaps(aStyle.variantCaps),
     variantSubSuper(aStyle.variantSubSuper)
 {
     featureSettings.AppendElements(aStyle.featureSettings);
     alternateValues.AppendElements(aStyle.alternateValues);
 }
 
 int8_t
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -56,17 +56,17 @@ namespace mozilla {
 namespace gfx {
 class GlyphRenderingOptions;
 }
 }
 
 struct gfxFontStyle {
     gfxFontStyle();
     gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
-                 gfxFloat aSize, nsIAtom *aLanguage,
+                 gfxFloat aSize, nsIAtom *aLanguage, bool aExplicitLanguage,
                  float aSizeAdjust, bool aSystemFont,
                  bool aPrinterFont,
                  bool aWeightSynthesis, bool aStyleSynthesis,
                  const nsString& aLanguageOverride);
     gfxFontStyle(const gfxFontStyle& aStyle);
 
     // the language (may be an internal langGroup code rather than an actual
     // language code) specified in the document or element's lang property,
@@ -138,16 +138,20 @@ struct gfxFontStyle {
     // Whether synthetic styles are allowed
     bool allowSyntheticWeight : 1;
     bool allowSyntheticStyle : 1;
 
     // some variant features require fallback which complicates the shaping
     // code, so set up a bool to indicate when shaping with fallback is needed
     bool noFallbackVariantFeatures : 1;
 
+    // whether the |language| field comes from explicit lang tagging in the
+    // document, or was inferred from charset/system locale
+    bool explicitLanguage : 1;
+
     // caps variant (small-caps, petite-caps, etc.)
     uint8_t variantCaps;
 
     // sub/superscript variant
     uint8_t variantSubSuper;
 
     // Return the final adjusted font size for the given aspect ratio.
     // Not meant to be called when sizeAdjust = 0.
@@ -176,16 +180,17 @@ struct gfxFontStyle {
             (style == other.style) &&
             (variantCaps == other.variantCaps) &&
             (variantSubSuper == other.variantSubSuper) &&
             (allowSyntheticWeight == other.allowSyntheticWeight) &&
             (allowSyntheticStyle == other.allowSyntheticStyle) &&
             (systemFont == other.systemFont) &&
             (printerFont == other.printerFont) &&
             (useGrayscaleAntialiasing == other.useGrayscaleAntialiasing) &&
+            (explicitLanguage == other.explicitLanguage) &&
             (weight == other.weight) &&
             (stretch == other.stretch) &&
             (language == other.language) &&
             (baselineOffset == other.baselineOffset) &&
             (*reinterpret_cast<const uint32_t*>(&sizeAdjust) ==
              *reinterpret_cast<const uint32_t*>(&other.sizeAdjust)) &&
             (featureSettings == other.featureSettings) &&
             (languageOverride == other.languageOverride) &&
--- a/gfx/thebes/gfxGraphiteShaper.cpp
+++ b/gfx/thebes/gfxGraphiteShaper.cpp
@@ -140,17 +140,17 @@ gfxGraphiteShaper::ShapeText(gfxContext 
     }
 
     gfxFontEntry *entry = mFont->GetFontEntry();
     uint32_t grLang = 0;
     if (style->languageOverride) {
         grLang = MakeGraphiteLangTag(style->languageOverride);
     } else if (entry->mLanguageOverride) {
         grLang = MakeGraphiteLangTag(entry->mLanguageOverride);
-    } else {
+    } else if (style->explicitLanguage) {
         nsAutoCString langString;
         style->language->ToUTF8String(langString);
         grLang = GetGraphiteTagForLang(langString);
     }
     gr_feature_val *grFeatures = gr_face_featureval_for_lang(mGrFace, grLang);
 
     // if style contains font-specific features
     nsDataHashtable<nsUint32HashKey,uint32_t> mergedFeatures;
--- a/gfx/thebes/gfxHarfBuzzShaper.cpp
+++ b/gfx/thebes/gfxHarfBuzzShaper.cpp
@@ -1223,21 +1223,23 @@ gfxHarfBuzzShaper::ShapeText(gfxContext 
     }
     hb_buffer_set_script(buffer, scriptTag);
 
     hb_language_t language;
     if (style->languageOverride) {
         language = hb_ot_tag_to_language(style->languageOverride);
     } else if (entry->mLanguageOverride) {
         language = hb_ot_tag_to_language(entry->mLanguageOverride);
-    } else {
+    } else if (style->explicitLanguage) {
         nsCString langString;
         style->language->ToUTF8String(langString);
         language =
             hb_language_from_string(langString.get(), langString.Length());
+    } else {
+        language = hb_ot_tag_to_language(HB_OT_TAG_DEFAULT_LANGUAGE);
     }
     hb_buffer_set_language(buffer, language);
 
     uint32_t length = aLength;
     hb_buffer_add_utf16(buffer,
                         reinterpret_cast<const uint16_t*>(aText),
                         length, 0, length);
 
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1073,16 +1073,30 @@ MOZ_ARG_ENABLE_BOOL(memory-sanitizer,
     MOZ_MSAN= )
 if test -n "$MOZ_MSAN"; then
     MOZ_LLVM_HACKS=1
     AC_DEFINE(MOZ_MSAN)
     MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
 fi
 AC_SUBST(MOZ_MSAN)
 
+dnl ========================================================
+dnl = Use Thread Sanitizer
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(thread-sanitizer,
+[  --enable-thread-sanitizer       Enable Thread Sanitizer (default=no)],
+   MOZ_TSAN=1,
+   MOZ_TSAN= )
+if test -n "$MOZ_TSAN"; then
+    MOZ_LLVM_HACKS=1
+    AC_DEFINE(MOZ_TSAN)
+    MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer)
+fi
+AC_SUBST(MOZ_TSAN)
+
 # The LLVM symbolizer is used by all sanitizers
 AC_SUBST(LLVM_SYMBOLIZER)
 
 dnl ========================================================
 dnl = Enable hacks required for LLVM instrumentations
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(llvm-hacks,
 [  --enable-llvm-hacks       Enable workarounds required for several LLVM instrumentations (default=no)],
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/recover-lambdas-bug1114566.js
@@ -0,0 +1,2 @@
+
+(new Function("return (function o() {}).caller;"))();
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1119,18 +1119,22 @@ FrameIter::matchCallee(JSContext *cx, Ha
     // compare it against the calleeTemplate.
     if (!fun->isLambda() || !currentCallee->isLambda())
         return currentCallee == fun;
 
     // Use the same condition as |js::CloneFunctionObject|, to know if we should
     // expect both functions to have the same JSScript. If so, and if they are
     // different, then they cannot be equal.
     bool useSameScript = CloneFunctionObjectUseSameScript(fun->compartment(), currentCallee);
-    if (useSameScript && currentCallee->nonLazyScript() != fun->nonLazyScript())
+    if (useSameScript &&
+        (currentCallee->hasScript() != fun->hasScript() ||
+         currentCallee->nonLazyScript() != fun->nonLazyScript()))
+    {
         return false;
+    }
 
     // If none of the previous filters worked, then take the risk of
     // invalidating the frame to identify the JSFunction.
     return callee(cx) == fun;
 }
 
 unsigned
 FrameIter::numActualArgs() const
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -405,26 +405,24 @@ RestyleManager::RecomputePosition(nsIFra
     StyleChangeReflow(aFrame, nsChangeHint_NeedReflow);
     return false;
   }
 
   aFrame->SchedulePaint();
 
   // For relative positioning, we can simply update the frame rect
   if (display->IsRelativelyPositionedStyle()) {
-    if (display->IsInnerTableStyle()) {
-      // We don't currently support relative positioning of inner table
-      // elements (bug 35168).  If we apply offsets to things we haven't
-      // previously offset, we'll get confused.  So bail.
-      return true;
-    }
-
-
     // Move the frame
     if (display->mPosition == NS_STYLE_POSITION_STICKY) {
+      if (display->IsInnerTableStyle()) {
+        // We don't currently support sticky positioning of inner table
+        // elements (bug 975644). Bail.
+        return true;
+      }
+
       // Update sticky positioning for an entire element at once, starting with
       // the first continuation or ib-split sibling.
       // It's rare that the frame we already have isn't already the first
       // continuation or ib-split sibling, but it can happen when styles differ
       // across continuations such as ::first-line or ::first-letter, and in
       // those cases we will generally (but maybe not always) do the work twice.
       nsIFrame *firstContinuation =
         nsLayoutUtils::FirstContinuationOrIBSplitSibling(aFrame);
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -711,19 +711,19 @@ nsDisplayScrollLayer::ComputeFrameMetric
   ViewID scrollId = FrameMetrics::NULL_SCROLL_ID;
   nsIContent* content = aScrollFrame ? aScrollFrame->GetContent() : nullptr;
   if (content) {
     if (!aForceNullScrollId) {
       scrollId = nsLayoutUtils::FindOrCreateIDFor(content);
     }
     nsRect dp;
     if (nsLayoutUtils::GetDisplayPort(content, &dp)) {
-      metrics.mDisplayPort = CSSRect::FromAppUnits(dp);
+      metrics.SetDisplayPort(CSSRect::FromAppUnits(dp));
       nsLayoutUtils::LogTestDataForPaint(aLayer->Manager(), scrollId, "displayport",
-          metrics.mDisplayPort);
+          metrics.GetDisplayPort());
     }
     if (nsLayoutUtils::GetCriticalDisplayPort(content, &dp)) {
       metrics.mCriticalDisplayPort = CSSRect::FromAppUnits(dp);
     }
     DisplayPortMarginsPropertyData* marginsData =
         static_cast<DisplayPortMarginsPropertyData*>(content->GetProperty(nsGkAtoms::DisplayPortMargins));
     if (marginsData) {
       metrics.SetDisplayPortMargins(marginsData->mMargins);
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3777,23 +3777,25 @@ nsLayoutUtils::GetFontMetricsForStyleCon
 
   // When aInflation is 1.0, avoid making a local copy of the nsFont.
   // This also avoids running font.size through floats when it is large,
   // which would be lossy.  Fortunately, in such cases, aInflation is
   // guaranteed to be 1.0f.
   if (aInflation == 1.0f) {
     return pc->DeviceContext()->GetMetricsFor(styleFont->mFont,
                                               styleFont->mLanguage,
+                                              styleFont->mExplicitLanguage,
                                               orientation, fs, tp,
                                               *aFontMetrics);
   }
 
   nsFont font = styleFont->mFont;
   font.size = NSToCoordRound(font.size * aInflation);
   return pc->DeviceContext()->GetMetricsFor(font, styleFont->mLanguage,
+                                            styleFont->mExplicitLanguage,
                                             orientation, fs, tp,
                                             *aFontMetrics);
 }
 
 nsIFrame*
 nsLayoutUtils::FindChildContainingDescendant(nsIFrame* aParent, nsIFrame* aDescendantFrame)
 {
   nsIFrame* result = aDescendantFrame;
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -10437,29 +10437,26 @@ void ReflowCountMgr::PaintCount(const ch
       DrawTarget* drawTarget = aRenderingContext->GetDrawTarget();
       int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
 
       aRenderingContext->ThebesContext()->Save();
       gfxPoint devPixelOffset =
         nsLayoutUtils::PointToGfxPoint(aOffset, appUnitsPerDevPixel);
       aRenderingContext->ThebesContext()->SetMatrix(
         aRenderingContext->ThebesContext()->CurrentMatrix().Translate(devPixelOffset));
+
+      // We don't care about the document language or user fonts here;
+      // just get a default Latin font.
       nsFont font(eFamily_serif, NS_FONT_STYLE_NORMAL,
                   NS_FONT_WEIGHT_NORMAL, NS_FONT_STRETCH_NORMAL, 0,
                   nsPresContext::CSSPixelsToAppUnits(11));
-
       nsRefPtr<nsFontMetrics> fm;
       aPresContext->DeviceContext()->GetMetricsFor(font,
-        // We have one frame, therefore we must have a root...
-        aPresContext->GetPresShell()->GetRootFrame()->
-          StyleFont()->mLanguage,
-        gfxFont::eHorizontal,
-        aPresContext->GetUserFontSet(),
-        aPresContext->GetTextPerfMetrics(),
-        *getter_AddRefs(fm));
+        nsGkAtoms::x_western, false, gfxFont::eHorizontal, nullptr,
+        aPresContext->GetTextPerfMetrics(), *getter_AddRefs(fm));
 
       char buf[16];
       int len = sprintf(buf, "%d", counter->mCount);
       nscoord x = 0, y = fm->MaxAscent();
       nscoord width, height = fm->MaxHeight();
       fm->SetTextRunRTL(false);
       width = fm->GetWidth(buf, len, aRenderingContext);;
 
--- a/layout/generic/MathMLTextRunFactory.cpp
+++ b/layout/generic/MathMLTextRunFactory.cpp
@@ -734,18 +734,20 @@ MathMLTextRunFactory::RebuildTextRun(nsT
   }
   gfxFontGroup* newFontGroup = nullptr;
 
   // Get the correct gfxFontGroup that corresponds to the earlier font changes.
   if (length) {
     font.size = NSToCoordRound(font.size * mFontInflation);
     nsPresContext* pc = styles[0]->PresContext();
     nsRefPtr<nsFontMetrics> metrics;
+    const nsStyleFont* styleFont = styles[0]->StyleFont();
     pc->DeviceContext()->GetMetricsFor(font,
-                                       styles[0]->StyleFont()->mLanguage,
+                                       styleFont->mLanguage,
+                                       styleFont->mExplicitLanguage,
                                        gfxFont::eHorizontal,
                                        pc->GetUserFontSet(),
                                        pc->GetTextPerfMetrics(),
                                        *getter_AddRefs(metrics));
     if (metrics) {
       newFontGroup = metrics->GetThebesFontGroup();
     }
   }
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -5297,16 +5297,29 @@ nsIFrame::MovePositionBy(const nsPoint& 
       (Properties().Get(nsIFrame::ComputedOffsetProperty()));
   }
   nsHTMLReflowState::ApplyRelativePositioning(this, computedOffsets ?
                                               *computedOffsets : nsMargin(),
                                               &position);
   SetPosition(position);
 }
 
+nsRect
+nsIFrame::GetNormalRect() const
+{
+  // It might be faster to first check
+  // StyleDisplay()->IsRelativelyPositionedStyle().
+  nsPoint* normalPosition = static_cast<nsPoint*>
+    (Properties().Get(NormalPositionProperty()));
+  if (normalPosition) {
+    return nsRect(*normalPosition, GetSize());
+  }
+  return GetRect();
+}
+
 nsPoint
 nsIFrame::GetNormalPosition() const
 {
   // It might be faster to first check
   // StyleDisplay()->IsRelativelyPositionedStyle().
   nsPoint* normalPosition = static_cast<nsPoint*>
     (Properties().Get(NormalPositionProperty()));
   if (normalPosition) {
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -745,16 +745,21 @@ public:
    */
   void MovePositionBy(mozilla::WritingMode aWritingMode,
                       const mozilla::LogicalPoint& aTranslation)
   {
     MovePositionBy(aTranslation.GetPhysicalPoint(aWritingMode, 0));
   }
 
   /**
+   * Return frame's rect without relative positioning
+   */
+  nsRect GetNormalRect() const;
+
+  /**
    * Return frame's position without relative positioning
    */
   nsPoint GetNormalPosition() const;
   mozilla::LogicalPoint
   GetLogicalNormalPosition(mozilla::WritingMode aWritingMode,
                            nscoord aContainerWidth) const
   {
     return mozilla::LogicalPoint(aWritingMode,
--- a/layout/generic/nsPageFrame.cpp
+++ b/layout/generic/nsPageFrame.cpp
@@ -593,17 +593,17 @@ nsPageFrame::PaintHeaderFooter(nsRenderi
       return;
   }
 
   nsRect rect(aPt, mRect.Size());
   aRenderingContext.ThebesContext()->SetColor(NS_RGB(0,0,0));
 
   // Get the FontMetrics to determine width.height of strings
   nsRefPtr<nsFontMetrics> fontMet;
-  pc->DeviceContext()->GetMetricsFor(mPD->mHeadFootFont, nullptr,
+  pc->DeviceContext()->GetMetricsFor(mPD->mHeadFootFont, nullptr, false,
                                      gfxFont::eHorizontal,
                                      pc->GetUserFontSet(),
                                      pc->GetTextPerfMetrics(),
                                      *getter_AddRefs(fontMet));
 
   nscoord ascent = 0;
   nscoord visibleHeight = 0;
   if (fontMet) {
--- a/layout/generic/nsTextRunTransformations.cpp
+++ b/layout/generic/nsTextRunTransformations.cpp
@@ -306,18 +306,21 @@ nsCaseTransformTextRunFactory::Transform
     uint32_t ch = str[i];
 
     nsStyleContext* styleContext;
     if (aTextRun) {
       styleContext = aTextRun->mStyles[i];
       style = aAllUppercase ? NS_STYLE_TEXT_TRANSFORM_UPPERCASE :
         styleContext->StyleText()->mTextTransform;
 
-      if (lang != styleContext->StyleFont()->mLanguage) {
-        lang = styleContext->StyleFont()->mLanguage;
+      const nsStyleFont* styleFont = styleContext->StyleFont();
+      nsIAtom* newLang = styleFont->mExplicitLanguage
+                         ? styleFont->mLanguage : nullptr;
+      if (lang != newLang) {
+        lang = newLang;
         languageSpecificCasing = GetCasingFor(lang);
         greekState.Reset();
         irishState.Reset();
         irishMark = uint32_t(-1);
       }
     }
 
     int extraChars = 0;
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -989,20 +989,22 @@ nsMathMLChar::SetFontFamily(nsPresContex
   }
 
   const FontFamilyList& familyList =
     aGlyphCode.font ? glyphCodeFont : aDefaultFamilyList;
 
   if (!*aFontGroup || !(aFont.fontlist == familyList)) {
     nsFont font = aFont;
     font.fontlist = familyList;
+    const nsStyleFont* styleFont = mStyleContext->StyleFont();
     nsRefPtr<nsFontMetrics> fm;
     aPresContext->DeviceContext()->
       GetMetricsFor(font,
-                    mStyleContext->StyleFont()->mLanguage,
+                    styleFont->mLanguage,
+                    styleFont->mExplicitLanguage,
                     gfxFont::eHorizontal,
                     aPresContext->GetUserFontSet(),
                     aPresContext->GetTextPerfMetrics(),
                     *getter_AddRefs(fm));
     // Set the font if it is an unicode table
     // or if the same family name has been found
     gfxFont *firstFont = fm->GetThebesFontGroup()->GetFirstValidFont();
     FontFamilyList firstFontList;
@@ -1532,20 +1534,22 @@ nsMathMLChar::StretchInternal(nsPresCont
   nsStretchDirection direction = nsMathMLOperators::GetStretchyDirection(mData);
 
   // Set default font and get the default bounding metrics
   // mStyleContext is a leaf context used only when stretching happens.
   // For the base size, the default font should come from the parent context
   nsFont font = mStyleContext->GetParent()->StyleFont()->mFont;
   NormalizeDefaultFont(font, aFontSizeInflation);
 
+  const nsStyleFont* styleFont = mStyleContext->StyleFont();
   nsRefPtr<nsFontMetrics> fm;
   aPresContext->DeviceContext()->
     GetMetricsFor(font,
-                  mStyleContext->StyleFont()->mLanguage,
+                  styleFont->mLanguage,
+                  styleFont->mExplicitLanguage,
                   gfxFont::eHorizontal,
                   aPresContext->GetUserFontSet(),
                   aPresContext->GetTextPerfMetrics(),
                   *getter_AddRefs(fm));
   uint32_t len = uint32_t(mData.Length());
   nsAutoPtr<gfxTextRun> textRun;
   textRun = fm->GetThebesFontGroup()->
     MakeTextRun(static_cast<const char16_t*>(mData.get()), len, aThebesContext,
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/983985-1-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Bug 983985</title>
+<style>
+@font-face {
+  font-family: test;
+  src: url(../fonts/dejavu-sans/DejaVuSans.ttf);
+}
+body {
+  font: 36px test;
+}
+</style>
+</head>
+<body>
+hello &#x530; world
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/983985-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Bug 983985</title>
+<style>
+@font-face {
+  font-family: test;
+  src: url(../fonts/dejavu-sans/DejaVuSans.ttf);
+}
+body {
+  font: 36px test;
+}
+</style>
+</head>
+<body>
+hello <i>&#x530;</i> world
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/983985-2-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Bug 983985</title>
+<style>
+@font-face {
+  font-family: test;
+  src: url(../fonts/dejavu-sans/DejaVuSans.ttf);
+}
+.outer {
+  font: italic 36px test;
+  width: 2.5em; /* enough to display "hello" but not the hexbox */
+  overflow: hidden;
+}
+.inner {
+  width: 10em;
+}
+</style>
+</head>
+<body>
+<div class="outer">
+<div class="inner">
+hello <em>&#x530;</em> world
+</div>
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/983985-2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Bug 983985</title>
+<style>
+@font-face {
+  font-family: test;
+  src: url(../fonts/dejavu-sans/DejaVuSans.ttf);
+}
+.outer {
+  font: italic 36px test;
+  width: 2.5em; /* enough to display "hello" but not the hexbox */
+  overflow: hidden;
+}
+.inner {
+  width: 10em;
+}
+</style>
+</head>
+<body>
+<div class="outer">
+<div class="inner">
+hello &#x530; world
+</div>
+</div>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1805,16 +1805,18 @@ fuzzy-if(B2G,1,7) == 942672-1.html 94267
 pref(layout.css.overflow-clip-box.enabled,true) fuzzy(50,31) == 966992-1.html 966992-1-ref.html
 skip-if(Android) == 966510-1.html 966510-1-ref.html # scrollable elements other than the root probably won't work well on android until bug 776030 is fixed
 skip-if(Android) == 966510-2.html 966510-2-ref.html # same as above
 == 978911-1.svg 978911-1-ref.svg
 == 983084-1.html 983084-1-ref.html
 == 983084-2.html 983084-2-ref.html
 == 983084-3.html 983084-1-ref.html
 == 983691-1.html 983691-ref.html
+HTTP(..) == 983985-1.html 983985-1-ref.html
+HTTP(..) == 983985-2.html 983985-2-ref.html
 == 985303-1a.html 985303-1-ref.html
 == 985303-1b.html 985303-1-ref.html
 == 987680-1.html 987680-1-ref.html
 fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,24) == 991046-1.html 991046-1-ref.html
 pref(layout.css.overflow-clip-box.enabled,true) == 992447.html 992447-ref.html
 == 1003425-1.html 1003425-1-ref.html
 == 1003425-2.html 1003425-2-ref.html
 pref(layout.css.sticky.enabled,true) == 1005405-1.html 1005405-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/reftest.list
@@ -0,0 +1,8 @@
+== table-collapse-1.html table-collapse-1-ref.html
+== table-collapse-2.html table-collapse-2-ref.html
+== table-collapse-3.html table-collapse-3-ref.html
+== table-collapse-4.html table-collapse-4-ref.html
+== table-separate-1.html table-separate-1-ref.html
+== table-separate-2.html table-separate-2-ref.html
+== table-separate-3.html table-separate-3-ref.html
+== table-separate-4.html table-separate-4-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-collapse-1-ref.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      border-collapse: collapse;
+      position: absolute;
+    }
+    #first-table {
+      left: 0px;
+    }
+    #second-table {
+      left: 100px;
+    }
+    #third-table {
+      left: 200px;
+    }
+    #fourth-table {
+      left: 300px;
+    }
+    td {
+      border-style: solid;
+      border-width: 5px;
+      border-color: transparent;
+      color: rgba(0,0,0,0);
+    }
+    #first-table td {
+      border-color: gold;
+    }
+    .bg-blue {
+      background-color: blue;
+    }
+    .bg-cyan {
+      background-color: cyan;
+    }
+    .bg-grey {
+      background-color: grey;
+    }
+    .bg-green {
+      background-color: green;
+    }
+    .bg-khaki {
+      background-color: khaki;
+    }
+    .bg-purple {
+      background-color: purple;
+    }
+    .bg-white {
+      background-color: white;
+    }
+    .show-text {
+      color: rgba(0,0,0,1);
+    }
+  </style>
+</head>
+<body>
+  <table id="first-table">
+    <thead>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td rowspan=2 class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-khaki">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="second-table">
+    <thead>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-green">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td rowspan=2 class="bg-green">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-grey show-text">&nbsp;</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-green">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white show-text">&nbsp;</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="third-table">
+    <thead>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td rowspan=2 class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-green">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-cyan show-text">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="fourth-table">
+    <thead>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white" rowspan=2>YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+      </tr>
+      <tr>
+        <td class="bg-white" colspan=2>XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-collapse-1.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      background-color: red;
+      border-collapse: collapse;
+    }
+    thead {
+      background-color: pink;
+    }
+    tbody {
+      background-color: grey;
+    }
+    tfoot {
+      background-color: orange;
+    }
+    tr {
+      background-color: green;
+    }
+    td:first-child {
+      background-color: cyan;
+    }
+    td {
+      background-color: blue;
+      border-style: solid;
+      border-width: 5px;
+      border-color: gold;
+    }
+    colgroup {
+      background-color: purple;
+    }
+    col:first-child {
+      background-color: khaki;
+    }
+    .rel {
+      position: relative;
+      left: 100px;
+    }
+  </style>
+</head>
+<body>
+  <table>
+    <colgroup>
+      <col>
+      <col>
+    </colgroup>
+    <thead class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td class="rel">YYY</td>
+      </tr>
+    </thead>
+    <tbody class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+      <tr>
+        <td>XXX</td>
+        <td rowspan=2 class="rel">YYY</td>
+      </tr>
+      <tr class="rel">
+        <td class="rel">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="rel">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-collapse-2-ref.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      border-collapse: collapse;
+      position: absolute;
+    }
+    #first-table {
+      left: 300px;
+    }
+    #second-table {
+      left: 200px;
+    }
+    #third-table {
+      left: 100px;
+    }
+    #fourth-table {
+      left: 0px;
+    }
+    td {
+      border-style: solid;
+      border-width: 5px;
+      border-color: transparent;
+      color: rgba(0,0,0,0);
+    }
+    #first-table td {
+      border-color: gold;
+    }
+    .bg-blue {
+      background-color: blue;
+    }
+    .bg-cyan {
+      background-color: cyan;
+    }
+    .bg-grey {
+      background-color: grey;
+    }
+    .bg-green {
+      background-color: green;
+    }
+    .bg-khaki {
+      background-color: khaki;
+    }
+    .bg-purple {
+      background-color: purple;
+    }
+    .bg-white {
+      background-color: white;
+    }
+    .show-text {
+      color: rgba(0,0,0,1);
+    }
+  </style>
+</head>
+<body>
+  <table id="first-table">
+    <thead>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td rowspan=2 class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-khaki">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="second-table">
+    <thead>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-green">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td rowspan=2 class="bg-green">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-grey show-text">&nbsp;</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-green">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white show-text">&nbsp;</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="third-table">
+    <thead>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td rowspan=2 class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-green">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-cyan show-text">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="fourth-table">
+    <thead>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white" rowspan=2>YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+      </tr>
+      <tr>
+        <td class="bg-white" colspan=2>XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-collapse-2.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      background-color: red;
+      border-collapse: collapse;
+      position: absolute;
+      left: 300px;
+    }
+    thead {
+      background-color: pink;
+    }
+    tbody {
+      background-color: grey;
+    }
+    tfoot {
+      background-color: orange;
+    }
+    tr {
+      background-color: green;
+    }
+    td:first-child {
+      background-color: cyan;
+    }
+    td {
+      background-color: blue;
+      border-style: solid;
+      border-width: 5px;
+      border-color: gold;
+    }
+    colgroup {
+      background-color: purple;
+    }
+    col:first-child {
+      background-color: khaki;
+    }
+    .rel {
+      position: relative;
+      right: 100px;
+    }
+  </style>
+</head>
+<body>
+  <table>
+    <colgroup>
+      <col>
+      <col>
+    </colgroup>
+    <thead class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td class="rel">YYY</td>
+      </tr>
+    </thead>
+    <tbody class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+      <tr>
+        <td>XXX</td>
+        <td rowspan=2 class="rel">YYY</td>
+      </tr>
+      <tr class="rel">
+        <td class="rel">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="rel">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-collapse-3-ref.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      border-collapse: collapse;
+      position: absolute;
+    }
+    #first-table {
+      top: 0px;
+    }
+    #second-table {
+      top: 175px;
+    }
+    #third-table {
+      top: 350px;
+    }
+    #fourth-table {
+      top: 525px;
+    }
+    td {
+      border-style: solid;
+      border-width: 5px;
+      border-color: transparent;
+      color: rgba(0,0,0,0);
+    }
+    #first-table td {
+      border-color: gold;
+    }
+    .bg-blue {
+      background-color: blue;
+    }
+    .bg-cyan {
+      background-color: cyan;
+    }
+    .bg-grey {
+      background-color: grey;
+    }
+    .bg-green {
+      background-color: green;
+    }
+    .bg-khaki {
+      background-color: khaki;
+    }
+    .bg-purple {
+      background-color: purple;
+    }
+    .bg-white {
+      background-color: white;
+    }
+    .show-text {
+      color: rgba(0,0,0,1);
+    }
+  </style>
+</head>
+<body>
+  <table id="first-table">
+    <thead>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td rowspan=2 class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-khaki">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="second-table">
+    <thead>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-green">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td rowspan=2 class="bg-green">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-grey show-text">&nbsp;</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-green">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white show-text">&nbsp;</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="third-table">
+    <thead>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td rowspan=2 class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-green">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-cyan show-text">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="fourth-table">
+    <thead>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white" rowspan=2>YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+      </tr>
+      <tr>
+        <td class="bg-white" colspan=2>XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-collapse-3.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      background-color: red;
+      border-collapse: collapse;
+    }
+    thead {
+      background-color: pink;
+    }
+    tbody {
+      background-color: grey;
+    }
+    tfoot {
+      background-color: orange;
+    }
+    tr {
+      background-color: green;
+    }
+    td:first-child {
+      background-color: cyan;
+    }
+    td {
+      background-color: blue;
+      border-style: solid;
+      border-width: 5px;
+      border-color: gold;
+    }
+    colgroup {
+      background-color: purple;
+    }
+    col:first-child {
+      background-color: khaki;
+    }
+    .rel {
+      position: relative;
+      top: 175px;
+    }
+  </style>
+</head>
+<body>
+  <table>
+    <colgroup>
+      <col>
+      <col>
+    </colgroup>
+    <thead class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td class="rel">YYY</td>
+      </tr>
+    </thead>
+    <tbody class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+      <tr>
+        <td>XXX</td>
+        <td rowspan=2 class="rel">YYY</td>
+      </tr>
+      <tr class="rel">
+        <td class="rel">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="rel">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-collapse-4-ref.html
@@ -0,0 +1,180 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      border-collapse: collapse;
+      position: absolute;
+    }
+    #first-table {
+      top: 525px;
+    }
+    #second-table {
+      top: 350px;
+    }
+    #third-table {
+      top: 175px;
+    }
+    #fourth-table {
+      top: 0px;
+    }
+    td {
+      border-style: solid;
+      border-width: 5px;
+      border-color: transparent;
+      color: rgba(0,0,0,0);
+    }
+    #first-table td {
+      border-color: gold;
+    }
+    .bg-blue {
+      background-color: blue;
+    }
+    .bg-cyan {
+      background-color: cyan;
+    }
+    .bg-grey {
+      background-color: grey;
+    }
+    .bg-green {
+      background-color: green;
+    }
+    .bg-khaki {
+      background-color: khaki;
+    }
+    .bg-purple {
+      background-color: purple;
+    }
+    .bg-white {
+      background-color: white;
+    }
+    .show-text {
+      color: rgba(0,0,0,1);
+    }
+  </style>
+</head>
+<body>
+  <table id="first-table">
+    <thead>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td rowspan=2 class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-khaki">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="second-table">
+    <thead>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-green">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td rowspan=2 class="bg-green">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-grey show-text">&nbsp;</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-green">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white show-text">&nbsp;</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="third-table">
+    <thead>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td rowspan=2 class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-green">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-cyan show-text">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="fourth-table">
+    <thead>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white" rowspan=2>YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+      </tr>
+      <tr>
+        <td class="bg-white" colspan=2>XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-collapse-4.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      background-color: red;
+      border-collapse: collapse;
+      position: absolute;
+      top: 525px;
+    }
+    thead {
+      background-color: pink;
+    }
+    tbody {
+      background-color: grey;
+    }
+    tfoot {
+      background-color: orange;
+    }
+    tr {
+      background-color: green;
+    }
+    td:first-child {
+      background-color: cyan;
+    }
+    td {
+      background-color: blue;
+      border-style: solid;
+      border-width: 5px;
+      border-color: gold;
+    }
+    colgroup {
+      background-color: purple;
+    }
+    col:first-child {
+      background-color: khaki;
+    }
+    .rel {
+      position: relative;
+      bottom: 175px;
+    }
+  </style>
+</head>
+<body>
+  <table>
+    <colgroup>
+      <col>
+      <col>
+    </colgroup>
+    <thead class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td class="rel">YYY</td>
+      </tr>
+    </thead>
+    <tbody class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+      <tr>
+        <td>XXX</td>
+        <td rowspan=2 class="rel">YYY</td>
+      </tr>
+      <tr class="rel">
+        <td class="rel">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="rel">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-separate-1-ref.html
@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      border-collapse: separate;
+      position: absolute;
+    }
+    #first-table {
+      background-color: red;
+      left: 0px;
+    }
+    #second-table {
+      left: 100px;
+    }
+    #third-table {
+      left: 200px;
+    }
+    #fourth-table {
+      left: 300px;
+    }
+    td {
+      border-style: solid;
+      border-width: 5px;
+      border-color: transparent;
+      color: rgba(0,0,0,0);
+    }
+    .bg-blue {
+      background-color: blue;
+    }
+    .bg-cyan {
+      background-color: cyan;
+    }
+    .bg-grey {
+      background-color: grey;
+    }
+    .bg-green {
+      background-color: green;
+    }
+    .bg-khaki {
+      background-color: khaki;
+    }
+    .bg-purple {
+      background-color: purple;
+    }
+    .bg-white {
+      background-color: white;
+    }
+    .show-text {
+      border-color: gold;
+      color: rgba(0,0,0,1);
+    }
+  </style>
+</head>
+<body>
+  <table id="first-table">
+    <thead>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td rowspan=2 class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-khaki">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-border show-text">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="second-table">
+    <thead>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-green">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td rowspan=2 class="bg-green">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-grey">&nbsp;</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-green">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">&nbsp;</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="third-table">
+    <thead>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td rowspan=2 class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-green">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-cyan show-text">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="fourth-table">
+    <thead>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white" rowspan=2>YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+      </tr>
+      <tr>
+        <td class="bg-white" colspan=2>XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-separate-1.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      background-color: red;
+      border-collapse: separate;
+    }
+    thead {
+      background-color: pink;
+    }
+    tbody {
+      background-color: grey;
+    }
+    tfoot {
+      background-color: orange;
+    }
+    tr {
+      background-color: green;
+    }
+    td:first-child {
+      background-color: cyan;
+    }
+    td {
+      background-color: blue;
+      border-style: solid;
+      border-width: 5px;
+      border-color: gold;
+    }
+    colgroup {
+      background-color: purple;
+    }
+    col:first-child {
+      background-color: khaki;
+    }
+    .rel {
+      position: relative;
+      left: 100px;
+    }
+  </style>
+</head>
+<body>
+  <table>
+    <colgroup>
+      <col>
+      <col>
+    </colgroup>
+    <thead class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td class="rel">YYY</td>
+      </tr>
+    </thead>
+    <tbody class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+      <tr>
+        <td>XXX</td>
+        <td rowspan=2 class="rel">YYY</td>
+      </tr>
+      <tr class="rel">
+        <td class="rel">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="rel">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-separate-2-ref.html
@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      border-collapse: separate;
+      position: absolute;
+    }
+    #first-table {
+      background-color: red;
+      left: 300px;
+    }
+    #second-table {
+      left: 200px;
+    }
+    #third-table {
+      left: 100px;
+    }
+    #fourth-table {
+      left: 0px;
+    }
+    td {
+      border-style: solid;
+      border-width: 5px;
+      border-color: transparent;
+      color: rgba(0,0,0,0);
+    }
+    .bg-blue {
+      background-color: blue;
+    }
+    .bg-cyan {
+      background-color: cyan;
+    }
+    .bg-grey {
+      background-color: grey;
+    }
+    .bg-green {
+      background-color: green;
+    }
+    .bg-khaki {
+      background-color: khaki;
+    }
+    .bg-purple {
+      background-color: purple;
+    }
+    .bg-white {
+      background-color: white;
+    }
+    .show-text {
+      border-color: gold;
+      color: rgba(0,0,0,1);
+    }
+  </style>
+</head>
+<body>
+  <table id="first-table">
+    <thead>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td rowspan=2 class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-khaki">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-border show-text">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="second-table">
+    <thead>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-green">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td rowspan=2 class="bg-green">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-grey">&nbsp;</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-green">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">&nbsp;</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="third-table">
+    <thead>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td rowspan=2 class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-green">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-cyan show-text">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="fourth-table">
+    <thead>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white" rowspan=2>YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+      </tr>
+      <tr>
+        <td class="bg-white" colspan=2>XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-separate-2.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      background-color: red;
+      border-collapse: separate;
+      position: absolute;
+      left: 300px;
+    }
+    thead {
+      background-color: pink;
+    }
+    tbody {
+      background-color: grey;
+    }
+    tfoot {
+      background-color: orange;
+    }
+    tr {
+      background-color: green;
+    }
+    td:first-child {
+      background-color: cyan;
+    }
+    td {
+      background-color: blue;
+      border-style: solid;
+      border-width: 5px;
+      border-color: gold;
+    }
+    colgroup {
+      background-color: purple;
+    }
+    col:first-child {
+      background-color: khaki;
+    }
+    .rel {
+      position: relative;
+      right: 100px;
+    }
+  </style>
+</head>
+<body>
+  <table>
+    <colgroup>
+      <col>
+      <col>
+    </colgroup>
+    <thead class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td class="rel">YYY</td>
+      </tr>
+    </thead>
+    <tbody class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+      <tr>
+        <td>XXX</td>
+        <td rowspan=2 class="rel">YYY</td>
+      </tr>
+      <tr class="rel">
+        <td class="rel">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="rel">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-separate-3-ref.html
@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      border-collapse: separate;
+      position: absolute;
+    }
+    #first-table {
+      background-color: red;
+      top: 0px;
+    }
+    #second-table {
+      top: 215px;
+    }
+    #third-table {
+      top: 430px;
+    }
+    #fourth-table {
+      top: 645px;
+    }
+    td {
+      border-style: solid;
+      border-width: 5px;
+      border-color: transparent;
+      color: rgba(0,0,0,0);
+    }
+    .bg-blue {
+      background-color: blue;
+    }
+    .bg-cyan {
+      background-color: cyan;
+    }
+    .bg-grey {
+      background-color: grey;
+    }
+    .bg-green {
+      background-color: green;
+    }
+    .bg-khaki {
+      background-color: khaki;
+    }
+    .bg-purple {
+      background-color: purple;
+    }
+    .bg-white {
+      background-color: white;
+    }
+    .show-text {
+      border-color: gold;
+      color: rgba(0,0,0,1);
+    }
+  </style>
+</head>
+<body>
+  <table id="first-table">
+    <thead>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td rowspan=2 class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-khaki">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-border show-text">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="second-table">
+    <thead>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-green">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td rowspan=2 class="bg-green">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-grey">&nbsp;</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-green">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">&nbsp;</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="third-table">
+    <thead>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td rowspan=2 class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-green">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-cyan show-text">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="fourth-table">
+    <thead>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white" rowspan=2>YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+      </tr>
+      <tr>
+        <td class="bg-white" colspan=2>XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-separate-3.html
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      background-color: red;
+      border-collapse: separate;
+    }
+    thead {
+      background-color: pink;
+    }
+    tbody {
+      background-color: grey;
+    }
+    tfoot {
+      background-color: orange;
+    }
+    tr {
+      background-color: green;
+    }
+    td:first-child {
+      background-color: cyan;
+    }
+    td {
+      background-color: blue;
+      border-style: solid;
+      border-width: 5px;
+      border-color: gold;
+    }
+    colgroup {
+      background-color: purple;
+    }
+    col:first-child {
+      background-color: khaki;
+    }
+    .rel {
+      position: relative;
+      top: 215px;
+    }
+  </style>
+</head>
+<body>
+  <table>
+    <colgroup>
+      <col>
+      <col>
+    </colgroup>
+    <thead class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td class="rel">YYY</td>
+      </tr>
+    </thead>
+    <tbody class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+      <tr>
+        <td>XXX</td>
+        <td rowspan=2 class="rel">YYY</td>
+      </tr>
+      <tr class="rel">
+        <td class="rel">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="rel">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-separate-4-ref.html
@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      border-collapse: separate;
+      position: absolute;
+    }
+    #first-table {
+      background-color: red;
+      top: 645px;
+    }
+    #second-table {
+      top: 430px;
+    }
+    #third-table {
+      top: 215px;
+    }
+    #fourth-table {
+      top: 0px;
+    }
+    td {
+      border-style: solid;
+      border-width: 5px;
+      border-color: transparent;
+      color: rgba(0,0,0,0);
+    }
+    .bg-blue {
+      background-color: blue;
+    }
+    .bg-cyan {
+      background-color: cyan;
+    }
+    .bg-grey {
+      background-color: grey;
+    }
+    .bg-green {
+      background-color: green;
+    }
+    .bg-khaki {
+      background-color: khaki;
+    }
+    .bg-purple {
+      background-color: purple;
+    }
+    .bg-white {
+      background-color: white;
+    }
+    .show-text {
+      border-color: gold;
+      color: rgba(0,0,0,1);
+    }
+  </style>
+</head>
+<body>
+  <table id="first-table">
+    <thead>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+        <td rowspan=2 class="bg-purple">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-khaki">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-khaki">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-border show-text">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="second-table">
+    <thead>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-green">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-green">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td rowspan=2 class="bg-green">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-grey">&nbsp;</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-green">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">&nbsp;</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="third-table">
+    <thead>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-blue show-text">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td rowspan=2 class="bg-blue show-text">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-green">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="bg-cyan show-text">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+  <table id="fourth-table">
+    <thead>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </thead>
+    <tbody>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white" rowspan=2>YYY</td>
+      </tr>
+      <tr>
+        <td class="bg-cyan show-text">XXX</td>
+      </tr>
+      <tr>
+        <td class="bg-white" colspan=2>XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="bg-white">XXX</td>
+        <td class="bg-white">YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/position-relative/table-separate-4.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<head>
+  <style>
+    body {
+      background-color: white;
+      margin: 0;
+    }
+    table {
+      background-color: red;
+      border-collapse: separate;
+      position: absolute;
+      top: 645px;
+    }
+    thead {
+      background-color: pink;
+    }
+    tbody {
+      background-color: grey;
+    }
+    tfoot {
+      background-color: orange;
+    }
+    tr {
+      background-color: green;
+    }
+    td:first-child {
+      background-color: cyan;
+    }
+    td {
+      background-color: blue;
+      border-style: solid;
+      border-width: 5px;
+      border-color: gold;
+    }
+    colgroup {
+      background-color: purple;
+    }
+    col:first-child {
+      background-color: khaki;
+    }
+    .rel {
+      position: relative;
+      bottom: 215px;
+    }
+  </style>
+</head>
+<body>
+  <table>
+    <colgroup>
+      <col>
+      <col>
+    </colgroup>
+    <thead class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td class="rel">YYY</td>
+      </tr>
+    </thead>
+    <tbody class="rel">
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+      <tr>
+        <td>XXX</td>
+        <td rowspan=2 class="rel">YYY</td>
+      </tr>
+      <tr class="rel">
+        <td class="rel">XXX</td>
+      </tr>
+      <tr>
+        <td colspan=2 class="rel">XXX</td>
+      </tr>
+    </tbody>
+    <tfoot>
+      <tr>
+        <td class="rel">XXX</td>
+        <td>YYY</td>
+      </tr>
+    </tfoot>
+  </table>
+</body>
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -12,16 +12,17 @@ include reftest-sanity/reftest.list
 include ../../image/test/reftest/reftest.list
 
 # CSSWG tests
 include w3c-css/submitted/reftest.list
 include w3c-css/received/reftest.list
 
 # relative and absolute positioning
 include abs-pos/reftest.list
+include position-relative/reftest.list
 
 include async-scrolling/reftest.list
 
 # backgrounds/
 include backgrounds/reftest.list
 
 # bidi/
 include bidi/reftest.list
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -293,16 +293,17 @@ GetMetricsFor(nsPresContext* aPresContex
     WritingMode wm(aStyleContext);
     if (wm.IsVertical() && !wm.IsSideways()) {
       orientation = gfxFont::eVertical;
     }
   }
   nsRefPtr<nsFontMetrics> fm;
   aPresContext->DeviceContext()->GetMetricsFor(font,
                                                aStyleFont->mLanguage,
+                                               aStyleFont->mExplicitLanguage,
                                                orientation,
                                                fs, tp, *getter_AddRefs(fm));
   return fm.forget();
 }
 
 
 static nsSize CalcViewportUnitsScale(nsPresContext* aPresContext)
 {
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1868,17 +1868,17 @@ nsTableFrame::Reflow(nsPresContext*     
 
       ReflowTable(aDesiredSize, aReflowState, aReflowState.AvailableHeight(),
                   lastChildReflowed, aStatus);
 
       if (lastChildReflowed && NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
         // if there is an incomplete child, then set the desired height to include it but not the next one
         nsMargin borderPadding = GetChildAreaOffset(&aReflowState);
         aDesiredSize.Height() = borderPadding.bottom + GetCellSpacingY(GetRowCount()) +
-                              lastChildReflowed->GetRect().YMost();
+                              lastChildReflowed->GetNormalRect().YMost();
       }
       haveDesiredHeight = true;
 
       mutable_rs.mFlags.mSpecialHeightReflow = false;
     }
   }
   else {
     // Calculate the overflow area contribution from our children.
@@ -2672,26 +2672,27 @@ nsTableFrame::InitChildReflowState(nsHTM
     aReflowState.SetHResize(true);
   }
 }
 
 // Position and size aKidFrame and update our reflow state. The origin of
 // aKidRect is relative to the upper-left origin of our frame
 void nsTableFrame::PlaceChild(nsTableReflowState&  aReflowState,
                               nsIFrame*            aKidFrame,
+                              nsPoint              aKidPosition,
                               nsHTMLReflowMetrics& aKidDesiredSize,
                               const nsRect&        aOriginalKidRect,
                               const nsRect&        aOriginalKidVisualOverflow)
 {
   bool isFirstReflow =
     (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
 
   // Place and size the child
   FinishReflowChild(aKidFrame, PresContext(), aKidDesiredSize, nullptr,
-                    aReflowState.x, aReflowState.y, 0);
+                    aKidPosition.x, aKidPosition.y, 0);
 
   InvalidateTableFrame(aKidFrame, aOriginalKidRect, aOriginalKidVisualOverflow,
                        isFirstReflow);
 
   // Adjust the running y-offset
   aReflowState.y += aKidDesiredSize.Height();
 
   // If our height is constrained, then update the available height
@@ -2865,17 +2866,20 @@ nsTableFrame::PlaceRepeatedFooter(nsTabl
   nsRect origTfootRect = aTfoot->GetRect();
   nsRect origTfootVisualOverflow = aTfoot->GetVisualOverflowRect();
           
   nsReflowStatus footerStatus;
   nsHTMLReflowMetrics desiredSize(aReflowState.reflowState);
   desiredSize.ClearSize();
   ReflowChild(aTfoot, presContext, desiredSize, footerReflowState,
               aReflowState.x, aReflowState.y, 0, footerStatus);
-  PlaceChild(aReflowState, aTfoot, desiredSize, origTfootRect,
+  nsPoint kidPosition(aReflowState.x, aReflowState.y);
+  footerReflowState.ApplyRelativePositioning(&kidPosition);
+
+  PlaceChild(aReflowState, aTfoot, kidPosition, desiredSize, origTfootRect,
              origTfootVisualOverflow);
 }
                     
 // Reflow the children based on the avail size and reason in aReflowState
 // update aReflowMetrics a aStatus
 void
 nsTableFrame::ReflowChildren(nsTableReflowState& aReflowState,
                              nsReflowStatus&     aStatus,
@@ -2985,31 +2989,33 @@ nsTableFrame::ReflowChildren(nsTableRefl
                                        nsHTMLReflowState::CALLER_WILL_INIT);
       InitChildReflowState(kidReflowState);
 
       // If this isn't the first row group, and the previous row group has a
       // nonzero YMost, then we can't be at the top of the page.
       // We ignore a repeated head row group in this check to avoid causing
       // infinite loops in some circumstances - see bug 344883.
       if (childX > ((thead && IsRepeatedFrame(thead)) ? 1u : 0u) &&
-          (rowGroups[childX - 1]->GetRect().YMost() > 0)) {
+          (rowGroups[childX - 1]->GetNormalRect().YMost() > 0)) {
         kidReflowState.mFlags.mIsTopOfPage = false;
       }
       aReflowState.y += cellSpacingY;
       if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
         aReflowState.availSize.height -= cellSpacingY;
       }
       // record the presence of a next in flow, it might get destroyed so we
       // need to reorder the row group array
       bool reorder = false;
       if (kidFrame->GetNextInFlow())
         reorder = true;
 
       ReflowChild(kidFrame, presContext, desiredSize, kidReflowState,
                   aReflowState.x, aReflowState.y, 0, aStatus);
+      nsPoint kidPosition(aReflowState.x, aReflowState.y);
+      kidReflowState.ApplyRelativePositioning(&kidPosition);
 
       if (reorder) {
         // reorder row groups the reflow may have changed the nextinflows
         OrderRowGroups(rowGroups, &thead, &tfoot);
         childX = rowGroups.IndexOf(kidFrame);
         if (childX == RowGroupArray::NoIndex) {
           // XXXbz can this happen?
           childX = rowGroups.Length();
@@ -3031,18 +3037,18 @@ nsTableFrame::ReflowChildren(nsTableRefl
           aStatus = NS_INLINE_LINE_BREAK_BEFORE();
           break;
         }
         // if we are on top of the page place with dataloss
         if (kidReflowState.mFlags.mIsTopOfPage) {
           if (childX+1 < rowGroups.Length()) {
             nsIFrame* nextRowGroupFrame = rowGroups[childX + 1];
             if (nextRowGroupFrame) {
-              PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
-                         oldKidVisualOverflow);
+              PlaceChild(aReflowState, kidFrame, kidPosition, desiredSize,
+                         oldKidRect, oldKidVisualOverflow);
               if (allowRepeatedFooter) {
                 PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
               }
               else if (tfoot && tfoot->IsRepeatable()) {
                 tfoot->SetRepeatable(false);
               }
               aStatus = NS_FRAME_NOT_COMPLETE;
               PushChildren(rowGroups, childX + 1);
@@ -3060,18 +3066,18 @@ nsTableFrame::ReflowChildren(nsTableRefl
               tfoot->SetRepeatable(false);
             }
             aStatus = NS_FRAME_NOT_COMPLETE;
             PushChildren(rowGroups, childX);
             aLastChildReflowed = prevKidFrame;
             break;
           }
           else { // we can't push so lets make clear how much space we need
-            PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
-                                     oldKidVisualOverflow);
+            PlaceChild(aReflowState, kidFrame, kidPosition, desiredSize,
+                       oldKidRect, oldKidVisualOverflow);
             aLastChildReflowed = kidFrame;
             if (allowRepeatedFooter) {
               PlaceRepeatedFooter(aReflowState, tfoot, footerHeight);
               aLastChildReflowed = tfoot;
             }
             break;
           }
         }
@@ -3084,17 +3090,17 @@ nsTableFrame::ReflowChildren(nsTableRefl
       if (NS_FRAME_IS_COMPLETE(aStatus) && isPaginated &&
           (NS_UNCONSTRAINEDSIZE != kidReflowState.AvailableHeight())) {
         nsIFrame* nextKid =
           (childX + 1 < rowGroups.Length()) ? rowGroups[childX + 1] : nullptr;
         pageBreak = PageBreakAfter(kidFrame, nextKid);
       }
 
       // Place the child
-      PlaceChild(aReflowState, kidFrame, desiredSize, oldKidRect,
+      PlaceChild(aReflowState, kidFrame, kidPosition, desiredSize, oldKidRect,
                  oldKidVisualOverflow);
 
       // Remember where we just were in case we end up pushing children
       prevKidFrame = kidFrame;
 
       // Special handling for incomplete children
       if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
         nsIFrame* kidNextInFlow = kidFrame->GetNextInFlow();
@@ -3131,22 +3137,22 @@ nsTableFrame::ReflowChildren(nsTableRefl
         if (nextSibling) {
           PushChildren(rowGroups, childX + 1);
         }
         break;
       }
     }
     else { // it isn't being reflowed
       aReflowState.y += cellSpacingY;
-      nsRect kidRect = kidFrame->GetRect();
+      nsRect kidRect = kidFrame->GetNormalRect();
       if (kidRect.y != aReflowState.y) {
         // invalidate the old position
         kidFrame->InvalidateFrameSubtree();
-        kidRect.y = aReflowState.y;
-        kidFrame->SetRect(kidRect);        // move to the new position
+        // move to the new position
+        kidFrame->MovePositionBy(nsPoint(0, aReflowState.y - kidRect.y));
         RePositionViews(kidFrame);
         // invalidate the new position
         kidFrame->InvalidateFrameSubtree();
       }
       aReflowState.y += kidRect.height;
 
       // If our height is constrained then update the available height.
       if (NS_UNCONSTRAINEDSIZE != aReflowState.availSize.height) {
@@ -3289,74 +3295,74 @@ nsTableFrame::DistributeHeightToRows(con
   nscoord pctBasis = aReflowState.ComputedHeight() - GetCellSpacingY(-1, GetRowCount());
   nscoord yOriginRG = borderPadding.top + GetCellSpacingY(0);
   nscoord yEndRG = yOriginRG;
   uint32_t rgX;
   for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
     nscoord amountUsedByRG = 0;
     nscoord yOriginRow = 0;
-    nsRect rgRect = rgFrame->GetRect();
+    nsRect rgNormalRect = rgFrame->GetNormalRect();
     if (!rgFrame->HasStyleHeight()) {
       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
       while (rowFrame) {
-        nsRect rowRect = rowFrame->GetRect();
+        nsRect rowNormalRect = rowFrame->GetNormalRect();
         nscoord cellSpacingY = GetCellSpacingY(rowFrame->GetRowIndex());
         if ((amountUsed < aAmount) && rowFrame->HasPctHeight()) {
           nscoord pctHeight = rowFrame->GetHeight(pctBasis);
-          nscoord amountForRow = std::min(aAmount - amountUsed, pctHeight - rowRect.height);
+          nscoord amountForRow = std::min(aAmount - amountUsed,
+                                          pctHeight - rowNormalRect.height);
           if (amountForRow > 0) {
-            nsRect oldRowRect = rowRect;
-            rowRect.height += amountForRow;
-            // XXXbz we don't need to change rowRect.y to be yOriginRow?
-            rowFrame->SetRect(rowRect);
-            yOriginRow += rowRect.height + cellSpacingY;
-            yEndRG += rowRect.height + cellSpacingY;
+            // XXXbz we don't need to move the row's y position to yOriginRow?
+            nsRect origRowRect = rowFrame->GetRect();
+            nscoord newRowHeight = rowNormalRect.height + amountForRow;
+            rowFrame->SetSize(nsSize(rowNormalRect.width, newRowHeight));
+            yOriginRow += newRowHeight + cellSpacingY;
+            yEndRG += newRowHeight + cellSpacingY;
             amountUsed += amountForRow;
             amountUsedByRG += amountForRow;
             //rowFrame->DidResize();
             nsTableFrame::RePositionViews(rowFrame);
 
-            rgFrame->InvalidateFrameWithRect(oldRowRect);
+            rgFrame->InvalidateFrameWithRect(origRowRect);
             rgFrame->InvalidateFrame();
           }
         }
         else {
-          if (amountUsed > 0 && yOriginRow != rowRect.y &&
+          if (amountUsed > 0 && yOriginRow != rowNormalRect.y &&
               !(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
             rowFrame->InvalidateFrameSubtree();
-            rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
+            rowFrame->MovePositionBy(nsPoint(0, yOriginRow - rowNormalRect.y));
             nsTableFrame::RePositionViews(rowFrame);
             rowFrame->InvalidateFrameSubtree();
           }
-          yOriginRow += rowRect.height + cellSpacingY;
-          yEndRG += rowRect.height + cellSpacingY;
+          yOriginRow += rowNormalRect.height + cellSpacingY;
+          yEndRG += rowNormalRect.height + cellSpacingY;
         }
         rowFrame = rowFrame->GetNextRow();
       }
       if (amountUsed > 0) {
-        if (rgRect.y != yOriginRG) {
+        if (rgNormalRect.y != yOriginRG) {
           rgFrame->InvalidateFrameSubtree();
         }
 
-        nsRect origRgRect = rgRect;
+        nsRect origRgNormalRect = rgFrame->GetRect();
         nsRect origRgVisualOverflow = rgFrame->GetVisualOverflowRect();
 
-        rgRect.y = yOriginRG;
-        rgRect.height += amountUsedByRG;
-
-        rgFrame->SetRect(rgRect);
-
-        nsTableFrame::InvalidateTableFrame(rgFrame, origRgRect,
+        rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y));
+        rgFrame->SetSize(nsSize(rgNormalRect.width,
+                                rgNormalRect.height + amountUsedByRG));
+
+        nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
                                            origRgVisualOverflow, false);
       }
     }
-    else if (amountUsed > 0 && yOriginRG != rgRect.y) {
+    else if (amountUsed > 0 && yOriginRG != rgNormalRect.y) {
       rgFrame->InvalidateFrameSubtree();
-      rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
+      rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y));
       // Make sure child views are properly positioned
       nsTableFrame::RePositionViews(rgFrame);
       rgFrame->InvalidateFrameSubtree();
     }
     yOriginRG = yEndRG;
   }
 
   if (amountUsed >= aAmount) {
@@ -3426,97 +3432,100 @@ nsTableFrame::DistributeHeightToRows(con
   // allocate the extra height to the unstyled row groups and rows
   nscoord heightToDistribute = aAmount - amountUsed;
   yOriginRG = borderPadding.top + GetCellSpacingY(-1);
   yEndRG = yOriginRG;
   for (rgX = 0; rgX < rowGroups.Length(); rgX++) {
     nsTableRowGroupFrame* rgFrame = rowGroups[rgX];
     nscoord amountUsedByRG = 0;
     nscoord yOriginRow = 0;
-    nsRect rgRect = rgFrame->GetRect();
+    nsRect rgNormalRect = rgFrame->GetNormalRect();
     nsRect rgVisualOverflow = rgFrame->GetVisualOverflowRect();
     // see if there is an eligible row group or we distribute to all rows
     if (!firstUnStyledRG || !rgFrame->HasStyleHeight() || !eligibleRows) {
       nsTableRowFrame* rowFrame = rgFrame->GetFirstRow();
       while (rowFrame) {
         nscoord cellSpacingY = GetCellSpacingY(rowFrame->GetRowIndex());
-        nsRect rowRect = rowFrame->GetRect();
+        nsRect rowNormalRect = rowFrame->GetNormalRect();
         nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
         // see if there is an eligible row or we distribute to all rows
         if (!firstUnStyledRow || !rowFrame->HasStyleHeight() || !eligibleRows) {
           float ratio;
           if (eligibleRows) {
             if (!expandEmptyRows) {
               // The amount of additional space each row gets is proportional to
               // its height
-              ratio = float(rowRect.height) / float(divisor);
+              ratio = float(rowNormalRect.height) / float(divisor);
             } else {
               // empty rows get all the same additional space
               ratio = 1.0f / float(eligibleRows);
             }
           }
           else {
             // all rows get the same additional space
             ratio = 1.0f / float(divisor);
           }
           // give rows their additional space, except for the last row which
           // gets the remainder
           nscoord amountForRow = (rowFrame == lastEligibleRow)
                                  ? aAmount - amountUsed : NSToCoordRound(((float)(heightToDistribute)) * ratio);
           amountForRow = std::min(amountForRow, aAmount - amountUsed);
 
-          if (yOriginRow != rowRect.y) {
+          if (yOriginRow != rowNormalRect.y) {
             rowFrame->InvalidateFrameSubtree();
           }
 
           // update the row height
-          nsRect newRowRect(rowRect.x, yOriginRow, rowRect.width,
-                            rowRect.height + amountForRow);
-          rowFrame->SetRect(newRowRect);
-
-          yOriginRow += newRowRect.height + cellSpacingY;
-          yEndRG += newRowRect.height + cellSpacingY;
+          nsRect origRowRect = rowFrame->GetRect();
+          nscoord newRowHeight = rowNormalRect.height + amountForRow;
+          rowFrame->MovePositionBy(nsPoint(0, yOriginRow - rowNormalRect.y));
+          rowFrame->SetSize(nsSize(rowNormalRect.width, newRowHeight));
+
+          yOriginRow += newRowHeight + cellSpacingY;
+          yEndRG += newRowHeight + cellSpacingY;
 
           amountUsed += amountForRow;
           amountUsedByRG += amountForRow;
           NS_ASSERTION((amountUsed <= aAmount), "invalid row allocation");
           //rowFrame->DidResize();
           nsTableFrame::RePositionViews(rowFrame);
 
-          nsTableFrame::InvalidateTableFrame(rowFrame, rowRect, rowVisualOverflow,
-                                             false);
+          nsTableFrame::InvalidateTableFrame(rowFrame, origRowRect,
+                                             rowVisualOverflow, false);
         }
         else {
-          if (amountUsed > 0 && yOriginRow != rowRect.y) {
+          if (amountUsed > 0 && yOriginRow != rowNormalRect.y) {
             rowFrame->InvalidateFrameSubtree();
-            rowFrame->SetPosition(nsPoint(rowRect.x, yOriginRow));
+            rowFrame->MovePositionBy(nsPoint(0, yOriginRow - rowNormalRect.y));
             nsTableFrame::RePositionViews(rowFrame);
             rowFrame->InvalidateFrameSubtree();
           }
-          yOriginRow += rowRect.height + cellSpacingY;
-          yEndRG += rowRect.height + cellSpacingY;
+          yOriginRow += rowNormalRect.height + cellSpacingY;
+          yEndRG += rowNormalRect.height + cellSpacingY;
         }
         rowFrame = rowFrame->GetNextRow();
       }
       if (amountUsed > 0) {
-        if (rgRect.y != yOriginRG) {
+        if (rgNormalRect.y != yOriginRG) {
           rgFrame->InvalidateFrameSubtree();
         }
 
-        rgFrame->SetRect(nsRect(rgRect.x, yOriginRG, rgRect.width,
-                                rgRect.height + amountUsedByRG));
-
-        nsTableFrame::InvalidateTableFrame(rgFrame, rgRect, rgVisualOverflow,
-                                           false);
+        nsRect origRgNormalRect = rgFrame->GetRect();
+        rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y));
+        rgFrame->SetSize(nsSize(rgNormalRect.width,
+                                rgNormalRect.height + amountUsedByRG));
+
+        nsTableFrame::InvalidateTableFrame(rgFrame, origRgNormalRect,
+                                           rgVisualOverflow, false);
       }
       // Make sure child views are properly positioned
     }
-    else if (amountUsed > 0 && yOriginRG != rgRect.y) {
+    else if (amountUsed > 0 && yOriginRG != rgNormalRect.y) {
       rgFrame->InvalidateFrameSubtree();
-      rgFrame->SetPosition(nsPoint(rgRect.x, yOriginRG));
+      rgFrame->MovePositionBy(nsPoint(0, yOriginRG - rgNormalRect.y));
       // Make sure child views are properly positioned
       nsTableFrame::RePositionViews(rgFrame);
       rgFrame->InvalidateFrameSubtree();
     }
     yOriginRG = yEndRG;
   }
 
   ResizeCells(*this);
@@ -3606,18 +3615,25 @@ nsTableFrame::GetLogicalBaseline(Writing
   OrderRowGroups(orderedRowGroups);
   nsTableRowFrame* firstRow = nullptr;
   // XXX not sure if this should be the width of the containing block instead.
   nscoord containerWidth = mRect.width;
   for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
     nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
     if (rgFrame->GetRowCount()) {
       firstRow = rgFrame->GetFirstRow();
-      ascent = rgFrame->BStart(aWritingMode, containerWidth) +
-               firstRow->BStart(aWritingMode, containerWidth) +
+
+      nscoord rgNormalBStart =
+        LogicalRect(aWritingMode, rgFrame->GetNormalRect(), containerWidth)
+        .Origin(aWritingMode).B(aWritingMode);
+      nscoord firstRowNormalBStart =
+        LogicalRect(aWritingMode, firstRow->GetNormalRect(), containerWidth)
+        .Origin(aWritingMode).B(aWritingMode);
+
+      ascent = rgNormalBStart + firstRowNormalBStart +
                firstRow->GetRowBaseline(aWritingMode);
       break;
     }
   }
   if (!firstRow)
     ascent = BSize(aWritingMode);
   return ascent;
 }
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -674,16 +674,17 @@ protected:
 
   // The following is a helper for CalcDesiredHeight 
  
   void DistributeHeightToRows(const nsHTMLReflowState& aReflowState,
                               nscoord                  aAmount);
 
   void PlaceChild(nsTableReflowState&  aReflowState,
                   nsIFrame*            aKidFrame,
+                  nsPoint              aKidPosition,
                   nsHTMLReflowMetrics& aKidDesiredSize,
                   const nsRect&        aOriginalKidRect,
                   const nsRect&        aOriginalKidVisualOverflow);
    void PlaceRepeatedFooter(nsTableReflowState& aReflowState,
                             nsTableRowGroupFrame *aTfoot,
                             nscoord aFooterHeight);
 
   nsIFrame* GetFirstBodyRowGroupFrame();
--- a/layout/tables/nsTablePainter.cpp
+++ b/layout/tables/nsTablePainter.cpp
@@ -87,21 +87,17 @@
    descendants' data) are not cached. The full loop is still executed, however,
    so that underlying layers can get painted at the cell level.
 
    The TableBackgroundPainter is then destroyed.
 
    Elements with stacking contexts set up their own painter to finish the
    painting process, since they were skipped. They call the appropriate
    sub-part of the loop (e.g. PaintRow) which will paint the frame and
-   descendants. Note that it is permissible according to CSS2.1 to ignore'
-   'position:relative' (and implicitly, 'opacity') on table parts so that
-   table parts can never create stacking contexts; if we want to, we can
-   implement that, and then we won't have to deal with TableBackgroundPainter
-   being used anywhere but from the nsTableFrame.
+   descendants.
    
    XXX views are going 
  */
 
 TableBackgroundPainter::TableBackgroundData::TableBackgroundData()
   : mFrame(nullptr),
     mVisible(false),
     mBorder(nullptr),
@@ -417,17 +413,25 @@ TableBackgroundPainter::PaintTable(nsTab
   }
 
   for (uint32_t i = 0; i < rowGroups.Length(); i++) {
     nsTableRowGroupFrame* rg = rowGroups[i];
     mRowGroup.SetFrame(rg);
     // Need to compute the right rect via GetOffsetTo, since the row
     // group may not be a child of the table.
     mRowGroup.mRect.MoveTo(rg->GetOffsetTo(aTableFrame));
-    if (mRowGroup.mRect.Intersects(mDirtyRect - mRenderPt)) {
+
+    // We have to draw backgrounds not only within the overflow region of this
+    // row group, but also possibly (in the case of column / column group
+    // backgrounds) at its pre-relative-positioning location.
+    nsRect rgVisualOverflow = rg->GetVisualOverflowRectRelativeToSelf();
+    nsRect rgOverflowRect = rgVisualOverflow + rg->GetPosition();
+    nsRect rgNormalRect = rgVisualOverflow + rg->GetNormalPosition();
+
+    if (rgOverflowRect.Union(rgNormalRect).Intersects(mDirtyRect - mRenderPt)) {
       nsresult rv = PaintRowGroup(rg, rg->IsPseudoStackingContextFromStyle());
       if (NS_FAILED(rv)) return rv;
     }
   }
   return NS_OK;
 }
 
 nsresult
@@ -465,26 +469,22 @@ TableBackgroundPainter::PaintRowGroup(ns
   /* translate everything into row group coord system*/
   if (eOrigin_TableRowGroup != mOrigin) {
     TranslateContext(mRowGroup.mRect.x, mRowGroup.mRect.y);
   }
   nsRect rgRect = mRowGroup.mRect;
   mRowGroup.mRect.MoveTo(0, 0);
 
   /* Find the right row to start with */
-  nscoord ignored; // We don't care about overflow above, since what we really
-                   // care about are backgrounds and overflow above doesn't
-                   // correspond to backgrounds, since cells can't span up from
-                   // their originating row.  We do care about overflow below,
-                   // however, since that can be due to rowspans.
 
   // Note that mDirtyRect  - mRenderPt is guaranteed to be in the row
   // group's coordinate system here, so passing its .y to
   // GetFirstRowContaining is ok.
-  nsIFrame* cursor = aFrame->GetFirstRowContaining(mDirtyRect.y - mRenderPt.y, &ignored);
+  nscoord overflowAbove;
+  nsIFrame* cursor = aFrame->GetFirstRowContaining(mDirtyRect.y - mRenderPt.y, &overflowAbove);
 
   // Sadly, it seems like there may be non-row frames in there... or something?
   // There are certainly null-checks in GetFirstRow() and GetNextRow().  :(
   while (cursor && cursor->GetType() != nsGkAtoms::tableRowFrame) {
     cursor = cursor->GetNextSibling();
   }
 
   // It's OK if cursor is null here.
@@ -495,19 +495,23 @@ TableBackgroundPainter::PaintRowGroup(ns
     // list for the rowgroup, so not having a cursor means that there's some
     // good reason we don't have a cursor and we shouldn't create one here.
     row = firstRow;
   }
   
   /* Finally paint */
   for (; row; row = row->GetNextRow()) {
     mRow.SetFrame(row);
-    if (mDirtyRect.YMost() - mRenderPt.y < mRow.mRect.y) { // Intersect wouldn't handle
-                                             // rowspans.
+    // Be sure to consider our positions both pre- and post-relative
+    // positioning, since we potentially need to paint at both places.
+    nscoord rowY = std::min(mRow.mRect.y, row->GetNormalPosition().y);
 
+    // Intersect wouldn't handle rowspans.
+    if (cursor &&
+        (mDirtyRect.YMost() - mRenderPt.y) <= (rowY - overflowAbove)) {
       // All done; cells originating in later rows can't intersect mDirtyRect.
       break;
     }
     
     nsresult rv = PaintRow(row, aPassThrough || row->IsPseudoStackingContextFromStyle());
     if (NS_FAILED(rv)) return rv;
   }
 
@@ -559,32 +563,46 @@ TableBackgroundPainter::PaintRow(nsTable
   /* Translate */
   if (eOrigin_TableRow == mOrigin) {
     /* If we originate from the row, then make the row the origin. */
     mRow.mRect.MoveTo(0, 0);
   }
   //else: Use row group's coord system -> no translation necessary
 
   for (nsTableCellFrame* cell = aFrame->GetFirstCell(); cell; cell = cell->GetNextCell()) {
-    //Translate to use the same coord system as mRow.
-    mCellRect = cell->GetRect() + mRow.mRect.TopLeft() + mRenderPt;
-    if (mCellRect.Intersects(mDirtyRect)) {
-      nsresult rv = PaintCell(cell, aPassThrough || cell->IsPseudoStackingContextFromStyle());
+    nsRect cellBGRect, rowBGRect, rowGroupBGRect, colBGRect;
+    ComputeCellBackgrounds(cell, cellBGRect, rowBGRect,
+                           rowGroupBGRect, colBGRect);
+
+    // Find the union of all the cell background layers.
+    nsRect combinedRect(cellBGRect);
+    combinedRect.UnionRect(combinedRect, rowBGRect);
+    combinedRect.UnionRect(combinedRect, rowGroupBGRect);
+    combinedRect.UnionRect(combinedRect, colBGRect);
+
+    if (combinedRect.Intersects(mDirtyRect)) {
+      bool passCell = aPassThrough || cell->IsPseudoStackingContextFromStyle();
+      nsresult rv = PaintCell(cell, cellBGRect, rowBGRect, rowGroupBGRect,
+                              colBGRect, passCell);
       if (NS_FAILED(rv)) return rv;
     }
   }
 
   /* Unload row data */
   mRow.Clear();
   return NS_OK;
 }
 
 nsresult
 TableBackgroundPainter::PaintCell(nsTableCellFrame* aCell,
-                                  bool aPassSelf)
+                                  nsRect&           aCellBGRect,
+                                  nsRect&           aRowBGRect,
+                                  nsRect&           aRowGroupBGRect,
+                                  nsRect&           aColBGRect,
+                                  bool              aPassSelf)
 {
   NS_PRECONDITION(aCell, "null frame");
 
   const nsStyleTableBorder* cellTableStyle;
   cellTableStyle = aCell->StyleTableBorder();
   if (NS_STYLE_TABLE_EMPTY_CELLS_SHOW != cellTableStyle->mEmptyCells &&
       aCell->GetContentEmpty() && !mIsBorderCollapse) {
     return NS_OK;
@@ -598,49 +616,104 @@ TableBackgroundPainter::PaintCell(nsTabl
 
   //Paint column group background
   if (mCols && mCols[colIndex].mColGroup && mCols[colIndex].mColGroup->IsVisible()) {
     nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
                                           mCols[colIndex].mColGroup->mFrame, mDirtyRect,
                                           mCols[colIndex].mColGroup->mRect + mRenderPt,
                                           mCols[colIndex].mColGroup->mFrame->StyleContext(),
                                           *mCols[colIndex].mColGroup->mBorder,
-                                          mBGPaintFlags, &mCellRect);
+                                          mBGPaintFlags, &aColBGRect);
   }
 
   //Paint column background
   if (mCols && mCols[colIndex].mCol.IsVisible()) {
     nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
                                           mCols[colIndex].mCol.mFrame, mDirtyRect,
                                           mCols[colIndex].mCol.mRect + mRenderPt,
                                           mCols[colIndex].mCol.mFrame->StyleContext(),
                                           *mCols[colIndex].mCol.mBorder,
-                                          mBGPaintFlags, &mCellRect);
+                                          mBGPaintFlags, &aColBGRect);
   }
 
   //Paint row group background
   if (mRowGroup.IsVisible()) {
     nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
                                           mRowGroup.mFrame, mDirtyRect,
                                           mRowGroup.mRect + mRenderPt,
                                           mRowGroup.mFrame->StyleContext(),
                                           *mRowGroup.mBorder,
-                                          mBGPaintFlags, &mCellRect);
+                                          mBGPaintFlags, &aRowGroupBGRect);
   }
 
   //Paint row background
   if (mRow.IsVisible()) {
     nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
                                           mRow.mFrame, mDirtyRect,
                                           mRow.mRect + mRenderPt,
                                           mRow.mFrame->StyleContext(),
                                           *mRow.mBorder,
-                                          mBGPaintFlags, &mCellRect);
+                                          mBGPaintFlags, &aRowBGRect);
   }
 
   //Paint cell background in border-collapse unless we're just passing
   if (mIsBorderCollapse && !aPassSelf) {
     aCell->PaintCellBackground(mRenderingContext, mDirtyRect,
-                               mCellRect.TopLeft(), mBGPaintFlags);
+                               aCellBGRect.TopLeft(), mBGPaintFlags);
   }
 
   return NS_OK;
 }
+
+void
+TableBackgroundPainter::ComputeCellBackgrounds(nsTableCellFrame* aCell,
+                                               nsRect&           aCellBGRect,
+                                               nsRect&           aRowBGRect,
+                                               nsRect&           aRowGroupBGRect,
+                                               nsRect&           aColBGRect)
+{
+  // We need to compute table background layer rects for this cell space,
+  // adjusted for possible relative positioning. This behavior is not specified
+  // at the time of this writing, but the approach below should be web
+  // compatible.
+  //
+  // Our goal is that relative positioning of a table part should leave
+  // backgrounds *under* that part unchanged. ("Under" being defined by CSS 2.1
+  // Section 17.5.1.) If a cell is positioned, we do not expect the row
+  // background to move. On the other hand, the backgrounds of layers *above*
+  // the positioned part are taken along for the ride -- for example,
+  // positioning a row group will also cause the row background to be drawn in
+  // the new location, unless it has further positioning applied.
+  //
+  // Each table part layer has its position stored in the coordinate space of
+  // the layer below (which is to say, its geometric parent), and the stored
+  // position is the post-relative-positioning one.  The position of each
+  // background layer rect is thus determined by peeling off successive table
+  // part layers, removing the contribution of each layer's positioning one by
+  // one.  Every rect we generate will be the same size, the size of the cell
+  // space.
+
+  // We cannot rely on the row group background data to be available, since some
+  // callers enter through PaintRow.
+  nsIFrame* rowGroupFrame =
+    mRowGroup.mFrame ? mRowGroup.mFrame : mRow.mFrame->GetParent();
+
+  // The cell background goes at the cell's position, translated to use the same
+  // coordinate system as mRow.
+  aCellBGRect = aCell->GetRect() + mRow.mRect.TopLeft() + mRenderPt;
+
+  // The row background goes at the normal position of the cell, which is to say
+  // the position without relative positioning applied.
+  aRowBGRect = aCellBGRect + (aCell->GetNormalPosition() - aCell->GetPosition());
+
+  // The row group background goes at the position we'd find the cell if neither
+  // the cell's relative positioning nor the row's were applied.
+  aRowGroupBGRect = aRowBGRect +
+                    (mRow.mFrame->GetNormalPosition() - mRow.mFrame->GetPosition());
+
+  // The column and column group backgrounds (they're always at the same
+  // location, since relative positioning doesn't apply to columns or column
+  // groups) are drawn at the position we'd find the cell if none of the cell's,
+  // row's, or row group's relative positioning were applied.
+  aColBGRect = aRowGroupBGRect +
+             (rowGroupFrame->GetNormalPosition() - rowGroupFrame->GetPosition());
+
+}
--- a/layout/tables/nsTablePainter.h
+++ b/layout/tables/nsTablePainter.h
@@ -126,22 +126,44 @@ class TableBackgroundPainter
      */
     nsresult PaintRowGroup(nsTableRowGroupFrame* aFrame,
                            bool                  aPassThrough);
     nsresult PaintRow(nsTableRowFrame* aFrame,
                       bool             aPassThrough);
 
     /** Paint table background layers for this cell space
       * Also paints cell's own background in border-collapse mode
-      * @param aFrame      - the cell
-      * @param aPassSelf   - pass this cell; i.e. paint only underlying layers
+      * @param aCell           - the cell
+      * @param aCellBGRect     - background rect for the cell
+      * @param aRowBGRect      - background rect for the row
+      * @param aRowGroupBGRect - background rect for the row group
+      * @param aColBGRect      - background rect for the column and column group
+      * @param aPassSelf       - pass this cell; i.e. paint only underlying layers
       */
-    nsresult PaintCell(nsTableCellFrame* aFrame,
+    nsresult PaintCell(nsTableCellFrame* aCell,
+                       nsRect&           aCellBGRect,
+                       nsRect&           aRowBGRect,
+                       nsRect&           aRowGroupBGRect,
+                       nsRect&           aColBGRect,
                        bool              aPassSelf);
 
+    /** Compute table background layer positions for this cell space
+      * @param aCell              - the cell
+      * @param aCellBGRectOut     - outparam: background rect for the cell
+      * @param aRowBGRectOut      - outparam: background rect for the row
+      * @param aRowGroupBGRectOut - outparam: background rect for the row group
+      * @param aColBGRectOut      - outparam: background rect for the column
+                                    and column group
+      */
+    void ComputeCellBackgrounds(nsTableCellFrame* aCell,
+                                nsRect&           aCellBGRect,
+                                nsRect&           aRowBGRect,
+                                nsRect&           aRowGroupBGRect,
+                                nsRect&           aColBGRect);
+
     /** Translate mRenderingContext, mDirtyRect, and mCols' column and
       * colgroup coords
       * @param aDX - origin's x-coord change
       * @param aDY - origin's y-coord change
       */
     void TranslateContext(nscoord aDX,
                           nscoord aDY);
 
@@ -209,15 +231,14 @@ class TableBackgroundPainter
 #endif
     bool                 mIsBorderCollapse;
     Origin               mOrigin; //user's table frame type
 
     ColData*             mCols;  //array of columns' ColData
     uint32_t             mNumCols;
     TableBackgroundData  mRowGroup; //current row group
     TableBackgroundData  mRow;      //current row
-    nsRect               mCellRect; //current cell's rect
 
     nsStyleBorder        mZeroBorder;  //cached zero-width border
     uint32_t             mBGPaintFlags;
 };
 
 #endif
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -1,12 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Maybe.h"
+
 #include "nsTableRowFrame.h"
 #include "nsTableRowGroupFrame.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
 #include "nsStyleConsts.h"
 #include "nsGkAtoms.h"
 #include "nsIContent.h"
@@ -390,17 +393,17 @@ nscoord nsTableRowFrame::GetRowBaseline(
   // bbbbbbbbbbbbbbbbbb
 
   nsTableIterator iter(*this);
   nsIFrame* childFrame = iter.First();
   nscoord ascent = 0;
    while (childFrame) {
     if (IS_TABLE_CELL(childFrame->GetType())) {
       nsIFrame* firstKid = childFrame->GetFirstPrincipalChild();
-      ascent = std::max(ascent, firstKid->GetRect().YMost());
+      ascent = std::max(ascent, firstKid->GetNormalRect().YMost());
     }
     // Get the next child
     childFrame = iter.Next();
   }
   return ascent;
 }
 nscoord
 nsTableRowFrame::GetHeight(nscoord aPctBasis) const
@@ -856,25 +859,29 @@ nsTableRowFrame::ReflowChildren(nsPresCo
                            iter.IsLeftToRight(), false);
     }
 
     // remember the rightmost (ltr) or leftmost (rtl) column this cell spans into
     prevColIndex = (iter.IsLeftToRight()) ? cellColIndex + (cellColSpan - 1) : cellColIndex;
 
     // Reflow the child frame
     nsRect kidRect = kidFrame->GetRect();
+    nsPoint origKidNormalPosition = kidFrame->GetNormalPosition();
+    MOZ_ASSERT(origKidNormalPosition.y == 0);
     nsRect kidVisualOverflow = kidFrame->GetVisualOverflowRect();
+    nsPoint kidPosition(x, 0);
     bool firstReflow =
       (kidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
 
     if (doReflowChild) {
       // Calculate the available width for the table cell using the known column widths
       nscoord availCellWidth =
         CalcAvailWidth(aTableFrame, *cellFrame);
 
+      Maybe<nsTableCellReflowState> kidReflowState;
       nsHTMLReflowMetrics desiredSize(aReflowState);
 
       // If the avail width is not the same as last time we reflowed the cell or
       // the cell wants to be bigger than what was available last time or
       // it is a style change reflow or we are printing, then we must reflow the
       // cell. Otherwise we can skip the reflow.
       // XXXldb Why is this condition distinct from doReflowChild above?
       WritingMode rowWM = aReflowState.GetWritingMode();
@@ -885,39 +892,39 @@ nsTableRowFrame::ReflowChildren(nsPresCo
           (GetStateBits() & NS_FRAME_IS_DIRTY)                      ||
           isPaginated                                               ||
           NS_SUBTREE_DIRTY(cellFrame)                               ||
           // See if it needs a special reflow, or if it had one that we need to undo.
           (cellFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_HEIGHT) ||
           HasPctHeight()) {
         // Reflow the cell to fit the available width, height
         // XXX The old IR_ChildIsDirty code used availCellWidth here.
-        nsSize  kidAvailSize(availCellWidth, aReflowState.AvailableHeight());
+        nsSize kidAvailSize(availCellWidth, aReflowState.AvailableHeight());
 
         // Reflow the child
-        nsTableCellReflowState
-          kidReflowState(aPresContext, aReflowState, kidFrame,
-                         LogicalSize(kidFrame->GetWritingMode(),
-                                     kidAvailSize),
-                         nsHTMLReflowState::CALLER_WILL_INIT);
+        kidReflowState.emplace(aPresContext, aReflowState, kidFrame,
+                               LogicalSize(kidFrame->GetWritingMode(),
+                                           kidAvailSize),
+                               // Cast needed for gcc 4.4.
+                               uint32_t(nsHTMLReflowState::CALLER_WILL_INIT));
         InitChildReflowState(*aPresContext, kidAvailSize, borderCollapse,
-                             kidReflowState);
+                             *kidReflowState);
 
         nsReflowStatus status;
-        ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
+        ReflowChild(kidFrame, aPresContext, desiredSize, *kidReflowState,
                     x, 0, 0, status);
 
         // allow the table to determine if/how the table needs to be rebalanced
         // If any of the cells are not complete, then we're not complete
         if (NS_FRAME_IS_NOT_COMPLETE(status)) {
           aStatus = NS_FRAME_NOT_COMPLETE;
         }
       }
       else {
-        if (x != kidRect.x) {
+        if (x != origKidNormalPosition.x) {
           kidFrame->InvalidateFrameSubtree();
         }
         
         desiredSize.SetSize(cellWM, cellDesiredSize);
         desiredSize.mOverflowAreas = cellFrame->GetOverflowAreas();
 
         // if we are in a floated table, our position is not yet established, so we cannot reposition our views
         // the containing block will do this for us after positioning the table
@@ -951,29 +958,41 @@ nsTableRowFrame::ReflowChildren(nsPresCo
         if (1 == rowSpan) {
           SetContentHeight(cellMaxHeight);
         }
       }
 
       // Place the child
       desiredSize.ISize(rowWM) = availCellWidth;
 
-      FinishReflowChild(kidFrame, aPresContext, desiredSize, nullptr, x, 0, 0);
+      if (kidReflowState) {
+        // We reflowed. Apply relative positioning in the normal way.
+        kidReflowState->ApplyRelativePositioning(&kidPosition);
+      } else {
+        // We didn't reflow. To take relative positioning into account,
+        // translate the new position by the vector from the previous 'normal'
+        // position to the previous position.
+        // XXX(seth): This doesn't work for 'position: sticky'.
+        kidPosition += kidRect.TopLeft() - origKidNormalPosition;
+      }
+      FinishReflowChild(kidFrame, aPresContext, desiredSize, nullptr,
+                        kidPosition.x, kidPosition.y, 0);
 
       nsTableFrame::InvalidateTableFrame(kidFrame, kidRect, kidVisualOverflow,
                                          firstReflow);
       
       x += desiredSize.Width();  
     }
     else {
-      if (kidRect.x != x) {
+      if (x != origKidNormalPosition.x) {
         // Invalidate the old position
         kidFrame->InvalidateFrameSubtree();
-        // move to the new position
-        kidFrame->SetPosition(nsPoint(x, kidRect.y));
+        // Move to the new position. As above, we need to account for relative
+        // positioning.
+        kidFrame->MovePositionBy(nsPoint(x - origKidNormalPosition.x, 0));
         nsTableFrame::RePositionViews(kidFrame);
         // invalidate the new position
         kidFrame->InvalidateFrameSubtree();
       }
       // we need to account for the cell's width even if it isn't reflowed
       x += kidRect.width;
 
       if (kidFrame->GetNextInFlow()) {
@@ -1258,24 +1277,26 @@ nsTableRowFrame::CollapseRowIfNecessary(
             nsRect nextRect = rowFrame->GetRect();
             cRect.height += nextRect.height +
                             tableFrame->GetCellSpacingY(rowFrame->GetRowIndex());
           }
           rowFrame = rowFrame->GetNextRow();
         }
 
         nsRect oldCellRect = cellFrame->GetRect();
+        nsPoint oldCellNormalPos = cellFrame->GetNormalPosition();
         nsRect oldCellVisualOverflow = cellFrame->GetVisualOverflowRect();
 
-        if (aRowOffset == 0 && cRect.TopLeft() != oldCellRect.TopLeft()) {
+        if (aRowOffset == 0 && cRect.TopLeft() != oldCellNormalPos) {
           // We're moving the cell.  Invalidate the old overflow area
           cellFrame->InvalidateFrameSubtree();
         }
         
-        cellFrame->SetRect(cRect);
+        cellFrame->MovePositionBy(cRect.TopLeft() - oldCellNormalPos);
+        cellFrame->SetSize(cRect.Size());
 
         // XXXbz This looks completely bogus in the cases when we didn't
         // collapse the cell!
         nsRect cellBounds(0, 0, cRect.width, cRect.height);
         nsOverflowAreas cellOverflow(cellBounds, cellBounds);
         cellFrame->FinishAndStoreOverflow(cellOverflow, cRect.Size());
         nsTableFrame::RePositionViews(cellFrame);
         ConsiderChildOverflow(overflow, cellFrame);
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -200,17 +200,18 @@ DisplayRows(nsDisplayListBuilder* aBuild
   // approximate it by checking it for |f|: if it's true for any row
   // in |f| then it's true for |f| itself.
   nsIFrame* kid = aBuilder->ShouldDescendIntoFrame(f) ?
     nullptr : f->GetFirstRowContaining(aDirtyRect.y, &overflowAbove);
   
   if (kid) {
     // have a cursor, use it
     while (kid) {
-      if (kid->GetRect().y - overflowAbove >= aDirtyRect.YMost())
+      if (kid->GetRect().y - overflowAbove >= aDirtyRect.YMost() &&
+          kid->GetNormalRect().y - overflowAbove >= aDirtyRect.YMost())
         break;
       f->BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
       kid = kid->GetNextSibling();
     }
     return;
   }
   
   // No cursor. Traverse children the hard way and build a cursor while we're at it
@@ -272,26 +273,27 @@ nsTableRowGroupFrame::GetLogicalSkipSide
 }
 
 // Position and size aKidFrame and update our reflow state. The origin of
 // aKidRect is relative to the upper-left origin of our frame
 void 
 nsTableRowGroupFrame::PlaceChild(nsPresContext*         aPresContext,
                                  nsRowGroupReflowState& aReflowState,
                                  nsIFrame*              aKidFrame,
+                                 nsPoint                aKidPosition,
                                  nsHTMLReflowMetrics&   aDesiredSize,
                                  const nsRect&          aOriginalKidRect,
                                  const nsRect&          aOriginalKidVisualOverflow)
 {
   bool isFirstReflow =
     (aKidFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW) != 0;
 
   // Place and size the child
-  FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, nullptr, 0,
-                    aReflowState.y, 0);
+  FinishReflowChild(aKidFrame, aPresContext, aDesiredSize, nullptr,
+                    aKidPosition.x, aKidPosition.y, 0);
 
   nsTableFrame::InvalidateTableFrame(aKidFrame, aOriginalKidRect,
                                      aOriginalKidVisualOverflow, isFirstReflow);
 
   // Adjust the running y-offset
   aReflowState.y += aDesiredSize.Height();
 
   // If our height is constrained then update the available height
@@ -395,41 +397,41 @@ nsTableRowGroupFrame::ReflowChildren(nsP
       if (aReflowState.reflowState.IsHResize()) {
         kidReflowState.SetHResize(true);
       }
      
       NS_ASSERTION(kidFrame == mFrames.FirstChild() || prevKidFrame, 
                    "If we're not on the first frame, we should have a "
                    "previous sibling...");
       // If prev row has nonzero YMost, then we can't be at the top of the page
-      if (prevKidFrame && prevKidFrame->GetRect().YMost() > 0) {
+      if (prevKidFrame && prevKidFrame->GetNormalRect().YMost() > 0) {
         kidReflowState.mFlags.mIsTopOfPage = false;
       }
 
       ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState,
                   0, aReflowState.y, 0, aStatus);
+      nsPoint kidPosition(0, aReflowState.y);
+      kidReflowState.ApplyRelativePositioning(&kidPosition);
 
       // Place the child
-      PlaceChild(aPresContext, aReflowState, kidFrame, desiredSize,
-                 oldKidRect, oldKidVisualOverflow);
+      PlaceChild(aPresContext, aReflowState, kidFrame, kidPosition,
+                 desiredSize, oldKidRect, oldKidVisualOverflow);
       aReflowState.y += cellSpacingY;
 
       if (!reflowAllKids) {
         if (IsSimpleRowFrame(aReflowState.tableFrame, kidFrame)) {
           // Inform the row of its new height.
           rowFrame->DidResize();
           // the overflow area may have changed inflate the overflow area
           const nsStylePosition *stylePos = StylePosition();
           nsStyleUnit unit = stylePos->mHeight.GetUnit();
           if (aReflowState.tableFrame->IsAutoHeight() &&
               unit != eStyleUnit_Coord) {
             // Because other cells in the row may need to be aligned
             // differently, repaint the entire row
-            nsRect kidRect(0, aReflowState.y,
-                           desiredSize.Width(), desiredSize.Height());
             InvalidateFrame();
           }
           else if (oldKidRect.height != desiredSize.Height())
             needToCalcRowHeights = true;
         } else {
           needToCalcRowHeights = true;
         }
       }
@@ -544,17 +546,17 @@ nsTableRowGroupFrame::CalculateRowHeight
 
   int32_t startRowIndex = GetStartRowIndex();
   // find the row corresponding to the row index we just found
   nsTableRowFrame* startRowFrame = GetFirstRow();
 
   if (!startRowFrame) return;
 
   // the current row group height is the y origin of the 1st row we are about to calculated a height for
-  nscoord startRowGroupHeight = startRowFrame->GetPosition().y;
+  nscoord startRowGroupHeight = startRowFrame->GetNormalPosition().y;
 
   int32_t numRows = GetRowCount() - (startRowFrame->GetRowIndex() - GetStartRowIndex());
   // collect the current height of each row.  nscoord* rowHeights = nullptr;
   if (numRows <= 0)
     return;
 
   nsTArray<RowInfo> rowInfo;
   if (!rowInfo.AppendElements(numRows)) {
@@ -773,35 +775,36 @@ nsTableRowGroupFrame::CalculateRowHeight
     rowGroupHeight = aReflowState.ComputedHeight();
   }
 
   nscoord yOrigin = startRowGroupHeight;
   // update the rows with their (potentially) new heights
   for (rowFrame = startRowFrame, rowIndex = 0; rowFrame; rowFrame = rowFrame->GetNextRow(), rowIndex++) {
     nsRect rowBounds = rowFrame->GetRect();
     nsRect rowVisualOverflow = rowFrame->GetVisualOverflowRect();
+    nscoord deltaY = yOrigin - rowFrame->GetNormalPosition().y;
 
-    bool movedFrame = (rowBounds.y != yOrigin);  
     nscoord rowHeight = (rowInfo[rowIndex].height > 0) ? rowInfo[rowIndex].height : 0;
     
-    if (movedFrame || (rowHeight != rowBounds.height)) {
+    if (deltaY != 0 || (rowHeight != rowBounds.height)) {
       // Resize/move the row to its final size and position
-      if (movedFrame) {
+      if (deltaY != 0) {
         rowFrame->InvalidateFrameSubtree();
       }
       
-      rowFrame->SetRect(nsRect(rowBounds.x, yOrigin, rowBounds.width,
-                               rowHeight));
+      rowFrame->MovePositionBy(nsPoint(0, deltaY));
+      rowFrame->SetSize(nsSize(rowBounds.width, rowHeight));
 
       nsTableFrame::InvalidateTableFrame(rowFrame, rowBounds, rowVisualOverflow,
                                          false);
-    }
-    if (movedFrame) {
-      nsTableFrame::RePositionViews(rowFrame);
-      // XXXbz we don't need to update our overflow area?
+
+      if (deltaY != 0) {
+        nsTableFrame::RePositionViews(rowFrame);
+        // XXXbz we don't need to update our overflow area?
+      }
     }
     yOrigin += rowHeight + tableFrame->GetCellSpacingY(startRowIndex + rowIndex);
   }
 
   if (isPaginated && styleHeightAllocation) {
     // since the row group has a style height, cache the row heights, so next in flows can honor them 
     CacheRowHeightsForPrinting(aPresContext, GetFirstRow());
   }
@@ -864,21 +867,22 @@ nsTableRowGroupFrame::CollapseRowGroupIf
 }
 
 // Move a child that was skipped during a reflow.
 void
 nsTableRowGroupFrame::SlideChild(nsRowGroupReflowState& aReflowState,
                                  nsIFrame*              aKidFrame)
 {
   // Move the frame if we need to
-  nsPoint oldPosition = aKidFrame->GetPosition();
+  nsPoint oldPosition = aKidFrame->GetNormalPosition();
   nsPoint newPosition = oldPosition;
   newPosition.y = aReflowState.y;
   if (oldPosition.y != newPosition.y) {
     aKidFrame->InvalidateFrameSubtree();
+    aReflowState.reflowState.ApplyRelativePositioning(&newPosition);
     aKidFrame->SetPosition(newPosition);
     nsTableFrame::RePositionViews(aKidFrame);
     aKidFrame->InvalidateFrameSubtree();
   }
 }
 
 // Create a continuing frame, add it to the child list, and then push it
 // and the frames that follow
@@ -922,32 +926,32 @@ nsTableRowGroupFrame::SplitSpanningCells
   const bool borderCollapse = aTable.IsBorderCollapse();
   int32_t lastRowIndex = aLastRow.GetRowIndex();
   bool wasLast = false;
   bool haveRowSpan = false;
   // Iterate the rows between aFirstRow and aLastRow
   for (nsTableRowFrame* row = &aFirstRow; !wasLast; row = row->GetNextRow()) {
     wasLast = (row == &aLastRow);
     int32_t rowIndex = row->GetRowIndex();
-    nsPoint rowPos = row->GetPosition();
+    nsPoint rowPos = row->GetNormalPosition();
     // Iterate the cells looking for those that have rowspan > 1
     for (nsTableCellFrame* cell = row->GetFirstCell(); cell; cell = cell->GetNextCell()) {
       int32_t rowSpan = aTable.GetEffectiveRowSpan(rowIndex, *cell);
       // Only reflow rowspan > 1 cells which span aLastRow. Those which don't span aLastRow
       // were reflowed correctly during the unconstrained height reflow. 
       if ((rowSpan > 1) && (rowIndex + rowSpan > lastRowIndex)) {
         haveRowSpan = true;
         nsReflowStatus status;
         // Ask the row to reflow the cell to the height of all the rows it spans up through aLastRow
         // aAvailHeight is the space between the row group start and the end of the page
         nscoord cellAvailHeight = aSpanningRowBottom - rowPos.y;
         NS_ASSERTION(cellAvailHeight >= 0, "No space for cell?");
         bool isTopOfPage = (row == &aFirstRow) && aFirstRowIsTopOfPage;
 
-        nsRect rowRect = row->GetRect();
+        nsRect rowRect = row->GetNormalRect();
         nsSize rowAvailSize(aReflowState.AvailableWidth(),
                             std::max(aReflowState.AvailableHeight() - rowRect.y,
                                    0));
         // don't let the available height exceed what
         // CalculateRowHeights set for it
         rowAvailSize.height = std::min(rowAvailSize.height, rowRect.height);
         nsHTMLReflowState rowReflowState(&aPresContext, aReflowState, row,
                                          LogicalSize(row->GetWritingMode(),
@@ -987,17 +991,17 @@ nsTableRowGroupFrame::SplitSpanningCells
               aContRow->InsertCellFrame(contCell, colIndex);
             }
           }
         }
       }
     }
   }
   if (!haveRowSpan) {
-    aDesiredHeight = aLastRow.GetRect().YMost();
+    aDesiredHeight = aLastRow.GetNormalRect().YMost();
   }
 }
 
 // Remove the next-in-flow of the row, its cells and their cell blocks. This 
 // is necessary in case the row doesn't need a continuation later on or needs 
 // a continuation which doesn't have the same number of cells that now exist. 
 void
 nsTableRowGroupFrame::UndoContinuedRow(nsPresContext*   aPresContext,
@@ -1068,17 +1072,17 @@ nsTableRowGroupFrame::SplitRowGroup(nsPr
   // reflowing its cell as an optimization.
   aTableFrame->SetGeometryDirty();
 
   // Walk each of the row frames looking for the first row frame that doesn't fit 
   // in the available space
   for (nsTableRowFrame* rowFrame = firstRowThisPage; rowFrame; rowFrame = rowFrame->GetNextRow()) {
     bool rowIsOnPage = true;
     nscoord cellSpacingY = aTableFrame->GetCellSpacingY(rowFrame->GetRowIndex());
-    nsRect rowRect = rowFrame->GetRect();
+    nsRect rowRect = rowFrame->GetNormalRect();
     // See if the row fits on this page
     if (rowRect.YMost() > availHeight) {
       nsTableRowFrame* contRow = nullptr;
       // Reflow the row in the availabe space and have it split if it is the 1st
       // row (on the page) or there is at least 5% of the current page available 
       // XXX this 5% should be made a preference 
       if (!prevRowFrame || (availHeight - aDesiredSize.Height() > pageHeight / 20)) { 
         nsSize availSize(availWidth, std::max(availHeight - rowRect.y, 0));
@@ -1174,17 +1178,17 @@ nsTableRowGroupFrame::SplitRowGroup(nsPr
       nscoord spanningRowBottom = availHeight;
       if (!rowIsOnPage) {
         NS_ASSERTION(!contRow, "We should not have created a continuation if none of this row fits");
         if (!aRowForcedPageBreak && ShouldAvoidBreakInside(aReflowState)) {
           aStatus = NS_INLINE_LINE_BREAK_BEFORE();
           break;
         }
         if (prevRowFrame) {
-          spanningRowBottom = prevRowFrame->GetRect().YMost();
+          spanningRowBottom = prevRowFrame->GetNormalRect().YMost();
           lastRowThisPage = prevRowFrame;
           isTopOfPage = (lastRowThisPage == firstRowThisPage) && aReflowState.mFlags.mIsTopOfPage;
           aStatus = NS_FRAME_NOT_COMPLETE;
         }
         else {
           // We can't push children, so let our parent reflow us again with more space
           aDesiredSize.Height() = rowRect.YMost();
           aStatus = NS_FRAME_COMPLETE;
@@ -1211,17 +1215,17 @@ nsTableRowGroupFrame::SplitRowGroup(nsPr
             UndoContinuedRow(aPresContext, contRow);
             contRow = nullptr;
           }
         }
         else { // (firstTruncatedRow != firstRowThisPage)
           // Try to put firstTruncateRow on the next page 
           nsTableRowFrame* rowBefore = ::GetRowBefore(*firstRowThisPage, *firstTruncatedRow);
           nscoord oldSpanningRowBottom = spanningRowBottom;
-          spanningRowBottom = rowBefore->GetRect().YMost();
+          spanningRowBottom = rowBefore->GetNormalRect().YMost();
 
           UndoContinuedRow(aPresContext, contRow);
           contRow = nullptr;
           nsTableRowFrame* oldLastRowThisPage = lastRowThisPage;
           lastRowThisPage = rowBefore;
           aStatus = NS_FRAME_NOT_COMPLETE;
 
           // Call SplitSpanningCells again with rowBefore as the last row on the page
@@ -1890,35 +1894,46 @@ nsTableRowGroupFrame::GetFirstRowContain
   // we don't need to check that here.
   
   // We use property->mOverflowBelow here instead of computing the frame's
   // true overflowArea.YMost(), because it is essential for the thresholds
   // to form a monotonically increasing sequence. Otherwise we would break
   // encountering a row whose overflowArea.YMost() is <= aY but which has
   // a row above it containing cell(s) that span to include aY.
   while (cursorIndex > 0 &&
-         cursorFrame->GetRect().YMost() + property->mOverflowBelow > aY) {
+         cursorFrame->GetNormalRect().YMost() + property->mOverflowBelow > aY) {
     --cursorIndex;
     cursorFrame = property->mFrames[cursorIndex];
   }
   while (cursorIndex + 1 < frameCount &&
-         cursorFrame->GetRect().YMost() + property->mOverflowBelow <= aY) {
+         cursorFrame->GetNormalRect().YMost() + property->mOverflowBelow <= aY) {
     ++cursorIndex;
     cursorFrame = property->mFrames[cursorIndex];
   }
 
   property->mCursorIndex = cursorIndex;
   *aOverflowAbove = property->mOverflowAbove;
   return cursorFrame;
 }
 
 bool
 nsTableRowGroupFrame::FrameCursorData::AppendFrame(nsIFrame* aFrame)
 {
-  nsRect overflowRect = aFrame->GetVisualOverflowRect();
+  // Relative positioning can cause table parts to move, but we will still paint
+  // the backgrounds for the parts under them at their 'normal' position. That
+  // means that we must consider the overflow rects at both positions. For
+  // example, if we use relative positioning to move a row-spanning cell, we
+  // will still paint the row background for that cell at its normal position,
+  // which will overflow the row.
+  // XXX(seth): This probably isn't correct in the presence of transforms.
+  nsRect positionedOverflowRect = aFrame->GetVisualOverflowRect();
+  nsPoint positionedToNormal = aFrame->GetNormalPosition() - aFrame->GetPosition();
+  nsRect normalOverflowRect = positionedOverflowRect + positionedToNormal;
+
+  nsRect overflowRect = positionedOverflowRect.Union(normalOverflowRect);
   if (overflowRect.IsEmpty())
     return true;
   nscoord overflowAbove = -overflowRect.y;
   nscoord overflowBelow = overflowRect.YMost() - aFrame->GetSize().height;
   mOverflowAbove = std::max(mOverflowAbove, overflowAbove);
   mOverflowBelow = std::max(mOverflowBelow, overflowBelow);
   return mFrames.AppendElement(aFrame) != nullptr;
 }
--- a/layout/tables/nsTableRowGroupFrame.h
+++ b/layout/tables/nsTableRowGroupFrame.h
@@ -332,16 +332,17 @@ protected:
                             bool               aBorderCollapse,
                             nsHTMLReflowState& aReflowState);
   
   virtual LogicalSides GetLogicalSkipSides(const nsHTMLReflowState* aReflowState = nullptr) const MOZ_OVERRIDE;
 
   void PlaceChild(nsPresContext*         aPresContext,
                   nsRowGroupReflowState& aReflowState,
                   nsIFrame*              aKidFrame,
+                  nsPoint                aKidPosition,
                   nsHTMLReflowMetrics&   aDesiredSize,
                   const nsRect&          aOriginalKidRect,
                   const nsRect&          aOriginalKidVisualOverflow);
 
   void CalculateRowHeights(nsPresContext*           aPresContext, 
                            nsHTMLReflowMetrics&     aDesiredSize,
                            const nsHTMLReflowState& aReflowState);
 
--- a/layout/xul/nsMenuBarFrame.cpp
+++ b/layout/xul/nsMenuBarFrame.cpp
@@ -77,19 +77,19 @@ nsMenuBarFrame::Init(nsIContent*       a
   // Also hook up the listener to the window listening for focus events. This is so we can keep proper
   // state as the user alt-tabs through processes.
 
   mTarget->AddSystemEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false);
   mTarget->AddSystemEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);
   mTarget->AddSystemEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
 
   // mousedown event should be handled in all phase
-  mTarget->AddSystemEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
-  mTarget->AddSystemEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
-  mTarget->AddSystemEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
+  mTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
+  mTarget->AddEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
+  mTarget->AddEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
 }
 
 NS_IMETHODIMP
 nsMenuBarFrame::SetActive(bool aActiveFlag)
 {
   // If the activity is not changed, there is nothing to do.
   if (mIsActive == aActiveFlag)
     return NS_OK;
@@ -411,16 +411,16 @@ nsMenuBarFrame::DestroyFrom(nsIFrame* aD
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (pm)
     pm->SetActiveMenuBar(this, false);
 
   mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keypress"), mMenuBarListener, false);
   mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keydown"), mMenuBarListener, false);
   mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("keyup"), mMenuBarListener, false);
 
-  mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
-  mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
-  mTarget->RemoveSystemEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
+  mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, true);
+  mTarget->RemoveEventListener(NS_LITERAL_STRING("mousedown"), mMenuBarListener, false);
+  mTarget->RemoveEventListener(NS_LITERAL_STRING("blur"), mMenuBarListener, true);
 
   NS_IF_RELEASE(mMenuBarListener);
 
   nsBoxFrame::DestroyFrom(aDestructRoot);
 }
--- a/memory/build/mozjemalloc_compat.c
+++ b/memory/build/mozjemalloc_compat.c
@@ -5,18 +5,21 @@
 #ifndef MOZ_JEMALLOC3
 #  error Should only compile this file when building with jemalloc 3
 #endif
 
 #define MOZ_JEMALLOC_IMPL
 
 #include "mozmemory_wrap.h"
 #include "jemalloc_types.h"
+#include "jemalloc/internal/jemalloc_internal_defs.h" // for JEMALLOC_HAS_ALLOCA_H
 #include "mozilla/Types.h"
 
+#include <stdbool.h>
+
 #if defined(MOZ_NATIVE_JEMALLOC)
 
 MOZ_IMPORT_API int
 je_(mallctl)(const char*, void*, size_t*, void*, size_t);
 MOZ_IMPORT_API int
 je_(mallctlnametomib)(const char *name, size_t *mibp, size_t *miblenp);
 MOZ_IMPORT_API int
 je_(mallctlbymib)(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
@@ -42,27 +45,98 @@ je_(nallocx)(size_t size, int flags);
 	size_t mib[6];							\
 	size_t miblen = sizeof(mib) / sizeof(mib[0]);			\
 	size_t sz = sizeof(v);						\
 	je_(mallctlnametomib)(n, mib, &miblen);			\
 	mib[2] = i;							\
 	je_(mallctlbymib)(mib, miblen, &v, &sz, NULL, 0);		\
 } while (0)
 
+#define	CTL_IJ_GET(n, v, i, j) do {					\
+	size_t mib[6];							\
+	size_t miblen = sizeof(mib) / sizeof(mib[0]);			\
+	size_t sz = sizeof(v);						\
+	je_(mallctlnametomib)(n, mib, &miblen);				\
+	mib[2] = i;							\
+	mib[4] = j;							\
+	je_(mallctlbymib)(mib, miblen, &v, &sz, NULL, 0);			\
+} while (0)
+
+/*
+ * VARIABLE_ARRAY is copied from
+ * memory/jemalloc/src/include/jemalloc/internal/jemalloc_internal.h.in
+ */
+#if __STDC_VERSION__ < 199901L
+#  ifdef _MSC_VER
+#    include <malloc.h>
+#    define alloca _alloca
+#  else
+#    ifdef JEMALLOC_HAS_ALLOCA_H
+#      include <alloca.h>
+#    else
+#      include <stdlib.h>
+#    endif
+#  endif
+#  define VARIABLE_ARRAY(type, name, count) \
+	type *name = alloca(sizeof(type) * (count))
+#else
+#  define VARIABLE_ARRAY(type, name, count) type name[(count)]
+#endif
+
 MOZ_MEMORY_API size_t
 malloc_good_size_impl(size_t size)
 {
   /* je_nallocx crashes when given a size of 0. As
    * malloc_usable_size(malloc(0)) and malloc_usable_size(malloc(1))
    * return the same value, use a size of 1. */
   if (size == 0)
     size = 1;
   return je_(nallocx)(size, 0);
 }
 
+static size_t
+compute_bin_unused(unsigned int narenas)
+{
+    size_t bin_unused = 0;
+
+    uint32_t nregs; // number of regions per run in the j-th bin
+    size_t reg_size; // size of regions served by the j-th bin
+    size_t curruns; // number of runs belonging to a bin
+    size_t curregs; // number of allocated regions in a bin
+
+    unsigned int nbins; // number of bins per arena
+    unsigned int i, j;
+
+    // narenas also counts uninitialized arenas, and initialized arenas
+    // are not guaranteed to be adjacent
+    VARIABLE_ARRAY(bool, initialized, narenas);
+    size_t isz = sizeof(initialized) / sizeof(initialized[0]);
+
+    je_(mallctl)("arenas.initialized", initialized, &isz, NULL, 0);
+    CTL_GET("arenas.nbins", nbins);
+
+    for (j = 0; j < nbins; j++) {
+        CTL_I_GET("arenas.bin.0.nregs", nregs, j);
+        CTL_I_GET("arenas.bin.0.size", reg_size, j);
+
+        for (i = 0; i < narenas; i++) {
+            if (!initialized[i]) {
+                continue;
+            }
+
+            CTL_IJ_GET("stats.arenas.0.bins.0.curruns", curruns, i, j);
+            CTL_IJ_GET("stats.arenas.0.bins.0.curregs", curregs, i, j);
+
+            bin_unused += (nregs * curruns - curregs) * reg_size;
+        }
+    }
+
+    return bin_unused;
+}
+
 MOZ_JEMALLOC_API void
 jemalloc_stats_impl(jemalloc_stats_t *stats)
 {
   unsigned narenas;
   size_t active, allocated, mapped, page, pdirty;
   size_t lg_chunk;
 
   // Refresh jemalloc's stats by updating its epoch, see ctl_refresh in
@@ -85,17 +159,18 @@ jemalloc_stats_impl(jemalloc_stats_t *st
   stats->mapped = mapped;
   stats->allocated = allocated;
   stats->waste = active - allocated;
   stats->page_cache = pdirty * page;
 
   // We could get this value out of base.c::base_pages, but that really should
   // be an upstream change, so don't worry about it for now.
   stats->bookkeeping = 0;
-  stats->bin_unused = 0;
+
+  stats->bin_unused = compute_bin_unused(narenas);
 }
 
 MOZ_JEMALLOC_API void
 jemalloc_purge_freed_pages_impl()
 {
 }
 
 MOZ_JEMALLOC_API void
--- a/netwerk/base/public/nsIURIClassifier.idl
+++ b/netwerk/base/public/nsIURIClassifier.idl
@@ -25,17 +25,17 @@ interface nsIURIClassifierCallback : nsI
    */
   void onClassifyComplete(in nsresult aErrorCode);
 };
 
 /**
  * The URI classifier service checks a URI against lists of phishing
  * and malware sites.
  */
-[scriptable, uuid(de4f03cd-1a28-4f51-906b-c54b47a533c5)]
+[scriptable, uuid(03d26681-0ef5-4718-9777-33c9e1ee3e32)]
 interface nsIURIClassifier : nsISupports
 {
   /**
    * Classify a Principal using its URI.
    *
    * @param aPrincipal
    *        The principal that should be checked by the URI classifier.
    * @param aTrackingProtectionEnabled
@@ -49,9 +49,17 @@ interface nsIURIClassifier : nsISupports
    * @return <code>false</code> if classification is not necessary.  The
    *         callback will not be called.
    *         <code>true</code> if classification will be performed.  The
    *         callback will be called.
    */
   boolean classify(in nsIPrincipal aPrincipal,
                    in boolean aTrackingProtectionEnabled,
                    in nsIURIClassifierCallback aCallback);
+
+  /**
+   * Synchronously classify a Principal locally using its URI. This does not
+   * make network requests. The result is an error code with which the channel
+   * should be cancelled, or NS_OK if no result was found.
+   */
+  nsresult classifyLocal(in nsIPrincipal aPrincipal,
+                         in boolean aTrackingProtectionEnabled);
 };
--- a/python/mozbuild/mozbuild/mozinfo.py
+++ b/python/mozbuild/mozbuild/mozinfo.py
@@ -77,16 +77,17 @@ def build_dict(config, env=os.environ):
         d["bits"] = 32
     # other CPUs will wind up with unknown bits
 
     d['debug'] = substs.get('MOZ_DEBUG') == '1'
     d['crashreporter'] = bool(substs.get('MOZ_CRASHREPORTER'))
     d['datareporting'] = bool(substs.get('MOZ_DATA_REPORTING'))
     d['healthreport'] = substs.get('MOZ_SERVICES_HEALTHREPORT') == '1'
     d['asan'] = substs.get('MOZ_ASAN') == '1'
+    d['tsan'] = substs.get('MOZ_TSAN') == '1'
     d['tests_enabled'] = substs.get('ENABLE_TESTS') == "1"
     d['bin_suffix'] = substs.get('BIN_SUFFIX', '')
 
     d['webm'] = bool(substs.get('MOZ_WEBM'))
     d['wave'] = bool(substs.get('MOZ_WAVE'))
 
     d['official'] = bool(substs.get('MOZILLA_OFFICIAL'))
 
--- a/toolkit/components/url-classifier/Classifier.cpp
+++ b/toolkit/components/url-classifier/Classifier.cpp
@@ -6,16 +6,17 @@
 #include "Classifier.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsISimpleEnumerator.h"
 #include "nsIRandomGenerator.h"
 #include "nsIInputStream.h"
 #include "nsISeekableStream.h"
 #include "nsIFile.h"
+#include "nsThreadUtils.h"
 #include "mozilla/Telemetry.h"
 #include "prlog.h"
 
 // NSPR_LOG_MODULES=UrlClassifierDbService:5
 extern PRLogModuleInfo *gUrlClassifierDbServiceLog;
 #if defined(PR_LOGGING)
 #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args)
 #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4)
@@ -49,17 +50,16 @@ Classifier::SplitTables(const nsACString
     begin = iter;
     if (begin != end) {
       begin++;
     }
   }
 }
 
 Classifier::Classifier()
-  : mFreshTime(45 * 60)
 {
 }
 
 Classifier::~Classifier()
 {
   Close();
 }
 
@@ -139,16 +139,19 @@ Classifier::Open(nsIFile& aCacheDirector
   // backup if so.
   rv = RecoverBackups();
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Make sure the main store directory exists.
   rv = CreateStoreDirectory();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  // Classifier keeps its own cryptoHash for doing operations on the background
+  // thread. Callers can optionally pass in an nsICryptoHash for working on the
+  // main thread.
   mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Build the list of know urlclassifier lists
   // XXX: Disk IO potentially on the main thread during startup
   RegenActiveTables();
 
   return NS_OK;
@@ -211,18 +214,25 @@ Classifier::TableRequest(nsACString& aRe
 
     aResult.Append('\n');
   }
 }
 
 nsresult
 Classifier::Check(const nsACString& aSpec,
                   const nsACString& aTables,
+                  uint32_t aFreshnessGuarantee,
+                  nsICryptoHash* aCryptoHash,
                   LookupResultArray& aResults)
 {
+  nsCOMPtr<nsICryptoHash> cryptoHash = aCryptoHash;
+  if (!aCryptoHash) {
+    MOZ_ASSERT(!NS_IsMainThread(), "mCryptoHash must be used on worker thread");
+    cryptoHash = mCryptoHash;
+  }
   Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_CL_CHECK_TIME> timer;
 
   // Get the set of fragments based on the url. This is necessary because we
   // only look up at most 5 URLs per aSpec, even if aSpec has more than 5
   // components.
   nsTArray<nsCString> fragments;
   nsresult rv = LookupCache::GetLookupFragments(aSpec, &fragments);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -239,21 +249,21 @@ Classifier::Check(const nsACString& aSpe
     } else {
       return NS_ERROR_FAILURE;
     }
   }
 
   // Now check each lookup fragment against the entries in the DB.
   for (uint32_t i = 0; i < fragments.Length(); i++) {
     Completion lookupHash;
-    lookupHash.FromPlaintext(fragments[i], mCryptoHash);
+    lookupHash.FromPlaintext(fragments[i], cryptoHash);
 
     // Get list of host keys to look up
     Completion hostKey;
-    rv = LookupCache::GetKey(fragments[i], &hostKey, mCryptoHash);
+    rv = LookupCache::GetKey(fragments[i], &hostKey, cryptoHash);
     if (NS_FAILED(rv)) {
       // Local host on the network.
       continue;
     }
 
 #if DEBUG && defined(PR_LOGGING)
     if (LOG_ENABLED()) {
       nsAutoCString checking;
@@ -283,17 +293,17 @@ Classifier::Check(const nsACString& aSpe
 
         LOG(("Found a result in %s: %s (Age: %Lds)",
              cache->TableName().get(),
              complete ? "complete." : "Not complete.",
              age));
 
         result->hash.complete = lookupHash;
         result->mComplete = complete;
-        result->mFresh = (age < mFreshTime);
+        result->mFresh = (age < aFreshnessGuarantee);
         result->mTableName.Assign(cache->TableName());
       }
     }
 
   }
 
   return NS_OK;
 }
--- a/toolkit/components/url-classifier/Classifier.h
+++ b/toolkit/components/url-classifier/Classifier.h
@@ -42,31 +42,32 @@ public:
    */
   nsresult ActiveTables(nsTArray<nsCString>& aTables);
 
   /**
    * Check a URL against the specified tables.
    */
   nsresult Check(const nsACString& aSpec,
                  const nsACString& tables,
+                 uint32_t aFreshnessGuarantee,
+                 nsICryptoHash* aCryptoHash,
                  LookupResultArray& aResults);
 
   /**
    * Apply the table updates in the array.  Takes ownership of
    * the updates in the array and clears it.  Wacky!
    */
   nsresult ApplyUpdates(nsTArray<TableUpdate*>* aUpdates);
   /**
    * Failed update. Spoil the entries so we don't block hosts
    * unnecessarily
    */
   nsresult MarkSpoiled(nsTArray<nsCString>& aTables);
   nsresult CacheCompletions(const CacheResultArray& aResults);
   uint32_t GetHashKey(void) { return mHashKey; }
-  void SetFreshTime(uint32_t aTime) { mFreshTime = aTime; }
   /*
    * Get a bunch of extra prefixes to query for completion
    * and mask the real entry being requested
    */
   nsresult ReadNoiseEntries(const Prefix& aPrefix,
                             const nsACString& aTableName,
                             uint32_t aCount,
                             PrefixArray* aNoiseEntries);
@@ -97,15 +98,14 @@ private:
   nsCOMPtr<nsIFile> mToDeleteDirectory;
   nsCOMPtr<nsICryptoHash> mCryptoHash;
   nsTArray<HashStore*> mHashStores;
   nsTArray<LookupCache*> mLookupCaches;
   nsTArray<nsCString> mActiveTablesCache;
   uint32_t mHashKey;
   // Stores the last time a given table was updated (seconds).
   nsDataHashtable<nsCStringHashKey, int64_t> mTableFreshness;
-  uint32_t mFreshTime;
 };
 
 }
 }
 
 #endif
--- a/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl
+++ b/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl
@@ -180,19 +180,21 @@ interface nsIUrlClassifierDBService : ns
    */
   void resetDatabase();
 };
 
 /**
  * Interface for the actual worker thread.  Implementations of this need not
  * be thread aware and just work on the database.
  */
-[scriptable, uuid(abcd7978-c304-4a7d-a44c-33c2ed5441e7)]
+[scriptable, uuid(b7b505d0-bfa2-44db-abf8-6e2bfc25bbab)]
 interface nsIUrlClassifierDBServiceWorker : nsIUrlClassifierDBService
 {
+  // Open the DB connection
+  void openDb();
   // Provide a way to forcibly close the db connection.
   void closeDb();
 
   [noscript]void cacheCompletions(in CacheCompletionArray completions);
   [noscript]void cacheMisses(in PrefixArray misses);
 };
 
 /**
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -116,25 +116,30 @@ public:
   nsresult QueueLookup(const nsACString& lookupKey,
                        const nsACString& tables,
                        nsIUrlClassifierLookupCallback* callback);
 
   // Handle any queued-up lookups.  We call this function during long-running
   // update operations to prevent lookups from blocking for too long.
   nsresult HandlePendingLookups();
 
+  // Perform a blocking classifier lookup for a given url. Can be called on
+  // either the main thread or the worker thread.
+  nsresult DoLocalLookup(const nsACString& spec,
+                         const nsACString& tables,
+                         nsICryptoHash* cryptoHash,
+                         LookupResultArray* results);
+
 private:
   // No subclassing
   ~nsUrlClassifierDBServiceWorker();
 
   // Disallow copy constructor
   nsUrlClassifierDBServiceWorker(nsUrlClassifierDBServiceWorker&);
 
-  nsresult OpenDb();
-
   // Applies the current transaction and resets the update/working times.
   nsresult ApplyUpdate();
 
   // Reset the in-progress update stream
   void ResetStream();
 
   // Reset the in-progress update
   void ResetUpdate();
@@ -144,16 +149,17 @@ private:
                     const nsACString& tables,
                     nsIUrlClassifierLookupCallback* c);
 
   nsresult AddNoise(const Prefix aPrefix,
                     const nsCString tableName,
                     uint32_t aCount,
                     LookupResultArray& results);
 
+  // Can only be used on the background thread
   nsCOMPtr<nsICryptoHash> mCryptoHash;
 
   nsAutoPtr<Classifier> mClassifier;
   // The class that actually parses the update chunks.
   nsAutoPtr<ProtocolParser> mProtocolParser;
 
   // Directory where to store the SB databases.
   nsCOMPtr<nsIFile> mCacheDir;
@@ -236,16 +242,86 @@ nsUrlClassifierDBServiceWorker::QueueLoo
   lookup->mStartTime = TimeStamp::Now();
   lookup->mKey = spec;
   lookup->mCallback = callback;
   lookup->mTables = tables;
 
   return NS_OK;
 }
 
+nsresult
+nsUrlClassifierDBServiceWorker::DoLocalLookup(const nsACString& spec,
+                                              const nsACString& tables,
+                                              nsICryptoHash* cryptoHash,
+                                              LookupResultArray* results)
+{
+  LOG(("nsUrlClassifierDBServiceWorker::DoLocalLookup %s (main=%s) %p",
+       spec.Data(), NS_IsMainThread() ? "true" : "false", this));
+  if (!results) {
+    return NS_ERROR_FAILURE;
+  }
+  // Bail if we haven't been initialized on the background thread.
+  if (!mClassifier) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  // We ignore failures from Check because we'd rather return the
+  // results that were found than fail.
+  mClassifier->Check(spec, tables, gFreshnessGuarantee, cryptoHash, *results);
+
+  LOG(("Found %d results.", results->Length()));
+  return NS_OK;
+}
+
+static nsresult
+TablesToResponse(const nsACString& tables,
+                 bool checkMalware,
+                 bool checkPhishing,
+                 bool checkTracking)
+{
+  if (checkMalware &&
+      FindInReadable(NS_LITERAL_CSTRING("-malware-"), tables)) {
+    return NS_ERROR_MALWARE_URI;
+  }
+  if (checkPhishing &&
+    FindInReadable(NS_LITERAL_CSTRING("-phish-"), tables)) {
+    return NS_ERROR_PHISHING_URI;
+  }
+  if (checkTracking &&
+    FindInReadable(NS_LITERAL_CSTRING("-track-"), tables)) {
+    return NS_ERROR_TRACKING_URI;
+  }
+  return NS_OK;
+}
+
+static nsresult
+ProcessLookupResults(LookupResultArray* results,
+                     bool checkMalware,
+                     bool checkPhishing,
+                     bool checkTracking)
+{
+  // Build a stringified list of result tables.
+  nsTArray<nsCString> tables;
+  for (uint32_t i = 0; i < results->Length(); i++) {
+    LookupResult& result = results->ElementAt(i);
+    MOZ_ASSERT(!result.mNoise, "Lookup results should not have noise added");
+    LOG(("Found result from table %s", result.mTableName.get()));
+    if (tables.IndexOf(result.mTableName) == nsTArray<nsCString>::NoIndex) {
+      tables.AppendElement(result.mTableName);
+    }
+  }
+  nsAutoCString tableStr;
+  for (uint32_t i = 0; i < tables.Length(); i++) {
+    if (i != 0)
+      tableStr.Append(',');
+    tableStr.Append(tables[i]);
+  }
+  return TablesToResponse(tableStr, checkMalware, checkPhishing, checkTracking);
+}
+
 /**
  * Lookup up a key in the database is a two step process:
  *
  * a) First we look for any Entries in the database that might apply to this
  *    url.  For each URL there are one or two possible domain names to check:
  *    the two-part domain name (example.com) and the three-part name
  *    (www.example.com).  We check the database for both of these.
  * b) If we find any entries, we check the list of fragments for that entry
@@ -257,40 +333,34 @@ nsUrlClassifierDBServiceWorker::DoLookup
                                          const nsACString& tables,
                                          nsIUrlClassifierLookupCallback* c)
 {
   if (gShuttingDownThread) {
     c->LookupComplete(nullptr);
     return NS_ERROR_NOT_INITIALIZED;
   }
 
-  nsresult rv = OpenDb();
-  if (NS_FAILED(rv)) {
-    c->LookupComplete(nullptr);
-    NS_ERROR("Unable to open SafeBrowsing database.");
-    return NS_ERROR_FAILURE;
-  }
-
 #if defined(PR_LOGGING)
   PRIntervalTime clockStart = 0;
   if (LOG_ENABLED()) {
     clockStart = PR_IntervalNow();
   }
 #endif
 
   nsAutoPtr<LookupResultArray> results(new LookupResultArray());
   if (!results) {
     c->LookupComplete(nullptr);
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  // we ignore failures from Check because we'd rather return the
-  // results that were found than fail.
-  mClassifier->SetFreshTime(gFreshnessGuarantee);
-  mClassifier->Check(spec, tables, *results);
+  nsresult rv = DoLocalLookup(spec, tables, nullptr, results);
+  if (NS_FAILED(rv)) {
+    c->LookupComplete(nullptr);
+    return rv;
+  }
 
   LOG(("Found %d results.", results->Length()));
 
 
 #if defined(PR_LOGGING)
   if (LOG_ENABLED()) {
     PRIntervalTime clockEnd = PR_IntervalNow();
     LOG(("query took %dms\n",
@@ -452,16 +522,17 @@ nsUrlClassifierDBServiceWorker::BeginUpd
   return NS_OK;
 }
 
 // Called from the stream updater.
 NS_IMETHODIMP
 nsUrlClassifierDBServiceWorker::BeginStream(const nsACString &table)
 {
   LOG(("nsUrlClassifierDBServiceWorker::BeginStream"));
+  MOZ_ASSERT(!NS_IsMainThread(), "Streaming must be on the background thread");
 
   if (gShuttingDownThread)
     return NS_ERROR_NOT_INITIALIZED;
 
   NS_ENSURE_STATE(mUpdateObserver);
   NS_ENSURE_STATE(!mInStream);
 
   mInStream = true;
@@ -727,34 +798,31 @@ nsUrlClassifierDBServiceWorker::CacheMis
     mMissCache.AppendElement(resultsPtr->ElementAt(i));
    }
   return NS_OK;
 }
 
 nsresult
 nsUrlClassifierDBServiceWorker::OpenDb()
 {
+  MOZ_ASSERT(!NS_IsMainThread(), "Must initialize DB on background thread");
   // Connection already open, don't do anything.
   if (mClassifier) {
     return NS_OK;
   }
 
-  LOG(("Opening db"));
-
   nsresult rv;
   mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoPtr<Classifier> classifier(new Classifier());
   if (!classifier) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  classifier->SetFreshTime(gFreshnessGuarantee);
-
   rv = classifier->Open(*mCacheDir);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mClassifier = classifier;
 
   return NS_OK;
 }
 
@@ -1020,35 +1088,19 @@ private:
 };
 
 NS_IMPL_ISUPPORTS(nsUrlClassifierClassifyCallback,
                   nsIUrlClassifierCallback)
 
 NS_IMETHODIMP
 nsUrlClassifierClassifyCallback::HandleEvent(const nsACString& tables)
 {
-  // XXX: we should probably have the wardens tell the service which table
-  // names match with which classification.  For now the table names give
-  // enough information.
-  nsresult response = NS_OK;
-
-  if (mCheckMalware &&
-      FindInReadable(NS_LITERAL_CSTRING("-malware-"), tables)) {
-    response = NS_ERROR_MALWARE_URI;
-  } else if (mCheckPhishing &&
-    FindInReadable(NS_LITERAL_CSTRING("-phish-"), tables)) {
-    response = NS_ERROR_PHISHING_URI;
-  } else if (mCheckTracking &&
-    FindInReadable(NS_LITERAL_CSTRING("-track-"), tables)) {
-    LOG(("Blocking tracking uri [this=%p]", this));
-    response = NS_ERROR_TRACKING_URI;
-  }
-
+  nsresult response = TablesToResponse(tables, mCheckMalware,
+                                       mCheckPhishing, mCheckTracking);
   mCallback->OnClassifyComplete(response);
-
   return NS_OK;
 }
 
 
 // -------------------------------------------------------------------------
 // Proxy class implementation
 
 NS_IMPL_ISUPPORTS(nsUrlClassifierDBService,
@@ -1136,16 +1188,17 @@ nsUrlClassifierDBService::ReadTablesFrom
 
 nsresult
 nsUrlClassifierDBService::Init()
 {
 #if defined(PR_LOGGING)
   if (!gUrlClassifierDbServiceLog)
     gUrlClassifierDbServiceLog = PR_NewLogModule("UrlClassifierDbService");
 #endif
+  MOZ_ASSERT(NS_IsMainThread(), "Must initialize DB service on main thread");
 
   // Retrieve all the preferences.
   mCheckMalware = Preferences::GetBool(CHECK_MALWARE_PREF,
     CHECK_MALWARE_DEFAULT);
   mCheckPhishing = Preferences::GetBool(CHECK_PHISHING_PREF,
     CHECK_PHISHING_DEFAULT);
   mCheckTracking = Preferences::GetBool(CHECK_TRACKING_PREF,
     CHECK_TRACKING_DEFAULT);
@@ -1165,17 +1218,17 @@ nsUrlClassifierDBService::Init()
   Preferences::AddStrongObserver(this, MALWARE_TABLE_PREF);
   Preferences::AddStrongObserver(this, TRACKING_TABLE_PREF);
   Preferences::AddStrongObserver(this, DOWNLOAD_BLOCK_TABLE_PREF);
   Preferences::AddStrongObserver(this, DOWNLOAD_ALLOW_TABLE_PREF);
   Preferences::AddStrongObserver(this, DISALLOW_COMPLETION_TABLE_PREF);
 
   // Force PSM loading on main thread
   nsresult rv;
-  nsCOMPtr<nsICryptoHash> acryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+  mCryptoHashMain = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Directory providers must also be accessed on the main thread.
   nsCOMPtr<nsIFile> cacheDir;
   rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR,
                               getter_AddRefs(cacheDir));
   if (NS_FAILED(rv)) {
     rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
@@ -1194,29 +1247,55 @@ nsUrlClassifierDBService::Init()
   rv = mWorker->Init(gethashNoise, cacheDir);
   if (NS_FAILED(rv)) {
     mWorker = nullptr;
     return rv;
   }
 
   // Proxy for calling the worker on the background thread
   mWorkerProxy = new UrlClassifierDBServiceWorkerProxy(mWorker);
+  rv = mWorkerProxy->OpenDb();
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   // Add an observer for shutdown
   nsCOMPtr<nsIObserverService> observerService =
       mozilla::services::GetObserverService();
   if (!observerService)
     return NS_ERROR_FAILURE;
 
   observerService->AddObserver(this, "profile-before-change", false);
   observerService->AddObserver(this, "xpcom-shutdown-threads", false);
 
   return NS_OK;
 }
 
+static void BuildTables(bool aTrackingProtectionEnabled, nsCString &tables)
+{
+  nsAutoCString malware;
+  // LookupURI takes a comma-separated list already.
+  Preferences::GetCString(MALWARE_TABLE_PREF, &malware);
+  if (!malware.IsEmpty()) {
+    tables.Append(malware);
+  }
+  nsAutoCString phishing;
+  Preferences::GetCString(PHISH_TABLE_PREF, &phishing);
+  if (!phishing.IsEmpty()) {
+    tables.Append(',');
+    tables.Append(phishing);
+  }
+  nsAutoCString tracking;
+  Preferences::GetCString(TRACKING_TABLE_PREF, &tracking);
+  if (aTrackingProtectionEnabled && !tracking.IsEmpty()) {
+    tables.Append(',');
+    tables.Append(tracking);
+  }
+}
+
 // nsChannelClassifier is the only consumer of this interface.
 NS_IMETHODIMP
 nsUrlClassifierDBService::Classify(nsIPrincipal* aPrincipal,
                                    bool aTrackingProtectionEnabled,
                                    nsIURIClassifierCallback* c,
                                    bool* result)
 {
   NS_ENSURE_ARG(aPrincipal);
@@ -1228,47 +1307,71 @@ nsUrlClassifierDBService::Classify(nsIPr
   }
 
   nsRefPtr<nsUrlClassifierClassifyCallback> callback =
     new nsUrlClassifierClassifyCallback(c, mCheckMalware, mCheckPhishing,
                                         mCheckTracking);
   if (!callback) return NS_ERROR_OUT_OF_MEMORY;
 
   nsAutoCString tables;
-  nsAutoCString malware;
-  // LookupURI takes a comma-separated list already.
-  Preferences::GetCString(MALWARE_TABLE_PREF, &malware);
-  if (!malware.IsEmpty()) {
-    tables.Append(malware);
-  }
-  nsAutoCString phishing;
-  Preferences::GetCString(PHISH_TABLE_PREF, &phishing);
-  if (!phishing.IsEmpty()) {
-    tables.Append(',');
-    tables.Append(phishing);
-  }
-  nsAutoCString tracking;
-  Preferences::GetCString(TRACKING_TABLE_PREF, &tracking);
-  if (aTrackingProtectionEnabled && !tracking.IsEmpty()) {
-    LOG(("Looking up third party in tracking table, [cb=%p]", callback.get()));
-    tables.Append(',');
-    tables.Append(tracking);
-  }
+  BuildTables(aTrackingProtectionEnabled, tables);
+
   nsresult rv = LookupURI(aPrincipal, tables, callback, false, result);
   if (rv == NS_ERROR_MALFORMED_URI) {
     *result = false;
     // The URI had no hostname, don't try to classify it.
     return NS_OK;
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsUrlClassifierDBService::ClassifyLocal(nsIPrincipal* aPrincipal,
+                                        bool aTrackingProtectionEnabled,
+                                        nsresult* aResponse)
+{
+  MOZ_ASSERT(NS_IsMainThread(), "ClassifyLocal must be on main thread");
+  *aResponse = NS_OK;
+  nsAutoCString tables;
+  BuildTables(aTrackingProtectionEnabled, tables);
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = aPrincipal->GetURI(getter_AddRefs(uri));
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+  uri = NS_GetInnermostURI(uri);
+  NS_ENSURE_TRUE(uri, NS_ERROR_FAILURE);
+
+  nsAutoCString key;
+  // Canonicalize the url
+  nsCOMPtr<nsIUrlClassifierUtils> utilsService =
+    do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
+  rv = utilsService->GetKeyForURI(uri, key);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsAutoPtr<LookupResultArray> results(new LookupResultArray());
+  if (!results) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
+
+  // We don't use the proxy, since this is a blocking lookup. In unittests, we
+  // may not have been initalized, so don't crash.
+  rv = mWorker->DoLocalLookup(key, tables, mCryptoHashMain, results);
+  if (NS_SUCCEEDED(rv)) {
+    rv = ProcessLookupResults(results, mCheckMalware, mCheckPhishing,
+                              mCheckTracking);
+    *aResponse = rv;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsUrlClassifierDBService::Lookup(nsIPrincipal* aPrincipal,
                                  const nsACString& tables,
                                  nsIUrlClassifierCallback* c)
 {
   NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED);
 
   bool dummy;
   return LookupURI(aPrincipal, tables, c, true, &dummy);
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h
@@ -28,16 +28,17 @@
 
 // The hash length of a partial hash entry.
 #define PARTIAL_LENGTH 4
 
 // The hash length of a complete hash entry.
 #define COMPLETE_LENGTH 32
 
 class nsUrlClassifierDBServiceWorker;
+class nsICryptoHash;
 class nsIThread;
 class nsIURI;
 
 // This is a proxy class that just creates a background thread and delagates
 // calls to the background thread.
 class nsUrlClassifierDBService MOZ_FINAL : public nsIUrlClassifierDBService,
                                            public nsIURIClassifier,
                                            public nsIObserver
@@ -112,13 +113,17 @@ private:
   // The list of tables that can use the default hash completer object.
   nsTArray<nsCString> mGethashTables;
 
   // The list of tables that should never be hash completed.
   nsTArray<nsCString> mDisallowCompletionsTables;
 
   // Thread that we do the updates on.
   static nsIThread* gDbBackgroundThread;
+
+  // nsICryptoHash for doing hash operations on the main thread. This is only
+  // used for nsIURIClassifier.ClassifyLocal
+  nsCOMPtr<nsICryptoHash> mCryptoHashMain;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsUrlClassifierDBService, NS_URLCLASSIFIERDBSERVICE_CID)
 
 #endif // nsUrlClassifierDBService_h_
--- a/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp
@@ -139,16 +139,25 @@ UrlClassifierDBServiceWorkerProxy::Reset
 {
   nsCOMPtr<nsIRunnable> r =
     NS_NewRunnableMethod(mTarget,
                          &nsIUrlClassifierDBServiceWorker::ResetDatabase);
   return DispatchToWorkerThread(r);
 }
 
 NS_IMETHODIMP
+UrlClassifierDBServiceWorkerProxy::OpenDb()
+{
+  nsCOMPtr<nsIRunnable> r =
+    NS_NewRunnableMethod(mTarget,
+                         &nsIUrlClassifierDBServiceWorker::OpenDb);
+  return DispatchToWorkerThread(r);
+}
+
+NS_IMETHODIMP
 UrlClassifierDBServiceWorkerProxy::CloseDb()
 {
   nsCOMPtr<nsIRunnable> r =
     NS_NewRunnableMethod(mTarget,
                          &nsIUrlClassifierDBServiceWorker::CloseDb);
   return DispatchToWorkerThread(r);
 }