Merge mozilla-beta to b2g37. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Mon, 02 Mar 2015 20:24:06 -0500
changeset 237309 c89aad487aa51a317ffc2b4a8da02bf947c1d36f
parent 237288 1dc89d79d0389c647fad0a446dd6b8d84a997494 (current diff)
parent 237308 082769bdd62ab09cf8b0bc16d9d7768c050ab4eb (diff)
child 237310 944d70726a49ec26e6294132c662ffb752ac6855
push id252
push userryanvm@gmail.com
push dateTue, 03 Mar 2015 01:24:12 +0000
treeherdermozilla-b2g37_v2_2@c89aad487aa5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone37.0
Merge mozilla-beta to b2g37. a=merge
layout/base/FrameLayerBuilder.cpp
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
layout/base/nsDisplayListInvalidation.cpp
layout/base/nsDisplayListInvalidation.h
modules/libpref/init/all.js
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -35,16 +35,28 @@ loop.panel = (function(_, mozL10n) {
       };
     },
 
     shouldComponentUpdate: function(nextProps, nextState) {
       var tabChange = this.state.selectedTab !== nextState.selectedTab;
       if (tabChange) {
         this.props.mozLoop.notifyUITour("Loop:PanelTabChanged", nextState.selectedTab);
       }
+
+      if (!tabChange && nextProps.buttonsHidden) {
+        if (nextProps.buttonsHidden.length !== this.props.buttonsHidden.length) {
+          tabChange = true;
+        } else {
+          for (var i = 0, l = nextProps.buttonsHidden.length; i < l && !tabChange; ++i) {
+            if (this.props.buttonsHidden.indexOf(nextProps.buttonsHidden[i]) === -1) {
+              tabChange = true;
+            }
+          }
+        }
+      }
       return tabChange;
     },
 
     getInitialState: function() {
       // XXX Work around props.selectedTab being undefined initially.
       // When we don't need to rely on the pref, this can move back to
       // getDefaultProps (bug 1100258).
       return {
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -35,16 +35,28 @@ loop.panel = (function(_, mozL10n) {
       };
     },
 
     shouldComponentUpdate: function(nextProps, nextState) {
       var tabChange = this.state.selectedTab !== nextState.selectedTab;
       if (tabChange) {
         this.props.mozLoop.notifyUITour("Loop:PanelTabChanged", nextState.selectedTab);
       }
+
+      if (!tabChange && nextProps.buttonsHidden) {
+        if (nextProps.buttonsHidden.length !== this.props.buttonsHidden.length) {
+          tabChange = true;
+        } else {
+          for (var i = 0, l = nextProps.buttonsHidden.length; i < l && !tabChange; ++i) {
+            if (this.props.buttonsHidden.indexOf(nextProps.buttonsHidden[i]) === -1) {
+              tabChange = true;
+            }
+          }
+        }
+      }
       return tabChange;
     },
 
     getInitialState: function() {
       // XXX Work around props.selectedTab being undefined initially.
       // When we don't need to rely on the pref, this can move back to
       // getDefaultProps (bug 1100258).
       return {
--- a/browser/components/loop/test/mochitest/browser_toolbarbutton.js
+++ b/browser/components/loop/test/mochitest/browser_toolbarbutton.js
@@ -33,26 +33,34 @@ add_task(function* test_LoopUI_getters()
   Assert.ok(LoopUI.panel, "LoopUI panel element should be set");
   Assert.strictEqual(LoopUI.browser, null, "Browser element should not be there yet");
   Assert.strictEqual(LoopUI.selectedTab, null, "No tab should be selected yet");
 
   // Load and show the Loop panel for the very first time this session.
   yield loadLoopPanel();
   Assert.ok(LoopUI.browser, "Browser element should be there");
   Assert.strictEqual(LoopUI.selectedTab, "rooms", "Initially the rooms tab should be selected");
+  let panelTabs = LoopUI.browser.contentDocument.querySelectorAll(".tab-view > li");
+  Assert.strictEqual(panelTabs.length, 1, "Only one tab, 'rooms', should be visible");
 
   // Hide the panel.
   yield LoopUI.togglePanel();
   Assert.strictEqual(LoopUI.selectedTab, "rooms", "Rooms tab should still be selected");
 
   // Make sure the contacts tab shows up by simulating a login.
   MozLoopServiceInternal.fxAOAuthTokenData = fxASampleToken;
   MozLoopServiceInternal.fxAOAuthProfile = fxASampleProfile;
   yield MozLoopServiceInternal.notifyStatusChanged("login");
 
+  yield LoopUI.togglePanel();
+  Assert.strictEqual(LoopUI.selectedTab, "rooms", "Rooms tab should still be selected");
+  panelTabs = LoopUI.browser.contentDocument.querySelectorAll(".tab-view > li");
+  Assert.strictEqual(panelTabs.length, 2, "Two tabs should be visible");
+  yield LoopUI.togglePanel();
+
   // Programmatically select the contacts tab.
   yield LoopUI.togglePanel(null, "contacts");
   Assert.strictEqual(LoopUI.selectedTab, "contacts", "Contacts tab should be selected now");
 
   // Switch back to the rooms tab.
   yield LoopUI.openCallPanel(null, "rooms");
   Assert.strictEqual(LoopUI.selectedTab, "rooms", "Rooms tab should be selected now");
 
--- a/dom/canvas/test/_webgl-conformance.ini
+++ b/dom/canvas/test/_webgl-conformance.ini
@@ -765,16 +765,17 @@ skip-if = (os == 'android') || (os == 'b
 [webgl-conformance/_wrappers/test_conformance__textures__texture-active-bind-2.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-active-bind.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-complete.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-formats-test.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-mips.html]
 [webgl-conformance/_wrappers/test_conformance__textures__texture-npot-video.html]
 skip-if = os == 'win'
 [webgl-conformance/_wrappers/test_conformance__textures__texture-npot.html]
+skip-if = os == 'android'
 [webgl-conformance/_wrappers/test_conformance__textures__texture-size.html]
 skip-if = os == 'android'
 [webgl-conformance/_wrappers/test_conformance__textures__texture-size-cube-maps.html]
 skip-if = os == 'android'
 [webgl-conformance/_wrappers/test_conformance__textures__texture-transparent-pixels-initialized.html]
 [webgl-conformance/_wrappers/test_conformance__typedarrays__array-buffer-crash.html]
 [webgl-conformance/_wrappers/test_conformance__typedarrays__array-buffer-view-crash.html]
 [webgl-conformance/_wrappers/test_conformance__typedarrays__array-unit-tests.html]
--- a/dom/canvas/test/webgl-conformance/mochi-single.html
+++ b/dom/canvas/test/webgl-conformance/mochi-single.html
@@ -117,17 +117,16 @@ function GetExpectedTestFailSet() {
         failSet['conformance/glsl/functions/glsl-function-step-gentype.html'] = true;
         failSet['conformance/limits/gl-max-texture-dimensions.html'] = true;
         failSet['conformance/limits/gl-min-textures.html'] = true;
         failSet['conformance/rendering/draw-elements-out-of-bounds.html'] = true;
         failSet['conformance/state/gl-get-calls.html'] = true;
         failSet['conformance/textures/tex-image-with-format-and-type.html'] = true;
         failSet['conformance/textures/tex-sub-image-2d.html'] = true;
         failSet['conformance/textures/texture-mips.html'] = true;
-        failSet['conformance/textures/texture-npot.html'] = true;
         failSet['conformance/textures/texture-size-cube-maps.html'] = true;
       } else {
         // Android 2.3 slaves.
         failSet['conformance/extensions/oes-texture-float.html'] = true;
         failSet['conformance/glsl/functions/glsl-function-sin.html'] = true;
         failSet['conformance/misc/object-deletion-behaviour.html'] = true;
         failSet['conformance/textures/tex-image-and-sub-image-2d-with-video.html'] = true;
         failSet['conformance/textures/texture-mips.html'] = true;
--- a/dom/canvas/test/webgl-conformance/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conformance/mochitest-errata.ini
@@ -62,16 +62,19 @@ skip-if = os == 'android'
 # Crashes on Android.
 skip-if = os == 'android'
 [_wrappers/test_conformance__textures__texture-size.html]
 # Crashes on Android 4.0.
 skip-if = os == 'android'
 [_wrappers/test_conformance__textures__texture-size-cube-maps.html]
 # Crashes on Android 4.0.
 skip-if = os == 'android'
+[_wrappers/test_conformance__textures__texture-npot.html]
+# Intermittent fail on Android 4.0.
+skip-if = os == 'android'
 
 ########################################################################
 # B2G
 [_wrappers/test_conformance__context__context-attributes-alpha-depth-stencil-antialias.html]
 # Asserts on 'B2G ICS Emulator Debug'.
 skip-if = (os == 'b2g')
 [_wrappers/test_conformance__textures__tex-image-and-uniform-binding-bugs.html]
 # Intermittently asserts on 'B2G ICS Emulator Debug'.
--- a/dom/media/fmp4/SharedDecoderManager.cpp
+++ b/dom/media/fmp4/SharedDecoderManager.cpp
@@ -191,18 +191,20 @@ SharedDecoderProxy::Flush()
   return NS_OK;
 }
 
 nsresult
 SharedDecoderProxy::Drain()
 {
   if (mManager->mActiveProxy == this) {
     return mManager->mDecoder->Drain();
+  } else {
+    mCallback->DrainComplete();
+    return NS_OK;
   }
-  return NS_OK;
 }
 
 nsresult
 SharedDecoderProxy::Shutdown()
 {
   mManager->SetIdle(this);
   return NS_OK;
 }
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -1000,17 +1000,17 @@ TrackBuffer::RangeRemoval(int64_t aStart
   nsTArray<SourceBufferDecoder*> decoders;
   decoders.AppendElements(mInitializedDecoders);
 
   if (aStart <= bufferedStart && aEnd < bufferedEnd) {
     // Evict data from beginning.
     for (size_t i = 0; i < decoders.Length(); ++i) {
       nsRefPtr<dom::TimeRanges> buffered = new dom::TimeRanges();
       decoders[i]->GetBuffered(buffered);
-      if (buffered->GetEndTime() < aEnd) {
+      if (int64_t(buffered->GetEndTime() * USECS_PER_S) < aEnd) {
         // Can be fully removed.
         MSE_DEBUG("remove all bufferedEnd=%f time=%f, size=%lld",
                   buffered->GetEndTime(), time,
                   decoders[i]->GetResource()->GetSize());
         decoders[i]->GetResource()->EvictAll();
       } else {
         int64_t offset = decoders[i]->ConvertToByteOffset(aEnd);
         MSE_DEBUG("removing some bufferedEnd=%f offset=%lld size=%lld",
@@ -1019,17 +1019,17 @@ TrackBuffer::RangeRemoval(int64_t aStart
         if (offset > 0) {
           decoders[i]->GetResource()->EvictData(offset, offset);
         }
       }
     }
   } else {
     // Only trimming existing buffers.
     for (size_t i = 0; i < decoders.Length(); ++i) {
-      if (aStart <= buffered->GetStartTime()) {
+      if (aStart <= int64_t(buffered->GetStartTime() * USECS_PER_S)) {
         // It will be entirely emptied, can clear all data.
         decoders[i]->GetResource()->EvictAll();
       } else {
         decoders[i]->Trim(aStart);
       }
     }
   }
 
--- a/image/public/imgIContainer.idl
+++ b/image/public/imgIContainer.idl
@@ -111,17 +111,17 @@ native nsIntSizeByVal(nsIntSize);
 
 /**
  * imgIContainer is the interface that represents an image. It allows
  * access to frames as Thebes surfaces. It also allows drawing of images
  * onto Thebes contexts.
  *
  * Internally, imgIContainer also manages animation of images.
  */
-[scriptable, builtinclass, uuid(4e7d0b17-cb57-4d68-860f-59b303a86dbd)]
+[scriptable, builtinclass, uuid(9a43298b-bf49-44fc-9abe-9ff702f1bd25)]
 interface imgIContainer : nsISupports
 {
   /**
    * The width of the container rectangle.  In the case of any error,
    * zero is returned, and an exception will be thrown.
    */
   readonly attribute int32_t width;
 
@@ -420,24 +420,16 @@ interface imgIContainer : nsISupports
    *               (FLAG_DECODE_*) and FLAG_SYNC_DECODE (which will
    *               synchronously decode images that can be decoded "quickly",
    *               just like startDecoding() does) are accepted; others will be
    *               ignored.
    */
   [noscript] void requestDecodeForSize([const] in nsIntSize aSize,
                                        in uint32_t aFlags);
 
-  /*
-   * Returns true if no more decoding can be performed on this image. Images
-   * with errors return true since they cannot be decoded any further. Note that
-   * because decoded images may be discarded, isDecoded() may return false even
-   * if it has returned true in the past.
-   */
-  [noscript, notxpcom, nostdcall] bool isDecoded();
-
   /**
     * Increments the lock count on the image. An image will not be discarded
     * as long as the lock count is nonzero. Note that it is still possible for
     * the image to be undecoded if decode-on-draw is enabled and the image
     * was never drawn.
     *
     * Upon instantiation images have a lock count of zero.
     */
--- a/image/src/DynamicImage.cpp
+++ b/image/src/DynamicImage.cpp
@@ -255,22 +255,16 @@ DynamicImage::StartDecoding()
 }
 
 NS_IMETHODIMP
 DynamicImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
 {
   return NS_OK;
 }
 
-bool
-DynamicImage::IsDecoded()
-{
-  return true;
-}
-
 NS_IMETHODIMP
 DynamicImage::LockImage()
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DynamicImage::UnlockImage()
--- a/image/src/ImageWrapper.cpp
+++ b/image/src/ImageWrapper.cpp
@@ -225,22 +225,16 @@ ImageWrapper::StartDecoding()
 }
 
 NS_IMETHODIMP
 ImageWrapper::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
 {
   return mInnerImage->RequestDecodeForSize(aSize, aFlags);
 }
 
-bool
-ImageWrapper::IsDecoded()
-{
-  return mInnerImage->IsDecoded();
-}
-
 NS_IMETHODIMP
 ImageWrapper::LockImage()
 {
   MOZ_ASSERT(NS_IsMainThread(),
              "Main thread to encourage serialization with UnlockImage");
   return mInnerImage->LockImage();
 }
 
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1464,23 +1464,16 @@ RasterImage::RequestDecodeForSize(const 
 
   // Look up the first frame of the image, which will implicitly start decoding
   // if it's not available right now.
   LookupFrame(0, targetSize, flags);
 
   return NS_OK;
 }
 
-bool
-RasterImage::IsDecoded()
-{
-  // XXX(seth): We need to get rid of this; it's not reliable.
-  return mHasBeenDecoded || mError || (mDecodeOnDraw && mHasSourceData);
-}
-
 NS_IMETHODIMP
 RasterImage::Decode(const Maybe<nsIntSize>& aSize, uint32_t aFlags)
 {
   MOZ_ASSERT(!aSize || NS_IsMainThread());
 
   if (mError) {
     return NS_ERROR_FAILURE;
   }
--- a/image/src/VectorImage.cpp
+++ b/image/src/VectorImage.cpp
@@ -926,22 +926,16 @@ NS_IMETHODIMP
 VectorImage::RequestDecodeForSize(const nsIntSize& aSize, uint32_t aFlags)
 {
   // Nothing to do for SVG images, though in theory we could rasterize to the
   // provided size ahead of time if we supported off-main-thread SVG
   // rasterization...
   return NS_OK;
 }
 
-bool
-VectorImage::IsDecoded()
-{
-  return mIsFullyLoaded || mError;
-}
-
 //******************************************************************************
 /* void lockImage() */
 NS_IMETHODIMP
 VectorImage::LockImage()
 {
   // This method is for image-discarding, which only applies to RasterImages.
   return NS_OK;
 }
--- a/js/src/asmjs/AsmJSSignalHandlers.cpp
+++ b/js/src/asmjs/AsmJSSignalHandlers.cpp
@@ -937,17 +937,17 @@ RedirectIonBackedgesToInterruptCheck(JSR
         // thus not in a JIT iloop. We assume that the interrupt flag will be
         // checked at least once before entering JIT code (if not, no big deal;
         // the browser will just request another interrupt in a second).
         if (!jitRuntime->mutatingBackedgeList())
             jitRuntime->patchIonBackedges(rt, jit::JitRuntime::BackedgeInterruptCheck);
     }
 }
 
-static void
+static bool
 RedirectJitCodeToInterruptCheck(JSRuntime *rt, CONTEXT *context)
 {
     RedirectIonBackedgesToInterruptCheck(rt);
 
     if (AsmJSActivation *activation = rt->mainThread.asmJSActivationStack()) {
         const AsmJSModule &module = activation->module();
 
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
@@ -955,18 +955,21 @@ RedirectJitCodeToInterruptCheck(JSRuntim
             rt->mainThread.simulator()->set_resume_pc(int32_t(module.interruptExit()));
 #endif
 
         uint8_t **ppc = ContextToPC(context);
         uint8_t *pc = *ppc;
         if (module.containsFunctionPC(pc)) {
             activation->setResumePC(pc);
             *ppc = module.interruptExit();
+            return true;
         }
     }
+
+    return false;
 }
 
 #if !defined(XP_WIN)
 // For the interrupt signal, pick a signal number that:
 //  - is not otherwise used by mozilla or standard libraries
 //  - defaults to nostop and noprint on gdb/lldb so that noone is bothered
 // SIGVTALRM a relative of SIGALRM, so intended for user code, but, unlike
 // SIGALRM, not used anywhere else in Mozilla.
@@ -1094,18 +1097,18 @@ js::InterruptRunningJitCode(JSRuntime *r
     // its context from this thread. SuspendThread can sporadically fail if the
     // thread is in the middle of a syscall. Rather than retrying in a loop,
     // just wait for the next request for interrupt.
     HANDLE thread = (HANDLE)rt->ownerThreadNative();
     if (SuspendThread(thread) != -1) {
         CONTEXT context;
         context.ContextFlags = CONTEXT_CONTROL;
         if (GetThreadContext(thread, &context)) {
-            RedirectJitCodeToInterruptCheck(rt, &context);
-            SetThreadContext(thread, &context);
+            if (RedirectJitCodeToInterruptCheck(rt, &context))
+                SetThreadContext(thread, &context);
         }
         ResumeThread(thread);
     }
 #else
     // On Unix, we instead deliver an async signal to the main thread which
     // halts the thread and callers our JitInterruptHandler (which has already
     // been installed by EnsureSignalHandlersInstalled).
     pthread_t thread = (pthread_t)rt->ownerThreadNative();
--- a/js/src/builtin/SymbolObject.cpp
+++ b/js/src/builtin/SymbolObject.cpp
@@ -24,17 +24,17 @@ const Class SymbolObject::class_ = {
     nullptr, /* getProperty */
     nullptr, /* setProperty */
     nullptr, /* enumerate */
     nullptr, /* resolve */
     convert
 };
 
 SymbolObject *
-SymbolObject::create(JSContext *cx, JS::Symbol *symbol)
+SymbolObject::create(JSContext *cx, JS::HandleSymbol symbol)
 {
     JSObject *obj = NewBuiltinClassInstance(cx, &class_);
     if (!obj)
         return nullptr;
     SymbolObject &symobj = obj->as<SymbolObject>();
     symobj.setPrimitiveValue(symbol);
     return &symobj;
 }
--- a/js/src/builtin/SymbolObject.h
+++ b/js/src/builtin/SymbolObject.h
@@ -23,17 +23,17 @@ class SymbolObject : public NativeObject
     static const Class class_;
 
     static JSObject *initClass(JSContext *cx, js::HandleObject obj);
 
     /*
      * Creates a new Symbol object boxing the given primitive Symbol.  The
      * object's [[Prototype]] is determined from context.
      */
-    static SymbolObject *create(JSContext *cx, JS::Symbol *symbol);
+    static SymbolObject *create(JSContext *cx, JS::HandleSymbol symbol);
 
     JS::Symbol *unbox() const {
         return getFixedSlot(PRIMITIVE_VALUE_SLOT).toSymbol();
     }
 
   private:
     inline void setPrimitiveValue(JS::Symbol *symbol) {
         setFixedSlot(PRIMITIVE_VALUE_SLOT, SymbolValue(symbol));
--- a/js/src/irregexp/RegExpEngine.cpp
+++ b/js/src/irregexp/RegExpEngine.cpp
@@ -1607,18 +1607,18 @@ RegExpCompiler::Assemble(JSContext *cx,
     while (!work_list_.empty())
         work_list_.popCopy()->Emit(this, &new_trace);
 
     RegExpCode code = macro_assembler_->GenerateCode(cx, match_only_);
     if (code.empty())
         return RegExpCode();
 
     if (reg_exp_too_big_) {
+        code.destroy();
         JS_ReportError(cx, "regexp too big");
-        code.destroy();
         return RegExpCode();
     }
 
     return code;
 }
 
 template <typename CharT>
 static void
--- a/js/src/jit/ExecutableAllocator.h
+++ b/js/src/jit/ExecutableAllocator.h
@@ -28,16 +28,17 @@
 
 #include <limits>
 #include <stddef.h> // for ptrdiff_t
 
 #include "jsalloc.h"
 
 #include "jit/arm/Simulator-arm.h"
 #include "jit/mips/Simulator-mips.h"
+#include "js/GCAPI.h"
 #include "js/HashTable.h"
 #include "js/Vector.h"
 
 #ifdef JS_CPU_SPARC
 #ifdef __linux__  // bugzilla 502369
 static void sync_instruction_memory(caddr_t v, u_int len)
 {
     caddr_t end = v + len;
@@ -241,18 +242,21 @@ public:
         // (found, or created if necessary) a pool that had enough space.
         void *result = (*poolp)->alloc(n, type);
         MOZ_ASSERT(result);
         return result;
     }
 
     void releasePoolPages(ExecutablePool *pool) {
         MOZ_ASSERT(pool->m_allocation.pages);
-        if (destroyCallback)
+        if (destroyCallback) {
+            // Do not allow GC during the page release callback.
+            JS::AutoSuppressGCAnalysis nogc;
             destroyCallback(pool->m_allocation.pages, pool->m_allocation.size);
+        }
         systemRelease(pool->m_allocation);
         MOZ_ASSERT(m_pools.initialized());
         m_pools.remove(m_pools.lookup(pool));   // this asserts if |pool| is not in m_pools
     }
 
     void addSizeOfCode(JS::CodeSizes *sizes) const;
 
     void setDestroyCallback(DestroyCallback destroyCallback) {
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -2980,16 +2980,19 @@ MClampToUint8::foldsTo(TempAllocator &al
         }
     }
     return this;
 }
 
 bool
 MCompare::tryFoldEqualOperands(bool *result)
 {
+    // Temporarily disabled due to bug 1130679.
+    return false;
+
     if (lhs() != rhs())
         return false;
 
     // Intuitively somebody would think that if lhs == rhs,
     // then we can just return true. (Or false for !==)
     // However NaN !== NaN is true! So we spend some time trying
     // to eliminate this case.
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3586,17 +3586,18 @@ js::PrimitiveToObject(JSContext *cx, con
         Rooted<JSString*> str(cx, v.toString());
         return StringObject::create(cx, str);
     }
     if (v.isNumber())
         return NumberObject::create(cx, v.toNumber());
     if (v.isBoolean())
         return BooleanObject::create(cx, v.toBoolean());
     MOZ_ASSERT(v.isSymbol());
-    return SymbolObject::create(cx, v.toSymbol());
+    RootedSymbol symbol(cx, v.toSymbol());
+    return SymbolObject::create(cx, symbol);
 }
 
 /*
  * Invokes the ES5 ToObject algorithm on vp, returning the result. If vp might
  * already be an object, use ToObject. reportCantConvert controls how null and
  * undefined errors are reported.
  *
  * Callers must handle the already-object case.
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -4405,19 +4405,18 @@ FrameLayerBuilder::PaintItems(nsTArray<C
                               gfxContext *aContext,
                               nsRenderingContext *aRC,
                               nsDisplayListBuilder* aBuilder,
                               nsPresContext* aPresContext,
                               const nsIntPoint& aOffset,
                               float aXScale, float aYScale,
                               int32_t aCommonClipCount)
 {
-#ifdef MOZ_DUMP_PAINTING
   DrawTarget& aDrawTarget = *aRC->GetDrawTarget();
-#endif
+
   int32_t appUnitsPerDevPixel = aPresContext->AppUnitsPerDevPixel();
   nsRect boundRect = aRect.ToAppUnits(appUnitsPerDevPixel);
   boundRect.MoveBy(NSIntPixelsToAppUnits(aOffset.x, appUnitsPerDevPixel),
                  NSIntPixelsToAppUnits(aOffset.y, appUnitsPerDevPixel));
   boundRect.ScaleInverseRoundOut(aXScale, aYScale);
 
   DisplayItemClip currentClip;
   bool currentClipIsSetInContext = false;
@@ -4457,17 +4456,19 @@ FrameLayerBuilder::PaintItems(nsTArray<C
         NS_ASSERTION(aCommonClipCount < 100,
           "Maybe you really do have more than a hundred clipping rounded rects, or maybe something has gone wrong.");
         currentClip.ApplyTo(aContext, aPresContext, aCommonClipCount);
         aContext->NewPath();
       }
     }
 
     if (cdi->mInactiveLayerManager) {
+      bool saved = aDrawTarget.GetPermitSubpixelAA();
       PaintInactiveLayer(aBuilder, cdi->mInactiveLayerManager, cdi->mItem, aContext, aRC);
+      aDrawTarget.SetPermitSubpixelAA(saved);
     } else {
       nsIFrame* frame = cdi->mItem->Frame();
       frame->AddStateBits(NS_FRAME_PAINTED_THEBES);
 #ifdef MOZ_DUMP_PAINTING
       if (gfxUtils::sDumpPainting) {
         DebugPaintItem(aDrawTarget, aPresContext, cdi->mItem, aBuilder);
       } else {
 #else
--- a/layout/base/nsCSSRendering.cpp
+++ b/layout/base/nsCSSRendering.cpp
@@ -57,17 +57,17 @@
 #include "gfxColor.h"
 #include "gfxGradientCache.h"
 #include "GraphicsFilter.h"
 #include <algorithm>
 
 using namespace mozilla;
 using namespace mozilla::css;
 using namespace mozilla::gfx;
-using mozilla::image::ImageOps;
+using namespace mozilla::image;
 using mozilla::CSSSizeOrRatio;
 
 static int gFrameTreeLockCount = 0;
 
 // To avoid storing this data on nsInlineFrame (bloat) and to avoid
 // recalculating this for each frame in a continuation (perf), hold
 // a cache of various coordinate information that we need in order
 // to paint inline backgrounds.
@@ -1613,17 +1613,17 @@ nsCSSRendering::PaintBoxShadowInner(nsPr
     shadowContext->Fill();
     shadowContext->NewPath();
 
     blurringArea.DoPaint();
     renderContext->Restore();
   }
 }
 
-void
+DrawResult
 nsCSSRendering::PaintBackground(nsPresContext* aPresContext,
                                 nsRenderingContext& aRenderingContext,
                                 nsIFrame* aForFrame,
                                 const nsRect& aDirtyRect,
                                 const nsRect& aBorderArea,
                                 uint32_t aFlags,
                                 nsRect* aBGClipRect,
                                 int32_t aLayer)
@@ -1637,31 +1637,31 @@ nsCSSRendering::PaintBackground(nsPresCo
   nsStyleContext *sc;
   if (!FindBackground(aForFrame, &sc)) {
     // We don't want to bail out if moz-appearance is set on a root
     // node. If it has a parent content node, bail because it's not
     // a root, otherwise keep going in order to let the theme stuff
     // draw the background. The canvas really should be drawing the
     // bg, but there's no way to hook that up via css.
     if (!aForFrame->StyleDisplay()->mAppearance) {
-      return;
+      return DrawResult::SUCCESS;
     }
 
     nsIContent* content = aForFrame->GetContent();
     if (!content || content->GetParent()) {
-      return;
+      return DrawResult::SUCCESS;
     }
 
     sc = aForFrame->StyleContext();
   }
 
-  PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
-                        aDirtyRect, aBorderArea, sc,
-                        *aForFrame->StyleBorder(), aFlags,
-                        aBGClipRect, aLayer);
+  return PaintBackgroundWithSC(aPresContext, aRenderingContext, aForFrame,
+                               aDirtyRect, aBorderArea, sc,
+                               *aForFrame->StyleBorder(), aFlags,
+                               aBGClipRect, aLayer);
 }
 
 static bool
 IsOpaqueBorderEdge(const nsStyleBorder& aBorder, mozilla::css::Side aSide)
 {
   if (aBorder.GetComputedBorder().Side(aSide) == 0)
     return true;
   switch (aBorder.GetBorderStyle(aSide)) {
@@ -2812,47 +2812,52 @@ nsCSSRendering::PaintGradient(nsPresCont
         ctx->SetPattern(gradientPattern);
       }
       ctx->Fill();
       ctx->SetMatrix(ctm);
     }
   }
 }
 
-void
+DrawResult
 nsCSSRendering::PaintBackgroundWithSC(nsPresContext* aPresContext,
                                       nsRenderingContext& aRenderingContext,
                                       nsIFrame* aForFrame,
                                       const nsRect& aDirtyRect,
                                       const nsRect& aBorderArea,
                                       nsStyleContext* aBackgroundSC,
                                       const nsStyleBorder& aBorder,
                                       uint32_t aFlags,
                                       nsRect* aBGClipRect,
                                       int32_t aLayer)
 {
   NS_PRECONDITION(aForFrame,
                   "Frame is expected to be provided to PaintBackground");
 
+  // Initialize our result to success. We update it only if its value is
+  // currently DrawResult::SUCCESS, which means that as soon as we hit our first
+  // non-successful draw, we stop updating and will return that value.
+  DrawResult result = DrawResult::SUCCESS;
+
   // Check to see if we have an appearance defined.  If so, we let the theme
   // renderer draw the background and bail out.
   // XXXzw this ignores aBGClipRect.
   const nsStyleDisplay* displayData = aForFrame->StyleDisplay();
   if (displayData->mAppearance) {
     nsITheme *theme = aPresContext->GetTheme();
     if (theme && theme->ThemeSupportsWidget(aPresContext, aForFrame,
                                             displayData->mAppearance)) {
       nsRect drawing(aBorderArea);
       theme->GetWidgetOverflow(aPresContext->DeviceContext(),
                                aForFrame, displayData->mAppearance, &drawing);
       drawing.IntersectRect(drawing, aDirtyRect);
       theme->DrawWidgetBackground(&aRenderingContext, aForFrame,
                                   displayData->mAppearance, aBorderArea,
                                   drawing);
-      return;
+      return DrawResult::SUCCESS;
     }
   }
 
   // For canvas frames (in the CSS sense) we draw the background color using
   // a solid color item that gets added in nsLayoutUtils::PaintFrame,
   // or nsSubDocumentFrame::BuildDisplayList (bug 488242). (The solid
   // color may be moved into nsDisplayCanvasBackground by
   // nsPresShell::AddCanvasBackgroundColorItem, and painted by
@@ -2877,17 +2882,17 @@ nsCSSRendering::PaintBackgroundWithSC(ns
   if (drawBackgroundColor && aLayer >= 0) {
     drawBackgroundColor = false;
   }
 
   // At this point, drawBackgroundImage and drawBackgroundColor are
   // true if and only if we are actually supposed to paint an image or
   // color into aDirtyRect, respectively.
   if (!drawBackgroundImage && !drawBackgroundColor)
-    return;
+    return DrawResult::SUCCESS;
 
   // Compute the outermost boundary of the area that might be painted.
   // Same coordinate space as aBorderArea & aBGClipRect.
   Sides skipSides = aForFrame->GetSkipSides();
   nsRect paintBorderArea =
     ::BoxDecorationRectForBackground(aForFrame, aBorderArea, skipSides, &aBorder);
   nsRect clipBorderArea =
     ::BoxDecorationRectForBorder(aForFrame, aBorderArea, skipSides, &aBorder);
@@ -2924,23 +2929,23 @@ nsCSSRendering::PaintBackgroundWithSC(ns
 
   // If there is no background image, draw a color.  (If there is
   // neither a background image nor a color, we wouldn't have gotten
   // this far.)
   if (!drawBackgroundImage) {
     if (!isCanvasFrame) {
       DrawBackgroundColor(clipState, ctx, appUnitsPerPixel);
     }
-    return;
+    return DrawResult::SUCCESS;
   }
 
   if (bg->mImageCount < 1) {
     // Return if there are no background layers, all work from this point
     // onwards happens iteratively on these.
-    return;
+    return DrawResult::SUCCESS;
   }
 
   // Validate the layer range before we start iterating.
   int32_t startLayer = aLayer;
   int32_t nLayers = 1;
   if (startLayer < 0) {
     startLayer = (int32_t)bg->mImageCount - 1;
     nLayers = bg->mImageCount;
@@ -2999,27 +3004,36 @@ nsCSSRendering::PaintBackgroundWithSC(ns
         nsBackgroundLayerState state = PrepareBackgroundLayer(aPresContext, aForFrame,
             aFlags, paintBorderArea, clipState.mBGClipArea, layer);
         if (!state.mFillArea.IsEmpty()) {
           if (state.mCompositingOp != gfxContext::OPERATOR_OVER) {
             NS_ASSERTION(ctx->CurrentOperator() == gfxContext::OPERATOR_OVER,
                          "It is assumed the initial operator is OPERATOR_OVER, when it is restored later");
             ctx->SetOperator(state.mCompositingOp);
           }
-          state.mImageRenderer.DrawBackground(aPresContext, aRenderingContext,
-                                              state.mDestArea, state.mFillArea,
-                                              state.mAnchor + paintBorderArea.TopLeft(),
-                                              clipState.mDirtyRect);
+
+          DrawResult resultForLayer =
+            state.mImageRenderer.DrawBackground(aPresContext, aRenderingContext,
+                                                state.mDestArea, state.mFillArea,
+                                                state.mAnchor + paintBorderArea.TopLeft(),
+                                                clipState.mDirtyRect);
+
+          if (result == DrawResult::SUCCESS) {
+            result = resultForLayer;
+          }
+
           if (state.mCompositingOp != gfxContext::OPERATOR_OVER) {
             ctx->SetOperator(gfxContext::OPERATOR_OVER);
           }
         }
       }
     }
   }
+
+  return result;
 }
 
 static inline bool
 IsTransformed(nsIFrame* aForFrame, nsIFrame* aTopFrame)
 {
   for (nsIFrame* f = aForFrame; f != aTopFrame; f = f->GetParent()) {
     if (f->IsTransformed()) {
       return true;
@@ -3328,44 +3342,16 @@ nsCSSRendering::GetBackgroundLayerRect(n
   nsRect borderArea =
     ::BoxDecorationRectForBackground(aForFrame, aBorderArea, skipSides);
   nsBackgroundLayerState state =
       PrepareBackgroundLayer(aPresContext, aForFrame, aFlags, borderArea,
                              aClipRect, aLayer);
   return state.mFillArea;
 }
 
-/* static */ bool
-nsCSSRendering::IsBackgroundImageDecodedForStyleContextAndLayer(
-  const nsStyleBackground *aBackground, uint32_t aLayer)
-{
-  const nsStyleImage* image = &aBackground->mLayers[aLayer].mImage;
-  if (image->GetType() == eStyleImageType_Image) {
-    nsCOMPtr<imgIContainer> img;
-    if (NS_SUCCEEDED(image->GetImageData()->GetImage(getter_AddRefs(img)))) {
-      if (!img->IsDecoded()) {
-        return false;
-      }
-    }
-  }
-  return true;
-}
-
-/* static */ bool
-nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(nsIFrame* aFrame)
-{
-  const nsStyleBackground *bg = aFrame->StyleContext()->StyleBackground();
-  NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
-    if (!IsBackgroundImageDecodedForStyleContextAndLayer(bg, i)) {
-      return false;
-    }
-  }
-  return true;
-}
-
 static void
 DrawBorderImage(nsPresContext*       aPresContext,
                 nsRenderingContext&  aRenderingContext,
                 nsIFrame*            aForFrame,
                 const nsRect&        aBorderArea,
                 const nsStyleBorder& aStyleBorder,
                 const nsRect&        aDirtyRect,
                 Sides                aSkipSides)
@@ -4904,62 +4890,62 @@ ConvertImageRendererToDrawFlags(uint32_t
     drawFlags |= imgIContainer::FLAG_SYNC_DECODE;
   }
   if (aImageRendererFlags & nsImageRenderer::FLAG_PAINTING_TO_WINDOW) {
     drawFlags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING;
   }
   return drawFlags;
 }
 
-void
+DrawResult
 nsImageRenderer::Draw(nsPresContext*       aPresContext,
                       nsRenderingContext&  aRenderingContext,
                       const nsRect&        aDirtyRect,
                       const nsRect&        aDest,
                       const nsRect&        aFill,
                       const nsPoint&       aAnchor,
                       const CSSIntRect&    aSrc)
 {
   if (!mIsReady) {
     NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
-    return;
+    return DrawResult::TEMPORARY_ERROR;
   }
   if (aDest.IsEmpty() || aFill.IsEmpty() ||
       mSize.width <= 0 || mSize.height <= 0) {
-    return;
+    return DrawResult::SUCCESS;
   }
 
   GraphicsFilter filter = nsLayoutUtils::GetGraphicsFilterForFrame(mForFrame);
 
   switch (mType) {
     case eStyleImageType_Image:
     {
       nsIntSize imageSize(nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
                           nsPresContext::AppUnitsToIntCSSPixels(mSize.height));
-      nsLayoutUtils::DrawBackgroundImage(*aRenderingContext.ThebesContext(),
-                                         aPresContext,
-                                         mImageContainer, imageSize, filter,
-                                         aDest, aFill, aAnchor, aDirtyRect,
-                                         ConvertImageRendererToDrawFlags(mFlags));
-      return;
+      return
+        nsLayoutUtils::DrawBackgroundImage(*aRenderingContext.ThebesContext(),
+                                           aPresContext,
+                                           mImageContainer, imageSize, filter,
+                                           aDest, aFill, aAnchor, aDirtyRect,
+                                           ConvertImageRendererToDrawFlags(mFlags));
     }
     case eStyleImageType_Gradient:
     {
       nsCSSRendering::PaintGradient(aPresContext, aRenderingContext,
                                     mGradientData, aDirtyRect,
                                     aDest, aFill, aSrc, mSize);
-      return;
+      return DrawResult::SUCCESS;
     }
     case eStyleImageType_Element:
     {
       nsRefPtr<gfxDrawable> drawable = DrawableForElement(aDest,
                                                           aRenderingContext);
       if (!drawable) {
         NS_WARNING("Could not create drawable for element");
-        return;
+        return DrawResult::TEMPORARY_ERROR;
       }
 
       gfxContext* ctx = aRenderingContext.ThebesContext();
       gfxContext::GraphicsOperator op = ctx->CurrentOperator();
       if (op != gfxContext::OPERATOR_OVER) {
         ctx->PushGroup(gfxContentType::COLOR_ALPHA);
         ctx->SetOperator(gfxContext::OPERATOR_OVER);
       }
@@ -4970,21 +4956,21 @@ nsImageRenderer::Draw(nsPresContext*    
                                filter, aDest, aFill, aAnchor, aDirtyRect,
                                ConvertImageRendererToDrawFlags(mFlags));
 
       if (op != gfxContext::OPERATOR_OVER) {
         ctx->PopGroupToSource();
         ctx->Paint();
       }
 
-      return;
+      return DrawResult::SUCCESS;
     }
     case eStyleImageType_Null:
     default:
-      return;
+      return DrawResult::SUCCESS;
   }
 }
 
 already_AddRefed<gfxDrawable>
 nsImageRenderer::DrawableForElement(const nsRect& aImageRect,
                                     nsRenderingContext&  aRenderingContext)
 {
   NS_ASSERTION(mType == eStyleImageType_Element,
@@ -5007,38 +4993,38 @@ nsImageRenderer::DrawableForElement(cons
   }
   NS_ASSERTION(mImageElementSurface.mSourceSurface, "Surface should be ready.");
   nsRefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(
                                 mImageElementSurface.mSourceSurface,
                                 mImageElementSurface.mSize);
   return drawable.forget();
 }
 
-void
+DrawResult
 nsImageRenderer::DrawBackground(nsPresContext*       aPresContext,
                                 nsRenderingContext&  aRenderingContext,
                                 const nsRect&        aDest,
                                 const nsRect&        aFill,
                                 const nsPoint&       aAnchor,
                                 const nsRect&        aDirty)
 {
   if (!mIsReady) {
     NS_NOTREACHED("Ensure PrepareImage() has returned true before calling me");
-    return;
+    return DrawResult::TEMPORARY_ERROR;
   }
   if (aDest.IsEmpty() || aFill.IsEmpty() ||
       mSize.width <= 0 || mSize.height <= 0) {
-    return;
+    return DrawResult::SUCCESS;
   }
 
-  Draw(aPresContext, aRenderingContext,
-       aDirty, aDest, aFill, aAnchor,
-       CSSIntRect(0, 0,
-                  nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
-                  nsPresContext::AppUnitsToIntCSSPixels(mSize.height)));
+  return Draw(aPresContext, aRenderingContext,
+              aDirty, aDest, aFill, aAnchor,
+              CSSIntRect(0, 0,
+                         nsPresContext::AppUnitsToIntCSSPixels(mSize.width),
+                         nsPresContext::AppUnitsToIntCSSPixels(mSize.height)));
 }
 
 /**
  * Compute the size and position of the master copy of the image. I.e., a single
  * tile used to fill the dest rect.
  * aFill The destination rect to be filled
  * aHFill and aVFill are the repeat patterns for the component -
  * NS_STYLE_BORDER_IMAGE_REPEAT_* - i.e., how a tiling unit is used to fill aFill
--- a/layout/base/nsCSSRendering.h
+++ b/layout/base/nsCSSRendering.h
@@ -5,16 +5,17 @@
 
 /* utility functions for drawing borders and backgrounds */
 
 #ifndef nsCSSRendering_h___
 #define nsCSSRendering_h___
 
 #include "gfxBlur.h"
 #include "gfxContext.h"
+#include "imgIContainer.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/gfx/Rect.h"
 #include "nsLayoutUtils.h"
 #include "nsStyleStruct.h"
 #include "nsIFrame.h"
 
 class gfxDrawable;
 class nsStyleContext;
@@ -104,16 +105,17 @@ struct CSSSizeOrRatio
  * nsStyleImage image, which may internally be a real image, a sub image, or a
  * CSS gradient.
  *
  * @note Always call the member functions in the order of PrepareImage(),
  * SetSize(), and Draw*().
  */
 class nsImageRenderer {
 public:
+  typedef mozilla::image::DrawResult DrawResult;
   typedef mozilla::layers::LayerManager LayerManager;
   typedef mozilla::layers::ImageContainer ImageContainer;
 
   enum {
     FLAG_SYNC_DECODE_IMAGES = 0x01,
     FLAG_PAINTING_TO_WINDOW = 0x02
   };
   enum FitType
@@ -197,22 +199,22 @@ public:
   void SetPreferredSize(const mozilla::CSSSizeOrRatio& aIntrinsicSize,
                         const nsSize& aDefaultSize);
 
   /**
    * Draws the image to the target rendering context using background-specific
    * arguments.
    * @see nsLayoutUtils::DrawImage() for parameters.
    */
-  void DrawBackground(nsPresContext*       aPresContext,
-                      nsRenderingContext&  aRenderingContext,
-                      const nsRect&        aDest,
-                      const nsRect&        aFill,
-                      const nsPoint&       aAnchor,
-                      const nsRect&        aDirty);
+  DrawResult DrawBackground(nsPresContext*       aPresContext,
+                            nsRenderingContext&  aRenderingContext,
+                            const nsRect&        aDest,
+                            const nsRect&        aFill,
+                            const nsPoint&       aAnchor,
+                            const nsRect&        aDirty);
 
   /**
    * Draw the image to a single component of a border-image style rendering.
    * aFill The destination rect to be drawn into
    * aSrc is the part of the image to be rendered into a tile (aUnitSize in
    * aFill), if aSrc and the dest tile are different sizes, the image will be
    * scaled to map aSrc onto the dest tile.
    * aHFill and aVFill are the repeat patterns for the component -
@@ -242,23 +244,23 @@ public:
 private:
   /**
    * Draws the image to the target rendering context.
    * aSrc is a rect on the source image which will be mapped to aDest; it's
    * currently only used for gradients.
    *
    * @see nsLayoutUtils::DrawImage() for other parameters.
    */
-  void Draw(nsPresContext*       aPresContext,
-            nsRenderingContext&  aRenderingContext,
-            const nsRect&        aDirtyRect,
-            const nsRect&        aDest,
-            const nsRect&        aFill,
-            const nsPoint&       aAnchor,
-            const mozilla::CSSIntRect& aSrc);
+  DrawResult Draw(nsPresContext*       aPresContext,
+                  nsRenderingContext&  aRenderingContext,
+                  const nsRect&        aDirtyRect,
+                  const nsRect&        aDest,
+                  const nsRect&        aFill,
+                  const nsPoint&       aAnchor,
+                  const mozilla::CSSIntRect& aSrc);
 
   /**
    * Helper method for creating a gfxDrawable from mPaintServerFrame or 
    * mImageElementSurface.
    * Requires mType is eStyleImageType_Element.
    * Returns null if we cannot create the drawable.
    */
   already_AddRefed<gfxDrawable> DrawableForElement(const nsRect& aImageRect,
@@ -316,16 +318,17 @@ struct nsBackgroundLayerState {
   gfxContext::GraphicsOperator mCompositingOp;
 };
 
 struct nsCSSRendering {
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Float Float;
   typedef mozilla::gfx::Rect Rect;
   typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
+  typedef mozilla::image::DrawResult DrawResult;
   typedef nsIFrame::Sides Sides;
 
   /**
    * Initialize any static variables used by nsCSSRendering.
    */
   static void Init();
   
   /**
@@ -549,70 +552,58 @@ struct nsCSSRendering {
      */
     PAINTBG_SYNC_DECODE_IMAGES = 0x02,
     /**
      * When this flag is passed, painting will go to the screen so we can
      * take advantage of the fact that it will be clipped to the viewport.
      */
     PAINTBG_TO_WINDOW = 0x04
   };
-  static void PaintBackground(nsPresContext* aPresContext,
-                              nsRenderingContext& aRenderingContext,
-                              nsIFrame* aForFrame,
-                              const nsRect& aDirtyRect,
-                              const nsRect& aBorderArea,
-                              uint32_t aFlags,
-                              nsRect* aBGClipRect = nullptr,
-                              int32_t aLayer = -1);
+  static DrawResult PaintBackground(nsPresContext* aPresContext,
+                                    nsRenderingContext& aRenderingContext,
+                                    nsIFrame* aForFrame,
+                                    const nsRect& aDirtyRect,
+                                    const nsRect& aBorderArea,
+                                    uint32_t aFlags,
+                                    nsRect* aBGClipRect = nullptr,
+                                    int32_t aLayer = -1);
 
   /**
    * Same as |PaintBackground|, except using the provided style structs.
    * This short-circuits the code that ensures that the root element's
    * background is drawn on the canvas.
    * The aLayer parameter allows you to paint a single layer of the background.
    * The default value for aLayer, -1, means that all layers will be painted.
    * The background color will only be painted if the back-most layer is also
    * being painted.
    */
-  static void PaintBackgroundWithSC(nsPresContext* aPresContext,
-                                    nsRenderingContext& aRenderingContext,
-                                    nsIFrame* aForFrame,
-                                    const nsRect& aDirtyRect,
-                                    const nsRect& aBorderArea,
-                                    nsStyleContext *aStyleContext,
-                                    const nsStyleBorder& aBorder,
-                                    uint32_t aFlags,
-                                    nsRect* aBGClipRect = nullptr,
-                                    int32_t aLayer = -1);
+  static DrawResult PaintBackgroundWithSC(nsPresContext* aPresContext,
+                                          nsRenderingContext& aRenderingContext,
+                                          nsIFrame* aForFrame,
+                                          const nsRect& aDirtyRect,
+                                          const nsRect& aBorderArea,
+                                          nsStyleContext *aStyleContext,
+                                          const nsStyleBorder& aBorder,
+                                          uint32_t aFlags,
+                                          nsRect* aBGClipRect = nullptr,
+                                          int32_t aLayer = -1);
 
   /**
    * Returns the rectangle covered by the given background layer image, taking
    * into account background positioning, sizing, and repetition, but not
    * clipping.
    */
   static nsRect GetBackgroundLayerRect(nsPresContext* aPresContext,
                                        nsIFrame* aForFrame,
                                        const nsRect& aBorderArea,
                                        const nsRect& aClipRect,
                                        const nsStyleBackground::Layer& aLayer,
                                        uint32_t aFlags);
 
   /**
-   * Checks if image in layer aLayer of aBackground is currently decoded.
-   */
-  static bool IsBackgroundImageDecodedForStyleContextAndLayer(
-    const nsStyleBackground *aBackground, uint32_t aLayer);
-
-  /**
-   * Checks if all images that are part of the background for aFrame are
-   * currently decoded.
-   */
-  static bool AreAllBackgroundImagesDecodedForFrame(nsIFrame* aFrame);
-
-  /**
    * Called when we start creating a display list. The frame tree will not
    * change until a matching EndFrameTreeLocked is called.
    */
   static void BeginFrameTreesLocked();
   /**
    * Called when we've finished using a display list. When all
    * BeginFrameTreeLocked calls have been balanced by an EndFrameTreeLocked,
    * the frame tree may start changing again.
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -71,16 +71,17 @@
 // GetTickCount().
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::dom;
+using namespace mozilla::image;
 using namespace mozilla::layout;
 using namespace mozilla::gfx;
 
 typedef FrameMetrics::ViewID ViewID;
 
 #ifdef DEBUG
 static bool
 SpammyLayoutWarningsEnabled()
@@ -1975,30 +1976,16 @@ nsDisplayItem::nsDisplayItem(nsDisplayLi
   NS_ASSERTION(aBuilder->GetDirtyRect().width >= 0 ||
                !aBuilder->IsForPainting(), "dirty rect not set");
   // The dirty rect is for mCurrentFrame, so we have to use
   // mCurrentOffsetToReferenceFrame
   mVisibleRect = aBuilder->GetDirtyRect() +
       aBuilder->GetCurrentFrameOffsetToReferenceFrame();
 }
 
-void
-nsDisplayItem::AddInvalidRegionForSyncDecodeBackgroundImages(
-  nsDisplayListBuilder* aBuilder,
-  const nsDisplayItemGeometry* aGeometry,
-  nsRegion* aInvalidRegion)
-{
-  if (aBuilder->ShouldSyncDecodeImages()) {
-    if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(mFrame)) {
-      bool snap;
-      aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
-    }
-  }
-}
-
 /* static */ bool
 nsDisplayItem::ForceActiveLayers()
 {
   static bool sForce = false;
   static bool sForceCached = false;
 
   if (!sForceCached) {
     Preferences::AddBoolVarCache(&sForce, "layers.force-active", false);
@@ -2511,16 +2498,21 @@ nsDisplayBackgroundImage::BuildLayer(nsD
 
 void
 nsDisplayBackgroundImage::ConfigureLayer(ImageLayer* aLayer, const nsIntPoint& aOffset)
 {
   aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame));
 
   mozilla::gfx::IntSize imageSize = mImageContainer->GetCurrentSize();
   NS_ASSERTION(imageSize.width != 0 && imageSize.height != 0, "Invalid image size!");
+  if (imageSize.width > 0 && imageSize.height > 0) {
+    // We're actually using the ImageContainer. Let our frame know that it
+    // should consider itself to have painted successfully.
+    nsDisplayBackgroundGeometry::UpdateDrawResult(this, DrawResult::SUCCESS);
+  }
 
   gfxPoint p = mDestRect.TopLeft() + aOffset;
   Matrix transform = Matrix::Translation(p.x, p.y);
   transform.PreScale(mDestRect.width / imageSize.width,
                      mDestRect.height / imageSize.height);
   aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform));
 }
 
@@ -2680,21 +2672,23 @@ nsDisplayBackgroundImage::Paint(nsDispla
 void
 nsDisplayBackgroundImage::PaintInternal(nsDisplayListBuilder* aBuilder,
                                         nsRenderingContext* aCtx, const nsRect& aBounds,
                                         nsRect* aClipRect) {
   nsPoint offset = ToReferenceFrame();
   uint32_t flags = aBuilder->GetBackgroundPaintFlags();
   CheckForBorderItem(this, flags);
 
-  nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame,
-                                  aBounds,
-                                  nsRect(offset, mFrame->GetSize()),
-                                  flags, aClipRect, mLayer);
-
+  DrawResult result =
+    nsCSSRendering::PaintBackground(mFrame->PresContext(), *aCtx, mFrame,
+                                    aBounds,
+                                    nsRect(offset, mFrame->GetSize()),
+                                    flags, aClipRect, mLayer);
+
+  nsDisplayBackgroundGeometry::UpdateDrawResult(this, result);
 }
 
 void nsDisplayBackgroundImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                          const nsDisplayItemGeometry* aGeometry,
                                                          nsRegion* aInvalidRegion)
 {
   if (!mBackgroundStyle) {
     return;
@@ -2713,18 +2707,19 @@ void nsDisplayBackgroundImage::ComputeIn
     aInvalidRegion->Or(bounds, geometry->mBounds);
 
     if (positioningArea.Size() != geometry->mPositioningArea.Size()) {
       NotifyRenderingChanged();
     }
     return;
   }
   if (aBuilder->ShouldSyncDecodeImages()) {
-    if (mBackgroundStyle &&
-        !nsCSSRendering::IsBackgroundImageDecodedForStyleContextAndLayer(mBackgroundStyle, mLayer)) {
+    const nsStyleImage& image = mBackgroundStyle->mLayers[mLayer].mImage;
+    if (image.GetType() == eStyleImageType_Image &&
+        geometry->ShouldInvalidateToSyncDecodeImages()) {
       aInvalidRegion->Or(*aInvalidRegion, bounds);
 
       NotifyRenderingChanged();
     }
   }
   if (!bounds.IsEqualInterior(geometry->mBounds)) {
     // Positioning area is unchanged, so invalidate just the change in the
     // painting area.
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1143,26 +1143,16 @@ public:
         aInvalidRegion->Or(aGeometry->mBounds, bounds);
       } else {
         aInvalidRegion->Xor(aGeometry->mBounds, bounds);
       }
     }
   }
 
   /**
-   * For display items types that just draw a background we use this function
-   * to do any invalidation that might be needed if we are asked to sync decode
-   * images.
-   */
-  void AddInvalidRegionForSyncDecodeBackgroundImages(
-    nsDisplayListBuilder* aBuilder,
-    const nsDisplayItemGeometry* aGeometry,
-    nsRegion* aInvalidRegion);
-
-  /**
    * Called when the area rendered by this display item has changed (been
    * invalidated or changed geometry) since the last paint. This includes
    * when the display item was not rendered at all in the last paint.
    * It does NOT get called when a display item was being rendered and no
    * longer is, because generally that means there is no display item to
    * call this method on.
    */
   virtual void NotifyRenderingChanged() {}
--- a/layout/base/nsDisplayListInvalidation.cpp
+++ b/layout/base/nsDisplayListInvalidation.cpp
@@ -19,16 +19,22 @@ nsDisplayItemGeometry::~nsDisplayItemGeo
   MOZ_COUNT_DTOR(nsDisplayItemGeometry);
 }
 
 nsDisplayItemGenericGeometry::nsDisplayItemGenericGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
   : nsDisplayItemGeometry(aItem, aBuilder)
   , mBorderRect(aItem->GetBorderRect())
 {}
 
+bool
+ShouldSyncDecodeImages(nsDisplayListBuilder* aBuilder)
+{
+  return aBuilder->ShouldSyncDecodeImages();
+}
+
 void
 nsDisplayItemGenericGeometry::MoveBy(const nsPoint& aOffset)
 {
   nsDisplayItemGeometry::MoveBy(aOffset);
   mBorderRect.MoveBy(aOffset);
 }
 
 nsDisplayItemBoundsGeometry::nsDisplayItemBoundsGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
@@ -48,16 +54,17 @@ nsDisplayBorderGeometry::MoveBy(const ns
 {
   nsDisplayItemGeometry::MoveBy(aOffset);
   mContentRect.MoveBy(aOffset);
 }
 
 nsDisplayBackgroundGeometry::nsDisplayBackgroundGeometry(nsDisplayBackgroundImage* aItem,
                                                          nsDisplayListBuilder* aBuilder)
   : nsDisplayItemGeometry(aItem, aBuilder)
+  , nsImageGeometryMixin(aItem, aBuilder)
   , mPositioningArea(aItem->GetPositioningArea())
 {}
 
 void
 nsDisplayBackgroundGeometry::MoveBy(const nsPoint& aOffset)
 {
   nsDisplayItemGeometry::MoveBy(aOffset);
   mPositioningArea.MoveBy(aOffset);
--- a/layout/base/nsDisplayListInvalidation.h
+++ b/layout/base/nsDisplayListInvalidation.h
@@ -69,70 +69,101 @@ class nsDisplayItemGenericGeometry : pub
 public:
   nsDisplayItemGenericGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder);
 
   virtual void MoveBy(const nsPoint& aOffset) MOZ_OVERRIDE;
 
   nsRect mBorderRect;
 };
 
+bool ShouldSyncDecodeImages(nsDisplayListBuilder* aBuilder);
+
 /**
  * nsImageGeometryMixin is a mixin for geometry items that draw images. Geometry
  * items that include this mixin can track drawing results and use that
  * information to inform invalidation decisions.
  *
  * This mixin uses CRTP; its template parameter should be the type of the class
  * that is inheriting from it. See nsDisplayItemGenericImageGeometry for an
  * example.
  */
 template <typename T>
 class nsImageGeometryMixin
 {
 public:
-  explicit nsImageGeometryMixin(nsDisplayItem* aItem)
+  nsImageGeometryMixin(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
     : mLastDrawResult(mozilla::image::DrawResult::NOT_READY)
+    , mWaitingForPaint(false)
   {
+    // Transfer state from the previous version of this geometry item.
     auto lastGeometry =
       static_cast<T*>(mozilla::FrameLayerBuilder::GetMostRecentGeometry(aItem));
     if (lastGeometry) {
-      mLastDrawResult = lastGeometry->LastDrawResult();
+      mLastDrawResult = lastGeometry->mLastDrawResult;
+      mWaitingForPaint = lastGeometry->mWaitingForPaint;
+    }
+
+    // If our display item is going to invalidate to trigger sync decoding of
+    // images, mark ourselves as waiting for a paint. If we actually get
+    // painted, UpdateDrawResult will get called, and we'll clear the flag.
+    if (ShouldSyncDecodeImages(aBuilder) &&
+        ShouldInvalidateToSyncDecodeImages()) {
+      mWaitingForPaint = true;
     }
   }
 
   static void UpdateDrawResult(nsDisplayItem* aItem,
                                mozilla::image::DrawResult aResult)
   {
     auto lastGeometry =
       static_cast<T*>(mozilla::FrameLayerBuilder::GetMostRecentGeometry(aItem));
     if (lastGeometry) {
       lastGeometry->mLastDrawResult = aResult;
+      lastGeometry->mWaitingForPaint = false;
     }
   }
 
-  mozilla::image::DrawResult LastDrawResult() const { return mLastDrawResult; }
+  bool ShouldInvalidateToSyncDecodeImages() const
+  {
+    if (mWaitingForPaint) {
+      // We previously invalidated for sync decoding and haven't gotten painted
+      // since them. This suggests that our display item is completely occluded
+      // and there's no point in invalidating again - and because the reftest
+      // harness takes a new snapshot every time we invalidate, doing so might
+      // lead to an invalidation loop if we're in a reftest.
+      return false;
+    }
+
+    if (mLastDrawResult == mozilla::image::DrawResult::SUCCESS) {
+      return false;
+    }
+
+    return true;
+  }
 
 private:
   mozilla::image::DrawResult mLastDrawResult;
+  bool mWaitingForPaint;
 };
 
 /**
  * nsDisplayItemGenericImageGeometry is a generic geometry item class that
  * includes nsImageGeometryMixin.
  *
  * This should be sufficient for most display items that draw images.
  */
 class nsDisplayItemGenericImageGeometry
   : public nsDisplayItemGenericGeometry
   , public nsImageGeometryMixin<nsDisplayItemGenericImageGeometry>
 {
 public:
   nsDisplayItemGenericImageGeometry(nsDisplayItem* aItem,
                                     nsDisplayListBuilder* aBuilder)
     : nsDisplayItemGenericGeometry(aItem, aBuilder)
-    , nsImageGeometryMixin(aItem)
+    , nsImageGeometryMixin(aItem, aBuilder)
   { }
 };
 
 class nsDisplayItemBoundsGeometry : public nsDisplayItemGeometry
 {
 public:
   nsDisplayItemBoundsGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder);
 
@@ -144,17 +175,19 @@ class nsDisplayBorderGeometry : public n
 public:
   nsDisplayBorderGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder);
 
   virtual void MoveBy(const nsPoint& aOffset) MOZ_OVERRIDE;
 
   nsRect mContentRect;
 };
 
-class nsDisplayBackgroundGeometry : public nsDisplayItemGeometry
+class nsDisplayBackgroundGeometry
+  : public nsDisplayItemGeometry
+  , public nsImageGeometryMixin<nsDisplayBackgroundGeometry>
 {
 public:
   nsDisplayBackgroundGeometry(nsDisplayBackgroundImage* aItem, nsDisplayListBuilder* aBuilder);
 
   virtual void MoveBy(const nsPoint& aOffset) MOZ_OVERRIDE;
 
   nsRect mPositioningArea;
 };
--- a/layout/forms/nsButtonFrameRenderer.cpp
+++ b/layout/forms/nsButtonFrameRenderer.cpp
@@ -14,16 +14,18 @@
 #include "nsFrame.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/Element.h"
 
 #define ACTIVE   "active"
 #define HOVER    "hover"
 #define FOCUS    "focus"
 
+using namespace mozilla::image;
+
 nsButtonFrameRenderer::nsButtonFrameRenderer()
 {
   MOZ_COUNT_CTOR(nsButtonFrameRenderer);
 }
 
 nsButtonFrameRenderer::~nsButtonFrameRenderer()
 {
   MOZ_COUNT_DTOR(nsButtonFrameRenderer);
@@ -125,16 +127,17 @@ public:
                        HitTestState* aState,
                        nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE {
     aOutFrames->AppendElement(mFrame);
   }
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) MOZ_OVERRIDE;
+  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion *aInvalidRegion) MOZ_OVERRIDE;
   NS_DISPLAY_DECL_NAME("ButtonBorderBackground", TYPE_BUTTON_BORDER_BACKGROUND)
 private:
   nsButtonFrameRenderer* mBFR;
 };
 
@@ -160,36 +163,52 @@ public:
 
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
   NS_DISPLAY_DECL_NAME("ButtonForeground", TYPE_BUTTON_FOREGROUND)
 private:
   nsButtonFrameRenderer* mBFR;
 };
 
+nsDisplayItemGeometry*
+nsDisplayButtonBorderBackground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
+{
+  return new nsDisplayItemGenericImageGeometry(this, aBuilder);
+}
+
 void
 nsDisplayButtonBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                            const nsDisplayItemGeometry* aGeometry,
                                                            nsRegion *aInvalidRegion)
 {
-  AddInvalidRegionForSyncDecodeBackgroundImages(aBuilder, aGeometry, aInvalidRegion);
+  auto geometry =
+    static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
+
+  if (aBuilder->ShouldSyncDecodeImages() &&
+      geometry->ShouldInvalidateToSyncDecodeImages()) {
+    bool snap;
+    aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
+  }
 
   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 }
 
 void nsDisplayButtonBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
                                             nsRenderingContext* aCtx)
 {
   NS_ASSERTION(mFrame, "No frame?");
   nsPresContext* pc = mFrame->PresContext();
   nsRect r = nsRect(ToReferenceFrame(), mFrame->GetSize());
   
   // draw the border and background inside the focus and outline borders
-  mBFR->PaintBorderAndBackground(pc, *aCtx, mVisibleRect, r,
-                                 aBuilder->GetBackgroundPaintFlags());
+  DrawResult result =
+    mBFR->PaintBorderAndBackground(pc, *aCtx, mVisibleRect, r,
+                                   aBuilder->GetBackgroundPaintFlags());
+
+  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
 }
 
 void nsDisplayButtonForeground::Paint(nsDisplayListBuilder* aBuilder,
                                       nsRenderingContext* aCtx)
 {
   nsPresContext *presContext = mFrame->PresContext();
   const nsStyleDisplay *disp = mFrame->StyleDisplay();
   if (!mFrame->IsThemed(disp) ||
@@ -255,36 +274,39 @@ nsButtonFrameRenderer::PaintOutlineAndFo
     GetButtonInnerFocusRect(aRect, rect);
 
     nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
                                 aDirtyRect, rect, mInnerFocusStyle);
   }
 }
 
 
-void
+DrawResult
 nsButtonFrameRenderer::PaintBorderAndBackground(nsPresContext* aPresContext,
           nsRenderingContext& aRenderingContext,
           const nsRect& aDirtyRect,
           const nsRect& aRect,
           uint32_t aBGFlags)
 
 {
   // get the button rect this is inside the focus and outline rects
   nsRect buttonRect;
   GetButtonRect(aRect, buttonRect);
 
   nsStyleContext* context = mFrame->StyleContext();
 
-  nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, mFrame,
-                                  aDirtyRect, buttonRect, aBGFlags);
+  DrawResult result =
+    nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, mFrame,
+                                    aDirtyRect, buttonRect, aBGFlags);
   nsCSSRendering::PaintBoxShadowInner(aPresContext, aRenderingContext,
                                       mFrame, buttonRect, aDirtyRect);
   nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, mFrame,
                               aDirtyRect, buttonRect, context);
+
+  return result;
 }
 
 
 void
 nsButtonFrameRenderer::GetButtonOuterFocusRect(const nsRect& aRect, nsRect& focusRect)
 {
   focusRect = aRect;
 }
--- a/layout/forms/nsButtonFrameRenderer.h
+++ b/layout/forms/nsButtonFrameRenderer.h
@@ -1,16 +1,17 @@
 /* -*- 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/. */
 
 #ifndef nsButtonFrameRenderer_h___
 #define nsButtonFrameRenderer_h___
 
+#include "imgIContainer.h"
 #include "nsAutoPtr.h"
 #include "nsMargin.h"
 
 class nsIFrame;
 class nsFrame;
 class nsDisplayList;
 class nsDisplayListBuilder;
 class nsPresContext;
@@ -19,16 +20,18 @@ struct nsRect;
 class nsStyleContext;
 
 
 #define NS_BUTTON_RENDERER_FOCUS_INNER_CONTEXT_INDEX  0
 #define NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX  1
 #define NS_BUTTON_RENDERER_LAST_CONTEXT_INDEX   NS_BUTTON_RENDERER_FOCUS_OUTER_CONTEXT_INDEX
 
 class nsButtonFrameRenderer {
+  typedef mozilla::image::DrawResult DrawResult;
+
 public:
 
   nsButtonFrameRenderer();
   ~nsButtonFrameRenderer();
 
   /**
    * Create display list items for the button
    */
@@ -36,21 +39,21 @@ public:
                          nsDisplayList* aBackground, nsDisplayList* aForeground);
 
 
   void PaintOutlineAndFocusBorders(nsPresContext* aPresContext,
                                    nsRenderingContext& aRenderingContext,
                                    const nsRect& aDirtyRect,
                                    const nsRect& aRect);
 
-  void PaintBorderAndBackground(nsPresContext* aPresContext,
-                                nsRenderingContext& aRenderingContext,
-                                const nsRect& aDirtyRect,
-                                const nsRect& aRect,
-                                uint32_t aBGFlags);
+  DrawResult PaintBorderAndBackground(nsPresContext* aPresContext,
+                                      nsRenderingContext& aRenderingContext,
+                                      const nsRect& aDirtyRect,
+                                      const nsRect& aRect,
+                                      uint32_t aBGFlags);
 
   void SetFrame(nsFrame* aFrame, nsPresContext* aPresContext);
  
   void SetDisabled(bool aDisabled, bool notify);
 
   bool isActive();
   bool isDisabled();
 
--- a/layout/forms/nsFieldSetFrame.cpp
+++ b/layout/forms/nsFieldSetFrame.cpp
@@ -19,16 +19,17 @@
 #include "nsDisplayList.h"
 #include "nsRenderingContext.h"
 #include "nsIScrollableFrame.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Maybe.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
+using namespace mozilla::image;
 using namespace mozilla::layout;
 
 nsContainerFrame*
 NS_NewFieldSetFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsFieldSetFrame(aContext);
 }
 
@@ -97,16 +98,17 @@ public:
   }
 #endif
 
   virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                        HitTestState* aState,
                        nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE;
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
+  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion *aInvalidRegion) MOZ_OVERRIDE;
   NS_DISPLAY_DECL_NAME("FieldSetBorderBackground", TYPE_FIELDSET_BORDER_BACKGROUND)
 };
 
 void nsDisplayFieldSetBorderBackground::HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
                                                 HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames)
@@ -116,27 +118,42 @@ void nsDisplayFieldSetBorderBackground::
   // It's not clear whether this is correct.
   aOutFrames->AppendElement(mFrame);
 }
 
 void
 nsDisplayFieldSetBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
                                          nsRenderingContext* aCtx)
 {
-  static_cast<nsFieldSetFrame*>(mFrame)->
+  DrawResult result = static_cast<nsFieldSetFrame*>(mFrame)->
     PaintBorderBackground(*aCtx, ToReferenceFrame(),
                           mVisibleRect, aBuilder->GetBackgroundPaintFlags());
+
+  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
+}
+
+nsDisplayItemGeometry*
+nsDisplayFieldSetBorderBackground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
+{
+  return new nsDisplayItemGenericImageGeometry(this, aBuilder);
 }
 
 void
 nsDisplayFieldSetBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                              const nsDisplayItemGeometry* aGeometry,
                                                              nsRegion *aInvalidRegion)
 {
-  AddInvalidRegionForSyncDecodeBackgroundImages(aBuilder, aGeometry, aInvalidRegion);
+  auto geometry =
+    static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
+
+  if (aBuilder->ShouldSyncDecodeImages() &&
+      geometry->ShouldInvalidateToSyncDecodeImages()) {
+    bool snap;
+    aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
+  }
 
   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 }
 
 void
 nsFieldSetFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                   const nsRect&           aDirtyRect,
                                   const nsDisplayListSet& aLists) {
@@ -183,31 +200,32 @@ nsFieldSetFrame::BuildDisplayList(nsDisp
   }
   // Put the inner frame's display items on the master list. Note that this
   // moves its border/background display items to our BorderBackground() list,
   // which isn't really correct, but it's OK because the inner frame is
   // anonymous and can't have its own border and background.
   contentDisplayItems.MoveTo(aLists);
 }
 
-void
+DrawResult
 nsFieldSetFrame::PaintBorderBackground(nsRenderingContext& aRenderingContext,
     nsPoint aPt, const nsRect& aDirtyRect, uint32_t aBGFlags)
 {
   // if the border is smaller than the legend. Move the border down
   // to be centered on the legend. 
   // FIXME: This means border-radius clamping is incorrect; we should
   // override nsIFrame::GetBorderRadii.
   nsRect rect = VisualBorderRectRelativeToSelf();
   nscoord yoff = rect.y;
   rect += aPt;
   nsPresContext* presContext = PresContext();
 
-  nsCSSRendering::PaintBackground(presContext, aRenderingContext, this,
-                                  aDirtyRect, rect, aBGFlags);
+  DrawResult result =
+    nsCSSRendering::PaintBackground(presContext, aRenderingContext, this,
+                                    aDirtyRect, rect, aBGFlags);
 
   nsCSSRendering::PaintBoxShadowInner(presContext, aRenderingContext,
                                       this, rect, aDirtyRect);
 
    if (nsIFrame* legend = GetLegend()) {
      nscoord topBorder = StyleBorder()->GetComputedBorderWidth(NS_SIDE_TOP);
 
     // Use the rect of the legend frame, not mLegendRect, so we draw our
@@ -258,16 +276,18 @@ nsFieldSetFrame::PaintBorderBackground(n
     gfx->Restore();
   } else {
 
     nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
                                 aDirtyRect,
                                 nsRect(aPt, mRect.Size()),
                                 mStyleContext);
   }
+
+  return result;
 }
 
 nscoord
 nsFieldSetFrame::GetIntrinsicISize(nsRenderingContext* aRenderingContext,
                                    nsLayoutUtils::IntrinsicISizeType aType)
 {
   nscoord legendWidth = 0;
   nscoord contentWidth = 0;
--- a/layout/forms/nsFieldSetFrame.h
+++ b/layout/forms/nsFieldSetFrame.h
@@ -2,20 +2,23 @@
 /* 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/. */
 
 #ifndef nsFieldSetFrame_h___
 #define nsFieldSetFrame_h___
 
 #include "mozilla/Attributes.h"
+#include "imgIContainer.h"
 #include "nsContainerFrame.h"
 
 class nsFieldSetFrame MOZ_FINAL : public nsContainerFrame
 {
+  typedef mozilla::image::DrawResult DrawResult;
+
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   explicit nsFieldSetFrame(nsStyleContext* aContext);
 
   nscoord
     GetIntrinsicISize(nsRenderingContext* aRenderingContext,
                       nsLayoutUtils::IntrinsicISizeType);
@@ -42,18 +45,19 @@ public:
                       nsHTMLReflowMetrics&     aDesiredSize,
                       const nsHTMLReflowState& aReflowState,
                       nsReflowStatus&          aStatus) MOZ_OVERRIDE;
                                
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
 
-  void PaintBorderBackground(nsRenderingContext& aRenderingContext,
-    nsPoint aPt, const nsRect& aDirtyRect, uint32_t aBGFlags);
+  DrawResult PaintBorderBackground(nsRenderingContext& aRenderingContext,
+                                   nsPoint aPt, const nsRect& aDirtyRect,
+                                   uint32_t aBGFlags);
 
 #ifdef DEBUG
   virtual void SetInitialChildList(ChildListID    aListID,
                                    nsFrameList&   aChildList) MOZ_OVERRIDE;
   virtual void AppendFrames(ChildListID    aListID,
                             nsFrameList&   aFrameList) MOZ_OVERRIDE;
   virtual void InsertFrames(ChildListID    aListID,
                             nsIFrame*      aPrevFrame,
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -172,17 +172,17 @@ nsBulletFrame::DidSetStyleContext(nsStyl
 
 class nsDisplayBulletGeometry
   : public nsDisplayItemGenericGeometry
   , public nsImageGeometryMixin<nsDisplayBulletGeometry>
 {
 public:
   nsDisplayBulletGeometry(nsDisplayItem* aItem, nsDisplayListBuilder* aBuilder)
     : nsDisplayItemGenericGeometry(aItem, aBuilder)
-    , nsImageGeometryMixin(aItem)
+    , nsImageGeometryMixin(aItem, aBuilder)
   {
     nsBulletFrame* f = static_cast<nsBulletFrame*>(aItem->Frame());
     mOrdinal = f->GetOrdinal();
   }
 
   int32_t mOrdinal;
 };
 
@@ -234,17 +234,17 @@ public:
     if (f->GetOrdinal() != geometry->mOrdinal) {
       bool snap;
       aInvalidRegion->Or(geometry->mBounds, GetBounds(aBuilder, &snap));
       return;
     }
 
     nsCOMPtr<imgIContainer> image = f->GetImage();
     if (aBuilder->ShouldSyncDecodeImages() && image &&
-        geometry->LastDrawResult() != DrawResult::SUCCESS) {
+        geometry->ShouldInvalidateToSyncDecodeImages()) {
       bool snap;
       aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
     }
 
     return nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
   }
 };
 
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1377,17 +1377,17 @@ void
 nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                           const nsDisplayItemGeometry* aGeometry,
                                           nsRegion* aInvalidRegion)
 {
   auto geometry =
     static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
 
   if (aBuilder->ShouldSyncDecodeImages() &&
-      geometry->LastDrawResult() != DrawResult::SUCCESS) {
+      geometry->ShouldInvalidateToSyncDecodeImages()) {
     bool snap;
     aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
   }
 
   nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 }
 
 already_AddRefed<ImageContainer>
--- a/layout/mathml/nsMathMLChar.cpp
+++ b/layout/mathml/nsMathMLChar.cpp
@@ -36,16 +36,17 @@
 #include "nsMathMLOperators.h"
 #include <algorithm>
 
 #include "gfxMathTable.h"
 #include "nsUnicodeScriptCodes.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
+using namespace mozilla::image;
 
 //#define NOISY_SEARCH 1
 
 // BUG 848725 Drawing failure with stretchy horizontal parenthesis when no fonts
 // are installed. "kMaxScaleFactor" is required to limit the scale for the
 // vertical and horizontal stretchy operators.
 static const float kMaxScaleFactor = 20.0;
 static const float kLargeOpFactor = float(M_SQRT2);
@@ -1880,46 +1881,64 @@ public:
     MOZ_COUNT_CTOR(nsDisplayMathMLCharBackground);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayMathMLCharBackground() {
     MOZ_COUNT_DTOR(nsDisplayMathMLCharBackground);
   }
 #endif
 
+  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion *aInvalidRegion) MOZ_OVERRIDE;
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
   NS_DISPLAY_DECL_NAME("MathMLCharBackground", TYPE_MATHML_CHAR_BACKGROUND)
 private:
   nsStyleContext* mStyleContext;
   nsRect          mRect;
 };
 
+nsDisplayItemGeometry*
+nsDisplayMathMLCharBackground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
+{
+  return new nsDisplayItemGenericImageGeometry(this, aBuilder);
+}
+
 void
 nsDisplayMathMLCharBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                          const nsDisplayItemGeometry* aGeometry,
                                                          nsRegion *aInvalidRegion)
 {
-  AddInvalidRegionForSyncDecodeBackgroundImages(aBuilder, aGeometry, aInvalidRegion);
+  auto geometry =
+    static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
+
+  if (aBuilder->ShouldSyncDecodeImages() &&
+      geometry->ShouldInvalidateToSyncDecodeImages()) {
+    bool snap;
+    aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
+  }
 
   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 }
 
 void nsDisplayMathMLCharBackground::Paint(nsDisplayListBuilder* aBuilder,
                                           nsRenderingContext* aCtx)
 {
   const nsStyleBorder* border = mStyleContext->StyleBorder();
   nsRect rect(mRect + ToReferenceFrame());
-  nsCSSRendering::PaintBackgroundWithSC(mFrame->PresContext(), *aCtx, mFrame,
-                                        mVisibleRect, rect,
-                                        mStyleContext, *border,
-                                        aBuilder->GetBackgroundPaintFlags());
+
+  DrawResult result =
+    nsCSSRendering::PaintBackgroundWithSC(mFrame->PresContext(), *aCtx, mFrame,
+                                          mVisibleRect, rect,
+                                          mStyleContext, *border,
+                                          aBuilder->GetBackgroundPaintFlags());
+
+  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
 }
 
 class nsDisplayMathMLCharForeground : public nsDisplayItem {
 public:
   nsDisplayMathMLCharForeground(nsDisplayListBuilder* aBuilder,
                                 nsIFrame* aFrame, nsMathMLChar* aChar,
 				                uint32_t aIndex, bool aIsSelected)
     : nsDisplayItem(aBuilder, aFrame), mChar(aChar), 
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -35,16 +35,17 @@
 #include <algorithm>
 
 //TABLECELL SELECTION
 #include "nsFrameSelection.h"
 #include "mozilla/LookAndFeel.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
+using namespace mozilla::image;
 
 nsTableCellFrame::nsTableCellFrame(nsStyleContext* aContext) :
   nsContainerFrame(aContext)
   , mDesiredSize(GetWritingMode())
 {
   mColIndex = 0;
   mPriorAvailWidth = 0;
 
@@ -358,37 +359,38 @@ nsTableCellFrame::DecorateForSelection(n
         StrokeLineWithSnapping(nsPoint(mRect.width - (2*onePixel), 2*onePixel),
                                nsPoint(mRect.width - (2*onePixel), mRect.height-onePixel),
                                appUnitsPerDevPixel, *drawTarget, color);
       }
     }
   }
 }
 
-void
+DrawResult
 nsTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext,
                                   const nsRect&        aDirtyRect,
                                   nsPoint              aPt,
                                   uint32_t             aFlags)
 {
   nsRect rect(aPt, GetSize());
-  nsCSSRendering::PaintBackground(PresContext(), aRenderingContext, this,
-                                  aDirtyRect, rect, aFlags);
+  return nsCSSRendering::PaintBackground(PresContext(), aRenderingContext, this,
+                                         aDirtyRect, rect, aFlags);
 }
 
 // Called by nsTablePainter
-void
+DrawResult
 nsTableCellFrame::PaintCellBackground(nsRenderingContext& aRenderingContext,
                                       const nsRect& aDirtyRect, nsPoint aPt,
                                       uint32_t aFlags)
 {
-  if (!StyleVisibility()->IsVisible())
-    return;
+  if (!StyleVisibility()->IsVisible()) {
+    return DrawResult::SUCCESS;
+  }
 
-  PaintBackground(aRenderingContext, aDirtyRect, aPt, aFlags);
+  return PaintBackground(aRenderingContext, aDirtyRect, aPt, aFlags);
 }
 
 nsresult
 nsTableCellFrame::ProcessBorders(nsTableFrame* aFrame,
                                  nsDisplayListBuilder* aBuilder,
                                  const nsDisplayListSet& aLists)
 {
   const nsStyleBorder* borderStyle = StyleBorder();
@@ -421,50 +423,61 @@ public:
                        HitTestState* aState,
                        nsTArray<nsIFrame*> *aOutFrames) MOZ_OVERRIDE {
     aOutFrames->AppendElement(mFrame);
   }
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
   virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder,
                            bool* aSnap) MOZ_OVERRIDE;
+  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion *aInvalidRegion) MOZ_OVERRIDE;
 
   NS_DISPLAY_DECL_NAME("TableCellBackground", TYPE_TABLE_CELL_BACKGROUND)
 };
 
 void nsDisplayTableCellBackground::Paint(nsDisplayListBuilder* aBuilder,
                                          nsRenderingContext* aCtx)
 {
-  static_cast<nsTableCellFrame*>(mFrame)->
+  DrawResult result = static_cast<nsTableCellFrame*>(mFrame)->
     PaintBackground(*aCtx, mVisibleRect, ToReferenceFrame(),
                     aBuilder->GetBackgroundPaintFlags());
+
+  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
 }
 
 nsRect
 nsDisplayTableCellBackground::GetBounds(nsDisplayListBuilder* aBuilder,
                                         bool* aSnap)
 {
   // revert from nsDisplayTableItem's implementation ... cell backgrounds
   // don't overflow the cell
   return nsDisplayItem::GetBounds(aBuilder, aSnap);
 }
 
+nsDisplayItemGeometry*
+nsDisplayTableCellBackground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
+{
+  return new nsDisplayItemGenericImageGeometry(this, aBuilder);
+}
+
 void
 nsDisplayTableCellBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                         const nsDisplayItemGeometry* aGeometry,
                                                         nsRegion *aInvalidRegion)
 {
-  if (aBuilder->ShouldSyncDecodeImages()) {
-    if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(mFrame)) {
-      bool snap;
-      aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
-    }
+  auto geometry =
+    static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
+
+  if (aBuilder->ShouldSyncDecodeImages() &&
+      geometry->ShouldInvalidateToSyncDecodeImages()) {
+    bool snap;
+    aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
   }
 
   nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 }
 
 void nsTableCellFrame::InvalidateFrame(uint32_t aDisplayItemKey)
 {
   nsIFrame::InvalidateFrame(aDisplayItemKey);
@@ -1214,17 +1227,17 @@ nsBCTableCellFrame::GetBorderOverflow()
   halfBorder.top = BC_BORDER_TOP_HALF_COORD(p2t, mTopBorder);
   halfBorder.right = BC_BORDER_RIGHT_HALF_COORD(p2t, mRightBorder);
   halfBorder.bottom = BC_BORDER_BOTTOM_HALF_COORD(p2t, mBottomBorder);
   halfBorder.left = BC_BORDER_LEFT_HALF_COORD(p2t, mLeftBorder);
   return halfBorder;
 }
 
 
-void
+DrawResult
 nsBCTableCellFrame::PaintBackground(nsRenderingContext& aRenderingContext,
                                     const nsRect&        aDirtyRect,
                                     nsPoint              aPt,
                                     uint32_t             aFlags)
 {
   // make border-width reflect the half of the border-collapse
   // assigned border that's inside the cell
   nsMargin borderWidth;
@@ -1234,13 +1247,13 @@ nsBCTableCellFrame::PaintBackground(nsRe
 
   NS_FOR_CSS_SIDES(side) {
     myBorder.SetBorderWidth(side, borderWidth.Side(side));
   }
 
   nsRect rect(aPt, GetSize());
   // bypassing nsCSSRendering::PaintBackground is safe because this kind
   // of frame cannot be used for the root element
-  nsCSSRendering::PaintBackgroundWithSC(PresContext(), aRenderingContext, this,
-                                        aDirtyRect, rect,
-                                        StyleContext(), myBorder,
-                                        aFlags, nullptr);
+  return nsCSSRendering::PaintBackgroundWithSC(PresContext(), aRenderingContext,
+                                               this, aDirtyRect, rect,
+                                               StyleContext(), myBorder,
+                                               aFlags, nullptr);
 }
--- a/layout/tables/nsTableCellFrame.h
+++ b/layout/tables/nsTableCellFrame.h
@@ -2,16 +2,17 @@
 /* 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/. */
 #ifndef nsTableCellFrame_h__
 #define nsTableCellFrame_h__
 
 #include "mozilla/Attributes.h"
 #include "celldata.h"
+#include "imgIContainer.h"
 #include "nsITableCellLayout.h"
 #include "nscore.h"
 #include "nsContainerFrame.h"
 #include "nsStyleContext.h"
 #include "nsIPercentHeightObserver.h"
 #include "nsGkAtoms.h"
 #include "nsLayoutUtils.h"
 #include "nsTArray.h"
@@ -28,16 +29,18 @@ class nsTableFrame;
  * no actual support is under the hood.
  *
  * @author  sclark
  */
 class nsTableCellFrame : public nsContainerFrame,
                          public nsITableCellLayout,
                          public nsIPercentHeightObserver
 {
+  typedef mozilla::image::DrawResult DrawResult;
+
 public:
   NS_DECL_QUERYFRAME_TARGET(nsTableCellFrame)
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   // default constructor supplied by the compiler
 
   explicit nsTableCellFrame(nsStyleContext* aContext);
@@ -88,19 +91,19 @@ public:
     * @return           the frame that was created
     */
   friend nsIFrame* NS_NewTableCellFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
 
-  void PaintCellBackground(nsRenderingContext& aRenderingContext,
-                           const nsRect& aDirtyRect, nsPoint aPt,
-                           uint32_t aFlags);
+  DrawResult PaintCellBackground(nsRenderingContext& aRenderingContext,
+                                 const nsRect& aDirtyRect, nsPoint aPt,
+                                 uint32_t aFlags);
 
  
   virtual nsresult ProcessBorders(nsTableFrame* aFrame,
                                   nsDisplayListBuilder* aBuilder,
                                   const nsDisplayListSet& aLists);
 
   virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
   virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) MOZ_OVERRIDE;
@@ -197,20 +200,20 @@ public:
 
   bool HasPctOverHeight();
   void SetHasPctOverHeight(bool aValue);
 
   nsTableCellFrame* GetNextCell() const;
 
   virtual nsMargin* GetBorderWidth(nsMargin& aBorder) const;
 
-  virtual void PaintBackground(nsRenderingContext& aRenderingContext,
-                               const nsRect&        aDirtyRect,
-                               nsPoint              aPt,
-                               uint32_t             aFlags);
+  virtual DrawResult PaintBackground(nsRenderingContext& aRenderingContext,
+                                     const nsRect&        aDirtyRect,
+                                     nsPoint              aPt,
+                                     uint32_t             aFlags);
 
   void DecorateForSelection(nsRenderingContext& aRenderingContext,
                             nsPoint              aPt);
 
   virtual bool UpdateOverflow() MOZ_OVERRIDE;
 
   virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE
   {
@@ -284,16 +287,17 @@ inline void nsTableCellFrame::SetHasPctO
   } else {
     mState &= ~NS_TABLE_CELL_HAS_PCT_OVER_HEIGHT;
   }
 }
 
 // nsBCTableCellFrame
 class nsBCTableCellFrame MOZ_FINAL : public nsTableCellFrame
 {
+  typedef mozilla::image::DrawResult DrawResult;
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   explicit nsBCTableCellFrame(nsStyleContext* aContext);
 
   ~nsBCTableCellFrame();
 
   virtual nsIAtom* GetType() const MOZ_OVERRIDE;
@@ -314,20 +318,20 @@ public:
   void SetBorderWidth(mozilla::css::Side aSide, BCPixelSize aPixelValue);
 
   virtual nsMargin GetBorderOverflow() MOZ_OVERRIDE;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
 #endif
 
-  virtual void PaintBackground(nsRenderingContext& aRenderingContext,
-                               const nsRect&        aDirtyRect,
-                               nsPoint              aPt,
-                               uint32_t             aFlags) MOZ_OVERRIDE;
+  virtual DrawResult PaintBackground(nsRenderingContext& aRenderingContext,
+                                     const nsRect&        aDirtyRect,
+                                     nsPoint              aPt,
+                                     uint32_t             aFlags) MOZ_OVERRIDE;
 
 private:
 
   // These are the entire width of the border (the cell edge contains only
   // the inner half, per the macros in nsTablePainter.h).
   BCPixelSize mTopBorder;
   BCPixelSize mRightBorder;
   BCPixelSize mBottomBorder;
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -42,16 +42,17 @@
 #include "nsStyleSet.h"
 #include "nsDisplayList.h"
 #include "nsIScrollableFrame.h"
 #include "nsCSSProps.h"
 #include "RestyleTracker.h"
 #include <algorithm>
 
 using namespace mozilla;
+using namespace mozilla::image;
 using namespace mozilla::layout;
 
 /********************************************************************************
  ** nsTableReflowState                                                         **
  ********************************************************************************/
 
 struct nsTableReflowState {
 
@@ -1109,16 +1110,17 @@ public:
     MOZ_COUNT_CTOR(nsDisplayTableBorderBackground);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayTableBorderBackground() {
     MOZ_COUNT_DTOR(nsDisplayTableBorderBackground);
   }
 #endif
 
+  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion *aInvalidRegion) MOZ_OVERRIDE;
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
   NS_DISPLAY_DECL_NAME("TableBorderBackground", TYPE_TABLE_BORDER_BACKGROUND)
 };
 
@@ -1131,62 +1133,49 @@ IsFrameAllowedInTable(nsIAtom* aType)
          nsGkAtoms::tableRowGroupFrame == aType ||
          nsGkAtoms::scrollFrame == aType ||
          nsGkAtoms::tableFrame == aType ||
          nsGkAtoms::tableColFrame == aType ||
          nsGkAtoms::tableColGroupFrame == aType;
 }
 #endif
 
-/* static */ bool
-nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(nsIFrame* aStart,
-                                                      nsIFrame* aEnd)
-{
-  for (nsIFrame* f = aStart; f != aEnd; f = f->GetNextSibling()) {
-    NS_ASSERTION(IsFrameAllowedInTable(f->GetType()), "unexpected frame type");
-
-    if (!nsCSSRendering::AreAllBackgroundImagesDecodedForFrame(f))
-      return true;
-
-    nsTableCellFrame *cellFrame = do_QueryFrame(f);
-    if (cellFrame)
-      continue;
-
-    if (AnyTablePartHasUndecodedBackgroundImage(f->PrincipalChildList().FirstChild(), nullptr))
-      return true;
-  }
-  
-  return false;
+nsDisplayItemGeometry*
+nsDisplayTableBorderBackground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
+{
+  return new nsDisplayItemGenericImageGeometry(this, aBuilder);
 }
 
 void
 nsDisplayTableBorderBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                           const nsDisplayItemGeometry* aGeometry,
                                                           nsRegion *aInvalidRegion)
 {
-  if (aBuilder->ShouldSyncDecodeImages()) {
-    if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling()) ||
-        nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(
-          mFrame->GetChildList(nsIFrame::kColGroupList).FirstChild(), nullptr)) {
-      bool snap;
-      aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
-    }
+  auto geometry =
+    static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
+
+  if (aBuilder->ShouldSyncDecodeImages() &&
+      geometry->ShouldInvalidateToSyncDecodeImages()) {
+    bool snap;
+    aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
   }
 
   nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 }
 
 void
 nsDisplayTableBorderBackground::Paint(nsDisplayListBuilder* aBuilder,
                                       nsRenderingContext* aCtx)
 {
-  static_cast<nsTableFrame*>(mFrame)->
+  DrawResult result = static_cast<nsTableFrame*>(mFrame)->
     PaintTableBorderBackground(*aCtx, mVisibleRect,
                                ToReferenceFrame(),
                                aBuilder->GetBackgroundPaintFlags());
+
+  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
 }
 
 static int32_t GetTablePartRank(nsDisplayItem* aItem)
 {
   nsIAtom* type = aItem->Frame()->GetType();
   if (type == nsGkAtoms::tableFrame)
     return 0;
   if (type == nsGkAtoms::tableRowGroupFrame)
@@ -1347,31 +1336,31 @@ nsTableFrame::GetDeflationForBackground(
       !IsBorderCollapse())
     return nsMargin(0,0,0,0);
 
   return GetOuterBCBorder();
 }
 
 // XXX We don't put the borders and backgrounds in tree order like we should.
 // That requires some major surgery which we aren't going to do right now.
-void
+DrawResult
 nsTableFrame::PaintTableBorderBackground(nsRenderingContext& aRenderingContext,
                                          const nsRect& aDirtyRect,
                                          nsPoint aPt, uint32_t aBGPaintFlags)
 {
   nsPresContext* presContext = PresContext();
 
   TableBackgroundPainter painter(this, TableBackgroundPainter::eOrigin_Table,
                                  presContext, aRenderingContext,
                                  aDirtyRect, aPt, aBGPaintFlags);
   nsMargin deflate = GetDeflationForBackground(presContext);
   // If 'deflate' is (0,0,0,0) then we'll paint the table background
   // in a separate display item, so don't do it here.
-  nsresult rv = painter.PaintTable(this, deflate, deflate != nsMargin(0, 0, 0, 0));
-  if (NS_FAILED(rv)) return;
+  DrawResult result =
+    painter.PaintTable(this, deflate, deflate != nsMargin(0, 0, 0, 0));
 
   if (StyleVisibility()->IsVisible()) {
     if (!IsBorderCollapse()) {
       Sides skipSides = GetSkipSides();
       nsRect rect(aPt, mRect.Size());
       nsCSSRendering::PaintBorder(presContext, aRenderingContext, this,
                                   aDirtyRect, rect, mStyleContext, skipSides);
     }
@@ -1385,16 +1374,18 @@ nsTableFrame::PaintTableBorderBackground
       // XXX we should probably get rid of this translation at some stage
       // But that would mean modifying PaintBCBorders, ugh
       gfxContextMatrixAutoSaveRestore autoSR(ctx);
       ctx->SetMatrix(ctx->CurrentMatrix().Translate(devPixelOffset));
 
       PaintBCBorders(aRenderingContext, aDirtyRect - aPt);
     }
   }
+
+  return result;
 }
 
 nsIFrame::LogicalSides
 nsTableFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const
 {
   if (MOZ_UNLIKELY(StyleBorder()->mBoxDecorationBreak ==
                      NS_STYLE_BOX_DECORATION_BREAK_CLONE)) {
     return LogicalSides();
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -2,16 +2,17 @@
 /* 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/. */
 #ifndef nsTableFrame_h__
 #define nsTableFrame_h__
 
 #include "mozilla/Attributes.h"
 #include "celldata.h"
+#include "imgIContainer.h"
 #include "nscore.h"
 #include "nsContainerFrame.h"
 #include "nsStyleCoord.h"
 #include "nsStyleConsts.h"
 #include "nsTableColFrame.h"
 #include "nsTableColGroupFrame.h"
 #include "nsCellMap.h"
 #include "nsGkAtoms.h"
@@ -101,16 +102,18 @@ private:
   * Used as a pseudo-frame within nsTableOuterFrame, it may also be used
   * stand-alone as the top-level frame.
   *
   * The principal child list contains row group frames. There is also an
   * additional child list, kColGroupList, which contains the col group frames.
   */
 class nsTableFrame : public nsContainerFrame
 {
+  typedef mozilla::image::DrawResult DrawResult;
+
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   static void DestroyPositionedTablePartArray(void* aPropertyValue);
   NS_DECLARE_FRAME_PROPERTY(PositionedTablePartArray, DestroyPositionedTablePartArray)
 
   /** nsTableOuterFrame has intimate knowledge of the inner table frame */
   friend class nsTableOuterFrame;
@@ -241,26 +244,19 @@ public:
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE;
 
   /**
    * Paint the background of the table and its parts (column groups,
    * columns, row groups, rows, and cells), and the table border, and all
    * internal borders if border-collapse is on.
    */
-  void PaintTableBorderBackground(nsRenderingContext& aRenderingContext,
-                                  const nsRect& aDirtyRect,
-                                  nsPoint aPt, uint32_t aBGPaintFlags);
-
-  /**
-   * Determines if any table part has a background image that is currently not
-   * decoded. Does not look into cell contents (ie only table parts).
-   */
-  static bool AnyTablePartHasUndecodedBackgroundImage(nsIFrame* aStart,
-                                                      nsIFrame* aEnd);
+  DrawResult PaintTableBorderBackground(nsRenderingContext& aRenderingContext,
+                                        const nsRect& aDirtyRect,
+                                        nsPoint aPt, uint32_t aBGPaintFlags);
 
   /** Get the outer half (i.e., the part outside the height and width of
    *  the table) of the largest segment (?) of border-collapsed border on
    *  the table on each side, or 0 for non border-collapsed tables.
    */
   nsMargin GetOuterBCBorder() const;
 
   /** Same as above, but only if it's included from the border-box width
--- a/layout/tables/nsTablePainter.cpp
+++ b/layout/tables/nsTablePainter.cpp
@@ -92,16 +92,18 @@
    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.
    
    XXX views are going 
  */
 
+using namespace mozilla::image;
+
 TableBackgroundPainter::TableBackgroundData::TableBackgroundData()
   : mFrame(nullptr),
     mVisible(false),
     mBorder(nullptr),
     mSynthBorder(nullptr)
 {
   MOZ_COUNT_CTOR(TableBackgroundData);
 }
@@ -168,33 +170,31 @@ TableBackgroundPainter::TableBackgroundD
   const nsStyleBackground *bg = mFrame->StyleBackground();
   NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, bg) {
     if (!bg->mLayers[i].mImage.IsEmpty())
       return true;
   }
   return false;
 }
 
-nsresult
+void
 TableBackgroundPainter::TableBackgroundData::SetBCBorder(nsMargin& aBorder,
                                                          TableBackgroundPainter* aPainter)
 {
-  NS_PRECONDITION(aPainter, "null painter");
+  MOZ_ASSERT(aPainter);
   if (!mSynthBorder) {
     mSynthBorder = new (aPainter->mPresContext)
                         nsStyleBorder(aPainter->mZeroBorder);
-    if (!mSynthBorder) return NS_ERROR_OUT_OF_MEMORY;
   }
 
   NS_FOR_CSS_SIDES(side) {
     mSynthBorder->SetBorderWidth(side, aBorder.Side(side));
   }
   
   mBorder = mSynthBorder;
-  return NS_OK;
 }
 
 TableBackgroundPainter::TableBackgroundPainter(nsTableFrame*        aTableFrame,
                                                Origin               aOrigin,
                                                nsPresContext*       aPresContext,
                                                nsRenderingContext& aRenderingContext,
                                                const nsRect&        aDirtyRect,
                                                const nsPoint&       aRenderPt,
@@ -240,23 +240,33 @@ TableBackgroundPainter::~TableBackground
     }
     delete [] mCols;
   }
   mRowGroup.Destroy(mPresContext);
   mRow.Destroy(mPresContext);
   MOZ_COUNT_DTOR(TableBackgroundPainter);
 }
 
-nsresult
+static void UpdateDrawResult(DrawResult* aCurrentResult,
+                             DrawResult aNewResult)
+{
+  MOZ_ASSERT(aCurrentResult);
+  if (*aCurrentResult == DrawResult::SUCCESS) {
+    *aCurrentResult = aNewResult;
+  }
+}
+
+DrawResult
 TableBackgroundPainter::PaintTableFrame(nsTableFrame*         aTableFrame,
                                         nsTableRowGroupFrame* aFirstRowGroup,
                                         nsTableRowGroupFrame* aLastRowGroup,
                                         const nsMargin&       aDeflate)
 {
-  NS_PRECONDITION(aTableFrame, "null frame");
+  MOZ_ASSERT(aTableFrame);
+
   TableBackgroundData tableData;
   tableData.SetFull(aTableFrame);
   tableData.mRect.MoveTo(0,0); //using table's coords
   tableData.mRect.Deflate(aDeflate);
   if (mIsBorderCollapse && tableData.ShouldSetBCBorder()) {
     if (aFirstRowGroup && aLastRowGroup && mNumCols > 0) {
       //only handle non-degenerate tables; we need a more robust BC model
       //to make degenerate tables' borders reasonable to deal with
@@ -273,33 +283,35 @@ TableBackgroundPainter::PaintTableFrame(
       nsTableRowFrame* rowFrame = aFirstRowGroup->GetFirstRow();
       if (rowFrame) {
         rowFrame->GetContinuousBCBorderWidth(tempBorder);
         border.top = tempBorder.top;
       }
 
       border.left = aTableFrame->GetContinuousLeftBCBorderWidth();
 
-      nsresult rv = tableData.SetBCBorder(border, this);
-      if (NS_FAILED(rv)) {
-        tableData.Destroy(mPresContext);
-        return rv;
-      }
+      tableData.SetBCBorder(border, this);
     }
   }
+
+  DrawResult result = DrawResult::SUCCESS;
+
   if (tableData.IsVisible()) {
-    nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
-                                          tableData.mFrame, mDirtyRect,
-                                          tableData.mRect + mRenderPt,
-                                          tableData.mFrame->StyleContext(),
-                                          *tableData.mBorder,
-                                          mBGPaintFlags);
+    result =
+      nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
+                                            tableData.mFrame, mDirtyRect,
+                                            tableData.mRect + mRenderPt,
+                                            tableData.mFrame->StyleContext(),
+                                            *tableData.mBorder,
+                                            mBGPaintFlags);
   }
+
   tableData.Destroy(mPresContext);
-  return NS_OK;
+
+  return result;
 }
 
 void
 TableBackgroundPainter::TranslateContext(nscoord aDX,
                                          nscoord aDY)
 {
   mRenderPt += nsPoint(aDX, aDY);
   if (mCols) {
@@ -313,46 +325,47 @@ TableBackgroundPainter::TranslateContext
           return;
         mCols[i].mColGroup->mRect.MoveBy(-aDX, -aDY);
         lastColGroup = mCols[i].mColGroup;
       }
     }
   }
 }
 
-nsresult
+DrawResult
 TableBackgroundPainter::PaintTable(nsTableFrame*   aTableFrame,
                                    const nsMargin& aDeflate,
                                    bool            aPaintTableBackground)
 {
   NS_PRECONDITION(aTableFrame, "null table frame");
 
   nsTableFrame::RowGroupArray rowGroups;
   aTableFrame->OrderRowGroups(rowGroups);
 
+  DrawResult result = DrawResult::SUCCESS;
+
   if (rowGroups.Length() < 1) { //degenerate case
     if (aPaintTableBackground) {
       PaintTableFrame(aTableFrame, nullptr, nullptr, nsMargin(0,0,0,0));
     }
     /* No cells; nothing else to paint */
-    return NS_OK;
+    return result;
   }
 
   if (aPaintTableBackground) {
     PaintTableFrame(aTableFrame, rowGroups[0], rowGroups[rowGroups.Length() - 1],
                     aDeflate);
   }
 
   /*Set up column background/border data*/
   if (mNumCols > 0) {
     nsFrameList& colGroupList = aTableFrame->GetColGroups();
     NS_ASSERTION(colGroupList.FirstChild(), "table should have at least one colgroup");
 
     mCols = new ColData[mNumCols];
-    if (!mCols) return NS_ERROR_OUT_OF_MEMORY;
 
     TableBackgroundData* cgData = nullptr;
     nsMargin border;
     /* BC left borders aren't stored on cols, but the previous column's
        right border is the next one's left border.*/
     //Start with table's left border.
     nscoord lastLeftBorder = aTableFrame->GetContinuousLeftBCBorderWidth();
     for (nsTableColGroupFrame* cgFrame = static_cast<nsTableColGroupFrame*>(colGroupList.FirstChild());
@@ -360,27 +373,21 @@ TableBackgroundPainter::PaintTable(nsTab
 
       if (cgFrame->GetColCount() < 1) {
         //No columns, no cells, so no need for data
         continue;
       }
 
       /*Create data struct for column group*/
       cgData = new TableBackgroundData;
-      if (!cgData) return NS_ERROR_OUT_OF_MEMORY;
       cgData->SetFull(cgFrame);
       if (mIsBorderCollapse && cgData->ShouldSetBCBorder()) {
         border.left = lastLeftBorder;
         cgFrame->GetContinuousBCBorderWidth(border);
-        nsresult rv = cgData->SetBCBorder(border, this);
-        if (NS_FAILED(rv)) {
-          cgData->Destroy(mPresContext);
-          delete cgData;
-          return rv;
-        }
+        cgData->SetBCBorder(border, this);
       }
 
       // Boolean that indicates whether mCols took ownership of cgData
       bool cgDataOwnershipTaken = false;
       
       /*Loop over columns in this colgroup*/
       for (nsTableColFrame* col = cgFrame->GetFirstColumn(); col;
            col = static_cast<nsTableColFrame*>(col->GetNextSibling())) {
@@ -394,18 +401,17 @@ TableBackgroundPainter::PaintTable(nsTab
         mCols[colIndex].mCol.mRect.MoveBy(cgData->mRect.x, cgData->mRect.y);
         //link to parent colgroup's data
         mCols[colIndex].mColGroup = cgData;
         cgDataOwnershipTaken = true;
         if (mIsBorderCollapse) {
           border.left = lastLeftBorder;
           lastLeftBorder = col->GetContinuousBCBorderWidth(border);
           if (mCols[colIndex].mCol.ShouldSetBCBorder()) {
-            nsresult rv = mCols[colIndex].mCol.SetBCBorder(border, this);
-            if (NS_FAILED(rv)) return rv;
+            mCols[colIndex].mCol.SetBCBorder(border, this);
           }
         }
       }
 
       if (!cgDataOwnershipTaken) {
         cgData->Destroy(mPresContext);
         delete cgData;
       }
@@ -422,28 +428,30 @@ TableBackgroundPainter::PaintTable(nsTab
     // 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;
+      DrawResult rowGroupResult =
+        PaintRowGroup(rg, rg->IsPseudoStackingContextFromStyle());
+      UpdateDrawResult(&result, rowGroupResult);
     }
   }
-  return NS_OK;
+
+  return result;
 }
 
-nsresult
+DrawResult
 TableBackgroundPainter::PaintRowGroup(nsTableRowGroupFrame* aFrame,
                                       bool                  aPassThrough)
 {
-  NS_PRECONDITION(aFrame, "null frame");
+  MOZ_ASSERT(aFrame);
 
   if (!mRowGroup.mFrame) {
     mRowGroup.SetFrame(aFrame);
   }
 
   nsTableRowFrame* firstRow = aFrame->GetFirstRow();
 
   /* Load row group data */
@@ -453,20 +461,17 @@ TableBackgroundPainter::PaintRowGroup(ns
       nsMargin border;
       if (firstRow) {
         //pick up first row's top border (= rg top border)
         firstRow->GetContinuousBCBorderWidth(border);
         /* (row group doesn't store its top border) */
       }
       //overwrite sides+bottom borders with rg's own
       aFrame->GetContinuousBCBorderWidth(border);
-      nsresult res = mRowGroup.SetBCBorder(border, this);
-      if (!NS_SUCCEEDED(res)) {
-        return res;
-      }
+      mRowGroup.SetBCBorder(border, this);
     }
     aPassThrough = !mRowGroup.IsVisible();
   }
 
   /* translate everything into row group coord system*/
   if (eOrigin_TableRowGroup != mOrigin) {
     TranslateContext(mRowGroup.mRect.x, mRowGroup.mRect.y);
   }
@@ -491,51 +496,55 @@ TableBackgroundPainter::PaintRowGroup(ns
   nsTableRowFrame* row = static_cast<nsTableRowFrame*>(cursor);  
   if (!row) {
     // No useful cursor; just start at the top.  Don't bother to set up a
     // cursor; if we've gotten this far then we've already built the display
     // 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;
   }
+
+  DrawResult result = DrawResult::SUCCESS;
   
   /* Finally paint */
   for (; row; row = row->GetNextRow()) {
     mRow.SetFrame(row);
     // 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;
+    DrawResult rowResult =
+      PaintRow(row, aPassThrough || row->IsPseudoStackingContextFromStyle());
+
+    UpdateDrawResult(&result, rowResult);
   }
 
   /* translate back into table coord system */
   if (eOrigin_TableRowGroup != mOrigin) {
     TranslateContext(-rgRect.x, -rgRect.y);
   }
   
   /* unload rg data */
   mRowGroup.Clear();
 
-  return NS_OK;
+  return result;
 }
 
-nsresult
+DrawResult
 TableBackgroundPainter::PaintRow(nsTableRowFrame* aFrame,
                                  bool             aPassThrough)
 {
-  NS_PRECONDITION(aFrame, "null frame");
+  MOZ_ASSERT(aFrame);
 
   if (!mRow.mFrame) {
     mRow.SetFrame(aFrame);
   }
 
   /* Load row data */
   if (!aPassThrough) {
     mRow.SetData();
@@ -547,125 +556,138 @@ TableBackgroundPainter::PaintRow(nsTable
       }
       else { //acquire rg's bottom border
         nsTableRowGroupFrame* rowGroup = static_cast<nsTableRowGroupFrame*>(aFrame->GetParent());
         rowGroup->GetContinuousBCBorderWidth(border);
       }
       //get the rest of the borders; will overwrite all but bottom
       aFrame->GetContinuousBCBorderWidth(border);
 
-      nsresult res = mRow.SetBCBorder(border, this);
-      if (!NS_SUCCEEDED(res)) {
-        return res;
-      }
+      mRow.SetBCBorder(border, this);
     }
     aPassThrough = !mRow.IsVisible();
   }
 
   /* 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
 
+  DrawResult result = DrawResult::SUCCESS;
+
   for (nsTableCellFrame* cell = aFrame->GetFirstCell(); cell; cell = cell->GetNextCell()) {
     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;
+      DrawResult cellResult = PaintCell(cell, cellBGRect, rowBGRect,
+                                        rowGroupBGRect, colBGRect, passCell);
+      UpdateDrawResult(&result, cellResult);
     }
   }
 
   /* Unload row data */
   mRow.Clear();
-  return NS_OK;
+
+  return result;
 }
 
-nsresult
+DrawResult
 TableBackgroundPainter::PaintCell(nsTableCellFrame* aCell,
                                   nsRect&           aCellBGRect,
                                   nsRect&           aRowBGRect,
                                   nsRect&           aRowGroupBGRect,
                                   nsRect&           aColBGRect,
                                   bool              aPassSelf)
 {
-  NS_PRECONDITION(aCell, "null frame");
+  MOZ_ASSERT(aCell);
 
   const nsStyleTableBorder* cellTableStyle;
   cellTableStyle = aCell->StyleTableBorder();
   if (NS_STYLE_TABLE_EMPTY_CELLS_SHOW != cellTableStyle->mEmptyCells &&
       aCell->GetContentEmpty() && !mIsBorderCollapse) {
-    return NS_OK;
+    return DrawResult::SUCCESS;
   }
 
   int32_t colIndex;
   aCell->GetColIndex(colIndex);
   NS_ASSERTION(colIndex < int32_t(mNumCols), "prevent array boundary violation");
-  if (int32_t(mNumCols) <= colIndex)
-    return NS_OK;
+  if (int32_t(mNumCols) <= colIndex) {
+    return DrawResult::SUCCESS;
+  }
+
+  DrawResult result = DrawResult::SUCCESS;
 
   //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, &aColBGRect);
+    DrawResult colGroupResult = 
+      nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
+                                            mCols[colIndex].mColGroup->mFrame, mDirtyRect,
+                                            mCols[colIndex].mColGroup->mRect + mRenderPt,
+                                            mCols[colIndex].mColGroup->mFrame->StyleContext(),
+                                            *mCols[colIndex].mColGroup->mBorder,
+                                            mBGPaintFlags, &aColBGRect);
+    UpdateDrawResult(&result, colGroupResult);
   }
 
   //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, &aColBGRect);
+    DrawResult colResult =
+      nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
+                                            mCols[colIndex].mCol.mFrame, mDirtyRect,
+                                            mCols[colIndex].mCol.mRect + mRenderPt,
+                                            mCols[colIndex].mCol.mFrame->StyleContext(),
+                                            *mCols[colIndex].mCol.mBorder,
+                                            mBGPaintFlags, &aColBGRect);
+    UpdateDrawResult(&result, colResult);
   }
 
   //Paint row group background
   if (mRowGroup.IsVisible()) {
-    nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
-                                          mRowGroup.mFrame, mDirtyRect,
-                                          mRowGroup.mRect + mRenderPt,
-                                          mRowGroup.mFrame->StyleContext(),
-                                          *mRowGroup.mBorder,
-                                          mBGPaintFlags, &aRowGroupBGRect);
+    DrawResult rowGroupResult =
+      nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
+                                            mRowGroup.mFrame, mDirtyRect,
+                                            mRowGroup.mRect + mRenderPt,
+                                            mRowGroup.mFrame->StyleContext(),
+                                            *mRowGroup.mBorder,
+                                            mBGPaintFlags, &aRowGroupBGRect);
+    UpdateDrawResult(&result, rowGroupResult);
   }
 
   //Paint row background
   if (mRow.IsVisible()) {
-    nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
-                                          mRow.mFrame, mDirtyRect,
-                                          mRow.mRect + mRenderPt,
-                                          mRow.mFrame->StyleContext(),
-                                          *mRow.mBorder,
-                                          mBGPaintFlags, &aRowBGRect);
+    DrawResult rowResult =
+      nsCSSRendering::PaintBackgroundWithSC(mPresContext, mRenderingContext,
+                                            mRow.mFrame, mDirtyRect,
+                                            mRow.mRect + mRenderPt,
+                                            mRow.mFrame->StyleContext(),
+                                            *mRow.mBorder,
+                                            mBGPaintFlags, &aRowBGRect);
+    UpdateDrawResult(&result, rowResult);
   }
 
   //Paint cell background in border-collapse unless we're just passing
   if (mIsBorderCollapse && !aPassSelf) {
-    aCell->PaintCellBackground(mRenderingContext, mDirtyRect,
-                               aCellBGRect.TopLeft(), mBGPaintFlags);
+    DrawResult cellResult =
+      aCell->PaintCellBackground(mRenderingContext, mDirtyRect,
+                                 aCellBGRect.TopLeft(), mBGPaintFlags);
+    UpdateDrawResult(&result, cellResult);
   }
 
-  return NS_OK;
+  return result;
 }
 
 void
 TableBackgroundPainter::ComputeCellBackgrounds(nsTableCellFrame* aCell,
                                                nsRect&           aCellBGRect,
                                                nsRect&           aRowBGRect,
                                                nsRect&           aRowGroupBGRect,
                                                nsRect&           aColBGRect)
--- a/layout/tables/nsTablePainter.h
+++ b/layout/tables/nsTablePainter.h
@@ -1,16 +1,18 @@
 /* -*- 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/. */
 
 #ifndef nsTablePainter_h__
 #define nsTablePainter_h__
 
+#include "imgIContainer.h"
+
 #include "celldata.h"
 
 // flags for Paint, PaintChild, PaintChildren are currently only used by tables.
 //Table-based paint call; not a direct call as with views
 #define NS_PAINT_FLAG_TABLE_BG_PAINT      0x00000001
 //Cells should paint their backgrounds only, no children
 #define NS_PAINT_FLAG_TABLE_CELL_BG_PASS  0x00000002
 
@@ -22,16 +24,18 @@ class nsTableCellFrame;
 
 class TableBackgroundPainter
 {
   /*
    * Helper class for painting table backgrounds
    *
    */
 
+  typedef mozilla::image::DrawResult DrawResult;
+
   public:
 
     enum Origin { eOrigin_Table, eOrigin_TableRowGroup, eOrigin_TableRow };
 
     /** Public constructor
       * @param aTableFrame       - the table's table frame
       * @param aOrigin           - what type of table frame is creating this instance
       * @param aPresContext      - the presentation context
@@ -75,80 +79,89 @@ class TableBackgroundPainter
       * (Cells themselves will only be painted in border collapse)
       * Table must do a flagged TABLE_BG_PAINT ::Paint call on its
       * children afterwards
       * @param aTableFrame - the table frame
       * @param aDeflate    - deflation needed to bring table's mRect
       *                      to the outer grid lines in border-collapse
       * @param aPaintTableBackground - if true, the table background
       * is included, otherwise it isn't
+      * @returns DrawResult::SUCCESS if all painting was successful. If some
+      *          painting failed or an improved result could be achieved by sync
+      *          decoding images, returns another value.
       */
-    nsresult PaintTable(nsTableFrame* aTableFrame, const nsMargin& aDeflate,
-                        bool aPaintTableBackground);
+    DrawResult PaintTable(nsTableFrame* aTableFrame, const nsMargin& aDeflate,
+                          bool aPaintTableBackground);
 
     /** Paint background for the row group and its children down through cells
       * (Cells themselves will only be painted in border collapse)
       * Standards mode only
       * Table Row Group must do a flagged TABLE_BG_PAINT ::Paint call on its
       * children afterwards
       * @param aFrame - the table row group frame
+      * @returns DrawResult::SUCCESS if all painting was successful. If some
+      *          painting failed or an improved result could be achieved by sync
+      *          decoding images, returns another value.
       */
-    nsresult PaintRowGroup(nsTableRowGroupFrame* aFrame)
+    DrawResult PaintRowGroup(nsTableRowGroupFrame* aFrame)
     { return PaintRowGroup(aFrame, false); }
 
     /** Paint background for the row and its children down through cells
       * (Cells themselves will only be painted in border collapse)
       * Standards mode only
       * Table Row must do a flagged TABLE_BG_PAINT ::Paint call on its
       * children afterwards
       * @param aFrame - the table row frame
+      * @returns DrawResult::SUCCESS if all painting was successful. If some
+      *          painting failed or an improved result could be achieved by sync
+      *          decoding images, returns another value.
       */
-    nsresult PaintRow(nsTableRowFrame* aFrame)
+    DrawResult PaintRow(nsTableRowFrame* aFrame)
     { return PaintRow(aFrame, false); }
 
   private:
 
     /** Paint table frame's background
       * @param aTableFrame     - the table frame
       * @param aFirstRowGroup  - the first (in layout order) row group
       *                          may be null
       * @param aLastRowGroup   - the last (in layout order) row group
       *                          may be null
       * @param aDeflate        - adjustment to frame's rect (used for quirks BC)
       *                          may be null
       */
-    nsresult PaintTableFrame(nsTableFrame*         aTableFrame,
-                             nsTableRowGroupFrame* aFirstRowGroup,
-                             nsTableRowGroupFrame* aLastRowGroup,
-                             const nsMargin&       aDeflate);
+    DrawResult PaintTableFrame(nsTableFrame*         aTableFrame,
+                               nsTableRowGroupFrame* aFirstRowGroup,
+                               nsTableRowGroupFrame* aLastRowGroup,
+                               const nsMargin&       aDeflate);
 
     /* aPassThrough params indicate whether to paint the element or to just
      * pass through and paint underlying layers only
      * See Public versions for function descriptions
      */
-    nsresult PaintRowGroup(nsTableRowGroupFrame* aFrame,
-                           bool                  aPassThrough);
-    nsresult PaintRow(nsTableRowFrame* aFrame,
-                      bool             aPassThrough);
+    DrawResult PaintRowGroup(nsTableRowGroupFrame* aFrame,
+                             bool                  aPassThrough);
+    DrawResult PaintRow(nsTableRowFrame* aFrame,
+                        bool             aPassThrough);
 
     /** Paint table background layers for this cell space
       * Also paints cell's own background in border-collapse mode
       * @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* aCell,
-                       nsRect&           aCellBGRect,
-                       nsRect&           aRowBGRect,
-                       nsRect&           aRowGroupBGRect,
-                       nsRect&           aColBGRect,
-                       bool              aPassSelf);
+    DrawResult 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
@@ -200,18 +213,18 @@ class TableBackgroundPainter
 
       /** Calculate the style data for mFrame */
       void SetData();
 
       /** True if need to set border-collapse border; must call SetFull beforehand */
       bool ShouldSetBCBorder();
 
       /** Set border-collapse border with aBorderWidth as widths */
-      nsresult SetBCBorder(nsMargin&               aBorderWidth,
-                           TableBackgroundPainter* aPainter);
+      void SetBCBorder(nsMargin&               aBorderWidth,
+                       TableBackgroundPainter* aPainter);
 
       private:
       nsStyleBorder* mSynthBorder;
     };
 
     struct ColData;
     friend struct ColData;
     struct ColData {
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -20,16 +20,17 @@
 #include "nsTableColGroupFrame.h"
 #include "nsTableColFrame.h"
 #include "nsCOMPtr.h"
 #include "nsDisplayList.h"
 #include "nsIFrameInlines.h"
 #include <algorithm>
 
 using namespace mozilla;
+using namespace mozilla::image;
 
 struct nsTableCellReflowState : public nsHTMLReflowState
 {
   nsTableCellReflowState(nsPresContext*           aPresContext,
                          const nsHTMLReflowState& aParentReflowState,
                          nsIFrame*                aFrame,
                          const LogicalSize&       aAvailableSpace,
                          uint32_t                 aFlags = 0)
@@ -530,50 +531,63 @@ public:
     MOZ_COUNT_CTOR(nsDisplayTableRowBackground);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayTableRowBackground() {
     MOZ_COUNT_DTOR(nsDisplayTableRowBackground);
   }
 #endif
 
+  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion *aInvalidRegion) MOZ_OVERRIDE;
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
   NS_DISPLAY_DECL_NAME("TableRowBackground", TYPE_TABLE_ROW_BACKGROUND)
 };
 
+nsDisplayItemGeometry*
+nsDisplayTableRowBackground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
+{
+  return new nsDisplayItemGenericImageGeometry(this, aBuilder);
+}
+
 void
 nsDisplayTableRowBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                        const nsDisplayItemGeometry* aGeometry,
                                                        nsRegion *aInvalidRegion)
 {
-  if (aBuilder->ShouldSyncDecodeImages()) {
-    if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling())) {
-      bool snap;
-      aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
-    }
+  auto geometry =
+    static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
+
+  if (aBuilder->ShouldSyncDecodeImages() &&
+      geometry->ShouldInvalidateToSyncDecodeImages()) {
+    bool snap;
+    aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
   }
 
   nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 }
 
 void
 nsDisplayTableRowBackground::Paint(nsDisplayListBuilder* aBuilder,
                                    nsRenderingContext* aCtx)
 {
   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame);
   TableBackgroundPainter painter(tableFrame,
                                  TableBackgroundPainter::eOrigin_TableRow,
                                  mFrame->PresContext(), *aCtx,
                                  mVisibleRect, ToReferenceFrame(),
                                  aBuilder->GetBackgroundPaintFlags());
-  painter.PaintRow(static_cast<nsTableRowFrame*>(mFrame));
+
+  DrawResult result =
+    painter.PaintRow(static_cast<nsTableRowFrame*>(mFrame));
+
+  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
 }
 
 void
 nsTableRowFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                   const nsRect&           aDirtyRect,
                                   const nsDisplayListSet& aLists)
 {
   nsDisplayTableItem* item = nullptr;
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -17,16 +17,17 @@
 #include "nsHTMLParts.h"
 #include "nsCSSFrameConstructor.h"
 #include "nsDisplayList.h"
 
 #include "nsCellMap.h"//table cell navigation
 #include <algorithm>
 
 using namespace mozilla;
+using namespace mozilla::image;
 using namespace mozilla::layout;
 
 nsTableRowGroupFrame::nsTableRowGroupFrame(nsStyleContext* aContext):
   nsContainerFrame(aContext)
 {
   SetRepeatable(false);
 }
 
@@ -142,51 +143,64 @@ public:
     MOZ_COUNT_CTOR(nsDisplayTableRowGroupBackground);
   }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayTableRowGroupBackground() {
     MOZ_COUNT_DTOR(nsDisplayTableRowGroupBackground);
   }
 #endif
 
+  virtual nsDisplayItemGeometry* AllocateGeometry(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE;
   virtual void ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                          const nsDisplayItemGeometry* aGeometry,
                                          nsRegion *aInvalidRegion) MOZ_OVERRIDE;
   virtual void Paint(nsDisplayListBuilder* aBuilder,
                      nsRenderingContext* aCtx) MOZ_OVERRIDE;
 
   NS_DISPLAY_DECL_NAME("TableRowGroupBackground", TYPE_TABLE_ROW_GROUP_BACKGROUND)
 };
 
+nsDisplayItemGeometry*
+nsDisplayTableRowGroupBackground::AllocateGeometry(nsDisplayListBuilder* aBuilder)
+{
+  return new nsDisplayItemGenericImageGeometry(this, aBuilder);
+}
+
 void
 nsDisplayTableRowGroupBackground::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder,
                                                             const nsDisplayItemGeometry* aGeometry,
                                                             nsRegion *aInvalidRegion)
 {
-  if (aBuilder->ShouldSyncDecodeImages()) {
-    if (nsTableFrame::AnyTablePartHasUndecodedBackgroundImage(mFrame, mFrame->GetNextSibling())) {
-      bool snap;
-      aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
-    }
+  auto geometry =
+    static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
+
+  if (aBuilder->ShouldSyncDecodeImages() &&
+      geometry->ShouldInvalidateToSyncDecodeImages()) {
+    bool snap;
+    aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
   }
 
   nsDisplayTableItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 }
 
 void
 nsDisplayTableRowGroupBackground::Paint(nsDisplayListBuilder* aBuilder,
                                         nsRenderingContext* aCtx)
 {
   nsTableFrame* tableFrame = nsTableFrame::GetTableFrame(mFrame);
   TableBackgroundPainter painter(tableFrame,
                                  TableBackgroundPainter::eOrigin_TableRowGroup,
                                  mFrame->PresContext(), *aCtx,
                                  mVisibleRect, ToReferenceFrame(),
                                  aBuilder->GetBackgroundPaintFlags());
-  painter.PaintRowGroup(static_cast<nsTableRowGroupFrame*>(mFrame));
+
+  DrawResult result =
+    painter.PaintRowGroup(static_cast<nsTableRowGroupFrame*>(mFrame));
+
+  nsDisplayItemGenericImageGeometry::UpdateDrawResult(this, result);
 }
 
 // Handle the child-traversal part of DisplayGenericTablePart
 static void
 DisplayRows(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
             const nsRect& aDirtyRect, const nsDisplayListSet& aLists)
 {
   nscoord overflowAbove;
--- a/layout/xul/nsImageBoxFrame.cpp
+++ b/layout/xul/nsImageBoxFrame.cpp
@@ -383,17 +383,17 @@ nsDisplayXULImage::ComputeInvalidationRe
                                              nsRegion* aInvalidRegion)
 {
   auto boxFrame = static_cast<nsImageBoxFrame*>(mFrame);
   auto geometry =
     static_cast<const nsDisplayItemGenericImageGeometry*>(aGeometry);
 
   if (aBuilder->ShouldSyncDecodeImages() &&
       boxFrame->mImageRequest &&
-      geometry->LastDrawResult() != DrawResult::SUCCESS) {
+      geometry->ShouldInvalidateToSyncDecodeImages()) {
       bool snap;
       aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap));
   }
 
   nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 }
 
 void
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -2768,22 +2768,50 @@ nsTreeBodyFrame::HandleEvent(nsPresConte
                   CloseCallback, nsITimer::TYPE_ONE_SHOT,
                   getter_AddRefs(mSlots->mTimer));
     }
   }
 
   return NS_OK;
 }
 
-static void
-PaintTreeBody(nsIFrame* aFrame, nsRenderingContext* aCtx,
-              const nsRect& aDirtyRect, nsPoint aPt)
-{
-  static_cast<nsTreeBodyFrame*>(aFrame)->PaintTreeBody(*aCtx, aDirtyRect, aPt);
-}
+class nsDisplayTreeBody MOZ_FINAL : public nsDisplayItem {
+public:
+  nsDisplayTreeBody(nsDisplayListBuilder* aBuilder, nsFrame* aFrame) :
+    nsDisplayItem(aBuilder, aFrame),
+    mDisableSubpixelAA(false) {
+    MOZ_COUNT_CTOR(nsDisplayTreeBody);
+  }
+#ifdef NS_BUILD_REFCNT_LOGGING
+  virtual ~nsDisplayTreeBody() {
+    MOZ_COUNT_DTOR(nsDisplayTreeBody);
+  }
+#endif
+
+  virtual void Paint(nsDisplayListBuilder* aBuilder,
+                     nsRenderingContext* aCtx) MOZ_OVERRIDE
+  {
+    gfxContext* ctx = aCtx->ThebesContext();
+    gfxContextAutoDisableSubpixelAntialiasing disable(ctx, mDisableSubpixelAA);
+    static_cast<nsTreeBodyFrame*>(mFrame)->
+      PaintTreeBody(*aCtx, mVisibleRect, ToReferenceFrame());
+  }
+  NS_DISPLAY_DECL_NAME("XULTreeBody", TYPE_XUL_TREE_BODY)
+
+  virtual nsRect GetComponentAlphaBounds(nsDisplayListBuilder* aBuilder) MOZ_OVERRIDE
+  {
+    bool snap;
+    return GetBounds(aBuilder, &snap);
+  }
+  virtual void DisableComponentAlpha() MOZ_OVERRIDE {
+    mDisableSubpixelAA = true;
+  }
+
+  bool mDisableSubpixelAA;
+};
 
 // Painting routines
 void
 nsTreeBodyFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                   const nsRect&           aDirtyRect,
                                   const nsDisplayListSet& aLists)
 {
   // REVIEW: why did we paint if we were collapsed? that makes no sense!
@@ -2794,18 +2822,17 @@ nsTreeBodyFrame::BuildDisplayList(nsDisp
   nsLeafBoxFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
 
   // Bail out now if there's no view or we can't run script because the
   // document is a zombie
   if (!mView || !GetContent ()->GetComposedDoc()->GetWindow())
     return;
 
   aLists.Content()->AppendNewToTop(new (aBuilder)
-    nsDisplayGeneric(aBuilder, this, ::PaintTreeBody, "XULTreeBody",
-                     nsDisplayItem::TYPE_XUL_TREE_BODY));
+    nsDisplayTreeBody(aBuilder, this));
 }
 
 void
 nsTreeBodyFrame::PaintTreeBody(nsRenderingContext& aRenderingContext,
                                const nsRect& aDirtyRect, nsPoint aPt)
 {
   // Update our available height and our page count.
   CalcInnerBox();
--- a/mobile/android/base/db/LocalBrowserDB.java
+++ b/mobile/android/base/db/LocalBrowserDB.java
@@ -27,16 +27,17 @@ import org.mozilla.gecko.AboutPages;
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.ExpirePriority;
 import org.mozilla.gecko.db.BrowserContract.Favicons;
 import org.mozilla.gecko.db.BrowserContract.History;
 import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
+import org.mozilla.gecko.db.BrowserContract.SearchHistory;
 import org.mozilla.gecko.db.BrowserContract.SyncColumns;
 import org.mozilla.gecko.db.BrowserContract.Thumbnails;
 import org.mozilla.gecko.db.BrowserDB.FilterFlags;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
 import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
 import org.mozilla.gecko.gfx.BitmapUtils;
 import org.mozilla.gecko.mozglue.RobocopTarget;
@@ -88,16 +89,17 @@ public class LocalBrowserDB {
     private final Uri mParentsUriWithProfile;
     private final Uri mHistoryUriWithProfile;
     private final Uri mHistoryExpireUriWithProfile;
     private final Uri mCombinedUriWithProfile;
     private final Uri mUpdateHistoryUriWithProfile;
     private final Uri mFaviconsUriWithProfile;
     private final Uri mThumbnailsUriWithProfile;
     private final Uri mReadingListUriWithProfile;
+    private final Uri mSearchHistoryUri;
 
     private static final String[] DEFAULT_BOOKMARK_COLUMNS =
             new String[] { Bookmarks._ID,
                            Bookmarks.GUID,
                            Bookmarks.URL,
                            Bookmarks.TITLE,
                            Bookmarks.TYPE,
                            Bookmarks.PARENT };
@@ -110,16 +112,18 @@ public class LocalBrowserDB {
         mParentsUriWithProfile = appendProfile(Bookmarks.PARENTS_CONTENT_URI);
         mHistoryUriWithProfile = appendProfile(History.CONTENT_URI);
         mHistoryExpireUriWithProfile = appendProfile(History.CONTENT_OLD_URI);
         mCombinedUriWithProfile = appendProfile(Combined.CONTENT_URI);
         mFaviconsUriWithProfile = appendProfile(Favicons.CONTENT_URI);
         mThumbnailsUriWithProfile = appendProfile(Thumbnails.CONTENT_URI);
         mReadingListUriWithProfile = appendProfile(ReadingListItems.CONTENT_URI);
 
+        mSearchHistoryUri = BrowserContract.SearchHistory.CONTENT_URI;
+
         mUpdateHistoryUriWithProfile = mHistoryUriWithProfile.buildUpon().
             appendQueryParameter(BrowserContract.PARAM_INCREMENT_VISITS, "true").
             appendQueryParameter(BrowserContract.PARAM_INSERT_IF_NEEDED, "true").build();
     }
 
     /**
      * Not thread safe. A helper to allocate new IDs for arbitrary strings.
      */
@@ -675,16 +679,17 @@ public class LocalBrowserDB {
     public void removeHistoryEntry(ContentResolver cr, String url) {
         cr.delete(mHistoryUriWithProfile,
                   History.URL + " = ?",
                   new String[] { url });
     }
 
     public void clearHistory(ContentResolver cr) {
         cr.delete(mHistoryUriWithProfile, null, null);
+        cr.delete(mSearchHistoryUri, null, null);
     }
 
     @RobocopTarget
     public Cursor getBookmarksInFolder(ContentResolver cr, long folderId) {
         final boolean addDesktopFolder;
 
         // We always want to show mobile bookmarks in the root view.
         if (folderId == Bookmarks.FIXED_ROOT_ID) {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1548,17 +1548,17 @@ pref("network.dns.disableIPv6", false);
 
 // This is the number of dns cache entries allowed
 pref("network.dnsCacheEntries", 400);
 
 // In the absence of OS TTLs, the DNS cache TTL value
 pref("network.dnsCacheExpiration", 60);
 
 // Get TTL; not supported on all platforms; nop on the unsupported ones.
-pref("network.dns.get-ttl", true);
+pref("network.dns.get-ttl", false);
 
 // The grace period allows the DNS cache to use expired entries, while kicking off
 // a revalidation in the background.
 pref("network.dnsCacheExpirationGracePeriod", 60);
 
 // This preference can be used to turn off DNS prefetch.
 pref("network.dns.disablePrefetch", false);
 
--- a/toolkit/components/passwordmgr/LoginManagerParent.jsm
+++ b/toolkit/components/passwordmgr/LoginManagerParent.jsm
@@ -102,18 +102,19 @@ var LoginManagerParent = {
   findLogins: function(showMasterPassword, formOrigin, actionOrigin,
                        requestId, target) {
     if (!showMasterPassword && !Services.logins.isLoggedIn) {
       target.sendAsyncMessage("RemoteLogins:loginsFound",
                               { requestId: requestId, logins: [] });
       return;
     }
 
+    let allLoginsCount = Services.logins.countLogins(formOrigin, "", null);
     // If there are no logins for this site, bail out now.
-    if (!Services.logins.countLogins(formOrigin, "", null)) {
+    if (!allLoginsCount) {
       target.sendAsyncMessage("RemoteLogins:loginsFound",
                               { requestId: requestId, logins: [] });
       return;
     }
 
     // If we're currently displaying a master password prompt, defer
     // processing this form until the user handles the prompt.
     if (Services.logins.uiBusy) {
@@ -147,16 +148,26 @@ var LoginManagerParent = {
       Services.obs.addObserver(observer, "passwordmgr-crypto-login", false);
       Services.obs.addObserver(observer, "passwordmgr-crypto-loginCanceled", false);
       return;
     }
 
     var logins = Services.logins.findLogins({}, formOrigin, actionOrigin, null);
     target.sendAsyncMessage("RemoteLogins:loginsFound",
                             { requestId: requestId, logins: logins });
+
+    const PWMGR_FORM_ACTION_EFFECT =  Services.telemetry.getHistogramById("PWMGR_FORM_ACTION_EFFECT");
+    if (logins.length == 0) {
+      PWMGR_FORM_ACTION_EFFECT.add(2);
+    } else if (logins.length == allLoginsCount) {
+      PWMGR_FORM_ACTION_EFFECT.add(0);
+    } else {
+      // logins.length < allLoginsCount
+      PWMGR_FORM_ACTION_EFFECT.add(1);
+    }
   },
 
   doAutocompleteSearch: function({ formOrigin, actionOrigin,
                                    searchString, previousResult,
                                    rect, requestId, remote }, target) {
     // Note: previousResult is a regular object, not an
     // nsIAutoCompleteResult.
     var result;
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -7197,16 +7197,22 @@
   "PWMGR_BLOCKLIST_NUM_SITES": {
     "expires_in_version": "never",
     "kind": "exponential",
     "high": 100,
     "n_buckets" : 10,
     "extended_statistics_ok": true,
     "description": "The number of sites for which the user has explicitly rejected saving logins"
   },
+  "PWMGR_FORM_ACTION_EFFECT": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values" : 5,
+    "description": "The effect of the form action on signon autofill. (0=No effect, 1=Fewer logins after considering the form action, 2=No logins match form origin and action."
+  },
   "PWMGR_FORM_AUTOFILL_RESULT": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values" : 20,
     "description": "The result of auto-filling a login form. See http://mzl.la/1Mbs6jL for bucket descriptions."
   },
   "PWMGR_NUM_PASSWORDS_PER_HOSTNAME": {
     "expires_in_version": "never",