merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 30 Oct 2014 14:45:37 +0100
changeset 213076 1aa1b23d799e5cf08b21bd8b08f78716d35d89f6
parent 212983 46cab998bd3114b42e03e07488dfb10b2f956db6 (current diff)
parent 213075 b991ca238a3538e152dbcd7317b2c5f178b61555 (diff)
child 213090 675913ddbb5571ffbd6a2dd44e82ad3579917d0b
push id27738
push usercbook@mozilla.com
push dateThu, 30 Oct 2014 13:46:07 +0000
treeherdermozilla-central@1aa1b23d799e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.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
browser/metro/shell/testing/Makefile.in
build/unix/test/Makefile.in
build/unix/test/moz.build
build/unix/test/runtest
build/unix/test/uniq.tpl
build/unix/uniq.pl
build/win32/crashinjectdll/Makefile.in
build/win32/vmwarerecordinghelper/Makefile.in
config/make-atom-strings.pl
dom/media/test/test_encryptedMediaExtensions.html
dom/network/tests/unit/test_multisend.js
dom/network/tests/unit/test_tcpserversocket.js
dom/network/tests/unit_ipc/test_tcpserversocket_ipc.js
dom/plugins/ipc/hangui/Makefile.in
gfx/tests/process-textruns.pl
memory/replace/dmd/Makefile.in
memory/replace/dummy/Makefile.in
memory/replace/jemalloc/Makefile.in
mfbt/tests/Makefile.in
mozglue/build/fixcrt.py
netwerk/test/PageList.txt
netwerk/test/neckoTiming.pl
security/sandbox/Makefile.in
security/sandbox/win/src/sandboxbroker/Makefile.in
testing/tools/screenshot/Makefile.in
toolkit/crashreporter/breakpad-windows-standalone/Makefile.in
toolkit/crashreporter/injector/Makefile.in
widget/gonk/HwcComposer2D.cpp
xpcom/tools/analyze-xpcom-log.pl
xpcom/typelib/xpt/tests/Makefile.in
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Backing out bug 1087560 needs a CLOBBER
+For some reason, Clobber had problems applying the changes for Android in Bug 1091118 
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -215,37 +215,29 @@ NotificationController::WillRefresh(mozi
       continue;
 
     nsIContent* ownerContent = mDocument->DocumentNode()->
       FindContentForSubDocument(childDoc->DocumentNode());
     if (ownerContent) {
       Accessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
       if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
         if (mDocument->AppendChildDocument(childDoc)) {
-          if (IPCAccessibilityActive()) {
-            DocAccessibleChild* ipcDoc = new DocAccessibleChild(childDoc);
-            childDoc->SetIPCDoc(ipcDoc);
-            auto contentChild = dom::ContentChild::GetSingleton();
-            DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc();
-            uint64_t id = reinterpret_cast<uintptr_t>(outerDocAcc->UniqueID());
-            contentChild->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc,
-                                                        id);
-          }
 
           continue;
         }
 
         outerDocAcc->RemoveChild(childDoc);
       }
 
       // Failed to bind the child document, destroy it.
       childDoc->Shutdown();
     }
   }
-  mHangingChildDocuments.Clear();
+
+  nsTArray<nsRefPtr<DocAccessible>> newChildDocs = Move(mHangingChildDocuments);
 
   // If the document is ready and all its subdocuments are completely loaded
   // then process the document load.
   if (mDocument->HasLoadState(DocAccessible::eReady) &&
       !mDocument->HasLoadState(DocAccessible::eCompletelyLoaded) &&
       hangingDocCnt == 0) {
     uint32_t childDocCnt = mDocument->ChildDocumentCount(), childDocIdx = 0;
     for (; childDocIdx < childDocCnt; childDocIdx++) {
@@ -277,16 +269,30 @@ NotificationController::WillRefresh(mozi
   mDocument->ProcessInvalidationList();
 
   // If a generic notification occurs after this point then we may be allowed to
   // process it synchronously.  However we do not want to reenter if fireing
   // events causes script to run.
   mObservingState = eRefreshProcessing;
 
   ProcessEventQueue();
+
+  if (IPCAccessibilityActive()) {
+    size_t newDocCount = newChildDocs.Length();
+    for (size_t i = 0; i < newDocCount; i++) {
+      DocAccessible* childDoc = newChildDocs[i];
+      DocAccessibleChild* ipcDoc = new DocAccessibleChild(childDoc);
+      childDoc->SetIPCDoc(ipcDoc);
+      auto contentChild = dom::ContentChild::GetSingleton();
+      DocAccessibleChild* parentIPCDoc = mDocument->IPCDoc();
+      uint64_t id = reinterpret_cast<uintptr_t>(childDoc->Parent()->UniqueID());
+      contentChild->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id);
+    }
+  }
+
   mObservingState = eRefreshObserving;
   if (!mDocument)
     return;
 
   // Stop further processing if there are no new notifications of any kind or
   // events and document load is processed.
   if (mContentInsertions.IsEmpty() && mNotifications.IsEmpty() &&
       mEvents.IsEmpty() && mTextHash.Count() == 0 &&
--- a/accessible/base/moz.build
+++ b/accessible/base/moz.build
@@ -58,16 +58,17 @@ if CONFIG['A11Y_LOG']:
     ]
 
 LOCAL_INCLUDES += [
     '/accessible/generic',
     '/accessible/html',
     '/accessible/ipc',
     '/accessible/xpcom',
     '/accessible/xul',
+    '/dom/base',
     '/dom/xbl',
     '/ipc/chromium/src',
     '/layout/generic',
     '/layout/style',
     '/layout/svg',
     '/layout/xul',
     '/layout/xul/tree/',
 ]
--- a/accessible/interfaces/ia2/moz.build
+++ b/accessible/interfaces/ia2/moz.build
@@ -1,15 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-SharedLibrary('IA2Marshal')
+GeckoSharedLibrary('IA2Marshal', linkage=None)
 
 DEFINES['REGISTER_PROXY_DLL'] = True
 
 DEFFILE = SRCDIR + '/IA2Marshal.def'
 
 OS_LIBS += [
     'uuid',
     'kernel32',
--- a/accessible/interfaces/msaa/moz.build
+++ b/accessible/interfaces/msaa/moz.build
@@ -1,15 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-SharedLibrary('AccessibleMarshal')
+GeckoSharedLibrary('AccessibleMarshal', linkage=None)
 
 GENERATED_SOURCES += [
     'dlldata.c',
     'ISimpleDOMDocument_i.c',
     'ISimpleDOMDocument_p.c',
     'ISimpleDOMNode_i.c',
     'ISimpleDOMNode_p.c',
     'ISimpleDOMText_i.c',
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -13,16 +13,22 @@ namespace a11y {
 
 void
 SerializeTree(Accessible* aRoot, nsTArray<AccessibleData>& aTree)
 {
   uint64_t id = reinterpret_cast<uint64_t>(aRoot->UniqueID());
   uint32_t role = aRoot->Role();
   uint32_t childCount = aRoot->ChildCount();
 
+  // OuterDocAccessibles are special because we don't want to serialize the
+  // child doc here, we'll call PDocAccessibleConstructor in
+  // NotificationController.
+  if (childCount == 1 && aRoot->GetChildAt(0)->IsDoc())
+    childCount = 0;
+
   aTree.AppendElement(AccessibleData(id, role, childCount));
   for (uint32_t i = 0; i < childCount; i++)
     SerializeTree(aRoot->GetChildAt(i), aTree);
 }
 
 void
 DocAccessibleChild::ShowEvent(AccShowEvent* aShowEvent)
 {
--- a/b2g/app/moz.build
+++ b/b2g/app/moz.build
@@ -1,19 +1,19 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if not CONFIG['LIBXUL_SDK']:
     if CONFIG['GAIADIR']:
-        Program(CONFIG['MOZ_APP_NAME'] + "-bin")
+        GeckoProgram(CONFIG['MOZ_APP_NAME'] + "-bin")
     else:
-        Program(CONFIG['MOZ_APP_NAME'])
+        GeckoProgram(CONFIG['MOZ_APP_NAME'])
     if CONFIG['MOZ_B2G_LOADER']:
         SOURCES += [
             'B2GLoader.cpp',
         ]
 
     SOURCES += [
         'nsBrowserApp.cpp',
     ]
@@ -21,18 +21,16 @@ if not CONFIG['LIBXUL_SDK']:
         # Always enter a Windows program through wmain, whether or not we're
         # a console application.
         WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
 
     USE_LIBS += [
         'zlib',
     ]
 
-DEFINES['XPCOM_GLUE'] = True
-
 for var in ('MOZ_APP_NAME', 'MOZ_APP_VERSION', 'MOZ_UPDATER'):
     DEFINES[var] = CONFIG[var]
 
 GENERATED_INCLUDES += [
     '/build',
 ]
 
 LOCAL_INCLUDES += [
@@ -65,18 +63,14 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk
             'gui',
             'suspend',
         ]
     OS_LIBS += [
         'binder',
         'utils',
     ]
 
-USE_LIBS += [
-    'xpcomglue',
-]
-
 DISABLE_STL_WRAPPING = True
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     OS_LIBS += [
         'version',
     ]
--- a/b2g/gaia/Makefile.in
+++ b/b2g/gaia/Makefile.in
@@ -1,17 +1,14 @@
 # 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/.
 
 GAIA_PATH := gaia/profile
 
-# This is needed to avoid making run-b2g depend on mozglue
-WRAP_LDFLAGS :=
-
 GENERATED_DIRS += $(DIST)/bin/$(GAIA_PATH)
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	+$(MAKE) -j1 -C $(GAIADIR) clean
 	+$(MAKE) -j1 -C $(GAIADIR) profile
 	(cd $(GAIADIR)/profile && tar $(TAR_CREATE_FLAGS) - .) | (cd $(abspath $(DIST))/bin/$(GAIA_PATH) && tar -xf -)
--- a/browser/app/moz.build
+++ b/browser/app/moz.build
@@ -1,45 +1,49 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
 DIRS += ['profile/extensions']
 
-Program(CONFIG['MOZ_APP_NAME'])
+if CONFIG['OS_ARCH'] == 'WINNT' and CONFIG['MOZ_METRO']:
+    GeckoProgram(CONFIG['MOZ_APP_NAME'])
+else:
+    GeckoProgram(CONFIG['MOZ_APP_NAME'], msvcrt='static')
 
 SOURCES += [
     'nsBrowserApp.cpp',
 ]
 
 DEFINES['APP_VERSION'] = CONFIG['MOZ_APP_VERSION']
 
 for var in ('MOZILLA_OFFICIAL', 'LIBXUL_SDK'):
     if CONFIG[var]:
         DEFINES[var] = True
 
-DEFINES['XPCOM_GLUE'] = True
-
 GENERATED_INCLUDES += [
     '/build',
 ]
 
 LOCAL_INCLUDES += [
     '/toolkit/xre',
     '/xpcom/base',
     '/xpcom/build',
 ]
 
 if not CONFIG['MOZ_METRO']:
   DELAYLOAD_DLLS += [
       'mozglue.dll',
   ]
-  USE_STATIC_LIBS = True
+
+USE_LIBS += [
+    'mozglue',
+]
 
 if CONFIG['_MSC_VER']:
     # Always enter a Windows program through wmain, whether or not we're
     # a console application.
     WIN32_EXE_LDFLAGS += ['-ENTRY:wmainCRTStartup']
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     RCINCLUDE = 'splash.rc'
@@ -51,26 +55,16 @@ if CONFIG['OS_ARCH'] == 'WINNT':
 #
 # The default heap size is 1MB on Win32.
 # The heap will grow if need be.
 #
 # Set it to 256k.  See bug 127069.
 if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['GNU_CC']:
     LDFLAGS += ['/HEAP:0x40000']
 
-if CONFIG['OS_ARCH'] == 'WINNT' and not CONFIG['MOZ_METRO']:
-    USE_LIBS += [
-        'mozglue',
-        'xpcomglue_staticruntime',
-    ]
-else:
-    USE_LIBS += [
-        'xpcomglue',
-    ]
-
 DISABLE_STL_WRAPPING = True
 
 if CONFIG['MOZ_LINKER']:
     OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
 
 if CONFIG['HAVE_CLOCK_MONOTONIC']:
     OS_LIBS += CONFIG['REALTIME_LIBS']
 
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -2339,17 +2339,22 @@ let E10SUINotification = {
   // e10s testing period to Nightly users.
   CURRENT_NOTICE_COUNT: 1,
   CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
   PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
 
   checkStatus: function() {
     let skipE10sChecks = false;
     try {
+      // This order matters, because
+      // browser.tabs.remote.autostart.disabled-because-using-a11y is not
+      // always defined and will throw when not present.
+      // privacy.trackingprotection.enabled is always defined.
       skipE10sChecks = (UpdateChannel.get() != "nightly") ||
+                       Services.prefs.getBoolPref("privacy.trackingprotection.enabled") ||
                        Services.prefs.getBoolPref("browser.tabs.remote.autostart.disabled-because-using-a11y");
     } catch(e) {}
 
     if (skipE10sChecks) {
       return;
     }
 
     if (Services.appinfo.browserTabsRemoteAutostart) {
--- a/browser/metro/shell/commandexecutehandler/Makefile.in
+++ b/browser/metro/shell/commandexecutehandler/Makefile.in
@@ -1,16 +1,11 @@
 # 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 MOZ_WINCONSOLE
 MOZ_WINCONSOLE = 0
 endif
 
-include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/config/rules.mk
 
 DIST_PROGRAM = CommandExecuteHandler$(BIN_SUFFIX)
-
-# Don't link against mozglue.dll
-MOZ_GLUE_LDFLAGS =
-MOZ_GLUE_PROGRAM_LDFLAGS =
deleted file mode 100644
--- a/browser/metro/shell/testing/Makefile.in
+++ /dev/null
@@ -1,7 +0,0 @@
-# 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/.
-
-# don't use moz glue libs
-MOZ_GLUE_LDFLAGS =
-MOZ_GLUE_PROGRAM_LDFLAGS =
--- a/build/autoconf/toolchain.m4
+++ b/build/autoconf/toolchain.m4
@@ -68,16 +68,24 @@ CLANG_CL=
 if test "$compiler" = "clang"; then
     GNU_CC=1
     GNU_CXX=1
     CLANG_CC=1
     CLANG_CXX=1
 fi
 if test "$compiler" = "clang-cl"; then
     CLANG_CL=1
+    # We force clang-cl to emulate Visual C++ 2013 in configure.in, but that
+    # is based on the CLANG_CL variable defined here, so make sure that we're
+    # getting the right version here manually.
+    CC_VERSION=1800
+    CXX_VERSION=1800
+    # Build on clang-cl with MSVC 2013 with fallback emulation.
+    CFLAGS="$CFLAGS -fmsc-version=1800 -fallback"
+    CXXFLAGS="$CXXFLAGS -fmsc-version=1800 -fallback"
 fi
 
 if test "$GNU_CC"; then
     if `$CC -print-prog-name=ld` -v 2>&1 | grep -c GNU >/dev/null; then
         GCC_USE_GNU_LD=1
     fi
 fi
 
--- a/build/clang-plugin/Makefile.in
+++ b/build/clang-plugin/Makefile.in
@@ -1,17 +1,15 @@
 # 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/.
 
 # LLVM_CXXFLAGS comes with its own optimization flags.
 MOZ_OPTIMIZE =
 
-MOZ_GLUE_LDFLAGS =
-
 include $(topsrcdir)/config/config.mk
 
 # In the current moz.build world, we need to override essentially every
 # variable to limit ourselves to what we need to build the clang plugin.
 OS_CXXFLAGS := $(LLVM_CXXFLAGS) -fno-rtti -fno-exceptions
 OS_COMPILE_CXXFLAGS :=
 OS_LDFLAGS := $(LLVM_LDFLAGS) $(CLANG_LDFLAGS)
 DSO_LDOPTS := -shared
--- a/build/docs/defining-binaries.rst
+++ b/build/docs/defining-binaries.rst
@@ -114,21 +114,16 @@ On OSX, one may want to create a special
 This is done with the ``Framework`` template.
 
    Framework('foo')
 
 With a ``Framework`` name of ``foo``, the framework file name will be ``foo``.
 This template however affects the behavior on all platforms, so it needs to
 be set only on OSX.
 
-Another special kind of library, XPCOM-specific, are XPCOM components. One can
-build such a component with the ``XPCOMBinaryComponent`` template.
-
-   XPCOMBinaryComponent('foo')
-
 
 Executables
 ===========
 
 Executables, a.k.a. programs, are, in the simplest form, defined with the
 ``Program`` template.
 
    Program('foobar')
@@ -288,8 +283,33 @@ instead of the ``Library`` name. This on
           'mylib',
           'otherlib',
       ]
 
 On e.g. Linux, the above ``myprog`` will have DT_NEEDED markers for
 ``libmylib.so`` and ``libfoo.so`` instead of ``libmylib.so`` and
 ``libotherlib.so`` if there weren't a ``SONAME``. This means the runtime
 requirement for ``myprog`` is ``libfoo.so`` instead of ``libotherlib.so``.
+
+
+Gecko-related binaries
+======================
+
+Some programs or libraries are totally independent of Gecko, and can use the
+above mentioned templates. Others are Gecko-related in some way, and may
+need XPCOM linkage, mozglue. These things are tedious. A set of additional
+templates exists to ease defining such programs and libraries. They are
+essentially the same as the above mentioned templates, prefixed with "Gecko":
+
+  - ``GeckoProgram``
+  - ``GeckoSimplePrograms``
+  - ``GeckoCppUnitTests``
+  - ``GeckoSharedLibrary``
+  - ``GeckoFramework``
+
+There is also ``XPCOMBinaryComponent`` for XPCOM components, which is a
+special kind of library.
+
+All the Gecko-prefixed templates take the same arguments as their
+non-Gecko-prefixed counterparts, and can take a few more arguments
+for non-standard cases. See the definition of ``GeckoBinary`` in
+build/gecko_templates.mozbuild for more details, but most usecases
+should not require these additional arguments.
new file mode 100644
--- /dev/null
+++ b/build/gecko_templates.mozbuild
@@ -0,0 +1,155 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+@template
+def GeckoBinary(linkage='dependent', msvcrt='dynamic', mozglue=None):
+    '''Template for Gecko-related binaries.
+
+    This template is meant to be used in other templates.
+
+    `linkage` indicates the wanted xpcom linkage type. Valid values are
+    'dependent', 'standalone' or None. 'dependent' is the default. It is
+    used for e.g. XPCOM components and executables with direct dependencies
+    on libxul. Most executables should use the 'standalone' linkage, which
+    uses the standalone XPCOM glue to load libxul. None means no XPCOM glue
+    or libxul linkage at all.
+
+    `msvcrt` indicates which Microsoft Visual Studio CRT, for Windows build,
+    ought to be linked: 'static' or 'dynamic'.
+
+    `mozglue` indicates whether to link against the mozglue library, and if
+    so, what linkage to apply. Valid values are None (mozglue not linked),
+    'program' (mozglue linked to an executable program), or 'library' (mozglue
+    linked to a shared library).
+    '''
+    if msvcrt == 'dynamic' or CONFIG['OS_ARCH'] != 'WINNT':
+        xpcomglue = 'xpcomglue'
+    elif msvcrt == 'static':
+        USE_STATIC_LIBS = True
+        xpcomglue = 'xpcomglue_staticruntime'
+        if not CONFIG['GNU_CC']:
+            mozglue = None
+    else:
+        error('msvcrt must be "dynamic" or "static"')
+
+    if linkage == 'dependent':
+        USE_LIBS += [
+            'mozalloc',
+            'nspr',
+            '%s_s' % xpcomglue,
+            'xul',
+        ]
+    elif linkage == 'standalone':
+        DEFINES['XPCOM_GLUE'] = True
+
+        USE_LIBS += [
+            xpcomglue,
+        ]
+    elif linkage != None:
+        error('`linkage` must be "dependent", "standalone" or None')
+
+    if mozglue:
+        if CONFIG['JS_STANDALONE']:
+            pass
+        elif CONFIG['MOZ_CRT']:
+            if msvcrt == 'dynamic':
+                USE_LIBS += ['mozcrt']
+            elif msvcrt == 'static':
+                USE_LIBS += ['mozglue']
+            else:
+                error('`msvcrt` must be "dynamic" or "static"')
+        else:
+            LDFLAGS += CONFIG['MOZ_GLUE_WRAP_LDFLAGS']
+            if mozglue == 'program':
+                USE_LIBS += ['mozglue']
+                if CONFIG['MOZ_GLUE_IN_PROGRAM']:
+                    if CONFIG['GNU_CC']:
+                        LDFLAGS += ['-rdynamic']
+                    if CONFIG['MOZ_MEMORY']:
+                        USE_LIBS += ['memory']
+                    if CONFIG['MOZ_LINKER']:
+                        OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
+            elif mozglue == 'library':
+                if not CONFIG['MOZ_GLUE_IN_PROGRAM']:
+                    USE_LIBS += ['mozglue']
+            else:
+                error('`mozglue` must be "program" or "library"')
+
+
+@template
+def GeckoProgram(name, linkage='standalone', **kwargs):
+    '''Template for program executables related to Gecko.
+
+    `name` identifies the executable base name.
+
+    See the documentation for `GeckoBinary` for other possible arguments,
+    with the notable difference that the default for `linkage` is 'standalone'.
+    '''
+    Program(name)
+
+    GeckoBinary(linkage=linkage, mozglue='program', **kwargs)
+
+
+@template
+def GeckoSimplePrograms(names, **kwargs):
+    '''Template for simple program executables related to Gecko.
+
+    `names` identifies the executable base names for each executable.
+
+    See the documentation for `GeckoBinary` for other possible arguments.
+    '''
+    SimplePrograms(names)
+
+    GeckoBinary(mozglue='program', **kwargs)
+
+
+@template
+def GeckoCppUnitTests(names, **kwargs):
+    '''Template for C++ unit tests related to Gecko.
+
+    `names` identifies the executable base names for each executable.
+
+    See the documentation for `GeckoBinary` for other possible arguments.
+    '''
+    CppUnitTests(names)
+
+    GeckoBinary(mozglue='program', **kwargs)
+
+
+@template
+def GeckoSharedLibrary(name, **kwargs):
+    '''Template for shared libraries related to Gecko.
+
+    `name` identifies the library base name.
+    See the documentation for `GeckoBinary` for other possible arguments.
+    '''
+    SharedLibrary(name)
+
+    GeckoBinary(mozglue='library', **kwargs)
+
+
+@template
+def GeckoFramework(name, **kwargs):
+    '''Template for OSX frameworks related to Gecko.
+
+    `name` identifies the library base name.
+    See the documentation for `GeckoBinary` for other possible arguments.
+    '''
+    Framework(name)
+
+    GeckoBinary(mozglue='library', **kwargs)
+
+
+@template
+def XPCOMBinaryComponent(name):
+    '''Template defining an XPCOM binary component for Gecko.
+
+    `name` is the name of the component.
+    '''
+    GeckoSharedLibrary(name)
+
+    IS_COMPONENT = True
+
--- a/build/templates.mozbuild
+++ b/build/templates.mozbuild
@@ -110,33 +110,9 @@ def HostSimplePrograms(names, ext='.cpp'
 
 
 @template
 def HostLibrary(name):
     '''Template for build tools libraries.'''
     HOST_LIBRARY_NAME = name
 
 
-@template
-def GeckoBinary():
-    '''Template for binaries using Gecko.
-
-    This template is meant to be used in other templates.
-    '''
-    USE_LIBS += [
-        'mozalloc',
-        'nspr',
-        'xpcomglue_s',
-        'xul',
-    ]
-
-
-@template
-def XPCOMBinaryComponent(name):
-    '''Template defining an XPCOM binary component for Gecko.
-
-    name is the name of the component.
-    '''
-    SharedLibrary(name)
-
-    GeckoBinary()
-
-    IS_COMPONENT = True
+include('gecko_templates.mozbuild')
--- a/build/unix/elfhack/Makefile.in
+++ b/build/unix/elfhack/Makefile.in
@@ -2,18 +2,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/.
 
 INTERNAL_TOOLS = 1
 
 OS_CXXFLAGS := $(filter-out -fno-exceptions,$(OS_CXXFLAGS)) -fexceptions
 
-WRAP_LDFLAGS=
-
 include $(topsrcdir)/config/rules.mk
 
 test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): %$(DLL_SUFFIX): %.$(OBJ_SUFFIX) elfhack
 	$(MKSHLIB) $(LDFLAGS) $< -nostartfiles
 	@echo ===
 	@echo === If you get failures below, please file a bug describing the error
 	@echo === and your environment \(compiler and linker versions\), and use
 	@echo === --disable-elf-hack until this is fixed.
--- a/build/unix/moz.build
+++ b/build/unix/moz.build
@@ -5,10 +5,8 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION'] or CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
     DIRS += ['stdc++compat']
 
 if CONFIG['USE_ELF_HACK']:
     DIRS += ['elfhack']
 
-TEST_DIRS += ['test']
-
deleted file mode 100644
--- a/build/unix/test/Makefile.in
+++ /dev/null
@@ -1,38 +0,0 @@
-# -*- makefile -*-
-#
-# 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 $(topsrcdir)/config/rules.mk
-
-##################################################
-## Gather a list of tests, generate timestamp deps
-##################################################
-TS=.ts
-ifneq (,$(findstring check,$(MAKECMDGOALS)))
-          allsrc = $(wildcard $(srcdir)/*)
-       tests2run = $(notdir $(filter %.tpl,$(allsrc)))
-  check_targets += $(addprefix $(TS)/,$(tests2run))
-endif
-
-check:: $(TS) $(check_targets)
-
-#############################################
-# Only invoke tests when sources have changed
-#############################################
-$(TS)/%: $(srcdir)/%
-	$(PERL) $(srcdir)/runtest $<
-	@touch $@
-
-#####################################################
-## Extra dep needed to synchronize parallel execution
-#####################################################
-$(TS): $(TS)/.done
-$(TS)/.done:
-	$(MKDIR) -p $(dir $@)
-	touch $@
-
-GARBAGE_DIRS += $(TS)
-
-# EOF
deleted file mode 100644
--- a/build/unix/test/moz.build
+++ /dev/null
@@ -1,6 +0,0 @@
-# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# 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/.
-
deleted file mode 100644
--- a/build/unix/test/runtest
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/usr/bin/env perl
-###########################################################################
-## Intent:
-##   Test::Harness is a testing wrapper that will process output
-##   from Test.pm module tests.  Sumarize results, report stats
-##   and exit with overall status for the testing suites.
-##
-## Run testing suite:
-##   % make clean test
-##   % perl runtest
-##
-## Run Individual tests
-##   % perl tUtils0
-###########################################################################
-
-##----------------------------##
-##---] CORE/CPAN INCLUDES [---##
-##----------------------------##
-use strict;
-use warnings;
-use Getopt::Long;
-
-use Test::Harness;
-
-##-------------------##
-##---]  EXPORTS  [---##
-##-------------------##
-our $VERSION = qw(1.0);
-use FindBin;
-
-##-------------------##
-##---]  GLOBALS  [---##
-##-------------------##
-my %argv;
-
-##----------------##
-##---]  MAIN  [---##
-##----------------##
-unless(GetOptions(\%argv,
-		  qw(debug|d)
-		 ))
-{
-    print "Usage: $0\n";
-    print "  --debug  Enable debug mode\n";
-    exit 1;
-}
-
-if (2 > $Test::Harness::VERSION)
-{
-    print "Unit tests will not be run, Test::Harness is too old\n"
-	if ($argv{debug});
-    exit 0;
-}
-
-
-my @tests;
-
-########################################
-## Gather a list of tests if none passed
-########################################
-unless (@tests = @ARGV)
-{
-  local *D;
-    opendir(D, '.');
-    while($_ = readdir(D)) {
-	next unless /.t\S+$/;
-	next if (/\.ts$/);
-	push(@tests, $_);
-    }
-    closedir(D);
-}
-
-###############################################
-## Glob a list of tests when directories passed
-###############################################
-my @tmp;
-foreach (@tests)
-{
-  local *D;
-    if (-d $_ && (my $dir = $_))
-    {
-        opendir(D, $_) || die "opendir(D) failed: $!";
-	my @tests = grep(/\.t[^\.\s]+/o, readdir(D));
-	closedir(D);
-	push(@tmp, map{ join('/', $dir, $_); } @tests);
-    } else {
-        push(@tmp, $_);
-    }
-}
-@tests = @tmp;
-
-print "$0: @ARGV\n" if ($argv{debug});
-runtests(@tests);
-
-# EOF
deleted file mode 100644
--- a/build/unix/test/uniq.tpl
+++ /dev/null
@@ -1,151 +0,0 @@
-#!/usr/bin/env perl
-###########################################################################
-## Intent: Unit test to verify uniq.pl
-###########################################################################
-
-##----------------------------##
-##---] CORE/CPAN INCLUDES [---##
-##----------------------------##
-use strict;
-use warnings;
-use Cwd;
-use Getopt::Long;  # GetOptions
-
-use Test;
-sub BEGIN { plan tests => 12 }
-
-##-------------------##
-##---]  EXPORTS  [---##
-##-------------------##
-our $VERSION = qw(1.0);
-
-##------------------##
-##---] INCLUDES [---##
-##------------------##
-use FindBin;
-
-##-------------------##
-##---]  GLOBALS  [---##
-##-------------------##
-my %argv;
-
-
-###########################################################################
-## Intent: Run the arch command for output
-##
-## Returns:
-##    0   on success
-##    $?  command shell exit status
-###########################################################################
-sub uniq_pl
-{
-    my $cmd = "perl $FindBin::RealBin/../uniq.pl @_";
-    print "Running: $cmd\n" if ($argv{debug});
-    my @tmp = `$cmd 2>&1`;
-    my @output = map{ split(/\s+/o); } @tmp;
-    wantarray ? @output : "@output";
-} # uniq_pl
-
-###########################################################################
-## Intent:
-##
-## Returns:
-##    0 on success
-###########################################################################
-sub check_uniq
-{
-    print STDERR "Running test: check_uniq\n" if ($argv{debug});
-
-    # TODO: improve test, uniq.pl regexpr handling not quite right
-
-    my @todo =
-      (
-       [ '', qw(a a/b a/b/c) ] => [  qw(a a/b a/b/c) ],
-       [ '', qw(a/b a a/b/c) ] => [ qw(a/b a a/b/c) ],
-       [ '', qw(a/b/c a/b a) ] => [ qw(a/b/c a/b a) ],
-
-       [ '', qw(a a/b a/b/c a/b a) ] => [  qw(a a/b a/b/c) ], # dup removal
-
-       [ '-s', qw(a a/b a/b/c) ] => [ qw(a a/b a/b/c)  ],
-       [ '-s', qw(a/b a a/b/c) ] => [ qw(a a/b a/b/c) ],
-       [ '-s', qw(a/b/c a/b a) ] => [ qw(a a/b a/b/c) ],
-
-       [ '-r', qw(a a/b a/b/c) ] => [ qw(a) ],
-       [ '-r', qw(a/b a a/b/c) ] => [ qw(a/b a) ],
-       [ '-r', qw(a/b/c a/b a) ] => [ qw(a/b/c a/b a) ],
-
-       [ '-r', qw(. .. a/b ../a aa/bb) ] => [ qw(. .. a/b aa/bb) ],
-       [ '-r', qw(.. a/b ../a . aa/bb) ] => [ qw(.. a/b . aa/bb) ],
-      );
-
-    my $ct=1;
-    while (@todo)
-    {
-        my ($a, $b) = splice(@todo, 0, 2);
-	my @args = @{ $a };
-	my @exp = @{ $b };
-
-	my @out = uniq_pl(@args);
-#	compareExp(\@out, \@exp, 'Failed on line ' . __LINE__ . ", dataset $ct");
-	if (0 && 7 == $ct)
-	  {
-	    print STDERR "\n";
-	    print STDERR map{ "args> $_\n" }@args;
-	    print STDERR "\n";
-	    print STDERR map{ "exp> $_\n" }@exp;
-	    print STDERR "\n";
-	    print STDERR map{ "out> $_\n" }@out;
-	  }
-
-	ok("@out", "@exp", 'Failed on line ' . __LINE__ . ", dataset $ct");
-	$ct++;
-    }
-
-} # check_uniq
-
-###########################################################################
-## Intent: Smoke tests for the unittests module
-###########################################################################
-sub smoke
-{
-    print STDERR "Running test: smoke()\n" if ($argv{debug});
-} # smoke()
-
-###########################################################################
-## Intent: Intitialize global test objects and consts
-###########################################################################
-sub init
-{
-    print "Running: init()\n" if ($argv{debug});
-#    testplan(24, 0);
-} # init()
-
-##----------------##
-##---]  MAIN  [---##
-##----------------##
-unless(GetOptions(\%argv,
-		  qw(
-		     debug|d
-                     manual
-		     test=s@
-		     verbose
-		     )))
-{
-    print "USAGE: $0\n";
-    print "  --debug    Enable script debug mode\n";
-    print "  --fail     Force a testing failure condition\n";
-    print "  --manual   Also run disabled tests\n";
-    print "  --smoke    Run smoke tests then exit\n";
-    print "  --test     Run a list of tests by function name\n";
-    print "  --verbose  Enable script verbose mode\n";
-    exit 1;
-}
-
-init();
-testbyname(@{ $argv{test} }) if ($argv{test});
-smoke();
-
-check_uniq();
-ok(1, 0, 'Forced failure by command line arg --fail') if ($argv{fail});
-
-# EOF
deleted file mode 100755
--- a/build/unix/uniq.pl
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env perl
-
-# 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/.
-
-##----------------------------##
-##---] CORE/CPAN INCLUDES [---##
-##----------------------------##
-use strict;
-use warnings;
-use Getopt::Long;
-
-##-------------------##
-##---]  EXPORTS  [---##
-##-------------------##
-our $VERSION = qw(1.1);
-
-##-------------------##
-##---]  GLOBALS  [---##
-##-------------------##
-my %argv;
-my $modver = $Getopt::Long::VERSION || 0;
-my $isOldGetopt = ($modver eq '2.25') ? 1 : 0;
-
-###########################################################################
-## Intent: Script init function
-###########################################################################
-sub init
-{
-    if ($isOldGetopt)
-    {
-	# mozilla.build/mingw perl in need of an upgrade
-	# emulate Getopt::Long switch|short:init
-	foreach (qw(debug regex sort))
-	{
-	    if (defined($argv{$_}))
-	    {
-		$argv{$_} ||= 1;
-	    }
-	}
-    }
-} # init
-
-##----------------##
-##---]  MAIN  [---##
-##----------------##
-my @args = ($isOldGetopt)
-    ? qw(debug|d regex|r sort|s)
-    : qw(debug|d:1 regex|r:1 sort|s:1)
-    ;
-
-unless(GetOptions(\%argv, @args))
-{
-    print "Usage: $0\n";
-    print "  --sort   Sort list elements early\n";
-    print "  --regex  Exclude subdirs by pattern\n";
-}
-
-init();
-my $debug = $argv{debug} || 0;
-
-my %seen;
-my @out;
-my @in = ($argv{sort}) ? sort @ARGV : @ARGV;
-
-foreach my $d (@in)
-{
-    next if ($seen{$d}++);
-
-    print "   arg is $d\n" if ($debug);
-
-    if ($argv{regex})
-    {
-        my $found = 0;
-        foreach my $dir (@out)
-	{
-	    my $dirM = quotemeta($dir);
-            $found++, last if ($d eq $dir || $d =~ m!^${dirM}\/!);
-        }
-	print "Adding $d\n" if ($debug && !$found);
-        push @out, $d if (!$found);
-    } else {
-	print "Adding: $d\n" if ($debug);
-        push(@out, $d);
-    }
-}
-
-print "@out\n"
-
-# EOF
new file mode 100755
--- /dev/null
+++ b/build/unix/uniq.py
@@ -0,0 +1,10 @@
+#! /usr/bin/env python
+# 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/.
+
+'''Prints the given arguments in sorted order with duplicates removed.'''
+
+import sys
+
+print(' '.join(sorted(set(sys.argv[1:]))))
--- a/build/win32/Makefile.in
+++ b/build/win32/Makefile.in
@@ -1,14 +1,12 @@
 # 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/.
 
-MOZ_GLUE_LDFLAGS =
-
 include $(topsrcdir)/config/rules.mk
 
 ifdef WIN32_REDIST_DIR
 
 REDIST_FILES = \
   $(MSVC_C_RUNTIME_DLL) \
   $(MSVC_CXX_RUNTIME_DLL) \
   $(NULL)
deleted file mode 100644
--- a/build/win32/crashinjectdll/Makefile.in
+++ /dev/null
@@ -1,5 +0,0 @@
-# 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/.
-
-MOZ_GLUE_LDFLAGS =
deleted file mode 100644
--- a/build/win32/vmwarerecordinghelper/Makefile.in
+++ /dev/null
@@ -1,5 +0,0 @@
-# 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/.
-
-MOZ_GLUE_LDFLAGS =
--- a/config/config.mk
+++ b/config/config.mk
@@ -229,30 +229,20 @@ ifdef HAVE_64BIT_BUILD
 OS_LDFLAGS = -DEBUG -OPT:REF,ICF
 else
 OS_LDFLAGS = -DEBUG -OPT:REF
 endif
 endif # NS_TRACE_MALLOC || MOZ_DMD
 
 endif # MOZ_DEBUG
 
-# We don't build a static CRT when building a custom CRT,
-# it appears to be broken. So don't link to jemalloc if
-# the Makefile wants static CRT linking.
-ifeq ($(MOZ_MEMORY)_$(USE_STATIC_LIBS),1_1)
-# Disable default CRT libs and add the right lib path for the linker
-MOZ_GLUE_LDFLAGS=
-endif
-
 endif # WINNT && !GNU_CC
 
-ifdef MOZ_GLUE_PROGRAM_LDFLAGS
+ifdef MOZ_GLUE_IN_PROGRAM
 DEFINES += -DMOZ_GLUE_IN_PROGRAM
-else
-MOZ_GLUE_PROGRAM_LDFLAGS=$(MOZ_GLUE_LDFLAGS)
 endif
 
 #
 # Build using PIC by default
 #
 _ENABLE_PIC=1
 
 # PGO on MSVC is opt-in
--- a/config/external/nss/Makefile.in
+++ b/config/external/nss/Makefile.in
@@ -103,21 +103,16 @@ else
 NSPR_INCLUDE_DIR = $(ABS_DIST)/include/nspr
 endif
 NSPR_LIB_DIR = $(firstword $(filter -L%,$(NSPR_LIBS)))
 ifneq (,$(strip $(NSPR_LIB_DIR)))
 NSPR_LIB_DIR := $(subst -L,,$(subst -L$(DIST),-L$(ABS_DIST),$(NSPR_LIB_DIR)))
 else
 NSPR_LIB_DIR = $(ABS_DIST)/lib
 endif
-# Can't pass this in DEFAULT_GMAKE_FLAGS because that overrides
-# definitions in NSS, so just export it into the sub-make's environment.
-ifeq (WINNT_1,$(OS_TARGET)_$(MOZ_MEMORY))
-export DLLFLAGS
-endif
 
 # To get debug symbols from NSS
 export MOZ_DEBUG_SYMBOLS
 
 DEFAULT_GMAKE_FLAGS =
 DEFAULT_GMAKE_FLAGS += CC='$(CC)'
 DEFAULT_GMAKE_FLAGS += SOURCE_MD_DIR=$(ABS_DIST)
 DEFAULT_GMAKE_FLAGS += SOURCE_MDHEADERS_DIR=$(NSPR_INCLUDE_DIR)
@@ -217,19 +212,27 @@ DEFAULT_GMAKE_FLAGS += \
 	OS_PTHREAD= \
 	$(NULL)
 
 DEFAULT_GMAKE_FLAGS += ARCHFLAG='$(CFLAGS) -DCHECK_FORK_GETPID -DRTLD_NOLOAD=0 -include $(topsrcdir)/security/manager/android_stub.h'
 endif
 endif
 
 ifdef WRAP_LDFLAGS
+NSS_EXTRA_LDFLAGS += $(WRAP_LDFLAGS)
+endif
+
+ifdef MOZ_GLUE_WRAP_LDFLAGS
+NSS_EXTRA_LDFLAGS += $(SHARED_LIBS:$(DEPTH)%=$(MOZ_BUILD_ROOT)%) $(MOZ_GLUE_WRAP_LDFLAGS)
+endif
+
+ifneq (,$(WRAP_LDFLAGS)$(MOZ_GLUE_WRAP_LDFLAGS))
 DEFAULT_GMAKE_FLAGS += \
-	LDFLAGS='$(LDFLAGS) $(WRAP_LDFLAGS)' \
-	DSO_LDOPTS='$(DSO_LDOPTS) $(LDFLAGS) $(WRAP_LDFLAGS)' \
+	LDFLAGS='$(LDFLAGS) $(NSS_EXTRA_LDFLAGS)' \
+	DSO_LDOPTS='$(DSO_LDOPTS) $(LDFLAGS) $(NSS_EXTRA_LDFLAGS)' \
 	$(NULL)
 endif
 
 DEFAULT_GMAKE_FLAGS += FREEBL_NO_DEPEND=0
 ifeq ($(OS_TARGET),Linux)
 DEFAULT_GMAKE_FLAGS += FREEBL_LOWHASH=1
 endif
 
@@ -352,16 +355,28 @@ GARBAGE += \
   $(NULL)
 endif # GCC_USE_GNU_LD
 endif # WINNT
 
 endif # MOZ_FOLD_LIBS
 
 include $(topsrcdir)/config/rules.mk
 
+# Can't pass this in DEFAULT_GMAKE_FLAGS because that overrides
+# definitions in NSS, so just export it into the sub-make's environment.
+ifeq (WINNT_1,$(OS_TARGET)_$(MOZ_MEMORY))
+ifdef MOZ_CRT
+# OS_LIBS comes from having mozcrt as a dependency in moz.build.
+DLLFLAGS := $(OS_LIBS)
+else
+DLLFLAGS := -LIBPATH:$(ABS_DIST)/lib -DEFAULTLIB:mozglue
+endif
+export DLLFLAGS
+endif
+
 ifdef MOZ_FOLD_LIBS
 # Force the linker to include everything from the static libraries.
 EXPAND_LIBS_EXEC += --extract
 
 $(SHARED_LIBRARY): $(addprefix $(DEPTH)/security/,$(NSS_STATIC_LIBS))
 
 ifdef IMPORT_LIB_SUFFIX
 IMPORT_PREFIX = $(LIB_PREFIX)
--- a/config/external/nss/moz.build
+++ b/config/external/nss/moz.build
@@ -5,17 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 DIRS += ['crmf']
 
 if CONFIG['MOZ_NATIVE_NSS']:
     Library('nss')
     OS_LIBS += CONFIG['NSS_LIBS']
 elif CONFIG['MOZ_FOLD_LIBS']:
-    SharedLibrary('nss')
+    GeckoSharedLibrary('nss', linkage=None)
     # TODO: The library name can be changed when bug 845217 is fixed.
     SHARED_LIBRARY_NAME = 'nss3'
 
     SDK_LIBRARY = True
 
     # Normally, there should be /something/ to ensure nspr is built
     # before this directory, but since nspr is built during "export",
     # it actually doesn't matter.
--- a/config/external/sqlite/Makefile.in
+++ b/config/external/sqlite/Makefile.in
@@ -21,15 +21,8 @@ GARBAGE += \
 # Convert to the format we need for ld.
 $(LD_VERSION_SCRIPT): $(topsrcdir)/db/sqlite3/src/sqlite.def
 	@$(call py_action,convert_def_file, \
 	  $(DEFINES) $(ACDEFINES) $(XULPPFLAGS) -o $@ $^)
 
 endif
 endif
 endif
-
-
-ifeq (Darwin,$(OS_TARGET))
-# On OSX, with jemalloc enabled, having sqlite linked against mozglue
-# causes crashes in NSS standalone tools.
-MOZ_GLUE_LDFLAGS =
-endif
deleted file mode 100644
--- a/config/make-atom-strings.pl
+++ /dev/null
@@ -1,57 +0,0 @@
-#! perl
-# 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/.
-
-
-# Converts a list of atoms in the form:
-# // OUTPUT_CLASS=<classname>
-# // MACRO_NAME=<macro>
-# <macroname>(atomName, "String")
-# <macroname>(atomName2, "String2")
-#
-# into a file suitable for gperf using static atoms
-#
-# usage:
-# make-atom-strings < file.h > file.gperf
-#
-# the lines in the C++ comments define two variables:
-# OUTPUT_CLASS is the class who has all the atoms as members
-# MACRO_NAME is the macro to look for in the rest of the file
-#
-# for example
-# // OUTPUT_CLASS=nsHTMLAtoms
-# // MACRO_NAME=HTML_ATOM
-# HTML_ATOM(a, "a")
-# HTML_ATOM(body, "body")
-#
-# etc...
-#
-# this will generate a file that looks like:
-# struct nsStaticAtom ( const char* mValue; nsIAtom** aAtom; }
-# %%
-# "a", &nsHTMLAtoms::a
-# "body", &nsHTMLAtoms::body
-#
-# etc...
-#
-# the output can be plugged into gperf to generate a perfect hash
-
-print "struct nsStaticAtom {const char* mValue; nsIAtom** aAtom; };\n";
-print "%%\n";
-
-my $classname, $macroname;
-
-while (<>) {
-  chop;
-  if (/OUTPUT_CLASS=(\S+)/) {
-    $classname=$1;
-  } elsif (/MACRO_NAME=(\S+)/) {
-    $macroname=$1;
-  }
-  elsif ($classname && $macroname &&
-         /$macroname\((\S+),\s*\"(.*?)\"\s*\)/) {
-    my ($str, $atom) = ($2, $1);
-    print "\"$str\", (nsIAtom**)&${classname}::$atom\n";
-  }
-}
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -153,10 +153,13 @@ toolkit/library/target: widget/gtk/mozgt
 endif
 ifdef MOZ_LDAP_XPCOM
 ldap/target: config/external/nss/target mozglue/build/target
 toolkit/library/target: ldap/target
 endif
 ifeq ($(MOZ_REPLACE_MALLOC_LINKAGE),dummy library)
 mozglue/build/target memory/replace/logalloc/replay/target: memory/replace/dummy/target
 endif
+ifdef MOZ_CRT
+mozglue/crt/target: mozglue/build/target
+endif
 
 endif
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -636,18 +636,16 @@ ifdef GNU_CC
 # Force rebuilding libraries and programs in both passes because each
 # pass uses different object files.
 $(PROGRAM) $(SHARED_LIBRARY) $(LIBRARY): FORCE
 endif
 endif
 
 endif # NO_PROFILE_GUIDED_OPTIMIZE
 
-MOZ_PROGRAM_LDFLAGS += $(MOZ_GLUE_PROGRAM_LDFLAGS)
-
 ##############################################
 
 checkout:
 	$(MAKE) -C $(topsrcdir) -f client.mk checkout
 
 clean clobber realclean clobber_all::
 	-$(RM) $(ALL_TRASH)
 	-$(RM) -r $(ALL_TRASH_DIRS)
--- a/configure.in
+++ b/configure.in
@@ -509,20 +509,17 @@ case "$target" in
         AC_SUBST(MSVS_VERSION)
         AC_SUBST(MSVC_C_RUNTIME_DLL)
         AC_SUBST(MSVC_CXX_RUNTIME_DLL)
 
         # Disable SEH on clang-cl because it doesn't implement them yet.
         if test -z "$CLANG_CL"; then
             AC_DEFINE(HAVE_SEH_EXCEPTIONS)
         else
-            # Build on clang-cl with MSVC 2013 with fallback emulation.
-            CFLAGS="$CFLAGS -fmsc-version=1800 -fallback"
-            CXXFLAGS="$CXXFLAGS -fmsc-version=1800 -fallback"
-            # Send our CFLAGS to NSS too
+            # Send our CFLAGS to NSS
             MOZ_CFLAGS_NSS=1
             AC_DEFINE_UNQUOTED(GTEST_HAS_SEH, 0)
         fi
 
         if test -n "$WIN32_REDIST_DIR"; then
           if test ! -d "$WIN32_REDIST_DIR"; then
             AC_MSG_ERROR([Invalid Win32 Redist directory: ${WIN32_REDIST_DIR}])
           fi
@@ -6268,17 +6265,17 @@ fi
 
 if test `echo "$MOZ_EXTENSIONS" | grep -c gio` -ne 0; then
     MOZ_GIO_COMPONENT=1
     MOZ_EXTENSIONS=`echo $MOZ_EXTENSIONS | sed -e 's|gio||'`
 fi
 AC_SUBST(MOZ_GIO_COMPONENT)
 
 dnl Remove dupes
-MOZ_EXTENSIONS=`${PERL} ${srcdir}/build/unix/uniq.pl ${MOZ_EXTENSIONS}`
+MOZ_EXTENSIONS=`$PYTHON ${srcdir}/build/unix/uniq.py ${MOZ_EXTENSIONS}`
 
 dnl Ensure every extension exists, to avoid mostly-inscrutable error messages
 dnl when trying to build a nonexistent extension.
 for extension in $MOZ_EXTENSIONS; do
     if test ! -d "${srcdir}/extensions/${extension}"; then
         AC_MSG_ERROR([Unrecognized extension provided to --enable-extensions: ${extension}.])
     fi
 done
@@ -7070,38 +7067,25 @@ MOZ_ARG_ENABLE_BOOL(jemalloc,
 [  --enable-jemalloc       Replace memory allocator with jemalloc],
     MOZ_MEMORY=1,
     MOZ_MEMORY=)
 
 if test "$NS_TRACE_MALLOC"; then
     MOZ_MEMORY=
 fi
 
-if test "${OS_TARGET}" = "Android"; then
-  dnl On Android, we use WRAP_LDFLAGS to link everything to mozglue
-  :
-elif test "${OS_TARGET}" = "WINNT" -o "${OS_TARGET}" = "Darwin"; then
-  dnl On Windows and OSX, we want to link all our binaries against mozglue
-  MOZ_GLUE_LDFLAGS='$(call EXPAND_LIBNAME_PATH,mozglue,$(LIBXUL_DIST)/lib)'
-else
-  dnl On other Unix systems, we only want to link executables against mozglue
-  MOZ_GLUE_PROGRAM_LDFLAGS='$(call EXPAND_LIBNAME_PATH,mozglue,$(LIBXUL_DIST)/lib)'
-  dnl On other Unix systems, where mozglue is a static library, jemalloc is
-  dnl separated for the SDK, so we need to add it here.
-  if test "$MOZ_MEMORY" = 1 -o \( "$LIBXUL_SDK" -a -f "$LIBXUL_SDK/lib/${LIB_PREFIX}memory.${LIB_SUFFIX}" \); then
-    MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS "'$(call EXPAND_LIBNAME_PATH,memory,$(LIBXUL_DIST)/lib)'
-  fi
-  if test -n "$GNU_CC"; then
-    dnl And we need mozglue symbols to be exported.
-    MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS -rdynamic"
-  fi
-  if test "$MOZ_LINKER" = 1; then
-    MOZ_GLUE_PROGRAM_LDFLAGS="$MOZ_GLUE_PROGRAM_LDFLAGS $MOZ_ZLIB_LIBS"
-  fi
-fi
+case "${OS_TARGET}" in
+Android|WINNT|Darwin)
+  MOZ_GLUE_IN_PROGRAM=
+  ;;
+*)
+  dnl On !Android !Windows !OSX, we only want to link executables against mozglue
+  MOZ_GLUE_IN_PROGRAM=1
+  ;;
+esac
 
 dnl ========================================================
 dnl = Enable dynamic replacement of malloc implementation
 dnl ========================================================
 if test -n "$NIGHTLY_BUILD" -a -n "$MOZ_MEMORY"; then
   # Enable on central for the debugging opportunities it adds.
   MOZ_REPLACE_MALLOC=1
 fi
@@ -7219,17 +7203,16 @@ else
     AC_DEFINE(MOZ_MEMORY_BSD)
     ;;
   *-android*|*-linuxandroid*)
     AC_DEFINE(MOZ_MEMORY_LINUX)
     AC_DEFINE(MOZ_MEMORY_ANDROID)
     if test -n "$gonkdir"; then
       AC_DEFINE(MOZ_MEMORY_GONK)
     fi
-    MOZ_GLUE_LDFLAGS=
     ;;
   *-*linux*)
     AC_DEFINE(MOZ_MEMORY_LINUX)
     ;;
   *-netbsd*)
     AC_DEFINE(MOZ_MEMORY_BSD)
     ;;
   *-solaris*)
@@ -7238,47 +7221,44 @@ else
   *-mingw*)
     AC_DEFINE(MOZ_MEMORY_WINDOWS)
     export MOZ_NO_DEBUG_RTL=1
     WIN32_CRT_LIBS="msvcrt.lib msvcprt.lib"
     dnl Look for a broken crtdll.obj
     WIN32_CRTDLL_FULLPATH=`lib -nologo -list $WIN32_CRT_LIBS | grep crtdll\\.obj`
     lib -NOLOGO -OUT:crtdll.obj $WIN32_CRT_LIBS -EXTRACT:$WIN32_CRTDLL_FULLPATH
     if grep -q '__imp__\{0,1\}free' crtdll.obj; then
-      MOZ_GLUE_LDFLAGS='-LIBPATH:$(DIST)/lib -NODEFAULTLIB:msvcrt -NODEFAULTLIB:msvcprt -DEFAULTLIB:mozcrt'
-      dnl Also pass this to NSPR/NSS
-      DLLFLAGS="$DLLFLAGS $MOZ_GLUE_LDFLAGS"
-    else
-      DLLFLAGS="$DLLFLAGS -LIBPATH:\$(DIST)/lib -DEFAULTLIB:mozglue"
+      MOZ_CRT=1
     fi
     rm crtdll.obj
     ;;
   *)
     AC_MSG_ERROR([--enable-jemalloc not supported on ${target}])
     ;;
   esac
 fi # MOZ_MEMORY
 AC_SUBST(MOZ_MEMORY)
 AC_SUBST(MOZ_JEMALLOC3)
 AC_SUBST(MOZ_NATIVE_JEMALLOC)
-AC_SUBST(MOZ_GLUE_LDFLAGS)
-AC_SUBST(MOZ_GLUE_PROGRAM_LDFLAGS)
+AC_SUBST(MOZ_CRT)
+export MOZ_CRT
+AC_SUBST(MOZ_GLUE_IN_PROGRAM)
 AC_SUBST_LIST(WIN32_CRT_LIBS)
-dnl Need to set this for make because NSS doesn't have configure
-AC_SUBST(DLLFLAGS)
 
 dnl We need to wrap dlopen and related functions on Android because we use
 dnl our own linker.
 if test "$OS_TARGET" = Android; then
-    WRAP_LDFLAGS="${WRAP_LDFLAGS} -L$_objdir/dist/lib -lmozglue"
-    WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv"
+    MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=PR_GetEnv,--wrap=PR_SetEnv"
     if test "$MOZ_WIDGET_TOOLKIT" = gonk -a -n "$MOZ_NUWA_PROCESS"; then
-        WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=__pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2,--wrap=tgkill"
-    fi
-fi
+        MOZ_GLUE_WRAP_LDFLAGS="${MOZ_GLUE_WRAP_LDFLAGS} -Wl,--wrap=pthread_create,--wrap=epoll_wait,--wrap=poll,--wrap=pthread_cond_timedwait,--wrap=__pthread_cond_timedwait,--wrap=pthread_cond_wait,--wrap=epoll_create,--wrap=epoll_ctl,--wrap=close,--wrap=pthread_key_create,--wrap=pthread_key_delete,--wrap=socketpair,--wrap=pthread_self,--wrap=pthread_mutex_lock,--wrap=pthread_join,--wrap=pipe,--wrap=pipe2,--wrap=tgkill"
+    fi
+fi
+
+AC_SUBST_LIST(MOZ_GLUE_WRAP_LDFLAGS)
+export MOZ_GLUE_WRAP_LDFLAGS
 
 dnl ========================================================
 dnl = Use JS Call tracing
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(trace-jscalls,
 [  --enable-trace-jscalls  Enable JS call enter/exit callback (default=no)],
     MOZ_TRACE_JSCALLS=1,
     MOZ_TRACE_JSCALLS= )
@@ -8350,17 +8330,17 @@ MOZ_ARG_ENABLE_STRING(necko-protocols,
         option=`echo $option | sed 's/^-//'`
         NECKO_PROTOCOLS=`echo "$NECKO_PROTOCOLS" | sed "s/ ${option}//"`
     else
         NECKO_PROTOCOLS="$NECKO_PROTOCOLS $option"
     fi
 done],
     NECKO_PROTOCOLS="$NECKO_PROTOCOLS_DEFAULT")
 dnl Remove dupes
-NECKO_PROTOCOLS=`${PERL} ${srcdir}/build/unix/uniq.pl ${NECKO_PROTOCOLS}`
+NECKO_PROTOCOLS=`$PYTHON ${srcdir}/build/unix/uniq.py ${NECKO_PROTOCOLS}`
 AC_SUBST_SET(NECKO_PROTOCOLS)
 for p in $NECKO_PROTOCOLS; do
     AC_DEFINE_UNQUOTED(NECKO_PROTOCOL_$p)
     _NON_GLOBAL_ACDEFINES="$_NON_GLOBAL_ACDEFINES NECKO_PROTOCOL_$p"
 done
 
 dnl
 dnl option to disable necko's wifi scanner
@@ -9198,18 +9178,21 @@ if test "$MOZ_TREE_FREETYPE"; then
    export ZLIB_CFLAGS="$MOZ_ZLIB_CFLAGS "
    export ZLIB_LIBS="$MOZ_ZLIB_LIBS "
    export CONFIG_FILES="unix-cc.mk:unix-cc.in unix-def.mk:unix-def.in freetype-config freetype2.pc:freetype2.in"
    ac_configure_args="$ac_configure_args --host=$target --disable-shared --with-pic=yes --with-zlib=yes --without-bzip2 --with-png=yes --without-harfbuzz"
 
    if ! test -e modules; then
      mkdir modules
    fi
-
-   AC_OUTPUT_SUBDIRS(modules/freetype2,$cache_file)
+   # Only export CC and CXX for the subconfigure, and avoid spilling that
+   # further down the road.
+   (export CC CXX;
+    AC_OUTPUT_SUBDIRS(modules/freetype2)
+   ) || exit 1
 fi
 
 if test -z "$direct_nspr_config"; then
     dnl ========================================================
     dnl = Setup a nice relatively clean build environment for
     dnl = sub-configures.
     dnl ========================================================
     CC="$_SUBDIR_CC"
@@ -9298,21 +9281,18 @@ fi
 if test -n "$NSPR_CFLAGS" -o -n "$NSPR_LIBS"; then
     ac_configure_args="$ac_configure_args --with-nspr-cflags='$NSPR_CFLAGS'"
     ac_configure_args="$ac_configure_args --with-nspr-libs='$NSPR_LIBS'"
 fi
 ac_configure_args="$ac_configure_args --prefix=$dist"
 if test "$MOZ_MEMORY"; then
    ac_configure_args="$ac_configure_args --enable-jemalloc"
 fi
-if test -n "$MOZ_GLUE_LDFLAGS"; then
-   export MOZ_GLUE_LDFLAGS
-fi
-if test -n "$MOZ_GLUE_PROGRAM_LDFLAGS"; then
-   export MOZ_GLUE_PROGRAM_LDFLAGS
+if test -n "$MOZ_GLUE_IN_PROGRAM"; then
+   export MOZ_GLUE_IN_PROGRAM
 fi
 if test -n "$ZLIB_IN_MOZGLUE"; then
    MOZ_ZLIB_LIBS=
 fi
 export MOZ_NATIVE_ZLIB
 export MOZ_ZLIB_CFLAGS
 export MOZ_ZLIB_LIBS
 export MOZ_APP_NAME
--- a/docshell/test/chrome/gen_template.pl
+++ b/docshell/test/chrome/gen_template.pl
@@ -1,19 +1,21 @@
 #!/usr/bin/perl
+
+# This script makes docshell test case templates. It takes one argument:
 #
-#  gen_template.pl
-#  Makes docshell test case templates.
-#  Takes one argument:
+#   -b: a bugnumber
 #
-#  -b : a bugnumber
+# For example, this command:
+#
+#   perl gen_template.pl -b 303267
 #
-#  e.g.: perl gen_template.pl -b 303267
-#
-#  Writes test case template files for bug 303267 to the current directory.
+# Writes test case template files test_bug303267.xul and bug303267_window.xul
+# to the current directory.
+
 use FindBin;
 use Getopt::Long;
 GetOptions("b=i"=> \$bug_number);
 
 $template = "$FindBin::RealBin/test.template.txt";
 
 open(IN,$template) or die("Failed to open input file for reading.");
 open(OUT, ">>test_bug" . $bug_number . ".xul") or die("Failed to open output file for appending.");
--- a/dom/audiochannel/tests/moz.build
+++ b/dom/audiochannel/tests/moz.build
@@ -1,23 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-CppUnitTests([
+GeckoCppUnitTests([
     'TestAudioChannelService',
 ])
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     DEFINES['NOMINMAX'] = True
 
 MOCHITEST_MANIFESTS += ['mochitest.ini']
 
 FAIL_ON_WARNINGS = True
-
-USE_LIBS += [
-    'mozalloc',
-    'nspr',
-    'xpcomglue_s',
-    'xul',
-]
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -1190,17 +1190,17 @@ Console::ProcessCallData(ConsoleCallData
       js::SetFunctionNativeReserved(funObj, SLOT_STACKOBJ, stackVal);
       js::SetFunctionNativeReserved(funObj, SLOT_RAW_STACK,
                                     JS::PrivateValue(aData->mStack.get()));
 
       if (!JS_DefineProperty(cx, eventObj, "stacktrace",
                              JS::UndefinedHandleValue,
                              JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER |
                              JSPROP_SETTER,
-                             JS_DATA_TO_FUNC_PTR(JSPropertyOp, funObj.get()),
+                             JS_DATA_TO_FUNC_PTR(JSNative, funObj.get()),
                              nullptr)) {
         return;
       }
     }
   }
 
   if (!mStorage) {
     mStorage = do_GetService("@mozilla.org/consoleAPI-storage;1");
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -71,17 +71,17 @@ class WebSocketImpl MOZ_FINAL : public n
 public:
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSIWEBSOCKETLISTENER
   NS_DECL_NSIOBSERVER
   NS_DECL_NSIREQUEST
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIEVENTTARGET
 
-  WebSocketImpl(WebSocket* aWebSocket)
+  explicit WebSocketImpl(WebSocket* aWebSocket)
   : mWebSocket(aWebSocket)
   , mOnCloseScheduled(false)
   , mFailed(false)
   , mDisconnected(false)
   , mCloseEventWasClean(false)
   , mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL)
   , mScriptLine(0)
   , mInnerWindowID(0)
@@ -320,21 +320,29 @@ WebSocketImpl::PrintErrorOnConsole(const
     rv = strBundle->FormatStringFromName(aError, aFormatStrings,
                                          aFormatStringsLen,
                                          getter_Copies(message));
   } else {
     rv = strBundle->GetStringFromName(aError, getter_Copies(message));
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = errorObject->InitWithWindowID(message,
-                                     NS_ConvertUTF8toUTF16(mScriptFile),
-                                     EmptyString(), mScriptLine, 0,
-                                     nsIScriptError::errorFlag, "Web Socket",
-                                     mInnerWindowID);
+  if (mInnerWindowID) {
+    rv = errorObject->InitWithWindowID(message,
+                                       NS_ConvertUTF8toUTF16(mScriptFile),
+                                       EmptyString(), mScriptLine, 0,
+                                       nsIScriptError::errorFlag, "Web Socket",
+                                       mInnerWindowID);
+  } else {
+    rv = errorObject->Init(message,
+                           NS_ConvertUTF8toUTF16(mScriptFile),
+                           EmptyString(), mScriptLine, 0,
+                           nsIScriptError::errorFlag, "Web Socket");
+  }
+
   NS_ENSURE_SUCCESS(rv, rv);
 
   // print the error message directly to the JS console
   rv = console->LogMessage(errorObject);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
@@ -458,17 +466,17 @@ WebSocketImpl::FailConnection(uint16_t a
   CloseConnection(aReasonCode, aReasonString);
 }
 
 namespace {
 
 class DisconnectInternalRunnable MOZ_FINAL : public WorkerMainThreadRunnable
 {
 public:
-  DisconnectInternalRunnable(WebSocketImpl* aImpl)
+  explicit DisconnectInternalRunnable(WebSocketImpl* aImpl)
     : WorkerMainThreadRunnable(aImpl->mWorkerPrivate)
     , mImpl(aImpl)
   { }
 
   bool MainThreadRun() MOZ_OVERRIDE
   {
     mImpl->DisconnectInternal();
     return true;
@@ -861,47 +869,67 @@ public:
 
     // Walk up to our containing page
     WorkerPrivate* wp = mWorkerPrivate;
     while (wp->GetParent()) {
       wp = wp->GetParent();
     }
 
     nsPIDOMWindow* window = wp->GetWindow();
-    if (!window) {
-      mRv.Throw(NS_ERROR_FAILURE);
-      return true;
+    if (window) {
+      return InitWithWindow(window);
     }
 
+    return InitWindowless();
+  }
+
+private:
+  bool InitWithWindow(nsPIDOMWindow* aWindow)
+  {
     AutoJSAPI jsapi;
-    if (NS_WARN_IF(!jsapi.Init(window))) {
+    if (NS_WARN_IF(!jsapi.Init(aWindow))) {
       mRv.Throw(NS_ERROR_FAILURE);
       return true;
     }
 
     ClearException ce(jsapi.cx());
 
-    nsIDocument* doc = window->GetExtantDoc();
+    nsIDocument* doc = aWindow->GetExtantDoc();
     if (!doc) {
       mRv.Throw(NS_ERROR_FAILURE);
       return true;
     }
 
     nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
     if (!principal) {
       mRv.Throw(NS_ERROR_FAILURE);
       return true;
     }
 
     mImpl->Init(jsapi.cx(), principal, mURL, mProtocolArray, mScriptFile,
                 mScriptLine, mRv, mConnectionFailed);
     return true;
   }
 
-private:
+  bool InitWindowless()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    WorkerPrivate* wp = mWorkerPrivate;
+    while (wp->GetParent()) {
+      wp = wp->GetParent();
+    }
+
+    MOZ_ASSERT(!wp->GetWindow());
+
+    mImpl->Init(nullptr, wp->GetPrincipal(), mURL, mProtocolArray, mScriptFile,
+                mScriptLine, mRv, mConnectionFailed);
+    return true;
+  }
+
   // Raw pointer. This worker runs synchronously.
   WebSocketImpl* mImpl;
 
   const nsAString& mURL;
   nsTArray<nsString>& mProtocolArray;
   nsCString mScriptFile;
   uint32_t mScriptLine;
   ErrorResult& mRv;
@@ -1031,17 +1059,17 @@ WebSocket::Constructor(const GlobalObjec
   // called asynchrounsly.
   if (!webSocket->mImpl->mChannel) {
     return webSocket.forget();
   }
 
   class MOZ_STACK_CLASS ClearWebSocket
   {
   public:
-    ClearWebSocket(WebSocketImpl* aWebSocketImpl)
+    explicit ClearWebSocket(WebSocketImpl* aWebSocketImpl)
       : mWebSocketImpl(aWebSocketImpl)
       , mDone(false)
     {
     }
 
     void Done()
     {
        mDone = true;
@@ -1197,26 +1225,32 @@ WebSocketImpl::Init(JSContext* aCx,
       return;
     }
   }
 
   if (mWorkerPrivate) {
     mScriptFile = aScriptFile;
     mScriptLine = aScriptLine;
   } else {
+    MOZ_ASSERT(aCx);
+
     unsigned lineno;
     JS::AutoFilename file;
     if (JS::DescribeScriptedCaller(aCx, &file, &lineno)) {
       mScriptFile = file.get();
       mScriptLine = lineno;
     }
   }
 
-  // Get WindowID
-  mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aCx);
+  // If we don't have aCx, we are window-less, so we don't have a
+  // inner-windowID. This can happen in sharedWorkers and ServiceWorkers or in
+  // DedicateWorkers created by JSM.
+  if (aCx) {
+    mInnerWindowID = nsJSUtils::GetCurrentlyRunningCodeInnerWindowID(aCx);
+  }
 
   // parses the url
   aRv = ParseURL(PromiseFlatString(aURL));
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   nsIScriptContext* sc = nullptr;
@@ -1582,17 +1616,17 @@ WebSocket::CreateAndDispatchCloseEvent(b
   return DispatchDOMEvent(nullptr, event, nullptr, nullptr);
 }
 
 namespace {
 
 class PrefEnabledRunnable MOZ_FINAL : public WorkerMainThreadRunnable
 {
 public:
-  PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate)
+  explicit PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate)
     : WorkerMainThreadRunnable(aWorkerPrivate)
     , mEnabled(false)
   { }
 
   bool MainThreadRun() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
     mEnabled = WebSocket::PrefEnabled(nullptr, nullptr);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -1029,18 +1029,19 @@ nsDOMClassInfo::ResolveConstructor(JSCon
 
   if (!val.isPrimitive()) {
     // If val is not an (non-null) object there either is no
     // constructor for this class, or someone messed with
     // window.classname, just fall through and let the JS engine
     // return the Object constructor.
 
     JS::Rooted<jsid> id(cx, sConstructor_id);
-    if (!::JS_DefinePropertyById(cx, obj, id, val, JSPROP_ENUMERATE,
-                                 JS_PropertyStub, JS_StrictPropertyStub)) {
+    if (!::JS_DefinePropertyById(cx, obj, id, val,
+                                 JSPROP_ENUMERATE,
+                                 JS_STUBGETTER, JS_STUBSETTER)) {
       return NS_ERROR_UNEXPECTED;
     }
 
     *objp = obj;
   }
 
   return NS_OK;
 }
@@ -1244,18 +1245,22 @@ nsDOMClassInfo::PostCreatePrototype(JSCo
   JS::Rooted<JSPropertyDescriptor> desc(cx);
   nsresult rv = ResolvePrototype(sXPConnect, win, cx, global, mData->mNameUTF16,
                                  mData, nullptr, nameSpaceManager, proto,
                                  &desc);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!contentDefinedProperty && desc.object() && !desc.value().isUndefined() &&
       !JS_DefineUCProperty(cx, global, mData->mNameUTF16,
                            NS_strlen(mData->mNameUTF16),
-                           desc.value(), desc.attributes(),
-                           desc.getter(), desc.setter())) {
+                           desc.value(),
+                           // Descriptors never store JSNatives for accessors:
+                           // they have either JSFunctions or JSPropertyOps.
+                           desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                           JS_PROPERTYOP_GETTER(desc.getter()),
+                           JS_PROPERTYOP_SETTER(desc.setter()))) {
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
 }
 
 // static
 nsIClassInfo *
@@ -1446,19 +1451,18 @@ DefineInterfaceConstants(JSContext *cx, 
 
   JS::Rooted<JS::Value> v(cx);
   for (i = parent_constant_count; i < constant_count; i++) {
     nsXPIDLCString name;
     rv = if_info->GetConstant(i, &v, getter_Copies(name));
     NS_ENSURE_TRUE(NS_SUCCEEDED(rv), rv);
 
     if (!::JS_DefineProperty(cx, obj, name, v,
-                             JSPROP_ENUMERATE | JSPROP_READONLY |
-                             JSPROP_PERMANENT,
-                             JS_PropertyStub, JS_StrictPropertyStub)) {
+                             JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,
+                             JS_STUBGETTER, JS_STUBSETTER)) {
       return NS_ERROR_UNEXPECTED;
     }
   }
 
   return NS_OK;
 }
 
 class nsDOMConstructor MOZ_FINAL : public nsIDOMDOMConstructor
@@ -2046,17 +2050,17 @@ ResolvePrototype(nsIXPConnect *aXPConnec
   v = OBJECT_TO_JSVAL(dot_prototype);
 
   JSAutoCompartment ac(cx, class_obj);
 
   // Per ECMA, the prototype property is {DontEnum, DontDelete, ReadOnly}
   if (!JS_WrapValue(cx, &v) ||
       !JS_DefineProperty(cx, class_obj, "prototype", v,
                          JSPROP_PERMANENT | JSPROP_READONLY,
-                         JS_PropertyStub, JS_StrictPropertyStub)) {
+                         JS_STUBGETTER, JS_STUBSETTER)) {
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
 }
 
 static bool
 OldBindingConstructorEnabled(const nsGlobalNameStruct *aStruct,
@@ -2505,17 +2509,17 @@ LookupComponentsShim(JSContext *cx, JS::
   NS_ENSURE_TRUE(components, NS_ERROR_OUT_OF_MEMORY);
 
   // Create a fake interfaces object.
   JS::Rooted<JSObject*> interfaces(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), global));
   NS_ENSURE_TRUE(interfaces, NS_ERROR_OUT_OF_MEMORY);
   bool ok =
     JS_DefineProperty(cx, components, "interfaces", interfaces,
                       JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY,
-                      JS_PropertyStub, JS_StrictPropertyStub);
+                      JS_STUBGETTER, JS_STUBSETTER);
   NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
 
   // Define a bunch of shims from the Ci.nsIDOMFoo to window.Foo for DOM
   // interfaces with constants.
   for (uint32_t i = 0; i < ArrayLength(kInterfaceShimMap); ++i) {
 
     // Grab the names from the table.
     const char *geckoName = kInterfaceShimMap[i].geckoName;
@@ -2528,17 +2532,17 @@ LookupComponentsShim(JSContext *cx, JS::
     if (!v.isObject()) {
       NS_WARNING("Unable to find interface object on global");
       continue;
     }
 
     // Define the shim on the interfaces object.
     ok = JS_DefineProperty(cx, interfaces, geckoName, v,
                            JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY,
-                           JS_PropertyStub, JS_StrictPropertyStub);
+                           JS_STUBGETTER, JS_STUBSETTER);
     NS_ENSURE_TRUE(ok, NS_ERROR_OUT_OF_MEMORY);
   }
 
   FillPropertyDescriptor(desc, global, JS::ObjectValue(*components), false);
 
   return NS_OK;
 }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2586,18 +2586,19 @@ nsGlobalWindow::SetNewDocument(nsIDocume
 
     // Set scriptability based on the state of the docshell.
     bool allow = GetDocShell()->GetCanExecuteScripts();
     xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow);
 
     if (!aState) {
       JS::Rooted<JSObject*> rootedWrapper(cx, GetWrapperPreserveColor());
       if (!JS_DefineProperty(cx, newInnerGlobal, "window", rootedWrapper,
-                             JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT,
-                             JS_PropertyStub, JS_StrictPropertyStub)) {
+                             JSPROP_ENUMERATE | JSPROP_READONLY |
+                             JSPROP_PERMANENT,
+                             JS_STUBGETTER, JS_STUBSETTER)) {
         NS_ERROR("can't create the 'window' property");
         return NS_ERROR_FAILURE;
       }
     }
   }
 
   JSAutoCompartment ac(cx, GetWrapperPreserveColor());
 
@@ -4523,17 +4524,17 @@ nsGlobalWindow::SetOpener(JSContext* aCx
     JS::Rooted<JSObject*> thisObj(aCx, GetWrapperPreserveColor());
     if (!thisObj) {
       aError.Throw(NS_ERROR_UNEXPECTED);
       return;
     }
 
     if (!JS_WrapObject(aCx, &thisObj) ||
         !JS_DefineProperty(aCx, thisObj, "opener", aOpener, JSPROP_ENUMERATE,
-                           JS_PropertyStub, JS_StrictPropertyStub)) {
+                           JS_STUBGETTER, JS_STUBSETTER)) {
       aError.Throw(NS_ERROR_FAILURE);
     }
 
     return;
   }
 
   if (!aOpener.isObjectOrNull()) {
     // Chrome code trying to set some random value as opener
@@ -14017,19 +14018,18 @@ NS_IMETHODIMP
 nsGlobalWindow::SetConsole(JSContext* aCx, JS::Handle<JS::Value> aValue)
 {
   JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
   if (!thisObj) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!JS_WrapObject(aCx, &thisObj) ||
-      !JS_DefineProperty(aCx, thisObj, "console", aValue,
-                         JSPROP_ENUMERATE, JS_PropertyStub,
-                         JS_StrictPropertyStub)) {
+      !JS_DefineProperty(aCx, thisObj, "console", aValue, JSPROP_ENUMERATE,
+                         JS_STUBGETTER, JS_STUBSETTER)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 Console*
 nsGlobalWindow::GetConsole(ErrorResult& aRv)
--- a/dom/base/test/moz.build
+++ b/dom/base/test/moz.build
@@ -5,17 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPCSHELL_TESTS_MANIFESTS += ['unit/xpcshell.ini']
 
 # FIXME/bug 575918: out-of-process xpcshell is broken on OS X
 if CONFIG['OS_ARCH'] != 'Darwin':
     XPCSHELL_TESTS_MANIFESTS += ['unit_ipc/xpcshell.ini']
 
-CppUnitTests([
+GeckoCppUnitTests([
     'TestCSPParser',
     'TestGetURL',
     'TestNativeXMLHttpRequest',
     'TestPlainTextSerializer',
 ])
 
 MOCHITEST_MANIFESTS += [
     'chrome/mochitest.ini',
@@ -33,15 +33,8 @@ if CONFIG['MOZ_CHILD_PERMISSIONS']:
 
 MOCHITEST_CHROME_MANIFESTS += [
     'chrome.ini',
     'chrome/chrome.ini',
     'csp/chrome.ini',
 ]
 
 BROWSER_CHROME_MANIFESTS += ['browser.ini']
-
-USE_LIBS += [
-    'mozalloc',
-    'nspr',
-    'xpcomglue_s',
-    'xul',
-]
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -523,18 +523,19 @@ CreateInterfaceObject(JSContext* cx, JS:
     int namedConstructorSlot = DOM_INTERFACE_SLOTS_BASE;
     while (namedConstructors->mName) {
       JS::Rooted<JSObject*> namedConstructor(cx,
         CreateConstructor(cx, global, namedConstructors->mName,
                           &namedConstructors->mHolder,
                           namedConstructors->mNargs));
       if (!namedConstructor ||
           !JS_DefineProperty(cx, namedConstructor, "prototype",
-                             proto, JSPROP_PERMANENT | JSPROP_READONLY,
-                             JS_PropertyStub, JS_StrictPropertyStub) ||
+                             proto,
+                             JSPROP_PERMANENT | JSPROP_READONLY,
+                             JS_STUBGETTER, JS_STUBSETTER) ||
           (defineOnGlobal &&
            !DefineConstructor(cx, global, namedConstructors->mName,
                               namedConstructor))) {
         return nullptr;
       }
       js::SetReservedSlot(constructor, namedConstructorSlot++,
                           JS::ObjectValue(*namedConstructor));
       ++namedConstructors;
@@ -963,34 +964,34 @@ XrayResolveAttribute(JSContext* cx, JS::
       for ( ; attributeIds[i] != JSID_VOID; ++i) {
         if (id == attributeIds[i]) {
           cacheOnHolder = true;
 
           const JSPropertySpec& attrSpec = attributeSpecs[i];
           // Because of centralization, we need to make sure we fault in the
           // JitInfos as well. At present, until the JSAPI changes, the easiest
           // way to do this is wrap them up as functions ourselves.
-          desc.setAttributes(attrSpec.flags & ~JSPROP_NATIVE_ACCESSORS);
+          desc.setAttributes(attrSpec.flags);
           // They all have getters, so we can just make it.
           JS::Rooted<JSFunction*> fun(cx,
-                                      JS_NewFunctionById(cx, (JSNative)attrSpec.getter.propertyOp.op,
+                                      JS_NewFunctionById(cx, attrSpec.getter.native.op,
                                                          0, 0, wrapper, id));
           if (!fun)
             return false;
-          SET_JITINFO(fun, attrSpec.getter.propertyOp.info);
+          SET_JITINFO(fun, attrSpec.getter.native.info);
           JSObject *funobj = JS_GetFunctionObject(fun);
           desc.setGetterObject(funobj);
           desc.attributesRef() |= JSPROP_GETTER;
-          if (attrSpec.setter.propertyOp.op) {
+          if (attrSpec.setter.native.op) {
             // We have a setter! Make it.
-            fun = JS_NewFunctionById(cx, (JSNative)attrSpec.setter.propertyOp.op, 1, 0,
+            fun = JS_NewFunctionById(cx, attrSpec.setter.native.op, 1, 0,
                                      wrapper, id);
             if (!fun)
               return false;
-            SET_JITINFO(fun, attrSpec.setter.propertyOp.info);
+            SET_JITINFO(fun, attrSpec.setter.native.info);
             funobj = JS_GetFunctionObject(fun);
             desc.setSetterObject(funobj);
             desc.attributesRef() |= JSPROP_SETTER;
           } else {
             desc.setSetter(nullptr);
           }
           desc.object().set(wrapper);
           return true;
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2407,17 +2407,17 @@ class AttrDefiner(PropertyDefiner):
                 assert not self.hasChromeOnly() and not self.hasNonChromeOnly()
 
     def generateArray(self, array, name, doIdArrays):
         if len(array) == 0:
             return ""
 
         def flags(attr):
             unforgeable = " | JSPROP_PERMANENT" if self.unforgeable else ""
-            return ("JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS" +
+            return ("JSPROP_SHARED | JSPROP_ENUMERATE" +
                     unforgeable)
 
         def getter(attr):
             if self.static:
                 accessor = 'get_' + IDLToCIdentifier(attr.identifier.name)
                 jitinfo = "nullptr"
             else:
                 if attr.hasLenientThis():
@@ -2425,38 +2425,38 @@ class AttrDefiner(PropertyDefiner):
                 elif attr.getExtendedAttribute("CrossOriginReadable"):
                     accessor = "genericCrossOriginGetter"
                 elif self.descriptor.needsSpecialGenericOps():
                     accessor = "genericGetter"
                 else:
                     accessor = "GenericBindingGetter"
                 jitinfo = ("&%s_getterinfo" %
                            IDLToCIdentifier(attr.identifier.name))
-            return "{ { JS_CAST_NATIVE_TO(%s, JSPropertyOp), %s } }" % \
+            return "{ { %s, %s } }" % \
                    (accessor, jitinfo)
 
         def setter(attr):
             if (attr.readonly and
                 attr.getExtendedAttribute("PutForwards") is None and
                 attr.getExtendedAttribute("Replaceable") is None):
-                return "JSOP_NULLWRAPPER"
+                return "JSNATIVE_WRAPPER(nullptr)"
             if self.static:
                 accessor = 'set_' + IDLToCIdentifier(attr.identifier.name)
                 jitinfo = "nullptr"
             else:
                 if attr.hasLenientThis():
                     accessor = "genericLenientSetter"
                 elif IsCrossOriginWritable(attr, self.descriptor):
                     accessor = "genericCrossOriginSetter"
                 elif self.descriptor.needsSpecialGenericOps():
                     accessor = "genericSetter"
                 else:
                     accessor = "GenericBindingSetter"
                 jitinfo = "&%s_setterinfo" % IDLToCIdentifier(attr.identifier.name)
-            return "{ { JS_CAST_NATIVE_TO(%s, JSStrictPropertyOp), %s } }" % \
+            return "{ { %s, %s } }" % \
                    (accessor, jitinfo)
 
         def specData(attr):
             return (attr.identifier.name, flags(attr), getter(attr),
                     setter(attr))
 
         return self.generatePrefableArray(
             array, name,
@@ -7626,18 +7626,19 @@ class CGNewResolveHook(CGAbstractBinding
             if (!desc.object()) {
               return true;
             }
             // If desc.value() is undefined, then the DoNewResolve call
             // has already defined it on the object.  Don't try to also
             // define it.
             if (!desc.value().isUndefined() &&
                 !JS_DefinePropertyById(cx, obj, id, desc.value(),
-                                       desc.attributes(),
-                                       desc.getter(), desc.setter())) {
+                                       desc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                                       JS_PROPERTYOP_GETTER(desc.getter()),
+                                       JS_PROPERTYOP_SETTER(desc.setter()))) {
               return false;
             }
             objp.set(obj);
             return true;
             """))
 
     def definition_body(self):
         if self.descriptor.isGlobal():
@@ -9598,18 +9599,19 @@ class CGResolveOwnPropertyViaNewresolve(
                 return false;
               }
               // If desc.value() is undefined, then the DoNewResolve call
               // has already defined the property on the object.  Don't
               // try to also define it.
               if (objDesc.object() &&
                   !objDesc.value().isUndefined() &&
                   !JS_DefinePropertyById(cx, obj, id, objDesc.value(),
-                                         objDesc.attributes(),
-                                         objDesc.getter(), objDesc.setter())) {
+                                         objDesc.attributes() | JSPROP_PROPOP_ACCESSORS,
+                                         JS_PROPERTYOP_GETTER(objDesc.getter()),
+                                         JS_PROPERTYOP_SETTER(objDesc.setter()))) {
                 return false;
               }
             }
             return self->DoNewResolve(cx, wrapper, id, desc);
             """))
 
 
 class CGEnumerateOwnProperties(CGAbstractStaticMethod):
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -605,182 +605,190 @@ public:
         return TexSubImage2D_base(texImageTarget.get(), level, xoffset, yoffset,
                                   size.width, size.height,
                                   data->Stride(), format, type,
                                   data->GetData(), byteLength,
                                   js::Scalar::TypeMax, srcFormat, mPixelStorePremultiplyAlpha);
 
     }
 
-    void Uniform1i(WebGLUniformLocation* location, GLint x);
-    void Uniform2i(WebGLUniformLocation* location, GLint x, GLint y);
-    void Uniform3i(WebGLUniformLocation* location, GLint x, GLint y,
-                   GLint z);
-    void Uniform4i(WebGLUniformLocation* location, GLint x, GLint y,
-                   GLint z, GLint w);
+    void Uniform1i(WebGLUniformLocation* loc, GLint x);
+    void Uniform2i(WebGLUniformLocation* loc, GLint x, GLint y);
+    void Uniform3i(WebGLUniformLocation* loc, GLint x, GLint y, GLint z);
+    void Uniform4i(WebGLUniformLocation* loc, GLint x, GLint y, GLint z,
+                   GLint w);
+
+    void Uniform1f(WebGLUniformLocation* loc, GLfloat x);
+    void Uniform2f(WebGLUniformLocation* loc, GLfloat x, GLfloat y);
+    void Uniform3f(WebGLUniformLocation* loc, GLfloat x, GLfloat y, GLfloat z);
+    void Uniform4f(WebGLUniformLocation* loc, GLfloat x, GLfloat y, GLfloat z,
+                   GLfloat w);
 
-    void Uniform1f(WebGLUniformLocation* location, GLfloat x);
-    void Uniform2f(WebGLUniformLocation* location, GLfloat x, GLfloat y);
-    void Uniform3f(WebGLUniformLocation* location, GLfloat x, GLfloat y,
-                   GLfloat z);
-    void Uniform4f(WebGLUniformLocation* location, GLfloat x, GLfloat y,
-                   GLfloat z, GLfloat w);
-
-    void Uniform1iv(WebGLUniformLocation* location,
-                    const dom::Int32Array& arr) {
+    // Int array
+    void Uniform1iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) {
         arr.ComputeLengthAndData();
-        Uniform1iv_base(location, arr.Length(), arr.Data());
+        Uniform1iv_base(loc, arr.Length(), arr.Data());
     }
-    void Uniform1iv(WebGLUniformLocation* location,
-                    const dom::Sequence<GLint>& arr) {
-        Uniform1iv_base(location, arr.Length(), arr.Elements());
+    void Uniform1iv(WebGLUniformLocation* loc,
+                    const dom::Sequence<GLint>& arr)
+    {
+        Uniform1iv_base(loc, arr.Length(), arr.Elements());
     }
-    void Uniform1iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
+    void Uniform1iv_base(WebGLUniformLocation* loc, size_t arrayLength,
                          const GLint* data);
 
-    void Uniform2iv(WebGLUniformLocation* location,
-                    const dom::Int32Array& arr) {
+    void Uniform2iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) {
         arr.ComputeLengthAndData();
-        Uniform2iv_base(location, arr.Length(), arr.Data());
+        Uniform2iv_base(loc, arr.Length(), arr.Data());
+    }
+    void Uniform2iv(WebGLUniformLocation* loc,
+                    const dom::Sequence<GLint>& arr)
+    {
+        Uniform2iv_base(loc, arr.Length(), arr.Elements());
     }
-    void Uniform2iv(WebGLUniformLocation* location,
-                    const dom::Sequence<GLint>& arr) {
-        Uniform2iv_base(location, arr.Length(), arr.Elements());
+    void Uniform2iv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                         const GLint* data);
+
+    void Uniform3iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) {
+        arr.ComputeLengthAndData();
+        Uniform3iv_base(loc, arr.Length(), arr.Data());
     }
-    void Uniform2iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
+    void Uniform3iv(WebGLUniformLocation* loc,
+                    const dom::Sequence<GLint>& arr)
+    {
+        Uniform3iv_base(loc, arr.Length(), arr.Elements());
+    }
+    void Uniform3iv_base(WebGLUniformLocation* loc, size_t arrayLength,
                          const GLint* data);
 
-    void Uniform3iv(WebGLUniformLocation* location,
-                    const dom::Int32Array& arr) {
+    void Uniform4iv(WebGLUniformLocation* loc, const dom::Int32Array& arr) {
         arr.ComputeLengthAndData();
-        Uniform3iv_base(location, arr.Length(), arr.Data());
+        Uniform4iv_base(loc, arr.Length(), arr.Data());
     }
-    void Uniform3iv(WebGLUniformLocation* location,
-                    const dom::Sequence<GLint>& arr) {
-        Uniform3iv_base(location, arr.Length(), arr.Elements());
+    void Uniform4iv(WebGLUniformLocation* loc,
+                    const dom::Sequence<GLint>& arr)
+    {
+        Uniform4iv_base(loc, arr.Length(), arr.Elements());
     }
-    void Uniform3iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
+    void Uniform4iv_base(WebGLUniformLocation* loc, size_t arrayLength,
                          const GLint* data);
 
-    void Uniform4iv(WebGLUniformLocation* location,
-                    const dom::Int32Array& arr) {
+    // Float array
+    void Uniform1fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) {
         arr.ComputeLengthAndData();
-        Uniform4iv_base(location, arr.Length(), arr.Data());
-    }
-    void Uniform4iv(WebGLUniformLocation* location,
-                    const dom::Sequence<GLint>& arr) {
-        Uniform4iv_base(location, arr.Length(), arr.Elements());
+        Uniform1fv_base(loc, arr.Length(), arr.Data());
     }
-    void Uniform4iv_base(WebGLUniformLocation* location, uint32_t arrayLength,
-                         const GLint* data);
-
-    void Uniform1fv(WebGLUniformLocation* location,
-                    const dom::Float32Array& arr) {
-        arr.ComputeLengthAndData();
-        Uniform1fv_base(location, arr.Length(), arr.Data());
+    void Uniform1fv(WebGLUniformLocation* loc,
+                    const dom::Sequence<GLfloat>& arr)
+    {
+        Uniform1fv_base(loc, arr.Length(), arr.Elements());
     }
-    void Uniform1fv(WebGLUniformLocation* location,
-                    const dom::Sequence<GLfloat>& arr) {
-        Uniform1fv_base(location, arr.Length(), arr.Elements());
-    }
-    void Uniform1fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
+    void Uniform1fv_base(WebGLUniformLocation* loc, size_t arrayLength,
                          const GLfloat* data);
 
-    void Uniform2fv(WebGLUniformLocation* location,
-                    const dom::Float32Array& arr) {
+    void Uniform2fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) {
         arr.ComputeLengthAndData();
-        Uniform2fv_base(location, arr.Length(), arr.Data());
+        Uniform2fv_base(loc, arr.Length(), arr.Data());
     }
-    void Uniform2fv(WebGLUniformLocation* location,
-                    const dom::Sequence<GLfloat>& arr) {
-        Uniform2fv_base(location, arr.Length(), arr.Elements());
+    void Uniform2fv(WebGLUniformLocation* loc,
+                    const dom::Sequence<GLfloat>& arr)
+    {
+        Uniform2fv_base(loc, arr.Length(), arr.Elements());
     }
-    void Uniform2fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
+    void Uniform2fv_base(WebGLUniformLocation* loc, size_t arrayLength,
                          const GLfloat* data);
 
-    void Uniform3fv(WebGLUniformLocation* location,
-                    const dom::Float32Array& arr) {
+    void Uniform3fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) {
         arr.ComputeLengthAndData();
-        Uniform3fv_base(location, arr.Length(), arr.Data());
+        Uniform3fv_base(loc, arr.Length(), arr.Data());
     }
-    void Uniform3fv(WebGLUniformLocation* location,
-                    const dom::Sequence<GLfloat>& arr) {
-        Uniform3fv_base(location, arr.Length(), arr.Elements());
+    void Uniform3fv(WebGLUniformLocation* loc,
+                    const dom::Sequence<GLfloat>& arr)
+    {
+        Uniform3fv_base(loc, arr.Length(), arr.Elements());
     }
-    void Uniform3fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
+    void Uniform3fv_base(WebGLUniformLocation* loc, size_t arrayLength,
                          const GLfloat* data);
 
-    void Uniform4fv(WebGLUniformLocation* location,
-                    const dom::Float32Array& arr) {
+    void Uniform4fv(WebGLUniformLocation* loc, const dom::Float32Array& arr) {
         arr.ComputeLengthAndData();
-        Uniform4fv_base(location, arr.Length(), arr.Data());
+        Uniform4fv_base(loc, arr.Length(), arr.Data());
     }
-    void Uniform4fv(WebGLUniformLocation* location,
-                    const dom::Sequence<GLfloat>& arr) {
-        Uniform4fv_base(location, arr.Length(), arr.Elements());
+    void Uniform4fv(WebGLUniformLocation* loc,
+                    const dom::Sequence<GLfloat>& arr)
+    {
+        Uniform4fv_base(loc, arr.Length(), arr.Elements());
     }
-    void Uniform4fv_base(WebGLUniformLocation* location, uint32_t arrayLength,
+    void Uniform4fv_base(WebGLUniformLocation* loc, size_t arrayLength,
                          const GLfloat* data);
 
-    void UniformMatrix2fv(WebGLUniformLocation* location,
-                          WebGLboolean transpose,
-                          const dom::Float32Array &value) {
+    // Matrix
+    void UniformMatrix2fv(WebGLUniformLocation* loc, WebGLboolean transpose,
+                          const dom::Float32Array& value)
+    {
         value.ComputeLengthAndData();
-        UniformMatrix2fv_base(location, transpose, value.Length(), value.Data());
+        UniformMatrix2fv_base(loc, transpose, value.Length(), value.Data());
     }
-    void UniformMatrix2fv(WebGLUniformLocation* location,
-                          WebGLboolean transpose,
-                          const dom::Sequence<float> &value) {
-        UniformMatrix2fv_base(location, transpose, value.Length(),
+    void UniformMatrix2fv(WebGLUniformLocation* loc, WebGLboolean transpose,
+                          const dom::Sequence<float>& value)
+    {
+        UniformMatrix2fv_base(loc, transpose, value.Length(),
                               value.Elements());
     }
-    void UniformMatrix2fv_base(WebGLUniformLocation* location,
-                               WebGLboolean transpose, uint32_t arrayLength,
+    void UniformMatrix2fv_base(WebGLUniformLocation* loc,
+                               WebGLboolean transpose, size_t arrayLength,
                                const float* data);
 
-    void UniformMatrix3fv(WebGLUniformLocation* location,
-                          WebGLboolean transpose,
-                          const dom::Float32Array &value) {
+    void UniformMatrix3fv(WebGLUniformLocation* loc, WebGLboolean transpose,
+                          const dom::Float32Array& value)
+    {
         value.ComputeLengthAndData();
-        UniformMatrix3fv_base(location, transpose, value.Length(), value.Data());
+        UniformMatrix3fv_base(loc, transpose, value.Length(), value.Data());
+    }
+    void UniformMatrix3fv(WebGLUniformLocation* loc, WebGLboolean transpose,
+                          const dom::Sequence<float>& value)
+    {
+        UniformMatrix3fv_base(loc, transpose, value.Length(), value.Elements());
     }
-    void UniformMatrix3fv(WebGLUniformLocation* location,
-                          WebGLboolean transpose,
-                          const dom::Sequence<float> &value) {
-        UniformMatrix3fv_base(location, transpose, value.Length(),
+    void UniformMatrix3fv_base(WebGLUniformLocation* loc,
+                               WebGLboolean transpose, size_t arrayLength,
+                               const float* data);
+
+    void UniformMatrix4fv(WebGLUniformLocation* loc, WebGLboolean transpose,
+                          const dom::Float32Array& value)
+    {
+        value.ComputeLengthAndData();
+        UniformMatrix4fv_base(loc, transpose, value.Length(), value.Data());
+    }
+    void UniformMatrix4fv(WebGLUniformLocation* loc, WebGLboolean transpose,
+                          const dom::Sequence<float>& value)
+    {
+        UniformMatrix4fv_base(loc, transpose, value.Length(),
                               value.Elements());
     }
-    void UniformMatrix3fv_base(WebGLUniformLocation* location,
-                               WebGLboolean transpose, uint32_t arrayLength,
-                               const float* data);
-
-    void UniformMatrix4fv(WebGLUniformLocation* location,
-                          WebGLboolean transpose,
-                          const dom::Float32Array &value) {
-        value.ComputeLengthAndData();
-        UniformMatrix4fv_base(location, transpose, value.Length(), value.Data());
-    }
-    void UniformMatrix4fv(WebGLUniformLocation* location,
-                          WebGLboolean transpose,
-                          const dom::Sequence<float> &value) {
-        UniformMatrix4fv_base(location, transpose, value.Length(),
-                              value.Elements());
-    }
-    void UniformMatrix4fv_base(WebGLUniformLocation* location,
-                               WebGLboolean transpose, uint32_t arrayLength,
+    void UniformMatrix4fv_base(WebGLUniformLocation* loc,
+                               WebGLboolean transpose, size_t arrayLength,
                                const float* data);
 
     void UseProgram(WebGLProgram *prog);
     bool ValidateAttribArraySetter(const char* name, uint32_t cnt, uint32_t arrayLength);
-    bool ValidateUniformArraySetter(const char* name, uint32_t expectedElemSize, WebGLUniformLocation *location_object,
-                                    GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength);
-    bool ValidateUniformMatrixArraySetter(const char* name, int dim, WebGLUniformLocation *location_object,
-                                          GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength,
-                                          WebGLboolean aTranspose);
-    bool ValidateUniformSetter(const char* name, WebGLUniformLocation *location_object, GLint& location);
+    bool ValidateUniformSetter(WebGLUniformLocation* loc, uint8_t setterSize,
+                               GLenum setterType, const char* info,
+                               GLuint* out_rawLoc);
+    bool ValidateUniformArraySetter(WebGLUniformLocation* loc,
+                                    uint8_t setterElemSize, GLenum setterType,
+                                    size_t setterArraySize, const char* info,
+                                    GLuint* out_rawLoc,
+                                    GLsizei* out_numElementsToUpload);
+    bool ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
+                                          uint8_t setterDims, GLenum setterType,
+                                          size_t setterArraySize,
+                                          bool setterTranspose,
+                                          const char* info, GLuint* out_rawLoc,
+                                          GLsizei* out_numElementsToUpload);
     void ValidateProgram(WebGLProgram *prog);
     bool ValidateUniformLocation(const char* info, WebGLUniformLocation *location_object);
     bool ValidateSamplerUniformSetter(const char* info,
                                     WebGLUniformLocation *location,
                                     GLint value);
     void Viewport(GLint x, GLint y, GLsizei width, GLsizei height);
 // -----------------------------------------------------------------------------
 // WEBGL_lose_context
@@ -1042,16 +1050,22 @@ protected:
     int32_t mGLMaxVertexTextureImageUnits;
     int32_t mGLMaxVaryingVectors;
     int32_t mGLMaxFragmentUniformVectors;
     int32_t mGLMaxVertexUniformVectors;
     int32_t mGLMaxColorAttachments;
     int32_t mGLMaxDrawBuffers;
     uint32_t mGLMaxTransformFeedbackSeparateAttribs;
 
+public:
+    GLuint MaxVertexAttribs() const {
+        return mGLMaxVertexAttribs;
+    }
+
+protected:
     // Represents current status of the context with respect to context loss.
     // That is, whether the context is lost, and what part of the context loss
     // process we currently are at.
     // This is used to support the WebGL spec's asyncronous nature in handling
     // context loss.
     enum ContextStatus {
         // The context is stable; there either are none or we don't know of any.
         ContextNotLost,
@@ -1110,17 +1124,16 @@ protected:
     bool ValidateComparisonEnum(GLenum target, const char *info);
     bool ValidateStencilOpEnum(GLenum action, const char *info);
     bool ValidateFaceEnum(GLenum face, const char *info);
     bool ValidateTexInputData(GLenum type,
                               js::Scalar::Type jsArrayType,
                               WebGLTexImageFunc func,
                               WebGLTexDimensions dims);
     bool ValidateDrawModeEnum(GLenum mode, const char *info);
-    bool ValidateAttribIndex(GLuint index, const char *info);
     bool ValidateStencilParamsForDrawCall();
 
     bool ValidateGLSLVariableName(const nsAString& name, const char *info);
     bool ValidateGLSLCharacter(char16_t c);
     bool ValidateGLSLString(const nsAString& string, const char *info);
 
     bool ValidateCopyTexImage(GLenum internalformat,
                               WebGLTexImageFunc func,
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -115,37 +115,40 @@ WebGLContext::AttachShader(WebGLProgram 
     // leave it for when we support more than one shader of each type.
     if (program->HasAttachedShaderOfType(shader->ShaderType()))
         return ErrorInvalidOperation("attachShader: only one of each type of shader may be attached to a program");
 
     if (!program->AttachShader(shader))
         return ErrorInvalidOperation("attachShader: shader is already attached");
 }
 
-
 void
-WebGLContext::BindAttribLocation(WebGLProgram *prog, GLuint location,
+WebGLContext::BindAttribLocation(WebGLProgram* prog, GLuint location,
                                  const nsAString& name)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObject("bindAttribLocation: program", prog))
         return;
 
     GLuint progname = prog->GLName();
 
     if (!ValidateGLSLVariableName(name, "bindAttribLocation"))
         return;
 
-    if (!ValidateAttribIndex(location, "bindAttribLocation"))
-        return;
+    if (location >= MaxVertexAttribs()) {
+        return ErrorInvalidValue("bindAttribLocation: `location` must be less"
+                                 " than MAX_VERTEX_ATTRIBS.");
+    }
 
     if (StringBeginsWith(name, NS_LITERAL_STRING("gl_")))
-        return ErrorInvalidOperation("bindAttribLocation: can't set the location of a name that starts with 'gl_'");
+        return ErrorInvalidOperation("bindAttribLocation: can't set the"
+                                     " location of a name that starts with"
+                                     " 'gl_'.");
 
     NS_LossyConvertUTF16toASCII cname(name);
     nsCString mappedName;
     if (mShaderValidation) {
         WebGLProgram::HashMapIdentifier(cname, &mappedName);
     } else {
         mappedName.Assign(cname);
     }
@@ -422,16 +425,27 @@ WebGLContext::CopyTexSubImage2D_base(Tex
     }
 
     TexInternalFormat effectiveInternalFormat =
         EffectiveInternalFormatFromUnsizedInternalFormatAndType(internalformat, framebuffertype);
 
     // this should never fail, validation happened earlier.
     MOZ_ASSERT(effectiveInternalFormat != LOCAL_GL_NONE);
 
+    const bool widthOrHeightIsZero = (width == 0 || height == 0);
+    if (gl->WorkAroundDriverBugs() &&
+        sub && widthOrHeightIsZero)
+    {
+        // NV driver on Linux complains that CopyTexSubImage2D(level=0,
+        // xoffset=0, yoffset=2, x=0, y=0, width=0, height=0) from a 300x150 FB
+        // to a 0x2 texture. This a useless thing to do, but technically legal.
+        // NV331.38 generates INVALID_VALUE.
+        return DummyFramebufferOperation(info);
+    }
+
     // check if the memory size of this texture may change with this call
     bool sizeMayChange = !sub;
     if (!sub && tex->HasImageInfoAt(texImageTarget, level)) {
         const WebGLTexture::ImageInfo& imageInfo = tex->ImageInfoAt(texImageTarget, level);
         sizeMayChange = width != imageInfo.Width() ||
                         height != imageInfo.Height() ||
                         effectiveInternalFormat != imageInfo.EffectiveInternalFormat();
     }
@@ -874,19 +888,29 @@ WebGLContext::GetActiveAttrib(WebGLProgr
 {
     if (IsContextLost())
         return nullptr;
 
     if (!ValidateObject("getActiveAttrib: program", prog))
         return nullptr;
 
     MakeContextCurrent();
+    GLuint progname = prog->GLName();
+
+    GLuint activeAttribs = 0;
+    gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTES,
+                      (GLint*)&activeAttribs);
+    if (index >= activeAttribs) {
+        ErrorInvalidValue("`index` (%i) must be less than ACTIVE_ATTRIBUTES"
+                          " (%i).",
+                          index, activeAttribs);
+        return nullptr;
+    }
 
     GLint len = 0;
-    GLuint progname = prog->GLName();;
     gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &len);
     if (len == 0)
         return nullptr;
 
     nsAutoArrayPtr<char> name(new char[len]);
     GLint attrsize = 0;
     GLuint attrtype = 0;
 
@@ -972,19 +996,29 @@ WebGLContext::GetActiveUniform(WebGLProg
 {
     if (IsContextLost())
         return nullptr;
 
     if (!ValidateObject("getActiveUniform: program", prog))
         return nullptr;
 
     MakeContextCurrent();
+    GLuint progname = prog->GLName();
+
+    GLuint activeUniforms = 0;
+    gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORMS,
+                      (GLint*)&activeUniforms);
+    if (index >= activeUniforms) {
+        ErrorInvalidValue("`index` (%i) must be less than ACTIVE_UNIFORMS"
+                          " (%i).",
+                          index, activeUniforms);
+        return nullptr;
+    }
 
     GLint len = 0;
-    GLuint progname = prog->GLName();
     gl->fGetProgramiv(progname, LOCAL_GL_ACTIVE_UNIFORM_MAX_LENGTH, &len);
     if (len == 0)
         return nullptr;
 
     nsAutoArrayPtr<char> name(new char[len]);
 
     GLint usize = 0;
     GLuint utype = 0;
@@ -1064,18 +1098,24 @@ WebGLContext::GetAttribLocation(WebGLPro
 }
 
 JS::Value
 WebGLContext::GetBufferParameter(GLenum target, GLenum pname)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (target != LOCAL_GL_ARRAY_BUFFER && target != LOCAL_GL_ELEMENT_ARRAY_BUFFER) {
-        ErrorInvalidEnumInfo("getBufferParameter: target", target);
+
+    WebGLRefPtr<WebGLBuffer>* slot = GetBufferSlotByTarget(target,
+                                                           "getBufferParameter");
+    if (!slot)
+        return JS::NullValue();
+
+    if (!*slot) {
+        ErrorInvalidOperation("No buffer bound to `target` (0x%4x).", target);
         return JS::NullValue();
     }
 
     MakeContextCurrent();
 
     switch (pname) {
         case LOCAL_GL_BUFFER_SIZE:
         case LOCAL_GL_BUFFER_USAGE:
@@ -2685,297 +2725,333 @@ WebGLContext::SurfaceFromElementResultTo
             return NS_ERROR_NOT_IMPLEMENTED;
     }
 
     imageOut = data;
 
     return NS_OK;
 }
 
-
+////////////////////////////////////////////////////////////////////////////////
+// Uniform setters.
 
 void
-WebGLContext::Uniform1i(WebGLUniformLocation *location_object, GLint a1)
+WebGLContext::Uniform1i(WebGLUniformLocation* loc, GLint a1)
 {
-    GLint location;
-    if (!ValidateUniformSetter("Uniform1i", location_object, location))
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_INT, "uniform1i", &rawLoc))
         return;
 
     // Only uniform1i can take sampler settings.
-    if (!ValidateSamplerUniformSetter("Uniform1i", location_object, a1))
+    if (!ValidateSamplerUniformSetter("Uniform1i", loc, a1))
         return;
 
     MakeContextCurrent();
-    gl->fUniform1i(location, a1);
+    gl->fUniform1i(rawLoc, a1);
 }
 
 void
-WebGLContext::Uniform2i(WebGLUniformLocation *location_object, GLint a1,
-                        GLint a2)
+WebGLContext::Uniform2i(WebGLUniformLocation* loc, GLint a1, GLint a2)
 {
-    GLint location;
-    if (!ValidateUniformSetter("Uniform2i", location_object, location))
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_INT, "uniform2i", &rawLoc))
         return;
 
     MakeContextCurrent();
-    gl->fUniform2i(location, a1, a2);
+    gl->fUniform2i(rawLoc, a1, a2);
 }
 
 void
-WebGLContext::Uniform3i(WebGLUniformLocation *location_object, GLint a1,
-                        GLint a2, GLint a3)
+WebGLContext::Uniform3i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3)
 {
-    GLint location;
-    if (!ValidateUniformSetter("Uniform3i", location_object, location))
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_INT, "uniform3i", &rawLoc))
         return;
 
     MakeContextCurrent();
-    gl->fUniform3i(location, a1, a2, a3);
+    gl->fUniform3i(rawLoc, a1, a2, a3);
 }
 
 void
-WebGLContext::Uniform4i(WebGLUniformLocation *location_object, GLint a1,
-                        GLint a2, GLint a3, GLint a4)
+WebGLContext::Uniform4i(WebGLUniformLocation* loc, GLint a1, GLint a2, GLint a3,
+                        GLint a4)
 {
-    GLint location;
-    if (!ValidateUniformSetter("Uniform4i", location_object, location))
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_INT, "uniform4i", &rawLoc))
         return;
 
     MakeContextCurrent();
-    gl->fUniform4i(location, a1, a2, a3, a4);
+    gl->fUniform4i(rawLoc, a1, a2, a3, a4);
 }
 
 void
-WebGLContext::Uniform1f(WebGLUniformLocation *location_object, GLfloat a1)
+WebGLContext::Uniform1f(WebGLUniformLocation* loc, GLfloat a1)
 {
-    GLint location;
-    if (!ValidateUniformSetter("Uniform1f", location_object, location))
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 1, LOCAL_GL_FLOAT, "uniform1f", &rawLoc))
         return;
+
     MakeContextCurrent();
-    gl->fUniform1f(location, a1);
+    gl->fUniform1f(rawLoc, a1);
 }
 
 void
-WebGLContext::Uniform2f(WebGLUniformLocation *location_object, GLfloat a1,
-                        GLfloat a2)
+WebGLContext::Uniform2f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2)
 {
-    GLint location;
-    if (!ValidateUniformSetter("Uniform2f", location_object, location))
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 2, LOCAL_GL_FLOAT, "uniform2f", &rawLoc))
         return;
+
     MakeContextCurrent();
-    gl->fUniform2f(location, a1, a2);
+    gl->fUniform2f(rawLoc, a1, a2);
 }
 
 void
-WebGLContext::Uniform3f(WebGLUniformLocation *location_object, GLfloat a1,
-                        GLfloat a2, GLfloat a3)
+WebGLContext::Uniform3f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2,
+                        GLfloat a3)
 {
-    GLint location;
-    if (!ValidateUniformSetter("Uniform3f", location_object, location))
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 3, LOCAL_GL_FLOAT, "uniform3f", &rawLoc))
         return;
+
     MakeContextCurrent();
-    gl->fUniform3f(location, a1, a2, a3);
+    gl->fUniform3f(rawLoc, a1, a2, a3);
 }
 
 void
-WebGLContext::Uniform4f(WebGLUniformLocation *location_object, GLfloat a1,
-                        GLfloat a2, GLfloat a3, GLfloat a4)
+WebGLContext::Uniform4f(WebGLUniformLocation* loc, GLfloat a1, GLfloat a2,
+                        GLfloat a3, GLfloat a4)
 {
-    GLint location;
-    if (!ValidateUniformSetter("Uniform4f", location_object, location))
+    GLuint rawLoc;
+    if (!ValidateUniformSetter(loc, 4, LOCAL_GL_FLOAT, "uniform4f", &rawLoc))
         return;
+
     MakeContextCurrent();
-    gl->fUniform4f(location, a1, a2, a3, a4);
+    gl->fUniform4f(rawLoc, a1, a2, a3, a4);
+}
+
+////////////////////////////////////////
+// Array
+
+void
+WebGLContext::Uniform1iv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                              const GLint* data)
+{
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+    if (!ValidateUniformArraySetter(loc, 1, LOCAL_GL_INT, arrayLength,
+                                    "uniform1iv", &rawLoc,
+                                    &numElementsToUpload))
+    {
+        return;
+    }
+
+    if (!ValidateSamplerUniformSetter("uniform1iv", loc, data[0]))
+        return;
+
+    MakeContextCurrent();
+    gl->fUniform1iv(rawLoc, numElementsToUpload, data);
 }
 
 void
-WebGLContext::Uniform1iv_base(WebGLUniformLocation *location_object,
-                              uint32_t arrayLength, const GLint* data)
+WebGLContext::Uniform2iv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                              const GLint* data)
 {
-    uint32_t numElementsToUpload;
-    GLint location;
-    if (!ValidateUniformArraySetter("Uniform1iv", 1, location_object, location,
-                                    numElementsToUpload, arrayLength)) {
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+    if (!ValidateUniformArraySetter(loc, 2, LOCAL_GL_INT, arrayLength,
+                                    "uniform2iv", &rawLoc,
+                                    &numElementsToUpload))
+    {
         return;
     }
 
-    if (!ValidateSamplerUniformSetter("Uniform1iv", location_object, data[0]))
-        return;
-
-    MakeContextCurrent();
-    gl->fUniform1iv(location, numElementsToUpload, data);
-}
-
-void
-WebGLContext::Uniform2iv_base(WebGLUniformLocation *location_object,
-                              uint32_t arrayLength, const GLint* data)
-{
-    uint32_t numElementsToUpload;
-    GLint location;
-    if (!ValidateUniformArraySetter("Uniform2iv", 2, location_object, location,
-                                    numElementsToUpload, arrayLength)) {
-        return;
-    }
-
-    if (!ValidateSamplerUniformSetter("Uniform2iv", location_object, data[0]) ||
-        !ValidateSamplerUniformSetter("Uniform2iv", location_object, data[1]))
+    if (!ValidateSamplerUniformSetter("uniform2iv", loc, data[0]) ||
+        !ValidateSamplerUniformSetter("uniform2iv", loc, data[1]))
     {
         return;
     }
 
     MakeContextCurrent();
-    gl->fUniform2iv(location, numElementsToUpload, data);
+    gl->fUniform2iv(rawLoc, numElementsToUpload, data);
 }
 
 void
-WebGLContext::Uniform3iv_base(WebGLUniformLocation *location_object,
-                              uint32_t arrayLength, const GLint* data)
+WebGLContext::Uniform3iv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                              const GLint* data)
 {
-    uint32_t numElementsToUpload;
-    GLint location;
-    if (!ValidateUniformArraySetter("Uniform3iv", 3, location_object, location,
-                                    numElementsToUpload, arrayLength)) {
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+    if (!ValidateUniformArraySetter(loc, 3, LOCAL_GL_INT, arrayLength,
+                                    "uniform3iv", &rawLoc,
+                                    &numElementsToUpload))
+    {
         return;
     }
 
-    if (!ValidateSamplerUniformSetter("Uniform3iv", location_object, data[0]) ||
-        !ValidateSamplerUniformSetter("Uniform3iv", location_object, data[1]) ||
-        !ValidateSamplerUniformSetter("Uniform3iv", location_object, data[2]))
+    if (!ValidateSamplerUniformSetter("uniform3iv", loc, data[0]) ||
+        !ValidateSamplerUniformSetter("uniform3iv", loc, data[1]) ||
+        !ValidateSamplerUniformSetter("uniform3iv", loc, data[2]))
     {
         return;
     }
 
     MakeContextCurrent();
-    gl->fUniform3iv(location, numElementsToUpload, data);
+    gl->fUniform3iv(rawLoc, numElementsToUpload, data);
 }
 
 void
-WebGLContext::Uniform4iv_base(WebGLUniformLocation *location_object,
-                              uint32_t arrayLength, const GLint* data)
+WebGLContext::Uniform4iv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                              const GLint* data)
 {
-    uint32_t numElementsToUpload;
-    GLint location;
-    if (!ValidateUniformArraySetter("Uniform4iv", 4, location_object, location,
-                                    numElementsToUpload, arrayLength)) {
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+    if (!ValidateUniformArraySetter(loc, 4, LOCAL_GL_INT, arrayLength,
+                                    "uniform4iv", &rawLoc,
+                                    &numElementsToUpload))
+    {
+        return;
+    }
+
+    if (!ValidateSamplerUniformSetter("uniform4iv", loc, data[0]) ||
+        !ValidateSamplerUniformSetter("uniform4iv", loc, data[1]) ||
+        !ValidateSamplerUniformSetter("uniform4iv", loc, data[2]) ||
+        !ValidateSamplerUniformSetter("uniform4iv", loc, data[3]))
+    {
         return;
     }
 
-    if (!ValidateSamplerUniformSetter("Uniform4iv", location_object, data[0]) ||
-        !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[1]) ||
-        !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[2]) ||
-        !ValidateSamplerUniformSetter("Uniform4iv", location_object, data[3]))
+    MakeContextCurrent();
+    gl->fUniform4iv(rawLoc, numElementsToUpload, data);
+}
+
+void
+WebGLContext::Uniform1fv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                              const GLfloat* data)
+{
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+    if (!ValidateUniformArraySetter(loc, 1, LOCAL_GL_FLOAT, arrayLength,
+                                    "uniform1fv", &rawLoc,
+                                    &numElementsToUpload))
+    {
+        return;
+    }
+
+    MakeContextCurrent();
+    gl->fUniform1fv(rawLoc, numElementsToUpload, data);
+}
+
+void
+WebGLContext::Uniform2fv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                              const GLfloat* data)
+{
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+    if (!ValidateUniformArraySetter(loc, 2, LOCAL_GL_FLOAT, arrayLength,
+                                    "uniform2fv", &rawLoc,
+                                    &numElementsToUpload))
     {
         return;
     }
 
     MakeContextCurrent();
-    gl->fUniform4iv(location, numElementsToUpload, data);
-}
-
-void
-WebGLContext::Uniform1fv_base(WebGLUniformLocation *location_object,
-                              uint32_t arrayLength, const GLfloat* data)
-{
-    uint32_t numElementsToUpload;
-    GLint location;
-    if (!ValidateUniformArraySetter("Uniform1fv", 1, location_object, location,
-                                    numElementsToUpload, arrayLength)) {
-        return;
-    }
-    MakeContextCurrent();
-    gl->fUniform1fv(location, numElementsToUpload, data);
+    gl->fUniform2fv(rawLoc, numElementsToUpload, data);
 }
 
 void
-WebGLContext::Uniform2fv_base(WebGLUniformLocation *location_object,
-                              uint32_t arrayLength, const GLfloat* data)
+WebGLContext::Uniform3fv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                              const GLfloat* data)
 {
-    uint32_t numElementsToUpload;
-    GLint location;
-    if (!ValidateUniformArraySetter("Uniform2fv", 2, location_object, location,
-                                    numElementsToUpload, arrayLength)) {
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+    if (!ValidateUniformArraySetter(loc, 3, LOCAL_GL_FLOAT, arrayLength,
+                                    "uniform3fv", &rawLoc,
+                                    &numElementsToUpload))
+    {
         return;
     }
+
     MakeContextCurrent();
-    gl->fUniform2fv(location, numElementsToUpload, data);
-}
-
-void
-WebGLContext::Uniform3fv_base(WebGLUniformLocation *location_object,
-                              uint32_t arrayLength, const GLfloat* data)
-{
-    uint32_t numElementsToUpload;
-    GLint location;
-    if (!ValidateUniformArraySetter("Uniform3fv", 3, location_object, location,
-                                    numElementsToUpload, arrayLength)) {
-        return;
-    }
-    MakeContextCurrent();
-    gl->fUniform3fv(location, numElementsToUpload, data);
+    gl->fUniform3fv(rawLoc, numElementsToUpload, data);
 }
 
 void
-WebGLContext::Uniform4fv_base(WebGLUniformLocation *location_object,
-                              uint32_t arrayLength, const GLfloat* data)
+WebGLContext::Uniform4fv_base(WebGLUniformLocation* loc, size_t arrayLength,
+                              const GLfloat* data)
 {
-    uint32_t numElementsToUpload;
-    GLint location;
-    if (!ValidateUniformArraySetter("Uniform4fv", 4, location_object, location,
-                                    numElementsToUpload, arrayLength)) {
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+    if (!ValidateUniformArraySetter(loc, 4, LOCAL_GL_FLOAT, arrayLength,
+                                    "uniform4fv", &rawLoc,
+                                    &numElementsToUpload))
+    {
         return;
     }
+
     MakeContextCurrent();
-    gl->fUniform4fv(location, numElementsToUpload, data);
+    gl->fUniform4fv(rawLoc, numElementsToUpload, data);
 }
 
+////////////////////////////////////////
+// Matrix
+
 void
-WebGLContext::UniformMatrix2fv_base(WebGLUniformLocation* location_object,
-                                    WebGLboolean aTranspose, uint32_t arrayLength,
-                                    const float* data)
+WebGLContext::UniformMatrix2fv_base(WebGLUniformLocation* loc, bool transpose,
+                                    size_t arrayLength, const float* data)
 {
-    uint32_t numElementsToUpload;
-    GLint location;
-    if (!ValidateUniformMatrixArraySetter("UniformMatrix2fv", 2, location_object, location,
-                                         numElementsToUpload, arrayLength, aTranspose)) {
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+    if (!ValidateUniformMatrixArraySetter(loc, 2, LOCAL_GL_FLOAT, arrayLength,
+                                          transpose, "uniformMatrix2fv",
+                                          &rawLoc, &numElementsToUpload))
+    {
         return;
     }
+
     MakeContextCurrent();
-    gl->fUniformMatrix2fv(location, numElementsToUpload, false, data);
+    gl->fUniformMatrix2fv(rawLoc, numElementsToUpload, false, data);
 }
 
 void
-WebGLContext::UniformMatrix3fv_base(WebGLUniformLocation* location_object,
-                                    WebGLboolean aTranspose, uint32_t arrayLength,
-                                    const float* data)
+WebGLContext::UniformMatrix3fv_base(WebGLUniformLocation* loc, bool transpose,
+                                    size_t arrayLength, const float* data)
 {
-    uint32_t numElementsToUpload;
-    GLint location;
-    if (!ValidateUniformMatrixArraySetter("UniformMatrix3fv", 3, location_object, location,
-                                         numElementsToUpload, arrayLength, aTranspose)) {
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+    if (!ValidateUniformMatrixArraySetter(loc, 3, LOCAL_GL_FLOAT, arrayLength,
+                                          transpose, "uniformMatrix3fv",
+                                          &rawLoc, &numElementsToUpload))
+    {
         return;
     }
+
     MakeContextCurrent();
-    gl->fUniformMatrix3fv(location, numElementsToUpload, false, data);
+    gl->fUniformMatrix3fv(rawLoc, numElementsToUpload, false, data);
 }
 
 void
-WebGLContext::UniformMatrix4fv_base(WebGLUniformLocation* location_object,
-                                    WebGLboolean aTranspose, uint32_t arrayLength,
-                                    const float* data)
+WebGLContext::UniformMatrix4fv_base(WebGLUniformLocation* loc, bool transpose,
+                                    size_t arrayLength, const float* data)
 {
-    uint32_t numElementsToUpload;
-    GLint location;
-    if (!ValidateUniformMatrixArraySetter("UniformMatrix4fv", 4, location_object, location,
-                                         numElementsToUpload, arrayLength, aTranspose)) {
+    GLuint rawLoc;
+    GLsizei numElementsToUpload;
+    if (!ValidateUniformMatrixArraySetter(loc, 4, LOCAL_GL_FLOAT, arrayLength,
+                                          transpose, "uniformMatrix4fv",
+                                          &rawLoc, &numElementsToUpload))
+    {
         return;
     }
+
     MakeContextCurrent();
-    gl->fUniformMatrix4fv(location, numElementsToUpload, false, data);
+    gl->fUniformMatrix4fv(rawLoc, numElementsToUpload, false, data);
 }
 
+////////////////////////////////////////////////////////////////////////////////
+
 void
 WebGLContext::UseProgram(WebGLProgram *prog)
 {
     if (IsContextLost())
         return;
 
     if (!ValidateObjectAllowNull("useProgram", prog))
         return;
@@ -4262,19 +4338,28 @@ void
 WebGLContext::Finish() {
     if (IsContextLost())
         return;
     MakeContextCurrent();
     gl->fFinish();
 }
 
 void
-WebGLContext::LineWidth(GLfloat width) {
+WebGLContext::LineWidth(GLfloat width)
+{
     if (IsContextLost())
         return;
+
+    // Doing it this way instead of `if (width <= 0.0)` handles NaNs.
+    const bool isValid = width > 0.0;
+    if (!isValid) {
+        ErrorInvalidValue("lineWidth: `width` must be positive and non-zero.");
+        return;
+    }
+
     MakeContextCurrent();
     gl->fLineWidth(width);
 }
 
 void
 WebGLContext::PolygonOffset(GLfloat factor, GLfloat units) {
     if (IsContextLost())
         return;
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -669,17 +669,17 @@ WebGLContext::IsTextureFormatCompressed(
 {
     return IsCompressedTextureFormat(format.get());
 }
 
 GLenum
 WebGLContext::GetAndFlushUnderlyingGLErrors()
 {
     // Get and clear GL error in ALL cases.
-    GLenum error = gl->GetAndClearError();
+    GLenum error = gl->fGetError();
 
     // Only store in mUnderlyingGLError if is hasn't already recorded an
     // error.
     if (!mUnderlyingGLError)
         mUnderlyingGLError = error;
 
     return error;
 }
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -1317,119 +1317,178 @@ WebGLContext::ValidateAttribArraySetter(
     }
     if (arrayLength < cnt) {
         ErrorInvalidOperation("%s: array must be >= %d elements", name, cnt);
         return false;
     }
     return true;
 }
 
-bool
-WebGLContext::ValidateUniformArraySetter(const char* name, uint32_t expectedElemSize, WebGLUniformLocation *location_object,
-                                         GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength)
+static bool
+IsUniformSetterTypeValid(GLenum setterType, GLenum uniformType)
 {
-    if (IsContextLost())
-        return false;
-    if (!ValidateUniformLocation(name, location_object))
-        return false;
-    location = location_object->Location();
-    uint32_t uniformElemSize = location_object->ElementSize();
-    if (expectedElemSize != uniformElemSize) {
-        ErrorInvalidOperation("%s: this function expected a uniform of element size %d,"
-                              " got a uniform of element size %d", name,
-                              expectedElemSize,
-                              uniformElemSize);
+    switch (uniformType) {
+    case LOCAL_GL_BOOL:
+    case LOCAL_GL_BOOL_VEC2:
+    case LOCAL_GL_BOOL_VEC3:
+    case LOCAL_GL_BOOL_VEC4:
+        return true; // GLfloat(0.0) sets a bool to false.
+
+    case LOCAL_GL_INT:
+    case LOCAL_GL_SAMPLER_2D:
+    case LOCAL_GL_SAMPLER_CUBE:
+    case LOCAL_GL_INT_VEC2:
+    case LOCAL_GL_INT_VEC3:
+    case LOCAL_GL_INT_VEC4:
+        return setterType == LOCAL_GL_INT;
+
+    case LOCAL_GL_FLOAT:
+    case LOCAL_GL_FLOAT_VEC2:
+    case LOCAL_GL_FLOAT_VEC3:
+    case LOCAL_GL_FLOAT_VEC4:
+    case LOCAL_GL_FLOAT_MAT2:
+    case LOCAL_GL_FLOAT_MAT3:
+    case LOCAL_GL_FLOAT_MAT4:
+        return setterType == LOCAL_GL_FLOAT;
+
+    default:
+        MOZ_ASSERT(false); // should never get here
         return false;
     }
-    if (arrayLength == 0 ||
-        arrayLength % expectedElemSize)
-    {
-        ErrorInvalidValue("%s: expected an array of length a multiple"
-                          " of %d, got an array of length %d", name,
-                          expectedElemSize,
-                          arrayLength);
+}
+
+static bool
+CheckUniformSizeAndType(WebGLContext& webgl, WebGLUniformLocation* loc,
+                        uint8_t setterElemSize, GLenum setterType,
+                        const char* info)
+{
+    if (setterElemSize != loc->ElementSize()) {
+        webgl.ErrorInvalidOperation("%s: Bad uniform size: %i", info,
+                                    loc->ElementSize());
+        return false;
+    }
+
+    if (!IsUniformSetterTypeValid(setterType, loc->Info().type)) {
+        webgl.ErrorInvalidOperation("%s: Bad uniform type: %i", info,
+                                    loc->Info().type);
         return false;
     }
-    const WebGLUniformInfo& info = location_object->Info();
-    if (!info.isArray &&
-        arrayLength != expectedElemSize) {
-        ErrorInvalidOperation("%s: expected an array of length exactly"
-                              " %d (since this uniform is not an array"
-                              " uniform), got an array of length %d", name,
-                              expectedElemSize,
-                              arrayLength);
+
+    return true;
+}
+
+static bool
+CheckUniformArrayLength(WebGLContext& webgl, WebGLUniformLocation* loc,
+                        uint8_t setterElemSize, size_t setterArraySize,
+                        const char* info)
+{
+    if (setterArraySize == 0 ||
+        setterArraySize % setterElemSize)
+    {
+        webgl.ErrorInvalidValue("%s: expected an array of length a multiple of"
+                                " %d, got an array of length %d.", info,
+                                setterElemSize, setterArraySize);
         return false;
     }
-    numElementsToUpload =
-        std::min(info.arraySize, arrayLength / expectedElemSize);
+
+    if (!loc->Info().isArray &&
+        setterArraySize != setterElemSize)
+    {
+        webgl.ErrorInvalidOperation("%s: expected an array of length exactly %d"
+                                    " (since this uniform is not an array"
+                                    " uniform), got an array of length %d.",
+                                    info, setterElemSize, setterArraySize);
+        return false;
+    }
+
     return true;
 }
 
 bool
-WebGLContext::ValidateUniformMatrixArraySetter(const char* name, int dim, WebGLUniformLocation *location_object,
-                                              GLint& location, uint32_t& numElementsToUpload, uint32_t arrayLength,
-                                              WebGLboolean aTranspose)
+WebGLContext::ValidateUniformSetter(WebGLUniformLocation* loc,
+                                    uint8_t setterElemSize, GLenum setterType,
+                                    const char* info, GLuint* out_rawLoc)
 {
-    uint32_t expectedElemSize = (dim)*(dim);
     if (IsContextLost())
         return false;
-    if (!ValidateUniformLocation(name, location_object))
-        return false;
-    location = location_object->Location();
-    uint32_t uniformElemSize = location_object->ElementSize();
-    if (expectedElemSize != uniformElemSize) {
-        ErrorInvalidOperation("%s: this function expected a uniform of element size %d,"
-                              " got a uniform of element size %d", name,
-                              expectedElemSize,
-                              uniformElemSize);
-        return false;
-    }
-    if (arrayLength == 0 ||
-        arrayLength % expectedElemSize)
-    {
-        ErrorInvalidValue("%s: expected an array of length a multiple"
-                          " of %d, got an array of length %d", name,
-                          expectedElemSize,
-                          arrayLength);
+
+    if (!ValidateUniformLocation(info, loc))
         return false;
-    }
-    const WebGLUniformInfo& info = location_object->Info();
-    if (!info.isArray &&
-        arrayLength != expectedElemSize) {
-        ErrorInvalidOperation("%s: expected an array of length exactly"
-                              " %d (since this uniform is not an array"
-                              " uniform), got an array of length %d", name,
-                              expectedElemSize,
-                              arrayLength);
+
+    if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info))
         return false;
-    }
-    if (aTranspose) {
-        ErrorInvalidValue("%s: transpose must be FALSE as per the "
-                          "OpenGL ES 2.0 spec", name);
-        return false;
-    }
-    numElementsToUpload =
-        std::min(info.arraySize, arrayLength / (expectedElemSize));
+
+    *out_rawLoc = loc->Location();
     return true;
 }
 
 bool
-WebGLContext::ValidateUniformSetter(const char* name, WebGLUniformLocation *location_object, GLint& location)
+WebGLContext::ValidateUniformArraySetter(WebGLUniformLocation* loc,
+                                         uint8_t setterElemSize, GLenum setterType,
+                                         size_t setterArraySize,
+                                         const char* info, GLuint* out_rawLoc,
+                                         GLsizei* out_numElementsToUpload)
 {
     if (IsContextLost())
         return false;
-    if (!ValidateUniformLocation(name, location_object))
+
+    if (!ValidateUniformLocation(info, loc))
+        return false;
+
+    if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info))
         return false;
-    location = location_object->Location();
+
+    if (!CheckUniformArrayLength(*this, loc, setterElemSize, setterArraySize,
+                                 info))
+    {
+        return false;
+    }
+
+    *out_rawLoc = loc->Location();
+    *out_numElementsToUpload = std::min((size_t)loc->Info().arraySize,
+                                        setterArraySize / setterElemSize);
     return true;
 }
 
-bool WebGLContext::ValidateAttribIndex(GLuint index, const char *info)
+bool
+WebGLContext::ValidateUniformMatrixArraySetter(WebGLUniformLocation* loc,
+                                               uint8_t setterDims,
+                                               GLenum setterType,
+                                               size_t setterArraySize,
+                                               bool setterTranspose,
+                                               const char* info,
+                                               GLuint* out_rawLoc,
+                                               GLsizei* out_numElementsToUpload)
 {
-    return mBoundVertexArray->EnsureAttrib(index, info);
+    uint8_t setterElemSize = setterDims * setterDims;
+
+    if (IsContextLost())
+        return false;
+
+    if (!ValidateUniformLocation(info, loc))
+        return false;
+
+    if (!CheckUniformSizeAndType(*this, loc, setterElemSize, setterType, info))
+        return false;
+
+    if (!CheckUniformArrayLength(*this, loc, setterElemSize, setterArraySize,
+                                 info))
+    {
+        return false;
+    }
+
+    if (setterTranspose) {
+        ErrorInvalidValue("%s: `transpose` must be false.", info);
+        return false;
+    }
+
+    *out_rawLoc = loc->Location();
+    *out_numElementsToUpload = std::min((size_t)loc->Info().arraySize,
+                                        setterArraySize / setterElemSize);
+    return true;
 }
 
 bool WebGLContext::ValidateStencilParamsForDrawCall()
 {
   const char *msg = "%s set different front and back stencil %s. Drawing in this configuration is not allowed.";
   if (mStencilRefFront != mStencilRefBack) {
       ErrorInvalidOperation(msg, "stencilFuncSeparate", "reference values");
       return false;
@@ -1615,41 +1674,32 @@ WebGLContext::InitAndValidateGL()
             mGLMaxFragmentUniformVectors /= 4;
             gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS, &mGLMaxVertexUniformVectors);
             mGLMaxVertexUniformVectors /= 4;
 
             // we are now going to try to read GL_MAX_VERTEX_OUTPUT_COMPONENTS and GL_MAX_FRAGMENT_INPUT_COMPONENTS,
             // however these constants only entered the OpenGL standard at OpenGL 3.2. So we will try reading,
             // and check OpenGL error for INVALID_ENUM.
 
-            // before we start, we check that no error already occurred, to prevent hiding it in our subsequent error handling
-            error = gl->GetAndClearError();
-            if (error != LOCAL_GL_NO_ERROR) {
-                GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error);
-                return false;
-            }
-
             // On the public_webgl list, "problematic GetParameter pnames" thread, the following formula was given:
             //   mGLMaxVaryingVectors = min (GL_MAX_VERTEX_OUTPUT_COMPONENTS, GL_MAX_FRAGMENT_INPUT_COMPONENTS) / 4
-            GLint maxVertexOutputComponents,
-                  minFragmentInputComponents;
-            gl->fGetIntegerv(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS, &maxVertexOutputComponents);
-            gl->fGetIntegerv(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS, &minFragmentInputComponents);
+            GLint maxVertexOutputComponents = 0;
+            GLint maxFragmentInputComponents = 0;
 
-            error = gl->GetAndClearError();
-            switch (error) {
-                case LOCAL_GL_NO_ERROR:
-                    mGLMaxVaryingVectors = std::min(maxVertexOutputComponents, minFragmentInputComponents) / 4;
-                    break;
-                case LOCAL_GL_INVALID_ENUM:
-                    mGLMaxVaryingVectors = 16; // = 64/4, 64 is the min value for maxVertexOutputComponents in OpenGL 3.2 spec
-                    break;
-                default:
-                    GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error);
-                    return false;
+            const bool ok = (gl->GetPotentialInteger(LOCAL_GL_MAX_VERTEX_OUTPUT_COMPONENTS,
+                                                     &maxVertexOutputComponents) &&
+                             gl->GetPotentialInteger(LOCAL_GL_MAX_FRAGMENT_INPUT_COMPONENTS,
+                                                     &maxFragmentInputComponents));
+
+            if (ok) {
+                mGLMaxVaryingVectors = std::min(maxVertexOutputComponents,
+                                                maxFragmentInputComponents) / 4;
+            } else {
+                mGLMaxVaryingVectors = 16;
+                // = 64/4, 64 is the min value for maxVertexOutputComponents in OpenGL 3.2 spec
             }
         }
     }
 
     // Always 1 for GLES2
     mMaxFramebufferColorAttachments = 1;
 
     if (!gl->IsGLES()) {
@@ -1689,19 +1739,20 @@ WebGLContext::InitAndValidateGL()
             GenerateWarning("GLSL translator initialization failed!");
             return false;
         }
     }
 
     // Mesa can only be detected with the GL_VERSION string, of the form "2.1 Mesa 7.11.0"
     mIsMesa = strstr((const char *)(gl->fGetString(LOCAL_GL_VERSION)), "Mesa");
 
-    // notice that the point of calling GetAndClearError here is not only to check for error,
-    // it is also to reset the error flags so that a subsequent WebGL getError call will give the correct result.
-    error = gl->GetAndClearError();
+    // Notice that the point of calling fGetError here is not only to check for
+    // errors, but also to reset the error flags so that a subsequent WebGL
+    // getError call will give the correct result.
+    error = gl->fGetError();
     if (error != LOCAL_GL_NO_ERROR) {
         GenerateWarning("GL error 0x%x occurred during WebGL context initialization!", error);
         return false;
     }
 
     if (IsWebGL2() &&
         !InitWebGL2())
     {
--- a/dom/canvas/WebGLContextVertices.cpp
+++ b/dom/canvas/WebGLContextVertices.cpp
@@ -15,22 +15,46 @@
 #include "WebGLTexture.h"
 #include "WebGLUniformInfo.h"
 #include "WebGLVertexArray.h"
 #include "WebGLVertexAttribData.h"
 
 using namespace mozilla;
 using namespace dom;
 
+static bool
+CheckAttribIndex(WebGLContext& webgl, GLuint index, const char* info)
+{
+    if (index >= webgl.MaxVertexAttribs()) {
+        if (index == GLuint(-1)) {
+            webgl.ErrorInvalidValue("%s: -1 is not a valid `index`. This value"
+                                    " probably comes from a getAttribLocation()"
+                                    " call, where this return value -1 means"
+                                    " that the passed name didn't correspond to"
+                                    " an active attribute in the specified"
+                                    " program.", info);
+        } else {
+            webgl.ErrorInvalidValue("%s: `index` must be less than"
+                                    " MAX_VERTEX_ATTRIBS.", info);
+        }
+        return false;
+    }
+
+    return true;
+}
+
 void
 WebGLContext::VertexAttrib1f(GLuint index, GLfloat x0)
 {
     if (IsContextLost())
         return;
 
+    if (!CheckAttribIndex(*this, index, "vertexAttrib1f"))
+        return;
+
     MakeContextCurrent();
 
     if (index) {
         gl->fVertexAttrib1f(index, x0);
     } else {
         mVertexAttrib0Vector[0] = x0;
         mVertexAttrib0Vector[1] = 0;
         mVertexAttrib0Vector[2] = 0;
@@ -41,16 +65,19 @@ WebGLContext::VertexAttrib1f(GLuint inde
 }
 
 void
 WebGLContext::VertexAttrib2f(GLuint index, GLfloat x0, GLfloat x1)
 {
     if (IsContextLost())
         return;
 
+    if (!CheckAttribIndex(*this, index, "vertexAttrib2f"))
+        return;
+
     MakeContextCurrent();
 
     if (index) {
         gl->fVertexAttrib2f(index, x0, x1);
     } else {
         mVertexAttrib0Vector[0] = x0;
         mVertexAttrib0Vector[1] = x1;
         mVertexAttrib0Vector[2] = 0;
@@ -61,16 +88,19 @@ WebGLContext::VertexAttrib2f(GLuint inde
 }
 
 void
 WebGLContext::VertexAttrib3f(GLuint index, GLfloat x0, GLfloat x1, GLfloat x2)
 {
     if (IsContextLost())
         return;
 
+    if (!CheckAttribIndex(*this, index, "vertexAttrib3f"))
+        return;
+
     MakeContextCurrent();
 
     if (index) {
         gl->fVertexAttrib3f(index, x0, x1, x2);
     } else {
         mVertexAttrib0Vector[0] = x0;
         mVertexAttrib0Vector[1] = x1;
         mVertexAttrib0Vector[2] = x2;
@@ -82,158 +112,178 @@ WebGLContext::VertexAttrib3f(GLuint inde
 
 void
 WebGLContext::VertexAttrib4f(GLuint index, GLfloat x0, GLfloat x1,
                              GLfloat x2, GLfloat x3)
 {
     if (IsContextLost())
         return;
 
+    if (!CheckAttribIndex(*this, index, "vertexAttrib4f"))
+        return;
+
     MakeContextCurrent();
 
     if (index) {
         gl->fVertexAttrib4f(index, x0, x1, x2, x3);
     } else {
         mVertexAttrib0Vector[0] = x0;
         mVertexAttrib0Vector[1] = x1;
         mVertexAttrib0Vector[2] = x2;
         mVertexAttrib0Vector[3] = x3;
         if (gl->IsGLES())
             gl->fVertexAttrib4f(index, x0, x1, x2, x3);
     }
 }
 
 
 void
-WebGLContext::VertexAttrib1fv_base(GLuint idx, uint32_t arrayLength,
+WebGLContext::VertexAttrib1fv_base(GLuint index, uint32_t arrayLength,
                                    const GLfloat* ptr)
 {
     if (!ValidateAttribArraySetter("VertexAttrib1fv", 1, arrayLength))
         return;
 
+    if (!CheckAttribIndex(*this, index, "vertexAttrib1fv"))
+        return;
+
     MakeContextCurrent();
-    if (idx) {
-        gl->fVertexAttrib1fv(idx, ptr);
+    if (index) {
+        gl->fVertexAttrib1fv(index, ptr);
     } else {
         mVertexAttrib0Vector[0] = ptr[0];
         mVertexAttrib0Vector[1] = GLfloat(0);
         mVertexAttrib0Vector[2] = GLfloat(0);
         mVertexAttrib0Vector[3] = GLfloat(1);
         if (gl->IsGLES())
-            gl->fVertexAttrib1fv(idx, ptr);
+            gl->fVertexAttrib1fv(index, ptr);
     }
 }
 
 void
-WebGLContext::VertexAttrib2fv_base(GLuint idx, uint32_t arrayLength,
+WebGLContext::VertexAttrib2fv_base(GLuint index, uint32_t arrayLength,
                                    const GLfloat* ptr)
 {
     if (!ValidateAttribArraySetter("VertexAttrib2fv", 2, arrayLength))
         return;
 
+    if (!CheckAttribIndex(*this, index, "vertexAttrib2fv"))
+        return;
+
     MakeContextCurrent();
-    if (idx) {
-        gl->fVertexAttrib2fv(idx, ptr);
+    if (index) {
+        gl->fVertexAttrib2fv(index, ptr);
     } else {
         mVertexAttrib0Vector[0] = ptr[0];
         mVertexAttrib0Vector[1] = ptr[1];
         mVertexAttrib0Vector[2] = GLfloat(0);
         mVertexAttrib0Vector[3] = GLfloat(1);
         if (gl->IsGLES())
-            gl->fVertexAttrib2fv(idx, ptr);
+            gl->fVertexAttrib2fv(index, ptr);
     }
 }
 
 void
-WebGLContext::VertexAttrib3fv_base(GLuint idx, uint32_t arrayLength,
+WebGLContext::VertexAttrib3fv_base(GLuint index, uint32_t arrayLength,
                                    const GLfloat* ptr)
 {
     if (!ValidateAttribArraySetter("VertexAttrib3fv", 3, arrayLength))
         return;
 
+    if (!CheckAttribIndex(*this, index, "vertexAttrib3fv"))
+        return;
+
     MakeContextCurrent();
-    if (idx) {
-        gl->fVertexAttrib3fv(idx, ptr);
+    if (index) {
+        gl->fVertexAttrib3fv(index, ptr);
     } else {
         mVertexAttrib0Vector[0] = ptr[0];
         mVertexAttrib0Vector[1] = ptr[1];
         mVertexAttrib0Vector[2] = ptr[2];
         mVertexAttrib0Vector[3] = GLfloat(1);
         if (gl->IsGLES())
-            gl->fVertexAttrib3fv(idx, ptr);
+            gl->fVertexAttrib3fv(index, ptr);
     }
 }
 
 void
-WebGLContext::VertexAttrib4fv_base(GLuint idx, uint32_t arrayLength,
+WebGLContext::VertexAttrib4fv_base(GLuint index, uint32_t arrayLength,
                                    const GLfloat* ptr)
 {
     if (!ValidateAttribArraySetter("VertexAttrib4fv", 4, arrayLength))
         return;
 
+    if (!CheckAttribIndex(*this, index, "vertexAttrib4fv"))
+        return;
+
     MakeContextCurrent();
-    if (idx) {
-        gl->fVertexAttrib4fv(idx, ptr);
+    if (index) {
+        gl->fVertexAttrib4fv(index, ptr);
     } else {
         mVertexAttrib0Vector[0] = ptr[0];
         mVertexAttrib0Vector[1] = ptr[1];
         mVertexAttrib0Vector[2] = ptr[2];
         mVertexAttrib0Vector[3] = ptr[3];
         if (gl->IsGLES())
-            gl->fVertexAttrib4fv(idx, ptr);
+            gl->fVertexAttrib4fv(index, ptr);
     }
 }
 
 void
 WebGLContext::EnableVertexAttribArray(GLuint index)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, "enableVertexAttribArray"))
+    if (!CheckAttribIndex(*this, index, "enableVertexAttribArray"))
         return;
 
     MakeContextCurrent();
     InvalidateBufferFetching();
 
     gl->fEnableVertexAttribArray(index);
-    MOZ_ASSERT(mBoundVertexArray->HasAttrib(index)); // should have been validated earlier
+
+    MOZ_ASSERT(mBoundVertexArray);
+    mBoundVertexArray->EnsureAttrib(index);
     mBoundVertexArray->mAttribs[index].enabled = true;
 }
 
 void
 WebGLContext::DisableVertexAttribArray(GLuint index)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, "disableVertexAttribArray"))
+    if (!CheckAttribIndex(*this, index, "disableVertexAttribArray"))
         return;
 
     MakeContextCurrent();
     InvalidateBufferFetching();
 
     if (index || gl->IsGLES())
         gl->fDisableVertexAttribArray(index);
 
-    MOZ_ASSERT(mBoundVertexArray->HasAttrib(index)); // should have been validated earlier
+    MOZ_ASSERT(mBoundVertexArray);
+    mBoundVertexArray->EnsureAttrib(index);
     mBoundVertexArray->mAttribs[index].enabled = false;
 }
 
-
 JS::Value
 WebGLContext::GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
                               ErrorResult& rv)
 {
     if (IsContextLost())
         return JS::NullValue();
 
-    if (!ValidateAttribIndex(index, "getVertexAttrib"))
+    if (!CheckAttribIndex(*this, index, "getVertexAttrib"))
         return JS::NullValue();
 
+    MOZ_ASSERT(mBoundVertexArray);
+    mBoundVertexArray->EnsureAttrib(index);
+
     MakeContextCurrent();
 
     switch (pname) {
         case LOCAL_GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING:
         {
             return WebGLObjectAsJSValue(cx, mBoundVertexArray->mAttribs[index].buf.get(), rv);
         }
 
@@ -295,45 +345,49 @@ WebGLContext::GetVertexAttrib(JSContext*
             return JS::BooleanValue(mBoundVertexArray->mAttribs[index].normalized);
         }
 
         default:
             break;
     }
 
     ErrorInvalidEnumInfo("getVertexAttrib: parameter", pname);
-
     return JS::NullValue();
 }
 
 WebGLsizeiptr
 WebGLContext::GetVertexAttribOffset(GLuint index, GLenum pname)
 {
     if (IsContextLost())
         return 0;
 
-    if (!ValidateAttribIndex(index, "getVertexAttribOffset"))
+    if (!CheckAttribIndex(*this, index, "getVertexAttribOffset"))
         return 0;
 
     if (pname != LOCAL_GL_VERTEX_ATTRIB_ARRAY_POINTER) {
         ErrorInvalidEnum("getVertexAttribOffset: bad parameter");
         return 0;
     }
 
+    MOZ_ASSERT(mBoundVertexArray);
+    mBoundVertexArray->EnsureAttrib(index);
     return mBoundVertexArray->mAttribs[index].byteOffset;
 }
 
 void
 WebGLContext::VertexAttribPointer(GLuint index, GLint size, GLenum type,
                                   WebGLboolean normalized, GLsizei stride,
                                   WebGLintptr byteOffset)
 {
     if (IsContextLost())
         return;
 
+    if (!CheckAttribIndex(*this, index, "vertexAttribPointer"))
+        return;
+
     if (mBoundArrayBuffer == nullptr)
         return ErrorInvalidOperation("vertexAttribPointer: must have valid GL_ARRAY_BUFFER binding");
 
     GLsizei requiredAlignment = 1;
     switch (type) {
         case LOCAL_GL_BYTE:
         case LOCAL_GL_UNSIGNED_BYTE:
             requiredAlignment = 1;
@@ -348,19 +402,18 @@ WebGLContext::VertexAttribPointer(GLuint
             break;
         default:
             return ErrorInvalidEnumInfo("vertexAttribPointer: type", type);
     }
 
     // requiredAlignment should always be a power of two.
     GLsizei requiredAlignmentMask = requiredAlignment - 1;
 
-    if (!ValidateAttribIndex(index, "vertexAttribPointer")) {
-        return;
-    }
+    MOZ_ASSERT(mBoundVertexArray);
+    mBoundVertexArray->EnsureAttrib(index);
 
     if (size < 1 || size > 4)
         return ErrorInvalidValue("vertexAttribPointer: invalid element size");
 
     if (stride < 0 || stride > 255) // see WebGL spec section 6.6 "Vertex Attribute Data Stride"
         return ErrorInvalidValue("vertexAttribPointer: negative or too large stride");
 
     if (byteOffset < 0)
@@ -401,19 +454,21 @@ WebGLContext::VertexAttribPointer(GLuint
 }
 
 void
 WebGLContext::VertexAttribDivisor(GLuint index, GLuint divisor)
 {
     if (IsContextLost())
         return;
 
-    if (!ValidateAttribIndex(index, "vertexAttribDivisor")) {
+    if (!CheckAttribIndex(*this, index, "vertexAttribDivisor"))
         return;
-    }
+
+    MOZ_ASSERT(mBoundVertexArray);
+    mBoundVertexArray->EnsureAttrib(index);
 
     WebGLVertexAttribData& vd = mBoundVertexArray->mAttribs[index];
     vd.divisor = divisor;
 
     InvalidateBufferFetching();
 
     MakeContextCurrent();
 
--- a/dom/canvas/WebGLVertexArray.cpp
+++ b/dom/canvas/WebGLVertexArray.cpp
@@ -44,34 +44,24 @@ WebGLVertexArray::Delete()
 {
     DeleteImpl();
 
     LinkedListElement<WebGLVertexArray>::removeFrom(mContext->mVertexArrays);
     mElementArrayBuffer = nullptr;
     mAttribs.Clear();
 }
 
-bool
-WebGLVertexArray::EnsureAttrib(GLuint index, const char *info)
+void
+WebGLVertexArray::EnsureAttrib(GLuint index)
 {
-    if (index >= GLuint(mContext->mGLMaxVertexAttribs)) {
-        if (index == GLuint(-1)) {
-            mContext->ErrorInvalidValue("%s: index -1 is invalid. That probably comes from a getAttribLocation() call, "
-                                        "where this return value -1 means that the passed name didn't correspond to an active attribute in "
-                                        "the specified program.", info);
-        } else {
-            mContext->ErrorInvalidValue("%s: index %d is out of range", info, index);
-        }
-        return false;
-    }
-    else if (index >= mAttribs.Length()) {
+    MOZ_ASSERT(index < GLuint(mContext->mGLMaxVertexAttribs));
+
+    if (index >= mAttribs.Length()) {
         mAttribs.SetLength(index + 1);
     }
-
-    return true;
 }
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebGLVertexArray,
   mAttribs,
   mElementArrayBuffer)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLVertexArray, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLVertexArray, Release)
--- a/dom/canvas/WebGLVertexArray.h
+++ b/dom/canvas/WebGLVertexArray.h
@@ -56,17 +56,17 @@ public:
     virtual JSObject* WrapObject(JSContext *cx) MOZ_OVERRIDE;
 
     NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLVertexArray)
     NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLVertexArray)
 
     // -------------------------------------------------------------------------
     // MEMBER FUNCTIONS
 
-    bool EnsureAttrib(GLuint index, const char *info);
+    void EnsureAttrib(GLuint index);
     bool HasAttrib(GLuint index) {
         return index < mAttribs.Length();
     }
     bool IsAttribArrayEnabled(GLuint index) {
         return HasAttrib(index) && mAttribs[index].enabled;
     }
 
 
--- a/dom/canvas/compiledtest/moz.build
+++ b/dom/canvas/compiledtest/moz.build
@@ -1,22 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-CppUnitTests([
+GeckoCppUnitTests([
     'TestWebGLElementArrayCache',
 ])
 
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '../',
 ]
-
-USE_LIBS += [
-    'mozalloc',
-    'nspr',
-    'xpcomglue_s',
-    'xul',
-]
--- a/dom/canvas/test/webgl-conformance/mochi-single.html
+++ b/dom/canvas/test/webgl-conformance/mochi-single.html
@@ -125,19 +125,17 @@ function GetExpectedTestFailSet() {
         failSet['conformance/textures/tex-image-with-format-and-type.html'] = true;
         failSet['conformance/textures/tex-sub-image-2d.html'] = true;
         failSet['conformance/textures/texture-mips.html'] = true;
         failSet['conformance/textures/texture-npot.html'] = true;
         failSet['conformance/textures/texture-size-cube-maps.html'] = true;
       } else {
         // Android 2.3 slaves.
         failSet['conformance/glsl/functions/glsl-function-sin.html'] = true;
-        failSet['conformance/misc/error-reporting.html'] = true;
         failSet['conformance/misc/object-deletion-behaviour.html'] = true;
-        failSet['conformance/programs/get-active-test.html'] = true;
         failSet['conformance/textures/tex-image-and-sub-image-2d-with-video.html'] = true;
         failSet['conformance/textures/texture-mips.html'] = true;
         failSet['conformance/textures/texture-npot.html'] = true;
       }
       break;
 
     case DriverInfo.OS.B2G:
       failSet['conformance/context/context-attributes-alpha-depth-stencil-antialias.html'] = true;
--- a/dom/events/Event.h
+++ b/dom/events/Event.h
@@ -263,17 +263,18 @@ protected:
   // True when popup control check should rely on event.type, not
   // WidgetEvent.message.
   bool                        mWantsPopupControlCheck;
 };
 
 class MOZ_STACK_CLASS WantsPopupControlCheck
 {
 public:
-  WantsPopupControlCheck(nsIDOMEvent* aEvent) : mEvent(aEvent->InternalDOMEvent())
+  explicit WantsPopupControlCheck(nsIDOMEvent* aEvent) :
+    mEvent(aEvent->InternalDOMEvent())
   {
     mOriginalWantsPopupControlCheck = mEvent->GetWantsPopupControlCheck();
     mEvent->SetWantsPopupControlCheck(mEvent->IsTrusted());
   }
 
   ~WantsPopupControlCheck()
   {
     mEvent->SetWantsPopupControlCheck(mOriginalWantsPopupControlCheck);
--- a/dom/events/MessageEvent.cpp
+++ b/dom/events/MessageEvent.cpp
@@ -6,16 +6,17 @@
 #include "mozilla/dom/MessageEvent.h"
 #include "mozilla/dom/MessageEventBinding.h"
 #include "mozilla/dom/MessagePort.h"
 #include "mozilla/dom/MessagePortBinding.h"
 #include "mozilla/dom/MessagePortList.h"
 
 #include "mozilla/HoldDropJSObjects.h"
 #include "jsapi.h"
+#include "nsGlobalWindow.h" // So we can assign an nsGlobalWindow* to mWindowSource
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MessageEvent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessageEvent, Event)
   tmp->mData = JSVAL_VOID;
@@ -136,18 +137,18 @@ MessageEvent::Constructor(const GlobalOb
     event->mOrigin = aParam.mOrigin.Value();
   }
 
   if (aParam.mLastEventId.WasPassed()) {
     event->mLastEventId = aParam.mLastEventId.Value();
   }
 
   if (!aParam.mSource.IsNull()) {
-    if (aParam.mSource.Value().IsWindowProxy()) {
-      event->mWindowSource = aParam.mSource.Value().GetAsWindowProxy();
+    if (aParam.mSource.Value().IsWindow()) {
+      event->mWindowSource = aParam.mSource.Value().GetAsWindow();
     } else {
       event->mPortSource = aParam.mSource.Value().GetAsMessagePort();
     }
 
     MOZ_ASSERT(event->mWindowSource || event->mPortSource);
   }
 
   if (aParam.mPorts.WasPassed() && !aParam.mPorts.Value().IsNull()) {
--- a/dom/fetch/Fetch.cpp
+++ b/dom/fetch/Fetch.cpp
@@ -66,17 +66,17 @@ private:
 
 class MainThreadFetchResolver MOZ_FINAL : public FetchDriverObserver
 {
   nsRefPtr<Promise> mPromise;
   nsRefPtr<InternalResponse> mInternalResponse;
 
   NS_DECL_OWNINGTHREAD
 public:
-  MainThreadFetchResolver(Promise* aPromise);
+  explicit MainThreadFetchResolver(Promise* aPromise);
 
   void
   OnResponseAvailable(InternalResponse* aResponse) MOZ_OVERRIDE;
 
 private:
   ~MainThreadFetchResolver();
 };
 
@@ -183,17 +183,17 @@ MainThreadFetchResolver::~MainThreadFetc
 {
   NS_ASSERT_OWNINGTHREAD(MainThreadFetchResolver);
 }
 
 class WorkerFetchResponseRunnable : public WorkerRunnable
 {
   nsRefPtr<WorkerFetchResolver> mResolver;
 public:
-  WorkerFetchResponseRunnable(WorkerFetchResolver* aResolver)
+  explicit WorkerFetchResponseRunnable(WorkerFetchResolver* aResolver)
     : WorkerRunnable(aResolver->GetWorkerPrivate(), WorkerThreadModifyBusyCount)
     , mResolver(aResolver)
   {
   }
 
   bool
   WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) MOZ_OVERRIDE
   {
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1782,17 +1782,21 @@ NS_IMETHODIMP HTMLMediaElement::SetMuted
 
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
 {
   nsIDOMWindow* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
     return nullptr;
   }
-
+#ifdef MOZ_EME
+  if (ContainsRestrictedContent()) {
+    return nullptr;
+  }
+#endif
   OutputMediaStream* out = mOutputStreams.AppendElement();
 #ifdef DEBUG
   // Estimate hints based on the type of the media element
   // under the preference media.capturestream_hints for the
   // debug builds only. This allows WebRTC Peer Connection
   // 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
@@ -3987,20 +3991,31 @@ NS_IMETHODIMP HTMLMediaElement::CanPlayC
 
 #ifdef MOZ_EME
 MediaKeys*
 HTMLMediaElement::GetMediaKeys() const
 {
   return mMediaKeys;
 }
 
+bool
+HTMLMediaElement::ContainsRestrictedContent()
+{
+  return GetMediaKeys() != nullptr;
+}
+
 already_AddRefed<Promise>
 HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
                                ErrorResult& aRv)
 {
+  if (MozAudioCaptured()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(OwnerDoc()->GetInnerWindow());
   if (!global) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
   if (aRv.Failed()) {
@@ -4025,16 +4040,18 @@ HTMLMediaElement::SetMediaKeys(mozilla::
     if (NS_FAILED(mMediaKeys->Bind(this))) {
       promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
       mMediaKeys = nullptr;
       return promise.forget();
     }
     if (mDecoder) {
       mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
     }
+    // Update the same-origin status.
+    NotifyDecoderPrincipalChanged();
   }
   promise->MaybeResolve(JS::UndefinedHandleValue);
   return promise.forget();
 }
 
 MediaWaitingFor
 HTMLMediaElement::WaitingFor() const
 {
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -545,16 +545,17 @@ public:
 
 
   bool IsEventAttributeName(nsIAtom* aName) MOZ_OVERRIDE;
 
   // Returns the principal of the "top level" document; the origin displayed
   // in the URL bar of the browser window.
   already_AddRefed<nsIPrincipal> GetTopLevelPrincipal();
 
+  bool ContainsRestrictedContent();
 #endif // MOZ_EME
 
   bool MozAutoplayEnabled() const
   {
     return mAutoplayEnabled;
   }
 
   already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv);
--- a/dom/ipc/Blob.cpp
+++ b/dom/ipc/Blob.cpp
@@ -724,17 +724,17 @@ private:
     // Nothing needs to be done here.
   }
 };
 
 class EmptyBlobImpl MOZ_FINAL
   : public FileImplBase
 {
 public:
-  EmptyBlobImpl(const nsAString& aContentType)
+  explicit EmptyBlobImpl(const nsAString& aContentType)
     : FileImplBase(aContentType, 0)
   {
     mImmutable = true;
   }
 
   EmptyBlobImpl(const nsAString& aName,
                 const nsAString& aContentType,
                 uint64_t aLastModifiedDate)
@@ -835,17 +835,17 @@ struct MOZ_STACK_CLASS CreateBlobImplMet
 {
   nsString mContentType;
   nsString mName;
   uint64_t mLength;
   uint64_t mLastModifiedDate;
   bool mHasRecursed;
   const bool mIsSameProcessActor;
 
-  CreateBlobImplMetadata(bool aIsSameProcessActor)
+  explicit CreateBlobImplMetadata(bool aIsSameProcessActor)
     : mLength(0)
     , mLastModifiedDate(0)
     , mHasRecursed(false)
     , mIsSameProcessActor(aIsSameProcessActor)
   {
     MOZ_COUNT_CTOR(CreateBlobImplMetadata);
 
     mName.SetIsVoid(true);
@@ -1673,17 +1673,17 @@ class BlobChild::RemoteBlobImpl::CreateS
   Monitor mMonitor;
   nsRefPtr<RemoteBlobImpl> mRemoteBlobImpl;
   nsRefPtr<RemoteInputStream> mInputStream;
   const uint64_t mStart;
   const uint64_t mLength;
   bool mDone;
 
 public:
-  CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl);
+  explicit CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl);
 
   nsresult
   GetStream(nsIInputStream** aInputStream);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIRUNNABLE
 
 private:
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -294,16 +294,18 @@ ThreadedDriver::RunThread()
     mNextStateComputedTime =
       mGraphImpl->RoundUpToNextAudioBlock(
         nextCurrentTime + mGraphImpl->MillisecondsToMediaTime(AUDIO_TARGET_MS));
     STREAM_LOG(PR_LOG_DEBUG,
                ("interval[%ld; %ld] state[%ld; %ld]",
                (long)mIterationStart, (long)mIterationEnd,
                (long)mStateComputedTime, (long)mNextStateComputedTime));
 
+    mGraphImpl->mFlushSourcesNow = mGraphImpl->mFlushSourcesOnNextIteration;
+    mGraphImpl->mFlushSourcesOnNextIteration = false;
     stillProcessing = mGraphImpl->OneIteration(prevCurrentTime,
                                                nextCurrentTime,
                                                StateComputedTime(),
                                                mNextStateComputedTime);
 
     if (mNextDriver && stillProcessing) {
       STREAM_LOG(PR_LOG_DEBUG, ("Switching to AudioCallbackDriver"));
       mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd,
@@ -791,17 +793,19 @@ bool
 AudioCallbackDriver::OSXDeviceSwitchingWorkaround()
 {
   MonitorAutoLock mon(GraphImpl()->GetMonitor());
   if (mSelfReference) {
     // Apparently, depending on the osx version, on device switch, the
     // callback is called "some" number of times, and then stops being called,
     // and then gets called again. 10 is to be safe, it's a low-enough number
     // of milliseconds anyways (< 100ms)
+    //STREAM_LOG(PR_LOG_DEBUG, ("Callbacks during switch: %d", mCallbackReceivedWhileSwitching+1));
     if (mCallbackReceivedWhileSwitching++ >= 10) {
+      STREAM_LOG(PR_LOG_DEBUG, ("Got %d callbacks, switching back to CallbackDriver", mCallbackReceivedWhileSwitching));
       // If we have a self reference, we have fallen back temporarily on a
       // system clock driver, but we just got called back, that means the osx
       // audio backend has switched to the new device.
       // Ask the graph to switch back to the previous AudioCallbackDriver
       // (`this`), and when the graph has effectively switched, we can drop
       // the self reference and unref the SystemClockDriver we fallen back on.
       if (GraphImpl()->CurrentDriver() == this) {
         mSelfReference.Drop(this);
@@ -1027,18 +1031,20 @@ AudioCallbackDriver::DeviceChangedCallba
   // SourceMediaStream.
   if (!GraphImpl()->Running()) {
     return;
   }
 
   if (mSelfReference) {
     return;
   }
+  STREAM_LOG(PR_LOG_ERROR, ("Switching to SystemClockDriver during output switch"));
   mSelfReference.Take(this);
   mCallbackReceivedWhileSwitching = 0;
+  mGraphImpl->mFlushSourcesOnNextIteration = true;
   mNextDriver = new SystemClockDriver(GraphImpl());
   mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd,
                             mStateComputedTime, mNextStateComputedTime);
   mGraphImpl->SetCurrentDriver(mNextDriver);
   mNextDriver->Start();
 #endif
 }
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -2970,18 +2970,18 @@ void MediaDecoderStateMachine::StartBuff
   // We can't just directly send an asynchronous runnable that
   // eventually fires the "waiting" event. The problem is that
   // there might be pending main-thread events, such as "data
   // received" notifications, that mean we're not actually still
   // buffering by the time this runnable executes. So instead
   // we just trigger UpdateReadyStateForData; when it runs, it
   // will check the current state and decide whether to tell
   // the element we're buffering or not.
+  SetState(DECODER_STATE_BUFFERING);
   UpdateReadyState();
-  SetState(DECODER_STATE_BUFFERING);
   DECODER_LOG("Changed state from DECODING to BUFFERING, decoded for %.3lfs",
               decodeDuration.ToSeconds());
 #ifdef PR_LOGGING
   MediaDecoder::Statistics stats = mDecoder->GetStatistics();
   DECODER_LOG("Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
               stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
               stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)");
 #endif
--- a/dom/media/MediaSegment.h
+++ b/dom/media/MediaSegment.h
@@ -84,16 +84,20 @@ public:
    */
   virtual void AppendSlice(const MediaSegment& aSource,
                            TrackTicks aStart, TrackTicks aEnd) = 0;
   /**
    * Replace all contents up to aDuration with null data.
    */
   virtual void ForgetUpTo(TrackTicks aDuration) = 0;
   /**
+   * Forget all data buffered after a given point
+   */
+  virtual void FlushAfter(TrackTicks aNewEnd) = 0;
+  /**
    * Insert aDuration of null data at the start of the segment.
    */
   virtual void InsertNullDataAtStart(TrackTicks aDuration) = 0;
   /**
    * Insert aDuration of null data at the end of the segment.
    */
   virtual void AppendNullData(TrackTicks aDuration) = 0;
   /**
@@ -171,16 +175,39 @@ public:
         mDuration += extraToForget;
       }
       return;
     }
     RemoveLeading(aDuration, 0);
     mChunks.InsertElementAt(0)->SetNull(aDuration);
     mDuration += aDuration;
   }
+  virtual void FlushAfter(TrackTicks aNewEnd)
+  {
+    if (mChunks.IsEmpty()) {
+      return;
+    }
+
+    if (mChunks[0].IsNull()) {
+      TrackTicks extraToKeep = aNewEnd - mChunks[0].GetDuration();
+      if (extraToKeep < 0) {
+        // reduce the size of the Null, get rid of everthing else
+        mChunks[0].SetNull(aNewEnd);
+        extraToKeep = 0;
+      }
+      RemoveTrailing(extraToKeep, 1);
+    } else {
+      if (aNewEnd > mDuration) {
+        NS_ASSERTION(aNewEnd <= mDuration, "can't add data in FlushAfter");
+        return;
+      }
+      RemoveTrailing(aNewEnd, 0);
+    }
+    mDuration = aNewEnd;
+  }
   virtual void InsertNullDataAtStart(TrackTicks aDuration)
   {
     if (aDuration <= 0) {
       return;
     }
     if (!mChunks.IsEmpty() && mChunks[0].IsNull()) {
       mChunks[0].mDuration += aDuration;
     } else {
@@ -345,16 +372,38 @@ protected:
       }
       t -= c->GetDuration();
       chunksToRemove = i + 1 - aStartIndex;
     }
     mChunks.RemoveElementsAt(aStartIndex, chunksToRemove);
     mDuration -= aDuration - t;
   }
 
+  void RemoveTrailing(TrackTicks aKeep, uint32_t aStartIndex)
+  {
+    NS_ASSERTION(aKeep >= 0, "Can't keep negative duration");
+    TrackTicks t = aKeep;
+    uint32_t i;
+    for (i = aStartIndex; i < mChunks.Length(); ++i) {
+      Chunk* c = &mChunks[i];
+      if (c->GetDuration() > t) {
+        c->SliceTo(0, t);
+        break;
+      }
+      t -= c->GetDuration();
+      if (t == 0) {
+        break;
+      }
+    }
+    if (i+1 < mChunks.Length()) {
+      mChunks.RemoveElementsAt(i+1, mChunks.Length() - (i+1));
+    }
+    // Caller must adjust mDuration
+  }
+
   nsTArray<Chunk> mChunks;
 #ifdef MOZILLA_INTERNAL_API
   mozilla::TimeStamp mTimeStamp;
 #endif
 };
 
 }
 
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -1434,16 +1434,17 @@ MediaStreamGraphImpl::OneIteration(Graph
       // stream is responsible for calling Destroy on them.
       return false;
     }
 
     CurrentDriver()->WaitForNextIteration();
 
     SwapMessageQueues();
   }
+  mFlushSourcesNow = false;
 
   return true;
 }
 
 void
 MediaStreamGraphImpl::ApplyStreamUpdate(StreamUpdate* aUpdate)
 {
   mMonitor.AssertCurrentThreadOwns();
@@ -2717,16 +2718,18 @@ MediaStreamGraphImpl::MediaStreamGraphIm
   , mNeedAnotherIteration(false)
   , mGraphDriverAsleep(false)
   , mMonitor("MediaStreamGraphImpl")
   , mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED)
   , mEndTime(GRAPH_TIME_MAX)
   , mSampleRate(aSampleRate)
   , mForceShutDown(false)
   , mPostedRunInStableStateEvent(false)
+  , mFlushSourcesNow(false)
+  , mFlushSourcesOnNextIteration(false)
   , mDetectedNotRunning(false)
   , mPostedRunInStableState(false)
   , mRealtime(aRealtime)
   , mNonRealtimeProcessing(false)
   , mStreamOrderDirty(false)
   , mLatencyLog(AsyncLatencyLogger::Get())
 #ifdef MOZ_WEBRTC
   , mFarendObserverRef(nullptr)
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -605,16 +605,23 @@ public:
    */
   bool mForceShutDown;
   /**
    * True when we have posted an event to the main thread to run
    * RunInStableState() and the event hasn't run yet.
    */
   bool mPostedRunInStableStateEvent;
 
+  /**
+   * Used to flush any accumulated data when the output streams
+   * may have stalled (on Mac after an output device change)
+   */
+  bool mFlushSourcesNow;
+  bool mFlushSourcesOnNextIteration;
+
   // Main thread only
 
   /**
    * Messages posted by the current event loop task. These are forwarded to
    * the media graph thread during RunInStableState. We can't forward them
    * immediately because we want all messages between stable states to be
    * processed as an atomic batch.
    */
--- a/dom/media/StreamBuffer.h
+++ b/dom/media/StreamBuffer.h
@@ -149,16 +149,22 @@ public:
     MediaSegment* RemoveSegment()
     {
       return mSegment.forget();
     }
     void ForgetUpTo(TrackTicks aTime)
     {
       mSegment->ForgetUpTo(aTime);
     }
+    void FlushAfter(TrackTicks aNewEnd)
+    {
+      // Forget everything after a given endpoint
+      // a specified amount
+      mSegment->FlushAfter(aNewEnd);
+    }
 
     size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
     {
       size_t amount = aMallocSizeOf(this);
       if (mSegment) {
         amount += mSegment->SizeOfIncludingThis(aMallocSizeOf);
       }
       return amount;
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -258,18 +258,19 @@ TrackUnionStream::TrackUnionStream(DOMMe
       StreamTime inputEnd = source->GraphTimeToStreamTime(interval.mEnd);
       TrackTicks inputTrackEndPoint = aInputTrack->GetEnd();
 
       if (aInputTrack->IsEnded() &&
           aInputTrack->GetEndTimeRoundDown() <= inputEnd) {
         *aOutputTrackFinished = true;
       }
 
-      if (interval.mStart >= interval.mEnd)
+      if (interval.mStart >= interval.mEnd) {
         break;
+      }
       next = interval.mEnd;
 
       // Ticks >= startTicks and < endTicks are in the interval
       StreamTime outputEnd = GraphTimeToStreamTime(interval.mEnd);
       TrackTicks startTicks = outputTrack->GetEnd();
       StreamTime outputStart = GraphTimeToStreamTime(interval.mStart);
       MOZ_ASSERT(startTicks == TimeToTicksRoundUp(rate, outputStart), "Samples missing");
       TrackTicks endTicks = TimeToTicksRoundUp(rate, outputEnd);
@@ -300,16 +301,24 @@ TrackUnionStream::TrackUnionStream(DOMMe
           // Start of a new series of intervals where neither stream is blocked.
           map->mEndOfConsumedInputTicks = TimeToTicksRoundDown(rate, inputStart) - 1;
         }
         TrackTicks inputStartTicks = map->mEndOfConsumedInputTicks;
         TrackTicks inputEndTicks = inputStartTicks + ticks;
         map->mEndOfConsumedInputTicks = inputEndTicks;
         map->mEndOfLastInputIntervalInInputStream = inputEnd;
         map->mEndOfLastInputIntervalInOutputStream = outputEnd;
+
+        if (GraphImpl()->mFlushSourcesNow) {
+          TrackTicks flushto = inputEndTicks;
+          STREAM_LOG(PR_LOG_DEBUG, ("TrackUnionStream %p flushing after %lld of %lld ticks of input data from track %d for track %d",
+              this, flushto, aInputTrack->GetSegment()->GetDuration(), aInputTrack->GetID(), outputTrack->GetID()));
+          aInputTrack->FlushAfter(flushto);
+          MOZ_ASSERT(inputTrackEndPoint >= aInputTrack->GetEnd());
+        }
         // Now we prove that the above properties hold:
         // Property #1: trivial by construction.
         // Property #3: trivial by construction. Between every two
         // intervals where both streams are not blocked, the above if condition
         // is false and mEndOfConsumedInputTicks advances exactly to match
         // the ticks that were consumed.
         // Property #2:
         // Let originalOutputStart be the value of outputStart and originalInputStart
--- a/dom/media/compiledtest/moz.build
+++ b/dom/media/compiledtest/moz.build
@@ -1,23 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-CppUnitTests([
+GeckoCppUnitTests([
     'TestAudioBuffers',
     'TestAudioMixer'
 ])
 
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '..',
 ]
-
-USE_LIBS += [
-    'mozalloc',
-    'nspr',
-    'xpcomglue_s',
-    'xul',
-]
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -526,16 +526,17 @@ MP4Reader::Decode(TrackType aTrack)
            (data.mInputExhausted ||
            (data.mNumSamplesInput - data.mNumSamplesOutput) < data.mDecodeAhead) &&
            !data.mEOS) {
       data.mMonitor.AssertCurrentThreadOwns();
       data.mMonitor.Unlock();
       nsAutoPtr<MP4Sample> compressed(PopSample(aTrack));
       if (!compressed) {
         // EOS, or error. Send the decoder a signal to drain.
+        LOG("MP4Reader: EOS or error - no samples available");
         LOG("Draining %s", TrackTypeToStr(aTrack));
         data.mMonitor.Lock();
         MOZ_ASSERT(!data.mEOS);
         data.mEOS = true;
         MOZ_ASSERT(!data.mDrainComplete);
         data.mDrainComplete = false;
         data.mMonitor.Unlock();
         data.mDecoder->Drain();
--- a/dom/media/fmp4/apple/AppleATDecoder.cpp
+++ b/dom/media/fmp4/apple/AppleATDecoder.cpp
@@ -267,17 +267,17 @@ AppleATDecoder::SampleCallback(uint32_t 
     OSStatus rv = AudioConverterFillComplexBuffer(mConverter,
                                                   _PassthroughInputDataCallback,
                                                   &userData,
                                                   &numFrames /* in/out */,
                                                   &decBuffer,
                                                   packets.get());
 
     if (rv && rv != kNeedMoreData) {
-      LOG("Error decoding audio stream: %#x\n", rv);
+      LOG("Error decoding audio stream: %d\n", rv);
       mCallback->Error();
       break;
     }
     LOG("%d frames decoded", numFrames);
 
     // If we decoded zero frames then AudioConverterFillComplexBuffer is out
     // of data to provide.  We drained its internal buffer completely on the
     // last pass.
--- a/dom/media/gmp-plugin/gmp-test-decryptor.cpp
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.cpp
@@ -104,17 +104,17 @@ public:
     WriteRecord(TruncateRecordId, nullptr, 0,
                 new ReadThenTask(TruncateRecordId, new TestEmptyContinuation()));
     delete this;
   }
 };
 
 class VerifyAndFinishContinuation : public ReadContinuation {
 public:
-  VerifyAndFinishContinuation(string aValue)
+  explicit VerifyAndFinishContinuation(string aValue)
     : mValue(aValue)
   {}
   void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE {
     if (aData != mValue) {
       FakeDecryptor::Message("FAIL VerifyAndFinishContinuation read data doesn't match expected data");
     }
     sFinishedReplaceTest = true;
     MaybeFinish();
@@ -141,17 +141,17 @@ public:
   string mValue;
   string mOverwrite;
 };
 
 static const string OpenAgainRecordId = "open-again-record-id";
 
 class OpenedSecondTimeContinuation : public OpenContinuation {
 public:
-  OpenedSecondTimeContinuation(GMPRecord* aRecord)
+  explicit OpenedSecondTimeContinuation(GMPRecord* aRecord)
     : mRecord(aRecord)
   {
   }
 
   virtual void OpenComplete(GMPErr aStatus, GMPRecord* aRecord) MOZ_OVERRIDE {
     if (GMP_SUCCEEDED(aStatus)) {
       FakeDecryptor::Message("FAIL OpenSecondTimeContinuation should not be able to re-open record.");
     }
@@ -243,17 +243,17 @@ public:
     delete this;
   }
   const string mRecordId;
   const string mValue;
 };
 
 class ReportReadStatusContinuation : public ReadContinuation {
 public:
-  ReportReadStatusContinuation(const string& aRecordId)
+  explicit ReportReadStatusContinuation(const string& aRecordId)
     : mRecordId(aRecordId)
   {}
   void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE {
     if (GMP_FAILED(aErr)) {
       FakeDecryptor::Message("retrieve " + mRecordId + " failed");
     } else {
       stringstream ss;
       ss << aData.size();
@@ -264,17 +264,17 @@ public:
     }
     delete this;
   }
   string mRecordId;
 };
 
 class ReportReadRecordContinuation : public ReadContinuation {
 public:
-  ReportReadRecordContinuation(const string& aRecordId)
+  explicit ReportReadRecordContinuation(const string& aRecordId)
     : mRecordId(aRecordId)
   {}
   void ReadComplete(GMPErr aErr, const std::string& aData) MOZ_OVERRIDE {
     if (GMP_FAILED(aErr)) {
       FakeDecryptor::Message("retrieved " + mRecordId + " failed");
     } else {
       FakeDecryptor::Message("retrieved " + mRecordId + " " + aData);
     }
@@ -325,17 +325,17 @@ FakeDecryptor::UpdateSession(uint32_t aP
     }
   } else if (task == "retrieve-shutdown-token") {
     ReadRecord("shutdown-token", new ReportReadRecordContinuation("shutdown-token"));
   }
 }
 
 class CompleteShutdownTask : public GMPTask {
 public:
-  CompleteShutdownTask(GMPAsyncShutdownHost* aHost)
+  explicit CompleteShutdownTask(GMPAsyncShutdownHost* aHost)
     : mHost(aHost)
   {
   }
   virtual void Run() {
     mHost->ShutdownComplete();
   }
   virtual void Destroy() { delete this; }
   GMPAsyncShutdownHost* mHost;
--- a/dom/media/gmp-plugin/gmp-test-decryptor.h
+++ b/dom/media/gmp-plugin/gmp-test-decryptor.h
@@ -75,17 +75,17 @@ private:
 
   void TestStorage();
 
   GMPDecryptorCallback* mCallback;
 };
 
 class TestAsyncShutdown : public GMPAsyncShutdown {
 public:
-  TestAsyncShutdown(GMPAsyncShutdownHost* aHost)
+  explicit TestAsyncShutdown(GMPAsyncShutdownHost* aHost)
     : mHost(aHost)
   {
   }
   virtual void BeginShutdown() MOZ_OVERRIDE;
 private:
   GMPAsyncShutdownHost* mHost;
 };
 
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -356,17 +356,17 @@ GMPParent::Shutdown()
     nsRefPtr<GMPParent> self(this);
     mService->ReAddOnGMPThread(self);
   } // else we've been asked to die and stay dead
   MOZ_ASSERT(mState == GMPStateNotLoaded);
 }
 
 class NotifyGMPShutdownTask : public nsRunnable {
 public:
-  NotifyGMPShutdownTask(const nsAString& aNodeId)
+  explicit NotifyGMPShutdownTask(const nsAString& aNodeId)
     : mNodeId(aNodeId)
   {
   }
   NS_IMETHOD Run() {
     MOZ_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIObserverService> obsService = mozilla::services::GetObserverService();
     MOZ_ASSERT(obsService);
     if (obsService) {
--- a/dom/media/gmp/GMPStorageParent.cpp
+++ b/dom/media/gmp/GMPStorageParent.cpp
@@ -122,17 +122,17 @@ CloseFile(const nsACString& key, PRFileD
   if (PR_Close(entry) != PR_SUCCESS) {
     NS_WARNING("GMPDiskStorage Failed to clsose file.");
   }
   return PL_DHASH_REMOVE;
 }
 
 class GMPDiskStorage : public GMPStorage {
 public:
-  GMPDiskStorage(const nsCString& aNodeId)
+  explicit GMPDiskStorage(const nsCString& aNodeId)
     : mNodeId(aNodeId)
   {
   }
   ~GMPDiskStorage() {
     mFiles.Enumerate(CloseFile, nullptr);
     MOZ_ASSERT(!mFiles.Count());
   }
 
--- a/dom/media/gtest/MockMediaResource.h
+++ b/dom/media/gtest/MockMediaResource.h
@@ -10,17 +10,17 @@
 #include "mozilla/Atomics.h"
 
 namespace mozilla
 {
 
 class MockMediaResource : public MediaResource
 {
 public:
-  MockMediaResource(const char* aFileName);
+  explicit MockMediaResource(const char* aFileName);
   virtual nsIURI* URI() const MOZ_OVERRIDE { return nullptr; }
   virtual nsresult Close() MOZ_OVERRIDE { return NS_OK; }
   virtual void Suspend(bool aCloseImmediately) MOZ_OVERRIDE {}
   virtual void Resume() MOZ_OVERRIDE {}
   virtual already_AddRefed<nsIPrincipal> GetCurrentPrincipal() MOZ_OVERRIDE
   {
     return nullptr;
   }
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -163,17 +163,17 @@ private:
   nsRefPtr<nsIRunnable> mContinuation;
   const nsString mNodeId;
 };
 
 NS_IMPL_ISUPPORTS(GMPShutdownObserver, nsIRunnable, nsIObserver)
 
 class NotifyObserversTask : public nsRunnable {
 public:
-  NotifyObserversTask(const char* aTopic)
+  explicit NotifyObserversTask(const char* aTopic)
     : mTopic(aTopic)
   {}
   NS_IMETHOD Run() {
     MOZ_ASSERT(NS_IsMainThread());
     nsCOMPtr<nsIObserverService> observerService =
         mozilla::services::GetObserverService();
     if (observerService) {
       observerService->NotifyObservers(nullptr, mTopic, nullptr);
--- a/dom/media/gtest/TestMP4Reader.cpp
+++ b/dom/media/gtest/TestMP4Reader.cpp
@@ -19,17 +19,17 @@ class TestBinding
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TestBinding);
 
   nsRefPtr<MP4Decoder> decoder;
   nsRefPtr<MockMediaResource> resource;
   nsRefPtr<MP4Reader> reader;
 
-  TestBinding(const char* aFileName = "gizmo.mp4")
+  explicit TestBinding(const char* aFileName = "gizmo.mp4")
     : decoder(new MP4Decoder())
     , resource(new MockMediaResource(aFileName))
     , reader(new MP4Reader(decoder))
   {
     EXPECT_EQ(NS_OK, Preferences::SetBool(
                        "media.fragmented-mp4.use-blank-decoder", true));
 
     EXPECT_EQ(NS_OK, resource->Open(nullptr));
--- a/dom/media/mediasource/MediaSourceResource.h
+++ b/dom/media/mediasource/MediaSourceResource.h
@@ -21,17 +21,17 @@ extern PRLogModuleInfo* GetMediaSourceAP
 
 #define UNIMPLEMENTED() MSE_DEBUG("MediaSourceResource(%p): UNIMPLEMENTED FUNCTION at %s:%d", this, __FILE__, __LINE__)
 
 namespace mozilla {
 
 class MediaSourceResource MOZ_FINAL : public MediaResource
 {
 public:
-  MediaSourceResource(nsIPrincipal* aPrincipal = nullptr)
+  explicit MediaSourceResource(nsIPrincipal* aPrincipal = nullptr)
     : mPrincipal(aPrincipal) {}
 
   virtual nsresult Close() MOZ_OVERRIDE { return NS_OK; }
   virtual void Suspend(bool aCloseImmediately) MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual void Resume() MOZ_OVERRIDE { UNIMPLEMENTED(); }
   virtual bool CanClone() MOZ_OVERRIDE { UNIMPLEMENTED(); return false; }
   virtual already_AddRefed<MediaResource> CloneData(MediaDecoder* aDecoder) MOZ_OVERRIDE { UNIMPLEMENTED(); return nullptr; }
   virtual void SetReadMode(MediaCacheStream::ReadMode aMode) MOZ_OVERRIDE { UNIMPLEMENTED(); }
--- a/dom/media/mediasource/SourceBufferDecoder.h
+++ b/dom/media/mediasource/SourceBufferDecoder.h
@@ -79,16 +79,28 @@ public:
   }
 
   void SetTaskQueue(MediaTaskQueue* aTaskQueue)
   {
     MOZ_ASSERT((!mTaskQueue && aTaskQueue) || (mTaskQueue && !aTaskQueue));
     mTaskQueue = aTaskQueue;
   }
 
+  void BreakCycles()
+  {
+    if (mReader) {
+      mReader->BreakCycles();
+      mReader = nullptr;
+    }
+    mTaskQueue = nullptr;
+#ifdef MOZ_EME
+    mCDMProxy = nullptr;
+#endif
+  }
+
 #ifdef MOZ_EME
   virtual nsresult SetCDMProxy(CDMProxy* aProxy)
   {
     MOZ_ASSERT(NS_IsMainThread());
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     mCDMProxy = aProxy;
     return NS_OK;
   }
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -386,17 +386,17 @@ TrackBuffer::ContainsTime(int64_t aTime)
 }
 
 void
 TrackBuffer::BreakCycles()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
-    mDecoders[i]->GetReader()->BreakCycles();
+    mDecoders[i]->BreakCycles();
   }
   mDecoders.Clear();
 
   // These are cleared in Shutdown()
   MOZ_ASSERT(mInitializedDecoders.IsEmpty());
   MOZ_ASSERT(!mParentDecoder);
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/media/test/eme.js
@@ -0,0 +1,190 @@
+const KEYSYSTEM_TYPE = "org.w3.clearkey";
+
+function bail(message)
+{
+  return function(err) {
+    ok(false, message);
+    if (err) {
+      info(err);
+    }
+    SimpleTest.finish();
+  }
+}
+
+function ArrayBufferToString(arr)
+{
+  var str = '';
+  var view = new Uint8Array(arr);
+  for (var i = 0; i < view.length; i++) {
+    str += String.fromCharCode(view[i]);
+  }
+  return str;
+}
+
+function StringToArrayBuffer(str)
+{
+  var arr = new ArrayBuffer(str.length);
+  var view = new Uint8Array(arr);
+  for (var i = 0; i < str.length; i++) {
+    view[i] = str.charCodeAt(i);
+  }
+  return arr;
+}
+
+function Base64ToHex(str)
+{
+  var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
+  var res = "";
+  for (var i = 0; i < bin.length; i++) {
+    res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+  }
+  return res;
+}
+
+function HexToBase64(hex)
+{
+  var bin = "";
+  for (var i = 0; i < hex.length; i += 2) {
+    bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+  }
+  return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
+}
+
+function UpdateSessionFunc(test, token) {
+  return function(ev) {
+    var msgStr = ArrayBufferToString(ev.message);
+    var msg = JSON.parse(msgStr);
+
+    info(token + " got message from CDM: " + msgStr);
+    is(msg.type, test.sessionType, token + " key session type should match");
+    ok(msg.kids, token + " message event should contain key ID array");
+
+    var outKeys = [];
+
+    for (var i = 0; i < msg.kids.length; i++) {
+      var id64 = msg.kids[i];
+      var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
+      var key = test.keys[idHex];
+
+      if (key) {
+        info(token + " found key " + key + " for key id " + idHex);
+        outKeys.push({
+          "kty":"oct",
+          "alg":"A128KW",
+          "kid":id64,
+          "k":HexToBase64(key)
+        });
+      } else {
+        bail(token + " Couldn't find key for key id " + idHex);
+      }
+    }
+
+    var update = JSON.stringify({
+      "keys" : outKeys,
+      "type" : msg.type
+    });
+    info(token + " sending update message to CDM: " + update);
+
+    ev.target.update(StringToArrayBuffer(update)).then(function() {
+      info(token + " MediaKeySession update ok!");
+    }, bail(token + " MediaKeySession update failed"));
+  }
+}
+
+function PlayFragmented(test, elem)
+{
+  return new Promise(function(resolve, reject) {
+    var ms = new MediaSource();
+    elem.src = URL.createObjectURL(ms);
+
+    var sb;
+    var curFragment = 0;
+
+    function addNextFragment() {
+      if (curFragment >= test.fragments.length) {
+        ms.endOfStream();
+        resolve();
+        return;
+      }
+
+      var fragmentFile = test.fragments[curFragment++];
+
+      var req = new XMLHttpRequest();
+      req.open("GET", fragmentFile);
+      req.responseType = "arraybuffer";
+
+      req.addEventListener("load", function() {
+        info("fetch of " + fragmentFile + " complete");
+        sb.appendBuffer(new Uint8Array(req.response));
+      });
+
+      req.addEventListener("error", bail("Error fetching " + fragmentFile));
+      req.addEventListener("abort", bail("Aborted fetching " + fragmentFile));
+
+      info("fetching resource " + fragmentFile);
+      req.send(null);
+    }
+
+    ms.addEventListener("sourceopen", function () {
+      sb = ms.addSourceBuffer(test.type);
+      sb.addEventListener("updateend", addNextFragment);
+
+      addNextFragment();
+    })
+  });
+}
+
+// Returns a promise that is resovled when the media element is ready to have
+// its play() function called; when it's loaded MSE fragments, or once the load
+// has started for non-MSE video.
+function LoadTest(test, elem)
+{
+  if (test.fragments) {
+    return PlayFragmented(test, elem);
+  }
+
+  // This file isn't fragmented; set the media source normally.
+  return new Promise(function(resolve, reject) {
+    elem.src = test.name;
+    resolve();
+  });
+}
+
+function SetupEME(test, token, params)
+{
+  var v = document.createElement("video");
+
+  // Log events dispatched to make debugging easier...
+  ["loadstart", "loadedmetadata", "loadeddata", "ended",
+    "play", "canplay", "playing", "canplaythrough"].forEach(function (e) {
+    v.addEventListener(e, function(event) {
+      info(token + " " + e);
+    }, false);
+  });
+
+  var onSetKeysFail = (params && params.onSetKeysFail)
+    ? params.onSetKeysFail
+    : bail(token + " Failed to set MediaKeys on <video> element");
+
+  v.addEventListener("encrypted", function(ev) {
+    info(token + " got encrypted event");
+    MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
+      info(token + " created MediaKeys object ok");
+      mediaKeys.sessions = [];
+      return v.setMediaKeys(mediaKeys);
+    }, bail("failed to create MediaKeys object")).then(function() {
+      info(token + " set MediaKeys on <video> element ok");
+
+      var session = v.mediaKeys.createSession(test.sessionType);
+      if (params && params.onsessioncreated) {
+        params.onsessioncreated(session);
+      }
+      session.addEventListener("message", UpdateSessionFunc(test, token));
+      session.generateRequest(ev.initDataType, ev.initData).then(function() {
+      }, bail(token + " Failed to initialise MediaKeySession"));
+
+    }, onSetKeysFail);
+  });
+
+  return v;
+}
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -135,16 +135,17 @@ support-files =
   detodos.opus
   detodos.opus^headers^
   detodos.webm
   detodos.webm^headers^
   dirac.ogg
   dirac.ogg^headers^
   dynamic_redirect.sjs
   dynamic_resource.sjs
+  eme.js
   gizmo-frag-cenc1.m4s
   gizmo-frag-cenc2.m4s
   gizmo-frag-cencinit.mp4
   file_access_controls.html
   fragment_noplay.js
   fragment_play.js
   gizmo.mp4
   gizmo.mp4^headers^
@@ -355,17 +356,21 @@ skip-if = (toolkit == 'android' && proce
 [test_contentDuration7.html]
 [test_controls.html]
 [test_currentTime.html]
 [test_decode_error.html]
 [test_decoder_disable.html]
 [test_defaultMuted.html]
 [test_delay_load.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
-[test_encryptedMediaExtensions.html]
+[test_eme_canvas_blocked.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+[test_eme_playback.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+[test_eme_stream_capture_blocked.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_error_in_video_document.html]
 skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_fastSeek-forwards.html]
 [test_imagecapture.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_eme_canvas_blocked.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Encrypted Media Extensions</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var manager = new MediaTestManager;
+
+function startTest(test, token)
+{
+  manager.started(token);
+
+  var sessions = [];
+
+  var v = SetupEME(test, token);
+  v.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+
+  v.addEventListener("loadeddata", function(ev) {
+    var video = ev.target;
+    var canvas = document.createElement("canvas");
+    canvas.width = video.videoWidth;
+    canvas.height = video.videoHeight;
+    document.body.appendChild(canvas);
+    var ctx = canvas.getContext("2d");
+    var threwError = false;
+    try {
+      ctx.drawImage(video, 0, 0);
+    } catch (ex) {
+      threwError = true;
+    }
+    ok(threwError, token + " - Should throw an error when trying to draw EME video to canvas.");
+    manager.finished(token);
+  });
+
+  v.addEventListener("error", bail(token + " got error event"));
+
+  LoadTest(test, v);
+}
+
+function beginTest() {
+  manager.runTests(gEMETests, startTest);
+}
+
+var prefs = [
+  [ "media.mediasource.enabled", true ],
+  [ "media.mediasource.ignore_codecs", true ],
+];
+
+if (/Linux/.test(navigator.userAgent) ||
+    !document.createElement('video').canPlayType("video/mp4")) {
+  // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
+  prefs.push([ "media.fragmented-mp4.exposed", true ]);
+  prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+</script>
+</pre>
+</body>
+</html>
rename from dom/media/test/test_encryptedMediaExtensions.html
rename to dom/media/test/test_eme_playback.html
--- a/dom/media/test/test_encryptedMediaExtensions.html
+++ b/dom/media/test/test_eme_playback.html
@@ -1,246 +1,96 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test Encrypted Media Extensions</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 var manager = new MediaTestManager;
 
-const KEYSYSTEM_TYPE = "org.w3.clearkey";
 
-function bail(message)
-{
-  return function(err) {
-    ok(false, message);
-    if (err) {
-      info(err);
-    }
-    SimpleTest.finish();
-  }
-}
-
-function ArrayBufferToString(arr)
-{
-  var str = '';
-  var view = new Uint8Array(arr);
-  for (var i = 0; i < view.length; i++) {
-    str += String.fromCharCode(view[i]);
-  }
-  return str;
-}
-
-function StringToArrayBuffer(str)
-{
-  var arr = new ArrayBuffer(str.length);
-  var view = new Uint8Array(arr);
-  for (var i = 0; i < str.length; i++) {
-    view[i] = str.charCodeAt(i);
-  }
-  return arr;
-}
-
-function Base64ToHex(str)
-{
-  var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
-  var res = "";
-  for (var i = 0; i < bin.length; i++) {
-    res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
-  }
-  return res;
-}
-
-function HexToBase64(hex)
-{
-  var bin = "";
-  for (var i = 0; i < hex.length; i += 2) {
-    bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
-  }
-  return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
-}
-
-function UpdateSessionFunc(test) {
-  return function(ev) {
-    var msgStr = ArrayBufferToString(ev.message);
-    var msg = JSON.parse(msgStr);
-
-    info("got message from CDM: " + msgStr);
-    is(msg.type, test.sessionType, "Key session type should match");
-    ok(msg.kids, "message event should contain key ID array");
-
-    var outKeys = [];
-
-    for (var i = 0; i < msg.kids.length; i++) {
-      var id64 = msg.kids[i];
-      var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
-      var key = test.keys[idHex];
-
-      if (key) {
-        info("found key " + key + " for key id " + idHex);
-        outKeys.push({
-          "kty":"oct",
-          "alg":"A128KW",
-          "kid":id64,
-          "k":HexToBase64(key)
-        });
-      } else {
-        bail("Couldn't find key for key id " + idHex);
-      }
-    }
-
-    var update = JSON.stringify({
-      "keys" : outKeys,
-      "type" : msg.type
-    });
-    info("sending update message to CDM: " + update);
-
-    ev.target.update(StringToArrayBuffer(update)).then(function() {
-      info("MediaKeySession update ok!");
-    }, bail("MediaKeySession update failed"));
-  }
-}
-
-function PlayFragmented(test, elem)
-{
-  var ms = new MediaSource();
-  elem.src = URL.createObjectURL(ms);
-
-  var sb;
-  var curFragment = 0;
-
-  function addNextFragment() {
-    if (curFragment >= test.fragments.length) {
-      ms.endOfStream();
-      elem.play();
-      return;
-    }
-
-    var fragmentFile = test.fragments[curFragment++];
-
-    var req = new XMLHttpRequest();
-    req.open("GET", fragmentFile);
-    req.responseType = "arraybuffer";
-
-    req.addEventListener("load", function() {
-      sb.appendBuffer(new Uint8Array(req.response));
-    });
-
-    info("fetching resource " + fragmentFile);
-    req.send(null);
-  }
-
-  ms.addEventListener("sourceopen", function () {
-    sb = ms.addSourceBuffer(test.type);
-    sb.addEventListener("updateend", addNextFragment);
-
-    addNextFragment();
-  });
-}
-
-function PlayTest(test, elem)
-{
-  if (test.fragments) {
-    PlayFragmented(test, elem);
-    return;
-  }
-
-  // This file isn't fragmented; set the media source normally.
-  elem.src = test.name;
-  elem.play();
-}
-
-function KeysChangeFunc(session, keys) {
+function KeysChangeFunc(session, keys, token) {
   session.keyIdsReceived = [];
   for (var keyid in keys) {
     info("Set " + keyid + " to false in session.keyIdsReceived");
     session.keyIdsReceived[keyid] = false;
   }
   return function(ev) {
     var session = ev.target;
     session.gotKeysChanged = true;
     session.getUsableKeyIds().then(function(keyIds) {
       for (var k = 0; k < keyIds.length; k++) {
         var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k])));
-        ok(kid in session.keyIdsReceived, "session.keyIdsReceived contained " + kid + " as expected.");
+        ok(kid in session.keyIdsReceived, token + " session.keyIdsReceived contained " + kid + " as expected.");
         session.keyIdsReceived[kid] = true;
       }
     }, bail("Failed to get keyIds"));
   }
 }
 
 function startTest(test, token)
 {
-  manager.started(test._token);
+  manager.started(token);
+
+  var sessions = [];
 
-  var v = document.createElement("video");
+  var v = SetupEME(test, token,
+    {
+      onsessioncreated: function(session) {
+        sessions.push(session);
+        session.addEventListener("keyschange", KeysChangeFunc(session, test.keys, token), false);
+      }
+    }
+  );
+
   var gotEncrypted = false;
   var gotPlaying = false;
 
   v.addEventListener("encrypted", function(ev) {
-    gotEncrypted = true;
-
-    info(token + " got encrypted event");
     ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
        token + " MediaKeys should support this keysystem");
-
-    MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
-      info(token + " created MediaKeys object ok");
-      mediaKeys.sessions = [];
-      return v.setMediaKeys(mediaKeys);
-    }, bail("failed to create MediaKeys object")).then(function() {
-      info(token + " set MediaKeys on <video> element ok");
-
-      ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
-         "MediaKeys should still support keysystem after CDM created...");
-
-      var session = v.mediaKeys.createSession(test.sessionType);
-      v.mediaKeys.sessions.push(session);
-      session.addEventListener("keyschange", KeysChangeFunc(session, test.keys), false);
-      session.addEventListener("message", UpdateSessionFunc(test));
-      session.generateRequest(ev.initDataType, ev.initData).then(function() {
-      }, bail(token + " Failed to initialise MediaKeySession"));
-
-    }, bail(token + " Failed to set MediaKeys on <video> element"));
+    gotEncrypted = true;
   });
 
   v.addEventListener("playing", function () { gotPlaying = true; });
 
   v.addEventListener("ended", function(ev) {
     ok(true, token + " got ended event");
-    manager.finished(test._token);
 
     ok(gotEncrypted, token + " encrypted event should have fired");
     ok(gotPlaying, token + " playing event should have fired");
 
     ok(Math.abs(test.duration - v.duration) < 0.1,
        token + " Duration of video should be corrrect");
     ok(Math.abs(test.duration - v.currentTime) < 0.1,
        token + " Current time should be same as duration");
+
     // Verify all sessions had all keys went sent the to the CDM usable, and thus
     // that we received keyschange event(s).
-    var sessions = v.mediaKeys.sessions;
-    is(sessions.length, 1, "should have 1 session");
+    is(sessions.length, 1, token + " should have 1 session");
     for (var i = 0; i < sessions.length; i++) {
       var session = sessions[i];
-      ok(session.gotKeysChanged, "Should have received at least one keychange event");
+      ok(session.gotKeysChanged, token + " should have received at least one keychange event");
       for (var kid in session.keyIdsReceived) {
-        ok(session.keyIdsReceived[kid], "key with id " + kid + " was usable as expected");
+        ok(session.keyIdsReceived[kid], token + " key with id " + kid + " was usable as expected");
       }
     }
-  });
+
+    manager.finished(token);
+   });
 
   v.addEventListener("error", bail(token + " got error event"));
 
-  PlayTest(test, v);
+  LoadTest(test, v).then(function(){v.play();}, bail(token + " failed to load"));
 }
 
 function testIsTypeSupported()
 {
   var t = MediaKeys.isTypeSupported;
   const clearkey = "org.w3.clearkey";
   ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
   ok(t(clearkey), "ClearKey supported.");
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_eme_stream_capture_blocked.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Encrypted Media Extensions</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var manager = new MediaTestManager;
+
+function startTest(test, token)
+{
+  // Three cases:
+  // 1. setting MediaKeys on an element captured by MediaElementSource should fail, and
+  // 2. creating a MediaElementSource on a media element with a MediaKeys should fail, and
+  // 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
+
+  // Case 1. setting MediaKeys on an element captured by MediaElementSource should fail.
+  var case1token = token + "_case1";
+  var setKeysFailed = function() {
+    ok(true, case1token + " setMediaKeys failed as expected.");
+    manager.finished(case1token);
+  };
+  var v1 = SetupEME(test, case1token,  { onSetKeysFail: setKeysFailed });
+  var context = new AudioContext();
+  var node = context.createMediaElementSource(v1);
+  v1.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+  v1.addEventListener("error", bail(case1token + " got error event"));
+  v1.addEventListener("canplay", function(ev) {
+    ok(false, case1token + " should never reach canplay, as setMediaKeys should fail");
+  });
+  manager.started(case1token);
+  LoadTest(test, v1);
+
+
+  // Case 2. creating a MediaElementSource on a media element with a MediaKeys should fail.
+  var case2token = token + "_case2";
+  var v2 = SetupEME(test, case2token);
+  v2.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+  v2.addEventListener("error", bail(case2token + " got error event"));
+  v2.addEventListener("canplay", function(ev) {
+    ok(true, case2token + " should reach canplay");
+    var threw = false;
+    try {
+      var context = new AudioContext();
+      var node = context.createMediaElementSource(v2);
+    } catch (e) {
+      threw = true;
+    }
+    ok(threw, "Should throw an error creating a MediaElementSource on an EME video.");
+    manager.finished(case2token);
+  });
+  manager.started(case2token);
+  LoadTest(test, v2);
+
+
+  // Case 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
+  var case3token = token + "_case3";
+  var v3 = SetupEME(test, case3token);
+  v3.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+  v3.addEventListener("error", bail(case3token + " got error event"));
+  v3.addEventListener("canplay", function(ev) {
+    ok(true, case3token + " should reach canplay");
+    var threw = false;
+    try {
+      var stream = v3.mozCaptureStreamUntilEnded();
+    } catch (e) {
+      threw = true;
+    }
+    ok(threw, "Should throw an error calling mozCaptureStreamUntilEnded an EME video.");
+    manager.finished(case3token);
+  });
+  manager.started(case3token);
+  LoadTest(test, v3);
+}
+
+function beginTest() {
+  manager.runTests(gEMETests, startTest);
+}
+
+var prefs = [
+  [ "media.mediasource.enabled", true ],
+  [ "media.mediasource.ignore_codecs", true ],
+];
+
+if (/Linux/.test(navigator.userAgent) ||
+    !document.createElement('video').canPlayType("video/mp4")) {
+  // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
+  prefs.push([ "media.fragmented-mp4.exposed", true ]);
+  prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -280,16 +280,22 @@ AudioContext::CreateAnalyser()
 already_AddRefed<MediaElementAudioSourceNode>
 AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
                                        ErrorResult& aRv)
 {
   if (mIsOffline) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
+#ifdef MOZ_EME
+  if (aMediaElement.ContainsRestrictedContent()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+#endif
   nsRefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   nsRefPtr<MediaElementAudioSourceNode> mediaElementAudioSourceNode =
     new MediaElementAudioSourceNode(this, stream);
   return mediaElementAudioSourceNode.forget();
 }
--- a/dom/media/webaudio/compiledtest/moz.build
+++ b/dom/media/webaudio/compiledtest/moz.build
@@ -1,22 +1,15 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-CppUnitTests([
+GeckoCppUnitTests([
     'TestAudioEventTimeline',
 ])
 
 FAIL_ON_WARNINGS = True
 
 LOCAL_INCLUDES += [
     '..',
 ]
-
-USE_LIBS += [
-    'mozalloc',
-    'nspr',
-    'xpcomglue_s',
-    'xul',
-]
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h
@@ -14,18 +14,18 @@
 #undef FF
 #include "webrtc/video_engine/include/vie_capture.h"
 
 namespace mozilla {
 
 class MediaEngineCameraVideoSource : public MediaEngineVideoSource
 {
 public:
-  MediaEngineCameraVideoSource(int aIndex,
-                               const char* aMonitorName = "Camera.Monitor")
+  explicit MediaEngineCameraVideoSource(int aIndex,
+                                        const char* aMonitorName = "Camera.Monitor")
     : MediaEngineVideoSource(kReleased)
     , mMonitor(aMonitorName)
     , mWidth(0)
     , mHeight(0)
     , mInitDone(false)
     , mHasDirectListeners(false)
     , mCaptureIndex(aIndex)
     , mTrackID(0)
--- a/dom/network/Connection.h
+++ b/dom/network/Connection.h
@@ -27,17 +27,17 @@ class Connection MOZ_FINAL : public DOME
                            , public nsINetworkProperties
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSINETWORKPROPERTIES
 
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(DOMEventTargetHelper)
 
-  Connection(nsPIDOMWindow *aWindow);
+  explicit Connection(nsPIDOMWindow *aWindow);
 
   void Shutdown();
 
   // For IObserver
   void Notify(const hal::NetworkInformation& aNetworkInfo);
 
   // WebIDL
 
--- a/dom/network/TCPServerSocket.js
+++ b/dom/network/TCPServerSocket.js
@@ -46,17 +46,17 @@ function TCPServerSocket() {
 }
 
 // When this API moves to WebIDL and these __exposedProps__ go away, remove
 // this call here and remove the API from XPConnect.
 Cu.skipCOWCallableChecks();
 
 TCPServerSocket.prototype = {
   __exposedProps__: {
-    port: 'r',
+    localPort: 'r',
     onconnect: 'rw',
     onerror: 'rw'
   },
   get localPort() {
     return this._localPort;
   },
   get onconnect() {
     return this._onconnect;
@@ -72,17 +72,17 @@ TCPServerSocket.prototype = {
   },
 
   _callListenerAcceptCommon: function tss_callListenerAcceptCommon(socket) {
     if (this._onconnect) {
       try {
         this["onconnect"].call(null, socket);
       } catch (e) {
         socket.close();
-      }      
+      }
     }
     else {
       socket.close();
       dump("Received unexpected connection!");
     }
   },
   init: function tss_init(aWindowObj) {
     this.useWin = aWindowObj;
@@ -123,17 +123,17 @@ TCPServerSocket.prototype = {
   },
 
   callListenerError: function tss_callListenerError(message, filename, lineNumber, columnNumber) {
     if (this._onerror) {
       var type = "error";
       var error = new Error(message, filename, lineNumber, columnNumber);
 
       this["onerror"].call(null, new TCPSocketEvent(type, this, error));
-    }    
+    }
   },
   /* end nsITCPServerSocketInternal method */
 
   close: function tss_close() {
     if (this._inChild) {
       this._serverBridge.close();
       return;
     }
@@ -143,17 +143,18 @@ TCPServerSocket.prototype = {
       this._neckoTCPServerSocket.close();
     }
   },
 
   // nsIServerSocketListener (Triggered by _neckoTCPServerSocket.asyncListen)
   onSocketAccepted: function tss_onSocketAccepted(server, trans) {
     // precondition: this._inChild == false
     try {
-      let that = TCPSocketInternal.createAcceptedParent(trans, this._binaryType);
+      let that = TCPSocketInternal.createAcceptedParent(trans, this._binaryType,
+                                                        this.useWin);
       this._callListenerAcceptCommon(that);
     }
     catch(e) {
       trans.close(Cr.NS_BINDING_ABORTED);
     }
   },
 
   // nsIServerSocketListener (Triggered by _neckoTCPServerSocket.asyncListen)
--- a/dom/network/TCPServerSocketParent.cpp
+++ b/dom/network/TCPServerSocketParent.cpp
@@ -2,16 +2,18 @@
  * 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 "TCPServerSocketParent.h"
 #include "nsJSUtils.h"
 #include "TCPSocketParent.h"
 #include "mozilla/unused.h"
 #include "mozilla/AppProcessChecker.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/TabParent.h"
 
 namespace mozilla {
 namespace dom {
 
 static void
 FireInteralError(mozilla::net::PTCPServerSocketParent* aActor,
                  uint32_t aLineNo)
 {
@@ -53,24 +55,38 @@ TCPServerSocketParent::Init(PNeckoParent
 
   nsresult rv;
   mIntermediary = do_CreateInstance("@mozilla.org/tcp-socket-intermediary;1", &rv);
   if (NS_FAILED(rv)) {
     FireInteralError(this, __LINE__);
     return true;
   }
 
-  rv = mIntermediary->Listen(this, aLocalPort, aBacklog, aBinaryType, getter_AddRefs(mServerSocket));
+  rv = mIntermediary->Listen(this, aLocalPort, aBacklog, aBinaryType, GetAppId(),
+                             getter_AddRefs(mServerSocket));
   if (NS_FAILED(rv) || !mServerSocket) {
     FireInteralError(this, __LINE__);
     return true;
   }
   return true;
 }
 
+uint32_t
+TCPServerSocketParent::GetAppId()
+{
+  uint32_t appId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
+  const PContentParent *content = Manager()->Manager();
+  const InfallibleTArray<PBrowserParent*>& browsers = content->ManagedPBrowserParent();
+  if (browsers.Length() > 0) {
+    TabParent *tab = static_cast<TabParent*>(browsers[0]);
+    appId = tab->OwnAppId();
+  }
+  return appId;
+};
+
 NS_IMETHODIMP
 TCPServerSocketParent::SendCallbackAccept(nsITCPSocketParent *socket)
 {
   TCPSocketParent* _socket = static_cast<TCPSocketParent*>(socket);
   PTCPSocketParent* _psocket = static_cast<PTCPSocketParent*>(_socket);
 
   _socket->AddIPDLReference();
 
--- a/dom/network/TCPServerSocketParent.h
+++ b/dom/network/TCPServerSocketParent.h
@@ -26,16 +26,18 @@ public:
   TCPServerSocketParent() : mNeckoParent(nullptr), mIPCOpen(false) {}
 
   bool Init(PNeckoParent* neckoParent, const uint16_t& aLocalPort, const uint16_t& aBacklog,
             const nsString& aBinaryType);
 
   virtual bool RecvClose() MOZ_OVERRIDE;
   virtual bool RecvRequestDelete() MOZ_OVERRIDE;
 
+  uint32_t GetAppId();
+
   void AddIPDLReference();
   void ReleaseIPDLReference();
 
 private:
   ~TCPServerSocketParent() {}
 
   virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
--- a/dom/network/TCPSocket.js
+++ b/dom/network/TCPSocket.js
@@ -72,26 +72,35 @@ function TCPSocketEvent(type, sock, data
 // When this API moves to WebIDL and these __exposedProps__ go away, remove
 // this call here and remove the API from XPConnect.
 Cu.skipCOWCallableChecks();
 
 TCPSocketEvent.prototype = {
   __exposedProps__: {
     type: 'r',
     target: 'r',
-    data: 'r'
+    data: 'r',
+    // Promise::ResolveInternal tries to check if the thing being resolved is
+    // itself a promise through the presence of "then".  Accordingly, we list
+    // it as an exposed property, although we return undefined for it.
+    // Bug 882123 covers making TCPSocket be a proper event target with proper
+    // events.
+    then: 'r'
   },
   get type() {
     return this._type;
   },
   get target() {
     return this._target;
   },
   get data() {
     return this._data;
+  },
+  get then() {
+    return undefined;
   }
 }
 
 /*
  * nsIDOMTCPSocket object
  */
 
 function TCPSocket() {
@@ -305,17 +314,17 @@ TCPSocket.prototype = {
             self._socketOutputStream.close();
             self._readyState = kCLOSED;
             self.callListener("close");
           }
         }
       }
     }, null);
   },
-  
+
   _initStream: function ts_initStream(binaryType) {
     this._binaryType = binaryType;
     this._socketInputStream = this._transport.openInputStream(0, 0, 0);
     this._socketOutputStream = this._transport.openOutputStream(
       Ci.nsITransport.OPEN_UNBUFFERED, 0, 0);
 
     // If the other side is not listening, we will
     // get an onInputStreamReady callback where available
@@ -426,43 +435,45 @@ TCPSocket.prototype = {
         this.callListener("drain");
       }
     } else {
       LOG("bufferedAmount is updated but haven't reaches zero. bufferedAmount: " +
           bufferedAmount);
     }
   },
 
-  createAcceptedParent: function ts_createAcceptedParent(transport, binaryType) {
+  createAcceptedParent: function ts_createAcceptedParent(transport, binaryType, windowObject) {
     let that = new TCPSocket();
     that._transport = transport;
     that._initStream(binaryType);
 
     // ReadyState is kOpen since accepted transport stream has already been connected
     that._readyState = kOPEN;
     that._inputStreamPump = new InputStreamPump(that._socketInputStream, -1, -1, 0, 0, false);
     that._inputStreamPump.asyncRead(that, null);
 
     // Grab host/port from SocketTransport.
     that._host = transport.host;
     that._port = transport.port;
+    that.useWin = windowObject;
 
     return that;
   },
 
   createAcceptedChild: function ts_createAcceptedChild(socketChild, binaryType, windowObject) {
     let that = new TCPSocket();
 
     that._binaryType = binaryType;
     that._inChild = true;
     that._readyState = kOPEN;
     socketChild.setSocketAndWindow(that, windowObject);
     that._socketBridge = socketChild;
     that._host = socketChild.host;
     that._port = socketChild.port;
+    that.useWin = windowObject;
 
     return that;
   },
 
   setAppId: function ts_setAppId(appId) {
 #ifdef MOZ_WIDGET_GONK
     this._appId = appId;
 #else
@@ -632,18 +643,17 @@ TCPSocket.prototype = {
   },
 
   listen: function ts_listen(localPort, options, backlog) {
     // in the testing case, init won't be called and
     // hasPrivileges will be null. We want to proceed to test.
     if (this._hasPrivileges !== true && this._hasPrivileges !== null) {
       throw new Error("TCPSocket does not have permission in this context.\n");
     }
-
-    let that = new TCPServerSocket(this.useWin || this);
+    let that = new TCPServerSocket(this.useWin);
 
     options = options || { binaryType : this.binaryType };
     backlog = backlog || -1;
     that.listen(localPort, options, backlog);
     return that;
   },
 
   close: function ts_close() {
--- a/dom/network/TCPSocketParentIntermediary.js
+++ b/dom/network/TCPSocketParentIntermediary.js
@@ -38,47 +38,51 @@ TCPSocketParentIntermediary.prototype = 
 
   open: function(aParentSide, aHost, aPort, aUseSSL, aBinaryType, aAppId) {
     let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
     let socket = baseSocket.open(aHost, aPort, {useSecureTransport: aUseSSL, binaryType: aBinaryType});
     if (!socket)
       return null;
 
     let socketInternal = socket.QueryInterface(Ci.nsITCPSocketInternal);
-    if (socketInternal) {
-      socketInternal.setAppId(aAppId);
-    }
+    socketInternal.setAppId(aAppId);
 
     // Handle parent's request to update buffered amount.
     socketInternal.setOnUpdateBufferedAmountHandler(
       this._onUpdateBufferedAmountHandler.bind(this, aParentSide));
 
     // Handlers are set to the JS-implemented socket object on the parent side.
     this._setCallbacks(aParentSide, socket);
     return socket;
   },
 
-  listen: function(aTCPServerSocketParent, aLocalPort, aBacklog, aBinaryType) {
+  listen: function(aTCPServerSocketParent, aLocalPort, aBacklog, aBinaryType, aAppId) {
     let baseSocket = Cc["@mozilla.org/tcp-socket;1"].createInstance(Ci.nsIDOMTCPSocket);
     let serverSocket = baseSocket.listen(aLocalPort, { binaryType: aBinaryType }, aBacklog);
     if (!serverSocket)
       return null;
 
     let localPort = serverSocket.localPort;
 
     serverSocket["onconnect"] = function(socket) {
       var socketParent = Cc["@mozilla.org/tcp-socket-parent;1"]
                             .createInstance(Ci.nsITCPSocketParent);
       var intermediary = new TCPSocketParentIntermediary();
+
+      let socketInternal = socket.QueryInterface(Ci.nsITCPSocketInternal);
+      socketInternal.setAppId(aAppId);
+      socketInternal.setOnUpdateBufferedAmountHandler(
+        intermediary._onUpdateBufferedAmountHandler.bind(intermediary, socketParent));
+
       // Handlers are set to the JS-implemented socket object on the parent side,
       // so that the socket parent object can communicate data
       // with the corresponding socket child object through IPC.
       intermediary._setCallbacks(socketParent, socket);
       // The members in the socket parent object are set with arguments,
-      // so that the socket parent object can communicate data 
+      // so that the socket parent object can communicate data
       // with the JS socket object on the parent side via the intermediary object.
       socketParent.setSocketAndIntermediary(socket, intermediary);
       aTCPServerSocketParent.sendCallbackAccept(socketParent);
     };
 
     serverSocket["onerror"] = function(data) {
         var error = data.data;
 
--- a/dom/network/interfaces/nsIDOMTCPSocket.idl
+++ b/dom/network/interfaces/nsIDOMTCPSocket.idl
@@ -211,17 +211,17 @@ interface nsIDOMTCPSocket : nsISupports
 };
 
 /*
  * This interface is implemented in TCPSocket.js as an internal interfaces
  * for use in cross-process socket implementation.
  * Needed to account for multiple possible types that can be provided to
  * the socket callbacks as arguments.
  */
-[scriptable, uuid(017f130f-2477-4215-8783-57eada957699)]
+[scriptable, uuid(b1235064-9a08-4714-ad03-1212e4562803)]
 interface nsITCPSocketInternal : nsISupports {
   // Trigger the callback for |type| and provide a DOMError() object with the given data
   void callListenerError(in DOMString type, in DOMString name);
 
   // Trigger the callback for |type| and provide a string argument
   void callListenerData(in DOMString type, in DOMString data);
 
   // Trigger the callback for |type| and provide an ArrayBuffer argument
@@ -248,18 +248,21 @@ interface nsITCPSocketInternal : nsISupp
   // Create a socket object on the parent side.
   // This is called in accepting any open request on the parent side.
   // 
   // @param transport
   //        The accepted socket transport.
   // @param binaryType
   //        "arraybuffer" to use ArrayBuffer instances 
   //        in the ondata callback and as the argument to send.
+  // @param window
+  //        An object to create ArrayBuffer for this window. See Bug 831107.
   nsIDOMTCPSocket createAcceptedParent(in nsISocketTransport transport,
-                                       in DOMString binaryType);
+                                       in DOMString binaryType,
+                                       in nsIDOMWindow window);
 
   // Create a DOM socket on the child side
   // This is called when the socket is accepted on the parent side.
   //
   // @param socketChild
   //        The socket child object for the IPC implementation.
   // @param binaryType
   //        "arraybuffer" to use ArrayBuffer instances
--- a/dom/network/interfaces/nsITCPSocketParent.idl
+++ b/dom/network/interfaces/nsITCPSocketParent.idl
@@ -71,16 +71,17 @@ interface nsITCPSocketIntermediary : nsI
   nsIDOMTCPSocket open(in nsITCPSocketParent parent,
                        in DOMString host, in unsigned short port,
                        in boolean useSSL, in DOMString binaryType,
                        in unsigned long appId);
 
   // Listen on a port
   nsIDOMTCPServerSocket listen(in nsITCPServerSocketParent parent,
                                in unsigned short port, in unsigned short backlog,
-                               in DOMString binaryType);
+                               in DOMString binaryType,
+                               in unsigned long appId);
 
   // Called when received a child request to send a string.
   void onRecvSendString(in DOMString data, in uint32_t trackingNumber);
 
   // Called when received a child request to send an array buffer.
   void onRecvSendArrayBuffer(in jsval data, in uint32_t trackingNumber);
 };
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/add_task.js
@@ -0,0 +1,83 @@
+// Temporary implementation of add_task for mochitest-plain until bug 1078657 is
+// implemented.
+SimpleTest.waitForExplicitFinish();
+(function(scope) {
+  var pendingTasks = [];
+  var pendingPromise = null;
+
+  // Strict spawn function that takes a known generatorFunc and assumes that
+  // every yielded value will be a Promise.  If nesting is desired, then yield*
+  // should be used!
+  function spawn(generatorFunc) {
+    return new Promise(function(resolve, reject) {
+      try {
+        var iterator = generatorFunc();
+      }
+      catch (ex) {
+        ok(false, 'Problem invoking generator func: ' + ex + ': ' + ex.stack);
+        return;
+      }
+      var stepResolved = function(result) {
+        try {
+          var iterStep = iterator.next(result);
+        }
+        catch (ex) {
+          ok(false, 'Problem invoking iterator step: ' + ex + ': ' + ex.stack);
+          return;
+        }
+        if (iterStep.done) {
+          resolve(iterStep.value);
+          return;
+        }
+        if (!iterStep.value || !iterStep.value.then) {
+          ok(false, 'Iterator step returned non-Promise: ' + iterStep.value);
+        }
+        iterStep.value.then(stepResolved, generalErrback);
+      };
+      stepResolved();
+    });
+  }
+
+  function maybeSpawn(promiseOrGenerator) {
+    if (promiseOrGenerator.then) {
+      return promiseOrGenerator;
+    }
+    return spawn(promiseOrGenerator);
+  }
+
+  scope.add_task = function(thing) {
+    pendingTasks.push(thing);
+  };
+
+  function generalErrback(ex) {
+    ok(false,
+       'A rejection happened: ' +
+       (ex ? (ex + ': ' + ex.stack) : ''));
+  }
+
+  function runNextTask() {
+    if (pendingTasks.length) {
+      pendingPromise = maybeSpawn(pendingTasks.shift());
+      pendingPromise.then(runNextTask, generalErrback);
+    } else {
+      SimpleTest.finish();
+    }
+  }
+
+  // Trigger runNextTask after we think all JS files have been loaded.
+  // The primary goal is that we can call SimpleTest.finish() after all test
+  // code has been loaded and run.  We gate this based on the document's
+  // readyState.
+  var running = false;
+  function maybeStartRunning() {
+    if (!running && document.readyState === 'complete') {
+      running = true;
+      document.removeEventListener('readystateChange', maybeStartRunning);
+      // Defer to a subsequent turn of the event loop to let micro-tasks and any
+      // other clever setTimeout(0) instances run first.
+      window.setTimeout(runNextTask, 0);
+    }
+  }
+  document.addEventListener('readystatechange', maybeStartRunning);
+  maybeStartRunning();
+})(this);
--- a/dom/network/tests/mochitest.ini
+++ b/dom/network/tests/mochitest.ini
@@ -1,14 +1,17 @@
 [DEFAULT]
 support-files =
+  add_task.js
   file_udpsocket_iframe.html
+  test_tcpsocket_client_and_server_basics.js
 
 [test_network_basics.html]
 skip-if = toolkit == "gonk" || toolkit == 'android'
+[test_tcpsocket_client_and_server_basics.html]
 [test_tcpsocket_default_permissions.html]
 skip-if = toolkit == "gonk"
 [test_tcpsocket_enabled_no_perm.html]
 skip-if = toolkit == "gonk"
 [test_tcpsocket_enabled_with_perm.html]
 skip-if = toolkit == "gonk" || e10s
 [test_networkstats_alarms.html]
 skip-if = toolkit != "gonk"
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Core tests for TCPSocket and TCPServerSocket that replace their previous
+separate xpcshell incarnations.  This migration and cleanup occurred as part
+of bug 1084245 in order to get coverage of the tests from content.
+
+https://bugzilla.mozilla.org/show_bug.cgi?id=1084245
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1084245</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript" src="add_task.js"></script>
+  <script type="application/javascript;version=1.7" src="test_tcpsocket_client_and_server_basics.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1084245">Mozilla Bug 1084245</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/test_tcpsocket_client_and_server_basics.js
@@ -0,0 +1,363 @@
+'use strict';
+
+const SERVER_BACKLOG = -1;
+
+const SOCKET_EVENTS = ['open', 'data', 'drain', 'error', 'close'];
+
+function concatUint8Arrays(a, b) {
+  let newArr = new Uint8Array(a.length + b.length);
+  newArr.set(a, 0);
+  newArr.set(b, a.length);
+  return newArr;
+}
+
+function assertUint8ArraysEqual(a, b, comparingWhat) {
+  if (a.length !== b.length) {
+    ok(false, comparingWhat + ' arrays do not have the same length; ' +
+       a.length + ' versus ' + b.length);
+    return;
+  }
+  for (let i = 0; i < a.length; i++) {
+    if (a[i] !== b[i]) {
+      ok(false, comparingWhat + ' arrays differ at index ' + i +
+         a[i] + ' versus ' + b[i]);
+      return;
+    }
+  }
+  ok(true, comparingWhat + ' arrays were equivalent.');
+}
+
+/**
+ * Helper method to add event listeners to a socket and provide two Promise-returning
+ * helpers (see below for docs on them).  This *must* be called during the turn of
+ * the event loop where TCPSocket.open is called or the onconnect method is being
+ * invoked.
+ */
+function listenForEventsOnSocket(socket, socketType) {
+  let wantDataLength = null;
+  let pendingResolve = null;
+  let receivedEvents = [];
+  let receivedData = null;
+  let handleGenericEvent = function(event) {
+    dump('(' + socketType + ' event: ' + event.type + ')\n');
+    if (pendingResolve && wantDataLength === null) {
+      pendingResolve(event);
+      pendingResolve = null;
+    } else {
+      receivedEvents.push(event);
+    }
+  };
+
+  socket.onopen = handleGenericEvent;
+  socket.ondrain = handleGenericEvent;
+  socket.onerror = handleGenericEvent;
+  socket.onclose = handleGenericEvent;
+  socket.ondata = function(event) {
+    dump('(' + socketType + ' event: ' + event.type + ' length: ' +
+         event.data.byteLength + ')\n');
+    var arr = new Uint8Array(event.data);
+    if (receivedData === null) {
+      receivedData = arr;
+    } else {
+      receivedData = concatUint8Arrays(receivedData, arr);
+    }
+    if (wantDataLength !== null &&
+        receivedData.length >= wantDataLength) {
+      pendingResolve(receivedData);
+      pendingResolve = null;
+      receivedData = null;
+      wantDataLength = null;
+    }
+  };
+
+
+  return {
+    /**
+     * Return a Promise that will be resolved with the next (non-data) event
+     * received by the socket.  If there are queued events, the Promise will
+     * be immediately resolved (but you won't see that until a future turn of
+     * the event loop).
+     */
+    waitForEvent: function() {
+      if (pendingResolve) {
+        throw new Error('only one wait allowed at a time.');
+      }
+
+      if (receivedEvents.length) {
+        return Promise.resolve(receivedEvents.shift());
+      }
+
+      dump('(' + socketType + ' waiting for event)\n');
+      return new Promise(function(resolve, reject) {
+        pendingResolve = resolve;
+      });
+    },
+    /**
+     * Return a Promise that will be resolved with a Uint8Array of at least the
+     * given length.  We buffer / accumulate received data until we have enough
+     * data.  Data is buffered even before you call this method, so be sure to
+     * explicitly wait for any and all data sent by the other side.
+     */
+    waitForDataWithAtLeastLength: function(length) {
+      if (pendingResolve) {
+        throw new Error('only one wait allowed at a time.');
+      }
+      if (receivedData && receivedData.length >= length) {
+        let promise = Promise.resolve(receivedData);
+        receivedData = null;
+        return promise;
+      }
+      dump('(' + socketType + ' waiting for ' + length + ' bytes)\n');
+      return new Promise(function(resolve, reject) {
+        pendingResolve = resolve;
+        wantDataLength = length;
+      });
+    }
+  };
+}
+
+/**
+ * Return a promise that is resolved when the server receives a connection.  The
+ * promise is resolved with { socket, queue } where `queue` is the result of
+ * calling listenForEventsOnSocket(socket).  This must be done because we need
+ * to add the event listener during the connection.
+ */
+function waitForConnection(listeningServer) {
+  return new Promise(function(resolve, reject) {
+    // Because of the event model of sockets, we can't use the
+    // listenForEventsOnSocket mechanism; we need to hook up listeners during
+    // the connect event.
+    listeningServer.onconnect = function(socket) {
+      // Clobber the listener to get upset if it receives any more connections
+      // after this.
+      listeningServer.onconnect = function() {
+        ok(false, 'Received a connection when not expecting one.');
+      };
+      ok(true, 'Listening server accepted socket');
+      resolve({
+        socket: socket,
+        queue: listenForEventsOnSocket(socket, 'server')
+      });
+    };
+  });
+}
+
+function defer() {
+  var deferred = {};
+  deferred.promise = new Promise(function(resolve, reject) {
+    deferred.resolve = resolve;
+    deferred.reject = reject;
+  });
+  return deferred;
+}
+
+
+function* test_basics() {
+  // Enable our use of TCPSocket
+  let prefDeferred = defer();
+  SpecialPowers.pushPrefEnv(
+    { set: [ ['dom.mozTCPSocket.enabled', true] ] },
+    prefDeferred.resolve);
+  yield prefDeferred.promise;
+
+  let permDeferred = defer();
+  SpecialPowers.pushPermissions(
+    [ { type: 'tcp-socket', allow: true, context: document } ],
+    permDeferred.resolve);
+  yield permDeferred.promise;
+
+  // See bug 903830; in e10s mode we never get to find out the localPort if we
+  // let it pick a free port by choosing 0.  This is the same port the xpcshell
+  // test was using.
+  let serverPort = 8085;
+
+  let TCPSocket = navigator.mozTCPSocket;
+  // - Start up a listening socket.
+  let listeningServer = TCPSocket.listen(serverPort,
+                                         { binaryType: 'arraybuffer' },
+                                         SERVER_BACKLOG);
+
+  let connectedPromise = waitForConnection(listeningServer);
+
+  // -- Open a connection to the server
+  let clientSocket = TCPSocket.open('127.0.0.1', serverPort,
+                                    { binaryType: 'arraybuffer' });
+  let clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+
+  // (the client connects)
+  is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
+  is(clientSocket.readyState, 'open', 'client readyState is open');
+
+  // (the server connected)
+  let { socket: serverSocket, queue: serverQueue } = yield connectedPromise;
+  is(serverSocket.readyState, 'open', 'server readyState is open');
+
+  // -- Simple send / receive
+  // - Send data from client to server
+  // (But not so much we cross the drain threshold.)
+  let smallUint8Array = new Uint8Array(256);
+  for (let i = 0; i < smallUint8Array.length; i++) {
+    smallUint8Array[i] = i;
+  }
+  is(clientSocket.send(smallUint8Array.buffer, 0, smallUint8Array.length), true,
+     'Client sending less than 64k, buffer should not be full.');
+
+  let serverReceived = yield serverQueue.waitForDataWithAtLeastLength(256);
+  assertUint8ArraysEqual(serverReceived, smallUint8Array,
+                         'Server received/client sent');
+
+  // - Send data from server to client
+  // (But not so much we cross the drain threshold.)
+  is(serverSocket.send(smallUint8Array.buffer, 0, smallUint8Array.length), true,
+     'Server sending less than 64k, buffer should not be full.');
+
+  let clientReceived = yield clientQueue.waitForDataWithAtLeastLength(256);
+  assertUint8ArraysEqual(clientReceived, smallUint8Array,
+                         'Client received/server sent');
+
+  // -- Perform sending multiple times with different buffer slices
+  // - Send data from client to server
+  // (But not so much we cross the drain threshold.)
+  is(clientSocket.send(smallUint8Array.buffer, 0, 7),
+     true, 'Client sending less than 64k, buffer should not be full.');
+  is(clientSocket.send(smallUint8Array.buffer, 7, smallUint8Array.length - 7),
+     true, 'Client sending less than 64k, buffer should not be full.');
+
+  serverReceived = yield serverQueue.waitForDataWithAtLeastLength(256);
+  assertUint8ArraysEqual(serverReceived, smallUint8Array,
+                         'Server received/client sent');
+
+  // - Send data from server to client
+  // (But not so much we cross the drain threshold.)
+  is(serverSocket.send(smallUint8Array.buffer, 0, 7),
+     true, 'Server sending less than 64k, buffer should not be full.');
+  is(serverSocket.send(smallUint8Array.buffer, 7, smallUint8Array.length - 7),
+     true, 'Server sending less than 64k, buffer should not be full.');
+
+  clientReceived = yield clientQueue.waitForDataWithAtLeastLength(256);
+  assertUint8ArraysEqual(clientReceived, smallUint8Array,
+                         'Client received/server sent');
+
+
+  // -- Send "big" data in both directions
+  // (Enough to cross the buffering/drain threshold; 64KiB)
+  let bigUint8Array = new Uint8Array(65536 + 3);
+  for (let i = 0; i < bigUint8Array.length; i++) {
+    bigUint8Array[i] = i % 256;
+  }
+  // Do this twice so we have confidence that the 'drain' event machinery
+  // doesn't break after the first use.
+  for (let iSend = 0; iSend < 2; iSend++) {
+    // - Send "big" data from the client to the server
+    is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
+       'Client sending more than 64k should result in the buffer being full.');
+    is((yield clientQueue.waitForEvent()).type, 'drain',
+       'The drain event should fire after a large send that indicated full.');
+
+    serverReceived = yield serverQueue.waitForDataWithAtLeastLength(
+      bigUint8Array.length);
+    assertUint8ArraysEqual(serverReceived, bigUint8Array,
+                           'server received/client sent');
+
+    // - Send "big" data from the server to the client
+    is(serverSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
+       'Server sending more than 64k should result in the buffer being full.');
+    is((yield serverQueue.waitForEvent()).type, 'drain',
+       'The drain event should fire after a large send that indicated full.');
+
+    clientReceived = yield clientQueue.waitForDataWithAtLeastLength(
+      bigUint8Array.length);
+    assertUint8ArraysEqual(clientReceived, bigUint8Array,
+                           'client received/server sent');
+  }
+
+  // -- Server closes the connection
+  serverSocket.close();
+  is(serverSocket.readyState, 'closing',
+     'readyState should be closing immediately after calling close');
+
+  is((yield clientQueue.waitForEvent()).type, 'close',
+     'The client should get a close event when the server closes.');
+  is(clientSocket.readyState, 'closed',
+     'client readyState should be closed after close event');
+  is((yield serverQueue.waitForEvent()).type, 'close',
+     'The server should get a close event when it closes itself.');
+  is(serverSocket.readyState, 'closed',
+     'server readyState should be closed after close event');
+
+  // -- Re-establish connection
+  connectedPromise = waitForConnection(listeningServer);
+  clientSocket = TCPSocket.open('127.0.0.1', serverPort,
+                                { binaryType: 'arraybuffer' });
+  clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+  is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
+
+  let connectedResult = yield connectedPromise;
+  // destructuring assignment is not yet ES6 compliant, must manually unpack
+  serverSocket = connectedResult.socket;
+  serverQueue = connectedResult.queue;
+
+  // -- Client closes the connection
+  clientSocket.close();
+  is(clientSocket.readyState, 'closing',
+     'client readyState should be losing immediately after calling close');
+
+  is((yield clientQueue.waitForEvent()).type, 'close',
+     'The client should get a close event when it closes itself.');
+  is(clientSocket.readyState, 'closed',
+     'client readyState should be closed after the close event is received');
+  is((yield serverQueue.waitForEvent()).type, 'close',
+     'The server should get a close event when the client closes.');
+  is(serverSocket.readyState, 'closed',
+     'server readyState should be closed after the close event is received');
+
+
+  // -- Re-establish connection
+  connectedPromise = waitForConnection(listeningServer);
+  clientSocket = TCPSocket.open('127.0.0.1', serverPort,
+                                { binaryType: 'arraybuffer' });
+  clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+  is((yield clientQueue.waitForEvent()).type, 'open', 'got open event');
+
+  connectedResult = yield connectedPromise;
+  // destructuring assignment is not yet ES6 compliant, must manually unpack
+  serverSocket = connectedResult.socket;
+  serverQueue = connectedResult.queue;
+
+  // -- Call close after enqueueing a lot of data, make sure it goes through.
+  // We'll have the client send and close.
+  is(clientSocket.send(bigUint8Array.buffer, 0, bigUint8Array.length), false,
+     'Client sending more than 64k should result in the buffer being full.');
+  clientSocket.close();
+  // The drain will still fire
+  is((yield clientQueue.waitForEvent()).type, 'drain',
+     'The drain event should fire after a large send that returned true.');
+  // Then we'll get a close
+  is((yield clientQueue.waitForEvent()).type, 'close',
+     'The close event should fire after the drain event.');
+
+  // The server will get its data
+  serverReceived = yield serverQueue.waitForDataWithAtLeastLength(
+    bigUint8Array.length);
+  assertUint8ArraysEqual(serverReceived, bigUint8Array,
+                         'server received/client sent');
+  // And a close.
+  is((yield serverQueue.waitForEvent()).type, 'close',
+     'The drain event should fire after a large send that returned true.');
+
+
+  // -- Close the listening server (and try to connect)
+  // We want to verify that the server actually closes / stops listening when
+  // we tell it to.
+  listeningServer.close();
+
+  // - try and connect, get an error
+  clientSocket = TCPSocket.open('127.0.0.1', serverPort,
+                                { binaryType: 'arraybuffer' });
+  clientQueue = listenForEventsOnSocket(clientSocket, 'client');
+  is((yield clientQueue.waitForEvent()).type, 'error', 'fail to connect');
+  is(clientSocket.readyState, 'closed',
+     'client readyState should be closed after the failure to connect');
+}
+
+add_task(test_basics);
deleted file mode 100644
--- a/dom/network/tests/unit/test_multisend.js
+++ /dev/null
@@ -1,152 +0,0 @@
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cr = Components.results;
-const Cu = Components.utils;
-const CC = Components.Constructor;
-
-Cu.import("resource://gre/modules/Services.jsm");
-
-const ServerSocket = CC("@mozilla.org/network/server-socket;1",
-                        "nsIServerSocket",
-                        "init"),
-      InputStreamPump = CC("@mozilla.org/network/input-stream-pump;1",
-                           "nsIInputStreamPump",
-                           "init"),
-      BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
-                             "nsIBinaryInputStream",
-                             "setInputStream"),
-      BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1",
-                              "nsIBinaryOutputStream",
-                              "setOutputStream"),
-      TCPSocket = new (CC("@mozilla.org/tcp-socket;1",
-                     "nsIDOMTCPSocket"))();
-
-var server = null, sock = null;
-
-/**
- * Spin up a listening socket and associate at most one live, accepted socket
- * with ourselves.
- */
-function TestServer() {
-  this.listener = ServerSocket(-1, true, -1);
-  do_print('server: listening on ' + this.listener.port);
-  this.listener.asyncListen(this);
-
-  this.binaryInput = null;
-  this.input = null;
-  this.binaryOutput = null;
-  this.output = null;
-
-  this.onaccept = null;
-  this.ondata = null;
-  this.onclose = null;
-}
-
-TestServer.prototype = {
-  onSocketAccepted: function(socket, trans) {
-    if (this.input)
-      do_throw("More than one live connection!?");
-
-    do_print('server: got client connection');
-    this.input = trans.openInputStream(0, 0, 0);
-    this.binaryInput = new BinaryInputStream(this.input);
-    this.output = trans.openOutputStream(0, 0, 0);
-    this.binaryOutput = new BinaryOutputStream(this.output);
-
-    new InputStreamPump(this.input, -1, -1, 0, 0, false).asyncRead(this, null);
-
-    if (this.onaccept)
-      this.onaccept();
-    else
-      do_throw("Received unexpected connection!");
-  },
-
-  onStopListening: function(socket) {
-  },
-
-  onDataAvailable: function(request, context, inputStream, offset, count) {
-    var readData = this.binaryInput.readByteArray(count);
-    if (this.ondata) {
-      try {
-        this.ondata(readData);
-      } catch(ex) {
-        // re-throw if this is from do_throw
-        if (ex === Cr.NS_ERROR_ABORT)
-          throw ex;
-        // log if there was a test problem
-        do_print('Caught exception: ' + ex + '\n' + ex.stack);
-        do_throw('test is broken; bad ondata handler; see above');
-      }
-    } else {
-      do_throw('Received ' + count + ' bytes of unexpected data!');
-    }
-  },
-
-  onStartRequest: function(request, context) {
-  },
-
-  onStopRequest: function(request, context, status) {
-    if (this.onclose)
-      this.onclose();
-    else
-      do_throw("Received unexpected close!");
-  },
-
-  close: function() {
-    this.binaryInput.close();
-    this.binaryOutput.close();
-  },
-
-  /**
-   * Forget about the socket we knew about before.
-   */
-  reset: function() {
-    this.binaryInput = null;
-    this.input = null;
-    this.binaryOutput = null;
-    this.output = null;
-  },
-};
-
-function run_test() {
-  Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true);
-
-  do_test_pending();
-
-  server = new TestServer();
-  server.reset();
-  sock = TCPSocket.open(
-    '127.0.0.1', server.listener.port,
-    { binaryType: 'arraybuffer' });
-
-  var encoder = new TextEncoder();
-  var ok = encoder.encode("OKBYE");
-
-  var expected = ['O', 'K', 'B', 'Y', 'E']
-                   .map(function(c) { return c.charCodeAt(0); });
-  var seenData = [];
-
-  server.onaccept = function() {};
-  server.ondata = function(data) {
-    do_print(data + ":" + data.length);
-
-    seenData = seenData.concat(data);
-
-    if (seenData.length == expected.length) {
-      do_print(expected);
-      do_check_eq(seenData.length, expected.length);
-      for (var i = 0; i < seenData.length; i++) {
-        do_check_eq(seenData[i], expected[i]);
-      }
-      sock.close();
-      server.close();
-      do_test_finished();
-    }
-  };
-  server.onclose = function() {};
-
-  sock.onopen = function() {
-    sock.send(ok.buffer, 0, 2);
-    sock.send(ok.buffer, 2, 3);
-  };
-}
deleted file mode 100644
--- a/dom/network/tests/unit/test_tcpserversocket.js
+++ /dev/null
@@ -1,311 +0,0 @@
-/**
- * Test TCPSocket.js by creating an XPCOM-style server socket, then sending
- * data in both directions and making sure each side receives their data
- * correctly and with the proper events.
- *
- * This test is derived from netwerk/test/unit/test_socks.js, except we don't
- * involve a subprocess.
- *
- * Future work:
- * - SSL.  see https://bugzilla.mozilla.org/show_bug.cgi?id=466524
- *             https://bugzilla.mozilla.org/show_bug.cgi?id=662180
- *   Alternatively, mochitests could be used.
- * - Testing overflow logic.
- *
- **/
-
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-const Cr = Components.results;
-const Cu = Components.utils;
-const CC = Components.Constructor;
-
-/**
- *
- * Constants
- *
- */
-
-// Test parameter.
-const PORT = 8085;
-const BACKLOG = -1;
-
-// Some binary data to send.
-const DATA_ARRAY = [0, 255, 254, 0, 1, 2, 3, 0, 255, 255, 254, 0],
-      DATA_ARRAY_BUFFER = new ArrayBuffer(DATA_ARRAY.length),
-      TYPED_DATA_ARRAY = new Uint8Array(DATA_ARRAY_BUFFER),
-      HELLO_WORLD = "hlo wrld. ",
-      BIG_ARRAY = new Array(65539);
-
-TYPED_DATA_ARRAY.set(DATA_ARRAY, 0);
-
-for (var i_big = 0; i_big < BIG_ARRAY.length; i_big++) {
-  BIG_ARRAY[i_big] = Math.floor(Math.random() * 256);
-}
-
-const BIG_ARRAY_BUFFER = new ArrayBuffer(BIG_ARRAY.length);
-const BIG_TYPED_ARRAY = new Uint8Array(BIG_ARRAY_BUFFER);
-BIG_TYPED_ARRAY.set(BIG_ARRAY);
-
-const TCPSocket = new (CC("@mozilla.org/tcp-socket;1",
-                     "nsIDOMTCPSocket"))();
-
-const gInChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
-                  .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-
-/**
- *
- * Helper functions
- *
- */
-
-
-function makeSuccessCase(name) {
-  return function() {
-    do_print('got expected: ' + name);
-    run_next_test();
-  };
-}
-
-function makeJointSuccess(names) {
-  let funcs = {}, successCount = 0;
-  names.forEach(function(name) {
-    funcs[name] = function() {
-      do_print('got expected: ' + name);
-      if (++successCount === names.length)
-        run_next_test();
-    };
-  });
-  return funcs;
-}
-
-function makeFailureCase(name) {
-  return function() {
-    let argstr;
-    if (arguments.length) {
-      argstr = '(args: ' +
-        Array.map(arguments, function(x) { return x.data + ""; }).join(" ") + ')';
-    }
-    else {
-      argstr = '(no arguments)';
-    }
-    do_throw('got unexpected: ' + name + ' ' + argstr);
-  };
-}
-
-function makeExpectData(name, expectedData, fromEvent, callback) {
-  let dataBuffer = fromEvent ? null : [], done = false;
-  let dataBufferView = null;
-  return function(receivedData) {
-    if (receivedData.data) {
-      receivedData = receivedData.data;
-    }
-    let recvLength = receivedData.byteLength !== undefined ?
-        receivedData.byteLength : receivedData.length;
-
-    if (fromEvent) {
-      if (dataBuffer) {
-        let newBuffer = new ArrayBuffer(dataBuffer.byteLength + recvLength);
-        let newBufferView = new Uint8Array(newBuffer);
-        newBufferView.set(dataBufferView, 0);
-        newBufferView.set(receivedData, dataBuffer.byteLength);
-        dataBuffer = newBuffer;
-        dataBufferView = newBufferView;
-      }
-      else {
-        dataBuffer = receivedData;
-        dataBufferView = new Uint8Array(dataBuffer);
-      }
-    }
-    else {
-      dataBuffer = dataBuffer.concat(receivedData);
-    }
-    do_print(name + ' received ' + recvLength + ' bytes');
-
-    if (done)
-      do_throw(name + ' Received data event when already done!');
-
-    let dataView = dataBuffer.byteLength !== undefined ? new Uint8Array(dataBuffer) : dataBuffer;
-    if (dataView.length >= expectedData.length) {
-      // check the bytes are equivalent
-      for (let i = 0; i < expectedData.length; i++) {
-        if (dataView[i] !== expectedData[i]) {
-          do_throw(name + ' Received mismatched character at position ' + i);
-        }
-      }
-      if (dataView.length > expectedData.length)
-        do_throw(name + ' Received ' + dataView.length + ' bytes but only expected ' +
-                 expectedData.length + ' bytes.');
-
-      done = true;
-      if (callback) {
-        callback();
-      } else {
-        run_next_test();
-      }
-    }
-  };
-}
-
-var server = null, sock = null, connectedsock = null, failure_drain = null;
-var count = 0;
-/**
- *
- * Test functions
- *
- */
-
-/**
- * Connect the socket to the server. This test is added as the first
- * test, and is also added after every test which results in the socket
- * being closed.
- */
-
-function connectSock() {
-  if (server) {
-    server.close();
-  }
-
-  var yayFuncs = makeJointSuccess(['serveropen', 'clientopen']);
-  var options = { binaryType: 'arraybuffer' };
-
-  server = TCPSocket.listen(PORT, options, BACKLOG);
-  server.onconnect = function(socket) {
-    // Bug 937528 - Accepted client tcp socket (mozTcpSocket) has
-    //              uninitialized host and port.
-    if (socket.host !== '127.0.0.1') {
-      do_throw('got unexpected: connected socket host should be 127.0.0.1 but not ' + socket.host);
-    } else {
-      do_print('Got expected connected socket host: ' + socket.host);
-    }
-
-    connectedsock = socket;
-    connectedsock.ondata = makeFailureCase('serverdata');
-    connectedsock.onerror = makeFailureCase('servererror');
-    connectedsock.onclose = makeFailureCase('serverclose');
-    yayFuncs.serveropen();
-  };
-  server.onerror = makeFailureCase('error');
-  sock = TCPSocket.open(
-    '127.0.0.1', PORT, options);
-  sock.onopen = yayFuncs.clientopen;
-  sock.ondrain = null;
-  sock.ondata = makeFailureCase('data');
-  sock.onerror = makeFailureCase('error');
-  sock.onclose = makeFailureCase('close');
-}
-
-/**
- * Connect the socket to the server after the server was closed.
- * This test is added after test to close the server was conducted.
- */
-function openSockInClosingServer() {
-  var success = makeSuccessCase('clientnotopen');
-  var options = { binaryType: 'arraybuffer' };
-
-  sock = TCPSocket.open(
-    '127.0.0.1', PORT, options);
-
-  sock.onopen = makeFailureCase('open');
-  sock.onerror = success;
-}
-
-/**
- * Test that sending a small amount of data works, and that buffering
- * does not take place for this small amount of data.
- */
-
-function sendDataToServer() {
-  connectedsock.ondata = makeExpectData('serverdata', DATA_ARRAY, true);
-  if (!sock.send(DATA_ARRAY_BUFFER)) {
-    do_throw("send should not have buffered such a small amount of data");
-  }
-}
-
-/**
- * Test that data sent from the server correctly fires the ondata
- * callback on the client side.
- */
-
-function receiveDataFromServer() {
-  connectedsock.ondata = makeFailureCase('serverdata');
-  sock.ondata = makeExpectData('data', DATA_ARRAY, true);
-
-  connectedsock.send(DATA_ARRAY_BUFFER);
-}
-
-/**
- * Test that when the server closes the connection, the onclose callback
- * is fired on the client side.
- */
-
-function serverCloses() {
-  // we don't really care about the server's close event, but we do want to
-  // make sure it happened for sequencing purposes.
-  sock.ondata = makeFailureCase('data');
-  sock.onclose = makeFailureCase('close1');
-  connectedsock.onclose = makeFailureCase('close2');
-
-  server.close();
-  run_next_test();
-}
-
-/**
- * Test that when the client closes the connection, the onclose callback
- * is fired on the server side.
- */
-
-function cleanup() {
-  do_print("Cleaning up");
-  sock.onclose = null;
-  connectedsock.onclose = null;
-
-  server.close();
-  sock.close();
-  if (count == 1){
-    if (!gInChild)
-      Services.prefs.clearUserPref('dom.mozTCPSocket.enabled');
-  }
-  count++;
-  run_next_test();
-}
-// - connect, data and events work both ways
-add_test(connectSock);
-
-add_test(sendDataToServer);
-
-add_test(receiveDataFromServer);
-// - server closes on us
-add_test(serverCloses);
-
-// - send and receive after closing server
-add_test(sendDataToServer);
-add_test(receiveDataFromServer);
-// - check a connection refused from client to server after closing server
-add_test(serverCloses);
-
-add_test(openSockInClosingServer);
-
-// - clean up
-add_test(cleanup);
-
-// - send and receive in reverse order for client and server
-add_test(connectSock);
-
-add_test(receiveDataFromServer);
-
-add_test(sendDataToServer);
-
-// - clean up
-
-add_test(cleanup);
-
-function run_test() {
-  if (!gInChild)
-    Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true);
-
-  run_next_test();
-}
--- a/dom/network/tests/unit/test_tcpsocket.js
+++ b/dom/network/tests/unit/test_tcpsocket.js
@@ -69,22 +69,16 @@ const gInChild = Cc["@mozilla.org/xre/ap
 Cu.import("resource://gre/modules/Services.jsm");
 
 /**
  *
  * Helper functions
  *
  */
 
-function get_platform() {
-  var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
-                              .getService(Components.interfaces.nsIXULRuntime);
-  return xulRuntime.OS;
-}
-
 function is_content() {
   return this._inChild = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
                             .processType != Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
 }
 
 /**
  * Spin up a listening socket and associate at most one live, accepted socket
  * with ourselves.
@@ -285,231 +279,24 @@ function connectSock() {
   sock.onerror = makeFailureCase('error');
   sock.onclose = makeFailureCase('close');
 
   server.onconnect = yayFuncs.serveropen;
   server.ondata = makeFailureCase('serverdata');
   server.onclose = makeFailureCase('serverclose');
 }
 
-/**
- * Test that sending a small amount of data works, and that buffering
- * does not take place for this small amount of data.
- */
-
-function sendData() {
-  server.ondata = makeExpectData('serverdata', DATA_ARRAY);
-  if (!sock.send(DATA_ARRAY_BUFFER)) {
-    do_throw("send should not have buffered such a small amount of data");
-  }
-}
-
-/**
- * Test that sending a large amount of data works, that buffering
- * takes place (send returns true), and that ondrain is called once
- * the data has been sent.
- */
-
-function sendBig() {
-  var yays = makeJointSuccess(['serverdata', 'clientdrain']),
-      amount = 0;
-
-  server.ondata = function (data) {
-    amount += data.length;
-    if (amount === BIG_TYPED_ARRAY.length) {
-      yays.serverdata();
-    }
-  };
-  sock.ondrain = function(evt) {
-    if (sock.bufferedAmount) {
-      do_throw("sock.bufferedAmount was > 0 in ondrain");
-    }
-    yays.clientdrain(evt);
-  }
-  if (sock.send(BIG_ARRAY_BUFFER)) {
-    do_throw("expected sock.send to return false on large buffer send");
-  }
-}
-
-/**
- * Test that data sent from the server correctly fires the ondata
- * callback on the client side.
- */
-
-function receiveData() {
-  server.ondata = makeFailureCase('serverdata');
-  sock.ondata = makeExpectData('data', DATA_ARRAY, true);
-
-  server.binaryOutput.writeByteArray(DATA_ARRAY, DATA_ARRAY.length);
-}
-
-/**
- * Test that when the server closes the connection, the onclose callback
- * is fired on the client side.
- */
-
-function serverCloses() {
-  // we don't really care about the server's close event, but we do want to
-  // make sure it happened for sequencing purposes.
-  var yayFuncs = makeJointSuccess(['clientclose', 'serverclose']);
-  sock.ondata = makeFailureCase('data');
-  sock.onclose = yayFuncs.clientclose;
-  server.onclose = yayFuncs.serverclose;
-
-  server.close();
-}
-
-/**
- * Test that when the client closes the connection, the onclose callback
- * is fired on the server side.
- */
-
-function clientCloses() {
-  // we want to make sure the server heard the close and also that the client's
-  // onclose event fired for consistency.
-  var yayFuncs = makeJointSuccess(['clientclose', 'serverclose']);
-  server.onclose = yayFuncs.serverclose;
-  sock.onclose = yayFuncs.clientclose;
-
-  sock.close();
-}
-
-/**
- * Send a large amount of data and immediately call close
- */
-
-function bufferedClose() {
-  var yays = makeJointSuccess(['serverdata', 'clientclose', 'serverclose']);
-  server.ondata = makeExpectData(
-    "ondata", BIG_TYPED_ARRAY, false, yays.serverdata);
-  server.onclose = yays.serverclose;
-  sock.onclose = yays.clientclose;
-  sock.send(BIG_ARRAY_BUFFER);
-  sock.close();
-}
-
-/**
- * Connect to a port we know is not listening so an error is assured,
- * and make sure that onerror and onclose are fired on the client side.
- */
-
-function badConnect() {
-  // There's probably nothing listening on tcp port 2.
-  sock = TCPSocket.open('127.0.0.1', 2);
-
-  sock.onopen = makeFailureCase('open');
-  sock.ondata = makeFailureCase('data');
-
-  let success = makeSuccessCase('error');
-  let gotError = false;
-  sock.onerror = function(event) {
-    do_check_eq(event.data.name, 'ConnectionRefusedError');
-    gotError = true;
-  };
-  sock.onclose = function() {
-    if (!gotError)
-      do_throw('got close without error!');
-    else
-      success();
-  };
-}
-
-/**
- * Test that calling send with enough data to buffer causes ondrain to
- * be invoked once the data has been sent, and then test that calling send
- * and buffering again causes ondrain to be fired again.
- */
-
-function drainTwice() {
-  let yays = makeJointSuccess(
-    ['ondrain', 'ondrain2',
-    'ondata', 'ondata2',
-    'serverclose', 'clientclose']);
-  let ondrainCalled = false,
-      ondataCalled = false;
-
-  function maybeSendNextData() {
-    if (!ondrainCalled || !ondataCalled) {
-      // make sure server got data and client got ondrain.
-      return;
-    }
-
-    server.ondata = makeExpectData(
-      "ondata2", BIG_TYPED_ARRAY_2, false, yays.ondata2);
-
-    sock.ondrain = yays.ondrain2;
-
-    if (sock.send(BIG_ARRAY_BUFFER_2)) {
-      do_throw("sock.send(BIG_TYPED_ARRAY_2) did not return false to indicate buffering");
-    }
-
-    sock.close();
-  }
-
-  function clientOndrain() {
-    yays.ondrain();
-    ondrainCalled = true;
-    maybeSendNextData();
-  }
-
-  function serverSideCallback() {
-    yays.ondata();
-    ondataCalled = true;
-    maybeSendNextData();
-  }
-
-  server.onclose = yays.serverclose;
-  server.ondata = makeExpectData(
-    "ondata", BIG_TYPED_ARRAY, false, serverSideCallback);
-
-  sock.onclose = yays.clientclose;
-  sock.ondrain = clientOndrain;
-
-  if (sock.send(BIG_ARRAY_BUFFER)) {
-    throw new Error("sock.send(BIG_TYPED_ARRAY) did not return false to indicate buffering");
-  }
-}
-
 function cleanup() {
   do_print("Cleaning up");
   sock.close();
   if (!gInChild)
     Services.prefs.clearUserPref('dom.mozTCPSocket.enabled');
   run_next_test();
 }
 
-/**
- * Test that calling send with enough data to buffer twice in a row without
- * waiting for ondrain still results in ondrain being invoked at least once.
- */
-
-function bufferTwice() {
-  let yays = makeJointSuccess(
-    ['ondata', 'ondrain', 'serverclose', 'clientclose']);
-
-  let double_array = new Uint8Array(BIG_ARRAY.concat(BIG_ARRAY_2));
-  server.ondata = makeExpectData(
-    "ondata", double_array, false, yays.ondata);
-
-  server.onclose = yays.serverclose;
-  sock.onclose = yays.clientclose;
-
-  sock.ondrain = function () {
-    sock.close();
-    yays.ondrain();
-  }
-
-  if (sock.send(BIG_ARRAY_BUFFER)) {
-    throw new Error("sock.send(BIG_TYPED_ARRAY) did not return false to indicate buffering");
-  }
-  if (sock.send(BIG_ARRAY_BUFFER_2)) {
-    throw new Error("sock.send(BIG_TYPED_ARRAY_2) did not return false to indicate buffering on second synchronous call to send");
-  }
-}
-
 // Test child behavior when child thinks it's buffering but parent doesn't
 // buffer.
 // 1. set bufferedAmount of content socket to a value that will make next
 //    send() call return false.
 // 2. send a small data to make send() return false, but it won't make
 //    parent buffer.
 // 3. we should get a ondrain.
 function childbuffered() {
@@ -556,58 +343,29 @@ function childnotbuffered() {
   sock.ondrain = makeFailureCase('drain');
   sock.onclose = yays.clientclose;
   server.onclose = yays.serverclose;
   do_timeout(1000, function() {
     sock.close();
   });
 };
 
-// - connect, data and events work both ways
-add_test(connectSock);
-add_test(sendData);
-add_test(sendBig);
-add_test(receiveData);
-// - server closes on us
-add_test(serverCloses);
-
-// - connect, we close on the server
-add_test(connectSock);
-add_test(clientCloses);
-
-// - connect, buffer, close
-add_test(connectSock);
-add_test(bufferedClose);
-
-if (get_platform() !== "Darwin") {
-  // This test intermittently fails way too often on OS X, for unknown reasons.
-  // Please, diagnose and fix it if you can.
-  // - get an error on an attempt to connect to a non-listening port
-  add_test(badConnect);
-}
-
-// send a buffer, get a drain, send a buffer, get a drain
-add_test(connectSock);
-add_test(drainTwice);
-
-// send a buffer, get a drain, send a buffer, get a drain
-add_test(connectSock);
-add_test(bufferTwice);
-
 if (is_content()) {
   add_test(connectSock);
   add_test(childnotbuffered);
 
   add_test(connectSock);
   add_test(childbuffered);
+
+  // clean up
+  add_test(cleanup);
+} else {
+  do_check_true(true, 'non-content process wants to pretend to one test');
 }
 
-// clean up
-add_test(cleanup);
-
 function run_test() {
   if (!gInChild)
     Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true);
 
   server = new TestServer();
 
   run_next_test();
 }
--- a/dom/network/tests/unit/xpcshell.ini
+++ b/dom/network/tests/unit/xpcshell.ini
@@ -1,10 +1,6 @@
 [DEFAULT]
 head =
 tail =
 skip-if = toolkit == 'gonk'
 
 [test_tcpsocket.js]
-[test_multisend.js]
-[test_tcpserversocket.js]
-run-sequentially = Uses hardcoded port, bug 903830.
-skip-if = os == 'mac' # bug 953208 - frequent timeouts on OSX
deleted file mode 100644
--- a/dom/network/tests/unit_ipc/test_tcpserversocket_ipc.js
+++ /dev/null
@@ -1,9 +0,0 @@
-Components.utils.import("resource://gre/modules/Services.jsm");
-
-function run_test() {
-  Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true);
-  run_test_in_child("../unit/test_tcpserversocket.js", function() {
-    Services.prefs.clearUserPref('dom.mozTCPSocket.enabled');
-    do_test_finished();
-  });
-}
--- a/dom/network/tests/unit_ipc/xpcshell.ini
+++ b/dom/network/tests/unit_ipc/xpcshell.ini
@@ -1,8 +1,6 @@
 [DEFAULT]
 head =
 tail =
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 
 [test_tcpsocket_ipc.js]
-[test_tcpserversocket_ipc.js]
-run-sequentially = Uses hardcoded port, bug 903830.
--- a/dom/plugins/base/nsPluginsDirDarwin.cpp
+++ b/dom/plugins/base/nsPluginsDirDarwin.cpp
@@ -469,47 +469,51 @@ nsresult nsPluginFile::GetPluginInfo(nsP
   // First look for data in a bundle plist
   if (bundle) {
     ParsePlistPluginInfo(info, bundle);
     ::CFRelease(bundle);
     if (info.fVariantCount > 0)
       return NS_OK;
   }
 
-  // Don't load "fbplugin" (a Facebook plugin) if we're running on OS X 10.10
-  // (Yosemite) or later.  It crashes on load, in the call to LoadPlugin()
-  // below.  See bug 1086977.
+  // Don't load "fbplugin" or any plugins whose name starts with "fbplugin_"
+  // (Facebook plugins) if we're running on OS X 10.10 (Yosemite) or later.
+  // A "fbplugin" file crashes on load, in the call to LoadPlugin() below.
+  // See bug 1086977.
   if (nsCocoaFeatures::OnYosemiteOrLater()) {
-    if (fileName.EqualsLiteral("fbplugin")) {
-      NS_WARNING("Preventing load of fbplugin (see bug 1086977)");
+    if (fileName.EqualsLiteral("fbplugin") ||
+        StringBeginsWith(fileName, NS_LITERAL_CSTRING("fbplugin_"))) {
+      nsAutoCString msg;
+      msg.AppendPrintf("Preventing load of %s (see bug 1086977)",
+                       fileName.get());
+      NS_WARNING(msg.get());
       return NS_ERROR_FAILURE;
     }
 #if defined(MOZ_CRASHREPORTER)
     // The block above assumes that "fbplugin" is the filename of the plugin
-    // to be blocked, but be don't yet know for sure if this is true.  It might
-    // also be the name of a file indirectly loaded by the plugin.  So for the
-    // time being we must record extra information in our crash logs.
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug 1086977"),
+    // to be blocked, or that the filename starts with "fbplugin_".  But we
+    // don't yet know for sure if this is always true.  So for the time being
+    // record extra information in our crash logs.
+    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug_1086977"),
                                        fileName);
 #endif
   }
 
   // It's possible that our plugin has 2 entry points that'll give us mime type
   // info. Quicktime does this to get around the need of having admin rights to
   // change mime info in the resource fork. We need to use this info instead of
   // the resource. See bug 113464.
 
   // Sadly we have to load the library for this to work.
   rv = LoadPlugin(outLibrary);
 #if defined(MOZ_CRASHREPORTER)
   if (nsCocoaFeatures::OnYosemiteOrLater()) {
     // If we didn't crash in LoadPlugin(), change the previous annotation so we
-    // don't sow confusion.  Unfortunately there's not (yet) any way to get rid
-    // of the annotation completely.
-    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug 1086977"),
+    // don't sow confusion.
+    CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("Bug_1086977"),
                                        NS_LITERAL_CSTRING("Didn't crash, please ignore"));
   }
 #endif
   if (NS_FAILED(rv))
     return rv;
 
   // Try to get data from NP_GetMIMEDescription
   if (pLibrary) {
--- a/dom/plugins/ipc/PluginScriptableObjectChild.h
+++ b/dom/plugins/ipc/PluginScriptableObjectChild.h
@@ -184,18 +184,18 @@ private:
 
     ~StoredIdentifier() { MOZ_COUNT_DTOR(StoredIdentifier); }
   };
 
 public:
   class MOZ_STACK_CLASS StackIdentifier
   {
   public:
-    StackIdentifier(const PluginIdentifier& aIdentifier);
-    StackIdentifier(NPIdentifier aIdentifier);
+    explicit StackIdentifier(const PluginIdentifier& aIdentifier);
+    explicit StackIdentifier(NPIdentifier aIdentifier);
     ~StackIdentifier();
 
     void MakePermanent()
     {
       if (mStored) {
         mStored->mPermanent = true;
       }
     }
--- a/dom/plugins/ipc/PluginScriptableObjectParent.cpp
+++ b/dom/plugins/ipc/PluginScriptableObjectParent.cpp
@@ -32,17 +32,18 @@ using namespace mozilla::plugins::parent
  * on the stack: although StackIdentifier roots all identifiers used, the GC has
  * no way to no that a jsid cast to an NPIdentifier needs to be fixed up if it
  * is moved.
  */
 
 class MOZ_STACK_CLASS StackIdentifier
 {
 public:
-  StackIdentifier(const PluginIdentifier& aIdentifier, bool aIntern = false);
+  explicit StackIdentifier(const PluginIdentifier& aIdentifier,
+                           bool aIntern = false);
 
   bool Failed() const { return mFailed; }
   NPIdentifier ToNPIdentifier() const { return mIdentifier; }
 
 private:
   bool mFailed;
   NPIdentifier mIdentifier;
   AutoSafeJSContext mCx;
deleted file mode 100644
--- a/dom/plugins/ipc/hangui/Makefile.in
+++ /dev/null
@@ -1,7 +0,0 @@
-# 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/.
-
-MOZ_GLUE_LDFLAGS =
-
-include $(topsrcdir)/config/rules.mk
--- a/dom/svg/SVGRectElement.cpp
+++ b/dom/svg/SVGRectElement.cpp
@@ -109,39 +109,43 @@ SVGRectElement::GetLengthInfo()
 
 //----------------------------------------------------------------------
 // nsSVGPathGeometryElement methods
 
 bool
 SVGRectElement::GetGeometryBounds(Rect* aBounds, Float aStrokeWidth,
                                   const Matrix& aTransform)
 {
-  Rect r;
+  Rect rect;
   Float rx, ry;
-  GetAnimatedLengthValues(&r.x, &r.y, &r.width, &r.height, &rx, &ry, nullptr);
+  GetAnimatedLengthValues(&rect.x, &rect.y, &rect.width,
+                          &rect.height, &rx, &ry, nullptr);
 
-  if (r.IsEmpty()) {
+  if (rect.IsEmpty()) {
     // Rendering of the element disabled
-    r.SetEmpty(); // make sure width/height are actually zero
-    *aBounds = r;
+    rect.SetEmpty(); // Make sure width/height are zero and not negative
+    *aBounds = rect; // We still want the x/y position from 'rect'
     return true;
   }
 
-  rx = std::max(rx, 0.0f);
-  ry = std::max(ry, 0.0f);
+  if (!aTransform.IsRectilinear()) {
+    // We can't ignore the radii in this case if we want tight bounds
+    rx = std::max(rx, 0.0f);
+    ry = std::max(ry, 0.0f);
 
-  if (rx != 0 || ry != 0) {
-    return false;
+    if (rx != 0 || ry != 0) {
+      return false;
+    }
   }
 
   if (aStrokeWidth > 0.f) {
-    r.Inflate(aStrokeWidth / 2.f);
+    rect.Inflate(aStrokeWidth / 2.f);
   }
 
-  *aBounds = aTransform.TransformBounds(r);
+  *aBounds = aTransform.TransformBounds(rect);
   return true;
 }
 
 void
 SVGRectElement::GetAsSimplePath(SimplePath* aSimplePath)
 {
   float x, y, width, height, rx, ry;
   GetAnimatedLengthValues(&x, &y, &width, &height, &rx, &ry, nullptr);
@@ -195,18 +199,17 @@ SVGRectElement::BuildPath(PathBuilder* a
     } else if (hasRy && !hasRx) {
       rx = ry;
     }
 
     // Clamp rx and ry to half the rect's width and height respectively:
     rx = std::min(rx, width / 2);
     ry = std::min(ry, height / 2);
 
-    Size cornerRadii(rx, ry);
-    Size radii[] = { cornerRadii, cornerRadii, cornerRadii, cornerRadii };
+    RectCornerRadii radii(rx, ry);
     AppendRoundedRectToPath(aBuilder, Rect(x, y, width, height), radii);
   }
 
   return aBuilder->Finish();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -62,17 +62,17 @@ public:
   }
 };
 
 class Telephony::EnumerationAck : public nsRunnable
 {
   nsRefPtr<Telephony> mTelephony;
 
 public:
-  EnumerationAck(Telephony* aTelephony)
+  explicit EnumerationAck(Telephony* aTelephony)
   : mTelephony(aTelephony)
   {
     MOZ_ASSERT(mTelephony);
   }
 
   NS_IMETHOD Run()
   {
     mTelephony->NotifyEvent(NS_LITERAL_STRING("ready"));
--- a/dom/telephony/TelephonyCallback.h
+++ b/dom/telephony/TelephonyCallback.h
@@ -17,17 +17,17 @@ class Promise;
 namespace telephony {
 
 class TelephonyCallback : public nsITelephonyCallback
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITELEPHONYCALLBACK
 
-  TelephonyCallback(Promise* aPromise);
+  explicit TelephonyCallback(Promise* aPromise);
 
 protected:
   virtual ~TelephonyCallback() {}
 
 protected:
   nsRefPtr<Promise> mPromise;
 };
 
--- a/dom/webidl/CompositionEvent.webidl
+++ b/dom/webidl/CompositionEvent.webidl
@@ -4,26 +4,24 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html#events-CompositionEvent
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-interface WindowProxy;
-
 interface CompositionEvent : UIEvent
 {
   readonly attribute DOMString? data;
   readonly attribute DOMString  locale;
 };
 
 partial interface CompositionEvent
 {
   [Throws]
   void initCompositionEvent(DOMString typeArg,
                             boolean canBubbleArg,
                             boolean cancelableArg,
-                            WindowProxy? viewArg,
+                            Window? viewArg,
                             DOMString? dataArg,
                             DOMString localeArg);
 };
--- a/dom/webidl/DragEvent.webidl
+++ b/dom/webidl/DragEvent.webidl
@@ -1,25 +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/.
  */
 
-interface WindowProxy;
-
 interface DragEvent : MouseEvent
 {
   readonly attribute DataTransfer? dataTransfer;
 
   [Throws]
   void initDragEvent(DOMString type,
                      boolean canBubble,
                      boolean cancelable,
-                     WindowProxy? aView,
+                     Window? aView,
                      long aDetail,
                      long aScreenX,
                      long aScreenY,
                      long aClientX,
                      long aClientY,
                      boolean aCtrlKey,
                      boolean aAltKey,
                      boolean aShiftKey,
--- a/dom/webidl/KeyEvent.webidl
+++ b/dom/webidl/KeyEvent.webidl
@@ -1,16 +1,14 @@
 /* -*- 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/.
  */
 
-interface WindowProxy;
-
 // http://www.w3.org/TR/1999/WD-DOM-Level-2-19990923/events.html#Events-KeyEvent
 interface KeyEvent
 {
   const unsigned long DOM_VK_CANCEL         = 0x03;
   const unsigned long DOM_VK_HELP           = 0x06;
   const unsigned long DOM_VK_BACK_SPACE     = 0x08;
   const unsigned long DOM_VK_TAB            = 0x09;
   const unsigned long DOM_VK_CLEAR          = 0x0C;
@@ -225,16 +223,16 @@ interface KeyEvent
   // OEM specific virtual keyCode of Windows should pass through DOM keyCode
   // for compatibility with the other web browsers on Windows.
   const unsigned long DOM_VK_WIN_OEM_CLEAR  = 0xFE;
 
   [Throws]
   void initKeyEvent(DOMString type,
                     boolean canBubble,
                     boolean cancelable,
-                    WindowProxy? view,
+                    Window? view,
                     boolean ctrlKey,
                     boolean altKey,
                     boolean shiftKey,
                     boolean metaKey,
                     unsigned long keyCode,
                     unsigned long charCode);
 };
--- a/dom/webidl/KeyboardEvent.webidl
+++ b/dom/webidl/KeyboardEvent.webidl
@@ -1,16 +1,14 @@
 /* -*- 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/.
  */
 
-interface WindowProxy;
-
 [Constructor(DOMString typeArg, optional KeyboardEventInit keyboardEventInitDict)]
 interface KeyboardEvent : UIEvent
 {
   readonly attribute unsigned long    charCode;
   readonly attribute unsigned long    keyCode;
 
   readonly attribute boolean          altKey;
   readonly attribute boolean          ctrlKey;
--- a/dom/webidl/MessageEvent.webidl
+++ b/dom/webidl/MessageEvent.webidl
@@ -2,18 +2,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/.
  *
  * For more information on this interface, please see
  * http://www.whatwg.org/specs/web-apps/current-work/#messageevent
  */
 
-interface WindowProxy;
-
 [Constructor(DOMString type, optional MessageEventInit eventInitDict),
  Exposed=(Window,Worker,System)]
 interface MessageEvent : Event {
   /**
    * Custom data associated with this event.
    */
   [GetterThrows]
   readonly attribute any data;
@@ -44,11 +42,11 @@ interface MessageEvent : Event {
    */
   readonly attribute MessagePortList? ports;
 };
 
 dictionary MessageEventInit : EventInit {
   any data;
   DOMString origin;
   DOMString lastEventId;
-  (WindowProxy or MessagePort)? source = null;
+  (Window or MessagePort)? source = null;
   sequence<MessagePort>? ports;
 };
--- a/dom/webidl/MouseEvent.webidl
+++ b/dom/webidl/MouseEvent.webidl
@@ -23,17 +23,17 @@ interface MouseEvent : UIEvent {
   readonly attribute unsigned short buttons;
   readonly attribute EventTarget?   relatedTarget;
   readonly attribute DOMString?     region;
   // Deprecated in DOM Level 3:
   [Throws]
   void                              initMouseEvent(DOMString typeArg, 
                                                    boolean canBubbleArg, 
                                                    boolean cancelableArg, 
-                                                   WindowProxy? viewArg, 
+                                                   Window? viewArg,
                                                    long detailArg, 
                                                    long screenXArg, 
                                                    long screenYArg, 
                                                    long clientXArg, 
                                                    long clientYArg, 
                                                    boolean ctrlKeyArg, 
                                                    boolean altKeyArg, 
                                                    boolean shiftKeyArg, 
@@ -47,25 +47,17 @@ interface MouseEvent : UIEvent {
 
 // Event Constructor Syntax:
 [Constructor(DOMString typeArg, optional MouseEventInit mouseEventInitDict)]
 partial interface MouseEvent
 {
 };
 
 // Suggested initMouseEvent replacement initializer:
-dictionary MouseEventInit {
-  // Attributes from Event:
-  boolean        bubbles       = false;
-  boolean        cancelable    = false;
-
-  // Attributes from UIEvent:
-  WindowProxy?   view          = null;
-  long           detail        = 0;
-
+dictionary MouseEventInit : UIEventInit {
   // Attributes for MouseEvent:
   long           screenX       = 0;
   long           screenY       = 0;
   long           clientX       = 0;
   long           clientY       = 0;
   boolean        ctrlKey       = false;
   boolean        shiftKey      = false;
   boolean        altKey        = false;
@@ -95,17 +87,17 @@ partial interface MouseEvent
   const unsigned short    MOZ_SOURCE_KEYBOARD   = 6;
 
   readonly attribute unsigned short mozInputSource;
 
   [Throws]
   void                initNSMouseEvent(DOMString typeArg,
                                        boolean canBubbleArg,
                                        boolean cancelableArg,
-                                       WindowProxy? viewArg,
+                                       Window? viewArg,
                                        long detailArg,
                                        long screenXArg,
                                        long screenYArg,
                                        long clientXArg,
                                        long clientYArg,
                                        boolean ctrlKeyArg,
                                        boolean altKeyArg,
                                        boolean shiftKeyArg,
--- a/dom/webidl/MouseScrollEvent.webidl
+++ b/dom/webidl/MouseScrollEvent.webidl
@@ -1,28 +1,26 @@
 /* -*- 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/.
  */
 
-interface WindowProxy;
-
 interface MouseScrollEvent : MouseEvent
 {
   const long HORIZONTAL_AXIS = 1;
   const long VERTICAL_AXIS = 2;
 
   readonly attribute long axis;
 
   [Throws]
   void initMouseScrollEvent(DOMString type,
                             boolean canBubble,
                             boolean cancelable,
-                            WindowProxy? view,
+                            Window? view,
                             long detail,
                             long screenX,
                             long screenY,
                             long clientX,
                             long clientY,
                             boolean ctrlKey,
                             boolean altKey,
                             boolean shiftKey,
--- a/dom/webidl/ScrollAreaEvent.webidl
+++ b/dom/webidl/ScrollAreaEvent.webidl
@@ -1,26 +1,24 @@
 /* -*- 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/.
  */
 
-interface WindowProxy;
-
 interface ScrollAreaEvent : UIEvent
 {
   readonly attribute float x;
   readonly attribute float y;
   readonly attribute float width;
   readonly attribute float height;
 
   [Throws]
   void initScrollAreaEvent(DOMString type,
                            boolean canBubble,
                            boolean cancelable,
-                           WindowProxy? view,
+                           Window? view,
                            long detail,
                            float x,
                            float y,
                            float width,
                            float height);
 };
--- a/dom/webidl/SimpleGestureEvent.webidl
+++ b/dom/webidl/SimpleGestureEvent.webidl
@@ -1,18 +1,16 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  *
  * For more information see nsIDOMSimpleGestureEvent.idl.
  */
 
-interface WindowProxy;
-
 interface SimpleGestureEvent : MouseEvent
 {
   const unsigned long DIRECTION_UP = 1;
   const unsigned long DIRECTION_DOWN = 2;
   const unsigned long DIRECTION_LEFT = 4;
   const unsigned long DIRECTION_RIGHT = 8;
 
   const unsigned long ROTATION_COUNTERCLOCKWISE = 1;
@@ -25,17 +23,17 @@ interface SimpleGestureEvent : MouseEven
   readonly attribute double delta;
 
   readonly attribute unsigned long clickCount;
 
   [Throws]
   void initSimpleGestureEvent(DOMString typeArg,
                               boolean canBubbleArg,
                               boolean cancelableArg,
-                              WindowProxy? viewArg,
+                              Window? viewArg,
                               long detailArg,
                               long screenXArg,
                               long screenYArg,
                               long clientXArg,
                               long clientYArg,
                               boolean ctrlKeyArg,
                               boolean altKeyArg,
                               boolean shiftKeyArg,
--- a/dom/webidl/TimeEvent.webidl
+++ b/dom/webidl/TimeEvent.webidl
@@ -5,18 +5,16 @@
  *
  * For more information on this interface please see
  * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-interface WindowProxy;
-
 interface TimeEvent : Event
 {
   readonly attribute long         detail;
   readonly attribute WindowProxy? view;
   void initTimeEvent(DOMString aType,
-                     WindowProxy? aView,
+                     Window? aView,
                      long aDetail);
 };
--- a/dom/webidl/TouchEvent.webidl
+++ b/dom/webidl/TouchEvent.webidl
@@ -1,32 +1,30 @@
 /* -*- 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/.
  */
 
-interface WindowProxy;
-
 [Func="mozilla::dom::TouchEvent::PrefEnabled"]
 interface TouchEvent : UIEvent {
   readonly attribute TouchList touches;
   readonly attribute TouchList targetTouches;
   readonly attribute TouchList changedTouches;
 
   readonly attribute boolean altKey;
   readonly attribute boolean metaKey;
   readonly attribute boolean ctrlKey;
   readonly attribute boolean shiftKey;
 
   [Throws]
   void initTouchEvent(DOMString type,
                       boolean canBubble,
                       boolean cancelable,
-                      WindowProxy? view,
+                      Window? view,
                       long detail,
                       boolean ctrlKey,
                       boolean altKey,
                       boolean shiftKey,
                       boolean metaKey,
                       TouchList? touches,
                       TouchList? targetTouches,
                       TouchList? changedTouches);
--- a/dom/webidl/UIEvent.webidl
+++ b/dom/webidl/UIEvent.webidl
@@ -5,27 +5,25 @@
  *
  * For more information on this interface please see
  * http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-interface WindowProxy;
-
 [Constructor(DOMString type, optional UIEventInit eventInitDict)]
 interface UIEvent : Event
 {
   readonly attribute WindowProxy? view;
   readonly attribute long         detail;
   void initUIEvent(DOMString aType,
                    boolean aCanBubble,
                    boolean aCancelable,
-                   WindowProxy? aView,
+                   Window? aView,
                    long aDetail);
 };
 
 // Additional DOM0 properties.
 partial interface UIEvent {
   const long SCROLL_PAGE_UP = -32768;
   const long SCROLL_PAGE_DOWN = 32768;
 
@@ -37,11 +35,11 @@ partial interface UIEvent {
   readonly attribute Node?         rangeParent;
   readonly attribute long          rangeOffset;
            attribute boolean       cancelBubble;
   readonly attribute boolean       isChar;
 };
 
 dictionary UIEventInit : EventInit
 {
-  WindowProxy? view = null;
-  long         detail = 0;
+  Window? view = null;
+  long    detail = 0;
 };
--- a/dom/webidl/XULCommandEvent.webidl
+++ b/dom/webidl/XULCommandEvent.webidl
@@ -1,30 +1,28 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-interface WindowProxy;
-
 [Func="IsChromeOrXBL"]
 interface XULCommandEvent : UIEvent
 {
   readonly attribute boolean ctrlKey;
   readonly attribute boolean shiftKey;
   readonly attribute boolean altKey;
   readonly attribute boolean metaKey;
 
   readonly attribute Event? sourceEvent;
 
   [Throws]
   void initCommandEvent(DOMString type,
                         boolean canBubble,
                         boolean cancelable,
-                        WindowProxy? view,
+                        Window? view,
                         long detail,
                         boolean ctrlKey,
                         boolean altKey,
                         boolean shiftKey,
                         boolean metaKey,
                         Event? sourceEvent);
 };
--- a/dom/workers/WorkerDebuggerManager.cpp
+++ b/dom/workers/WorkerDebuggerManager.cpp
@@ -44,17 +44,17 @@ NS_IMPL_ISUPPORTS(RegisterDebuggerRunnab
 BEGIN_WORKERS_NAMESPACE
 
 class WorkerDebuggerEnumerator MOZ_FINAL : public nsISimpleEnumerator
 {
   nsTArray<nsCOMPtr<nsISupports>> mDebuggers;
   uint32_t mIndex;
 
 public:
-  WorkerDebuggerEnumerator(const nsTArray<WorkerDebugger*>& aDebuggers)
+  explicit WorkerDebuggerEnumerator(const nsTArray<WorkerDebugger*>& aDebuggers)
   : mIndex(0)
   {
     mDebuggers.AppendElements(aDebuggers);
   }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISIMPLEENUMERATOR
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -2176,18 +2176,16 @@ WorkerPrivateParent<Derived>::DisableDeb
 
   if (!self->mDebugger) {
     return;
   }
 
   if (NS_FAILED(UnregisterWorkerDebugger(self->mDebugger))) {
     NS_WARNING("Failed to unregister worker debugger!");
   }
-
-  self->mDebugger = nullptr;
 }
 
 template <class Derived>
 nsresult
 WorkerPrivateParent<Derived>::DispatchControlRunnable(
                                   WorkerControlRunnable* aWorkerControlRunnable)
 {
   // May be called on any thread!
@@ -3499,46 +3497,163 @@ WorkerDebugger::WorkerDebugger(WorkerPri
   mIsEnabled(false)
 {
   mWorkerPrivate->AssertIsOnParentThread();
 }
 
 WorkerDebugger::~WorkerDebugger()
 {
   MOZ_ASSERT(!mWorkerPrivate);
+  MOZ_ASSERT(!mIsEnabled);
+
+  if (!NS_IsMainThread()) {
+    nsCOMPtr<nsIThread> mainThread;
+    if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread)))) {
+      NS_WARNING("Failed to proxy release of listeners, leaking instead!");
+    }
+
+    for (size_t index = 0; index < mListeners.Length(); ++index) {
+      nsIWorkerDebuggerListener* listener = nullptr;
+      mListeners[index].forget(&listener);
+      if (NS_FAILED(NS_ProxyRelease(mainThread, listener))) {
+        NS_WARNING("Failed to proxy release of listener, leaking instead!");
+      }
+    }
+  }
 }
 
 NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger)
 
 NS_IMETHODIMP
 WorkerDebugger::GetIsClosed(bool* aResult)
 {
   AssertIsOnMainThread();
 
   MutexAutoLock lock(mMutex);
 
   *aResult = !mWorkerPrivate;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+WorkerDebugger::GetIsChrome(bool* aResult)
+{
+  AssertIsOnMainThread();
+
+  MutexAutoLock lock(mMutex);
+
+  if (!mWorkerPrivate) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  *aResult = mWorkerPrivate->IsChromeWorker();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WorkerDebugger::GetParent(nsIWorkerDebugger** aResult)
+{
+  AssertIsOnMainThread();
+
+  MutexAutoLock lock(mMutex);
+
+  if (!mWorkerPrivate) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  WorkerPrivate* parent = mWorkerPrivate->GetParent();
+  if (!parent) {
+    *aResult = nullptr;
+    return NS_OK;
+  }
+
+  MOZ_ASSERT(mWorkerPrivate->IsDedicatedWorker());
+
+  nsCOMPtr<nsIWorkerDebugger> debugger = parent->Debugger();
+  debugger.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WorkerDebugger::GetType(uint32_t* aResult)
+{
+  AssertIsOnMainThread();
+
+  MutexAutoLock lock(mMutex);
+
+  if (!mWorkerPrivate) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  *aResult = mWorkerPrivate->Type();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 WorkerDebugger::GetUrl(nsAString& aResult)
 {
   AssertIsOnMainThread();
 
   MutexAutoLock lock(mMutex);
 
   if (!mWorkerPrivate) {
     return NS_ERROR_UNEXPECTED;
   }
 
   aResult = mWorkerPrivate->ScriptURL();
   return NS_OK;
 }
 
+NS_IMETHODIMP
+WorkerDebugger::GetWindow(nsIDOMWindow** aResult)
+{
+  AssertIsOnMainThread();
+
+  MutexAutoLock lock(mMutex);
+
+  if (!mWorkerPrivate) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  if (mWorkerPrivate->GetParent() || !mWorkerPrivate->IsDedicatedWorker()) {
+    *aResult = nullptr;
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> window = mWorkerPrivate->GetWindow();
+  window.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WorkerDebugger::AddListener(nsIWorkerDebuggerListener* aListener)
+{
+  AssertIsOnMainThread();
+
+  if (mListeners.Contains(aListener)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mListeners.AppendElement(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WorkerDebugger::RemoveListener(nsIWorkerDebuggerListener* aListener)
+{
+  AssertIsOnMainThread();
+
+  if (!mListeners.Contains(aListener)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mListeners.RemoveElement(aListener);
+  return NS_OK;
+}
+
 void
 WorkerDebugger::WaitIsEnabled(bool aIsEnabled)
 {
   MutexAutoLock lock(mMutex);
 
   while (mIsEnabled != aIsEnabled) {
     mCondVar.Wait();
   }
@@ -3571,16 +3686,25 @@ WorkerDebugger::Disable()
 {
   AssertIsOnMainThread();
 
   MutexAutoLock lock(mMutex);
 
   MOZ_ASSERT(mWorkerPrivate);
   mWorkerPrivate = nullptr;
 
+  {
+    MutexAutoUnlock unlock(mMutex);
+
+    nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> listeners(mListeners);
+    for (size_t index = 0; index < listeners.Length(); ++index) {
+      listeners[index]->OnClose();
+    }
+  }
+
   NotifyIsEnabled(false);
 }
 
 WorkerPrivate::WorkerPrivate(JSContext* aCx,
                              WorkerPrivate* aParent,
                              const nsAString& aScriptURL,
                              bool aIsChromeWorker, WorkerType aWorkerType,
                              const nsACString& aSharedWorkerName,
--- a/dom/workers/WorkerPrivate.h
+++ b/dom/workers/WorkerPrivate.h
@@ -59,16 +59,18 @@ class AutoSyncLoopHolder;
 class MessagePort;
 class SharedWorker;
 class WorkerControlRunnable;
 class WorkerGlobalScope;
 class WorkerPrivate;
 class WorkerRunnable;
 class WorkerDebugger;
 
+// If you change this, the corresponding list in nsIWorkerDebugger.idl needs to
+// be updated too.
 enum WorkerType
 {
   WorkerTypeDedicated,
   WorkerTypeShared,
   WorkerTypeService
 };
 
 // SharedMutex is a small wrapper around an (internal) reference-counted Mutex
@@ -658,16 +660,22 @@ public:
   // The ability to be a chrome worker is orthogonal to the type of
   // worker [Dedicated|Shared|Service].
   bool
   IsChromeWorker() const
   {
     return mIsChromeWorker;
   }
 
+  WorkerType
+  Type() const
+  {
+    return mWorkerType;
+  }
+
   bool
   IsDedicatedWorker() const
   {
     return mWorkerType == WorkerTypeDedicated;
   }
 
   bool
   IsSharedWorker() const
@@ -732,18 +740,21 @@ public:
 class WorkerDebugger : public nsIWorkerDebugger {
   mozilla::Mutex mMutex;
   mozilla::CondVar mCondVar;
 
   // Protected by mMutex
   WorkerPrivate* mWorkerPrivate;
   bool mIsEnabled;
 
+  // Only touched on the main thread.
+  nsTArray<nsCOMPtr<nsIWorkerDebuggerListener>> mListeners;
+
 public:
-  WorkerDebugger(WorkerPrivate* aWorkerPrivate);
+  explicit WorkerDebugger(WorkerPrivate* aWorkerPrivate);
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIWORKERDEBUGGER
 
   void AssertIsOnParentThread();
 
   void WaitIsEnabled(bool aIsEnabled);
 
@@ -863,16 +874,24 @@ public:
   static bool
   WorkerAvailable(JSContext* /* unused */, JSObject* /* unused */);
 
   static nsresult
   GetLoadInfo(JSContext* aCx, nsPIDOMWindow* aWindow, WorkerPrivate* aParent,
               const nsAString& aScriptURL, bool aIsChromeWorker,
               LoadInfo* aLoadInfo);
 
+  WorkerDebugger*
+  Debugger() const
+  {
+    AssertIsOnMainThread();
+    MOZ_ASSERT(mDebugger);
+    return mDebugger;
+  }
+
   void
   DoRunLoop(JSContext* aCx);
 
   bool
   InterruptCallback(JSContext* aCx);
 
   nsresult
   IsOnCurrentThread(bool* aIsOnCurrentThread);
--- a/dom/workers/nsIWorkerDebugger.idl
+++ b/dom/workers/nsIWorkerDebugger.idl
@@ -1,9 +1,33 @@
 #include "nsISupports.idl"
 
+interface nsIDOMWindow;
+
+[scriptable, uuid(54fd2dd3-c01b-4f71-888f-462f37a54f57)]
+interface nsIWorkerDebuggerListener : nsISupports
+{
+  void onClose();
+};
+
 [scriptable, builtinclass, uuid(0833b363-bffe-4cdb-ad50-1c4563e0C8ff)]
 interface nsIWorkerDebugger : nsISupports
 {
+  const unsigned long TYPE_DEDICATED = 0;
+  const unsigned long TYPE_SHARED = 1;
+  const unsigned long TYPE_SERVICE = 2;
+
   readonly attribute bool isClosed;
 
+  readonly attribute bool isChrome;
+
+  readonly attribute nsIWorkerDebugger parent;
+
+  readonly attribute unsigned long type;
+
   readonly attribute DOMString url;
+
+  readonly attribute nsIDOMWindow window;
+
+  void addListener(in nsIWorkerDebuggerListener listener);
+
+  void removeListener(in nsIWorkerDebuggerListener listener);
 };
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger_childWorker.js
@@ -0,0 +1,3 @@
+"use strict";
+
+onmessage = function () {};
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger_parentWorker.js
@@ -0,0 +1,3 @@
+"use strict";
+
+var worker = new Worker("WorkerDebugger_childWorker.js");
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/WorkerDebugger_sharedWorker.js
@@ -0,0 +1,11 @@
+"use strict";
+
+onconnect = function (event) {
+  event.ports[0].onmessage = function (event) {
+    switch (event.data) {
+    case "close":
+      close();
+      break;
+    }
+  };
+};
--- a/dom/workers/test/chrome.ini
+++ b/dom/workers/test/chrome.ini
@@ -1,12 +1,15 @@
 [DEFAULT]
 support-files =
   WorkerDebuggerManager_childWorker.js
   WorkerDebuggerManager_parentWorker.js
+  WorkerDebugger_childWorker.js
+  WorkerDebugger_parentWorker.js
+  WorkerDebugger_sharedWorker.js
   WorkerTest.jsm
   WorkerTest_subworker.js
   WorkerTest_worker.js
   chromeWorker_subworker.js
   chromeWorker_worker.js
   dom_worker_helper.js
   file_url.jsm
   file_worker.js
@@ -19,16 +22,17 @@ support-files =
   fileSlice_worker.js
   fileSubWorker_worker.js
   file_worker.js
   jsm_url_worker.js
   workersDisabled_worker.js
   file_url.jsm
   bug1062920_worker.js
 
+[test_WorkerDebugger.xul]
 [test_WorkerDebuggerManager.xul]
 [test_bug883784.jsm]
 [test_bug883784.xul]
 [test_chromeWorker.xul]
 [test_chromeWorkerJSM.xul]
 [test_extension.xul]
 [test_extensionBootstrap.xul]
 [test_file.xul]
--- a/dom/workers/test/dom_worker_helper.js
+++ b/dom/workers/test/dom_worker_helper.js
@@ -79,16 +79,30 @@ function waitForUnregister(predicate = (
         }
         wdm.removeListener(this);
         resolve(dbg);
       }
     });
   });
 }
 
+function waitForDebuggerClose(dbg, predicate = () => true) {
+  return new Promise(function (resolve) {
+    dbg.addListener({
+      onClose: function () {
+        if (!predicate()) {
+          return;
+        }
+        dbg.removeListener(this);
+        resolve();
+      }
+    });
+  });
+}
+
 function waitForMultiple(promises) {
   return new Promise(function (resolve) {
     let results = [];
     for (let i = 0; i < promises.length; ++i) {
       let promise = promises[i];
       let index = i;
       promise.then(function (result) {
         is(results.length, index, "events should occur in the specified order");
--- a/dom/workers/test/mochitest.ini
+++ b/dom/workers/test/mochitest.ini
@@ -83,16 +83,17 @@ support-files =
   subdir/relativeLoad_sub_worker2.js
   subdir/relativeLoad_sub_import.js
   test_worker_interfaces.js
   test_worker_performance_now.js
   worker_driver.js
   worker_wrapper.js
   bug1060621_worker.js
   bug1062920_worker.js
+  webSocket_sharedWorker.js
 
 [test_404.html]
 [test_atob.html]
 [test_blobConstructor.html]
 [test_blobWorkers.html]
 [test_bug1002702.html]
 [test_bug949946.html]
 [test_bug1010784.html]
@@ -180,8 +181,10 @@ skip-if = (os == "win") || (os == "mac")
 [test_bug1060621.html]
 [test_websocket_basic.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828
 [test_websocket.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828
 [test_websocket_loadgroup.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828
 [test_bug1062920.html]
+[test_webSocket_sharedWorker.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s #bug 982828
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_WorkerDebugger.xul
@@ -0,0 +1,98 @@
+<?xml version="1.0"?>
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<window title="Test for WorkerDebugger"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="test();">
+
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+  <script type="application/javascript" src="dom_worker_helper.js"/>
+
+  <script type="application/javascript">
+  <![CDATA[
+
+    const PARENT_WORKER_URL = "WorkerDebugger_parentWorker.js";
+    const CHILD_WORKER_URL = "WorkerDebugger_childWorker.js";
+    const SHARED_WORKER_URL = "WorkerDebugger_sharedWorker.js";
+
+    function test() {
+      Task.spawn(function* () {
+        SimpleTest.waitForExplicitFinish();
+
+        let promise = waitForMultiple([
+          waitForRegister((dbg) => dbg.url === PARENT_WORKER_URL),
+          waitForRegister((dbg) => dbg.url === CHILD_WORKER_URL),
+        ]);
+        worker = new ChromeWorker(PARENT_WORKER_URL);
+        let dbgs = yield promise;
+        is(dbgs[0].isChrome, true, "debugger should be for chrome worker");
+        is(dbgs[0].parent, null,
+           "debugger for a top-level worker should not have parent");
+        is(dbgs[0].type, Ci.nsIWorkerDebugger.TYPE_DEDICATED,
+           "debugger should be for dedicated worker");
+        is(dbgs[0].window, window,
+           "debugger for top-level dedicated worker should have window");
+        is(dbgs[1].isChrome, false, "debugger should be for content worker");
+        is(dbgs[1].parent, dbgs[0],
+           "debugger for child worker should have parent");
+        is(dbgs[1].type, Ci.nsIWorkerDebugger.TYPE_DEDICATED);
+        is(dbgs[1].window, null,
+           "debugger for non-top-level worker should not have window");
+
+        promise = waitForMultiple([
+          waitForUnregister((dbg) => dbg.url === CHILD_WORKER_URL),
+          waitForDebuggerClose(dbgs[1]),
+          waitForUnregister((dbg) => dbg.url === PARENT_WORKER_URL),
+          waitForDebuggerClose(dbgs[0]),
+        ]);
+        worker.terminate();
+        yield promise;
+
+        promise = waitForRegister();
+        worker = new SharedWorker(SHARED_WORKER_URL);
+        let dbg = yield promise;
+        is(dbg.isChrome, false, "debugger should be for content worker");
+        is(dbg.parent, null,
+           "debugger for top-level worker should not have parent");
+        is(dbg.type, Ci.nsIWorkerDebugger.TYPE_SHARED,
+           "debugger should be for shared worker");
+        is(dbg.window, null,
+           "debugger for non-dedicated worker should not have window");
+
+        let listener = {
+          onRegistered: function () {
+            ok(false,
+               "debugger for shared worker should not be registered twice");
+          },
+        };
+        wdm.addListener(listener);
+        worker = new SharedWorker(SHARED_WORKER_URL);
+
+        dbg.addListener({
+          onClose: function () {
+            is(dbg.isClosed, true, "debugger should be closed");
+            wdm.removeListener(listener);
+            dbg.removeListener(this);
+            SimpleTest.finish();
+          }
+        });
+        worker.port.start();
+        worker.port.postMessage("close");
+      });
+    }
+
+  ]]>
+  </script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <p id="display"></p>
+    <div id="content" style="display:none;"></div>
+    <pre id="test"></pre>
+  </body>
+  <label id="test-result"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_webSocket_sharedWorker.html
@@ -0,0 +1,32 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for bug 1090183</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<script class="testbody" type="text/javascript">
+
+SpecialPowers.pushPrefEnv({ set: [["dom.workers.sharedWorkers.enabled", true]] }, function() {
+  var sw = new SharedWorker('webSocket_sharedWorker.js');
+  sw.port.onmessage = function(event) {
+    if (event.data.type == 'finish') {
+      SimpleTest.finish();
+    } else if (event.data.type == 'status') {
+      ok(event.data.status, event.data.msg);
+    }
+  }
+});
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/webSocket_sharedWorker.js
@@ -0,0 +1,20 @@
+onconnect = function(evt) {
+  var ws = new WebSocket("ws://mochi.test:8888/tests/dom/base/test/file_websocket_hello");
+
+  ws.onopen = function(e) {
+    evt.ports[0].postMessage({type: 'status', status: true, msg: 'OnOpen called' });
+    ws.send("data");
+  }
+
+  ws.onclose = function(e) {}
+
+  ws.onerror = function(e) {
+    evt.ports[0].postMessage({type: 'status', status: false, msg: 'onerror called!'});
+  }
+
+  ws.onmessage = function(e) {
+    evt.ports[0].postMessage({type: 'status', status: e.data == 'Hello world!', msg: 'Wrong data'});
+    ws.close();
+    evt.ports[0].postMessage({type: 'finish' });
+  }
+}
--- a/dom/xbl/nsXBLBinding.cpp
+++ b/dom/xbl/nsXBLBinding.cpp
@@ -883,17 +883,17 @@ GetOrCreateClassObjectMap(JSContext *cx,
       JS::IsWeakMapObject(&desc.value().toObject())) {
     return &desc.value().toObject();
   }
 
   // It's not there. Create and define it.
   JS::Rooted<JSObject*> map(cx, JS::NewWeakMapObject(cx));
   if (!map || !JS_DefineProperty(cx, scope, mapName, map,
                                  JSPROP_PERMANENT | JSPROP_READONLY,
-                                 JS_PropertyStub, JS_StrictPropertyStub))
+                                 JS_STUBGETTER, JS_STUBSETTER))
   {
     return nullptr;
   }
   return map;
 }
 
 static JSObject*
 GetOrCreateMapEntryForPrototype(JSContext *cx, JS::Handle<JSObject*> proto)
@@ -1032,17 +1032,17 @@ nsXBLBinding::DoInitJSClass(JSContext *c
     JS_SetReservedSlot(proto, 0, PRIVATE_TO_JSVAL(aProtoBinding));
 
     // Next, enter the compartment of the property holder, wrap the proto, and
     // stick it on.
     JSAutoCompartment ac3(cx, holder);
     if (!JS_WrapObject(cx, &proto) ||
         !JS_DefineProperty(cx, holder, aClassName.get(), proto,
                            JSPROP_READONLY | JSPROP_PERMANENT,
-                           JS_PropertyStub, JS_StrictPropertyStub))
+                           JS_STUBGETTER, JS_STUBSETTER))
     {
       return NS_ERROR_OUT_OF_MEMORY;
     }
   }
 
   // Whew. We have the proto. Wrap it back into the compartment of |obj|,
   // splice it in, and return it.
   JSAutoCompartment ac4(cx, obj);
--- a/dom/xbl/nsXBLProtoImpl.cpp
+++ b/dom/xbl/nsXBLProtoImpl.cpp
@@ -114,17 +114,17 @@ nsXBLProtoImpl::InstallImplementation(ns
     // This is just a property holder, so it doesn't need any special JSClass.
     propertyHolder = JS_NewObjectWithGivenProto(cx, nullptr, JS::NullPtr(), scopeObject);
     NS_ENSURE_TRUE(propertyHolder, NS_ERROR_OUT_OF_MEMORY);
 
     // Define it as a property on the scopeObject, using the same name used on
     // the content side.
     bool ok = JS_DefineProperty(cx, scopeObject, className, propertyHolder,
                                 JSPROP_PERMANENT | JSPROP_READONLY,
-                                JS_PropertyStub, JS_StrictPropertyStub);
+                                JS_STUBGETTER, JS_STUBSETTER);
     NS_ENSURE_TRUE(ok, NS_ERROR_UNEXPECTED);
   } else {
     propertyHolder = targetClassObject;
   }
 
   // Walk our member list and install each one in turn on the XBL scope object.
   if (propertyHolderIsNew) {
     for (nsXBLProtoImplMember* curr = mMembers;
--- a/dom/xbl/nsXBLProtoImplField.cpp
+++ b/dom/xbl/nsXBLProtoImplField.cpp
@@ -362,18 +362,18 @@ nsXBLProtoImplField::InstallAccessors(JS
   // them there.
   JSAutoCompartment ac2(aCx, aTargetClassObject);
   if (!JS_WrapObject(aCx, &get) || !JS_WrapObject(aCx, &set)) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   if (!::JS_DefinePropertyById(aCx, aTargetClassObject, id, JS::UndefinedHandleValue,
                                AccessorAttributes(),
-                               JS_DATA_TO_FUNC_PTR(JSPropertyOp, get.get()),
-                               JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, set.get()))) {
+                               JS_DATA_TO_FUNC_PTR(JSNative, get.get()),
+                               JS_DATA_TO_FUNC_PTR(JSNative, set.get()))) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   return NS_OK;
 }
 
 nsresult
 nsXBLProtoImplField::InstallField(JS::Handle<JSObject*> aBoundNode,
--- a/dom/xbl/nsXBLProtoImplProperty.cpp
+++ b/dom/xbl/nsXBLProtoImplProperty.cpp
@@ -145,18 +145,18 @@ nsXBLProtoImplProperty::InstallMember(JS
       if (!(setter = ::JS_CloneFunctionObject(aCx, setter, globalObject)))
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     nsDependentString name(mName);
     if (!::JS_DefineUCProperty(aCx, aTargetClassObject,
                                static_cast<const char16_t*>(mName),
                                name.Length(), JS::UndefinedHandleValue, mJSAttributes,
-                               JS_DATA_TO_FUNC_PTR(JSPropertyOp, getter.get()),
-                               JS_DATA_TO_FUNC_PTR(JSStrictPropertyOp, setter.get())))
+                               JS_DATA_TO_FUNC_PTR(JSNative, getter.get()),
+                               JS_DATA_TO_FUNC_PTR(JSNative, setter.get())))
       return NS_ERROR_OUT_OF_MEMORY;
   }
   return NS_OK;
 }
 
 nsresult
 nsXBLProtoImplProperty::CompileMember(AutoJSAPI& jsapi, const nsCString& aClassStr,
                                       JS::Handle<JSObject*> aClassObject)
--- a/dom/xslt/xslt/txMozillaXSLTProcessor.h
+++ b/dom/xslt/xslt/txMozillaXSLTProcessor.h
@@ -151,17 +151,17 @@ public:
     {
         return (mFlags & DISABLE_ALL_LOADS) != 0;
     }
 
     static nsresult Startup();
     static void Shutdown();
 
 private:
-    txMozillaXSLTProcessor(nsISupports* aOwner);
+    explicit txMozillaXSLTProcessor(nsISupports* aOwner);
     /**
      * Default destructor for txMozillaXSLTProcessor
      */
     ~txMozillaXSLTProcessor();
 
     nsresult DoTransform();
     void notifyError();
     nsresult ensureStylesheet();
--- a/editor/txmgr/tests/moz.build
+++ b/editor/txmgr/tests/moz.build
@@ -1,18 +1,11 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-CppUnitTests([
+GeckoCppUnitTests([
     'TestTXMgr',
 ])
 
 FAIL_ON_WARNINGS = True
-
-USE_LIBS += [
-    'mozalloc',
-    'nspr',
-    'xpcomglue_s',
-    'xul',
-]
--- a/embedding/tests/winEmbed/moz.build
+++ b/embedding/tests/winEmbed/moz.build
@@ -1,26 +1,24 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # 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/.
 
-Program('winEmbed')
+GeckoProgram('winEmbed')
 
 SOURCES += [
     'WebBrowserChrome.cpp',
     'WindowCreator.cpp',
     'winEmbed.cpp',
 ]
 
 XPI_NAME = 'winembed'
 
-DEFINES['XPCOM_GLUE'] = True
-
 RESFILE = 'winEmbed.res'
 
 if CONFIG['GNU_CC']:
     # Get rid of console window
     LDFLAGS += ['-mwindows']
 else:
     # Control the default heap size.
     # This is the heap returned by GetProcessHeap().
@@ -31,17 +29,16 @@ else:
     #
     # Set it to 256k.  See bug 127069.
     LDFLAGS += ['/HEAP:0x40000']
 
 DISABLE_STL_WRAPPING = True
 
 USE_LIBS += [
     'profdirserviceprovidersa_s',
-    'xpcomglue',
 ]
 
 OS_LIBS += [
     'ole32',
     'comdlg32',
     'shell32',
     'version',
 ]
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -1206,44 +1206,50 @@ DrawTargetD2D1::CreateBrushForPattern(co
 
     if (!pat->mSurface) {
       gfxDebug() << "No source surface specified for surface pattern";
       return CreateTransparentBlackBrush();
     }
 
     D2D1_RECT_F samplingBounds;
     Matrix mat = pat->mMatrix;
-    if (!pat->mSamplingRect.IsEmpty()) {
+
+    bool useSamplingRect = false;
+    if (!pat->mSamplingRect.IsEmpty() &&
+        (pat->mSurface->GetType() == SurfaceType::D2D1_1_IMAGE)) {
       samplingBounds = D2DRect(pat->mSamplingRect);
       mat.PreTranslate(pat->mSamplingRect.x, pat->mSamplingRect.y);
+    } else if (!pat->mSamplingRect.IsEmpty()) {
+      // We will do a partial upload of the sampling restricted area from GetImageForSurface.
+      samplingBounds = D2D1::RectF(0, 0, pat->mSamplingRect.width, pat->mSamplingRect.height);
     } else {
       samplingBounds = D2D1::RectF(0, 0,
                                    Float(pat->mSurface->GetSize().width),
                                    Float(pat->mSurface->GetSize().height));
     }
 
     RefPtr<ID2D1ImageBrush> imageBrush;
-    RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode);
+    RefPtr<ID2D1Image> image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode, !pat->mSamplingRect.IsEmpty() ? &pat->mSamplingRect : nullptr);
     mDC->CreateImageBrush(image,
                           D2D1::ImageBrushProperties(samplingBounds,
                                                      D2DExtend(pat->mExtendMode),
                                                      D2DExtend(pat->mExtendMode),
                                                      D2DInterpolationMode(pat->mFilter)),
                           D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
                           byRef(imageBrush));
     return imageBrush.forget();
   }
 
   gfxWarning() << "Invalid pattern type detected.";
   return CreateTransparentBlackBrush();
 }
 
 TemporaryRef<ID2D1Image>
 DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
-                                   ExtendMode aExtendMode)
+                                   ExtendMode aExtendMode, const IntRect* aSourceRect)
 {
   RefPtr<ID2D1Image> image;
 
   switch (aSurface->GetType()) {
   case SurfaceType::D2D1_1_IMAGE:
     {
       SourceSurfaceD2D1 *surf = static_cast<SourceSurfaceD2D1*>(aSurface);
       image = surf->GetImage();
@@ -1253,17 +1259,17 @@ DrawTargetD2D1::GetImageForSurface(Sourc
   default:
     {
       RefPtr<DataSourceSurface> dataSurf = aSurface->GetDataSurface();
       if (!dataSurf) {
         gfxWarning() << "Invalid surface type.";
         return nullptr;
       }
       return CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, aExtendMode,
-                                           aSourceTransform, mDC);
+                                           aSourceTransform, mDC, aSourceRect);
     }
     break;
   }
 
   return image.forget();
 }
 
 TemporaryRef<SourceSurface>
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -124,21 +124,21 @@ public:
 
   virtual void *GetNativeSurface(NativeSurfaceType aType) { return nullptr; }
 
   bool Init(const IntSize &aSize, SurfaceFormat aFormat);
   bool Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat);
   uint32_t GetByteSize() const;
 
   TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform,
-                                              ExtendMode aExtendMode);
+                                              ExtendMode aExtendMode, const IntRect* aSourceRect = nullptr);
 
   TemporaryRef<ID2D1Image> GetImageForSurface(SourceSurface *aSurface, ExtendMode aExtendMode) {
     Matrix mat;
-    return GetImageForSurface(aSurface, mat, aExtendMode);
+    return GetImageForSurface(aSurface, mat, aExtendMode, nullptr);
   }
 
   static ID2D1Factory1 *factory();
   static void CleanupD2D();
   static IDWriteFactory *GetDWriteFactory();
 
   operator std::string() const {
     std::stringstream stream;
--- a/gfx/2d/PathHelpers.cpp
+++ b/gfx/2d/PathHelpers.cpp
@@ -6,20 +6,38 @@
 #include "PathHelpers.h"
 
 namespace mozilla {
 namespace gfx {
 
 UserDataKey sDisablePixelSnapping;
 
 void
+AppendRectToPath(PathBuilder* aPathBuilder,
+                 const Rect& aRect,
+                 bool aDrawClockwise)
+{
+  if (aDrawClockwise) {
+    aPathBuilder->MoveTo(aRect.TopLeft());
+    aPathBuilder->LineTo(aRect.TopRight());
+    aPathBuilder->LineTo(aRect.BottomRight());
+    aPathBuilder->LineTo(aRect.BottomLeft());
+  } else {
+    aPathBuilder->MoveTo(aRect.TopRight());
+    aPathBuilder->LineTo(aRect.TopLeft());
+    aPathBuilder->LineTo(aRect.BottomLeft());
+    aPathBuilder->LineTo(aRect.BottomRight());
+  }
+  aPathBuilder->Close();
+}
+
+void
 AppendRoundedRectToPath(PathBuilder* aPathBuilder,
                         const Rect& aRect,
-                        // paren's needed due to operator precedence:
-                        const Size(& aCornerRadii)[4],
+                        const RectCornerRadii& aRadii,
                         bool aDrawClockwise)
 {
   // For CW drawing, this looks like:
   //
   //  ...******0**      1    C
   //              ****
   //                  ***    2
   //                     **
@@ -101,70 +119,67 @@ AppendRoundedRectToPath(PathBuilder* aPa
 
   twoFloats *cornerMults = aDrawClockwise ? cwCornerMults : ccwCornerMults;
 
   Point cornerCoords[] = { aRect.TopLeft(), aRect.TopRight(),
                            aRect.BottomRight(), aRect.BottomLeft() };
 
   Point pc, p0, p1, p2, p3;
 
-  // The indexes of the corners:
-  const int kTopLeft = 0, kTopRight = 1;
-
   if (aDrawClockwise) {
-    aPathBuilder->MoveTo(Point(aRect.X() + aCornerRadii[kTopLeft].width,
+    aPathBuilder->MoveTo(Point(aRect.X() + aRadii[RectCorner::TopLeft].width,
                                aRect.Y()));
   } else {
-    aPathBuilder->MoveTo(Point(aRect.X() + aRect.Width() - aCornerRadii[kTopRight].width,
+    aPathBuilder->MoveTo(Point(aRect.X() + aRect.Width() - aRadii[RectCorner::TopRight].width,
                                aRect.Y()));
   }
 
   for (int i = 0; i < 4; ++i) {
     // the corner index -- either 1 2 3 0 (cw) or 0 3 2 1 (ccw)
     int c = aDrawClockwise ? ((i+1) % 4) : ((4-i) % 4);
 
     // i+2 and i+3 respectively.  These are used to index into the corner
     // multiplier table, and were deduced by calculating out the long form
     // of each corner and finding a pattern in the signs and values.
     int i2 = (i+2) % 4;
     int i3 = (i+3) % 4;
 
     pc = cornerCoords[c];
 
-    if (aCornerRadii[c].width > 0.0 && aCornerRadii[c].height > 0.0) {
-      p0.x = pc.x + cornerMults[i].a * aCornerRadii[c].width;
-      p0.y = pc.y + cornerMults[i].b * aCornerRadii[c].height;
+    if (aRadii[c].width > 0.0 && aRadii[c].height > 0.0) {
+      p0.x = pc.x + cornerMults[i].a * aRadii[c].width;
+      p0.y = pc.y + cornerMults[i].b * aRadii[c].height;
 
-      p3.x = pc.x + cornerMults[i3].a * aCornerRadii[c].width;
-      p3.y = pc.y + cornerMults[i3].b * aCornerRadii[c].height;
+      p3.x = pc.x + cornerMults[i3].a * aRadii[c].width;
+      p3.y = pc.y + cornerMults[i3].b * aRadii[c].height;
 
-      p1.x = p0.x + alpha * cornerMults[i2].a * aCornerRadii[c].width;
-      p1.y = p0.y + alpha * cornerMults[i2].b * aCornerRadii[c].height;
+      p1.x = p0.x + alpha * cornerMults[i2].a * aRadii[c].width;
+      p1.y = p0.y + alpha * cornerMults[i2].b * aRadii[c].height;
 
-      p2.x = p3.x - alpha * cornerMults[i3].a * aCornerRadii[c].width;
-      p2.y = p3.y - alpha * cornerMults[i3].b * aCornerRadii[c].height;
+      p2.x = p3.x - alpha * cornerMults[i3].a * aRadii[c].width;
+      p2.y = p3.y - alpha * cornerMults[i3].b * aRadii[c].height;
 
       aPathBuilder->LineTo(p0);
       aPathBuilder->BezierTo(p1, p2, p3);
     } else {
       aPathBuilder->LineTo(pc);
     }
   }
 
   aPathBuilder->Close();
 }
 
 void
 AppendEllipseToPath(PathBuilder* aPathBuilder,
                     const Point& aCenter,
                     const Size& aDimensions)
 {
-  Size halfDim = aDimensions / 2.0;
+  Size halfDim = aDimensions / 2.f;
   Rect rect(aCenter - Point(halfDim.width, halfDim.height), aDimensions);
-  Size radii[] = { halfDim, halfDim, halfDim, halfDim };
+  RectCornerRadii radii(halfDim.width, halfDim.height);
 
   AppendRoundedRectToPath(aPathBuilder, rect, radii);
 }
 
 bool
 SnapLineToDevicePixelsForStroking(Point& aP1, Point& aP2,
                                   const DrawTarget& aDrawTarget)
 {
--- a/gfx/2d/PathHelpers.h
+++ b/gfx/2d/PathHelpers.h
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_PATHHELPERS_H_
 #define MOZILLA_GFX_PATHHELPERS_H_
 
 #include "2D.h"
 #include "mozilla/Constants.h"
+#include "mozilla/TypedEnum.h"
 #include "UserData.h"
 
 namespace mozilla {
 namespace gfx {
 
 template <typename T>
 void ArcToBezier(T* aSink, const Point &aOrigin, const Size &aRadius,
                  float aStartAngle, float aEndAngle, bool aAntiClockwise)
@@ -125,39 +126,136 @@ void EllipseToBezier(T* aSink, const Poi
     // sin(x+pi/2) == cos(x)
     Float tmp = cosStartAngle;
     cosStartAngle = -sinStartAngle;
     sinStartAngle = tmp;
   }
 }
 
 /**
+ * Appends a path represending a rectangle to the path being built by
+ * aPathBuilder.
+ *
+ * aRect           The rectangle to append.
+ * aDrawClockwise  If set to true, the path will start at the left of the top
+ *                 left edge and draw clockwise. If set to false the path will
+ *                 start at the right of the top left edge and draw counter-
+ *                 clockwise.
+ */
+GFX2D_API void AppendRectToPath(PathBuilder* aPathBuilder,
+                                const Rect& aRect,
+                                bool aDrawClockwise = true);
+
+inline TemporaryRef<Path> MakePathForRect(const DrawTarget& aDrawTarget,
+                                          const Rect& aRect,
+                                          bool aDrawClockwise = true)
+{
+  RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
+  AppendRectToPath(builder, aRect, aDrawClockwise);
+  return builder->Finish();
+}
+
+// We can't use MOZ_BEGIN_ENUM_CLASS here because that prevents the enum
+// values from being used for indexing. Wrapping the enum in a struct does at
+// least gives us name scoping.
+struct RectCorner {
+  enum {
+    // This order is important since AppendRoundedRectToPath and other code
+    // depends on it!
+    TopLeft = 0,
+    TopRight = 1,
+    BottomRight = 2,
+    BottomLeft = 3,
+    Count = 4
+  };
+};
+
+struct RectCornerRadii {
+  Size radii[RectCorner::Count];
+
+  RectCornerRadii() {}
+
+  explicit RectCornerRadii(Float radius) {
+    for (int i = 0; i < RectCorner::Count; i++) {
+      radii[i].SizeTo(radius, radius);
+    }
+  }
+
+  explicit RectCornerRadii(Float radiusX, Float radiusY) {
+    for (int i = 0; i < RectCorner::Count; i++) {
+      radii[i].SizeTo(radiusX, radiusY);
+    }
+  }
+
+  RectCornerRadii(Float tl, Float tr, Float br, Float bl) {
+    radii[RectCorner::TopLeft].SizeTo(tl, tl);
+    radii[RectCorner::TopRight].SizeTo(tr, tr);
+    radii[RectCorner::BottomRight].SizeTo(br, br);
+    radii[RectCorner::BottomLeft].SizeTo(bl, bl);
+  }
+
+  RectCornerRadii(const Size& tl, const Size& tr,
+                  const Size& br, const Size& bl) {
+    radii[RectCorner::TopLeft] = tl;
+    radii[RectCorner::TopRight] = tr;
+    radii[RectCorner::BottomRight] = br;
+    radii[RectCorner::BottomLeft] = bl;
+  }
+
+  const Size& operator[](size_t aCorner) const {
+    return radii[aCorner];
+  }
+
+  Size& operator[](size_t aCorner) {
+    return radii[aCorner];
+  }
+
+  void Scale(Float aXScale, Float aYScale) {
+    for (int i = 0; i < RectCorner::Count; i++) {
+      radii[i].Scale(aXScale, aYScale);
+    }
+  }
+
+  const Size TopLeft() const { return radii[RectCorner::TopLeft]; }
+  Size& TopLeft() { return radii[RectCorner::TopLeft]; }
+
+  const Size TopRight() const { return radii[RectCorner::TopRight]; }
+  Size& TopRight() { return radii[RectCorner::TopRight]; }
+
+  const Size BottomRight() const { return radii[RectCorner::BottomRight]; }
+  Size& BottomRight() { return radii[RectCorner::BottomRight]; }
+
+  const Size BottomLeft() const { return radii[RectCorner::BottomLeft]; }
+  Size& BottomLeft() { return radii[RectCorner::BottomLeft]; }
+};
+
+/**
  * Appends a path represending a rounded rectangle to the path being built by
  * aPathBuilder.
  *
  * aRect           The rectangle to append.
  * aCornerRadii    Contains the radii of the top-left, top-right, bottom-right
  *                 and bottom-left corners, in that order.
  * aDrawClockwise  If set to true, the path will start at the left of the top
  *                 left edge and draw clockwise. If set to false the path will
  *                 start at the right of the top left edge and draw counter-
  *                 clockwise.
  */
 GFX2D_API void AppendRoundedRectToPath(PathBuilder* aPathBuilder,
                                        const Rect& aRect,
-                                       const Size(& aCornerRadii)[4],
+                                       const RectCornerRadii& aRadii,
                                        bool aDrawClockwise = true);
 
 inline TemporaryRef<Path> MakePathForRoundedRect(const DrawTarget& aDrawTarget,
                                                  const Rect& aRect,
-                                                 const Size(& aCornerRadii)[4],
+                                                 const RectCornerRadii& aRadii,
                                                  bool aDrawClockwise = true)
 {
   RefPtr<PathBuilder> builder = aDrawTarget.CreatePathBuilder();
-  AppendRoundedRectToPath(builder, aRect, aCornerRadii, aDrawClockwise);
+  AppendRoundedRectToPath(builder, aRect, aRadii, aDrawClockwise);
   return builder->Finish();
 }
 
 /**
  * Appends a path represending an ellipse to the path being built by
  * aPathBuilder.
  *
  * The ellipse extends aDimensions.width / 2.0 in the horizontal direction
--- a/gfx/angle/src/libEGL/moz.build
+++ b/gfx/angle/src/libEGL/moz.build
@@ -51,12 +51,12 @@ DEFINES['EGLAPI'] = ""
 
 # ANGLE uses the STL, so we can't use our derpy STL wrappers.
 DISABLE_STL_WRAPPING = True
 
 
 LOCAL_INCLUDES += [ '../../include', '../../src' ]
 USE_LIBS += [ 'libGLESv2' ]
 
-SharedLibrary('libEGL')
+GeckoSharedLibrary('libEGL', linkage=None)
 
 RCFILE = SRCDIR + '/libEGL.rc'
 DEFFILE = SRCDIR + '/libEGL.def'
--- a/gfx/angle/src/libGLESv2/formatutils.cpp
+++ b/gfx/angle/src/libGLESv2/formatutils.cpp
@@ -449,17 +449,17 @@ static InternalFormatInfoMap BuildIntern
     // Depth stencil formats
     //                               | Internal format         |                  | D |S | X | Format            | Type                             | Component type        | Supported                                    | Renderable                                                                         | Filterable                                  |
     map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT16,     DepthStencilFormat(16, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,                 GL_UNSIGNED_NORMALIZED, RequireES<2>,                                  RequireES<2>,                                                                        RequireESOrExt<3, &Extensions::depthTextures>)));
     map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT24,     DepthStencilFormat(24, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,                   GL_UNSIGNED_NORMALIZED, RequireES<3>,                                  RequireES<3>,                                                                        RequireESOrExt<3, &Extensions::depthTextures>)));
     map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT32F,    DepthStencilFormat(32, 0,  0, GL_DEPTH_COMPONENT, GL_FLOAT,                          GL_FLOAT,               RequireES<3>,                                  RequireES<3>,                                                                        RequireESOrExt<3, &Extensions::depthTextures>)));
     map.insert(InternalFormatInfoPair(GL_DEPTH_COMPONENT32_OES, DepthStencilFormat(32, 0,  0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,                   GL_UNSIGNED_NORMALIZED, RequireExt<&Extensions::depthTextures>,        RequireExt<&Extensions::depthTextures>,                                              AlwaysSupported                              )));
     map.insert(InternalFormatInfoPair(GL_DEPTH24_STENCIL8,      DepthStencilFormat(24, 8,  0, GL_DEPTH_STENCIL,   GL_UNSIGNED_INT_24_8,              GL_UNSIGNED_NORMALIZED, RequireESOrExt<3, &Extensions::depthTextures>, RequireESOrExtOrExt<3, &Extensions::depthTextures, &Extensions::packedDepthStencil>, AlwaysSupported                              )));
     map.insert(InternalFormatInfoPair(GL_DEPTH32F_STENCIL8,     DepthStencilFormat(32, 8, 24, GL_DEPTH_STENCIL,   GL_FLOAT_32_UNSIGNED_INT_24_8_REV, GL_FLOAT,               RequireES<3>,                                  RequireES<3>,                                                                        AlwaysSupported                              )));
-    map.insert(InternalFormatInfoPair(GL_STENCIL_INDEX8,        DepthStencilFormat( 0, 8,  0, GL_DEPTH_STENCIL,   GL_UNSIGNED_BYTE,                  GL_UNSIGNED_INT,        RequireES<2>,                                  RequireES<2>,                                                                        NeverSupported                               )));
+    map.insert(InternalFormatInfoPair(GL_STENCIL_INDEX8,        DepthStencilFormat( 0, 8,  0, GL_DEPTH_STENCIL,   GL_UNSIGNED_BYTE,                  GL_UNSIGNED_BYTE,       RequireES<2>,                                  RequireES<2>,                                                                        NeverSupported                               )));
 
     // Luminance alpha formats
     //                               | Internal format          |          | L | A | Format            | Type            | Component type        | Supported                                                                    | Renderable    | Filterable    |
     map.insert(InternalFormatInfoPair(GL_ALPHA8_EXT,             LUMAFormat( 0,  8, GL_ALPHA,           GL_UNSIGNED_BYTE, GL_UNSIGNED_NORMALIZED, RequireExt<&Extensions::textureStorage>,                                      NeverSupported, AlwaysSupported)));
     map.insert(InternalFormatInfoPair(GL_LUMINANCE8_EXT,         LUMAFormat( 8,  0, GL_LUMINANCE,       GL_UNSIGNED_BYTE, GL_UNSIGNED_NORMALIZED, RequireExt<&Extensions::textureStorage>,                                      NeverSupported, AlwaysSupported)));
     map.insert(InternalFormatInfoPair(GL_ALPHA32F_EXT,           LUMAFormat( 0, 32, GL_ALPHA,           GL_FLOAT,         GL_FLOAT,               RequireExtAndExt<&Extensions::textureStorage, &Extensions::textureFloat>,     NeverSupported, AlwaysSupported)));
     map.insert(InternalFormatInfoPair(GL_LUMINANCE32F_EXT,       LUMAFormat(32,  0, GL_LUMINANCE,       GL_FLOAT,         GL_FLOAT,               RequireExtAndExt<&Extensions::textureStorage, &Extensions::textureFloat>,     NeverSupported, AlwaysSupported)));
     map.insert(InternalFormatInfoPair(GL_ALPHA16F_EXT,           LUMAFormat( 0, 16, GL_ALPHA,           GL_HALF_FLOAT,    GL_FLOAT,               RequireExtAndExt<&Extensions::textureStorage, &Extensions::textureHalfFloat>, NeverSupported, AlwaysSupported)));
--- a/gfx/angle/src/libGLESv2/moz.build
+++ b/gfx/angle/src/libGLESv2/moz.build
@@ -218,15 +218,15 @@ LOCAL_INCLUDES += [ '../../include', '..
 if CONFIG['MOZ_HAS_WINSDK_WITH_D3D']:
   OS_LIBS += [ 'd3d9', 'dxguid' ]
 else:
   EXTRA_DSO_LDOPTS += [
     '\'%s/lib/%s/d3d9.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
     '\'%s/lib/%s/dxguid.lib\'' % (CONFIG['MOZ_DIRECTX_SDK_PATH'], CONFIG['MOZ_D3D_CPU_SUFFIX']),
   ]
 
-SharedLibrary('libGLESv2')
+GeckoSharedLibrary('libGLESv2', linkage=None)
 
 RCFILE = SRCDIR + '/libGLESv2.rc'
 DEFFILE = SRCDIR + '/libGLESv2.def'
 
 SOURCES['renderer/d3d/HLSLCompiler.cpp'].flags += ['-DANGLE_PRELOADED_D3DCOMPILER_MODULE_NAMES=\'{ TEXT("d3dcompiler_47.dll"), TEXT("d3dcompiler_46.dll"), TEXT("d3dcompiler_43.dll") }\'']
 
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -157,52 +157,49 @@ static const char *sExtensionNames[] = {
     "GL_OES_texture_half_float",
     "GL_OES_texture_half_float_linear",
     "GL_OES_texture_npot",
     "GL_OES_vertex_array_object",
     nullptr
 };
 
 static bool
-ParseGLVersion(GLContext* gl, unsigned int* version)
+ParseGLVersion(GLContext* gl, uint32_t* out_version)
 {
-    GLenum error = gl->fGetError();
-    if (error != LOCAL_GL_NO_ERROR) {
+    if (gl->fGetError() != LOCAL_GL_NO_ERROR) {
         MOZ_ASSERT(false, "An OpenGL error has been triggered before.");
         return false;
     }
 
     /**
      * B2G emulator bug work around: The emulator implements OpenGL ES 2.0 on
      * OpenGL 3.2. The bug is that GetIntegerv(LOCAL_GL_{MAJOR,MINOR}_VERSION)
      * returns OpenGL 3.2 instead of generating an error.
      */
-    if (!gl->IsGLES())
-    {
+    if (!gl->IsGLES()) {
         /**
          * OpenGL 3.1 and OpenGL ES 3.0 both introduce GL_{MAJOR,MINOR}_VERSION
          * with GetIntegerv. So we first try those constants even though we
-         * might not have an OpenGL context supporting them, has this is a
+         * might not have an OpenGL context supporting them, as this is a
          * better way than parsing GL_VERSION.
          */
         GLint majorVersion = 0;
         GLint minorVersion = 0;
 
-        gl->fGetIntegerv(LOCAL_GL_MAJOR_VERSION, &majorVersion);
-        gl->fGetIntegerv(LOCAL_GL_MINOR_VERSION, &minorVersion);
+        const bool ok = (gl->GetPotentialInteger(LOCAL_GL_MAJOR_VERSION,
+                                                 &majorVersion) &&
+                         gl->GetPotentialInteger(LOCAL_GL_MINOR_VERSION,
+                                                 &minorVersion));
 
         // If it's not an OpenGL (ES) 3.0 context, we will have an error
-        error = gl->fGetError();
-        while (gl->fGetError() != LOCAL_GL_NO_ERROR);
-
-        if (error == LOCAL_GL_NO_ERROR &&
+        if (ok &&
             majorVersion > 0 &&
             minorVersion >= 0)
         {
-            *version = majorVersion * 100 + minorVersion * 10;
+            *out_version = majorVersion * 100 + minorVersion * 10;
             return true;
         }
     }
 
     /**
      * We were not able to use GL_{MAJOR,MINOR}_VERSION, so we parse
      * GL_VERSION.
      *
@@ -227,79 +224,77 @@ ParseGLVersion(GLContext* gl, unsigned i
      *  one or more digits.
      *
      *
      * Note:
      *  We don't care about release_number.
      */
     const char* versionString = (const char*)gl->fGetString(LOCAL_GL_VERSION);
 
-    error = gl->fGetError();
-    if (error != LOCAL_GL_NO_ERROR) {
+    if (gl->fGetError() != LOCAL_GL_NO_ERROR) {
         MOZ_ASSERT(false, "glGetString(GL_VERSION) has generated an error");
         return false;
     } else if (!versionString) {
         MOZ_ASSERT(false, "glGetString(GL_VERSION) has returned 0");
         return false;
     }
 
     const char kGLESVersionPrefix[] = "OpenGL ES ";
     if (strncmp(versionString, kGLESVersionPrefix, strlen(kGLESVersionPrefix)) == 0) {
         versionString += strlen(kGLESVersionPrefix);
     }
 
     const char* itr = versionString;
     char* end = nullptr;
-    int majorVersion = (int)strtol(itr, &end, 10);
+    auto majorVersion = strtol(itr, &end, 10);
 
     if (!end) {
         MOZ_ASSERT(false, "Failed to parse the GL major version number.");
         return false;
     } else if (*end != '.') {
         MOZ_ASSERT(false, "Failed to parse GL's major-minor version number separator.");
         return false;
     }
 
     // we skip the '.' between the major and the minor version
     itr = end + 1;
 
     end = nullptr;
 
-    int minorVersion = (int)strtol(itr, &end, 10);
+    auto minorVersion = strtol(itr, &end, 10);
     if (!end) {
         MOZ_ASSERT(false, "Failed to parse GL's minor version number.");
         return false;
     }
 
     if (majorVersion <= 0 || majorVersion >= 100) {
         MOZ_ASSERT(false, "Invalid major version.");
         return false;
     } else if (minorVersion < 0 || minorVersion >= 10) {
         MOZ_ASSERT(false, "Invalid minor version.");
         return false;
     }
 
-    *version = (unsigned int)(majorVersion * 100 + minorVersion * 10);
+    *out_version = (uint32_t)majorVersion * 100 + (uint32_t)minorVersion * 10;
     return true;
 }
 
 GLContext::GLContext(const SurfaceCaps& caps,
           GLContext* sharedContext,
           bool isOffscreen)
   : mInitialized(false),
     mIsOffscreen(isOffscreen),
     mContextLost(false),
     mVersion(0),
     mProfile(ContextProfile::Unknown),
     mVendor(GLVendor::Other),
     mRenderer(GLRenderer::Other),
     mHasRobustness(false),
-#ifdef MOZ_GL_DEBUG
-    mIsInLocalErrorCheck(false),
-#endif
+    mTopError(LOCAL_GL_NO_ERROR),
+    mLocalErrorScope(nullptr),
     mSharedContext(sharedContext),
     mCaps(caps),
     mScreen(nullptr),
     mLockedSurface(nullptr),
     mMaxTextureSize(0),
     mMaxCubeMapTextureSize(0),
     mMaxTextureImageSize(0),
     mMaxRenderbufferSize(0),
@@ -502,18 +497,17 @@ GLContext::InitWithPrefix(const char *pr
 
         END_SYMBOLS
 
     };
 
     mInitialized = LoadSymbols(&symbols[0], trygl, prefix);
     MakeCurrent();
     if (mInitialized) {
-        unsigned int version = 0;
-
+        uint32_t version = 0;
         ParseGLVersion(this, &version);
 
 #ifdef MOZ_GL_DEBUG
         printf_stderr("OpenGL version detected: %u\n", version);
         printf_stderr("OpenGL vendor: %s\n", fGetString(LOCAL_GL_VENDOR));
         printf_stderr("OpenGL renderer: %s\n", fGetString(LOCAL_GL_RENDERER));
 #endif
 
@@ -1586,17 +1580,17 @@ GLContext::DebugCallback(GLenum source,
     case LOCAL_GL_DEBUG_SEVERITY_NOTIFICATION:
         sevStr = NS_LITERAL_CSTRING("SEVERITY_NOTIFICATION");
         break;
     default:
         sevStr = nsPrintfCString("<severity 0x%04x>", severity);
         break;
     }
 
-    printf_stderr("[KHR_debug: 0x%" PRIxPTR "] ID %u: %s %s %s:\n    %s",
+    printf_stderr("[KHR_debug: 0x%" PRIxPTR "] ID %u: %s, %s, %s:\n    %s\n",
                   (uintptr_t)this,
                   id,
                   sourceStr.BeginReading(),
                   typeStr.BeginReading(),
                   sevStr.BeginReading(),
                   message);
 }
 
@@ -2396,30 +2390,31 @@ GLContext::CleanDirtyScreen()
     BeforeGLReadCall();
     // no-op; we just want to make sure the Read FBO is updated if it needs to be
     AfterGLReadCall();
 }
 
 void
 GLContext::EmptyTexGarbageBin()
 {
-   TexGarbageBin()->EmptyGarbage();
+    TexGarbageBin()->EmptyGarbage();
 }
 
 bool
-GLContext::IsOffscreenSizeAllowed(const IntSize& aSize) const {
-  int32_t biggerDimension = std::max(aSize.width, aSize.height);
-  int32_t maxAllowed = std::min(mMaxRenderbufferSize, mMaxTextureSize);
-  return biggerDimension <= maxAllowed;
+GLContext::IsOffscreenSizeAllowed(const IntSize& aSize) const
+{
+    int32_t biggerDimension = std::max(aSize.width, aSize.height);
+    int32_t maxAllowed = std::min(mMaxRenderbufferSize, mMaxTextureSize);
+    return biggerDimension <= maxAllowed;
 }
 
 bool
 GLContext::IsOwningThreadCurrent()
 {
-  return PlatformThread::CurrentId() == mOwningThreadId;
+    return PlatformThread::CurrentId() == mOwningThreadId;
 }
 
 GLBlitHelper*
 GLContext::BlitHelper()
 {
     if (!mBlitHelper) {
         mBlitHelper = MakeUnique<GLBlitHelper>(this);
     }
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -271,17 +271,17 @@ public:
         return profile == mProfile;
     }
 
     /**
      * Return the version of the context.
      * Example :
      *   If this a OpenGL 2.1, that will return 210
      */
-    inline unsigned int Version() const {
+    inline uint32_t Version() const {
         return mVersion;
     }
 
     const char* VersionString() const {
         return mVersionString.get();
     }
 
     GLVendor Vendor() const {
@@ -313,17 +313,17 @@ protected:
     bool mIsOffscreen;
     bool mIsGlobalSharedContext;
     bool mContextLost;
 
     /**
      * mVersion store the OpenGL's version, multiplied by 100. For example, if
      * the context is an OpenGL 2.1 context, mVersion value will be 210.
      */
-    unsigned int mVersion;
+    uint32_t mVersion;
     nsCString mVersionString;
     ContextProfile mProfile;
 
     GLVendor mVendor;
     GLRenderer mRenderer;
 
     inline void SetProfileVersion(ContextProfile profile, unsigned int version) {
         MOZ_ASSERT(!mInitialized, "SetProfileVersion can only be called before initialization!");
@@ -575,123 +575,106 @@ public:
                 return "GL_TABLE_TOO_LARGE";
             case LOCAL_GL_INVALID_FRAMEBUFFER_OPERATION:
                 return "GL_INVALID_FRAMEBUFFER_OPERATION";
             default:
                 return "";
         }
     }
 
-    /** \returns the first GL error, and guarantees that all GL error flags are cleared,
-     * i.e. that a subsequent GetError call will return NO_ERROR
-     */
-    GLenum GetAndClearError() {
-        // the first error is what we want to return
-        GLenum error = fGetError();
-
-        if (error) {
-            // clear all pending errors
-            while(fGetError()) {}
-        }
-
-        return error;
-    }
-
 private:
-    GLenum raw_fGetError() {
+    GLenum mTopError;
+
+    GLenum RawGetError() {
         return mSymbols.fGetError();
     }
 
-    std::queue<GLenum> mGLErrorQueue;
+    GLenum RawGetErrorAndClear() {
+        GLenum err = RawGetError();
+
+        if (err)
+            while (RawGetError()) {}
+
+        return err;
+    }
 
 public:
+    GLenum FlushErrors() {
+        GLenum err = RawGetErrorAndClear();
+        if (!mTopError)
+            mTopError = err;
+        return err;
+    }
+
+    // We smash all errors together, so you never have to loop on this. We
+    // guarantee that immediately after this call, there are no errors left.
     GLenum fGetError() {
-        if (!mGLErrorQueue.empty()) {
-            GLenum err = mGLErrorQueue.front();
-            mGLErrorQueue.pop();
-            return err;
-        }
-
-        return GetUnpushedError();
-    }
-
-private:
-    GLenum GetUnpushedError() {
-        return raw_fGetError();
-    }
-
-    void ClearUnpushedErrors() {
-        while (GetUnpushedError()) {
-            // Discard errors.
-        }
-    }
-
-    GLenum GetAndClearUnpushedErrors() {
-        GLenum err = GetUnpushedError();
-        if (err) {
-            ClearUnpushedErrors();
-        }
+        FlushErrors();
+
+        GLenum err = mTopError;
+        mTopError = LOCAL_GL_NO_ERROR;
         return err;
     }
 
-    void PushError(GLenum err) {
-        mGLErrorQueue.push(err);
-    }
-
-    void GetAndPushAllErrors() {
-        while (true) {
-            GLenum err = GetUnpushedError();
-            if (!err)
-                break;
-
-            PushError(err);
-        }
-    }
-
     ////////////////////////////////////
     // Use this safer option.
+    class LocalErrorScope;
+
 private:
-#ifdef MOZ_GL_DEBUG
-    bool mIsInLocalErrorCheck;
-#endif
+    LocalErrorScope* mLocalErrorScope;
 
 public:
-    class ScopedLocalErrorCheck {
-        GLContext* const mGL;
+    class LocalErrorScope {
+        GLContext& mGL;
+        GLenum mOldTop;
         bool mHasBeenChecked;
 
     public:
-        explicit ScopedLocalErrorCheck(GLContext* gl)
+        explicit LocalErrorScope(GLContext& gl)
             : mGL(gl)
             , mHasBeenChecked(false)
         {
-#ifdef MOZ_GL_DEBUG
-            MOZ_ASSERT(!mGL->mIsInLocalErrorCheck);
-            mGL->mIsInLocalErrorCheck = true;
-#endif
-            mGL->GetAndPushAllErrors();
+            MOZ_ASSERT(!mGL.mLocalErrorScope);
+            mGL.mLocalErrorScope = this;
+
+            mGL.FlushErrors();
+
+            mOldTop = mGL.mTopError;
+            mGL.mTopError = LOCAL_GL_NO_ERROR;
         }
 
-        GLenum GetLocalError() {
-#ifdef MOZ_GL_DEBUG
-            MOZ_ASSERT(mGL->mIsInLocalErrorCheck);
-            mGL->mIsInLocalErrorCheck = false;
-#endif
-
+        GLenum GetError() {
             MOZ_ASSERT(!mHasBeenChecked);
             mHasBeenChecked = true;
 
-            return mGL->GetAndClearUnpushedErrors();
+            return mGL.fGetError();
         }
 
-        ~ScopedLocalErrorCheck() {
+        ~LocalErrorScope() {
             MOZ_ASSERT(mHasBeenChecked);
+
+            MOZ_ASSERT(mGL.fGetError() == LOCAL_GL_NO_ERROR);
+
+            mGL.mTopError = mOldTop;
+
+            MOZ_ASSERT(mGL.mLocalErrorScope == this);
+            mGL.mLocalErrorScope = nullptr;
         }
     };
 
+    bool GetPotentialInteger(GLenum pname, GLint* param) {
+        LocalErrorScope localError(*this);
+
+        fGetIntegerv(pname, param);
+
+        GLenum err = localError.GetError();
+        MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_INVALID_ENUM);
+        return err == LOCAL_GL_NO_ERROR;
+    }
+
 private:
     static void GLAPIENTRY StaticDebugCallback(GLenum source,
                                                GLenum type,
                                                GLuint id,
                                                GLenum severity,
                                                GLsizei length,
                                                const GLchar* message,
                                                const GLvoid* userParam);
@@ -717,53 +700,57 @@ private:
 #  define MOZ_FUNCTION_NAME __PRETTY_FUNCTION__
 # elif defined(_MSC_VER)
 #  define MOZ_FUNCTION_NAME __FUNCTION__
 # else
 #  define MOZ_FUNCTION_NAME __func__  // defined in C99, supported in various C++ compilers. Just raw function name.
 # endif
 #endif
 
-    void BeforeGLCall(const char* glFunction) {
+    void BeforeGLCall(const char* funcName) {
         MOZ_ASSERT(IsCurrent());
+
         if (DebugMode()) {
-            GLContext *currentGLContext = nullptr;
-
-            currentGLContext = (GLContext*)PR_GetThreadPrivate(sCurrentGLContextTLS);
+            FlushErrors();
 
             if (DebugMode() & DebugTrace)
-                printf_stderr("[gl:%p] > %s\n", this, glFunction);
-            if (this != currentGLContext) {
-                printf_stderr("Fatal: %s called on non-current context %p. "
-                              "The current context for this thread is %p.\n",
-                              glFunction, this, currentGLContext);
-                NS_ABORT();
+                printf_stderr("[gl:%p] > %s\n", this, funcName);
+
+            GLContext* tlsContext = (GLContext*)PR_GetThreadPrivate(sCurrentGLContextTLS);
+            if (this != tlsContext) {
+                printf_stderr("Fatal: %s called on non-current context %p. The"
+                              " current context for this thread is %p.\n",
+                              funcName, this, tlsContext);
+                MOZ_CRASH("GLContext is not current.");
             }
         }
     }
 
-    void AfterGLCall(const char* glFunction) {
+    void AfterGLCall(const char* funcName) {
         if (DebugMode()) {
             // calling fFinish() immediately after every GL call makes sure that if this GL command crashes,
             // the stack trace will actually point to it. Otherwise, OpenGL being an asynchronous API, stack traces
             // tend to be meaningless
             mSymbols.fFinish();
-            GLenum err = GetUnpushedError();
-            PushError(err);
-
-            if (DebugMode() & DebugTrace)
-                printf_stderr("[gl:%p] < %s [0x%04x]\n", this, glFunction, err);
-
-            if (err != LOCAL_GL_NO_ERROR) {
-                printf_stderr("GL ERROR: %s generated GL error %s(0x%04x)\n",
-                              glFunction,
-                              GLErrorToString(err),
-                              err);
+            GLenum err = FlushErrors();
+
+            if (DebugMode() & DebugTrace) {
+                printf_stderr("[gl:%p] < %s [%s (0x%04x)]\n", this, funcName,
+                              GLErrorToString(err), err);
+            }
+
+            if (err != LOCAL_GL_NO_ERROR &&
+                !mLocalErrorScope)
+            {
+                printf_stderr("[gl:%p] %s: Generated unexpected %s error."
+                              " (0x%04x)\n", this, funcName,
+                              GLErrorToString(err), err);
+
                 if (DebugMode() & DebugAbortOnError)
-                    NS_ABORT();
+                    MOZ_CRASH("MOZ_GL_DEBUG_ABORT_ON_ERROR");
             }
         }
     }
 
     GLContext *TrackingContext()
     {
         GLContext *tip = this;
         while (tip->mSharedContext)
@@ -3475,32 +3462,17 @@ public:
     }
 
 protected:
     // Note that it does -not- clear the resized buffers.
     bool CreateScreenBuffer(const gfx::IntSize& size, const SurfaceCaps& caps) {
         if (!IsOffscreenSizeAllowed(size))
             return false;
 
-        SurfaceCaps tryCaps = caps;
-        if (tryCaps.antialias) {
-            // AA path
-            if (CreateScreenBufferImpl(size, tryCaps))
-                return true;
-
-            NS_WARNING("CreateScreenBuffer failed to initialize an AA context! Falling back to no AA...");
-            tryCaps.antialias = false;
-        }
-        MOZ_ASSERT(!tryCaps.antialias);
-
-        if (CreateScreenBufferImpl(size, tryCaps))
-            return true;
-
-        NS_WARNING("CreateScreenBuffer failed to initialize non-AA context!");
-        return false;
+       return CreateScreenBufferImpl(size, caps);
     }
 
     bool CreateScreenBufferImpl(const gfx::IntSize& size,
                                 const SurfaceCaps& caps);
 
 public:
     bool ResizeScreenBuffer(const gfx::IntSize& size);
 
@@ -3637,16 +3609,19 @@ protected:
                               : mMaxTextureSize;
             return width <= maxSize && height <= maxSize;
         }
         return true;
     }
 
 
 public:
+    GLsizei MaxSamples() const {
+        return mMaxSamples;
+    }
 
     void fViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
         if (mViewportRect[0] == x &&
             mViewportRect[1] == y &&
             mViewportRect[2] == width &&
             mViewportRect[3] == height)
         {
             return;
--- a/gfx/gl/GLScreenBuffer.cpp
+++ b/gfx/gl/GLScreenBuffer.cpp
@@ -567,18 +567,22 @@ DrawBuffer::Create(GLContext* const gl,
 
     if (!caps.color) {
         MOZ_ASSERT(!caps.alpha && !caps.depth && !caps.stencil);
 
         // Nothing is needed.
         return true;
     }
 
-    if (caps.antialias && formats.samples == 0)
-        return false; // Can't create it
+    if (caps.antialias) {
+        if (formats.samples == 0)
+            return false; // Can't create it.
+
+        MOZ_ASSERT(formats.samples <= gl->MaxSamples());
+    }
 
     GLuint colorMSRB = 0;
     GLuint depthRB   = 0;
     GLuint stencilRB = 0;
 
     GLuint* pColorMSRB = caps.antialias ? &colorMSRB : nullptr;
     GLuint* pDepthRB   = caps.depth     ? &depthRB   : nullptr;
     GLuint* pStencilRB = caps.stencil   ? &stencilRB : nullptr;
@@ -595,29 +599,30 @@ DrawBuffer::Create(GLContext* const gl,
     } else {
         if (!formats.depth)
             pDepthRB = nullptr;
 
         if (!formats.stencil)
             pStencilRB = nullptr;
     }
 
-    GLContext::ScopedLocalErrorCheck localError(gl);
+    GLContext::LocalErrorScope localError(*gl);
 
     CreateRenderbuffersForOffscreen(gl, formats, size, caps.antialias,
                                     pColorMSRB, pDepthRB, pStencilRB);
 
     GLuint fb = 0;
     gl->fGenFramebuffers(1, &fb);
     gl->AttachBuffersToFB(0, colorMSRB, depthRB, stencilRB, fb);
 
     UniquePtr<DrawBuffer> ret( new DrawBuffer(gl, size, fb, colorMSRB,
                                               depthRB, stencilRB) );
 
-    GLenum err = localError.GetLocalError();
+    GLenum err = localError.GetError();
+    MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY);
     if (err || !gl->IsFramebufferComplete(fb))
         return false;
 
     *out_buffer = Move(ret);
     return true;
 }
 
 DrawBuffer::~DrawBuffer()
@@ -654,17 +659,17 @@ ReadBuffer::Create(GLContext* gl,
     }
 
     GLuint depthRB = 0;
     GLuint stencilRB = 0;
 
     GLuint* pDepthRB   = caps.depth   ? &depthRB   : nullptr;
     GLuint* pStencilRB = caps.stencil ? &stencilRB : nullptr;
 
-    GLContext::ScopedLocalErrorCheck localError(gl);
+    GLContext::LocalErrorScope localError(*gl);
 
     CreateRenderbuffersForOffscreen(gl, formats, surf->mSize, caps.antialias,
                                     nullptr, pDepthRB, pStencilRB);
 
     GLuint colorTex = 0;
     GLuint colorRB = 0;
     GLenum target = 0;
 
@@ -684,17 +689,18 @@ ReadBuffer::Create(GLContext* gl,
     GLuint fb = 0;
     gl->fGenFramebuffers(1, &fb);
     gl->AttachBuffersToFB(colorTex, colorRB, depthRB, stencilRB, fb, target);
     gl->mFBOMapping[fb] = surf;
 
     UniquePtr<ReadBuffer> ret( new ReadBuffer(gl, fb, depthRB,
                                               stencilRB, surf) );
 
-    GLenum err = localError.GetLocalError();
+    GLenum err = localError.GetError();
+    MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY);
     if (err || !gl->IsFramebufferComplete(fb)) {
         ret = nullptr;
     }
 
     return Move(ret);
 }
 
 ReadBuffer::~ReadBuffer()
--- a/gfx/gl/SharedSurfaceGL.cpp
+++ b/gfx/gl/SharedSurfaceGL.cpp
@@ -21,22 +21,24 @@ using gfx::SurfaceFormat;
 SharedSurface_Basic::Create(GLContext* gl,
                             const GLFormats& formats,
                             const IntSize& size,
                             bool hasAlpha)
 {
     UniquePtr<SharedSurface_Basic> ret;
     gl->MakeCurrent();
 
-    GLContext::ScopedLocalErrorCheck localError(gl);
+    GLContext::LocalErrorScope localError(*gl);
     GLuint tex = CreateTexture(gl, formats.color_texInternalFormat,
                                formats.color_texFormat,
                                formats.color_texType,
                                size);
-    GLenum err = localError.GetLocalError();
+
+    GLenum err = localError.GetError();
+    MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY);
     if (err) {
         gl->fDeleteTextures(1, &tex);
         return Move(ret);
     }
 
     SurfaceFormat format = SurfaceFormat::B8G8R8X8;
     switch (formats.color_texInternalFormat) {
     case LOCAL_GL_RGB:
@@ -116,27 +118,28 @@ SharedSurface_GLTexture::Create(GLContex
 
     GLuint tex = texture;
 
     bool ownsTex = false;
 
     UniquePtr<SharedSurface_GLTexture> ret;
 
     if (!tex) {
-      GLContext::ScopedLocalErrorCheck localError(prodGL);
+        GLContext::LocalErrorScope localError(*prodGL);
 
-      tex = CreateTextureForOffscreen(prodGL, formats, size);
+        tex = CreateTextureForOffscreen(prodGL, formats, size);
 
-      GLenum err = localError.GetLocalError();
-      if (err) {
-          prodGL->fDeleteTextures(1, &tex);
-          return Move(ret);
-      }
+        GLenum err = localError.GetError();
+        MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_OUT_OF_MEMORY);
+        if (err) {
+            prodGL->fDeleteTextures(1, &tex);
+            return Move(ret);
+        }
 
-      ownsTex = true;
+        ownsTex = true;
     }
 
     ret.reset( new SharedSurface_GLTexture(prodGL, consGL, size,
                                            hasAlpha, tex, ownsTex) );
     return Move(ret);
 }
 
 SharedSurface_GLTexture::~SharedSurface_GLTexture()
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -917,35 +917,42 @@ CompositorD3D11::EnsureSize()
 
   mSize = rect.Size();
 }
 
 void
 CompositorD3D11::VerifyBufferSize()
 {
   DXGI_SWAP_CHAIN_DESC swapDesc;
-  mSwapChain->GetDesc(&swapDesc);
+  HRESULT hr;
+
+  hr = mSwapChain->GetDesc(&swapDesc);
+  if (Failed(hr)) {
+    return;
+  }
 
   if ((swapDesc.BufferDesc.Width == mSize.width &&
        swapDesc.BufferDesc.Height == mSize.height) ||
       mSize.width == 0 || mSize.height == 0) {
     return;
   }
 
   mDefaultRT = nullptr;
 
   if (IsRunningInWindowsMetro()) {
-    mSwapChain->ResizeBuffers(2, mSize.width, mSize.height,
-                              DXGI_FORMAT_B8G8R8A8_UNORM,
-                              0);
+    hr = mSwapChain->ResizeBuffers(2, mSize.width, mSize.height,
+                                   DXGI_FORMAT_B8G8R8A8_UNORM,
+                                   0);
+    HandleError(hr);
     mDisableSequenceForNextFrame = true;
   } else {
-    mSwapChain->ResizeBuffers(1, mSize.width, mSize.height,
-                              DXGI_FORMAT_B8G8R8A8_UNORM,
-                              0);
+    hr = mSwapChain->ResizeBuffers(1, mSize.width, mSize.height,
+                                   DXGI_FORMAT_B8G8R8A8_UNORM,
+                                   0);
+    HandleError(hr);
   }
 }
 
 void
 CompositorD3D11::UpdateRenderTarget()
 {
   EnsureSize();
   VerifyBufferSize();
@@ -1071,50 +1078,64 @@ CompositorD3D11::SetSamplerForFilter(Fil
 
   mContext->PSSetSamplers(0, 1, &sampler);
 }
 
 void
 CompositorD3D11::PaintToTarget()
 {
   nsRefPtr<ID3D11Texture2D> backBuf;
+  HRESULT hr;
 
-  mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment());
+  hr = mSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)backBuf.StartAssignment());
+  if (Failed(hr)) {
+    return;
+  }
 
   D3D11_TEXTURE2D_DESC bbDesc;
   backBuf->GetDesc(&bbDesc);
 
   CD3D11_TEXTURE2D_DESC softDesc(bbDesc.Format, bbDesc.Width, bbDesc.Height);
   softDesc.MipLevels = 1;
   softDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
   softDesc.Usage = D3D11_USAGE_STAGING;
   softDesc.BindFlags = 0;
 
   nsRefPtr<ID3D11Texture2D> readTexture;
 
-  HRESULT hr = mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture));
+  hr = mDevice->CreateTexture2D(&softDesc, nullptr, getter_AddRefs(readTexture));
+  if (Failed(hr)) {
+    return;
+  }
   mContext->CopyResource(readTexture, backBuf);
 
   D3D11_MAPPED_SUBRESOURCE map;
-  mContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &map);
+  hr = mContext->Map(readTexture, 0, D3D11_MAP_READ, 0, &map);
+  if (Failed(hr)) {
+    return;
+  }
   RefPtr<DataSourceSurface> sourceSurface =
     Factory::CreateWrappingDataSourceSurface((uint8_t*)map.pData,
                                              map.RowPitch,
                                              IntSize(bbDesc.Width, bbDesc.Height),
                                              SurfaceFormat::B8G8R8A8);
   mTarget->CopySurface(sourceSurface,
                        IntRect(0, 0, bbDesc.Width, bbDesc.Height),
                        IntPoint(-mTargetBounds.x, -mTargetBounds.y));
+
   mTarget->Flush();
   mContext->Unmap(readTexture, 0);
 }
 
 void
 CompositorD3D11::HandleError(HRESULT hr, Severity aSeverity)
 {
+  if (SUCCEEDED(hr)) {
+    return;
+  }
   // XXX - It would be nice to use gfxCriticalError, but it needs to
   // be made to work off the main thread first.
   MOZ_ASSERT(aSeverity != DebugAssert);
 
   if (aSeverity == Critical) {
     MOZ_CRASH("Unrecoverable D3D11 error");
   }
 
@@ -1130,23 +1151,21 @@ CompositorD3D11::HandleError(HRESULT hr,
   if (aSeverity == Recoverable) {
     NS_WARNING("Encountered a recoverable D3D11 error");
   }
 }
 
 bool
 CompositorD3D11::Failed(HRESULT hr, Severity aSeverity)
 {
-  if (FAILED(hr)) {
-    HandleError(hr, aSeverity);
-    return true;
-  }
-  return false;
+  HandleError(hr, aSeverity);
+  return FAILED(hr);
 }
 
 bool
 CompositorD3D11::Succeeded(HRESULT hr, Severity aSeverity)
 {
-  return !Failed(hr, aSeverity);
+  HandleError(hr, aSeverity);
+  return SUCCEEDED(hr);
 }
 
 }
 }
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -144,17 +144,17 @@ public:
 
 private:
   enum Severity {
     Recoverable,
     DebugAssert,
     Critical,
   };
 
-  void HandleError(HRESULT hr, Severity aSeverity);
+  void HandleError(HRESULT hr, Severity aSeverity = DebugAssert);
   bool Failed(HRESULT hr, Severity aSeverity = DebugAssert);
   bool Succeeded(HRESULT hr, Severity aSeverity = DebugAssert);
 
   // ensure mSize is up to date with respect to mWidget
   void EnsureSize();
   void VerifyBufferSize();
   void UpdateRenderTarget();
   bool CreateShaders();
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -597,16 +597,17 @@ CompositorParent::ResumeComposition()
     __android_log_print(ANDROID_LOG_INFO, "CompositorParent", "Unable to renew compositor surface; remaining in paused state");
 #endif
     lock.NotifyAll();
     return;
   }
 
   mPaused = false;
 
+  mLastCompose = TimeStamp::Now();
   CompositeToTarget(nullptr);