Bug 709490 - Part 2: Introduce OffscreenCanvas and let WebGL context work on workers., r=ehsan, r=jgilbert, r=nical
☠☠ backed out by c4f66a050ed0 ☠ ☠
authorMorris Tseng <mtseng@mozilla.com>
Tue, 29 Sep 2015 11:51:24 +0100
changeset 286127 2ae1386916b3fe46a30ffcc45bb67c6dc2a48905
parent 286126 6b29a2a0a8fb4a385504ab0507ad34ee986d6c7f
child 286128 a5f8646fa1569c2480575b5d5d6cc82c9a8aea7b
push id8654
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:48:40 +0000
treeherdermozilla-aurora@bc4551debe17 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersehsan, jgilbert, nical
bugs709490
milestone44.0a1
Bug 709490 - Part 2: Introduce OffscreenCanvas and let WebGL context work on workers., r=ehsan, r=jgilbert, r=nical
dom/canvas/CanvasRenderingContextHelper.cpp
dom/canvas/CanvasRenderingContextHelper.h
dom/canvas/CanvasUtils.cpp
dom/canvas/CanvasUtils.h
dom/canvas/OffscreenCanvas.cpp
dom/canvas/OffscreenCanvas.h
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextExtensions.cpp
dom/canvas/WebGLContextGL.cpp
dom/canvas/WebGLContextLossHandler.cpp
dom/canvas/WebGLContextLossHandler.h
dom/canvas/WebGLContextReporter.cpp
dom/canvas/WebGLContextValidate.cpp
dom/canvas/WebGLMemoryTracker.cpp
dom/canvas/WebGLShaderValidator.cpp
dom/canvas/moz.build
dom/canvas/nsICanvasRenderingContextInternal.h
dom/html/HTMLCanvasElement.cpp
dom/html/HTMLCanvasElement.h
dom/webidl/HTMLCanvasElement.webidl
dom/webidl/OffscreenCanvas.webidl
dom/webidl/WebGLRenderingContext.webidl
dom/webidl/moz.build
gfx/gl/GLLibraryEGL.cpp
gfx/layers/AsyncCanvasRenderer.h
gfx/src/gfxCrashReporterUtils.cpp
gfx/thebes/gfxPrefs.h
gfx/thebes/gfxUtils.cpp
gfx/thebes/gfxUtils.h
gfx/thebes/moz.build
modules/libpref/init/all.js
new file mode 100644
--- /dev/null
+++ b/dom/canvas/CanvasRenderingContextHelper.cpp
@@ -0,0 +1,264 @@
+/* -*- 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 "CanvasRenderingContextHelper.h"
+#include "ImageEncoder.h"
+#include "mozilla/dom/CanvasRenderingContext2D.h"
+#include "mozilla/Telemetry.h"
+#include "nsContentUtils.h"
+#include "nsDOMJSUtils.h"
+#include "nsIScriptContext.h"
+#include "nsJSUtils.h"
+#include "WebGL1Context.h"
+#include "WebGL2Context.h"
+
+namespace mozilla {
+namespace dom {
+
+void
+CanvasRenderingContextHelper::ToBlob(JSContext* aCx,
+                                     nsIGlobalObject* aGlobal,
+                                     FileCallback& aCallback,
+                                     const nsAString& aType,
+                                     JS::Handle<JS::Value> aParams,
+                                     ErrorResult& aRv)
+{
+  nsAutoString type;
+  nsContentUtils::ASCIIToLower(aType, type);
+
+  nsAutoString params;
+  bool usingCustomParseOptions;
+  aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
+  if (aRv.Failed()) {
+    return;
+  }
+
+#ifdef DEBUG
+  if (mCurrentContext) {
+    // We disallow canvases of width or height zero, and set them to 1, so
+    // we will have a discrepancy with the sizes of the canvas and the context.
+    // That discrepancy is OK, the rest are not.
+    nsIntSize elementSize = GetWidthHeight();
+    MOZ_ASSERT(elementSize.width == mCurrentContext->GetWidth() ||
+               (elementSize.width == 0 && mCurrentContext->GetWidth() == 1));
+    MOZ_ASSERT(elementSize.height == mCurrentContext->GetHeight() ||
+               (elementSize.height == 0 && mCurrentContext->GetHeight() == 1));
+  }
+#endif
+
+  uint8_t* imageBuffer = nullptr;
+  int32_t format = 0;
+  if (mCurrentContext) {
+    mCurrentContext->GetImageBuffer(&imageBuffer, &format);
+  }
+
+  // Encoder callback when encoding is complete.
+  class EncodeCallback : public EncodeCompleteCallback
+  {
+  public:
+    EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback)
+      : mGlobal(aGlobal)
+      , mFileCallback(aCallback) {}
+
+    // This is called on main thread.
+    nsresult ReceiveBlob(already_AddRefed<Blob> aBlob)
+    {
+      nsRefPtr<Blob> blob = aBlob;
+
+      ErrorResult rv;
+      uint64_t size = blob->GetSize(rv);
+      if (rv.Failed()) {
+        rv.SuppressException();
+      } else {
+        AutoJSAPI jsapi;
+        if (jsapi.Init(mGlobal)) {
+          JS_updateMallocCounter(jsapi.cx(), size);
+        }
+      }
+
+      nsRefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
+
+      mFileCallback->Call(*newBlob, rv);
+
+      mGlobal = nullptr;
+      mFileCallback = nullptr;
+
+      return rv.StealNSResult();
+    }
+
+    nsCOMPtr<nsIGlobalObject> mGlobal;
+    nsRefPtr<FileCallback> mFileCallback;
+  };
+
+  nsRefPtr<EncodeCompleteCallback> callback =
+    new EncodeCallback(aGlobal, &aCallback);
+
+  aRv = ImageEncoder::ExtractDataAsync(type,
+                                       params,
+                                       usingCustomParseOptions,
+                                       imageBuffer,
+                                       format,
+                                       GetWidthHeight(),
+                                       callback);
+}
+
+already_AddRefed<nsICanvasRenderingContextInternal>
+CanvasRenderingContextHelper::CreateContext(CanvasContextType aContextType)
+{
+  MOZ_ASSERT(aContextType != CanvasContextType::NoContext);
+  nsRefPtr<nsICanvasRenderingContextInternal> ret;
+
+  switch (aContextType) {
+  case CanvasContextType::NoContext:
+    break;
+
+  case CanvasContextType::Canvas2D:
+    Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
+    ret = new CanvasRenderingContext2D();
+    break;
+
+  case CanvasContextType::WebGL1:
+    Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
+
+    ret = WebGL1Context::Create();
+    if (!ret)
+      return nullptr;
+
+    break;
+
+  case CanvasContextType::WebGL2:
+    Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
+
+    ret = WebGL2Context::Create();
+    if (!ret)
+      return nullptr;
+
+    break;
+  }
+  MOZ_ASSERT(ret);
+
+  return ret.forget();
+}
+
+already_AddRefed<nsISupports>
+CanvasRenderingContextHelper::GetContext(JSContext* aCx,
+                                         const nsAString& aContextId,
+                                         JS::Handle<JS::Value> aContextOptions,
+                                         ErrorResult& aRv)
+{
+  CanvasContextType contextType;
+  if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType))
+    return nullptr;
+
+  if (!mCurrentContext) {
+    // This canvas doesn't have a context yet.
+    nsRefPtr<nsICanvasRenderingContextInternal> context;
+    context = CreateContext(contextType);
+    if (!context) {
+      return nullptr;
+    }
+
+    // Ensure that the context participates in CC.  Note that returning a
+    // CC participant from QI doesn't addref.
+    nsXPCOMCycleCollectionParticipant* cp = nullptr;
+    CallQueryInterface(context, &cp);
+    if (!cp) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+
+    mCurrentContext = context.forget();
+    mCurrentContextType = contextType;
+
+    aRv = UpdateContext(aCx, aContextOptions);
+    if (aRv.Failed()) {
+      aRv = NS_OK; // See bug 645792
+      return nullptr;
+    }
+  } else {
+    // We already have a context of some type.
+    if (contextType != mCurrentContextType)
+      return nullptr;
+  }
+
+  nsCOMPtr<nsICanvasRenderingContextInternal> context = mCurrentContext;
+  return context.forget();
+}
+
+nsresult
+CanvasRenderingContextHelper::UpdateContext(JSContext* aCx,
+                                            JS::Handle<JS::Value> aNewContextOptions)
+{
+  if (!mCurrentContext)
+    return NS_OK;
+
+  nsIntSize sz = GetWidthHeight();
+
+  nsCOMPtr<nsICanvasRenderingContextInternal> currentContext = mCurrentContext;
+
+  nsresult rv = currentContext->SetIsOpaque(GetOpaqueAttr());
+  if (NS_FAILED(rv)) {
+    mCurrentContext = nullptr;
+    return rv;
+  }
+
+  rv = currentContext->SetContextOptions(aCx, aNewContextOptions);
+  if (NS_FAILED(rv)) {
+    mCurrentContext = nullptr;
+    return rv;
+  }
+
+  rv = currentContext->SetDimensions(sz.width, sz.height);
+  if (NS_FAILED(rv)) {
+    mCurrentContext = nullptr;
+  }
+
+  return rv;
+}
+
+nsresult
+CanvasRenderingContextHelper::ParseParams(JSContext* aCx,
+                                          const nsAString& aType,
+                                          const JS::Value& aEncoderOptions,
+                                          nsAString& outParams,
+                                          bool* const outUsingCustomParseOptions)
+{
+  // Quality parameter is only valid for the image/jpeg MIME type
+  if (aType.EqualsLiteral("image/jpeg")) {
+    if (aEncoderOptions.isNumber()) {
+      double quality = aEncoderOptions.toNumber();
+      // Quality must be between 0.0 and 1.0, inclusive
+      if (quality >= 0.0 && quality <= 1.0) {
+        outParams.AppendLiteral("quality=");
+        outParams.AppendInt(NS_lround(quality * 100.0));
+      }
+    }
+  }
+
+  // If we haven't parsed the aParams check for proprietary options.
+  // The proprietary option -moz-parse-options will take a image lib encoder
+  // parse options string as is and pass it to the encoder.
+  *outUsingCustomParseOptions = false;
+  if (outParams.Length() == 0 && aEncoderOptions.isString()) {
+    NS_NAMED_LITERAL_STRING(mozParseOptions, "-moz-parse-options:");
+    nsAutoJSString paramString;
+    if (!paramString.init(aCx, aEncoderOptions.toString())) {
+      return NS_ERROR_FAILURE;
+    }
+    if (StringBeginsWith(paramString, mozParseOptions)) {
+      nsDependentSubstring parseOptions = Substring(paramString,
+                                                    mozParseOptions.Length(),
+                                                    paramString.Length() -
+                                                    mozParseOptions.Length());
+      outParams.Append(parseOptions);
+      *outUsingCustomParseOptions = true;
+    }
+  }
+
+  return NS_OK;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/CanvasRenderingContextHelper.h
@@ -0,0 +1,71 @@
+/* -*- 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 MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
+#define MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
+
+#include "mozilla/dom/BindingDeclarations.h"
+#include "nsSize.h"
+
+class nsICanvasRenderingContextInternal;
+class nsIGlobalObject;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+class FileCallback;
+
+enum class CanvasContextType : uint8_t {
+  NoContext,
+  Canvas2D,
+  WebGL1,
+  WebGL2
+};
+
+/**
+ * Povides common RenderingContext functionality used by both OffscreenCanvas
+ * and HTMLCanvasElement.
+ */
+class CanvasRenderingContextHelper
+{
+public:
+  virtual already_AddRefed<nsISupports>
+  GetContext(JSContext* aCx,
+             const nsAString& aContextId,
+             JS::Handle<JS::Value> aContextOptions,
+             ErrorResult& aRv);
+
+  virtual bool GetOpaqueAttr() = 0;
+
+protected:
+  virtual nsresult UpdateContext(JSContext* aCx,
+                                 JS::Handle<JS::Value> aNewContextOptions);
+
+  virtual nsresult ParseParams(JSContext* aCx,
+                               const nsAString& aType,
+                               const JS::Value& aEncoderOptions,
+                               nsAString& outParams,
+                               bool* const outCustomParseOptions);
+
+  void ToBlob(JSContext* aCx, nsIGlobalObject* global, FileCallback& aCallback,
+              const nsAString& aType, JS::Handle<JS::Value> aParams,
+              ErrorResult& aRv);
+
+  virtual already_AddRefed<nsICanvasRenderingContextInternal>
+  CreateContext(CanvasContextType aContextType);
+
+  virtual nsIntSize GetWidthHeight() = 0;
+
+  CanvasContextType mCurrentContextType;
+  nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // MOZILLA_DOM_CANVASRENDERINGCONTEXTHELPER_H_
--- a/dom/canvas/CanvasUtils.cpp
+++ b/dom/canvas/CanvasUtils.cpp
@@ -18,22 +18,57 @@
 #include "nsIPrincipal.h"
 
 #include "nsGfxCIID.h"
 
 #include "nsTArray.h"
 
 #include "CanvasUtils.h"
 #include "mozilla/gfx/Matrix.h"
+#include "WebGL2Context.h"
 
 using namespace mozilla::gfx;
 
 namespace mozilla {
 namespace CanvasUtils {
 
+bool
+GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type)
+{
+  if (str.EqualsLiteral("2d")) {
+    *out_type = dom::CanvasContextType::Canvas2D;
+    return true;
+  }
+
+  if (str.EqualsLiteral("experimental-webgl")) {
+    *out_type = dom::CanvasContextType::WebGL1;
+    return true;
+  }
+
+#ifdef MOZ_WEBGL_CONFORMANT
+  if (str.EqualsLiteral("webgl")) {
+    /* WebGL 1.0, $2.1 "Context Creation":
+     *   If the user agent supports both the webgl and experimental-webgl
+     *   canvas context types, they shall be treated as aliases.
+     */
+    *out_type = dom::CanvasContextType::WebGL1;
+    return true;
+  }
+#endif
+
+  if (WebGL2Context::IsSupported()) {
+    if (str.EqualsLiteral("webgl2")) {
+      *out_type = dom::CanvasContextType::WebGL2;
+      return true;
+    }
+  }
+
+  return false;
+}
+
 /**
  * This security check utility might be called from an source that never taints
  * others. For example, while painting a CanvasPattern, which is created from an
  * ImageBitmap, onto a canvas. In this case, the caller could set the CORSUsed
  * true in order to pass this check and leave the aPrincipal to be a nullptr
  * since the aPrincipal is not going to be used.
  */
 void
--- a/dom/canvas/CanvasUtils.h
+++ b/dom/canvas/CanvasUtils.h
@@ -16,16 +16,17 @@ class nsIPrincipal;
 namespace mozilla {
 
 namespace dom {
 class HTMLCanvasElement;
 } // namespace dom
 
 namespace CanvasUtils {
 
+bool GetCanvasContextType(const nsAString& str, dom::CanvasContextType* const out_type);
 
 // Check that the rectangle [x,y,w,h] is a subrectangle of [0,0,realWidth,realHeight]
 
 inline bool CheckSaneSubrectSize(int32_t x, int32_t y, int32_t w, int32_t h,
                             int32_t realWidth, int32_t realHeight) {
     CheckedInt32 checked_xmost  = CheckedInt32(x) + w;
     CheckedInt32 checked_ymost  = CheckedInt32(y) + h;
 
new file mode 100644
--- /dev/null
+++ b/dom/canvas/OffscreenCanvas.cpp
@@ -0,0 +1,210 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 "OffscreenCanvas.h"
+
+#include "mozilla/dom/OffscreenCanvasBinding.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/layers/AsyncCanvasRenderer.h"
+#include "mozilla/layers/CanvasClient.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/Telemetry.h"
+#include "CanvasRenderingContext2D.h"
+#include "CanvasUtils.h"
+#include "GLScreenBuffer.h"
+#include "WebGL1Context.h"
+#include "WebGL2Context.h"
+
+namespace mozilla {
+namespace dom {
+
+OffscreenCanvasCloneData::OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
+                                                   uint32_t aWidth, uint32_t aHeight,
+                                                   bool aNeutered)
+  : mRenderer(aRenderer)
+  , mWidth(aWidth)
+  , mHeight(aHeight)
+  , mNeutered(aNeutered)
+{
+}
+
+OffscreenCanvasCloneData::~OffscreenCanvasCloneData()
+{
+}
+
+OffscreenCanvas::OffscreenCanvas(uint32_t aWidth,
+                                 uint32_t aHeight,
+                                 layers::AsyncCanvasRenderer* aRenderer)
+  : mAttrDirty(false)
+  , mNeutered(false)
+  , mWidth(aWidth)
+  , mHeight(aHeight)
+  , mCanvasClient(nullptr)
+  , mCanvasRenderer(aRenderer)
+{}
+
+OffscreenCanvas::~OffscreenCanvas()
+{
+  if (mCanvasRenderer) {
+    mCanvasRenderer->SetCanvasClient(nullptr);
+    mCanvasRenderer->mContext = nullptr;
+    mCanvasRenderer->mActiveThread = nullptr;
+  }
+
+  if (mCanvasClient) {
+    ImageBridgeChild::DispatchReleaseCanvasClient(mCanvasClient);
+  }
+}
+
+OffscreenCanvas*
+OffscreenCanvas::GetParentObject() const
+{
+  return nullptr;
+}
+
+JSObject*
+OffscreenCanvas::WrapObject(JSContext* aCx,
+                            JS::Handle<JSObject*> aGivenProto)
+{
+  return OffscreenCanvasBinding::Wrap(aCx, this, aGivenProto);
+}
+
+already_AddRefed<nsISupports>
+OffscreenCanvas::GetContext(JSContext* aCx,
+                            const nsAString& aContextId,
+                            JS::Handle<JS::Value> aContextOptions,
+                            ErrorResult& aRv)
+{
+  if (mNeutered) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  // We only support WebGL in workers for now
+  CanvasContextType contextType;
+  if (!CanvasUtils::GetCanvasContextType(aContextId, &contextType)) {
+    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+    return nullptr;
+  }
+
+  if (!(contextType == CanvasContextType::WebGL1 ||
+        contextType == CanvasContextType::WebGL2))
+  {
+    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+    return nullptr;
+  }
+
+  already_AddRefed<nsISupports> result =
+    CanvasRenderingContextHelper::GetContext(aCx,
+                                             aContextId,
+                                             aContextOptions,
+                                             aRv);
+
+  if (mCanvasRenderer && mCurrentContext && ImageBridgeChild::IsCreated()) {
+    TextureFlags flags = TextureFlags::ORIGIN_BOTTOM_LEFT;
+
+    mCanvasClient = ImageBridgeChild::GetSingleton()->
+      CreateCanvasClient(CanvasClient::CanvasClientTypeShSurf, flags).take();
+    mCanvasRenderer->SetCanvasClient(mCanvasClient);
+    gl::GLContext* gl = static_cast<WebGLContext*>(mCurrentContext.get())->GL();
+    mCanvasRenderer->mContext = mCurrentContext;
+    mCanvasRenderer->mActiveThread = NS_GetCurrentThread();
+    mCanvasRenderer->mGLContext = gl;
+
+    gl::GLScreenBuffer* screen = gl->Screen();
+    gl::SurfaceCaps caps = screen->mCaps;
+    auto forwarder = mCanvasClient->GetForwarder();
+
+    UniquePtr<gl::SurfaceFactory> factory =
+      gl::GLScreenBuffer::CreateFactory(gl, caps, forwarder, flags);
+
+    if (factory)
+      screen->Morph(Move(factory));
+  }
+
+  return result;
+}
+
+already_AddRefed<nsICanvasRenderingContextInternal>
+OffscreenCanvas::CreateContext(CanvasContextType aContextType)
+{
+  nsRefPtr<nsICanvasRenderingContextInternal> ret =
+    CanvasRenderingContextHelper::CreateContext(aContextType);
+
+  ret->SetOffscreenCanvas(this);
+  return ret.forget();
+}
+
+void
+OffscreenCanvas::CommitFrameToCompositor()
+{
+  // The attributes has changed, we have to notify main
+  // thread to change canvas size.
+  if (mAttrDirty) {
+    if (mCanvasRenderer) {
+      mCanvasRenderer->SetWidth(mWidth);
+      mCanvasRenderer->SetHeight(mHeight);
+      mCanvasRenderer->NotifyElementAboutAttributesChanged();
+    }
+    mAttrDirty = false;
+  }
+
+  if (mCurrentContext) {
+    static_cast<WebGLContext*>(mCurrentContext.get())->PresentScreenBuffer();
+  }
+
+  if (mCanvasRenderer && mCanvasRenderer->mGLContext) {
+    ImageBridgeChild::GetSingleton()->
+      UpdateAsyncCanvasRenderer(mCanvasRenderer);
+  }
+}
+
+OffscreenCanvasCloneData*
+OffscreenCanvas::ToCloneData()
+{
+  return new OffscreenCanvasCloneData(mCanvasRenderer, mWidth,
+                                      mHeight, mNeutered);
+}
+
+/* static */ already_AddRefed<OffscreenCanvas>
+OffscreenCanvas::CreateFromCloneData(OffscreenCanvasCloneData* aData)
+{
+  MOZ_ASSERT(aData);
+  nsRefPtr<OffscreenCanvas> wc =
+    new OffscreenCanvas(aData->mWidth, aData->mHeight, aData->mRenderer);
+  if (aData->mNeutered) {
+    wc->SetNeutered();
+  }
+  return wc.forget();
+}
+
+/* static */ bool
+OffscreenCanvas::PrefEnabled(JSContext* aCx, JSObject* aObj)
+{
+  return gfxPrefs::OffscreenCanvasEnabled();
+}
+
+/* static */ bool
+OffscreenCanvas::PrefEnabledOnWorkerThread(JSContext* aCx, JSObject* aObj)
+{
+  if (NS_IsMainThread()) {
+    return true;
+  }
+
+  return PrefEnabled(aCx, aObj);
+}
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(OffscreenCanvas, DOMEventTargetHelper, mCurrentContext)
+
+NS_IMPL_ADDREF_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(OffscreenCanvas)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/OffscreenCanvas.h
@@ -0,0 +1,166 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 MOZILLA_DOM_OFFSCREENCANVAS_H_
+#define MOZILLA_DOM_OFFSCREENCANVAS_H_
+
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/RefPtr.h"
+#include "CanvasRenderingContextHelper.h"
+#include "nsCycleCollectionParticipant.h"
+
+struct JSContext;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace layers {
+class AsyncCanvasRenderer;
+class CanvasClient;
+} // namespace layers
+
+namespace dom {
+
+// This is helper class for transferring OffscreenCanvas to worker thread.
+// Because OffscreenCanvas is not thread-safe. So we cannot pass Offscreen-
+// Canvas to worker thread directly. Thus, we create this helper class and
+// store necessary data in it then pass it to worker thread.
+struct OffscreenCanvasCloneData final
+{
+  OffscreenCanvasCloneData(layers::AsyncCanvasRenderer* aRenderer,
+                           uint32_t aWidth, uint32_t aHeight,
+                           bool aNeutered);
+  ~OffscreenCanvasCloneData();
+
+  RefPtr<layers::AsyncCanvasRenderer> mRenderer;
+  uint32_t mWidth;
+  uint32_t mHeight;
+  bool mNeutered;
+};
+
+class OffscreenCanvas final : public DOMEventTargetHelper
+                            , public CanvasRenderingContextHelper
+{
+public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(OffscreenCanvas, DOMEventTargetHelper)
+
+  OffscreenCanvas(uint32_t aWidth,
+                  uint32_t aHeight,
+                  layers::AsyncCanvasRenderer* aRenderer);
+
+  OffscreenCanvas* GetParentObject() const;
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  uint32_t Width() const
+  {
+    return mWidth;
+  }
+
+  uint32_t Height() const
+  {
+    return mHeight;
+  }
+
+  void SetWidth(uint32_t aWidth, ErrorResult& aRv)
+  {
+    if (mNeutered) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    if (mWidth != aWidth) {
+      mWidth = aWidth;
+      CanvasAttrChanged();
+    }
+  }
+
+  void SetHeight(uint32_t aHeight, ErrorResult& aRv)
+  {
+    if (mNeutered) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
+    if (mHeight != aHeight) {
+      mHeight = aHeight;
+      CanvasAttrChanged();
+    }
+  }
+
+  nsICanvasRenderingContextInternal* GetContext() const
+  {
+    return mCurrentContext;
+  }
+
+  static already_AddRefed<OffscreenCanvas>
+  CreateFromCloneData(OffscreenCanvasCloneData* aData);
+
+  static bool PrefEnabled(JSContext* aCx, JSObject* aObj);
+
+  // Return true on main-thread, and return gfx.offscreencanvas.enabled
+  // on worker thread.
+  static bool PrefEnabledOnWorkerThread(JSContext* aCx, JSObject* aObj);
+
+  OffscreenCanvasCloneData* ToCloneData();
+
+  void CommitFrameToCompositor();
+
+  virtual bool GetOpaqueAttr() override
+  {
+    return false;
+  }
+
+  virtual nsIntSize GetWidthHeight() override
+  {
+    return nsIntSize(mWidth, mHeight);
+  }
+
+  virtual already_AddRefed<nsICanvasRenderingContextInternal>
+  CreateContext(CanvasContextType aContextType) override;
+
+  virtual already_AddRefed<nsISupports>
+  GetContext(JSContext* aCx,
+             const nsAString& aContextId,
+             JS::Handle<JS::Value> aContextOptions,
+             ErrorResult& aRv) override;
+
+  void SetNeutered()
+  {
+    mNeutered = true;
+  }
+
+  bool IsNeutered() const
+  {
+    return mNeutered;
+  }
+
+private:
+  ~OffscreenCanvas();
+
+  void CanvasAttrChanged()
+  {
+    mAttrDirty = true;
+    UpdateContext(nullptr, JS::NullHandleValue);
+  }
+
+  bool mAttrDirty;
+  bool mNeutered;
+
+  uint32_t mWidth;
+  uint32_t mHeight;
+
+  layers::CanvasClient* mCanvasClient;
+  RefPtr<layers::AsyncCanvasRenderer> mCanvasRenderer;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // MOZILLA_DOM_OFFSCREENCANVAS_H_
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -17,16 +17,17 @@
 #include "GLContext.h"
 #include "GLContextProvider.h"
 #include "GLReadTexImageHelper.h"
 #include "GLScreenBuffer.h"
 #include "ImageContainer.h"
 #include "ImageEncoder.h"
 #include "Layers.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/Event.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/ImageData.h"
 #include "mozilla/EnumeratedArrayCycleCollection.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessPriorityManager.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "nsContentUtils.h"
@@ -74,146 +75,27 @@
 
 namespace mozilla {
 
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::gl;
 using namespace mozilla::layers;
 
-WebGLObserver::WebGLObserver(WebGLContext* webgl)
-    : mWebGL(webgl)
-{
-}
-
-WebGLObserver::~WebGLObserver()
-{
-}
-
-void
-WebGLObserver::Destroy()
-{
-    UnregisterMemoryPressureEvent();
-    UnregisterVisibilityChangeEvent();
-    mWebGL = nullptr;
-}
-
-void
-WebGLObserver::RegisterVisibilityChangeEvent()
-{
-    if (!mWebGL)
-        return;
-
-    HTMLCanvasElement* canvas = mWebGL->GetCanvas();
-    MOZ_ASSERT(canvas);
-
-    if (canvas) {
-        nsIDocument* document = canvas->OwnerDoc();
-
-        document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
-                                         this, true, false);
-    }
-}
-
-void
-WebGLObserver::UnregisterVisibilityChangeEvent()
-{
-    if (!mWebGL)
-        return;
-
-    HTMLCanvasElement* canvas = mWebGL->GetCanvas();
-
-    if (canvas) {
-        nsIDocument* document = canvas->OwnerDoc();
-
-        document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
-                                            this, true);
-    }
-}
-
-void
-WebGLObserver::RegisterMemoryPressureEvent()
-{
-    if (!mWebGL)
-        return;
-
-    nsCOMPtr<nsIObserverService> observerService =
-        mozilla::services::GetObserverService();
-
-    MOZ_ASSERT(observerService);
-
-    if (observerService)
-        observerService->AddObserver(this, "memory-pressure", false);
-}
-
-void
-WebGLObserver::UnregisterMemoryPressureEvent()
-{
-    if (!mWebGL)
-        return;
-
-    nsCOMPtr<nsIObserverService> observerService =
-        mozilla::services::GetObserverService();
-
-    // Do not assert on observerService here. This might be triggered by
-    // the cycle collector at a late enough time, that XPCOM services are
-    // no longer available. See bug 1029504.
-    if (observerService)
-        observerService->RemoveObserver(this, "memory-pressure");
-}
-
-NS_IMETHODIMP
-WebGLObserver::Observe(nsISupports*, const char* topic, const char16_t*)
-{
-    if (!mWebGL || strcmp(topic, "memory-pressure")) {
-        return NS_OK;
-    }
-
-    bool wantToLoseContext = mWebGL->mLoseContextOnMemoryPressure;
-
-    if (!mWebGL->mCanLoseContextInForeground &&
-        ProcessPriorityManager::CurrentProcessIsForeground())
-    {
-        wantToLoseContext = false;
-    }
-
-    if (wantToLoseContext)
-        mWebGL->ForceLoseContext();
-
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-WebGLObserver::HandleEvent(nsIDOMEvent* event)
-{
-    nsAutoString type;
-    event->GetType(type);
-    if (!mWebGL || !type.EqualsLiteral("visibilitychange"))
-        return NS_OK;
-
-    HTMLCanvasElement* canvas = mWebGL->GetCanvas();
-    MOZ_ASSERT(canvas);
-
-    if (canvas && !canvas->OwnerDoc()->Hidden())
-        mWebGL->ForceRestoreContext();
-
-    return NS_OK;
-}
-
 WebGLContextOptions::WebGLContextOptions()
     : alpha(true)
     , depth(true)
     , stencil(false)
     , premultipliedAlpha(true)
     , antialias(true)
     , preserveDrawingBuffer(false)
     , failIfMajorPerformanceCaveat(false)
 {
     // Set default alpha state based on preference.
-    if (Preferences::GetBool("webgl.default-no-alpha", false))
+    if (gfxPrefs::WebGLDefaultNoAlpha())
         alpha = false;
 }
 
 WebGLContext::WebGLContext()
     : WebGLContextUnchecked(nullptr)
     , mBypassShaderValidation(false)
     , mGLMaxSamples(1)
     , mNeedsFakeNoAlpha(false)
@@ -277,67 +159,67 @@ WebGLContext::WebGLContext()
     mGLMaxUniformBufferBindings = 0;
     mGLMax3DTextureSize = 0;
     mGLMaxArrayTextureLayers = 0;
 
     // See OpenGL ES 2.0.25 spec, 6.2 State Tables, table 6.13
     mPixelStorePackAlignment = 4;
     mPixelStoreUnpackAlignment = 4;
 
-    WebGLMemoryTracker::AddWebGLContext(this);
+    if (NS_IsMainThread()) {
+        // XXX mtseng: bug 709490, not thread safe
+        WebGLMemoryTracker::AddWebGLContext(this);
+    }
 
     mAllowContextRestore = true;
     mLastLossWasSimulated = false;
     mContextLossHandler = new WebGLContextLossHandler(this);
     mContextStatus = ContextNotLost;
     mLoseContextOnMemoryPressure = false;
     mCanLoseContextInForeground = true;
     mRestoreWhenVisible = false;
 
     mAlreadyGeneratedWarnings = 0;
     mAlreadyWarnedAboutFakeVertexAttrib0 = false;
     mAlreadyWarnedAboutViewportLargerThanDest = false;
 
-    mMaxWarnings = Preferences::GetInt("webgl.max-warnings-per-context", 32);
+    mMaxWarnings = gfxPrefs::WebGLMaxWarningsPerContext();
     if (mMaxWarnings < -1) {
         GenerateWarning("webgl.max-warnings-per-context size is too large (seems like a negative value wrapped)");
         mMaxWarnings = 0;
     }
 
-    mContextObserver = new WebGLObserver(this);
-    MOZ_RELEASE_ASSERT(mContextObserver, "Can't alloc WebGLContextObserver");
-
     mLastUseIndex = 0;
 
     InvalidateBufferFetching();
 
     mBackbufferNeedsClear = true;
 
     mDisableFragHighP = false;
 
     mDrawCallsSinceLastFlush = 0;
 }
 
 WebGLContext::~WebGLContext()
 {
     RemovePostRefreshObserver();
-    mContextObserver->Destroy();
 
     DestroyResourcesAndContext();
-    WebGLMemoryTracker::RemoveWebGLContext(this);
+    if (NS_IsMainThread()) {
+        // XXX mtseng: bug 709490, not thread safe
+        WebGLMemoryTracker::RemoveWebGLContext(this);
+    }
 
     mContextLossHandler->DisableTimer();
     mContextLossHandler = nullptr;
 }
 
 void
 WebGLContext::DestroyResourcesAndContext()
 {
-    mContextObserver->UnregisterMemoryPressureEvent();
-
     if (!gl)
         return;
 
     gl->MakeCurrent();
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
     mBound3DTextures.Clear();
@@ -426,16 +308,45 @@ WebGLContext::Invalidate()
         return;
 
     nsSVGEffects::InvalidateDirectRenderingObservers(mCanvasElement);
 
     mInvalidated = true;
     mCanvasElement->InvalidateCanvasContent(nullptr);
 }
 
+void
+WebGLContext::OnVisibilityChange()
+{
+    if (!IsContextLost()) {
+        return;
+    }
+
+    if (!mRestoreWhenVisible || mLastLossWasSimulated) {
+        return;
+    }
+
+    ForceRestoreContext();
+}
+
+void
+WebGLContext::OnMemoryPressure()
+{
+    bool shouldLoseContext = mLoseContextOnMemoryPressure;
+
+    if (!mCanLoseContextInForeground &&
+        ProcessPriorityManager::CurrentProcessIsForeground())
+    {
+        shouldLoseContext = false;
+    }
+
+    if (shouldLoseContext)
+        ForceLoseContext();
+}
+
 //
 // nsICanvasRenderingContextInternal
 //
 
 NS_IMETHODIMP
 WebGLContext::SetContextOptions(JSContext* cx, JS::Handle<JS::Value> options)
 {
     if (options.isNullOrUndefined() && mOptionsFrozen)
@@ -510,40 +421,50 @@ WebGLContext::GetHeight() const
  * caps. Finally, resize the backbuffer to an acceptable size given the
  * requested size.
  */
 
 static bool
 IsFeatureInBlacklist(const nsCOMPtr<nsIGfxInfo>& gfxInfo, int32_t feature)
 {
     int32_t status;
-    if (!NS_SUCCEEDED(gfxInfo->GetFeatureStatus(feature, &status)))
+    if (!NS_SUCCEEDED(gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo, feature, &status)))
         return false;
 
     return status != nsIGfxInfo::FEATURE_STATUS_OK;
 }
 
 static bool
 HasAcceleratedLayers(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
 {
     int32_t status;
 
-    gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS, &status);
+    gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
+                                         nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
+                                         &status);
     if (status)
         return true;
-    gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS, &status);
+    gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
+                                         nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
+                                         &status);
     if (status)
         return true;
-    gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS, &status);
+    gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
+                                         nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
+                                         &status);
     if (status)
         return true;
-    gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status);
+    gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
+                                         nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
+                                         &status);
     if (status)
         return true;
-    gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_OPENGL_LAYERS, &status);
+    gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
+                                         nsIGfxInfo::FEATURE_OPENGL_LAYERS,
+                                         &status);
     if (status)
         return true;
 
     return false;
 }
 
 static void
 PopulateCapFallbackQueue(const gl::SurfaceCaps& baseCaps,
@@ -590,21 +511,24 @@ BaseCaps(const WebGLContextOptions& opti
     baseCaps.stencil = options.stencil;
 
     if (!baseCaps.alpha)
         baseCaps.premultAlpha = true;
 
     // we should really have this behind a
     // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
     // for now it's just behind a pref for testing/evaluation.
-    baseCaps.bpp16 = Preferences::GetBool("webgl.prefer-16bpp", false);
+    baseCaps.bpp16 = gfxPrefs::WebGLPrefer16bpp();
 
 #ifdef MOZ_WIDGET_GONK
     do {
         auto canvasElement = webgl->GetCanvas();
+        if (!canvasElement)
+            break;
+
         auto ownerDoc = canvasElement->OwnerDoc();
         nsIWidget* docWidget = nsContentUtils::WidgetForDocument(ownerDoc);
         if (!docWidget)
             break;
 
         layers::LayerManager* layerManager = docWidget->GetLayerManager();
         if (!layerManager)
             break;
@@ -615,17 +539,17 @@ BaseCaps(const WebGLContextOptions& opti
             break;
 
         baseCaps.surfaceAllocator = static_cast<layers::ISurfaceAllocator*>(forwarder);
     } while (false);
 #endif
 
     // Done with baseCaps construction.
 
-    bool forceAllowAA = Preferences::GetBool("webgl.msaa-force", false);
+    bool forceAllowAA = gfxPrefs::WebGLForceMSAA();
     nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
     if (!forceAllowAA &&
         IsFeatureInBlacklist(gfxInfo, nsIGfxInfo::FEATURE_WEBGL_MSAA))
     {
         webgl->GenerateWarning("Disallowing antialiased backbuffers due"
                                " to blacklisting.");
         baseCaps.antialias = false;
     }
@@ -735,17 +659,17 @@ WebGLContext::CreateAndInitGLWith(FnCrea
 
     return true;
 }
 
 bool
 WebGLContext::CreateAndInitGL(bool forceEnabled)
 {
     bool preferEGL = PR_GetEnv("MOZ_WEBGL_PREFER_EGL");
-    bool disableANGLE = Preferences::GetBool("webgl.disable-angle", false);
+    bool disableANGLE = gfxPrefs::WebGLDisableANGLE();
 
     if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL"))
         disableANGLE = true;
 
     gl::CreateContextFlags flags = gl::CreateContextFlags::NONE;
     if (forceEnabled) flags |= gl::CreateContextFlags::FORCE_ENABLE_HARDWARE;
     if (!IsWebGL2())  flags |= gl::CreateContextFlags::REQUIRE_COMPAT_PROFILE;
 
@@ -816,30 +740,29 @@ WebGLContext::ResizeBackbuffer(uint32_t 
                         width, height);
     }
     return true;
 }
 
 NS_IMETHODIMP
 WebGLContext::SetDimensions(int32_t signedWidth, int32_t signedHeight)
 {
-    // Early error return cases
-    if (!GetCanvas())
-        return NS_ERROR_FAILURE;
-
     if (signedWidth < 0 || signedHeight < 0) {
         GenerateWarning("Canvas size is too large (seems like a negative value wrapped)");
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     uint32_t width = signedWidth;
     uint32_t height = signedHeight;
 
     // Early success return cases
-    GetCanvas()->InvalidateCanvas();
+
+    // May have a OffscreenCanvas instead of an HTMLCanvasElement
+    if (GetCanvas())
+        GetCanvas()->InvalidateCanvas();
 
     // Zero-sized surfaces can cause problems.
     if (width == 0)
         width = 1;
 
     if (height == 0)
         height = 1;
 
@@ -902,20 +825,17 @@ WebGLContext::SetDimensions(int32_t sign
         return NS_ERROR_FAILURE; // exit without changing the value of mGeneration
     }
 
     // increment the generation number - Do this early because later
     // in CreateOffscreenGL(), "default" objects are created that will
     // pick up the old generation.
     ++mGeneration;
 
-    // Get some prefs for some preferred/overriden things
-    NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
-
-    bool disabled = Preferences::GetBool("webgl.disabled", false);
+    bool disabled = gfxPrefs::WebGLDisabled();
 
     // TODO: When we have software webgl support we should use that instead.
     disabled |= gfxPlatform::InSafeMode();
 
     if (disabled) {
         GenerateWarning("WebGL creation is disabled, and so disallowed here.");
         return NS_ERROR_FAILURE;
     }
@@ -928,17 +848,17 @@ WebGLContext::SetDimensions(int32_t sign
         dom::Nullable<dom::WebGLContextAttributes> contextAttributes;
         this->GetContextAttributes(contextAttributes);
         if (contextAttributes.Value().mFailIfMajorPerformanceCaveat) {
             return NS_ERROR_FAILURE;
         }
     }
 
     // Alright, now let's start trying.
-    bool forceEnabled = Preferences::GetBool("webgl.force-enabled", false);
+    bool forceEnabled = gfxPrefs::WebGLForceEnabled();
     ScopedGfxFeatureReporter reporter("WebGL", forceEnabled);
 
     MOZ_ASSERT(!gl);
     if (!CreateAndInitGL(forceEnabled)) {
         GenerateWarning("WebGL creation failed.");
         return NS_ERROR_FAILURE;
     }
     MOZ_ASSERT(gl);
@@ -1049,16 +969,21 @@ WebGLContext::LoseOldestWebGLContextIfLi
     const size_t kMaxWebGLContextsPerPrincipal = 2;
     const size_t kMaxWebGLContexts             = 4;
 #else
     const size_t kMaxWebGLContextsPerPrincipal = 16;
     const size_t kMaxWebGLContexts             = 32;
 #endif
     MOZ_ASSERT(kMaxWebGLContextsPerPrincipal < kMaxWebGLContexts);
 
+    if (!NS_IsMainThread()) {
+        // XXX mtseng: bug 709490, WebGLMemoryTracker is not thread safe.
+        return;
+    }
+
     // it's important to update the index on a new context before losing old contexts,
     // otherwise new unused contexts would all have index 0 and we couldn't distinguish older ones
     // when choosing which one to lose first.
     UpdateLastUseIndex();
 
     WebGLMemoryTracker::ContextsArrayType& contexts = WebGLMemoryTracker::Contexts();
 
     // quick exit path, should cover a majority of cases
@@ -1265,35 +1190,36 @@ WebGLContext::GetCanvasLayer(nsDisplayLi
 
     nsRefPtr<CanvasLayer> canvasLayer = manager->CreateCanvasLayer();
     if (!canvasLayer) {
         NS_WARNING("CreateCanvasLayer returned null!");
         return nullptr;
     }
 
     WebGLContextUserData* userData = nullptr;
-    if (builder->IsPaintingToWindow()) {
-      // Make the layer tell us whenever a transaction finishes (including
-      // the current transaction), so we can clear our invalidation state and
-      // start invalidating again. We need to do this for the layer that is
-      // being painted to a window (there shouldn't be more than one at a time,
-      // and if there is, flushing the invalidation state more often than
-      // necessary is harmless).
+    if (builder->IsPaintingToWindow() && mCanvasElement) {
+        // Make the layer tell us whenever a transaction finishes (including
+        // the current transaction), so we can clear our invalidation state and
+        // start invalidating again. We need to do this for the layer that is
+        // being painted to a window (there shouldn't be more than one at a time,
+        // and if there is, flushing the invalidation state more often than
+        // necessary is harmless).
 
-      // The layer will be destroyed when we tear down the presentation
-      // (at the latest), at which time this userData will be destroyed,
-      // releasing the reference to the element.
-      // The userData will receive DidTransactionCallbacks, which flush the
-      // the invalidation state to indicate that the canvas is up to date.
-      userData = new WebGLContextUserData(mCanvasElement);
-      canvasLayer->SetDidTransactionCallback(
-              WebGLContextUserData::DidTransactionCallback, userData);
-      canvasLayer->SetPreTransactionCallback(
-              WebGLContextUserData::PreTransactionCallback, userData);
+        // The layer will be destroyed when we tear down the presentation
+        // (at the latest), at which time this userData will be destroyed,
+        // releasing the reference to the element.
+        // The userData will receive DidTransactionCallbacks, which flush the
+        // the invalidation state to indicate that the canvas is up to date.
+        userData = new WebGLContextUserData(mCanvasElement);
+        canvasLayer->SetDidTransactionCallback(
+            WebGLContextUserData::DidTransactionCallback, userData);
+        canvasLayer->SetPreTransactionCallback(
+            WebGLContextUserData::PreTransactionCallback, userData);
     }
+
     canvasLayer->SetUserData(&gWebGLLayerUserData, userData);
 
     CanvasLayer::Data data;
     data.mGLContext = gl;
     data.mSize = nsIntSize(mWidth, mHeight);
     data.mHasAlpha = gl->Caps().alpha;
     data.mIsGLAlphaPremult = IsPremultAlpha() || !data.mHasAlpha;
 
@@ -1314,16 +1240,37 @@ WebGLContext::GetCompositorBackendType()
     if (docWidget) {
         layers::LayerManager* layerManager = docWidget->GetLayerManager();
         return layerManager->GetCompositorBackendType();
     }
     return LayersBackend::LAYERS_NONE;
 }
 
 void
+WebGLContext::Commit()
+{
+    if (mOffscreenCanvas) {
+        mOffscreenCanvas->CommitFrameToCompositor();
+    }
+}
+
+void
+WebGLContext::GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval)
+{
+    if (mCanvasElement) {
+        MOZ_RELEASE_ASSERT(!mOffscreenCanvas);
+        retval.SetValue().SetAsHTMLCanvasElement() = mCanvasElement;
+    } else if (mOffscreenCanvas) {
+        retval.SetValue().SetAsOffscreenCanvas() = mOffscreenCanvas;
+    } else {
+        retval.SetNull();
+    }
+}
+
+void
 WebGLContext::GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
     dom::WebGLContextAttributes& result = retval.SetValue();
 
@@ -1621,31 +1568,37 @@ WebGLContext::TryToRestoreContext()
 }
 
 void
 WebGLContext::RunContextLossTimer()
 {
     mContextLossHandler->RunTimer();
 }
 
-class UpdateContextLossStatusTask : public nsRunnable
+class UpdateContextLossStatusTask : public nsCancelableRunnable
 {
     nsRefPtr<WebGLContext> mWebGL;
 
 public:
     explicit UpdateContextLossStatusTask(WebGLContext* webgl)
         : mWebGL(webgl)
     {
     }
 
     NS_IMETHOD Run() {
-        mWebGL->UpdateContextLossStatus();
+        if (mWebGL)
+            mWebGL->UpdateContextLossStatus();
 
         return NS_OK;
     }
+
+    NS_IMETHOD Cancel() {
+        mWebGL = nullptr;
+        return NS_OK;
+    }
 };
 
 void
 WebGLContext::EnqueueUpdateContextLossStatus()
 {
     nsCOMPtr<nsIRunnable> task = new UpdateContextLossStatusTask(this);
     NS_DispatchToCurrentThread(task);
 }
@@ -1662,17 +1615,17 @@ WebGLContext::EnqueueUpdateContextLossSt
 // fires.
 // Note that this timer mechanism is not used unless one of these 3 criteria
 // are met.
 // At a bare minimum, from context lost to context restores, it would take 3
 // full timer iterations: detection, webglcontextlost, webglcontextrestored.
 void
 WebGLContext::UpdateContextLossStatus()
 {
-    if (!mCanvasElement) {
+    if (!mCanvasElement && !mOffscreenCanvas) {
         // the canvas is gone. That happens when the page was closed before we got
         // this timer event. In this case, there's nothing to do here, just don't crash.
         return;
     }
     if (mContextStatus == ContextNotLost) {
         // We don't know that we're lost, but we might be, so we need to
         // check. If we're guilty, don't allow restores, though.
 
@@ -1690,22 +1643,33 @@ WebGLContext::UpdateContextLossStatus()
         // Fall through.
     }
 
     if (mContextStatus == ContextLostAwaitingEvent) {
         // The context has been lost and we haven't yet triggered the
         // callback, so do that now.
 
         bool useDefaultHandler;
-        nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
-                                             static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
-                                             NS_LITERAL_STRING("webglcontextlost"),
-                                             true,
-                                             true,
-                                             &useDefaultHandler);
+
+        if (mCanvasElement) {
+            nsContentUtils::DispatchTrustedEvent(
+                mCanvasElement->OwnerDoc(),
+                static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
+                NS_LITERAL_STRING("webglcontextlost"),
+                true,
+                true,
+                &useDefaultHandler);
+        } else {
+            // OffscreenCanvas case
+            nsRefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
+            event->InitEvent(NS_LITERAL_STRING("webglcontextlost"), true, true);
+            event->SetTrusted(true);
+            mOffscreenCanvas->DispatchEvent(event, &useDefaultHandler);
+        }
+
         // We sent the callback, so we're just 'regular lost' now.
         mContextStatus = ContextLost;
         // If we're told to use the default handler, it means the script
         // didn't bother to handle the event. In this case, we shouldn't
         // auto-restore the context.
         if (useDefaultHandler)
             mAllowContextRestore = false;
 
@@ -1747,21 +1711,32 @@ WebGLContext::UpdateContextLossStatus()
         if (!TryToRestoreContext()) {
             // Failed to restore. Try again later.
             mContextLossHandler->RunTimer();
             return;
         }
 
         // Revival!
         mContextStatus = ContextNotLost;
-        nsContentUtils::DispatchTrustedEvent(mCanvasElement->OwnerDoc(),
-                                             static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
-                                             NS_LITERAL_STRING("webglcontextrestored"),
-                                             true,
-                                             true);
+
+        if (mCanvasElement) {
+            nsContentUtils::DispatchTrustedEvent(
+                mCanvasElement->OwnerDoc(),
+                static_cast<nsIDOMHTMLCanvasElement*>(mCanvasElement),
+                NS_LITERAL_STRING("webglcontextrestored"),
+                true,
+                true);
+        } else {
+            nsRefPtr<Event> event = new Event(mOffscreenCanvas, nullptr, nullptr);
+            event->InitEvent(NS_LITERAL_STRING("webglcontextrestored"), true, true);
+            event->SetTrusted(true);
+            bool unused;
+            mOffscreenCanvas->DispatchEvent(event, &unused);
+        }
+
         mEmitContextLostErrorOnce = true;
         return;
     }
 }
 
 void
 WebGLContext::ForceLoseContext(bool simulateLosing)
 {
@@ -1769,35 +1744,27 @@ WebGLContext::ForceLoseContext(bool simu
     MOZ_ASSERT(!IsContextLost());
     mContextStatus = ContextLostAwaitingEvent;
     mContextLostErrorSet = false;
 
     // Burn it all!
     DestroyResourcesAndContext();
     mLastLossWasSimulated = simulateLosing;
 
-    // Register visibility change observer to defer the context restoring.
-    // Restore the context when the app is visible.
-    if (mRestoreWhenVisible && !mLastLossWasSimulated) {
-        mContextObserver->RegisterVisibilityChangeEvent();
-    }
-
     // Queue up a task, since we know the status changed.
     EnqueueUpdateContextLossStatus();
 }
 
 void
 WebGLContext::ForceRestoreContext()
 {
     printf_stderr("WebGL(%p)::ForceRestoreContext\n", this);
     mContextStatus = ContextLostAwaitingRestore;
     mAllowContextRestore = true; // Hey, you did say 'force'.
 
-    mContextObserver->UnregisterVisibilityChangeEvent();
-
     // Queue up a task, since we know the status changed.
     EnqueueUpdateContextLossStatus();
 }
 
 void
 WebGLContext::MakeContextCurrent() const
 {
     gl->MakeCurrent();
@@ -1922,16 +1889,17 @@ WebGLContext::ScopedMaskWorkaround::~Sco
 ////////////////////////////////////////////////////////////////////////////////
 // XPCOM goop
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLContext,
   mCanvasElement,
+  mOffscreenCanvas,
   mExtensions,
   mBound2DTextures,
   mBoundCubeMapTextures,
   mBound3DTextures,
   mBoundSamplers,
   mBoundArrayBuffer,
   mBoundCopyReadBuffer,
   mBoundCopyWriteBuffer,
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -35,17 +35,21 @@
 #include "WebGLContextUnchecked.h"
 #include "WebGLFormats.h"
 #include "WebGLObjectModel.h"
 #include "WebGLStrongTypes.h"
 
 // Generated
 #include "nsIDOMEventListener.h"
 #include "nsIDOMWebGLRenderingContext.h"
+#include "nsICanvasRenderingContextInternal.h"
 #include "nsIObserver.h"
+#include "mozilla/dom/HTMLCanvasElement.h"
+#include "nsWrapperCache.h"
+#include "nsLayoutUtils.h"
 
 
 class nsIDocShell;
 
 /*
  * Minimum value constants defined in 6.2 State Tables of OpenGL ES - 2.0.25
  *   https://bugzilla.mozilla.org/show_bug.cgi?id=686732
  *
@@ -75,31 +79,31 @@ class nsIDocShell;
 
 namespace mozilla {
 
 class WebGLActiveInfo;
 class WebGLContextLossHandler;
 class WebGLBuffer;
 class WebGLExtensionBase;
 class WebGLFramebuffer;
-class WebGLObserver;
 class WebGLProgram;
 class WebGLQuery;
 class WebGLRenderbuffer;
 class WebGLSampler;
 class WebGLShader;
 class WebGLShaderPrecisionFormat;
 class WebGLTexture;
 class WebGLTransformFeedback;
 class WebGLUniformLocation;
 class WebGLVertexArray;
 
 namespace dom {
 class Element;
 class ImageData;
+class OwningHTMLCanvasElementOrOffscreenCanvas;
 struct WebGLContextAttributes;
 template<typename> struct Nullable;
 } // namespace dom
 
 namespace gfx {
 class SourceSurface;
 } // namespace gfx
 
@@ -179,17 +183,16 @@ class WebGLContext
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionDepthTexture;
     friend class WebGLExtensionDisjointTimerQuery;
     friend class WebGLExtensionDrawBuffers;
     friend class WebGLExtensionLoseContext;
     friend class WebGLExtensionVertexArray;
     friend class WebGLMemoryTracker;
-    friend class WebGLObserver;
 
     enum {
         UNPACK_FLIP_Y_WEBGL = 0x9240,
         UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
         CONTEXT_LOST_WEBGL = 0x9242,
         UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243,
         BROWSER_DEFAULT_WEBGL = 0x9244,
         UNMASKED_VENDOR_WEBGL = 0x9245,
@@ -209,16 +212,19 @@ public:
 
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(WebGLContext,
                                                            nsIDOMWebGLRenderingContext)
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override = 0;
 
     NS_DECL_NSIDOMWEBGLRENDERINGCONTEXT
 
+    virtual void OnVisibilityChange() override;
+    virtual void OnMemoryPressure() override;
+
     // nsICanvasRenderingContextInternal
 #ifdef DEBUG
     virtual int32_t GetWidth() const override;
     virtual int32_t GetHeight() const override;
 #endif
     NS_IMETHOD SetDimensions(int32_t width, int32_t height) override;
     NS_IMETHOD InitializeWithSurface(nsIDocShell*, gfxASurface*, int32_t,
                                      int32_t) override
@@ -357,18 +363,21 @@ public:
     void UpdateContextLossStatus();
     void EnqueueUpdateContextLossStatus();
 
     bool TryToRestoreContext();
 
     void AssertCachedBindings();
     void AssertCachedState();
 
+    dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
+
     // WebIDL WebGLRenderingContext API
-    dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
+    void Commit();
+    void GetCanvas(Nullable<dom::OwningHTMLCanvasElementOrOffscreenCanvas>& retval);
     GLsizei DrawingBufferWidth() const { return IsContextLost() ? 0 : mWidth; }
     GLsizei DrawingBufferHeight() const {
         return IsContextLost() ? 0 : mHeight;
     }
 
     layers::LayersBackend GetCompositorBackendType() const;
 
     void
@@ -1504,18 +1513,16 @@ protected:
     // see bug 713305. This RAII helper guarantees that we're on the discrete GPU, during its lifetime
     // Debouncing note: we don't want to switch GPUs too frequently, so try to not create and destroy
     // these objects at high frequency. Having WebGLContext's hold one such object seems fine,
     // because WebGLContext objects only go away during GC, which shouldn't happen too frequently.
     // If in the future GC becomes much more frequent, we may have to revisit then (maybe use a timer).
     ForceDiscreteGPUHelperCGL mForceDiscreteGPUHelper;
 #endif
 
-    nsRefPtr<WebGLObserver> mContextObserver;
-
 public:
     // console logging helpers
     void GenerateWarning(const char* fmt, ...);
     void GenerateWarning(const char* fmt, va_list ap);
 
 public:
     UniquePtr<webgl::FormatUsageAuthority> mFormatUsage;
     virtual UniquePtr<webgl::FormatUsageAuthority> CreateFormatUsage() const = 0;
@@ -1610,42 +1617,16 @@ WebGLContext::ValidateObject(const char*
     if (!object) {
         ErrorInvalidValue("%s: null object passed as argument", info);
         return false;
     }
 
     return ValidateObjectAssumeNonNull(info, object);
 }
 
-// Listen visibilitychange and memory-pressure event for context lose/restore
-class WebGLObserver final
-    : public nsIObserver
-    , public nsIDOMEventListener
-{
-public:
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIOBSERVER
-    NS_DECL_NSIDOMEVENTLISTENER
-
-    explicit WebGLObserver(WebGLContext* webgl);
-
-    void Destroy();
-
-    void RegisterVisibilityChangeEvent();
-    void UnregisterVisibilityChangeEvent();
-
-    void RegisterMemoryPressureEvent();
-    void UnregisterMemoryPressureEvent();
-
-private:
-    ~WebGLObserver();
-
-    WebGLContext* mWebGL;
-};
-
 size_t RoundUpToMultipleOf(size_t value, size_t multiple);
 
 bool
 ValidateTexTarget(WebGLContext* webgl, GLenum rawTexTarget, const char* funcName,
                   TexTarget* const out_texTarget, WebGLTexture** const out_tex);
 bool
 ValidateTexImageTarget(WebGLContext* webgl, GLenum rawTexImageTarget,
                        const char* funcName, TexImageTarget* const out_texImageTarget,
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLContext.h"
 #include "WebGLContextUtils.h"
 #include "WebGLExtensions.h"
+#include "gfxPrefs.h"
 #include "GLContext.h"
 
 #include "nsString.h"
 #include "mozilla/Preferences.h"
 #include "AccessCheck.h"
 
 namespace mozilla {
 
@@ -69,22 +70,25 @@ WebGLContext::IsExtensionEnabled(WebGLEx
 
 bool WebGLContext::IsExtensionSupported(JSContext* cx,
                                         WebGLExtensionID ext) const
 {
     bool allowPrivilegedExts = false;
 
     // Chrome contexts need access to debug information even when
     // webgl.disable-extensions is set. This is used in the graphics
-    // section of about:support.
-    if (xpc::AccessCheck::isChrome(js::GetContextCompartment(cx)))
+    // section of about:support
+    if (NS_IsMainThread() &&
+        xpc::AccessCheck::isChrome(js::GetContextCompartment(cx))) {
         allowPrivilegedExts = true;
+    }
 
-    if (Preferences::GetBool("webgl.enable-privileged-extensions", false))
+    if (gfxPrefs::WebGLPrivilegedExtensionsEnabled()) {
         allowPrivilegedExts = true;
+    }
 
     if (allowPrivilegedExts) {
         switch (ext) {
         case WebGLExtensionID::WEBGL_debug_renderer_info:
             return true;
         case WebGLExtensionID::WEBGL_debug_shaders:
             return true;
         default:
@@ -176,19 +180,17 @@ WebGLContext::IsExtensionSupported(WebGL
         // We always support this extension.
         return true;
 
     default:
         // For warnings-as-errors.
         break;
     }
 
-    if (Preferences::GetBool("webgl.enable-draft-extensions", false) ||
-        IsWebGL2())
-    {
+    if (gfxPrefs::WebGLDraftExtensionsEnabled() || IsWebGL2()) {
         switch (ext) {
         case WebGLExtensionID::EXT_disjoint_timer_query:
             return WebGLExtensionDisjointTimerQuery::IsSupported(this);
         default:
             // For warnings-as-errors.
             break;
         }
     }
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -1385,17 +1385,20 @@ void
 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
                          GLsizei height, GLenum format,
                          GLenum type, const dom::Nullable<dom::ArrayBufferView>& pixels,
                          ErrorResult& rv)
 {
     if (IsContextLost())
         return;
 
-    if (mCanvasElement->IsWriteOnly() && !nsContentUtils::IsCallerChrome()) {
+    if (mCanvasElement &&
+        mCanvasElement->IsWriteOnly() &&
+        !nsContentUtils::IsCallerChrome())
+    {
         GenerateWarning("readPixels: Not allowed");
         return rv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     }
 
     if (width < 0 || height < 0)
         return ErrorInvalidValue("readPixels: negative size passed");
 
     if (pixels.IsNull())
--- a/dom/canvas/WebGLContextLossHandler.cpp
+++ b/dom/canvas/WebGLContextLossHandler.cpp
@@ -3,25 +3,113 @@
  * 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 "WebGLContextLossHandler.h"
 
 #include "nsITimer.h"
 #include "nsThreadUtils.h"
 #include "WebGLContext.h"
+#include "mozilla/dom/WorkerPrivate.h"
 
 namespace mozilla {
 
+// -------------------------------------------------------------------
+// Begin worker specific code
+// -------------------------------------------------------------------
+
+// On workers we can only dispatch CancelableRunnables, so we have to wrap the
+// timer's EventTarget to use our own cancelable runnable
+
+class ContextLossWorkerEventTarget final : public nsIEventTarget
+{
+public:
+    explicit ContextLossWorkerEventTarget(nsIEventTarget* aEventTarget)
+        : mEventTarget(aEventTarget)
+    {
+        MOZ_ASSERT(aEventTarget);
+    }
+
+    NS_DECL_NSIEVENTTARGET
+    NS_DECL_THREADSAFE_ISUPPORTS
+
+protected:
+    ~ContextLossWorkerEventTarget() {}
+
+private:
+    nsCOMPtr<nsIEventTarget> mEventTarget;
+};
+
+class ContextLossWorkerRunnable final : public nsICancelableRunnable
+{
+public:
+    explicit ContextLossWorkerRunnable(nsIRunnable* aRunnable)
+        : mRunnable(aRunnable)
+    {
+    }
+
+    NS_DECL_NSICANCELABLERUNNABLE
+    NS_DECL_THREADSAFE_ISUPPORTS
+
+    NS_FORWARD_NSIRUNNABLE(mRunnable->)
+
+protected:
+    ~ContextLossWorkerRunnable() {}
+
+private:
+    nsCOMPtr<nsIRunnable> mRunnable;
+};
+
+NS_IMPL_ISUPPORTS(ContextLossWorkerEventTarget, nsIEventTarget,
+                  nsISupports)
+
+NS_IMETHODIMP
+ContextLossWorkerEventTarget::DispatchFromScript(nsIRunnable* aEvent, uint32_t aFlags)
+{
+    nsCOMPtr<nsIRunnable> event(aEvent);
+    return Dispatch(event.forget(), aFlags);
+}
+
+NS_IMETHODIMP
+ContextLossWorkerEventTarget::Dispatch(already_AddRefed<nsIRunnable>&& aEvent,
+                                       uint32_t aFlags)
+{
+    nsCOMPtr<nsIRunnable> eventRef(aEvent);
+    nsRefPtr<ContextLossWorkerRunnable> wrappedEvent =
+        new ContextLossWorkerRunnable(eventRef);
+    return mEventTarget->Dispatch(wrappedEvent, aFlags);
+}
+
+NS_IMETHODIMP
+ContextLossWorkerEventTarget::IsOnCurrentThread(bool* aResult)
+{
+    return mEventTarget->IsOnCurrentThread(aResult);
+}
+
+NS_IMPL_ISUPPORTS(ContextLossWorkerRunnable, nsICancelableRunnable,
+                  nsIRunnable)
+
+NS_IMETHODIMP
+ContextLossWorkerRunnable::Cancel()
+{
+    mRunnable = nullptr;
+    return NS_OK;
+}
+
+// -------------------------------------------------------------------
+// End worker-specific code
+// -------------------------------------------------------------------
+
 WebGLContextLossHandler::WebGLContextLossHandler(WebGLContext* webgl)
     : mWeakWebGL(webgl)
     , mTimer(do_CreateInstance(NS_TIMER_CONTRACTID))
     , mIsTimerRunning(false)
     , mShouldRunTimerAgain(false)
     , mIsDisabled(false)
+    , mFeatureAdded(false)
 #ifdef DEBUG
     , mThread(NS_GetCurrentThread())
 #endif
 {
 }
 
 WebGLContextLossHandler::~WebGLContextLossHandler()
 {
@@ -85,35 +173,66 @@ WebGLContextLossHandler::RunTimer()
     // wait until the previous call is done, then fire it one more time.
     // This is an optimization to prevent unnecessary
     // cross-communication between threads.
     if (mIsTimerRunning) {
         mShouldRunTimerAgain = true;
         return;
     }
 
+    if (!NS_IsMainThread()) {
+        dom::workers::WorkerPrivate* workerPrivate =
+            dom::workers::GetCurrentThreadWorkerPrivate();
+        nsCOMPtr<nsIEventTarget> target = workerPrivate->GetEventTarget();
+        mTimer->SetTarget(new ContextLossWorkerEventTarget(target));
+        if (!mFeatureAdded) {
+            workerPrivate->AddFeature(workerPrivate->GetJSContext(), this);
+            mFeatureAdded = true;
+        }
+    }
+
     StartTimer(1000);
 
     mIsTimerRunning = true;
     mShouldRunTimerAgain = false;
 }
 
 void
 WebGLContextLossHandler::DisableTimer()
 {
     if (mIsDisabled)
         return;
 
     mIsDisabled = true;
 
+    if (mFeatureAdded) {
+        dom::workers::WorkerPrivate* workerPrivate =
+            dom::workers::GetCurrentThreadWorkerPrivate();
+        MOZ_RELEASE_ASSERT(workerPrivate);
+        workerPrivate->RemoveFeature(workerPrivate->GetJSContext(), this);
+        mFeatureAdded = false;
+    }
+
     // We can't just Cancel() the timer, as sometimes we end up
     // receiving a callback after calling Cancel(). This could cause us
     // to receive the callback after object destruction.
 
     // Instead, we let the timer finish, but ignore it.
 
     if (!mIsTimerRunning)
         return;
 
     mTimer->SetDelay(0);
 }
 
+bool
+WebGLContextLossHandler::Notify(JSContext* aCx, dom::workers::Status aStatus)
+{
+    bool isWorkerRunning = aStatus < dom::workers::Closing;
+    if (!isWorkerRunning && mIsTimerRunning) {
+        mIsTimerRunning = false;
+        this->Release();
+    }
+
+    return true;
+}
+
 } // namespace mozilla
--- a/dom/canvas/WebGLContextLossHandler.h
+++ b/dom/canvas/WebGLContextLossHandler.h
@@ -5,39 +5,42 @@
 
 #ifndef WEBGL_CONTEXT_LOSS_HANDLER_H_
 #define WEBGL_CONTEXT_LOSS_HANDLER_H_
 
 #include "mozilla/DebugOnly.h"
 #include "mozilla/WeakPtr.h"
 #include "nsCOMPtr.h"
 #include "nsISupportsImpl.h"
+#include "WorkerFeature.h"
 
 class nsIThread;
 class nsITimer;
 
 namespace mozilla {
 class WebGLContext;
 
-class WebGLContextLossHandler
+class WebGLContextLossHandler : public dom::workers::WorkerFeature
 {
     WeakPtr<WebGLContext> mWeakWebGL;
     nsCOMPtr<nsITimer> mTimer;
     bool mIsTimerRunning;
     bool mShouldRunTimerAgain;
     bool mIsDisabled;
+    bool mFeatureAdded;
     DebugOnly<nsIThread*> mThread;
 
 public:
     NS_INLINE_DECL_REFCOUNTING(WebGLContextLossHandler)
 
     explicit WebGLContextLossHandler(WebGLContext* webgl);
 
     void RunTimer();
     void DisableTimer();
+    bool Notify(JSContext* aCx, dom::workers::Status aStatus) override;
 
 protected:
     ~WebGLContextLossHandler();
 
     void StartTimer(unsigned long delayMS);
     static void StaticTimerCallback(nsITimer*, void* tempRefForTimer);
     void TimerCallback();
 };
--- a/dom/canvas/WebGLContextReporter.cpp
+++ b/dom/canvas/WebGLContextReporter.cpp
@@ -4,18 +4,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebGLBuffer.h"
 #include "WebGLContext.h"
 #include "WebGLMemoryTracker.h"
 
 namespace mozilla {
 
-NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
-
 NS_IMETHODIMP
 WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
                                    nsISupports* data, bool)
 {
 #define REPORT(_path, _kind, _units, _amount, _desc)                         \
     do {                                                                     \
       nsresult rv;                                                           \
       rv = handleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -3,16 +3,17 @@
  * 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 "WebGLContext.h"
 
 #include <algorithm>
 #include "angle/ShaderLang.h"
 #include "CanvasUtils.h"
+#include "gfxPrefs.h"
 #include "GLContext.h"
 #include "jsfriendapi.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "WebGLActiveInfo.h"
 #include "WebGLBuffer.h"
@@ -1660,21 +1661,21 @@ WebGLContext::InitAndValidateGL()
 
     GLenum error = gl->fGetError();
     if (error != LOCAL_GL_NO_ERROR) {
         GenerateWarning("GL error 0x%x occurred during OpenGL context"
                         " initialization, before WebGL initialization!", error);
         return false;
     }
 
-    mMinCapability = Preferences::GetBool("webgl.min_capability_mode", false);
-    mDisableExtensions = Preferences::GetBool("webgl.disable-extensions", false);
-    mLoseContextOnMemoryPressure = Preferences::GetBool("webgl.lose-context-on-memory-pressure", false);
-    mCanLoseContextInForeground = Preferences::GetBool("webgl.can-lose-context-in-foreground", true);
-    mRestoreWhenVisible = Preferences::GetBool("webgl.restore-context-when-visible", true);
+    mMinCapability = gfxPrefs::WebGLMinCapabilityMode();
+    mDisableExtensions = gfxPrefs::WebGLDisableExtensions();
+    mLoseContextOnMemoryPressure = gfxPrefs::WebGLLoseContextOnMemoryPressure();
+    mCanLoseContextInForeground = gfxPrefs::WebGLCanLoseContextInForeground();
+    mRestoreWhenVisible = gfxPrefs::WebGLRestoreWhenVisible();
 
     if (MinCapabilityMode())
         mDisableFragHighP = true;
 
     // These are the default values, see 6.2 State tables in the
     // OpenGL ES 2.0.25 spec.
     mColorWriteMask[0] = 1;
     mColorWriteMask[1] = 1;
@@ -1873,20 +1874,17 @@ WebGLContext::InitAndValidateGL()
         // The Mac ATI driver, in all known OSX version up to and including
         // 10.8, renders points sprites upside-down. (Apple bug 11778921)
         gl->fPointParameterf(LOCAL_GL_POINT_SPRITE_COORD_ORIGIN,
                              LOCAL_GL_LOWER_LEFT);
     }
 #endif
 
     // Check the shader validator pref
-    NS_ENSURE_TRUE(Preferences::GetRootBranch(), false);
-
-    mBypassShaderValidation = Preferences::GetBool("webgl.bypass-shader-validation",
-                                                   mBypassShaderValidation);
+    mBypassShaderValidation = gfxPrefs::WebGLBypassShaderValidator();
 
     // initialize shader translator
     if (!ShInitialize()) {
         GenerateWarning("GLSL translator initialization failed!");
         return false;
     }
 
     // Mesa can only be detected with the GL_VERSION string, of the form
@@ -1932,19 +1930,16 @@ WebGLContext::InitAndValidateGL()
     // vertex array object (the name zero) is also deprecated. [...]"
 
     if (gl->IsCoreProfile()) {
         MakeContextCurrent();
         mDefaultVertexArray->GenVertexArray();
         mDefaultVertexArray->BindVertexArray();
     }
 
-    if (mLoseContextOnMemoryPressure)
-        mContextObserver->RegisterMemoryPressureEvent();
-
     return true;
 }
 
 bool
 WebGLContext::ValidateFramebufferTarget(GLenum target,
                                         const char* const info)
 {
     bool isValid = true;
--- a/dom/canvas/WebGLMemoryTracker.cpp
+++ b/dom/canvas/WebGLMemoryTracker.cpp
@@ -11,18 +11,16 @@
 #include "WebGLProgram.h"
 #include "WebGLRenderbuffer.h"
 #include "WebGLShader.h"
 #include "WebGLTexture.h"
 #include "WebGLUniformLocation.h"
 
 namespace mozilla {
 
-NS_IMPL_ISUPPORTS(WebGLObserver, nsIObserver)
-
 NS_IMETHODIMP
 WebGLMemoryTracker::CollectReports(nsIHandleReportCallback* handleReport,
                                    nsISupports* data, bool)
 {
 #define REPORT(_path, _kind, _units, _amount, _desc)                         \
     do {                                                                     \
       nsresult rv;                                                           \
       rv = handleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
--- a/dom/canvas/WebGLShaderValidator.cpp
+++ b/dom/canvas/WebGLShaderValidator.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "WebGLShaderValidator.h"
 
 #include "angle/ShaderLang.h"
+#include "gfxPrefs.h"
 #include "GLContext.h"
 #include "mozilla/Preferences.h"
 #include "MurmurHash3.h"
 #include "nsPrintfCString.h"
 #include "nsTArray.h"
 #include <string>
 #include <vector>
 #include "WebGLContext.h"
@@ -38,17 +39,17 @@ ChooseValidatorCompileOptions(const ShBu
                   SH_OBJECT_CODE |
                   SH_LIMIT_CALL_STACK_DEPTH |
                   SH_INIT_GL_POSITION;
 
     if (resources.MaxExpressionComplexity > 0) {
         options |= SH_LIMIT_EXPRESSION_COMPLEXITY;
     }
 
-    if (Preferences::GetBool("webgl.all-angle-options", false)) {
+    if (gfxPrefs::WebGLAllANGLEOptions()) {
         return options |
                SH_VALIDATE_LOOP_INDEXING |
                SH_UNROLL_FOR_LOOP_WITH_INTEGER_INDEX |
                SH_UNROLL_FOR_LOOP_WITH_SAMPLER_ARRAY_INDEX |
                SH_EMULATE_BUILT_IN_FUNCTIONS |
                SH_CLAMP_INDIRECT_ARRAY_BOUNDS |
                SH_UNFOLD_SHORT_CIRCUIT |
                SH_SCALARIZE_VEC_AND_MAT_CONSTRUCTOR_ARGS |
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -23,33 +23,37 @@ EXPORTS.mozilla.ipc += [
     'DocumentRendererParent.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'CanvasGradient.h',
     'CanvasPath.h',
     'CanvasPattern.h',
     'CanvasRenderingContext2D.h',
+    'CanvasRenderingContextHelper.h',
     'CanvasUtils.h',
     'ImageBitmap.h',
     'ImageBitmapSource.h',
     'ImageData.h',
+    'OffscreenCanvas.h',
     'TextMetrics.h',
     'WebGLVertexArrayObject.h',
 ]
 
 # Canvas 2D and common sources
 UNIFIED_SOURCES += [
     'CanvasImageCache.cpp',
     'CanvasRenderingContext2D.cpp',
+    'CanvasRenderingContextHelper.cpp',
     'CanvasUtils.cpp',
     'DocumentRendererChild.cpp',
     'DocumentRendererParent.cpp',
     'ImageBitmap.cpp',
     'ImageData.cpp',
+    'OffscreenCanvas.cpp',
 ]
 
 # WebGL Sources
 UNIFIED_SOURCES += [
     'MurmurHash3.cpp',
     'WebGL1Context.cpp',
     'WebGL1ContextBuffers.cpp',
     'WebGL1ContextUniforms.cpp',
@@ -145,16 +149,17 @@ LOCAL_INCLUDES += [
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '../workers',
     '/dom/base',
     '/dom/html',
     '/dom/svg',
+    '/dom/workers',
     '/dom/xul',
     '/gfx/gl',
     '/image',
     '/js/xpconnect/src',
     '/layout/generic',
     '/layout/style',
     '/layout/xul',
     '/media/libyuv/include',
--- a/dom/canvas/nsICanvasRenderingContextInternal.h
+++ b/dom/canvas/nsICanvasRenderingContextInternal.h
@@ -7,22 +7,23 @@
 #define nsICanvasRenderingContextInternal_h___
 
 #include "mozilla/gfx/2D.h"
 #include "nsISupports.h"
 #include "nsIInputStream.h"
 #include "nsIDocShell.h"
 #include "nsRefreshDriver.h"
 #include "mozilla/dom/HTMLCanvasElement.h"
+#include "mozilla/dom/OffscreenCanvas.h"
 #include "GraphicsFilter.h"
 #include "mozilla/RefPtr.h"
 
 #define NS_ICANVASRENDERINGCONTEXTINTERNAL_IID \
-{ 0x3cc9e801, 0x1806, 0x4ff6, \
-  { 0x86, 0x14, 0xf9, 0xd0, 0xf4, 0xfb, 0x3b, 0x08 } }
+{ 0xb84f2fed, 0x9d4b, 0x430b, \
+  { 0xbd, 0xfb, 0x85, 0x57, 0x8a, 0xc2, 0xb4, 0x4b } }
 
 class gfxASurface;
 class nsDisplayListBuilder;
 
 namespace mozilla {
 namespace layers {
 class CanvasLayer;
 class LayerManager;
@@ -75,16 +76,21 @@ public:
     mRefreshDriver->AddPostRefreshObserver(this);
   }
 
   mozilla::dom::HTMLCanvasElement* GetParentObject() const
   {
     return mCanvasElement;
   }
 
+  void SetOffscreenCanvas(mozilla::dom::OffscreenCanvas* aOffscreenCanvas)
+  {
+    mOffscreenCanvas = aOffscreenCanvas;
+  }
+
 #ifdef DEBUG
     // Useful for testing
     virtual int32_t GetWidth() const = 0;
     virtual int32_t GetHeight() const = 0;
 #endif
 
   // Sets the dimensions of the canvas, in pixels.  Called
   // whenever the size of the element changes.
@@ -149,27 +155,32 @@ public:
   NS_IMETHOD SetContextOptions(JSContext* cx, JS::Handle<JS::Value> options) { return NS_OK; }
 
   // return true and fills in the bounding rect if elementis a child and has a hit region.
   virtual bool GetHitRegionRect(mozilla::dom::Element* element, nsRect& rect) { return false; }
 
   // Given a point, return hit region ID if it exists or an empty string if it doesn't
   virtual nsString GetHitRegion(const mozilla::gfx::Point& point) { return nsString(); }
 
+  virtual void OnVisibilityChange() {}
+
+  virtual void OnMemoryPressure() {}
+
   //
   // shmem support
   //
 
   // If this context can be set to use Mozilla's Shmem segments as its backing
   // store, this will set it to that state. Note that if you have drawn
   // anything into this canvas before changing the shmem state, it will be
   // lost.
   NS_IMETHOD SetIsIPC(bool isIPC) = 0;
 
 protected:
   nsRefPtr<mozilla::dom::HTMLCanvasElement> mCanvasElement;
+  nsRefPtr<mozilla::dom::OffscreenCanvas> mOffscreenCanvas;
   nsRefPtr<nsRefreshDriver> mRefreshDriver;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsICanvasRenderingContextInternal,
                               NS_ICANVASRENDERINGCONTEXTINTERNAL_IID)
 
 #endif /* nsICanvasRenderingContextInternal_h___ */
--- a/dom/html/HTMLCanvasElement.cpp
+++ b/dom/html/HTMLCanvasElement.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/Assertions.h"
 #include "mozilla/Base64.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/dom/CanvasCaptureMediaStream.h"
 #include "mozilla/dom/CanvasRenderingContext2D.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/HTMLCanvasElementBinding.h"
 #include "mozilla/dom/MouseEvent.h"
+#include "mozilla/dom/OffscreenCanvas.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/gfx/Rect.h"
 #include "mozilla/layers/AsyncCanvasRenderer.h"
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "nsAttrValueInlines.h"
 #include "nsContentUtils.h"
@@ -235,24 +236,136 @@ HTMLCanvasPrintState::NotifyDone()
   mPendingNotify = false;
   if (mCallback) {
     mCallback->Notify(nullptr);
   }
 }
 
 // ---------------------------------------------------------------------------
 
+HTMLCanvasElementObserver::HTMLCanvasElementObserver(HTMLCanvasElement* aElement)
+    : mElement(aElement)
+{
+  RegisterVisibilityChangeEvent();
+  RegisterMemoryPressureEvent();
+}
+
+HTMLCanvasElementObserver::~HTMLCanvasElementObserver()
+{
+  Destroy();
+}
+
+void
+HTMLCanvasElementObserver::Destroy()
+{
+  UnregisterMemoryPressureEvent();
+  UnregisterVisibilityChangeEvent();
+  mElement = nullptr;
+}
+
+void
+HTMLCanvasElementObserver::RegisterVisibilityChangeEvent()
+{
+  if (!mElement) {
+    return;
+  }
+
+  nsIDocument* document = mElement->OwnerDoc();
+  document->AddSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
+                                   this, true, false);
+}
+
+void
+HTMLCanvasElementObserver::UnregisterVisibilityChangeEvent()
+{
+  if (!mElement) {
+    return;
+  }
+
+  nsIDocument* document = mElement->OwnerDoc();
+  document->RemoveSystemEventListener(NS_LITERAL_STRING("visibilitychange"),
+                                      this, true);
+}
+
+void
+HTMLCanvasElementObserver::RegisterMemoryPressureEvent()
+{
+  if (!mElement) {
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> observerService =
+    mozilla::services::GetObserverService();
+
+  MOZ_ASSERT(observerService);
+
+  if (observerService)
+    observerService->AddObserver(this, "memory-pressure", false);
+}
+
+void
+HTMLCanvasElementObserver::UnregisterMemoryPressureEvent()
+{
+  if (!mElement) {
+    return;
+  }
+
+  nsCOMPtr<nsIObserverService> observerService =
+    mozilla::services::GetObserverService();
+
+  // Do not assert on observerService here. This might be triggered by
+  // the cycle collector at a late enough time, that XPCOM services are
+  // no longer available. See bug 1029504.
+  if (observerService)
+    observerService->RemoveObserver(this, "memory-pressure");
+}
+
+NS_IMETHODIMP
+HTMLCanvasElementObserver::Observe(nsISupports*, const char* aTopic, const char16_t*)
+{
+  if (!mElement || strcmp(aTopic, "memory-pressure")) {
+    return NS_OK;
+  }
+
+  mElement->OnMemoryPressure();
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+HTMLCanvasElementObserver::HandleEvent(nsIDOMEvent* aEvent)
+{
+  nsAutoString type;
+  aEvent->GetType(type);
+  if (!mElement || !type.EqualsLiteral("visibilitychange")) {
+    return NS_OK;
+  }
+
+  mElement->OnVisibilityChange();
+
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(HTMLCanvasElementObserver, nsIObserver)
+
+// ---------------------------------------------------------------------------
+
 HTMLCanvasElement::HTMLCanvasElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
   : nsGenericHTMLElement(aNodeInfo),
     mWriteOnly(false)
 {
 }
 
 HTMLCanvasElement::~HTMLCanvasElement()
 {
+  if (mContextObserver) {
+    mContextObserver->Destroy();
+    mContextObserver = nullptr;
+  }
+
   ResetPrintCallback();
   if (mRequestedFrameRefreshObserver) {
     mRequestedFrameRefreshObserver->DetachFromRefreshDriver();
   }
 
   if (mAsyncCanvasRenderer) {
     mAsyncCanvasRenderer->mHTMLCanvasElement = nullptr;
   }
@@ -272,16 +385,32 @@ NS_INTERFACE_TABLE_TAIL_INHERITING(nsGen
 NS_IMPL_ELEMENT_CLONE(HTMLCanvasElement)
 
 /* virtual */ JSObject*
 HTMLCanvasElement::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return HTMLCanvasElementBinding::Wrap(aCx, this, aGivenProto);
 }
 
+already_AddRefed<nsICanvasRenderingContextInternal>
+HTMLCanvasElement::CreateContext(CanvasContextType aContextType)
+{
+  nsRefPtr<nsICanvasRenderingContextInternal> ret =
+    CanvasRenderingContextHelper::CreateContext(aContextType);
+
+  // Add Observer for webgl canvas.
+  if (aContextType == CanvasContextType::WebGL1 ||
+      aContextType == CanvasContextType::WebGL2) {
+    mContextObserver = new HTMLCanvasElementObserver(this);
+  }
+
+  ret->SetCanvasElement(this);
+  return ret.forget();
+}
+
 nsIntSize
 HTMLCanvasElement::GetWidthHeight()
 {
   nsIntSize size(DEFAULT_CANVAS_WIDTH, DEFAULT_CANVAS_HEIGHT);
   const nsAttrValue* value;
 
   if ((value = GetParsedAttr(nsGkAtoms::width)) &&
       value->Type() == nsAttrValue::eInteger)
@@ -560,58 +689,16 @@ HTMLCanvasElement::ExtractData(nsAString
   return ImageEncoder::ExtractData(aType,
                                    aOptions,
                                    GetSize(),
                                    mCurrentContext,
                                    aStream);
 }
 
 nsresult
-HTMLCanvasElement::ParseParams(JSContext* aCx,
-                               const nsAString& aType,
-                               const JS::Value& aEncoderOptions,
-                               nsAString& aParams,
-                               bool* usingCustomParseOptions)
-{
-  // Quality parameter is only valid for the image/jpeg MIME type
-  if (aType.EqualsLiteral("image/jpeg")) {
-    if (aEncoderOptions.isNumber()) {
-      double quality = aEncoderOptions.toNumber();
-      // Quality must be between 0.0 and 1.0, inclusive
-      if (quality >= 0.0 && quality <= 1.0) {
-        aParams.AppendLiteral("quality=");
-        aParams.AppendInt(NS_lround(quality * 100.0));
-      }
-    }
-  }
-
-  // If we haven't parsed the aParams check for proprietary options.
-  // The proprietary option -moz-parse-options will take a image lib encoder
-  // parse options string as is and pass it to the encoder.
-  *usingCustomParseOptions = false;
-  if (aParams.Length() == 0 && aEncoderOptions.isString()) {
-    NS_NAMED_LITERAL_STRING(mozParseOptions, "-moz-parse-options:");
-    nsAutoJSString paramString;
-    if (!paramString.init(aCx, aEncoderOptions.toString())) {
-      return NS_ERROR_FAILURE;
-    }
-    if (StringBeginsWith(paramString, mozParseOptions)) {
-      nsDependentSubstring parseOptions = Substring(paramString,
-                                                    mozParseOptions.Length(),
-                                                    paramString.Length() -
-                                                    mozParseOptions.Length());
-      aParams.Append(parseOptions);
-      *usingCustomParseOptions = true;
-    }
-  }
-
-  return NS_OK;
-}
-
-nsresult
 HTMLCanvasElement::ToDataURLImpl(JSContext* aCx,
                                  const nsAString& aMimeType,
                                  const JS::Value& aEncoderOptions,
                                  nsAString& aDataURL)
 {
   nsIntSize size = GetWidthHeight();
   if (size.height == 0 || size.width == 0) {
     aDataURL = NS_LITERAL_STRING("data:,");
@@ -659,93 +746,45 @@ HTMLCanvasElement::ToBlob(JSContext* aCx
                           ErrorResult& aRv)
 {
   // do a trust check if this is a write-only canvas
   if (mWriteOnly && !nsContentUtils::IsCallerChrome()) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
     return;
   }
 
-  nsAutoString type;
-  nsContentUtils::ASCIIToLower(aType, type);
+  nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
+  MOZ_ASSERT(global);
+
+  CanvasRenderingContextHelper::ToBlob(aCx, global, aCallback, aType,
+                                       aParams, aRv);
 
-  nsAutoString params;
-  bool usingCustomParseOptions;
-  aRv = ParseParams(aCx, type, aParams, params, &usingCustomParseOptions);
-  if (aRv.Failed()) {
-    return;
-  }
+}
 
-#ifdef DEBUG
+OffscreenCanvas*
+HTMLCanvasElement::TransferControlToOffscreen(ErrorResult& aRv)
+{
   if (mCurrentContext) {
-    // We disallow canvases of width or height zero, and set them to 1, so
-    // we will have a discrepancy with the sizes of the canvas and the context.
-    // That discrepancy is OK, the rest are not.
-    nsIntSize elementSize = GetWidthHeight();
-    MOZ_ASSERT(elementSize.width == mCurrentContext->GetWidth() ||
-               (elementSize.width == 0 && mCurrentContext->GetWidth() == 1));
-    MOZ_ASSERT(elementSize.height == mCurrentContext->GetHeight() ||
-               (elementSize.height == 0 && mCurrentContext->GetHeight() == 1));
-  }
-#endif
-
-  uint8_t* imageBuffer = nullptr;
-  int32_t format = 0;
-  if (mCurrentContext) {
-    mCurrentContext->GetImageBuffer(&imageBuffer, &format);
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return nullptr;
   }
 
-  // Encoder callback when encoding is complete.
-  class EncodeCallback : public EncodeCompleteCallback
-  {
-  public:
-    EncodeCallback(nsIGlobalObject* aGlobal, FileCallback* aCallback)
-      : mGlobal(aGlobal)
-      , mFileCallback(aCallback) {}
-
-    // This is called on main thread.
-    nsresult ReceiveBlob(already_AddRefed<Blob> aBlob)
-    {
-      nsRefPtr<Blob> blob = aBlob;
-
-      ErrorResult rv;
-      uint64_t size = blob->GetSize(rv);
-      if (rv.Failed()) {
-        rv.SuppressException();
-      } else {
-        AutoJSAPI jsapi;
-        if (jsapi.Init(mGlobal)) {
-          JS_updateMallocCounter(jsapi.cx(), size);
-        }
-      }
+  if (!mOffscreenCanvas) {
+    nsIntSize sz = GetWidthHeight();
+    nsRefPtr<AsyncCanvasRenderer> renderer = GetAsyncCanvasRenderer();
+    renderer->SetWidth(sz.width);
+    renderer->SetHeight(sz.height);
 
-      nsRefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl());
-
-      mFileCallback->Call(*newBlob, rv);
-
-      mGlobal = nullptr;
-      mFileCallback = nullptr;
-
-      return rv.StealNSResult();
-    }
+    mOffscreenCanvas = new OffscreenCanvas(sz.width, sz.height, renderer);
+    mContextObserver = new HTMLCanvasElementObserver(this);
+  } else {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+  }
 
-    nsCOMPtr<nsIGlobalObject> mGlobal;
-    nsRefPtr<FileCallback> mFileCallback;
-  };
-
-  nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject();
-  MOZ_ASSERT(global);
-  nsRefPtr<EncodeCompleteCallback> callback = new EncodeCallback(global, &aCallback);
-  aRv = ImageEncoder::ExtractDataAsync(type,
-                                       params,
-                                       usingCustomParseOptions,
-                                       imageBuffer,
-                                       format,
-                                       GetSize(),
-                                       callback);
+  return mOffscreenCanvas;
 }
 
 already_AddRefed<File>
 HTMLCanvasElement::MozGetAsFile(const nsAString& aName,
                                 const nsAString& aType,
                                 ErrorResult& aRv)
 {
   nsCOMPtr<nsISupports> file;
@@ -806,138 +845,37 @@ HTMLCanvasElement::MozGetAsBlobImpl(cons
   nsCOMPtr<nsIDOMBlob> file =
     File::CreateMemoryFile(win, imgData, (uint32_t)imgSize, aName, type,
                            PR_Now());
 
   file.forget(aResult);
   return NS_OK;
 }
 
-static bool
-GetCanvasContextType(const nsAString& str, CanvasContextType* const out_type)
-{
-  if (str.EqualsLiteral("2d")) {
-    *out_type = CanvasContextType::Canvas2D;
-    return true;
-  }
-
-  if (str.EqualsLiteral("experimental-webgl")) {
-    *out_type = CanvasContextType::WebGL1;
-    return true;
-  }
-
-#ifdef MOZ_WEBGL_CONFORMANT
-  if (str.EqualsLiteral("webgl")) {
-    /* WebGL 1.0, $2.1 "Context Creation":
-     *   If the user agent supports both the webgl and experimental-webgl
-     *   canvas context types, they shall be treated as aliases.
-     */
-    *out_type = CanvasContextType::WebGL1;
-    return true;
-  }
-#endif
-
-  if (WebGL2Context::IsSupported()) {
-    if (str.EqualsLiteral("webgl2")) {
-      *out_type = CanvasContextType::WebGL2;
-      return true;
-    }
-  }
-
-  return false;
-}
-
-static already_AddRefed<nsICanvasRenderingContextInternal>
-CreateContextForCanvas(CanvasContextType contextType, HTMLCanvasElement* canvas)
-{
-  MOZ_ASSERT(contextType != CanvasContextType::NoContext);
-  nsRefPtr<nsICanvasRenderingContextInternal> ret;
-
-  switch (contextType) {
-  case CanvasContextType::NoContext:
-    break;
-  case CanvasContextType::Canvas2D:
-    Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
-    ret = new CanvasRenderingContext2D();
-    break;
-
-  case CanvasContextType::WebGL1:
-    Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
-
-    ret = WebGL1Context::Create();
-    if (!ret)
-      return nullptr;
-    break;
-
-  case CanvasContextType::WebGL2:
-    Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
-
-    ret = WebGL2Context::Create();
-    if (!ret)
-      return nullptr;
-    break;
-  }
-  MOZ_ASSERT(ret);
-
-  ret->SetCanvasElement(canvas);
-  return ret.forget();
-}
-
 nsresult
 HTMLCanvasElement::GetContext(const nsAString& aContextId,
                               nsISupports** aContext)
 {
   ErrorResult rv;
   *aContext = GetContext(nullptr, aContextId, JS::NullHandleValue, rv).take();
   return rv.StealNSResult();
 }
 
 already_AddRefed<nsISupports>
 HTMLCanvasElement::GetContext(JSContext* aCx,
                               const nsAString& aContextId,
                               JS::Handle<JS::Value> aContextOptions,
-                              ErrorResult& rv)
+                              ErrorResult& aRv)
 {
-  CanvasContextType contextType;
-  if (!GetCanvasContextType(aContextId, &contextType))
+  if (mOffscreenCanvas) {
     return nullptr;
-
-  if (!mCurrentContext) {
-    // This canvas doesn't have a context yet.
-
-    nsRefPtr<nsICanvasRenderingContextInternal> context;
-    context = CreateContextForCanvas(contextType, this);
-    if (!context)
-      return nullptr;
-
-    // Ensure that the context participates in CC.  Note that returning a
-    // CC participant from QI doesn't addref.
-    nsXPCOMCycleCollectionParticipant* cp = nullptr;
-    CallQueryInterface(context, &cp);
-    if (!cp) {
-      rv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
-    }
-
-    mCurrentContext = context.forget();
-    mCurrentContextType = contextType;
-
-    rv = UpdateContext(aCx, aContextOptions);
-    if (rv.Failed()) {
-      rv = NS_OK; // See bug 645792
-      return nullptr;
-    }
-  } else {
-    // We already have a context of some type.
-    if (contextType != mCurrentContextType)
-      return nullptr;
   }
 
-  nsCOMPtr<nsICanvasRenderingContextInternal> context = mCurrentContext;
-  return context.forget();
+  return CanvasRenderingContextHelper::GetContext(aCx, aContextId,
+                                                  aContextOptions, aRv);
 }
 
 NS_IMETHODIMP
 HTMLCanvasElement::MozGetIPCContext(const nsAString& aContextId,
                                     nsISupports **aContext)
 {
   if(!nsContentUtils::IsCallerChrome()) {
     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
@@ -949,17 +887,17 @@ HTMLCanvasElement::MozGetIPCContext(cons
     return NS_ERROR_INVALID_ARG;
 
   CanvasContextType contextType = CanvasContextType::Canvas2D;
 
   if (!mCurrentContext) {
     // This canvas doesn't have a context yet.
 
     nsRefPtr<nsICanvasRenderingContextInternal> context;
-    context = CreateContextForCanvas(contextType, this);
+    context = CreateContext(contextType);
     if (!context) {
       *aContext = nullptr;
       return NS_OK;
     }
 
     mCurrentContext = context;
     mCurrentContext->SetIsIPC(true);
     mCurrentContextType = contextType;
@@ -971,46 +909,16 @@ HTMLCanvasElement::MozGetIPCContext(cons
     if (contextType != mCurrentContextType)
       return NS_ERROR_INVALID_ARG;
   }
 
   NS_ADDREF (*aContext = mCurrentContext);
   return NS_OK;
 }
 
-nsresult
-HTMLCanvasElement::UpdateContext(JSContext* aCx, JS::Handle<JS::Value> aNewContextOptions)
-{
-  if (!mCurrentContext)
-    return NS_OK;
-
-  nsIntSize sz = GetWidthHeight();
-
-  nsCOMPtr<nsICanvasRenderingContextInternal> currentContext = mCurrentContext;
-
-  nsresult rv = currentContext->SetIsOpaque(HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque));
-  if (NS_FAILED(rv)) {
-    mCurrentContext = nullptr;
-    return rv;
-  }
-
-  rv = currentContext->SetContextOptions(aCx, aNewContextOptions);
-  if (NS_FAILED(rv)) {
-    mCurrentContext = nullptr;
-    return rv;
-  }
-
-  rv = currentContext->SetDimensions(sz.width, sz.height);
-  if (NS_FAILED(rv)) {
-    mCurrentContext = nullptr;
-    return rv;
-  }
-
-  return rv;
-}
 
 nsIntSize
 HTMLCanvasElement::GetSize()
 {
   return GetWidthHeight();
 }
 
 bool
@@ -1104,34 +1012,75 @@ HTMLCanvasElement::GetContextAtIndex(int
 
 bool
 HTMLCanvasElement::GetIsOpaque()
 {
   if (mCurrentContext) {
     return mCurrentContext->GetIsOpaque();
   }
 
+  return GetOpaqueAttr();
+}
+
+bool
+HTMLCanvasElement::GetOpaqueAttr()
+{
   return HasAttr(kNameSpaceID_None, nsGkAtoms::moz_opaque);
 }
 
 already_AddRefed<CanvasLayer>
 HTMLCanvasElement::GetCanvasLayer(nsDisplayListBuilder* aBuilder,
                                   CanvasLayer *aOldLayer,
                                   LayerManager *aManager)
 {
-  if (!mCurrentContext)
-    return nullptr;
+  // The address of sOffscreenCanvasLayerUserDataDummy is used as the user
+  // data key for retained LayerManagers managed by FrameLayerBuilder.
+  // We don't much care about what value in it, so just assign a dummy
+  // value for it.
+  static uint8_t sOffscreenCanvasLayerUserDataDummy = 0;
+
+  if (mCurrentContext) {
+    return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
+  }
 
-  return mCurrentContext->GetCanvasLayer(aBuilder, aOldLayer, aManager);
+  if (mOffscreenCanvas) {
+    if (aOldLayer && aOldLayer->HasUserData(&sOffscreenCanvasLayerUserDataDummy)) {
+      nsRefPtr<CanvasLayer> ret = aOldLayer;
+      return ret.forget();
+    }
+
+    nsRefPtr<CanvasLayer> layer = aManager->CreateCanvasLayer();
+    if (!layer) {
+      NS_WARNING("CreateCanvasLayer failed!");
+      return nullptr;
+    }
+
+    LayerUserData* userData = nullptr;
+    layer->SetUserData(&sOffscreenCanvasLayerUserDataDummy, userData);
+    layer->SetAsyncRenderer(GetAsyncCanvasRenderer());
+    layer->Updated();
+    return layer.forget();
+  }
+
+  return nullptr;
 }
 
 bool
-HTMLCanvasElement::ShouldForceInactiveLayer(LayerManager *aManager)
+HTMLCanvasElement::ShouldForceInactiveLayer(LayerManager* aManager)
 {
-  return !mCurrentContext || mCurrentContext->ShouldForceInactiveLayer(aManager);
+  if (mCurrentContext) {
+    return mCurrentContext->ShouldForceInactiveLayer(aManager);
+  }
+
+  if (mOffscreenCanvas) {
+    // TODO: We should handle offscreen canvas case.
+    return false;
+  }
+
+  return true;
 }
 
 void
 HTMLCanvasElement::MarkContextClean()
 {
   if (!mCurrentContext)
     return;
 
@@ -1247,16 +1196,102 @@ HTMLCanvasElement::GetAsyncCanvasRendere
   if (!mAsyncCanvasRenderer) {
     mAsyncCanvasRenderer = new AsyncCanvasRenderer();
     mAsyncCanvasRenderer->mHTMLCanvasElement = this;
   }
 
   return mAsyncCanvasRenderer;
 }
 
+void
+HTMLCanvasElement::OnVisibilityChange()
+{
+  if (OwnerDoc()->Hidden()) {
+    return;
+  }
+
+  if (mOffscreenCanvas) {
+    class Runnable final : public nsCancelableRunnable
+    {
+    public:
+      Runnable(AsyncCanvasRenderer* aRenderer)
+        : mRenderer(aRenderer)
+      {}
+
+      NS_IMETHOD Run()
+      {
+        if (mRenderer && mRenderer->mContext) {
+          mRenderer->mContext->OnVisibilityChange();
+        }
+
+        return NS_OK;
+      }
+
+      void Revoke()
+      {
+        mRenderer = nullptr;
+      }
+
+    private:
+      nsRefPtr<AsyncCanvasRenderer> mRenderer;
+    };
+
+    nsRefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
+    if (mAsyncCanvasRenderer->mActiveThread) {
+      mAsyncCanvasRenderer->mActiveThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
+    }
+    return;
+  }
+
+  if (mCurrentContext) {
+    mCurrentContext->OnVisibilityChange();
+  }
+}
+
+void
+HTMLCanvasElement::OnMemoryPressure()
+{
+  if (mOffscreenCanvas) {
+    class Runnable final : public nsCancelableRunnable
+    {
+    public:
+      Runnable(AsyncCanvasRenderer* aRenderer)
+        : mRenderer(aRenderer)
+      {}
+
+      NS_IMETHOD Run()
+      {
+        if (mRenderer && mRenderer->mContext) {
+          mRenderer->mContext->OnMemoryPressure();
+        }
+
+        return NS_OK;
+      }
+
+      void Revoke()
+      {
+        mRenderer = nullptr;
+      }
+
+    private:
+      nsRefPtr<AsyncCanvasRenderer> mRenderer;
+    };
+
+    nsRefPtr<nsIRunnable> runnable = new Runnable(mAsyncCanvasRenderer);
+    if (mAsyncCanvasRenderer->mActiveThread) {
+      mAsyncCanvasRenderer->mActiveThread->Dispatch(runnable, nsIThread::DISPATCH_NORMAL);
+    }
+    return;
+  }
+
+  if (mCurrentContext) {
+    mCurrentContext->OnMemoryPressure();
+  }
+}
+
 /* static */ void
 HTMLCanvasElement::SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer)
 {
   HTMLCanvasElement *element = aRenderer->mHTMLCanvasElement;
   if (!element) {
     return;
   }
 
--- a/dom/html/HTMLCanvasElement.h
+++ b/dom/html/HTMLCanvasElement.h
@@ -3,52 +3,76 @@
 /* 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/. */
 #if !defined(mozilla_dom_HTMLCanvasElement_h)
 #define mozilla_dom_HTMLCanvasElement_h
 
 #include "mozilla/Attributes.h"
 #include "mozilla/WeakPtr.h"
+#include "nsIDOMEventListener.h"
 #include "nsIDOMHTMLCanvasElement.h"
+#include "nsIObserver.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsSize.h"
 #include "nsError.h"
 
+#include "mozilla/dom/CanvasRenderingContextHelper.h"
 #include "mozilla/gfx/Rect.h"
 
 class nsICanvasRenderingContextInternal;
 class nsITimerCallback;
 
 namespace mozilla {
 
+class WebGLContext;
+
 namespace layers {
 class AsyncCanvasRenderer;
 class CanvasLayer;
 class Image;
 class LayerManager;
 } // namespace layers
 namespace gfx {
 class SourceSurface;
 } // namespace gfx
 
 namespace dom {
 class CanvasCaptureMediaStream;
 class File;
 class FileCallback;
 class HTMLCanvasPrintState;
+class OffscreenCanvas;
 class PrintCallback;
 class RequestedFrameRefreshObserver;
 
-enum class CanvasContextType : uint8_t {
-  NoContext,
-  Canvas2D,
-  WebGL1,
-  WebGL2
+// Listen visibilitychange and memory-pressure event and inform
+// context when event is fired.
+class HTMLCanvasElementObserver final : public nsIObserver
+                                      , public nsIDOMEventListener
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+  NS_DECL_NSIDOMEVENTLISTENER
+
+  explicit HTMLCanvasElementObserver(HTMLCanvasElement* aElement);
+  void Destroy();
+
+  void RegisterVisibilityChangeEvent();
+  void UnregisterVisibilityChangeEvent();
+
+  void RegisterMemoryPressureEvent();
+  void UnregisterMemoryPressureEvent();
+
+private:
+  ~HTMLCanvasElementObserver();
+
+  HTMLCanvasElement* mElement;
 };
 
 /*
  * FrameCaptureListener is used by captureStream() as a way of getting video
  * frames from the canvas. On a refresh driver tick after something has been
  * drawn to the canvas since the last such tick, all registered
  * FrameCaptureListeners whose `mFrameCaptureRequested` equals `true`,
  * will be given a copy of the just-painted canvas.
@@ -80,17 +104,18 @@ public:
 
 protected:
   virtual ~FrameCaptureListener() {}
 
   bool mFrameCaptureRequested;
 };
 
 class HTMLCanvasElement final : public nsGenericHTMLElement,
-                                public nsIDOMHTMLCanvasElement
+                                public nsIDOMHTMLCanvasElement,
+                                public CanvasRenderingContextHelper
 {
   enum {
     DEFAULT_CANVAS_WIDTH = 300,
     DEFAULT_CANVAS_HEIGHT = 150
   };
 
   typedef layers::AsyncCanvasRenderer AsyncCanvasRenderer;
   typedef layers::CanvasLayer CanvasLayer;
@@ -113,48 +138,68 @@ public:
 
   // WebIDL
   uint32_t Height()
   {
     return GetUnsignedIntAttr(nsGkAtoms::height, DEFAULT_CANVAS_HEIGHT);
   }
   void SetHeight(uint32_t aHeight, ErrorResult& aRv)
   {
+    if (mOffscreenCanvas) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
     SetUnsignedIntAttr(nsGkAtoms::height, aHeight, aRv);
   }
   uint32_t Width()
   {
     return GetUnsignedIntAttr(nsGkAtoms::width, DEFAULT_CANVAS_WIDTH);
   }
   void SetWidth(uint32_t aWidth, ErrorResult& aRv)
   {
+    if (mOffscreenCanvas) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
     SetUnsignedIntAttr(nsGkAtoms::width, aWidth, aRv);
   }
-  already_AddRefed<nsISupports>
+
+  virtual already_AddRefed<nsISupports>
   GetContext(JSContext* aCx, const nsAString& aContextId,
              JS::Handle<JS::Value> aContextOptions,
-             ErrorResult& aRv);
+             ErrorResult& aRv) override;
+
   void ToDataURL(JSContext* aCx, const nsAString& aType,
                  JS::Handle<JS::Value> aParams,
                  nsAString& aDataURL, ErrorResult& aRv)
   {
     aRv = ToDataURL(aType, aParams, aCx, aDataURL);
   }
+
   void ToBlob(JSContext* aCx,
               FileCallback& aCallback,
               const nsAString& aType,
               JS::Handle<JS::Value> aParams,
               ErrorResult& aRv);
 
+  OffscreenCanvas* TransferControlToOffscreen(ErrorResult& aRv);
+
   bool MozOpaque() const
   {
     return GetBoolAttr(nsGkAtoms::moz_opaque);
   }
   void SetMozOpaque(bool aValue, ErrorResult& aRv)
   {
+    if (mOffscreenCanvas) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return;
+    }
+
     SetHTMLBoolAttr(nsGkAtoms::moz_opaque, aValue, aRv);
   }
   already_AddRefed<File> MozGetAsFile(const nsAString& aName,
                                       const nsAString& aType,
                                       ErrorResult& aRv);
   already_AddRefed<nsISupports> MozGetIPCContext(const nsAString& aContextId,
                                                  ErrorResult& aRv)
   {
@@ -201,16 +246,17 @@ public:
   int32_t CountContexts ();
   nsICanvasRenderingContextInternal *GetContextAtIndex (int32_t index);
 
   /*
    * Returns true if the canvas context content is guaranteed to be opaque
    * across its entire area.
    */
   bool GetIsOpaque();
+  virtual bool GetOpaqueAttr() override;
 
   virtual already_AddRefed<gfx::SourceSurface> GetSurfaceSnapshot(bool* aPremultAlpha = nullptr);
 
   /*
    * Register a FrameCaptureListener with this canvas.
    * The canvas hooks into the RefreshDriver while there are
    * FrameCaptureListeners registered.
    * The registered FrameCaptureListeners are stored as WeakPtrs, thus it's the
@@ -279,53 +325,54 @@ public:
   // copies for future frames when no drawing has occurred.
   void MarkContextCleanForFrameCapture();
 
   // Starts returning false when something is drawn.
   bool IsContextCleanForFrameCapture();
 
   nsresult GetContext(const nsAString& aContextId, nsISupports** aContext);
 
+  void OnVisibilityChange();
+
+  void OnMemoryPressure();
+
   static void SetAttrFromAsyncCanvasRenderer(AsyncCanvasRenderer *aRenderer);
 
 protected:
   virtual ~HTMLCanvasElement();
 
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
-  nsIntSize GetWidthHeight();
+  virtual nsIntSize GetWidthHeight() override;
 
-  nsresult UpdateContext(JSContext* aCx, JS::Handle<JS::Value> options);
-  nsresult ParseParams(JSContext* aCx,
-                       const nsAString& aType,
-                       const JS::Value& aEncoderOptions,
-                       nsAString& aParams,
-                       bool* usingCustomParseOptions);
+  virtual already_AddRefed<nsICanvasRenderingContextInternal>
+  CreateContext(CanvasContextType aContextType) override;
+
   nsresult ExtractData(nsAString& aType,
                        const nsAString& aOptions,
                        nsIInputStream** aStream);
   nsresult ToDataURLImpl(JSContext* aCx,
                          const nsAString& aMimeType,
                          const JS::Value& aEncoderOptions,
                          nsAString& aDataURL);
   nsresult MozGetAsBlobImpl(const nsAString& aName,
                             const nsAString& aType,
                             nsISupports** aResult);
   void CallPrintCallback();
 
   AsyncCanvasRenderer* GetAsyncCanvasRenderer();
 
-  CanvasContextType mCurrentContextType;
   nsRefPtr<HTMLCanvasElement> mOriginalCanvas;
   nsRefPtr<PrintCallback> mPrintCallback;
-  nsCOMPtr<nsICanvasRenderingContextInternal> mCurrentContext;
   nsRefPtr<HTMLCanvasPrintState> mPrintState;
   nsTArray<WeakPtr<FrameCaptureListener>> mRequestedFrameListeners;
   nsRefPtr<RequestedFrameRefreshObserver> mRequestedFrameRefreshObserver;
   nsRefPtr<AsyncCanvasRenderer> mAsyncCanvasRenderer;
+  nsRefPtr<OffscreenCanvas> mOffscreenCanvas;
+  nsRefPtr<HTMLCanvasElementObserver> mContextObserver;
 
 public:
   // Record whether this canvas should be write-only or not.
   // We set this when script paints an image from a different origin.
   // We also transitively set it when script paints a canvas which
   // is itself write-only.
   bool                     mWriteOnly;
 
--- a/dom/webidl/HTMLCanvasElement.webidl
+++ b/dom/webidl/HTMLCanvasElement.webidl
@@ -41,16 +41,23 @@ partial interface HTMLCanvasElement {
   [ChromeOnly, Throws]
   nsISupports? MozGetIPCContext(DOMString contextId);
            attribute PrintCallback? mozPrintCallback;
 
   [Throws, UnsafeInPrerendering, Pref="canvas.capturestream.enabled"]
   CanvasCaptureMediaStream captureStream(optional double frameRate);
 };
 
+// For OffscreenCanvas
+// Reference: https://wiki.whatwg.org/wiki/OffscreenCanvas
+partial interface HTMLCanvasElement {
+  [Pref="gfx.offscreencanvas.enabled", Throws]
+  OffscreenCanvas transferControlToOffscreen();
+};
+
 [ChromeOnly]
 interface MozCanvasPrintState
 {
   // A canvas rendering context.
   readonly attribute nsISupports context;
 
   // To be called when rendering to the context is done.
   void done();
new file mode 100644
--- /dev/null
+++ b/dom/webidl/OffscreenCanvas.webidl
@@ -0,0 +1,28 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * For more information on this interface, please see
+ * https://wiki.whatwg.org/wiki/OffscreenCanvas
+ *
+ * Current implementation focus on transfer canvas from main thread to worker.
+ * So there are some spec doesn't implement, such as [Constructor], toBlob() and
+ * transferToImageBitmap in OffscreenCanvas. Bug 1172796 will implement
+ * remaining spec.
+ */
+
+[Exposed=(Window,Worker),
+ Func="mozilla::dom::OffscreenCanvas::PrefEnabled"]
+interface OffscreenCanvas : EventTarget {
+  [Pure, SetterThrows]
+  attribute unsigned long width;
+  [Pure, SetterThrows]
+  attribute unsigned long height;
+
+  [Throws]
+  nsISupports? getContext(DOMString contextId,
+                          optional any contextOptions = null);
+};
+
+// OffscreenCanvas implements Transferable;
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -40,53 +40,73 @@ dictionary WebGLContextAttributes {
     boolean depth = true;
     boolean stencil = false;
     boolean antialias = true;
     boolean premultipliedAlpha = true;
     boolean preserveDrawingBuffer = false;
     boolean failIfMajorPerformanceCaveat = false;
 };
 
+[Exposed=(Window,Worker),
+ Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLBuffer {
 };
 
+[Exposed=(Window,Worker),
+ Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLFramebuffer {
 };
 
+[Exposed=(Window,Worker),
+ Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLProgram {
 };
 
+[Exposed=(Window,Worker),
+ Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLRenderbuffer {
 };
 
+[Exposed=(Window,Worker),
+ Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLShader {
 };
 
+[Exposed=(Window,Worker),
+ Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLTexture {
 };
 
+[Exposed=(Window,Worker),
+ Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLUniformLocation {
 };
 
 [NoInterfaceObject]
 interface WebGLVertexArrayObjectOES {
 };
 
+[Exposed=(Window,Worker),
+ Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLActiveInfo {
     readonly attribute GLint size;
     readonly attribute GLenum type;
     readonly attribute DOMString name;
 };
 
+[Exposed=(Window,Worker),
+ Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLShaderPrecisionFormat {
     readonly attribute GLint rangeMin;
     readonly attribute GLint rangeMax;
     readonly attribute GLint precision;
 };
 
+[Exposed=(Window,Worker),
+ Func="mozilla::dom::OffscreenCanvas::PrefEnabledOnWorkerThread"]
 interface WebGLRenderingContext {
 
     /* ClearBufferMask */
     const GLenum DEPTH_BUFFER_BIT               = 0x00000100;
     const GLenum STENCIL_BUFFER_BIT             = 0x00000400;
     const GLenum COLOR_BUFFER_BIT               = 0x00004000;
 
     /* BeginMode */
@@ -499,17 +519,17 @@ interface WebGLRenderingContext {
     /* WebGL-specific enums */
     const GLenum UNPACK_FLIP_Y_WEBGL            = 0x9240;
     const GLenum UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241;
     const GLenum CONTEXT_LOST_WEBGL             = 0x9242;
     const GLenum UNPACK_COLORSPACE_CONVERSION_WEBGL = 0x9243;
     const GLenum BROWSER_DEFAULT_WEBGL          = 0x9244;
 
     // The canvas might actually be null in some cases, apparently.
-    readonly attribute HTMLCanvasElement? canvas;
+    readonly attribute (HTMLCanvasElement or OffscreenCanvas)? canvas;
     readonly attribute GLsizei drawingBufferWidth;
     readonly attribute GLsizei drawingBufferHeight;
 
     [WebGLHandlesContextLoss] WebGLContextAttributes? getContextAttributes();
     [WebGLHandlesContextLoss] boolean isContextLost();
 
     sequence<DOMString>? getSupportedExtensions();
 
@@ -761,16 +781,24 @@ interface WebGLRenderingContext {
     void vertexAttrib4fv(GLuint indx, Float32Array values);
     void vertexAttrib4fv(GLuint indx, sequence<GLfloat> values);
     void vertexAttribPointer(GLuint indx, GLint size, GLenum type,
                              GLboolean normalized, GLsizei stride, GLintptr offset);
 
     void viewport(GLint x, GLint y, GLsizei width, GLsizei height);
 };
 
+// For OffscreenCanvas
+// Reference: https://wiki.whatwg.org/wiki/OffscreenCanvas
+[Exposed=(Window,Worker)]
+partial interface WebGLRenderingContext {
+  [Func="mozilla::dom::OffscreenCanvas::PrefEnabled"]
+  void commit();
+};
+
 /*[Constructor(DOMString type, optional WebGLContextEventInit eventInit)]
 interface WebGLContextEvent : Event {
     readonly attribute DOMString statusMessage;
     };*/
 
 // EventInit is defined in the DOM4 specification.
 /*dictionary WebGLContextEventInit : EventInit {
     DOMString statusMessage;
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -339,16 +339,17 @@ WEBIDL_FILES = [
     'NodeIterator.webidl',
     'NodeList.webidl',
     'Notification.webidl',
     'NotificationEvent.webidl',
     'NotifyPaintEvent.webidl',
     'OfflineAudioCompletionEvent.webidl',
     'OfflineAudioContext.webidl',
     'OfflineResourceList.webidl',
+    'OffscreenCanvas.webidl',
     'OscillatorNode.webidl',
     'PaintRequest.webidl',
     'PaintRequestList.webidl',
     'PannerNode.webidl',
     'ParentNode.webidl',
     'Performance.webidl',
     'PerformanceCompositeTiming.webidl',
     'PerformanceEntry.webidl',
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -1,15 +1,16 @@
 /* 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 "GLLibraryEGL.h"
 
 #include "gfxCrashReporterUtils.h"
+#include "gfxUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Assertions.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsIGfxInfo.h"
 #include "nsPrintfCString.h"
 #ifdef XP_WIN
 #include "nsWindowsHelpers.h"
@@ -123,17 +124,19 @@ GetAndInitWARPDisplay(GLLibraryEGL& egl,
 
     return display;
 }
 
 static bool
 IsAccelAngleSupported(const nsCOMPtr<nsIGfxInfo>& gfxInfo)
 {
     int32_t angleSupport;
-    gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &angleSupport);
+    gfxUtils::ThreadSafeGetFeatureStatus(gfxInfo,
+                                         nsIGfxInfo::FEATURE_WEBGL_ANGLE,
+                                         &angleSupport);
     return (angleSupport == nsIGfxInfo::FEATURE_STATUS_OK);
 }
 
 static EGLDisplay
 GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
 {
     EGLDisplay display = egl.fGetDisplay(displayType);
     if (display == EGL_NO_DISPLAY)
--- a/gfx/layers/AsyncCanvasRenderer.h
+++ b/gfx/layers/AsyncCanvasRenderer.h
@@ -4,18 +4,20 @@
  * 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 MOZILLA_LAYERS_ASYNCCANVASRENDERER_H_
 #define MOZILLA_LAYERS_ASYNCCANVASRENDERER_H_
 
 #include "mozilla/gfx/Point.h"          // for IntSize
 #include "mozilla/RefPtr.h"             // for nsAutoPtr, nsRefPtr, etc
+#include "nsCOMPtr.h"                   // for nsCOMPtr
 
 class nsICanvasRenderingContextInternal;
+class nsIThread;
 
 namespace mozilla {
 
 namespace gl {
 class GLContext;
 }
 
 namespace dom {
@@ -77,16 +79,17 @@ public:
   dom::HTMLCanvasElement* mHTMLCanvasElement;
 
   nsICanvasRenderingContextInternal* mContext;
 
   // We need to keep a reference to the context around here, otherwise the
   // canvas' surface texture destructor will deref and destroy it too early
   RefPtr<gl::GLContext> mGLContext;
 
+  nsCOMPtr<nsIThread> mActiveThread;
 private:
 
   virtual ~AsyncCanvasRenderer();
 
   uint32_t mWidth;
   uint32_t mHeight;
   uint64_t mCanvasClientAsyncID;
 
--- a/gfx/src/gfxCrashReporterUtils.cpp
+++ b/gfx/src/gfxCrashReporterUtils.cpp
@@ -79,16 +79,32 @@ public:
     if (!observerService)
       return NS_OK;
     nsRefPtr<ObserverToDestroyFeaturesAlreadyReported> observer = new ObserverToDestroyFeaturesAlreadyReported;
     observerService->AddObserver(observer, "xpcom-shutdown", false);
     return NS_OK;
   }
 };
 
+class AppendAppNotesRunnable : public nsCancelableRunnable {
+public:
+  explicit AppendAppNotesRunnable(nsAutoCString aFeatureStr)
+    : mFeatureString(aFeatureStr)
+  {
+  }
+
+  NS_IMETHOD Run() override {
+    CrashReporter::AppendAppNotesToCrashReport(mFeatureString);
+    return NS_OK;
+  }
+
+private:
+  nsCString mFeatureString;
+};
+
 void
 ScopedGfxFeatureReporter::WriteAppNote(char statusChar)
 {
   StaticMutexAutoLock al(gFeaturesAlreadyReportedMutex);
 
   if (!gFeaturesAlreadyReported) {
     gFeaturesAlreadyReported = new nsTArray<nsCString>;
     nsCOMPtr<nsIRunnable> r = new RegisterObserverRunnable();
@@ -97,17 +113,18 @@ ScopedGfxFeatureReporter::WriteAppNote(c
 
   nsAutoCString featureString;
   featureString.AppendPrintf("%s%c ",
                              mFeature,
                              statusChar);
 
   if (!gFeaturesAlreadyReported->Contains(featureString)) {
     gFeaturesAlreadyReported->AppendElement(featureString);
-    CrashReporter::AppendAppNotesToCrashReport(featureString);
+    nsCOMPtr<nsIRunnable> r = new AppendAppNotesRunnable(featureString);
+    NS_DispatchToMainThread(r);
   }
 }
 
 } // end namespace mozilla
 
 #else
 
 namespace mozilla {
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -239,16 +239,17 @@ private:
   DECL_GFX_PREF(Once, "gfx.direct2d.force-enabled",            Direct2DForceEnabled, bool, false);
   DECL_GFX_PREF(Live, "gfx.direct2d.use1_1",                   Direct2DUse1_1, bool, false);
   DECL_GFX_PREF(Live, "gfx.draw-color-bars",                   CompositorDrawColorBars, bool, false);
   DECL_GFX_PREF(Live, "gfx.gralloc.fence-with-readpixels",     GrallocFenceWithReadPixels, bool, false);
   DECL_GFX_PREF(Live, "gfx.layerscope.enabled",                LayerScopeEnabled, bool, false);
   DECL_GFX_PREF(Live, "gfx.layerscope.port",                   LayerScopePort, int32_t, 23456);
   // Note that        "gfx.logging.level" is defined in Logging.h
   DECL_GFX_PREF(Once, "gfx.logging.crash.length",              GfxLoggingCrashLength, uint32_t, 6);
+  DECL_GFX_PREF(Live, "gfx.offscreencanvas.enabled",           OffscreenCanvasEnabled, bool, false);
   DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled",             PerfWarnings, bool, false);
   DECL_GFX_PREF(Live, "gfx.SurfaceTexture.detach.enabled",     SurfaceTextureDetachEnabled, bool, true);
   DECL_GFX_PREF(Live, "gfx.testing.device-reset",              DeviceResetForTesting, int32_t, 0);
   DECL_GFX_PREF(Live, "gfx.testing.device-fail",               DeviceFailForTesting, bool, false);
 
   // These times should be in milliseconds
   DECL_GFX_PREF(Once, "gfx.touch.resample.delay-threshold",    TouchResampleVsyncDelayThreshold, int32_t, 20);
   DECL_GFX_PREF(Once, "gfx.touch.resample.max-predict",        TouchResampleMaxPredict, int32_t, 8);
@@ -382,22 +383,42 @@ private:
   DECL_GFX_PREF(Live, "mousewheel.transaction.timeout",        MouseWheelTransactionTimeoutMs, int32_t, (int32_t)1500);
 
   DECL_GFX_PREF(Live, "nglayout.debug.widget_update_flashing", WidgetUpdateFlashing, bool, false);
 
   DECL_GFX_PREF(Live, "test.events.async.enabled",             TestEventsAsyncEnabled, bool, false);
   DECL_GFX_PREF(Live, "test.mousescroll",                      MouseScrollTestingEnabled, bool, false);
 
   DECL_GFX_PREF(Live, "ui.click_hold_context_menus.delay",     UiClickHoldContextMenusDelay, int32_t, 500);
-  DECL_GFX_PREF(Once, "webgl.angle.force-d3d11",               WebGLANGLEForceD3D11, bool, false);
-  DECL_GFX_PREF(Once, "webgl.angle.try-d3d11",                 WebGLANGLETryD3D11, bool, false);
+
+  // WebGL (for pref access from Worker threads)
+  DECL_GFX_PREF(Live, "webgl.all-angle-options",               WebGLAllANGLEOptions, bool, false);
+  DECL_GFX_PREF(Live, "webgl.angle.force-d3d11",               WebGLANGLEForceD3D11, bool, false);
+  DECL_GFX_PREF(Live, "webgl.angle.try-d3d11",                 WebGLANGLETryD3D11, bool, false);
   DECL_GFX_PREF(Once, "webgl.angle.force-warp",                WebGLANGLEForceWARP, bool, false);
+  DECL_GFX_PREF(Live, "webgl.bypass-shader-validation",        WebGLBypassShaderValidator, bool, true);
+  DECL_GFX_PREF(Live, "webgl.can-lose-context-in-foreground",  WebGLCanLoseContextInForeground, bool, true);
+  DECL_GFX_PREF(Live, "webgl.default-no-alpha",                WebGLDefaultNoAlpha, bool, false);
+  DECL_GFX_PREF(Live, "webgl.disable-angle",                   WebGLDisableANGLE, bool, false);
+  DECL_GFX_PREF(Live, "webgl.disable-extensions",              WebGLDisableExtensions, bool, false);
+
   DECL_GFX_PREF(Live, "webgl.disable-fail-if-major-performance-caveat",
                 WebGLDisableFailIfMajorPerformanceCaveat, bool, false);
-  DECL_GFX_PREF(Once, "webgl.force-layers-readback",           WebGLForceLayersReadback, bool, false);
+  DECL_GFX_PREF(Live, "webgl.disabled",                        WebGLDisabled, bool, false);
+
+  DECL_GFX_PREF(Live, "webgl.enable-draft-extensions",         WebGLDraftExtensionsEnabled, bool, false);
+  DECL_GFX_PREF(Live, "webgl.enable-privileged-extensions",    WebGLPrivilegedExtensionsEnabled, bool, false);
+  DECL_GFX_PREF(Live, "webgl.force-enabled",                   WebGLForceEnabled, bool, false);
+  DECL_GFX_PREF(Live, "webgl.force-layers-readback",           WebGLForceLayersReadback, bool, false);
+  DECL_GFX_PREF(Live, "webgl.lose-context-on-memory-pressure", WebGLLoseContextOnMemoryPressure, bool, false);
+  DECL_GFX_PREF(Live, "webgl.max-warnings-per-context",        WebGLMaxWarningsPerContext, uint32_t, 32);
+  DECL_GFX_PREF(Live, "webgl.min_capability_mode",             WebGLMinCapabilityMode, bool, false);
+  DECL_GFX_PREF(Live, "webgl.msaa-force",                      WebGLForceMSAA, bool, false);
+  DECL_GFX_PREF(Live, "webgl.prefer-16bpp",                    WebGLPrefer16bpp, bool, false);
+  DECL_GFX_PREF(Live, "webgl.restore-context-when-visible",    WebGLRestoreWhenVisible, bool, true);
 
   // WARNING:
   // Please make sure that you've added your new preference to the list above in alphabetical order.
   // Please do not just append it to the end of the list.
 
 public:
   // Manage the singleton:
   static gfxPrefs& GetSingleton()
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -7,25 +7,28 @@
 
 #include "cairo.h"
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 #include "gfxPlatform.h"
 #include "gfxDrawable.h"
 #include "imgIEncoder.h"
 #include "mozilla/Base64.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRunnable.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/DataSurfaceHelpers.h"
 #include "mozilla/gfx/Logging.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/Vector.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIClipboardHelper.h"
 #include "nsIFile.h"
+#include "nsIGfxInfo.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsRegion.h"
 #include "nsServiceManagerUtils.h"
 #include "yuv_convert.h"
 #include "ycbcr_to_rgb565.h"
 #include "GeckoProfiler.h"
 #include "ImageContainer.h"
@@ -1538,16 +1541,72 @@ gfxUtils::CopyAsDataURI(DrawTarget* aDT)
   RefPtr<SourceSurface> surface = aDT->Snapshot();
   if (surface) {
     CopyAsDataURI(surface);
   } else {
     NS_WARNING("Failed to get surface!");
   }
 }
 
+class GetFeatureStatusRunnable final : public dom::workers::WorkerMainThreadRunnable
+{
+public:
+    GetFeatureStatusRunnable(dom::workers::WorkerPrivate* workerPrivate,
+                             const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+                             int32_t feature,
+                             int32_t* status)
+      : WorkerMainThreadRunnable(workerPrivate)
+      , mGfxInfo(gfxInfo)
+      , mFeature(feature)
+      , mStatus(status)
+      , mNSResult(NS_OK)
+    {
+    }
+
+    bool MainThreadRun() override
+    {
+      if (mGfxInfo) {
+        mNSResult = mGfxInfo->GetFeatureStatus(mFeature, mStatus);
+      }
+      return true;
+    }
+
+    nsresult GetNSResult() const
+    {
+      return mNSResult;
+    }
+
+protected:
+    ~GetFeatureStatusRunnable() {}
+
+private:
+    nsCOMPtr<nsIGfxInfo> mGfxInfo;
+    int32_t mFeature;
+    int32_t* mStatus;
+    nsresult mNSResult;
+};
+
+/* static */ nsresult
+gfxUtils::ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+                                     int32_t feature, int32_t* status)
+{
+  if (!NS_IsMainThread()) {
+    dom::workers::WorkerPrivate* workerPrivate =
+      dom::workers::GetCurrentThreadWorkerPrivate();
+    nsRefPtr<GetFeatureStatusRunnable> runnable =
+      new GetFeatureStatusRunnable(workerPrivate, gfxInfo, feature, status);
+
+    runnable->Dispatch(workerPrivate->GetJSContext());
+
+    return runnable->GetNSResult();
+  }
+
+  return gfxInfo->GetFeatureStatus(feature, status);
+}
+
 /* static */ bool
 gfxUtils::DumpDisplayList() {
   return gfxPrefs::LayoutDumpDisplayList();
 }
 
 FILE *gfxUtils::sDumpPaintFile = stderr;
 
 #ifdef MOZ_DUMP_PAINTING
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -12,16 +12,17 @@
 #include "mozilla/gfx/2D.h"
 #include "mozilla/RefPtr.h"
 #include "nsColor.h"
 #include "nsPrintfCString.h"
 #include "mozilla/gfx/Rect.h"
 
 class gfxASurface;
 class gfxDrawable;
+class nsIGfxInfo;
 class nsIntRegion;
 class nsIPresShell;
 
 namespace mozilla {
 namespace layers {
 struct PlanarYCbCrData;
 } // namespace layers
 namespace image {
@@ -276,16 +277,20 @@ public:
     static void DumpAsDataURI(DrawTarget* aDT, FILE* aFile);
     static inline void DumpAsDataURI(DrawTarget* aDT) {
         DumpAsDataURI(aDT, stdout);
     }
     static nsCString GetAsDataURI(SourceSurface* aSourceSurface);
     static nsCString GetAsDataURI(DrawTarget* aDT);
     static nsCString GetAsLZ4Base64Str(DataSourceSurface* aSourceSurface);
 
+    static nsresult ThreadSafeGetFeatureStatus(const nsCOMPtr<nsIGfxInfo>& gfxInfo,
+                                               int32_t feature,
+                                               int32_t* status);
+
     /**
      * Copy to the clipboard as a PNG encoded Data URL.
      */
     static void CopyAsDataURI(SourceSurface* aSourceSurface);
     static void CopyAsDataURI(DrawTarget* aDT);
 
     static bool DumpDisplayList();
 
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -267,16 +267,17 @@ include('/ipc/chromium/chromium-config.m
 
 FINAL_LIBRARY = 'xul'
 
 GENERATED_FILES = [
     'DeprecatedPremultiplyTables.h',
 ]
 
 LOCAL_INCLUDES += [
+    '/dom/workers',
     '/dom/xml',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
   LOCAL_INCLUDES += ['/widget/gonk']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3', 'gonk', 'qt'):
     DEFINES['MOZ_ENABLE_FREETYPE'] = True
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4160,16 +4160,18 @@ pref("webgl.renderer-string-override", "
 pref("webgl.vendor-string-override", "");
 
 #ifdef XP_WIN
 pref("webgl.angle.try-d3d11", true);
 pref("webgl.angle.force-d3d11", false);
 pref("webgl.angle.force-warp", false);
 #endif
 
+pref("gfx.offscreencanvas.enabled", false);
+
 #ifdef MOZ_WIDGET_GONK
 pref("gfx.gralloc.fence-with-readpixels", false);
 #endif
 
 // Stagefright prefs
 pref("stagefright.force-enabled", false);
 pref("stagefright.disabled", false);