merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 14 Jul 2014 14:34:36 +0200
changeset 215688 415c2a4dc7784ca4c129fd1004fc364a18ce2b75
parent 215599 f7aef4fc9d4718fada8a87c235f37067c48ddda3 (current diff)
parent 215687 665539a1bbb3f7648209ae10a216358f5747f7c4 (diff)
child 215708 340b19c14d3d388b7ae1a15bff9695cb0141ac0a
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone33.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
configure.in
dom/interfaces/html/nsIDOMHTMLAudioElement.idl
dom/interfaces/html/nsIDOMHTMLVideoElement.idl
gfx/gl/SurfaceFactory.cpp
gfx/gl/SurfaceFactory.h
gfx/thebes/gfxPath.cpp
gfx/thebes/gfxPath.h
mfbt/lz4_encoder.h
--- a/configure.in
+++ b/configure.in
@@ -7793,16 +7793,35 @@ if test "$SOLARIS_SUNPRO_CC"; then
   PROFILE_USE_LDFLAGS="-xprofile=use:$_objdir/$enable_application"
 fi
 
 AC_SUBST(PROFILE_GEN_CFLAGS)
 AC_SUBST(PROFILE_GEN_LDFLAGS)
 AC_SUBST(PROFILE_USE_CFLAGS)
 AC_SUBST(PROFILE_USE_LDFLAGS)
 
+dnl =============================================
+dnl Support for -fno-integrated-as (recent clang)
+dnl =============================================
+dnl Under clang 3.4+, use -fno-integrated-as to
+dnl build libvpx's vp8_asm_enc_offsets.c
+
+_SAVE_CFLAGS="$CFLAGS"
+CFLAGS="$CFLAGS -fno-integrated-as"
+
+AC_MSG_CHECKING([whether C compiler supports -fno-integrated-as])
+AC_TRY_COMPILE([], [return 0;],
+               [ NO_INTEGRATED_AS_CFLAGS="-fno-integrated-as"
+                 result="yes" ], result="no")
+AC_MSG_RESULT([$result])
+
+CFLAGS="$_SAVE_CFLAGS"
+
+AC_SUBST(NO_INTEGRATED_AS_CFLAGS)
+
 fi # ! SKIP_COMPILER_CHECKS
 
 AC_DEFINE(CPP_THROW_NEW, [throw()])
 AC_LANG_C
 
 if test "$COMPILE_ENVIRONMENT"; then
 MOZ_EXPAND_LIBS
 fi # COMPILE_ENVIRONMENT
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -61,16 +61,17 @@ class nsIPresShell;
 class nsIPrincipal;
 class nsIRequest;
 class nsIStreamListener;
 class nsIStructuredCloneContainer;
 class nsIStyleRule;
 class nsIStyleSheet;
 class nsIURI;
 class nsIVariant;
+class nsLocation;
 class nsViewManager;
 class nsPresContext;
 class nsRange;
 class nsScriptLoader;
 class nsSMILAnimationController;
 class nsStyleSet;
 class nsTextNode;
 class nsWindowSizes;
@@ -2179,17 +2180,17 @@ public:
     CreateCDATASection(const nsAString& aData, mozilla::ErrorResult& rv);
   already_AddRefed<mozilla::dom::Attr>
     CreateAttribute(const nsAString& aName, mozilla::ErrorResult& rv);
   already_AddRefed<mozilla::dom::Attr>
     CreateAttributeNS(const nsAString& aNamespaceURI,
                       const nsAString& aQualifiedName,
                       mozilla::ErrorResult& rv);
   void GetInputEncoding(nsAString& aInputEncoding);
-  already_AddRefed<nsIDOMLocation> GetLocation() const;
+  already_AddRefed<nsLocation> GetLocation() const;
   void GetReferrer(nsAString& aReferrer) const;
   void GetLastModified(nsAString& aLastModified) const;
   void GetReadyState(nsAString& aReadyState) const;
   // Not const because otherwise the compiler can't figure out whether to call
   // this GetTitle or the nsAString version from non-const methods, since
   // neither is an exact match.
   virtual void GetTitle(nsString& aTitle) = 0;
   virtual void SetTitle(const nsAString& aTitle, mozilla::ErrorResult& rv) = 0;
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -123,17 +123,17 @@ Link::GetURI() const
   Link *self = const_cast<Link *>(this);
   Element *element = self->mElement;
   mCachedURI = element->GetHrefURI();
 
   return mCachedURI;
 }
 
 void
-Link::SetProtocol(const nsAString &aProtocol)
+Link::SetProtocol(const nsAString &aProtocol, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   nsAString::const_iterator start, end;
@@ -142,83 +142,83 @@ Link::SetProtocol(const nsAString &aProt
   nsAString::const_iterator iter(start);
   (void)FindCharInReadable(':', iter, end);
   (void)uri->SetScheme(NS_ConvertUTF16toUTF8(Substring(start, iter)));
 
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetPassword(const nsAString &aPassword)
+Link::SetPassword(const nsAString &aPassword, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   uri->SetPassword(NS_ConvertUTF16toUTF8(aPassword));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetUsername(const nsAString &aUsername)
+Link::SetUsername(const nsAString &aUsername, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   uri->SetUsername(NS_ConvertUTF16toUTF8(aUsername));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetHost(const nsAString &aHost)
+Link::SetHost(const nsAString &aHost, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   (void)uri->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetHostname(const nsAString &aHostname)
+Link::SetHostname(const nsAString &aHostname, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   (void)uri->SetHost(NS_ConvertUTF16toUTF8(aHostname));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetPathname(const nsAString &aPathname)
+Link::SetPathname(const nsAString &aPathname, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (!url) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   (void)url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetSearch(const nsAString& aSearch)
+Link::SetSearch(const nsAString& aSearch, ErrorResult& aError)
 {
   SetSearchInternal(aSearch);
   UpdateURLSearchParams();
 }
 
 void
 Link::SetSearchInternal(const nsAString& aSearch)
 {
@@ -229,17 +229,17 @@ Link::SetSearchInternal(const nsAString&
     return;
   }
 
   (void)url->SetQuery(NS_ConvertUTF16toUTF8(aSearch));
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetPort(const nsAString &aPort)
+Link::SetPort(const nsAString &aPort, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   nsresult rv;
@@ -254,91 +254,91 @@ Link::SetPort(const nsAString &aPort)
     }
   }
 
   (void)uri->SetPort(port);
   SetHrefAttribute(uri);
 }
 
 void
-Link::SetHash(const nsAString &aHash)
+Link::SetHash(const nsAString &aHash, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURIToMutate());
   if (!uri) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   (void)uri->SetRef(NS_ConvertUTF16toUTF8(aHash));
   SetHrefAttribute(uri);
 }
 
 void
-Link::GetOrigin(nsAString &aOrigin)
+Link::GetOrigin(nsAString &aOrigin, ErrorResult& aError)
 {
   aOrigin.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     return;
   }
 
   nsString origin;
   nsContentUtils::GetUTFNonNullOrigin(uri, origin);
   aOrigin.Assign(origin);
 }
 
 void
-Link::GetProtocol(nsAString &_protocol)
+Link::GetProtocol(nsAString &_protocol, ErrorResult& aError)
 {
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     _protocol.AssignLiteral("http");
   }
   else {
     nsAutoCString scheme;
     (void)uri->GetScheme(scheme);
     CopyASCIItoUTF16(scheme, _protocol);
   }
   _protocol.Append(char16_t(':'));
   return;
 }
 
 void
-Link::GetUsername(nsAString& aUsername)
+Link::GetUsername(nsAString& aUsername, ErrorResult& aError)
 {
   aUsername.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     return;
   }
 
   nsAutoCString username;
   uri->GetUsername(username);
   CopyASCIItoUTF16(username, aUsername);
 }
 
 void
-Link::GetPassword(nsAString &aPassword)
+Link::GetPassword(nsAString &aPassword, ErrorResult& aError)
 {
   aPassword.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     return;
   }
 
   nsAutoCString password;
   uri->GetPassword(password);
   CopyASCIItoUTF16(password, aPassword);
 }
 
 void
-Link::GetHost(nsAString &_host)
+Link::GetHost(nsAString &_host, ErrorResult& aError)
 {
   _host.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     // Do not throw!  Not having a valid URI should result in an empty string.
     return;
   }
@@ -346,17 +346,17 @@ Link::GetHost(nsAString &_host)
   nsAutoCString hostport;
   nsresult rv = uri->GetHostPort(hostport);
   if (NS_SUCCEEDED(rv)) {
     CopyUTF8toUTF16(hostport, _host);
   }
 }
 
 void
-Link::GetHostname(nsAString &_hostname)
+Link::GetHostname(nsAString &_hostname, ErrorResult& aError)
 {
   _hostname.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     // Do not throw!  Not having a valid URI should result in an empty string.
     return;
   }
@@ -366,17 +366,17 @@ Link::GetHostname(nsAString &_hostname)
   // Note that failure to get the host from the URI is not necessarily a bad
   // thing.  Some URIs do not have a host.
   if (NS_SUCCEEDED(rv)) {
     CopyUTF8toUTF16(host, _hostname);
   }
 }
 
 void
-Link::GetPathname(nsAString &_pathname)
+Link::GetPathname(nsAString &_pathname, ErrorResult& aError)
 {
   _pathname.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (!url) {
     // Do not throw!  Not having a valid URI or URL should result in an empty
     // string.
@@ -386,17 +386,17 @@ Link::GetPathname(nsAString &_pathname)
   nsAutoCString file;
   nsresult rv = url->GetFilePath(file);
   if (NS_SUCCEEDED(rv)) {
     CopyUTF8toUTF16(file, _pathname);
   }
 }
 
 void
-Link::GetSearch(nsAString &_search)
+Link::GetSearch(nsAString &_search, ErrorResult& aError)
 {
   _search.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
   if (!url) {
     // Do not throw!  Not having a valid URI or URL should result in an empty
     // string.
@@ -406,17 +406,17 @@ Link::GetSearch(nsAString &_search)
   nsAutoCString search;
   nsresult rv = url->GetQuery(search);
   if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
     CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, _search);
   }
 }
 
 void
-Link::GetPort(nsAString &_port)
+Link::GetPort(nsAString &_port, ErrorResult& aError)
 {
   _port.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     // Do not throw!  Not having a valid URI should result in an empty string.
     return;
   }
@@ -428,17 +428,17 @@ Link::GetPort(nsAString &_port)
   if (NS_SUCCEEDED(rv) && port != -1) {
     nsAutoString portStr;
     portStr.AppendInt(port, 10);
     _port.Assign(portStr);
   }
 }
 
 void
-Link::GetHash(nsAString &_hash)
+Link::GetHash(nsAString &_hash, ErrorResult& aError)
 {
   _hash.Truncate();
 
   nsCOMPtr<nsIURI> uri(GetURI());
   if (!uri) {
     // Do not throw!  Not having a valid URI should result in an empty
     // string.
     return;
--- a/content/base/src/Link.h
+++ b/content/base/src/Link.h
@@ -52,37 +52,37 @@ public:
   nsIURI* GetURI() const;
   virtual nsIURI* GetURIExternal() const {
     return GetURI();
   }
 
   /**
    * Helper methods for modifying and obtaining parts of the URI of the Link.
    */
-  void SetProtocol(const nsAString &aProtocol);
-  void SetUsername(const nsAString &aUsername);
-  void SetPassword(const nsAString &aPassword);
-  void SetHost(const nsAString &aHost);
-  void SetHostname(const nsAString &aHostname);
-  void SetPathname(const nsAString &aPathname);
-  void SetSearch(const nsAString &aSearch);
+  void SetProtocol(const nsAString &aProtocol, ErrorResult& aError);
+  void SetUsername(const nsAString &aUsername, ErrorResult& aError);
+  void SetPassword(const nsAString &aPassword, ErrorResult& aError);
+  void SetHost(const nsAString &aHost, ErrorResult& aError);
+  void SetHostname(const nsAString &aHostname, ErrorResult& aError);
+  void SetPathname(const nsAString &aPathname, ErrorResult& aError);
+  void SetSearch(const nsAString &aSearch, ErrorResult& aError);
   void SetSearchParams(mozilla::dom::URLSearchParams& aSearchParams);
-  void SetPort(const nsAString &aPort);
-  void SetHash(const nsAString &aHash);
-  void GetOrigin(nsAString &aOrigin);
-  void GetProtocol(nsAString &_protocol);
-  void GetUsername(nsAString &aUsername);
-  void GetPassword(nsAString &aPassword);
-  void GetHost(nsAString &_host);
-  void GetHostname(nsAString &_hostname);
-  void GetPathname(nsAString &_pathname);
-  void GetSearch(nsAString &_search);
+  void SetPort(const nsAString &aPort, ErrorResult& aError);
+  void SetHash(const nsAString &aHash, ErrorResult& aError);
+  void GetOrigin(nsAString &aOrigin, ErrorResult& aError);
+  void GetProtocol(nsAString &_protocol, ErrorResult& aError);
+  void GetUsername(nsAString &aUsername, ErrorResult& aError);
+  void GetPassword(nsAString &aPassword, ErrorResult& aError);
+  void GetHost(nsAString &_host, ErrorResult& aError);
+  void GetHostname(nsAString &_hostname, ErrorResult& aError);
+  void GetPathname(nsAString &_pathname, ErrorResult& aError);
+  void GetSearch(nsAString &_search, ErrorResult& aError);
   URLSearchParams* SearchParams();
-  void GetPort(nsAString &_port);
-  void GetHash(nsAString &_hash);
+  void GetPort(nsAString &_port, ErrorResult& aError);
+  void GetHash(nsAString &_hash, ErrorResult& aError);
 
   /**
    * Invalidates any link caching, and resets the state to the default.
    *
    * @param aNotify
    *        true if ResetLinkState should notify the owning document about style
    *        changes or false if it should not.
    */
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -218,16 +218,17 @@
 #include "mozilla/dom/XPathResult.h"
 #include "nsIDocumentEncoder.h"
 #include "nsIDocumentActivity.h"
 #include "nsIStructuredCloneContainer.h"
 #include "nsIMutableArray.h"
 #include "nsContentPermissionHelper.h"
 #include "mozilla/dom/DOMStringList.h"
 #include "nsWindowMemoryReporter.h"
+#include "nsLocation.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 typedef nsTArray<Link*> LinkArray;
 
 #ifdef PR_LOGGING
 static PRLogModuleInfo* gDocumentLeakPRLog;
@@ -6541,28 +6542,28 @@ nsDocument::GetDefaultView(nsIDOMWindow*
 
 NS_IMETHODIMP
 nsDocument::GetLocation(nsIDOMLocation **_retval)
 {
   *_retval = nsIDocument::GetLocation().take();
   return NS_OK;
 }
 
-already_AddRefed<nsIDOMLocation>
+already_AddRefed<nsLocation>
 nsIDocument::GetLocation() const
 {
   nsCOMPtr<nsIDOMWindow> w = do_QueryInterface(mScriptGlobalObject);
 
   if (!w) {
     return nullptr;
   }
 
   nsCOMPtr<nsIDOMLocation> loc;
   w->GetLocation(getter_AddRefs(loc));
-  return loc.forget();
+  return loc.forget().downcast<nsLocation>();
 }
 
 Element*
 nsIDocument::GetHtmlElement() const
 {
   Element* rootElement = GetRootElement();
   if (rootElement && rootElement->IsHTML(nsGkAtoms::html))
     return rootElement;
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -362,17 +362,17 @@ public:
     RefPtr<SourceSurface> snapshot = mTarget->Snapshot();
 
     mCtx->mTarget->DrawSurfaceWithShadow(snapshot, mTempRect.TopLeft(),
                                          Color::FromABGR(mCtx->CurrentState().shadowColor),
                                          mCtx->CurrentState().shadowOffset, mSigma,
                                          mCtx->CurrentState().op);
   }
 
-  operator DrawTarget*() 
+  operator DrawTarget*()
   {
     return mTarget;
   }
 
   DrawTarget* operator->()
   {
     return mTarget;
   }
@@ -939,17 +939,18 @@ CanvasRenderingContext2D::EnsureTarget()
         DemoteOldestContextIfNecessary();
 
         SkiaGLGlue* glue = gfxPlatform::GetPlatform()->GetSkiaGLGlue();
 
 #if USE_SKIA
         if (glue && glue->GetGrContext() && glue->GetGLContext()) {
           mTarget = Factory::CreateDrawTargetSkiaWithGrContext(glue->GetGrContext(), size, format);
           if (mTarget) {
-            mStream = gfx::SurfaceStream::CreateForType(SurfaceStreamType::TripleBuffer, glue->GetGLContext());
+            mStream = gl::SurfaceStream::CreateForType(gl::SurfaceStreamType::TripleBuffer,
+                                                       glue->GetGLContext());
             AddDemotableContext(this);
           } else {
             printf_stderr("Failed to create a SkiaGL DrawTarget, falling back to software\n");
           }
         }
 #endif
         if (!mTarget) {
           mTarget = layerManager->CreateDrawTarget(size, format);
@@ -2521,17 +2522,17 @@ CanvasRenderingContext2D::AddHitRegion(c
         break;
       }
     }
 #ifdef ACCESSIBILITY
   options.mControl->SetProperty(nsGkAtoms::hitregion, new bool(true),
                                 nsINode::DeleteProperty<bool>);
 #endif
   }
-  
+
   // finally, add the region to the list
   RegionInfo info;
   info.mId = options.mId;
   info.mElement = options.mControl;
   RefPtr<PathBuilder> pathBuilder = mPath->TransformedCopyToBuilder(mTarget->GetTransform());
   info.mPath = pathBuilder->Finish();
 
   mHitRegionsOptions.InsertElementAt(0, info);
@@ -3463,17 +3464,17 @@ CanvasRenderingContext2D::DrawDirectlyTo
   transformMatrix.Translate(gfxPoint(sx, sy));
   if (dw > 0 && dh > 0) {
     transformMatrix.Scale(sw/dw, sh/dh);
   }
   transformMatrix.Translate(gfxPoint(-dx, -dy));
 
   nsRefPtr<gfxContext> context = new gfxContext(tempTarget);
   context->SetMatrix(contextMatrix);
-  
+
   // FLAG_CLAMP is added for increased performance
   uint32_t modifiedFlags = image.mDrawingFlags | imgIContainer::FLAG_CLAMP;
 
   nsresult rv = image.mImgContainer->
     Draw(context, GraphicsFilter::FILTER_GOOD, transformMatrix,
          gfxRect(gfxPoint(dx, dy), gfxIntSize(dw, dh)),
          nsIntRect(nsIntPoint(0, 0), gfxIntSize(imgSize.width, imgSize.height)),
          gfxIntSize(imgSize.width, imgSize.height), nullptr, image.mWhichFrame,
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -25,17 +25,17 @@
 #include "imgIEncoder.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/EnumeratedArray.h"
 
 class nsGlobalWindow;
 class nsXULElement;
 
 namespace mozilla {
-namespace gfx {
+namespace gl {
 class SourceSurface;
 class SurfaceStream;
 }
 
 namespace dom {
 class HTMLImageElementOrHTMLCanvasElementOrHTMLVideoElement;
 class ImageData;
 class StringOrCanvasGradientOrCanvasPattern;
@@ -388,31 +388,31 @@ public:
 
   void ArcTo(double x1, double y1, double x2, double y2, double radius,
              mozilla::ErrorResult& error);
   void Rect(double x, double y, double w, double h);
   void Arc(double x, double y, double radius, double startAngle,
            double endAngle, bool anticlockwise, mozilla::ErrorResult& error);
 
   void GetMozCurrentTransform(JSContext* cx,
-			      JS::MutableHandle<JSObject*> result,
-			      mozilla::ErrorResult& error) const;
+                              JS::MutableHandle<JSObject*> result,
+                              mozilla::ErrorResult& error) const;
   void SetMozCurrentTransform(JSContext* cx,
                               JS::Handle<JSObject*> currentTransform,
                               mozilla::ErrorResult& error);
   void GetMozCurrentTransformInverse(JSContext* cx,
-				     JS::MutableHandle<JSObject*> result,
-				     mozilla::ErrorResult& error) const;
+                                     JS::MutableHandle<JSObject*> result,
+                                     mozilla::ErrorResult& error) const;
   void SetMozCurrentTransformInverse(JSContext* cx,
                                      JS::Handle<JSObject*> currentTransform,
                                      mozilla::ErrorResult& error);
   void GetFillRule(nsAString& fillRule);
   void SetFillRule(const nsAString& fillRule);
   void GetMozDash(JSContext* cx, JS::MutableHandle<JS::Value> retval,
-		  mozilla::ErrorResult& error);
+                  mozilla::ErrorResult& error);
   void SetMozDash(JSContext* cx, const JS::Value& mozDash,
                   mozilla::ErrorResult& error);
 
   void SetLineDash(const Sequence<double>& mSegments);
   void GetLineDash(nsTArray<double>& mSegments) const;
 
   void SetLineDashOffset(double mOffset);
   double LineDashOffset() const;
@@ -656,17 +656,17 @@ protected:
   /**
     * Returns the surface format this canvas should be allocated using. Takes
     * into account mOpaque, platform requirements, etc.
     */
   mozilla::gfx::SurfaceFormat GetSurfaceFormat() const;
 
   void DrawImage(const HTMLImageOrCanvasOrVideoElement &imgElt,
                  double sx, double sy, double sw, double sh,
-                 double dx, double dy, double dw, double dh, 
+                 double dx, double dy, double dw, double dh,
                  uint8_t optional_argc, mozilla::ErrorResult& error);
 
   void DrawDirectlyToCanvas(const nsLayoutUtils::DirectDrawInfo& image,
                             mozilla::gfx::Rect* bounds, double dx, double dy,
                             double dw, double dh, double sx, double sy,
                             double sw, double sh, gfxIntSize imgSize);
 
   nsString& GetFont()
@@ -706,17 +706,17 @@ protected:
   // If mCanvasElement is not provided, then a docshell is
   nsCOMPtr<nsIDocShell> mDocShell;
 
   // This is created lazily so it is necessary to call EnsureTarget before
   // accessing it. In the event of an error it will be equal to
   // sErrorTarget.
   mozilla::RefPtr<mozilla::gfx::DrawTarget> mTarget;
 
-  RefPtr<gfx::SurfaceStream> mStream;
+  RefPtr<gl::SurfaceStream> mStream;
 
   /**
     * Flag to avoid duplicate calls to InvalidateFrame. Set to true whenever
     * Redraw is called, reset to false when Render is called.
     */
   bool mIsEntireFrameInvalid;
   /**
     * When this is set, the first call to Redraw(gfxRect) should set
@@ -784,17 +784,17 @@ protected:
     * drawing operation.
     */
   bool NeedToDrawShadow()
   {
     const ContextState& state = CurrentState();
 
     // The spec says we should not draw shadows if the operator is OVER.
     // If it's over and the alpha value is zero, nothing needs to be drawn.
-    return NS_GET_A(state.shadowColor) != 0 && 
+    return NS_GET_A(state.shadowColor) != 0 &&
       (state.shadowBlur != 0 || state.shadowOffset.x != 0 || state.shadowOffset.y != 0);
   }
 
   mozilla::gfx::CompositionOp UsedOperation()
   {
     if (NeedToDrawShadow()) {
       // In this case the shadow rendering will use the operator.
       return mozilla::gfx::CompositionOp::OP_OVER;
--- a/content/canvas/src/WebGLContextDraw.cpp
+++ b/content/canvas/src/WebGLContextDraw.cpp
@@ -445,17 +445,27 @@ WebGLContext::ValidateBufferFetching(con
             ErrorInvalidOperation("%s: integer overflow occured while checking vertex attrib %d", info, i);
             return false;
         }
 
         if (vd.divisor == 0) {
             maxVertices = std::min(maxVertices, checked_maxAllowedCount.value());
             hasPerVertex = true;
         } else {
-            maxInstances = std::min(maxInstances, checked_maxAllowedCount.value() / vd.divisor);
+            CheckedUint32 checked_curMaxInstances = checked_maxAllowedCount * vd.divisor;
+
+            uint32_t curMaxInstances = UINT32_MAX;
+            // If this isn't valid, it's because we overflowed our
+            // uint32 above. Just leave this as UINT32_MAX, since
+            // sizeof(uint32) becomes our limiting factor.
+            if (checked_curMaxInstances.isValid()) {
+                curMaxInstances = checked_curMaxInstances.value();
+            }
+
+            maxInstances = std::min(maxInstances, curMaxInstances);
         }
     }
 
     mBufferFetchingIsVerified = true;
     mBufferFetchingHasPerVertex = hasPerVertex;
     mMaxFetchedVertices = maxVertices;
     mMaxFetchedInstances = maxInstances;
 
--- a/content/html/content/public/HTMLAudioElement.h
+++ b/content/html/content/public/HTMLAudioElement.h
@@ -2,41 +2,37 @@
 /* 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_HTMLAudioElement_h
 #define mozilla_dom_HTMLAudioElement_h
 
 #include "mozilla/Attributes.h"
-#include "nsIDOMHTMLAudioElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/TypedArray.h"
 
 typedef uint16_t nsMediaNetworkState;
 typedef uint16_t nsMediaReadyState;
 
 namespace mozilla {
 namespace dom {
 
-class HTMLAudioElement MOZ_FINAL : public HTMLMediaElement,
-                                   public nsIDOMHTMLAudioElement
+class HTMLAudioElement MOZ_FINAL : public HTMLMediaElement
 {
 public:
-  HTMLAudioElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
+  typedef mozilla::dom::NodeInfo NodeInfo;
 
-  // nsISupports
-  NS_DECL_ISUPPORTS_INHERITED
+  HTMLAudioElement(already_AddRefed<NodeInfo>& aNodeInfo);
 
   // nsIDOMHTMLMediaElement
   using HTMLMediaElement::GetPaused;
-  NS_FORWARD_NSIDOMHTMLMEDIAELEMENT(HTMLMediaElement::)
 
-  virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const;
-  virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel);
+  virtual nsresult Clone(NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
+  virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel) MOZ_OVERRIDE;
 
   virtual nsIDOMNode* AsDOMNode() MOZ_OVERRIDE { return this; }
 
   // WebIDL
 
   static already_AddRefed<HTMLAudioElement>
   Audio(const GlobalObject& aGlobal,
         const Optional<nsAString>& aSrc, ErrorResult& aRv);
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -67,16 +67,17 @@ namespace dom {
 
 class MediaError;
 class MediaSource;
 class TextTrackList;
 class AudioTrackList;
 class VideoTrackList;
 
 class HTMLMediaElement : public nsGenericHTMLElement,
+                         public nsIDOMHTMLMediaElement,
                          public nsIObserver,
                          public MediaDecoderOwner,
                          public nsIAudioChannelAgentCallback
 {
 public:
   typedef mozilla::TimeStamp TimeStamp;
   typedef mozilla::layers::ImageContainer ImageContainer;
   typedef mozilla::VideoFrameContainer VideoFrameContainer;
--- a/content/html/content/public/HTMLVideoElement.h
+++ b/content/html/content/public/HTMLVideoElement.h
@@ -3,60 +3,56 @@
 /* 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_HTMLVideoElement_h
 #define mozilla_dom_HTMLVideoElement_h
 
 #include "mozilla/Attributes.h"
-#include "nsIDOMHTMLVideoElement.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 
 namespace mozilla {
 namespace dom {
 
 class WakeLock;
 class VideoPlaybackQuality;
 
-class HTMLVideoElement MOZ_FINAL : public HTMLMediaElement,
-                                   public nsIDOMHTMLVideoElement
+class HTMLVideoElement MOZ_FINAL : public HTMLMediaElement
 {
 public:
-  HTMLVideoElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo);
+  typedef mozilla::dom::NodeInfo NodeInfo;
+
+  HTMLVideoElement(already_AddRefed<NodeInfo>& aNodeInfo);
 
   NS_IMPL_FROMCONTENT_HTML_WITH_TAG(HTMLVideoElement, video)
 
-  // nsISupports
-  NS_DECL_ISUPPORTS_INHERITED
+  using HTMLMediaElement::GetPaused;
 
-  // nsIDOMHTMLMediaElement
-  using HTMLMediaElement::GetPaused;
-  NS_FORWARD_NSIDOMHTMLMEDIAELEMENT(HTMLMediaElement::)
-
-  // nsIDOMHTMLVideoElement
-  NS_DECL_NSIDOMHTMLVIDEOELEMENT
+  NS_IMETHOD_(bool) IsVideo() MOZ_OVERRIDE {
+    return true;
+  }
 
   virtual bool ParseAttribute(int32_t aNamespaceID,
-                                nsIAtom* aAttribute,
-                                const nsAString& aValue,
-                                nsAttrValue& aResult);
+                              nsIAtom* aAttribute,
+                              const nsAString& aValue,
+                              nsAttrValue& aResult) MOZ_OVERRIDE;
   NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const MOZ_OVERRIDE;
 
   static void Init();
 
   virtual nsMapRuleToAttributesFunc GetAttributeMappingFunction() const MOZ_OVERRIDE;
 
-  virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
+  virtual nsresult Clone(NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // Set size with the current video frame's height and width.
   // If there is no video frame, returns NS_ERROR_FAILURE.
   nsresult GetVideoSize(nsIntSize* size);
 
-  virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel);
+  virtual nsresult SetAcceptHeader(nsIHttpChannel* aChannel) MOZ_OVERRIDE;
 
   // WebIDL
 
   uint32_t Width() const
   {
     return GetIntAttr(nsGkAtoms::width, 0);
   }
 
@@ -80,17 +76,20 @@ public:
     return mMediaSize.width == -1 ? 0 : mMediaSize.width;
   }
 
   uint32_t VideoHeight() const
   {
     return mMediaSize.height == -1 ? 0 : mMediaSize.height;
   }
 
-  // XPCOM GetPoster is OK
+  void GetPoster(nsAString& aValue)
+  {
+    GetURIAttr(nsGkAtoms::poster, nullptr, aValue);
+  }
   void SetPoster(const nsAString& aValue, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::poster, aValue, aRv);
   }
 
   uint32_t MozParsedFrames() const;
 
   uint32_t MozDecodedFrames() const;
--- a/content/html/content/src/HTMLAnchorElement.cpp
+++ b/content/html/content/src/HTMLAnchorElement.cpp
@@ -295,23 +295,27 @@ HTMLAnchorElement::RelList()
   }
   return mRelList;
 }
 
 #define IMPL_URI_PART(_part)                                 \
   NS_IMETHODIMP                                              \
   HTMLAnchorElement::Get##_part(nsAString& a##_part)         \
   {                                                          \
-    Link::Get##_part(a##_part);                              \
+    ErrorResult rv;                                          \
+    Link::Get##_part(a##_part, rv);                          \
+    MOZ_ASSERT(!rv.Failed());                                \
     return NS_OK;                                            \
   }                                                          \
   NS_IMETHODIMP                                              \
   HTMLAnchorElement::Set##_part(const nsAString& a##_part)   \
   {                                                          \
-    Link::Set##_part(a##_part);                              \
+    ErrorResult rv;                                          \
+    Link::Set##_part(a##_part, rv);                          \
+    MOZ_ASSERT(!rv.Failed());                                \
     return NS_OK;                                            \
   }
 
 IMPL_URI_PART(Protocol)
 IMPL_URI_PART(Host)
 IMPL_URI_PART(Hostname)
 IMPL_URI_PART(Pathname)
 IMPL_URI_PART(Search)
--- a/content/html/content/src/HTMLAnchorElement.h
+++ b/content/html/content/src/HTMLAnchorElement.h
@@ -81,17 +81,17 @@ public:
 
   virtual EventStates IntrinsicState() const MOZ_OVERRIDE;
 
   virtual void OnDNSPrefetchDeferred();
   virtual void OnDNSPrefetchRequested();
   virtual bool HasDeferredDNSPrefetchRequest();
 
   // WebIDL API
-  void GetHref(nsString& aValue)
+  void GetHref(nsAString& aValue, ErrorResult& rv)
   {
     GetHTMLURIAttr(nsGkAtoms::href, aValue);
   }
   void SetHref(const nsAString& aValue, mozilla::ErrorResult& rv)
   {
     SetHTMLAttr(nsGkAtoms::href, aValue, rv);
   }
   // The XPCOM GetTarget is OK for us
@@ -140,21 +140,42 @@ public:
   // The XPCOM GetText is OK for us
   void SetText(const nsAString& aValue, mozilla::ErrorResult& rv)
   {
     rv = SetText(aValue);
   }
 
   // Link::GetOrigin is OK for us
 
+  using Link::GetProtocol;
+  using Link::SetProtocol;
+
   // Link::GetUsername is OK for us
   // Link::SetUsername is OK for us
 
-  // Link::Getpassword is OK for us
-  // Link::Setpassword is OK for us
+  // Link::GetPassword is OK for us
+  // Link::SetPassword is OK for us
+
+  using Link::GetHost;
+  using Link::SetHost;
+
+  using Link::GetHostname;
+  using Link::SetHostname;
+
+  using Link::GetPort;
+  using Link::SetPort;
+
+  using Link::GetPathname;
+  using Link::SetPathname;
+
+  using Link::GetSearch;
+  using Link::SetSearch;
+
+  using Link::GetHash;
+  using Link::SetHash;
 
   // The XPCOM URI decomposition attributes are fine for us
   void GetCoords(nsString& aValue)
   {
     GetHTMLAttr(nsGkAtoms::coords, aValue);
   }
   void SetCoords(const nsAString& aValue, mozilla::ErrorResult& rv)
   {
@@ -187,19 +208,19 @@ public:
   void GetShape(nsString& aValue)
   {
     GetHTMLAttr(nsGkAtoms::shape, aValue);
   }
   void SetShape(const nsAString& aValue, mozilla::ErrorResult& rv)
   {
     SetHTMLAttr(nsGkAtoms::shape, aValue, rv);
   }
-  void Stringify(nsAString& aResult)
+  void Stringify(nsAString& aResult, ErrorResult& aError)
   {
-    GetHref(aResult);
+    GetHref(aResult, aError);
   }
 
 protected:
   virtual ~HTMLAnchorElement();
 
   virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
   virtual void SetItemValueText(const nsAString& text) MOZ_OVERRIDE;
   virtual JSObject* WrapNode(JSContext *aCx) MOZ_OVERRIDE;
--- a/content/html/content/src/HTMLAreaElement.cpp
+++ b/content/html/content/src/HTMLAreaElement.cpp
@@ -197,23 +197,27 @@ HTMLAreaElement::UnsetAttr(int32_t aName
 
   return rv;
 }
 
 #define IMPL_URI_PART(_part)                                 \
   NS_IMETHODIMP                                              \
   HTMLAreaElement::Get##_part(nsAString& a##_part)           \
   {                                                          \
-    Link::Get##_part(a##_part);                              \
+    ErrorResult rv;                                          \
+    Link::Get##_part(a##_part, rv);                          \
+    MOZ_ASSERT(!rv.Failed());                                \
     return NS_OK;                                            \
   }                                                          \
   NS_IMETHODIMP                                              \
   HTMLAreaElement::Set##_part(const nsAString& a##_part)     \
   {                                                          \
-    Link::Set##_part(a##_part);                              \
+    ErrorResult rv;                                          \
+    Link::Set##_part(a##_part, rv);                          \
+    MOZ_ASSERT(!rv.Failed());                                \
     return NS_OK;                                            \
   }
 
 IMPL_URI_PART(Protocol)
 IMPL_URI_PART(Host)
 IMPL_URI_PART(Hostname)
 IMPL_URI_PART(Pathname)
 IMPL_URI_PART(Search)
--- a/content/html/content/src/HTMLAreaElement.h
+++ b/content/html/content/src/HTMLAreaElement.h
@@ -85,17 +85,20 @@ public:
   }
 
   // The XPCOM GetShape is OK for us
   void SetShape(const nsAString& aShape, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::shape, aShape, aError);
   }
 
-  // The XPCOM GetHref is OK for us
+  void GetHref(nsAString& aHref, ErrorResult& aError)
+  {
+    aError = GetHref(aHref);
+  }
   void SetHref(const nsAString& aHref, ErrorResult& aError)
   {
     aError = SetHref(aHref);
   }
 
   // The XPCOM GetTarget is OK for us
   void SetTarget(const nsAString& aTarget, ErrorResult& aError)
   {
@@ -119,61 +122,62 @@ public:
     GetHTMLAttr(nsGkAtoms::rel, aValue);
   }
 
   void SetRel(const nsAString& aRel, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::rel, aRel, aError);
   } 
   nsDOMTokenList* RelList();
+
   // The Link::GetOrigin is OK for us
 
-  // The XPCOM GetProtocol is OK for us
-  // The XPCOM SetProtocol is OK for us
+  using Link::GetProtocol;
+  using Link::SetProtocol;
 
   // The Link::GetUsername is OK for us
   // The Link::SetUsername is OK for us
 
   // The Link::GetPassword is OK for us
   // The Link::SetPassword is OK for us
 
-  // The XPCOM GetHost is OK for us
-  // The XPCOM SetHost is OK for us
+  using Link::GetHost;
+  using Link::SetHost;
 
-  // The XPCOM GetHostname is OK for us
-  // The XPCOM SetHostname is OK for us
+  using Link::GetHostname;
+  using Link::SetHostname;
 
-  // The XPCOM GetPort is OK for us
-  // The XPCOM SetPort is OK for us
+  using Link::GetPort;
+  using Link::SetPort;
 
-  // The XPCOM GetPathname is OK for us
-  // The XPCOM SetPathname is OK for us
+  using Link::GetPathname;
+  using Link::SetPathname;
 
-  // The XPCOM GetSearch is OK for us
-  // The XPCOM SetSearch is OK for us
+  using Link::GetSearch;
+  using Link::SetSearch;
 
-  // The XPCOM GetHash is OK for us
-  // The XPCOM SetHash is OK for us
+  using Link::GetHash;
+  using Link::SetHash;
 
   // The Link::GetSearchParams is OK for us
   // The Link::SetSearchParams is OK for us
 
   bool NoHref() const
   {
     return GetBoolAttr(nsGkAtoms::nohref);
   }
 
   void SetNoHref(bool aValue, ErrorResult& aError)
   {
     SetHTMLBoolAttr(nsGkAtoms::nohref, aValue, aError);
   }
 
-  void Stringify(nsAString& aResult)
+  void Stringify(nsAString& aResult, ErrorResult& aError)
   {
-    GetHref(aResult);
+    GetHref(aResult, aError);
   }
 
 protected:
   virtual ~HTMLAreaElement();
 
   virtual JSObject* WrapNode(JSContext* aCx) MOZ_OVERRIDE;
 
   virtual void GetItemValueText(nsAString& text) MOZ_OVERRIDE;
--- a/content/html/content/src/HTMLAudioElement.cpp
+++ b/content/html/content/src/HTMLAudioElement.cpp
@@ -2,17 +2,16 @@
 /* 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 "mozilla/dom/HTMLAudioElement.h"
 #include "mozilla/dom/HTMLAudioElementBinding.h"
 #include "nsError.h"
-#include "nsIDOMHTMLAudioElement.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsIDocument.h"
 #include "jsfriendapi.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "AudioSampleFormat.h"
 #include "AudioChannelCommon.h"
@@ -24,32 +23,28 @@
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Audio)
 
 namespace mozilla {
 namespace dom {
 
 extern bool IsAudioAPIEnabled();
 
-NS_IMPL_ISUPPORTS_INHERITED(HTMLAudioElement, HTMLMediaElement,
-                            nsIDOMHTMLMediaElement, nsIDOMHTMLAudioElement)
-
 NS_IMPL_ELEMENT_CLONE(HTMLAudioElement)
 
 
-HTMLAudioElement::HTMLAudioElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+HTMLAudioElement::HTMLAudioElement(already_AddRefed<NodeInfo>& aNodeInfo)
   : HTMLMediaElement(aNodeInfo)
 {
 }
 
 HTMLAudioElement::~HTMLAudioElement()
 {
 }
 
-
 already_AddRefed<HTMLAudioElement>
 HTMLAudioElement::Audio(const GlobalObject& aGlobal,
                         const Optional<nsAString>& aSrc,
                         ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(aGlobal.GetAsSupports());
   nsIDocument* doc;
   if (!win || !(doc = win->GetExtantDoc())) {
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -43,17 +43,16 @@
 
 #include "nsITimer.h"
 
 #include "MediaError.h"
 #include "MediaDecoder.h"
 #include "nsICategoryManager.h"
 #include "MediaResource.h"
 
-#include "nsIDOMHTMLVideoElement.h"
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsCrossSiteListenerProxy.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsICachingChannel.h"
 #include "nsLayoutUtils.h"
 #include "nsVideoFrame.h"
 #include "Layers.h"
@@ -453,16 +452,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_IN
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlayed)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextTrackManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioTrackList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoTrackList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(HTMLMediaElement)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLMediaElement)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
 NS_INTERFACE_MAP_END_INHERITING(nsGenericHTMLElement)
 
 // nsIDOMHTMLMediaElement
 NS_IMPL_URI_ATTR(HTMLMediaElement, Src, src)
 NS_IMPL_STRING_ATTR(HTMLMediaElement, CrossOrigin, crossorigin)
 NS_IMPL_BOOL_ATTR(HTMLMediaElement, Controls, controls)
@@ -483,16 +483,22 @@ HTMLMediaElement::GetMozAudioChannelType
 }
 
 NS_IMETHODIMP
 HTMLMediaElement::SetMozAudioChannelType(const nsAString& aValue)
 {
   return SetAttrHelper(nsGkAtoms::mozaudiochannel, aValue);
 }
 
+NS_IMETHODIMP_(bool)
+HTMLMediaElement::IsVideo()
+{
+  return false;
+}
+
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::GetMozSrcObject() const
 {
   NS_ASSERTION(!mSrcAttrStream || mSrcAttrStream->GetStream(),
                "MediaStream should have been set up properly");
   nsRefPtr<DOMMediaStream> stream = mSrcAttrStream;
   return stream.forget();
 }
@@ -1815,18 +1821,17 @@ HTMLMediaElement::CaptureStreamInternal(
   // to behave appropriately when media streams generated
   // via mozCaptureStream*() are added to the Peer Connection.
   // This functionality is planned to be used as part of Audio
   // Quality Performance testing for WebRTC.
   // Bug932845: Revisit this once hints mechanism is dealt with
   // holistically.
   uint8_t hints = 0;
   if (Preferences::GetBool("media.capturestream_hints.enabled")) {
-    nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
-    if (video && GetVideoFrameContainer()) {
+    if (IsVideo() && GetVideoFrameContainer()) {
       hints = DOMMediaStream::HINT_CONTENTS_VIDEO | DOMMediaStream::HINT_CONTENTS_AUDIO;
     } else {
       hints = DOMMediaStream::HINT_CONTENTS_AUDIO;
     }
   }
   out->mStream = DOMMediaStream::CreateTrackUnionStream(window, hints);
 #else
   out->mStream = DOMMediaStream::CreateTrackUnionStream(window);
@@ -2758,21 +2763,24 @@ public:
     nsCOMPtr<nsIRunnable> event;
     if (aBlocked == BLOCKED) {
       event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyBlocked);
     } else {
       event = NS_NewRunnableMethod(this, &StreamListener::DoNotifyUnblocked);
     }
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
-  virtual void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE
+  virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                           MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE
   {
-    nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    if (event == EVENT_FINISHED) {
+      nsCOMPtr<nsIRunnable> event =
+        NS_NewRunnableMethod(this, &StreamListener::DoNotifyFinished);
+      aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    }
   }
   virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) MOZ_OVERRIDE
   {
     MutexAutoLock lock(mMutex);
     nsCOMPtr<nsIRunnable> event =
       NS_NewRunnableMethod(this, &StreamListener::DoNotifyHaveCurrentData);
     aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
   }
@@ -3270,19 +3278,19 @@ VideoFrameContainer* HTMLMediaElement::G
       mMediaSize == nsIntSize(-1, -1)) {
     return nullptr;
   }
 
   if (mVideoFrameContainer)
     return mVideoFrameContainer;
 
   // Only video frames need an image container.
-  nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
-  if (!video)
+  if (!IsVideo()) {
     return nullptr;
+  }
 
   mVideoFrameContainer =
     new VideoFrameContainer(this, LayerManager::CreateAsynchronousImageContainer());
 
   return mVideoFrameContainer;
 }
 
 nsresult HTMLMediaElement::DispatchEvent(const nsAString& aName)
@@ -3879,19 +3887,18 @@ void HTMLMediaElement::UpdateAudioChanne
     mPlayingThroughTheAudioChannel = playingThroughTheAudioChannel;
 
     if (!mAudioChannelAgent) {
       nsresult rv;
       mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1", &rv);
       if (!mAudioChannelAgent) {
         return;
       }
-      nsCOMPtr<nsIDOMHTMLVideoElement> video = do_QueryObject(this);
       // Use a weak ref so the audio channel agent can't leak |this|.
-      if (AudioChannel::Normal == mAudioChannel && video) {
+      if (AudioChannel::Normal == mAudioChannel && IsVideo()) {
         mAudioChannelAgent->InitWithVideo(OwnerDoc()->GetWindow(),
                                           static_cast<int32_t>(mAudioChannel),
                                           this, true);
       } else {
         mAudioChannelAgent->InitWithWeakCallback(OwnerDoc()->GetWindow(),
                                                  static_cast<int32_t>(mAudioChannel),
                                                  this);
       }
--- a/content/html/content/src/HTMLMetaElement.cpp
+++ b/content/html/content/src/HTMLMetaElement.cpp
@@ -44,16 +44,31 @@ HTMLMetaElement::GetItemValueText(nsAStr
 void
 HTMLMetaElement::SetItemValueText(const nsAString& aValue)
 {
   SetContent(aValue);
 }
 
 
 nsresult
+HTMLMetaElement::AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                              const nsAttrValue* aValue, bool aNotify)
+{
+  if (aNameSpaceID == kNameSpaceID_None) {
+    if (aName == nsGkAtoms::content) {
+      nsIDocument *document = GetCurrentDoc();
+      CreateAndDispatchEvent(document, NS_LITERAL_STRING("DOMMetaChanged"));
+    }
+  }
+
+  return nsGenericHTMLElement::AfterSetAttr(aNameSpaceID, aName, aValue,
+                                            aNotify);
+}
+
+nsresult
 HTMLMetaElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                             nsIContent* aBindingParent,
                             bool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
                                                  aCompileEventHandlers);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/html/content/src/HTMLMetaElement.h
+++ b/content/html/content/src/HTMLMetaElement.h
@@ -25,16 +25,20 @@ public:
   // nsIDOMHTMLMetaElement
   NS_DECL_NSIDOMHTMLMETAELEMENT
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers) MOZ_OVERRIDE;
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) MOZ_OVERRIDE;
+
+  virtual nsresult AfterSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
+                                const nsAttrValue* aValue, bool aNotify) MOZ_OVERRIDE;
+
   void CreateAndDispatchEvent(nsIDocument* aDoc, const nsAString& aEventName);
 
   virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE;
 
   // XPCOM GetName is fine.
   void SetName(const nsAString& aName, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::name, aName, aRv);
--- a/content/html/content/src/HTMLVideoElement.cpp
+++ b/content/html/content/src/HTMLVideoElement.cpp
@@ -1,15 +1,14 @@
 /* -*- 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 "nsIDOMHTMLVideoElement.h"
 #include "nsIDOMHTMLSourceElement.h"
 #include "mozilla/dom/HTMLVideoElement.h"
 #include "mozilla/dom/HTMLVideoElementBinding.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsSize.h"
 #include "nsError.h"
 #include "nsNodeInfoManager.h"
@@ -35,41 +34,19 @@
 
 NS_IMPL_NS_NEW_HTML_ELEMENT(Video)
 
 namespace mozilla {
 namespace dom {
 
 static bool sVideoStatsEnabled;
 
-NS_IMPL_ISUPPORTS_INHERITED(HTMLVideoElement, HTMLMediaElement,
-                            nsIDOMHTMLMediaElement, nsIDOMHTMLVideoElement)
-
 NS_IMPL_ELEMENT_CLONE(HTMLVideoElement)
 
-// nsIDOMHTMLVideoElement
-NS_IMPL_INT_ATTR(HTMLVideoElement, Width, width)
-NS_IMPL_INT_ATTR(HTMLVideoElement, Height, height)
-
-// nsIDOMHTMLVideoElement
-/* readonly attribute unsigned long videoWidth; */
-NS_IMETHODIMP HTMLVideoElement::GetVideoWidth(uint32_t *aVideoWidth)
-{
-  *aVideoWidth = VideoWidth();
-  return NS_OK;
-}
-
-/* readonly attribute unsigned long videoHeight; */
-NS_IMETHODIMP HTMLVideoElement::GetVideoHeight(uint32_t *aVideoHeight)
-{
-  *aVideoHeight = VideoHeight();
-  return NS_OK;
-}
-
-HTMLVideoElement::HTMLVideoElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+HTMLVideoElement::HTMLVideoElement(already_AddRefed<NodeInfo>& aNodeInfo)
   : HTMLMediaElement(aNodeInfo)
 {
 }
 
 HTMLVideoElement::~HTMLVideoElement()
 {
 }
 
@@ -144,103 +121,66 @@ nsresult HTMLVideoElement::SetAcceptHead
       "application/ogg;q=0.7,"
       "audio/*;q=0.6,*/*;q=0.5");
 
   return aChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                     value,
                                     false);
 }
 
-NS_IMPL_URI_ATTR(HTMLVideoElement, Poster, poster)
-
 uint32_t HTMLVideoElement::MozParsedFrames() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   if (!sVideoStatsEnabled) {
     return 0;
   }
   return mDecoder ? mDecoder->GetFrameStatistics().GetParsedFrames() : 0;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozParsedFrames(uint32_t *aMozParsedFrames)
-{
-  *aMozParsedFrames = MozParsedFrames();
-  return NS_OK;
-}
-
 uint32_t HTMLVideoElement::MozDecodedFrames() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   if (!sVideoStatsEnabled) {
     return 0;
   }
   return mDecoder ? mDecoder->GetFrameStatistics().GetDecodedFrames() : 0;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozDecodedFrames(uint32_t *aMozDecodedFrames)
-{
-  *aMozDecodedFrames = MozDecodedFrames();
-  return NS_OK;
-}
-
 uint32_t HTMLVideoElement::MozPresentedFrames() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   if (!sVideoStatsEnabled) {
     return 0;
   }
   return mDecoder ? mDecoder->GetFrameStatistics().GetPresentedFrames() : 0;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozPresentedFrames(uint32_t *aMozPresentedFrames)
-{
-  *aMozPresentedFrames = MozPresentedFrames();
-  return NS_OK;
-}
-
 uint32_t HTMLVideoElement::MozPaintedFrames()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   if (!sVideoStatsEnabled) {
     return 0;
   }
   layers::ImageContainer* container = GetImageContainer();
   return container ? container->GetPaintCount() : 0;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozPaintedFrames(uint32_t *aMozPaintedFrames)
-{
-  *aMozPaintedFrames = MozPaintedFrames();
-  return NS_OK;
-}
-
 double HTMLVideoElement::MozFrameDelay()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   VideoFrameContainer* container = GetVideoFrameContainer();
   return container ?  container->GetFrameDelay() : 0;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozFrameDelay(double *aMozFrameDelay) {
-  *aMozFrameDelay = MozFrameDelay();
-  return NS_OK;
-}
-
-/* readonly attribute bool mozHasAudio */
 bool HTMLVideoElement::MozHasAudio() const
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   return mHasAudio;
 }
 
-NS_IMETHODIMP HTMLVideoElement::GetMozHasAudio(bool *aHasAudio) {
-  *aHasAudio = MozHasAudio();
-  return NS_OK;
-}
-
 JSObject*
 HTMLVideoElement::WrapNode(JSContext* aCx)
 {
   return HTMLVideoElementBinding::Wrap(aCx, this);
 }
 
 void
 HTMLVideoElement::NotifyOwnerDocumentActivityChanged()
@@ -319,10 +259,11 @@ HTMLVideoElement::UpdateScreenWakeLock()
   }
 }
 
 void
 HTMLVideoElement::Init()
 {
   Preferences::AddBoolVarCache(&sVideoStatsEnabled, "media.video_stats.enabled");
 }
+
 } // namespace dom
 } // namespace mozilla
--- a/content/html/content/src/nsHTMLDNSPrefetch.cpp
+++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp
@@ -170,17 +170,18 @@ nsresult
 nsHTMLDNSPrefetch::CancelPrefetch(Link *aElement,
                                   uint16_t flags,
                                   nsresult aReason)
 {
   if (!(sInitialized && sPrefetches && sDNSService && sDNSListener))
     return NS_ERROR_NOT_AVAILABLE;
 
   nsAutoString hostname;
-  aElement->GetHostname(hostname);
+  ErrorResult rv;
+  aElement->GetHostname(hostname, rv);
   return CancelPrefetch(hostname, flags, aReason);
 }
 
 nsresult
 nsHTMLDNSPrefetch::CancelPrefetch(const nsAString &hostname,
                                   uint16_t flags,
                                   nsresult aReason)
 {
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -235,17 +235,17 @@ public:
   void Clear() const
   {
     // Deprecated
   }
   mozilla::dom::Selection* GetSelection(mozilla::ErrorResult& aRv);
   // The XPCOM CaptureEvents works fine for us.
   // The XPCOM ReleaseEvents works fine for us.
   // We're picking up GetLocation from Document
-  already_AddRefed<nsIDOMLocation> GetLocation() const {
+  already_AddRefed<nsLocation> GetLocation() const {
     return nsIDocument::GetLocation();
   }
 
   virtual nsHTMLDocument* AsHTMLDocument() MOZ_OVERRIDE { return this; }
 
 protected:
   ~nsHTMLDocument();
 
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -256,23 +256,25 @@ MediaDecoder::DecodedStreamGraphListener
     }
   }
 
   MutexAutoLock lock(mMutex);
   mStreamFinishedOnMainThread = true;
 }
 
 void
-MediaDecoder::DecodedStreamGraphListener::NotifyFinished(MediaStreamGraph* aGraph)
+MediaDecoder::DecodedStreamGraphListener::NotifyEvent(MediaStreamGraph* aGraph,
+  MediaStreamListener::MediaStreamGraphEvent event)
 {
-  nsCOMPtr<nsIRunnable> event =
-    NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
-  aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+  if (event == EVENT_FINISHED) {
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(this, &DecodedStreamGraphListener::DoNotifyFinished);
+    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+  }
 }
-
 void MediaDecoder::DestroyDecodedStream()
 {
   MOZ_ASSERT(NS_IsMainThread());
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   // All streams are having their SourceMediaStream disconnected, so they
   // need to be explicitly blocked again.
   for (int32_t i = mOutputStreams.Length() - 1; i >= 0; --i) {
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -435,17 +435,18 @@ public:
     // mDecoderStateMachine is non-null and MediaDecoderStateMachine is false.
     bool mHaveBlockedForStateMachineNotPlaying;
   };
 
   class DecodedStreamGraphListener : public MediaStreamListener {
   public:
     DecodedStreamGraphListener(MediaStream* aStream, DecodedStreamData* aData);
     virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) MOZ_OVERRIDE;
-    virtual void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+    virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                             MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE;
 
     void DoNotifyFinished();
 
     int64_t GetLastOutputTime() // microseconds
     {
       MutexAutoLock lock(mMutex);
       return mLastOutputTime;
     }
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -392,17 +392,17 @@ MediaStreamGraphImpl::UpdateCurrentTime(
   streamHasOutput.SetLength(mStreams.Length());
   for (uint32_t i = 0; i < mStreams.Length(); ++i) {
     MediaStream* stream = mStreams[i];
 
     // Calculate blocked time and fire Blocked/Unblocked events
     GraphTime blockedTime = 0;
     GraphTime t = prevCurrentTime;
     // include |nextCurrentTime| to ensure NotifyBlockingChanged() is called
-    // before NotifyFinished() when |nextCurrentTime == stream end time|
+    // before NotifyEvent(this, EVENT_FINISHED) when |nextCurrentTime == stream end time|
     while (t <= nextCurrentTime) {
       GraphTime end;
       bool blocked = stream->mBlocked.GetAt(t, &end);
       if (blocked) {
         blockedTime += std::min(end, nextCurrentTime) - t;
       }
       if (blocked != stream->mNotifiedBlocked) {
         for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
@@ -455,17 +455,17 @@ MediaStreamGraphImpl::UpdateCurrentTime(
           stream->StreamTimeToGraphTime(stream->GetStreamBuffer().GetAllTracksEnd()))  {
       NS_WARN_IF_FALSE(stream->mNotifiedBlocked,
         "Should've notified blocked=true for a fully finished stream");
       stream->mNotifiedFinished = true;
       stream->mLastPlayedVideoFrame.SetNull();
       SetStreamOrderDirty();
       for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
         MediaStreamListener* l = stream->mListeners[j];
-        l->NotifyFinished(this);
+        l->NotifyEvent(this, MediaStreamListener::EVENT_FINISHED);
       }
     }
   }
 }
 
 bool
 MediaStreamGraphImpl::WillUnderrun(MediaStream* aStream, GraphTime aTime,
                                    GraphTime aEndBlockingDecisions, GraphTime* aEnd)
@@ -1928,17 +1928,17 @@ MediaStream::EnsureTrack(TrackID aTrackI
   return track;
 }
 
 void
 MediaStream::RemoveAllListenersImpl()
 {
   for (int32_t i = mListeners.Length() - 1; i >= 0; --i) {
     nsRefPtr<MediaStreamListener> listener = mListeners[i].forget();
-    listener->NotifyRemoved(GraphImpl());
+    listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
   }
   mListeners.Clear();
 }
 
 void
 MediaStream::DestroyImpl()
 {
   RemoveAllListenersImpl();
@@ -2106,17 +2106,17 @@ MediaStream::ChangeExplicitBlockerCount(
 
 void
 MediaStream::AddListenerImpl(already_AddRefed<MediaStreamListener> aListener)
 {
   MediaStreamListener* listener = *mListeners.AppendElement() = aListener;
   listener->NotifyBlockingChanged(GraphImpl(),
     mNotifiedBlocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
   if (mNotifiedFinished) {
-    listener->NotifyFinished(GraphImpl());
+    listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_FINISHED);
   }
   if (mNotifiedHasCurrentData) {
     listener->NotifyHasCurrentData(GraphImpl());
   }
 }
 
 void
 MediaStream::AddListener(MediaStreamListener* aListener)
@@ -2135,17 +2135,17 @@ MediaStream::AddListener(MediaStreamList
 }
 
 void
 MediaStream::RemoveListenerImpl(MediaStreamListener* aListener)
 {
   // wouldn't need this if we could do it in the opposite order
   nsRefPtr<MediaStreamListener> listener(aListener);
   mListeners.RemoveElement(aListener);
-  listener->NotifyRemoved(GraphImpl());
+  listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_REMOVED);
 }
 
 void
 MediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, MediaStreamListener* aListener) :
@@ -2362,25 +2362,47 @@ SourceMediaStream::NotifyDirectConsumers
     l->NotifyRealtimeData(static_cast<MediaStreamGraph*>(GraphImpl()), aTrack->mID, aTrack->mOutputRate,
                           offset, aTrack->mCommands, *aSegment);
   }
 }
 
 void
 SourceMediaStream::AddDirectListener(MediaStreamDirectListener* aListener)
 {
-  MutexAutoLock lock(mMutex);
-  mDirectListeners.AppendElement(aListener);
+  bool wasEmpty;
+  {
+    MutexAutoLock lock(mMutex);
+    wasEmpty = mDirectListeners.IsEmpty();
+    mDirectListeners.AppendElement(aListener);
+  }
+
+  if (wasEmpty) {
+    for (uint32_t j = 0; j < mListeners.Length(); ++j) {
+      MediaStreamListener* l = mListeners[j];
+      l->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_HAS_DIRECT_LISTENERS);
+    }
+  }
 }
 
 void
 SourceMediaStream::RemoveDirectListener(MediaStreamDirectListener* aListener)
 {
-  MutexAutoLock lock(mMutex);
-  mDirectListeners.RemoveElement(aListener);
+  bool isEmpty;
+  {
+    MutexAutoLock lock(mMutex);
+    mDirectListeners.RemoveElement(aListener);
+    isEmpty = mDirectListeners.IsEmpty();
+  }
+
+  if (isEmpty) {
+    for (uint32_t j = 0; j < mListeners.Length(); ++j) {
+      MediaStreamListener* l = mListeners[j];
+      l->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_HAS_NO_DIRECT_LISTENERS);
+    }
+  }
 }
 
 bool
 SourceMediaStream::HaveEnoughBuffered(TrackID aID)
 {
   MutexAutoLock lock(mMutex);
   TrackData *track = FindDataForTrack(aID);
   if (track) {
@@ -2450,17 +2472,17 @@ void
 SourceMediaStream::EndAllTrackAndFinish()
 {
   MutexAutoLock lock(mMutex);
   for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
     SourceMediaStream::TrackData* data = &mUpdateTracks[i];
     data->mCommands |= TRACK_END;
   }
   FinishWithLockHeld();
-  // we will call NotifyFinished() to let GetUserMedia know
+  // we will call NotifyEvent() to let GetUserMedia know
 }
 
 TrackTicks
 SourceMediaStream::GetBufferedTicks(TrackID aID)
 {
   StreamBuffer::Track* track  = mBuffer.FindTrack(aID);
   if (track) {
     MediaSegment* segment = track->GetSegment();
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -106,16 +106,17 @@ protected:
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
 
   enum Consumption {
     CONSUMED,
     NOT_CONSUMED
   };
+
   /**
    * Notify that the stream is hooked up and we'd like to start or stop receiving
    * data on it. Only fires on SourceMediaStreams.
    * The initial state is assumed to be NOT_CONSUMED.
    */
   virtual void NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming) {}
 
   /**
@@ -152,26 +153,27 @@ public:
 
   /**
    * Notify that the stream output is advancing. aCurrentTime is the graph's
    * current time. MediaStream::GraphTimeToStreamTime can be used to get the
    * stream time.
    */
   virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
 
-  /**
-   * Notify that the stream finished.
-   */
-  virtual void NotifyFinished(MediaStreamGraph* aGraph) {}
+  enum MediaStreamGraphEvent {
+    EVENT_FINISHED,
+    EVENT_REMOVED,
+    EVENT_HAS_DIRECT_LISTENERS, // transition from no direct listeners
+    EVENT_HAS_NO_DIRECT_LISTENERS,  // transition to no direct listeners
+  };
 
   /**
-   * Notify that your listener has been removed, either due to RemoveListener(),
-   * or due to the stream being destroyed.  You will get no further notifications.
+   * Notify that an event has occurred on the Stream
    */
-  virtual void NotifyRemoved(MediaStreamGraph* aGraph) {}
+  virtual void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent aEvent) {}
 
   enum {
     TRACK_EVENT_CREATED = 0x01,
     TRACK_EVENT_ENDED = 0x02
   };
   /**
    * Notify that changes to one of the stream tracks have been queued.
    * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
--- a/content/media/encoder/MediaEncoder.cpp
+++ b/content/media/encoder/MediaEncoder.cpp
@@ -59,27 +59,27 @@ MediaEncoder::NotifyQueuedTrackChanges(M
   } else if (mVideoEncoder && aQueuedMedia.GetType() == MediaSegment::VIDEO) {
       mVideoEncoder->NotifyQueuedTrackChanges(aGraph, aID, aTrackRate,
                                               aTrackOffset, aTrackEvents,
                                               aQueuedMedia);
   }
 }
 
 void
-MediaEncoder::NotifyRemoved(MediaStreamGraph* aGraph)
+MediaEncoder::NotifyEvent(MediaStreamGraph* aGraph,
+                          MediaStreamListener::MediaStreamGraphEvent event)
 {
   // In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
   LOG(PR_LOG_DEBUG, ("NotifyRemoved in [MediaEncoder]."));
   if (mAudioEncoder) {
-    mAudioEncoder->NotifyRemoved(aGraph);
+    mAudioEncoder->NotifyEvent(aGraph, event);
   }
   if (mVideoEncoder) {
-    mVideoEncoder->NotifyRemoved(aGraph);
+    mVideoEncoder->NotifyEvent(aGraph, event);
   }
-
 }
 
 /* static */
 already_AddRefed<MediaEncoder>
 MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
 {
 #ifdef PR_LOGGING
   if (!gMediaEncoderLog) {
--- a/content/media/encoder/MediaEncoder.h
+++ b/content/media/encoder/MediaEncoder.h
@@ -75,22 +75,23 @@ public :
   /**
    * Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw
    * track data in form of MediaSegment.
    */
   virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                         TrackRate aTrackRate,
                                         TrackTicks aTrackOffset,
                                         uint32_t aTrackEvents,
-                                        const MediaSegment& aQueuedMedia);
+                                        const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
 
   /**
    * Notified the stream is being removed.
    */
-  virtual void NotifyRemoved(MediaStreamGraph* aGraph);
+  virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                           MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE;
 
   /**
    * Creates an encoder with a given MIME type. Returns null if we are unable
    * to create the encoder. For now, default aMIMEType to "audio/ogg" and use
    * Ogg+Opus if it is empty.
    */
   static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType,
                                                       uint8_t aTrackTypes = ContainerWriter::CREATE_AUDIO_TRACK);
--- a/content/media/encoder/TrackEncoder.h
+++ b/content/media/encoder/TrackEncoder.h
@@ -8,21 +8,20 @@
 
 #include "mozilla/ReentrantMonitor.h"
 
 #include "AudioSegment.h"
 #include "EncodedFrameContainer.h"
 #include "StreamBuffer.h"
 #include "TrackMetadataBase.h"
 #include "VideoSegment.h"
+#include "MediaStreamGraph.h"
 
 namespace mozilla {
 
-class MediaStreamGraph;
-
 /**
  * Base class of AudioTrackEncoder and VideoTrackEncoder. Lifetimes managed by
  * MediaEncoder. Most methods can only be called on the MediaEncoder's thread,
  * but some subclass methods can be called on other threads when noted.
  *
  * NotifyQueuedTrackChanges is called on subclasses of this class from the
  * MediaStreamGraph thread, and AppendAudioSegment/AppendVideoSegment is then
  * called to store media data in the TrackEncoder. Later on, GetEncodedTrack is
@@ -44,17 +43,23 @@ public:
                                         TrackTicks aTrackOffset,
                                         uint32_t aTrackEvents,
                                         const MediaSegment& aQueuedMedia) = 0;
 
   /**
    * Notified by the same callback of MediaEncoder when it has been removed from
    * MediaStreamGraph. Called on the MediaStreamGraph thread.
    */
-  void NotifyRemoved(MediaStreamGraph* aGraph) { NotifyEndOfStream(); }
+  void NotifyEvent(MediaStreamGraph* aGraph,
+                   MediaStreamListener::MediaStreamGraphEvent event)
+  {
+    if (event == MediaStreamListener::MediaStreamGraphEvent::EVENT_REMOVED) {
+      NotifyEndOfStream();
+    }
+  }
 
   /**
    * Creates and sets up meta data for a specific codec, called on the worker
    * thread.
    */
   virtual already_AddRefed<TrackMetadataBase> GetMetadata() = 0;
 
   /**
--- a/content/media/gmp/GMPParent.cpp
+++ b/content/media/gmp/GMPParent.cpp
@@ -8,16 +8,17 @@
 #include "nsComponentManagerUtils.h"
 #include "nsIInputStream.h"
 #include "nsILineInputStream.h"
 #include "nsNetUtil.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsThreadUtils.h"
 #include "nsIRunnable.h"
 #include "mozIGeckoMediaPluginService.h"
+#include "mozilla/unused.h"
 
 namespace mozilla {
 namespace gmp {
 
 GMPParent::GMPParent()
   : mState(GMPStateNotLoaded)
   , mProcess(nullptr)
 {
@@ -121,30 +122,32 @@ GMPParent::UnloadProcess()
   }
 }
 
 void
 GMPParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder)
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
-  MOZ_ALWAYS_TRUE(mVideoDecoders.RemoveElement(aDecoder));
+  // If the constructor fails, we'll get called before it's added
+  unused << NS_WARN_IF(!mVideoDecoders.RemoveElement(aDecoder));
 
   // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
   // until after this has completed.
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::MaybeUnloadProcess);
   NS_DispatchToCurrentThread(event);
 }
 
 void
 GMPParent::VideoEncoderDestroyed(GMPVideoEncoderParent* aEncoder)
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
-  MOZ_ALWAYS_TRUE(mVideoEncoders.RemoveElement(aEncoder));
+  // If the constructor fails, we'll get called before it's added
+  unused << NS_WARN_IF(!mVideoEncoders.RemoveElement(aEncoder));
 
   // Recv__delete__ is on the stack, don't potentially destroy the top-level actor
   // until after this has completed.
   nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &GMPParent::MaybeUnloadProcess);
   NS_DispatchToCurrentThread(event);
 }
 
 GMPState
new file mode 100644
--- /dev/null
+++ b/content/media/omx/MediaCodecProxy.cpp
@@ -0,0 +1,392 @@
+/* -*- 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 "MediaCodecProxy.h"
+
+#include <string.h>
+
+#include <binder/IPCThreadState.h>
+
+namespace android {
+
+sp<MediaCodecProxy>
+MediaCodecProxy::CreateByType(sp<ALooper> aLooper,
+                              const char *aMime,
+                              bool aEncoder,
+                              bool aAsync,
+                              wp<CodecResourceListener> aListener)
+{
+  sp<MediaCodecProxy> codec = new MediaCodecProxy(aLooper, aMime, aEncoder, aAsync, aListener);
+  if ((!aAsync && codec->allocated()) || codec->requestResource()) {
+    return codec;
+  }
+  return nullptr;
+}
+
+MediaCodecProxy::MediaCodecProxy(sp<ALooper> aLooper,
+                                 const char *aMime,
+                                 bool aEncoder,
+                                 bool aAsync,
+                                 wp<CodecResourceListener> aListener)
+  : mCodecLooper(aLooper)
+  , mCodecMime(aMime)
+  , mCodecEncoder(aEncoder)
+  , mListener(aListener)
+{
+  MOZ_ASSERT(mCodecLooper != nullptr, "ALooper should not be nullptr.");
+  if (aAsync) {
+    mResourceHandler = new MediaResourceHandler(this);
+  } else {
+    allocateCodec();
+  }
+}
+
+MediaCodecProxy::~MediaCodecProxy()
+{
+  releaseCodec();
+
+  // Complete all pending Binder ipc transactions
+  IPCThreadState::self()->flushCommands();
+
+  cancelResource();
+}
+
+bool
+MediaCodecProxy::requestResource()
+{
+  if (mResourceHandler == nullptr) {
+    return false;
+  }
+
+  if (strncasecmp(mCodecMime.get(), "video/", 6) == 0) {
+    mResourceHandler->requestResource(mCodecEncoder
+        ? IMediaResourceManagerService::HW_VIDEO_ENCODER
+        : IMediaResourceManagerService::HW_VIDEO_DECODER);
+  } else if (strncasecmp(mCodecMime.get(), "audio/", 6) == 0) {
+    mResourceHandler->requestResource(mCodecEncoder
+        ? IMediaResourceManagerService::HW_AUDIO_ENCODER
+        : IMediaResourceManagerService::HW_AUDIO_DECODER);
+  } else {
+    return false;
+  }
+
+  return true;
+}
+
+void
+MediaCodecProxy::cancelResource()
+{
+  if (mResourceHandler == nullptr) {
+    return;
+  }
+
+  mResourceHandler->cancelResource();
+}
+
+bool
+MediaCodecProxy::allocateCodec()
+{
+  if (mCodecLooper == nullptr) {
+    return false;
+  }
+
+  // Write Lock for mCodec
+  RWLock::AutoWLock awl(mCodecLock);
+
+  // Create MediaCodec
+  mCodec = MediaCodec::CreateByType(mCodecLooper, mCodecMime.get(), mCodecEncoder);
+  if (mCodec == nullptr) {
+    return false;
+  }
+
+  return true;
+}
+
+void
+MediaCodecProxy::releaseCodec()
+{
+  wp<MediaCodec> codec;
+
+  {
+    // Write Lock for mCodec
+    RWLock::AutoWLock awl(mCodecLock);
+
+    codec = mCodec;
+
+    // Release MediaCodec
+    if (mCodec != nullptr) {
+      mCodec->release();
+      mCodec = nullptr;
+    }
+  }
+
+  while (codec.promote() != nullptr) {
+    // this value come from stagefright's AwesomePlayer.
+    usleep(1000);
+  }
+}
+
+bool
+MediaCodecProxy::allocated() const
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  return mCodec != nullptr;
+}
+
+status_t
+MediaCodecProxy::configure(const sp<AMessage> &aFormat,
+                           const sp<Surface> &aNativeWindow,
+                           const sp<ICrypto> &aCrypto,
+                           uint32_t aFlags)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->configure(aFormat, aNativeWindow, aCrypto, aFlags);
+}
+
+status_t
+MediaCodecProxy::start()
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->start();
+}
+
+status_t
+MediaCodecProxy::stop()
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->stop();
+}
+
+status_t
+MediaCodecProxy::release()
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->release();
+}
+
+status_t
+MediaCodecProxy::flush()
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->flush();
+}
+
+status_t
+MediaCodecProxy::queueInputBuffer(size_t aIndex,
+                                  size_t aOffset,
+                                  size_t aSize,
+                                  int64_t aPresentationTimeUs,
+                                  uint32_t aFlags,
+                                  AString *aErrorDetailMessage)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->queueInputBuffer(aIndex, aOffset, aSize,
+      aPresentationTimeUs, aFlags, aErrorDetailMessage);
+}
+
+status_t
+MediaCodecProxy::queueSecureInputBuffer(size_t aIndex,
+                                        size_t aOffset,
+                                        const CryptoPlugin::SubSample *aSubSamples,
+                                        size_t aNumSubSamples,
+                                        const uint8_t aKey[16],
+                                        const uint8_t aIV[16],
+                                        CryptoPlugin::Mode aMode,
+                                        int64_t aPresentationTimeUs,
+                                        uint32_t aFlags,
+                                        AString *aErrorDetailMessage)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->queueSecureInputBuffer(aIndex, aOffset,
+      aSubSamples, aNumSubSamples, aKey, aIV, aMode,
+      aPresentationTimeUs, aFlags, aErrorDetailMessage);
+}
+
+status_t
+MediaCodecProxy::dequeueInputBuffer(size_t *aIndex,
+                                    int64_t aTimeoutUs)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->dequeueInputBuffer(aIndex, aTimeoutUs);
+}
+
+status_t
+MediaCodecProxy::dequeueOutputBuffer(size_t *aIndex,
+                                     size_t *aOffset,
+                                     size_t *aSize,
+                                     int64_t *aPresentationTimeUs,
+                                     uint32_t *aFlags,
+                                     int64_t aTimeoutUs)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->dequeueOutputBuffer(aIndex, aOffset, aSize,
+      aPresentationTimeUs, aFlags, aTimeoutUs);
+}
+
+status_t
+MediaCodecProxy::renderOutputBufferAndRelease(size_t aIndex)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->renderOutputBufferAndRelease(aIndex);
+}
+
+status_t
+MediaCodecProxy::releaseOutputBuffer(size_t aIndex)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->releaseOutputBuffer(aIndex);
+}
+
+status_t
+MediaCodecProxy::signalEndOfInputStream()
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->signalEndOfInputStream();
+}
+
+status_t
+MediaCodecProxy::getOutputFormat(sp<AMessage> *aFormat) const
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->getOutputFormat(aFormat);
+}
+
+status_t
+MediaCodecProxy::getInputBuffers(Vector<sp<ABuffer>> *aBuffers) const
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->getInputBuffers(aBuffers);
+}
+
+status_t
+MediaCodecProxy::getOutputBuffers(Vector<sp<ABuffer>> *aBuffers) const
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return NO_INIT;
+  }
+  return mCodec->getOutputBuffers(aBuffers);
+}
+
+void
+MediaCodecProxy::requestActivityNotification(const sp<AMessage> &aNotify)
+{
+  // Read Lock for mCodec
+  RWLock::AutoRLock arl(mCodecLock);
+
+  if (mCodec == nullptr) {
+    return;
+  }
+  mCodec->requestActivityNotification(aNotify);
+}
+
+// Called on a Binder thread
+void
+MediaCodecProxy::resourceReserved()
+{
+  // Create MediaCodec
+  releaseCodec();
+  if (!allocateCodec()) {
+    cancelResource();
+    return;
+  }
+
+  // Notification
+  sp<CodecResourceListener> listener = mListener.promote();
+  if (listener != nullptr) {
+    listener->codecReserved();
+  }
+}
+
+// Called on a Binder thread
+void
+MediaCodecProxy::resourceCanceled()
+{
+  // Release MediaCodec
+  releaseCodec();
+
+  // Notification
+  sp<CodecResourceListener> listener = mListener.promote();
+  if (listener != nullptr) {
+    listener->codecCanceled();
+  }
+}
+
+} // namespace android
new file mode 100644
--- /dev/null
+++ b/content/media/omx/MediaCodecProxy.h
@@ -0,0 +1,156 @@
+/* -*- 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 MEDIA_CODEC_PROXY_H
+#define MEDIA_CODEC_PROXY_H
+
+#include <nsString.h>
+
+#include <stagefright/MediaCodec.h>
+#include <utils/threads.h>
+
+#include "MediaResourceHandler.h"
+
+namespace android {
+
+class MediaCodecProxy : public MediaResourceHandler::ResourceListener
+{
+public:
+  /* Codec resource notification listener.
+   * All functions are called on the Binder thread.
+   */
+  struct CodecResourceListener : public virtual RefBase {
+    /* The codec resource is reserved and can be granted.
+     * The client can allocate the requested resource.
+     */
+    virtual void codecReserved() = 0;
+    /* The codec resource is not reserved any more.
+     * The client should release the resource as soon as possible if the
+     * resource is still being held.
+     */
+    virtual void codecCanceled() = 0;
+  };
+
+  // Check whether MediaCodec has been allocated.
+  bool allocated() const;
+
+  // Static MediaCodec methods
+  // Only support MediaCodec::CreateByType()
+  static sp<MediaCodecProxy> CreateByType(sp<ALooper> aLooper,
+                                          const char *aMime,
+                                          bool aEncoder,
+                                          bool aAsync=false,
+                                          wp<CodecResourceListener> aListener=nullptr);
+
+  // MediaCodec methods
+  status_t configure(const sp<AMessage> &aFormat,
+                     const sp<Surface> &aNativeWindow,
+                     const sp<ICrypto> &aCrypto,
+                     uint32_t aFlags);
+
+  status_t start();
+
+  status_t stop();
+
+  status_t release();
+
+  status_t flush();
+
+  status_t queueInputBuffer(size_t aIndex,
+                            size_t aOffset,
+                            size_t aSize,
+                            int64_t aPresentationTimeUs,
+                            uint32_t aFlags,
+                            AString *aErrorDetailMessage=nullptr);
+
+  status_t queueSecureInputBuffer(size_t aIndex,
+                                  size_t aOffset,
+                                  const CryptoPlugin::SubSample *aSubSamples,
+                                  size_t aNumSubSamples,
+                                  const uint8_t aKey[16],
+                                  const uint8_t aIV[16],
+                                  CryptoPlugin::Mode aMode,
+                                  int64_t aPresentationTimeUs,
+                                  uint32_t aFlags,
+                                  AString *aErrorDetailMessage=nullptr);
+
+  status_t dequeueInputBuffer(size_t *aIndex,
+                              int64_t aTimeoutUs=INT64_C(0));
+
+  status_t dequeueOutputBuffer(size_t *aIndex,
+                               size_t *aOffset,
+                               size_t *aSize,
+                               int64_t *aPresentationTimeUs,
+                               uint32_t *aFlags,
+                               int64_t aTimeoutUs=INT64_C(0));
+
+  status_t renderOutputBufferAndRelease(size_t aIndex);
+
+  status_t releaseOutputBuffer(size_t aIndex);
+
+  status_t signalEndOfInputStream();
+
+  status_t getOutputFormat(sp<AMessage> *aFormat) const;
+
+  status_t getInputBuffers(Vector<sp<ABuffer>> *aBuffers) const;
+
+  status_t getOutputBuffers(Vector<sp<ABuffer>> *aBuffers) const;
+
+  // Notification will be posted once there "is something to do", i.e.
+  // an input/output buffer has become available, a format change is
+  // pending, an error is pending.
+  void requestActivityNotification(const sp<AMessage> &aNotify);
+
+protected:
+  virtual ~MediaCodecProxy();
+
+  // MediaResourceHandler::EventListener::resourceReserved()
+  virtual void resourceReserved();
+  // MediaResourceHandler::EventListener::resourceCanceled()
+  virtual void resourceCanceled();
+
+private:
+  // Forbidden
+  MediaCodecProxy() MOZ_DELETE;
+  MediaCodecProxy(const MediaCodecProxy &) MOZ_DELETE;
+  const MediaCodecProxy &operator=(const MediaCodecProxy &) MOZ_DELETE;
+
+  // Constructor for MediaCodecProxy::CreateByType
+  MediaCodecProxy(sp<ALooper> aLooper,
+                  const char *aMime,
+                  bool aEncoder,
+                  bool aAsync,
+                  wp<CodecResourceListener> aListener);
+
+  // Request Resource
+  bool requestResource();
+  // Cancel Resource
+  void cancelResource();
+
+  // Allocate Codec Resource
+  bool allocateCodec();
+  // Release Codec Resource
+  void releaseCodec();
+
+  // MediaCodec Parameter
+  sp<ALooper> mCodecLooper;
+  nsCString mCodecMime;
+  bool mCodecEncoder;
+
+  // Codec Resource Notification Listener
+  wp<CodecResourceListener> mListener;
+
+  // Media Resource Management
+  sp<MediaResourceHandler> mResourceHandler;
+
+  // MediaCodec instance
+  mutable RWLock mCodecLock;
+  sp<MediaCodec> mCodec;
+};
+
+} // namespace android
+
+#endif // MEDIA_CODEC_PROXY_H
--- a/content/media/omx/mediaresourcemanager/IMediaResourceManagerService.h
+++ b/content/media/omx/mediaresourcemanager/IMediaResourceManagerService.h
@@ -22,16 +22,17 @@ class IMediaResourceManagerService : pub
 public:
     DECLARE_META_INTERFACE(MediaResourceManagerService);
 
     // Enumeration for the resource types
     enum ResourceType {
       HW_VIDEO_DECODER = 0,
       HW_AUDIO_DECODER,  // Not supported currently.
       HW_VIDEO_ENCODER,
+      HW_AUDIO_ENCODER,  // Not supported currently.
       HW_CAMERA,          // Not supported currently.
       NUM_OF_RESOURCE_TYPES,
       INVALID_RESOURCE_TYPE = -1
     };
 
     enum ErrorCode {
         RESOURCE_NOT_AVAILABLE = -EAGAIN
     };
new file mode 100644
--- /dev/null
+++ b/content/media/omx/mediaresourcemanager/MediaResourceHandler.cpp
@@ -0,0 +1,92 @@
+/* -*- 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 "MediaResourceHandler.h"
+
+#include "mozilla/NullPtr.h"
+
+namespace android {
+
+MediaResourceHandler::MediaResourceHandler(const wp<ResourceListener> &aListener)
+  : mListener(aListener)
+  , mState(MediaResourceManagerClient::CLIENT_STATE_WAIT_FOR_RESOURCE)
+  , mType(IMediaResourceManagerService::INVALID_RESOURCE_TYPE)
+{
+}
+
+MediaResourceHandler::~MediaResourceHandler()
+{
+  cancelResource();
+}
+
+bool
+MediaResourceHandler::requestResource(IMediaResourceManagerService::ResourceType aType)
+{
+  Mutex::Autolock al(mLock);
+
+  if (mClient != nullptr && mService != nullptr) {
+    return false;
+  }
+
+  sp<MediaResourceManagerClient> client = new MediaResourceManagerClient(this);
+  sp<IMediaResourceManagerService> service = client->getMediaResourceManagerService();
+
+  if (service == nullptr) {
+    return false;
+  }
+
+  if (service->requestMediaResource(client, (int)aType, true) != OK) {
+    return false;
+  }
+
+  mClient = client;
+  mService = service;
+  mType = aType;
+
+  return true;
+}
+
+void
+MediaResourceHandler::cancelResource()
+{
+  Mutex::Autolock al(mLock);
+
+  if (mClient != nullptr && mService != nullptr) {
+    mService->cancelClient(mClient, (int)mType);
+  }
+
+  mClient = nullptr;
+  mService = nullptr;
+}
+
+// Called on a Binder thread
+void
+MediaResourceHandler::statusChanged(int aEvent)
+{
+  sp<ResourceListener> listener;
+
+  Mutex::Autolock autoLock(mLock);
+
+  MediaResourceManagerClient::State state = (MediaResourceManagerClient::State)aEvent;
+  if (state == mState) {
+    return;
+  }
+
+  mState = state;
+
+  listener = mListener.promote();
+  if (listener == nullptr) {
+    return;
+  }
+
+  if (mState == MediaResourceManagerClient::CLIENT_STATE_RESOURCE_ASSIGNED) {
+    listener->resourceReserved();
+  } else {
+    listener->resourceCanceled();
+  }
+}
+
+} // namespace android
new file mode 100644
--- /dev/null
+++ b/content/media/omx/mediaresourcemanager/MediaResourceHandler.h
@@ -0,0 +1,68 @@
+/* -*- 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 MEDIA_RESOURCE_HANDLER_H
+#define MEDIA_RESOURCE_HANDLER_H
+
+#include <utils/threads.h>
+
+#include <mozilla/Attributes.h>
+
+#include "MediaResourceManagerClient.h"
+
+namespace android {
+
+class MediaResourceHandler : public MediaResourceManagerClient::EventListener
+{
+public:
+  /* Resource notification listener.
+   * All functions are called on the Binder thread.
+   */
+  struct ResourceListener : public virtual RefBase {
+    /* The resource is reserved and can be granted.
+     * The client can allocate the requested resource.
+     */
+    virtual void resourceReserved() = 0;
+    /* The resource is not reserved any more.
+     * The client should release the resource as soon as possible if the
+     * resource is still being held.
+     */
+    virtual void resourceCanceled() = 0;
+  };
+
+  MediaResourceHandler(const wp<ResourceListener> &aListener);
+
+  virtual ~MediaResourceHandler();
+
+  // Request Resource
+  bool requestResource(IMediaResourceManagerService::ResourceType aType);
+  // Cancel Resource
+  void cancelResource();
+
+protected:
+  // MediaResourceManagerClient::EventListener::statusChanged()
+  virtual void statusChanged(int event);
+
+private:
+  // Forbidden
+  MediaResourceHandler() MOZ_DELETE;
+  MediaResourceHandler(const MediaResourceHandler &) MOZ_DELETE;
+  const MediaResourceHandler &operator=(const MediaResourceHandler &) MOZ_DELETE;
+
+  // Resource Notification Listener
+  wp<ResourceListener> mListener;
+
+  // Resource Management
+  Mutex mLock;
+  MediaResourceManagerClient::State mState;
+  sp<IMediaResourceManagerClient> mClient;
+  sp<IMediaResourceManagerService> mService;
+  IMediaResourceManagerService::ResourceType mType;
+};
+
+} // namespace android
+
+#endif // MEDIA_RESOURCE_HANDLER_H
--- a/content/media/omx/mediaresourcemanager/moz.build
+++ b/content/media/omx/mediaresourcemanager/moz.build
@@ -11,16 +11,17 @@ EXPORTS += [
     'MediaResourceManagerClient.h',
     'MediaResourceManagerService.h',
 ]
 
 SOURCES += [
     'IMediaResourceManagerClient.cpp',
     'IMediaResourceManagerDeathNotifier.cpp',
     'IMediaResourceManagerService.cpp',
+    'MediaResourceHandler.cpp',
     'MediaResourceManagerClient.cpp',
     'MediaResourceManagerService.cpp',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 CXXFLAGS += [
     '-I%s/%s' % (CONFIG['ANDROID_SOURCE'], d) for d in [
--- a/content/media/omx/moz.build
+++ b/content/media/omx/moz.build
@@ -42,16 +42,24 @@ if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
         'RtspOmxDecoder.h',
         'RtspOmxReader.h',
     ]
     SOURCES += [
         'RtspOmxDecoder.cpp',
         'RtspOmxReader.cpp',
     ]
 
+if int(CONFIG['ANDROID_VERSION']) >= 16:
+    EXPORTS += [
+        'MediaCodecProxy.h',
+    ]
+    SOURCES += [
+        'MediaCodecProxy.cpp',
+    ]
+
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'gklayout'
 LOCAL_INCLUDES += [
     '/content/base/src',
     '/content/html/content/src',
     '/ipc/chromium/src',
     'mediaresourcemanager',
--- a/content/media/webrtc/MediaEngine.h
+++ b/content/media/webrtc/MediaEngine.h
@@ -81,16 +81,19 @@ public:
   /* Release the device back to the system. */
   virtual nsresult Deallocate() = 0;
 
   /* Start the device and add the track to the provided SourceMediaStream, with
    * the provided TrackID. You may start appending data to the track
    * immediately after. */
   virtual nsresult Start(SourceMediaStream*, TrackID) = 0;
 
+  /* tell the source if there are any direct listeners attached */
+  virtual void SetDirectListeners(bool) = 0;
+
   /* Take a snapshot from this source. In the case of video this is a single
    * image, and for audio, it is a snippet lasting aDuration milliseconds. The
    * duration argument is ignored for a MediaEngineVideoSource.
    */
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile) = 0;
 
   /* Called when the stream wants more data */
   virtual void NotifyPull(MediaStreamGraph* aGraph,
--- a/content/media/webrtc/MediaEngineDefault.h
+++ b/content/media/webrtc/MediaEngineDefault.h
@@ -40,16 +40,17 @@ public:
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
   virtual nsresult Allocate(const VideoTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs);
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop(SourceMediaStream*, TrackID);
+  virtual void SetDirectListeners(bool aHasDirectListeners) {};
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) { return NS_OK; };
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
@@ -95,16 +96,17 @@ public:
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
   virtual nsresult Allocate(const AudioTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs);
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop(SourceMediaStream*, TrackID);
+  virtual void SetDirectListeners(bool aHasDirectListeners) {};
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) { return NS_OK; };
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
--- a/content/media/webrtc/MediaEngineTabVideoSource.h
+++ b/content/media/webrtc/MediaEngineTabVideoSource.h
@@ -19,16 +19,17 @@ class MediaEngineTabVideoSource : public
     MediaEngineTabVideoSource();
 
     virtual void GetName(nsAString_internal&);
     virtual void GetUUID(nsAString_internal&);
     virtual nsresult Allocate(const VideoTrackConstraintsN &,
                               const mozilla::MediaEnginePrefs&);
     virtual nsresult Deallocate();
     virtual nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID);
+    virtual void SetDirectListeners(bool aHasDirectListeners) {};
     virtual nsresult Snapshot(uint32_t, nsIDOMFile**);
     virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime, mozilla::TrackTicks&);
     virtual nsresult Stop(mozilla::SourceMediaStream*, mozilla::TrackID);
     virtual nsresult Config(bool, uint32_t, bool, uint32_t, bool, uint32_t, int32_t);
     virtual bool IsFake();
     void Draw();
 
     class StartRunnable : public nsRunnable {
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -99,16 +99,17 @@ public:
     : mCameraControl(nullptr)
     , mCallbackMonitor("WebRTCCamera.CallbackMonitor")
     , mRotation(0)
     , mBackCamera(false)
     , mCaptureIndex(aIndex)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
     , mHeight(0)
+    , mHasDirectListeners(false)
     , mInitDone(false)
     , mInSnapshotMode(false)
     , mSnapshotPath(nullptr)
   {
     mState = kReleased;
     Init();
   }
 #else
@@ -126,32 +127,34 @@ public:
   MediaEngineWebRTCVideoSource(webrtc::VideoEngine* aVideoEnginePtr, int aIndex)
     : mVideoEngine(aVideoEnginePtr)
     , mCaptureIndex(aIndex)
     , mFps(-1)
     , mMinFps(-1)
     , mMonitor("WebRTCCamera.Monitor")
     , mWidth(0)
     , mHeight(0)
+    , mHasDirectListeners(false)
     , mInitDone(false)
     , mInSnapshotMode(false)
     , mSnapshotPath(nullptr) {
     MOZ_ASSERT(aVideoEnginePtr);
     mState = kReleased;
     Init();
   }
 #endif
 
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
   virtual nsresult Allocate(const VideoTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs);
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop(SourceMediaStream*, TrackID);
+  virtual void SetDirectListeners(bool aHasListeners);
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay) { return NS_OK; };
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
                           TrackID aId,
@@ -240,16 +243,17 @@ private:
   // mMonitor protects mImage access/changes, and transitions of mState
   // from kStarted to kStopped (which are combined with EndTrack() and
   // image changes).  Note that mSources is not accessed from other threads
   // for video and is not protected.
   Monitor mMonitor; // Monitor for processing WebRTC frames.
   int mWidth, mHeight;
   nsRefPtr<layers::Image> mImage;
   nsRefPtr<layers::ImageContainer> mImageContainer;
+  bool mHasDirectListeners;
 
   nsTArray<SourceMediaStream *> mSources; // When this goes empty, we shut down HW
 
   bool mInitDone;
   bool mInSnapshotMode;
   nsString* mSnapshotPath;
 
   nsString mDeviceName;
@@ -292,16 +296,17 @@ public:
   virtual void GetName(nsAString&);
   virtual void GetUUID(nsAString&);
 
   virtual nsresult Allocate(const AudioTrackConstraintsN &aConstraints,
                             const MediaEnginePrefs &aPrefs);
   virtual nsresult Deallocate();
   virtual nsresult Start(SourceMediaStream*, TrackID);
   virtual nsresult Stop(SourceMediaStream*, TrackID);
+  virtual void SetDirectListeners(bool aHasDirectListeners) {};
   virtual nsresult Snapshot(uint32_t aDuration, nsIDOMFile** aFile);
   virtual nsresult Config(bool aEchoOn, uint32_t aEcho,
                           bool aAgcOn, uint32_t aAGC,
                           bool aNoiseOn, uint32_t aNoise,
                           int32_t aPlayoutDelay);
 
   virtual void NotifyPull(MediaStreamGraph* aGraph,
                           SourceMediaStream *aSource,
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -579,16 +579,23 @@ MediaEngineWebRTCVideoSource::Stop(Sourc
   mViERender->StopRender(mCaptureIndex);
   mViERender->RemoveRenderer(mCaptureIndex);
   mViECapture->StopCapture(mCaptureIndex);
 #endif
 
   return NS_OK;
 }
 
+void
+MediaEngineWebRTCVideoSource::SetDirectListeners(bool aHasDirectListeners)
+{
+  LOG((__FUNCTION__));
+  mHasDirectListeners = aHasDirectListeners;
+}
+
 nsresult
 MediaEngineWebRTCVideoSource::Snapshot(uint32_t aDuration, nsIDOMFile** aFile)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 /**
  * Initialization and Shutdown functions for the video source, called by the
@@ -729,26 +736,29 @@ GetRotateAmount(ScreenOrientation aScree
   } else {
     //front camera
     result = (aCameraMountAngle + screenAngle) % 360;
   }
   return result;
 }
 
 // undefine to remove on-the-fly rotation support
-// #define DYNAMIC_GUM_ROTATION
+#define DYNAMIC_GUM_ROTATION
 
 void
 MediaEngineWebRTCVideoSource::Notify(const hal::ScreenConfiguration& aConfiguration) {
 #ifdef DYNAMIC_GUM_ROTATION
-  MonitorAutoLock enter(mMonitor);
-  mRotation = GetRotateAmount(aConfiguration.orientation(), mCameraAngle, mBackCamera);
+  if (mHasDirectListeners) {
+    // aka hooked to PeerConnection
+    MonitorAutoLock enter(mMonitor);
+    mRotation = GetRotateAmount(aConfiguration.orientation(), mCameraAngle, mBackCamera);
 
-  LOG(("*** New orientation: %d (Camera %d Back %d MountAngle: %d)",
-       mRotation, mCaptureIndex, mBackCamera, mCameraAngle));
+    LOG(("*** New orientation: %d (Camera %d Back %d MountAngle: %d)",
+         mRotation, mCaptureIndex, mBackCamera, mCameraAngle));
+  }
 #endif
 }
 
 void
 MediaEngineWebRTCVideoSource::StartImpl(webrtc::CaptureCapability aCapability) {
   MOZ_ASSERT(NS_IsMainThread());
 
   ICameraControl::Configuration config;
--- a/content/media/webspeech/recognition/SpeechStreamListener.cpp
+++ b/content/media/webspeech/recognition/SpeechStreamListener.cpp
@@ -79,15 +79,16 @@ SpeechStreamListener::ConvertAndDispatch
 
   int16_t* to = static_cast<int16_t*>(samples->Data());
   ConvertAudioSamplesWithScale(aData, to, aDuration, aVolume);
 
   mRecognition->FeedAudioData(samples.forget(), aDuration, this);
 }
 
 void
-SpeechStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
+SpeechStreamListener::NotifyEvent(MediaStreamGraph* aGraph,
+                                  MediaStreamListener::MediaStreamGraphEvent event)
 {
   // TODO dispatch SpeechEnd event so services can be informed
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/media/webspeech/recognition/SpeechStreamListener.h
+++ b/content/media/webspeech/recognition/SpeechStreamListener.h
@@ -25,17 +25,18 @@ public:
   ~SpeechStreamListener();
 
   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
                                 TrackRate aTrackRate,
                                 TrackTicks aTrackOffset,
                                 uint32_t aTrackEvents,
                                 const MediaSegment& aQueuedMedia) MOZ_OVERRIDE;
 
-  void NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+  void NotifyEvent(MediaStreamGraph* aGraph,
+                   MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE;
 
 private:
   template<typename SampleFormatType>
   void ConvertAndDispatchAudioChunk(int aDuration, float aVolume, SampleFormatType* aData);
   nsRefPtr<SpeechRecognition> mRecognition;
 };
 
 } // namespace dom
--- a/content/media/webspeech/synth/nsSpeechTask.cpp
+++ b/content/media/webspeech/synth/nsSpeechTask.cpp
@@ -44,38 +44,45 @@ public:
   void DoNotifyFinished()
   {
     if (mSpeechTask) {
       mSpeechTask->DispatchEndImpl(mSpeechTask->GetCurrentTime(),
                                    mSpeechTask->GetCurrentCharOffset());
     }
   }
 
-  virtual void NotifyFinished(MediaStreamGraph* aGraph)
+  virtual void NotifyEvent(MediaStreamGraph* aGraph,
+                           MediaStreamListener::MediaStreamGraphEvent event) MOZ_OVERRIDE
   {
-    nsCOMPtr<nsIRunnable> event =
-      NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
-    aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
+    switch (event) {
+      case EVENT_FINISHED:
+        {
+          nsCOMPtr<nsIRunnable> runnable =
+            NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyFinished);
+          aGraph->DispatchToMainThreadAfterStreamStateUpdate(runnable.forget());
+        }
+        break;
+      case EVENT_REMOVED:
+        mSpeechTask = nullptr;
+        break;
+      default:
+        break;
+    }
   }
 
   virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked)
   {
     if (aBlocked == MediaStreamListener::UNBLOCKED && !mStarted) {
       mStarted = true;
       nsCOMPtr<nsIRunnable> event =
         NS_NewRunnableMethod(this, &SynthStreamListener::DoNotifyStarted);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(event.forget());
     }
   }
 
-  virtual void NotifyRemoved(MediaStreamGraph* aGraph)
-  {
-    mSpeechTask = nullptr;
-  }
-
 private:
   // Raw pointer; if we exist, the stream exists,
   // and 'mSpeechTask' exclusively owns it and therefor exists as well.
   nsSpeechTask* mSpeechTask;
 
   bool mStarted;
 };
 
--- a/content/svg/content/src/SVGEllipseElement.cpp
+++ b/content/svg/content/src/SVGEllipseElement.cpp
@@ -98,18 +98,17 @@ SVGEllipseElement::ConstructPath(gfxCont
 {
   RefPtr<DrawTarget> dt = aCtx->GetDrawTarget();
   FillRule fillRule =
     aCtx->CurrentFillRule() == gfxContext::FILL_RULE_WINDING ?
       FillRule::FILL_WINDING : FillRule::FILL_EVEN_ODD;
   RefPtr<PathBuilder> builder = dt->CreatePathBuilder(fillRule);
   RefPtr<Path> path = BuildPath(builder);
   if (path) {
-    nsRefPtr<gfxPath> gfxpath = new gfxPath(path);
-    aCtx->SetPath(gfxpath);
+    aCtx->SetPath(path);
   }
 }
 
 TemporaryRef<Path>
 SVGEllipseElement::BuildPath(PathBuilder* aBuilder)
 {
   float x, y, rx, ry;
   GetAnimatedLengthValues(&x, &y, &rx, &ry, nullptr);
--- a/docshell/base/nsIContentViewer.idl
+++ b/docshell/base/nsIContentViewer.idl
@@ -22,17 +22,17 @@ class nsDOMNavigationTiming;
 
 [ptr] native nsIWidgetPtr(nsIWidget);
 [ref] native nsIntRectRef(nsIntRect);
 [ptr] native nsIPresShellPtr(nsIPresShell);
 [ptr] native nsPresContextPtr(nsPresContext);
 [ptr] native nsViewPtr(nsView);
 [ptr] native nsDOMNavigationTimingPtr(nsDOMNavigationTiming);
 
-[scriptable, builtinclass, uuid(0cb321bd-5b38-4586-8fcd-d43b366886fb)]
+[scriptable, builtinclass, uuid(f92298b8-4fe3-40d1-aad7-44e704fffd0d)]
 interface nsIContentViewer : nsISupports
 {
 
   [noscript] void init(in nsIWidgetPtr aParentWidget,
                        [const] in nsIntRectRef aBounds);
 
   attribute nsIDocShell container;
 
@@ -171,18 +171,20 @@ interface nsIContentViewer : nsISupports
    * we're part way through some operation (eg beforeunload) that shouldn't be
    * rentrant if the user closes the tab while the prompt is showing.
    * See bug 613800.
    */
   readonly attribute boolean isTabModalPromptAllowed;
 
   /**
    * Returns whether this content viewer is in a hidden state.
+   *
+   * @note Only Gecko internal code should set the attribute!
    */
-  readonly attribute boolean isHidden;
+  attribute boolean isHidden;
 
   [noscript] readonly attribute nsIPresShellPtr presShell;
   [noscript] readonly attribute nsPresContextPtr presContext;
   // aDocument must not be null.
   [noscript] void setDocumentInternal(in nsIDocument aDocument,
                                       in boolean aForceReuseInnerWindow);
   /**
    * Find the view to use as the container view for MakeWindow. Returns
--- a/dom/apps/tests/file_packaged_app.sjs
+++ b/dom/apps/tests/file_packaged_app.sjs
@@ -15,33 +15,45 @@ var gMiniManifestTemplate = "file_packag
 var gAppTemplate = "file_packaged_app.template.html";
 var gAppName = "appname";
 var gDevName = "devname";
 var gDevUrl = "http://dev.url";
 
 function handleRequest(request, response) {
   var query = getQuery(request);
 
-  response.setHeader("Access-Control-Allow-Origin", "*", false);
-
   var packageSize = ("packageSize" in query) ? query.packageSize : 0;
   var appName = ("appName" in query) ? query.appName : gAppName;
   var devName = ("devName" in query) ? query.devName : gDevName;
   var devUrl = ("devUrl" in query) ? query.devUrl : gDevUrl;
+  // allowCancel just means deliver the file slowly so we have time to cancel it
+  var allowCancel = "allowCancel" in query;
+  var getPackage = "getPackage" in query;
+  var alreadyDeferred = Number(getState("alreadyDeferred"));
+
+  if (allowCancel && getPackage && !alreadyDeferred) {
+    // Only do this for the actual package delivery.
+    response.processAsync();
+    // And to avoid timer problems, only do this once.
+    setState("alreadyDeferred", "1");
+  }
+
+  response.setHeader("Access-Control-Allow-Origin", "*", false);
 
   // If this is a version update, update state, prepare the manifest,
   // the application package and return.
   if ("setVersion" in query) {
     var version = query.setVersion;
     setState("version", version);
     var packageVersion = ("dontUpdatePackage" in query) ? version - 1 : version;
     var packageName = "test_packaged_app_" + packageVersion + ".zip";
 
     setState("packageName", packageName);
-    var packagePath = "/" + gBasePath + "file_packaged_app.sjs?getPackage=" +
+    var packagePath = "/" + gBasePath + "file_packaged_app.sjs?" +
+                      (allowCancel?"allowCancel&": "") + "getPackage=" +
                       packageName;
     setState("packagePath", packagePath);
 
     if (version == packageVersion) {
       // Create the application package.
       var zipWriter = Cc["@mozilla.org/zipwriter;1"]
                         .createInstance(Ci.nsIZipWriter);
       var zipFile = FileUtils.getFile("TmpD", [packageName]);
@@ -79,21 +91,29 @@ function handleRequest(request, response
     dump("Etags Match. Sending 304\n");
     response.setStatusLine(request.httpVersion, "304", "Not modified");
     return;
   }
 
   response.setHeader("Etag", etag, false);
 
   // Serve the application package corresponding to the requested app version.
-  if ("getPackage" in query) {
+  if (getPackage) {
     var resource = readFile(packageName, true);
     response.setHeader("Content-Type",
                        "Content-Type: application/java-archive", false);
-    response.write(resource);
+    if (allowCancel && !alreadyDeferred) {
+      var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+      timer.initWithCallback(function (aTimer) {
+          response.write(resource);
+          response.finish();
+      }, 3000, Ci.nsITimer.TYPE_ONE_SHOT);
+    } else {
+      response.write(resource);
+    }
     return;
   }
 
   // Serve the mini-manifest corresponding to the requested app version.
   if ("getManifest" in query) {
     var template = gBasePath + gMiniManifestTemplate;
     if (!("noManifestContentType" in query)) {
       response.setHeader("Content-Type",
--- a/dom/apps/tests/test_packaged_app_common.js
+++ b/dom/apps/tests/test_packaged_app_common.js
@@ -52,23 +52,27 @@ var PackagedTestHelper = (function Packa
     finish();
   }
 
   function xhrAbort(url) {
     ok(false, "XHR abort loading " + url);
     finish();
   }
 
-  function setAppVersion(aVersion, aCb, aDontUpdatePackage) {
+  function setAppVersion(aVersion, aCb, aDontUpdatePackage, aAllowCancel) {
     var xhr = new XMLHttpRequest();
     var dontUpdate = "";
+    var allowCancel = "";
     if (aDontUpdatePackage) {
       dontUpdate = "&dontUpdatePackage=1";
     }
-    var url = gSJS + "?setVersion=" + aVersion + dontUpdate;
+    if (aAllowCancel) {
+      allowCancel= "&allowCancel=1";
+    }
+    var url = gSJS + "?setVersion=" + aVersion + dontUpdate + allowCancel;
     xhr.addEventListener("load", function() {
                            is(xhr.responseText, "OK", "setAppVersion OK");
                            aCb();
                          });
     xhr.addEventListener("error", event => xhrError(event, url));
     xhr.addEventListener("abort", event => xhrAbort(url));
     xhr.open("GET", url, true);
     xhr.send();
--- a/dom/apps/tests/test_packaged_app_install.html
+++ b/dom/apps/tests/test_packaged_app_install.html
@@ -29,29 +29,29 @@ function checkAppInstallError(aMiniManif
   var req = navigator.mozApps.installPackage(aMiniManifestURL);
   req.onsuccess = function() {
     ok(false, "We are supposed to throw " + aExpectedError);
     PackagedTestHelper.finish();
   };
   req.onerror = function(evt) {
     var error = evt.target.error.name;
     if (error == aExpectedError) {
-      ok(true, "Got expected " + aExpectedError);
+      info("Got expected " + aExpectedError);
       PackagedTestHelper.next();
     } else {
       ok(false, "Got unexpected " + error);
       PackagedTestHelper.finish();
     }
   };
 }
 
 function checkUninstallApp(aApp) {
   var req = navigator.mozApps.mgmt.uninstall(aApp);
   req.onsuccess = function() {
-    ok(true, "App uninstalled");
+    info("App uninstalled");
     aApp.ondownloadsuccess = null;
     aApp.ondownloaderror = null;
     aApp.onprogress = null;
     PackagedTestHelper.next();
   };
   req.onerror = function(evt) {
     ok(false, "Got unexpected " + evt.target.error.name);
     PackagedTestHelper.finish();
@@ -78,84 +78,84 @@ function checkInstalledApp(aMiniManifest
 
 SimpleTest.waitForExplicitFinish();
 
 var steps = [
   function() {
     // Set up
     SpecialPowers.setAllAppsLaunchable(true);
     SpecialPowers.addPermission("webapps-manage", true, document);
-    ok(true, "Set up");
+    info("Set up");
     PackagedTestHelper.next();
   },
   function() {
-    ok(true, "autoConfirmAppInstall");
+    info("autoConfirmAppInstall");
     SpecialPowers.autoConfirmAppInstall(PackagedTestHelper.next);
   },
   function() {
     PackagedTestHelper.setAppVersion(0, PackagedTestHelper.next);
   },
   function() {
     // Bug 927699 - navigator.mozApps.install(url) lets NS_ERROR_FAILURE onto
     //              the web.
-    ok(true, "== TEST == INVALID_URL");
+    info("== TEST == INVALID_URL");
     checkAppInstallError("", "INVALID_URL");
   },
   function() {
     // Test network error.
-    ok(true, "== TEST == Network error");
+    info("== TEST == Network error");
     checkAppInstallError("http://notvalidurl", "NETWORK_ERROR");
   },
   function() {
     // Test wrong mini-manifest content type.
-    ok(true, "== TEST == Not valid mini-manifest content type");
+    info("== TEST == Not valid mini-manifest content type");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&noManifestContentType=true";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST_CONTENT_TYPE");
   },
   function() {
     // Test mini-manifest 'size' value is not number. Bug 839435.
-    ok(true, "== TEST == Size value is not a number");
+    info("== TEST == Size value is not a number");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&packageSize=\"NotANumber\"";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST");
   },
   function() {
     // Test mini-manifest  negative 'size' value. Bug 839435.
-    ok(true, "== TEST == Negative size value");
+    info("== TEST == Negative size value");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&packageSize=-1";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST");
   },
   function() {
     // Test wrong package path
-    ok(true, "== TEST == Installing app with wrong package path");
+    info("== TEST == Installing app with wrong package path");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&wrongPackagePath=true";
     checkAppInstallError(miniManifestURL, "INVALID_MANIFEST");
   },
   function() {
     // Test no manifest in zip file.
-    ok(true, "== TEST == No manifest in the zip file");
+    info("== TEST == No manifest in the zip file");
     var miniManifestURL = PackagedTestHelper.gSJS + "?getManifest=true";
     PackagedTestHelper.checkAppDownloadError(miniManifestURL,
                                             "MISSING_MANIFEST", 0, true, true,
                                              PackagedTestHelper.gAppName);
   },
   function() {
       PackagedTestHelper.setAppVersion(1, PackagedTestHelper.next);
   },
   function() {
     // Test mini-manifest app name is different from the webapp manifest name.
     // Bug 844243.
-    ok(true, "== TEST == Mini-manifest app name is different from webapp " +
+    info("== TEST == Mini-manifest app name is different from webapp " +
              "manifest name");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true" +
                           "&appName=arandomname";
     PackagedTestHelper.checkAppDownloadError(miniManifestURL,
                                              "MANIFEST_MISMATCH", 1, true, true,
                                              "arandomname");
   },
@@ -182,29 +182,29 @@ var steps = [
     PackagedTestHelper.checkAppDownloadError(miniManifestURL,
                                              "MANIFEST_MISMATCH", 1, true, true,
                                              PackagedTestHelper.gAppName);
   },
   function() {
     PackagedTestHelper.setAppVersion(2, PackagedTestHelper.next);
   },
   function() {
-    ok(true, "== TEST == Install packaged app");
+    info("== TEST == Install packaged app");
     var miniManifestURL = PackagedTestHelper.gSJS +
                           "?getManifest=true";
     navigator.mozApps.mgmt.oninstall = function(evt) {
-      ok(true, "Got oninstall event");
+      info("Got oninstall event");
       PackagedTestHelper.gApp = evt.application;
       PackagedTestHelper.gApp.ondownloaderror = function() {
         ok(false, "Download error " +
                   PackagedTestHelper.gApp.downloadError.name);
         PackagedTestHelper.finish();
       };
       PackagedTestHelper.gApp.ondownloadsuccess = function() {
-        ok(true, "App downloaded");
+        info("App downloaded");
         var expected = {
           name: PackagedTestHelper.gAppName,
           manifestURL: miniManifestURL,
           installOrigin: PackagedTestHelper.gInstallOrigin,
           progress: 0,
           installState: "installed",
           downloadAvailable: false,
           downloading: false,
@@ -215,21 +215,95 @@ var steps = [
         PackagedTestHelper.checkAppState(PackagedTestHelper.gApp, 2, expected,
                                          true, false, PackagedTestHelper.next);
       };
     };
 
     var request = navigator.mozApps.installPackage(miniManifestURL);
     request.onerror = PackagedTestHelper.mozAppsError;
     request.onsuccess = function() {
-      ok(true, "Application installed");
+      info("Application installed");
     };
   },
   function() {
-    ok(true, "all done!\n");
+    PackagedTestHelper.setAppVersion(3, PackagedTestHelper.next, false, true);
+  },
+  function() {
+    info("== TEST == Install packaged app with a cancel/resume");
+    var miniManifestURL = PackagedTestHelper.gSJS +
+                          "?getManifest=true&allowCancel";
+    navigator.mozApps.mgmt.oninstall = function(evt) {
+      info("Got oninstall event");
+      PackagedTestHelper.gApp = evt.application;
+
+      PackagedTestHelper.gApp.onprogress = function() {
+        // Let's try cancelling and resuming the download later on.
+        PackagedTestHelper.gApp.cancelDownload();
+        // And only do this once.
+        PackagedTestHelper.gApp.onprogress = null;
+      };
+
+      var alreadyCancelled = false;
+      PackagedTestHelper.gApp.ondownloaderror = function() {
+        ok(!alreadyCancelled, "The download should be cancelled only once!");
+        is(PackagedTestHelper.gApp.downloadError.name, "DOWNLOAD_CANCELED",
+           "Download error " + PackagedTestHelper.gApp.downloadError.name);
+        if (!alreadyCancelled) {
+          PackagedTestHelper.gApp.download();
+          alreadyCancelled = true;
+        }
+      };
+
+      PackagedTestHelper.gApp.ondownloadsuccess = function() {
+        info("App downloaded");
+        // We could try also applying the download we just made.
+        var expected = {
+          name: PackagedTestHelper.gAppName,
+          manifestURL: miniManifestURL,
+          installOrigin: PackagedTestHelper.gInstallOrigin,
+          progress: 0,
+          installState: "pending",
+          downloadAvailable: false,
+          downloading: false,
+          downloadSize: 0,
+          size: 0,
+          readyToApplyDownload: true
+        };
+        PackagedTestHelper.checkAppState(PackagedTestHelper.gApp, 3, expected,
+                                         true, false, function() {});
+      };
+
+      PackagedTestHelper.gApp.ondownloadapplied = function() {
+        info("App download applied.");
+        var expected = {
+          name: PackagedTestHelper.gAppName,
+          manifestURL: miniManifestURL,
+          installOrigin: PackagedTestHelper.gInstallOrigin,
+          progress: 0,
+          installState: "installed",
+          downloadAvailable: false,
+          downloading: false,
+          downloadSize: 0,
+          size: 0,
+          readyToApplyDownload: false
+        };
+        PackagedTestHelper.checkAppState(PackagedTestHelper.gApp, 3, expected,
+                                         true, false, PackagedTestHelper.next);
+      }
+
+    };
+
+    var request = navigator.mozApps.installPackage(miniManifestURL);
+    request.onerror = PackagedTestHelper.mozAppsError;
+    request.onsuccess = function() {
+      info("Application installed");
+    };
+  },
+  function() {
+    info("all done!\n");
     PackagedTestHelper.finish();
   }
 ];
 
 PackagedTestHelper.setSteps(steps);
 
 addLoadEvent(PackagedTestHelper.start);
 
--- a/dom/base/URL.cpp
+++ b/dom/base/URL.cpp
@@ -199,17 +199,17 @@ URL::RevokeObjectURL(const GlobalObject&
     if (window && window->GetExtantDoc()) {
       window->GetExtantDoc()->UnregisterHostObjectUri(asciiurl);
     }
     nsHostObjectProtocolHandler::RemoveDataEntry(asciiurl);
   }
 }
 
 void
-URL::GetHref(nsString& aHref) const
+URL::GetHref(nsString& aHref, ErrorResult& aRv) const
 {
   aHref.Truncate();
 
   nsAutoCString href;
   nsresult rv = mURI->GetSpec(href);
   if (NS_SUCCEEDED(rv)) {
     CopyUTF8toUTF16(href, aHref);
   }
@@ -235,35 +235,35 @@ URL::SetHref(const nsAString& aHref, Err
     return;
   }
 
   mURI = uri;
   UpdateURLSearchParams();
 }
 
 void
-URL::GetOrigin(nsString& aOrigin) const
+URL::GetOrigin(nsString& aOrigin, ErrorResult& aRv) const
 {
   nsContentUtils::GetUTFNonNullOrigin(mURI, aOrigin);
 }
 
 void
-URL::GetProtocol(nsString& aProtocol) const
+URL::GetProtocol(nsString& aProtocol, ErrorResult& aRv) const
 {
   nsCString protocol;
   if (NS_SUCCEEDED(mURI->GetScheme(protocol))) {
     aProtocol.Truncate();
   }
 
   CopyASCIItoUTF16(protocol, aProtocol);
   aProtocol.Append(char16_t(':'));
 }
 
 void
-URL::SetProtocol(const nsAString& aProtocol)
+URL::SetProtocol(const nsAString& aProtocol, ErrorResult& aRv)
 {
   nsAString::const_iterator start, end;
   aProtocol.BeginReading(start);
   aProtocol.EndReading(end);
   nsAString::const_iterator iter(start);
 
   FindCharInReadable(':', iter, end);
 
@@ -300,47 +300,47 @@ URL::SetProtocol(const nsAString& aProto
   value.Truncate();               \
   nsAutoCString tmp;              \
   nsresult rv = mURI->func(tmp);  \
   if (NS_SUCCEEDED(rv)) {         \
     CopyUTF8toUTF16(tmp, value);  \
   }
 
 void
-URL::GetUsername(nsString& aUsername) const
+URL::GetUsername(nsString& aUsername, ErrorResult& aRv) const
 {
   URL_GETTER(aUsername, GetUsername);
 }
 
 void
-URL::SetUsername(const nsAString& aUsername)
+URL::SetUsername(const nsAString& aUsername, ErrorResult& aRv)
 {
   mURI->SetUsername(NS_ConvertUTF16toUTF8(aUsername));
 }
 
 void
-URL::GetPassword(nsString& aPassword) const
+URL::GetPassword(nsString& aPassword, ErrorResult& aRv) const
 {
   URL_GETTER(aPassword, GetPassword);
 }
 
 void
-URL::SetPassword(const nsAString& aPassword)
+URL::SetPassword(const nsAString& aPassword, ErrorResult& aRv)
 {
   mURI->SetPassword(NS_ConvertUTF16toUTF8(aPassword));
 }
 
 void
-URL::GetHost(nsString& aHost) const
+URL::GetHost(nsString& aHost, ErrorResult& aRv) const
 {
   URL_GETTER(aHost, GetHostPort);
 }
 
 void
-URL::SetHost(const nsAString& aHost)
+URL::SetHost(const nsAString& aHost, ErrorResult& aRv)
 {
   mURI->SetHostPort(NS_ConvertUTF16toUTF8(aHost));
 }
 
 void
 URL::URLSearchParamsUpdated()
 {
   MOZ_ASSERT(mSearchParams);
@@ -365,56 +365,56 @@ URL::UpdateURLSearchParams()
       NS_WARNING("Failed to get the query from a nsIURL.");
     }
   }
 
   mSearchParams->ParseInput(search, this);
 }
 
 void
-URL::GetHostname(nsString& aHostname) const
+URL::GetHostname(nsString& aHostname, ErrorResult& aRv) const
 {
   aHostname.Truncate();
   nsAutoCString tmp;
   nsresult rv = mURI->GetHost(tmp);
   if (NS_SUCCEEDED(rv)) {
     if (tmp.FindChar(':') != -1) { // Escape IPv6 address
       MOZ_ASSERT(!tmp.Length() ||
         (tmp[0] !='[' && tmp[tmp.Length() - 1] != ']'));
       tmp.Insert('[', 0);
       tmp.Append(']');
     }
     CopyUTF8toUTF16(tmp, aHostname);
   }
 }
 
 void
-URL::SetHostname(const nsAString& aHostname)
+URL::SetHostname(const nsAString& aHostname, ErrorResult& aRv)
 {
   // nsStandardURL returns NS_ERROR_UNEXPECTED for an empty hostname
   // The return code is silently ignored
   mURI->SetHost(NS_ConvertUTF16toUTF8(aHostname));
 }
 
 void
-URL::GetPort(nsString& aPort) const
+URL::GetPort(nsString& aPort, ErrorResult& aRv) const
 {
   aPort.Truncate();
 
   int32_t port;
   nsresult rv = mURI->GetPort(&port);
   if (NS_SUCCEEDED(rv) && port != -1) {
     nsAutoString portStr;
     portStr.AppendInt(port, 10);
     aPort.Assign(portStr);
   }
 }
 
 void
-URL::SetPort(const nsAString& aPort)
+URL::SetPort(const nsAString& aPort, ErrorResult& aRv)
 {
   nsresult rv;
   nsAutoString portStr(aPort);
   int32_t port = -1;
 
   // nsIURI uses -1 as default value.
   if (!portStr.IsEmpty()) {
     port = portStr.ToInteger(&rv);
@@ -422,17 +422,17 @@ URL::SetPort(const nsAString& aPort)
       return;
     }
   }
 
   mURI->SetPort(port);
 }
 
 void
-URL::GetPathname(nsString& aPathname) const
+URL::GetPathname(nsString& aPathname, ErrorResult& aRv) const
 {
   aPathname.Truncate();
 
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (!url) {
     // Do not throw!  Not having a valid URI or URL should result in an empty
     // string.
     return;
@@ -441,29 +441,29 @@ URL::GetPathname(nsString& aPathname) co
   nsAutoCString file;
   nsresult rv = url->GetFilePath(file);
   if (NS_SUCCEEDED(rv)) {
     CopyUTF8toUTF16(file, aPathname);
   }
 }
 
 void
-URL::SetPathname(const nsAString& aPathname)
+URL::SetPathname(const nsAString& aPathname, ErrorResult& aRv)
 {
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (!url) {
     // Ignore failures to be compatible with NS4.
     return;
   }
 
   url->SetFilePath(NS_ConvertUTF16toUTF8(aPathname));
 }
 
 void
-URL::GetSearch(nsString& aSearch) const
+URL::GetSearch(nsString& aSearch, ErrorResult& aRv) const
 {
   aSearch.Truncate();
 
   nsCOMPtr<nsIURL> url(do_QueryInterface(mURI));
   if (!url) {
     // Do not throw!  Not having a valid URI or URL should result in an empty
     // string.
     return;
@@ -472,17 +472,17 @@ URL::GetSearch(nsString& aSearch) const
   nsAutoCString search;
   nsresult rv = url->GetQuery(search);
   if (NS_SUCCEEDED(rv) && !search.IsEmpty()) {
     CopyUTF8toUTF16(NS_LITERAL_CSTRING("?") + search, aSearch);
   }
 }
 
 void
-URL::SetSearch(const nsAString& aSearch)
+URL::SetSearch(const nsAString& aSearch, ErrorResult& aRv)
 {
   SetSearchInternal(aSearch);
   UpdateURLSearchParams();
 }
 
 void
 URL::SetSearchInternal(const nsAString& aSearch)
 {
@@ -514,31 +514,31 @@ URL::SetSearchParams(URLSearchParams& aS
   mSearchParams->AddObserver(this);
 
   nsAutoString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
-URL::GetHash(nsString& aHash) const
+URL::GetHash(nsString& aHash, ErrorResult& aRv) const
 {
   aHash.Truncate();
 
   nsAutoCString ref;
   nsresult rv = mURI->GetRef(ref);
   if (NS_SUCCEEDED(rv) && !ref.IsEmpty()) {
     NS_UnescapeURL(ref); // XXX may result in random non-ASCII bytes!
     aHash.Assign(char16_t('#'));
     AppendUTF8toUTF16(ref, aHash);
   }
 }
 
 void
-URL::SetHash(const nsAString& aHash)
+URL::SetHash(const nsAString& aHash, ErrorResult& aRv)
 {
   mURI->SetRef(NS_ConvertUTF16toUTF8(aHash));
 }
 
 bool IsChromeURI(nsIURI* aURI)
 {
   bool isChrome = false;
   if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)))
--- a/dom/base/URL.h
+++ b/dom/base/URL.h
@@ -64,65 +64,65 @@ public:
   static void CreateObjectURL(const GlobalObject& aGlobal,
                               MediaSource& aSource,
                               const objectURLOptions& aOptions,
                               nsString& aResult,
                               ErrorResult& aError);
   static void RevokeObjectURL(const GlobalObject& aGlobal,
                               const nsAString& aURL);
 
-  void GetHref(nsString& aHref) const;
+  void GetHref(nsString& aHref, ErrorResult& aRv) const;
 
   void SetHref(const nsAString& aHref, ErrorResult& aRv);
 
-  void GetOrigin(nsString& aOrigin) const;
+  void GetOrigin(nsString& aOrigin, ErrorResult& aRv) const;
 
-  void GetProtocol(nsString& aProtocol) const;
+  void GetProtocol(nsString& aProtocol, ErrorResult& aRv) const;
 
-  void SetProtocol(const nsAString& aProtocol);
+  void SetProtocol(const nsAString& aProtocol, ErrorResult& aRv);
 
-  void GetUsername(nsString& aUsername) const;
+  void GetUsername(nsString& aUsername, ErrorResult& aRv) const;
 
-  void SetUsername(const nsAString& aUsername);
+  void SetUsername(const nsAString& aUsername, ErrorResult& aRv);
 
-  void GetPassword(nsString& aPassword) const;
+  void GetPassword(nsString& aPassword, ErrorResult& aRv) const;
 
-  void SetPassword(const nsAString& aPassword);
+  void SetPassword(const nsAString& aPassword, ErrorResult& aRv);
 
-  void GetHost(nsString& aHost) const;
+  void GetHost(nsString& aHost, ErrorResult& aRv) const;
 
-  void SetHost(const nsAString& aHost);
+  void SetHost(const nsAString& aHost, ErrorResult& aRv);
 
-  void GetHostname(nsString& aHostname) const;
+  void GetHostname(nsString& aHostname, ErrorResult& aRv) const;
 
-  void SetHostname(const nsAString& aHostname);
+  void SetHostname(const nsAString& aHostname, ErrorResult& aRv);
 
-  void GetPort(nsString& aPort) const;
+  void GetPort(nsString& aPort, ErrorResult& aRv) const;
 
-  void SetPort(const nsAString& aPort);
+  void SetPort(const nsAString& aPort, ErrorResult& aRv);
 
-  void GetPathname(nsString& aPathname) const;
+  void GetPathname(nsString& aPathname, ErrorResult& aRv) const;
 
-  void SetPathname(const nsAString& aPathname);
+  void SetPathname(const nsAString& aPathname, ErrorResult& aRv);
 
-  void GetSearch(nsString& aRetval) const;
+  void GetSearch(nsString& aRetval, ErrorResult& aRv) const;
 
-  void SetSearch(const nsAString& aArg);
+  void SetSearch(const nsAString& aArg, ErrorResult& aRv);
 
   URLSearchParams* SearchParams();
 
   void SetSearchParams(URLSearchParams& aSearchParams);
 
-  void GetHash(nsString& aRetval) const;
+  void GetHash(nsString& aRetval, ErrorResult& aRv) const;
 
-  void SetHash(const nsAString& aArg);
+  void SetHash(const nsAString& aArg, ErrorResult& aRv);
 
-  void Stringify(nsString& aRetval) const
+  void Stringify(nsString& aRetval, ErrorResult& aRv) const
   {
-    GetHref(aRetval);
+    GetHref(aRetval, aRv);
   }
 
   // URLSearchParamsObserver
   void URLSearchParamsUpdated() MOZ_OVERRIDE;
 
 private:
   nsIURI* GetURI() const
   {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -9644,24 +9644,24 @@ nsGlobalWindow::GetPrivateRoot()
       }
     }
   }
 
   return static_cast<nsGlobalWindow*>(top.get());
 }
 
 
-nsIDOMLocation*
+nsLocation*
 nsGlobalWindow::GetLocation(ErrorResult& aError)
 {
   FORWARD_TO_INNER_OR_THROW(GetLocation, (aError), aError, nullptr);
 
   nsIDocShell *docShell = GetDocShell();
   if (!mLocation && docShell) {
-    mLocation = new nsLocation(docShell);
+    mLocation = new nsLocation(this, docShell);
   }
   return mLocation;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetLocation(nsIDOMLocation ** aLocation)
 {
   ErrorResult rv;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -822,17 +822,17 @@ public:
   nsIDOMWindow* GetWindow(mozilla::ErrorResult& aError);
   nsIDOMWindow* GetSelf(mozilla::ErrorResult& aError);
   nsIDocument* GetDocument()
   {
     return GetDoc();
   }
   void GetName(nsAString& aName, mozilla::ErrorResult& aError);
   void SetName(const nsAString& aName, mozilla::ErrorResult& aError);
-  nsIDOMLocation* GetLocation(mozilla::ErrorResult& aError);
+  nsLocation* GetLocation(mozilla::ErrorResult& aError);
   nsHistory* GetHistory(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetLocationbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetMenubar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetPersonalbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetScrollbars(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetStatusbar(mozilla::ErrorResult& aError);
   mozilla::dom::BarProp* GetToolbar(mozilla::ErrorResult& aError);
   void GetStatus(nsAString& aStatus, mozilla::ErrorResult& aError);
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -3074,51 +3074,42 @@ mozilla::dom::ShutdownJSEnvironment()
     NS_IF_RELEASE(sRuntimeService);
     NS_IF_RELEASE(sSecurityManager);
   }
 
   sShuttingDown = true;
   sDidShutdown = true;
 }
 
-class nsJSArgArray;
-
-namespace mozilla {
-template<>
-struct HasDangerousPublicDestructor<nsJSArgArray>
-{
-  static const bool value = true;
-};
-}
-
 // A fast-array class for JS.  This class supports both nsIJSScriptArray and
 // nsIArray.  If it is JS itself providing and consuming this class, all work
 // can be done via nsIJSScriptArray, and avoid the conversion of elements
 // to/from nsISupports.
 // When consumed by non-JS (eg, another script language), conversion is done
 // on-the-fly.
 class nsJSArgArray MOZ_FINAL : public nsIJSArgArray {
 public:
   nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
                nsresult *prv);
-  ~nsJSArgArray();
+
   // nsISupports
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSArgArray,
                                                          nsIJSArgArray)
 
   // nsIArray
   NS_DECL_NSIARRAY
 
   // nsIJSArgArray
   nsresult GetArgs(uint32_t *argc, void **argv);
 
   void ReleaseJSObjects();
 
 protected:
+  ~nsJSArgArray();
   JSContext *mContext;
   JS::Heap<JS::Value> *mArgv;
   uint32_t mArgc;
 };
 
 nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
                            nsresult *prv) :
     mContext(aContext),
@@ -3238,18 +3229,16 @@ NS_IMETHODIMP nsJSArgArray::Enumerate(ns
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 // The factory function
 nsresult NS_CreateJSArgv(JSContext *aContext, uint32_t argc, void *argv,
                          nsIJSArgArray **aArray)
 {
   nsresult rv;
-  nsJSArgArray *ret = new nsJSArgArray(aContext, argc,
-                                       static_cast<JS::Value *>(argv), &rv);
-  if (ret == nullptr)
-    return NS_ERROR_OUT_OF_MEMORY;
+  nsCOMPtr<nsIJSArgArray> ret = new nsJSArgArray(aContext, argc,
+                                                static_cast<JS::Value *>(argv), &rv);
   if (NS_FAILED(rv)) {
-    delete ret;
     return rv;
   }
-  return ret->QueryInterface(NS_GET_IID(nsIArray), (void **)aArray);
+  ret.forget(aArray);
+  return NS_OK;
 }
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -344,18 +344,18 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
     // if CSP is enabled, and setTimeout/setInterval was called with a string,
     // disable the registration and log an error
     ErrorResult error;
     *aAllowEval = CheckCSPForEval(cx, aWindow, error);
     if (error.Failed() || !*aAllowEval) {
       return error.ErrorCode();
     }
 
-    mExpr.Append(JS_GetFlatStringChars(expr),
-                 JS_GetStringLength(JS_FORGET_STRING_FLATNESS(expr)));
+    MOZ_ASSERT(mExpr.IsEmpty());
+    AssignJSFlatString(mExpr, expr);
 
     // Get the calling location.
     const char *filename;
     if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) {
       mFileName.Assign(filename);
     }
   } else if (funobj) {
     *aAllowEval = true;
--- a/dom/base/nsJSUtils.h
+++ b/dom/base/nsJSUtils.h
@@ -152,16 +152,28 @@ AssignJSString(JSContext *cx, T &dest, J
   if (MOZ_UNLIKELY(!js::CopyStringChars(cx, dest.BeginWriting(), s, len))) {
     return false;
   }
   dest.BeginWriting()[len] = '\0';
   dest.SetLength(len);
   return true;
 }
 
+inline void
+AssignJSFlatString(nsAString &dest, JSFlatString *s)
+{
+  size_t len = js::GetFlatStringLength(s);
+  static_assert(js::MaxStringLength < (1 << 28),
+                "Shouldn't overflow here or in SetCapacity");
+  dest.SetCapacity(len + 1);
+  js::CopyFlatStringChars(dest.BeginWriting(), s, len);
+  dest.BeginWriting()[len] = '\0';
+  dest.SetLength(len);
+}
+
 class nsAutoJSString : public nsAutoString
 {
 public:
 
   /**
    * nsAutoJSString should be default constructed, which leaves it empty
    * (this->IsEmpty()), and initialized with one of the init() methods below.
    */
--- a/dom/base/nsLocation.cpp
+++ b/dom/base/nsLocation.cpp
@@ -27,16 +27,20 @@
 #include "nsReadableUtils.h"
 #include "nsITextToSubURI.h"
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "mozilla/Likely.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsNullPrincipal.h"
 #include "ScriptSettings.h"
+#include "mozilla/dom/LocationBinding.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
 
 static nsresult
 GetDocumentCharacterSetForURI(const nsAString& aHref, nsACString& aCharset)
 {
   aCharset.Truncate();
 
   JSContext *cx = nsContentUtils::GetCurrentJSContext();
   if (cx) {
@@ -47,40 +51,41 @@ GetDocumentCharacterSetForURI(const nsAS
     if (nsIDocument* doc = window->GetDoc()) {
       aCharset = doc->GetDocumentCharacterSet();
     }
   }
 
   return NS_OK;
 }
 
-nsLocation::nsLocation(nsIDocShell *aDocShell)
+nsLocation::nsLocation(nsPIDOMWindow* aWindow, nsIDocShell *aDocShell)
+  : mInnerWindow(aWindow)
 {
   MOZ_ASSERT(aDocShell);
+  MOZ_ASSERT(mInnerWindow->IsInnerWindow());
+  SetIsDOMBinding();
 
   mDocShell = do_GetWeakReference(aDocShell);
-  nsCOMPtr<nsIDOMWindow> outer = aDocShell->GetWindow();
-  mOuter = do_GetWeakReference(outer);
 }
 
 nsLocation::~nsLocation()
 {
 }
 
 DOMCI_DATA(Location, nsLocation)
 
 // QueryInterface implementation for nsLocation
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsLocation)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIDOMLocation)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Location)
 NS_INTERFACE_MAP_END
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(nsLocation)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsLocation, mInnerWindow)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsLocation)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsLocation)
 
 void
 nsLocation::SetDocShell(nsIDocShell *aDocShell)
 {
    mDocShell = do_GetWeakReference(aDocShell);
 }
@@ -724,16 +729,112 @@ nsLocation::SetProtocol(const nsAString&
   rv = uri->SetScheme(NS_ConvertUTF16toUTF8(aProtocol));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   return SetURI(uri);
 }
 
+void
+nsLocation::GetUsername(nsAString& aUsername, ErrorResult& aError)
+{
+  if (!CallerSubsumes()) {
+    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  aUsername.Truncate();
+  nsCOMPtr<nsIURI> uri;
+  nsresult result = GetURI(getter_AddRefs(uri));
+  if (uri) {
+    nsAutoCString username;
+    result = uri->GetUsername(username);
+    if (NS_SUCCEEDED(result)) {
+      CopyUTF8toUTF16(username, aUsername);
+    }
+  }
+}
+
+void
+nsLocation::SetUsername(const nsAString& aUsername, ErrorResult& aError)
+{
+  if (!CallerSubsumes()) {
+    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = GetWritableURI(getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  if (!uri) {
+    return;
+  }
+
+  rv = uri->SetUsername(NS_ConvertUTF16toUTF8(aUsername));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  rv = SetURI(uri);
+}
+
+void
+nsLocation::GetPassword(nsAString& aPassword, ErrorResult& aError)
+{
+  if (!CallerSubsumes()) {
+    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  aPassword.Truncate();
+  nsCOMPtr<nsIURI> uri;
+  nsresult result = GetURI(getter_AddRefs(uri));
+  if (uri) {
+    nsAutoCString password;
+    result = uri->GetPassword(password);
+    if (NS_SUCCEEDED(result)) {
+      CopyUTF8toUTF16(password, aPassword);
+    }
+  }
+}
+
+void
+nsLocation::SetPassword(const nsAString& aPassword, ErrorResult& aError)
+{
+  if (!CallerSubsumes()) {
+    aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
+    return;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = GetWritableURI(getter_AddRefs(uri));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  if (!uri) {
+    return;
+  }
+
+  rv = uri->SetPassword(NS_ConvertUTF16toUTF8(aPassword));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aError.Throw(rv);
+    return;
+  }
+
+  rv = SetURI(uri);
+}
+
 NS_IMETHODIMP
 nsLocation::GetSearch(nsAString& aSearch)
 {
   if (!CallerSubsumes())
     return NS_ERROR_DOM_SECURITY_ERR;
 
   aSearch.SetLength(0);
 
@@ -913,18 +1014,28 @@ nsLocation::GetSourceBaseURL(JSContext* 
   NS_ENSURE_TRUE(doc, NS_OK);
   *sourceURL = doc->GetBaseURI().take();
   return NS_OK;
 }
 
 bool
 nsLocation::CallerSubsumes()
 {
-  // Get the principal associated with the location object.
-  nsCOMPtr<nsIDOMWindow> outer = do_QueryReferent(mOuter);
+  // Get the principal associated with the location object.  Note that this is
+  // the principal of the page which will actually be navigated, not the
+  // principal of the Location object itself.  This is why we need this check
+  // even though we only allow limited cross-origin access to Location objects
+  // in general.
+  nsCOMPtr<nsIDOMWindow> outer = mInnerWindow->GetOuterWindow();
   if (MOZ_UNLIKELY(!outer))
     return false;
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(outer);
   bool subsumes = false;
   nsresult rv = nsContentUtils::SubjectPrincipal()->SubsumesConsideringDomain(sop->GetPrincipal(), &subsumes);
   NS_ENSURE_SUCCESS(rv, false);
   return subsumes;
 }
+
+JSObject*
+nsLocation::WrapObject(JSContext* aCx)
+{
+  return LocationBinding::Wrap(aCx, this);
+}
--- a/dom/base/nsLocation.h
+++ b/dom/base/nsLocation.h
@@ -8,40 +8,139 @@
 #define nsLocation_h__
 
 #include "nsIDOMLocation.h"
 #include "nsString.h"
 #include "nsIWeakReferenceUtils.h"
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "js/TypeDecls.h"
+#include "mozilla/ErrorResult.h"
+#include "nsPIDOMWindow.h"
 
 class nsIURI;
 class nsIDocShell;
 class nsIDocShellLoadInfo;
 
 //*****************************************************************************
 // nsLocation: Script "location" object
 //*****************************************************************************
 
-class nsLocation : public nsIDOMLocation
-                 , public nsWrapperCache
+class nsLocation MOZ_FINAL : public nsIDOMLocation
+                           , public nsWrapperCache
 {
+  typedef mozilla::ErrorResult ErrorResult;
+
 public:
-  nsLocation(nsIDocShell *aDocShell);
+  nsLocation(nsPIDOMWindow* aWindow, nsIDocShell *aDocShell);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsLocation)
 
   void SetDocShell(nsIDocShell *aDocShell);
   nsIDocShell *GetDocShell();
 
   // nsIDOMLocation
   NS_DECL_NSIDOMLOCATION
 
+  // WebIDL API:
+  void Assign(const nsAString& aUrl, ErrorResult& aError)
+  {
+    aError = Assign(aUrl);
+  }
+  void Replace(const nsAString& aUrl, ErrorResult& aError)
+  {
+    aError = Replace(aUrl);
+  }
+  void Reload(bool aForceget, ErrorResult& aError)
+  {
+    aError = Reload(aForceget);
+  }
+  void GetHref(nsAString& aHref, ErrorResult& aError)
+  {
+    aError = GetHref(aHref);
+  }
+  void SetHref(const nsAString& aHref, ErrorResult& aError)
+  {
+    aError = SetHref(aHref);
+  }
+  void GetOrigin(nsAString& aOrigin, ErrorResult& aError)
+  {
+    aError = GetOrigin(aOrigin);
+  }
+  void GetProtocol(nsAString& aProtocol, ErrorResult& aError)
+  {
+    aError = GetProtocol(aProtocol);
+  }
+  void SetProtocol(const nsAString& aProtocol, ErrorResult& aError)
+  {
+    aError = SetProtocol(aProtocol);
+  }
+  void GetUsername(nsAString& aUsername, ErrorResult& aError);
+  void SetUsername(const nsAString& aUsername, ErrorResult& aError);
+  void GetPassword(nsAString& aPassword, ErrorResult& aError);
+  void SetPassword(const nsAString& aPassword, ErrorResult& aError);
+  void GetHost(nsAString& aHost, ErrorResult& aError)
+  {
+    aError = GetHost(aHost);
+  }
+  void SetHost(const nsAString& aHost, ErrorResult& aError)
+  {
+    aError = SetHost(aHost);
+  }
+  void GetHostname(nsAString& aHostname, ErrorResult& aError)
+  {
+    aError = GetHostname(aHostname);
+  }
+  void SetHostname(const nsAString& aHostname, ErrorResult& aError)
+  {
+    aError = SetHostname(aHostname);
+  }
+  void GetPort(nsAString& aPort, ErrorResult& aError)
+  {
+    aError = GetPort(aPort);
+  }
+  void SetPort(const nsAString& aPort, ErrorResult& aError)
+  {
+    aError = SetPort(aPort);
+  }
+  void GetPathname(nsAString& aPathname, ErrorResult& aError)
+  {
+    aError = GetPathname(aPathname);
+  }
+  void SetPathname(const nsAString& aPathname, ErrorResult& aError)
+  {
+    aError = SetPathname(aPathname);
+  }
+  void GetSearch(nsAString& aSeach, ErrorResult& aError)
+  {
+    aError = GetSearch(aSeach);
+  }
+  void SetSearch(const nsAString& aSeach, ErrorResult& aError)
+  {
+    aError = SetSearch(aSeach);
+  }
+  void GetHash(nsAString& aHash, ErrorResult& aError)
+  {
+    aError = GetHash(aHash);
+  }
+  void SetHash(const nsAString& aHash, ErrorResult& aError)
+  {
+    aError = SetHash(aHash);
+  }
+  void Stringify(nsAString& aRetval, ErrorResult& aError)
+  {
+    GetHref(aRetval, aError);
+  }
+  nsPIDOMWindow* GetParentObject() const
+  {
+    return mInnerWindow;
+  }
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
 protected:
   virtual ~nsLocation();
 
   // In the case of jar: uris, we sometimes want the place the jar was
   // fetched from as the URI instead of the jar: uri itself.  Pass in
   // true for aGetInnermostURI when that's the case.
   nsresult GetURI(nsIURI** aURL, bool aGetInnermostURI = false);
   nsresult GetWritableURI(nsIURI** aURL);
@@ -51,14 +150,14 @@ protected:
   nsresult SetHrefWithContext(JSContext* cx, const nsAString& aHref,
                               bool aReplace);
 
   nsresult GetSourceBaseURL(JSContext* cx, nsIURI** sourceURL);
   nsresult CheckURL(nsIURI *url, nsIDocShellLoadInfo** aLoadInfo);
   bool CallerSubsumes();
 
   nsString mCachedHash;
+  nsCOMPtr<nsPIDOMWindow> mInnerWindow;
   nsWeakPtr mDocShell;
-  nsWeakPtr mOuter;
 };
 
 #endif // nsLocation_h__
 
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -81,20 +81,23 @@ ThrowInvalidThis(JSContext* aCx, const J
   // This should only be called for DOM methods/getters/setters, which
   // are JSNative-backed functions, so we can assume that
   // JS_ValueToFunction and JS_GetFunctionDisplayId will both return
   // non-null and that JS_GetStringCharsZ returns non-null.
   JS::Rooted<JSFunction*> func(aCx, JS_ValueToFunction(aCx, aArgs.calleev()));
   MOZ_ASSERT(func);
   JS::Rooted<JSString*> funcName(aCx, JS_GetFunctionDisplayId(func));
   MOZ_ASSERT(funcName);
+  nsAutoJSString funcNameStr;
+  if (!funcNameStr.init(aCx, funcName)) {
+    return false;
+  }
   JS_ReportErrorNumberUC(aCx, GetErrorMessage, nullptr,
                          static_cast<const unsigned>(aErrorNumber),
-                         JS_GetStringCharsZ(aCx, funcName),
-                         ifaceName.get());
+                         funcNameStr.get(), ifaceName.get());
   return false;
 }
 
 bool
 ThrowInvalidThis(JSContext* aCx, const JS::CallArgs& aArgs,
                  const ErrNum aErrorNumber,
                  prototypes::ID aProtoId)
 {
@@ -905,16 +908,24 @@ GetInterfaceImpl(JSContext* aCx, nsIInte
   }
 
   if (!WrapObject(aCx, result, iid, aRetval)) {
     aError.Throw(NS_ERROR_FAILURE);
   }
 }
 
 bool
+UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp)
+{
+  JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+  args.rval().set(args.thisv());
+  return true;
+}
+
+bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
 }
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name)
 {
@@ -1048,28 +1059,101 @@ XrayResolveAttribute(JSContext* cx, JS::
           return true;
         }
       }
     }
   }
   return true;
 }
 
+static bool
+XrayResolveMethod(JSContext* cx, JS::Handle<JSObject*> wrapper,
+                  JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
+                  const Prefable<const JSFunctionSpec>* methods,
+                  jsid* methodIds,
+                  const JSFunctionSpec* methodsSpecs,
+                  JS::MutableHandle<JSPropertyDescriptor> desc)
+{
+  const Prefable<const JSFunctionSpec>* method;
+  for (method = methods; method->specs; ++method) {
+    if (method->isEnabled(cx, obj)) {
+      // Set i to be the index into our full list of ids/specs that we're
+      // looking at now.
+      size_t i = method->specs - methodsSpecs;
+      for ( ; methodIds[i] != JSID_VOID; ++i) {
+        if (id == methodIds[i]) {
+          const JSFunctionSpec& methodSpec = methodsSpecs[i];
+          JSFunction *fun;
+          if (methodSpec.selfHostedName) {
+            fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id, methodSpec.nargs);
+            if (!fun) {
+              return false;
+            }
+            MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native");
+            MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo");
+          } else {
+            fun = JS_NewFunctionById(cx, methodSpec.call.op, methodSpec.nargs, 0, wrapper, id);
+            if (!fun) {
+              return false;
+            }
+            SET_JITINFO(fun, methodSpec.call.info);
+          }
+          JSObject *funobj = JS_GetFunctionObject(fun);
+          desc.value().setObject(*funobj);
+          desc.setAttributes(methodSpec.flags);
+          desc.object().set(wrapper);
+          desc.setSetter(nullptr);
+          desc.setGetter(nullptr);
+          return true;
+        }
+      }
+    }
+  }
+  return true;
+}
+
 /* static */ bool
 XrayResolveUnforgeableProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                                JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                                JS::MutableHandle<JSPropertyDescriptor> desc,
                                const NativeProperties* nativeProperties)
 {
-  return !nativeProperties || !nativeProperties->unforgeableAttributes ||
-         XrayResolveAttribute(cx, wrapper, obj, id,
+  if (!nativeProperties) {
+    return true;
+  }
+
+  if (nativeProperties->unforgeableAttributes) {
+    if (!XrayResolveAttribute(cx, wrapper, obj, id,
                               nativeProperties->unforgeableAttributes,
                               nativeProperties->unforgeableAttributeIds,
                               nativeProperties->unforgeableAttributeSpecs,
-                              desc);
+                              desc)) {
+      return false;
+    }
+
+    if (desc.object()) {
+      return true;
+    }
+  }
+
+  if (nativeProperties->unforgeableMethods) {
+    if (!XrayResolveMethod(cx, wrapper, obj, id,
+                           nativeProperties->unforgeableMethods,
+                           nativeProperties->unforgeableMethodIds,
+                           nativeProperties->unforgeableMethodSpecs,
+                           desc)) {
+      return false;
+    }
+
+    if (desc.object()) {
+      return true;
+    }
+  }
+
+  return true;
 }
 
 static bool
 XrayResolveProperty(JSContext* cx, JS::Handle<JSObject*> wrapper,
                     JS::Handle<JSObject*> obj, JS::Handle<jsid> id,
                     JS::MutableHandle<JSPropertyDescriptor> desc, DOMObjectType type,
                     const NativeProperties* nativeProperties)
 {
@@ -1081,50 +1165,22 @@ XrayResolveProperty(JSContext* cx, JS::H
     methodIds = nativeProperties->staticMethodIds;
     methodsSpecs = nativeProperties->staticMethodsSpecs;
   } else {
     methods = nativeProperties->methods;
     methodIds = nativeProperties->methodIds;
     methodsSpecs = nativeProperties->methodsSpecs;
   }
   if (methods) {
-    const Prefable<const JSFunctionSpec>* method;
-    for (method = methods; method->specs; ++method) {
-      if (method->isEnabled(cx, obj)) {
-        // Set i to be the index into our full list of ids/specs that we're
-        // looking at now.
-        size_t i = method->specs - methodsSpecs;
-        for ( ; methodIds[i] != JSID_VOID; ++i) {
-          if (id == methodIds[i]) {
-            const JSFunctionSpec& methodSpec = methodsSpecs[i];
-            JSFunction *fun;
-            if (methodSpec.selfHostedName) {
-              fun = JS::GetSelfHostedFunction(cx, methodSpec.selfHostedName, id, methodSpec.nargs);
-              if (!fun) {
-                return false;
-              }
-              MOZ_ASSERT(!methodSpec.call.op, "Bad FunctionSpec declaration: non-null native");
-              MOZ_ASSERT(!methodSpec.call.info, "Bad FunctionSpec declaration: non-null jitinfo");
-            } else {
-              fun = JS_NewFunctionById(cx, methodSpec.call.op, methodSpec.nargs, 0, wrapper, id);
-              if (!fun) {
-                return false;
-              }
-              SET_JITINFO(fun, methodSpec.call.info);
-            }
-            JSObject *funobj = JS_GetFunctionObject(fun);
-            desc.value().setObject(*funobj);
-            desc.setAttributes(methodSpec.flags);
-            desc.object().set(wrapper);
-            desc.setSetter(nullptr);
-            desc.setGetter(nullptr);
-           return true;
-          }
-        }
-      }
+    if (!XrayResolveMethod(cx, wrapper, obj, id, methods, methodIds,
+                           methodsSpecs, desc)) {
+      return false;
+    }
+    if (desc.object()) {
+      return true;
     }
   }
 
   if (type == eInterface) {
     if (nativeProperties->staticAttributes) {
       if (!XrayResolveAttribute(cx, wrapper, obj, id,
                                 nativeProperties->staticAttributes,
                                 nativeProperties->staticAttributeIds,
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1696,16 +1696,19 @@ template<class T>
 void
 GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID,
              JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
 {
   GetInterfaceImpl(aCx, aThis, aThis, aIID, aRetval, aError);
 }
 
 bool
+UnforgeableValueOf(JSContext* cx, unsigned argc, JS::Value* vp);
+
+bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name);
 
 // vp is allowed to be null; in that case no get will be attempted,
 // and *found will simply indicate whether the property exists.
 bool
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -737,21 +737,17 @@ DOMInterfaces = {
 },
 
 'LocalMediaStream': {
     'headerFile': 'DOMMediaStream.h',
     'nativeType': 'mozilla::DOMLocalMediaStream'
 },
 
 'Location': {
-    # NOTE: Before you turn on codegen for Location, make sure all the
-    # Unforgeable stuff is dealt with.
-    'nativeType': 'nsIDOMLocation',
-    'skipGen': True,
-    'register': False
+    'nativeType': 'nsLocation',
 },
 
 'MediaList': {
     'nativeType': 'nsMediaList',
     'headerFile': 'nsIMediaList.h',
 },
 
 'MediaSource': [{
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -484,20 +484,32 @@ def PrototypeIDAndDepth(descriptor):
             prototypeID += "_workers"
         depth = "PrototypeTraits<%s>::Depth" % prototypeID
     else:
         prototypeID += "_ID_Count"
         depth = "0"
     return (prototypeID, depth)
 
 
+def MemberIsUnforgeable(member, descriptor):
+    # Note: "or" and "and" return either their LHS or RHS, not
+    # necessarily booleans.  Make sure to return a boolean from this
+    # method, because callers will compare its return value to
+    # booleans.
+    return bool((member.isAttr() or member.isMethod()) and
+                not member.isStatic() and
+                (member.isUnforgeable() or
+                 descriptor.interface.getExtendedAttribute("Unforgeable")))
+
+
 def UseHolderForUnforgeable(descriptor):
     return (descriptor.concrete and
             descriptor.proxy and
-            any(m for m in descriptor.interface.members if (m.isAttr() or m.isMethod()) and m.isUnforgeable()))
+            any(m for m in descriptor.interface.members
+                if MemberIsUnforgeable(m, descriptor)))
 
 
 def CallOnUnforgeableHolder(descriptor, code, isXrayCheck=None,
                             useSharedRoot=False):
     """
     Generate the code to execute the code in "code" on an unforgeable holder if
     needed. code should be a string containing the code to execute. If it
     contains a ${holder} string parameter it will be replaced with the
@@ -1994,17 +2006,17 @@ class MethodDefiner(PropertyDefiner):
         # FIXME https://bugzilla.mozilla.org/show_bug.cgi?id=772822
         #       We should be able to check for special operations without an
         #       identifier. For now we check if the name starts with __
 
         # Ignore non-static methods for interfaces without a proto object
         if descriptor.interface.hasInterfacePrototypeObject() or static:
             methods = [m for m in descriptor.interface.members if
                        m.isMethod() and m.isStatic() == static and
-                       m.isUnforgeable() == unforgeable and
+                       MemberIsUnforgeable(m, descriptor) == unforgeable and
                        not m.isIdentifierLess()]
         else:
             methods = []
         self.chrome = []
         self.regular = []
         for m in methods:
             if m.identifier.name == 'queryInterface':
                 if self.descriptor.workers:
@@ -2067,17 +2079,18 @@ class MethodDefiner(PropertyDefiner):
                 "selfHostedName": "ArrayValues",
                 "length": 0,
                 "flags": "JSPROP_ENUMERATE",
                 "condition": MemberCondition(None, None)
             })
 
         if not static:
             stringifier = descriptor.operations['Stringifier']
-            if stringifier:
+            if (stringifier and
+                unforgeable == MemberIsUnforgeable(stringifier, descriptor)):
                 toStringDesc = {
                     "name": "toString",
                     "nativeName": stringifier.identifier.name,
                     "length": 0,
                     "flags": "JSPROP_ENUMERATE",
                     "condition": PropertyDefiner.getControllingCondition(stringifier, descriptor)
                 }
                 if isChromeOnly(stringifier):
@@ -2092,16 +2105,28 @@ class MethodDefiner(PropertyDefiner):
                     "length": 0,
                     "flags": "JSPROP_ENUMERATE",
                     "condition": PropertyDefiner.getControllingCondition(jsonifier, descriptor)
                 }
                 if isChromeOnly(jsonifier):
                     self.chrome.append(toJSONDesc)
                 else:
                     self.regular.append(toJSONDesc)
+            if (unforgeable and
+                descriptor.interface.getExtendedAttribute("Unforgeable")):
+                # Synthesize our valueOf method
+                self.regular.append({
+                    "name": 'valueOf',
+                    "nativeName": "UnforgeableValueOf",
+                    "methodInfo": False,
+                    "length": 0,
+                    "flags": "JSPROP_ENUMERATE", # readonly/permanent added
+                                                 # automatically.
+                    "condition": MemberCondition(None, None)
+                })
         elif (descriptor.interface.isJSImplemented() and
               descriptor.interface.hasInterfaceObject()):
             self.chrome.append({
                 "name": '_create',
                 "nativeName": ("%s::_Create" % descriptor.name),
                 "methodInfo": False,
                 "length": 2,
                 "flags": "0",
@@ -2122,17 +2147,17 @@ class MethodDefiner(PropertyDefiner):
     def generateArray(self, array, name, doIdArrays):
         if len(array) == 0:
             return ""
 
         def condition(m, d):
             return m["condition"]
 
         def flags(m):
-            unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
+            unforgeable = " | JSPROP_PERMANENT | JSPROP_READONLY" if self.unforgeable else ""
             return m["flags"] + unforgeable
 
         def specData(m):
             if "selfHostedName" in m:
                 selfHostedName = '"%s"' % m["selfHostedName"]
                 assert not m.get("methodInfo", True)
                 accessor = "nullptr"
                 jitinfo = "nullptr"
@@ -2170,26 +2195,43 @@ class MethodDefiner(PropertyDefiner):
         return self.generatePrefableArray(
             array, name,
             '  JS_FNSPEC("%s", %s, %s, %s, %s, %s)',
             '  JS_FS_END',
             'JSFunctionSpec',
             condition, specData, doIdArrays)
 
 
+def IsCrossOriginWritable(attr, descriptor):
+    """
+    Return whether the IDLAttribute in question is cross-origin writable on the
+    interface represented by descriptor.  This is needed to handle the fact that
+    some, but not all, interfaces implementing URLUtils want a cross-origin
+    writable .href.
+    """
+    crossOriginWritable = attr.getExtendedAttribute("CrossOriginWritable")
+    if not crossOriginWritable:
+        return False
+    if crossOriginWritable == True:
+        return True
+    assert (isinstance(crossOriginWritable, list) and
+            len(crossOriginWritable) == 1)
+    return crossOriginWritable[0] == descriptor.interface.identifier.name
+
+
 class AttrDefiner(PropertyDefiner):
     def __init__(self, descriptor, name, static, unforgeable=False):
         assert not (static and unforgeable)
         PropertyDefiner.__init__(self, descriptor, name)
         self.name = name
         # Ignore non-static attributes for interfaces without a proto object
         if descriptor.interface.hasInterfacePrototypeObject() or static:
             attributes = [m for m in descriptor.interface.members if
                           m.isAttr() and m.isStatic() == static and
-                          m.isUnforgeable() == unforgeable]
+                          MemberIsUnforgeable(m, descriptor) == unforgeable]
         else:
             attributes = []
         self.chrome = [m for m in attributes if isChromeOnly(m)]
         self.regular = [m for m in attributes if not isChromeOnly(m)]
         self.static = static
         self.unforgeable = unforgeable
 
         if static:
@@ -2233,17 +2275,17 @@ class AttrDefiner(PropertyDefiner):
                 attr.getExtendedAttribute("Replaceable") is None):
                 return "JSOP_NULLWRAPPER"
             if self.static:
                 accessor = 'set_' + attr.identifier.name
                 jitinfo = "nullptr"
             else:
                 if attr.hasLenientThis():
                     accessor = "genericLenientSetter"
-                elif attr.getExtendedAttribute("CrossOriginWritable"):
+                elif IsCrossOriginWritable(attr, self.descriptor):
                     accessor = "genericCrossOriginSetter"
                 elif self.descriptor.needsSpecialGenericOps():
                     accessor = "genericSetter"
                 else:
                     accessor = "GenericBindingSetter"
                 jitinfo = "&%s_setterinfo" % attr.identifier.name
             return "{ { JS_CAST_NATIVE_TO(%s, JSStrictPropertyOp), %s } }" % \
                    (accessor, jitinfo)
@@ -2781,45 +2823,63 @@ def CreateBindingJSObject(descriptor, pr
 
 
 def InitUnforgeablePropertiesOnObject(descriptor, obj, properties, failureReturnValue=""):
     """
     properties is a PropertyArrays instance
     """
     unforgeables = []
 
+    if failureReturnValue:
+        failureReturnValue = " " + failureReturnValue
+    else:
+        failureReturnValue = ""
+
     defineUnforgeableAttrs = fill(
         """
         if (!DefineUnforgeableAttributes(aCx, ${obj}, %s)) {
           return${rv};
         }
         """,
         obj=obj,
-        rv=" " + failureReturnValue if failureReturnValue else "")
+        rv=failureReturnValue)
     defineUnforgeableMethods = fill(
         """
         if (!DefineUnforgeableMethods(aCx, ${obj}, %s)) {
           return${rv};
         }
         """,
         obj=obj,
-        rv=" " + failureReturnValue if failureReturnValue else "")
+        rv=failureReturnValue)
 
     unforgeableMembers = [
         (defineUnforgeableAttrs, properties.unforgeableAttrs),
         (defineUnforgeableMethods, properties.unforgeableMethods)
     ]
     for (template, array) in unforgeableMembers:
         if array.hasNonChromeOnly():
             unforgeables.append(CGGeneric(template % array.variableName(False)))
         if array.hasChromeOnly():
             unforgeables.append(
                 CGIfWrapper(CGGeneric(template % array.variableName(True)),
                             "nsContentUtils::ThreadsafeIsCallerChrome()"))
 
+    if descriptor.interface.getExtendedAttribute("Unforgeable"):
+        # We do our undefined toJSON here, not as a regular property
+        # because we don't have a concept of value props anywhere.
+        unforgeables.append(CGGeneric(fill(
+            """
+            if (!JS_DefineProperty(aCx, ${obj}, "toJSON", JS::UndefinedHandleValue,
+                                   JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
+              return${rv};
+            }
+            """,
+            obj=obj,
+            rv=failureReturnValue)))
+
     return CGList(unforgeables)
 
 
 def InitUnforgeableProperties(descriptor, properties):
     """
     properties is a PropertyArrays instance
     """
     unforgeableAttrs = properties.unforgeableAttrs
@@ -10348,23 +10408,23 @@ class CGDescriptor(CGThing):
                                             (extAttr, m.location))
                     if m.isStatic():
                         assert descriptor.interface.hasInterfaceObject()
                         cgThings.append(CGStaticSetter(descriptor, m))
                     elif descriptor.interface.hasInterfacePrototypeObject():
                         cgThings.append(CGSpecializedSetter(descriptor, m))
                         if m.hasLenientThis():
                             hasLenientSetter = True
-                        elif m.getExtendedAttribute("CrossOriginWritable"):
+                        elif IsCrossOriginWritable(m, descriptor):
                             crossOriginSetters.add(m.identifier.name)
                         elif descriptor.needsSpecialGenericOps():
                             hasSetter = True
                 elif m.getExtendedAttribute("PutForwards"):
                     cgThings.append(CGSpecializedForwardingSetter(descriptor, m))
-                    if m.getExtendedAttribute("CrossOriginWritable"):
+                    if IsCrossOriginWritable(m, descriptor):
                         crossOriginSetters.add(m.identifier.name)
                     elif descriptor.needsSpecialGenericOps():
                         hasSetter = True
                 elif m.getExtendedAttribute("Replaceable"):
                     cgThings.append(CGSpecializedReplaceableSetter(descriptor, m))
                     if descriptor.needsSpecialGenericOps():
                         hasSetter = True
                 if (not m.isStatic() and
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -389,19 +389,17 @@ NS_IMETHODIMP JSStackFrame::GetFilename(
 }
 
 /* readonly attribute AString name; */
 NS_IMETHODIMP JSStackFrame::GetName(nsAString& aFunction)
 {
   if (!mFunnameInitialized) {
     JS::FrameDescription& desc = mStackDescription->FrameAt(mIndex);
     if (JSFlatString *name = desc.funDisplayName()) {
-      mFunname.Assign(JS_GetFlatStringChars(name),
-                      // XXXbz Can't JS_GetStringLength on JSFlatString!
-                      JS_GetStringLength(JS_FORGET_STRING_FLATNESS(name)));
+      AssignJSFlatString(mFunname, name);
     }
     mFunnameInitialized = true;
   }
 
   // The function name must be set to null if empty.
   if (mFunname.IsEmpty()) {
     aFunction.SetIsVoid(true);
   } else {
--- a/dom/bindings/parser/WebIDL.py
+++ b/dom/bindings/parser/WebIDL.py
@@ -664,18 +664,46 @@ class IDLInterface(IDLObjectWithScope):
             self.members.extend(additionalMembers)
             iface.interfacesImplementingSelf.add(self)
 
         for ancestor in self.getInheritedInterfaces():
             ancestor.interfacesBasedOnSelf.add(self)
             for ancestorConsequential in ancestor.getConsequentialInterfaces():
                 ancestorConsequential.interfacesBasedOnSelf.add(self)
 
+        # Deal with interfaces marked [Unforgeable], now that we have our full
+        # member list, except unforgeables pulled in from parents.  We want to
+        # do this before we set "originatingInterface" on our unforgeable
+        # members.
+        if self.getExtendedAttribute("Unforgeable"):
+            # Check that the interface already has all the things the
+            # spec would otherwise require us to synthesize and is
+            # missing the ones we plan to synthesize.
+            if not any(m.isMethod() and m.isStringifier() for m in self.members):
+                raise WebIDLError("Unforgeable interface %s does not have a "
+                                  "stringifier" % self.identifier.name,
+                                  [self.location])
+
+            for m in self.members:
+                if ((m.isMethod() and m.isJsonifier()) or
+                    m.identifier.name == "toJSON"):
+                    raise WebIDLError("Unforgeable interface %s has a "
+                                      "jsonifier so we won't be able to add "
+                                      "one ourselves" % self.identifier.name,
+                                      [self.location, m.location])
+
+                if m.identifier.name == "valueOf" and not m.isStatic():
+                    raise WebIDLError("Unforgeable interface %s has a valueOf "
+                                      "member so we won't be able to add one "
+                                      "ourselves" % self.identifier.name,
+                                      [self.location, m.location])
+
         for member in self.members:
-            if (member.isAttr() and member.isUnforgeable() and
+            if ((member.isAttr() or member.isMethod()) and
+                member.isUnforgeable() and
                 not hasattr(member, "originatingInterface")):
                 member.originatingInterface = self
 
         # Compute slot indices for our members before we pull in
         # unforgeable members from our parent.
         for member in self.members:
             if (member.isAttr() and
                 (member.getExtendedAttribute("StoreInSlot") or
@@ -688,38 +716,38 @@ class IDLInterface(IDLObjectWithScope):
         if self.parent:
             # Make sure we don't shadow any of the [Unforgeable] attributes on
             # our ancestor interfaces.  We don't have to worry about
             # consequential interfaces here, because those have already been
             # imported into the relevant .members lists.  And we don't have to
             # worry about anything other than our parent, because it has already
             # imported its ancestors unforgeable attributes into its member
             # list.
-            for unforgeableAttr in (attr for attr in self.parent.members if
-                                    attr.isAttr() and not attr.isStatic() and
-                                    attr.isUnforgeable()):
+            for unforgeableMember in (member for member in self.parent.members if
+                                      (member.isAttr() or member.isMethod()) and
+                                      member.isUnforgeable()):
                 shadows = [ m for m in self.members if
                             (m.isAttr() or m.isMethod()) and
                             not m.isStatic() and
-                            m.identifier.name == unforgeableAttr.identifier.name ]
+                            m.identifier.name == unforgeableMember.identifier.name ]
                 if len(shadows) != 0:
-                    locs = [unforgeableAttr.location] + [ s.location for s
-                                                          in shadows ]
+                    locs = [unforgeableMember.location] + [ s.location for s
+                                                            in shadows ]
                     raise WebIDLError("Interface %s shadows [Unforgeable] "
                                       "members of %s" %
                                       (self.identifier.name,
                                        ancestor.identifier.name),
                                       locs)
                 # And now just stick it in our members, since we won't be
                 # inheriting this down the proto chain.  If we really cared we
                 # could try to do something where we set up the unforgeable
-                # attributes of ancestor interfaces, with their corresponding
-                # getters, on our interface, but that gets pretty complicated
-                # and seems unnecessary.
-                self.members.append(unforgeableAttr)
+                # attributes/methods of ancestor interfaces, with their
+                # corresponding getters, on our interface, but that gets pretty
+                # complicated and seems unnecessary.
+                self.members.append(unforgeableMember)
 
         # Ensure that there's at most one of each {named,indexed}
         # {getter,setter,creator,deleter}, at most one stringifier,
         # and at most one legacycaller.  Note that this last is not
         # quite per spec, but in practice no one overloads
         # legacycallers.
         specialMembersSeen = {}
         for member in self.members:
@@ -780,16 +808,36 @@ class IDLInterface(IDLObjectWithScope):
                 if parent.getExtendedAttribute("OverrideBuiltins"):
                     raise WebIDLError("Interface with [Global] inherits from "
                                       "interface with [OverrideBuiltins]",
                                       [self.location, parent.location])
                 parent._isOnGlobalProtoChain = True
                 parent = parent.parent
 
     def validate(self):
+        # We don't support consequential unforgeable interfaces.  Need to check
+        # this here, becaue in finish() an interface might not know yet that
+        # it's consequential.
+        if self.getExtendedAttribute("Unforgeable") and self.isConsequential():
+            raise WebIDLError(
+                "%s is an unforgeable consequential interface" %
+                self.identifier.name,
+                [self.location] +
+                list(i.location for i in
+                     (self.interfacesBasedOnSelf - { self }) ))
+
+        # We also don't support inheriting from unforgeable interfaces.
+        if self.getExtendedAttribute("Unforgeable") and self.hasChildInterfaces():
+            raise WebIDLError("%s is an unforgeable ancestor interface" %
+                self.identifier.name,
+                [self.location] +
+                list(i.location for i in
+                     self.interfacesBasedOnSelf if i.parent == self))
+
+
         for member in self.members:
             member.validate()
 
             # Check that PutForwards refers to another attribute and that no
             # cycles exist in forwarded assignments.
             if member.isAttr():
                 iface = self
                 attr = member
@@ -987,16 +1035,17 @@ class IDLInterface(IDLObjectWithScope):
             elif identifier == "Global":
                 if not attr.noArguments():
                     raise WebIDLError("[Global] must take no arguments",
                                       [attr.location])
                 self._isOnGlobalProtoChain = True
             elif (identifier == "NeedNewResolve" or
                   identifier == "OverrideBuiltins" or
                   identifier == "ChromeOnly" or
+                  identifier == "Unforgeable" or
                   identifier == "LegacyEventInit"):
                 # Known extended attributes that do not take values
                 if not attr.noArguments():
                     raise WebIDLError("[%s] must take no arguments" % identifier,
                                       [attr.location])
             elif (identifier == "Pref" or
                   identifier == "JSImplementation" or
                   identifier == "HeaderFile" or
@@ -2884,19 +2933,16 @@ class IDLAttribute(IDLInterfaceMember):
                                   "with [CrossOriginReadable]",
                                   [attr.location, self.location])
             if self.getExtendedAttribute("CrossOriginWritable"):
                 raise WebIDLError("[LenientThis] is not allowed in combination "
                                   "with [CrossOriginWritable]",
                                   [attr.location, self.location])
             self.lenientThis = True
         elif identifier == "Unforgeable":
-            if not self.readonly:
-                raise WebIDLError("[Unforgeable] is only allowed on readonly "
-                                  "attributes", [attr.location, self.location])
             if self.isStatic():
                 raise WebIDLError("[Unforgeable] is only allowed on non-static "
                                   "attributes", [attr.location, self.location])
             self._unforgeable = True
         elif identifier == "SameObject" and not self.readonly:
             raise WebIDLError("[SameObject] only allowed on readonly attributes",
                               [attr.location, self.location])
         elif identifier == "Constant" and not self.readonly:
@@ -2946,17 +2992,17 @@ class IDLAttribute(IDLInterfaceMember):
                                   [attr.location, self.location])
         elif identifier == "Cached":
             if self.getExtendedAttribute("StoreInSlot"):
                 raise WebIDLError("[Cached] and [StoreInSlot] must not be "
                                   "specified on the same attribute",
                                   [attr.location, self.location])
         elif (identifier == "CrossOriginReadable" or
               identifier == "CrossOriginWritable"):
-            if not attr.noArguments():
+            if not attr.noArguments() and identifier == "CrossOriginReadable":
                 raise WebIDLError("[%s] must take no arguments" % identifier,
                                   [attr.location])
             if self.isStatic():
                 raise WebIDLError("[%s] is only allowed on non-static "
                                   "attributes" % identifier,
                                   [attr.location, self.location])
             if self.getExtendedAttribute("LenientThis"):
                 raise WebIDLError("[LenientThis] is not allowed in combination "
@@ -3540,25 +3586,29 @@ class IDLMethod(IDLInterfaceMember, IDLS
             sig = self.signatures()[0]
             if not sig[0].isVoid():
                 raise WebIDLError("[LenientFloat] used on a non-void method",
                                   [attr.location, self.location])
             if not any(arg.type.includesRestrictedFloat() for arg in sig[1]):
                 raise WebIDLError("[LenientFloat] used on an operation with no "
                                   "restricted float type arguments",
                                   [attr.location, self.location])
+        elif (identifier == "Pure" or
+              identifier == "CrossOriginCallable" or
+              identifier == "WebGLHandlesContextLoss"):
+            # Known no-argument attributes.
+            if not attr.noArguments():
+                raise WebIDLError("[%s] must take no arguments" % identifier,
+                                  [attr.location])
         elif (identifier == "Throws" or
               identifier == "NewObject" or
               identifier == "ChromeOnly" or
               identifier == "Pref" or
               identifier == "Func" or
               identifier == "AvailableIn" or
-              identifier == "Pure" or
-              identifier == "CrossOriginCallable" or
-              identifier == "WebGLHandlesContextLoss" or
               identifier == "CheckPermissions"):
             # Known attributes that we don't need to do anything with here
             pass
         else:
             raise WebIDLError("Unknown extended attribute %s on method" % identifier,
                               [attr.location])
         IDLInterfaceMember.handleExtendedAttribute(self, attr)
 
--- a/dom/bindings/parser/tests/test_unforgeable.py
+++ b/dom/bindings/parser/tests/test_unforgeable.py
@@ -79,31 +79,69 @@ def WebIDLTest(parser, harness):
                "Should have thrown when shadowing unforgeable attribute on "
                "parent with operation.")
 
     parser = parser.reset();
     threw = False
     try:
         parser.parse("""
             interface Child : Parent {
+              void foo();
+            };
+            interface Parent {
+              [Unforgeable] void foo();
+            };
+        """)
+
+        results = parser.finish()
+    except:
+        threw = True
+    harness.ok(threw,
+               "Should have thrown when shadowing unforgeable operation on "
+               "parent with operation.")
+
+    parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface Child : Parent {
               attribute short foo;
             };
             interface Parent {
               [Unforgeable] readonly attribute long foo;
             };
         """)
 
         results = parser.finish()
     except Exception,x:
         threw = True
     harness.ok(threw,
                "Should have thrown when shadowing unforgeable attribute on "
                "parent with attribute.")
 
     parser = parser.reset();
+    threw = False
+    try:
+        parser.parse("""
+            interface Child : Parent {
+              attribute short foo;
+            };
+            interface Parent {
+              [Unforgeable] void foo();
+            };
+        """)
+
+        results = parser.finish()
+    except Exception,x:
+        threw = True
+    harness.ok(threw,
+               "Should have thrown when shadowing unforgeable operation on "
+               "parent with attribute.")
+
+    parser = parser.reset();
     parser.parse("""
             interface Child : Parent {
             };
             interface Parent {};
             interface Consequential {
               [Unforgeable] readonly attribute long foo;
             };
             Parent implements Consequential;
@@ -161,26 +199,48 @@ def WebIDLTest(parser, harness):
     harness.ok(threw,
                "Should have thrown when our consequential interface shadows unforgeable attribute "
                "of ancestor's consequential interface.")
 
     parser = parser.reset();
     threw = False
     try:
         parser.parse("""
-            interface iface {
-              [Unforgeable] attribute long foo;
+            interface Child : Parent {
             };
+            interface Parent : GrandParent {};
+            interface GrandParent {};
+            interface Consequential {
+              [Unforgeable] void foo();
+            };
+            GrandParent implements Consequential;
+            interface ChildConsequential {
+              void foo();
+            };
+            Child implements ChildConsequential;
         """)
 
         results = parser.finish()
     except:
         threw = True
 
-    harness.ok(threw, "Should have thrown for writable [Unforgeable] attribute.")
+    harness.ok(threw,
+               "Should have thrown when our consequential interface shadows unforgeable operation "
+               "of ancestor's consequential interface.")
+
+    parser = parser.reset();
+    parser.parse("""
+        interface iface {
+          [Unforgeable] attribute long foo;
+        };
+    """)
+
+    results = parser.finish()
+    harness.check(len(results), 1,
+                  "Should allow writable [Unforgeable] attribute.")
 
     parser = parser.reset();
     threw = False
     try:
         parser.parse("""
             interface iface {
               [Unforgeable] static readonly attribute long foo;
             };
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -181,20 +181,31 @@ BrowserElementChild.prototype = {
                      /* wantsUntrusted = */ false);
 
     addEventListener('DOMLinkAdded',
                      this._linkAddedHandler.bind(this),
                      /* useCapture = */ true,
                      /* wantsUntrusted = */ false);
 
     addEventListener('DOMMetaAdded',
-                     this._metaAddedHandler.bind(this),
+                     this._metaChangedHandler.bind(this),
                      /* useCapture = */ true,
                      /* wantsUntrusted = */ false);
 
+    addEventListener('DOMMetaChanged',
+                     this._metaChangedHandler.bind(this),
+                     /* useCapture = */ true,
+                     /* wantsUntrusted = */ false);
+
+    addEventListener('DOMMetaRemoved',
+                     this._metaChangedHandler.bind(this),
+                     /* useCapture = */ true,
+                     /* wantsUntrusted = */ false);
+
+
     // This listens to unload events from our message manager, but /not/ from
     // the |content| window.  That's because the window's unload event doesn't
     // bubble, and we're not using a capturing listener.  If we'd used
     // useCapture == true, we /would/ hear unload events from the window, which
     // is not what we want!
     addEventListener('unload',
                      this._unloadHandler.bind(this),
                      /* useCapture = */ false,
@@ -508,62 +519,88 @@ BrowserElementChild.prototype = {
     e.target.rel.split(' ').forEach(function(x) {
       let token = x.toLowerCase();
       if (handlers[token]) {
         handlers[token](e);
       }
     }, this);
   },
 
-  _metaAddedHandler: function(e) {
+  _metaChangedHandler: function(e) {
     let win = e.target.ownerDocument.defaultView;
     // Ignore metas which don't come from the top-level
     // <iframe mozbrowser> window.
     if (win != content) {
       debug('Not top level!');
       return;
     }
 
     if (!e.target.name) {
       return;
     }
 
-    debug('Got metaAdded: (' + e.target.name + ') ' + e.target.content);
-    if (e.target.name == 'application-name') {
-      let meta = { name: e.target.name,
-                   content: e.target.content };
+    debug('Got metaChanged: (' + e.target.name + ') ' + e.target.content);
 
-      let lang;
-      let elm;
+    let handlers = {
+      'theme-color': this._themeColorChangedHandler,
+      'application-name': this._applicationNameChangedHandler
+    };
+
+    let handler = handlers[e.target.name];
+    if (handler) {
+      handler(e.type, e.target);
+    }
+  },
 
-      for (elm = e.target;
-           !lang && elm && elm.nodeType == e.target.ELEMENT_NODE;
-           elm = elm.parentNode) {
-        if (elm.hasAttribute('lang')) {
-          lang = elm.getAttribute('lang');
-          continue;
-        }
+  _applicationNameChangedHandler: function(eventType, target) {
+    if (eventType !== 'DOMMetaAdded') {
+      // Bug 1037448 - Decide what to do when <meta name="application-name">
+      // changes
+      return;
+    }
+
+    let meta = { name: 'application-name',
+                 content: target.content };
 
-        if (elm.hasAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang')) {
-          lang = elm.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang');
-          continue;
-        }
+    let lang;
+    let elm;
+
+    for (elm = target;
+         !lang && elm && elm.nodeType == target.ELEMENT_NODE;
+         elm = elm.parentNode) {
+      if (elm.hasAttribute('lang')) {
+        lang = elm.getAttribute('lang');
+        continue;
       }
 
-      // No lang has been detected.
-      if (!lang && elm.nodeType == e.target.DOCUMENT_NODE) {
-        lang = elm.contentLanguage;
+      if (elm.hasAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang')) {
+        lang = elm.getAttributeNS('http://www.w3.org/XML/1998/namespace', 'lang');
+        continue;
       }
+    }
+
+    // No lang has been detected.
+    if (!lang && elm.nodeType == target.DOCUMENT_NODE) {
+      lang = elm.contentLanguage;
+    }
 
-      if (lang) {
-        meta.lang = lang;
-      }
+    if (lang) {
+      meta.lang = lang;
+    }
+
+    sendAsyncMsg('metachange', meta);
+  },
 
-      sendAsyncMsg('metachange', meta);
-    }
+  _themeColorChangedHandler: function(eventType, target) {
+    let meta = {
+      name: 'theme-color',
+      content: target.content,
+      type: eventType.replace('DOMMeta', '').toLowerCase()
+    };
+    sendAsyncMsg('metachange', meta);
   },
 
   _addMozAfterPaintHandler: function(callback) {
     function onMozAfterPaint() {
       let uri = docShell.QueryInterface(Ci.nsIWebNavigation).currentURI;
       if (uri.spec != "about:blank") {
         debug("Got afterpaint event: " + uri.spec);
         removeEventListener('MozAfterPaint', onMozAfterPaint,
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/browserElement_ThemeColor.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the public domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that the onmozbrowsermetachange event for theme-color works.
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+browserElementTestHelpers.setEnabledPref(true);
+browserElementTestHelpers.addPermission();
+
+function runTest() {
+  function loadFrameScript(script) {
+    SpecialPowers.getBrowserFrameMessageManager(iframe1)
+                 .loadFrameScript("data:," + script,
+                                  /* allowDelayedLoad = */ false);
+  }
+
+  let iframe1 = document.createElement('iframe');
+  SpecialPowers.wrap(iframe1).mozbrowser = true;
+  iframe1.src = "http://test/tests/dom/browser-element/mochitest/file_browserElement_ThemeColor.html";
+  iframe1.addEventListener('mozbrowsermetachange', tests);
+  document.body.appendChild(iframe1);
+
+  let numMetaChanges = 0;
+  function tests(e) {
+    let detail = e.detail;
+
+    switch (numMetaChanges++) {
+      case 0: {
+        is(detail.name, 'theme-color', 'name matches');
+        is(detail.content, 'pink', 'content matches');
+        is(detail.type, 'added', 'type matches');
+
+        let script =
+          "var meta = content.document.head.querySelector('meta');" +
+          "meta.content = 'green';";
+        loadFrameScript(script);
+        break;
+      }
+
+      case 1: {
+        is(detail.name, 'theme-color', 'name matches');
+        is(detail.content, 'green', 'content matches');
+        is(detail.type, 'changed', 'type matches');
+
+        let script =
+          "var meta = content.document.head.querySelector('meta');" +
+          "meta.parentNode.removeChild(meta);";
+        loadFrameScript(script);
+        break;
+      }
+
+      case 2: {
+        is(detail.name, 'theme-color', 'name matches');
+        is(detail.content, 'green', 'content matches');
+        is(detail.type, 'removed', 'type matches');
+
+        SimpleTest.finish();
+        break;
+      }
+
+      default: {
+        ok(false, 'Too many metachange events.');
+        break;
+      }
+    }
+  };
+}
+
+window.addEventListener('testready', runTest);
+
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/file_browserElement_ThemeColor.html
@@ -0,0 +1,8 @@
+<html>
+  <head>
+    <meta name="theme-color" content="pink">
+  </head>
+
+  <body>
+  </body>
+</html>
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -3,16 +3,17 @@
 # so we don't run that test on platforms which don't support OOP tests.
 # OOP tests don't work on native-fennec (bug 774939).
 # Bug 960345 - Disabled on OSX debug for frequent crashes.
 skip-if = os == "android" || (toolkit == "cocoa" && debug) || (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || e10s
 support-files =
   browserElement_OpenMixedProcess.js
   file_browserElement_OpenMixedProcess.html
 
+[test_browserElement_oop_ThemeColor.html]
 [test_browserElement_inproc_ErrorSecurity.html]
 skip-if = toolkit=='gonk'
 [test_browserElement_inproc_OpenMixedProcess.html]
 skip-if = toolkit=='gonk' || (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_Alert.html]
 [test_browserElement_oop_AlertInFrame.html]
 [test_browserElement_oop_AppFramePermission.html]
 skip-if = (toolkit == 'gonk' && !debug)
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -6,16 +6,17 @@ support-files =
   browserElementTestHelpers.js
   browserElement_Alert.js
   browserElement_AlertInFrame.js
   browserElement_AppFramePermission.js
   browserElement_AppWindowNamespace.js
   browserElement_Auth.js
   browserElement_BackForward.js
   browserElement_BadScreenshot.js
+  browserElement_ThemeColor.js
   browserElement_BrowserWindowNamespace.js
   browserElement_BrowserWindowResize.js
   browserElement_Close.js
   browserElement_CloseApp.js
   browserElement_CloseFromOpener.js
   browserElement_ContextmenuEvents.js
   browserElement_CookiesNotThirdParty.js
   browserElement_DOMRequestError.js
@@ -63,16 +64,17 @@ support-files =
   browserElement_XFrameOptions.js
   browserElement_XFrameOptionsAllowFrom.js
   browserElement_XFrameOptionsDeny.js
   browserElement_XFrameOptionsSameOrigin.js
   file_browserElement_AlertInFrame.html
   file_browserElement_AlertInFrame_Inner.html
   file_browserElement_AppFramePermission.html
   file_browserElement_AppWindowNamespace.html
+  file_browserElement_ThemeColor.html
   file_browserElement_BrowserWindowNamespace.html
   file_browserElement_CloseApp.html
   file_browserElement_CloseFromOpener.html
   file_browserElement_CookiesNotThirdParty.html
   file_browserElement_ForwardName.html
   file_browserElement_FrameWrongURI.html
   file_browserElement_LoadEvents.html
   file_browserElement_Metachange.sjs
@@ -108,16 +110,17 @@ support-files =
 
 # Note: browserElementTestHelpers.js looks at the test's filename to determine
 # whether the test should be OOP.  "_oop_" signals OOP, "_inproc_" signals in
 # process.  Default is OOP.
 [test_browserElement_NoAttr.html]
 [test_browserElement_NoPref.html]
 [test_browserElement_NoPermission.html]
 [test_browserElement_inproc_Alert.html]
+[test_browserElement_inproc_ThemeColor.html]
 skip-if = buildapp == 'b2g'
 [test_browserElement_inproc_AlertInFrame.html]
 [test_browserElement_inproc_AppFramePermission.html]
 skip-if = toolkit == 'android' || buildapp == 'b2g'
 [test_browserElement_inproc_AppWindowNamespace.html]
 skip-if = toolkit == 'android' || buildapp == 'b2g' # android(TIMED_OUT, bug 783509) androidx86(TIMED_OUT, bug 783509)
 [test_browserElement_inproc_Auth.html]
 skip-if = buildapp == 'b2g'
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_inproc_ThemeColor.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1013913
+-->
+<head>
+  <title>Test for Bug 1013913</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1013913">Mozilla Bug 1013913</a>
+
+<script type="application/javascript;version=1.7" src="browserElement_ThemeColor.js">
+</script>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/mochitest/test_browserElement_oop_ThemeColor.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1013913
+-->
+<head>
+  <title>Test for Bug 1013913</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1013913">Mozilla Bug 1013913</a>
+
+<script type="application/javascript;version=1.7" src="browserElement_ThemeColor.js">
+</script>
+
+</body>
+</html>
--- a/dom/camera/CameraPreviewMediaStream.cpp
+++ b/dom/camera/CameraPreviewMediaStream.cpp
@@ -98,17 +98,17 @@ CameraPreviewMediaStream::AddListener(Me
 void
 CameraPreviewMediaStream::RemoveListener(MediaStreamListener* aListener)
 {
   MutexAutoLock lock(mMutex);
 
   MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
   nsRefPtr<MediaStreamListener> listener(aListener);
   mListeners.RemoveElement(aListener);
-  listener->NotifyRemoved(gm);
+  listener->NotifyEvent(gm, MediaStreamListener::EVENT_REMOVED);
 }
 
 void
 CameraPreviewMediaStream::Destroy()
 {
   MutexAutoLock lock(mMutex);
   DestroyImpl();
 }
--- a/dom/crypto/AesKeyAlgorithm.h
+++ b/dom/crypto/AesKeyAlgorithm.h
@@ -2,44 +2,35 @@
 /* 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_AesKeyAlgorithm_h
 #define mozilla_dom_AesKeyAlgorithm_h
 
-#include "mozilla/dom/KeyAlgorithm.h"
+#include "mozilla/dom/BasicSymmetricKeyAlgorithm.h"
 #include "js/TypeDecls.h"
 
 namespace mozilla {
 namespace dom {
 
-class AesKeyAlgorithm MOZ_FINAL : public KeyAlgorithm
+class AesKeyAlgorithm MOZ_FINAL : public BasicSymmetricKeyAlgorithm
 {
 public:
   AesKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength)
-    : KeyAlgorithm(aGlobal, aName)
-    , mLength(aLength)
+    : BasicSymmetricKeyAlgorithm(aGlobal, aName, aLength)
   {}
 
   ~AesKeyAlgorithm()
   {}
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
-  uint16_t Length() const
-  {
-    return mLength;
-  }
-
   virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
   static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
                               JSStructuredCloneReader* aReader);
-
-protected:
-  uint16_t mLength;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_AesKeyAlgorithm_h
new file mode 100644
--- /dev/null
+++ b/dom/crypto/BasicSymmetricKeyAlgorithm.h
@@ -0,0 +1,38 @@
+/* -*- 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_BasicSymmetricKeyAlgorithm_h
+#define mozilla_dom_BasicSymmetricKeyAlgorithm_h
+
+#include "mozilla/dom/KeyAlgorithm.h"
+
+namespace mozilla {
+namespace dom {
+
+class BasicSymmetricKeyAlgorithm : public KeyAlgorithm
+{
+public:
+  BasicSymmetricKeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName, uint16_t aLength)
+    : KeyAlgorithm(aGlobal, aName)
+    , mLength(aLength)
+  {}
+
+  ~BasicSymmetricKeyAlgorithm()
+  {}
+
+  uint16_t Length() const
+  {
+    return mLength;
+  }
+
+protected:
+  uint16_t mLength;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_BasicSymmetricKeyAlgorithm_h
--- a/dom/crypto/KeyAlgorithm.cpp
+++ b/dom/crypto/KeyAlgorithm.cpp
@@ -27,37 +27,18 @@ NS_INTERFACE_MAP_END
 
 KeyAlgorithm::KeyAlgorithm(nsIGlobalObject* aGlobal, const nsString& aName)
   : mGlobal(aGlobal)
   , mName(aName)
 {
   SetIsDOMBinding();
 
   // Set mechanism based on algorithm name
-  if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
-    mMechanism = CKM_AES_CBC_PAD;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
-    mMechanism = CKM_AES_CTR;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
-    mMechanism = CKM_AES_GCM;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
-    mMechanism = CKM_SHA_1;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
-    mMechanism = CKM_SHA256;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
-    mMechanism = CKM_SHA384;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
-    mMechanism = CKM_SHA512;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
-    mMechanism = CKM_RSA_PKCS;
-  } else if (mName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
-    mMechanism = CKM_RSA_PKCS;
-  } else {
-    mMechanism = UNKNOWN_CK_MECHANISM;
-  }
+  mMechanism = MapAlgorithmNameToMechanism(aName);
+
   // HMAC not handled here, since it requires extra info
 }
 
 KeyAlgorithm::~KeyAlgorithm()
 {
 }
 
 JSObject*
--- a/dom/crypto/WebCryptoCommon.h
+++ b/dom/crypto/WebCryptoCommon.h
@@ -16,16 +16,17 @@
 #define WEBCRYPTO_ALG_AES_CBC       "AES-CBC"
 #define WEBCRYPTO_ALG_AES_CTR       "AES-CTR"
 #define WEBCRYPTO_ALG_AES_GCM       "AES-GCM"
 #define WEBCRYPTO_ALG_SHA1          "SHA-1"
 #define WEBCRYPTO_ALG_SHA256        "SHA-256"
 #define WEBCRYPTO_ALG_SHA384        "SHA-384"
 #define WEBCRYPTO_ALG_SHA512        "SHA-512"
 #define WEBCRYPTO_ALG_HMAC          "HMAC"
+#define WEBCRYPTO_ALG_PBKDF2        "PBKDF2"
 #define WEBCRYPTO_ALG_RSAES_PKCS1   "RSAES-PKCS1-v1_5"
 #define WEBCRYPTO_ALG_RSASSA_PKCS1  "RSASSA-PKCS1-v1_5"
 
 // WebCrypto key formats
 #define WEBCRYPTO_KEY_FORMAT_RAW    "raw"
 #define WEBCRYPTO_KEY_FORMAT_PKCS8  "pkcs8"
 #define WEBCRYPTO_KEY_FORMAT_SPKI   "spki"
 #define WEBCRYPTO_KEY_FORMAT_JWK    "jwk"
@@ -114,12 +115,43 @@ WriteBuffer(JSStructuredCloneWriter* aWr
 {
   bool ret = JS_WriteUint32Pair(aWriter, aBuffer.Length(), 0);
   if (ret && aBuffer.Length() > 0) {
     ret = JS_WriteBytes(aWriter, aBuffer.Elements(), aBuffer.Length());
   }
   return ret;
 }
 
+inline CK_MECHANISM_TYPE
+MapAlgorithmNameToMechanism(const nsString& aName)
+{
+  CK_MECHANISM_TYPE mechanism(UNKNOWN_CK_MECHANISM);
+
+  // Set mechanism based on algorithm name
+  if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC)) {
+    mechanism = CKM_AES_CBC_PAD;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR)) {
+    mechanism = CKM_AES_CTR;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
+    mechanism = CKM_AES_GCM;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1)) {
+    mechanism = CKM_SHA_1;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
+    mechanism = CKM_SHA256;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384)) {
+    mechanism = CKM_SHA384;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
+    mechanism = CKM_SHA512;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
+    mechanism = CKM_PKCS5_PBKD2;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1)) {
+    mechanism = CKM_RSA_PKCS;
+  } else if (aName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
+    mechanism = CKM_RSA_PKCS;
+  }
+
+  return mechanism;
+}
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_WebCryptoCommon_h
--- a/dom/crypto/WebCryptoTask.cpp
+++ b/dom/crypto/WebCryptoTask.cpp
@@ -143,16 +143,88 @@ Coerce(JSContext* aCx, T& aTarget, const
   JS::RootedValue value(aCx, JS::ObjectValue(*aAlgorithm.GetAsObject()));
   if (!aTarget.Init(aCx, value)) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   return NS_OK;
 }
 
+inline size_t
+MapHashAlgorithmNameToBlockSize(const nsString& aName)
+{
+  if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA1) ||
+      aName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) {
+    return 512;
+  }
+
+  if (aName.EqualsLiteral(WEBCRYPTO_ALG_SHA384) ||
+      aName.EqualsLiteral(WEBCRYPTO_ALG_SHA512)) {
+    return 1024;
+  }
+
+  return 0;
+}
+
+inline nsresult
+GetKeySizeForAlgorithm(JSContext* aCx, const ObjectOrString& aAlgorithm,
+                       size_t& aLength)
+{
+  aLength = 0;
+
+  // Extract algorithm name
+  nsString algName;
+  if (NS_FAILED(GetAlgorithmName(aCx, aAlgorithm, algName))) {
+    return NS_ERROR_DOM_SYNTAX_ERR;
+  }
+
+  // Read AES key length from given algorithm object.
+  if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
+      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
+      algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
+    RootedDictionary<AesKeyGenParams> params(aCx);
+    if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) ||
+        !params.mLength.WasPassed()) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    size_t length = params.mLength.Value();
+    if (length != 128 && length != 192 && length != 256) {
+      return NS_ERROR_DOM_DATA_ERR;
+    }
+
+    aLength = length;
+    return NS_OK;
+  }
+
+  // Determine HMAC key length as the block size of the given hash.
+  if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
+    RootedDictionary<HmacImportParams> params(aCx);
+    if (NS_FAILED(Coerce(aCx, params, aAlgorithm)) ||
+        !params.mHash.WasPassed()) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    nsString hashName;
+    if (NS_FAILED(GetAlgorithmName(aCx, params.mHash.Value(), hashName))) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    size_t length = MapHashAlgorithmNameToBlockSize(hashName);
+    if (length == 0) {
+      return NS_ERROR_DOM_SYNTAX_ERR;
+    }
+
+    aLength = length;
+    return NS_OK;
+  }
+
+  return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+}
+
 // Implementation of WebCryptoTask methods
 
 void
 WebCryptoTask::FailWithError(nsresult aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_RESOLVED, false);
 
@@ -823,20 +895,22 @@ public:
     }
 
     // Import the key data
     if (aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_RAW)) {
       if (aKeyData.IsArrayBufferView()) {
         mKeyData.Assign(aKeyData.GetAsArrayBufferView());
       } else if (aKeyData.IsArrayBuffer()) {
         mKeyData.Assign(aKeyData.GetAsArrayBuffer());
-      } else {
-        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
-        return;
       }
+      // We would normally fail here if the key data is not an ArrayBuffer or
+      // an ArrayBufferView but let's wait for BeforeCrypto() to be called in
+      // case PBKDF2's deriveKey() operation passed dummy key data. When that
+      // happens DerivePbkdfKeyTask is responsible for calling SetKeyData()
+      // itself before this task is actually run.
     } else if (aFormat.EqualsLiteral(WEBCRYPTO_KEY_FORMAT_JWK)) {
       mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
       return;
     } else {
       // Invalid key format
       mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
       return;
     }
@@ -854,32 +928,42 @@ public:
         mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
         return;
       }
     }
   }
 
   virtual nsresult BeforeCrypto() MOZ_OVERRIDE
   {
+    // Check that we have valid key data.
+    if (mKeyData.Length() == 0) {
+      return NS_ERROR_DOM_DATA_ERR;
+    }
+
     // Construct an appropriate KeyAlorithm,
     // and verify that usages are appropriate
     nsRefPtr<KeyAlgorithm> algorithm;
     nsIGlobalObject* global = mKey->GetParentObject();
     uint32_t length = 8 * mKeyData.Length(); // bytes to bits
     if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
         mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
         mAlgName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
       if (mKey->HasUsageOtherThan(CryptoKey::ENCRYPT | CryptoKey::DECRYPT)) {
         return NS_ERROR_DOM_DATA_ERR;
       }
 
       if ( (length != 128) && (length != 192) && (length != 256) ) {
         return NS_ERROR_DOM_DATA_ERR;
       }
       algorithm = new AesKeyAlgorithm(global, mAlgName, length);
+    } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2)) {
+      if (mKey->HasUsageOtherThan(CryptoKey::DERIVEKEY)) {
+        return NS_ERROR_DOM_DATA_ERR;
+      }
+      algorithm = new BasicSymmetricKeyAlgorithm(global, mAlgName, length);
     } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
       if (mKey->HasUsageOtherThan(CryptoKey::SIGN | CryptoKey::VERIFY)) {
         return NS_ERROR_DOM_DATA_ERR;
       }
 
       algorithm = new HmacKeyAlgorithm(global, mAlgName, length, mHashName);
       if (algorithm->Mechanism() == UNKNOWN_CK_MECHANISM) {
         return NS_ERROR_DOM_SYNTAX_ERR;
@@ -890,16 +974,22 @@ public:
 
     mKey->SetAlgorithm(algorithm);
     mKey->SetSymKey(mKeyData);
     mKey->SetType(CryptoKey::SECRET);
     mEarlyComplete = true;
     return NS_OK;
   }
 
+  void SetKeyData(const CryptoBuffer& aKeyData)
+  {
+    // An OOM will just result in an error in BeforeCrypto
+    mKeyData = aKeyData;
+  }
+
 private:
   CryptoBuffer mKeyData;
   nsString mHashName;
 };
 
 class ImportRsaKeyTask : public ImportKeyTask
 {
 public:
@@ -1124,26 +1214,18 @@ public:
     }
 
     // Construct an appropriate KeyAlorithm
     nsRefPtr<KeyAlgorithm> algorithm;
     uint32_t allowedUsages = 0;
     if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
         algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
         algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM)) {
-      RootedDictionary<AesKeyGenParams> params(aCx);
-      mEarlyRv = Coerce(aCx, params, aAlgorithm);
-      if (NS_FAILED(mEarlyRv) || !params.mLength.WasPassed()) {
-        mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
-        return;
-      }
-
-      mLength = params.mLength.Value();
-      if (mLength != 128 && mLength != 192 && mLength != 256) {
-        mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+      mEarlyRv = GetKeySizeForAlgorithm(aCx, aAlgorithm, mLength);
+      if (NS_FAILED(mEarlyRv)) {
         return;
       }
       algorithm = new AesKeyAlgorithm(global, algName, mLength);
       allowedUsages = CryptoKey::ENCRYPT | CryptoKey::DECRYPT;
     } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
       RootedDictionary<HmacKeyGenParams> params(aCx);
       mEarlyRv = Coerce(aCx, params, aAlgorithm);
       if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed()) {
@@ -1162,30 +1244,17 @@ public:
           return;
         }
         hashName.Assign(hashAlg.mName.Value());
       }
 
       if (params.mLength.WasPassed()) {
         mLength = params.mLength.Value();
       } else {
-        nsRefPtr<KeyAlgorithm> hashAlg = new KeyAlgorithm(global, hashName);
-        switch (hashAlg->Mechanism()) {
-          case CKM_SHA_1:
-          case CKM_SHA256:
-            mLength = 512;
-            break;
-          case CKM_SHA384:
-          case CKM_SHA512:
-            mLength = 1024;
-            break;
-          default:
-            mLength = 0;
-            break;
-        }
+        mLength = MapHashAlgorithmNameToBlockSize(hashName);
       }
 
       if (mLength == 0) {
         mEarlyRv = NS_ERROR_DOM_DATA_ERR;
         return;
       }
 
       algorithm = new HmacKeyAlgorithm(global, algName, mLength, hashName);
@@ -1416,16 +1485,170 @@ private:
   }
 
   virtual void Cleanup() MOZ_OVERRIDE
   {
     mKeyPair = nullptr;
   }
 };
 
+class DerivePbkdfBitsTask : public ReturnArrayBufferViewTask
+{
+public:
+  DerivePbkdfBitsTask(JSContext* aCx,
+      const ObjectOrString& aAlgorithm, CryptoKey& aKey, uint32_t aLength)
+    : mSymKey(aKey.GetSymKey())
+  {
+    Init(aCx, aAlgorithm, aKey, aLength);
+  }
+
+  DerivePbkdfBitsTask(JSContext* aCx, const ObjectOrString& aAlgorithm,
+                      CryptoKey& aKey, const ObjectOrString& aTargetAlgorithm)
+    : mSymKey(aKey.GetSymKey())
+  {
+    size_t length;
+    mEarlyRv = GetKeySizeForAlgorithm(aCx, aTargetAlgorithm, length);
+
+    if (NS_SUCCEEDED(mEarlyRv)) {
+      Init(aCx, aAlgorithm, aKey, length);
+    }
+  }
+
+  void Init(JSContext* aCx, const ObjectOrString& aAlgorithm, CryptoKey& aKey,
+            uint32_t aLength)
+  {
+    // Check that we got a symmetric key
+    if (mSymKey.Length() == 0) {
+      mEarlyRv = NS_ERROR_DOM_INVALID_ACCESS_ERR;
+      return;
+    }
+
+    RootedDictionary<Pbkdf2Params> params(aCx);
+    mEarlyRv = Coerce(aCx, params, aAlgorithm);
+    if (NS_FAILED(mEarlyRv) || !params.mHash.WasPassed() ||
+        !params.mIterations.WasPassed() || !params.mSalt.WasPassed()) {
+      mEarlyRv = NS_ERROR_DOM_SYNTAX_ERR;
+      return;
+    }
+
+    // length must be a multiple of 8 bigger than zero.
+    if (aLength == 0 || aLength % 8) {
+      mEarlyRv = NS_ERROR_DOM_DATA_ERR;
+      return;
+    }
+
+    // Extract the hash algorithm.
+    nsString hashName;
+    mEarlyRv = GetAlgorithmName(aCx, params.mHash.Value(), hashName);
+    if (NS_FAILED(mEarlyRv)) {
+      return;
+    }
+
+    // Check the given hash algorithm.
+    switch (MapAlgorithmNameToMechanism(hashName)) {
+      case CKM_SHA_1: mHashOidTag = SEC_OID_HMAC_SHA1; break;
+      case CKM_SHA256: mHashOidTag = SEC_OID_HMAC_SHA256; break;
+      case CKM_SHA384: mHashOidTag = SEC_OID_HMAC_SHA384; break;
+      case CKM_SHA512: mHashOidTag = SEC_OID_HMAC_SHA512; break;
+      default: {
+        mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR;
+        return;
+      }
+    }
+
+    ATTEMPT_BUFFER_INIT(mSalt, params.mSalt.Value())
+    mLength = aLength >> 3; // bits to bytes
+    mIterations = params.mIterations.Value();
+  }
+
+private:
+  size_t mLength;
+  size_t mIterations;
+  CryptoBuffer mSalt;
+  CryptoBuffer mSymKey;
+  SECOidTag mHashOidTag;
+
+  virtual nsresult DoCrypto() MOZ_OVERRIDE
+  {
+    ScopedSECItem salt;
+    ATTEMPT_BUFFER_TO_SECITEM(salt, mSalt);
+
+    // Always pass in cipherAlg=SEC_OID_HMAC_SHA1 (i.e. PBMAC1) as this
+    // parameter is unused for key generation. It is currently only used
+    // for PBKDF2 authentication or key (un)wrapping when specifying an
+    // encryption algorithm (PBES2).
+    ScopedSECAlgorithmID alg_id(PK11_CreatePBEV2AlgorithmID(
+      SEC_OID_PKCS5_PBKDF2, SEC_OID_HMAC_SHA1, mHashOidTag,
+      mLength, mIterations, salt));
+
+    if (!alg_id.get()) {
+      return NS_ERROR_DOM_OPERATION_ERR;
+    }
+
+    ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+    if (!slot.get()) {
+      return NS_ERROR_DOM_OPERATION_ERR;
+    }
+
+    ScopedSECItem keyItem;
+    ATTEMPT_BUFFER_TO_SECITEM(keyItem, mSymKey);
+
+    ScopedPK11SymKey symKey(PK11_PBEKeyGen(slot, alg_id, keyItem, false, nullptr));
+    if (!symKey.get()) {
+      return NS_ERROR_DOM_OPERATION_ERR;
+    }
+
+    nsresult rv = MapSECStatus(PK11_ExtractKeyValue(symKey));
+    if (NS_FAILED(rv)) {
+      return NS_ERROR_DOM_OPERATION_ERR;
+    }
+
+    // This doesn't leak, because the SECItem* returned by PK11_GetKeyData
+    // just refers to a buffer managed by symKey. The assignment copies the
+    // data, so mResult manages one copy, while symKey manages another.
+    ATTEMPT_BUFFER_ASSIGN(mResult, PK11_GetKeyData(symKey));
+    return NS_OK;
+  }
+};
+
+class DerivePbkdfKeyTask : public DerivePbkdfBitsTask
+{
+public:
+  DerivePbkdfKeyTask(JSContext* aCx,
+                     const ObjectOrString& aAlgorithm, CryptoKey& aBaseKey,
+                     const ObjectOrString& aDerivedKeyType, bool aExtractable,
+                     const Sequence<nsString>& aKeyUsages)
+    : DerivePbkdfBitsTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType)
+  {
+    if (NS_FAILED(mEarlyRv)) {
+      return;
+    }
+
+    CryptoOperationData dummy;
+    NS_NAMED_LITERAL_STRING(format, WEBCRYPTO_KEY_FORMAT_RAW);
+
+    mTask = new ImportSymmetricKeyTask(aCx, format, dummy, aDerivedKeyType,
+                                       aExtractable, aKeyUsages);
+  }
+
+protected:
+  nsRefPtr<ImportSymmetricKeyTask> mTask;
+
+private:
+  virtual void Resolve() MOZ_OVERRIDE {
+    mTask->SetKeyData(mResult);
+    mTask->DispatchWithPromise(mResultPromise);
+  }
+
+  virtual void Cleanup() MOZ_OVERRIDE
+  {
+    mTask = nullptr;
+  }
+};
+
 
 // Task creation methods for WebCryptoTask
 
 WebCryptoTask*
 WebCryptoTask::EncryptDecryptTask(JSContext* aCx,
                                   const ObjectOrString& aAlgorithm,
                                   CryptoKey& aKey,
                                   const CryptoOperationData& aData,
@@ -1515,16 +1738,17 @@ WebCryptoTask::ImportKeyTask(JSContext* 
   nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
   if (NS_FAILED(rv)) {
     return new FailureTask(rv);
   }
 
   if (algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CBC) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_CTR) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_AES_GCM) ||
+      algName.EqualsLiteral(WEBCRYPTO_ALG_PBKDF2) ||
       algName.EqualsLiteral(WEBCRYPTO_ALG_HMAC)) {
     return new ImportSymmetricKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
                                       aExtractable, aKeyUsages);
   } else if (algName.EqualsLiteral(WEBCRYPTO_ALG_RSAES_PKCS1) ||
              algName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) {
     return new ImportRsaKeyTask(aCx, aFormat, aKeyData, aAlgorithm,
                                 aExtractable, aKeyUsages);
   } else {
@@ -1577,24 +1801,47 @@ WebCryptoTask*
 WebCryptoTask::DeriveKeyTask(JSContext* aCx,
                              const ObjectOrString& aAlgorithm,
                              CryptoKey& aBaseKey,
                              const ObjectOrString& aDerivedKeyType,
                              bool aExtractable,
                              const Sequence<nsString>& aKeyUsages)
 {
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEKEY);
+
+  nsString algName;
+  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
+  if (NS_FAILED(rv)) {
+    return new FailureTask(rv);
+  }
+
+  if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
+    return new DerivePbkdfKeyTask(aCx, aAlgorithm, aBaseKey, aDerivedKeyType,
+                                  aExtractable, aKeyUsages);
+  }
+
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 WebCryptoTask*
 WebCryptoTask::DeriveBitsTask(JSContext* aCx,
                               const ObjectOrString& aAlgorithm,
                               CryptoKey& aKey,
                               uint32_t aLength)
 {
   Telemetry::Accumulate(Telemetry::WEBCRYPTO_METHOD, TM_DERIVEBITS);
+
+  nsString algName;
+  nsresult rv = GetAlgorithmName(aCx, aAlgorithm, algName);
+  if (NS_FAILED(rv)) {
+    return new FailureTask(rv);
+  }
+
+  if (algName.EqualsASCII(WEBCRYPTO_ALG_PBKDF2)) {
+    return new DerivePbkdfBitsTask(aCx, aAlgorithm, aKey, aLength);
+  }
+
   return new FailureTask(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/crypto/moz.build
+++ b/dom/crypto/moz.build
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 TEST_DIRS += ['test']
 
 EXPORTS.mozilla.dom += [
     'AesKeyAlgorithm.h',
+    'BasicSymmetricKeyAlgorithm.h',
     'CryptoBuffer.h',
     'CryptoKey.h',
     'CryptoKeyPair.h',
     'HmacKeyAlgorithm.h',
     'KeyAlgorithm.h',
     'RsaHashedKeyAlgorithm.h',
     'RsaKeyAlgorithm.h',
     'WebCryptoCommon.h',
--- a/dom/crypto/test/test-vectors.js
+++ b/dom/crypto/test/test-vectors.js
@@ -305,9 +305,34 @@ tv = {
     ),
     sig_fail: util.hex2abv(
       "8000000080e5c7b4b5e672929f664c4896e50c35134b6de4d5a934252a3a245f" +
       "f48340920e1034b7d5a5b524eb0e1cf12befef49b27b732d2c19e1c43217d6e1" +
       "417381111a1d36de6375cf455b3c9812639dbc27600c751994fb61799ecf7da6" +
       "bcf51540afd0174db4033188556675b1d763360af46feeca5b60f882829ee7b2"
     ),
   },
+
+  // RFC 6070 <http://tools.ietf.org/html/rfc6070>
+  pbkdf2_sha1: {
+    password: new TextEncoder("utf-8").encode("passwordPASSWORDpassword"),
+    salt: new TextEncoder("utf-8").encode("saltSALTsaltSALTsaltSALTsaltSALTsalt"),
+    iterations: 4096,
+    length: 25 * 8,
+
+    derived: util.hex2abv(
+      "3d2eec4fe41c849b80c8d83662c0e44a8b291a964cf2f07038"
+    )
+  },
+
+  // https://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors
+  pbkdf2_sha256: {
+    password: new TextEncoder("utf-8").encode("passwordPASSWORDpassword"),
+    salt: new TextEncoder("utf-8").encode("saltSALTsaltSALTsaltSALTsaltSALTsalt"),
+    iterations: 4096,
+    length: 40 * 8,
+
+    derived: util.hex2abv(
+      "348c89dbcbd32b2f32d814b8116e84cf2b17347ebc1800181c4e2a1fb8dd53e1" +
+      "c635518c7dac47e9"
+    )
+  },
 }
--- a/dom/crypto/test/tests.js
+++ b/dom/crypto/test/tests.js
@@ -1013,9 +1013,140 @@ TestArray.addTest(
       .then( doVerify, fail )
       .then(
         complete(that, function(x) { return !x; }),
         fail
       );
   }
 );
 
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Import raw PBKDF2 key",
+  function() {
+    var that = this;
+    var alg = "PBKDF2";
+    var key = new TextEncoder("utf-8").encode("password");
 
+    crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"]).then(
+      complete(that, hasKeyFields),
+      error(that)
+    );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Import raw PBKDF2 key and derive bits using HMAC-SHA-1",
+  function() {
+    var that = this;
+    var alg = "PBKDF2";
+    var key = tv.pbkdf2_sha1.password;
+
+    function doDerive(x) {
+      console.log("deriving");
+      if (!hasKeyFields(x)) {
+        throw "Invalid key; missing field(s)";
+      }
+
+      var alg = {
+        name: "PBKDF2",
+        hash: "SHA-1",
+        salt: tv.pbkdf2_sha1.salt,
+        iterations: tv.pbkdf2_sha1.iterations
+      };
+      return crypto.subtle.deriveBits(alg, x, tv.pbkdf2_sha1.length);
+    }
+    function fail(x) { console.log("failing"); error(that)(x); }
+
+    crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"])
+      .then( doDerive, fail )
+      .then( memcmp_complete(that, tv.pbkdf2_sha1.derived), fail );
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Import raw PBKDF2 key and derive a new key using HMAC-SHA-1",
+  function() {
+    var that = this;
+    var alg = "PBKDF2";
+    var key = tv.pbkdf2_sha1.password;
+
+    function doDerive(x) {
+      console.log("deriving");
+      if (!hasKeyFields(x)) {
+        throw "Invalid key; missing field(s)";
+      }
+
+      var alg = {
+        name: "PBKDF2",
+        hash: "SHA-1",
+        salt: tv.pbkdf2_sha1.salt,
+        iterations: tv.pbkdf2_sha1.iterations
+      };
+
+      var algDerived = {
+        name: "HMAC",
+        hash: {name: "SHA-1"}
+      };
+
+      return crypto.subtle.deriveKey(alg, x, algDerived, false, ["sign", "verify"])
+        .then(function (x) {
+          if (!hasKeyFields(x)) {
+            throw "Invalid key; missing field(s)";
+          }
+
+          if (x.algorithm.length != 512) {
+            throw "Invalid key; incorrect length";
+          }
+
+          return x;
+        });
+    }
+
+    function doSignAndVerify(x) {
+      var data = new Uint8Array(1024);
+
+      return crypto.subtle.sign("HMAC", x, data)
+        .then(function (sig) {
+          return crypto.subtle.verify("HMAC", x, sig, data);
+        });
+    }
+
+    function fail(x) { console.log("failing"); error(that)(x); }
+
+    crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"])
+      .then( doDerive, fail )
+      .then( doSignAndVerify, fail )
+      .then( complete(that), fail );
+  }
+);
+
+// -----------------------------------------------------------------------------
+/*TestArray.addTest(
+  "Import raw PBKDF2 key and derive bits using HMAC-SHA-256",
+  function() {
+    var that = this;
+    var alg = "PBKDF2";
+    var key = tv.pbkdf2_sha256.password;
+
+    function doDerive(x) {
+      console.log("deriving");
+      if (!hasKeyFields(x)) {
+        throw "Invalid key; missing field(s)";
+      }
+
+      var alg = {
+        name: "PBKDF2",
+        hash: "SHA-256",
+        salt: tv.pbkdf2_sha256.salt,
+        iterations: tv.pbkdf2_sha256.iterations
+      };
+      return crypto.subtle.deriveBits(alg, x, tv.pbkdf2_sha256.length);
+    }
+    function fail(x) { console.log("failing"); error(that)(x); }
+
+    crypto.subtle.importKey("raw", key, alg, false, ["deriveKey"])
+      .then( doDerive, fail )
+      .then( memcmp_complete(that, tv.pbkdf2_sha256.derived), fail );
+  }
+);*/
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -1,21 +1,23 @@
 /* -*- 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/. */
 
 #include "domstubs.idl"
 
-[uuid(d9539ecb-6665-452c-bae7-4e42f25d23aa)]
+[uuid(44ef0b7e-92c0-48a7-a092-5a49f2533792)]
 interface nsIServiceWorkerManager : nsISupports
 {
   // Returns a Promise
   nsISupports register(in nsIDOMWindow aWindow, in DOMString aScope, in DOMString aScriptURI);
 
   // Returns a Promise
   nsISupports unregister(in nsIDOMWindow aWindow, in DOMString aScope);
 
+  // Testing
+  DOMString getScopeForUrl(in DOMString path);
 };
 
 %{ C++
 #define SERVICEWORKERMANAGER_CONTRACTID "@mozilla.org/serviceworkers/manager;1"
 %}
--- a/dom/interfaces/html/moz.build
+++ b/dom/interfaces/html/moz.build
@@ -3,17 +3,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/.
 
 XPIDL_SOURCES += [
     'nsIDOMHTMLAnchorElement.idl',
     'nsIDOMHTMLAppletElement.idl',
     'nsIDOMHTMLAreaElement.idl',
-    'nsIDOMHTMLAudioElement.idl',
     'nsIDOMHTMLBaseElement.idl',
     'nsIDOMHTMLBodyElement.idl',
     'nsIDOMHTMLBRElement.idl',
     'nsIDOMHTMLButtonElement.idl',
     'nsIDOMHTMLCanvasElement.idl',
     'nsIDOMHTMLCollection.idl',
     'nsIDOMHTMLDirectoryElement.idl',
     'nsIDOMHTMLDivElement.idl',
@@ -53,17 +52,16 @@ XPIDL_SOURCES += [
     'nsIDOMHTMLSourceElement.idl',
     'nsIDOMHTMLStyleElement.idl',
     'nsIDOMHTMLTableCaptionElem.idl',
     'nsIDOMHTMLTableCellElement.idl',
     'nsIDOMHTMLTableElement.idl',
     'nsIDOMHTMLTextAreaElement.idl',
     'nsIDOMHTMLTitleElement.idl',
     'nsIDOMHTMLUListElement.idl',
-    'nsIDOMHTMLVideoElement.idl',
     'nsIDOMMediaError.idl',
     'nsIDOMMozBrowserFrame.idl',
     'nsIDOMTimeRanges.idl',
     'nsIDOMValidityState.idl',
     'nsIMozBrowserFrame.idl',
 ]
 
 XPIDL_MODULE = 'dom_html'
deleted file mode 100644
--- a/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
+++ /dev/null
@@ -1,22 +0,0 @@
-/* -*- 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 "nsIDOMHTMLMediaElement.idl"
-
-/**
- * The nsIDOMHTMLAudioElement interface is the interface to a HTML
- * <audio> element.
- *
- * For more information on this interface, please see
- * http://www.whatwg.org/specs/web-apps/current-work/#audio
- *
- * @status UNDER_DEVELOPMENT
- */
-
-[uuid(75a7f3ca-0761-4b63-863b-6fd6a87ed51c)]
-interface nsIDOMHTMLAudioElement : nsIDOMHTMLMediaElement
-{
-};
--- a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
@@ -22,17 +22,17 @@ interface nsIDOMMediaStream;
 
 // undef the GetCurrentTime macro defined in WinBase.h from the MS Platform SDK
 %{C++
 #ifdef GetCurrentTime
 #undef GetCurrentTime
 #endif
 %}
 
-[uuid(1f9393e8-2df0-4072-87b9-c26999b09acc)]
+[uuid(0e14e6ad-2074-48b7-a247-e545a3a15131)]
 interface nsIDOMHTMLMediaElement : nsISupports
 {
   // error state
   readonly attribute nsIDOMMediaError error;
 
   // network state
            attribute DOMString src;
            attribute nsIDOMMediaStream mozSrcObject;
@@ -128,9 +128,11 @@ interface nsIDOMHTMLMediaElement : nsISu
    //   Always plays in speaker, even when headphones are plugged in.
    //   Use case: Camera shutter sound.
    attribute DOMString mozAudioChannelType;
 
   // In addition the media element has this new events:
   // * onmozinterruptbegin - called when the media element is interrupted
   //   because of the audiochannel manager.
   // * onmozinterruptend - called when the interruption is concluded
+
+  [notxpcom] boolean isVideo();
 };
deleted file mode 100644
--- a/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
+++ /dev/null
@@ -1,50 +0,0 @@
-/* -*- 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 "nsIDOMHTMLMediaElement.idl"
-
-/**
- * The nsIDOMHTMLVideoElement interface is the interface to a HTML
- * <video> element.
- *
- * For more information on this interface, please see
- * http://www.whatwg.org/specs/web-apps/current-work/#video
- *
- * @status UNDER_DEVELOPMENT
- */
-
-[uuid(185a3e8f-56a7-4bda-8dc7-2cff6ed07d1d)]
-interface nsIDOMHTMLVideoElement : nsIDOMHTMLMediaElement
-{
-           attribute long width; 
-           attribute long height;
-  readonly attribute unsigned long videoWidth;
-  readonly attribute unsigned long videoHeight;
-           attribute DOMString poster;
-           
-  // A count of the number of video frames that have demuxed from the media
-  // resource. If we were playing perfectly, we'd be able to paint this many
-  // frames.
-  readonly attribute unsigned long mozParsedFrames;
-
-  // A count of the number of frames that have been decoded. We may drop
-  // frames if the decode is taking too much time.
-  readonly attribute unsigned long mozDecodedFrames;
-
-  // A count of the number of frames that have been presented to the rendering
-  // pipeline. We may drop frames if they arrive late at the renderer.
-  readonly attribute unsigned long mozPresentedFrames;
-
-  // Number of presented frames which were painted on screen.
-  readonly attribute unsigned long mozPaintedFrames;
-
-  // Time which the last painted video frame was late by, in seconds.
-  readonly attribute double mozFrameDelay;
-
-  // True if the video has an audio track available.
-  readonly attribute bool mozHasAudio;
-};
-
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -2033,16 +2033,29 @@ void
 GetUserMediaCallbackMediaStreamListener::NotifyFinished(MediaStreamGraph* aGraph)
 {
   mFinished = true;
   Invalidate(); // we know it's been activated
   NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, this));
 }
 
 // Called from the MediaStreamGraph thread
+void
+GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph* aGraph,
+                                                               bool aHasListeners)
+{
+  nsRefPtr<MediaOperationRunnable> runnable;
+  runnable = new MediaOperationRunnable(MEDIA_DIRECT_LISTENERS,
+                                        this, nullptr, nullptr,
+                                        mAudioSource, mVideoSource,
+                                        aHasListeners, mWindowID, nullptr);
+  mMediaThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
+}
+
+// Called from the MediaStreamGraph thread
 // this can be in response to our own RemoveListener() (via ::Remove()), or
 // because the DOM GC'd the DOMLocalMediaStream/etc we're attached to.
 void
 GetUserMediaCallbackMediaStreamListener::NotifyRemoved(MediaStreamGraph* aGraph)
 {
   {
     MutexAutoLock lock(mLock); // protect access to mRemoved
     MM_LOG(("Listener removed by DOM Destroy(), mFinished = %d", (int) mFinished));
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -170,20 +170,45 @@ public:
       mAudioSource->NotifyPull(aGraph, mStream, kAudioTrack, aDesiredTime, mLastEndTimeAudio);
     }
     if (mVideoSource) {
       mVideoSource->NotifyPull(aGraph, mStream, kVideoTrack, aDesiredTime, mLastEndTimeVideo);
     }
   }
 
   virtual void
-  NotifyFinished(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+  NotifyEvent(MediaStreamGraph* aGraph,
+              MediaStreamListener::MediaStreamGraphEvent aEvent) MOZ_OVERRIDE
+  {
+    switch (aEvent) {
+      case EVENT_FINISHED:
+        NotifyFinished(aGraph);
+        break;
+      case EVENT_REMOVED:
+        NotifyRemoved(aGraph);
+        break;
+      case EVENT_HAS_DIRECT_LISTENERS:
+        NotifyDirectListeners(aGraph, true);
+        break;
+      case EVENT_HAS_NO_DIRECT_LISTENERS:
+        NotifyDirectListeners(aGraph, false);
+        break;
+      default:
+        break;
+    }
+  }
 
   virtual void
-  NotifyRemoved(MediaStreamGraph* aGraph) MOZ_OVERRIDE;
+  NotifyFinished(MediaStreamGraph* aGraph);
+
+  virtual void
+  NotifyRemoved(MediaStreamGraph* aGraph);
+
+  virtual void
+  NotifyDirectListeners(MediaStreamGraph* aGraph, bool aHasListeners);
 
 private:
   // Set at construction
   nsCOMPtr<nsIThread> mMediaThread;
   uint64_t mWindowID;
 
   bool mStopped; // MainThread only
 
@@ -239,17 +264,18 @@ class GetUserMediaNotificationEvent: pub
     bool mIsAudio;
     bool mIsVideo;
     uint64_t mWindowID;
     nsRefPtr<nsIDOMGetUserMediaErrorCallback> mError;
 };
 
 typedef enum {
   MEDIA_START,
-  MEDIA_STOP
+  MEDIA_STOP,
+  MEDIA_DIRECT_LISTENERS
 } MediaOperation;
 
 class MediaManager;
 class GetUserMediaRunnable;
 
 /**
  * Send an error back to content. The error is the form a string.
  * Do this only on the main thread. The success callback is also passed here
@@ -294,26 +320,26 @@ class MediaOperationRunnable : public ns
 public:
   // so we can send Stop without AddRef()ing from the MSG thread
   MediaOperationRunnable(MediaOperation aType,
     GetUserMediaCallbackMediaStreamListener* aListener,
     DOMMediaStream* aStream,
     DOMMediaStream::OnTracksAvailableCallback* aOnTracksAvailableCallback,
     MediaEngineSource* aAudioSource,
     MediaEngineSource* aVideoSource,
-    bool aNeedsFinish,
+    bool aBool,
     uint64_t aWindowID,
     already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError)
     : mType(aType)
     , mStream(aStream)
     , mOnTracksAvailableCallback(aOnTracksAvailableCallback)
     , mAudioSource(aAudioSource)
     , mVideoSource(aVideoSource)
     , mListener(aListener)
-    , mFinish(aNeedsFinish)
+    , mBool(aBool)
     , mWindowID(aWindowID)
     , mError(aError)
   {}
 
   ~MediaOperationRunnable()
   {
     // MediaStreams can be released on any thread.
   }
@@ -393,46 +419,55 @@ public:
             mAudioSource->Stop(source, kAudioTrack);
             mAudioSource->Deallocate();
           }
           if (mVideoSource) {
             mVideoSource->Stop(source, kVideoTrack);
             mVideoSource->Deallocate();
           }
           // Do this after stopping all tracks with EndTrack()
-          if (mFinish) {
+          if (mBool) {
             source->Finish();
           }
           nsIRunnable *event =
             new GetUserMediaNotificationEvent(mListener,
                                               GetUserMediaNotificationEvent::STOPPING,
                                               mAudioSource != nullptr,
                                               mVideoSource != nullptr,
                                               mWindowID);
           // event must always be released on mainthread due to the JS callbacks
           // in the TracksAvailableCallback
           NS_DispatchToMainThread(event);
         }
         break;
 
+      case MEDIA_DIRECT_LISTENERS:
+        {
+          NS_ASSERTION(!NS_IsMainThread(), "Never call on main thread");
+          if (mVideoSource) {
+            mVideoSource->SetDirectListeners(mBool);
+          }
+        }
+        break;
+
       default:
         MOZ_ASSERT(false,"invalid MediaManager operation");
         break;
     }
     return NS_OK;
   }
 
 private:
   MediaOperation mType;
   nsRefPtr<DOMMediaStream> mStream;
   nsAutoPtr<DOMMediaStream::OnTracksAvailableCallback> mOnTracksAvailableCallback;
   nsRefPtr<MediaEngineSource> mAudioSource; // threadsafe
   nsRefPtr<MediaEngineSource> mVideoSource; // threadsafe
   nsRefPtr<GetUserMediaCallbackMediaStreamListener> mListener; // threadsafe
-  bool mFinish;
+  bool mBool;
   uint64_t mWindowID;
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> mError;
 };
 
 typedef nsTArray<nsRefPtr<GetUserMediaCallbackMediaStreamListener> > StreamListeners;
 typedef nsClassHashtable<nsUint64HashKey, StreamListeners> WindowTable;
 
 class MediaDevice : public nsIMediaDevice
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -10,16 +10,17 @@
 
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsJSNPRuntime.h"
 #include "nsNPAPIPlugin.h"
 #include "nsNPAPIPluginInstance.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIScriptContext.h"
 #include "nsDOMJSUtils.h"
+#include "nsJSUtils.h"
 #include "nsCxPusher.h"
 #include "nsIDocument.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIXPConnect.h"
 #include "nsIDOMElement.h"
 #include "prmem.h"
 #include "nsIContent.h"
 #include "nsPluginInstanceOwner.h"
@@ -400,24 +401,22 @@ JSValToNPVariant(NPP npp, JSContext *cx,
       int i;
       if (JS_DoubleIsInt32(d, &i)) {
         INT32_TO_NPVARIANT(i, *variant);
       } else {
         DOUBLE_TO_NPVARIANT(d, *variant);
       }
     } else if (val.isString()) {
       JSString *jsstr = val.toString();
-      size_t length;
-      const jschar *chars = ::JS_GetStringCharsZAndLength(cx, jsstr, &length);
-      if (!chars) {
-          return false;
+
+      nsAutoJSString str;
+      if (!str.init(cx, jsstr)) {
+        return false;
       }
 
-      nsDependentString str(chars, length);
-
       uint32_t len;
       char *p = ToNewUTF8String(str, &len);
 
       if (!p) {
         return false;
       }
 
       STRINGN_TO_NPVARIANT(p, len, *variant);
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -1298,20 +1298,20 @@ NPUTF8*
   if (!id)
     return nullptr;
 
   if (!NPIdentifierIsString(id)) {
     return nullptr;
   }
 
   JSString *str = NPIdentifierToString(id);
-
-  return
-    ToNewUTF8String(nsDependentString(::JS_GetInternedStringChars(str),
-                                      ::JS_GetStringLength(str)));
+  nsAutoString autoStr;
+  AssignJSFlatString(autoStr, JS_ASSERT_STRING_IS_FLAT(str));
+
+  return ToNewUTF8String(autoStr);
 }
 
 int32_t
 _intfromidentifier(NPIdentifier id)
 {
   if (!NS_IsMainThread()) {
     NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_intfromidentifier called from the wrong thread\n"));
   }
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -50,16 +50,17 @@ using namespace mozilla;
 #include "mozilla/CondVar.h"
 #include "AndroidBridge.h"
 #include "mozilla/dom/ScreenOrientation.h"
 #include "mozilla/Hal.h"
 #include "GLContextProvider.h"
 #include "GLContext.h"
 #include "TexturePoolOGL.h"
 #include "GLSharedHandleHelpers.h"
+#include "SurfaceTypes.h"
 
 using namespace mozilla::gl;
 
 typedef nsNPAPIPluginInstance::VideoInfo VideoInfo;
 
 class PluginEventRunnable : public nsRunnable
 {
 public:
@@ -84,17 +85,17 @@ private:
 
 static nsRefPtr<GLContext> sPluginContext = nullptr;
 
 static bool EnsureGLContext()
 {
   if (!sPluginContext) {
     gfxIntSize dummySize(16, 16);
     sPluginContext = GLContextProvider::CreateOffscreen(dummySize,
-                                                        GLContext::SurfaceCaps::Any());
+                                                        SurfaceCaps::Any());
   }
 
   return sPluginContext != nullptr;
 }
 
 class SharedPluginTexture MOZ_FINAL {
 public:
   NS_INLINE_DECL_REFCOUNTING(SharedPluginTexture)
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -160,17 +160,17 @@ nsPluginInstanceOwner::GetImageContainer
 
   LayoutDeviceRect r = GetPluginRect();
 
   // NotifySize() causes Flash to do a bunch of stuff like ask for surfaces to render
   // into, set y-flip flags, etc, so we do this at the beginning.
   gfxSize resolution = mObjectFrame->PresContext()->PresShell()->GetCumulativeResolution();
   ScreenSize screenSize = (r * LayoutDeviceToScreenScale(resolution.width, resolution.height)).Size();
   mInstance->NotifySize(nsIntSize(screenSize.width, screenSize.height));
-  
+
   container = LayerManager::CreateImageContainer();
 
   nsRefPtr<Image> img = container->CreateImage(ImageFormat::SHARED_TEXTURE);
 
   SharedTextureImage::Data data;
   data.mSize = gfx::IntSize(r.width, r.height);
   data.mHandle = mInstance->CreateSharedHandle();
   data.mShareType = mozilla::gl::SharedTextureShareType::SameProcess;
@@ -255,17 +255,17 @@ nsPluginInstanceOwner::GetCurrentImageSi
   return size;
 }
 
 nsPluginInstanceOwner::nsPluginInstanceOwner()
 {
   // create nsPluginNativeWindow object, it is derived from NPWindow
   // struct and allows to manipulate native window procedure
   nsCOMPtr<nsIPluginHost> pluginHostCOM = do_GetService(MOZ_PLUGIN_HOST_CONTRACTID);
-  mPluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());  
+  mPluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
   if (mPluginHost)
     mPluginHost->NewPluginNativeWindow(&mPluginWindow);
   else
     mPluginWindow = nullptr;
 
   mObjectFrame = nullptr;
   mContent = nullptr;
   mWidgetCreationComplete = false;
@@ -424,17 +424,17 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
 
   return rv;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::GetAttribute(const char* name, const char* *result)
 {
   NS_ENSURE_ARG_POINTER(name);
   NS_ENSURE_ARG_POINTER(result);
-  
+
   nsresult rv = EnsureCachedAttrParamArrays();
   NS_ENSURE_SUCCESS(rv, rv);
 
   *result = nullptr;
 
   for (int i = 0; i < mNumCachedAttrs; i++) {
     if (0 == PL_strcasecmp(mCachedAttrParamNames[i], name)) {
       *result = mCachedAttrParamValues[i];
@@ -525,19 +525,19 @@ NS_IMETHODIMP nsPluginInstanceOwner::Get
                        aPostStream, headersDataStream, true);
 
   return rv;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const char *aStatusMsg)
 {
   nsresult  rv = NS_ERROR_FAILURE;
-  
+
   rv = this->ShowStatus(NS_ConvertUTF8toUTF16(aStatusMsg).get());
-  
+
   return rv;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::ShowStatus(const char16_t *aStatusMsg)
 {
   nsresult  rv = NS_ERROR_FAILURE;
 
   if (!mObjectFrame) {
@@ -553,17 +553,17 @@ NS_IMETHODIMP nsPluginInstanceOwner::Sho
   if (NS_FAILED(rv) || !treeOwner) {
     return rv;
   }
 
   nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(treeOwner, &rv));
   if (NS_FAILED(rv) || !browserChrome) {
     return rv;
   }
-  rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT, 
+  rv = browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_SCRIPT,
                                 aStatusMsg);
 
   return rv;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::GetDocument(nsIDocument* *aDocument)
 {
   if (!aDocument)
@@ -621,73 +621,73 @@ NS_IMETHODIMP nsPluginInstanceOwner::Inv
   mObjectFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN, &rect);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRegion(NPRegion invalidRegion)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
- 
+
 NS_IMETHODIMP
 nsPluginInstanceOwner::RedrawPlugin()
 {
   if (mObjectFrame) {
     mObjectFrame->InvalidateLayer(nsDisplayItem::TYPE_PLUGIN);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::GetNetscapeWindow(void *value)
 {
   if (!mObjectFrame) {
     NS_WARNING("plugin owner has no owner in getting doc's window handle");
     return NS_ERROR_FAILURE;
   }
-  
+
 #if defined(XP_WIN)
   void** pvalue = (void**)value;
   nsViewManager* vm = mObjectFrame->PresContext()->GetPresShell()->GetViewManager();
   if (!vm)
     return NS_ERROR_FAILURE;
 #if defined(XP_WIN)
   // This property is provided to allow a "windowless" plugin to determine the window it is drawing
   // in, so it can translate mouse coordinates it receives directly from the operating system
   // to coordinates relative to itself.
-  
+
   // The original code (outside this #if) returns the document's window, which is OK if the window the "windowless" plugin
   // is drawing into has the same origin as the document's window, but this is not the case for "windowless" plugins inside of scrolling DIVs etc
-  
+
   // To make sure "windowless" plugins always get the right origin for translating mouse coordinates, this code
   // determines the window handle of the mozilla window containing the "windowless" plugin.
-  
+
   // Given that this HWND may not be that of the document's window, there is a slight risk
   // of confusing a plugin that is using this HWND for illicit purposes, but since the documentation
   // does not suggest this HWND IS that of the document window, rather that of the window
   // the plugin is drawn in, this seems like a safe fix.
-  
+
   // we only attempt to get the nearest window if this really is a "windowless" plugin so as not
   // to change any behaviour for the much more common windowed plugins,
   // though why this method would even be being called for a windowed plugin escapes me.
   if (mPluginWindow && mPluginWindow->type == NPWindowTypeDrawable) {
     // it turns out that flash also uses this window for determining focus, and is currently
     // unable to show a caret correctly if we return the enclosing window. Therefore for
     // now we only return the enclosing window when there is an actual offset which
     // would otherwise cause coordinates to be offset incorrectly. (i.e.
     // if the enclosing window if offset from the document window)
     //
     // fixing both the caret and ability to interact issues for a windowless control in a non document aligned windw
     // does not seem to be possible without a change to the flash plugin
-    
+
     nsIWidget* win = mObjectFrame->GetNearestWidget();
     if (win) {
       nsView *view = nsView::GetViewFor(win);
       NS_ASSERTION(view, "No view for widget");
       nsPoint offset = view->GetOffsetTo(nullptr);
-      
+
       if (offset.x || offset.y) {
         // in the case the two windows are offset from eachother, we do go ahead and return the correct enclosing window
         // so that mouse co-ordinates are not messed up.
         *pvalue = (void*)win->GetNativeData(NS_NATIVE_WINDOW);
         if (*pvalue)
           return NS_OK;
       }
     }
@@ -1175,17 +1175,17 @@ void nsPluginInstanceOwner::CARefresh(ns
     instanceOwner->GetWindow(window);
     if (!window) {
       continue;
     }
     NPRect r;
     r.left = 0;
     r.top = 0;
     r.right = window->width;
-    r.bottom = window->height; 
+    r.bottom = window->height;
     instanceOwner->InvalidateRect(&r);
   }
 }
 
 void nsPluginInstanceOwner::AddToCARefreshTimer() {
   if (!mInstance) {
     return;
   }
@@ -1215,17 +1215,17 @@ void nsPluginInstanceOwner::AddToCARefre
     sCATimer = new nsCOMPtr<nsITimer>();
     if (!sCATimer) {
       return;
     }
   }
 
   if (sCARefreshListeners->Length() == 1) {
     *sCATimer = do_CreateInstance("@mozilla.org/timer;1");
-    (*sCATimer)->InitWithFuncCallback(CARefresh, nullptr, 
+    (*sCATimer)->InitWithFuncCallback(CARefresh, nullptr,
                    DEFAULT_REFRESH_RATE, nsITimer::TYPE_REPEATING_SLACK);
   }
 }
 
 void nsPluginInstanceOwner::RemoveFromCARefreshTimer() {
   if (!sCARefreshListeners || sCARefreshListeners->Contains(this) == false) {
     return;
   }
@@ -1316,23 +1316,23 @@ void nsPluginInstanceOwner::RenderCoreAn
     ::CGContextDrawImage(aCGContext, CGRectMake(0,0,aWidth,aHeight), caImage);
   } else {
     NS_NOTREACHED("nsCARenderer::Render failure");
   }
 }
 
 void* nsPluginInstanceOwner::GetPluginPortCopy()
 {
-  if (GetDrawingModel() == NPDrawingModelCoreGraphics || 
+  if (GetDrawingModel() == NPDrawingModelCoreGraphics ||
       GetDrawingModel() == NPDrawingModelCoreAnimation ||
       GetDrawingModel() == NPDrawingModelInvalidatingCoreAnimation)
     return &mCGPluginPortCopy;
   return nullptr;
 }
-  
+
 // Currently (on OS X in Cocoa widgets) any changes made as a result of
 // calling GetPluginPortFromWidget() are immediately reflected in the NPWindow
 // structure that has been passed to the plugin via SetWindow().  This is
 // because calls to nsChildView::GetNativeData(NS_NATIVE_PLUGIN_PORT_CG)
 // always return a pointer to the same internal (private) object, but may
 // make changes inside that object.  All calls to GetPluginPortFromWidget() made while
 // the plugin is active (i.e. excluding those made at our initialization)
 // need to take this into account.  The easiest way to do so is to replace
@@ -1441,17 +1441,17 @@ LayoutDeviceRect nsPluginInstanceOwner::
   LayoutDeviceIntRect rect = LayoutDeviceIntRect::FromAppUnitsToNearest(bounds, mObjectFrame->PresContext()->AppUnitsPerDevPixel());
   return LayoutDeviceRect(rect);
 }
 
 bool nsPluginInstanceOwner::AddPluginView(const LayoutDeviceRect& aRect /* = LayoutDeviceRect(0, 0, 0, 0) */)
 {
   if (!mJavaView) {
     mJavaView = mInstance->GetJavaSurface();
-  
+
     if (!mJavaView)
       return false;
 
     mJavaView = (void*)AndroidBridge::GetJNIEnv()->NewGlobalRef((jobject)mJavaView);
   }
 
   if (AndroidBridge::Bridge())
     AndroidBridge::Bridge()->AddPluginView((jobject)mJavaView, aRect, mFullScreen);
@@ -1532,17 +1532,17 @@ void nsPluginInstanceOwner::RequestFullS
 
 void nsPluginInstanceOwner::ExitFullScreen() {
   if (!mFullScreen)
     return;
 
   RemovePluginView();
 
   mFullScreen = false;
-  
+
   int32_t model = mInstance->GetANPDrawingModel();
 
   if (model == kSurface_ANPDrawingModel) {
     // We need to do this immediately, otherwise Flash
     // sometimes causes a deadlock (bug 762407)
     AddPluginView(GetPluginRect());
   }
 
@@ -1576,17 +1576,17 @@ nsresult nsPluginInstanceOwner::Dispatch
     aFocusEvent->GetType(eventType);
     if (eventType.EqualsLiteral("focus")) {
       event.data.lifecycle.action = kGainFocus_ANPLifecycleAction;
     }
     else if (eventType.EqualsLiteral("blur")) {
       event.data.lifecycle.action = kLoseFocus_ANPLifecycleAction;
     }
     else {
-      NS_ASSERTION(false, "nsPluginInstanceOwner::DispatchFocusToPlugin, wierd eventType");   
+      NS_ASSERTION(false, "nsPluginInstanceOwner::DispatchFocusToPlugin, wierd eventType");
     }
     mInstance->HandleEvent(&event, nullptr);
   }
 #endif
 
 #ifndef XP_MACOSX
   if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow)) {
     // continue only for cases without child window
@@ -1598,21 +1598,21 @@ nsresult nsPluginInstanceOwner::Dispatch
   if (theEvent) {
     // we only care about the message in ProcessEvent
     WidgetGUIEvent focusEvent(theEvent->mFlags.mIsTrusted, theEvent->message,
                               nullptr);
     nsEventStatus rv = ProcessEvent(focusEvent);
     if (nsEventStatus_eConsumeNoDefault == rv) {
       aFocusEvent->PreventDefault();
       aFocusEvent->StopPropagation();
-    }   
+    }
   }
-  
+
   return NS_OK;
-}    
+}
 
 nsresult nsPluginInstanceOwner::ProcessKeyPress(nsIDOMEvent* aKeyEvent)
 {
 #ifdef XP_MACOSX
   return DispatchKeyToPlugin(aKeyEvent);
 #else
   if (SendNativeEvents())
     DispatchKeyToPlugin(aKeyEvent);
@@ -1638,54 +1638,54 @@ nsresult nsPluginInstanceOwner::Dispatch
   if (mInstance) {
     WidgetKeyboardEvent* keyEvent =
       aKeyEvent->GetInternalNSEvent()->AsKeyboardEvent();
     if (keyEvent && keyEvent->eventStructType == NS_KEY_EVENT) {
       nsEventStatus rv = ProcessEvent(*keyEvent);
       if (nsEventStatus_eConsumeNoDefault == rv) {
         aKeyEvent->PreventDefault();
         aKeyEvent->StopPropagation();
-      }   
+      }
     }
   }
 
   return NS_OK;
-}    
+}
 
 nsresult
 nsPluginInstanceOwner::ProcessMouseDown(nsIDOMEvent* aMouseEvent)
 {
 #if !defined(XP_MACOSX)
   if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow))
     return aMouseEvent->PreventDefault(); // consume event
   // continue only for cases without child window
 #endif
 
   // if the plugin is windowless, we need to set focus ourselves
   // otherwise, we might not get key events
   if (mObjectFrame && mPluginWindow &&
       mPluginWindow->type == NPWindowTypeDrawable) {
-    
+
     nsIFocusManager* fm = nsFocusManager::GetFocusManager();
     if (fm) {
       nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(mContent);
       fm->SetFocus(elem, 0);
     }
   }
 
   WidgetMouseEvent* mouseEvent =
     aMouseEvent->GetInternalNSEvent()->AsMouseEvent();
   if (mouseEvent && mouseEvent->eventStructType == NS_MOUSE_EVENT) {
     mLastMouseDownButtonType = mouseEvent->button;
     nsEventStatus rv = ProcessEvent(*mouseEvent);
     if (nsEventStatus_eConsumeNoDefault == rv) {
       return aMouseEvent->PreventDefault(); // consume event
     }
   }
-  
+
   return NS_OK;
 }
 
 nsresult nsPluginInstanceOwner::DispatchMouseToPlugin(nsIDOMEvent* aMouseEvent,
                                                       bool aAllowPropagate)
 {
 #if !defined(XP_MACOSX)
   if (!mPluginWindow || (mPluginWindow->type == NPWindowTypeWindow))
@@ -2110,17 +2110,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
               event.same_screen = True;
             }
             break;
           }
       }
       break;
 
    //XXX case NS_MOUSE_SCROLL_EVENT: not received.
- 
+
    case NS_KEY_EVENT:
       if (anEvent.pluginEvent)
         {
           XKeyEvent &event = pluginEvent.xkey;
 #ifdef MOZ_WIDGET_GTK
           event.root = GDK_ROOT_WINDOW();
           event.time = anEvent.time;
           const GdkEventKey* gdkEvent =
@@ -2158,17 +2158,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
           // If we need to send synthesized key events, then
           // DOMKeyCodeToGdkKeyCode(keyEvent.keyCode) and
           // gdk_keymap_get_entries_for_keyval will be useful, but the
           // mappings will not be unique.
           NS_WARNING("Synthesized key event not sent to plugin");
         }
       break;
 
-    default: 
+    default:
       switch (anEvent.message)
         {
         case NS_FOCUS_CONTENT:
         case NS_BLUR_CONTENT:
           {
             XFocusChangeEvent &event = pluginEvent.xfocus;
             event.type =
               anEvent.message == NS_FOCUS_CONTENT ? FocusIn : FocusOut;
@@ -2278,17 +2278,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
      }
      break;
 
     default:
       break;
     }
     rv = nsEventStatus_eConsumeNoDefault;
 #endif
- 
+
   return rv;
 }
 
 nsresult
 nsPluginInstanceOwner::Destroy()
 {
   SetFrame(nullptr);
 
@@ -2349,17 +2349,17 @@ nsPluginInstanceOwner::Destroy()
 // Paints are handled differently, so we just simulate an update event.
 
 #ifdef XP_MACOSX
 void nsPluginInstanceOwner::Paint(const gfxRect& aDirtyRect, CGContextRef cgContext)
 {
   if (!mInstance || !mObjectFrame)
     return;
 
-  gfxRect dirtyRectCopy = aDirtyRect; 
+  gfxRect dirtyRectCopy = aDirtyRect;
   double scaleFactor = 1.0;
   GetContentsScaleFactor(&scaleFactor);
   if (scaleFactor != 1.0) {
     ::CGContextScaleCTM(cgContext, scaleFactor, scaleFactor);
     // Convert aDirtyRect from device pixels to "display pixels"
     // for HiDPI modes
     dirtyRectCopy.ScaleRoundOut(1.0 / scaleFactor);
   }
@@ -2370,17 +2370,17 @@ void nsPluginInstanceOwner::Paint(const 
     pluginWidget->EndDrawPlugin();
   }
 }
 
 void nsPluginInstanceOwner::DoCocoaEventDrawRect(const gfxRect& aDrawRect, CGContextRef cgContext)
 {
   if (!mInstance || !mObjectFrame)
     return;
- 
+
   // The context given here is only valid during the HandleEvent call.
   NPCocoaEvent updateEvent;
   InitializeNPCocoaEvent(&updateEvent);
   updateEvent.type = NPCocoaEventDrawRect;
   updateEvent.data.draw.context = cgContext;
   updateEvent.data.draw.x = aDrawRect.X();
   updateEvent.data.draw.y = aDrawRect.Y();
   updateEvent.data.draw.width = aDrawRect.Width();
@@ -2427,44 +2427,44 @@ void nsPluginInstanceOwner::Paint(gfxCon
 
 #ifdef ANP_BITMAP_DRAWING_MODEL
   static nsRefPtr<gfxImageSurface> pluginSurface;
 
   if (pluginSurface == nullptr ||
       aFrameRect.width  != pluginSurface->Width() ||
       aFrameRect.height != pluginSurface->Height()) {
 
-    pluginSurface = new gfxImageSurface(gfxIntSize(aFrameRect.width, aFrameRect.height), 
+    pluginSurface = new gfxImageSurface(gfxIntSize(aFrameRect.width, aFrameRect.height),
                                         gfxImageFormat::ARGB32);
     if (!pluginSurface)
       return;
   }
 
   // Clears buffer.  I think this is needed.
   gfxUtils::ClearThebesSurface(pluginSurface);
-  
+
   ANPEvent event;
   event.inSize = sizeof(ANPEvent);
   event.eventType = 4;
   event.data.draw.model = 1;
-  
+
   event.data.draw.clip.top     = 0;
   event.data.draw.clip.left    = 0;
   event.data.draw.clip.bottom  = aFrameRect.width;
   event.data.draw.clip.right   = aFrameRect.height;
-  
+
   event.data.draw.data.bitmap.format   = kRGBA_8888_ANPBitmapFormat;
   event.data.draw.data.bitmap.width    = aFrameRect.width;
   event.data.draw.data.bitmap.height   = aFrameRect.height;
   event.data.draw.data.bitmap.baseAddr = pluginSurface->Data();
   event.data.draw.data.bitmap.rowBytes = aFrameRect.width * 4;
-  
+
   if (!mInstance)
     return;
-    
+
   mInstance->HandleEvent(&event, nullptr);
 
   aContext->SetOperator(gfxContext::OPERATOR_SOURCE);
   aContext->SetSource(pluginSurface, gfxPoint(aFrameRect.x, aFrameRect.y));
   aContext->Clip(aFrameRect);
   aContext->Paint();
 #endif
 }
@@ -2538,17 +2538,17 @@ void nsPluginInstanceOwner::Paint(gfxCon
   Visual* visual = DefaultVisualOfScreen(screen);
 
   renderer.Draw(aContext, nsIntSize(window->width, window->height),
                 rendererFlags, screen, visual);
 }
 nsresult
 nsPluginInstanceOwner::Renderer::DrawWithXlib(cairo_surface_t* xsurface,
                                               nsIntPoint offset,
-                                              nsIntRect *clipRects, 
+                                              nsIntRect *clipRects,
                                               uint32_t numClipRects)
 {
   Screen *screen = cairo_xlib_surface_get_screen(xsurface);
   Colormap colormap;
   Visual* visual;
   if (!gfxXlibSurface::GetColormapAndVisual(xsurface, &colormap, &visual)) {
     NS_ERROR("Failed to get visual and colormap");
     return NS_ERROR_UNEXPECTED;
@@ -2607,17 +2607,17 @@ nsPluginInstanceOwner::Renderer::DrawWit
   if (mWindow->clipRect.left    != newClipRect.left   ||
       mWindow->clipRect.top     != newClipRect.top    ||
       mWindow->clipRect.right   != newClipRect.right  ||
       mWindow->clipRect.bottom  != newClipRect.bottom) {
     mWindow->clipRect = newClipRect;
     doupdatewindow = true;
   }
 
-  NPSetWindowCallbackStruct* ws_info = 
+  NPSetWindowCallbackStruct* ws_info =
     static_cast<NPSetWindowCallbackStruct*>(mWindow->ws_info);
 #ifdef MOZ_X11
   if (ws_info->visual != visual || ws_info->colormap != colormap) {
     ws_info->visual = visual;
     ws_info->colormap = colormap;
     ws_info->depth = gfxXlibSurface::DepthOfVisual(screen, visual);
     doupdatewindow = true;
   }
@@ -2720,32 +2720,32 @@ nsresult nsPluginInstanceOwner::Init(nsI
   mContent->AddEventListener(NS_LITERAL_STRING("dragenter"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("dragover"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("dragleave"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("dragexit"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("dragstart"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("draggesture"), this, true);
   mContent->AddEventListener(NS_LITERAL_STRING("dragend"), this, true);
 
-  return NS_OK; 
+  return NS_OK;
 }
 
 void* nsPluginInstanceOwner::GetPluginPortFromWidget()
 {
 //!!! Port must be released for windowless plugins on Windows, because it is HDC !!!
 
   void* result = nullptr;
   if (mWidget) {
 #ifdef XP_WIN
     if (mPluginWindow && (mPluginWindow->type == NPWindowTypeDrawable))
       result = mWidget->GetNativeData(NS_NATIVE_GRAPHIC);
     else
 #endif
 #ifdef XP_MACOSX
-    if (GetDrawingModel() == NPDrawingModelCoreGraphics || 
+    if (GetDrawingModel() == NPDrawingModelCoreGraphics ||
         GetDrawingModel() == NPDrawingModelCoreAnimation ||
         GetDrawingModel() == NPDrawingModelInvalidatingCoreAnimation)
       result = mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT_CG);
     else
 #endif
       result = mWidget->GetNativeData(NS_NATIVE_PLUGIN_PORT);
   }
   return result;
@@ -2761,23 +2761,23 @@ void nsPluginInstanceOwner::ReleasePlugi
 #endif
 }
 
 NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void)
 {
   NS_ENSURE_TRUE(mPluginWindow, NS_ERROR_NULL_POINTER);
 
   nsresult rv = NS_ERROR_FAILURE;
-  
+
   // Can't call this twice!
   if (mWidget) {
     NS_WARNING("Trying to create a plugin widget twice!");
     return NS_ERROR_FAILURE;
   }
-  
+
   bool windowless = false;
   mInstance->IsWindowless(&windowless);
   if (!windowless
 // Mac plugins use the widget for setting up the plugin window and event handling.
 // We want to use the puppet widgets there with e10s
 #ifndef XP_MACOSX
       && !nsIWidget::UsePuppetWidgets()
 #endif
@@ -2839,33 +2839,33 @@ NS_IMETHODIMP nsPluginInstanceOwner::Cre
 
     // this needs to be a HDC according to the spec, but I do
     // not see the right way to release it so let's postpone
     // passing HDC till paint event when it is really
     // needed. Change spec?
     mPluginWindow->window = nullptr;
 #ifdef MOZ_X11
     // Fill in the display field.
-    NPSetWindowCallbackStruct* ws_info = 
+    NPSetWindowCallbackStruct* ws_info =
     static_cast<NPSetWindowCallbackStruct*>(mPluginWindow->ws_info);
     ws_info->display = DefaultXDisplay();
-    
+
     nsAutoCString description;
     GetPluginDescription(description);
     NS_NAMED_LITERAL_CSTRING(flash10Head, "Shockwave Flash 10.");
     mFlash10Quirks = StringBeginsWith(description, flash10Head);
 #endif
   } else if (mWidget) {
     // mPluginWindow->type is used in |GetPluginPort| so it must
     // be initialized first
     mPluginWindow->type = NPWindowTypeWindow;
     mPluginWindow->window = GetPluginPortFromWidget();
     // tell the plugin window about the widget
     mPluginWindow->SetPluginWidget(mWidget);
-    
+
     // tell the widget about the current plugin instance owner.
     nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
     if (pluginWidget) {
       pluginWidget->SetPluginInstanceOwner(this);
     }
   }
 
   mWidgetCreationComplete = true;
--- a/dom/plugins/ipc/PluginInstanceParent.cpp
+++ b/dom/plugins/ipc/PluginInstanceParent.cpp
@@ -578,17 +578,17 @@ PluginInstanceParent::RecvShow(const NPR
         RefPtr<MacIOSurface> newIOSurface =
           MacIOSurface::LookupSurface(iodesc.surfaceId(),
                                       iodesc.contentsScaleFactor());
 
         if (!newIOSurface) {
             NS_WARNING("Got bad IOSurfaceDescriptor in RecvShow");
             return false;
         }
-      
+
         if (mFrontIOSurface)
             *prevSurface = IOSurfaceDescriptor(mFrontIOSurface->GetIOSurfaceID(),
                                                mFrontIOSurface->GetContentsScaleFactor());
         else
             *prevSurface = null_t();
 
         mFrontIOSurface = newIOSurface;
 
@@ -619,17 +619,17 @@ PluginInstanceParent::RecvShow(const NPR
         // referencing it.
 #ifdef MOZ_X11
         if (mFrontSurface->GetType() == gfxSurfaceType::Xlib) {
             // Finish with the surface and XSync here to ensure the server has
             // finished operations on the surface before the plugin starts
             // scribbling on it again, or worse, destroys it.
             mFrontSurface->Finish();
             FinishX(DefaultXDisplay());
-        } else 
+        } else
 #endif
         {
             mFrontSurface->Flush();
         }
     }
 
     if (mFrontSurface && gfxSharedImageSurface::IsSharedImage(mFrontSurface))
         *prevSurface = static_cast<gfxSharedImageSurface*>(mFrontSurface.get())->GetShmem();
@@ -692,17 +692,17 @@ PluginInstanceParent::AsyncSetWindow(NPW
     return NS_OK;
 }
 
 nsresult
 PluginInstanceParent::GetImageContainer(ImageContainer** aContainer)
 {
 #ifdef XP_MACOSX
     MacIOSurface* ioSurface = nullptr;
-  
+
     if (mFrontIOSurface) {
       ioSurface = mFrontIOSurface;
     } else if (mIOSurface) {
       ioSurface = mIOSurface;
     }
 
     if (!mFrontSurface && !ioSurface)
 #else
@@ -996,34 +996,34 @@ PluginInstanceParent::NPP_SetWindow(cons
 
 #if defined(XP_MACOSX)
     double floatScaleFactor = 1.0;
     mNPNIface->getvalue(mNPP, NPNVcontentsScaleFactor, &floatScaleFactor);
     int scaleFactor = ceil(floatScaleFactor);
     window.contentsScaleFactor = floatScaleFactor;
 
     if (mShWidth != window.width * scaleFactor || mShHeight != window.height * scaleFactor) {
-        if (mDrawingModel == NPDrawingModelCoreAnimation || 
+        if (mDrawingModel == NPDrawingModelCoreAnimation ||
             mDrawingModel == NPDrawingModelInvalidatingCoreAnimation) {
             mIOSurface = MacIOSurface::CreateIOSurface(window.width, window.height,
                                                        floatScaleFactor);
         } else if (uint32_t(mShWidth * mShHeight) !=
                    window.width * scaleFactor * window.height * scaleFactor) {
             if (mShWidth != 0 && mShHeight != 0) {
                 DeallocShmem(mShSurface);
                 mShWidth = 0;
                 mShHeight = 0;
             }
 
             if (window.width != 0 && window.height != 0) {
                 if (!AllocShmem(window.width * scaleFactor * window.height*4 * scaleFactor,
                                 SharedMemory::TYPE_BASIC, &mShSurface)) {
                     PLUGIN_LOG_DEBUG(("Shared memory could not be allocated."));
                     return NPERR_GENERIC_ERROR;
-                } 
+                }
             }
         }
         mShWidth = window.width * scaleFactor;
         mShHeight = window.height * scaleFactor;
     }
 #endif
 
 #if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX)
@@ -1275,31 +1275,31 @@ PluginInstanceParent::NPP_HandleEvent(vo
 #ifdef XP_MACOSX
     if (npevent->type == NPCocoaEventDrawRect) {
         if (mDrawingModel == NPDrawingModelCoreAnimation ||
             mDrawingModel == NPDrawingModelInvalidatingCoreAnimation) {
             if (!mIOSurface) {
                 NS_ERROR("No IOSurface allocated.");
                 return false;
             }
-            if (!CallNPP_HandleEvent_IOSurface(npremoteevent, 
-                                               mIOSurface->GetIOSurfaceID(), 
-                                               &handled)) 
+            if (!CallNPP_HandleEvent_IOSurface(npremoteevent,
+                                               mIOSurface->GetIOSurfaceID(),
+                                               &handled))
                 return false; // no good way to handle errors here...
 
             CGContextRef cgContext = npevent->data.draw.context;
             if (!mShColorSpace) {
                 mShColorSpace = CreateSystemColorSpace();
             }
             if (!mShColorSpace) {
                 PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
                 return false;
             }
             if (cgContext) {
-                nsCARenderer::DrawSurfaceToCGContext(cgContext, mIOSurface, 
+                nsCARenderer::DrawSurfaceToCGContext(cgContext, mIOSurface,
                                                      mShColorSpace,
                                                      npevent->data.draw.x,
                                                      npevent->data.draw.y,
                                                      npevent->data.draw.width,
                                                      npevent->data.draw.height);
             }
             return true;
         } else if (mFrontIOSurface) {
@@ -1307,17 +1307,17 @@ PluginInstanceParent::NPP_HandleEvent(vo
             if (!mShColorSpace) {
                 mShColorSpace = CreateSystemColorSpace();
             }
             if (!mShColorSpace) {
                 PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
                 return false;
             }
             if (cgContext) {
-                nsCARenderer::DrawSurfaceToCGContext(cgContext, mFrontIOSurface, 
+                nsCARenderer::DrawSurfaceToCGContext(cgContext, mFrontIOSurface,
                                                      mShColorSpace,
                                                      npevent->data.draw.x,
                                                      npevent->data.draw.y,
                                                      npevent->data.draw.width,
                                                      npevent->data.draw.height);
             }
             return true;
         } else {
@@ -1325,51 +1325,51 @@ PluginInstanceParent::NPP_HandleEvent(vo
                 PLUGIN_LOG_DEBUG(("NPCocoaEventDrawRect on window of size 0."));
                 return false;
             }
             if (!mShSurface.IsReadable()) {
                 PLUGIN_LOG_DEBUG(("Shmem is not readable."));
                 return false;
             }
 
-            if (!CallNPP_HandleEvent_Shmem(npremoteevent, mShSurface, 
-                                           &handled, &mShSurface)) 
+            if (!CallNPP_HandleEvent_Shmem(npremoteevent, mShSurface,
+                                           &handled, &mShSurface))
                 return false; // no good way to handle errors here...
 
             if (!mShSurface.IsReadable()) {
                 PLUGIN_LOG_DEBUG(("Shmem not returned. Either the plugin crashed "
                                   "or we have a bug."));
                 return false;
             }
 
             char* shContextByte = mShSurface.get<char>();
 
             if (!mShColorSpace) {
                 mShColorSpace = CreateSystemColorSpace();
             }
             if (!mShColorSpace) {
                 PLUGIN_LOG_DEBUG(("Could not allocate ColorSpace."));
                 return false;
-            } 
-            CGContextRef shContext = ::CGBitmapContextCreate(shContextByte, 
-                                    mShWidth, mShHeight, 8, 
-                                    mShWidth*4, mShColorSpace, 
-                                    kCGImageAlphaPremultipliedFirst | 
+            }
+            CGContextRef shContext = ::CGBitmapContextCreate(shContextByte,
+                                    mShWidth, mShHeight, 8,
+                                    mShWidth*4, mShColorSpace,
+                                    kCGImageAlphaPremultipliedFirst |
                                     kCGBitmapByteOrder32Host);
             if (!shContext) {
                 PLUGIN_LOG_DEBUG(("Could not allocate CGBitmapContext."));
                 return false;
             }
 
             CGImageRef shImage = ::CGBitmapContextCreateImage(shContext);
             if (shImage) {
                 CGContextRef cgContext = npevent->data.draw.context;
-     
-                ::CGContextDrawImage(cgContext, 
-                                     CGRectMake(0,0,mShWidth,mShHeight), 
+
+                ::CGContextDrawImage(cgContext,
+                                     CGRectMake(0,0,mShWidth,mShHeight),
                                      shImage);
                 ::CGImageRelease(shImage);
             } else {
                 ::CGContextRelease(shContext);
                 return false;
             }
             ::CGContextRelease(shContext);
             return true;
@@ -1718,17 +1718,17 @@ PluginInstanceParent::AnswerNPN_InitAsyn
             *result = true;
             return true;
         }
 #ifdef XP_WIN
     case NPDrawingModelAsyncWindowsDXGISurface: {
             ID3D10Device1 *device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
 
             nsRefPtr<ID3D10Texture2D> texture;
-            
+
             CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, size.width, size.height, 1, 1);
             desc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
             desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
             if (FAILED(device->CreateTexture2D(&desc, nullptr, getter_AddRefs(texture)))) {
                 *result = false;
                 return true;
             }
 
@@ -1739,17 +1739,17 @@ PluginInstanceParent::AnswerNPN_InitAsyn
             }
 
             HANDLE sharedHandle;
 
             if (FAILED(resource->GetSharedHandle(&sharedHandle))) {
                 *result = false;
                 return true;
             }
-            
+
             surfData->size() = size;
             surfData->data() = sharedHandle;
             surfData->format() = format;
 
             mTextureMap.Put(sharedHandle, texture);
             *result = true;
         }
 #endif
@@ -1793,17 +1793,17 @@ PluginInstanceParent::RecvReleaseDXGISha
 #if defined(OS_WIN)
 
 /*
   plugin focus changes between processes
 
   focus from dom -> child:
     Focus manager calls on widget to set the focus on the window.
     We pick up the resulting wm_setfocus event here, and forward
-    that over ipc to the child which calls set focus on itself. 
+    that over ipc to the child which calls set focus on itself.
 
   focus from child -> focus manager:
     Child picks up the local wm_setfocus and sends it via ipc over
     here. We then post a custom event to widget/windows/nswindow
     which fires off a gui event letting the browser know.
 */
 
 static const wchar_t kPluginInstanceParentProperty[] =
@@ -1849,17 +1849,17 @@ PluginInstanceParent::PluginWindowHookPr
 void
 PluginInstanceParent::SubclassPluginWindow(HWND aWnd)
 {
     NS_ASSERTION(!(mPluginHWND && aWnd != mPluginHWND),
       "PluginInstanceParent::SubclassPluginWindow hwnd is not our window!");
 
     if (!mPluginHWND) {
         mPluginHWND = aWnd;
-        mPluginWndProc = 
+        mPluginWndProc =
             (WNDPROC)::SetWindowLongPtrA(mPluginHWND, GWLP_WNDPROC,
                          reinterpret_cast<LONG_PTR>(PluginWindowHookProc));
         DebugOnly<bool> bRes = ::SetPropW(mPluginHWND, kPluginInstanceParentProperty, this);
         NS_ASSERTION(mPluginWndProc,
           "PluginInstanceParent::SubclassPluginWindow failed to set subclass!");
         NS_ASSERTION(bRes,
           "PluginInstanceParent::SubclassPluginWindow failed to set prop!");
    }
@@ -1881,23 +1881,23 @@ PluginInstanceParent::UnsubclassPluginWi
 
 /* windowless drawing helpers */
 
 /*
  * Origin info:
  *
  * windowless, offscreen:
  *
- * WM_WINDOWPOSCHANGED: origin is relative to container 
+ * WM_WINDOWPOSCHANGED: origin is relative to container
  * setwindow: origin is 0,0
  * WM_PAINT: origin is 0,0
  *
  * windowless, native:
  *
- * WM_WINDOWPOSCHANGED: origin is relative to container 
+ * WM_WINDOWPOSCHANGED: origin is relative to container
  * setwindow: origin is relative to container
  * WM_PAINT: origin is relative to container
  *
  * PluginInstanceParent:
  *
  * painting: mPluginPort (nsIntRect, saved in SetWindow)
  */
 
--- a/dom/tests/mochitest/bugs/test_bug291377.html
+++ b/dom/tests/mochitest/bugs/test_bug291377.html
@@ -21,20 +21,20 @@ https://bugzilla.mozilla.org/show_bug.cg
 var threw = true;
 try {
   window.location.port = -2;
   threw = false;
 } catch (e) {
   /* Check that we can touch various properties */
   isnot(e.lineNumber, undefined, "Unexpected line number"); //This line number is dependent on the implementation of the SpecialPowers API
   is(e.name, "NS_ERROR_MALFORMED_URI", "Unexpected exception name");
-  isnot(e.message, "", "Should have a message");
+  ise(e.message, "", "Should not have a message for this case");
   isnot(e.result, 0, "Should have a result");
   
   is(e.result, SpecialPowers.Cr.NS_ERROR_MALFORMED_URI);
 }
 
-is(threw, true, "Not enough arguments to a call should throw");
+is(threw, true, "We need a different testcase for XPConnect exceptions?");
 </script>
 </pre>
 </body>
 </html>
 
--- a/dom/tests/mochitest/bugs/test_bug541530.html
+++ b/dom/tests/mochitest/bugs/test_bug541530.html
@@ -45,38 +45,39 @@ try {
     ok(false, "should not be able to defineGetter(window.location)");
 } catch (e) {
 }
 
 try {
     this.__defineGetter__.call(window.location, 'href', function(){});
     ok(false, "shouldn't be able to override location.href");
 } catch (e) {
-    ok(/shadow/.exec(e.message), "Should be caught by the anti-shadow mechanism.");
+    ok(/shadow/.exec(e.message) ||
+       /can't redefine non-configurable/.exec(e.message),
+       "Should be caught by the anti-shadow mechanism.");
 }
 
 // Try deleting the property.
 delete window.location.href;
 ok(typeof window.location.href !== 'undefined',
    "shouldn't be able to delete the inherited property");
 delete Object.getPrototypeOf(window.location).href;
 ok(typeof window.location.href !== 'undefined',
    "shouldn't be able to delete the property off of the prototype");
 
-try {
-    this.__defineGetter__.call(Object.getPrototypeOf(window.location), 'href', function(){});
-    ok(false, "shouldn't be able to use the prototype");
-} catch (e) {
-}
+this.__defineGetter__.call(Object.getPrototypeOf(window.location), 'href', function(){});
+ok(true, "should be able to define things on the prototype");
 
 try {
     this.__defineSetter__.call(window.location, 'href', function(){});
     ok(false, "overrode a setter for location.href?");
 } catch (e) {
-    ok(/shadow/.exec(e.message), "Should be caught by the anti-shadow mechanism.");
+    ok(/shadow/.exec(e.message) ||
+       /can't redefine non-configurable/.exec(e.message),
+       "Should be caught by the anti-shadow mechanism.");
 }
 
 try {
     this.__defineGetter__.call(document, 'location', function(){});
     ok(false, "shouldn't be able to override document.location");
 } catch (e) {
 }
 
--- a/dom/tests/mochitest/webapps/head.js
+++ b/dom/tests/mochitest/webapps/head.js
@@ -1,11 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
 function runAll(steps) {
   SimpleTest.waitForExplicitFinish();
 
   /**
    * On Mac, apps aren't considered launchable right after they've been
    * installed because the OS takes some time to detect them (so
    * nsIMacWebAppUtils::pathForAppWithIdentifier() returns null).
    * That causes methods like mgmt.getAll() to exclude the app from their
@@ -41,8 +45,49 @@ function confirmNextInstall() {
                    PopupNotifications.panel;
 
   function onPopupShown() {
     popupPanel.removeEventListener("popupshown", onPopupShown, false);
     SpecialPowers.wrap(this).childNodes[0].button.doCommand();
   }
   popupPanel.addEventListener("popupshown", onPopupShown, false);
 }
+
+// We need to mock the Alerts service, otherwise the alert that is shown
+// at the end of an installation makes the test leak the app's icon.
+
+const CID = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID();
+const ALERTS_SERVICE_CONTRACT_ID = "@mozilla.org/alerts-service;1";
+const ALERTS_SERVICE_CID = Components.ID(Cc[ALERTS_SERVICE_CONTRACT_ID].number);
+
+var AlertsService = {
+  classID: Components.ID(CID),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIFactory,
+                                         Ci.nsIAlertsService]),
+
+  createInstance: function(aOuter, aIID) {
+    if (aOuter) {
+      throw Cr.NS_ERROR_NO_AGGREGATION;
+    }
+
+    return this.QueryInterface(aIID);
+  },
+
+  init: function() {
+    Components.manager.nsIComponentRegistrar.registerFactory(this.classID,
+      "", ALERTS_SERVICE_CONTRACT_ID, this);
+  },
+
+  restore: function() {
+    Components.manager.nsIComponentRegistrar.registerFactory(ALERTS_SERVICE_CID,
+      "", ALERTS_SERVICE_CONTRACT_ID, null);
+  },
+
+  showAlertNotification: function() {
+  },
+};
+
+AlertsService.init();
+
+SimpleTest.registerCleanupFunction(() => {
+  AlertsService.restore();
+});
--- a/dom/tests/mochitest/webapps/test_bug_771294.xul
+++ b/dom/tests/mochitest/webapps/test_bug_771294.xul
@@ -12,19 +12,16 @@
   <script type="application/javascript" src="head.js"/>
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=771294"
      target="_blank">Mozilla Bug 771294</a>
   </body>
 
 <script>
-const Ci = Components.interfaces;
-const Cu = Components.utils;
-
 SimpleTest.waitForExplicitFinish();
 
 SpecialPowers.setAllAppsLaunchable(true);
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/PopupNotifications.jsm");
 
 let blocked = true;
--- a/dom/tests/mochitest/webapps/test_getNotInstalled.xul
+++ b/dom/tests/mochitest/webapps/test_getNotInstalled.xul
@@ -13,17 +13,16 @@
   <!-- test results are displayed in the html:body -->
   <body xmlns="http://www.w3.org/1999/xhtml">
   <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=741549"
      target="_blank">Mozilla Bug 781379</a>
   </body>
 
 <script type="application/javascript;version=1.8">
 
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 Cu.import("resource://gre/modules/Webapps.jsm");
 
 // We use a different origin than other webapps test files because we compare
 // the number of apps before and after installing this one; and if a test file
 // installs our app and then doesn't uninstall it (f.e. because it isn't written
 // to clean up after itself, or because it throws an exception or times out),
 // then this test will *reinstall* the app, and the number of apps won't change,
 // which will look like a failure in this test although it's actually a failure
--- a/dom/webidl/Location.webidl
+++ b/dom/webidl/Location.webidl
@@ -6,16 +6,20 @@
  * The origin of this IDL file is
  * http://www.whatwg.org/specs/web-apps/current-work/#the-location-interface
  *
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
-// No support for [Unforgeable] on interfaces yet
-//[Unforgeable]
+[Unforgeable]
 interface Location {
+  [Throws]
   void assign(DOMString url);
+  [Throws, CrossOriginCallable]
   void replace(DOMString url);
-  void reload();
+  // XXXbz there is no forceget argument in the spec!  See bug 1037721.
+  [Throws]
+  void reload(optional boolean forceget = false);
 };
-Location implements URLUtils;
+// No support for .searchParams on Location yet.  See bug 1037715.
+Location implements URLUtilsNoSearchParams;
--- a/dom/webidl/ServiceWorkerContainer.webidl
+++ b/dom/webidl/ServiceWorkerContainer.webidl
@@ -39,15 +39,19 @@ interface ServiceWorkerContainer {
   attribute EventHandler onerror;
 };
 
 // Testing only.
 [ChromeOnly, Pref="dom.serviceWorkers.testing.enabled"]
 partial interface ServiceWorkerContainer {
   [Throws]
   Promise clearAllServiceWorkerData();
+
+  [Throws]
+  DOMString getScopeForUrl(DOMString url);
+
   [Throws]
   DOMString getControllingWorkerScriptURLForPath(DOMString path);
 };
 
 dictionary RegistrationOptionList {
   DOMString scope = "/*";
 };
--- a/dom/webidl/SubtleCrypto.webidl
+++ b/dom/webidl/SubtleCrypto.webidl
@@ -62,16 +62,22 @@ dictionary AesGcmParams : Algorithm {
   CryptoOperationData additionalData;
   [EnforceRange] octet tagLength;
 };
 
 dictionary HmacImportParams : Algorithm {
   AlgorithmIdentifier hash;
 };
 
+dictionary Pbkdf2Params : Algorithm {
+  CryptoOperationData salt;
+  [EnforceRange] unsigned long iterations;
+  AlgorithmIdentifier hash;
+};
+
 dictionary RsaHashedImportParams {
   AlgorithmIdentifier hash;
 };
 
 dictionary AesKeyGenParams : Algorithm {
   [EnforceRange] unsigned short length;
 };
 
--- a/dom/webidl/URLUtils.webidl
+++ b/dom/webidl/URLUtils.webidl
@@ -9,30 +9,46 @@
  * To the extent possible under law, the editors have waived all copyright
  * and related or neighboring rights to this work. In addition, as of 17
  * February 2013, the editors have made this specification available under
  * the Open Web Foundation Agreement Version 1.0, which is available at
  * http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0.
  */
 
 [NoInterfaceObject]
-interface URLUtils {
-  [SetterThrows]
+interface URLUtilsNoSearchParams {
   // Bug 824857: no support for stringifier attributes yet.
   //  stringifier attribute DOMString href;
+  [Throws, CrossOriginWritable=Location]
            attribute DOMString href;
+  [Throws]
   readonly attribute DOMString origin;
 
+  [Throws]
            attribute DOMString protocol;
+  [Throws]
            attribute DOMString username;
+  [Throws]
            attribute DOMString password;
+  [Throws]
            attribute DOMString host;
+  [Throws]
            attribute DOMString hostname;
+  [Throws]
            attribute DOMString port;
+  [Throws]
            attribute DOMString pathname;
+  [Throws]
            attribute DOMString search;
-           attribute URLSearchParams searchParams;
+  // searchParams should go here once Location implements it.  See bug 1037715.
+  [Throws]
            attribute DOMString hash;
 
   // Bug 824857 should remove this.
+  [Throws]
   stringifier;
 };
 
+[NoInterfaceObject]
+interface URLUtils : URLUtilsNoSearchParams
+{
+           attribute URLSearchParams searchParams;
+};
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -132,16 +132,32 @@ already_AddRefed<Promise>
 ServiceWorkerContainer::ClearAllServiceWorkerData(ErrorResult& aRv)
 {
   aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   return nullptr;
 }
 
 // Testing only.
 void
+ServiceWorkerContainer::GetScopeForUrl(const nsAString& aUrl,
+                                       nsString& aScope,
+                                       ErrorResult& aRv)
+{
+  nsresult rv;
+  nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  aRv = swm->GetScopeForUrl(aUrl, aScope);
+}
+
+// Testing only.
+void
 ServiceWorkerContainer::GetControllingWorkerScriptURLForPath(
                                                         const nsAString& aPath,
                                                         nsString& aScriptURL,
                                                         ErrorResult& aRv)
 {
   aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
 }
 } // namespace workers
--- a/dom/workers/ServiceWorkerContainer.h
+++ b/dom/workers/ServiceWorkerContainer.h
@@ -76,16 +76,20 @@ public:
   Ready();
 
   // Testing only.
   already_AddRefed<Promise>
   ClearAllServiceWorkerData(ErrorResult& aRv);
 
   // Testing only.
   void
+  GetScopeForUrl(const nsAString& aUrl, nsString& aScope, ErrorResult& aRv);
+
+  // Testing only.
+  void
   GetControllingWorkerScriptURLForPath(const nsAString& aPath,
                                        nsString& aScriptURL,
                                        ErrorResult& aRv);
 private:
   ~ServiceWorkerContainer()
   {
     // FIXME(nsm): Bug 983497. Unhook from events.
   }
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -362,18 +362,17 @@ public:
     nsCString domain;
     nsresult rv = mScriptURI->GetHost(domain);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       mPromise->MaybeReject(rv);
       return NS_OK;
     }
 
     nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
-    ServiceWorkerManager::ServiceWorkerDomainInfo* domainInfo =
-      swm->mDomainMap.Get(domain);
+    ServiceWorkerManager::ServiceWorkerDomainInfo* domainInfo;
     // XXXnsm: This pattern can be refactored if we end up using it
     // often enough.
     if (!swm->mDomainMap.Get(domain, &domainInfo)) {
       domainInfo = new ServiceWorkerManager::ServiceWorkerDomainInfo;
       swm->mDomainMap.Put(domain, domainInfo);
     }
 
     nsRefPtr<ServiceWorkerRegistration> registration =
@@ -982,16 +981,173 @@ ServiceWorkerManager::CreateServiceWorke
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   serviceWorker.forget(aServiceWorker);
   return rv;
 }
 
+already_AddRefed<ServiceWorkerRegistration>
+ServiceWorkerManager::GetServiceWorkerRegistration(nsPIDOMWindow* aWindow)
+{
+  nsCOMPtr<nsIURI> documentURI = aWindow->GetDocumentURI();
+  return GetServiceWorkerRegistration(documentURI);
+}
+
+already_AddRefed<ServiceWorkerRegistration>
+ServiceWorkerManager::GetServiceWorkerRegistration(nsIDocument* aDoc)
+{
+  nsCOMPtr<nsIURI> documentURI = aDoc->GetDocumentURI();
+  return GetServiceWorkerRegistration(documentURI);
+}
+
+already_AddRefed<ServiceWorkerRegistration>
+ServiceWorkerManager::GetServiceWorkerRegistration(nsIURI* aURI)
+{
+  if (!aURI) {
+    return nullptr;
+  }
+
+  nsCString domain;
+  nsresult rv = aURI->GetHost(domain);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  ServiceWorkerDomainInfo* domainInfo = mDomainMap.Get(domain);
+
+  // XXXnsm: This pattern can be refactored if we end up using it
+  // often enough.
+  if (!domainInfo) {
+    return nullptr;
+  }
+
+  nsCString spec;
+  rv = aURI->GetSpec(spec);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  nsCString scope = FindScopeForPath(domainInfo->mOrderedScopes, spec);
+  if (scope.IsEmpty()) {
+    return nullptr;
+  }
+
+  ServiceWorkerRegistration* registration;
+  domainInfo->mServiceWorkerRegistrations.Get(scope, &registration);
+  // ordered scopes and registrations better be in sync.
+  MOZ_ASSERT(registration);
+
+  return registration;
+}
+
+namespace {
+/*
+ * Returns string without trailing '*'.
+ */
+void ScopeWithoutStar(const nsACString& aScope, nsACString& out)
+{
+  if (aScope.Last() == '*') {
+    out.Assign(StringHead(aScope, aScope.Length() - 1));
+    return;
+  }
+
+  out.Assign(aScope);
+}
+}; // anonymous namespace
+
+/* static */ void
+ServiceWorkerManager::AddScope(nsTArray<nsCString>& aList, const nsACString& aScope)
+{
+  for (uint32_t i = 0; i < aList.Length(); ++i) {
+    const nsCString& current = aList[i];
+
+    // Perfect match!
+    if (aScope.Equals(current)) {
+      return;
+    }
+
+    nsCString withoutStar;
+    ScopeWithoutStar(current, withoutStar);
+    // Edge case of match without '*'.
+    // /foo should be sorted before /foo*.
+    if (aScope.Equals(withoutStar)) {
+      aList.InsertElementAt(i, aScope);
+      return;
+    }
+
+    // /foo/bar* should be before /foo/*
+    // Similarly /foo/b* is between the two.
+    // But is /foo* categorically different?
+    if (StringBeginsWith(aScope, withoutStar)) {
+      // If the new scope is a pattern and the old one is a path, the new one
+      // goes after.  This way Add(/foo) followed by Add(/foo*) ends up with
+      // [/foo, /foo*].
+      if (aScope.Last() == '*' &&
+          withoutStar.Equals(current)) {
+        aList.InsertElementAt(i+1, aScope);
+      } else {
+        aList.InsertElementAt(i, aScope);
+      }
+      return;
+    }
+  }
+
+  aList.AppendElement(aScope);
+}
+
+// aPath can have a '*' at the end, but it is treated literally.
+/* static */ nsCString
+ServiceWorkerManager::FindScopeForPath(nsTArray<nsCString>& aList, const nsACString& aPath)
+{
+  MOZ_ASSERT(aPath.FindChar('*') == -1);
+
+  nsCString match;
+
+  for (uint32_t i = 0; i < aList.Length(); ++i) {
+    const nsCString& current = aList[i];
+    nsCString withoutStar;
+    ScopeWithoutStar(current, withoutStar);
+    if (StringBeginsWith(aPath, withoutStar)) {
+      // If non-pattern match, then check equality.
+      if (current.Last() == '*' ||
+          aPath.Equals(current)) {
+        match = current;
+        break;
+      }
+    }
+  }
+
+  return match;
+}
+
+/* static */ void
+ServiceWorkerManager::RemoveScope(nsTArray<nsCString>& aList, const nsACString& aScope)
+{
+  aList.RemoveElement(aScope);
+}
+
+NS_IMETHODIMP
+ServiceWorkerManager::GetScopeForUrl(const nsAString& aUrl, nsAString& aScope)
+{
+  nsCOMPtr<nsIURI> uri;
+  nsresult rv = NS_NewURI(getter_AddRefs(uri), aUrl, nullptr, nullptr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<ServiceWorkerRegistration> r = GetServiceWorkerRegistration(uri);
+  if (!r) {
+      return NS_ERROR_FAILURE;
+  }
+
+  aScope = NS_ConvertUTF8toUTF16(r->mScope);
+  return NS_OK;
+}
 NS_IMETHODIMP
 ServiceWorkerManager::CreateServiceWorker(const nsACString& aScriptSpec,
                                           const nsACString& aScope,
                                           ServiceWorker** aServiceWorker)
 {
   AssertIsOnMainThread();
 
   WorkerPrivate::LoadInfo info;
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -193,16 +193,25 @@ public:
 
   /*
    * This struct is used for passive ServiceWorker management.
    * Actively running ServiceWorkers use the SharedWorker infrastructure in
    * RuntimeService for execution and lifetime management.
    */
   struct ServiceWorkerDomainInfo
   {
+    // Ordered list of scopes for glob matching.
+    // Each entry is an absolute URL representing the scope.
+    //
+    // An array is used for now since the number of controlled scopes per
+    // domain is expected to be relatively low. If that assumption was proved
+    // wrong this should be replaced with a better structure to avoid the
+    // memmoves associated with inserting stuff in the middle of the array.
+    nsTArray<nsCString> mOrderedScopes;
+
     // Scope to registration.
     nsRefPtrHashtable<nsCStringHashKey, ServiceWorkerRegistration> mServiceWorkerRegistrations;
 
     ServiceWorkerDomainInfo()
     { }
 
     already_AddRefed<ServiceWorkerRegistration>
     GetRegistration(const nsCString& aScope) const
@@ -215,16 +224,17 @@ public:
     ServiceWorkerRegistration*
     CreateNewRegistration(const nsCString& aScope)
     {
       ServiceWorkerRegistration* registration =
         new ServiceWorkerRegistration(aScope);
       // From now on ownership of registration is with
       // mServiceWorkerRegistrations.
       mServiceWorkerRegistrations.Put(aScope, registration);
+      ServiceWorkerManager::AddScope(mOrderedScopes, aScope);
       return registration;
     }
   };
 
   nsClassHashtable<nsCStringHashKey, ServiceWorkerDomainInfo> mDomainMap;
 
   void
   ResolveRegisterPromises(ServiceWorkerRegistration* aRegistration,
@@ -280,16 +290,35 @@ private:
   CreateServiceWorker(const nsACString& aScriptSpec,
                       const nsACString& aScope,
                       ServiceWorker** aServiceWorker);
 
   static PLDHashOperator
   CleanupServiceWorkerInformation(const nsACString& aDomain,
                                   ServiceWorkerDomainInfo* aDomainInfo,
                                   void *aUnused);
+
+  already_AddRefed<ServiceWorkerRegistration>
+  GetServiceWorkerRegistration(nsPIDOMWindow* aWindow);
+
+  already_AddRefed<ServiceWorkerRegistration>
+  GetServiceWorkerRegistration(nsIDocument* aDoc);
+
+  already_AddRefed<ServiceWorkerRegistration>
+  GetServiceWorkerRegistration(nsIURI* aURI);
+
+  static void
+  AddScope(nsTArray<nsCString>& aList, const nsACString& aScope);
+
+  static nsCString
+  FindScopeForPath(nsTArray<nsCString>& aList, const nsACString& aPath);
+
+  static void
+  RemoveScope(nsTArray<nsCString>& aList, const nsACString& aScope);
+
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(ServiceWorkerManager,
                               NS_SERVICEWORKERMANAGER_IMPL_IID);
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
--- a/dom/workers/URL.cpp
+++ b/dom/workers/URL.cpp
@@ -313,62 +313,64 @@ public:
     mWorkerPrivate->AssertIsOnWorkerThread();
   }
 
   bool
   MainThreadRun()
   {
     AssertIsOnMainThread();
 
+    ErrorResult rv;
     switch (mType) {
       case GetterHref:
-        mURLProxy->URL()->GetHref(mValue);
+        mURLProxy->URL()->GetHref(mValue, rv);
         break;
 
       case GetterOrigin:
-        mURLProxy->URL()->GetOrigin(mValue);
+        mURLProxy->URL()->GetOrigin(mValue, rv);
         break;
 
       case GetterProtocol:
-        mURLProxy->URL()->GetProtocol(mValue);
+        mURLProxy->URL()->GetProtocol(mValue, rv);
         break;
 
       case GetterUsername:
-        mURLProxy->URL()->GetUsername(mValue);
+        mURLProxy->URL()->GetUsername(mValue, rv);
         break;
 
       case GetterPassword:
-        mURLProxy->URL()->GetPassword(mValue);
+        mURLProxy->URL()->GetPassword(mValue, rv);
         break;
 
       case GetterHost:
-        mURLProxy->URL()->GetHost(mValue);
+        mURLProxy->URL()->GetHost(mValue, rv);
         break;
 
       case GetterHostname:
-        mURLProxy->URL()->GetHostname(mValue);
+        mURLProxy->URL()->GetHostname(mValue, rv);
         break;
 
       case GetterPort:
-        mURLProxy->URL()->GetPort(mValue);
+        mURLProxy->URL()->GetPort(mValue, rv);
         break;
 
       case GetterPathname:
-        mURLProxy->URL()->GetPathname(mValue);
+        mURLProxy->URL()->GetPathname(mValue, rv);
         break;
 
       case GetterSearch:
-        mURLProxy->URL()->GetSearch(mValue);
+        mURLProxy->URL()->GetSearch(mValue, rv);
         break;
 
       case GetterHash:
-        mURLProxy->URL()->GetHash(mValue);
+        mURLProxy->URL()->GetHash(mValue, rv);
         break;
     }
 
+    MOZ_ASSERT(!rv.Failed());
     return true;
   }
 
 private:
   nsString& mValue;
   GetterType mType;
   nsRefPtr<URLProxy> mURLProxy;
 };
@@ -408,49 +410,49 @@ public:
     AssertIsOnMainThread();
 
     switch (mType) {
       case SetterHref:
         mURLProxy->URL()->SetHref(mValue, mRv);
         break;
 
       case SetterProtocol:
-        mURLProxy->URL()->SetProtocol(mValue);
+        mURLProxy->URL()->SetProtocol(mValue, mRv);
         break;
 
       case SetterUsername:
-        mURLProxy->URL()->SetUsername(mValue);
+        mURLProxy->URL()->SetUsername(mValue, mRv);
         break;
 
       case SetterPassword:
-        mURLProxy->URL()->SetPassword(mValue);
+        mURLProxy->URL()->SetPassword(mValue, mRv);
         break;
 
       case SetterHost:
-        mURLProxy->URL()->SetHost(mValue);
+        mURLProxy->URL()->SetHost(mValue, mRv);
         break;
 
       case SetterHostname:
-        mURLProxy->URL()->SetHostname(mValue);
+        mURLProxy->URL()->SetHostname(mValue, mRv);
         break;
 
       case SetterPort:
-        mURLProxy->URL()->SetPort(mValue);
+        mURLProxy->URL()->SetPort(mValue, mRv);
         break;
 
       case SetterPathname:
-        mURLProxy->URL()->SetPathname(mValue);
+        mURLProxy->URL()->SetPathname(mValue, mRv);
         break;
 
       case SetterSearch:
-        mURLProxy->URL()->SetSearch(mValue);
+        mURLProxy->URL()->SetSearch(mValue, mRv);
         break;
 
       case SetterHash:
-        mURLProxy->URL()->SetHash(mValue);
+        mURLProxy->URL()->SetHash(mValue, mRv);
         break;
     }
 
     return true;
   }
 
 private:
   const nsString mValue;
@@ -542,17 +544,17 @@ URL::~URL()
 
 JSObject*
 URL::WrapObject(JSContext* aCx)
 {
   return URLBinding_workers::Wrap(aCx, this);
 }
 
 void
-URL::GetHref(nsString& aHref) const
+URL::GetHref(nsString& aHref, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHref, aHref,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
@@ -568,216 +570,216 @@ URL::SetHref(const nsAString& aHref, Err
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 
   UpdateURLSearchParams();
 }
 
 void
-URL::GetOrigin(nsString& aOrigin) const
+URL::GetOrigin(nsString& aOrigin, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterOrigin, aOrigin,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::GetProtocol(nsString& aProtocol) const
+URL::GetProtocol(nsString& aProtocol, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterProtocol, aProtocol,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::SetProtocol(const nsAString& aProtocol)
+URL::SetProtocol(const nsAString& aProtocol, ErrorResult& aRv)
 {
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterProtocol,
                        aProtocol, mURLProxy, rv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::GetUsername(nsString& aUsername) const
+URL::GetUsername(nsString& aUsername, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterUsername, aUsername,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::SetUsername(const nsAString& aUsername)
+URL::SetUsername(const nsAString& aUsername, ErrorResult& aRv)
 {
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterUsername,
                        aUsername, mURLProxy, rv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::GetPassword(nsString& aPassword) const
+URL::GetPassword(nsString& aPassword, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterPassword, aPassword,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::SetPassword(const nsAString& aPassword)
+URL::SetPassword(const nsAString& aPassword, ErrorResult& aRv)
 {
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterPassword,
                        aPassword, mURLProxy, rv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::GetHost(nsString& aHost) const
+URL::GetHost(nsString& aHost, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHost, aHost,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::SetHost(const nsAString& aHost)
+URL::SetHost(const nsAString& aHost, ErrorResult& aRv)
 {
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHost,
                        aHost, mURLProxy, rv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::GetHostname(nsString& aHostname) const
+URL::GetHostname(nsString& aHostname, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHostname, aHostname,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::SetHostname(const nsAString& aHostname)
+URL::SetHostname(const nsAString& aHostname, ErrorResult& aRv)
 {
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHostname,
                        aHostname, mURLProxy, rv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::GetPort(nsString& aPort) const
+URL::GetPort(nsString& aPort, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterPort, aPort,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::SetPort(const nsAString& aPort)
+URL::SetPort(const nsAString& aPort, ErrorResult& aRv)
 {
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterPort,
                        aPort, mURLProxy, rv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::GetPathname(nsString& aPathname) const
+URL::GetPathname(nsString& aPathname, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterPathname, aPathname,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::SetPathname(const nsAString& aPathname)
+URL::SetPathname(const nsAString& aPathname, ErrorResult& aRv)
 {
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterPathname,
                        aPathname, mURLProxy, rv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::GetSearch(nsString& aSearch) const
+URL::GetSearch(nsString& aSearch, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterSearch, aSearch,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::SetSearch(const nsAString& aSearch)
+URL::SetSearch(const nsAString& aSearch, ErrorResult& aRv)
 {
   SetSearchInternal(aSearch);
   UpdateURLSearchParams();
 }
 
 void
 URL::SetSearchInternal(const nsAString& aSearch)
 {
@@ -809,29 +811,29 @@ URL::SetSearchParams(URLSearchParams& aS
   mSearchParams->AddObserver(this);
 
   nsString search;
   mSearchParams->Serialize(search);
   SetSearchInternal(search);
 }
 
 void
-URL::GetHash(nsString& aHash) const
+URL::GetHash(nsString& aHash, ErrorResult& aRv) const
 {
   nsRefPtr<GetterRunnable> runnable =
     new GetterRunnable(mWorkerPrivate, GetterRunnable::GetterHash, aHash,
                        mURLProxy);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
   }
 }
 
 void
-URL::SetHash(const nsAString& aHash)
+URL::SetHash(const nsAString& aHash, ErrorResult& aRv)
 {
   ErrorResult rv;
   nsRefPtr<SetterRunnable> runnable =
     new SetterRunnable(mWorkerPrivate, SetterRunnable::SetterHash,
                        aHash, mURLProxy, rv);
 
   if (!runnable->Dispatch(mWorkerPrivate->GetJSContext())) {
     JS_ReportPendingException(mWorkerPrivate->GetJSContext());
@@ -901,17 +903,18 @@ URL::URLSearchParamsUpdated()
   SetSearchInternal(search);
 }
 
 void
 URL::UpdateURLSearchParams()
 {
   if (mSearchParams) {
     nsString search;
-    GetSearch(search);
+    ErrorResult rv;
+    GetSearch(search, rv);
     mSearchParams->ParseInput(NS_ConvertUTF16toUTF8(Substring(search, 1)), this);
   }
 }
 
 void
 URL::CreateSearchParamsIfNeeded()
 {
   if (!mSearchParams) {
--- a/dom/workers/URL.h
+++ b/dom/workers/URL.h
@@ -62,65 +62,65 @@ public:
   static void
   CreateObjectURL(const GlobalObject& aGlobal,
                   JSObject& aArg, const objectURLOptions& aOptions,
                   nsString& aResult, ErrorResult& aRv);
 
   static void
   RevokeObjectURL(const GlobalObject& aGlobal, const nsAString& aUrl);
 
-  void GetHref(nsString& aHref) const;
+  void GetHref(nsString& aHref, ErrorResult& aRv) const;
 
   void SetHref(const nsAString& aHref, ErrorResult& aRv);
 
-  void GetOrigin(nsString& aOrigin) const;
+  void GetOrigin(nsString& aOrigin, ErrorResult& aRv) const;
 
-  void GetProtocol(nsString& aProtocol) const;
+  void GetProtocol(nsString& aProtocol, ErrorResult& aRv) const;
 
-  void SetProtocol(const nsAString& aProtocol);
+  void SetProtocol(const nsAString& aProtocol, ErrorResult& aRv);
 
-  void GetUsername(nsString& aUsername) const;
+  void GetUsername(nsString& aUsername, ErrorResult& aRv) const;
 
-  void SetUsername(const nsAString& aUsername);
+  void SetUsername(const nsAString& aUsername, ErrorResult& aRv);
 
-  void GetPassword(nsString& aPassword) const;
+  void GetPassword(nsString& aPassword, ErrorResult& aRv) const;
 
-  void SetPassword(const nsAString& aPassword);
+  void SetPassword(const nsAString& aPassword, ErrorResult& aRv);
 
-  void GetHost(nsString& aHost) const;
+  void GetHost(nsString& aHost, ErrorResult& aRv) const;
 
-  void SetHost(const nsAString& aHost);
+  void SetHost(const nsAString& aHost, ErrorResult& aRv);
 
-  void GetHostname(nsString& aHostname) const;
+  void GetHostname(nsString& aHostname, ErrorResult& aRv) const;
 
-  void SetHostname(const nsAString& aHostname);
+  void SetHostname(const nsAString& aHostname, ErrorResult& aRv);
 
-  void GetPort(nsString& aPort) const;
+  void GetPort(nsString& aPort, ErrorResult& aRv) const;
 
-  void SetPort(const nsAString& aPort);
+  void SetPort(const nsAString& aPort, ErrorResult& aRv);
 
-  void GetPathname(nsString& aPathname) const;
+  void GetPathname(nsString& aPathname, ErrorResult& aRv) const;
 
-  void SetPathname(const nsAString& aPathname);
+  void SetPathname(const nsAString& aPathname, ErrorResult& aRv);
 
-  void GetSearch(nsString& aSearch) const;
+  void GetSearch(nsString& aSearch, ErrorResult& aRv) const;
 
-  void SetSearch(const nsAString& aSearch);
+  void SetSearch(const nsAString& aSearch, ErrorResult& aRv);
 
   URLSearchParams* SearchParams();
 
   void SetSearchParams(URLSearchParams& aSearchParams);
 
-  void GetHash(nsString& aHost) const;
+  void GetHash(nsString& aHost, ErrorResult& aRv) const;
 
-  void SetHash(const nsAString& aHash);
+  void SetHash(const nsAString& aHash, ErrorResult& aRv);
 
-  void Stringify(nsString& aRetval) const
+  void Stringify(nsString& aRetval, ErrorResult& aRv) const
   {
-    GetHref(aRetval);
+    GetHref(aRetval, aRv);
   }
 
   // IURLSearchParamsObserver
   void URLSearchParamsUpdated() MOZ_OVERRIDE;
 
 private:
   URLProxy* GetURLProxy() const
   {
--- a/dom/workers/test/promise_worker.js
+++ b/dom/workers/test/promise_worker.js
@@ -551,24 +551,16 @@ function promiseRaceValuesArray() {
     runTest();
   }, function(err) {
     ok(false, "Should not fail " + err + ".");
     runTest();
   });
 }
 
 function promiseRacePromiseArray() {
-  function timeoutPromise(n) {
-    return new Promise(function(resolve) {
-      setTimeout(function() {
-        resolve(n);
-      }, n);
-    });
-  }
-
   var arr = [
     new Promise(function(resolve) {
       resolve("first");
     }),
     Promise.resolve("second"),
     new Promise(function() {}),
     new Promise(function(resolve) {
       setTimeout(function() {
@@ -669,27 +661,30 @@ function promiseResolvePromise() {
 function promiseResolveThenableCleanStack() {
   function immed(s) { x++; s(); }
   function incX(){ x++; }
 
   var x = 0;
   var thenable = { then: immed };
   var results = [];
 
-  Promise.resolve(thenable).then(incX);
+  var p = Promise.resolve(thenable).then(incX);
   results.push(x);
 
   // check what happens after all "next cycle" steps
   // have had a chance to complete
   setTimeout(function(){
     results.push(x);
     // Result should be [0, 2] since `thenable` will be called async.
     is(results[0], 0, "Expected thenable to be called asynchronously");
-    is(results[1], 2, "Expected thenable to be called asynchronously");
-    runTest();
+    // See Bug 1023547 comment 13 for why this check has to be gated on p.
+    p.then(function() {
+      is(results[1], 2, "Expected thenable to be called asynchronously");
+      runTest();
+    });
   },1000);
 }
 
 var tests = [
     promiseResolve,
     promiseReject,
     promiseException,
     promiseAsync,
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -4,8 +4,9 @@ support-files =
   worker2.js
   worker3.js
   parse_error_worker.js
   install_event_worker.js
 
 [test_installation_simple.html]
 [test_install_event.html]
 [test_navigator.html]
+[test_scopes.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_scopes.html
@@ -0,0 +1,69 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 984048 - Test scope glob matching.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  function registerWorkers() {
+    return Promise.all([
+      navigator.serviceWorker.register("worker.js", { scope: "*" }),
+      navigator.serviceWorker.register("worker.js", { scope: "sub/dir/*"}),
+      navigator.serviceWorker.register("worker.js", { scope: "sub/dir*" }),
+      navigator.serviceWorker.register("worker.js", { scope: "sub/dir" }),
+      navigator.serviceWorker.register("worker.js", { scope: "sub/dir/a*" }),
+      navigator.serviceWorker.register("worker.js", { scope: "sub*" }),
+    ]);
+  }
+
+  function testScopes() {
+    return new Promise(function(resolve, reject) {
+      var getScope = navigator.serviceWorker.getScopeForUrl.bind(navigator.serviceWorker);
+      var base = new URL(".", document.baseURI);
+
+      function p(s) {
+        return base + s;
+      }
+
+      ok(getScope(p("index.html")) === p("*"), "Scope should match");
+      ok(getScope(p("sua.html")) === p("*"), "Scope should match");
+      ok(getScope(p("sub.html")) === p("sub*"), "Scope should match");
+      ok(getScope(p("sub/dir.html")) === p("sub/dir*"), "Scope should match");
+      ok(getScope(p("sub/dir")) === p("sub/dir"), "Scope should match");
+      ok(getScope(p("sub/dir/foo")) === p("sub/dir/*"), "Scope should match");
+      ok(getScope(p("sub/dir/afoo")) === p("sub/dir/a*"), "Scope should match");
+      resolve(true);
+    });
+  }
+
+  function runTest() {
+    registerWorkers()
+      .then(testScopes)
+      .then(function() {
+        SimpleTest.finish();
+      }).catch(function(e) {
+        ok(false, "Some test failed with error " + e);
+        SimpleTest.finish();
+      });
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true]
+  ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
--- a/gfx/2d/MacIOSurface.cpp
+++ b/gfx/2d/MacIOSurface.cpp
@@ -38,16 +38,17 @@ void*                         MacIOSurfa
 void*                         MacIOSurfaceLib::sCoreGraphicsFramework;
 IOSurfaceCreateFunc           MacIOSurfaceLib::sCreate;
 IOSurfaceGetIDFunc            MacIOSurfaceLib::sGetID;
 IOSurfaceLookupFunc           MacIOSurfaceLib::sLookup;
 IOSurfaceGetBaseAddressFunc   MacIOSurfaceLib::sGetBaseAddress;
 IOSurfaceGetWidthFunc         MacIOSurfaceLib::sWidth;
 IOSurfaceGetHeightFunc        MacIOSurfaceLib::sHeight;
 IOSurfaceGetBytesPerRowFunc   MacIOSurfaceLib::sBytesPerRow;
+IOSurfaceGetPropertyMaximumFunc   MacIOSurfaceLib::sGetPropertyMaximum;
 IOSurfaceLockFunc             MacIOSurfaceLib::sLock;
 IOSurfaceUnlockFunc           MacIOSurfaceLib::sUnlock;
 CGLTexImageIOSurface2DFunc    MacIOSurfaceLib::sTexImage;
 IOSurfaceContextCreateFunc    MacIOSurfaceLib::sIOSurfaceContextCreate;
 IOSurfaceContextCreateImageFunc   MacIOSurfaceLib::sIOSurfaceContextCreateImage;
 IOSurfaceContextGetSurfaceFunc    MacIOSurfaceLib::sIOSurfaceContextGetSurface;
 unsigned int                  (*MacIOSurfaceLib::sCGContextGetTypePtr) (CGContextRef) = nullptr;
 
@@ -89,16 +90,20 @@ size_t MacIOSurfaceLib::IOSurfaceGetWidt
 size_t MacIOSurfaceLib::IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr) {
   return sHeight(aIOSurfacePtr);
 }
 
 size_t MacIOSurfaceLib::IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr) {
   return sBytesPerRow(aIOSurfacePtr);
 }
 
+size_t MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(CFStringRef property) {
+  return sGetPropertyMaximum(property);
+}
+
 IOReturn MacIOSurfaceLib::IOSurfaceLock(IOSurfacePtr aIOSurfacePtr, 
                                        uint32_t options, uint32_t *seed) {
   return sLock(aIOSurfacePtr, options, seed);
 }
 
 IOReturn MacIOSurfaceLib::IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr, 
                                          uint32_t options, uint32_t *seed) {
   return sUnlock(aIOSurfacePtr, options, seed);
@@ -172,32 +177,33 @@ void MacIOSurfaceLib::LoadLibrary() {
   kPropBytesPerElem = GetIOConst("kIOSurfaceBytesPerElement");
   kPropBytesPerRow = GetIOConst("kIOSurfaceBytesPerRow");
   kPropIsGlobal = GetIOConst("kIOSurfaceIsGlobal");
   sCreate = GET_IOSYM(sCreate, "IOSurfaceCreate");
   sGetID  = GET_IOSYM(sGetID,  "IOSurfaceGetID");
   sWidth = GET_IOSYM(sWidth, "IOSurfaceGetWidth");
   sHeight = GET_IOSYM(sHeight, "IOSurfaceGetHeight");
   sBytesPerRow = GET_IOSYM(sBytesPerRow, "IOSurfaceGetBytesPerRow");
+  sGetPropertyMaximum = GET_IOSYM(sGetPropertyMaximum, "IOSurfaceGetPropertyMaximum");
   sLookup = GET_IOSYM(sLookup, "IOSurfaceLookup");
   sLock = GET_IOSYM(sLock, "IOSurfaceLock");
   sUnlock = GET_IOSYM(sUnlock, "IOSurfaceUnlock");
   sGetBaseAddress = GET_IOSYM(sGetBaseAddress, "IOSurfaceGetBaseAddress");
   sTexImage = GET_CGLSYM(sTexImage, "CGLTexImageIOSurface2D");
   sCGContextGetTypePtr = (unsigned int (*)(CGContext*))dlsym(RTLD_DEFAULT, "CGContextGetType");
 
   // Optional symbols
   sIOSurfaceContextCreate = GET_CGSYM(sIOSurfaceContextCreate, "CGIOSurfaceContextCreate");
   sIOSurfaceContextCreateImage = GET_CGSYM(sIOSurfaceContextCreateImage, "CGIOSurfaceContextCreateImage");
   sIOSurfaceContextGetSurface = GET_CGSYM(sIOSurfaceContextGetSurface, "CGIOSurfaceContextGetSurface");
 
   if (!sCreate || !sGetID || !sLookup || !sTexImage || !sGetBaseAddress ||
       !kPropWidth || !kPropHeight || !kPropBytesPerElem || !kPropIsGlobal ||
       !sLock || !sUnlock || !sWidth || !sHeight || !kPropBytesPerRow ||
-      !sBytesPerRow) {
+      !sBytesPerRow || !sGetPropertyMaximum) {
     CloseLibrary();
   }
 }
 
 void MacIOSurfaceLib::CloseLibrary() {
   if (sIOSurfaceFramework) {
     dlclose(sIOSurfaceFramework);
   }
@@ -220,16 +226,19 @@ TemporaryRef<MacIOSurface> MacIOSurface:
 
   CFMutableDictionaryRef props = ::CFDictionaryCreateMutable(
                       kCFAllocatorDefault, 4,
                       &kCFTypeDictionaryKeyCallBacks,
                       &kCFTypeDictionaryValueCallBacks);
   if (!props)
     return nullptr;
 
+  MOZ_ASSERT((size_t)aWidth <= GetMaxWidth());
+  MOZ_ASSERT((size_t)aHeight <= GetMaxHeight());
+
   int32_t bytesPerElem = 4;
   size_t intScaleFactor = ceil(aContentsScaleFactor);
   aWidth *= intScaleFactor;
   aHeight *= intScaleFactor;
   CFNumberRef cfWidth = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aWidth);
   CFNumberRef cfHeight = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &aHeight);
   CFNumberRef cfBytesPerElem = ::CFNumberCreate(nullptr, kCFNumberSInt32Type, &bytesPerElem);
   ::CFDictionaryAddValue(props, MacIOSurfaceLib::kPropWidth,
@@ -290,16 +299,24 @@ size_t MacIOSurface::GetWidth() {
   return GetDevicePixelWidth() / intScaleFactor;
 }
 
 size_t MacIOSurface::GetHeight() {
   size_t intScaleFactor = ceil(mContentsScaleFactor);
   return GetDevicePixelHeight() / intScaleFactor;
 }
 
+/*static*/ size_t MacIOSurface::GetMaxWidth() {
+  return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropWidth);
+}
+
+/*static*/ size_t MacIOSurface::GetMaxHeight() {
+  return MacIOSurfaceLib::IOSurfaceGetPropertyMaximum(MacIOSurfaceLib::kPropHeight);
+}
+
 size_t MacIOSurface::GetDevicePixelWidth() {
   return MacIOSurfaceLib::IOSurfaceGetWidth(mIOSurfacePtr);
 }
 
 size_t MacIOSurface::GetDevicePixelHeight() {
   return MacIOSurfaceLib::IOSurfaceGetHeight(mIOSurfacePtr);
 }
 
--- a/gfx/2d/MacIOSurface.h
+++ b/gfx/2d/MacIOSurface.h
@@ -20,30 +20,30 @@ typedef IOReturn (*IOSurfaceLockFunc) (C
                                        uint32_t *seed);
 typedef IOReturn (*IOSurfaceUnlockFunc) (CFTypeRef io_surface, 
                                          uint32_t options, 
                                          uint32_t *seed);
 typedef void* (*IOSurfaceGetBaseAddressFunc) (CFTypeRef io_surface);
 typedef size_t (*IOSurfaceGetWidthFunc) (IOSurfacePtr io_surface);
 typedef size_t (*IOSurfaceGetHeightFunc) (IOSurfacePtr io_surface);
 typedef size_t (*IOSurfaceGetBytesPerRowFunc) (IOSurfacePtr io_surface);
+typedef size_t (*IOSurfaceGetPropertyMaximumFunc) (CFStringRef property);
 typedef CGLError (*CGLTexImageIOSurface2DFunc) (CGLContextObj ctxt,
                              GLenum target, GLenum internalFormat,
                              GLsizei width, GLsizei height,
                              GLenum format, GLenum type,
                              IOSurfacePtr ioSurface, GLuint plane);
 typedef CGContextRef (*IOSurfaceContextCreateFunc)(CFTypeRef io_surface,
                              unsigned width, unsigned height,
                              unsigned bitsPerComponent, unsigned bytes,
                              CGColorSpaceRef colorSpace, CGBitmapInfo bitmapInfo);
 typedef CGImageRef (*IOSurfaceContextCreateImageFunc)(CGContextRef ref);
 typedef IOSurfacePtr (*IOSurfaceContextGetSurfaceFunc)(CGContextRef ref);
 
 
-
 #import <OpenGL/OpenGL.h>
 #include "2D.h"
 #include "mozilla/RefPtr.h"
 
 struct _CGLContextObject;
 
 typedef _CGLContextObject* CGLContextObj;
 typedef struct CGContext* CGContextRef;
@@ -96,16 +96,18 @@ public:
   mozilla::TemporaryRef<SourceSurface> GetAsSurface();
   CGContextRef CreateIOSurfaceContext();
 
   // FIXME This doesn't really belong here
   static CGImageRef CreateImageFromIOSurfaceContext(CGContextRef aContext);
   static mozilla::TemporaryRef<MacIOSurface> IOSurfaceContextGetSurface(CGContextRef aContext,
                                                                         double aContentsScaleFactor = 1.0,
                                                                         bool aHasAlpha = true);
+  static size_t GetMaxWidth();
+  static size_t GetMaxHeight();
 
 private:
   friend class nsCARenderer;
   const void* mIOSurfacePtr;
   double mContentsScaleFactor;
   bool mHasAlpha;
 };
 
@@ -120,35 +122,37 @@ public:
   static IOSurfaceGetIDFunc           sGetID;
   static IOSurfaceLookupFunc          sLookup;
   static IOSurfaceGetBaseAddressFunc  sGetBaseAddress;
   static IOSurfaceLockFunc            sLock;
   static IOSurfaceUnlockFunc          sUnlock;
   static IOSurfaceGetWidthFunc        sWidth;
   static IOSurfaceGetHeightFunc       sHeight;
   static IOSurfaceGetBytesPerRowFunc  sBytesPerRow;
+  static IOSurfaceGetPropertyMaximumFunc  sGetPropertyMaximum;
   static CGLTexImageIOSurface2DFunc   sTexImage;
   static IOSurfaceContextCreateFunc   sIOSurfaceContextCreate;
   static IOSurfaceContextCreateImageFunc  sIOSurfaceContextCreateImage;
   static IOSurfaceContextGetSurfaceFunc   sIOSurfaceContextGetSurface;
   static CFStringRef                  kPropWidth;
   static CFStringRef                  kPropHeight;
   static CFStringRef                  kPropBytesPerElem;
   static CFStringRef                  kPropBytesPerRow;
   static CFStringRef                  kPropIsGlobal;
 
   static bool isInit();
   static CFStringRef GetIOConst(const char* symbole);
   static IOSurfacePtr IOSurfaceCreate(CFDictionaryRef properties);
   static IOSurfacePtr IOSurfaceLookup(IOSurfaceID aIOSurfaceID);
   static IOSurfaceID  IOSurfaceGetID(IOSurfacePtr aIOSurfacePtr);
-  static void        *IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr);
+  static void*        IOSurfaceGetBaseAddress(IOSurfacePtr aIOSurfacePtr);
   static size_t       IOSurfaceGetWidth(IOSurfacePtr aIOSurfacePtr);
   static size_t       IOSurfaceGetHeight(IOSurfacePtr aIOSurfacePtr);
   static size_t       IOSurfaceGetBytesPerRow(IOSurfacePtr aIOSurfacePtr);
+  static size_t       IOSurfaceGetPropertyMaximum(CFStringRef property);
   static IOReturn     IOSurfaceLock(IOSurfacePtr aIOSurfacePtr, 
                                     uint32_t options, uint32_t *seed);
   static IOReturn     IOSurfaceUnlock(IOSurfacePtr aIOSurfacePtr, 
                                       uint32_t options, uint32_t *seed);
   static CGLError     CGLTexImageIOSurface2D(CGLContextObj ctxt,
                              GLenum target, GLenum internalFormat,
                              GLsizei width, GLsizei height,
                              GLenum format, GLenum type,
--- a/gfx/2d/Tools.h
+++ b/gfx/2d/Tools.h
@@ -147,16 +147,22 @@ struct AlignedArray
       mStorage = nullptr;
       mPtr = nullptr;
       mCount = 0;
       return;
     }
     // We don't create an array of T here, since we don't want ctors to be
     // invoked at the wrong places if we realign below.
     mStorage = new (std::nothrow) uint8_t[storageByteCount.value()];
+    if (!mStorage) {
+      mStorage = nullptr;
+      mPtr = nullptr;
+      mCount = 0;
+      return;
+    }
     if (uintptr_t(mStorage) % alignment) {
       // Our storage does not start at a <alignment>-byte boundary. Make sure mPtr does!
       mPtr = (T*)(uintptr_t(mStorage) + alignment - (uintptr_t(mStorage) % alignment));
     } else {
       mPtr = (T*)(mStorage);
     }
     // Now that mPtr is pointing to the aligned position we can use placement
     // |operator new| to invoke any ctors at the correct positions. For types
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -672,20 +672,16 @@ GLContext::InitWithPrefix(const char *pr
             // to properly support this.  See 814839
             // this has been fixed in Mac OS X 10.9. See 907946
             if (Vendor() == gl::GLVendor::NVIDIA &&
                 !nsCocoaFeatures::OnMavericksOrLater())
             {
                 MarkUnsupported(GLFeature::depth_texture);
             }
 #endif
-            // ANGLE's divisor support is busted. (see bug 916816)
-            if (IsANGLE()) {
-                MarkUnsupported(GLFeature::instanced_arrays);
-            }
         }
 
         NS_ASSERTION(!IsExtensionSupported(GLContext::ARB_pixel_buffer_object) ||
                      (mSymbols.fMapBuffer && mSymbols.fUnmapBuffer),
                      "ARB_pixel_buffer_object supported without glMapBuffer/UnmapBuffer being available!");
 
         if (SupportsRobustness()) {
             mHasRobustness = false;
@@ -1664,32 +1660,25 @@ GLContext::AssembleOffscreenFBs(const GL
 
 
 
 bool
 GLContext::PublishFrame()
 {
     MOZ_ASSERT(mScreen);
 
-    if (!mScreen->PublishFrame(OffscreenSize()))
-        return false;
-
-    return true;
+    return mScreen->PublishFrame(OffscreenSize());
 }
 
-SharedSurface_GL*
+SharedSurface*
 GLContext::RequestFrame()
 {
     MOZ_ASSERT(mScreen);
 
-    SharedSurface* ret = mScreen->Stream()->SwapConsumer();
-    if (!ret)
-        return nullptr;
-
-    return SharedSurface_GL::Cast(ret);
+    return mScreen->Stream()->SwapConsumer();
 }
 
 
 
 void
 GLContext::ClearSafely()
 {
     // bug 659349 --- we must be very careful here: clearing a GL framebuffer is nontrivial, relies on a lot of state,
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -49,30 +49,29 @@ class nsIRunnable;
 class nsIThread;
 
 namespace android {
     class GraphicBuffer;
 }
 
 namespace mozilla {
     namespace gfx {
+        class DataSourceSurface;
         class SourceSurface;
-        class DataSourceSurface;
-        struct SurfaceCaps;
     }
 
     namespace gl {
         class GLContext;
         class GLLibraryEGL;
         class GLScreenBuffer;
         class TextureGarbageBin;
         class GLBlitHelper;
         class GLBlitTextureImageHelper;
         class GLReadTexImageHelper;
-        class SharedSurface_GL;
+        struct SurfaceCaps;
     }
 
     namespace layers {
         class ColorTextureLayerProgram;
     }
 }
 
 namespace mozilla {
@@ -845,19 +844,19 @@ public:
     void fBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) {
         raw_fBufferData(target, size, data, usage);
 
         // bug 744888
         if (WorkAroundDriverBugs() &&
             !data &&
             Vendor() == GLVendor::NVIDIA)
         {
-            ScopedDeleteArray<char> buf(new char[1]);
+            UniquePtr<char[]> buf = MakeUnique<char[]>(1);
             buf[0] = 0;
-            fBufferSubData(target, size-1, 1, buf);
+            fBufferSubData(target, size-1, 1, buf.get());
         }
     }
 
     void fBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) {
         ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(data);
         BEFORE_GL_CALL;
         mSymbols.fBufferSubData(target, offset, size, data);
         AFTER_GL_CALL;
@@ -2499,38 +2498,31 @@ public:
         realGLboolean ret = mSymbols.fIsVertexArray(array);
         AFTER_GL_CALL;
         return ret;
     }
 
 
 // -----------------------------------------------------------------------------
 // Constructor
-public:
-
-    typedef struct gfx::SurfaceCaps SurfaceCaps;
-
-
 protected:
     GLContext(const SurfaceCaps& caps,
               GLContext* sharedContext = nullptr,
               bool isOffscreen = false);
 
 
 // -----------------------------------------------------------------------------
 // Destructor
 public:
     virtual ~GLContext();
 
 
 // -----------------------------------------------------------------------------
 // Everything that isn't standard GL APIs
 protected:
-    typedef class gfx::SharedSurface SharedSurface;
-    typedef gfx::SharedSurfaceType SharedSurfaceType;
     typedef gfx::SurfaceFormat SurfaceFormat;
 
     virtual bool MakeCurrentImpl(bool aForce) = 0;
 
 public:
 #ifdef DEBUG
     static void StaticInit() {
         PR_NewThreadPrivateIndex(&sCurrentGLContextTLS, nullptr);
@@ -2704,17 +2696,17 @@ public:
 
     // Shared code for GL extensions and GLX extensions.
     static bool ListHasExtension(const GLubyte *extensions,
                                  const char *extension);
 
     GLint GetMaxTextureImageSize() { return mMaxTextureImageSize; }
 
 public:
-    std::map<GLuint, SharedSurface_GL*> mFBOMapping;
+    std::map<GLuint, SharedSurface*> mFBOMapping;
 
     enum {
         DebugEnabled = 1 << 0,
         DebugTrace = 1 << 1,
         DebugAbortOnError = 1 << 2
     };
 
     static uint32_t sDebugMode;
@@ -2773,17 +2765,17 @@ public:
         if (!CreateScreenBuffer(size, caps))
             return false;
 
         MakeCurrent();
         fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
         fScissor(0, 0, size.width, size.height);
         fViewport(0, 0, size.width, size.height);
 
-        mCaps = mScreen->Caps();
+        mCaps = mScreen->mCaps;
         if (mCaps.any)
             DetermineCaps();
 
         UpdateGLFormats(mCaps);
         UpdatePixelFormat();
 
         return true;
     }
@@ -2864,43 +2856,43 @@ public:
                               GLuint* readFB);
 
 protected:
     friend class GLScreenBuffer;
     GLScreenBuffer* mScreen;
 
     void DestroyScreenBuffer();
 
-    SharedSurface_GL* mLockedSurface;
+    SharedSurface* mLockedSurface;
 
 public:
-    void LockSurface(SharedSurface_GL* surf) {
+    void LockSurface(SharedSurface* surf) {
         MOZ_ASSERT(!mLockedSurface);
         mLockedSurface = surf;
     }
 
-    void UnlockSurface(SharedSurface_GL* surf) {
+    void UnlockSurface(SharedSurface* surf) {
         MOZ_ASSERT(mLockedSurface == surf);
         mLockedSurface = nullptr;
     }
 
-    SharedSurface_GL* GetLockedSurface() const {
+    SharedSurface* GetLockedSurface() const {
         return mLockedSurface;
     }
 
     bool IsOffscreen() const {
         return mScreen;
     }
 
     GLScreenBuffer* Screen() const {
         return mScreen;
     }
 
     bool PublishFrame();
-    SharedSurface_GL* RequestFrame();
+    SharedSurface* RequestFrame();
 
     /* Clear to transparent black, with 0 depth and stencil,
      * while preserving current ClearColor etc. values.
      * Useful for resizing offscreen buffers.
      */
     void ClearSafely();
 
     bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
--- a/gfx/gl/GLContextProviderImpl.h
+++ b/gfx/gl/GLContextProviderImpl.h
@@ -9,17 +9,16 @@
 
 #ifndef GL_CONTEXT_PROVIDER_NAME
 #error GL_CONTEXT_PROVIDER_NAME not defined
 #endif
 
 class GL_CONTEXT_PROVIDER_NAME
 {
 public:
-    typedef gfx::SurfaceCaps SurfaceCaps;
     /**
      * Create a context that renders to the surface of the widget that is
      * passed in.  The context is always created with an RGB pixel format,
      * with no alpha, depth or stencil.  If any of those features are needed,
      * either use a framebuffer, or use CreateOffscreen.
      *
      * This context will attempt to share resources with all other window
      * contexts.  As such, it's critical that resources allocated that are not
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -19,45 +19,44 @@
 #include "SharedSurfaceIO.h"
 #endif
 #include "ScopedGLHelpers.h"
 #include "gfx2DGlue.h"
 
 namespace mozilla {
 namespace gl {
 
-using namespace mozilla::gfx;
+using gfx::SurfaceFormat;
 
 GLScreenBuffer*
 GLScreenBuffer::Create(GLContext* gl,
-                     const gfx::IntSize& size,
-                     const SurfaceCaps& caps)
+                       const gfx::IntSize& size,
+                       const SurfaceCaps& caps)
 {
     if (caps.antialias &&
         !gl->IsSupported(GLFeature::framebuffer_multisample))
     {
         return nullptr;
     }
 
-    SurfaceFactory_GL* factory = nullptr;
+    SurfaceFactory* factory = nullptr;
 
 #ifdef MOZ_WIDGET_GONK
     /* On B2G, we want a Gralloc factory, and we want one right at the start */
     if (!factory &&
         caps.surfaceAllocator &&
         XRE_GetProcessType() != GeckoProcessType_Default)
     {
         factory = new SurfaceFactory_Gralloc(gl, caps);
     }
 #endif
 #ifdef XP_MACOSX
     /* On OSX, we want an IOSurface factory, and we want one right at the start */
-    if (!factory)
-    {
-        factory = new SurfaceFactory_IOSurface(gl, caps);
+    if (!factory) {
+        factory = SurfaceFactory_IOSurface::Create(gl, caps);
     }
 #endif
 
     if (!factory)
         factory = new SurfaceFactory_Basic(gl, caps);
 
     SurfaceStream* stream = SurfaceStream::CreateForType(
         SurfaceStream::ChooseGLStreamType(SurfaceStream::MainThread,
@@ -295,25 +294,27 @@ GLScreenBuffer::BeforeReadCall()
 {
     if (mUserReadFB != 0)
         return;
 
     AssureBlitted();
 }
 
 bool
-GLScreenBuffer::ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
-                           GLenum format, GLenum type, GLvoid *pixels)
+GLScreenBuffer::ReadPixels(GLint x, GLint y,
+                           GLsizei width, GLsizei height,
+                           GLenum format, GLenum type,
+                           GLvoid* pixels)
 {
-    // If the currently bound framebuffer is backed by a SharedSurface_GL
+    // If the currently bound framebuffer is backed by a SharedSurface
     // then it might want to override how we read pixel data from it.
     // This is normally only the default framebuffer, but we can also
     // have SharedSurfaces bound to other framebuffers when doing
     // readback for BasicLayers.
-    SharedSurface_GL* surf;
+    SharedSurface* surf;
     if (GetReadFB() == 0) {
         surf = SharedSurf();
     } else {
         surf = mGL->mFBOMapping[GetReadFB()];
     }
     if (surf) {
         return surf->ReadPixels(x, y, width, height, format, type, pixels);
     }
@@ -335,39 +336,39 @@ GLScreenBuffer::AssureBlitted()
 
     if (mDraw) {
         GLuint drawFB = DrawFB();
         GLuint readFB = ReadFB();
 
         MOZ_ASSERT(drawFB != 0);
         MOZ_ASSERT(drawFB != readFB);
         MOZ_ASSERT(mGL->IsSupported(GLFeature::framebuffer_blit));
-        MOZ_ASSERT(mDraw->Size() == mRead->Size());
+        MOZ_ASSERT(mDraw->mSize == mRead->Size());
 
         ScopedBindFramebuffer boundFB(mGL);
         ScopedGLState scissor(mGL, LOCAL_GL_SCISSOR_TEST, false);
 
         BindReadFB_Internal(drawFB);
         BindDrawFB_Internal(readFB);
 
-        const gfx::IntSize&  srcSize = mDraw->Size();
+        const gfx::IntSize&  srcSize = mDraw->mSize;
         const gfx::IntSize& destSize = mRead->Size();
 
         mGL->raw_fBlitFramebuffer(0, 0,  srcSize.width,  srcSize.height,
                                   0, 0, destSize.width, destSize.height,
                                   LOCAL_GL_COLOR_BUFFER_BIT,
                                   LOCAL_GL_NEAREST);
         // Done!
     }
 
     mNeedsBlit = false;
 }
 
 void
-GLScreenBuffer::Morph(SurfaceFactory_GL* newFactory, SurfaceStreamType streamType)
+GLScreenBuffer::Morph(SurfaceFactory* newFactory, SurfaceStreamType streamType)
 {
     MOZ_ASSERT(mStream);
 
     if (newFactory) {
         delete mFactory;
         mFactory = newFactory;
     }
 
@@ -376,28 +377,27 @@ GLScreenBuffer::Morph(SurfaceFactory_GL*
 
     SurfaceStream* newStream = SurfaceStream::CreateForType(streamType, mGL, mStream);
     MOZ_ASSERT(newStream);
 
     mStream = newStream;
 }
 
 bool
-GLScreenBuffer::Attach(SharedSurface* surface, const gfx::IntSize& size)
+GLScreenBuffer::Attach(SharedSurface* surf, const gfx::IntSize& size)
 {
     ScopedBindFramebuffer autoFB(mGL);
 
-    SharedSurface_GL* surf = SharedSurface_GL::Cast(surface);
     if (mRead && SharedSurf())
         SharedSurf()->UnlockProd();
 
     surf->LockProd();
 
     if (mRead &&
-        surf->AttachType() == SharedSurf()->AttachType() &&
+        surf->mAttachType == SharedSurf()->mAttachType &&
         size == Size())
     {
         // Same size, same type, ready for reuse!
         mRead->Attach(surf);
     } else {
         // Else something changed, so resize:
         DrawBuffer* draw = nullptr;
         bool drawOk = CreateDraw(size, &draw);  // Can be null.
@@ -431,17 +431,17 @@ GLScreenBuffer::Attach(SharedSurface* su
     return true;
 }
 
 bool
 GLScreenBuffer::Swap(const gfx::IntSize& size)
 {
     SharedSurface* nextSurf = mStream->SwapProducer(mFactory, size);
     if (!nextSurf) {
-        SurfaceFactory_Basic basicFactory(mGL, mFactory->Caps());
+        SurfaceFactory_Basic basicFactory(mGL, mFactory->mCaps);
         nextSurf = mStream->SwapProducer(&basicFactory, size);
         if (!nextSurf)
           return false;
 
         NS_WARNING("SwapProd failed for sophisticated Factory type, fell back to Basic.");
     }
     MOZ_ASSERT(nextSurf);
 
@@ -465,63 +465,66 @@ GLScreenBuffer::Resize(const gfx::IntSiz
         return false;
 
     return Attach(surface, size);
 }
 
 bool
 GLScreenBuffer::CreateDraw(const gfx::IntSize& size, DrawBuffer** out_buffer)
 {
-    GLContext* gl = mFactory->GL();
-    const GLFormats& formats = mFactory->Formats();
+    GLContext* gl = mFactory->mGL;
+    const GLFormats& formats = mFactory->mFormats;
     const SurfaceCaps& caps = mFactory->DrawCaps();
 
     return DrawBuffer::Create(gl, caps, formats, size, out_buffer);
 }
 
 ReadBuffer*
-GLScreenBuffer::CreateRead(SharedSurface_GL* surf)
+GLScreenBuffer::CreateRead(SharedSurface* surf)
 {
-    GLContext* gl = mFactory->GL();
-    const GLFormats& formats = mFactory->Formats();
+    GLContext* gl = mFactory->mGL;
+    const GLFormats& formats = mFactory->mFormats;
     const SurfaceCaps& caps = mFactory->ReadCaps();
 
     return ReadBuffer::Create(gl, caps, formats, surf);
 }
 
 void
-GLScreenBuffer::Readback(SharedSurface_GL* src, DataSourceSurface* dest)
+GLScreenBuffer::Readback(SharedSurface* src, gfx::DataSourceSurface* dest)
 {
   MOZ_ASSERT(src && dest);
-  MOZ_ASSERT(dest->GetSize() == src->Size());
-  MOZ_ASSERT(dest->GetFormat() == (src->HasAlpha() ? SurfaceFormat::B8G8R8A8
-                                                   : SurfaceFormat::B8G8R8X8));
+  MOZ_ASSERT(dest->GetSize() == src->mSize);
+  MOZ_ASSERT(dest->GetFormat() == (src->mHasAlpha ? SurfaceFormat::B8G8R8A8
+                                                  : SurfaceFormat::B8G8R8X8));
 
   mGL->MakeCurrent();
 
   bool needsSwap = src != SharedSurf();
   if (needsSwap) {
       SharedSurf()->UnlockProd();
       src->LockProd();
   }
 
   ReadBuffer* buffer = CreateRead(src);
   MOZ_ASSERT(buffer);
 
-  ScopedBindFramebuffer autoFB(mGL, buffer->FB());
+  ScopedBindFramebuffer autoFB(mGL, buffer->mFB);
   ReadPixelsIntoDataSurface(mGL, dest);
 
   delete buffer;
 
   if (needsSwap) {
       src->UnlockProd();
       SharedSurf()->LockProd();
   }
 }
 
+////////////////////////////////////////////////////////////////////////
+// DrawBuffer
+
 bool
 DrawBuffer::Create(GLContext* const gl,
                    const SurfaceCaps& caps,
                    const GLFormats& formats,
                    const gfx::IntSize& size,
                    DrawBuffer** out_buffer)
 {
     MOZ_ASSERT(out_buffer);
@@ -586,51 +589,49 @@ DrawBuffer::~DrawBuffer()
         mDepthRB,
         mStencilRB
     };
 
     mGL->fDeleteFramebuffers(1, &fb);
     mGL->fDeleteRenderbuffers(3, rbs);
 }
 
-
-
-
-
+////////////////////////////////////////////////////////////////////////
+// ReadBuffer
 
 ReadBuffer*
 ReadBuffer::Create(GLContext* gl,
                    const SurfaceCaps& caps,
                    const GLFormats& formats,
-                   SharedSurface_GL* surf)
+                   SharedSurface* surf)
 {
     MOZ_ASSERT(surf);
 
-    if (surf->AttachType() == AttachmentType::Screen) {
+    if (surf->mAttachType == AttachmentType::Screen) {
         // Don't need anything. Our read buffer will be the 'screen'.
 
         return new ReadBuffer(gl,
                               0, 0, 0,
                               surf);
     }
 
     GLuint depthRB = 0;
     GLuint stencilRB = 0;
 
     GLuint* pDepthRB   = caps.depth   ? &depthRB   : nullptr;
     GLuint* pStencilRB = caps.stencil ? &stencilRB : nullptr;
 
-    CreateRenderbuffersForOffscreen(gl, formats, surf->Size(), caps.antialias,
+    CreateRenderbuffersForOffscreen(gl, formats, surf->mSize, caps.antialias,
                                     nullptr, pDepthRB, pStencilRB);
 
     GLuint colorTex = 0;
     GLuint colorRB = 0;
     GLenum target = 0;
 
-    switch (surf->AttachType()) {
+    switch (surf->mAttachType) {
     case AttachmentType::GLTexture:
         colorTex = surf->ProdTexture();
         target = surf->ProdTextureTarget();
         break;
     case AttachmentType::GLRenderbuffer:
         colorRB = surf->ProdRenderbuffer();
         break;
     default:
@@ -665,29 +666,29 @@ ReadBuffer::~ReadBuffer()
     };
 
     mGL->fDeleteFramebuffers(1, &fb);
     mGL->fDeleteRenderbuffers(2, rbs);
     mGL->mFBOMapping.erase(mFB);
 }
 
 void
-ReadBuffer::Attach(SharedSurface_GL* surf)
+ReadBuffer::Attach(SharedSurface* surf)
 {
     MOZ_ASSERT(surf && mSurf);
-    MOZ_ASSERT(surf->AttachType() == mSurf->AttachType());
-    MOZ_ASSERT(surf->Size() == mSurf->Size());
+    MOZ_ASSERT(surf->mAttachType == mSurf->mAttachType);
+    MOZ_ASSERT(surf->mSize == mSurf->mSize);
 
     // Nothing else is needed for AttachType Screen.
-    if (surf->AttachType() != AttachmentType::Screen) {
+    if (surf->mAttachType != AttachmentType::Screen) {
         GLuint colorTex = 0;
         GLuint colorRB = 0;
         GLenum target = 0;
 
-        switch (surf->AttachType()) {
+        switch (surf->mAttachType) {
         case AttachmentType::GLTexture:
             colorTex = surf->ProdTexture();
             target = surf->ProdTextureTarget();
             break;
         case AttachmentType::GLRenderbuffer:
             colorRB = surf->ProdRenderbuffer();
             break;
         default:
@@ -700,13 +701,13 @@ ReadBuffer::Attach(SharedSurface_GL* sur
     }
 
     mSurf = surf;
 }
 
 const gfx::IntSize&
 ReadBuffer::Size() const
 {
-    return mSurf->Size();
+    return mSurf->mSize;
 }
 
 } /* namespace gl */
 } /* namespace mozilla */
--- a/gfx/gl/GLScreenBuffer.h
+++ b/gfx/gl/GLScreenBuffer.h
@@ -18,48 +18,39 @@
 #include "SurfaceTypes.h"
 #include "SurfaceStream.h"
 #include "GLContextTypes.h"
 #include "GLDefs.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Point.h"
 
 namespace mozilla {
-    namespace gfx {
-        class SurfaceStream;
-        class SharedSurface;
-    }
-    namespace gl {
-        class GLContext;
-        class SharedSurface_GL;
-        class SurfaceFactory_GL;
-    }
-}
+namespace gl {
 
-namespace mozilla {
-namespace gl {
+class GLContext;
+class SharedSurface;
+class SurfaceStream;
 
 class DrawBuffer
 {
-protected:
-    typedef struct gfx::SurfaceCaps SurfaceCaps;
-
 public:
     // Fallible!
     // But it may return true with *out_buffer==nullptr if unneeded.
     static bool Create(GLContext* const gl,
                        const SurfaceCaps& caps,
                        const GLFormats& formats,
                        const gfx::IntSize& size,
                        DrawBuffer** out_buffer);
 
 protected:
     GLContext* const mGL;
+public:
     const gfx::IntSize mSize;
     const GLuint mFB;
+protected:
     const GLuint mColorMSRB;
     const GLuint mDepthRB;
     const GLuint mStencilRB;
 
     DrawBuffer(GLContext* gl,
                const gfx::IntSize& size,
                GLuint fb,
                GLuint colorMSRB,
@@ -70,97 +61,78 @@ protected:
         , mFB(fb)
         , mColorMSRB(colorMSRB)
         , mDepthRB(depthRB)
         , mStencilRB(stencilRB)
     {}
 
 public:
     virtual ~DrawBuffer();
-
-    const gfx::IntSize& Size() const {
-        return mSize;
-    }
-
-    GLuint FB() const {
-        return mFB;
-    }
 };
 
 class ReadBuffer
 {
-protected:
-    typedef struct gfx::SurfaceCaps SurfaceCaps;
-
 public:
     // Infallible, always non-null.
     static ReadBuffer* Create(GLContext* gl,
                               const SurfaceCaps& caps,
                               const GLFormats& formats,
-                              SharedSurface_GL* surf);
+                              SharedSurface* surf);
 
 protected:
     GLContext* const mGL;
-
+public:
     const GLuint mFB;
+protected:
     // mFB has the following attachments:
     const GLuint mDepthRB;
     const GLuint mStencilRB;
     // note no mColorRB here: this is provided by mSurf.
-    SharedSurface_GL* mSurf; // Owned by GLScreenBuffer's SurfaceStream.
+    SharedSurface* mSurf; // Owned by GLScreenBuffer's SurfaceStream.
 
     ReadBuffer(GLContext* gl,
                GLuint fb,
                GLuint depthRB,
                GLuint stencilRB,
-               SharedSurface_GL* surf)
+               SharedSurface* surf)
         : mGL(gl)
         , mFB(fb)
         , mDepthRB(depthRB)
         , mStencilRB(stencilRB)
         , mSurf(surf)
     {}
 
 public:
     virtual ~ReadBuffer();
 
     // Cannot attach a surf of a different AttachType or Size than before.
-    void Attach(SharedSurface_GL* surf);
+    void Attach(SharedSurface* surf);
 
     const gfx::IntSize& Size() const;
 
-    GLuint FB() const {
-        return mFB;
-    }
-
-    SharedSurface_GL* SharedSurf() const {
+    SharedSurface* SharedSurf() const {
         return mSurf;
     }
 };
 
 
 class GLScreenBuffer
 {
-protected:
-    typedef class gfx::SurfaceStream SurfaceStream;
-    typedef class gfx::SharedSurface SharedSurface;
-    typedef gfx::SurfaceStreamType SurfaceStreamType;
-    typedef gfx::SharedSurfaceType SharedSurfaceType;
-    typedef struct gfx::SurfaceCaps SurfaceCaps;
-
 public:
     // Infallible.
     static GLScreenBuffer* Create(GLContext* gl,
                                   const gfx::IntSize& size,
                                   const SurfaceCaps& caps);
 
 protected:
     GLContext* const mGL;         // Owns us.
-    SurfaceCaps mCaps;
-    SurfaceFactory_GL* mFactory;  // Owned by us.
+public:
+    const SurfaceCaps mCaps;
+protected:
+    SurfaceFactory* mFactory;  // Owned by us.
     RefPtr<SurfaceStream> mStream;
 
     DrawBuffer* mDraw;            // Owned by us.
     ReadBuffer* mRead;            // Owned by us.
 
     bool mNeedsBlit;
 
     // Below are the parts that help us pretend to be framebuffer 0:
@@ -171,17 +143,17 @@ protected:
 
 #ifdef DEBUG
     bool mInInternalMode_DrawFB;
     bool mInInternalMode_ReadFB;
 #endif
 
     GLScreenBuffer(GLContext* gl,
                    const SurfaceCaps& caps,
-                   SurfaceFactory_GL* factory,
+                   SurfaceFactory* factory,
                    SurfaceStream* stream)
         : mGL(gl)
         , mCaps(caps)
         , mFactory(factory)
         , mStream(stream)
         , mDraw(nullptr)
         , mRead(nullptr)
         , mNeedsBlit(true)
@@ -197,62 +169,58 @@ protected:
 
 public:
     virtual ~GLScreenBuffer();
 
     SurfaceStream* Stream() const {
         return mStream;
     }
 
-    SurfaceFactory_GL* Factory() const {
+    SurfaceFactory* Factory() const {
         return mFactory;
     }
 
-    SharedSurface_GL* SharedSurf() const {
+    SharedSurface* SharedSurf() const {
         MOZ_ASSERT(mRead);
         return mRead->SharedSurf();
     }
 
     bool PreserveBuffer() const {
         return mCaps.preserve;
     }
 
-    const SurfaceCaps& Caps() const {
-        return mCaps;
-    }
-
     GLuint DrawFB() const {
         if (!mDraw)
             return ReadFB();
 
-        return mDraw->FB();
+        return mDraw->mFB;
     }
 
     GLuint ReadFB() const {
-        return mRead->FB();
+        return mRead->mFB;
     }
 
     void DeletingFB(GLuint fb);
 
     const gfx::IntSize& Size() const {
         MOZ_ASSERT(mRead);
-        MOZ_ASSERT(!mDraw || mDraw->Size() == mRead->Size());
+        MOZ_ASSERT(!mDraw || mDraw->mSize == mRead->Size());
         return mRead->Size();
     }
 
     void BindAsFramebuffer(GLContext* const gl, GLenum target) const;
 
     void RequireBlit();
     void AssureBlitted();
     void AfterDrawCall();
     void BeforeReadCall();
 
     /**
      * Attempts to read pixels from the current bound framebuffer, if
-     * it is backed by a SharedSurface_GL.
+     * it is backed by a SharedSurface.
      *
      * Returns true if the pixel data has been read back, false
      * otherwise.
      */
     bool ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                     GLenum format, GLenum type, GLvoid *pixels);
 
     /* Morph swaps out our SurfaceStream mechanism and replaces it with
@@ -262,34 +230,34 @@ public:
      * We haven't made any guarantee that rendering is actually
      * done when Morph is run, just that it can't run concurrently
      * with rendering. This means that we can't just drop the contents
      * of the buffer, since we may only be partially done rendering.
      *
      * Once you pass newFactory into Morph, newFactory will be owned by
      * GLScreenBuffer, so `forget` any references to it that still exist.
      */
-    void Morph(SurfaceFactory_GL* newFactory, SurfaceStreamType streamType);
+    void Morph(SurfaceFactory* newFactory, SurfaceStreamType streamType);
 
 protected:
     // Returns false on error or inability to resize.
     bool Swap(const gfx::IntSize& size);
 
 public:
     bool PublishFrame(const gfx::IntSize& size);
 
     bool Resize(const gfx::IntSize& size);
 
-    void Readback(SharedSurface_GL* src, gfx::DataSourceSurface* dest);
+    void Readback(SharedSurface* src, gfx::DataSourceSurface* dest);
 
 protected:
     bool Attach(SharedSurface* surface, const gfx::IntSize& size);
 
     bool CreateDraw(const gfx::IntSize& size, DrawBuffer** out_buffer);
-    ReadBuffer* CreateRead(SharedSurface_GL* surf);
+    ReadBuffer* CreateRead(SharedSurface* surf);
 
 public:
     /* `fb` in these functions is the framebuffer the GLContext is hoping to
      * bind. When this is 0, we intercept the call and bind our own
      * framebuffers. As a client of these functions, just bind 0 when you want
      * to draw to the default framebuffer/'screen'.
      */
     void BindFB(GLuint fb);
--- a/gfx/gl/ScopedGLHelpers.cpp
+++ b/gfx/gl/ScopedGLHelpers.cpp
@@ -1,13 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; 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 "mozilla/UniquePtr.h"
+
 #include "GLContext.h"
 #include "ScopedGLHelpers.h"
 
 namespace mozilla {
 namespace gl {
 
 /* ScopedGLState - Wraps glEnable/glDisable. **********************************/
 
@@ -423,17 +425,17 @@ ScopedGLDrawState::ScopedGLDrawState(GLC
     , stencil     (aGL, LOCAL_GL_STENCIL_TEST,    false)
     , mGL(aGL)
     , packAlign(4)
 {
     mGL->GetUIntegerv(LOCAL_GL_UNPACK_ALIGNMENT, &packAlign);
     mGL->GetUIntegerv(LOCAL_GL_CURRENT_PROGRAM, &boundProgram);
     mGL->GetUIntegerv(LOCAL_GL_ARRAY_BUFFER_BINDING, &boundBuffer);
     mGL->GetUIntegerv(LOCAL_GL_MAX_VERTEX_ATTRIBS, &maxAttrib);
-    attrib_enabled = new GLint[maxAttrib];
+    attrib_enabled = MakeUnique<GLint[]>(maxAttrib);
 
     for (unsigned int i = 0; i < maxAttrib; i++) {
         mGL->fGetVertexAttribiv(i, LOCAL_GL_VERTEX_ATTRIB_ARRAY_ENABLED, &attrib_enabled[i]);
         mGL->fDisableVertexAttribArray(i);
     }
     // Only Attrib0's client side state affected
     mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_SIZE, &attrib0_size);
     mGL->fGetVertexAttribiv(0, LOCAL_GL_VERTEX_ATTRIB_ARRAY_STRIDE, &attrib0_stride);
--- a/gfx/gl/ScopedGLHelpers.h
+++ b/gfx/gl/ScopedGLHelpers.h
@@ -1,16 +1,18 @@
 /* -*- Mode: C++; tab-width: 8; 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/. */
 
 #ifndef SCOPEDGLHELPERS_H_
 #define SCOPEDGLHELPERS_H_
 
+#include "mozilla/UniquePtr.h"
+
 #include "GLContext.h"
 
 namespace mozilla {
 namespace gl {
 
 //RAII via CRTP!
 template <class Derived>
 struct ScopedGLWrapper
@@ -317,17 +319,17 @@ struct ScopedGLDrawState {
     ScopedGLState dither;
     ScopedGLState polyOffsFill;
     ScopedGLState sampleAToC;
     ScopedGLState sampleCover;
     ScopedGLState scissor;
     ScopedGLState stencil;
 
     GLuint maxAttrib;
-    ScopedDeleteArray<GLint> attrib_enabled;
+    UniquePtr<GLint[]> attrib_enabled;
     GLint attrib0_size;
     GLint attrib0_stride;
     GLint attrib0_type;
     GLint attrib0_normalized;
     GLint attrib0_bufferBinding;
     void* attrib0_pointer;
 
     realGLboolean colorMask[4];
--- a/gfx/gl/SharedSurface.cpp
+++ b/gfx/gl/SharedSurface.cpp
@@ -1,29 +1,301 @@
 /* -*- Mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40; -*- */
 /* 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 "SharedSurface.h"
+
+#include "GLContext.h"
+#include "GLBlitHelper.h"
+#include "ScopedGLHelpers.h"
 #include "SharedSurfaceGL.h"
 
-using namespace mozilla::gl;
-
 namespace mozilla {
-namespace gfx {
+namespace gl {
 
-// |src| must begin and end locked, though it
-// can be temporarily unlocked if needed.
-void
-SharedSurface::Copy(SharedSurface* src, SharedSurface* dest, SurfaceFactory* factory)
+/*static*/ void
+SharedSurface::ProdCopy(SharedSurface* src, SharedSurface* dest,
+                        SurfaceFactory* factory)
 {
-    MOZ_ASSERT( src->APIType() == APITypeT::OpenGL);
-    MOZ_ASSERT(dest->APIType() == APITypeT::OpenGL);
+    GLContext* gl = src->mGL;
+
+    // If `src` begins locked, it must end locked, though we may
+    // temporarily unlock it if we need to.
+    MOZ_ASSERT((src == gl->GetLockedSurface()) == src->IsLocked());
+
+    gl->MakeCurrent();
+
+    if (src->mAttachType  == AttachmentType::Screen &&
+        dest->mAttachType == AttachmentType::Screen)
+    {
+        // Here, we actually need to blit through a temp surface, so let's make one.
+        nsAutoPtr<SharedSurface_GLTexture> tempSurf;
+        tempSurf = SharedSurface_GLTexture::Create(gl,
+                                                   gl,
+                                                   factory->mFormats,
+                                                   src->mSize,
+                                                   factory->mCaps.alpha);
+
+        ProdCopy(src, tempSurf, factory);
+        ProdCopy(tempSurf, dest, factory);
+        return;
+    }
+
+    if (src->mAttachType == AttachmentType::Screen) {
+        SharedSurface* origLocked = gl->GetLockedSurface();
+        bool srcNeedsUnlock = false;
+        bool origNeedsRelock = false;
+        if (origLocked != src) {
+            if (origLocked) {
+                origLocked->UnlockProd();
+                origNeedsRelock = true;
+            }
+
+            src->LockProd();
+            srcNeedsUnlock = true;
+        }
+
+        if (dest->mAttachType == AttachmentType::GLTexture) {
+            GLuint destTex = dest->ProdTexture();
+            GLenum destTarget = dest->ProdTextureTarget();
+
+            gl->BlitHelper()->BlitFramebufferToTexture(0, destTex, src->mSize, dest->mSize, destTarget);
+        } else if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
+            GLuint destRB = dest->ProdRenderbuffer();
+            ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
+
+            gl->BlitHelper()->BlitFramebufferToFramebuffer(0, destWrapper.FB(),
+                                                           src->mSize, dest->mSize);
+        } else {
+            MOZ_CRASH("Unhandled dest->mAttachType.");
+        }
+
+        if (srcNeedsUnlock)
+            src->UnlockProd();
+
+        if (origNeedsRelock)
+            origLocked->LockProd();
+
+        return;
+    }
+
+    if (dest->mAttachType == AttachmentType::Screen) {
+        SharedSurface* origLocked = gl->GetLockedSurface();
+        bool destNeedsUnlock = false;
+        bool origNeedsRelock = false;
+        if (origLocked != dest) {
+            if (origLocked) {
+                origLocked->UnlockProd();
+                origNeedsRelock = true;
+            }
+
+            dest->LockProd();
+            destNeedsUnlock = true;
+        }
+
+        if (src->mAttachType == AttachmentType::GLTexture) {
+            GLuint srcTex = src->ProdTexture();
+            GLenum srcTarget = src->ProdTextureTarget();
+
+            gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, 0, src->mSize, dest->mSize, srcTarget);
+        } else if (src->mAttachType == AttachmentType::GLRenderbuffer) {
+            GLuint srcRB = src->ProdRenderbuffer();
+            ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB);
+
+            gl->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper.FB(), 0,
+                                                           src->mSize, dest->mSize);
+        } else {
+            MOZ_CRASH("Unhandled src->mAttachType.");
+        }
+
+        if (destNeedsUnlock)
+            dest->UnlockProd();
+
+        if (origNeedsRelock)
+            origLocked->LockProd();
+
+        return;
+    }
+
+    // Alright, done with cases involving Screen types.
+    // Only {src,dest}x{texture,renderbuffer} left.
+
+    if (src->mAttachType == AttachmentType::GLTexture) {
+        GLuint srcTex = src->ProdTexture();
+        GLenum srcTarget = src->ProdTextureTarget();
+
+        if (dest->mAttachType == AttachmentType::GLTexture) {
+            GLuint destTex = dest->ProdTexture();
+            GLenum destTarget = dest->ProdTextureTarget();
+
+            gl->BlitHelper()->BlitTextureToTexture(srcTex, destTex,
+                                                   src->mSize, dest->mSize,
+                                                   srcTarget, destTarget);
+
+            return;
+        }
+
+        if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
+            GLuint destRB = dest->ProdRenderbuffer();
+            ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
+
+            gl->BlitHelper()->BlitTextureToFramebuffer(srcTex, destWrapper.FB(),
+                                                       src->mSize, dest->mSize, srcTarget);
+
+            return;
+        }
+
+        MOZ_CRASH("Unhandled dest->mAttachType.");
+    }
+
+    if (src->mAttachType == AttachmentType::GLRenderbuffer) {
+        GLuint srcRB = src->ProdRenderbuffer();
+        ScopedFramebufferForRenderbuffer srcWrapper(gl, srcRB);
+
+        if (dest->mAttachType == AttachmentType::GLTexture) {
+            GLuint destTex = dest->ProdTexture();
+            GLenum destTarget = dest->ProdTextureTarget();
 
-    SharedSurface_GL* srcGL = (SharedSurface_GL*)src;
-    SharedSurface_GL* destGL = (SharedSurface_GL*)dest;
+            gl->BlitHelper()->BlitFramebufferToTexture(srcWrapper.FB(), destTex,
+                                                       src->mSize, dest->mSize, destTarget);
+
+            return;
+        }
+
+        if (dest->mAttachType == AttachmentType::GLRenderbuffer) {
+            GLuint destRB = dest->ProdRenderbuffer();
+            ScopedFramebufferForRenderbuffer destWrapper(gl, destRB);
+
+            gl->BlitHelper()->BlitFramebufferToFramebuffer(srcWrapper.FB(), destWrapper.FB(),
+                                                           src->mSize, dest->mSize);
+
+            return;
+        }
+
+        MOZ_CRASH("Unhandled dest->mAttachType.");
+    }
+
+    MOZ_CRASH("Unhandled src->mAttachType.");
+}
+
+////////////////////////////////////////////////////////////////////////
+// SharedSurface
+
+void
+SharedSurface::LockProd()
+{
+    MOZ_ASSERT(!mIsLocked);
+
+    LockProdImpl();
+
+    mGL->LockSurface(this);
+    mIsLocked = true;
+}
+
+void
+SharedSurface::UnlockProd()
+{
+    if (!mIsLocked)
+        return;
+
+    UnlockProdImpl();
+
+    mGL->UnlockSurface(this);
+    mIsLocked = false;
+}
+
+////////////////////////////////////////////////////////////////////////
+// SurfaceFactory
+
+static void
+ChooseBufferBits(const SurfaceCaps& caps,
+                 SurfaceCaps* const out_drawCaps,
+                 SurfaceCaps* const out_readCaps)
+{
+    MOZ_ASSERT(out_drawCaps);
+    MOZ_ASSERT(out_readCaps);
+
+    SurfaceCaps screenCaps;
+
+    screenCaps.color = caps.color;
+    screenCaps.alpha = caps.alpha;
+    screenCaps.bpp16 = caps.bpp16;
+
+    screenCaps.depth = caps.depth;
+    screenCaps.stencil = caps.stencil;
 
-    SharedSurface_GL::ProdCopy(srcGL, destGL, (SurfaceFactory_GL*)factory);
+    screenCaps.antialias = caps.antialias;
+    screenCaps.preserve = caps.preserve;
+
+    if (caps.antialias) {
+        *out_drawCaps = screenCaps;
+        out_readCaps->Clear();
+
+        // Color caps need to be duplicated in readCaps.
+        out_readCaps->color = caps.color;
+        out_readCaps->alpha = caps.alpha;
+        out_readCaps->bpp16 = caps.bpp16;
+    } else {
+        out_drawCaps->Clear();
+        *out_readCaps = screenCaps;
+    }
+}
+
+SurfaceFactory::SurfaceFactory(GLContext* gl,
+                               SharedSurfaceType type,
+                               const SurfaceCaps& caps)
+    : mGL(gl)
+    , mCaps(caps)
+    , mType(type)
+    , mFormats(gl->ChooseGLFormats(caps))
+{
+    ChooseBufferBits(mCaps, &mDrawCaps, &mReadCaps);
+}
+
+SurfaceFactory::~SurfaceFactory()
+{
+    while (!mScraps.empty()) {
+        SharedSurface* cur = mScraps.front();
+        mScraps.pop();
+
+        delete cur;
+    }
+}
+
+SharedSurface*
+SurfaceFactory::NewSharedSurface(const gfx::IntSize& size)
+{
+    // Attempt to reuse an old surface.
+    while (!mScraps.empty()) {
+        SharedSurface* cur = mScraps.front();
+        mScraps.pop();
+        if (cur->mSize == size)
+            return cur;
+
+        // Destroy old surfaces of the wrong size.
+        delete cur;
+    }
+
+    SharedSurface* ret = CreateShared(size);
+
+    return ret;
+}
+
+// Auto-deletes surfs of the wrong type.
+void
+SurfaceFactory::Recycle(SharedSurface*& surf)
+{
+    if (!surf)
+        return;
+
+    if (surf->mType == mType) {
+        mScraps.push(surf);
+    } else {
+        delete surf;
+    }
+
+    surf = nullptr;
 }
 
 } /* namespace gfx */
 } /* namespace mozilla */
--- a/gfx/gl/SharedSurface.h
+++ b/gfx/gl/SharedSurface.h
@@ -10,103 +10,147 @@
  *     SharedSurface_GLTexture
  *     SharedSurface_EGLImage
  *     SharedSurface_ANGLEShareHandle
  */
 
 #ifndef SHARED_SURFACE_H_
 #define SHARED_SURFACE_H_
 
+#include <queue>
 #include <stdint.h>
+
+#include "GLContextTypes.h"
+#include "GLDefs.h"
 #include "mozilla/Attributes.h"
-#include "GLDefs.h"
 #include "mozilla/gfx/Point.h"
 #include "SurfaceTypes.h"
 
 namespace mozilla {
-namespace gfx {
+namespace gl {
 
+class GLContext;
 class SurfaceFactory;
 
 class SharedSurface
 {
-protected:
+public:
+    static void ProdCopy(SharedSurface* src, SharedSurface* dest,
+                         SurfaceFactory* factory);
+
     const SharedSurfaceType mType;
-    const APITypeT mAPI;
     const AttachmentType mAttachType;
+    GLContext* const mGL;
     const gfx::IntSize mSize;
     const bool mHasAlpha;
+protected:
     bool mIsLocked;
 
     SharedSurface(SharedSurfaceType type,
-                  APITypeT api,
                   AttachmentType attachType,
+                  GLContext* gl,
                   const gfx::IntSize& size,
                   bool hasAlpha)
         : mType(type)
-        , mAPI(api)
         , mAttachType(attachType)
+        , mGL(gl)
         , mSize(size)
         , mHasAlpha(hasAlpha)
         , mIsLocked(false)
     {
     }
 
 public:
     virtual ~SharedSurface() {
     }
 
-    static void Copy(SharedSurface* src, SharedSurface* dest,
-                     SurfaceFactory* factory);
+    bool IsLocked() const {
+        return mIsLocked;
+    }
 
     // This locks the SharedSurface as the production buffer for the context.
     // This is needed by backends which use PBuffers and/or EGLSurfaces.
-    virtual void LockProd() {
-        MOZ_ASSERT(!mIsLocked);
-        LockProdImpl();
-        mIsLocked = true;
-    }
+    void LockProd();
 
     // Unlocking is harmless if we're already unlocked.
-    virtual void UnlockProd() {
-        if (!mIsLocked)
-            return;
+    void UnlockProd();