Backed out 4 changesets (bug 1436247) for c2 failures in dom/events/test/test_DataTransferItemList.html on a CLOSED TREE
authorMargareta Eliza Balazs <ebalazs@mozilla.com>
Tue, 13 Feb 2018 19:56:26 +0200
changeset 456161 f1e39e547b29ee6eb323ff5fe18c5084791c8da1
parent 456160 284c21b1d5e4c6ec9a2e91243e95aa50b462ca13
child 456162 6d15b284662e7fc9403a25e9260727885e8065d3
push id8799
push usermtabara@mozilla.com
push dateThu, 01 Mar 2018 16:46:23 +0000
treeherdermozilla-beta@15334014dc67 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1436247
milestone60.0a1
backs out858d629f761ddb77258cbb3e5c557df1ead5b2df
4ce2bfe462a08e004300a002fadb7d44e2e780ff
3650631487c779979d0b2c693864617d5351c516
c32ead4e35257a9067fa2fa73a96fafca4acdacd
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 4 changesets (bug 1436247) for c2 failures in dom/events/test/test_DataTransferItemList.html on a CLOSED TREE Backed out changeset 858d629f761d (bug 1436247) Backed out changeset 4ce2bfe462a0 (bug 1436247) Backed out changeset 3650631487c7 (bug 1436247) Backed out changeset c32ead4e3525 (bug 1436247)
gfx/thebes/gfxPrefs.h
image/DecodePool.cpp
image/DecodePool.h
image/Decoder.h
image/ImageOps.cpp
image/RasterImage.cpp
image/RasterImage.h
modules/libpref/init/all.js
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -532,17 +532,16 @@ private:
   DECL_GFX_PREF(Live, "image.mem.discardable",                 ImageMemDiscardable, bool, false);
   DECL_GFX_PREF(Once, "image.mem.animated.discardable",        ImageMemAnimatedDiscardable, bool, false);
   DECL_OVERRIDE_PREF(Live, "image.mem.shared",                 ImageMemShared, gfxPrefs::WebRenderAll());
   DECL_GFX_PREF(Once, "image.mem.surfacecache.discard_factor", ImageMemSurfaceCacheDiscardFactor, uint32_t, 1);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.max_size_kb",    ImageMemSurfaceCacheMaxSizeKB, uint32_t, 100 * 1024);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.min_expiration_ms", ImageMemSurfaceCacheMinExpirationMS, uint32_t, 60*1000);
   DECL_GFX_PREF(Once, "image.mem.surfacecache.size_factor",    ImageMemSurfaceCacheSizeFactor, uint32_t, 64);
   DECL_GFX_PREF(Once, "image.multithreaded_decoding.limit",    ImageMTDecodingLimit, int32_t, -1);
-  DECL_GFX_PREF(Once, "image.multithreaded_decoding.idle_timeout", ImageMTDecodingIdleTimeout, int32_t, -1);
 
   DECL_GFX_PREF(Once, "layers.acceleration.disabled",          LayersAccelerationDisabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps",          LayersDrawFPS, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.print-histogram",  FPSPrintHistogram, bool, false);
   DECL_GFX_PREF(Live, "layers.acceleration.draw-fps.write-to-file", WriteFPSToFile, bool, false);
   DECL_GFX_PREF(Once, "layers.acceleration.force-enabled",     LayersAccelerationForceEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Live, "layers.advanced.background-color",      LayersAllowBackgroundColorLayers, bool, false);
   DECL_OVERRIDE_PREF(Live, "layers.advanced.background-image",        LayersAllowBackgroundImage, gfxPrefs::OverrideBase_WebRender());
--- a/image/DecodePool.cpp
+++ b/image/DecodePool.cpp
@@ -1,20 +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/. */
 
 #include "DecodePool.h"
 
 #include <algorithm>
-#include <queue>
 
 #include "mozilla/ClearOnShutdown.h"
-#include "mozilla/DebugOnly.h"
 #include "mozilla/Monitor.h"
 #include "nsCOMPtr.h"
 #include "nsIObserverService.h"
 #include "nsIThreadPool.h"
 #include "nsThreadManager.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMCIDInternal.h"
 #include "prsystem.h"
@@ -52,281 +50,157 @@ struct Work
 };
 
 class DecodePoolImpl
 {
 public:
   MOZ_DECLARE_REFCOUNTED_TYPENAME(DecodePoolImpl)
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodePoolImpl)
 
-  DecodePoolImpl(uint8_t aMaxThreads,
-                 uint8_t aMaxIdleThreads,
-                 PRIntervalTime aIdleTimeout)
+  DecodePoolImpl()
     : mMonitor("DecodePoolImpl")
-    , mThreads(aMaxThreads)
-    , mIdleTimeout(aIdleTimeout)
-    , mMaxIdleThreads(aMaxIdleThreads)
-    , mAvailableThreads(aMaxThreads)
-    , mIdleThreads(0)
     , mShuttingDown(false)
-  {
-    MonitorAutoLock lock(mMonitor);
-    bool success = CreateThread();
-    MOZ_RELEASE_ASSERT(success, "Must create first image decoder thread!");
-  }
+  { }
 
   /// Shut down the provided decode pool thread.
-  void ShutdownThread(nsIThread* aThisThread, bool aShutdownIdle)
+  static void ShutdownThread(nsIThread* aThisThread)
   {
-    {
-      // If this is an idle thread shutdown, then we need to remove it from the
-      // worker array. Process shutdown will move the entire array.
-      MonitorAutoLock lock(mMonitor);
-      if (!mShuttingDown) {
-        ++mAvailableThreads;
-        DebugOnly<bool> removed = mThreads.RemoveElement(aThisThread);
-        MOZ_ASSERT(aShutdownIdle);
-        MOZ_ASSERT(mAvailableThreads < mThreads.Capacity());
-        MOZ_ASSERT(removed);
-      }
-    }
-
     // Threads have to be shut down from another thread, so we'll ask the
     // main thread to do it for us.
-    SystemGroup::Dispatch(TaskCategory::Other,
-                          NewRunnableMethod("DecodePoolImpl::ShutdownThread",
-                                            aThisThread, &nsIThread::Shutdown));
+    NS_DispatchToMainThread(NewRunnableMethod("DecodePoolImpl::ShutdownThread",
+                                              aThisThread, &nsIThread::Shutdown));
   }
 
   /**
    * Requests shutdown. New work items will be dropped on the floor, and all
    * decode pool threads will be shut down once existing work items have been
    * processed.
    */
-  void Shutdown()
+  void RequestShutdown()
   {
-    nsTArray<nsCOMPtr<nsIThread>> threads;
-
-    {
-      MonitorAutoLock lock(mMonitor);
-      mShuttingDown = true;
-      mAvailableThreads = 0;
-      threads.SwapElements(mThreads);
-      mMonitor.NotifyAll();
-    }
-
-    for (uint32_t i = 0 ; i < threads.Length() ; ++i) {
-      threads[i]->Shutdown();
-    }
+    MonitorAutoLock lock(mMonitor);
+    mShuttingDown = true;
+    mMonitor.NotifyAll();
   }
 
   /// Pushes a new decode work item.
   void PushWork(IDecodingTask* aTask)
   {
     MOZ_ASSERT(aTask);
     RefPtr<IDecodingTask> task(aTask);
 
     MonitorAutoLock lock(mMonitor);
 
     if (mShuttingDown) {
       // Drop any new work on the floor if we're shutting down.
       return;
     }
 
     if (task->Priority() == TaskPriority::eHigh) {
-      mHighPriorityQueue.push(Move(task));
+      mHighPriorityQueue.AppendElement(Move(task));
     } else {
-      mLowPriorityQueue.push(Move(task));
-    }
-
-    // If there are pending tasks, create more workers if and only if we have
-    // not exceeded the capacity, and any previously created workers are ready.
-    if (mAvailableThreads) {
-      size_t pending = mHighPriorityQueue.size() + mLowPriorityQueue.size();
-      if (pending > mIdleThreads) {
-        CreateThread();
-      }
+      mLowPriorityQueue.AppendElement(Move(task));
     }
 
     mMonitor.Notify();
   }
 
-  Work StartWork(bool aShutdownIdle)
+  /// Pops a new work item, blocking if necessary.
+  Work PopWork()
   {
     MonitorAutoLock lock(mMonitor);
 
-    // The thread was already marked as idle when it was created. Once it gets
-    // its first work item, it is assumed it is busy performing that work until
-    // it blocks on the monitor once again.
-    MOZ_ASSERT(mIdleThreads > 0);
-    --mIdleThreads;
-    return PopWorkLocked(aShutdownIdle);
-  }
-
-  Work PopWork(bool aShutdownIdle)
-  {
-    MonitorAutoLock lock(mMonitor);
-    return PopWorkLocked(aShutdownIdle);
-  }
-
-private:
-  /// Pops a new work item, blocking if necessary.
-  Work PopWorkLocked(bool aShutdownIdle)
-  {
-    mMonitor.AssertCurrentThreadOwns();
-
-    PRIntervalTime timeout = mIdleTimeout;
     do {
-      if (!mHighPriorityQueue.empty()) {
+      if (!mHighPriorityQueue.IsEmpty()) {
         return PopWorkFromQueue(mHighPriorityQueue);
       }
 
-      if (!mLowPriorityQueue.empty()) {
+      if (!mLowPriorityQueue.IsEmpty()) {
         return PopWorkFromQueue(mLowPriorityQueue);
       }
 
       if (mShuttingDown) {
-        return CreateShutdownWork();
+        Work work;
+        work.mType = Work::Type::SHUTDOWN;
+        return work;
       }
 
       // Nothing to do; block until some work is available.
-      if (!aShutdownIdle) {
-        // This thread was created before we hit the idle thread maximum. It
-        // will never shutdown until the process itself is torn down.
-        ++mIdleThreads;
-        MOZ_ASSERT(mIdleThreads <= mThreads.Capacity());
-        mMonitor.Wait();
-      } else {
-        // This thread should shutdown if it is idle. If we have waited longer
-        // than the timeout period without having done any work, then we should
-        // shutdown the thread.
-        if (timeout == 0) {
-          return CreateShutdownWork();
-        }
-
-        ++mIdleThreads;
-        MOZ_ASSERT(mIdleThreads <= mThreads.Capacity());
-
-        PRIntervalTime now = PR_IntervalNow();
-        mMonitor.Wait(timeout);
-        PRIntervalTime delta = PR_IntervalNow() - now;
-        if (delta > timeout) {
-          timeout = 0;
-        } else {
-          timeout -= delta;
-        }
-      }
-
-      MOZ_ASSERT(mIdleThreads > 0);
-      --mIdleThreads;
+      mMonitor.Wait();
     } while (true);
   }
 
+  nsresult CreateThread(nsIThread** aThread, nsIRunnable* aInitialEvent)
+  {
+    return NS_NewNamedThread(mThreadNaming.GetNextThreadName("ImgDecoder"),
+                             aThread, aInitialEvent);
+  }
+
+private:
   ~DecodePoolImpl() { }
 
-  bool CreateThread();
-
-  Work PopWorkFromQueue(std::queue<RefPtr<IDecodingTask>>& aQueue)
+  Work PopWorkFromQueue(nsTArray<RefPtr<IDecodingTask>>& aQueue)
   {
     Work work;
     work.mType = Work::Type::TASK;
-    work.mTask = aQueue.front().forget();
-    aQueue.pop();
-    return work;
-  }
+    work.mTask = aQueue.LastElement().forget();
+    aQueue.RemoveElementAt(aQueue.Length() - 1);
 
-  Work CreateShutdownWork() const
-  {
-    Work work;
-    work.mType = Work::Type::SHUTDOWN;
     return work;
   }
 
   nsThreadPoolNaming mThreadNaming;
 
-  // mMonitor guards everything below.
+  // mMonitor guards the queues and mShuttingDown.
   Monitor mMonitor;
-  std::queue<RefPtr<IDecodingTask>> mHighPriorityQueue;
-  std::queue<RefPtr<IDecodingTask>> mLowPriorityQueue;
-  nsTArray<nsCOMPtr<nsIThread>> mThreads;
-  PRIntervalTime mIdleTimeout;
-  uint8_t mMaxIdleThreads;   // Maximum number of workers when idle.
-  uint8_t mAvailableThreads; // How many new threads can be created.
-  uint8_t mIdleThreads; // How many created threads are waiting.
+  nsTArray<RefPtr<IDecodingTask>> mHighPriorityQueue;
+  nsTArray<RefPtr<IDecodingTask>> mLowPriorityQueue;
   bool mShuttingDown;
 };
 
-class DecodePoolWorker final : public Runnable
+class DecodePoolWorker : public Runnable
 {
 public:
-  explicit DecodePoolWorker(DecodePoolImpl* aImpl,
-                            bool aShutdownIdle)
+  explicit DecodePoolWorker(DecodePoolImpl* aImpl)
     : Runnable("image::DecodePoolWorker")
     , mImpl(aImpl)
-    , mShutdownIdle(aShutdownIdle)
   { }
 
   NS_IMETHOD Run() override
   {
     MOZ_ASSERT(!NS_IsMainThread());
 
     nsCOMPtr<nsIThread> thisThread;
     nsThreadManager::get().GetCurrentThread(getter_AddRefs(thisThread));
 
-    Work work = mImpl->StartWork(mShutdownIdle);
     do {
+      Work work = mImpl->PopWork();
       switch (work.mType) {
         case Work::Type::TASK:
           work.mTask->Run();
-          work.mTask = nullptr;
           break;
 
         case Work::Type::SHUTDOWN:
-          mImpl->ShutdownThread(thisThread, mShutdownIdle);
+          DecodePoolImpl::ShutdownThread(thisThread);
           PROFILER_UNREGISTER_THREAD();
           return NS_OK;
 
         default:
           MOZ_ASSERT_UNREACHABLE("Unknown work type");
       }
-
-      work = mImpl->PopWork(mShutdownIdle);
     } while (true);
 
     MOZ_ASSERT_UNREACHABLE("Exiting thread without Work::Type::SHUTDOWN");
     return NS_OK;
   }
 
 private:
   RefPtr<DecodePoolImpl> mImpl;
-  bool mShutdownIdle;
 };
 
-bool DecodePoolImpl::CreateThread()
-{
-  mMonitor.AssertCurrentThreadOwns();
-  MOZ_ASSERT(mAvailableThreads > 0);
-
-  bool shutdownIdle = mThreads.Length() >= mMaxIdleThreads;
-  nsCOMPtr<nsIRunnable> worker = new DecodePoolWorker(this, shutdownIdle);
-  nsCOMPtr<nsIThread> thread;
-  nsresult rv = NS_NewNamedThread(mThreadNaming.GetNextThreadName("ImgDecoder"),
-                                  getter_AddRefs(thread), worker);
-  if (NS_FAILED(rv) || !thread) {
-    MOZ_ASSERT_UNREACHABLE("Should successfully create image decoding threads");
-    return false;
-  }
-
-  mThreads.AppendElement(Move(thread));
-  --mAvailableThreads;
-  ++mIdleThreads;
-  MOZ_ASSERT(mIdleThreads <= mThreads.Capacity());
-  return true;
-}
-
 /* static */ void
 DecodePool::Initialize()
 {
   MOZ_ASSERT(NS_IsMainThread());
   sNumCores = max<int32_t>(PR_GetNumberOfProcessors(), 1);
   DecodePool::Singleton();
 }
 
@@ -344,17 +218,18 @@ DecodePool::Singleton()
 
 /* static */ uint32_t
 DecodePool::NumberOfCores()
 {
   return sNumCores;
 }
 
 DecodePool::DecodePool()
-  : mMutex("image::DecodePool")
+  : mImpl(new DecodePoolImpl)
+  , mMutex("image::DecodePool")
 {
   // Determine the number of threads we want.
   int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
   uint32_t limit;
   if (prefLimit <= 0) {
     int32_t numCores = NumberOfCores();
     if (numCores <= 1) {
       limit = 1;
@@ -373,33 +248,26 @@ DecodePool::DecodePool()
     limit = 32;
   }
   // The parent process where there are content processes doesn't need as many
   // threads for decoding images.
   if (limit > 4 && XRE_IsE10sParentProcess()) {
     limit = 4;
   }
 
-  // The maximum number of idle threads allowed.
-  uint32_t idleLimit;
-
-  // The timeout period before shutting down idle threads.
-  int32_t prefIdleTimeout = gfxPrefs::ImageMTDecodingIdleTimeout();
-  PRIntervalTime idleTimeout;
-  if (prefIdleTimeout <= 0) {
-    idleTimeout = PR_INTERVAL_NO_TIMEOUT;
-    idleLimit = limit;
-  } else {
-    idleTimeout = PR_MillisecondsToInterval(static_cast<uint32_t>(prefIdleTimeout));
-    idleLimit = (limit + 1) / 2;
+  // Initialize the thread pool.
+  for (uint32_t i = 0 ; i < limit ; ++i) {
+    nsCOMPtr<nsIRunnable> worker = new DecodePoolWorker(mImpl);
+    nsCOMPtr<nsIThread> thread;
+    nsresult rv = mImpl->CreateThread(getter_AddRefs(thread), worker);
+    MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && thread,
+                       "Should successfully create image decoding threads");
+    mThreads.AppendElement(Move(thread));
   }
 
-  // Initialize the thread pool.
-  mImpl = new DecodePoolImpl(limit, idleLimit, idleTimeout);
-
   // Initialize the I/O thread.
   nsresult rv = NS_NewNamedThread("ImageIO", getter_AddRefs(mIOThread));
   MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mIOThread,
                      "Should successfully create image I/O thread");
 
   nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
   if (obsSvc) {
     obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
@@ -411,24 +279,30 @@ DecodePool::~DecodePool()
   MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
 }
 
 NS_IMETHODIMP
 DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*)
 {
   MOZ_ASSERT(strcmp(aTopic, "xpcom-shutdown-threads") == 0, "Unexpected topic");
 
+  nsTArray<nsCOMPtr<nsIThread>> threads;
   nsCOMPtr<nsIThread> ioThread;
 
   {
     MutexAutoLock lock(mMutex);
+    threads.SwapElements(mThreads);
     ioThread.swap(mIOThread);
   }
 
-  mImpl->Shutdown();
+  mImpl->RequestShutdown();
+
+  for (uint32_t i = 0 ; i < threads.Length() ; ++i) {
+    threads[i]->Shutdown();
+  }
 
   if (ioThread) {
     ioThread->Shutdown();
   }
 
   return NS_OK;
 }
 
--- a/image/DecodePool.h
+++ b/image/DecodePool.h
@@ -34,17 +34,17 @@ class IDecodingTask;
  * owns a pool of image decoding threads that are used for asynchronous
  * decoding.
  *
  * DecodePool allows callers to run a decoder, handling management of the
  * decoder's lifecycle and whether it executes on the main thread,
  * off-main-thread in the image decoding thread pool, or on some combination of
  * the two.
  */
-class DecodePool final : public nsIObserver
+class DecodePool : public nsIObserver
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIOBSERVER
 
   /// Initializes the singleton instance. Should be called from the main thread.
   static void Initialize();
 
@@ -90,17 +90,18 @@ private:
   DecodePool();
   virtual ~DecodePool();
 
   static StaticRefPtr<DecodePool> sSingleton;
   static uint32_t sNumCores;
 
   RefPtr<DecodePoolImpl> mImpl;
 
-  // mMutex protects mIOThread.
+  // mMutex protects mThreads and mIOThread.
   Mutex                         mMutex;
+  nsTArray<nsCOMPtr<nsIThread>> mThreads;
   nsCOMPtr<nsIThread>           mIOThread;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_DecodePool_h
--- a/image/Decoder.h
+++ b/image/Decoder.h
@@ -6,16 +6,17 @@
 #ifndef mozilla_image_Decoder_h
 #define mozilla_image_Decoder_h
 
 #include "FrameAnimator.h"
 #include "RasterImage.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/NotNull.h"
 #include "mozilla/RefPtr.h"
+#include "DecodePool.h"
 #include "DecoderFlags.h"
 #include "Downscaler.h"
 #include "ImageMetadata.h"
 #include "Orientation.h"
 #include "SourceBuffer.h"
 #include "StreamingLexer.h"
 #include "SurfaceFlags.h"
 
--- a/image/ImageOps.cpp
+++ b/image/ImageOps.cpp
@@ -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/. */
 
 #include "ImageOps.h"
 
 #include "ClippedImage.h"
+#include "DecodePool.h"
 #include "Decoder.h"
 #include "DecoderFactory.h"
 #include "DynamicImage.h"
 #include "FrozenImage.h"
 #include "IDecodingTask.h"
 #include "Image.h"
 #include "ImageMetadata.h"
 #include "imgIContainer.h"
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -7,17 +7,16 @@
 // that #include prlog.h
 #include "ImageLogging.h"
 
 #include "RasterImage.h"
 
 #include "gfxPlatform.h"
 #include "nsComponentManagerUtils.h"
 #include "nsError.h"
-#include "DecodePool.h"
 #include "Decoder.h"
 #include "prenv.h"
 #include "prsystem.h"
 #include "IDecodingTask.h"
 #include "ImageRegion.h"
 #include "Layers.h"
 #include "LookupResult.h"
 #include "nsIConsoleService.h"
--- a/image/RasterImage.h
+++ b/image/RasterImage.h
@@ -19,16 +19,17 @@
 
 #include "Image.h"
 #include "nsCOMPtr.h"
 #include "imgIContainer.h"
 #include "nsIProperties.h"
 #include "nsTArray.h"
 #include "LookupResult.h"
 #include "nsThreadUtils.h"
+#include "DecodePool.h"
 #include "DecoderFactory.h"
 #include "FrameAnimator.h"
 #include "ImageMetadata.h"
 #include "ISurfaceProvider.h"
 #include "Orientation.h"
 #include "nsIObserver.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Maybe.h"
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4756,19 +4756,16 @@ pref("image.mem.surfacecache.size_factor
 // of the data, and so forth. The default should be a good balance for desktop
 // and laptop systems, where we never discard visible images.
 pref("image.mem.surfacecache.discard_factor", 1);
 
 // How many threads we'll use for multithreaded decoding. If < 0, will be
 // automatically determined based on the system's number of cores.
 pref("image.multithreaded_decoding.limit", -1);
 
-// How long in ms before we should start shutting down idle decoder threads.
-pref("image.multithreaded_decoding.idle_timeout", 600000);
-
 // Limit for the canvas image cache. 0 means we don't limit the size of the
 // cache.
 pref("canvas.image.cache.limit", 0);
 
 // WebGL prefs
 #ifdef ANDROID
 // Disable MSAA on mobile.
 pref("gl.msaa-level", 0);