Merge inbound to m-c a=merge
authorWes Kocher <wkocher@mozilla.com>
Fri, 25 Jul 2014 15:59:01 -0700
changeset 217783 e07264876182f1f407e99df3fbd0759e8f0ab4fa
parent 217619 68e96eb00d3e81c5cca8e40742aa61ceec915a9a (current diff)
parent 217782 42369dee3289aec68f5f06ba55a059d3a683044a (diff)
child 217784 cc4b1c9427f26fc2973498c898ae2330f018aeea
child 217794 f4f95c3ba8290542ea16597781b8b3ac828c94e8
child 217842 f61a9a2aa3bc92a0f153f5903144c1bc0fa4c524
child 217928 f59f7e13d384e841d07b4862ea761d3956ec5e41
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone34.0a1
first release with
nightly linux32
e07264876182 / 34.0a1 / 20140726030204 / files
nightly linux64
e07264876182 / 34.0a1 / 20140726030204 / files
nightly mac
e07264876182 / 34.0a1 / 20140726030204 / files
nightly win32
e07264876182 / 34.0a1 / 20140726030204 / files
nightly win64
e07264876182 / 34.0a1 / 20140726030204 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c a=merge
intl/chardet/public/moz.build
intl/chardet/public/nsDetectionConfident.h
intl/chardet/public/nsICharsetDetectionObserver.h
intl/chardet/public/nsICharsetDetector.h
intl/chardet/public/nsIStringCharsetDetector.h
intl/chardet/src/moz.build
intl/chardet/src/nsCharDetConstructors.h
intl/chardet/src/nsChardetModule.cpp
intl/chardet/src/nsCyrillicClass.h
intl/chardet/src/nsCyrillicDetector.cpp
intl/chardet/src/nsCyrillicDetector.h
intl/chardet/src/nsCyrillicProb.h
intl/hyphenation/public/moz.build
intl/hyphenation/public/nsHyphenationManager.h
intl/hyphenation/public/nsHyphenator.h
intl/hyphenation/src/COPYING
intl/hyphenation/src/COPYING.LGPL
intl/hyphenation/src/COPYING.MPL
intl/hyphenation/src/README
intl/hyphenation/src/README.compound
intl/hyphenation/src/README.hyphen
intl/hyphenation/src/README.mozilla
intl/hyphenation/src/README.nonstandard
intl/hyphenation/src/hnjalloc.h
intl/hyphenation/src/hnjstdio.cpp
intl/hyphenation/src/hyphen.c
intl/hyphenation/src/hyphen.h
intl/hyphenation/src/moz.build
intl/hyphenation/src/nsHyphenationManager.cpp
intl/hyphenation/src/nsHyphenator.cpp
intl/locale/idl/moz.build
intl/locale/idl/nsICollation.idl
intl/locale/idl/nsILocale.idl
intl/locale/idl/nsILocaleService.idl
intl/locale/idl/nsIScriptableDateFormat.idl
intl/locale/public/moz.build
intl/locale/public/nsCollationCID.h
intl/locale/public/nsDateTimeFormatCID.h
intl/locale/public/nsIDateTimeFormat.h
intl/locale/public/nsILanguageAtomService.h
intl/locale/public/nsIPlatformCharset.h
intl/locale/public/nsPosixLocale.h
intl/locale/public/nsWin32Locale.h
intl/locale/src/Makefile.in
intl/locale/src/PluralForm.jsm
intl/locale/src/langGroups.properties
intl/locale/src/language.properties
intl/locale/src/mac/moz.build
intl/locale/src/mac/nsCollationMacUC.cpp
intl/locale/src/mac/nsCollationMacUC.h
intl/locale/src/mac/nsDateTimeFormatMac.cpp
intl/locale/src/mac/nsDateTimeFormatMac.h
intl/locale/src/mac/nsMacCharset.cpp
intl/locale/src/moz.build
intl/locale/src/nsCollation.cpp
intl/locale/src/nsCollation.h
intl/locale/src/nsLanguageAtomService.cpp
intl/locale/src/nsLanguageAtomService.h
intl/locale/src/nsLocale.cpp
intl/locale/src/nsLocale.h
intl/locale/src/nsLocaleConstructors.h
intl/locale/src/nsLocaleService.cpp
intl/locale/src/nsPlatformCharset.h
intl/locale/src/nsScriptableDateFormat.cpp
intl/locale/src/nsUConvPropertySearch.cpp
intl/locale/src/nsUConvPropertySearch.h
intl/locale/src/props2arrays.py
intl/locale/src/unix/Makefile.in
intl/locale/src/unix/moz.build
intl/locale/src/unix/nsAndroidCharset.cpp
intl/locale/src/unix/nsCollationUnix.cpp
intl/locale/src/unix/nsCollationUnix.h
intl/locale/src/unix/nsDateTimeFormatUnix.cpp
intl/locale/src/unix/nsDateTimeFormatUnix.h
intl/locale/src/unix/nsPosixLocale.cpp
intl/locale/src/unix/nsUNIXCharset.cpp
intl/locale/src/unix/unixcharset.properties
intl/locale/src/windows/Makefile.in
intl/locale/src/windows/moz.build
intl/locale/src/windows/nsCollationWin.cpp
intl/locale/src/windows/nsCollationWin.h
intl/locale/src/windows/nsDateTimeFormatWin.cpp
intl/locale/src/windows/nsDateTimeFormatWin.h
intl/locale/src/windows/nsWin32Locale.cpp
intl/locale/src/windows/nsWinCharset.cpp
intl/locale/src/windows/wincharset.properties
intl/lwbrk/idl/moz.build
intl/lwbrk/idl/nsISemanticUnitScanner.idl
intl/lwbrk/public/moz.build
intl/lwbrk/public/nsILineBreaker.h
intl/lwbrk/public/nsIWordBreaker.h
intl/lwbrk/public/nsLWBrkCIID.h
intl/lwbrk/src/Makefile.in
intl/lwbrk/src/crashtests/416721.html
intl/lwbrk/src/crashtests/crashtests.list
intl/lwbrk/src/jisx4051class.h
intl/lwbrk/src/jisx4051pairtable.txt
intl/lwbrk/src/moz.build
intl/lwbrk/src/nsCarbonBreaker.cpp
intl/lwbrk/src/nsComplexBreaker.h
intl/lwbrk/src/nsJISx4051LineBreaker.cpp
intl/lwbrk/src/nsJISx4051LineBreaker.h
intl/lwbrk/src/nsPangoBreaker.cpp
intl/lwbrk/src/nsRuleBreaker.cpp
intl/lwbrk/src/nsSampleWordBreaker.cpp
intl/lwbrk/src/nsSampleWordBreaker.h
intl/lwbrk/src/nsSemanticUnitScanner.cpp
intl/lwbrk/src/nsSemanticUnitScanner.h
intl/lwbrk/src/nsUniscribeBreaker.cpp
intl/lwbrk/src/rulebrk.c
intl/lwbrk/src/rulebrk.h
intl/lwbrk/src/th_char.h
intl/strres/public/moz.build
intl/strres/public/nsIStringBundle.idl
intl/strres/public/nsIStringBundleOverride.idl
intl/strres/src/moz.build
intl/strres/src/nsStringBundle.cpp
intl/strres/src/nsStringBundle.h
intl/strres/src/nsStringBundleService.h
intl/strres/src/nsStringBundleTextOverride.cpp
intl/strres/src/nsStringBundleTextOverride.h
intl/uconv/idl/moz.build
intl/uconv/idl/nsICurrentCharsetListener.idl
intl/uconv/idl/nsIScriptableUConv.idl
intl/uconv/idl/nsITextToSubURI.idl
intl/uconv/idl/nsIUTF8ConverterService.idl
intl/uconv/public/moz.build
intl/uconv/public/nsEncoderDecoderUtils.h
intl/uconv/public/nsIUnicodeDecoder.h
intl/uconv/public/nsIUnicodeEncoder.h
intl/uconv/public/nsUCSupport.h
intl/uconv/public/nsUConvCID.h
intl/uconv/public/uconvutil.h
intl/uconv/src/8859-1.uf
intl/uconv/src/8859-1.ut
intl/uconv/src/cp1252.uf
intl/uconv/src/cp1252.ut
intl/uconv/src/macroman.uf
intl/uconv/src/macroman.ut
intl/uconv/src/moz.build
intl/uconv/src/nsCP1252ToUnicode.cpp
intl/uconv/src/nsCP1252ToUnicode.h
intl/uconv/src/nsConverterInputStream.cpp
intl/uconv/src/nsConverterInputStream.h
intl/uconv/src/nsConverterOutputStream.cpp
intl/uconv/src/nsConverterOutputStream.h
intl/uconv/src/nsISO88591ToUnicode.cpp
intl/uconv/src/nsISO88591ToUnicode.h
intl/uconv/src/nsMacRomanToUnicode.cpp
intl/uconv/src/nsMacRomanToUnicode.h
intl/uconv/src/nsReplacementToUnicode.cpp
intl/uconv/src/nsReplacementToUnicode.h
intl/uconv/src/nsScriptableUConv.cpp
intl/uconv/src/nsScriptableUConv.h
intl/uconv/src/nsTextToSubURI.cpp
intl/uconv/src/nsTextToSubURI.h
intl/uconv/src/nsUConvModule.cpp
intl/uconv/src/nsUTF8ConverterService.cpp
intl/uconv/src/nsUTF8ConverterService.h
intl/uconv/src/nsUTF8ToUnicode.cpp
intl/uconv/src/nsUTF8ToUnicode.h
intl/uconv/src/nsUTF8ToUnicodeSSE2.cpp
intl/uconv/src/nsUnicodeToCP1252.cpp
intl/uconv/src/nsUnicodeToCP1252.h
intl/uconv/src/nsUnicodeToISO88591.cpp
intl/uconv/src/nsUnicodeToISO88591.h
intl/uconv/src/nsUnicodeToMacRoman.cpp
intl/uconv/src/nsUnicodeToMacRoman.h
intl/uconv/src/nsUnicodeToUTF8.cpp
intl/uconv/src/nsUnicodeToUTF8.h
intl/unicharutil/idl/moz.build
intl/unicharutil/idl/nsIEntityConverter.idl
intl/unicharutil/idl/nsISaveAsCharset.idl
intl/unicharutil/idl/nsIUnicodeNormalizer.idl
intl/unicharutil/public/moz.build
intl/unicharutil/public/nsICaseConversion.h
intl/unicharutil/public/nsIUGenCategory.h
intl/unicharutil/public/nsUnicharUtilCIID.h
intl/unicharutil/public/nsUnicodeNormalizer.h
intl/unicharutil/src/moz.build
intl/unicharutil/src/normalization_data.h
intl/unicharutil/src/nsCaseConversionImp2.cpp
intl/unicharutil/src/nsCaseConversionImp2.h
intl/unicharutil/src/nsCategoryImp.cpp
intl/unicharutil/src/nsCategoryImp.h
intl/unicharutil/src/nsEntityConverter.cpp
intl/unicharutil/src/nsEntityConverter.h
intl/unicharutil/src/nsSaveAsCharset.cpp
intl/unicharutil/src/nsSaveAsCharset.h
intl/unicharutil/src/nsUnicodeNormalizer.cpp
intl/unicharutil/src/ucdata.c
intl/unicharutil/src/ucdata.h
js/src/jit-test/tests/auto-regress/bug432365.js
js/src/jit-test/tests/basic/bug656381.js
js/src/jit-test/tests/basic/bug656555.js
js/src/jit-test/tests/basic/bug657975.js
js/src/jit-test/tests/basic/testBug666292.js
js/src/jit-test/tests/basic/testBug677367.js
js/src/jit-test/tests/basic/testBug683470.js
js/src/jit-test/tests/basic/testBug692274-1.js
js/src/jit-test/tests/basic/testEvalFromTrap.js
js/src/jit-test/tests/basic/testTrapOnEval.js
js/src/jit-test/tests/debug/breakpoint-gc-03.js
js/src/jit-test/tests/jaeger/bug563000/eif-trap-newvar.js
js/src/jit-test/tests/jaeger/bug563000/eif-trap-typechange.js
js/src/jit-test/tests/jaeger/bug563000/eif-trap.js
js/src/jit-test/tests/jaeger/bug563000/simple-trap-1.js
js/src/jit-test/tests/jaeger/bug563000/simple-trap-2.js
js/src/jit-test/tests/jaeger/bug563000/simple-untrap.js
js/src/jit-test/tests/jaeger/bug563000/test-debugger-1.js
js/src/jit-test/tests/jaeger/bug563000/test-debugger-2.js
js/src/jit-test/tests/jaeger/bug563000/trap-force-return-1.js
js/src/jit-test/tests/jaeger/bug563000/trap-force-return-2.js
js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js
js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js
js/src/jit-test/tests/jaeger/bug563000/trap-own-callsite.js
js/src/jit-test/tests/jaeger/bug563000/trap-parent-from-trap.js
js/src/jit-test/tests/jaeger/bug563000/trap-parent.js
js/src/jit-test/tests/jaeger/bug563000/trap-self-as-parent.js
js/src/jit-test/tests/jaeger/bug563000/trap-self-from-trap.js
js/src/jit-test/tests/jaeger/bug563000/trap-self.js
js/src/jit-test/tests/jaeger/bug563000/untrap-cross-global.js
js/src/jit-test/tests/jaeger/bug563000/untrap-own-trapsite.js
js/src/jit-test/tests/jaeger/bug563000/untrap-self.js
js/src/jit-test/tests/jaeger/bug707384.js
js/src/jsapi-tests/testTrap.cpp
js/src/tests/ecma/extensions/trapflatclosure.js
js/src/tests/ecma_3/extensions/regress-429248.js
js/src/tests/js1_5/extensions/regress-422137.js
js/src/tests/js1_5/extensions/regress-429264.js
js/src/tests/js1_5/extensions/regress-431428.js
js/src/tests/js1_7/extensions/regress-429266.js
js/src/tests/js1_8_5/extensions/regress-672804-1.js
js/src/tests/js1_8_5/extensions/regress-672804-2.js
js/src/tests/js1_8_5/extensions/regress-672804-3.js
js/src/tests/js1_8_5/regress/regress-476088.js
mobile/android/base/resources/drawable/phone.png
mozglue/tests/Makefile.in
mozglue/tests/TestZip.cpp
mozglue/tests/no_central_dir.zip
mozglue/tests/test.zip
netwerk/base/public/nsINetworkZonePolicy.idl
netwerk/base/src/nsNetworkZonePolicy.cpp
netwerk/base/src/nsNetworkZonePolicy.h
netwerk/sctp/src/Makefile.in
netwerk/srtp/src/Makefile.in
netwerk/test/unit/test_networkZonePolicy.js
netwerk/test/unit/xpcshell.ini
testing/mochitest/runtestsvmware.py
toolkit/components/jsdownloads/test/data/Makefile.in
toolkit/components/jsdownloads/test/data/moz.build
--- a/Makefile.in
+++ b/Makefile.in
@@ -312,10 +312,24 @@ source-package install:
 config/export:
 
 endif
 
 # Interdependencies for parallel export.
 js/xpconnect/src/export: dom/bindings/export xpcom/xpidl/export
 accessible/xpcom/export: xpcom/xpidl/export
 ifdef ENABLE_CLANG_PLUGIN
-js/src/export config/export: build/clang-plugin/export
+js/src/export config/host: build/clang-plugin/export
+endif
+
+# Interdependencies that moz.build world don't know about yet for compilation.
+# Note some others are hardcoded or "guessed" in recursivemake.py and emitter.py
+ifeq ($(MOZ_WIDGET_TOOLKIT),gtk3)
+toolkit/library/target: widget/gtk/mozgtk/gtk3/target
 endif
+ifndef MOZ_FOLD_LIBS
+ifndef MOZ_NATIVE_SQLITE
+security/build/target: db/sqlite3/src/target
+endif
+endif
+ifeq ($(MOZ_REPLACE_MALLOC_LINKAGE),dummy library)
+mozglue/build/target: memory/replace/dummy/target
+endif
--- a/browser/modules/test/browser.ini
+++ b/browser/modules/test/browser.ini
@@ -21,16 +21,16 @@ skip-if = e10s # Bug 941428 - UITour.jsm
 skip-if = os == "linux" || e10s # Linux: Bug 986760, Bug 989101; e10s: Bug 941428 - UITour.jsm not e10s friendly
 [browser_UITour_availableTargets.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
 [browser_UITour_detach_tab.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
 [browser_UITour_annotation_size_attributes.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
 [browser_UITour_panel_close_annotation.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
+skip-if = true # Disabled due to frequent failures, bugs 1026310 and 1032137
 [browser_UITour_registerPageID.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
 [browser_UITour_sync.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
 [browser_taskbar_preview.js]
 run-if = os == "win"
 skip-if = e10s # Bug 666808 - AeroPeek support for e10s
--- a/build/autoconf/nspr-build.m4
+++ b/build/autoconf/nspr-build.m4
@@ -46,26 +46,22 @@ MOZ_ARG_WITH_BOOL(system-nspr,
 [  --with-system-nspr      Use an NSPR that is already built and installed.
                           Use the 'nspr-config' script in the current path,
                           or look for the script in the directories given with
                           --with-nspr-exec-prefix or --with-nspr-prefix.
                           (Those flags are only checked if you specify
                           --with-system-nspr.)],
     _USE_SYSTEM_NSPR=1 )
 
-if test -z "$BUILDING_JS"; then
-    JS_THREADSAFE=1
-fi
-
 JS_POSIX_NSPR=unset
 ifdef([CONFIGURING_JS],[
     if test -n "$JS_STANDALONE"; then
       case "$target" in
         *linux*|*darwin*|*dragonfly*|*freebsd*|*netbsd*|*openbsd*)
-          if test -z "$_HAS_NSPR" && test "$JS_THREADSAFE"; then
+          if test -z "$_HAS_NSPR"; then
             JS_POSIX_NSPR_DEFAULT=1
           fi
           ;;
       esac
     fi
 
     MOZ_ARG_ENABLE_BOOL(posix-nspr-emulation,
 [  --enable-posix-nspr-emulation
@@ -105,26 +101,22 @@ fi
 if test -z "$nspr_opts"; then
     if test -z "$BUILDING_JS"; then
       dnl Toplevel configure defaults to using nsprpub from the source tree
       MOZ_BUILD_NSPR=1
       which_nspr="source-tree"
     else
       dnl JS configure defaults to emulated NSPR if available, falling back
       dnl to nsprpub.
-      if test -n "$JS_THREADSAFE"; then
-          JS_POSIX_NSPR="$JS_POSIX_NSPR_DEFAULT"
-          if test -z "$JS_POSIX_NSPR"; then
-             MOZ_BUILD_NSPR=1
-             which_nspr="source-tree"
-          else
-             which_nspr="posix-wrapper"
-          fi
+      JS_POSIX_NSPR="$JS_POSIX_NSPR_DEFAULT"
+      if test -z "$JS_POSIX_NSPR"; then
+        MOZ_BUILD_NSPR=1
+        which_nspr="source-tree"
       else
-          which_nspr="none"
+        which_nspr="posix-wrapper"
       fi
    fi
 fi
 
 if test -z "$nspr_opts" || test "$nspr_opts" = x; then
     AC_MSG_RESULT($which_nspr)
 else
     AC_MSG_ERROR([only one way of using NSPR may be selected. See 'configure --help'.])
@@ -159,17 +151,17 @@ if test -n "$MOZ_NATIVE_NSPR" -o -n "$NS
                 AC_MSG_ERROR([system NSPR does not support PR_STATIC_ASSERT or including prtypes.h does not provide it]))
     AC_TRY_COMPILE([#include "prtypes.h"],
                 [#ifndef PR_UINT64
                  #error PR_UINT64 not defined or requires including prtypes.h
                  #endif],
                 ,
                 AC_MSG_ERROR([system NSPR does not support PR_UINT64 or including prtypes.h does not provide it]))
     CFLAGS=$_SAVE_CFLAGS
-elif test -z "$JS_POSIX_NSPR" -a -n "$JS_THREADSAFE"; then
+elif test -z "$JS_POSIX_NSPR"; then
     if test -z "$LIBXUL_SDK"; then
         NSPR_CFLAGS="-I${LIBXUL_DIST}/include/nspr"
         if test -n "$GNU_CC"; then
             NSPR_LIBS="-L${LIBXUL_DIST}/lib -lnspr${NSPR_VERSION} -lplc${NSPR_VERSION} -lplds${NSPR_VERSION}"
         else
             NSPR_LIBS="${LIBXUL_DIST}/lib/nspr${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plc${NSPR_VERSION}.lib ${LIBXUL_DIST}/lib/plds${NSPR_VERSION}.lib "
         fi
     else
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -1,21 +1,25 @@
 #
 # 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/.
 
 from __future__ import with_statement
-import glob, logging, os, platform, shutil, subprocess, sys, tempfile, urllib2, zipfile
-import base64
-import re
+import logging
+from operator import itemgetter
 import os
+import platform
+import re
+import signal
+import subprocess
+import sys
+import tempfile
 from urlparse import urlparse
-from operator import itemgetter
-import signal
+import zipfile
 
 try:
   import mozinfo
 except ImportError:
   # Stub out fake mozinfo since this is not importable on Android 4.0 Opt.
   # This should be fixed; see
   # https://bugzilla.mozilla.org/show_bug.cgi?id=650881
   mozinfo = type('mozinfo', (), dict(info={}))()
@@ -552,17 +556,16 @@ def environment(xrePath, env=None, crash
     else:
       log.info(message)
 
   return env
 
 def dumpScreen(utilityPath):
   """dumps a screenshot of the entire screen to a directory specified by
   the MOZ_UPLOAD_DIR environment variable"""
-  import mozfile
 
   # Need to figure out which OS-dependent tool to use
   if mozinfo.isUnix:
     utility = [os.path.join(utilityPath, "screentopng")]
     utilityname = "screentopng"
   elif mozinfo.isMac:
     utility = ['/usr/sbin/screencapture', '-C', '-x', '-t', 'png']
     utilityname = "screencapture"
--- a/build/unix/elfhack/Makefile.in
+++ b/build/unix/elfhack/Makefile.in
@@ -22,16 +22,17 @@ test-array$(DLL_SUFFIX) test-ctors$(DLL_
 	$(TOOLCHAIN_PREFIX)readelf -d $@ | grep '($(DT_TYPE))'
 	@rm -f $@.bak
 	$(CURDIR)/elfhack -b -f $@
 	# Fail if the backup file doesn't exist
 	[ -f '$@.bak' ]
 	# Fail if the new library doesn't contain less relocations
 	[ $$($(TOOLCHAIN_PREFIX)objdump -R $@.bak | wc -l) -gt $$(objdump -R $@ | wc -l) ]
 
+test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): DSO_SONAME=$@
 test-array$(DLL_SUFFIX): DT_TYPE=INIT_ARRAY
 test-ctors$(DLL_SUFFIX): DT_TYPE=INIT
 
 .PRECIOUS: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX)
 
 GARBAGE += test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) test-array$(DLL_SUFFIX).bak test-ctors$(DLL_SUFFIX).bak
 
 ifndef CROSS_COMPILE
--- a/build/unix/elfhack/moz.build
+++ b/build/unix/elfhack/moz.build
@@ -2,24 +2,21 @@
 # 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/.
 
 NO_DIST_INSTALL = True
 DIRS += ['inject']
 
-SOURCES += [
-    'test-array.c',
-    'test-ctors.c',
-]
-
 if not CONFIG['CROSS_COMPILE']:
     SOURCES += [
         'dummy.c',
+        'test-array.c',
+        'test-ctors.c',
     ]
 
 HOST_SOURCES += [
     'elf.cpp',
     'elfhack.cpp',
 ]
 
 HOST_PROGRAM = 'elfhack'
--- a/config/Makefile.in
+++ b/config/Makefile.in
@@ -1,18 +1,14 @@
 # -*- 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/.
 
-# STDCXX_COMPAT is not needed here, and will actually fail because
-# libstdc++-compat is not built yet.
-MOZ_LIBSTDCXX_HOST_VERSION =
-
 # IMPORTANT: Disable NSBUILDROOT for this directory only, otherwise we have
 # a recursive rule for finding nsinstall and the Perl scripts.
 ifdef NSBUILDROOT
 override NSBUILDROOT :=
 endif
 
 ifdef GNU_CC
 MODULE_OPTIMIZE_FLAGS = -O3
@@ -24,17 +20,17 @@ ifneq (WINNT,$(HOST_OS_ARCH))
 ifdef COMPILE_ENVIRONMENT
 # Ensure nsinstall is atomically created
 nsinstall$(HOST_BIN_SUFFIX): $(HOST_PROGRAM)
 	cp $^ $@.tmp
 	mv $@.tmp $@
 
 NSINSTALL_EXECUTABLES := nsinstall$(HOST_BIN_SUFFIX)
 NSINSTALL_DEST := $(DIST)/bin
-NSINSTALL_TARGET := export
+NSINSTALL_TARGET := host
 INSTALL_TARGETS += NSINSTALL
 endif
 endif
 
 ifndef JS_STANDALONE
 HEADERS_FILES = \
 	$(DEPTH)/mozilla-config.h \
 	$(NULL)
--- a/config/config.mk
+++ b/config/config.mk
@@ -767,23 +767,16 @@ ifdef SYMBOL_ORDER
 EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER)
 endif
 EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB)
 
 ifneq (,$(MOZ_LIBSTDCXX_TARGET_VERSION)$(MOZ_LIBSTDCXX_HOST_VERSION))
 ifneq ($(OS_ARCH),Darwin)
 CHECK_STDCXX = @$(TOOLCHAIN_PREFIX)objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo 'TEST-UNEXPECTED-FAIL | check_stdcxx | We do not want these libstdc++ symbols to be used:' && $(TOOLCHAIN_PREFIX)objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && false || true
 endif
-
-ifdef MOZ_LIBSTDCXX_TARGET_VERSION
-OS_LIBS += $(call EXPAND_LIBNAME_PATH,stdc++compat,$(DEPTH)/build/unix/stdc++compat)
-endif
-ifdef MOZ_LIBSTDCXX_HOST_VERSION
-HOST_EXTRA_LIBS += $(call EXPAND_LIBNAME_PATH,host_stdc++compat,$(DEPTH)/build/unix/stdc++compat)
-endif
 endif
 
 ifeq (,$(filter $(OS_TARGET),WINNT Darwin))
 CHECK_TEXTREL = @$(TOOLCHAIN_PREFIX)readelf -d $(1) | grep TEXTREL > /dev/null && echo 'TEST-UNEXPECTED-FAIL | check_textrel | We do not want text relocations in libraries and programs' || true
 endif
 
 define CHECK_BINARY
 $(call CHECK_STDCXX,$(1))
--- a/config/makefiles/target_binaries.mk
+++ b/config/makefiles/target_binaries.mk
@@ -18,69 +18,69 @@ endif # IS_COMPONENT
 endif # SHARED_LIBRARY
 endif # !NO_DIST_INSTALL
 
 ifndef NO_DIST_INSTALL
 
 ifneq (,$(strip $(PROGRAM)$(SIMPLE_PROGRAMS)))
 PROGRAMS_EXECUTABLES = $(SIMPLE_PROGRAMS) $(PROGRAM)
 PROGRAMS_DEST ?= $(FINAL_TARGET)
-PROGRAMS_TARGET := binaries libs
+PROGRAMS_TARGET := binaries libs target
 INSTALL_TARGETS += PROGRAMS
 endif
 
 ifdef LIBRARY
 ifdef DIST_INSTALL
 ifdef IS_COMPONENT
 $(error Shipping static component libs makes no sense.)
 else
 DIST_LIBRARY_FILES = $(LIBRARY)
 DIST_LIBRARY_DEST ?= $(DIST)/lib
-DIST_LIBRARY_TARGET = binaries libs
+DIST_LIBRARY_TARGET = binaries libs target
 INSTALL_TARGETS += DIST_LIBRARY
 endif
 endif # DIST_INSTALL
 endif # LIBRARY
 
 
 ifdef SHARED_LIBRARY
 ifndef IS_COMPONENT
 SHARED_LIBRARY_FILES = $(SHARED_LIBRARY)
 SHARED_LIBRARY_DEST ?= $(FINAL_TARGET)
-SHARED_LIBRARY_TARGET = binaries libs
+SHARED_LIBRARY_TARGET = binaries libs target
 INSTALL_TARGETS += SHARED_LIBRARY
 
 ifneq (,$(filter WINNT,$(OS_ARCH)))
 ifndef NO_INSTALL_IMPORT_LIBRARY
 IMPORT_LIB_FILES = $(IMPORT_LIBRARY)
 endif # NO_INSTALL_IMPORT_LIBRARY
 else
 IMPORT_LIB_FILES = $(SHARED_LIBRARY)
 endif
 
 IMPORT_LIB_DEST ?= $(DIST)/lib
-IMPORT_LIB_TARGET = binaries libs
+IMPORT_LIB_TARGET = binaries libs target
 ifdef IMPORT_LIB_FILES
 INSTALL_TARGETS += IMPORT_LIB
 endif
 
 endif # ! IS_COMPONENT
 endif # SHARED_LIBRARY
 
 ifneq (,$(strip $(HOST_SIMPLE_PROGRAMS)$(HOST_PROGRAM)))
 HOST_PROGRAMS_EXECUTABLES = $(HOST_SIMPLE_PROGRAMS) $(HOST_PROGRAM)
 HOST_PROGRAMS_DEST ?= $(DIST)/host/bin
-HOST_PROGRAMS_TARGET = binaries libs
+HOST_PROGRAMS_TARGET = binaries libs host
 INSTALL_TARGETS += HOST_PROGRAMS
 endif
 
 ifdef HOST_LIBRARY
 HOST_LIBRARY_FILES = $(HOST_LIBRARY)
 HOST_LIBRARY_DEST ?= $(DIST)/host/lib
-HOST_LIBRARY_TARGET = binaries libs
+HOST_LIBRARY_TARGET = binaries libs host
 INSTALL_TARGETS += HOST_LIBRARY
 endif
 
 endif # !NO_DIST_INSTALL
 
 BINARIES_INSTALL_TARGETS := $(foreach category,$(INSTALL_TARGETS),$(if $(filter binaries,$($(category)_TARGET)),$(category)))
 
 # Fill a dependency file with all the binaries installed somewhere in $(DIST)
--- a/config/nsinstall.py
+++ b/config/nsinstall.py
@@ -122,17 +122,18 @@ def _nsinstall_internal(argv):
         entries = [os.path.join(srcpath, e) for e in os.listdir(srcpath)]
         copy_all_entries(entries, targetpath)
         # options.t is not relevant for directories
         if options.m:
           os.chmod(targetpath, options.m)
       else:
         if os.path.exists(targetpath):
           # On Windows, read-only files can't be deleted
-          os.chmod(targetpath, stat.S_IWUSR)
+          if sys.platform == "win32":
+            os.chmod(targetpath, stat.S_IWUSR)
           os.remove(targetpath)
         if options.t:
           shutil.copy2(srcpath, targetpath)
         else:
           shutil.copy(srcpath, targetpath)
 
   # the last argument is the target directory
   target = args.pop()
--- a/config/nspr/Makefile.in
+++ b/config/nspr/Makefile.in
@@ -24,20 +24,21 @@ EXTRA_MAKE_FLAGS := SHARED_LIBRARY= IMPO
 # See bug #838566.
 EXTRA_MAKE_FLAGS += XP_DEFINE=-DlibVersionPoint='libVersionPoint$$(LIBRARY_NAME)'
 else
 # nspr's make export compiles and links everything, but linking can't happen
 # during export on platforms where nspr is linked against mozcrt/mozglue.
 export:: EXTRA_MAKE_FLAGS := SHARED_LIBRARY= IMPORT_LIBRARY= SHARED_LIB_PDB=
 endif
 
-libs export clean distclean::
+clean distclean export::
 	$(MAKE) -C $(DEPTH)/nsprpub $@ $(EXTRA_MAKE_FLAGS)
 
-libs::
+target::
+	$(MAKE) -C $(DEPTH)/nsprpub libs $(EXTRA_MAKE_FLAGS)
 	$(MAKE) -C $(DEPTH)/nsprpub install prefix=$(ABS_DIST)/sdk exec_prefix=$(ABS_DIST)/sdk bindir=$(ABS_DIST)/sdk/dummy includedir=$(ABS_DIST)/include/nspr libdir=$(ABS_DIST)/sdk/lib datadir=$(ABS_DIST)/sdk/dummy DESTDIR= $(EXTRA_MAKE_FLAGS)
 	$(INSTALL) $(DEPTH)/nsprpub/config/nspr-config $(DIST)/sdk/bin
 	$(RM) -rf $(DIST)/sdk/dummy
 ifneq (,$(filter WINNT,$(OS_ARCH))) # {
 	$(RM) -f $(DIST)/sdk/lib/$(DLL_PREFIX)nspr4$(DLL_SUFFIX) $(DIST)/sdk/lib/$(DLL_PREFIX)plc4$(DLL_SUFFIX) $(DIST)/sdk/lib/$(DLL_PREFIX)plds4$(DLL_SUFFIX)
 	$(RM) -f $(DIST)/sdk/lib/$(LIB_PREFIX)nspr4_s.$(LIB_SUFFIX) $(DIST)/sdk/lib/$(LIB_PREFIX)plc4_s.$(LIB_SUFFIX) $(DIST)/sdk/lib/$(LIB_PREFIX)plds4_s.$(LIB_SUFFIX)
 else # } {
 	$(RM) -f $(DIST)/sdk/lib/$(LIB_PREFIX)nspr4.$(LIB_SUFFIX) $(DIST)/sdk/lib/$(LIB_PREFIX)plc4.$(LIB_SUFFIX) $(DIST)/sdk/lib/$(LIB_PREFIX)plds4.$(LIB_SUFFIX)
--- a/config/recurse.mk
+++ b/config/recurse.mk
@@ -71,35 +71,62 @@ endif
 # TIERS (like for js/src).
 CURRENT_DIRS := $($(CURRENT_TIER)_dirs)
 
 ifneq (,$(filter binaries libs,$(CURRENT_TIER)))
 WANT_STAMPS = 1
 STAMP_TOUCH = $(TOUCH) $(@D)/binaries
 endif
 
+# The compile tier has different rules from other tiers.
+ifeq ($(CURRENT_TIER),compile)
+
+# Need a list of compile targets because we can't use pattern rules:
+# https://savannah.gnu.org/bugs/index.php?42833
+.PHONY: $(compile_targets)
+$(compile_targets):
+	$(call SUBMAKE,$(if $(filter $(@D),$(staticdirs)),,$(@F)),$(@D))
+
+else
+
 # Recursion rule for all directories traversed for all subtiers in the
 # current tier.
 $(addsuffix /$(CURRENT_TIER),$(CURRENT_DIRS)): %/$(CURRENT_TIER):
-	$(call SUBMAKE,$(if $(filter $*,$(staticdirs)),,$(CURRENT_TIER)),$*)
+	$(call SUBMAKE,$(CURRENT_TIER),$*)
 # Ensure existing stamps are up-to-date, but don't create one if submake didn't create one.
 	$(if $(wildcard $@),@$(STAMP_TOUCH))
 
 ifndef STAMP_TOUCH
 .PHONY: $(addsuffix /$(CURRENT_TIER),$(CURRENT_DIRS))
 endif
 
 # Dummy rules for possibly inexisting dependencies for the above tier targets
 $(addsuffix /Makefile,$(CURRENT_DIRS)) $(addsuffix /backend.mk,$(CURRENT_DIRS)):
 
+# At least build/export requires config/export for buildid, but who knows what
+# else, so keep this global dependency to make config/export first for now.
+$(addsuffix /$(CURRENT_TIER),$(filter-out config,$(CURRENT_DIRS))): config/$(CURRENT_TIER)
+
 # The export tier requires nsinstall, which is built from config. So every
-# subdirectory traversal needs to happen after traversing config.
+# subdirectory traversal needs to happen after building nsinstall in config, which
+# is done with the config/host target. Note the config/host target only exists if
+# nsinstall is actually built, which it is not on Windows, because we use
+# nsinstall.py there.
 ifeq ($(CURRENT_TIER),export)
-$(addsuffix /$(CURRENT_TIER),$(filter-out config,$(CURRENT_DIRS))): config/$(CURRENT_TIER)
+ifneq (,$(filter config/host, $(compile_targets)))
+$(addsuffix /$(CURRENT_TIER),$(CURRENT_DIRS)): config/host
+
+# Ensure rules for config/host and its possible dependencies.
+.PHONY: $(filter %/host, $(compile_targets))
+$(filter %/host, $(compile_targets)):
+	$(call SUBMAKE,host,$(@D))
 endif
+endif
+
+endif # ifeq ($(CURRENT_TIER),compile)
 
 ifdef COMPILE_ENVIRONMENT
 # Disable dependency aggregation on PGO builds because of bug 934166.
 ifeq (,$(MOZ_PGO)$(MOZ_PROFILE_USE)$(MOZ_PROFILE_GENERATE))
 ifneq (,$(filter libs binaries,$(CURRENT_TIER)))
 # When doing a "libs" build, target_libs.mk ensures the interesting dependency data
 # is available in the "binaries" stamp. Once recursion is done, aggregate all that
 # dependency info so that stamps depend on relevant files and relevant other stamps.
@@ -166,17 +193,17 @@ endif
 $(1):: $$(SUBMAKEFILES)
 ifdef PARALLEL_DIRS
 	+@$(MAKE) $$(PARALLEL_DIRS_$(1))
 endif
 	$$(LOOP_OVER_DIRS)
 
 endef
 
-$(foreach subtier,export compile binaries libs tools,$(eval $(call CREATE_SUBTIER_TRAVERSAL_RULE,$(subtier))))
+$(foreach subtier,export binaries libs tools,$(eval $(call CREATE_SUBTIER_TRAVERSAL_RULE,$(subtier))))
 
 tools export:: $(SUBMAKEFILES)
 	$(LOOP_OVER_TOOL_DIRS)
 
 endif # ifdef TIERS
 
 endif # ifeq ($(NO_RECURSE_MAKELEVEL),$(MAKELEVEL))
 
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -81,18 +81,20 @@ ifdef COMPILE_ENVIRONMENT
 # Compile the tests to $(DIST)/bin.  Make lots of niceties available by default
 # through TestHarness.h, by modifying the list of includes and the libs against
 # which stuff links.
 SIMPLE_PROGRAMS += $(CPP_UNIT_TESTS)
 INCLUDES += -I$(DIST)/include/testing
 LIBS += $(NSPR_LIBS)
 
 ifndef MOZ_PROFILE_GENERATE
-libs:: $(CPP_UNIT_TESTS) $(call mkdir_deps,$(DIST)/cppunittests)
-	$(NSINSTALL) $(CPP_UNIT_TESTS) $(DIST)/cppunittests
+CPP_UNIT_TESTS_FILES = $(CPP_UNIT_TESTS)
+CPP_UNIT_TESTS_DEST = $(DIST)/cppunittests
+CPP_UNIT_TESTS_TARGET = binaries libs target
+INSTALL_TARGETS += CPP_UNIT_TESTS
 endif
 
 run-cppunittests::
 	@$(PYTHON) $(topsrcdir)/testing/runcppunittests.py --xre-path=$(DIST)/bin --symbols-path=$(DIST)/crashreporter-symbols $(CPP_UNIT_TESTS)
 
 cppunittests-remote: DM_TRANS?=adb
 cppunittests-remote:
 	@if [ '${TEST_DEVICE}' != '' -o '$(DM_TRANS)' = 'adb' ]; then \
@@ -584,17 +586,21 @@ HOST_LIBS_DEPS = $(filter %.$(LIB_SUFFIX
 
 # Dependencies which, if modified, should cause everything to rebuild
 GLOBAL_DEPS += Makefile $(DEPTH)/config/autoconf.mk $(topsrcdir)/config/config.mk
 
 ##############################################
 ifdef COMPILE_ENVIRONMENT
 OBJ_TARGETS = $(OBJS) $(PROGOBJS) $(HOST_OBJS) $(HOST_PROGOBJS)
 
-compile:: $(OBJ_TARGETS)
+compile:: host target
+
+host:: $(HOST_LIBRARY) $(HOST_PROGRAM) $(HOST_SIMPLE_PROGRAMS)
+
+target:: $(LIBRARY) $(SHARED_LIBRARY) $(PROGRAM) $(SIMPLE_PROGRAMS)
 
 include $(topsrcdir)/config/makefiles/target_binaries.mk
 endif
 
 ifdef IS_TOOL_DIR
 # One would think "tools:: libs" would work, but it turns out that combined with
 # bug 907365, this makes make forget to run some rules sometimes.
 tools::
@@ -710,17 +716,17 @@ ifdef MSMANIFEST_TOOL
 	fi
 endif	# MSVC with manifest tool
 ifdef MOZ_PROFILE_GENERATE
 # touch it a few seconds into the future to work around FAT's
 # 2-second granularity
 	touch -t `date +%Y%m%d%H%M.%S -d 'now+5seconds'` pgo.relink
 endif
 else # !WINNT || GNU_CC
-	$(EXPAND_CCC) -o $@ $(CXXFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(LIBS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE) $(STLPORT_LIBS)
+	$(EXPAND_CCC) -o $@ $(CXXFLAGS) $(PROGOBJS) $(RESFILE) $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(EXE_DEF_FILE) $(STLPORT_LIBS)
 	$(call CHECK_BINARY,$@)
 endif # WINNT && !GNU_CC
 
 ifdef ENABLE_STRIP
 	$(STRIP) $(STRIP_FLAGS) $@
 endif
 ifdef MOZ_POST_PROGRAM_COMMAND
 	$(MOZ_POST_PROGRAM_COMMAND) $@
@@ -766,17 +772,17 @@ ifeq (_WINNT,$(GNU_CC)_$(OS_ARCH))
 	$(EXPAND_LD) -nologo -out:$@ -pdb:$(LINK_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS)
 ifdef MSMANIFEST_TOOL
 	@if test -f $@.manifest; then \
 		mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \
 		rm -f $@.manifest; \
 	fi
 endif	# MSVC with manifest tool
 else
-	$(EXPAND_CCC) $(CXXFLAGS) -o $@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(LIBS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(STLPORT_LIBS)
+	$(EXPAND_CCC) $(CXXFLAGS) -o $@ $< $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(WRAP_LDFLAGS) $(MOZ_GLUE_PROGRAM_LDFLAGS) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) $(BIN_FLAGS) $(STLPORT_LIBS)
 	$(call CHECK_BINARY,$@)
 endif # WINNT && !GNU_CC
 
 ifdef ENABLE_STRIP
 	$(STRIP) $(STRIP_FLAGS) $@
 endif
 ifdef MOZ_POST_PROGRAM_COMMAND
 	$(MOZ_POST_PROGRAM_COMMAND) $@
@@ -1262,24 +1268,27 @@ endif
 
 ################################################################################
 # SDK
 
 ifneq (,$(SDK_LIBRARY))
 ifndef NO_DIST_INSTALL
 SDK_LIBRARY_FILES := $(SDK_LIBRARY)
 SDK_LIBRARY_DEST := $(SDK_LIB_DIR)
+SDK_LIBRARY_TARGET := binaries libs target
 INSTALL_TARGETS += SDK_LIBRARY
 endif
 endif # SDK_LIBRARY
 
 ifneq (,$(strip $(SDK_BINARY)))
 ifndef NO_DIST_INSTALL
 SDK_BINARY_FILES := $(SDK_BINARY)
 SDK_BINARY_DEST := $(SDK_BIN_DIR)
+# SDK_BINARY_TARGET is set in xpcom/idl-parser/Makefile.in
+SDK_BINARY_TARGET ?= binaries libs target
 INSTALL_TARGETS += SDK_BINARY
 endif
 endif # SDK_BINARY
 
 ################################################################################
 # CHROME PACKAGING
 
 chrome::
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -76,28 +76,42 @@ enum {
   // Set if the element has a pending animation style change.
   ELEMENT_HAS_PENDING_ANIMATION_RESTYLE =       ELEMENT_FLAG_BIT(2),
 
   // Set if the element is a potential animation restyle root (that is,
   // has an animation style change pending _and_ that style change
   // will attempt to restyle descendants).
   ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT = ELEMENT_FLAG_BIT(3),
 
+  // Set if the element has a pending animation-only style change as
+  // part of an animation-only style update (where we update styles from
+  // animation to the current refresh tick, but leave everything else as
+  // it was).
+  ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE =  ELEMENT_FLAG_BIT(4),
+
+  // Set if the element is a potential animation-only restyle root (that
+  // is, has an animation-only style change pending _and_ that style
+  // change will attempt to restyle descendants).
+  ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT = ELEMENT_FLAG_BIT(5),
+
   // All of those bits together, for convenience.
   ELEMENT_ALL_RESTYLE_FLAGS = ELEMENT_HAS_PENDING_RESTYLE |
                               ELEMENT_IS_POTENTIAL_RESTYLE_ROOT |
                               ELEMENT_HAS_PENDING_ANIMATION_RESTYLE |
-                              ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT,
+                              ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT |
+                              ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE |
+                              ELEMENT_IS_POTENTIAL_ANIMATION_ONLY_RESTYLE_ROOT,
 
   // Just the HAS_PENDING bits, for convenience
   ELEMENT_PENDING_RESTYLE_FLAGS = ELEMENT_HAS_PENDING_RESTYLE |
-                                  ELEMENT_HAS_PENDING_ANIMATION_RESTYLE,
+                                  ELEMENT_HAS_PENDING_ANIMATION_RESTYLE |
+                                  ELEMENT_HAS_PENDING_ANIMATION_ONLY_RESTYLE,
 
   // Remaining bits are for subclasses
-  ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 4
+  ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 6
 };
 
 #undef ELEMENT_FLAG_BIT
 
 // Make sure we have space for our bits
 ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET);
 
 namespace mozilla {
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -2178,16 +2178,22 @@ public:
 
   /**
    * Returns whether a given header is forbidden for a system XHR
    * request.
    */
   static bool IsForbiddenSystemRequestHeader(const nsACString& aHeader);
 
   /**
+   * Returns whether a given Content-Type header value is allowed
+   * for a non-CORS XHR or fetch request.
+   */
+  static bool IsAllowedNonCorsContentType(const nsACString& aHeaderValue);
+
+  /**
    * Returns whether a given header is forbidden for an XHR or fetch
    * response.
    */
   static bool IsForbiddenResponseHeader(const nsACString& aHeader);
 
 private:
   static bool InitializeEventTable();
 
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -146,45 +146,43 @@ enum {
   // child's later siblings must also be restyled.
   NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS = NODE_FLAG_BIT(13),
 
   NODE_ALL_SELECTOR_FLAGS =               NODE_HAS_EMPTY_SELECTOR |
                                           NODE_HAS_SLOW_SELECTOR |
                                           NODE_HAS_EDGE_CHILD_SELECTOR |
                                           NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS,
 
-  NODE_ATTACH_BINDING_ON_POSTCREATE =     NODE_FLAG_BIT(14),
-
   // This node needs to go through frame construction to get a frame (or
   // undisplayed entry).
-  NODE_NEEDS_FRAME =                      NODE_FLAG_BIT(15),
+  NODE_NEEDS_FRAME =                      NODE_FLAG_BIT(14),
 
   // At least one descendant in the flattened tree has NODE_NEEDS_FRAME set.
   // This should be set on every node on the flattened tree path between the
   // node(s) with NODE_NEEDS_FRAME and the root content.
-  NODE_DESCENDANTS_NEED_FRAMES =          NODE_FLAG_BIT(16),
+  NODE_DESCENDANTS_NEED_FRAMES =          NODE_FLAG_BIT(15),
 
   // Set if the node has the accesskey attribute set.
-  NODE_HAS_ACCESSKEY =                    NODE_FLAG_BIT(17),
+  NODE_HAS_ACCESSKEY =                    NODE_FLAG_BIT(16),
 
   // Set if the node has right-to-left directionality
-  NODE_HAS_DIRECTION_RTL =                NODE_FLAG_BIT(18),
+  NODE_HAS_DIRECTION_RTL =                NODE_FLAG_BIT(17),
 
   // Set if the node has left-to-right directionality
-  NODE_HAS_DIRECTION_LTR =                NODE_FLAG_BIT(19),
+  NODE_HAS_DIRECTION_LTR =                NODE_FLAG_BIT(18),
 
   NODE_ALL_DIRECTION_FLAGS =              NODE_HAS_DIRECTION_LTR |
                                           NODE_HAS_DIRECTION_RTL,
 
-  NODE_CHROME_ONLY_ACCESS =               NODE_FLAG_BIT(20),
+  NODE_CHROME_ONLY_ACCESS =               NODE_FLAG_BIT(19),
 
-  NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS =    NODE_FLAG_BIT(21),
+  NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS =    NODE_FLAG_BIT(20),
 
   // Remaining bits are node type specific.
-  NODE_TYPE_SPECIFIC_BITS_OFFSET =        22
+  NODE_TYPE_SPECIFIC_BITS_OFFSET =        21
 };
 
 // Make sure we have space for our bits
 #define ASSERT_NODE_FLAGS_SPACE(n) \
   static_assert(WRAPPER_CACHE_FLAGS_BITS_USED + (n) <=                          \
                   sizeof(nsWrapperCache::FlagsType) * 8,                        \
                 "Not enough space for our bits")
 ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET);
@@ -989,17 +987,16 @@ public:
   }
 #endif
 
   void SetFlags(FlagsType aFlagsToSet)
   {
     NS_ASSERTION(!(aFlagsToSet & (NODE_IS_ANONYMOUS_ROOT |
                                   NODE_IS_NATIVE_ANONYMOUS_ROOT |
                                   NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE |
-                                  NODE_ATTACH_BINDING_ON_POSTCREATE |
                                   NODE_DESCENDANTS_NEED_FRAMES |
                                   NODE_NEEDS_FRAME |
                                   NODE_CHROME_ONLY_ACCESS)) ||
                  IsNodeOfType(eCONTENT),
                  "Flag only permitted on nsIContent nodes");
     nsWrapperCache::SetFlags(aFlagsToSet);
   }
 
--- a/content/base/src/WebSocket.cpp
+++ b/content/base/src/WebSocket.cpp
@@ -674,27 +674,38 @@ WebSocket::Init(JSContext* aCx,
 
   // parses the url
   rv = ParseURL(PromiseFlatString(aURL));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsIScriptContext* sc = GetContextForEventHandlers(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  nsCOMPtr<nsIDocument> originDoc = nsContentUtils::GetDocumentFromScriptContext(sc);
-
   // Don't allow https:// to open ws://
   if (!mSecure &&
       !Preferences::GetBool("network.websocket.allowInsecureFromHTTPS",
                             false)) {
     // Confirmed we are opening plain ws:// and want to prevent this from a
-    // secure context (e.g. https). Check the security context of the document
-    // associated with this script, which is the same as associated with mOwner.
-    if (originDoc && originDoc->GetSecurityInfo()) {
-      return NS_ERROR_DOM_SECURITY_ERR;
+    // secure context (e.g. https). Check the principal's uri to determine if
+    // we were loaded from https.
+    nsCOMPtr<nsIGlobalObject> globalObject(BrokenGetEntryGlobal());
+    if (globalObject) {
+      nsCOMPtr<nsIPrincipal> principal(globalObject->PrincipalOrNull());
+      if (principal) {
+        nsCOMPtr<nsIURI> uri;
+        principal->GetURI(getter_AddRefs(uri));
+        if (uri) {
+          bool originIsHttps = false;
+          rv = uri->SchemeIs("https", &originIsHttps);
+          NS_ENSURE_SUCCESS(rv,rv);
+          if (originIsHttps) {
+            return NS_ERROR_DOM_SECURITY_ERR;
+          }
+        }
+      }
     }
   }
 
   // Assign the sub protocol list and scan it for illegal values
   for (uint32_t index = 0; index < aProtocolArray.Length(); ++index) {
     for (uint32_t i = 0; i < aProtocolArray[index].Length(); ++i) {
       if (aProtocolArray[index][i] < static_cast<char16_t>(0x0021) ||
           aProtocolArray[index][i] > static_cast<char16_t>(0x007E))
@@ -705,16 +716,17 @@ WebSocket::Init(JSContext* aCx,
       mRequestedProtocolList.AppendLiteral(", ");
     }
 
     AppendUTF16toUTF8(aProtocolArray[index], mRequestedProtocolList);
   }
 
   // Check content policy.
   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+  nsCOMPtr<nsIDocument> originDoc = nsContentUtils::GetDocumentFromScriptContext(sc);
   rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_WEBSOCKET,
                                  mURI,
                                  mPrincipal,
                                  originDoc,
                                  EmptyCString(),
                                  nullptr,
                                  &shouldLoad,
                                  nsContentUtils::GetContentPolicy(),
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -6900,16 +6900,33 @@ nsContentUtils::IsForbiddenSystemRequest
 // static
 bool
 nsContentUtils::IsForbiddenResponseHeader(const nsACString& aHeader)
 {
   return (aHeader.LowerCaseEqualsASCII("set-cookie") ||
           aHeader.LowerCaseEqualsASCII("set-cookie2"));
 }
 
+// static
+bool
+nsContentUtils::IsAllowedNonCorsContentType(const nsACString& aHeaderValue)
+{
+  nsAutoCString contentType;
+  nsAutoCString unused;
+
+  nsresult rv = NS_ParseContentType(aHeaderValue, contentType, unused);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  return contentType.LowerCaseEqualsLiteral("text/plain") ||
+         contentType.LowerCaseEqualsLiteral("application/x-www-form-urlencoded") ||
+         contentType.LowerCaseEqualsLiteral("multipart/form-data");
+}
+
 bool
 nsContentUtils::DOMWindowDumpEnabled()
 {
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
   // In optimized builds we check a pref that controls if we should
   // enable output from dump() or not, in debug builds it's always
   // enabled.
   return nsContentUtils::sDOMWindowDumpEnabled;
--- a/content/base/src/nsCrossSiteListenerProxy.cpp
+++ b/content/base/src/nsCrossSiteListenerProxy.cpp
@@ -499,56 +499,16 @@ nsCORSListenerProxy::OnStartRequest(nsIR
     mOuterListener->OnStartRequest(aRequest, aContext);
 
     return NS_ERROR_DOM_BAD_URI;
   }
 
   return mOuterListener->OnStartRequest(aRequest, aContext);
 }
 
-bool
-IsValidHTTPToken(const nsCSubstring& aToken)
-{
-  if (aToken.IsEmpty()) {
-    return false;
-  }
-
-  nsCSubstring::const_char_iterator iter, end;
-
-  aToken.BeginReading(iter);
-  aToken.EndReading(end);
-
-  while (iter != end) {
-    if (*iter <= 32 ||
-        *iter >= 127 ||
-        *iter == '(' ||
-        *iter == ')' ||
-        *iter == '<' ||
-        *iter == '>' ||
-        *iter == '@' ||
-        *iter == ',' ||
-        *iter == ';' ||
-        *iter == ':' ||
-        *iter == '\\' ||
-        *iter == '\"' ||
-        *iter == '/' ||
-        *iter == '[' ||
-        *iter == ']' ||
-        *iter == '?' ||
-        *iter == '=' ||
-        *iter == '{' ||
-        *iter == '}') {
-      return false;
-    }
-    ++iter;
-  }
-
-  return true;
-}
-
 nsresult
 nsCORSListenerProxy::CheckRequestApproved(nsIRequest* aRequest)
 {
   // Check if this was actually a cross domain request
   if (!mHasBeenCrossSite) {
     return NS_OK;
   }
 
@@ -611,17 +571,17 @@ nsCORSListenerProxy::CheckRequestApprove
                          mPreflightMethod.EqualsLiteral("HEAD") ||
                          mPreflightMethod.EqualsLiteral("POST");
     nsCCharSeparatedTokenizer methodTokens(headerVal, ',');
     while(methodTokens.hasMoreTokens()) {
       const nsDependentCSubstring& method = methodTokens.nextToken();
       if (method.IsEmpty()) {
         continue;
       }
-      if (!IsValidHTTPToken(method)) {
+      if (!NS_IsValidHTTPToken(method)) {
         return NS_ERROR_DOM_BAD_URI;
       }
       foundMethod |= mPreflightMethod.Equals(method);
     }
     NS_ENSURE_TRUE(foundMethod, NS_ERROR_DOM_BAD_URI);
 
     // The "Access-Control-Allow-Headers" header contains a comma separated
     // list of header names.
@@ -630,17 +590,17 @@ nsCORSListenerProxy::CheckRequestApprove
                             headerVal);
     nsTArray<nsCString> headers;
     nsCCharSeparatedTokenizer headerTokens(headerVal, ',');
     while(headerTokens.hasMoreTokens()) {
       const nsDependentCSubstring& header = headerTokens.nextToken();
       if (header.IsEmpty()) {
         continue;
       }
-      if (!IsValidHTTPToken(header)) {
+      if (!NS_IsValidHTTPToken(header)) {
         return NS_ERROR_DOM_BAD_URI;
       }
       headers.AppendElement(header);
     }
     for (uint32_t i = 0; i < mPreflightHeaders.Length(); ++i) {
       if (!headers.Contains(mPreflightHeaders[i],
                             nsCaseInsensitiveCStringArrayComparator())) {
         return NS_ERROR_DOM_BAD_URI;
--- a/content/base/src/nsCrossSiteListenerProxy.h
+++ b/content/base/src/nsCrossSiteListenerProxy.h
@@ -16,19 +16,16 @@
 #include "nsIChannelEventSink.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "mozilla/Attributes.h"
 
 class nsIURI;
 class nsIParser;
 class nsIPrincipal;
 
-extern bool
-IsValidHTTPToken(const nsCSubstring& aToken);
-
 nsresult
 NS_StartCORSPreflight(nsIChannel* aRequestChannel,
                       nsIStreamListener* aListener,
                       nsIPrincipal* aPrincipal,
                       bool aWithCredentials,
                       nsTArray<nsCString>& aACUnsafeHeaders,
                       nsIChannel** aPreflightChannel);
 
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -1290,17 +1290,17 @@ nsXMLHttpRequest::IsSafeHeader(const nsA
                         headerVal);
   nsCCharSeparatedTokenizer exposeTokens(headerVal, ',');
   bool isSafe = false;
   while (exposeTokens.hasMoreTokens()) {
     const nsDependentCSubstring& token = exposeTokens.nextToken();
     if (token.IsEmpty()) {
       continue;
     }
-    if (!IsValidHTTPToken(token)) {
+    if (!NS_IsValidHTTPToken(token)) {
       return false;
     }
     if (header.Equals(token, nsCaseInsensitiveCStringComparator())) {
       isSafe = true;
     }
   }
   return isSafe;
 }
@@ -2864,23 +2864,17 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
     }
   }
 
   if (httpChannel) {
     nsAutoCString contentTypeHeader;
     rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("Content-Type"),
                                        contentTypeHeader);
     if (NS_SUCCEEDED(rv)) {
-      nsAutoCString contentType, charset;
-      rv = NS_ParseContentType(contentTypeHeader, contentType, charset);
-      NS_ENSURE_SUCCESS(rv, rv);
-  
-      if (!contentType.LowerCaseEqualsLiteral("text/plain") &&
-          !contentType.LowerCaseEqualsLiteral("application/x-www-form-urlencoded") &&
-          !contentType.LowerCaseEqualsLiteral("multipart/form-data")) {
+      if (!nsContentUtils::IsAllowedNonCorsContentType(contentTypeHeader)) {
         mCORSUnsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
       }
     }
   }
 
   ResetResponse();
 
   rv = CheckChannelForCrossSiteRequest(mChannel);
@@ -3094,17 +3088,17 @@ nsXMLHttpRequest::SetRequestHeader(const
   // Step 1 and 2
   if (!(mState & XML_HTTP_REQUEST_OPENED)) {
     return NS_ERROR_DOM_INVALID_STATE_ERR;
   }
   NS_ASSERTION(mChannel, "mChannel must be valid if we're OPENED.");
 
   // Step 3
   // Make sure we don't store an invalid header name in mCORSUnsafeHeaders
-  if (!IsValidHTTPToken(header)) { // XXX nsHttp::IsValidToken?
+  if (!NS_IsValidHTTPToken(header)) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   // Check that we haven't already opened the channel. We can't rely on
   // the channel throwing from mChannel->SetRequestHeader since we might
   // still be waiting for mCORSPreflightChannel to actually open mChannel
   if (mCORSPreflightChannel) {
     bool pending;
--- a/content/media/AudioSink.cpp
+++ b/content/media/AudioSink.cpp
@@ -134,21 +134,23 @@ AudioSink::AudioLoop()
   SINK_LOG("AudioLoop started");
 
   if (NS_FAILED(InitializeAudioStream())) {
     NS_WARNING("Initializing AudioStream failed.");
     return;
   }
 
   while (1) {
-    WaitForAudioToPlay();
-    if (!IsPlaybackContinuing()) {
-      break;
+    {
+      ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+      WaitForAudioToPlay();
+      if (!IsPlaybackContinuing()) {
+        break;
+      }
     }
-
     // See if there's a gap in the audio. If there is, push silence into the
     // audio hardware, so we can play across the gap.
     // Calculate the timestamp of the next chunk of audio in numbers of
     // samples.
     NS_ASSERTION(AudioQueue().GetSize() > 0, "Should have data to play");
     CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate);
 
     // Calculate the number of frames that have been pushed onto the audio hardware.
@@ -171,17 +173,18 @@ AudioSink::AudioLoop()
       mWritten += PlayFromAudioQueue();
     }
     int64_t endTime = GetEndTime();
     if (endTime != -1) {
       mStateMachine->OnAudioEndTimeUpdate(endTime);
     }
   }
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  if (!mStopAudioThread && AudioQueue().AtEndOfStream()) {
+  MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream());
+  if (!mStopAudioThread && mPlaying) {
     Drain();
   }
   SINK_LOG("AudioLoop complete");
   Cleanup();
   SINK_LOG("AudioLoop exit");
 }
 
 nsresult
@@ -233,29 +236,29 @@ AudioSink::ExpectMoreAudioData()
   return AudioQueue().GetSize() == 0 && !AudioQueue().IsFinished();
 }
 
 void
 AudioSink::WaitForAudioToPlay()
 {
   // Wait while we're not playing, and we're not shutting down, or we're
   // playing and we've got no audio to play.
-  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  AssertCurrentThreadInMonitor();
   while (!mStopAudioThread && (!mPlaying || ExpectMoreAudioData())) {
     if (!mPlaying && !mAudioStream->IsPaused()) {
       mAudioStream->Pause();
     }
-    mon.Wait();
+    GetReentrantMonitor().Wait();
   }
 }
 
 bool
 AudioSink::IsPlaybackContinuing()
 {
-  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
+  AssertCurrentThreadInMonitor();
   if (mPlaying && mAudioStream->IsPaused()) {
     mAudioStream->Resume();
   }
 
   // If we're shutting down, captured, or at EOS, break out and exit the audio
   // thread.
   if (mStopAudioThread || AudioQueue().AtEndOfStream()) {
     return false;
--- a/content/media/AudioStream.cpp
+++ b/content/media/AudioStream.cpp
@@ -545,16 +545,23 @@ AudioStream::Init(int32_t aNumChannels, 
 }
 
 // This code used to live inside AudioStream::Init(), but on Mac (others?)
 // it has been known to take 300-800 (or even 8500) ms to execute(!)
 nsresult
 AudioStream::OpenCubeb(cubeb_stream_params &aParams,
                        LatencyRequest aLatencyRequest)
 {
+  {
+    MonitorAutoLock mon(mMonitor);
+    if (mState == AudioStream::SHUTDOWN) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
   cubeb* cubebContext = GetCubebContext();
   if (!cubebContext) {
     MonitorAutoLock mon(mMonitor);
     mState = AudioStream::ERRORED;
     return NS_ERROR_FAILURE;
   }
 
   // If the latency pref is set, use it. Otherwise, if this stream is intended
--- a/content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
+++ b/content/media/encoder/fmp4_muxer/ISOMediaWriter.cpp
@@ -133,23 +133,21 @@ ISOMediaWriter::WriteEncodedTrack(const 
   // inside the for-loop.
   if (frag && (aFlags & END_OF_STREAM)) {
     frag->SetEndOfStream();
   }
 
   nsresult rv;
   bool EOS;
   if (ReadyToRunState(EOS)) {
-    // TODO:
-    // The MediaEncoder doesn't use nsRunnable, so thread will be
-    // stocked on that part and the new added nsRunnable won't get to run
-    // before MediaEncoder completing. Before MediaEncoder change, it needs
-    // to call RunState directly.
-    // https://bugzilla.mozilla.org/show_bug.cgi?id=950429
-    rv = RunState();
+    // Because track encoder won't generate new data after EOS, it needs to make
+    // sure the state reaches MUXING_DONE when EOS is signaled.
+    do {
+      rv = RunState();
+    } while (EOS && mState != MUXING_DONE);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
 
 bool
 ISOMediaWriter::ReadyToRunState(bool& aEOS)
--- a/content/media/fmp4/BlankDecoderModule.cpp
+++ b/content/media/fmp4/BlankDecoderModule.cpp
@@ -72,16 +72,17 @@ public:
     return NS_OK;
   }
 
   virtual nsresult Flush() MOZ_OVERRIDE {
     return NS_OK;
   }
 
   virtual nsresult Drain() MOZ_OVERRIDE {
+    mCallback->DrainComplete();
     return NS_OK;
   }
 
 private:
   nsAutoPtr<BlankMediaDataCreator> mCreator;
   nsAutoPtr<MediaData> mOutput;
   RefPtr<MediaTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
--- a/content/media/fmp4/MP4Reader.cpp
+++ b/content/media/fmp4/MP4Reader.cpp
@@ -344,51 +344,55 @@ MP4Reader::Decode(TrackType aTrack)
     // if we need output.
     while (prevNumFramesOutput == data.mNumSamplesOutput &&
            (data.mInputExhausted ||
            (data.mNumSamplesInput - data.mNumSamplesOutput) < data.mDecodeAhead)) {
       data.mMonitor.AssertCurrentThreadOwns();
       data.mMonitor.Unlock();
       nsAutoPtr<MP4Sample> compressed(PopSample(aTrack));
       if (!compressed) {
-        // EOS, or error. Let the state machine know there are no more
-        // frames coming.
+        // EOS, or error. Send the decoder a signal to drain.
         LOG("Draining %s", TrackTypeToStr(aTrack));
+        data.mMonitor.Lock();
+        data.mDrainComplete = false;
+        data.mMonitor.Unlock();
         data.mDecoder->Drain();
-        return false;
       } else {
 #ifdef LOG_SAMPLE_DECODE
         LOG("PopSample %s time=%lld dur=%lld", TrackTypeToStr(aTrack),
             compressed->composition_timestamp, compressed->duration);
 #endif
-      }
-      data.mMonitor.Lock();
-      data.mInputExhausted = false;
-      data.mNumSamplesInput++;
-      data.mMonitor.Unlock();
+        data.mMonitor.Lock();
+        data.mDrainComplete = false;
+        data.mInputExhausted = false;
+        data.mNumSamplesInput++;
+        data.mMonitor.Unlock();
 
-      if (NS_FAILED(data.mDecoder->Input(compressed))) {
-        return false;
+        if (NS_FAILED(data.mDecoder->Input(compressed))) {
+          return false;
+        }
+        // If Input() failed, we let the auto pointer delete |compressed|.
+        // Otherwise, we assume the decoder will delete it when it's finished
+        // with it.
+        compressed.forget();
       }
-      // If Input() failed, we let the auto pointer delete |compressed|.
-      // Otherwise, we assume the decoder will delete it when it's finished
-      // with it.
-      compressed.forget();
       data.mMonitor.Lock();
     }
     data.mMonitor.AssertCurrentThreadOwns();
     while (!data.mError &&
            prevNumFramesOutput == data.mNumSamplesOutput &&
-           !data.mInputExhausted ) {
+           !data.mInputExhausted &&
+           !data.mDrainComplete) {
       data.mMonitor.Wait();
     }
   }
   data.mMonitor.AssertCurrentThreadOwns();
+  bool drainComplete = data.mDrainComplete;
   data.mMonitor.Unlock();
-  return true;
+  return !drainComplete;
 }
 
 void
 MP4Reader::Output(TrackType aTrack, MediaData* aSample)
 {
 #ifdef LOG_SAMPLE_DECODE
   LOG("Decoded %s sample time=%lld dur=%lld",
       TrackTypeToStr(aTrack), aSample->mTime, aSample->mDuration);
@@ -417,16 +421,25 @@ MP4Reader::Output(TrackType aTrack, Medi
       break;
   }
 
   data.mNumSamplesOutput++;
   mon.NotifyAll();
 }
 
 void
+MP4Reader::DrainComplete(TrackType aTrack)
+{
+  DecoderData& data = GetDecoderData(aTrack);
+  MonitorAutoLock mon(data.mMonitor);
+  data.mDrainComplete = true;
+  mon.NotifyAll();
+}
+
+void
 MP4Reader::InputExhausted(TrackType aTrack)
 {
   DecoderData& data = GetDecoderData(aTrack);
   MonitorAutoLock mon(data.mMonitor);
   data.mInputExhausted = true;
   mon.NotifyAll();
 }
 
--- a/content/media/fmp4/MP4Reader.h
+++ b/content/media/fmp4/MP4Reader.h
@@ -69,16 +69,17 @@ private:
 
   bool SkipVideoDemuxToNextKeyFrame(int64_t aTimeThreshold, uint32_t& parsed);
 
   void Output(mp4_demuxer::TrackType aType, MediaData* aSample);
   void InputExhausted(mp4_demuxer::TrackType aTrack);
   void Error(mp4_demuxer::TrackType aTrack);
   bool Decode(mp4_demuxer::TrackType aTrack);
   void Flush(mp4_demuxer::TrackType aTrack);
+  void DrainComplete(mp4_demuxer::TrackType aTrack);
 
   nsAutoPtr<mp4_demuxer::MP4Demuxer> mDemuxer;
   nsAutoPtr<PlatformDecoderModule> mPlatform;
 
   class DecoderCallback : public MediaDataDecoderCallback {
   public:
     DecoderCallback(MP4Reader* aReader,
                     mp4_demuxer::TrackType aType)
@@ -90,32 +91,36 @@ private:
       mReader->Output(mType, aSample);
     }
     virtual void InputExhausted() MOZ_OVERRIDE {
       mReader->InputExhausted(mType);
     }
     virtual void Error() MOZ_OVERRIDE {
       mReader->Error(mType);
     }
+    virtual void DrainComplete() MOZ_OVERRIDE {
+      mReader->DrainComplete(mType);
+    }
   private:
     MP4Reader* mReader;
     mp4_demuxer::TrackType mType;
   };
 
   struct DecoderData {
     DecoderData(const char* aMonitorName,
                 uint32_t aDecodeAhead)
       : mMonitor(aMonitorName)
       , mNumSamplesInput(0)
       , mNumSamplesOutput(0)
       , mDecodeAhead(aDecodeAhead)
       , mActive(false)
       , mInputExhausted(false)
       , mError(false)
       , mIsFlushing(false)
+      , mDrainComplete(false)
     {
     }
 
     // The platform decoder.
     RefPtr<MediaDataDecoder> mDecoder;
     // TaskQueue on which decoder can choose to decode.
     // Only non-null up until the decoder is created.
     RefPtr<MediaTaskQueue> mTaskQueue;
@@ -127,16 +132,17 @@ private:
     uint64_t mNumSamplesInput;
     uint64_t mNumSamplesOutput;
     uint32_t mDecodeAhead;
     // Whether this stream exists in the media.
     bool mActive;
     bool mInputExhausted;
     bool mError;
     bool mIsFlushing;
+    bool mDrainComplete;
   };
   DecoderData mAudio;
   DecoderData mVideo;
   // Queued frame extracted by the demuxer, but not yet sent to the platform
   // decoder.
   nsAutoPtr<mp4_demuxer::MP4Sample> mQueuedVideoSample;
 
   // The last number of decoded output frames that we've reported to
--- a/content/media/fmp4/PlatformDecoderModule.h
+++ b/content/media/fmp4/PlatformDecoderModule.h
@@ -124,16 +124,18 @@ public:
 
   // Denotes an error in the decoding process. The reader will stop calling
   // the decoder.
   virtual void Error() = 0;
 
   // Denotes that the last input sample has been inserted into the decoder,
   // and no more output can be produced unless more input is sent.
   virtual void InputExhausted() = 0;
+
+  virtual void DrainComplete() = 0;
 };
 
 // MediaDataDecoder is the interface exposed by decoders created by the
 // PlatformDecoderModule's Create*Decoder() functions. The type of
 // media data that the decoder accepts as valid input and produces as
 // output is determined when the MediaDataDecoder is created.
 //
 // All functions must be threadsafe, and be able to be called on an
@@ -168,24 +170,26 @@ public:
   // this function returns, the decoder must be ready to accept new input
   // for decoding. This function is called when the demuxer seeks, before
   // decoding resumes after the seek.
   // While the reader calls Flush(), it ignores all output sent to it;
   // it is safe (but pointless) to send output while Flush is called.
   // The MP4Reader will not call Input() while it's calling Flush().
   virtual nsresult Flush() = 0;
 
+
   // Causes all complete samples in the pipeline that can be decoded to be
   // output. If the decoder can't produce samples from the current output,
   // it drops the input samples. The decoder may be holding onto samples
   // that are required to decode samples that it expects to get in future.
   // This is called when the demuxer reaches end of stream.
   // The MP4Reader will not call Input() while it's calling Drain().
-  // This function is synchronous. Once it's returned, all samples to be
-  // output should have been returned via callback to the MP4Reader.
+  // This function is asynchronous. The MediaDataDecoder must call
+  // MediaDataDecoderCallback::DrainComplete() once all remaining
+  // samples have been output.
   virtual nsresult Drain() = 0;
 
   // Cancels all init/input/drain operations, and shuts down the
   // decoder. The platform decoder should clean up any resources it's using
   // and release memory etc. Shutdown() must block until the decoder has
   // completed shutdown. The reader calls Flush() before calling Shutdown().
   // The reader will delete the decoder once Shutdown() returns.
   // The MediaDataDecoderCallback *must* not be called after Shutdown() has
--- a/content/media/fmp4/ffmpeg/FFmpegAACDecoder.cpp
+++ b/content/media/fmp4/ffmpeg/FFmpegAACDecoder.cpp
@@ -112,17 +112,17 @@ FFmpegAACDecoder<LIBAV_VER>::Input(MP4Sa
     this, &FFmpegAACDecoder::DecodePacket, nsAutoPtr<MP4Sample>(aSample)));
 
   return NS_OK;
 }
 
 nsresult
 FFmpegAACDecoder<LIBAV_VER>::Drain()
 {
-  // AAC is never delayed; nothing to do here.
+  mCallback->DrainComplete();
   return NS_OK;
 }
 
 FFmpegAACDecoder<LIBAV_VER>::~FFmpegAACDecoder()
 {
   MOZ_COUNT_DTOR(FFmpegAACDecoder);
 }
 
--- a/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
+++ b/content/media/fmp4/ffmpeg/FFmpegH264Decoder.cpp
@@ -218,29 +218,37 @@ FFmpegH264Decoder<LIBAV_VER>::Input(mp4_
   mTaskQueue->Dispatch(
     NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample> >(
       this, &FFmpegH264Decoder<LIBAV_VER>::DecodeFrame,
       nsAutoPtr<mp4_demuxer::MP4Sample>(aSample)));
 
   return NS_OK;
 }
 
+void
+FFmpegH264Decoder<LIBAV_VER>::NotifyDrain()
+{
+  mCallback->DrainComplete();
+}
+
 nsresult
 FFmpegH264Decoder<LIBAV_VER>::Drain()
 {
   // The maximum number of frames that can be waiting to be decoded is
   // max_b_frames + 1: One P frame and max_b_frames B frames.
   for (int32_t i = 0; i <= mCodecContext.max_b_frames; i++) {
     // An empty frame tells FFmpeg to decode the next delayed frame it has in
     // its queue, if it has any.
     nsAutoPtr<MP4Sample> empty(new MP4Sample());
 
     nsresult rv = Input(empty.forget());
     NS_ENSURE_SUCCESS(rv, rv);
   }
+  mTaskQueue->Dispatch(
+    NS_NewRunnableMethod(this, &FFmpegH264Decoder<LIBAV_VER>::NotifyDrain));
 
   return NS_OK;
 }
 
 nsresult
 FFmpegH264Decoder<LIBAV_VER>::Flush()
 {
   nsresult rv = FFmpegDataDecoder::Flush();
--- a/content/media/fmp4/ffmpeg/FFmpegH264Decoder.h
+++ b/content/media/fmp4/ffmpeg/FFmpegH264Decoder.h
@@ -32,16 +32,17 @@ public:
 
   virtual nsresult Init() MOZ_OVERRIDE;
   virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
   virtual nsresult Drain() MOZ_OVERRIDE;
   virtual nsresult Flush() MOZ_OVERRIDE;
 
 private:
   void DecodeFrame(mp4_demuxer::MP4Sample* aSample);
+  void NotifyDrain();
   void OutputDelayedFrames();
 
   /**
    * This method allocates a buffer for FFmpeg's decoder, wrapped in an Image.
    * Currently it only supports Planar YUV420, which appears to be the only
    * non-hardware accelerated image format that FFmpeg's H264 decoder is
    * capable of outputting.
    */
--- a/content/media/fmp4/wmf/WMFMediaDataDecoder.cpp
+++ b/content/media/fmp4/wmf/WMFMediaDataDecoder.cpp
@@ -119,16 +119,17 @@ void
 WMFMediaDataDecoder::ProcessDrain()
 {
   // Order the decoder to drain...
   if (FAILED(mDecoder->SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0))) {
     NS_WARNING("Failed to send DRAIN command to MFT");
   }
   // Then extract all available output.
   ProcessOutput();
+  mCallback->DrainComplete();
 }
 
 nsresult
 WMFMediaDataDecoder::Drain()
 {
   mTaskQueue->Dispatch(NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessDrain));
   return NS_OK;
 }
new file mode 100644
--- /dev/null
+++ b/content/media/gmp/GMPCallbackBase.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GMPCallbackBase_h_
+#define GMPCallbackBase_h_
+
+class GMPCallbackBase
+{
+public:
+  virtual ~GMPCallbackBase() {}
+
+  // The GMP code will call this if the codec crashes or shuts down.  It's
+  // expected that the consumer (destination of this callback) will respond
+  // by dropping their reference to the proxy, allowing the proxy/parent to
+  // be destroyed.
+  virtual void Terminated() = 0;
+};
+
+#endif
--- a/content/media/gmp/GMPChild.cpp
+++ b/content/media/gmp/GMPChild.cpp
@@ -42,27 +42,28 @@ GMPChild::~GMPChild()
 }
 
 bool
 GMPChild::Init(const std::string& aPluginPath,
                base::ProcessHandle aParentProcessHandle,
                MessageLoop* aIOLoop,
                IPC::Channel* aChannel)
 {
-#ifdef GMP_CRASHREPORTER_READY
-// See bug 1041226
+  if (!Open(aChannel, aParentProcessHandle, aIOLoop)) {
+    return false;
+  }
+
 #ifdef MOZ_CRASHREPORTER
   SendPCrashReporterConstructor(CrashReporter::CurrentThreadId());
 #endif
-#endif
 #if defined(XP_WIN)
   mozilla::SandboxTarget::Instance()->StartSandbox();
 #endif
-  return LoadPluginLibrary(aPluginPath) &&
-         Open(aChannel, aParentProcessHandle, aIOLoop);
+
+  return LoadPluginLibrary(aPluginPath);
 }
 
 bool
 GMPChild::LoadPluginLibrary(const std::string& aPluginPath)
 {
   nsDependentCString pluginPath(aPluginPath.c_str());
 
   nsCOMPtr<nsIFile> libFile;
--- a/content/media/gmp/GMPParent.cpp
+++ b/content/media/gmp/GMPParent.cpp
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GMPParent.h"
+#include "prlog.h"
 #include "nsComponentManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIInputStream.h"
 #include "nsILineInputStream.h"
 #include "nsNetUtil.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsThreadUtils.h"
 #include "nsIRunnable.h"
@@ -21,41 +22,66 @@
 using mozilla::dom::CrashReporterParent;
 
 #ifdef MOZ_CRASHREPORTER
 using CrashReporter::AnnotationTable;
 using CrashReporter::GetIDFromMinidump;
 #endif
 
 namespace mozilla {
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* GetGMPLog();
+
+#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg)
+#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
+#else
+#define LOGD(msg)
+#define LOG(level, msg)
+#endif
+
+#ifdef __CLASS__
+#undef __CLASS__
+#endif
+#define __CLASS__ "GMPParent"
+
 namespace gmp {
 
 GMPParent::GMPParent()
   : mState(GMPStateNotLoaded)
   , mProcess(nullptr)
+  , mDeleteProcessOnUnload(false)
 {
 }
 
 GMPParent::~GMPParent()
 {
+  // Can't Close or Destroy the process here, since destruction is MainThread only
+  MOZ_ASSERT(NS_IsMainThread());
 }
 
 nsresult
 GMPParent::Init(nsIFile* aPluginDir)
 {
   MOZ_ASSERT(aPluginDir);
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
   mDirectory = aPluginDir;
 
   nsAutoString leafname;
   nsresult rv = aPluginDir->GetLeafName(leafname);
   if (NS_FAILED(rv)) {
     return rv;
   }
+  LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this, 
+       NS_LossyConvertUTF16toASCII(leafname).get()));
 
   MOZ_ASSERT(leafname.Length() > 4);
   mName = Substring(leafname, 4);
 
   return ReadGMPMetaData();
 }
 
 nsresult
@@ -64,92 +90,107 @@ GMPParent::LoadProcess()
   MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!");
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
   MOZ_ASSERT(mState == GMPStateNotLoaded);
 
   nsAutoCString path;
   if (NS_FAILED(mDirectory->GetNativePath(path))) {
     return NS_ERROR_FAILURE;
   }
+  LOGD(("%s::%s: %p for %s", __CLASS__, __FUNCTION__, this, path.get()));
 
-  mProcess = new GMPProcessParent(path.get());
-  if (!mProcess->Launch(30 * 1000)) {
-    mProcess->Delete();
-    mProcess = nullptr;
-    return NS_ERROR_FAILURE;
-  }
+  if (!mProcess) {
+    mProcess = new GMPProcessParent(path.get());
+    if (!mProcess->Launch(30 * 1000)) {
+      mProcess->Delete();
+      mProcess = nullptr;
+      return NS_ERROR_FAILURE;
+    }
 
-  bool opened = Open(mProcess->GetChannel(), mProcess->GetChildProcessHandle());
-  if (!opened) {
-    mProcess->Delete();
-    mProcess = nullptr;
-    return NS_ERROR_FAILURE;
+    bool opened = Open(mProcess->GetChannel(), mProcess->GetChildProcessHandle());
+    if (!opened) {
+      mProcess->Delete();
+      mProcess = nullptr;
+      return NS_ERROR_FAILURE;
+    }
   }
 
   mState = GMPStateLoaded;
 
   return NS_OK;
 }
 
 void
 GMPParent::CloseIfUnused()
 {
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
-  if ((mState == GMPStateLoaded ||
+  if ((mDeleteProcessOnUnload ||
+       mState == GMPStateLoaded ||
        mState == GMPStateUnloading) &&
       mVideoDecoders.IsEmpty() &&
       mVideoEncoders.IsEmpty()) {
     Shutdown();
   }
 }
 
 void
-GMPParent::CloseActive()
+GMPParent::CloseActive(bool aDieWhenUnloaded)
 {
+  LOGD(("%s::%s: %p state %d", __CLASS__, __FUNCTION__, this, mState));
+  if (aDieWhenUnloaded) {
+    mDeleteProcessOnUnload = true; // don't allow this to go back...
+  }
   if (mState == GMPStateLoaded) {
     mState = GMPStateUnloading;
   }
 
   // Invalidate and remove any remaining API objects.
   for (uint32_t i = mVideoDecoders.Length(); i > 0; i--) {
-    mVideoDecoders[i - 1]->DecodingComplete();
+    mVideoDecoders[i - 1]->Shutdown();
   }
 
   // Invalidate and remove any remaining API objects.
   for (uint32_t i = mVideoEncoders.Length(); i > 0; i--) {
-    mVideoEncoders[i - 1]->EncodingComplete();
+    mVideoEncoders[i - 1]->Shutdown();
   }
 
   // Note: the shutdown of the codecs is async!  don't kill
   // the plugin-container until they're all safely shut down via
   // CloseIfUnused();
   CloseIfUnused();
 }
 
 void
 GMPParent::Shutdown()
 {
+  LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
   MOZ_ASSERT(GMPThread() == NS_GetCurrentThread());
 
   MOZ_ASSERT(mVideoDecoders.IsEmpty() && mVideoEncoders.IsEmpty());
   if (mState == GMPStateNotLoaded || mState == GMPStateClosing) {
     return;
   }
 
-  mState = GMPStateClosing;
-  Close();
-  DeleteProcess();
+  // XXX Get rid of mDeleteProcessOnUnload and do this on all Shutdowns once
+  // Bug 1043671 is fixed (backout this patch)
+  if (mDeleteProcessOnUnload) {
+    mState = GMPStateClosing;
+    DeleteProcess();
+  } else {
+    mState = GMPStateNotLoaded;
+  }
   MOZ_ASSERT(mState == GMPStateNotLoaded);
 }
 
 void
 GMPParent::DeleteProcess()
 {
-  MOZ_ASSERT(mState == GMPStateClosing);
+  LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
+  Close();
   mProcess->Delete();
   mProcess = nullptr;
   mState = GMPStateNotLoaded;
 }
 
 void
 GMPParent::VideoDecoderDestroyed(GMPVideoDecoderParent* aDecoder)
 {
@@ -256,16 +297,19 @@ GMPParent::GetGMPVideoDecoder(GMPVideoDe
   }
 
   // returned with one anonymous AddRef that locks it until Destroy
   PGMPVideoDecoderParent* pvdp = SendPGMPVideoDecoderConstructor();
   if (!pvdp) {
     return NS_ERROR_FAILURE;
   }
   GMPVideoDecoderParent *vdp = static_cast<GMPVideoDecoderParent*>(pvdp);
+  // This addref corresponds to the Proxy pointer the consumer is returned.
+  // It's dropped by calling Close() on the interface.
+  NS_ADDREF(vdp);
   *aGMPVD = vdp;
   mVideoDecoders.AppendElement(vdp);
 
   return NS_OK;
 }
 
 nsresult
 GMPParent::GetGMPVideoEncoder(GMPVideoEncoderParent** aGMPVE)
@@ -277,16 +321,19 @@ GMPParent::GetGMPVideoEncoder(GMPVideoEn
   }
 
   // returned with one anonymous AddRef that locks it until Destroy
   PGMPVideoEncoderParent* pvep = SendPGMPVideoEncoderConstructor();
   if (!pvep) {
     return NS_ERROR_FAILURE;
   }
   GMPVideoEncoderParent *vep = static_cast<GMPVideoEncoderParent*>(pvep);
+  // This addref corresponds to the Proxy pointer the consumer is returned.
+  // It's dropped by calling Close() on the interface.
+  NS_ADDREF(vep);
   *aGMPVE = vep;
   mVideoEncoders.AppendElement(vep);
 
   return NS_OK;
 }
 
 #ifdef MOZ_CRASHREPORTER
 void
@@ -327,20 +374,20 @@ GMPNotifyObservers(nsAString& aData)
 {
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     nsString temp(aData);
     obs->NotifyObservers(nullptr, "gmp-plugin-crash", temp.get());
   }
 }
 #endif
-
 void
 GMPParent::ActorDestroy(ActorDestroyReason aWhy)
 {
+  LOGD(("%s::%s: %p (%d)", __CLASS__, __FUNCTION__, this, (int) aWhy));
 #ifdef MOZ_CRASHREPORTER
   if (AbnormalShutdown == aWhy) {
     nsString dumpID;
     GetCrashID(dumpID);
     nsString id;
     // use the parent address to identify it
     // We could use any unique-to-the-parent value
     id.AppendInt(reinterpret_cast<uint64_t>(this));
@@ -351,17 +398,17 @@ GMPParent::ActorDestroy(ActorDestroyReas
 
     // NotifyObservers is mainthread-only
     NS_DispatchToMainThread(WrapRunnableNM(&GMPNotifyObservers, id),
                             NS_DISPATCH_NORMAL);
   }
 #endif
   // warn us off trying to close again
   mState = GMPStateClosing;
-  CloseActive();
+  CloseActive(false);
 
   // Normal Shutdown() will delete the process on unwind.
   if (AbnormalShutdown == aWhy) {
     NS_DispatchToCurrentThread(NS_NewRunnableMethod(this, &GMPParent::DeleteProcess));
   }
 }
 
 mozilla::dom::PCrashReporterParent*
--- a/content/media/gmp/GMPParent.h
+++ b/content/media/gmp/GMPParent.h
@@ -59,17 +59,17 @@ public:
   nsresult Init(nsIFile* aPluginDir);
   nsresult LoadProcess();
 
   // Called internally to close this if we don't need it
   void CloseIfUnused();
 
   // Notify all active de/encoders that we are closing, either because of
   // normal shutdown or unexpected shutdown/crash.
-  void CloseActive();
+  void CloseActive(bool aDieWhenUnloaded);
 
   // Called by the GMPService to forcibly close active de/encoders at shutdown
   void Shutdown();
 
   // This must not be called while we're in the middle of abnormal ActorDestroy
   void DeleteProcess();
 
   bool SupportsAPI(const nsCString& aAPI, const nsCString& aTag);
@@ -127,16 +127,17 @@ private:
   GMPState mState;
   nsCOMPtr<nsIFile> mDirectory; // plugin directory on disk
   nsString mName; // base name of plugin on disk, UTF-16 because used for paths
   nsCString mDisplayName; // name of plugin displayed to users
   nsCString mDescription; // description of plugin for display to users
   nsCString mVersion;
   nsTArray<nsAutoPtr<GMPCapability>> mCapabilities;
   GMPProcessParent* mProcess;
+  bool mDeleteProcessOnUnload;
 
   nsTArray<nsRefPtr<GMPVideoDecoderParent>> mVideoDecoders;
   nsTArray<nsRefPtr<GMPVideoEncoderParent>> mVideoEncoders;
 #ifdef DEBUG
   nsCOMPtr<nsIThread> mGMPThread;
 #endif
   // Origin the plugin is assigned to, or empty if the the plugin is not
   // assigned to an origin.
--- a/content/media/gmp/GMPService.cpp
+++ b/content/media/gmp/GMPService.cpp
@@ -1,27 +1,50 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GMPService.h"
+#include "prlog.h"
 #include "GMPParent.h"
 #include "GMPVideoDecoderParent.h"
 #include "nsIObserverService.h"
 #include "GeckoChildProcessHost.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/SyncRunnable.h"
 #include "nsXPCOMPrivate.h"
 #include "mozilla/Services.h"
 #include "nsNativeCharsetUtils.h"
 #include "nsIConsoleService.h"
 #include "mozilla/unused.h"
 
 namespace mozilla {
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#ifdef PR_LOGGING
+PRLogModuleInfo*
+GetGMPLog()
+{
+  static PRLogModuleInfo *sLog;
+  if (!sLog)
+    sLog = PR_NewLogModule("GMP");
+  return sLog;
+}
+
+#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg)
+#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
+#else
+#define LOGD(msg)
+#define LOG(leve1, msg)
+#endif
+
 namespace gmp {
 
 static StaticRefPtr<GeckoMediaPluginService> sSingletonService;
 
 class GMPServiceCreateHelper MOZ_FINAL : public nsRunnable
 {
   nsRefPtr<GeckoMediaPluginService> mService;
 
@@ -198,20 +221,25 @@ GeckoMediaPluginService::GetGMPVideoDeco
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aOrigin,
                                                NS_LITERAL_CSTRING("decode-video"),
                                                *aTags);
+#ifdef PR_LOGGING
+  nsCString api = (*aTags)[0];
+  LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
+#endif
   if (!gmp) {
     return NS_ERROR_FAILURE;
   }
 
+
   GMPVideoDecoderParent* gmpVDP;
   nsresult rv = gmp->GetGMPVideoDecoder(&gmpVDP);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   *aGMPVD = gmpVDP;
   *aOutVideoHost = &gmpVDP->Host();
@@ -232,16 +260,20 @@ GeckoMediaPluginService::GetGMPVideoEnco
 
   if (mShuttingDownOnGMPThread) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<GMPParent> gmp = SelectPluginForAPI(aOrigin,
                                                NS_LITERAL_CSTRING("encode-video"),
                                                *aTags);
+#ifdef PR_LOGGING
+  nsCString api = (*aTags)[0];
+  LOGD(("%s: %p returning %p for api %s", __FUNCTION__, (void *)this, (void *)gmp, api.get()));
+#endif
   if (!gmp) {
     return NS_ERROR_FAILURE;
   }
 
   GMPVideoEncoderParent* gmpVEP;
   nsresult rv = gmp->GetGMPVideoEncoder(&gmpVEP);
   if (NS_FAILED(rv)) {
     return rv;
@@ -260,17 +292,17 @@ GeckoMediaPluginService::UnloadPlugins()
 
   MOZ_ASSERT(!mShuttingDownOnGMPThread);
   mShuttingDownOnGMPThread = true;
 
   MutexAutoLock lock(mMutex);
   // Note: CloseActive is async; it will actually finish
   // shutting down when all the plugins have unloaded.
   for (uint32_t i = 0; i < mPlugins.Length(); i++) {
-    mPlugins[i]->CloseActive();
+    mPlugins[i]->CloseActive(true);
   }
   mPlugins.Clear();
 }
 
 void
 GeckoMediaPluginService::LoadFromEnvironment()
 {
   MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
@@ -441,17 +473,17 @@ GeckoMediaPluginService::RemoveOnGMPThre
     return;
   }
 
   MutexAutoLock lock(mMutex);
   for (uint32_t i = 0; i < mPlugins.Length(); ++i) {
     nsCOMPtr<nsIFile> pluginpath = mPlugins[i]->GetDirectory();
     bool equals;
     if (NS_SUCCEEDED(directory->Equals(pluginpath, &equals)) && equals) {
-      mPlugins[i]->CloseActive();
+      mPlugins[i]->CloseActive(true);
       mPlugins.RemoveElementAt(i);
       return;
     }
   }
   NS_WARNING("Removing GMP which was never added.");
   nsCOMPtr<nsIConsoleService> cs = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
   cs->LogStringMessage(MOZ_UTF16("Removing GMP which was never added."));
 }
--- a/content/media/gmp/GMPVideoDecoderParent.cpp
+++ b/content/media/gmp/GMPVideoDecoderParent.cpp
@@ -1,36 +1,61 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GMPVideoDecoderParent.h"
+#include "prlog.h"
+#include "mozilla/unused.h"
+#include "nsAutoRef.h"
+#include "nsThreadUtils.h"
 #include "GMPVideoEncodedFrameImpl.h"
 #include "GMPVideoi420FrameImpl.h"
 #include "GMPParent.h"
-#include <stdio.h>
-#include "mozilla/unused.h"
 #include "GMPMessageUtils.h"
-#include "nsAutoRef.h"
-#include "nsThreadUtils.h"
 #include "mozilla/gmp/GMPTypes.h"
 
 template <>
 class nsAutoRefTraits<GMPVideoEncodedFrame> : public nsPointerRefTraits<GMPVideoEncodedFrame>
 {
 public:
   static void Release(GMPVideoEncodedFrame* aFrame) { aFrame->Destroy(); }
 };
 
 namespace mozilla {
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* GetGMPLog();
+
+#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg)
+#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
+#else
+#define LOGD(msg)
+#define LOG(level, msg)
+#endif
+
 namespace gmp {
 
+// States:
+// Initial: mIsOpen == false
+//    on InitDecode success -> Open
+//    on Shutdown -> Dead
+// Open: mIsOpen == true
+//    on Close -> Dead
+//    on ActorDestroy -> Dead
+//    on Shutdown -> Dead
+// Dead: mIsOpen == false
+
 GMPVideoDecoderParent::GMPVideoDecoderParent(GMPParent* aPlugin)
-  : mCanSendMessages(true)
+  : mIsOpen(false)
   , mPlugin(aPlugin)
   , mCallback(nullptr)
   , mVideoHost(MOZ_THIS_IN_INITIALIZER_LIST())
 {
   MOZ_ASSERT(mPlugin);
 }
 
 GMPVideoDecoderParent::~GMPVideoDecoderParent()
@@ -38,150 +63,173 @@ GMPVideoDecoderParent::~GMPVideoDecoderP
 }
 
 GMPVideoHostImpl&
 GMPVideoDecoderParent::Host()
 {
   return mVideoHost;
 }
 
+// Note: may be called via Terminated()
+void
+GMPVideoDecoderParent::Close()
+{
+  LOGD(("%s: %p", __FUNCTION__, this));
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+  // Consumer is done with us; we can shut down.  No more callbacks should
+  // be made to mCallback.  Note: do this before Shutdown()!
+  mCallback = nullptr;
+  // Let Shutdown mark us as dead so it knows if we had been alive
+
+  // In case this is the last reference
+  nsRefPtr<GMPVideoDecoderParent> kungfudeathgrip(this);
+  NS_RELEASE(kungfudeathgrip);
+  Shutdown();
+}
+
 nsresult
 GMPVideoDecoderParent::InitDecode(const GMPVideoCodec& aCodecSettings,
                                   const nsTArray<uint8_t>& aCodecSpecific,
-                                  GMPVideoDecoderCallback* aCallback,
+                                  GMPVideoDecoderCallbackProxy* aCallback,
                                   int32_t aCoreCount)
 {
-  if (!mCanSendMessages) {
-    NS_WARNING("Trying to use an invalid GMP video decoder!");
+  if (mIsOpen) {
+    NS_WARNING("Trying to re-init an in-use GMP video decoder!");
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (!aCallback) {
     return NS_ERROR_FAILURE;
   }
   mCallback = aCallback;
 
   if (!SendInitDecode(aCodecSettings, aCodecSpecific, aCoreCount)) {
     return NS_ERROR_FAILURE;
   }
+  mIsOpen = true;
 
   // Async IPC, we don't have access to a return value.
   return NS_OK;
 }
 
 nsresult
 GMPVideoDecoderParent::Decode(GMPVideoEncodedFrame* aInputFrame,
                               bool aMissingFrames,
                               const nsTArray<uint8_t>& aCodecSpecificInfo,
                               int64_t aRenderTimeMs)
 {
   nsAutoRef<GMPVideoEncodedFrame> autoDestroy(aInputFrame);
 
-  if (!mCanSendMessages) {
-    NS_WARNING("Trying to use an invalid GMP video decoder!");
+  if (!mIsOpen) {
+    NS_WARNING("Trying to use an dead GMP video decoder");
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   auto inputFrameImpl = static_cast<GMPVideoEncodedFrameImpl*>(aInputFrame);
 
-  GMPVideoEncodedFrameData frameData;
-  inputFrameImpl->RelinquishFrameData(frameData);
-
   // Very rough kill-switch if the plugin stops processing.  If it's merely
   // hung and continues, we'll come back to life eventually.
   // 3* is because we're using 3 buffers per frame for i420 data for now.
   if (NumInUse(kGMPFrameData) > 3*GMPSharedMemManager::kGMPBufLimit ||
       NumInUse(kGMPEncodedData) > GMPSharedMemManager::kGMPBufLimit) {
     return NS_ERROR_FAILURE;
   }
 
+  GMPVideoEncodedFrameData frameData;
+  inputFrameImpl->RelinquishFrameData(frameData);
+
   if (!SendDecode(frameData,
                   aMissingFrames,
                   aCodecSpecificInfo,
                   aRenderTimeMs)) {
     return NS_ERROR_FAILURE;
   }
 
   // Async IPC, we don't have access to a return value.
   return NS_OK;
 }
 
 nsresult
 GMPVideoDecoderParent::Reset()
 {
-  if (!mCanSendMessages) {
-    NS_WARNING("Trying to use an invalid GMP video decoder!");
+  if (!mIsOpen) {
+    NS_WARNING("Trying to use an dead GMP video decoder");
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (!SendReset()) {
     return NS_ERROR_FAILURE;
   }
 
   // Async IPC, we don't have access to a return value.
   return NS_OK;
 }
 
 nsresult
 GMPVideoDecoderParent::Drain()
 {
-  if (!mCanSendMessages) {
-    NS_WARNING("Trying to use an invalid GMP video decoder!");
+  if (!mIsOpen) {
+    NS_WARNING("Trying to use an dead GMP video decoder");
     return NS_ERROR_FAILURE;
   }
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (!SendDrain()) {
     return NS_ERROR_FAILURE;
   }
 
   // Async IPC, we don't have access to a return value.
   return NS_OK;
 }
 
 // Note: Consider keeping ActorDestroy sync'd up when making changes here.
 nsresult
-GMPVideoDecoderParent::DecodingComplete()
+GMPVideoDecoderParent::Shutdown()
 {
-  if (!mCanSendMessages) {
-    NS_WARNING("Trying to use an invalid GMP video decoder!");
-    return NS_ERROR_FAILURE;
-  }
-
+  LOGD(("%s: %p", __FUNCTION__, this));
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
-  mCanSendMessages = false;
-
-  mCallback = nullptr;
-
+  // Notify client we're gone!  Won't occur after Close()
+  if (mCallback) {
+    mCallback->Terminated();
+    mCallback = nullptr;
+  }
   mVideoHost.DoneWithAPI();
 
-  unused << SendDecodingComplete();
+  if (mIsOpen) {
+    // Don't send DecodingComplete if we died
+    mIsOpen = false;
+    unused << SendDecodingComplete();
+  }
 
   return NS_OK;
 }
 
-// Note: Keep this sync'd up with DecodingComplete
+// Note: Keep this sync'd up with Shutdown
 void
 GMPVideoDecoderParent::ActorDestroy(ActorDestroyReason aWhy)
 {
+  mIsOpen = false;
+  if (mCallback) {
+    // May call Close() (and Shutdown()) immediately or with a delay
+    mCallback->Terminated();
+    mCallback = nullptr;
+  }
   if (mPlugin) {
     // Ignore any return code. It is OK for this to fail without killing the process.
     mPlugin->VideoDecoderDestroyed(this);
     mPlugin = nullptr;
   }
-  mCanSendMessages = false;
-  mCallback = nullptr;
   mVideoHost.ActorDestroyed();
 }
 
 void
 GMPVideoDecoderParent::CheckThread()
 {
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 }
@@ -294,16 +342,18 @@ GMPVideoDecoderParent::AnswerNeedShmem(c
                                        Shmem* aMem)
 {
   ipc::Shmem mem;
 
   if (!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPFrameData,
                                                 aFrameBufferSize,
                                                 ipc::SharedMemory::TYPE_BASIC, &mem))
   {
+    LOG(PR_LOG_ERROR, ("%s: Failed to get a shared mem buffer for Child! size %u",
+                       __FUNCTION__, aFrameBufferSize));
     return false;
   }
   *aMem = mem;
   mem = ipc::Shmem();
   return true;
 }
 
 bool
--- a/content/media/gmp/GMPVideoDecoderParent.h
+++ b/content/media/gmp/GMPVideoDecoderParent.h
@@ -24,29 +24,30 @@ class GMPVideoDecoderParent MOZ_FINAL : 
                                       , public GMPVideoDecoderProxy
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPVideoDecoderParent)
 
   GMPVideoDecoderParent(GMPParent *aPlugin);
 
   GMPVideoHostImpl& Host();
+  nsresult Shutdown();
 
   // GMPVideoDecoder
+  virtual void Close() MOZ_OVERRIDE;
   virtual nsresult InitDecode(const GMPVideoCodec& aCodecSettings,
                               const nsTArray<uint8_t>& aCodecSpecific,
-                              GMPVideoDecoderCallback* aCallback,
+                              GMPVideoDecoderCallbackProxy* aCallback,
                               int32_t aCoreCount) MOZ_OVERRIDE;
   virtual nsresult Decode(GMPVideoEncodedFrame* aInputFrame,
                           bool aMissingFrames,
                           const nsTArray<uint8_t>& aCodecSpecificInfo,
                           int64_t aRenderTimeMs = -1) MOZ_OVERRIDE;
   virtual nsresult Reset() MOZ_OVERRIDE;
   virtual nsresult Drain() MOZ_OVERRIDE;
-  virtual nsresult DecodingComplete() MOZ_OVERRIDE;
   virtual const uint64_t ParentID() MOZ_OVERRIDE { return reinterpret_cast<uint64_t>(mPlugin.get()); }
 
   // GMPSharedMemManager
   virtual void CheckThread();
   virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem)
   {
 #ifdef GMP_SAFE_SHMEM
     return AllocShmem(aSize, aType, aMem);
@@ -71,18 +72,18 @@ private:
   virtual bool RecvDrainComplete() MOZ_OVERRIDE;
   virtual bool RecvResetComplete() MOZ_OVERRIDE;
   virtual bool RecvError(const GMPErr& aError) MOZ_OVERRIDE;
   virtual bool RecvParentShmemForPool(Shmem& aEncodedBuffer) MOZ_OVERRIDE;
   virtual bool AnswerNeedShmem(const uint32_t& aFrameBufferSize,
                                Shmem* aMem) MOZ_OVERRIDE;
   virtual bool Recv__delete__() MOZ_OVERRIDE;
 
-  bool mCanSendMessages;
+  bool mIsOpen;
   nsRefPtr<GMPParent> mPlugin;
-  GMPVideoDecoderCallback* mCallback;
+  GMPVideoDecoderCallbackProxy* mCallback;
   GMPVideoHostImpl mVideoHost;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPVideoDecoderParent_h_
--- a/content/media/gmp/GMPVideoDecoderProxy.h
+++ b/content/media/gmp/GMPVideoDecoderProxy.h
@@ -6,29 +6,48 @@
 #ifndef GMPVideoDecoderProxy_h_
 #define GMPVideoDecoderProxy_h_
 
 #include "nsTArray.h"
 #include "gmp-video-decode.h"
 #include "gmp-video-frame-i420.h"
 #include "gmp-video-frame-encoded.h"
 
+#include "GMPCallbackBase.h"
+
+class GMPVideoDecoderCallbackProxy : public GMPCallbackBase,
+                                     public GMPVideoDecoderCallback
+{
+public:
+  virtual ~GMPVideoDecoderCallbackProxy() {}
+};
+
 // A proxy to GMPVideoDecoder in the child process.
 // GMPVideoDecoderParent exposes this to users the GMP.
 // This enables Gecko to pass nsTArrays to the child GMP and avoid
 // an extra copy when doing so.
+
+// The consumer must call Close() when done with the codec, or when
+// Terminated() is called by the GMP plugin indicating an abnormal shutdown
+// of the underlying plugin.  After calling Close(), the consumer must
+// not access this again.
+
+// This interface is not thread-safe and must only be used from GMPThread.
 class GMPVideoDecoderProxy {
 public:
   virtual nsresult InitDecode(const GMPVideoCodec& aCodecSettings,
                               const nsTArray<uint8_t>& aCodecSpecific,
-                              GMPVideoDecoderCallback* aCallback,
+                              GMPVideoDecoderCallbackProxy* aCallback,
                               int32_t aCoreCount) = 0;
   virtual nsresult Decode(GMPVideoEncodedFrame* aInputFrame,
                           bool aMissingFrames,
                           const nsTArray<uint8_t>& aCodecSpecificInfo,
                           int64_t aRenderTimeMs = -1) = 0;
   virtual nsresult Reset() = 0;
   virtual nsresult Drain() = 0;
-  virtual nsresult DecodingComplete() = 0;
   virtual const uint64_t ParentID() = 0;
+
+  // Call to tell GMP/plugin the consumer will no longer use this
+  // interface/codec.
+  virtual void Close() = 0;
 };
 
 #endif
--- a/content/media/gmp/GMPVideoEncoderParent.cpp
+++ b/content/media/gmp/GMPVideoEncoderParent.cpp
@@ -1,36 +1,66 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GMPVideoEncoderParent.h"
+#include "prlog.h"
 #include "GMPVideoi420FrameImpl.h"
 #include "GMPVideoEncodedFrameImpl.h"
-#include <stdio.h>
 #include "mozilla/unused.h"
 #include "GMPMessageUtils.h"
 #include "nsAutoRef.h"
 #include "GMPParent.h"
 #include "mozilla/gmp/GMPTypes.h"
 #include "nsThreadUtils.h"
 
 template <>
 class nsAutoRefTraits<GMPVideoi420Frame> : public nsPointerRefTraits<GMPVideoi420Frame>
 {
 public:
   static void Release(GMPVideoi420Frame* aFrame) { aFrame->Destroy(); }
 };
 
 namespace mozilla {
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#ifdef PR_LOGGING
+extern PRLogModuleInfo* GetGMPLog();
+
+#define LOGD(msg) PR_LOG(GetGMPLog(), PR_LOG_DEBUG, msg)
+#define LOG(level, msg) PR_LOG(GetGMPLog(), (level), msg)
+#else
+#define LOGD(msg)
+#define LOG(level, msg)
+#endif
+
+#ifdef __CLASS__
+#undef __CLASS__
+#endif
+#define __CLASS__ "GMPVideoEncoderParent"
+
 namespace gmp {
 
+// States:
+// Initial: mIsOpen == false
+//    on InitDecode success -> Open
+//    on Shutdown -> Dead
+// Open: mIsOpen == true
+//    on Close -> Dead
+//    on ActorDestroy -> Dead
+//    on Shutdown -> Dead
+// Dead: mIsOpen == false
+
 GMPVideoEncoderParent::GMPVideoEncoderParent(GMPParent *aPlugin)
-: mCanSendMessages(true),
+: mIsOpen(false),
   mPlugin(aPlugin),
   mCallback(nullptr),
   mVideoHost(MOZ_THIS_IN_INITIALIZER_LIST())
 {
   MOZ_ASSERT(mPlugin);
 }
 
 GMPVideoEncoderParent::~GMPVideoEncoderParent()
@@ -38,84 +68,103 @@ GMPVideoEncoderParent::~GMPVideoEncoderP
 }
 
 GMPVideoHostImpl&
 GMPVideoEncoderParent::Host()
 {
   return mVideoHost;
 }
 
+// Note: may be called via Terminated()
+void
+GMPVideoEncoderParent::Close()
+{
+  LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
+  MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
+  // Consumer is done with us; we can shut down.  No more callbacks should
+  // be made to mCallback.  Note: do this before Shutdown()!
+  mCallback = nullptr;
+  // Let Shutdown mark us as dead so it knows if we had been alive
+
+  // In case this is the last reference
+  nsRefPtr<GMPVideoEncoderParent> kungfudeathgrip(this);
+  NS_RELEASE(kungfudeathgrip);
+  Shutdown();
+}
+
 GMPErr
 GMPVideoEncoderParent::InitEncode(const GMPVideoCodec& aCodecSettings,
                                   const nsTArray<uint8_t>& aCodecSpecific,
                                   GMPVideoEncoderCallbackProxy* aCallback,
                                   int32_t aNumberOfCores,
                                   uint32_t aMaxPayloadSize)
 {
-  if (!mCanSendMessages) {
-    NS_WARNING("Trying to use an invalid GMP video encoder!");
-    return GMPGenericErr;
+  LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
+  if (mIsOpen) {
+    NS_WARNING("Trying to re-init an in-use GMP video encoder!");
+    return GMPGenericErr;;
   }
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (!aCallback) {
     return GMPGenericErr;
   }
   mCallback = aCallback;
 
   if (!SendInitEncode(aCodecSettings, aCodecSpecific, aNumberOfCores, aMaxPayloadSize)) {
     return GMPGenericErr;
   }
+  mIsOpen = true;
 
   // Async IPC, we don't have access to a return value.
   return GMPNoErr;
 }
 
 GMPErr
 GMPVideoEncoderParent::Encode(GMPVideoi420Frame* aInputFrame,
                               const nsTArray<uint8_t>& aCodecSpecificInfo,
                               const nsTArray<GMPVideoFrameType>& aFrameTypes)
 {
   nsAutoRef<GMPVideoi420Frame> frameRef(aInputFrame);
 
-  if (!mCanSendMessages) {
-    NS_WARNING("Trying to use an invalid GMP video encoder!");
+  if (!mIsOpen) {
+    NS_WARNING("Trying to use an dead GMP video encoder");
     return GMPGenericErr;
   }
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   auto inputFrameImpl = static_cast<GMPVideoi420FrameImpl*>(aInputFrame);
 
-  GMPVideoi420FrameData frameData;
-  inputFrameImpl->InitFrameData(frameData);
-
   // Very rough kill-switch if the plugin stops processing.  If it's merely
   // hung and continues, we'll come back to life eventually.
   // 3* is because we're using 3 buffers per frame for i420 data for now.
   if (NumInUse(kGMPFrameData) > 3*GMPSharedMemManager::kGMPBufLimit ||
       NumInUse(kGMPEncodedData) > GMPSharedMemManager::kGMPBufLimit) {
     return GMPGenericErr;
   }
 
+  GMPVideoi420FrameData frameData;
+  inputFrameImpl->InitFrameData(frameData);
+
   if (!SendEncode(frameData,
                   aCodecSpecificInfo,
                   aFrameTypes)) {
     return GMPGenericErr;
   }
 
   // Async IPC, we don't have access to a return value.
   return GMPNoErr;
 }
 
 GMPErr
 GMPVideoEncoderParent::SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT)
 {
-  if (!mCanSendMessages) {
+  if (!mIsOpen) {
     NS_WARNING("Trying to use an invalid GMP video encoder!");
     return GMPGenericErr;
   }
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (!SendSetChannelParameters(aPacketLoss, aRTT)) {
     return GMPGenericErr;
@@ -123,80 +172,85 @@ GMPVideoEncoderParent::SetChannelParamet
 
   // Async IPC, we don't have access to a return value.
   return GMPNoErr;
 }
 
 GMPErr
 GMPVideoEncoderParent::SetRates(uint32_t aNewBitRate, uint32_t aFrameRate)
 {
-  if (!mCanSendMessages) {
-    NS_WARNING("Trying to use an invalid GMP video encoder!");
+  if (!mIsOpen) {
+    NS_WARNING("Trying to use an dead GMP video decoder");
     return GMPGenericErr;
   }
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (!SendSetRates(aNewBitRate, aFrameRate)) {
     return GMPGenericErr;
   }
 
   // Async IPC, we don't have access to a return value.
   return GMPNoErr;
 }
 
 GMPErr
 GMPVideoEncoderParent::SetPeriodicKeyFrames(bool aEnable)
 {
-  if (!mCanSendMessages) {
+  if (!mIsOpen) {
     NS_WARNING("Trying to use an invalid GMP video encoder!");
     return GMPGenericErr;
   }
 
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
   if (!SendSetPeriodicKeyFrames(aEnable)) {
     return GMPGenericErr;
   }
 
   // Async IPC, we don't have access to a return value.
   return GMPNoErr;
 }
 
 // Note: Consider keeping ActorDestroy sync'd up when making changes here.
 void
-GMPVideoEncoderParent::EncodingComplete()
+GMPVideoEncoderParent::Shutdown()
 {
-  if (!mCanSendMessages) {
-    NS_WARNING("Trying to use an invalid GMP video encoder!");
-    return;
-  }
-
+  LOGD(("%s::%s: %p", __CLASS__, __FUNCTION__, this));
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 
-  mCanSendMessages = false;
-
-  mCallback = nullptr;
-
+  // Notify client we're gone!  Won't occur after Close()
+  if (mCallback) {
+    mCallback->Terminated();
+    mCallback = nullptr;
+  }
   mVideoHost.DoneWithAPI();
-
-  unused << SendEncodingComplete();
+  if (mIsOpen) {
+    // Don't send EncodingComplete if we died
+    mIsOpen = false;
+    unused << SendEncodingComplete();
+  }
 }
 
-// Note: Keep this sync'd up with DecodingComplete
+// Note: Keep this sync'd up with Shutdown
 void
 GMPVideoEncoderParent::ActorDestroy(ActorDestroyReason aWhy)
 {
+  LOGD(("%s::%s: %p (%d)", __CLASS__, __FUNCTION__, this, (int) aWhy));
+  mIsOpen = false;
+  if (mCallback) {
+    // May call Close() (and Shutdown()) immediately or with a delay
+    mCallback->Terminated();
+    mCallback = nullptr;
+  }
   if (mPlugin) {
     // Ignore any return code. It is OK for this to fail without killing the process.
     mPlugin->VideoEncoderDestroyed(this);
     mPlugin = nullptr;
   }
-  mCanSendMessages = false;
-  mCallback = nullptr;
   mVideoHost.ActorDestroyed();
 }
 
 void
 GMPVideoEncoderParent::CheckThread()
 {
   MOZ_ASSERT(mPlugin->GMPThread() == NS_GetCurrentThread());
 }
@@ -247,16 +301,18 @@ GMPVideoEncoderParent::AnswerNeedShmem(c
                                        Shmem* aMem)
 {
   ipc::Shmem mem;
 
   if (!mVideoHost.SharedMemMgr()->MgrAllocShmem(GMPSharedMemManager::kGMPEncodedData,
                                                 aEncodedBufferSize,
                                                 ipc::SharedMemory::TYPE_BASIC, &mem))
   {
+    LOG(PR_LOG_ERROR, ("%s::%s: Failed to get a shared mem buffer for Child! size %u",
+                       __CLASS__, __FUNCTION__, aEncodedBufferSize));
     return false;
   }
   *aMem = mem;
   mem = ipc::Shmem();
   return true;
 }
 
 bool
--- a/content/media/gmp/GMPVideoEncoderParent.h
+++ b/content/media/gmp/GMPVideoEncoderParent.h
@@ -24,30 +24,31 @@ class GMPVideoEncoderParent : public GMP
                               public GMPSharedMemManager
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(GMPVideoEncoderParent)
 
   GMPVideoEncoderParent(GMPParent *aPlugin);
 
   GMPVideoHostImpl& Host();
+  void Shutdown();
 
   // GMPVideoEncoderProxy
+  virtual void Close() MOZ_OVERRIDE;
   virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings,
                             const nsTArray<uint8_t>& aCodecSpecific,
                             GMPVideoEncoderCallbackProxy* aCallback,
                             int32_t aNumberOfCores,
                             uint32_t aMaxPayloadSize) MOZ_OVERRIDE;
   virtual GMPErr Encode(GMPVideoi420Frame* aInputFrame,
                         const nsTArray<uint8_t>& aCodecSpecificInfo,
                         const nsTArray<GMPVideoFrameType>& aFrameTypes) MOZ_OVERRIDE;
   virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) MOZ_OVERRIDE;
   virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) MOZ_OVERRIDE;
   virtual GMPErr SetPeriodicKeyFrames(bool aEnable) MOZ_OVERRIDE;
-  virtual void EncodingComplete() MOZ_OVERRIDE;
   virtual const uint64_t ParentID() MOZ_OVERRIDE { return reinterpret_cast<uint64_t>(mPlugin.get()); }
 
   // GMPSharedMemManager
   virtual void CheckThread();
   virtual bool Alloc(size_t aSize, Shmem::SharedMemory::SharedMemoryType aType, Shmem* aMem)
   {
 #ifdef GMP_SAFE_SHMEM
     return AllocShmem(aSize, aType, aMem);
@@ -68,17 +69,17 @@ private:
   virtual bool RecvEncoded(const GMPVideoEncodedFrameData& aEncodedFrame,
                            const nsTArray<uint8_t>& aCodecSpecificInfo) MOZ_OVERRIDE;
   virtual bool RecvError(const GMPErr& aError) MOZ_OVERRIDE;
   virtual bool RecvParentShmemForPool(Shmem& aFrameBuffer) MOZ_OVERRIDE;
   virtual bool AnswerNeedShmem(const uint32_t& aEncodedBufferSize,
                                Shmem* aMem) MOZ_OVERRIDE;
   virtual bool Recv__delete__() MOZ_OVERRIDE;
 
-  bool mCanSendMessages;
+  bool mIsOpen;
   nsRefPtr<GMPParent> mPlugin;
   GMPVideoEncoderCallbackProxy* mCallback;
   GMPVideoHostImpl mVideoHost;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
--- a/content/media/gmp/GMPVideoEncoderProxy.h
+++ b/content/media/gmp/GMPVideoEncoderProxy.h
@@ -6,37 +6,50 @@
 #ifndef GMPVideoEncoderProxy_h_
 #define GMPVideoEncoderProxy_h_
 
 #include "nsTArray.h"
 #include "gmp-video-encode.h"
 #include "gmp-video-frame-i420.h"
 #include "gmp-video-frame-encoded.h"
 
-class GMPVideoEncoderCallbackProxy {
+#include "GMPCallbackBase.h"
+
+class GMPVideoEncoderCallbackProxy : public GMPCallbackBase {
 public:
+  virtual ~GMPVideoEncoderCallbackProxy() {}
   virtual void Encoded(GMPVideoEncodedFrame* aEncodedFrame,
                        const nsTArray<uint8_t>& aCodecSpecificInfo) = 0;
   virtual void Error(GMPErr aError) = 0;
 };
 
 // A proxy to GMPVideoEncoder in the child process.
 // GMPVideoEncoderParent exposes this to users the GMP.
 // This enables Gecko to pass nsTArrays to the child GMP and avoid
 // an extra copy when doing so.
+
+// The consumer must call Close() when done with the codec, or when
+// Terminated() is called by the GMP plugin indicating an abnormal shutdown
+// of the underlying plugin.  After calling Close(), the consumer must
+// not access this again.
+
+// This interface is not thread-safe and must only be used from GMPThread.
 class GMPVideoEncoderProxy {
 public:
   virtual GMPErr InitEncode(const GMPVideoCodec& aCodecSettings,
                             const nsTArray<uint8_t>& aCodecSpecific,
                             GMPVideoEncoderCallbackProxy* aCallback,
                             int32_t aNumberOfCores,
                             uint32_t aMaxPayloadSize) = 0;
   virtual GMPErr Encode(GMPVideoi420Frame* aInputFrame,
                         const nsTArray<uint8_t>& aCodecSpecificInfo,
                         const nsTArray<GMPVideoFrameType>& aFrameTypes) = 0;
   virtual GMPErr SetChannelParameters(uint32_t aPacketLoss, uint32_t aRTT) = 0;
   virtual GMPErr SetRates(uint32_t aNewBitRate, uint32_t aFrameRate) = 0;
   virtual GMPErr SetPeriodicKeyFrames(bool aEnable) = 0;
-  virtual void EncodingComplete() = 0;
   virtual const uint64_t ParentID() = 0;
+
+  // Call to tell GMP/plugin the consumer will no longer use this
+  // interface/codec.
+  virtual void Close() = 0;
 };
 
 #endif // GMPVideoEncoderProxy_h_
--- a/content/media/gmp/moz.build
+++ b/content/media/gmp/moz.build
@@ -24,16 +24,17 @@ EXPORTS += [
     'gmp-api/gmp-video-codec.h',
     'gmp-api/gmp-video-decode.h',
     'gmp-api/gmp-video-encode.h',
     'gmp-api/gmp-video-frame-encoded.h',
     'gmp-api/gmp-video-frame-i420.h',
     'gmp-api/gmp-video-frame.h',
     'gmp-api/gmp-video-host.h',
     'gmp-api/gmp-video-plane.h',
+    'GMPCallbackBase.h',
     'GMPChild.h',
     'GMPMessageUtils.h',
     'GMPParent.h',
     'GMPPlatform.h',
     'GMPProcessChild.h',
     'GMPProcessParent.h',
     'GMPService.h',
     'GMPSharedMemManager.h',
--- a/content/media/omx/OmxDecoder.cpp
+++ b/content/media/omx/OmxDecoder.cpp
@@ -506,25 +506,45 @@ bool OmxDecoder::AllocateMediaResources(
       return false;
     }
   }
   return true;
 }
 
 
 void OmxDecoder::ReleaseMediaResources() {
+  ReleaseVideoBuffer();
+  ReleaseAudioBuffer();
+
+  {
+    Mutex::Autolock autoLock(mPendingVideoBuffersLock);
+    MOZ_ASSERT(mPendingRecycleTexutreClients.empty());
+    // Release all pending recycle TextureClients, if they are not recycled yet.
+    // This should not happen. See Bug 1042308.
+    if (!mPendingRecycleTexutreClients.empty()) {
+      printf_stderr("OmxDecoder::ReleaseMediaResources -- TextureClients are not recycled yet\n");
+      for (std::set<TextureClient*>::iterator it=mPendingRecycleTexutreClients.begin();
+           it!=mPendingRecycleTexutreClients.end(); it++)
+      {
+        GrallocTextureClientOGL* client = static_cast<GrallocTextureClientOGL*>(*it);
+        client->ClearRecycleCallback();
+        if (client->GetMediaBuffer()) {
+          mPendingVideoBuffers.push(BufferItem(client->GetMediaBuffer(), client->GetReleaseFenceHandle()));
+        }
+      }
+      mPendingRecycleTexutreClients.clear();
+    }
+  }
+
   {
     // Free all pending video buffers.
     Mutex::Autolock autoLock(mSeekLock);
     ReleaseAllPendingVideoBuffersLocked();
   }
 
-  ReleaseVideoBuffer();
-  ReleaseAudioBuffer();
-
   if (mVideoSource.get()) {
     mVideoSource->stop();
     mVideoSource.clear();
   }
 
   if (mAudioSource.get()) {
     mAudioSource->stop();
     mAudioSource.clear();
@@ -769,16 +789,22 @@ bool OmxDecoder::ReadVideo(VideoFrame *a
     if (textureClient) {
       // Manually increment reference count to keep MediaBuffer alive
       // during TextureClient is in use.
       mVideoBuffer->add_ref();
       GrallocTextureClientOGL* grallocClient = static_cast<GrallocTextureClientOGL*>(textureClient.get());
       grallocClient->SetMediaBuffer(mVideoBuffer);
       // Set recycle callback for TextureClient
       textureClient->SetRecycleCallback(OmxDecoder::RecycleCallback, this);
+      {
+        Mutex::Autolock autoLock(mPendingVideoBuffersLock);
+        // Store pending recycle TextureClient.
+        MOZ_ASSERT(mPendingRecycleTexutreClients.find(textureClient) == mPendingRecycleTexutreClients.end());
+        mPendingRecycleTexutreClients.insert(textureClient);
+      }
 
       aFrame->mGraphicBuffer = textureClient;
       aFrame->mRotation = mVideoRotation;
       aFrame->mTimeUs = timeUs;
       aFrame->mKeyFrame = keyFrame;
       aFrame->Y.mWidth = mVideoWidth;
       aFrame->Y.mHeight = mVideoHeight;
       // Release to hold video buffer in OmxDecoder more.
@@ -1019,24 +1045,42 @@ void OmxDecoder::ReleaseAllPendingVideoB
     metaData->setInt32(kKeyRendered, 1);
 #endif
     // Return MediaBuffer to OMXCodec.
     buffer->release();
   }
   releasingVideoBuffers.clear();
 }
 
+void OmxDecoder::RecycleCallbackImp(TextureClient* aClient)
+{
+  aClient->ClearRecycleCallback();
+  {
+    Mutex::Autolock autoLock(mPendingVideoBuffersLock);
+    if (mPendingRecycleTexutreClients.find(aClient) == mPendingRecycleTexutreClients.end()) {
+      printf_stderr("OmxDecoder::RecycleCallbackImp -- TextureClient is not pending recycle\n");
+      return;
+    }
+    mPendingRecycleTexutreClients.erase(aClient);
+    GrallocTextureClientOGL* client = static_cast<GrallocTextureClientOGL*>(aClient);
+    if (client->GetMediaBuffer()) {
+      mPendingVideoBuffers.push(BufferItem(client->GetMediaBuffer(), client->GetReleaseFenceHandle()));
+    }
+  }
+  sp<AMessage> notify =
+            new AMessage(kNotifyPostReleaseVideoBuffer, mReflector->id());
+  // post AMessage to OmxDecoder via ALooper.
+  notify->post();
+}
+
 /* static */ void
 OmxDecoder::RecycleCallback(TextureClient* aClient, void* aClosure)
 {
   OmxDecoder* decoder = static_cast<OmxDecoder*>(aClosure);
-  GrallocTextureClientOGL* client = static_cast<GrallocTextureClientOGL*>(aClient);
-
-  aClient->ClearRecycleCallback();
-  decoder->PostReleaseVideoBuffer(client->GetMediaBuffer(), client->GetReleaseFenceHandle());
+  decoder->RecycleCallbackImp(aClient);
 }
 
 int64_t OmxDecoder::ProcessCachedData(int64_t aOffset, bool aWaitForCompletion)
 {
   // We read data in chunks of 32 KiB. We can reduce this
   // value if media, such as sdcards, is too slow.
   // Because of SD card's slowness, need to keep sReadSize to small size.
   // See Bug 914870.
--- a/content/media/omx/OmxDecoder.h
+++ b/content/media/omx/OmxDecoder.h
@@ -1,8 +1,9 @@
+#include <set>
 #include <stagefright/foundation/ABase.h>
 #include <stagefright/foundation/AHandlerReflector.h>
 #include <stagefright/foundation/ALooper.h>
 #include <utils/RefBase.h>
 #include <stagefright/MediaExtractor.h>
 
 #include "GonkNativeWindow.h"
 #include "GonkNativeWindowClient.h"
@@ -84,17 +85,21 @@ class OmxDecoder : public OMXCodecProxy:
     FenceHandle mReleaseFenceHandle;
   };
 
   // Hold video's MediaBuffers that are released during video seeking.
   // The holded MediaBuffers are released soon after seek completion.
   // OMXCodec does not accept MediaBuffer during seeking. If MediaBuffer is
   //  returned to OMXCodec during seeking, OMXCodec calls assert.
   Vector<BufferItem> mPendingVideoBuffers;
-  // The lock protects mPendingVideoBuffers.
+
+  // Hold TextureClients that are waiting to be recycled.
+  std::set<TextureClient*> mPendingRecycleTexutreClients;
+
+  // The lock protects mPendingVideoBuffers and mPendingRecycleTexutreClients.
   Mutex mPendingVideoBuffersLock;
 
   // Show if OMXCodec is seeking.
   bool mIsVideoSeeking;
   // The lock protects video MediaBuffer release()'s pending operations called
   //  from multiple threads. The pending operations happen only during video
   //  seeking. Holding mSeekLock long time could affect to video rendering.
   // Holding time should be minimum.
@@ -204,13 +209,15 @@ public:
   // Receive a message from AHandlerReflector.
   // Called on ALooper thread.
   void onMessageReceived(const sp<AMessage> &msg);
 
   int64_t ProcessCachedData(int64_t aOffset, bool aWaitForCompletion);
 
   sp<MediaSource> GetAudioOffloadTrack() { return mAudioOffloadTrack; }
 
+  void RecycleCallbackImp(TextureClient* aClient);
+
   static void RecycleCallback(TextureClient* aClient, void* aClosure);
 };
 
 }
 
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -61,16 +61,20 @@
 #include "private/pprio.h"
 #include "nsContentPermissionHelper.h"
 
 #include "mozilla/dom/DeviceStorageBinding.h"
 
 // Microsoft's API Name hackery sucks
 #undef CreateEvent
 
+#ifdef MOZ_WIDGET_ANDROID
+#include "AndroidBridge.h"
+#endif
+
 #ifdef MOZ_WIDGET_GONK
 #include "nsIVolume.h"
 #include "nsIVolumeService.h"
 #endif
 
 #define DEVICESTORAGE_PROPERTIES \
   "chrome://global/content/devicestorage.properties"
 #define DEFAULT_THREAD_TIMEOUT_MS 30000
@@ -632,24 +636,26 @@ OverrideRootDir::GetSingleton()
     return sSingleton;
   }
   // Preference changes are automatically forwarded from parent to child
   // in ContentParent::Observe, so we'll see the change in both the parent
   // and the child process.
 
   sSingleton = new OverrideRootDir();
   Preferences::AddStrongObserver(sSingleton, "device.storage.overrideRootDir");
+  Preferences::AddStrongObserver(sSingleton, "device.storage.testing");
   ClearOnShutdown(&sSingleton);
 
   return sSingleton;
 }
 
 OverrideRootDir::~OverrideRootDir()
 {
   Preferences::RemoveObserver(this, "device.storage.overrideRootDir");
+  Preferences::RemoveObserver(this, "device.storage.testing");
 }
 
 NS_IMETHODIMP
 OverrideRootDir::Observe(nsISupports *aSubject,
                               const char *aTopic,
                               const char16_t *aData)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -727,55 +733,86 @@ InitDirs()
   ClearOnShutdown(&sDirs);
 
   nsCOMPtr<nsIProperties> dirService
     = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
   MOZ_ASSERT(dirService);
 
 #if !defined(MOZ_WIDGET_GONK)
 
+// Keep MOZ_WIDGET_COCOA above XP_UNIX,
+// because both are defined in Darwin builds.
 #if defined (MOZ_WIDGET_COCOA)
   dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR,
                   NS_GET_IID(nsIFile),
                   getter_AddRefs(sDirs->pictures));
   dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR,
                   NS_GET_IID(nsIFile),
                   getter_AddRefs(sDirs->videos));
   dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR,
                   NS_GET_IID(nsIFile),
                   getter_AddRefs(sDirs->music));
+
+// Keep MOZ_WIDGET_ANDROID above XP_UNIX,
+// because both are defined in Android builds.
+#elif defined (MOZ_WIDGET_ANDROID)
+  nsAutoString path;
+  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
+      NS_LITERAL_STRING(DEVICESTORAGE_PICTURES), path))) {
+    NS_NewLocalFile(path, /* aFollowLinks */ true,
+                    getter_AddRefs(sDirs->pictures));
+  }
+  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
+      NS_LITERAL_STRING(DEVICESTORAGE_VIDEOS), path))) {
+    NS_NewLocalFile(path, /* aFollowLinks */ true,
+                    getter_AddRefs(sDirs->videos));
+  }
+  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
+      NS_LITERAL_STRING(DEVICESTORAGE_MUSIC), path))) {
+    NS_NewLocalFile(path, /* aFollowLinks */ true,
+                    getter_AddRefs(sDirs->music));
+  }
+  if (NS_SUCCEEDED(mozilla::AndroidBridge::GetExternalPublicDirectory(
+      NS_LITERAL_STRING(DEVICESTORAGE_SDCARD), path))) {
+    NS_NewLocalFile(path, /* aFollowLinks */ true,
+                    getter_AddRefs(sDirs->sdcard));
+  }
+
 #elif defined (XP_UNIX)
   dirService->Get(NS_UNIX_XDG_PICTURES_DIR,
                   NS_GET_IID(nsIFile),
                   getter_AddRefs(sDirs->pictures));
   dirService->Get(NS_UNIX_XDG_VIDEOS_DIR,
                   NS_GET_IID(nsIFile),
                   getter_AddRefs(sDirs->videos));
   dirService->Get(NS_UNIX_XDG_MUSIC_DIR,
                   NS_GET_IID(nsIFile),
                   getter_AddRefs(sDirs->music));
+
 #elif defined (XP_WIN)
   dirService->Get(NS_WIN_PICTURES_DIR,
                   NS_GET_IID(nsIFile),
                   getter_AddRefs(sDirs->pictures));
   dirService->Get(NS_WIN_VIDEOS_DIR,
                   NS_GET_IID(nsIFile),
                   getter_AddRefs(sDirs->videos));
   dirService->Get(NS_WIN_MUSIC_DIR,
                   NS_GET_IID(nsIFile),
                   getter_AddRefs(sDirs->music));
 #endif
 
+#ifndef MOZ_WIDGET_ANDROID
   // Eventually, on desktop, we want to do something smarter -- for example,
   // detect when an sdcard is inserted, and use that instead of this.
   dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
                   getter_AddRefs(sDirs->sdcard));
   if (sDirs->sdcard) {
     sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard"));
   }
+#endif // !MOZ_WIDGET_ANDROID
 #endif // !MOZ_WIDGET_GONK
 
 #ifdef MOZ_WIDGET_GONK
   NS_NewLocalFile(NS_LITERAL_STRING("/data"),
                   false,
                   getter_AddRefs(sDirs->apps));
 #else
   dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
--- a/dom/devicestorage/test/mochitest.ini
+++ b/dom/devicestorage/test/mochitest.ini
@@ -1,28 +1,29 @@
 [DEFAULT]
-skip-if = toolkit == 'android' || e10s #bug 781789 & bug 782275
+skip-if = e10s # bug 781789 & bug 782275
 support-files = devicestorage_common.js
 
 [test_823965.html]
 # [test_add.html]
 # man, our mime database sucks hard.  followup bug # 788273
 [test_addCorrectType.html]
 [test_available.html]
 [test_basic.html]
-[test_overrideDir.html]
+[test_dirs.html]
 # [test_diskSpace.html]
 # Possible race between the time we write a file, and the
 # time it takes to be reflected by statfs().  Bug # 791287
 [test_dotdot.html]
 [test_enumerate.html]
 [test_enumerateMultipleContinue.html]
 [test_enumerateOptions.html]
 [test_freeSpace.html]
 [test_lastModificationFilter.html]
+[test_overrideDir.html]
 [test_overwrite.html]
 [test_sanity.html]
 [test_usedSpace.html]
 [test_watch.html]
 [test_watchOther.html]
 
 # FileSystem API tests
 [test_fs_basic.html]
new file mode 100644
--- /dev/null
+++ b/dom/devicestorage/test/test_dirs.html
@@ -0,0 +1,70 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=886627
+-->
+<head>
+  <title>Test for the device storage API</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="devicestorage_common.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=886627">
+    Mozilla Bug 886627
+  </a>
+  <p id="display"></p>
+  <div id="content" style="display: none"></div>
+  <pre id="test">
+  <script class="testbody" type="text/javascript">
+
+/**
+ * Test that common device storage directories are available.
+ *
+ * This test differs from other device storage tests in that other tests use a
+ * "testing mode", which relocates the device storage directories to a testing
+ * directory. On the other hand, this test turns off testing mode to makes sure
+ * that the normal, non-testing directories also work properly.
+ */
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({
+  'set': [
+    ["device.storage.enabled", true],
+    ["device.storage.testing", false],
+    ["device.storage.prompt.testing", true],
+  ]
+}, function() {
+  ok(navigator.getDeviceStorage, "Should have getDeviceStorage");
+  ok(!navigator.getDeviceStorage("nonexistent-type"), "Should not have nonexistent storage");
+
+  ok(navigator.getDeviceStorage("pictures"), "Should have pictures storage");
+  ok(navigator.getDeviceStorage("videos"), "Should have videos storage");
+  ok(navigator.getDeviceStorage("music"), "Should have music storage");
+
+  // Need special permission to access "apps". We always have the permission in B2G
+  // mochitests, but on other platforms, we need to manually add the permission.
+  if (!SpecialPowers.testPermission(
+      "webapps-manage", SpecialPowers.Ci.nsIPermissionManager.ALLOW_ACTION, document)) {
+    ok(!navigator.getDeviceStorage("apps"), "Should not have apps storage without permission");
+    SpecialPowers.addPermission("webapps-manage", true, document);
+  }
+  ok(navigator.getDeviceStorage("apps"), "Should have apps storage with permission");
+
+  ok(navigator.getDeviceStorage("sdcard"), "Should have sdcard storage");
+  ok(navigator.getDeviceStorage("crashes"), "Should have crashes storage");
+
+  // The test harness reverts our pref changes automatically.
+  SimpleTest.finish();
+});
+
+  </script>
+  </pre>
+</body>
+</html>
+
--- a/dom/encoding/Makefile.in
+++ b/dom/encoding/Makefile.in
@@ -1,15 +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/.
 
 include $(topsrcdir)/config/rules.mk
 
-PROPS2ARRAYS = $(topsrcdir)/intl/locale/src/props2arrays.py
+PROPS2ARRAYS = $(topsrcdir)/intl/locale/props2arrays.py
 labelsencodings.properties.h: $(PROPS2ARRAYS) labelsencodings.properties
 	$(PYTHON) $^ $@
 localesfallbacks.properties.h: $(PROPS2ARRAYS) localesfallbacks.properties
 	$(PYTHON) $^ $@
 domainsfallbacks.properties.h: $(PROPS2ARRAYS) domainsfallbacks.properties
 	$(PYTHON) $^ $@
 encodingsgroups.properties.h: $(PROPS2ARRAYS) encodingsgroups.properties
 	$(PYTHON) $^ $@
--- a/dom/encoding/moz.build
+++ b/dom/encoding/moz.build
@@ -19,17 +19,17 @@ UNIFIED_SOURCES += [
     'TextDecoder.cpp',
     'TextEncoder.cpp',
 ]
 
 FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
-    '/intl/locale/src',
+    '/intl/locale',
 ]
 
 GENERATED_FILES += [
     'domainsfallbacks.properties.h',
     'encodingsgroups.properties.h',
     'labelsencodings.properties.h',
     'localesfallbacks.properties.h',
     'nonparticipatingdomains.properties.h',
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3600,17 +3600,17 @@ CreateMouseOrPointerWidgetEvent(WidgetMo
                            aMouseEvent->widget, WidgetMouseEvent::eReal);
     aNewEvent->relatedTarget = aRelatedContent;
   }
   aNewEvent->refPoint = aMouseEvent->refPoint;
   aNewEvent->modifiers = aMouseEvent->modifiers;
   aNewEvent->button = aMouseEvent->button;
   aNewEvent->buttons = aMouseEvent->buttons;
   aNewEvent->pressure = aMouseEvent->pressure;
-  aNewEvent->pluginEvent = aMouseEvent->pluginEvent;
+  aNewEvent->mPluginEvent = aMouseEvent->mPluginEvent;
   aNewEvent->inputSource = aMouseEvent->inputSource;
 }
 
 nsIFrame*
 EventStateManager::DispatchMouseOrPointerEvent(WidgetMouseEvent* aMouseEvent,
                                                uint32_t aMessage,
                                                nsIContent* aTargetContent,
                                                nsIContent* aRelatedContent)
--- a/dom/events/test/test_bug603008.html
+++ b/dom/events/test/test_bug603008.html
@@ -422,17 +422,17 @@ function testPreventDefault() {
      { name: "touchend", prevent: true }],
     [{ name: "touchstart", prevent: false },
      { name: "touchmove", prevent: false },
      { name: "touchmove", prevent: false, doPrevent: true },
      { name: "touchend", prevent: false }],
     [{ name: "touchstart", prevent: false },
      { name: "touchmove", prevent: false },
      { name: "touchmove", prevent: false },
-     { name: "touchend", prevent: true, doPrevent: true }]
+     { name: "touchend", prevent: false, doPrevent: true }]
   ];
 
   var dotest = function(aTest) {
     if (aTest.doPrevent) {
       target.addEventListener(aTest.name, preventFunction, false);
     }
 
     if (aTest.name == "touchmove") {
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl
+++ b/dom/interfaces/base/nsIServiceWorkerManager.idl
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 
 interface nsIDocument;
 interface nsIURI;
 
-[uuid(6e1382f4-3cbc-435f-8ce0-70175f6eb400)]
+[uuid(cc539f1e-1ce6-4af5-bf94-195b30bde010)]
 interface nsIServiceWorkerManager : nsISupports
 {
   // Returns a Promise
   nsISupports register(in nsIDOMWindow aWindow, in DOMString aScope, in DOMString aScriptURI);
 
   // Returns a Promise
   nsISupports unregister(in nsIDOMWindow aWindow, in DOMString aScope);
 
@@ -31,15 +31,21 @@ interface nsIServiceWorkerManager : nsIS
 
   /**
    * Documents that have called MaybeStartControlling() should call this when
    * they are destroyed. This function may be called multiple times, and is
    * idempotent.
    */
   [notxpcom,nostdcall] void MaybeStopControlling(in nsIDocument aDoc);
 
+  // Returns a ServiceWorker
+  [noscript] nsISupports GetInstalling(in nsIDOMWindow aWindow);
+  [noscript] nsISupports GetWaiting(in nsIDOMWindow aWindow);
+  [noscript] nsISupports GetActive(in nsIDOMWindow aWindow);
+  [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow);
+
   // Testing
   DOMString getScopeForUrl(in DOMString path);
 };
 
 %{ C++
 #define SERVICEWORKERMANAGER_CONTRACTID "@mozilla.org/serviceworkers/manager;1"
 %}
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -724,17 +724,16 @@ TabChild::TabChild(nsIContentChild* aMan
   , mAppPackageFileDescriptorRecved(false)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
   , mPendingTouchPreventedResponse(false)
-  , mTouchEndIsClick(Unknown)
   , mIgnoreKeyPressEvent(false)
   , mActiveElementManager(new ActiveElementManager())
   , mHasValidInnerSize(false)
   , mUniqueId(0)
 {
   if (!sActiveDurationMsSet) {
     Preferences::AddIntVarCache(&sActiveDurationMs,
                                 "ui.touch_activation.duration_ms",
@@ -1811,20 +1810,16 @@ TabChild::RecvHandleDoubleTap(const CSSP
 
 bool
 TabChild::RecvHandleSingleTap(const CSSPoint& aPoint, const ScrollableLayerGuid& aGuid)
 {
   if (!mGlobal || !mTabChildGlobal) {
     return true;
   }
 
-  if (mTouchEndIsClick == IsNotClick) {
-    return true;
-  }
-
   LayoutDevicePoint currentPoint = APZCCallbackHelper::ApplyCallbackTransform(aPoint, aGuid) * mWidget->GetDefaultScale();;
 
   MessageLoop::current()->PostDelayedTask(
     FROM_HERE,
     NewRunnableMethod(this, &TabChild::FireSingleTapEvent, currentPoint),
     sActiveDurationMs);
   return true;
 }
@@ -1907,17 +1902,17 @@ TabChild::RecvNotifyAPZStateChange(const
   }
   case APZStateChange::StartPanning:
   {
     mActiveElementManager->HandlePanStart();
     break;
   }
   case APZStateChange::EndTouch:
   {
-    mTouchEndIsClick = (aArg ? IsClick : IsNotClick);
+    mActiveElementManager->HandleTouchEnd(aArg);
     break;
   }
   default:
     // APZStateChange has a 'sentinel' value, and the compiler complains
     // if an enumerator is not handled and there is no 'default' case.
     break;
   }
   return true;
@@ -2126,43 +2121,34 @@ TabChild::RecvRealTouchEvent(const Widge
   if (aEvent.message == NS_TOUCH_START && localEvent.touches.Length() > 0) {
     mActiveElementManager->SetTargetElement(localEvent.touches[0]->GetTarget());
   }
 
   bool isTouchPrevented = nsIPresShell::gPreventMouseEvents ||
                           localEvent.mFlags.mMultipleActionsPrevented;
   switch (aEvent.message) {
   case NS_TOUCH_START: {
-    mTouchEndIsClick = Unknown;
     if (mPendingTouchPreventedResponse) {
       // We can enter here if we get two TOUCH_STARTs in a row and didn't
       // respond to the first one. Respond to it now.
       SendContentReceivedTouch(mPendingTouchPreventedGuid, false);
       mPendingTouchPreventedResponse = false;
     }
     if (isTouchPrevented) {
       SendContentReceivedTouch(aGuid, isTouchPrevented);
     } else {
       mPendingTouchPreventedResponse = true;
       mPendingTouchPreventedGuid = aGuid;
     }
     break;
   }
 
+  case NS_TOUCH_MOVE:
   case NS_TOUCH_END:
-    if (isTouchPrevented && mTouchEndIsClick == IsClick) {
-      mTouchEndIsClick = IsNotClick;
-    }
-    // fall through
-  case NS_TOUCH_CANCEL:
-    if (mTouchEndIsClick != Unknown) {
-      mActiveElementManager->HandleTouchEnd(mTouchEndIsClick == IsClick);
-    }
-    // fall through
-  case NS_TOUCH_MOVE: {
+  case NS_TOUCH_CANCEL: {
     SendPendingTouchPreventedResponse(isTouchPrevented, aGuid);
     break;
   }
 
   default:
     NS_WARNING("Unknown touch event type");
   }
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -589,23 +589,16 @@ private:
     bool mNotified;
     bool mTriedBrowserInit;
     ScreenOrientation mOrientation;
     bool mUpdateHitRegion;
     bool mPendingTouchPreventedResponse;
     ScrollableLayerGuid mPendingTouchPreventedGuid;
     void FireSingleTapEvent(LayoutDevicePoint aPoint);
 
-    enum ClickState {
-      Unknown,
-      IsClick,
-      IsNotClick
-    };
-    ClickState mTouchEndIsClick;
-
     bool mIgnoreKeyPressEvent;
     nsRefPtr<ActiveElementManager> mActiveElementManager;
     bool mHasValidInnerSize;
     uint64_t mUniqueId;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -59,18 +59,17 @@ skip-if = toolkit == 'gonk' # b2g(Bug 96
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_basicVideo.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_basicScreenshare.html]
 skip-if = toolkit == 'gonk' || toolkit == 'android' # no screenshare on b2g/android
 [test_peerConnection_basicWindowshare.html]
 skip-if = toolkit == 'gonk' || toolkit == 'android' # no windowshare on b2g/android
 [test_peerConnection_basicH264Video.html]
-skip-if = true # disabled until we can resolve plugin installation issues
-#skip-if = toolkit == 'gonk' || toolkit == 'android' # no openh264 on b2g/android
+skip-if = buildapp == 'b2g' || os == 'android' # bug 1043403
 [test_peerConnection_bug822674.html]
 [test_peerConnection_bug825703.html]
 [test_peerConnection_bug827843.html]
 skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_bug834153.html]
 [test_peerConnection_bug835370.html]
 [test_peerConnection_bug1013809.html]
 skip-if = (toolkit == 'gonk' && debug) # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -1807,17 +1807,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
   nsCOMPtr<nsIPluginWidget> pluginWidget = do_QueryInterface(mWidget);
   if (!pluginWidget || NS_FAILED(pluginWidget->StartDrawPlugin()))
     return nsEventStatus_eIgnore;
 
   NPEventModel eventModel = GetEventModel();
 
   // If we have to synthesize an event we'll use one of these.
   NPCocoaEvent synthCocoaEvent;
-  void* event = anEvent.pluginEvent;
+  const NPCocoaEvent* event = static_cast<const NPCocoaEvent*>(anEvent.mPluginEvent);
   nsPoint pt =
   nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) -
   mObjectFrame->GetContentRectRelativeToSelf().TopLeft();
   nsPresContext* presContext = mObjectFrame->PresContext();
   // Plugin event coordinates need to be translated from device pixels
   // into "display pixels" in HiDPI modes.
   double scaleFactor = 1.0;
   GetContentsScaleFactor(&scaleFactor);
@@ -1874,17 +1874,19 @@ nsEventStatus nsPluginInstanceOwner::Pro
       pluginWidget->EndDrawPlugin();
       return nsEventStatus_eIgnore;
     }
   }
 
   int16_t response = kNPEventNotHandled;
   void* window = FixUpPluginWindow(ePluginPaintEnable);
   if (window || (eventModel == NPEventModelCocoa)) {
-    mInstance->HandleEvent(event, &response, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+    mInstance->HandleEvent(const_cast<NPCocoaEvent*>(event),
+                           &response,
+                           NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
   }
 
   if (eventModel == NPEventModelCocoa && response == kNPEventStartIME) {
     pluginWidget->StartComplexTextInputForCurrentEvent();
   }
 
   if ((response == kNPEventHandled || response == kNPEventStartIME) &&
       !(anEvent.message == NS_MOUSE_BUTTON_DOWN &&
@@ -1893,17 +1895,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
     rv = nsEventStatus_eConsumeNoDefault;
   }
 
   pluginWidget->EndDrawPlugin();
 #endif
 
 #ifdef XP_WIN
   // this code supports windowless plugins
-  NPEvent *pPluginEvent = (NPEvent*)anEvent.pluginEvent;
+  const NPEvent *pPluginEvent = static_cast<const NPEvent*>(anEvent.mPluginEvent);
   // we can get synthetic events from the EventStateManager... these
   // have no pluginEvent
   NPEvent pluginEvent;
   if (anEvent.eventStructType == NS_MOUSE_EVENT) {
     if (!pPluginEvent) {
       // XXX Should extend this list to synthesize events for more event
       // types
       pluginEvent.event = 0;
@@ -1962,17 +1964,17 @@ nsEventStatus nsPluginInstanceOwner::Pro
                    "Incorrect event type for coordinate translation");
       nsPoint pt =
         nsLayoutUtils::GetEventCoordinatesRelativeTo(&anEvent, mObjectFrame) -
         mObjectFrame->GetContentRectRelativeToSelf().TopLeft();
       nsPresContext* presContext = mObjectFrame->PresContext();
       nsIntPoint ptPx(presContext->AppUnitsToDevPixels(pt.x),
                       presContext->AppUnitsToDevPixels(pt.y));
       nsIntPoint widgetPtPx = ptPx + mObjectFrame->GetWindowOriginInPixels(true);
-      pPluginEvent->lParam = MAKELPARAM(widgetPtPx.x, widgetPtPx.y);
+      const_cast<NPEvent*>(pPluginEvent)->lParam = MAKELPARAM(widgetPtPx.x, widgetPtPx.y);
     }
   }
   else if (!pPluginEvent) {
     switch (anEvent.message) {
       case NS_FOCUS_CONTENT:
         pluginEvent.event = WM_SETFOCUS;
         pluginEvent.wParam = 0;
         pluginEvent.lParam = 0;
@@ -1990,17 +1992,19 @@ nsEventStatus nsPluginInstanceOwner::Pro
   if (pPluginEvent && !pPluginEvent->event) {
     // Don't send null events to plugins.
     NS_WARNING("nsObjectFrame ProcessEvent: trying to send null event to plugin.");
     return rv;
   }
 
   if (pPluginEvent) {
     int16_t response = kNPEventNotHandled;
-    mInstance->HandleEvent(pPluginEvent, &response, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+    mInstance->HandleEvent(const_cast<NPEvent*>(pPluginEvent),
+                           &response,
+                           NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
     if (response == kNPEventHandled)
       rv = nsEventStatus_eConsumeNoDefault;
   }
 #endif
 
 #ifdef MOZ_X11
   // this code supports windowless plugins
   nsIWidget* widget = anEvent.widget;
@@ -2112,24 +2116,24 @@ nsEventStatus nsPluginInstanceOwner::Pro
             break;
           }
       }
       break;
 
    //XXX case NS_MOUSE_SCROLL_EVENT: not received.
 
    case NS_KEY_EVENT:
-      if (anEvent.pluginEvent)
+      if (anEvent.mPluginEvent)
         {
           XKeyEvent &event = pluginEvent.xkey;
 #ifdef MOZ_WIDGET_GTK
           event.root = GDK_ROOT_WINDOW();
           event.time = anEvent.time;
           const GdkEventKey* gdkEvent =
-            static_cast<const GdkEventKey*>(anEvent.pluginEvent);
+            static_cast<const GdkEventKey*>(anEvent.mPluginEvent);
           event.keycode = gdkEvent->hardware_keycode;
           event.state = gdkEvent->state;
           switch (anEvent.message)
             {
             case NS_KEY_DOWN:
               // Handle NS_KEY_DOWN for modifier key presses
               // For non-modifiers we get NS_KEY_PRESS
               if (gdkEvent->is_modifier)
@@ -2264,21 +2268,23 @@ nsEventStatus nsPluginInstanceOwner::Pro
       }
       break;
 
     case NS_KEY_EVENT:
      {
        const WidgetKeyboardEvent& keyEvent = *anEvent.AsKeyboardEvent();
        LOG("Firing NS_KEY_EVENT %d %d\n", keyEvent.keyCode, keyEvent.charCode);
        // pluginEvent is initialized by nsWindow::InitKeyEvent().
-       ANPEvent* pluginEvent = reinterpret_cast<ANPEvent*>(keyEvent.pluginEvent);
+       const ANPEvent* pluginEvent = static_cast<const ANPEvent*>(keyEvent.mPluginEvent);
        if (pluginEvent) {
          MOZ_ASSERT(pluginEvent->inSize == sizeof(ANPEvent));
          MOZ_ASSERT(pluginEvent->eventType == kKey_ANPEventType);
-         mInstance->HandleEvent(pluginEvent, nullptr, NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
+         mInstance->HandleEvent(const_cast<ANPEvent*>(pluginEvent),
+                                nullptr,
+                                NS_PLUGIN_CALL_SAFE_TO_REENTER_GECKO);
        }
      }
      break;
 
     default:
       break;
     }
     rv = nsEventStatus_eConsumeNoDefault;
@@ -2982,17 +2988,17 @@ void* nsPluginInstanceOwner::FixUpPlugin
     // Set this before calling ProcessEvent to avoid endless recursion.
     mSentInitialTopLevelWindowEvent = true;
 
     WidgetPluginEvent pluginEvent(true, NS_PLUGIN_FOCUS_EVENT, nullptr);
     NPCocoaEvent cocoaEvent;
     InitializeNPCocoaEvent(&cocoaEvent);
     cocoaEvent.type = NPCocoaEventWindowFocusChanged;
     cocoaEvent.data.focus.hasFocus = cocoaTopLevelWindow ? NS_NPAPI_CocoaWindowIsMain(cocoaTopLevelWindow) : true;
-    pluginEvent.pluginEvent = &cocoaEvent;
+    pluginEvent.mPluginEvent.Copy(cocoaEvent);
     ProcessEvent(pluginEvent);
   }
 
   return nullptr;
 }
 
 void
 nsPluginInstanceOwner::HidePluginWindow()
--- a/dom/workers/ServiceWorkerContainer.cpp
+++ b/dom/workers/ServiceWorkerContainer.cpp
@@ -12,27 +12,45 @@
 
 #include "nsCycleCollectionParticipant.h"
 #include "nsServiceManagerUtils.h"
 
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ServiceWorkerContainerBinding.h"
 #include "mozilla/dom/workers/bindings/ServiceWorker.h"
 
+#include "ServiceWorker.h"
+
 namespace mozilla {
 namespace dom {
 namespace workers {
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper, mWindow)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper,
+                                   mInstallingWorker,
+                                   mWaitingWorker,
+                                   mActiveWorker,
+                                   mControllerWorker)
+
+ServiceWorkerContainer::ServiceWorkerContainer(nsPIDOMWindow* aWindow)
+  : mWindow(aWindow)
+{
+  SetIsDOMBinding();
+  StartListeningForEvents();
+}
+
+ServiceWorkerContainer::~ServiceWorkerContainer()
+{
+  StopListeningForEvents();
+}
 
 JSObject*
 ServiceWorkerContainer::WrapObject(JSContext* aCx)
 {
   return ServiceWorkerContainerBinding::Wrap(aCx, this);
 }
 
 already_AddRefed<Promise>
@@ -80,39 +98,67 @@ ServiceWorkerContainer::Unregister(const
   nsRefPtr<Promise> ret = static_cast<Promise*>(promise.get());
   MOZ_ASSERT(ret);
   return ret.forget();
 }
 
 already_AddRefed<workers::ServiceWorker>
 ServiceWorkerContainer::GetInstalling()
 {
-  // FIXME(nsm): Bug 1002570
-  return nullptr;
+  if (!mInstallingWorker) {
+    mInstallingWorker = GetWorkerReference(WhichServiceWorker::INSTALLING_WORKER);
+  }
+
+  nsRefPtr<ServiceWorker> ret = mInstallingWorker;
+  return ret.forget();
 }
 
 already_AddRefed<workers::ServiceWorker>
 ServiceWorkerContainer::GetWaiting()
 {
-  // FIXME(nsm): Bug 1002570
-  return nullptr;
+  if (!mWaitingWorker) {
+    mWaitingWorker = GetWorkerReference(WhichServiceWorker::WAITING_WORKER);
+  }
+
+  nsRefPtr<ServiceWorker> ret = mWaitingWorker;
+  return ret.forget();
 }
 
 already_AddRefed<workers::ServiceWorker>
 ServiceWorkerContainer::GetActive()
 {
-  // FIXME(nsm): Bug 1002570
-  return nullptr;
+  if (!mActiveWorker) {
+    mActiveWorker = GetWorkerReference(WhichServiceWorker::ACTIVE_WORKER);
+  }
+
+  nsRefPtr<ServiceWorker> ret = mActiveWorker;
+  return ret.forget();
 }
 
 already_AddRefed<workers::ServiceWorker>
 ServiceWorkerContainer::GetController()
 {
-  // FIXME(nsm): Bug 1002570
-  return nullptr;
+  if (!mControllerWorker) {
+    nsresult rv;
+    nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+
+    nsCOMPtr<nsISupports> serviceWorker;
+    rv = swm->GetDocumentController(mWindow, getter_AddRefs(serviceWorker));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return nullptr;
+    }
+
+    mControllerWorker = static_cast<ServiceWorker*>(serviceWorker.get());
+  }
+
+  nsRefPtr<ServiceWorker> ref = mControllerWorker;
+  return ref.forget();
 }
 
 already_AddRefed<Promise>
 ServiceWorkerContainer::GetAll(ErrorResult& aRv)
 {
   // FIXME(nsm): Bug 1002571
   aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   return nullptr;
@@ -141,16 +187,64 @@ void
 ServiceWorkerContainer::StopListeningForEvents()
 {
   nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID);
   if (swm) {
     swm->RemoveContainerEventListener(mWindow->GetDocumentURI(), this);
   }
 }
 
+void
+ServiceWorkerContainer::InvalidateWorkerReference(WhichServiceWorker aWhichOnes)
+{
+  if (aWhichOnes & WhichServiceWorker::INSTALLING_WORKER) {
+    mInstallingWorker = nullptr;
+  }
+
+  if (aWhichOnes & WhichServiceWorker::WAITING_WORKER) {
+    mWaitingWorker = nullptr;
+  }
+
+  if (aWhichOnes & WhichServiceWorker::ACTIVE_WORKER) {
+    mActiveWorker = nullptr;
+  }
+}
+
+already_AddRefed<workers::ServiceWorker>
+ServiceWorkerContainer::GetWorkerReference(WhichServiceWorker aWhichOne)
+{
+  nsresult rv;
+  nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsISupports> serviceWorker;
+  switch(aWhichOne) {
+    case WhichServiceWorker::INSTALLING_WORKER:
+      rv = swm->GetInstalling(mWindow, getter_AddRefs(serviceWorker));
+      break;
+    case WhichServiceWorker::WAITING_WORKER:
+      rv = swm->GetWaiting(mWindow, getter_AddRefs(serviceWorker));
+      break;
+    case WhichServiceWorker::ACTIVE_WORKER:
+      rv = swm->GetActive(mWindow, getter_AddRefs(serviceWorker));
+      break;
+    default:
+      MOZ_CRASH("Invalid enum value");
+  }
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return nullptr;
+  }
+
+  nsRefPtr<ServiceWorker> ref = static_cast<ServiceWorker*>(serviceWorker.get());
+  return ref.forget();
+}
+
 // Testing only.
 already_AddRefed<Promise>
 ServiceWorkerContainer::ClearAllServiceWorkerData(ErrorResult& aRv)
 {
   aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   return nullptr;
 }
 
--- a/dom/workers/ServiceWorkerContainer.h
+++ b/dom/workers/ServiceWorkerContainer.h
@@ -4,16 +4,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/. */
 
 #ifndef mozilla_dom_workers_serviceworkercontainer_h__
 #define mozilla_dom_workers_serviceworkercontainer_h__
 
 #include "mozilla/DOMEventTargetHelper.h"
 
+#include "ServiceWorkerManager.h"
+
 class nsPIDOMWindow;
 
 namespace mozilla {
 namespace dom {
 
 class Promise;
 struct RegistrationOptionList;
 
@@ -28,22 +30,17 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
 
   IMPL_EVENT_HANDLER(updatefound)
   IMPL_EVENT_HANDLER(controllerchange)
   IMPL_EVENT_HANDLER(reloadpage)
   IMPL_EVENT_HANDLER(error)
 
-  explicit ServiceWorkerContainer(nsPIDOMWindow* aWindow)
-    : mWindow(aWindow)
-  {
-    SetIsDOMBinding();
-    StartListeningForEvents();
-  }
+  explicit ServiceWorkerContainer(nsPIDOMWindow* aWindow);
 
   nsPIDOMWindow*
   GetParentObject() const
   {
     return mWindow;
   }
 
   JSObject*
@@ -76,41 +73,56 @@ public:
   GetReady(ErrorResult& aRv);
 
   nsIURI*
   GetDocumentURI() const
   {
     return mWindow->GetDocumentURI();
   }
 
+  void
+  InvalidateWorkerReference(WhichServiceWorker aWhichOnes);
+
+  already_AddRefed<workers::ServiceWorker>
+  GetWorkerReference(WhichServiceWorker aWhichOne);
+
   // Testing only.
   already_AddRefed<Promise>
   ClearAllServiceWorkerData(ErrorResult& aRv);
 
   // Testing only.
   void
   GetScopeForUrl(const nsAString& aUrl, nsString& aScope, ErrorResult& aRv);
 
   // Testing only.
   void
   GetControllingWorkerScriptURLForPath(const nsAString& aPath,
                                        nsString& aScriptURL,
                                        ErrorResult& aRv);
 private:
-  ~ServiceWorkerContainer()
-  {
-    StopListeningForEvents();
-  }
+  ~ServiceWorkerContainer();
 
   void
   StartListeningForEvents();
 
   void
   StopListeningForEvents();
 
   nsCOMPtr<nsPIDOMWindow> mWindow;
+
+  // The following properties are cached here to ensure JS equality is satisfied
+  // instead of acquiring a new worker instance from the ServiceWorkerManager
+  // for every access. A null value is considered a cache miss.
+  // These three may change to a new worker at any time.
+  nsRefPtr<ServiceWorker> mInstallingWorker;
+  nsRefPtr<ServiceWorker> mWaitingWorker;
+  nsRefPtr<ServiceWorker> mActiveWorker;
+  // This only changes when a worker hijacks everything in its scope by calling
+  // replace().
+  // FIXME(nsm): Bug 982711. Provide API to let SWM invalidate this.
+  nsRefPtr<ServiceWorker> mControllerWorker;
 };
 
 } // namespace workers
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_workers_serviceworkercontainer_h__ */
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -20,16 +20,17 @@
 #include "nsContentUtils.h"
 #include "nsCxPusher.h"
 #include "nsNetUtil.h"
 #include "nsProxyRelease.h"
 #include "nsTArray.h"
 
 #include "RuntimeService.h"
 #include "ServiceWorker.h"
+#include "ServiceWorkerContainer.h"
 #include "ServiceWorkerEvents.h"
 #include "WorkerInlines.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "WorkerScope.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -566,16 +567,18 @@ ServiceWorkerManager::Update(ServiceWork
   if (aRegistration->mInstallingWorker) {
     // FIXME(nsm): Terminate the worker. We still haven't figured out worker
     // instance ownership when not associated with a window, so let's wait on
     // this.
     // FIXME(nsm): We should be setting the state on the actual worker
     // instance.
     // FIXME(nsm): Fire "statechange" on installing worker instance.
     aRegistration->mInstallingWorker = nullptr;
+    InvalidateServiceWorkerContainerWorker(aRegistration,
+                                           WhichServiceWorker::INSTALLING_WORKER);
   }
 
   aRegistration->mUpdatePromise = new UpdatePromise();
   // FIXME(nsm): Bug 931249. If we don't need to fetch & install, resolve
   // promise and skip this.
   // FIXME(nsm): Bug 931249. Force cache update if > 1 day.
 
   aRegistration->mUpdateInstance =
@@ -773,16 +776,19 @@ public:
 
   NS_IMETHOD
   Run() MOZ_OVERRIDE
   {
     AssertIsOnMainThread();
     // FIXME(nsm): Change installing worker state to redundant.
     // FIXME(nsm): Fire statechange.
     mRegistration->mInstallingWorker = nullptr;
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    swm->InvalidateServiceWorkerContainerWorker(mRegistration,
+                                                WhichServiceWorker::INSTALLING_WORKER);
     return NS_OK;
   }
 };
 
 /*
  * Used to handle InstallEvent::waitUntil() and proceed with installation.
  */
 class FinishInstallHandler MOZ_FINAL : public PromiseNativeHandler
@@ -995,28 +1001,31 @@ private:
 
 void
 ServiceWorkerManager::Install(ServiceWorkerRegistration* aRegistration,
                               ServiceWorkerInfo* aServiceWorkerInfo)
 {
   AssertIsOnMainThread();
   aRegistration->mInstallingWorker = aServiceWorkerInfo;
   MOZ_ASSERT(aRegistration->mInstallingWorker);
+  InvalidateServiceWorkerContainerWorker(aRegistration,
+                                         WhichServiceWorker::INSTALLING_WORKER);
 
   nsMainThreadPtrHandle<ServiceWorkerRegistration> handle =
     new nsMainThreadPtrHolder<ServiceWorkerRegistration>(aRegistration);
 
   nsRefPtr<ServiceWorker> serviceWorker;
   nsresult rv =
     CreateServiceWorker(aServiceWorkerInfo->GetScriptSpec(),
                         aRegistration->mScope,
                         getter_AddRefs(serviceWorker));
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     aRegistration->mInstallingWorker = nullptr;
+    // We don't need to invalidate here since the upper one will have done it.
     return;
   }
 
   nsRefPtr<InstallEventRunnable> r =
     new InstallEventRunnable(serviceWorker->GetWorkerPrivate(), handle);
 
   AutoSafeJSContext cx;
   r->Dispatch(cx);
@@ -1052,19 +1061,21 @@ public:
   Run() MOZ_OVERRIDE
   {
     if (mRegistration->mCurrentWorker) {
       // FIXME(nsm). Steps 3.1-3.4 of the algorithm.
     }
 
     mRegistration->mCurrentWorker = mRegistration->mWaitingWorker.forget();
 
-    // FIXME(nsm): Steps 7 of the algorithm.
+    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    swm->InvalidateServiceWorkerContainerWorker(mRegistration,
+                                                WhichServiceWorker::ACTIVE_WORKER | WhichServiceWorker::WAITING_WORKER);
 
-    nsRefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+    // FIXME(nsm): Steps 7 of the algorithm.
 
     swm->FireEventOnServiceWorkerContainers(mRegistration,
                                             NS_LITERAL_STRING("controllerchange"));
 
     MOZ_ASSERT(mRegistration->mCurrentWorker);
     nsRefPtr<ServiceWorker> serviceWorker;
     nsresult rv =
       swm->CreateServiceWorker(mRegistration->mCurrentWorker->GetScriptSpec(),
@@ -1104,16 +1115,18 @@ ServiceWorkerManager::FinishInstall(Serv
     // a different script leading to [[Update]] terminating the
     // installingWorker and setting it to null. The FinishInstallRunnable may
     // already have been dispatched, hence the check.
     return;
   }
 
   aRegistration->mWaitingWorker = aRegistration->mInstallingWorker.forget();
   MOZ_ASSERT(aRegistration->mWaitingWorker);
+  InvalidateServiceWorkerContainerWorker(aRegistration,
+                                         WhichServiceWorker::WAITING_WORKER | WhichServiceWorker::INSTALLING_WORKER);
 
   // FIXME(nsm): Actually update state of active ServiceWorker instances to
   // installed.
   // FIXME(nsm): Fire statechange on the instances.
 
   // FIXME(nsm): Handle replace().
 
   if (!aRegistration->IsControllingDocuments()) {
@@ -1343,17 +1356,17 @@ ServiceWorkerManager::MaybeStartControll
 
   nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(aDoc);
   if (!domainInfo) {
     return;
   }
 
   nsRefPtr<ServiceWorkerRegistration> registration =
     GetServiceWorkerRegistration(aDoc);
-  if (registration) {
+  if (registration && registration->mCurrentWorker) {
     MOZ_ASSERT(!domainInfo->mControlledDocuments.Contains(aDoc));
     registration->StartControllingADocument();
     // Use the already_AddRefed<> form of Put to avoid the addref-deref since
     // we don't need the registration pointer in this function anymore.
     domainInfo->mControlledDocuments.Put(aDoc, registration.forget());
   }
 }
 
@@ -1466,16 +1479,127 @@ ServiceWorkerManager::FireEventOnService
         continue;
       }
 
       target->DispatchTrustedEvent(aName);
     }
   }
 }
 
+/*
+ * This is used for installing, waiting and active, and uses the registration
+ * most specifically matching the current scope.
+ */
+NS_IMETHODIMP
+ServiceWorkerManager::GetServiceWorkerForWindow(nsIDOMWindow* aWindow,
+                                                WhichServiceWorker aWhichWorker,
+                                                nsISupports** aServiceWorker)
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
+  MOZ_ASSERT(window);
+
+  nsRefPtr<ServiceWorkerRegistration> registration =
+    GetServiceWorkerRegistration(window);
+
+  if (!registration) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<ServiceWorkerInfo> info;
+  if (aWhichWorker == WhichServiceWorker::INSTALLING_WORKER) {
+    info = registration->mInstallingWorker;
+  } else if (aWhichWorker == WhichServiceWorker::WAITING_WORKER) {
+    info = registration->mWaitingWorker;
+  } else if (aWhichWorker == WhichServiceWorker::ACTIVE_WORKER) {
+    info = registration->mCurrentWorker;
+  } else {
+    MOZ_CRASH("Invalid worker type");
+  }
+
+  if (!info) {
+    return NS_ERROR_DOM_NOT_FOUND_ERR;
+  }
+
+  nsRefPtr<ServiceWorker> serviceWorker;
+  nsresult rv = CreateServiceWorkerForWindow(window,
+                                             info->GetScriptSpec(),
+                                             registration->mScope,
+                                             getter_AddRefs(serviceWorker));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  serviceWorker.forget(aServiceWorker);
+  return NS_OK;
+}
+
+/*
+ * The .controller is for the registration associated with the document when
+ * the document was loaded.
+ */
+NS_IMETHODIMP
+ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow, nsISupports** aServiceWorker)
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
+  MOZ_ASSERT(window);
+  if (!window || !window->GetExtantDoc()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
+
+  nsRefPtr<ServiceWorkerDomainInfo> domainInfo = GetDomainInfo(doc);
+  if (!domainInfo) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsRefPtr<ServiceWorkerRegistration> registration;
+  if (!domainInfo->mControlledDocuments.Get(doc, getter_AddRefs(registration))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // If the document is controlled, the current worker MUST be non-null.
+  MOZ_ASSERT(registration->mCurrentWorker);
+
+  nsRefPtr<ServiceWorker> serviceWorker;
+  nsresult rv = CreateServiceWorkerForWindow(window,
+                                             registration->mCurrentWorker->GetScriptSpec(),
+                                             registration->mScope,
+                                             getter_AddRefs(serviceWorker));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+
+  serviceWorker.forget(aServiceWorker);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ServiceWorkerManager::GetInstalling(nsIDOMWindow* aWindow,
+                                    nsISupports** aServiceWorker)
+{
+  return GetServiceWorkerForWindow(aWindow, WhichServiceWorker::INSTALLING_WORKER,
+                                   aServiceWorker);
+}
+
+NS_IMETHODIMP
+ServiceWorkerManager::GetWaiting(nsIDOMWindow* aWindow,
+                                 nsISupports** aServiceWorker)
+{
+  return GetServiceWorkerForWindow(aWindow, WhichServiceWorker::WAITING_WORKER,
+                                   aServiceWorker);
+}
+
+NS_IMETHODIMP
+ServiceWorkerManager::GetActive(nsIDOMWindow* aWindow, nsISupports** aServiceWorker)
+{
+  return GetServiceWorkerForWindow(aWindow, WhichServiceWorker::ACTIVE_WORKER,
+                                   aServiceWorker);
+}
+
 NS_IMETHODIMP
 ServiceWorkerManager::CreateServiceWorker(const nsACString& aScriptSpec,
                                           const nsACString& aScope,
                                           ServiceWorker** aServiceWorker)
 {
   AssertIsOnMainThread();
 
   WorkerPrivate::LoadInfo info;
@@ -1514,9 +1638,40 @@ ServiceWorkerManager::CreateServiceWorke
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   serviceWorker.forget(aServiceWorker);
   return NS_OK;
 }
 
+void
+ServiceWorkerManager::InvalidateServiceWorkerContainerWorker(ServiceWorkerRegistration* aRegistration,
+                                                             WhichServiceWorker aWhichOnes)
+{
+  AssertIsOnMainThread();
+  nsRefPtr<ServiceWorkerDomainInfo> domainInfo =
+    GetDomainInfo(aRegistration->mScriptSpec);
+
+  if (domainInfo) {
+    nsTObserverArray<ServiceWorkerContainer*>::ForwardIterator it(domainInfo->mServiceWorkerContainers);
+    while (it.HasMore()) {
+      nsRefPtr<ServiceWorkerContainer> target = it.GetNext();
+
+      nsIURI* targetURI = target->GetDocumentURI();
+      nsCString path;
+      nsresult rv = targetURI->GetSpec(path);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        continue;
+      }
+
+      nsCString scope = FindScopeForPath(domainInfo->mOrderedScopes, path);
+      if (scope.IsEmpty() ||
+          !scope.Equals(aRegistration->mScope)) {
+        continue;
+      }
+
+      target->InvalidateWorkerReference(aWhichOnes);
+    }
+  }
+}
+
 END_WORKERS_NAMESPACE
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -6,19 +6,20 @@
 #define mozilla_dom_workers_serviceworkermanager_h
 
 #include "nsIServiceWorkerManager.h"
 #include "nsCOMPtr.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/LinkedList.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/TypedEnum.h"
+#include "mozilla/TypedEnumBits.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/Promise.h"
-#include "mozilla/dom/ServiceWorkerContainer.h"
 #include "nsRefPtrHashtable.h"
 #include "nsTArrayForwardDeclare.h"
 #include "nsTObserverArray.h"
 #include "nsTWeakRef.h"
 
 class nsIScriptError;
 
 namespace mozilla {
@@ -89,16 +90,25 @@ public:
     return mScriptSpec;
   }
 
   explicit ServiceWorkerInfo(const nsACString& aScriptSpec)
     : mScriptSpec(aScriptSpec)
   { }
 };
 
+// Use multiples of 2 since they can be bitwise ORed when calling
+// InvalidateServiceWorkerContainerWorker.
+MOZ_BEGIN_ENUM_CLASS(WhichServiceWorker)
+  INSTALLING_WORKER = 1,
+  WAITING_WORKER    = 2,
+  ACTIVE_WORKER     = 4,
+MOZ_END_ENUM_CLASS(WhichServiceWorker)
+MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(WhichServiceWorker)
+
 // Needs to inherit from nsISupports because NS_ProxyRelease() does not support
 // non-ISupports classes.
 class ServiceWorkerRegistration MOZ_FINAL : public nsISupports
 {
   uint32_t mControlledDocumentsCounter;
 
   virtual ~ServiceWorkerRegistration();
 
@@ -184,16 +194,17 @@ public:
  * installation, querying and event dispatch of ServiceWorkers for all the
  * origins in the process.
  */
 class ServiceWorkerManager MOZ_FINAL : public nsIServiceWorkerManager
 {
   friend class ActivationRunnable;
   friend class RegisterRunnable;
   friend class CallInstallRunnable;
+  friend class CancelServiceWorkerInstallationRunnable;
   friend class ServiceWorkerUpdateInstance;
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSISERVICEWORKERMANAGER
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_SERVICEWORKERMANAGER_IMPL_IID)
 
   static ServiceWorkerManager* FactoryCreate()
@@ -334,16 +345,25 @@ private:
   GetDomainInfo(nsIDocument* aDoc);
 
   already_AddRefed<ServiceWorkerDomainInfo>
   GetDomainInfo(nsIURI* aURI);
 
   already_AddRefed<ServiceWorkerDomainInfo>
   GetDomainInfo(const nsCString& aURL);
 
+  NS_IMETHODIMP
+  GetServiceWorkerForWindow(nsIDOMWindow* aWindow,
+                            WhichServiceWorker aWhichWorker,
+                            nsISupports** aServiceWorker);
+
+  void
+  InvalidateServiceWorkerContainerWorker(ServiceWorkerRegistration* aRegistration,
+                                         WhichServiceWorker aWhichOnes);
+
   already_AddRefed<ServiceWorkerRegistration>
   GetServiceWorkerRegistration(nsPIDOMWindow* aWindow);
 
   already_AddRefed<ServiceWorkerRegistration>
   GetServiceWorkerRegistration(nsIDocument* aDoc);
 
   already_AddRefed<ServiceWorkerRegistration>
   GetServiceWorkerRegistration(nsIURI* aURI);
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/controller/index.html
@@ -0,0 +1,51 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 94048 - test install event.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  // Make sure to use good, unique messages, since the actual expression will not show up in test results.
+  function my_ok(result, msg) {
+    window.opener.postMessage({status: "ok", result: result, message: msg}, "*");
+  }
+
+  function finish() {
+    window.opener.postMessage({status: "done"}, "*");
+  }
+
+  if (navigator.serviceWorker.controller) {
+    my_ok(navigator.serviceWorker.controller.scope.match(/serviceworkers\/control\*$/),
+          "This page should be controlled by upper level registration");
+    my_ok(navigator.serviceWorker.installing == undefined,
+          "Upper level registration should not have a installing worker.");
+    // We are controlled.
+    // Register a new worker for this sub-scope. After that, controller should still be for upper level, but active should change to be this scope's.
+    navigator.serviceWorker.register("../worker2.js", { scope: "./*" }).then(function(e) {
+      my_ok(navigator.serviceWorker.installing &&
+            navigator.serviceWorker.installing.scope.match(/controller\/\*$/),
+            "Installing is serviceworker/controller/*");
+      my_ok(navigator.serviceWorker.controller.scope.match(/serviceworkers\/control\*$/),
+            "Controller is still serviceworker/*");
+      finish();
+    });
+  } else {
+    my_ok(false, "Should've been controlled!");
+    finish();
+  }
+</script>
+</pre>
+</body>
+</html>
+
+
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -1,13 +1,16 @@
 [DEFAULT]
 run-if = os == "win" # Bug 1040924 - Failure prone on !Windows
 support-files =
   worker.js
   worker2.js
   worker3.js
   parse_error_worker.js
   install_event_worker.js
+  simpleregister/index.html
+  controller/index.html
 
 [test_installation_simple.html]
 [test_install_event.html]
 [test_navigator.html]
 [test_scopes.html]
+[test_controller.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/simpleregister/index.html
@@ -0,0 +1,11 @@
+<html>
+  <head></head>
+  <body>
+      <script type="text/javascript">
+          navigator.serviceWorker.onupdatefound = function(e) {
+            window.parent.postMessage("updatefound", "*");
+          }
+          window.parent.postMessage("ready", "*");
+      </script>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_controller.html
@@ -0,0 +1,61 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1002570 - test controller instance.</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+
+  function simpleRegister() {
+    // We use the control* scope for the less specific registration. The window will register a worker on controller/*
+    return navigator.serviceWorker.register("worker.js", { scope: "./control*" });
+  }
+
+  function testController() {
+    var p = new Promise(function(resolve, reject) {
+      window.onmessage = function(e) {
+        if (e.data.status == "ok") {
+          ok(e.data.result, e.data.message);
+        } else if (e.data.status == "done") {
+          window.onmessage = null;
+          w.close();
+          resolve();
+        }
+      }
+    });
+
+    var w = window.open("controller/index.html");
+    return p;
+  }
+
+  // This document just flips the prefs and opens the window for the actual test.
+  function runTest() {
+    simpleRegister()
+      .then(testController)
+      .then(function() {
+        SimpleTest.finish();
+      }).catch(function(e) {
+        ok(false, "Some test failed with error " + e);
+        SimpleTest.finish();
+      });
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true]
+  ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/workers/test/serviceworkers/test_installation_simple.html
+++ b/dom/workers/test/serviceworkers/test_installation_simple.html
@@ -11,17 +11,17 @@
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none"></div>
 <pre id="test"></pre>
 <script class="testbody" type="text/javascript">
 
   function simpleRegister() {
-    var p = navigator.serviceWorker.register("worker.js");
+    var p = navigator.serviceWorker.register("worker.js", { scope: "simpleregister/*" });
     ok(p instanceof Promise, "register() should return a Promise");
     return Promise.resolve();
   }
 
   function sameOriginWorker() {
     p = navigator.serviceWorker.register("http://some-other-origin/worker.js");
     return p.then(function(w) {
       ok(false, "Worker from different origin should fail");
@@ -103,37 +103,56 @@
     info("NSM " + e.name);
       ok(e instanceof Error, "Registration should fail with parse error");
     });
   }
 
   // FIXME(nsm): test for parse error when Update step doesn't happen (directly from register).
 
   function updatefound() {
-    var p = navigator.serviceWorker.register("worker.js");
+    var frame = document.createElement("iframe");
+    frame.setAttribute("id", "simpleregister-frame");
+    frame.setAttribute("src", new URL("simpleregister/index.html", document.baseURI).href);
+    document.body.appendChild(frame);
+    var resolve, reject;
+    var p = new Promise(function(res, rej) {
+      resolve = res;
+      reject = rej;
+    });
 
-    return new Promise(function(resolve, reject) {
-      navigator.serviceWorker.onupdatefound = function(e) {
-        ok(true, "Got updatefound event");
-        navigator.serviceWorker.onupdatefound = null;
+    function continueTest() {
+      navigator.serviceWorker.register("worker2.js", { scope: "simpleregister/*" });
+    }
+
+    window.onmessage = function(e) {
+      if (e.data == "ready") {
+        continueTest();
+      } else if (e.data == "updatefound") {
+        window.onmessage = null;
+        // We have to make frame navigate away, otherwise it will call
+        // MaybeStopControlling() when this document is unloaded. At that point
+        // the pref has been disabled, and so MaybeStopControlling() will just
+        // return since it is currently gated.
+        frame.setAttribute("src", new URL("about:blank").href);
         resolve();
       }
-    });
+    }
+    return p;
   }
 
   function runTest() {
     simpleRegister()
       .then(sameOriginWorker)
       .then(sameOriginScope)
       .then(httpsOnly)
       .then(realWorker)
       .then(abortPrevious)
       .then(networkError404)
       .then(parseError)
-      //.then(updatefound)
+      .then(updatefound)
       // put more tests here.
       .then(function() {
         SimpleTest.finish();
       }).catch(function(e) {
         ok(false, "Some test failed with error " + e);
         SimpleTest.finish();
       });
   }
--- a/dom/workers/test/serviceworkers/test_navigator.html
+++ b/dom/workers/test/serviceworkers/test_navigator.html
@@ -18,18 +18,18 @@
   function checkEnabled() {
     ok(navigator.serviceWorker, "navigator.serviceWorker should exist when ServiceWorkers are enabled.");
     ok(typeof navigator.serviceWorker.register === "function", "navigator.serviceWorker.register() should be a function.");
     ok(typeof navigator.serviceWorker.unregister === "function", "navigator.serviceWorker.unregister() should be a function.");
     ok(typeof navigator.serviceWorker.getAll === "function", "navigator.serviceWorker.getAll() should be a function.");
     ok(navigator.serviceWorker.ready instanceof Promise, "navigator.serviceWorker.ready should be a Promise.");
     ok(navigator.serviceWorker.installing === null, "There should be no installing worker for an uncontrolled scope.");
     ok(navigator.serviceWorker.waiting === null, "There should be no waiting worker for an uncontrolled scope.");
-    ok(navigator.serviceWorker.active === null, "There should be no active worker for an uncontrolled scope.");
-    ok(navigator.serviceWorker.controller === null, "There should be no active worker for an uncontrolled document.");
+    // ok(navigator.serviceWorker.active === null, "There should be no active worker for an uncontrolled scope.");
+    ok(navigator.serviceWorker.controller === null, "There should be no controller worker for an uncontrolled document.");
   }
 
   SimpleTest.waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({"set": [
     ["dom.serviceWorkers.enabled", true]
   ]}, function() {
     checkEnabled();
--- a/gfx/2d/DrawTargetCG.cpp
+++ b/gfx/2d/DrawTargetCG.cpp
@@ -135,29 +135,30 @@ InterpolationQualityFromFilter(Filter aF
     case Filter::POINT:
       return kCGInterpolationNone;
     case Filter::GOOD:
       return kCGInterpolationDefault;
   }
 }
 
 
-DrawTargetCG::DrawTargetCG() : mCg(nullptr), mSnapshot(nullptr)
+DrawTargetCG::DrawTargetCG()
+  : mColorSpace(nullptr)
+  , mCg(nullptr)
 {
 }
 
 DrawTargetCG::~DrawTargetCG()
 {
   MarkChanged();
 
-  // We need to conditionally release these because Init can fail without initializing these.
-  if (mColorSpace)
-    CGColorSpaceRelease(mColorSpace);
-  if (mCg)
-    CGContextRelease(mCg);
+  // Both of these are OK with nullptr arguments, so we do not
+  // need to check (these could be nullptr if Init fails)
+  CGColorSpaceRelease(mColorSpace);
+  CGContextRelease(mCg);
 }
 
 DrawTargetType
 DrawTargetCG::GetType() const
 {
   return GetBackendType() == BackendType::COREGRAPHICS_ACCELERATED ?
            DrawTargetType::HARDWARE_RASTER : DrawTargetType::SOFTWARE_RASTER;
 }
@@ -377,71 +378,72 @@ static CGColorRef ColorToCGColor(CGColor
   CGFloat components[4] = {aColor.r, aColor.g, aColor.b, aColor.a};
   return CGColorCreate(aColorSpace, components);
 }
 
 class GradientStopsCG : public GradientStops
 {
   public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(GradientStopsCG)
-  //XXX: The skia backend uses a vector and passes in aNumStops. It should do better
-  GradientStopsCG(GradientStop* aStops, uint32_t aNumStops, ExtendMode aExtendMode)
+
+  GradientStopsCG(CGColorSpaceRef aColorSpace,
+                  const std::vector<GradientStop>& aStops,
+                  ExtendMode aExtendMode)
+    : mGradient(nullptr)
   {
+    // This all works fine with empty aStops vector
+
     mExtend = aExtendMode;
     if (aExtendMode == ExtendMode::CLAMP) {
-      //XXX: do the stops need to be in any particular order?
-      // what should we do about the color space here? we certainly shouldn't be
-      // recreating it all the time
+      size_t numStops = aStops.size();
+
       std::vector<CGFloat> colors;
       std::vector<CGFloat> offsets;
-      colors.reserve(aNumStops*4);
-      offsets.reserve(aNumStops);
+      colors.reserve(numStops*4);
+      offsets.reserve(numStops);
 
-      for (uint32_t i = 0; i < aNumStops; i++) {
+      for (size_t i = 0; i < numStops; i++) {
         colors.push_back(aStops[i].color.r);
         colors.push_back(aStops[i].color.g);
         colors.push_back(aStops[i].color.b);
         colors.push_back(aStops[i].color.a);
 
         offsets.push_back(aStops[i].offset);
       }
 
-      CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
-      mGradient = CGGradientCreateWithColorComponents(colorSpace,
+      mGradient = CGGradientCreateWithColorComponents(aColorSpace,
                                                       &colors.front(),
                                                       &offsets.front(),
-                                                      aNumStops);
-      CGColorSpaceRelease(colorSpace);
+                                                      offsets.size());
     } else {
-      mGradient = nullptr;
-      mStops.reserve(aNumStops);
-      for (uint32_t i = 0; i < aNumStops; i++) {
-        mStops.push_back(aStops[i]);
-      }
+      mStops = aStops;
     }
 
   }
+
   virtual ~GradientStopsCG() {
-    if (mGradient)
-        CGGradientRelease(mGradient);
+    // CGGradientRelease is OK with nullptr argument
+    CGGradientRelease(mGradient);
   }
+
   // Will always report BackendType::COREGRAPHICS, but it is compatible
   // with BackendType::COREGRAPHICS_ACCELERATED
   BackendType GetBackendType() const { return BackendType::COREGRAPHICS; }
   // XXX this should be a union
   CGGradientRef mGradient;
   std::vector<GradientStop> mStops;
   ExtendMode mExtend;
 };
 
 TemporaryRef<GradientStops>
 DrawTargetCG::CreateGradientStops(GradientStop *aStops, uint32_t aNumStops,
                                   ExtendMode aExtendMode) const
 {
-  return new GradientStopsCG(aStops, aNumStops, aExtendMode);
+  std::vector<GradientStop> stops(aStops, aStops+aNumStops);
+  return new GradientStopsCG(mColorSpace, stops, aExtendMode);
 }
 
 static void
 UpdateLinearParametersToIncludePoint(double *min_t, double *max_t,
                                      CGPoint *start,
                                      double dx, double dy,
                                      double x, double y)
 {
@@ -520,17 +522,18 @@ CalculateRepeatingGradientParams(CGPoint
   aStart->x = aStart->x + dx * t_min;
   aStart->y = aStart->y + dy * t_min;
 
   *aRepeatStartFactor = t_min;
   *aRepeatEndFactor = t_max;
 }
 
 static CGGradientRef
-CreateRepeatingGradient(CGContextRef cg, GradientStopsCG* aStops,
+CreateRepeatingGradient(CGColorSpaceRef aColorSpace,
+                        CGContextRef cg, GradientStopsCG* aStops,
                         int aRepeatStartFactor, int aRepeatEndFactor,
                         bool aReflect)
 {
   int repeatCount = aRepeatEndFactor - aRepeatStartFactor;
   uint32_t stopCount = aStops->mStops.size();
   double scale = 1./repeatCount;
 
   std::vector<CGFloat> colors;
@@ -550,42 +553,41 @@ CreateRepeatingGradient(CGContextRef cg,
       CGFloat offset = aStops->mStops[stopIndex].offset;
       if (isReflected) {
         offset = 1 - offset;
       }
       offsets.push_back((offset + (j - aRepeatStartFactor)) * scale);
     }
   }
 
-  CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
-  CGGradientRef gradient = CGGradientCreateWithColorComponents(colorSpace,
+  CGGradientRef gradient = CGGradientCreateWithColorComponents(aColorSpace,
                                                                &colors.front(),
                                                                &offsets.front(),
                                                                repeatCount*stopCount);
-  CGColorSpaceRelease(colorSpace);
   return gradient;
 }
 
 static void
-DrawLinearRepeatingGradient(CGContextRef cg, const LinearGradientPattern &aPattern,
+DrawLinearRepeatingGradient(CGColorSpaceRef aColorSpace, CGContextRef cg,
+                            const LinearGradientPattern &aPattern,
                             const CGRect &aExtents, bool aReflect)
 {
   GradientStopsCG *stops = static_cast<GradientStopsCG*>(aPattern.mStops.get());
   CGPoint startPoint = { aPattern.mBegin.x, aPattern.mBegin.y };
   CGPoint endPoint = { aPattern.mEnd.x, aPattern.mEnd.y };
 
   int repeatStartFactor = 0, repeatEndFactor = 1;
   // if we don't have a line then we can't extend it
   if (aPattern.mEnd.x != aPattern.mBegin.x ||
       aPattern.mEnd.y != aPattern.mBegin.y) {
     CalculateRepeatingGradientParams(&startPoint, &endPoint, aExtents,
                                      &repeatStartFactor, &repeatEndFactor);
   }
 
-  CGGradientRef gradient = CreateRepeatingGradient(cg, stops, repeatStartFactor, repeatEndFactor, aReflect);
+  CGGradientRef gradient = CreateRepeatingGradient(aColorSpace, cg, stops, repeatStartFactor, repeatEndFactor, aReflect);
 
   CGContextDrawLinearGradient(cg, gradient, startPoint, endPoint,
                               kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
   CGGradientRelease(gradient);
 }
 
 static CGPoint CGRectTopLeft(CGRect a)
 { return a.origin; }
@@ -598,17 +600,18 @@ static CGPoint CGRectBottomRight(CGRect 
 
 static CGFloat
 CGPointDistance(CGPoint a, CGPoint b)
 {
   return hypot(a.x-b.x, a.y-b.y);
 }
 
 static void
-DrawRadialRepeatingGradient(CGContextRef cg, const RadialGradientPattern &aPattern,
+DrawRadialRepeatingGradient(CGColorSpaceRef aColorSpace, CGContextRef cg,
+                            const RadialGradientPattern &aPattern,
                             const CGRect &aExtents, bool aReflect)
 {
   GradientStopsCG *stops = static_cast<GradientStopsCG*>(aPattern.mStops.get());
   CGPoint startCenter = { aPattern.mCenter1.x, aPattern.mCenter1.y };
   CGFloat startRadius = aPattern.mRadius1;
   CGPoint endCenter   = { aPattern.mCenter2.x, aPattern.mCenter2.y };
   CGFloat endRadius   = aPattern.mRadius2;
 
@@ -626,26 +629,27 @@ DrawRadialRepeatingGradient(CGContextRef
     repeatEndFactor++;
   }
 
   while (startRadius-length >= 0) {
     startRadius -= length;
     repeatStartFactor--;
   }
 
-  CGGradientRef gradient = CreateRepeatingGradient(cg, stops, repeatStartFactor, repeatEndFactor, aReflect);
+  CGGradientRef gradient = CreateRepeatingGradient(aColorSpace, cg, stops, repeatStartFactor, repeatEndFactor, aReflect);
 
   //XXX: are there degenerate radial gradients that we should avoid drawing?
   CGContextDrawRadialGradient(cg, gradient, startCenter, startRadius, endCenter, endRadius,
                               kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
   CGGradientRelease(gradient);
 }
 
 static void
-DrawGradient(CGContextRef cg, const Pattern &aPattern, const CGRect &aExtents)
+DrawGradient(CGColorSpaceRef aColorSpace,
+             CGContextRef cg, const Pattern &aPattern, const CGRect &aExtents)
 {
   if (CGRectIsEmpty(aExtents)) {
     return;
   }
 
   if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
     const LinearGradientPattern& pat = static_cast<const LinearGradientPattern&>(aPattern);
     GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
@@ -658,17 +662,17 @@ DrawGradient(CGContextRef cg, const Patt
 
       // Canvas spec states that we should avoid drawing degenerate gradients (XXX: should this be in common code?)
       //if (startPoint.x == endPoint.x && startPoint.y == endPoint.y)
       //  return;
 
       CGContextDrawLinearGradient(cg, stops->mGradient, startPoint, endPoint,
                                   kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
     } else if (stops->mExtend == ExtendMode::REPEAT || stops->mExtend == ExtendMode::REFLECT) {
-      DrawLinearRepeatingGradient(cg, pat, aExtents, stops->mExtend == ExtendMode::REFLECT);
+      DrawLinearRepeatingGradient(aColorSpace, cg, pat, aExtents, stops->mExtend == ExtendMode::REFLECT);
     }
   } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
     const RadialGradientPattern& pat = static_cast<const RadialGradientPattern&>(aPattern);
     CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(pat.mMatrix));
     GradientStopsCG *stops = static_cast<GradientStopsCG*>(pat.mStops.get());
     if (stops->mExtend == ExtendMode::CLAMP) {
 
       // XXX: we should take the m out of the properties of RadialGradientPatterns
@@ -676,17 +680,17 @@ DrawGradient(CGContextRef cg, const Patt
       CGFloat startRadius = pat.mRadius1;
       CGPoint endCenter   = { pat.mCenter2.x, pat.mCenter2.y };
       CGFloat endRadius   = pat.mRadius2;
 
       //XXX: are there degenerate radial gradients that we should avoid drawing?
       CGContextDrawRadialGradient(cg, stops->mGradient, startCenter, startRadius, endCenter, endRadius,
                                   kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
     } else if (stops->mExtend == ExtendMode::REPEAT || stops->mExtend == ExtendMode::REFLECT) {
-      DrawRadialRepeatingGradient(cg, pat, aExtents, stops->mExtend == ExtendMode::REFLECT);
+      DrawRadialRepeatingGradient(aColorSpace, cg, pat, aExtents, stops->mExtend == ExtendMode::REFLECT);
     }
   } else {
     assert(0);
   }
 
 }
 
 static void
@@ -713,16 +717,23 @@ CGPatternCallbacks patternCallbacks = {
 };
 
 static bool
 isGradient(const Pattern &aPattern)
 {
   return aPattern.GetType() == PatternType::LINEAR_GRADIENT || aPattern.GetType() == PatternType::RADIAL_GRADIENT;
 }
 
+static bool
+isNonRepeatingSurface(const Pattern& aPattern)
+{
+  return aPattern.GetType() == PatternType::SURFACE &&
+    static_cast<const SurfacePattern&>(aPattern).mExtendMode != ExtendMode::REPEAT;
+}
+
 /* CoreGraphics patterns ignore the userspace transform so
  * we need to multiply it in */
 static CGPatternRef
 CreateCGPattern(const Pattern &aPattern, CGAffineTransform aUserSpace)
 {
   const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
   // XXX: is .get correct here?
   CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get());
@@ -844,74 +855,72 @@ DrawTargetCG::MaskSurface(const Pattern 
   IntSize size = aMask->GetSize();
 
   CGContextClipToMask(cg, CGRectMake(aOffset.x, -(aOffset.y + size.height), size.width, size.height), image);
 
   CGContextScaleCTM(cg, 1, -1);
   if (isGradient(aSource)) {
     // we shouldn't need to clip to an additional rectangle
     // as the cliping to the mask should be sufficient.
-    DrawGradient(cg, aSource, CGRectMake(aOffset.x, aOffset.y, size.width, size.height));
+    DrawGradient(mColorSpace, cg, aSource, CGRectMake(aOffset.x, aOffset.y, size.width, size.height));
   } else {
     SetFillFromPattern(cg, mColorSpace, aSource);
     CGContextFillRect(cg, CGRectMake(aOffset.x, aOffset.y, size.width, size.height));
   }
 
   CGImageRelease(image);
 
   fixer.Fix(mCg);
 
   CGContextRestoreGState(mCg);
 }
 
 
 
 void
 DrawTargetCG::FillRect(const Rect &aRect,
-                        const Pattern &aPattern,
-                        const DrawOptions &aDrawOptions)
+                       const Pattern &aPattern,
+                       const DrawOptions &aDrawOptions)
 {
   MarkChanged();
 
   CGContextSaveGState(mCg);
 
   UnboundnessFixer fixer;
   CGContextRef cg = fixer.Check(mCg, aDrawOptions.mCompositionOp, &aRect);
   CGContextSetAlpha(mCg, aDrawOptions.mAlpha);
   CGContextSetShouldAntialias(cg, aDrawOptions.mAntialiasMode != AntialiasMode::NONE);
   CGContextSetBlendMode(mCg, ToBlendMode(aDrawOptions.mCompositionOp));
 
   CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(mTransform));
 
   if (isGradient(aPattern)) {
     CGContextClipToRect(cg, RectToCGRect(aRect));
     CGRect clipBounds = CGContextGetClipBoundingBox(cg);
-    DrawGradient(cg, aPattern, clipBounds);
+    DrawGradient(mColorSpace, cg, aPattern, clipBounds);
+  } else if (isNonRepeatingSurface(aPattern)) {
+    // SetFillFromPattern can handle this case but using CGContextDrawImage
+    // should give us better performance, better output, smaller PDF and
+    // matches what cairo does.
+    const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
+    CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get());
+    CGContextClipToRect(cg, RectToCGRect(aRect));
+    CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(pat.mMatrix));
+    CGContextTranslateCTM(cg, 0, CGImageGetHeight(image));
+    CGContextScaleCTM(cg, 1, -1);
+
+    CGRect imageRect = CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image));
+
+    CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter));
+
+    CGContextDrawImage(cg, imageRect, image);
+    CGImageRelease(image);
   } else {
-    if (aPattern.GetType() == PatternType::SURFACE && static_cast<const SurfacePattern&>(aPattern).mExtendMode != ExtendMode::REPEAT) {
-      // SetFillFromPattern can handle this case but using CGContextDrawImage
-      // should give us better performance, better output, smaller PDF and
-      // matches what cairo does.
-      const SurfacePattern& pat = static_cast<const SurfacePattern&>(aPattern);
-      CGImageRef image = GetRetainedImageFromSourceSurface(pat.mSurface.get());
-      CGContextClipToRect(cg, RectToCGRect(aRect));
-      CGContextConcatCTM(cg, GfxMatrixToCGAffineTransform(pat.mMatrix));
-      CGContextTranslateCTM(cg, 0, CGImageGetHeight(image));
-      CGContextScaleCTM(cg, 1, -1);
-
-      CGRect imageRect = CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image));
-
-      CGContextSetInterpolationQuality(cg, InterpolationQualityFromFilter(pat.mFilter));
-
-      CGContextDrawImage(cg, imageRect, image);
-      CGImageRelease(image);
-    } else {
-      SetFillFromPattern(cg, mColorSpace, aPattern);
-      CGContextFillRect(cg, RectToCGRect(aRect));
-    }
+    SetFillFromPattern(cg, mColorSpace, aPattern);
+    CGContextFillRect(cg, RectToCGRect(aRect));
   }
 
   fixer.Fix(mCg);
   CGContextRestoreGState(mCg);
 }
 
 void
 DrawTargetCG::StrokeLine(const Point &p1, const Point &p2, const Pattern &aPattern, const StrokeOptions &aStrokeOptions, const DrawOptions &aDrawOptions)
@@ -941,17 +950,17 @@ DrawTargetCG::StrokeLine(const Point &p1
 
   SetStrokeOptions(cg, aStrokeOptions);
 
   if (isGradient(aPattern)) {
     CGContextReplacePathWithStrokedPath(cg);
     CGRect extents = CGContextGetPathBoundingBox(cg);
     //XXX: should we use EO clip here?
     CGContextClip(cg);
-    DrawGradient(cg, aPattern, extents);
+    DrawGradient(mColorSpace, cg, aPattern, extents);
   } else {
     SetStrokeFromPattern(cg, mColorSpace, aPattern);
     CGContextStrokePath(cg);
   }
 
   fixer.Fix(mCg);
   CGContextRestoreGState(mCg);
 }
@@ -983,17 +992,17 @@ DrawTargetCG::StrokeRect(const Rect &aRe
   if (isGradient(aPattern)) {
     // There's no CGContextClipStrokeRect so we do it by hand
     CGContextBeginPath(cg);
     CGContextAddRect(cg, RectToCGRect(aRect));
     CGContextReplacePathWithStrokedPath(cg);
     CGRect extents = CGContextGetPathBoundingBox(cg);
     //XXX: should we use EO clip here?
     CGContextClip(cg);
-    DrawGradient(cg, aPattern, extents);
+    DrawGradient(mColorSpace, cg, aPattern, extents);
   } else {
     SetStrokeFromPattern(cg, mColorSpace, aPattern);
     CGContextStrokeRect(cg, RectToCGRect(aRect));
   }
 
   fixer.Fix(mCg);
   CGContextRestoreGState(mCg);
 }
@@ -1040,17 +1049,17 @@ DrawTargetCG::Stroke(const Path *aPath, 
 
   SetStrokeOptions(cg, aStrokeOptions);
 
   if (isGradient(aPattern)) {
     CGContextReplacePathWithStrokedPath(cg);
     CGRect extents = CGContextGetPathBoundingBox(cg);
     //XXX: should we use EO clip here?
     CGContextClip(cg);
-    DrawGradient(cg, aPattern, extents);
+    DrawGradient(mColorSpace, cg, aPattern, extents);
   } else {
     // XXX: we could put fill mode into the path fill rule if we wanted
 
     SetStrokeFromPattern(cg, mColorSpace, aPattern);
     CGContextStrokePath(cg);
   }
 
   fixer.Fix(mCg);
@@ -1090,17 +1099,17 @@ DrawTargetCG::Fill(const Path *aPath, co
       CGContextAddPath(cg, cgPath->GetPath());
       extents = CGContextGetPathBoundingBox(cg);
       if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD)
         CGContextEOClip(mCg);
       else
         CGContextClip(mCg);
     }
 
-    DrawGradient(cg, aPattern, extents);
+    DrawGradient(mColorSpace, cg, aPattern, extents);
   } else {
     CGContextAddPath(cg, cgPath->GetPath());
 
     SetFillFromPattern(cg, mColorSpace, aPattern);
 
     if (cgPath->GetFillRule() == FillRule::FILL_EVEN_ODD)
       CGContextEOFillPath(cg);
     else
@@ -1196,17 +1205,17 @@ DrawTargetCG::FillGlyphs(ScaledFont *aFo
 
       CGContextSetFont(cg, macFont->mFont);
       CGContextSetFontSize(cg, macFont->mSize);
       CGContextShowGlyphsAtPositions(cg, &glyphs.front(), &positions.front(),
                                      aBuffer.mNumGlyphs);
       delete bboxes;
     }
     CGContextScaleCTM(cg, 1, -1);
-    DrawGradient(cg, aPattern, extents);
+    DrawGradient(mColorSpace, cg, aPattern, extents);
   } else {
     //XXX: with CoreGraphics we can stroke text directly instead of going
     // through GetPath. It would be nice to add support for using that
     CGContextSetTextDrawingMode(cg, kCGTextFill);
     SetFillFromPattern(cg, mColorSpace, aPattern);
     if (ScaledFontMac::CTFontDrawGlyphsPtr != nullptr) {
       ScaledFontMac::CTFontDrawGlyphsPtr(macFont->mCTFont, &glyphs.front(),
                                          &positions.front(),
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -594,16 +594,17 @@ GLContext::InitWithPrefix(const char *pr
                 "Adreno (TM) 205",
                 "Adreno (TM) 320",
                 "PowerVR SGX 530",
                 "PowerVR SGX 540",
                 "NVIDIA Tegra",
                 "Android Emulator",
                 "Gallium 0.4 on llvmpipe",
                 "Intel HD Graphics 3000 OpenGL Engine",
+                "Microsoft Basic Render Driver"
         };
 
         mRenderer = GLRenderer::Other;
         for (size_t i = 0; i < size_t(GLRenderer::Other); ++i) {
             if (DoesStringMatch(glRendererString, rendererMatchStrings[i])) {
                 mRenderer = GLRenderer(i);
                 break;
             }
@@ -662,16 +663,22 @@ GLContext::InitWithPrefix(const char *pr
             }
 
             if (Vendor() == GLVendor::Imagination &&
                 Renderer() == GLRenderer::SGX540) {
                 // Bug 980048
                 MarkExtensionUnsupported(OES_EGL_sync);
             }
 
+            if (Renderer() == GLRenderer::MicrosoftBasicRenderDriver) {
+                // Bug 978966: on Microsoft's "Basic Render Driver" (software renderer)
+                // multisampling hardcodes blending with the default blendfunc, which breaks WebGL.
+                MarkUnsupported(GLFeature::framebuffer_multisample);
+            }
+
 #ifdef XP_MACOSX
             // The Mac Nvidia driver, for versions up to and including 10.8, don't seem
             // to properly support this.  See 814839
             // this has been fixed in Mac OS X 10.9. See 907946
             if (Vendor() == gl::GLVendor::NVIDIA &&
                 !nsCocoaFeatures::OnMavericksOrLater())
             {
                 MarkUnsupported(GLFeature::depth_texture);
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -142,16 +142,17 @@ MOZ_BEGIN_ENUM_CLASS(GLRenderer)
     AdrenoTM205,
     AdrenoTM320,
     SGX530,
     SGX540,
     Tegra,
     AndroidEmulator,
     GalliumLlvmpipe,
     IntelHD3000,
+    MicrosoftBasicRenderDriver,
     Other
 MOZ_END_ENUM_CLASS(GLRenderer)
 
 class GLContext
     : public GLLibraryLoader
     , public GenericAtomicRefCounted
 {
 // -----------------------------------------------------------------------------
--- a/gfx/gl/GLTextureImage.cpp
+++ b/gfx/gl/GLTextureImage.cpp
@@ -60,27 +60,16 @@ TileGenFunc(GLContext* gl,
             return TileGenFuncEGL(gl, aSize, aContentType, aFlags, aImageFormat);
         default:
             return nullptr;
     }
 }
 
 already_AddRefed<TextureImage>
 TextureImage::Create(GLContext* gl,
-                     const nsIntSize& size,
-                     TextureImage::ContentType contentType,
-                     GLenum wrapMode,
-                     TextureImage::Flags flags)
-{
-    return Create(gl, size.ToIntSize(), contentType, wrapMode, flags);
-}
-
-// Moz2D equivalent...
-already_AddRefed<TextureImage>
-TextureImage::Create(GLContext* gl,
                      const gfx::IntSize& size,
                      TextureImage::ContentType contentType,
                      GLenum wrapMode,
                      TextureImage::Flags flags)
 {
     return CreateTextureImage(gl, size, contentType, wrapMode, flags);
 }
 
--- a/gfx/gl/GLTextureImage.h
+++ b/gfx/gl/GLTextureImage.h
@@ -62,23 +62,16 @@ public:
         DisallowBigImage = 0x4
     };
 
     typedef gfxContentType ContentType;
     typedef gfxImageFormat ImageFormat;
 
     static already_AddRefed<TextureImage> Create(
                        GLContext* gl,
-                       const nsIntSize& aSize,
-                       TextureImage::ContentType aContentType,
-                       GLenum aWrapMode,
-                       TextureImage::Flags aFlags = TextureImage::NoFlags);
-    // Moz2D equivalent...
-    static already_AddRefed<TextureImage> Create(
-                       GLContext* gl,
                        const gfx::IntSize& aSize,
                        TextureImage::ContentType aContentType,
                        GLenum aWrapMode,
                        TextureImage::Flags aFlags = TextureImage::NoFlags);
 
     /**
      * Returns a gfxASurface for updating |aRegion| of the client's
      * image if successul, nullptr if not.  |aRegion|'s bounds must fit
--- a/gfx/gl/GfxTexturesReporter.cpp
+++ b/gfx/gl/GfxTexturesReporter.cpp
@@ -7,17 +7,18 @@
 #include "GfxTexturesReporter.h"
 #include "GLDefs.h"
 
 using namespace mozilla;
 using namespace mozilla::gl;
 
 NS_IMPL_ISUPPORTS(GfxTexturesReporter, nsIMemoryReporter)
 
-int64_t GfxTexturesReporter::sAmount = 0;
+Atomic<int32_t> GfxTexturesReporter::sAmount(0);
+Atomic<int32_t> GfxTexturesReporter::sTileWasteAmount(0);
 
 static uint32_t
 GetBitsPerTexel(GLenum format, GLenum type)
 {
     // If there is no defined format or type, we're not taking up any memory
     if (!format || !type) {
         return 0;
     }
--- a/gfx/gl/GfxTexturesReporter.h
+++ b/gfx/gl/GfxTexturesReporter.h
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
 /* 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 GFXTEXTURESREPORTER_H_
 #define GFXTEXTURESREPORTER_H_
 
+#include "mozilla/Atomics.h"
 #include "nsIMemoryReporter.h"
 #include "GLTypes.h"
 
 namespace mozilla {
 namespace gl {
 
 class GfxTexturesReporter MOZ_FINAL : public nsIMemoryReporter
 {
@@ -38,24 +39,57 @@ public:
         MemoryFreed
     };
 
     // When memory is used/freed for tile textures, call this method to update
     // the value reported by this memory reporter.
     static void UpdateAmount(MemoryUse action, GLenum format, GLenum type,
                              int32_t tileWidth, int32_t tileHeight);
 
+    static void UpdateWasteAmount(int32_t delta) {
+      sTileWasteAmount += delta;
+    }
+
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                               nsISupports* aData, bool aAnonymize)
     {
+        MOZ_COLLECT_REPORT("gfx-tiles-waste", KIND_OTHER, UNITS_BYTES,
+            sTileWasteAmount,
+            "Memory lost due to tiles extending past content boundaries");
         return MOZ_COLLECT_REPORT(
             "gfx-textures", KIND_OTHER, UNITS_BYTES, sAmount,
             "Memory used for storing GL textures.");
     }
 
 private:
-    static int64_t sAmount;
+    static Atomic<int32_t> sAmount;
+    // Count the amount of memory lost to tile waste
+    static Atomic<int32_t> sTileWasteAmount;
+};
+
+class GfxTextureWasteTracker {
+public:
+  GfxTextureWasteTracker()
+    : mBytes(0)
+  {
+    MOZ_COUNT_CTOR(GfxTextureWasteTracker);
+  }
+
+  void Update(int32_t aPixelArea, int32_t aBytesPerPixel) {
+    GfxTexturesReporter::UpdateWasteAmount(-mBytes);
+    mBytes = aPixelArea * aBytesPerPixel;
+    GfxTexturesReporter::UpdateWasteAmount(mBytes);
+  }
+
+  ~GfxTextureWasteTracker() {
+    GfxTexturesReporter::UpdateWasteAmount(-mBytes);
+    MOZ_COUNT_DTOR(GfxTextureWasteTracker);
+  }
+private:
+  GfxTextureWasteTracker(const GfxTextureWasteTracker& aRef);
+
+  int32_t mBytes;
 };
 
 }
 }
 
 #endif // GFXTEXTURESREPORTER_H_
--- a/gfx/layers/TextureDIB.cpp
+++ b/gfx/layers/TextureDIB.cpp
@@ -20,16 +20,29 @@ DIBTextureClient::DIBTextureClient(gfx::
   MOZ_COUNT_CTOR(DIBTextureClient);
 }
 
 DIBTextureClient::~DIBTextureClient()
 {
   MOZ_COUNT_DTOR(DIBTextureClient);
 }
 
+TemporaryRef<TextureClient>
+DIBTextureClient::CreateSimilar(TextureFlags aFlags,
+                                  TextureAllocationFlags aAllocFlags) const
+{
+  RefPtr<TextureClient> tex = new DIBTextureClient(mFormat, mFlags | aFlags);
+
+  if (!tex->AllocateForSurface(mSize, ALLOC_DEFAULT)) {
+    return nullptr;
+  }
+
+  return tex;
+}
+
 bool
 DIBTextureClient::Lock(OpenMode)
 {
   MOZ_ASSERT(!mIsLocked);
   if (!IsValid()) {
     return false;
   }
   mIsLocked = true;
--- a/gfx/layers/TextureDIB.h
+++ b/gfx/layers/TextureDIB.h
@@ -47,16 +47,20 @@ public:
 
   virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
 
   virtual bool AllocateForSurface(gfx::IntSize aSize,
     TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE;
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; }
 
+  virtual TemporaryRef<TextureClient>
+  CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
+                TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE;
+
 protected:
   nsRefPtr<gfxWindowsSurface> mSurface;
   RefPtr<gfx::DrawTarget> mDrawTarget;
   gfx::IntSize mSize;
   gfx::SurfaceFormat mFormat;
   bool mIsLocked;
 };
 
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -33,17 +33,21 @@ Axis::Axis(AsyncPanZoomController* aAsyn
 {
 }
 
 void Axis::UpdateWithTouchAtDevicePoint(int32_t aPos, uint32_t aTimestampMs) {
   // mVelocityQueue is controller-thread only
   AsyncPanZoomController::AssertOnControllerThread();
 
   if (aTimestampMs == mPosTimeMs) {
-    // Duplicate event?
+    // This could be a duplicate event, or it could be a legitimate event
+    // on some platforms that generate events really fast. As a compromise
+    // update mPos so we don't run into problems like bug 1042734, even though
+    // that means the velocity will be stale. Better than doing a divide-by-zero.
+    mPos = aPos;
     return;
   }
 
   float newVelocity = mAxisLocked ? 0 : (float)(mPos - aPos) / (float)(aTimestampMs - mPosTimeMs);
   if (gfxPrefs::APZMaxVelocity() > 0.0f) {
     newVelocity = std::min(newVelocity, gfxPrefs::APZMaxVelocity() * APZCTreeManager::GetDPI());
   }
 
--- a/gfx/layers/basic/TextureClientX11.cpp
+++ b/gfx/layers/basic/TextureClientX11.cpp
@@ -27,16 +27,29 @@ TextureClientX11::TextureClientX11(ISurf
   MOZ_COUNT_CTOR(TextureClientX11);
 }
 
 TextureClientX11::~TextureClientX11()
 {
   MOZ_COUNT_DTOR(TextureClientX11);
 }
 
+TemporaryRef<TextureClient>
+TextureClientX11::CreateSimilar(TextureFlags aFlags,
+                                TextureAllocationFlags aAllocFlags) const
+{
+  RefPtr<TextureClient> tex = new TextureClientX11(mAllocator, mFormat, mFlags);
+
+  if (!tex->AllocateForSurface(mSize, aAllocFlags)) {
+    return nullptr;
+  }
+
+  return tex;
+}
+
 bool
 TextureClientX11::IsAllocated() const
 {
   return !!mSurface;
 }
 
 bool
 TextureClientX11::Lock(OpenMode aMode)
--- a/gfx/layers/basic/TextureClientX11.h
+++ b/gfx/layers/basic/TextureClientX11.h
@@ -42,16 +42,20 @@ class TextureClientX11 : public TextureC
   virtual bool CanExposeDrawTarget() const MOZ_OVERRIDE { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
 
   virtual gfx::SurfaceFormat GetFormat() const { return mFormat; }
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return false; }
 
+  virtual TemporaryRef<TextureClient>
+  CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
+                TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE;
+
  private:
   gfx::SurfaceFormat mFormat;
   gfx::IntSize mSize;
   RefPtr<gfxXlibSurface> mSurface;
   RefPtr<ISurfaceAllocator> mAllocator;
   RefPtr<gfx::DrawTarget> mDrawTarget;
   bool mLocked;
 };
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/layers/SimpleTextureClientPool.h" // for SimpleTextureClientPool
 #include "nsAString.h"
 #include "nsIWidget.h"                  // for nsIWidget
 #include "nsIWidgetListener.h"
 #include "nsTArray.h"                   // for AutoInfallibleTArray
 #include "nsXULAppAPI.h"                // for XRE_GetProcessType, etc
 #include "TiledLayerBuffer.h"
 #include "mozilla/dom/WindowBinding.h"  // for Overfill Callback
+#include "gfxPrefs.h"
 #ifdef MOZ_WIDGET_ANDROID
 #include "AndroidBridge.h"
 #endif
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
@@ -160,17 +161,17 @@ ClientLayerManager::BeginTransactionWith
 
   // If we have a non-default target, we need to let our shadow manager draw
   // to it. This will happen at the end of the transaction.
   if (aTarget && XRE_GetProcessType() == GeckoProcessType_Default) {
     mShadowTarget = aTarget;
   }
 
   // If this is a new paint, increment the paint sequence number.
-  if (!mIsRepeatTransaction) {
+  if (!mIsRepeatTransaction && gfxPrefs::APZTestLoggingEnabled()) {
     ++mPaintSequenceNumber;
     mApzTestData.StartNewPaint(mPaintSequenceNumber);
   }
 }
 
 void
 ClientLayerManager::BeginTransaction()
 {
@@ -319,16 +320,24 @@ ClientLayerManager::GetCompositorSideAPZ
 {
   if (mForwarder->HasShadowManager()) {
     if (!mForwarder->GetShadowManager()->SendGetAPZTestData(aData)) {
       NS_WARNING("Call to PLayerTransactionChild::SendGetAPZTestData() failed");
     }
   }
 }
 
+void
+ClientLayerManager::StartNewRepaintRequest(SequenceNumber aSequenceNumber)
+{
+  if (gfxPrefs::APZTestLoggingEnabled()) {
+    mApzTestData.StartNewRepaintRequest(aSequenceNumber);
+  }
+}
+
 bool
 ClientLayerManager::RequestOverfill(mozilla::dom::OverfillCallback* aCallback)
 {
   MOZ_ASSERT(aCallback != nullptr);
   MOZ_ASSERT(HasShadowManager(), "Request Overfill only supported on b2g for now");
 
   if (HasShadowManager()) {
     CompositorChild* child = GetRemoteRenderer();
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -198,20 +198,18 @@ public:
                                   const std::string& aValue)
   {
     mApzTestData.LogTestDataForPaint(mPaintSequenceNumber, aScrollId, aKey, aValue);
   }
 
   // Log APZ test data for a repaint request. The sequence number must be
   // passed in from outside, and APZTestData::StartNewRepaintRequest() needs
   // to be called from the outside as well when a new repaint request is started.
-  void StartNewRepaintRequest(SequenceNumber aSequenceNumber)
-  {
-    mApzTestData.StartNewRepaintRequest(aSequenceNumber);
-  }
+  void StartNewRepaintRequest(SequenceNumber aSequenceNumber);
+
   // TODO(botond): When we start using this and write a wrapper similar to
   // nsLayoutUtils::LogTestDataForPaint(), make sure that wrapper checks
   // gfxPrefs::APZTestLoggingEnabled().
   void LogTestDataForRepaintRequest(SequenceNumber aSequenceNumber,
                                     FrameMetrics::ViewID aScrollId,
                                     const std::string& aKey,
                                     const std::string& aValue)
   {
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -190,48 +190,16 @@ ContentClientRemoteBuffer::EndPaint()
     mTextureClient->Unlock();
   }
   if (mTextureClientOnWhite && mTextureClientOnWhite->IsLocked()) {
     mTextureClientOnWhite->Unlock();
   }
   ContentClientRemote::EndPaint();
 }
 
-bool
-ContentClientRemoteBuffer::CreateAndAllocateTextureClient(RefPtr<TextureClient>& aClient,
-                                                          TextureFlags aFlags)
-{
-  TextureAllocationFlags allocFlags = TextureAllocationFlags::ALLOC_CLEAR_BUFFER;
-  if (aFlags & TextureFlags::ON_WHITE) {
-    allocFlags = TextureAllocationFlags::ALLOC_CLEAR_BUFFER_WHITE;
-  }
-
-  // gfx::BackendType::NONE means fallback to the content backend
-  aClient = CreateTextureClientForDrawing(mSurfaceFormat, mSize,
-                                          gfx::BackendType::NONE,
-                                          mTextureInfo.mTextureFlags | aFlags,
-                                          allocFlags);
-  if (!aClient) {
-    // try with ALLOC_FALLBACK
-    aClient = CreateTextureClientForDrawing(mSurfaceFormat, mSize,
-                                            gfx::BackendType::NONE,
-                                            mTextureInfo.mTextureFlags
-                                            | TextureFlags::ALLOC_FALLBACK
-                                            | aFlags,
-                                            allocFlags);
-  }
-
-  if (!aClient) {
-    return false;
-  }
-
-  NS_WARN_IF_FALSE(aClient->IsValid(), "Created an invalid texture client");
-  return true;
-}
-
 void
 ContentClientRemoteBuffer::BuildTextureClients(SurfaceFormat aFormat,
                                                const nsIntRect& aRect,
                                                uint32_t aFlags)
 {
   // If we hit this assertion, then it might be due to an empty transaction
   // followed by a real transaction. Our buffers should be created (but not
   // painted in the empty transaction) and then painted (but not created) in the
@@ -254,24 +222,42 @@ ContentClientRemoteBuffer::BuildTextureC
   }
 
   CreateBackBuffer(mBufferRect);
 }
 
 void
 ContentClientRemoteBuffer::CreateBackBuffer(const nsIntRect& aBufferRect)
 {
-  if (!CreateAndAllocateTextureClient(mTextureClient, TextureFlags::ON_BLACK) ||
-    !AddTextureClient(mTextureClient)) {
+  // gfx::BackendType::NONE means fallback to the content backend
+  mTextureClient = CreateTextureClientForDrawing(
+    mSurfaceFormat, mSize, gfx::BackendType::NONE,
+    mTextureInfo.mTextureFlags,
+    TextureAllocationFlags::ALLOC_CLEAR_BUFFER
+  );
+  if (!mTextureClient) {
+    // try with ALLOC_FALLBACK
+    mTextureClient = CreateTextureClientForDrawing(
+      mSurfaceFormat, mSize, gfx::BackendType::NONE,
+      mTextureInfo.mTextureFlags | TextureFlags::ALLOC_FALLBACK,
+      TextureAllocationFlags::ALLOC_CLEAR_BUFFER
+    );
+  }
+
+  if (!mTextureClient || !AddTextureClient(mTextureClient)) {
     AbortTextureClientCreation();
     return;
   }
+
   if (mTextureInfo.mTextureFlags & TextureFlags::COMPONENT_ALPHA) {
-    if (!CreateAndAllocateTextureClient(mTextureClientOnWhite, TextureFlags::ON_WHITE) ||
-      !AddTextureClient(mTextureClientOnWhite)) {
+    mTextureClientOnWhite = mTextureClient->CreateSimilar(
+      mTextureInfo.mTextureFlags,
+      TextureAllocationFlags::ALLOC_CLEAR_BUFFER_WHITE
+    );
+    if (!mTextureClientOnWhite || !AddTextureClient(mTextureClientOnWhite)) {
       AbortTextureClientCreation();
       return;
     }
   }
 }
 
 void
 ContentClientRemoteBuffer::CreateBuffer(ContentType aType,
--- a/gfx/layers/client/ContentClient.h
+++ b/gfx/layers/client/ContentClient.h
@@ -274,19 +274,16 @@ protected:
   // Ensure we have a valid back buffer if we have a valid front buffer (i.e.
   // if a backbuffer has been created.)
   virtual void EnsureBackBufferIfFrontBuffer() {}
 
   // Create the front buffer for the ContentClient/Host pair if necessary
   // and notify the compositor that we have created the buffer(s).
   virtual void DestroyFrontBuffer() {}
 
-  bool CreateAndAllocateTextureClient(RefPtr<TextureClient>& aClient,
-                                      TextureFlags aFlags = TextureFlags::NO_FLAGS);
-
   virtual void AbortTextureClientCreation()
   {
     mTextureClient = nullptr;
     mTextureClientOnWhite = nullptr;
     mIsNewBuffer = false;
   }
 
   RefPtr<TextureClient> mTextureClient;
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -645,16 +645,29 @@ BufferTextureClient::BufferTextureClient
   , mBackend(aMoz2DBackend)
   , mOpenMode(OpenMode::OPEN_NONE)
   , mLocked(false)
 {}
 
 BufferTextureClient::~BufferTextureClient()
 {}
 
+TemporaryRef<TextureClient>
+BufferTextureClient::CreateSimilar(TextureFlags aFlags,
+                                   TextureAllocationFlags aAllocFlags) const
+{
+  // This may return null
+  RefPtr<BufferTextureClient> newBufferTex = TextureClient::CreateForRawBufferAccess(
+    mAllocator, mFormat, mSize, mBackend, mFlags | aFlags, aAllocFlags
+  );
+
+  RefPtr<TextureClient> newTex = newBufferTex.get();
+  return newTex;
+}
+
 ISurfaceAllocator*
 BufferTextureClient::GetAllocator() const
 {
   return mAllocator;
 }
 
 bool
 BufferTextureClient::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags)
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -21,16 +21,17 @@
 #include "mozilla/ipc/Shmem.h"          // for Shmem
 #include "mozilla/layers/AtomicRefCountedWithFinalize.h"
 #include "mozilla/layers/CompositorTypes.h"  // for TextureFlags, etc
 #include "mozilla/layers/LayersSurfaces.h"  // for SurfaceDescriptor
 #include "mozilla/mozalloc.h"           // for operator delete
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsISupportsImpl.h"            // for TextureImage::AddRef, etc
+#include "GfxTexturesReporter.h"
 
 class gfxReusableSurfaceWrapper;
 class gfxImageSurface;
 
 namespace mozilla {
 namespace gl {
 class GLContext;
 class SurfaceStream;
@@ -149,16 +150,36 @@ public:
   // pointers) with a certain buffer size. It's unfortunate that we need this.
   // providing format and sizes could let us do more optimization.
   static TemporaryRef<BufferTextureClient>
   CreateWithBufferSize(ISurfaceAllocator* aAllocator,
                        gfx::SurfaceFormat aFormat,
                        size_t aSize,
                        TextureFlags aTextureFlags);
 
+  // Creates and allocates a TextureClient of the same type.
+  virtual TemporaryRef<TextureClient>
+  CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
+                TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const = 0;
+
+  /**
+   * Allocates for a given surface size, taking into account the pixel format
+   * which is part of the state of the TextureClient.
+   *
+   * Does not clear the surface by default, clearing the surface can be done
+   * by passing the CLEAR_BUFFER flag.
+   *
+   * TextureClients that can expose a DrawTarget should override this method.
+   */
+  virtual bool AllocateForSurface(gfx::IntSize aSize,
+                                  TextureAllocationFlags flags = ALLOC_DEFAULT)
+  {
+    return false;
+  }
+
   virtual TextureClientYCbCr* AsTextureClientYCbCr() { return nullptr; }
 
   /**
    * Locks the shared data, allowing the caller to get access to it.
    *
    * Please always lock/unlock when accessing the shared data.
    * If Lock() returns false, you should not attempt to access the shared data.
    */
@@ -339,16 +360,24 @@ public:
    */
   virtual void SetRemoveFromCompositableTracker(AsyncTransactionTracker* aTracker) {}
 
   /**
    * This function waits until the buffer is no longer being used.
    */
   virtual void WaitForBufferOwnership() {}
 
+  /**
+   * Track how much of this texture is wasted.
+   * For example we might allocate a 256x256 tile but only use 10x10.
+   */
+   void SetWaste(int aWasteArea) {
+     mWasteTracker.Update(aWasteArea, BytesPerPixel(GetFormat()));
+   }
+
 private:
   /**
    * Called once, just before the destructor.
    *
    * Here goes the shut-down code that uses virtual methods.
    * Must only be called by Release().
    */
   void Finalize();
@@ -358,32 +387,16 @@ private:
 protected:
   /**
    * An invalid TextureClient cannot provide access to its shared data
    * anymore. This usually means it will soon be destroyed.
    */
   void MarkInvalid() { mValid = false; }
 
   /**
-   * Allocates for a given surface size, taking into account the pixel format
-   * which is part of the state of the TextureClient.
-   *
-   * Does not clear the surface by default, clearing the surface can be done
-   * by passing the CLEAR_BUFFER flag.
-   *
-   * TextureClients that can expose a DrawTarget should override this method.
-   */
-  virtual bool AllocateForSurface(gfx::IntSize aSize,
-                                  TextureAllocationFlags flags = ALLOC_DEFAULT)
-  {
-    return false;
-  }
-
-
-  /**
    * Should only be called *once* per texture, in TextureClient::InitIPDLActor.
    * Some texture implementations rely on the fact that the descriptor will be
    * deserialized.
    * Calling ToSurfaceDescriptor again after it has already returned true,
    * or never constructing a TextureHost with aDescriptor may result in a memory
    * leak (see CairoTextureClientD3D9 for example).
    */
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aDescriptor) = 0;
@@ -397,20 +410,21 @@ protected:
   ISurfaceAllocator* GetAllocator()
   {
     return mAllocator;
   }
 
   RefPtr<TextureChild> mActor;
   RefPtr<ISurfaceAllocator> mAllocator;
   TextureFlags mFlags;
+  FenceHandle mReleaseFenceHandle;
+  FenceHandle mAcquireFenceHandle;
+  gl::GfxTextureWasteTracker mWasteTracker;
   bool mShared;
   bool mValid;
-  FenceHandle mReleaseFenceHandle;
-  FenceHandle mAcquireFenceHandle;
 
   friend class TextureChild;
   friend class RemoveTextureFromCompositableTracker;
   friend void TestTextureClientSurface(TextureClient*, gfxImageSurface*);
   friend void TestTextureClientYCbCr(TextureClient*, PlanarYCbCrData&);
 };
 
 /**
@@ -484,16 +498,20 @@ public:
   virtual bool Allocate(uint32_t aSize) = 0;
 
   virtual size_t GetBufferSize() const = 0;
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; }
 
   ISurfaceAllocator* GetAllocator() const;
 
+  virtual TemporaryRef<TextureClient>
+  CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
+                TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE;
+
 protected:
   RefPtr<gfx::DrawTarget> mDrawTarget;
   RefPtr<ISurfaceAllocator> mAllocator;
   gfx::SurfaceFormat mFormat;
   gfx::IntSize mSize;
   gfx::BackendType mBackend;
   OpenMode mOpenMode;
   bool mLocked;
@@ -592,16 +610,22 @@ public:
 
   virtual gfx::IntSize GetSize() const { return gfx::IntSize(); }
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE
   {
     return gfx::SurfaceFormat::UNKNOWN;
   }
 
+  // This TextureClient should not be used in a context where we use CreateSimilar
+  // (ex. component alpha) because the underlying texture data is always created by
+  // an external producer.
+  virtual TemporaryRef<TextureClient>
+  CreateSimilar(TextureFlags, TextureAllocationFlags) const MOZ_OVERRIDE { return nullptr; }
+
   virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags) MOZ_OVERRIDE
   {
     MOZ_CRASH("Should never hit this.");
     return false;
   }
 
 protected:
   bool mIsLocked;
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -909,19 +909,20 @@ ClientTiledLayerBuffer::ValidateTile(Til
   offsetScaledDirtyRegion.ScaleRoundOut(mResolution, mResolution);
 
   bool usingSinglePaintBuffer = !!mSinglePaintDrawTarget;
   RefPtr<TextureClient> backBuffer =
     aTile.GetBackBuffer(offsetScaledDirtyRegion,
                         mManager->GetTexturePool(gfxPlatform::GetPlatform()->Optimal2DFormatForContent(GetContentType())),
                         &createdTextureClient, !usingSinglePaintBuffer);
 
-  if (!backBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
+  if (!backBuffer || !backBuffer->Lock(OpenMode::OPEN_READ_WRITE)) {
     NS_WARNING("Failed to lock tile TextureClient for updating.");
     aTile.DiscardFrontBuffer();
+    aTile.DiscardBackBuffer();
     return aTile;
   }
 
   // We must not keep a reference to the DrawTarget after it has been unlocked,
   // make sure these are null'd before unlocking as destruction of the context
   // may cause the target to be flushed.
   RefPtr<DrawTarget> drawTarget = backBuffer->BorrowDrawTarget();
   drawTarget->SetTransform(Matrix());
@@ -1030,16 +1031,24 @@ ClientTiledLayerBuffer::ValidateTile(Til
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   DrawDebugOverlay(drawTarget, aTileOrigin.x * mResolution,
                    aTileOrigin.y * mResolution, GetTileLength(), GetTileLength());
 #endif
 
   ctxt = nullptr;
   drawTarget = nullptr;
 
+  nsIntRegion tileRegion =
+    nsIntRect(aTileOrigin.x, aTileOrigin.y,
+              GetScaledTileSize().width, GetScaledTileSize().height);
+  // Intersect this area with the portion that's invalid.
+  tileRegion = tileRegion.Sub(tileRegion, GetValidRegion());
+  tileRegion = tileRegion.Sub(tileRegion, aDirtyRegion); // Has now been validated
+
+  backBuffer->SetWaste(tileRegion.Area() * mResolution * mResolution);
   backBuffer->Unlock();
 
   if (createdTextureClient) {
     if (!mCompositableClient->AddTextureClient(backBuffer)) {
       NS_WARNING("Failed to add tile TextureClient.");
       aTile.DiscardFrontBuffer();
       aTile.DiscardBackBuffer();
       return aTile;
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -180,16 +180,29 @@ TextureClientD3D11::~TextureClientD3D11(
     MOZ_ASSERT(mDrawTarget->refCount() == 1);
     LockD3DTexture(mTexture.get());
     mDrawTarget = nullptr;
     UnlockD3DTexture(mTexture.get());
   }
 #endif
 }
 
+TemporaryRef<TextureClient>
+TextureClientD3D11::CreateSimilar(TextureFlags aFlags,
+                                  TextureAllocationFlags aAllocFlags) const
+{
+  RefPtr<TextureClient> tex = new TextureClientD3D11(mFormat, mFlags | aFlags);
+
+  if (!tex->AllocateForSurface(mSize, ALLOC_DEFAULT)) {
+    return nullptr;
+  }
+
+  return tex;
+}
+
 bool
 TextureClientD3D11::Lock(OpenMode aMode)
 {
   if (!mTexture) {
     return false;
   }
   MOZ_ASSERT(!mIsLocked, "The Texture is already locked!");
 
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -54,16 +54,20 @@ public:
 
   virtual bool CanExposeDrawTarget() const MOZ_OVERRIDE { return true; }
 
   virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
 
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE;
 
+  virtual TemporaryRef<TextureClient>
+  CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
+                TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE;
+
 protected:
   gfx::IntSize mSize;
   RefPtr<ID3D10Texture2D> mTexture;
   RefPtr<gfx::DrawTarget> mDrawTarget;
   gfx::SurfaceFormat mFormat;
   bool mIsLocked;
   bool mNeedsClear;
   bool mNeedsClearWhite;
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -561,16 +561,28 @@ CairoTextureClientD3D9::CairoTextureClie
   MOZ_COUNT_CTOR(CairoTextureClientD3D9);
 }
 
 CairoTextureClientD3D9::~CairoTextureClientD3D9()
 {
   MOZ_COUNT_DTOR(CairoTextureClientD3D9);
 }
 
+TemporaryRef<TextureClient>
+CairoTextureClientD3D9::CreateSimilar(TextureFlags aFlags, TextureAllocationFlags aAllocFlags) const
+{
+  RefPtr<TextureClient> tex = new CairoTextureClientD3D9(mFormat, mFlags | aFlags);
+
+  if (!tex->AllocateForSurface(mSize, aAllocFlags)) {
+    return nullptr;
+  }
+
+  return tex;
+}
+
 bool
 CairoTextureClientD3D9::Lock(OpenMode aMode)
 {
   MOZ_ASSERT(!mIsLocked);
   if (!IsValid() || !IsAllocated()) {
     return false;
   }
 
--- a/gfx/layers/d3d9/TextureD3D9.h
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -211,16 +211,20 @@ public:
 
   virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
 
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE;
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; }
 
+  virtual TemporaryRef<TextureClient>
+  CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
+                TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE;
+
 private:
   RefPtr<IDirect3DTexture9> mTexture;
   nsRefPtr<IDirect3DSurface9> mD3D9Surface;
   RefPtr<gfx::DrawTarget> mDrawTarget;
   nsRefPtr<gfxASurface> mSurface;
   gfx::IntSize mSize;
   gfx::SurfaceFormat mFormat;
   bool mIsLocked;
@@ -263,16 +267,22 @@ public:
 
   virtual gfx::IntSize GetSize() const
   {
     return gfx::IntSize(mDesc.Width, mDesc.Height);
   }
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; }
 
+  // This TextureClient should not be used in a context where we use CreateSimilar
+  // (ex. component alpha) because the underlying texture data is always created by
+  // an external producer.
+  virtual TemporaryRef<TextureClient>
+  CreateSimilar(TextureFlags, TextureAllocationFlags) const MOZ_OVERRIDE { return nullptr; }
+
 private:
   RefPtr<IDirect3DTexture9> mTexture;
   gfx::SurfaceFormat mFormat;
   HANDLE mHandle;
   D3DSURFACE_DESC mDesc;
   bool mIsLocked;
 };
 
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -244,16 +244,19 @@ static void ConnectImageBridge(ImageBrid
   child->Open(parentChannel, parentMsgLoop, mozilla::ipc::ChildSide);
 }
 
 ImageBridgeChild::ImageBridgeChild()
   : mShuttingDown(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
+  // Always run destructor on the main thread
+  SetMessageLoopToPostDestructionTo(MessageLoop::current());
+
   mTxn = new CompositableTransaction();
 }
 ImageBridgeChild::~ImageBridgeChild()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   delete mTxn;
 }
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -590,17 +590,17 @@ ShadowLayerForwarder::EndTransaction(Inf
   } else {
     // If we don't require a swap we can call SendUpdateNoSwap which
     // assumes that aReplies is empty (DEBUG assertion)
     MOZ_LAYERS_LOG(("[LayersForwarder] sending no swap transaction..."));
     RenderTraceScope rendertrace3("Forward NoSwap Transaction", "000093");
     if (!HasShadowManager() ||
         !mShadowManager->IPCOpen() ||
         !mShadowManager->SendUpdateNoSwap(cset, aId, targetConfig, mIsFirstPaint,
-                                          aPaintSequenceNumber, aScheduleComposite,
+                                          aScheduleComposite, aPaintSequenceNumber,
                                           aIsRepeatTransaction)) {
       MOZ_LAYERS_LOG(("[LayersForwarder] WARNING: sending transaction failed!"));
       return false;
     }
   }
 
   *aSent = true;
   mIsFirstPaint = false;
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -50,16 +50,31 @@ GrallocTextureClientOGL::~GrallocTexture
   ISurfaceAllocator* allocator = GetAllocator();
   if (ShouldDeallocateInDestructor()) {
     allocator->DeallocGrallocBuffer(&mGrallocHandle);
   } else {
     allocator->DropGrallocBuffer(&mGrallocHandle);
   }
 }
 
+TemporaryRef<TextureClient>
+GrallocTextureClientOGL::CreateSimilar(TextureFlags aFlags,
+                                       TextureAllocationFlags aAllocFlags) const
+{
+  RefPtr<TextureClient> tex = new GrallocTextureClientOGL(
+    mAllocator, mFormat, mBackend, mFlags | aFlags
+  );
+
+  if (!tex->AllocateForSurface(mSize, aAllocFlags)) {
+    return nullptr;
+  }
+
+  return tex;
+}
+
 void
 GrallocTextureClientOGL::InitWith(MaybeMagicGrallocBufferHandle aHandle, gfx::IntSize aSize)
 {
   MOZ_ASSERT(!IsAllocated());
   MOZ_ASSERT(IsValid());
   mGrallocHandle = aHandle;
   mGraphicBuffer = GetGraphicBufferFrom(aHandle);
   mSize = aSize;
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -109,16 +109,20 @@ public:
     mMediaBuffer = aMediaBuffer;
   }
 
   android::MediaBuffer* GetMediaBuffer()
   {
     return mMediaBuffer;
   }
 
+  virtual TemporaryRef<TextureClient>
+  CreateSimilar(TextureFlags aFlags = TextureFlags::DEFAULT,
+                TextureAllocationFlags aAllocFlags = ALLOC_DEFAULT) const MOZ_OVERRIDE;
+
 protected:
   /**
    * Unfortunately, until bug 879681 is fixed we need to use a GrallocBufferActor.
    */
   MaybeMagicGrallocBufferHandle mGrallocHandle;
 
   RefPtr<AsyncTransactionTracker> mRemoveFromCompositableTracker;
 
--- a/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
+++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
@@ -31,16 +31,22 @@ public:
   virtual bool IsAllocated() const MOZ_OVERRIDE { return !!mSurface; }
 
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE;
 
   virtual gfx::IntSize GetSize() const;
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return false; }
 
+  // This TextureClient should not be used in a context where we use CreateSimilar
+  // (ex. component alpha) because the underlying texture data is always created by
+  // an external producer.
+  virtual TemporaryRef<TextureClient>
+  CreateSimilar(TextureFlags, TextureAllocationFlags) const MOZ_OVERRIDE { return nullptr; }
+
 protected:
   RefPtr<MacIOSurface> mSurface;
   bool mIsLocked;
 };
 
 }
 }
 
--- a/gfx/layers/opengl/TextureClientOGL.h
+++ b/gfx/layers/opengl/TextureClientOGL.h
@@ -55,16 +55,22 @@ public:
 
   virtual gfx::IntSize GetSize() const { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE
   {
     return gfx::SurfaceFormat::UNKNOWN;
   }
 
+  // This TextureClient should not be used in a context where we use CreateSimilar
+  // (ex. component alpha) because the underlying texture data is always created by
+  // an external producer.
+  virtual TemporaryRef<TextureClient>
+  CreateSimilar(TextureFlags, TextureAllocationFlags) const MOZ_OVERRIDE { return nullptr; }
+
   virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags) MOZ_OVERRIDE
   {
     return false;
   }
 
 protected:
   gl::SharedTextureHandle mHandle;
   gfx::IntSize mSize;
--- a/gfx/thebes/gfxContext.h
+++ b/gfx/thebes/gfxContext.h
@@ -14,17 +14,17 @@
 #include "gfxMatrix.h"
 #include "gfxPattern.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 
 #include "mozilla/gfx/2D.h"
 
 typedef struct _cairo cairo_t;
-struct GlyphBufferAzure;
+class GlyphBufferAzure;
 template <typename T> class FallibleTArray;
 
 /**
  * This is the main class for doing actual drawing. It is initialized using
  * a surface and can be drawn on. It manages various state information like
  * a current transformation matrix (CTM), a current path, current color,
  * etc.
  *
@@ -711,17 +711,17 @@ public:
 #endif
 
     static mozilla::gfx::UserDataKey sDontUseAsSourceKey;
 
 private:
     ~gfxContext();
 
   friend class GeneralPattern;
-  friend struct GlyphBufferAzure;
+  friend class GlyphBufferAzure;
 
   typedef mozilla::gfx::Matrix Matrix;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Color Color;
   typedef mozilla::gfx::StrokeOptions StrokeOptions;
   typedef mozilla::gfx::Float Float;
   typedef mozilla::gfx::Rect Rect;
   typedef mozilla::gfx::CompositionOp CompositionOp;
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -3039,236 +3039,222 @@ gfxFont::HasFeatureSet(uint32_t aFeature
 
 /**
  * A helper function in case we need to do any rounding or other
  * processing here.
  */
 #define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
     (double(aAppUnits)*double(aDevUnitsPerAppUnit))
 
-struct GlyphBuffer {
-#define GLYPH_BUFFER_SIZE (2048/sizeof(cairo_glyph_t))
-    cairo_glyph_t mGlyphBuffer[GLYPH_BUFFER_SIZE];
-    unsigned int mNumGlyphs;
-
-    GlyphBuffer()
-        : mNumGlyphs(0) { }
-
-    cairo_glyph_t *AppendGlyph() {
-        return &mGlyphBuffer[mNumGlyphs++];
-    }
-
-    void Flush(cairo_t *aCR, DrawMode aDrawMode, bool aReverse,
-               gfxTextContextPaint *aContextPaint,
-               const gfxMatrix& aGlobalMatrix, bool aFinish = false) {
-        // Ensure there's enough room for a glyph to be added to the buffer
-        // and we actually have glyphs to draw
-        if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
-            return;
-        }
-
-        if (aReverse) {
-            for (uint32_t i = 0; i < mNumGlyphs/2; ++i) {
-                cairo_glyph_t tmp = mGlyphBuffer[i];
-                mGlyphBuffer[i] = mGlyphBuffer[mNumGlyphs - 1 - i];
-                mGlyphBuffer[mNumGlyphs - 1 - i] = tmp;
-            }
-        }
-
-        if (aDrawMode == DrawMode::GLYPH_PATH) {
-            cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
-        } else {
-            if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
-                                  (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) {
-                FlushStroke(aCR, aContextPaint, aGlobalMatrix);
-            }
-            if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) {
-                PROFILER_LABEL("GlyphBuffer", "Flush::cairo_show_glyphs",
-                    js::ProfileEntry::Category::GRAPHICS);
-
-                nsRefPtr<gfxPattern> pattern;
-                if (aContextPaint &&
-                    !!(pattern = aContextPaint->GetFillPattern(aGlobalMatrix))) {
-                    cairo_save(aCR);
-                    cairo_set_source(aCR, pattern->CairoPattern());
-                }
-
-                cairo_show_glyphs(aCR, mGlyphBuffer, mNumGlyphs);
-
-                if (pattern) {
-                    cairo_restore(aCR);
-                }
-            }
-            if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
-                                  int(DrawMode::GLYPH_STROKE)) {
-                FlushStroke(aCR, aContextPaint, aGlobalMatrix);
-            }
-        }
-
-        mNumGlyphs = 0;
-    }
-
-private:
-    void FlushStroke(cairo_t *aCR, gfxTextContextPaint *aContextPaint,
-                     const gfxMatrix& aGlobalMatrix) {
-        nsRefPtr<gfxPattern> pattern;
-        if (aContextPaint &&
-            !!(pattern = aContextPaint->GetStrokePattern(aGlobalMatrix))) {
-            cairo_save(aCR);
-            cairo_set_source(aCR, pattern->CairoPattern());
-        }
-
-        cairo_new_path(aCR);
-        cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
-        cairo_stroke(aCR);
-
-        if (pattern) {
-            cairo_restore(aCR);
-        }
-    }
-
-#undef GLYPH_BUFFER_SIZE
-};
-
 static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) {
   switch (aAAOption) {
   case gfxFont::kAntialiasSubpixel:
     return AntialiasMode::SUBPIXEL;
   case gfxFont::kAntialiasGrayscale:
     return AntialiasMode::GRAY;
   case gfxFont::kAntialiasNone:
     return AntialiasMode::NONE;
   default:
     return AntialiasMode::DEFAULT;
   }
 }
 
-struct GlyphBufferAzure {
+// Parameters passed to gfxFont methods for drawing glyphs from a textrun.
+// The TextRunDrawParams are set up once per textrun; the FontDrawParams
+// are dependent on the specific font, so they are set per GlyphRun.
+
+struct TextRunDrawParams {
+    RefPtr<DrawTarget>             dt;
+    gfxContext                    *context;
+    gfxFont::Spacing              *spacing;
+    gfxTextRunDrawCallbacks       *callbacks;
+    gfxTextContextPaint           *contextPaint;
+    gfxFloat                       direction;
+    double                         devPerApp;
+    DrawMode                       drawMode;
+    bool                           isRTL;
+    bool                           paintSVGGlyphs;
+};
+
+struct FontDrawParams {
+    RefPtr<ScaledFont>             scaledFont;
+    RefPtr<GlyphRenderingOptions>  renderingOptions;
+    Matrix                        *passedInvMatrix;
+    Matrix                         matInv;
+    double                         synBoldOnePixelOffset;
+    int32_t                        extraStrikes;
+    DrawOptions                    drawOptions;
+    bool                           haveSVGGlyphs;
+    bool                           haveColorGlyphs;
+};
+
+class GlyphBufferAzure
+{
+public:
+    GlyphBufferAzure(const TextRunDrawParams& aRunParams,
+                     const FontDrawParams&    aFontParams)
+        : mRunParams(aRunParams)
+        , mFontParams(aFontParams)
+        , mNumGlyphs(0)
+    {
+    }
+
+    ~GlyphBufferAzure()
+    {
+        Flush(true); // flush any remaining buffered glyphs
+    }
+
+    void OutputGlyph(uint32_t aGlyphID, const gfxPoint& aPt)
+    {
+        Glyph *glyph = AppendGlyph();
+        glyph->mIndex = aGlyphID;
+        glyph->mPosition.x = aPt.x;
+        glyph->mPosition.y = aPt.y;
+        glyph->mPosition = mFontParams.matInv * glyph->mPosition;
+        Flush(false); // this will flush only if the buffer is full
+    }
+
+    const TextRunDrawParams& mRunParams;
+    const FontDrawParams& mFontParams;
+
+private:
 #define GLYPH_BUFFER_SIZE (2048/sizeof(Glyph))
-    Glyph mGlyphBuffer[GLYPH_BUFFER_SIZE];
-    unsigned int mNumGlyphs;
-
-    GlyphBufferAzure()
-        : mNumGlyphs(0) { }
-
-    Glyph *AppendGlyph() {
+
+    Glyph *AppendGlyph()
+    {
         return &mGlyphBuffer[mNumGlyphs++];
     }
 
-    void Flush(DrawTarget *aDT, gfxTextContextPaint *aContextPaint, ScaledFont *aFont,
-               DrawMode aDrawMode, bool aReverse, const GlyphRenderingOptions *aOptions,
-               gfxContext *aThebesContext, const Matrix *aInvFontMatrix, const DrawOptions &aDrawOptions,
-               bool aFinish = false)
+    // Render the buffered glyphs to the draw target and clear the buffer.
+    // This actually flushes the glyphs only if the buffer is full, or if the
+    // aFinish parameter is true; otherwise it simply returns.
+    void Flush(bool aFinish)
     {
         // Ensure there's enough room for a glyph to be added to the buffer
         if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
             return;
         }
 
-        if (aReverse) {
+        if (mRunParams.isRTL) {
             Glyph *begin = &mGlyphBuffer[0];
             Glyph *end = &mGlyphBuffer[mNumGlyphs];
             std::reverse(begin, end);
         }
-        
+
         gfx::GlyphBuffer buf;
         buf.mGlyphs = mGlyphBuffer;
         buf.mNumGlyphs = mNumGlyphs;
 
-        gfxContext::AzureState state = aThebesContext->CurrentState();
-        if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
-                              (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) {
-            FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state);
-        }
-        if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) {
-            if (state.pattern || aContextPaint) {
+        gfxContext::AzureState state = mRunParams.context->CurrentState();
+        if ((int(mRunParams.drawMode) &
+            (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
+            (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) {
+            FlushStroke(buf, state);
+        }
+        if (int(mRunParams.drawMode) & int(DrawMode::GLYPH_FILL)) {
+            if (state.pattern || mRunParams.contextPaint) {
                 Pattern *pat;
 
                 nsRefPtr<gfxPattern> fillPattern;
-                if (!aContextPaint ||
-                    !(fillPattern = aContextPaint->GetFillPattern(aThebesContext->CurrentMatrix()))) {
+                if (!mRunParams.contextPaint ||
+                    !(fillPattern = mRunParams.contextPaint->GetFillPattern(
+                                        mRunParams.context->CurrentMatrix()))) {
                     if (state.pattern) {
-                        pat = state.pattern->GetPattern(aDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
+                        pat = state.pattern->GetPattern(mRunParams.dt,
+                                      state.patternTransformChanged ?
+                                          &state.patternTransform : nullptr);
                     } else {
                         pat = nullptr;
                     }
                 } else {
-                    pat = fillPattern->GetPattern(aDT);
+                    pat = fillPattern->GetPattern(mRunParams.dt);
                 }
 
                 if (pat) {
                     Matrix saved;
                     Matrix *mat = nullptr;
-                    if (aInvFontMatrix) {
-                        // The brush matrix needs to be multiplied with the inverted matrix
-                        // as well, to move the brush into the space of the glyphs. Before
-                        // the render target transformation
-
-                        // This relies on the returned Pattern not to be reused by
-                        // others, but regenerated on GetPattern calls. This is true!
+                    if (mFontParams.passedInvMatrix) {
+                        // The brush matrix needs to be multiplied with the
+                        // inverted matrix as well, to move the brush into the
+                        // space of the glyphs.
+
+                        // This relies on the returned Pattern not to be reused
+                        // by others, but regenerated on GetPattern calls. This
+                        // is true!
                         if (pat->GetType() == PatternType::LINEAR_GRADIENT) {
                             mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix;
                         } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) {
                             mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix;
                         } else if (pat->GetType() == PatternType::SURFACE) {
                             mat = &static_cast<SurfacePattern*>(pat)->mMatrix;
                         }
 
                         if (mat) {
                             saved = *mat;
-                            *mat = (*mat) * (*aInvFontMatrix);
+                            *mat = (*mat) * (*mFontParams.passedInvMatrix);
                         }
                     }
 
-                    aDT->FillGlyphs(aFont, buf, *pat,
-                                    aDrawOptions, aOptions);
+                    mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
+                                              *pat, mFontParams.drawOptions,
+                                              mFontParams.renderingOptions);
 
                     if (mat) {
                         *mat = saved;
                     }
                 }
             } else if (state.sourceSurface) {
-                aDT->FillGlyphs(aFont, buf, SurfacePattern(state.sourceSurface,
-                                                           ExtendMode::CLAMP,
-                                                           state.surfTransform),
-                                aDrawOptions, aOptions);
+                mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
+                                          SurfacePattern(state.sourceSurface,
+                                                         ExtendMode::CLAMP,
+                                                         state.surfTransform),
+                                          mFontParams.drawOptions,
+                                          mFontParams.renderingOptions);
             } else {
-                aDT->FillGlyphs(aFont, buf, ColorPattern(state.color),
-                                aDrawOptions, aOptions);
+                mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
+                                          ColorPattern(state.color),
+                                          mFontParams.drawOptions,
+                                          mFontParams.renderingOptions);
             }
         }
-        if (int(aDrawMode) & int(DrawMode::GLYPH_PATH)) {
-            aThebesContext->EnsurePathBuilder();
-			Matrix mat = aDT->GetTransform();
-            aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder,
-                                       aDT->GetBackendType(), &mat);
-        }
-        if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
-                              int(DrawMode::GLYPH_STROKE)) {
-            FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state);
+        if (int(mRunParams.drawMode) & int(DrawMode::GLYPH_PATH)) {
+            mRunParams.context->EnsurePathBuilder();
+            Matrix mat = mRunParams.dt->GetTransform();
+            mFontParams.scaledFont->CopyGlyphsToBuilder(
+                buf, mRunParams.context->mPathBuilder,
+                mRunParams.dt->GetBackendType(), &mat);
+        }
+        if ((int(mRunParams.drawMode) &
+            (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
+             int(DrawMode::GLYPH_STROKE)) {
+            FlushStroke(buf, state);
         }
 
         mNumGlyphs = 0;
     }
 
-private:
-    void FlushStroke(DrawTarget *aDT, gfxTextContextPaint *aContextPaint,
-                     ScaledFont *aFont, gfxContext *aThebesContext,
-                     gfx::GlyphBuffer& aBuf, gfxContext::AzureState& aState)
+    void FlushStroke(gfx::GlyphBuffer& aBuf, gfxContext::AzureState& aState)
     {
-        RefPtr<Path> path = aFont->GetPathForGlyphs(aBuf, aDT);
-        if (aContextPaint) {
+        RefPtr<Path> path =
+            mFontParams.scaledFont->GetPathForGlyphs(aBuf, mRunParams.dt);
+        if (mRunParams.contextPaint) {
             nsRefPtr<gfxPattern> strokePattern =
-              aContextPaint->GetStrokePattern(aThebesContext->CurrentMatrix());
+                mRunParams.contextPaint->GetStrokePattern(
+                    mRunParams.context->CurrentMatrix());
             if (strokePattern) {
-                aDT->Stroke(path, *strokePattern->GetPattern(aDT), aState.strokeOptions);
+                mRunParams.dt->Stroke(path,
+                                      *strokePattern->GetPattern(mRunParams.dt),
+                                      aState.strokeOptions);
             }
         }
     }
 
+    Glyph        mGlyphBuffer[GLYPH_BUFFER_SIZE];
+    unsigned int mNumGlyphs;
+
 #undef GLYPH_BUFFER_SIZE
 };
 
 // Bug 674909. When synthetic bolding text by drawing twice, need to
 // render using a pixel offset in device pixels, otherwise text
 // doesn't appear bolded, it appears as if a bad text shadow exists
 // when a non-identity transform exists.  Use an offset factor so that
 // the second draw occurs at a constant offset in device pixels.
@@ -3297,332 +3283,258 @@ gfxFont::CalcXScale(gfxContext *aContext
 static DrawMode
 ForcePaintingDrawMode(DrawMode aDrawMode)
 {
     return aDrawMode == DrawMode::GLYPH_PATH ?
         DrawMode(int(DrawMode::GLYPH_FILL) | int(DrawMode::GLYPH_STROKE)) :
         aDrawMode;
 }
 
+// Draw an individual glyph at a specific location.
+// *aPt is the glyph position in appUnits; it is converted to device
+// coordinates (devPt) here.
+void
+gfxFont::DrawOneGlyph(uint32_t aGlyphID, double aAdvance, gfxPoint *aPt,
+                      GlyphBufferAzure& aBuffer, bool *aEmittedGlyphs) const
+{
+    const TextRunDrawParams& runParams(aBuffer.mRunParams);
+    const FontDrawParams& fontParams(aBuffer.mFontParams);
+
+    double glyphX;
+    if (runParams.isRTL) {
+        aPt->x -= aAdvance;
+        glyphX = aPt->x;
+    } else {
+        glyphX = aPt->x;
+        aPt->x += aAdvance;
+    }
+    gfxPoint devPt(ToDeviceUnits(glyphX, runParams.devPerApp),
+                   ToDeviceUnits(aPt->y, runParams.devPerApp));
+
+    if (fontParams.haveSVGGlyphs) {
+        if (!runParams.paintSVGGlyphs) {
+            return;
+        }
+        DrawMode mode = ForcePaintingDrawMode(runParams.drawMode);
+        if (RenderSVGGlyph(runParams.context, devPt, mode,
+                           aGlyphID, runParams.contextPaint,
+                           runParams.callbacks, *aEmittedGlyphs)) {
+            return;
+        }
+    }
+
+    if (fontParams.haveColorGlyphs &&
+        RenderColorGlyph(runParams.context, fontParams.scaledFont,
+                         fontParams.renderingOptions, fontParams.drawOptions,
+                         fontParams.matInv * gfx::Point(devPt.x, devPt.y),
+                         aGlyphID)) {
+        return;
+    }
+
+    aBuffer.OutputGlyph(aGlyphID, devPt);
+
+    // Synthetic bolding (if required) by multi-striking.
+    for (int32_t i = 0; i < fontParams.extraStrikes; ++i) {
+        devPt.x += fontParams.synBoldOnePixelOffset;
+        aBuffer.OutputGlyph(aGlyphID, devPt);
+    }
+
+    *aEmittedGlyphs = true;
+}
+
+// Draw a run of CharacterGlyph records from the given offset in aShapedText.
+// Returns true if glyph paths were actually emitted.
+bool
+gfxFont::DrawGlyphs(gfxShapedText            *aShapedText,
+                    uint32_t                  aOffset, // offset in the textrun
+                    uint32_t                  aCount, // length of run to draw
+                    gfxPoint                 *aPt,
+                    const TextRunDrawParams&  aRunParams,
+                    const FontDrawParams&     aFontParams)
+{
+    bool emittedGlyphs = false;
+    GlyphBufferAzure buffer(aRunParams, aFontParams);
+
+    if (aRunParams.spacing) {
+        aPt->x += aRunParams.direction * aRunParams.spacing[0].mBefore;
+    }
+
+    const gfxShapedText::CompressedGlyph *glyphData =
+        &aShapedText->GetCharacterGlyphs()[aOffset];
+
+    for (uint32_t i = 0; i < aCount; ++i, ++glyphData) {
+        if (glyphData->IsSimpleGlyph()) {
+            DrawOneGlyph(glyphData->GetSimpleGlyph(),
+                         glyphData->GetSimpleAdvance(),
+                         aPt, buffer, &emittedGlyphs);
+        } else {
+            uint32_t glyphCount = glyphData->GetGlyphCount();
+            if (glyphCount > 0) {
+                const gfxShapedText::DetailedGlyph *details =
+                    aShapedText->GetDetailedGlyphs(aOffset + i);
+                NS_ASSERTION(details, "detailedGlyph should not be missing!");
+                for (uint32_t j = 0; j < glyphCount; ++j, ++details) {
+                    double advance = details->mAdvance;
+                    if (glyphData->IsMissing()) {
+                        // Default-ignorable chars will have zero advance width;
+                        // we don't have to draw the hexbox for them.
+                        if (aRunParams.drawMode != DrawMode::GLYPH_PATH &&
+                            advance > 0) {
+                            double glyphX = aPt->x;
+                            if (aRunParams.isRTL) {
+                                glyphX -= advance;
+                            }
+                            gfxPoint pt(ToDeviceUnits(glyphX, aRunParams.devPerApp),
+                                        ToDeviceUnits(aPt->y, aRunParams.devPerApp));
+                            gfxFloat advanceDevUnits =
+                                ToDeviceUnits(advance, aRunParams.devPerApp);
+                            gfxFloat height = GetMetrics().maxAscent;
+                            gfxRect glyphRect(pt.x, pt.y - height,
+                                              advanceDevUnits, height);
+                            gfxFontMissingGlyphs::DrawMissingGlyph(
+                                aRunParams.context, glyphRect, details->mGlyphID,
+                                aShapedText->GetAppUnitsPerDevUnit());
+                        }
+                    } else {
+                        gfxPoint glyphXY(*aPt);
+                        glyphXY.x += details->mXOffset;
+                        glyphXY.y += details->mYOffset;
+                        DrawOneGlyph(details->mGlyphID, advance, &glyphXY,
+                                     buffer, &emittedGlyphs);
+                    }
+                    aPt->x += aRunParams.direction * advance;
+                }
+            }
+        }
+
+        if (aRunParams.spacing) {
+            double space = aRunParams.spacing[i].mAfter;
+            if (i + 1 < aCount) {
+                space += aRunParams.spacing[i + 1].mBefore;
+            }
+            aPt->x += aRunParams.direction * space;
+        }
+    }
+
+    return emittedGlyphs;
+}
+
 void
 gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
-              gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aPt,
-              Spacing *aSpacing, gfxTextContextPaint *aContextPaint,
-              gfxTextRunDrawCallbacks *aCallbacks)
-{
-    NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)),
+              gfxPoint *aPt, TextRunDrawParams& aRunParams)
+{
+    NS_ASSERTION(aRunParams.drawMode == DrawMode::GLYPH_PATH ||
+                 !(int(aRunParams.drawMode) & int(DrawMode::GLYPH_PATH)),
                  "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
 
-    if (aStart >= aEnd)
+    if (aStart >= aEnd) {
+        return;
+    }
+
+    FontDrawParams fontParams;
+
+    fontParams.scaledFont = GetScaledFont(aRunParams.dt);
+    if (!fontParams.scaledFont) {
         return;
-
-    const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
-    const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
-    const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
-    bool isRTL = aTextRun->IsRightToLeft();
-    double direction = aTextRun->GetDirection();
-
-    bool haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
-    bool haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs();
+    }
+
+    fontParams.haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
+    fontParams.haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs();
+
     nsAutoPtr<gfxTextContextPaint> contextPaint;
-    if (haveSVGGlyphs && !aContextPaint) {
+    if (fontParams.haveSVGGlyphs && !aRunParams.contextPaint) {
         // If no pattern is specified for fill, use the current pattern
-        NS_ASSERTION((int(aDrawMode) & int(DrawMode::GLYPH_STROKE)) == 0, "no pattern supplied for stroking text");
-        nsRefPtr<gfxPattern> fillPattern = aContext->GetPattern();
-        contextPaint = new SimpleTextContextPaint(fillPattern, nullptr,
-                                                 aContext->CurrentMatrix());
-        aContextPaint = contextPaint;
-    }
-
-    // synthetic-bold strikes are each offset one device pixel in run direction
+        NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
+                     "no pattern supplied for stroking text");
+        nsRefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern();
+        contextPaint =
+            new SimpleTextContextPaint(fillPattern, nullptr,
+                                       aRunParams.context->CurrentMatrix());
+        aRunParams.contextPaint = contextPaint;
+    }
+
+    // Synthetic-bold strikes are each offset one device pixel in run direction.
     // (these values are only needed if IsSyntheticBold() is true)
-    double synBoldOnePixelOffset = 0;
-    int32_t strikes = 1;
     if (IsSyntheticBold()) {
-        double xscale = CalcXScale(aContext);
-        synBoldOnePixelOffset = direction * xscale;
+        double xscale = CalcXScale(aRunParams.context);
+        fontParams.synBoldOnePixelOffset = aRunParams.direction * xscale;
         if (xscale != 0.0) {
             // use as many strikes as needed for the the increased advance
-            strikes = NS_lroundf(GetSyntheticBoldOffset() / xscale);
-        }
-    }
-
-    uint32_t i;
-    // Current position in appunits
-    double x = aPt->x;
-    double y = aPt->y;
-    double origY = aPt->y;
-    if (mStyle.baselineOffset != 0.0) {
-        y += mStyle.baselineOffset * appUnitsPerDevUnit;
-    }
-
-    RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
-
-    bool paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs;
-    bool emittedGlyphs = false;
-
-    {
-      RefPtr<ScaledFont> scaledFont = GetScaledFont(dt);
-
-      if (!scaledFont) {
-        return;
-      }
-
-      bool oldSubpixelAA = dt->GetPermitSubpixelAA();
-
-      if (!AllowSubpixelAA()) {
-          dt->SetPermitSubpixelAA(false);
-      }
-
-      GlyphBufferAzure glyphs;
-      Glyph *glyph;
-
-      Matrix mat, matInv;
-      Matrix oldMat = dt->GetTransform();
-
-      // This is nullptr when we have inverse-transformed glyphs and we need
-      // to transform the Brush inside flush.
-      Matrix *passedInvMatrix = nullptr;
-
-      RefPtr<GlyphRenderingOptions> renderingOptions =
-        GetGlyphRenderingOptions();
-
-      DrawOptions drawOptions;
-      drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
-
-      // The cairo DrawTarget backend uses the cairo_scaled_font directly
-      // and so has the font skew matrix applied already.
-      if (mScaledFont &&
-          dt->GetBackendType() != BackendType::CAIRO) {
+            fontParams.extraStrikes =
+                std::max(1, NS_lroundf(GetSyntheticBoldOffset() / xscale));
+        }
+    } else {
+        fontParams.synBoldOnePixelOffset = 0;
+        fontParams.extraStrikes = 0;
+    }
+
+    bool oldSubpixelAA = aRunParams.dt->GetPermitSubpixelAA();
+    if (!AllowSubpixelAA()) {
+        aRunParams.dt->SetPermitSubpixelAA(false);
+    }
+
+    Matrix mat;
+    Matrix oldMat = aRunParams.dt->GetTransform();
+
+    // This is nullptr when we have inverse-transformed glyphs and we need
+    // to transform the Brush inside flush.
+    fontParams.passedInvMatrix = nullptr;
+
+    fontParams.renderingOptions = GetGlyphRenderingOptions();
+    fontParams.drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
+
+    // The cairo DrawTarget backend uses the cairo_scaled_font directly
+    // and so has the font skew matrix applied already.
+    if (mScaledFont &&
+        aRunParams.dt->GetBackendType() != BackendType::CAIRO) {
         cairo_matrix_t matrix;
         cairo_scaled_font_get_font_matrix(mScaledFont, &matrix);
         if (matrix.xy != 0) {
-          // If this matrix applies a skew, which can happen when drawing
-          // oblique fonts, we will set the DrawTarget matrix to apply the
-          // skew. We'll need to move the glyphs by the inverse of the skew to
-          // get the glyphs positioned correctly in the new device space
-          // though, since the font matrix should only be applied to drawing
-          // the glyphs, and not to their position.
-          mat = ToMatrix(*reinterpret_cast<gfxMatrix*>(&matrix));
-
-          mat._11 = mat._22 = 1.0;
-          float adjustedSize = mAdjustedSize > 0 ? mAdjustedSize : GetStyle()->size;
-          mat._21 /= adjustedSize;
-
-          dt->SetTransform(mat * oldMat);
-
-          matInv = mat;
-          matInv.Invert();
-
-          passedInvMatrix = &matInv;
-        }
-      }
-
-      if (aSpacing) {
-          x += direction*aSpacing[0].mBefore;
-      }
-      for (i = aStart; i < aEnd; ++i) {
-          const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
-          if (glyphData->IsSimpleGlyph()) {
-              double advance = glyphData->GetSimpleAdvance();
-              double glyphX;
-              if (isRTL) {
-                  x -= advance;
-                  glyphX = x;
-              } else {
-                  glyphX = x;
-                  x += advance;
-              }
-
-              if (haveSVGGlyphs) {
-                  if (!paintSVGGlyphs) {
-                      continue;
-                  }
-                  gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
-                                 ToDeviceUnits(y, devUnitsPerAppUnit));
-                  DrawMode mode = ForcePaintingDrawMode(aDrawMode);
-                  if (RenderSVGGlyph(aContext, point, mode,
-                                     glyphData->GetSimpleGlyph(), aContextPaint,
-                                     aCallbacks, emittedGlyphs)) {
-                      continue;
-                  }
-              }
-
-              if (haveColorGlyphs) {
-                  mozilla::gfx::Point point(ToDeviceUnits(glyphX,
-                                                          devUnitsPerAppUnit),
-                                            ToDeviceUnits(y,
-                                                          devUnitsPerAppUnit));
-                  if (RenderColorGlyph(aContext, scaledFont, renderingOptions,
-                                       drawOptions, matInv * point,
-                                       glyphData->GetSimpleGlyph())) {
-                      continue;
-                  }
-              }
-
-              // Perhaps we should put a scale in the cairo context instead of
-              // doing this scaling here...
-              // Multiplying by the reciprocal may introduce tiny error here,
-              // but we assume cairo is going to round coordinates at some stage
-              // and this is faster
-              glyph = glyphs.AppendGlyph();
-              glyph->mIndex = glyphData->GetSimpleGlyph();
-              glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
-              glyph->mPosition.y = ToDeviceUnits(y, devUnitsPerAppUnit);
-              glyph->mPosition = matInv * glyph->mPosition;
-              glyphs.Flush(dt, aContextPaint, scaledFont,
-                           aDrawMode, isRTL, renderingOptions,
-                           aContext, passedInvMatrix,
-                           drawOptions);
-
-              // synthetic bolding by multi-striking with 1-pixel offsets
-              // at least once, more if there's room (large font sizes)
-              if (IsSyntheticBold()) {
-                  double strikeOffset = synBoldOnePixelOffset;
-                  int32_t strikeCount = strikes;
-                  do {
-                      Glyph *doubleglyph;
-                      doubleglyph = glyphs.AppendGlyph();
-                      doubleglyph->mIndex = glyph->mIndex;
-                      doubleglyph->mPosition.x =
-                          ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit,
-                                        devUnitsPerAppUnit);
-                      doubleglyph->mPosition.y = glyph->mPosition.y;
-                      doubleglyph->mPosition = matInv * doubleglyph->mPosition;
-                      strikeOffset += synBoldOnePixelOffset;
-                      glyphs.Flush(dt, aContextPaint, scaledFont,
-                                   aDrawMode, isRTL, renderingOptions,
-                                   aContext, passedInvMatrix,
-                                   drawOptions);
-                  } while (--strikeCount > 0);
-              }
-              emittedGlyphs = true;
-          } else {
-              uint32_t glyphCount = glyphData->GetGlyphCount();
-              if (glyphCount > 0) {
-                  const gfxTextRun::DetailedGlyph *details =
-                      aTextRun->GetDetailedGlyphs(i);
-                  NS_ASSERTION(details, "detailedGlyph should not be missing!");
-                  double advance;
-                  for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) {
-                      advance = details->mAdvance;
-                      if (glyphData->IsMissing()) {
-                          // default ignorable characters will have zero advance width.
-                          // we don't have to draw the hexbox for them
-                          if (aDrawMode != DrawMode::GLYPH_PATH && advance > 0) {
-                              double glyphX = x;
-                              if (isRTL) {
-                                  glyphX -= advance;
-                              }
-                              gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
-                                          ToDeviceUnits(y, devUnitsPerAppUnit));
-                              gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit);
-                              gfxFloat height = GetMetrics().maxAscent;
-                              gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height);
-                              gfxFontMissingGlyphs::DrawMissingGlyph(aContext,
-                                                                     glyphRect,
-                                                                     details->mGlyphID,
-                                                                     appUnitsPerDevUnit);
-                          }
-                      } else {
-                          double glyphX = x + details->mXOffset;
-                          if (isRTL) {
-                              glyphX -= advance;
-                          }
-
-                          gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
-                                         ToDeviceUnits(y, devUnitsPerAppUnit));
-
-                          if (haveSVGGlyphs) {
-                              if (!paintSVGGlyphs) {
-                                  continue;
-                              }
-                              DrawMode mode = ForcePaintingDrawMode(aDrawMode);
-                              if (RenderSVGGlyph(aContext, point, mode,
-                                                 details->mGlyphID,
-                                                 aContextPaint, aCallbacks,
-                                                 emittedGlyphs)) {
-                                  continue;
-                              }
-                          }
-
-                          if (haveColorGlyphs) {
-                              mozilla::gfx::Point point(ToDeviceUnits(glyphX,
-                                                                      devUnitsPerAppUnit),
-                                                        ToDeviceUnits(y + details->mYOffset,
-                                                                      devUnitsPerAppUnit));
-                              if (RenderColorGlyph(aContext, scaledFont,
-                                                   renderingOptions,
-                                                   drawOptions, matInv * point,
-                                                   details->mGlyphID)) {
-                                  continue;
-                              }
-                          }
-
-                          glyph = glyphs.AppendGlyph();
-                          glyph->mIndex = details->mGlyphID;
-                          glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
-                          glyph->mPosition.y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
-                          glyph->mPosition = matInv * glyph->mPosition;
-                          glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode,
-                                       isRTL, renderingOptions, aContext, passedInvMatrix,
-                                       drawOptions);
-
-                          if (IsSyntheticBold()) {
-                              double strikeOffset = synBoldOnePixelOffset;
-                              int32_t strikeCount = strikes;
-                              do {
-                                  Glyph *doubleglyph;
-                                  doubleglyph = glyphs.AppendGlyph();
-                                  doubleglyph->mIndex = glyph->mIndex;
-                                  doubleglyph->mPosition.x =
-                                      ToDeviceUnits(glyphX + strikeOffset *
-                                                    appUnitsPerDevUnit,
-                                                    devUnitsPerAppUnit);
-                                  doubleglyph->mPosition.y = glyph->mPosition.y;
-                                  strikeOffset += synBoldOnePixelOffset;
-                                  doubleglyph->mPosition = matInv * doubleglyph->mPosition;
-                                  glyphs.Flush(dt, aContextPaint, scaledFont,
-                                               aDrawMode, isRTL, renderingOptions,
-                                               aContext, passedInvMatrix, drawOptions);
-                              } while (--strikeCount > 0);
-                          }
-                          emittedGlyphs = true;
-                      }
-                  }
-              }
-          }
-
-          if (aSpacing) {
-              double space = aSpacing[i - aStart].mAfter;
-              if (i + 1 < aEnd) {
-                  space += aSpacing[i + 1 - aStart].mBefore;
-              }
-              x += direction*space;
-          }
-      }
-
-      glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode, isRTL,
-                   renderingOptions, aContext, passedInvMatrix,
-                   drawOptions, true);
-      if (aCallbacks && emittedGlyphs) {
-          aCallbacks->NotifyGlyphPathEmitted();
-      }
-
-      dt->SetTransform(oldMat);
-
-      dt->SetPermitSubpixelAA(oldSubpixelAA);
-    }
-
-    *aPt = gfxPoint(x, origY);
+            // If this matrix applies a skew, which can happen when drawing
+            // oblique fonts, we will set the DrawTarget matrix to apply the
+            // skew. We'll need to move the glyphs by the inverse of the skew to
+            // get the glyphs positioned correctly in the new device space
+            // though, since the font matrix should only be applied to drawing
+            // the glyphs, and not to their position.
+            mat = ToMatrix(*reinterpret_cast<gfxMatrix*>(&matrix));
+
+            mat._11 = mat._22 = 1.0;
+            mat._21 /= GetAdjustedSize();
+
+            aRunParams.dt->SetTransform(mat * oldMat);
+
+            fontParams.matInv = mat;
+            fontParams.matInv.Invert();
+
+            fontParams.passedInvMatrix = &fontParams.matInv;
+        }
+    }
+
+    double origY = aPt->y;
+    if (mStyle.baselineOffset != 0.0) {
+        aPt->y += mStyle.baselineOffset * aTextRun->GetAppUnitsPerDevUnit();
+    }
+
+    bool emittedGlyphs =
+        DrawGlyphs(aTextRun, aStart, aEnd - aStart, aPt,
+                   aRunParams, fontParams);
+
+    aPt->y = origY;
+
+    if (aRunParams.callbacks && emittedGlyphs) {
+        aRunParams.callbacks->NotifyGlyphPathEmitted();
+    }
+
+    aRunParams.dt->SetTransform(oldMat);
+    aRunParams.dt->SetPermitSubpixelAA(oldSubpixelAA);
 }
 
 bool
 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
-                        uint32_t aGlyphId, gfxTextContextPaint *aContextPaint)
+                        uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) const
 {
     if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
         return false;
     }
 
     const gfxFloat devUnitsPerSVGUnit =
         GetAdjustedSize() / GetFontEntry()->UnitsPerEm();
     gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
@@ -3635,17 +3547,17 @@ gfxFont::RenderSVGGlyph(gfxContext *aCon
     return GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, int(aDrawMode),
                                           aContextPaint);
 }
 
 bool
 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
                         uint32_t aGlyphId, gfxTextContextPaint *aContextPaint,
                         gfxTextRunDrawCallbacks *aCallbacks,
-                        bool& aEmittedGlyphs)
+                        bool& aEmittedGlyphs) const
 {
     if (aCallbacks) {
         if (aEmittedGlyphs) {
             aCallbacks->NotifyGlyphPathEmitted();
             aEmittedGlyphs = false;
         }
         aCallbacks->NotifyBeforeSVGGlyphPainted();
     }
@@ -3653,56 +3565,22 @@ gfxFont::RenderSVGGlyph(gfxContext *aCon
                                    aContextPaint);
     if (aCallbacks) {
         aCallbacks->NotifyAfterSVGGlyphPainted();
     }
     return rendered;
 }
 
 bool
-gfxFont::RenderColorGlyph(gfxContext* aContext, gfxPoint& point,
-                          uint32_t aGlyphId)
-{
-    nsAutoTArray<uint16_t, 8> layerGlyphs;
-    nsAutoTArray<mozilla::gfx::Color, 8> layerColors;
-
-    if (!GetFontEntry()->GetColorLayersInfo(aGlyphId, layerGlyphs, layerColors)) {
-        return false;
-    }
-
-    cairo_t* cr = aContext->GetCairo();
-    cairo_save(cr);
-    for (uint32_t layerIndex = 0; layerIndex < layerGlyphs.Length();
-         layerIndex++) {
-
-        cairo_glyph_t glyph;
-        glyph.index = layerGlyphs[layerIndex];
-        glyph.x = point.x;
-        glyph.y = point.y;
-
-        mozilla::gfx::Color &color = layerColors[layerIndex];
-        cairo_pattern_t* pattern =
-            cairo_pattern_create_rgba(color.r, color.g, color.b, color.a);
-
-        cairo_set_source(cr, pattern);
-        cairo_show_glyphs(cr, &glyph, 1);
-        cairo_pattern_destroy(pattern);
-    }
-    cairo_restore(cr);
-
-    return true;
-}
-
-bool
 gfxFont::RenderColorGlyph(gfxContext* aContext,
                           mozilla::gfx::ScaledFont* scaledFont,
                           GlyphRenderingOptions* aRenderingOptions,
                           mozilla::gfx::DrawOptions aDrawOptions,
                           const mozilla::gfx::Point& aPoint,
-                          uint32_t aGlyphId)
+                          uint32_t aGlyphId) const
 {
     nsAutoTArray<uint16_t, 8> layerGlyphs;
     nsAutoTArray<mozilla::gfx::Color, 8> layerColors;
 
     if (!GetFontEntry()->GetColorLayersInfo(aGlyphId, layerGlyphs, layerColors)) {
         return false;
     }
 
@@ -7034,30 +6912,26 @@ gfxTextRun::ShrinkToLigatureBoundaries(u
     if (*aEnd < GetLength()) {
         while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
             --(*aEnd);
         }
     }
 }
 
 void
-gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
-                       DrawMode aDrawMode, gfxPoint *aPt,
-                       gfxTextContextPaint *aContextPaint,
-                       uint32_t aStart, uint32_t aEnd,
-                       PropertyProvider *aProvider,
+gfxTextRun::DrawGlyphs(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
+                       gfxPoint *aPt, PropertyProvider *aProvider,
                        uint32_t aSpacingStart, uint32_t aSpacingEnd,
-                       gfxTextRunDrawCallbacks *aCallbacks)
+                       TextRunDrawParams& aParams)
 {
     nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
     bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
         aSpacingStart, aSpacingEnd, &spacingBuffer);
-    aFont->Draw(this, aStart, aEnd, aContext, aDrawMode, aPt,
-                haveSpacing ? spacingBuffer.Elements() : nullptr, aContextPaint,
-                aCallbacks);
+    aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
+    aFont->Draw(this, aStart, aEnd, aPt, aParams);
 }
 
 static void
 ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight,
                     gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature)
 {
     if (aLigature->mClipBeforePart) {
         if (aTextRun->IsRightToLeft()) {
@@ -7072,59 +6946,54 @@ ClipPartialLigature(gfxTextRun *aTextRun
             *aLeft = std::max(*aLeft, endEdge);
         } else {
             *aRight = std::min(*aRight, endEdge);
         }
     }    
 }
 
 void
-gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx,
-                                uint32_t aStart, uint32_t aEnd,
-                                gfxPoint *aPt,
-                                PropertyProvider *aProvider,
-                                gfxTextRunDrawCallbacks *aCallbacks)
+gfxTextRun::DrawPartialLigature(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
+                                gfxPoint *aPt, PropertyProvider *aProvider,
+                                TextRunDrawParams& aParams)
 {
     if (aStart >= aEnd)
         return;
 
     // Draw partial ligature. We hack this by clipping the ligature.
     LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
-    gfxRect clipExtents = aCtx->GetClipExtents();
-    gfxFloat left = clipExtents.X()*mAppUnitsPerDevUnit;
-    gfxFloat right = clipExtents.XMost()*mAppUnitsPerDevUnit;
+    gfxRect clipExtents = aParams.context->GetClipExtents();
+    gfxFloat left = clipExtents.X() * mAppUnitsPerDevUnit;
+    gfxFloat right = clipExtents.XMost() * mAppUnitsPerDevUnit;
     ClipPartialLigature(this, &left, &right, aPt->x, &data);
 
     {
       // Need to preserve the path, otherwise this can break canvas text-on-path;
       // in general it seems like a good thing, as naive callers probably won't
       // expect gfxTextRun::Draw to implicitly destroy the current path.
-      gfxContextPathAutoSaveRestore savePath(aCtx);
+      gfxContextPathAutoSaveRestore savePath(aParams.context);
 
       // use division here to ensure that when the rect is aligned on multiples
       // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
       // Also, make sure we snap the rectangle to device pixels.
-      aCtx->Save();
-      aCtx->NewPath();
-      aCtx->Rectangle(gfxRect(left / mAppUnitsPerDevUnit,
-                              clipExtents.Y(),
-                              (right - left) / mAppUnitsPerDevUnit,
-                              clipExtents.Height()), true);
-      aCtx->Clip();
-    }
-
-    gfxFloat direction = GetDirection();
-    gfxPoint pt(aPt->x - direction*data.mPartAdvance, aPt->y);
-    DrawGlyphs(aFont, aCtx,
-               aCallbacks ? DrawMode::GLYPH_PATH : DrawMode::GLYPH_FILL, &pt,
-               nullptr, data.mLigatureStart, data.mLigatureEnd, aProvider,
-               aStart, aEnd, aCallbacks);
-    aCtx->Restore();
-
-    aPt->x += direction*data.mPartWidth;
+      aParams.context->Save();
+      aParams.context->NewPath();
+      aParams.context->Rectangle(gfxRect(left / mAppUnitsPerDevUnit,
+                                         clipExtents.Y(),
+                                         (right - left) / mAppUnitsPerDevUnit,
+                                         clipExtents.Height()), true);
+      aParams.context->Clip();
+    }
+
+    gfxPoint pt(aPt->x - aParams.direction * data.mPartAdvance, aPt->y);
+    DrawGlyphs(aFont, data.mLigatureStart, data.mLigatureEnd, &pt,
+               aProvider, aStart, aEnd, aParams);
+    aParams.context->Restore();
+
+    aPt->x += aParams.direction * data.mPartWidth;
 }
 
 // returns true if a glyph run is using a font with synthetic bolding enabled, false otherwise
 static bool
 HasSyntheticBold(gfxTextRun *aRun, uint32_t aStart, uint32_t aLength)
 {
     gfxTextRun::GlyphRunIterator iter(aRun, aStart, aLength);
     while (iter.NextRun()) {
@@ -7191,19 +7060,21 @@ struct BufferAlphaColor {
 void
 gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode,
                  uint32_t aStart, uint32_t aLength,
                  PropertyProvider *aProvider, gfxFloat *aAdvanceWidth,
                  gfxTextContextPaint *aContextPaint,
                  gfxTextRunDrawCallbacks *aCallbacks)
 {
     NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
-    NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)),
+    NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH ||
+                 !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)),
                  "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
-    NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !aCallbacks, "callback must not be specified unless using GLYPH_PATH");
+    NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !aCallbacks,
+                 "callback must not be specified unless using GLYPH_PATH");
 
     bool skipDrawing = mSkipDrawing;
     if (aDrawMode == DrawMode::GLYPH_FILL) {
         gfxRGBA currentColor;
         if (aContext->GetDeviceColor(currentColor) && currentColor.a == 0) {
             skipDrawing = true;
         }
     }
@@ -7219,57 +7090,73 @@ gfxTextRun::Draw(gfxContext *aContext, g
                                                       aContext, aProvider);
             *aAdvanceWidth = metrics.mAdvanceWidth * direction;
         }
 
         // return without drawing
         return;
     }
 
+    // Set up parameters that will be constant across all glyph runs we need
+    // to draw, regardless of the font used.
+    TextRunDrawParams params;
+    params.context = aContext;
+    params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit());
+    params.isRTL = IsRightToLeft();
+    params.direction = direction;
+    params.drawMode = aDrawMode;
+    params.callbacks = aCallbacks;
+    params.contextPaint = aContextPaint;
+    params.paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs;
+    params.dt = aContext->GetDrawTarget();
+
     gfxPoint pt = aPt;
 
-    // synthetic bolding draws glyphs twice ==> colors with opacity won't draw correctly unless first drawn without alpha
+    // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
+    // correctly unless first drawn without alpha
     BufferAlphaColor syntheticBoldBuffer(aContext);
     gfxRGBA currentColor;
     bool needToRestore = false;
 
-    if (aDrawMode == DrawMode::GLYPH_FILL && HasNonOpaqueColor(aContext, currentColor)
-                                          && HasSyntheticBold(this, aStart, aLength)) {
+    if (aDrawMode == DrawMode::GLYPH_FILL &&
+        HasNonOpaqueColor(aContext, currentColor) &&
+        HasSyntheticBold(this, aStart, aLength)) {
         needToRestore = true;
         // measure text, use the bounding box
-        gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, gfxFont::LOOSE_INK_EXTENTS,
+        gfxTextRun::Metrics metrics = MeasureText(aStart, aLength,
+                                                  gfxFont::LOOSE_INK_EXTENTS,
                                                   aContext, aProvider);
         metrics.mBoundingBox.MoveBy(aPt);
-        syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor, GetAppUnitsPerDevUnit());
+        syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor,
+                                           GetAppUnitsPerDevUnit());
     }
 
     GlyphRunIterator iter(this, aStart, aLength);
     while (iter.NextRun()) {
         gfxFont *font = iter.GetGlyphRun()->mFont;
         uint32_t start = iter.GetStringStart();
         uint32_t end = iter.GetStringEnd();
         uint32_t ligatureRunStart = start;
         uint32_t ligatureRunEnd = end;
         ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
-        
+
         bool drawPartial = aDrawMode == DrawMode::GLYPH_FILL ||
                            (aDrawMode == DrawMode::GLYPH_PATH && aCallbacks);
 
         if (drawPartial) {
-            DrawPartialLigature(font, aContext, start, ligatureRunStart, &pt,
-                                aProvider, aCallbacks);
-        }
-
-        DrawGlyphs(font, aContext, aDrawMode, &pt, aContextPaint, ligatureRunStart,
-                   ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd,
-                   aCallbacks);
+            DrawPartialLigature(font, start, ligatureRunStart, &pt,
+                                aProvider, params);
+        }
+
+        DrawGlyphs(font, ligatureRunStart, ligatureRunEnd, &pt,
+                   aProvider, ligatureRunStart, ligatureRunEnd, params);
 
         if (drawPartial) {
-            DrawPartialLigature(font, aContext, ligatureRunEnd, end, &pt,
-                                aProvider, aCallbacks);
+            DrawPartialLigature(font, ligatureRunEnd, end, &pt,
+                                aProvider, params);
         }
     }
 
     // composite result when synthetic bolding used
     if (needToRestore) {
         syntheticBoldBuffer.PopAlpha();
     }
 
--- a/gfx/thebes/gfxFont.h
+++ b/gfx/thebes/gfxFont.h
@@ -1522,17 +1522,21 @@ public:
                       bool aAddSmallCaps,
                       nsDataHashtable<nsUint32HashKey,uint32_t>& aMergedFeatures);
 
 protected:
     // the font this shaper is working with
     gfxFont * mFont;
 };
 
-/* a SPECIFIC single font family */
+
+class GlyphBufferAzure;
+struct TextRunDrawParams;
+struct FontDrawParams;
+
 class gfxFont {
 
     friend class gfxHarfBuzzShaper;
     friend class gfxGraphiteShaper;
 
 public:
     nsrefcnt AddRef(void) {
         NS_PRECONDITION(int32_t(mRefCnt) >= 0, "illegal refcnt");
@@ -1624,17 +1628,17 @@ public:
 
     virtual cairo_scaled_font_t* GetCairoScaledFont() { return mScaledFont; }
 
     virtual gfxFont* CopyWithAntialiasOption(AntialiasOption anAAOption) {
         // platforms where this actually matters should override
         return nullptr;
     }
 
-    virtual gfxFloat GetAdjustedSize() {
+    virtual gfxFloat GetAdjustedSize() const {
         return mAdjustedSize > 0.0 ? mAdjustedSize : mStyle.size;
     }
 
     float FUnitsToDevUnitsFactor() const {
         // check this was set up during font initialization
         NS_ASSERTION(mFUnitsConvFactor > 0.0f, "mFUnitsConvFactor not valid");
         return mFUnitsConvFactor;
     }
@@ -1759,41 +1763,40 @@ public:
         gfxRect  mBoundingBox;
     };
 
     /**
      * Draw a series of glyphs to aContext. The direction of aTextRun must
      * be honoured.
      * @param aStart the first character to draw
      * @param aEnd draw characters up to here
-     * @param aBaselineOrigin the baseline origin; the left end of the baseline
-     * for LTR textruns, the right end of the baseline for RTL textruns. On return,
-     * this should be updated to the other end of the baseline. In application
-     * units, really!
-     * @param aSpacing spacing to insert before and after characters (for RTL
-     * glyphs, before-spacing is inserted to the right of characters). There
-     * are aEnd - aStart elements in this array, unless it's null to indicate
-     * that there is no spacing.
-     * @param aDrawMode specifies whether the fill or stroke of the glyph should be
-     * drawn, or if it should be drawn into the current path
-     * @param aContextPaint information about how to construct the fill and
-     * stroke pattern. Can be nullptr if we are not stroking the text, which
-     * indicates that the current source from aContext should be used for filling
-     * 
+     * @param aPt the baseline origin; the left end of the baseline
+     * for LTR textruns, the right end for RTL textruns.
+     * On return, this will be updated to the other end of the baseline.
+     * In application units, really!
+     * @param aParams record with drawing parameters, see TextRunDrawParams.
+     * Particular fields of interest include
+     * .spacing  spacing to insert before and after characters (for RTL
+     *   glyphs, before-spacing is inserted to the right of characters). There
+     *   are aEnd - aStart elements in this array, unless it's null to indicate
+     *   that there is no spacing.
+     * .drawMode  specifies whether the fill or stroke of the glyph should be
+     *   drawn, or if it should be drawn into the current path
+     * .contextPaint  information about how to construct the fill and
+     *   stroke pattern. Can be nullptr if we are not stroking the text, which
+     *   indicates that the current source from context should be used for fill
+     * .context  the Thebes graphics context to which we're drawing
+     * .dt  Moz2D DrawTarget to which we're drawing
+     *
      * Callers guarantee:
      * -- aStart and aEnd are aligned to cluster and ligature boundaries
      * -- all glyphs use this font
-     * 
-     * The default implementation builds a cairo glyph array and
-     * calls cairo_show_glyphs or cairo_glyph_path.
      */
-    virtual void Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
-                      gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aBaselineOrigin,
-                      Spacing *aSpacing, gfxTextContextPaint *aContextPaint,
-                      gfxTextRunDrawCallbacks *aCallbacks);
+    void Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
+              gfxPoint *aPt, TextRunDrawParams& aParams);
 
     /**
      * Measure a run of characters. See gfxTextRun::Metrics.
      * @param aTight if false, then return the union of the glyph extents
      * with the font-box for the characters (the rectangle with x=0,width=
      * the advance width for the character run,y=-(font ascent), and height=
      * font ascent + font descent). Otherwise, we must return as tight as possible
      * an approximation to the area actually painted by glyphs.
@@ -1850,17 +1853,17 @@ public:
     // simply use (S / T).
     gfxFloat GetSyntheticBoldOffset() {
         gfxFloat size = GetAdjustedSize();
         const gfxFloat threshold = 48.0;
         return size < threshold ? (0.25 + 0.75 * size / threshold) :
                                   (size / threshold);
     }
 
-    gfxFontEntry *GetFontEntry() { return mFontEntry.get(); }
+    gfxFontEntry *GetFontEntry() const { return mFontEntry.get(); }
     bool HasCharacter(uint32_t ch) {
         if (!mIsValid)
             return false;
         return mFontEntry->HasCharacter(ch); 
     }
 
     uint16_t GetUVSGlyph(uint32_t aCh, uint32_t aVS) {
         if (!mIsValid) {
@@ -2014,16 +2017,35 @@ public:
         return mFontEntry->GetMathConstant(aConstant);
     }
 
     // return a cloned font resized and offset to simulate sub/superscript glyphs
     virtual already_AddRefed<gfxFont>
     GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel);
 
 protected:
+    // Output a single glyph at *aPt, which is updated by the glyph's advance.
+    // Normal glyphs are simply accumulated in aBuffer until it is full and
+    // gets flushed, but SVG or color-font glyphs will instead be rendered
+    // directly to the destination (found from the buffer's parameters).
+    void DrawOneGlyph(uint32_t           aGlyphID,
+                      double             aAdvance,
+                      gfxPoint          *aPt,
+                      GlyphBufferAzure&  aBuffer,
+                      bool              *aEmittedGlyphs) const;
+
+    // Output a run of glyphs at *aPt, which is updated to follow the last glyph
+    // in the run. This method also takes account of any letter-spacing provided
+    // in aRunParams.
+    bool DrawGlyphs(gfxShapedText            *aShapedText,
+                    uint32_t                  aOffset, // offset in the textrun
+                    uint32_t                  aCount, // length of run to draw
+                    gfxPoint                 *aPt,
+                    const TextRunDrawParams&  aRunParams,
+                    const FontDrawParams&     aFontParams);
 
     // set the font size and offset used for
     // synthetic subscript/superscript glyphs
     void CalculateSubSuperSizeAndOffset(int32_t aAppUnitsPerDevPixel,
                                         gfxFloat& aSubSuperSizeRatio,
                                         float& aBaselineOffset);
 
     // Return a font that is a "clone" of this one, but reduced to 80% size
@@ -2251,29 +2273,28 @@ protected:
     // InitMetricsFromSfntTables or equivalent platform code
     void CalculateDerivedMetrics(Metrics& aMetrics);
 
     // some fonts have bad metrics, this method sanitize them.
     // if this font has bad underline offset, aIsBadUnderlineFont should be true.
     void SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont);
 
     bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
-                        uint32_t aGlyphId, gfxTextContextPaint *aContextPaint);
+                        uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) const;
     bool RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
                         uint32_t aGlyphId, gfxTextContextPaint *aContextPaint,
                         gfxTextRunDrawCallbacks *aCallbacks,
-                        bool& aEmittedGlyphs);
-
-    bool RenderColorGlyph(gfxContext* aContext, gfxPoint& point, uint32_t aGlyphId);
+                        bool& aEmittedGlyphs) const;
+
     bool RenderColorGlyph(gfxContext* aContext,
                           mozilla::gfx::ScaledFont* scaledFont,
                           mozilla::gfx::GlyphRenderingOptions* renderingOptions,
                           mozilla::gfx::DrawOptions drawOptions,
                           const mozilla::gfx::Point& aPoint,
-                          uint32_t aGlyphId);
+                          uint32_t aGlyphId) const;
 
     // Bug 674909. When synthetic bolding text by drawing twice, need to
     // render using a pixel offset in device pixels, otherwise text
     // doesn't appear bolded, it appears as if a bad text shadow exists
     // when a non-identity transform exists.  Use an offset factor so that
     // the second draw occurs at a constant offset in device pixels.
     // This helper calculates the scale factor we need to apply to the
     // synthetic-bold offset.
@@ -3533,20 +3554,19 @@ private:
     // (Platforms do the actual ligaturization, but we need to do a bunch of stuff
     // to handle requests that begin or end inside a ligature)
 
     // if aProvider is null then mBeforeSpacing and mAfterSpacing are set to zero
     LigatureData ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
                                      PropertyProvider *aProvider);
     gfxFloat ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd,
                                          PropertyProvider *aProvider);
-    void DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx,
-                             uint32_t aStart, uint32_t aEnd, gfxPoint *aPt,
-                             PropertyProvider *aProvider,
-                             gfxTextRunDrawCallbacks *aCallbacks);
+    void DrawPartialLigature(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
+                             gfxPoint *aPt, PropertyProvider *aProvider,
+                             TextRunDrawParams& aParams);
     // Advance aStart to the start of the nearest ligature; back up aEnd
     // to the nearest ligature end; may result in *aStart == *aEnd
     void ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd);
     // result in appunits
     gfxFloat GetPartialLigatureWidth(uint32_t aStart, uint32_t aEnd, PropertyProvider *aProvider);
     void AccumulatePartialLigatureMetrics(gfxFont *aFont,
                                           uint32_t aStart, uint32_t aEnd,
                                           gfxFont::BoundingBoxType aBoundingBoxType,
@@ -3558,22 +3578,20 @@ private:
     void AccumulateMetricsForRun(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
                                  gfxFont::BoundingBoxType aBoundingBoxType,
                                  gfxContext *aRefContext,
                                  PropertyProvider *aProvider,
                                  uint32_t aSpacingStart, uint32_t aSpacingEnd,
                                  Metrics *aMetrics);
 
     // **** drawing helper ****
-    void DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
-                    DrawMode aDrawMode, gfxPoint *aPt,
-                    gfxTextContextPaint *aContextPaint, uint32_t aStart,
-                    uint32_t aEnd, PropertyProvider *aProvider,
+    void DrawGlyphs(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
+                    gfxPoint *aPt, PropertyProvider *aProvider,
                     uint32_t aSpacingStart, uint32_t aSpacingEnd,
-                    gfxTextRunDrawCallbacks *aCallbacks);
+                    TextRunDrawParams& aParams);
 
     // XXX this should be changed to a GlyphRun plus a maybe-null GlyphRun*,
     // for smaller size especially in the super-common one-glyphrun case
     nsAutoTArray<GlyphRun,1>        mGlyphRuns;
 
     void             *mUserData;
     gfxFontGroup     *mFontGroup; // addrefed on creation, but our reference
                                   // may be released by ReleaseFontGroup()
--- a/intl/build/moz.build
+++ b/intl/build/moz.build
@@ -5,15 +5,15 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SOURCES += [
     'nsI18nModule.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
-    '../locale/src',
-    '../lwbrk/src',
-    '../strres/src',
-    '../uconv/src',
-    '../unicharutil/src',
+    '../locale',
+    '../lwbrk',
+    '../strres',
+    '../uconv',
+    '../unicharutil',
 ]
 
--- a/intl/chardet/moz.build
+++ b/intl/chardet/moz.build
@@ -1,7 +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/.
 
-DIRS += ['public', 'src']
+EXPORTS += [
+    'nsDetectionConfident.h',
+    'nsICharsetDetectionObserver.h',
+    'nsICharsetDetector.h',
+    'nsIStringCharsetDetector.h',
+]
+
+UNIFIED_SOURCES += [
+    'nsChardetModule.cpp',
+    'nsCyrillicDetector.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
rename from intl/chardet/src/nsCharDetConstructors.h
rename to intl/chardet/nsCharDetConstructors.h
rename from intl/chardet/src/nsChardetModule.cpp
rename to intl/chardet/nsChardetModule.cpp
rename from intl/chardet/src/nsCyrillicClass.h
rename to intl/chardet/nsCyrillicClass.h
rename from intl/chardet/src/nsCyrillicDetector.cpp
rename to intl/chardet/nsCyrillicDetector.cpp
rename from intl/chardet/src/nsCyrillicDetector.h
rename to intl/chardet/nsCyrillicDetector.h
rename from intl/chardet/src/nsCyrillicProb.h
rename to intl/chardet/nsCyrillicProb.h
rename from intl/chardet/public/nsDetectionConfident.h
rename to intl/chardet/nsDetectionConfident.h
rename from intl/chardet/public/nsICharsetDetectionObserver.h
rename to intl/chardet/nsICharsetDetectionObserver.h
rename from intl/chardet/public/nsICharsetDetector.h
rename to intl/chardet/nsICharsetDetector.h
rename from intl/chardet/public/nsIStringCharsetDetector.h
rename to intl/chardet/nsIStringCharsetDetector.h
deleted file mode 100644
--- a/intl/chardet/public/moz.build
+++ /dev/null
@@ -1,13 +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/.
-
-EXPORTS += [
-    'nsDetectionConfident.h',
-    'nsICharsetDetectionObserver.h',
-    'nsICharsetDetector.h',
-    'nsIStringCharsetDetector.h',
-]
-
deleted file mode 100644
--- a/intl/chardet/src/moz.build
+++ /dev/null
@@ -1,12 +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/.
-
-UNIFIED_SOURCES += [
-    'nsChardetModule.cpp',
-    'nsCyrillicDetector.cpp',
-]
-
-FINAL_LIBRARY = 'xul'
rename from intl/hyphenation/src/COPYING
rename to intl/hyphenation/COPYING
rename from intl/hyphenation/src/COPYING.LGPL
rename to intl/hyphenation/COPYING.LGPL
rename from intl/hyphenation/src/COPYING.MPL
rename to intl/hyphenation/COPYING.MPL
rename from intl/hyphenation/src/README
rename to intl/hyphenation/README
rename from intl/hyphenation/src/README.compound
rename to intl/hyphenation/README.compound
rename from intl/hyphenation/src/README.hyphen
rename to intl/hyphenation/README.hyphen
rename from intl/hyphenation/src/README.mozilla
rename to intl/hyphenation/README.mozilla
rename from intl/hyphenation/src/README.nonstandard
rename to intl/hyphenation/README.nonstandard
rename from intl/hyphenation/src/hnjalloc.h
rename to intl/hyphenation/hnjalloc.h
rename from intl/hyphenation/src/hnjstdio.cpp
rename to intl/hyphenation/hnjstdio.cpp
rename from intl/hyphenation/src/hyphen.c
rename to intl/hyphenation/hyphen.c
rename from intl/hyphenation/src/hyphen.h
rename to intl/hyphenation/hyphen.h
--- a/intl/hyphenation/moz.build
+++ b/intl/hyphenation/moz.build
@@ -1,8 +1,32 @@
 # -*- 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 += ['public', 'src']
+EXPORTS += [
+    'nsHyphenationManager.h',
+    'nsHyphenator.h',
+]
+
+UNIFIED_SOURCES += [
+    'nsHyphenationManager.cpp',
+    'nsHyphenator.cpp',
+]
 
+# These files cannot be built in unified mode because they include hnjalloc.h.
+SOURCES += [
+    'hnjstdio.cpp',
+    'hyphen.c',
+]
+
+MSVC_ENABLE_PGO = True
+
+FINAL_LIBRARY = 'xul'
+
+# Suppress warnings in third-party code.
+if CONFIG['GNU_CC']:
+    CFLAGS += [
+        '-Wno-sign-compare',
+        '-Wno-type-limits',
+    ]
rename from intl/hyphenation/src/nsHyphenationManager.cpp
rename to intl/hyphenation/nsHyphenationManager.cpp
rename from intl/hyphenation/public/nsHyphenationManager.h
rename to intl/hyphenation/nsHyphenationManager.h
rename from intl/hyphenation/src/nsHyphenator.cpp
rename to intl/hyphenation/nsHyphenator.cpp
rename from intl/hyphenation/public/nsHyphenator.h
rename to intl/hyphenation/nsHyphenator.h
deleted file mode 100644
--- a/intl/hyphenation/public/moz.build
+++ /dev/null
@@ -1,11 +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/.
-
-EXPORTS += [
-    'nsHyphenationManager.h',
-    'nsHyphenator.h',
-]
-
deleted file mode 100644
--- a/intl/hyphenation/src/moz.build
+++ /dev/null
@@ -1,27 +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/.
-
-UNIFIED_SOURCES += [
-    'nsHyphenationManager.cpp',
-    'nsHyphenator.cpp',
-]
-
-# These files cannot be built in unified mode because they include hnjalloc.h.
-SOURCES += [
-    'hnjstdio.cpp',
-    'hyphen.c',
-]
-
-MSVC_ENABLE_PGO = True
-
-FINAL_LIBRARY = 'xul'
-
-# Suppress warnings in third-party code.
-if CONFIG['GNU_CC']:
-    CFLAGS += [
-        '-Wno-sign-compare',
-        '-Wno-type-limits',
-    ]
rename from intl/locale/src/Makefile.in
rename to intl/locale/Makefile.in
rename from intl/locale/src/PluralForm.jsm
rename to intl/locale/PluralForm.jsm
deleted file mode 100644
--- a/intl/locale/idl/moz.build
+++ /dev/null
@@ -1,15 +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/.
-
-XPIDL_SOURCES += [
-    'nsICollation.idl',
-    'nsILocale.idl',
-    'nsILocaleService.idl',
-    'nsIScriptableDateFormat.idl',
-]
-
-XPIDL_MODULE = 'locale'
-
rename from intl/locale/src/langGroups.properties
rename to intl/locale/langGroups.properties
rename from intl/locale/src/language.properties
rename to intl/locale/language.properties
rename from intl/locale/src/mac/moz.build
rename to intl/locale/mac/moz.build
rename from intl/locale/src/mac/nsCollationMacUC.cpp
rename to intl/locale/mac/nsCollationMacUC.cpp
rename from intl/locale/src/mac/nsCollationMacUC.h
rename to intl/locale/mac/nsCollationMacUC.h
rename from intl/locale/src/mac/nsDateTimeFormatMac.cpp
rename to intl/locale/mac/nsDateTimeFormatMac.cpp
rename from intl/locale/src/mac/nsDateTimeFormatMac.h
rename to intl/locale/mac/nsDateTimeFormatMac.h
rename from intl/locale/src/mac/nsMacCharset.cpp
rename to intl/locale/mac/nsMacCharset.cpp
--- a/intl/locale/moz.build
+++ b/intl/locale/moz.build
@@ -1,9 +1,62 @@
 # -*- 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 += ['public', 'idl', 'src']
 TEST_DIRS += ['tests']
 
+toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
+
+if toolkit == 'windows':
+    DIRS += ['windows']
+elif toolkit == 'cocoa':
+    DIRS += ['mac']
+else:
+    DIRS += ['unix']
+
+XPIDL_SOURCES += [
+    'nsICollation.idl',
+    'nsILocale.idl',
+    'nsILocaleService.idl',
+    'nsIScriptableDateFormat.idl',
+]
+
+XPIDL_MODULE = 'locale'
+
+EXPORTS += [
+    'nsCollation.h',
+    'nsCollationCID.h',
+    'nsDateTimeFormatCID.h',
+    'nsIDateTimeFormat.h',
+    'nsILanguageAtomService.h',
+    'nsIPlatformCharset.h',
+    'nsPosixLocale.h',
+    'nsWin32Locale.h',
+]
+
+UNIFIED_SOURCES += [
+    'nsCollation.cpp',
+    'nsLanguageAtomService.cpp',
+    'nsLocale.cpp',
+    'nsLocaleService.cpp',
+    'nsScriptableDateFormat.cpp',
+    'nsUConvPropertySearch.cpp',
+]
+
+EXTRA_JS_MODULES += [
+    'PluralForm.jsm',
+]
+
+MSVC_ENABLE_PGO = True
+
+FINAL_LIBRARY = 'xul'
+
+LOCAL_INCLUDES += [
+    '/intl/uconv',
+]
+
+RESOURCE_FILES += [
+    'langGroups.properties',
+    'language.properties',
+]
rename from intl/locale/src/nsCollation.cpp
rename to intl/locale/nsCollation.cpp
rename from intl/locale/src/nsCollation.h
rename to intl/locale/nsCollation.h
rename from intl/locale/public/nsCollationCID.h
rename to intl/locale/nsCollationCID.h
rename from intl/locale/public/nsDateTimeFormatCID.h
rename to intl/locale/nsDateTimeFormatCID.h
rename from intl/locale/idl/nsICollation.idl
rename to intl/locale/nsICollation.idl
rename from intl/locale/public/nsIDateTimeFormat.h
rename to intl/locale/nsIDateTimeFormat.h
rename from intl/locale/public/nsILanguageAtomService.h
rename to intl/locale/nsILanguageAtomService.h
rename from intl/locale/idl/nsILocale.idl
rename to intl/locale/nsILocale.idl
rename from intl/locale/idl/nsILocaleService.idl
rename to intl/locale/nsILocaleService.idl
rename from intl/locale/public/nsIPlatformCharset.h
rename to intl/locale/nsIPlatformCharset.h
rename from intl/locale/idl/nsIScriptableDateFormat.idl
rename to intl/locale/nsIScriptableDateFormat.idl
rename from intl/locale/src/nsLanguageAtomService.cpp
rename to intl/locale/nsLanguageAtomService.cpp
rename from intl/locale/src/nsLanguageAtomService.h
rename to intl/locale/nsLanguageAtomService.h
rename from intl/locale/src/nsLocale.cpp
rename to intl/locale/nsLocale.cpp
rename from intl/locale/src/nsLocale.h
rename to intl/locale/nsLocale.h
rename from intl/locale/src/nsLocaleConstructors.h
rename to intl/locale/nsLocaleConstructors.h
rename from intl/locale/src/nsLocaleService.cpp
rename to intl/locale/nsLocaleService.cpp
rename from intl/locale/src/nsPlatformCharset.h
rename to intl/locale/nsPlatformCharset.h
rename from intl/locale/public/nsPosixLocale.h
rename to intl/locale/nsPosixLocale.h
rename from intl/locale/src/nsScriptableDateFormat.cpp
rename to intl/locale/nsScriptableDateFormat.cpp
rename from intl/locale/src/nsUConvPropertySearch.cpp
rename to intl/locale/nsUConvPropertySearch.cpp
rename from intl/locale/src/nsUConvPropertySearch.h
rename to intl/locale/nsUConvPropertySearch.h
rename from intl/locale/public/nsWin32Locale.h
rename to intl/locale/nsWin32Locale.h
rename from intl/locale/src/props2arrays.py
rename to intl/locale/props2arrays.py
deleted file mode 100644
--- a/intl/locale/public/moz.build
+++ /dev/null
@@ -1,16 +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/.
-
-EXPORTS += [
-    'nsCollationCID.h',
-    'nsDateTimeFormatCID.h',
-    'nsIDateTimeFormat.h',
-    'nsILanguageAtomService.h',
-    'nsIPlatformCharset.h',
-    'nsPosixLocale.h',
-    'nsWin32Locale.h',
-]
-
deleted file mode 100644
--- a/intl/locale/src/moz.build
+++ /dev/null
@@ -1,44 +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/.
-
-toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
-
-if toolkit == 'windows':
-    DIRS += ['windows']
-elif toolkit == 'cocoa':
-    DIRS += ['mac']
-else:
-    DIRS += ['unix']
-
-EXPORTS += [
-    'nsCollation.h',
-]
-
-UNIFIED_SOURCES += [
-    'nsCollation.cpp',
-    'nsLanguageAtomService.cpp',
-    'nsLocale.cpp',
-    'nsLocaleService.cpp',
-    'nsScriptableDateFormat.cpp',
-    'nsUConvPropertySearch.cpp',
-]
-
-EXTRA_JS_MODULES += [
-    'PluralForm.jsm',
-]
-
-MSVC_ENABLE_PGO = True
-
-FINAL_LIBRARY = 'xul'
-
-LOCAL_INCLUDES += [
-    '/intl/uconv/src',
-]
-
-RESOURCE_FILES += [
-    'langGroups.properties',
-    'language.properties',
-]
rename from intl/locale/src/unix/Makefile.in
rename to intl/locale/unix/Makefile.in
rename from intl/locale/src/unix/moz.build
rename to intl/locale/unix/moz.build
rename from intl/locale/src/unix/nsAndroidCharset.cpp
rename to intl/locale/unix/nsAndroidCharset.cpp
rename from intl/locale/src/unix/nsCollationUnix.cpp
rename to intl/locale/unix/nsCollationUnix.cpp
rename from intl/locale/src/unix/nsCollationUnix.h
rename to intl/locale/unix/nsCollationUnix.h
rename from intl/locale/src/unix/nsDateTimeFormatUnix.cpp
rename to intl/locale/unix/nsDateTimeFormatUnix.cpp
rename from intl/locale/src/unix/nsDateTimeFormatUnix.h
rename to intl/locale/unix/nsDateTimeFormatUnix.h
rename from intl/locale/src/unix/nsPosixLocale.cpp
rename to intl/locale/unix/nsPosixLocale.cpp
rename from intl/locale/src/unix/nsUNIXCharset.cpp
rename to intl/locale/unix/nsUNIXCharset.cpp
rename from intl/locale/src/unix/unixcharset.properties
rename to intl/locale/unix/unixcharset.properties
rename from intl/locale/src/windows/Makefile.in
rename to intl/locale/windows/Makefile.in
rename from intl/locale/src/windows/moz.build
rename to intl/locale/windows/moz.build
rename from intl/locale/src/windows/nsCollationWin.cpp
rename to intl/locale/windows/nsCollationWin.cpp
rename from intl/locale/src/windows/nsCollationWin.h
rename to intl/locale/windows/nsCollationWin.h
rename from intl/locale/src/windows/nsDateTimeFormatWin.cpp
rename to intl/locale/windows/nsDateTimeFormatWin.cpp
rename from intl/locale/src/windows/nsDateTimeFormatWin.h
rename to intl/locale/windows/nsDateTimeFormatWin.h
rename from intl/locale/src/windows/nsWin32Locale.cpp
rename to intl/locale/windows/nsWin32Locale.cpp
rename from intl/locale/src/windows/nsWinCharset.cpp
rename to intl/locale/windows/nsWinCharset.cpp
rename from intl/locale/src/windows/wincharset.properties
rename to intl/locale/windows/wincharset.properties
rename from intl/lwbrk/src/Makefile.in
rename to intl/lwbrk/Makefile.in
rename from intl/lwbrk/src/crashtests/416721.html
rename to intl/lwbrk/crashtests/416721.html
rename from intl/lwbrk/src/crashtests/crashtests.list
rename to intl/lwbrk/crashtests/crashtests.list
deleted file mode 100644
--- a/intl/lwbrk/idl/moz.build
+++ /dev/null
@@ -1,12 +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/.
-
-XPIDL_SOURCES += [
-    'nsISemanticUnitScanner.idl',
-]
-
-XPIDL_MODULE = 'lwbrk'
-
rename from intl/lwbrk/src/jisx4051class.h
rename to intl/lwbrk/jisx4051class.h
rename from intl/lwbrk/src/jisx4051pairtable.txt
rename to intl/lwbrk/jisx4051pairtable.txt
--- a/intl/lwbrk/moz.build
+++ b/intl/lwbrk/moz.build
@@ -1,9 +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 += ['idl', 'public', 'src']
 TEST_TOOL_DIRS += ['tests']
 
+XPIDL_SOURCES += [
+    'nsISemanticUnitScanner.idl',
+]
+
+XPIDL_MODULE = 'lwbrk'
+
+EXPORTS += [
+    'nsILineBreaker.h',
+    'nsIWordBreaker.h',
+    'nsLWBrkCIID.h',
+]
+
+UNIFIED_SOURCES += [
+    'nsJISx4051LineBreaker.cpp',
+    'nsSampleWordBreaker.cpp',
+    'nsSemanticUnitScanner.cpp',
+]
+
+if CONFIG['MOZ_WIDGET_GTK']:
+    SOURCES += [
+        'nsPangoBreaker.cpp',
+    ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
+    SOURCES += [
+        'nsUniscribeBreaker.cpp',
+    ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+    UNIFIED_SOURCES += [
+        'nsCarbonBreaker.cpp',
+    ]
+else:
+    SOURCES += [
+        'nsRuleBreaker.cpp',
+    ]
+    SOURCES += [
+        'rulebrk.c',
+    ]
+
+MSVC_ENABLE_PGO = True
+
+FINAL_LIBRARY = 'xul'
rename from intl/lwbrk/src/nsCarbonBreaker.cpp
rename to intl/lwbrk/nsCarbonBreaker.cpp
rename from intl/lwbrk/src/nsComplexBreaker.h
rename to intl/lwbrk/nsComplexBreaker.h
rename from intl/lwbrk/public/nsILineBreaker.h
rename to intl/lwbrk/nsILineBreaker.h
rename from intl/lwbrk/idl/nsISemanticUnitScanner.idl
rename to intl/lwbrk/nsISemanticUnitScanner.idl
rename from intl/lwbrk/public/nsIWordBreaker.h
rename to intl/lwbrk/nsIWordBreaker.h
rename from intl/lwbrk/src/nsJISx4051LineBreaker.cpp
rename to intl/lwbrk/nsJISx4051LineBreaker.cpp
rename from intl/lwbrk/src/nsJISx4051LineBreaker.h
rename to intl/lwbrk/nsJISx4051LineBreaker.h
rename from intl/lwbrk/public/nsLWBrkCIID.h
rename to intl/lwbrk/nsLWBrkCIID.h
rename from intl/lwbrk/src/nsPangoBreaker.cpp
rename to intl/lwbrk/nsPangoBreaker.cpp
rename from intl/lwbrk/src/nsRuleBreaker.cpp
rename to intl/lwbrk/nsRuleBreaker.cpp
rename from intl/lwbrk/src/nsSampleWordBreaker.cpp
rename to intl/lwbrk/nsSampleWordBreaker.cpp
rename from intl/lwbrk/src/nsSampleWordBreaker.h
rename to intl/lwbrk/nsSampleWordBreaker.h
rename from intl/lwbrk/src/nsSemanticUnitScanner.cpp
rename to intl/lwbrk/nsSemanticUnitScanner.cpp
rename from intl/lwbrk/src/nsSemanticUnitScanner.h
rename to intl/lwbrk/nsSemanticUnitScanner.h
rename from intl/lwbrk/src/nsUniscribeBreaker.cpp
rename to intl/lwbrk/nsUniscribeBreaker.cpp
deleted file mode 100644
--- a/intl/lwbrk/public/moz.build
+++ /dev/null
@@ -1,12 +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/.
-
-EXPORTS += [
-    'nsILineBreaker.h',
-    'nsIWordBreaker.h',
-    'nsLWBrkCIID.h',
-]
-
rename from intl/lwbrk/src/rulebrk.c
rename to intl/lwbrk/rulebrk.c
rename from intl/lwbrk/src/rulebrk.h
rename to intl/lwbrk/rulebrk.h
deleted file mode 100644
--- a/intl/lwbrk/src/moz.build
+++ /dev/null
@@ -1,37 +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/.
-
-UNIFIED_SOURCES += [
-    'nsJISx4051LineBreaker.cpp',
-    'nsSampleWordBreaker.cpp',
-    'nsSemanticUnitScanner.cpp',
-]
-
-if CONFIG['MOZ_WIDGET_GTK']:
-    SOURCES += [
-        'nsPangoBreaker.cpp',
-    ]
-elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
-    SOURCES += [
-        'nsUniscribeBreaker.cpp',
-    ]
-elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
-    UNIFIED_SOURCES += [
-        'nsCarbonBreaker.cpp',
-    ]
-else:
-    SOURCES += [
-        'nsRuleBreaker.cpp',
-    ]
-    SOURCES += [
-        'rulebrk.c',
-    ]
-
-
-
-MSVC_ENABLE_PGO = True
-
-FINAL_LIBRARY = 'xul'
rename from intl/lwbrk/src/th_char.h
rename to intl/lwbrk/th_char.h
--- a/intl/strres/moz.build
+++ b/intl/strres/moz.build
@@ -1,9 +1,23 @@
 # -*- 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 += ['public', 'src']
 TEST_TOOL_DIRS += ['tests']
 
+XPIDL_SOURCES += [
+    'nsIStringBundle.idl',
+    'nsIStringBundleOverride.idl',
+]
+
+XPIDL_MODULE = 'intl'
+
+UNIFIED_SOURCES += [
+    'nsStringBundle.cpp',
+    'nsStringBundleTextOverride.cpp',
+]
+
+MSVC_ENABLE_PGO = True
+
+FINAL_LIBRARY = 'xul'
rename from intl/strres/public/nsIStringBundle.idl
rename to intl/strres/nsIStringBundle.idl
rename from intl/strres/public/nsIStringBundleOverride.idl
rename to intl/strres/nsIStringBundleOverride.idl
rename from intl/strres/src/nsStringBundle.cpp
rename to intl/strres/nsStringBundle.cpp
rename from intl/strres/src/nsStringBundle.h
rename to intl/strres/nsStringBundle.h
rename from intl/strres/src/nsStringBundleService.h
rename to intl/strres/nsStringBundleService.h
rename from intl/strres/src/nsStringBundleTextOverride.cpp
rename to intl/strres/nsStringBundleTextOverride.cpp
rename from intl/strres/src/nsStringBundleTextOverride.h
rename to intl/strres/nsStringBundleTextOverride.h
deleted file mode 100644
--- a/intl/strres/public/moz.build
+++ /dev/null
@@ -1,13 +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/.
-
-XPIDL_SOURCES += [
-    'nsIStringBundle.idl',
-    'nsIStringBundleOverride.idl',
-]
-
-XPIDL_MODULE = 'intl'
-
deleted file mode 100644
--- a/intl/strres/src/moz.build
+++ /dev/null
@@ -1,14 +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/.
-
-UNIFIED_SOURCES += [
-    'nsStringBundle.cpp',
-    'nsStringBundleTextOverride.cpp',
-]
-
-MSVC_ENABLE_PGO = True
-
-FINAL_LIBRARY = 'xul'
rename from intl/uconv/src/8859-1.uf
rename to intl/uconv/8859-1.uf
rename from intl/uconv/src/8859-1.ut
rename to intl/uconv/8859-1.ut
rename from intl/uconv/src/cp1252.uf
rename to intl/uconv/cp1252.uf
rename from intl/uconv/src/cp1252.ut
rename to intl/uconv/cp1252.ut
deleted file mode 100644
--- a/intl/uconv/idl/moz.build
+++ /dev/null
@@ -1,15 +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/.
-
-XPIDL_SOURCES += [
-    'nsICurrentCharsetListener.idl',
-    'nsIScriptableUConv.idl',
-    'nsITextToSubURI.idl',
-    'nsIUTF8ConverterService.idl',
-]
-
-XPIDL_MODULE = 'uconv'
-
rename from intl/uconv/src/macroman.uf
rename to intl/uconv/macroman.uf
rename from intl/uconv/src/macroman.ut
rename to intl/uconv/macroman.ut
--- a/intl/uconv/moz.build
+++ b/intl/uconv/moz.build
@@ -1,20 +1,226 @@
 # -*- 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 += [
-    'idl',
-    'public',
     'ucvja',
     'ucvcn',
     'ucvlatin',
     'ucvtw',
     'ucvko',
     'ucvibm',
-    'src',
 ]
 
 TEST_TOOL_DIRS += ['tests']
 
+XPIDL_SOURCES += [
+    'nsICurrentCharsetListener.idl',
+    'nsIScriptableUConv.idl',
+    'nsITextToSubURI.idl',
+    'nsIUTF8ConverterService.idl',
+]
+
+XPIDL_MODULE = 'uconv'
+
+EXPORTS += [
+    'nsEncoderDecoderUtils.h',
+    'nsIUnicodeDecoder.h',
+    'nsIUnicodeEncoder.h',
+    'nsUConvCID.h',
+    'nsUCSupport.h',
+    'uconvutil.h',
+]
+
+UNIFIED_SOURCES += [
+    'nsConverterInputStream.cpp',
+    'nsConverterOutputStream.cpp',
+    'nsCP1252ToUnicode.cpp',
+    'nsISO88591ToUnicode.cpp',
+    'nsMacRomanToUnicode.cpp',
+    'nsReplacementToUnicode.cpp',
+    'nsScriptableUConv.cpp',
+    'nsTextToSubURI.cpp',
+    'nsUConvModule.cpp',
+    'nsUnicodeToCP1252.cpp',
+    'nsUnicodeToISO88591.cpp',
+    'nsUnicodeToMacRoman.cpp',
+    'nsUnicodeToUTF8.cpp',
+    'nsUTF8ConverterService.cpp',
+    'nsUTF8ToUnicode.cpp',
+]
+
+UNIFIED_SOURCES += [
+    'ucvcn/nsGBKConvUtil.cpp',
+    'ucvcn/nsGBKToUnicode.cpp',
+    'ucvcn/nsHZToUnicode.cpp',
+    'ucvcn/nsUnicodeToGB2312V2.cpp',
+    'ucvcn/nsUnicodeToGBK.cpp',
+    'ucvcn/nsUnicodeToHZ.cpp',
+]
+
+UNIFIED_SOURCES += [
+    'ucvibm/nsCP850ToUnicode.cpp',
+    'ucvibm/nsCP852ToUnicode.cpp',
+    'ucvibm/nsCP855ToUnicode.cpp',
+    'ucvibm/nsCP857ToUnicode.cpp',
+    'ucvibm/nsCP862ToUnicode.cpp',
+    'ucvibm/nsCP864ToUnicode.cpp',
+    'ucvibm/nsUnicodeToCP850.cpp',
+    'ucvibm/nsUnicodeToCP852.cpp',
+    'ucvibm/nsUnicodeToCP855.cpp',
+    'ucvibm/nsUnicodeToCP857.cpp',
+    'ucvibm/nsUnicodeToCP862.cpp',
+    'ucvibm/nsUnicodeToCP864.cpp',
+]
+
+UNIFIED_SOURCES += [
+    'ucvja/nsJapaneseToUnicode.cpp',
+    'ucvja/nsUnicodeToEUCJP.cpp',
+    'ucvja/nsUnicodeToISO2022JP.cpp',
+    'ucvja/nsUnicodeToJISx0201.cpp',
+    'ucvja/nsUnicodeToSJIS.cpp',
+]
+
+UNIFIED_SOURCES += [
+    'ucvko/nsCP949ToUnicode.cpp',
+    'ucvko/nsUnicodeToCP949.cpp',
+]
+
+UNIFIED_SOURCES += [
+    'ucvlatin/nsARMSCII8ToUnicode.cpp',
+    'ucvlatin/nsAsciiToUnicode.cpp',
+    'ucvlatin/nsCP1250ToUnicode.cpp',
+    'ucvlatin/nsCP1251ToUnicode.cpp',
+    'ucvlatin/nsCP1253ToUnicode.cpp',
+    'ucvlatin/nsCP1254ToUnicode.cpp',
+    'ucvlatin/nsCP1255ToUnicode.cpp',
+    'ucvlatin/nsCP1256ToUnicode.cpp',
+    'ucvlatin/nsCP1257ToUnicode.cpp',
+    'ucvlatin/nsCP1258ToUnicode.cpp',
+    'ucvlatin/nsCP866ToUnicode.cpp',
+    'ucvlatin/nsCP874ToUnicode.cpp',
+    'ucvlatin/nsISO885910ToUnicode.cpp',
+    'ucvlatin/nsISO885911ToUnicode.cpp',
+    'ucvlatin/nsISO885913ToUnicode.cpp',
+    'ucvlatin/nsISO885914ToUnicode.cpp',
+    'ucvlatin/nsISO885915ToUnicode.cpp',
+    'ucvlatin/nsISO885916ToUnicode.cpp',
+    'ucvlatin/nsISO88592ToUnicode.cpp',
+    'ucvlatin/nsISO88593ToUnicode.cpp',
+    'ucvlatin/nsISO88594ToUnicode.cpp',
+    'ucvlatin/nsISO88595ToUnicode.cpp',
+    'ucvlatin/nsISO88596EToUnicode.cpp',
+    'ucvlatin/nsISO88596IToUnicode.cpp',
+    'ucvlatin/nsISO88596ToUnicode.cpp',
+    'ucvlatin/nsISO88597ToUnicode.cpp',
+    'ucvlatin/nsISO88598EToUnicode.cpp',
+    'ucvlatin/nsISO88598IToUnicode.cpp',
+    'ucvlatin/nsISO88598ToUnicode.cpp',
+    'ucvlatin/nsISO88599ToUnicode.cpp',
+    'ucvlatin/nsISOIR111ToUnicode.cpp',
+    'ucvlatin/nsKOI8RToUnicode.cpp',
+    'ucvlatin/nsKOI8UToUnicode.cpp',
+    'ucvlatin/nsMacArabicToUnicode.cpp',
+    'ucvlatin/nsMacCEToUnicode.cpp',
+    'ucvlatin/nsMacCroatianToUnicode.cpp',
+    'ucvlatin/nsMacCyrillicToUnicode.cpp',
+    'ucvlatin/nsMacDevanagariToUnicode.cpp',
+    'ucvlatin/nsMacFarsiToUnicode.cpp',
+    'ucvlatin/nsMacGreekToUnicode.cpp',
+    'ucvlatin/nsMacGujaratiToUnicode.cpp',
+    'ucvlatin/nsMacGurmukhiToUnicode.cpp',
+    'ucvlatin/nsMacHebrewToUnicode.cpp',
+    'ucvlatin/nsMacIcelandicToUnicode.cpp',
+    'ucvlatin/nsMacRomanianToUnicode.cpp',
+    'ucvlatin/nsMacTurkishToUnicode.cpp',
+    'ucvlatin/nsTIS620ToUnicode.cpp',
+    'ucvlatin/nsUnicodeToARMSCII8.cpp',
+    'ucvlatin/nsUnicodeToAscii.cpp',
+    'ucvlatin/nsUnicodeToCP1250.cpp',
+    'ucvlatin/nsUnicodeToCP1251.cpp',
+    'ucvlatin/nsUnicodeToCP1253.cpp',
+    'ucvlatin/nsUnicodeToCP1254.cpp',
+    'ucvlatin/nsUnicodeToCP1255.cpp',
+    'ucvlatin/nsUnicodeToCP1256.cpp',
+    'ucvlatin/nsUnicodeToCP1257.cpp',
+    'ucvlatin/nsUnicodeToCP1258.cpp',
+    'ucvlatin/nsUnicodeToCP866.cpp',
+    'ucvlatin/nsUnicodeToCP874.cpp',
+    'ucvlatin/nsUnicodeToISO885910.cpp',
+    'ucvlatin/nsUnicodeToISO885911.cpp',
+    'ucvlatin/nsUnicodeToISO885913.cpp',
+    'ucvlatin/nsUnicodeToISO885914.cpp',
+    'ucvlatin/nsUnicodeToISO885915.cpp',
+    'ucvlatin/nsUnicodeToISO885916.cpp',
+    'ucvlatin/nsUnicodeToISO88592.cpp',
+    'ucvlatin/nsUnicodeToISO88593.cpp',
+    'ucvlatin/nsUnicodeToISO88594.cpp',
+    'ucvlatin/nsUnicodeToISO88595.cpp',
+    'ucvlatin/nsUnicodeToISO88596.cpp',
+    'ucvlatin/nsUnicodeToISO88596E.cpp',
+    'ucvlatin/nsUnicodeToISO88596I.cpp',
+    'ucvlatin/nsUnicodeToISO88597.cpp',
+    'ucvlatin/nsUnicodeToISO88598.cpp',
+    'ucvlatin/nsUnicodeToISO88598E.cpp',
+    'ucvlatin/nsUnicodeToISO88598I.cpp',
+    'ucvlatin/nsUnicodeToISO88599.cpp',
+    'ucvlatin/nsUnicodeToISOIR111.cpp',
+    'ucvlatin/nsUnicodeToKOI8R.cpp',
+    'ucvlatin/nsUnicodeToKOI8U.cpp',
+    'ucvlatin/nsUnicodeToMacArabic.cpp',
+    'ucvlatin/nsUnicodeToMacCE.cpp',
+    'ucvlatin/nsUnicodeToMacCroatian.cpp',
+    'ucvlatin/nsUnicodeToMacCyrillic.cpp',
+    'ucvlatin/nsUnicodeToMacDevanagari.cpp',
+    'ucvlatin/nsUnicodeToMacFarsi.cpp',
+    'ucvlatin/nsUnicodeToMacGreek.cpp',
+    'ucvlatin/nsUnicodeToMacGujarati.cpp',
+    'ucvlatin/nsUnicodeToMacGurmukhi.cpp',
+    'ucvlatin/nsUnicodeToMacHebrew.cpp',
+    'ucvlatin/nsUnicodeToMacIcelandic.cpp',
+    'ucvlatin/nsUnicodeToMacRomanian.cpp',
+    'ucvlatin/nsUnicodeToMacTurkish.cpp',
+    'ucvlatin/nsUnicodeToTIS620.cpp',
+    'ucvlatin/nsUnicodeToUserDefined.cpp',
+    'ucvlatin/nsUnicodeToUTF16.cpp',
+    'ucvlatin/nsUserDefinedToUnicode.cpp',
+    'ucvlatin/nsUTF16ToUnicode.cpp',
+]
+
+UNIFIED_SOURCES += [
+    'ucvtw/nsBIG5HKSCSToUnicode.cpp',
+    'ucvtw/nsBIG5ToUnicode.cpp',
+    'ucvtw/nsUnicodeToBIG5.cpp',
+    'ucvtw/nsUnicodeToBIG5HKSCS.cpp',
+    'ucvtw/nsUnicodeToHKSCS.cpp',
+]
+
+UNIFIED_SOURCES += [
+    'util/nsUCConstructors.cpp',
+    'util/nsUCSupport.cpp',
+    'util/nsUnicodeDecodeHelper.cpp',
+    'util/nsUnicodeEncodeHelper.cpp',
+    'util/ugen.c',
+    'util/umap.c',
+    'util/uscan.c',
+]
+
+if CONFIG['INTEL_ARCHITECTURE']:
+    SOURCES += ['nsUTF8ToUnicodeSSE2.cpp']
+    SOURCES['nsUTF8ToUnicodeSSE2.cpp'].flags += CONFIG['SSE2_FLAGS']
+
+MSVC_ENABLE_PGO = True
+
+LOCAL_INCLUDES += [
+    'ucvcn',
+    'ucvibm',
+    'ucvja',
+    'ucvko',
+    'ucvlatin',
+    'ucvtw',
+    'util',
+]
+
+FINAL_LIBRARY = 'xul'
rename from intl/uconv/src/nsCP1252ToUnicode.cpp
rename to intl/uconv/nsCP1252ToUnicode.cpp
rename from intl/uconv/src/nsCP1252ToUnicode.h
rename to intl/uconv/nsCP1252ToUnicode.h
rename from intl/uconv/src/nsConverterInputStream.cpp
rename to intl/uconv/nsConverterInputStream.cpp
rename from intl/uconv/src/nsConverterInputStream.h
rename to intl/uconv/nsConverterInputStream.h
rename from intl/uconv/src/nsConverterOutputStream.cpp
rename to intl/uconv/nsConverterOutputStream.cpp
rename from intl/uconv/src/nsConverterOutputStream.h
rename to intl/uconv/nsConverterOutputStream.h
rename from intl/uconv/public/nsEncoderDecoderUtils.h
rename to intl/uconv/nsEncoderDecoderUtils.h
rename from intl/uconv/idl/nsICurrentCharsetListener.idl
rename to intl/uconv/nsICurrentCharsetListener.idl
rename from intl/uconv/src/nsISO88591ToUnicode.cpp
rename to intl/uconv/nsISO88591ToUnicode.cpp
rename from intl/uconv/src/nsISO88591ToUnicode.h
rename to intl/uconv/nsISO88591ToUnicode.h
rename from intl/uconv/idl/nsIScriptableUConv.idl
rename to intl/uconv/nsIScriptableUConv.idl
rename from intl/uconv/idl/nsITextToSubURI.idl
rename to intl/uconv/nsITextToSubURI.idl
rename from intl/uconv/idl/nsIUTF8ConverterService.idl
rename to intl/uconv/nsIUTF8ConverterService.idl
rename from intl/uconv/public/nsIUnicodeDecoder.h
rename to intl/uconv/nsIUnicodeDecoder.h
rename from intl/uconv/public/nsIUnicodeEncoder.h
rename to intl/uconv/nsIUnicodeEncoder.h
rename from intl/uconv/src/nsMacRomanToUnicode.cpp
rename to intl/uconv/nsMacRomanToUnicode.cpp
rename from intl/uconv/src/nsMacRomanToUnicode.h
rename to intl/uconv/nsMacRomanToUnicode.h
rename from intl/uconv/src/nsReplacementToUnicode.cpp
rename to intl/uconv/nsReplacementToUnicode.cpp
rename from intl/uconv/src/nsReplacementToUnicode.h
rename to intl/uconv/nsReplacementToUnicode.h
rename from intl/uconv/src/nsScriptableUConv.cpp
rename to intl/uconv/nsScriptableUConv.cpp
rename from intl/uconv/src/nsScriptableUConv.h
rename to intl/uconv/nsScriptableUConv.h
rename from intl/uconv/src/nsTextToSubURI.cpp
rename to intl/uconv/nsTextToSubURI.cpp
rename from intl/uconv/src/nsTextToSubURI.h
rename to intl/uconv/nsTextToSubURI.h
rename from intl/uconv/public/nsUCSupport.h
rename to intl/uconv/nsUCSupport.h
rename from intl/uconv/public/nsUConvCID.h
rename to intl/uconv/nsUConvCID.h
rename from intl/uconv/src/nsUConvModule.cpp
rename to intl/uconv/nsUConvModule.cpp
rename from intl/uconv/src/nsUTF8ConverterService.cpp
rename to intl/uconv/nsUTF8ConverterService.cpp
rename from intl/uconv/src/nsUTF8ConverterService.h
rename to intl/uconv/nsUTF8ConverterService.h
rename from intl/uconv/src/nsUTF8ToUnicode.cpp
rename to intl/uconv/nsUTF8ToUnicode.cpp
rename from intl/uconv/src/nsUTF8ToUnicode.h
rename to intl/uconv/nsUTF8ToUnicode.h
rename from intl/uconv/src/nsUTF8ToUnicodeSSE2.cpp
rename to intl/uconv/nsUTF8ToUnicodeSSE2.cpp
rename from intl/uconv/src/nsUnicodeToCP1252.cpp
rename to intl/uconv/nsUnicodeToCP1252.cpp
rename from intl/uconv/src/nsUnicodeToCP1252.h
rename to intl/uconv/nsUnicodeToCP1252.h
rename from intl/uconv/src/nsUnicodeToISO88591.cpp
rename to intl/uconv/nsUnicodeToISO88591.cpp
rename from intl/uconv/src/nsUnicodeToISO88591.h
rename to intl/uconv/nsUnicodeToISO88591.h
rename from intl/uconv/src/nsUnicodeToMacRoman.cpp
rename to intl/uconv/nsUnicodeToMacRoman.cpp
rename from intl/uconv/src/nsUnicodeToMacRoman.h
rename to intl/uconv/nsUnicodeToMacRoman.h
rename from intl/uconv/src/nsUnicodeToUTF8.cpp
rename to intl/uconv/nsUnicodeToUTF8.cpp
rename from intl/uconv/src/nsUnicodeToUTF8.h
rename to intl/uconv/nsUnicodeToUTF8.h
deleted file mode 100644
--- a/intl/uconv/public/moz.build
+++ /dev/null
@@ -1,15 +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/.
-
-EXPORTS += [
-    'nsEncoderDecoderUtils.h',
-    'nsIUnicodeDecoder.h',
-    'nsIUnicodeEncoder.h',
-    'nsUConvCID.h',
-    'nsUCSupport.h',
-    'uconvutil.h',
-]
-
deleted file mode 100644
--- a/intl/uconv/src/moz.build
+++ /dev/null
@@ -1,198 +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/.
-
-UNIFIED_SOURCES += [
-    'nsConverterInputStream.cpp',
-    'nsConverterOutputStream.cpp',
-    'nsCP1252ToUnicode.cpp',
-    'nsISO88591ToUnicode.cpp',
-    'nsMacRomanToUnicode.cpp',
-    'nsReplacementToUnicode.cpp',
-    'nsScriptableUConv.cpp',
-    'nsTextToSubURI.cpp',
-    'nsUConvModule.cpp',
-    'nsUnicodeToCP1252.cpp',
-    'nsUnicodeToISO88591.cpp',
-    'nsUnicodeToMacRoman.cpp',
-    'nsUnicodeToUTF8.cpp',
-    'nsUTF8ConverterService.cpp',
-    'nsUTF8ToUnicode.cpp',
-]
-
-UNIFIED_SOURCES += [
-    '../ucvcn/nsGBKConvUtil.cpp',
-    '../ucvcn/nsGBKToUnicode.cpp',
-    '../ucvcn/nsHZToUnicode.cpp',
-    '../ucvcn/nsUnicodeToGB2312V2.cpp',
-    '../ucvcn/nsUnicodeToGBK.cpp',
-    '../ucvcn/nsUnicodeToHZ.c