merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 19 Nov 2014 13:35:26 +0100
changeset 240739 07ad59382922a8c4d03da24a27540df00b28b840
parent 240666 bc2c36dda0a92157cabc54b78256e02402ffcd3b (current diff)
parent 240738 3870aaf0cf48c527916cd469e2502794ff1620a4 (diff)
child 240762 aa72ddfe9f9365cf4e9e3a70ff639c0e5c3829b8
push id4311
push userraliiev@mozilla.com
push dateMon, 12 Jan 2015 19:37:41 +0000
treeherdermozilla-beta@150c9fed433b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
dom/interfaces/base/nsIDOMGlobalObjectConstructor.idl
image/src/DecodeStrategy.h
js/src/tests/js1_7/extensions/regress-470176.js
js/src/tests/js1_8_1/regress/regress-452498-039.js
js/src/tests/js1_8_1/regress/regress-452498-077.js
js/src/tests/js1_8_1/regress/regress-452498-110.js
js/src/tests/js1_8_1/regress/regress-452498-184.js
js/src/tests/js1_8_5/extensions/compound-assign-const.js
--- a/accessible/base/nsAccessibilityService.h
+++ b/accessible/base/nsAccessibilityService.h
@@ -246,16 +246,18 @@ GetAccService()
 }
 
 /**
  * Return true if we're in a content process and not B2G.
  */
 inline bool
 IPCAccessibilityActive()
 {
+  // XXX temporarily disable ipc accessibility because of crashes.
+return false;
 #ifdef MOZ_B2G
   return false;
 #else
   return XRE_GetProcessType() == GeckoProcessType_Content;
 #endif
 }
 
 /**
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -622,30 +622,36 @@ nsContextMenu.prototype = {
         if (descURL) {
           this.imageDescURL = makeURLAbsolute(this.target.ownerDocument.body.baseURI, descURL);
         }
       }
       else if (this.target instanceof HTMLCanvasElement) {
         this.onCanvas = true;
       }
       else if (this.target instanceof HTMLVideoElement) {
-        this.mediaURL = this.target.currentSrc || this.target.src;
+        let mediaURL = this.target.currentSrc || this.target.src;
+        if (this.isMediaURLReusable(mediaURL)) {
+          this.mediaURL = mediaURL;
+        }
         // Firefox always creates a HTMLVideoElement when loading an ogg file
         // directly. If the media is actually audio, be smarter and provide a
         // context menu with audio operations.
         if (this.target.readyState >= this.target.HAVE_METADATA &&
             (this.target.videoWidth == 0 || this.target.videoHeight == 0)) {
           this.onAudio = true;
         } else {
           this.onVideo = true;
         }
       }
       else if (this.target instanceof HTMLAudioElement) {
         this.onAudio = true;
-        this.mediaURL = this.target.currentSrc || this.target.src;
+        let mediaURL = this.target.currentSrc || this.target.src;
+        if (this.isMediaURLReusable(mediaURL)) {
+          this.mediaURL = mediaURL;
+        }
       }
       else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
         this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
         this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
         if (this.onEditableArea) {
           if (this.isRemote) {
             InlineSpellCheckerUI.initFromRemote(gContextMenuContentData.spellInfo);
           }
@@ -1499,16 +1505,20 @@ nsContextMenu.prototype = {
     return text;
   },
 
   // Returns true if anything is selected.
   isContentSelection: function() {
     return !this.focusedWindow.getSelection().isCollapsed;
   },
 
+  isMediaURLReusable: function(aURL) {
+    return !/^(?:blob|mediasource):/.test(aURL);
+  },
+
   toString: function () {
     return "contextMenu.target     = " + this.target + "\n" +
            "contextMenu.onImage    = " + this.onImage + "\n" +
            "contextMenu.onLink     = " + this.onLink + "\n" +
            "contextMenu.link       = " + this.link + "\n" +
            "contextMenu.inFrame    = " + this.inFrame + "\n" +
            "contextMenu.hasBGImage = " + this.hasBGImage + "\n";
   },
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -431,17 +431,16 @@ skip-if = e10s # Bug 921935 - focusmanag
 [browser_tabkeynavigation.js]
 skip-if = e10s
 [browser_tabopen_reflows.js]
 skip-if = e10s # Bug ?????? - test needs to be updated for e10s (captures a stack that isn't correct in e10s)
 [browser_tabs_isActive.js]
 skip-if = e10s # Bug 1100664 - test relies on linkedBrowser.docShell
 [browser_tabs_owner.js]
 [browser_trackingUI.js]
-skip-if = e10s # Bug 1093155 - tries to use context menu from browser-chrome and gets in a mess when in e10s mode
 support-files =
   trackingPage.html
   benignPage.html
 [browser_typeAheadFind.js]
 skip-if = buildapp == 'mulet' || e10s # Bug 921935 - focusmanager issues with e10s (test calls waitForFocus)
 [browser_unloaddialogs.js]
 skip-if = e10s # Bug 1100700 - test relies on unload event firing on closed tabs, which it doesn't
 [browser_urlHighlight.js]
--- a/build/valgrind/mach_commands.py
+++ b/build/valgrind/mach_commands.py
@@ -103,17 +103,18 @@ class MachCommands(MachCommandBase):
             valgrind_args = [
                 valgrind,
                 '--smc-check=all-non-file',
                 '--vex-iropt-register-updates=allregs-at-mem-access',
                 '--gen-suppressions=all',
                 '--num-callers=36',
                 '--leak-check=full',
                 '--show-possibly-lost=no',
-                '--track-origins=yes'
+                '--track-origins=yes',
+                '--trace-children=yes',
             ]
 
             for s in suppressions:
                 valgrind_args.append('--suppressions=' + s)
 
             supps_dir = os.path.join(build_dir, 'valgrind')
             supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup')
             valgrind_args.append('--suppressions=' + supps_file1)
--- a/configure.in
+++ b/configure.in
@@ -66,17 +66,17 @@ GTK3_VERSION=3.0.0
 WINDRES_VERSION=2.14.90
 W32API_VERSION=3.14
 GNOMEVFS_VERSION=2.0
 GNOMEUI_VERSION=2.2.0
 GCONF_VERSION=1.2.1
 GIO_VERSION=2.20
 STARTUP_NOTIFICATION_VERSION=0.8
 DBUS_VERSION=0.60
-SQLITE_VERSION=3.8.7.1
+SQLITE_VERSION=3.8.7.2
 
 MSMANIFEST_TOOL=
 
 dnl Set various checks
 dnl ========================================================
 MISSING_X=
 AC_PROG_AWK
 
--- a/db/sqlite3/README.MOZILLA
+++ b/db/sqlite3/README.MOZILLA
@@ -1,11 +1,9 @@
-This is SQLite 3.8.7.1
-
--- Ryan VanderMeulen <ryanvm@gmail.com>, 11/2014
+This is SQLite 3.8.7.2
 
 See http://www.sqlite.org/ for more info.
 
 We have a mozilla-specific Makefile.in in src/ (normally no
 Makefile.in there) that we use to build.
 
 To move to a new version:
 
--- a/db/sqlite3/src/sqlite3.c
+++ b/db/sqlite3/src/sqlite3.c
@@ -1,11 +1,11 @@
 /******************************************************************************
 ** This file is an amalgamation of many separate C source files from SQLite
-** version 3.8.7.1.  By combining all the individual C code files into this 
+** version 3.8.7.2.  By combining all the individual C code files into this 
 ** single large file, the entire code can be compiled as a single translation
 ** unit.  This allows many compilers to do optimizations that would not be
 ** possible if the files were compiled separately.  Performance improvements
 ** of 5% or more are commonly seen when SQLite is compiled as a single
 ** translation unit.
 **
 ** This file is all you need to compile SQLite.  To use SQLite in other
 ** programs, you need this file and the "sqlite3.h" header file that defines
@@ -226,19 +226,19 @@ extern "C" {
 ** within its configuration management system.  ^The SQLITE_SOURCE_ID
 ** string contains the date and time of the check-in (UTC) and an SHA1
 ** hash of the entire source tree.
 **
 ** See also: [sqlite3_libversion()],
 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
 ** [sqlite_version()] and [sqlite_source_id()].
 */
-#define SQLITE_VERSION        "3.8.7.1"
+#define SQLITE_VERSION        "3.8.7.2"
 #define SQLITE_VERSION_NUMBER 3008007
-#define SQLITE_SOURCE_ID      "2014-10-29 13:59:56 3b7b72c4685aa5cf5e675c2c47ebec10d9704221"
+#define SQLITE_SOURCE_ID      "2014-11-18 20:57:56 2ab564bf9655b7c7b97ab85cafc8a48329b27f93"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers
 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
 **
 ** These interfaces provide the same information as the [SQLITE_VERSION],
 ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
 ** but are associated with the library instead of the header file.  ^(Cautious
@@ -9008,17 +9008,17 @@ SQLITE_PRIVATE int sqlite3BtreeGetReserv
 SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p);
 #endif
 SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int);
 SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *);
 SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int);
 SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
 SQLITE_PRIVATE int sqlite3BtreeCommitPhaseTwo(Btree*, int);
 SQLITE_PRIVATE int sqlite3BtreeCommit(Btree*);
-SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int);
+SQLITE_PRIVATE int sqlite3BtreeRollback(Btree*,int,int);
 SQLITE_PRIVATE int sqlite3BtreeBeginStmt(Btree*,int);
 SQLITE_PRIVATE int sqlite3BtreeCreateTable(Btree*, int*, int flags);
 SQLITE_PRIVATE int sqlite3BtreeIsInTrans(Btree*);
 SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*);
 SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*);
 SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *));
 SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree);
 SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock);
@@ -9041,17 +9041,17 @@ SQLITE_PRIVATE int sqlite3BtreeIncrVacuu
 ** indices.)
 */
 #define BTREE_INTKEY     1    /* Table has only 64-bit signed integer keys */
 #define BTREE_BLOBKEY    2    /* Table has keys only - no data */
 
 SQLITE_PRIVATE int sqlite3BtreeDropTable(Btree*, int, int*);
 SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*);
 SQLITE_PRIVATE int sqlite3BtreeClearTableOfCursor(BtCursor*);
-SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int);
+SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree*, int, int);
 
 SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue);
 SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value);
 
 SQLITE_PRIVATE int sqlite3BtreeNewDb(Btree *p);
 
 /*
 ** The second parameter to sqlite3BtreeGetMeta or sqlite3BtreeUpdateMeta
@@ -13057,17 +13057,17 @@ SQLITE_PRIVATE int sqlite3CreateFunc(sql
   FuncDestructor *pDestructor
 );
 SQLITE_PRIVATE int sqlite3ApiExit(sqlite3 *db, int);
 SQLITE_PRIVATE int sqlite3OpenTempDatabase(Parse *);
 
 SQLITE_PRIVATE void sqlite3StrAccumInit(StrAccum*, char*, int, int);
 SQLITE_PRIVATE void sqlite3StrAccumAppend(StrAccum*,const char*,int);
 SQLITE_PRIVATE void sqlite3StrAccumAppendAll(StrAccum*,const char*);
-SQLITE_PRIVATE void sqlite3AppendSpace(StrAccum*,int);
+SQLITE_PRIVATE void sqlite3AppendChar(StrAccum*,int,char);
 SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*);
 SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum*);
 SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int);
 SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int);
 
 SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *);
 SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *);
 
@@ -20942,17 +20942,17 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
   u8 bArgList;               /* True for SQLITE_PRINTF_SQLFUNC */
   u8 useIntern;              /* Ok to use internal conversions (ex: %T) */
   char prefix;               /* Prefix character.  "+" or "-" or " " or '\0'. */
   sqlite_uint64 longvalue;   /* Value for integer types */
   LONGDOUBLE_TYPE realvalue; /* Value for real types */
   const et_info *infop;      /* Pointer to the appropriate info structure */
   char *zOut;                /* Rendering buffer */
   int nOut;                  /* Size of the rendering buffer */
-  char *zExtra;              /* Malloced memory used by some conversion */
+  char *zExtra = 0;          /* Malloced memory used by some conversion */
 #ifndef SQLITE_OMIT_FLOATING_POINT
   int  exp, e2;              /* exponent of real numbers */
   int nsd;                   /* Number of significant digits returned */
   double rounder;            /* Used for rounding floating point values */
   etByte flag_dp;            /* True if decimal point should be shown */
   etByte flag_rtz;           /* True if trailing zeros should be removed */
 #endif
   PrintfArguments *pArgList = 0; /* Arguments for SQLITE_PRINTF_SQLFUNC */
@@ -21059,17 +21059,16 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
         if( useIntern || (infop->flags & FLAG_INTERN)==0 ){
           xtype = infop->type;
         }else{
           return;
         }
         break;
       }
     }
-    zExtra = 0;
 
     /*
     ** At this point, variables are initialized as follows:
     **
     **   flag_alternateform          TRUE if a '#' is present.
     **   flag_altform2               TRUE if a '!' is present.
     **   flag_plussign               TRUE if a '+' is present.
     **   flag_leftjustify            TRUE if a '-' is present or if the
@@ -21350,23 +21349,26 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
         break;
       case etCHARX:
         if( bArgList ){
           bufpt = getTextArg(pArgList);
           c = bufpt ? bufpt[0] : 0;
         }else{
           c = va_arg(ap,int);
         }
-        buf[0] = (char)c;
-        if( precision>=0 ){
-          for(idx=1; idx<precision; idx++) buf[idx] = (char)c;
-          length = precision;
-        }else{
-          length =1;
-        }
+        if( precision>1 ){
+          width -= precision-1;
+          if( width>1 && !flag_leftjustify ){
+            sqlite3AppendChar(pAccum, width-1, ' ');
+            width = 0;
+          }
+          sqlite3AppendChar(pAccum, precision-1, c);
+        }
+        length = 1;
+        buf[0] = c;
         bufpt = buf;
         break;
       case etSTRING:
       case etDYNSTRING:
         if( bArgList ){
           bufpt = getTextArg(pArgList);
         }else{
           bufpt = va_arg(ap,char*);
@@ -21457,21 +21459,24 @@ SQLITE_PRIVATE void sqlite3VXPrintf(
       }
     }/* End switch over the format type */
     /*
     ** The text of the conversion is pointed to by "bufpt" and is
     ** "length" characters long.  The field width is "width".  Do
     ** the output.
     */
     width -= length;
-    if( width>0 && !flag_leftjustify ) sqlite3AppendSpace(pAccum, width);
+    if( width>0 && !flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
     sqlite3StrAccumAppend(pAccum, bufpt, length);
-    if( width>0 && flag_leftjustify ) sqlite3AppendSpace(pAccum, width);
-
-    if( zExtra ) sqlite3_free(zExtra);
+    if( width>0 && flag_leftjustify ) sqlite3AppendChar(pAccum, width, ' ');
+
+    if( zExtra ){
+      sqlite3_free(zExtra);
+      zExtra = 0;
+    }
   }/* End for loop over the format string */
 } /* End of function */
 
 /*
 ** Enlarge the memory allocation on a StrAccum object so that it is
 ** able to accept at least N more bytes of text.
 **
 ** Return the number of bytes of text that StrAccum is able to accept
@@ -21514,21 +21519,21 @@ static int sqlite3StrAccumEnlarge(StrAcc
       setStrAccumError(p, STRACCUM_NOMEM);
       return 0;
     }
   }
   return N;
 }
 
 /*
-** Append N space characters to the given string buffer.
-*/
-SQLITE_PRIVATE void sqlite3AppendSpace(StrAccum *p, int N){
+** Append N copies of character c to the given string buffer.
+*/
+SQLITE_PRIVATE void sqlite3AppendChar(StrAccum *p, int N, char c){
   if( p->nChar+N >= p->nAlloc && (N = sqlite3StrAccumEnlarge(p, N))<=0 ) return;
-  while( (N--)>0 ) p->zText[p->nChar++] = ' ';
+  while( (N--)>0 ) p->zText[p->nChar++] = c;
 }
 
 /*
 ** The StrAccum "p" is not large enough to accept N new bytes of z[].
 ** So enlarge if first, then do the append.
 **
 ** This is a helper routine to sqlite3StrAccumAppend() that does special-case
 ** work (enlarging the buffer) using tail recursion, so that the
@@ -50631,17 +50636,16 @@ SQLITE_PRIVATE int sqlite3WalUndo(Wal *p
       ** page 1 is never written to the log until the transaction is
       ** committed. As a result, the call to xUndo may not fail.
       */
       assert( walFramePgno(pWal, iFrame)!=1 );
       rc = xUndo(pUndoCtx, walFramePgno(pWal, iFrame));
     }
     if( iMax!=pWal->hdr.mxFrame ) walCleanupHash(pWal);
   }
-  assert( rc==SQLITE_OK );
   return rc;
 }
 
 /* 
 ** Argument aWalData must point to an array of WAL_SAVEPOINT_NDATA u32 
 ** values. This function populates the array with values required to 
 ** "rollback" the write position of the WAL handle back to the current 
 ** point in the event of a savepoint rollback (via WalSavepointUndo()).
@@ -51710,29 +51714,35 @@ struct CellInfo {
 ** MemPage.aCell[] of the entry.
 **
 ** A single database file can be shared by two more database connections,
 ** but cursors cannot be shared.  Each cursor is associated with a
 ** particular database connection identified BtCursor.pBtree.db.
 **
 ** Fields in this structure are accessed under the BtShared.mutex
 ** found at self->pBt->mutex. 
+**
+** skipNext meaning:
+**    eState==SKIPNEXT && skipNext>0:  Next sqlite3BtreeNext() is no-op.
+**    eState==SKIPNEXT && skipNext<0:  Next sqlite3BtreePrevious() is no-op.
+**    eState==FAULT:                   Cursor fault with skipNext as error code.
 */
 struct BtCursor {
   Btree *pBtree;            /* The Btree to which this cursor belongs */
   BtShared *pBt;            /* The BtShared this cursor points to */
   BtCursor *pNext, *pPrev;  /* Forms a linked list of all cursors */
   struct KeyInfo *pKeyInfo; /* Argument passed to comparison function */
   Pgno *aOverflow;          /* Cache of overflow page locations */
   CellInfo info;            /* A parse of the cell we are pointing at */
   i64 nKey;                 /* Size of pKey, or last integer key */
   void *pKey;               /* Saved key that was cursor last known position */
   Pgno pgnoRoot;            /* The root page of this tree */
   int nOvflAlloc;           /* Allocated size of aOverflow[] array */
-  int skipNext;    /* Prev() is noop if negative. Next() is noop if positive */
+  int skipNext;    /* Prev() is noop if negative. Next() is noop if positive.
+                   ** Error code if eState==CURSOR_FAULT */
   u8 curFlags;              /* zero or more BTCF_* flags defined below */
   u8 eState;                /* One of the CURSOR_XXX constants (see below) */
   u8 hints;                             /* As configured by CursorSetHints() */
   i16 iPage;                            /* Index of current page in apPage */
   u16 aiIdx[BTCURSOR_MAX_DEPTH];        /* Current index in apPage[i] */
   MemPage *apPage[BTCURSOR_MAX_DEPTH];  /* Pages from root to current page */
 };
 
@@ -51768,17 +51778,17 @@ struct BtCursor {
 **   this state, restoreCursorPosition() can be called to attempt to
 **   seek the cursor to the saved position.
 **
 ** CURSOR_FAULT:
 **   An unrecoverable error (an I/O error or a malloc failure) has occurred
 **   on a different connection that shares the BtShared cache with this
 **   cursor.  The error has left the cache in an inconsistent state.
 **   Do nothing else with this cursor.  Any attempt to use the cursor
-**   should return the error code stored in BtCursor.skip
+**   should return the error code stored in BtCursor.skipNext
 */
 #define CURSOR_INVALID           0
 #define CURSOR_VALID             1
 #define CURSOR_SKIPNEXT          2
 #define CURSOR_REQUIRESEEK       3
 #define CURSOR_FAULT             4
 
 /* 
@@ -54350,17 +54360,17 @@ SQLITE_PRIVATE int sqlite3BtreeClose(Btr
       sqlite3BtreeCloseCursor(pTmp);
     }
   }
 
   /* Rollback any active transaction and free the handle structure.
   ** The call to sqlite3BtreeRollback() drops any table-locks held by
   ** this handle.
   */
-  sqlite3BtreeRollback(p, SQLITE_OK);
+  sqlite3BtreeRollback(p, SQLITE_OK, 0);
   sqlite3BtreeLeave(p);
 
   /* If there are still other outstanding references to the shared-btree
   ** structure, return now. The remainder of this procedure cleans 
   ** up the shared-btree.
   */
   assert( p->wantToLock==0 && p->locked==0 );
   if( !p->sharable || removeFromSharingList(pBt) ){
@@ -55643,70 +55653,101 @@ SQLITE_PRIVATE int sqlite3BtreeCommit(Bt
     rc = sqlite3BtreeCommitPhaseTwo(p, 0);
   }
   sqlite3BtreeLeave(p);
   return rc;
 }
 
 /*
 ** This routine sets the state to CURSOR_FAULT and the error
-** code to errCode for every cursor on BtShared that pBtree
-** references.
-**
-** Every cursor is tripped, including cursors that belong
-** to other database connections that happen to be sharing
-** the cache with pBtree.
-**
-** This routine gets called when a rollback occurs.
-** All cursors using the same cache must be tripped
-** to prevent them from trying to use the btree after
-** the rollback.  The rollback may have deleted tables
-** or moved root pages, so it is not sufficient to
-** save the state of the cursor.  The cursor must be
-** invalidated.
-*/
-SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode){
+** code to errCode for every cursor on any BtShared that pBtree
+** references.  Or if the writeOnly flag is set to 1, then only
+** trip write cursors and leave read cursors unchanged.
+**
+** Every cursor is a candidate to be tripped, including cursors
+** that belong to other database connections that happen to be
+** sharing the cache with pBtree.
+**
+** This routine gets called when a rollback occurs. If the writeOnly
+** flag is true, then only write-cursors need be tripped - read-only
+** cursors save their current positions so that they may continue 
+** following the rollback. Or, if writeOnly is false, all cursors are 
+** tripped. In general, writeOnly is false if the transaction being
+** rolled back modified the database schema. In this case b-tree root
+** pages may be moved or deleted from the database altogether, making
+** it unsafe for read cursors to continue.
+**
+** If the writeOnly flag is true and an error is encountered while 
+** saving the current position of a read-only cursor, all cursors, 
+** including all read-cursors are tripped.
+**
+** SQLITE_OK is returned if successful, or if an error occurs while
+** saving a cursor position, an SQLite error code.
+*/
+SQLITE_PRIVATE int sqlite3BtreeTripAllCursors(Btree *pBtree, int errCode, int writeOnly){
   BtCursor *p;
-  if( pBtree==0 ) return;
-  sqlite3BtreeEnter(pBtree);
-  for(p=pBtree->pBt->pCursor; p; p=p->pNext){
-    int i;
-    sqlite3BtreeClearCursor(p);
-    p->eState = CURSOR_FAULT;
-    p->skipNext = errCode;
-    for(i=0; i<=p->iPage; i++){
-      releasePage(p->apPage[i]);
-      p->apPage[i] = 0;
-    }
-  }
-  sqlite3BtreeLeave(pBtree);
-}
-
-/*
-** Rollback the transaction in progress.  All cursors will be
-** invalided by this operation.  Any attempt to use a cursor
-** that was open at the beginning of this operation will result
-** in an error.
+  int rc = SQLITE_OK;
+
+  assert( (writeOnly==0 || writeOnly==1) && BTCF_WriteFlag==1 );
+  if( pBtree ){
+    sqlite3BtreeEnter(pBtree);
+    for(p=pBtree->pBt->pCursor; p; p=p->pNext){
+      int i;
+      if( writeOnly && (p->curFlags & BTCF_WriteFlag)==0 ){
+        if( p->eState==CURSOR_VALID ){
+          rc = saveCursorPosition(p);
+          if( rc!=SQLITE_OK ){
+            (void)sqlite3BtreeTripAllCursors(pBtree, rc, 0);
+            break;
+          }
+        }
+      }else{
+        sqlite3BtreeClearCursor(p);
+        p->eState = CURSOR_FAULT;
+        p->skipNext = errCode;
+      }
+      for(i=0; i<=p->iPage; i++){
+        releasePage(p->apPage[i]);
+        p->apPage[i] = 0;
+      }
+    }
+    sqlite3BtreeLeave(pBtree);
+  }
+  return rc;
+}
+
+/*
+** Rollback the transaction in progress.
+**
+** If tripCode is not SQLITE_OK then cursors will be invalidated (tripped).
+** Only write cursors are tripped if writeOnly is true but all cursors are
+** tripped if writeOnly is false.  Any attempt to use
+** a tripped cursor will result in an error.
 **
 ** This will release the write lock on the database file.  If there
 ** are no active cursors, it also releases the read lock.
 */
-SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p, int tripCode){
+SQLITE_PRIVATE int sqlite3BtreeRollback(Btree *p, int tripCode, int writeOnly){
   int rc;
   BtShared *pBt = p->pBt;
   MemPage *pPage1;
 
+  assert( writeOnly==1 || writeOnly==0 );
+  assert( tripCode==SQLITE_ABORT_ROLLBACK || tripCode==SQLITE_OK );
   sqlite3BtreeEnter(p);
   if( tripCode==SQLITE_OK ){
     rc = tripCode = saveAllCursors(pBt, 0, 0);
+    if( rc ) writeOnly = 0;
   }else{
     rc = SQLITE_OK;
   }
   if( tripCode ){
-    sqlite3BtreeTripAllCursors(p, tripCode);
+    int rc2 = sqlite3BtreeTripAllCursors(p, tripCode, writeOnly);
+    assert( rc==SQLITE_OK || (writeOnly==0 && rc2==SQLITE_OK) );
+    if( rc2!=SQLITE_OK ) rc = rc2;
   }
   btreeIntegrity(p);
 
   if( p->inTrans==TRANS_WRITE ){
     int rc2;
 
     assert( TRANS_WRITE==pBt->inTransaction );
     rc2 = sqlite3PagerRollback(pBt->pPager);
@@ -56031,23 +56072,19 @@ SQLITE_PRIVATE int sqlite3BtreeCursorIsV
 ** itself, not the number of bytes in the key.
 **
 ** The caller must position the cursor prior to invoking this routine.
 ** 
 ** This routine cannot fail.  It always returns SQLITE_OK.  
 */
 SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){
   assert( cursorHoldsMutex(pCur) );
-  assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID );
-  if( pCur->eState!=CURSOR_VALID ){
-    *pSize = 0;
-  }else{
-    getCellInfo(pCur);
-    *pSize = pCur->info.nKey;
-  }
+  assert( pCur->eState==CURSOR_VALID );
+  getCellInfo(pCur);
+  *pSize = pCur->info.nKey;
   return SQLITE_OK;
 }
 
 /*
 ** Set *pSize to the number of bytes of data in the entry the
 ** cursor currently points to.
 **
 ** The caller must guarantee that the cursor is pointing to a non-NULL
@@ -61461,17 +61498,17 @@ SQLITE_API int sqlite3_backup_finish(sql
     pp = sqlite3PagerBackupPtr(sqlite3BtreePager(p->pSrc));
     while( *pp!=p ){
       pp = &(*pp)->pNext;
     }
     *pp = p->pNext;
   }
 
   /* If a transaction is still open on the Btree, roll it back. */
-  sqlite3BtreeRollback(p->pDest, SQLITE_OK);
+  sqlite3BtreeRollback(p->pDest, SQLITE_OK, 0);
 
   /* Set the error code of the destination database handle. */
   rc = (p->rc==SQLITE_DONE) ? SQLITE_OK : p->rc;
   if( p->pDestDb ){
     sqlite3Error(p->pDestDb, rc);
 
     /* Exit the mutexes and free the backup context structure. */
     sqlite3LeaveMutexAndCloseZombie(p->pDestDb);
@@ -71299,17 +71336,17 @@ case OP_Column: {
       if( pCrsr==0 ){
         assert( pC->pseudoTableReg>0 );
         pReg = &aMem[pC->pseudoTableReg];
         assert( pReg->flags & MEM_Blob );
         assert( memIsValid(pReg) );
         pC->payloadSize = pC->szRow = avail = pReg->n;
         pC->aRow = (u8*)pReg->z;
       }else{
-        MemSetTypeFlag(pDest, MEM_Null);
+        sqlite3VdbeMemSetNull(pDest);
         goto op_column_out;
       }
     }else{
       assert( pCrsr );
       if( pC->isTable==0 ){
         assert( sqlite3BtreeCursorIsValid(pCrsr) );
         VVA_ONLY(rc =) sqlite3BtreeKeySize(pCrsr, &payloadSize64);
         assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */
@@ -71823,29 +71860,36 @@ case OP_Savepoint: {
           p->pc = pc;
           db->autoCommit = 0;
           p->rc = rc = SQLITE_BUSY;
           goto vdbe_return;
         }
         db->isTransactionSavepoint = 0;
         rc = p->rc;
       }else{
+        int isSchemaChange;
         iSavepoint = db->nSavepoint - iSavepoint - 1;
         if( p1==SAVEPOINT_ROLLBACK ){
+          isSchemaChange = (db->flags & SQLITE_InternChanges)!=0;
           for(ii=0; ii<db->nDb; ii++){
-            sqlite3BtreeTripAllCursors(db->aDb[ii].pBt, SQLITE_ABORT);
-          }
+            rc = sqlite3BtreeTripAllCursors(db->aDb[ii].pBt,
+                                       SQLITE_ABORT_ROLLBACK,
+                                       isSchemaChange==0);
+            if( rc!=SQLITE_OK ) goto abort_due_to_error;
+          }
+        }else{
+          isSchemaChange = 0;
         }
         for(ii=0; ii<db->nDb; ii++){
           rc = sqlite3BtreeSavepoint(db->aDb[ii].pBt, p1, iSavepoint);
           if( rc!=SQLITE_OK ){
             goto abort_due_to_error;
           }
         }
-        if( p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){
+        if( isSchemaChange ){
           sqlite3ExpirePreparedStatements(db);
           sqlite3ResetAllSchemasOfConnection(db);
           db->flags = (db->flags | SQLITE_InternChanges);
         }
       }
   
       /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all 
       ** savepoints nested inside of the savepoint being operated on. */
@@ -72232,17 +72276,17 @@ case OP_OpenWrite: {
 
   assert( (pOp->p5&(OPFLAG_P2ISREG|OPFLAG_BULKCSR))==pOp->p5 );
   assert( pOp->opcode==OP_OpenWrite || pOp->p5==0 );
   assert( p->bIsReader );
   assert( pOp->opcode==OP_OpenRead || pOp->opcode==OP_ReopenIdx
           || p->readOnly==0 );
 
   if( p->expired ){
-    rc = SQLITE_ABORT;
+    rc = SQLITE_ABORT_ROLLBACK;
     break;
   }
 
   nField = 0;
   pKeyInfo = 0;
   p2 = pOp->p2;
   iDb = pOp->p3;
   assert( iDb>=0 && iDb<db->nDb );
@@ -73399,16 +73443,20 @@ case OP_Rowid: {                 /* out2
     assert( pModule->xRowid );
     rc = pModule->xRowid(pC->pVtabCursor, &v);
     sqlite3VtabImportErrmsg(p, pVtab);
 #endif /* SQLITE_OMIT_VIRTUALTABLE */
   }else{
     assert( pC->pCursor!=0 );
     rc = sqlite3VdbeCursorRestore(pC);
     if( rc ) goto abort_due_to_error;
+    if( pC->nullRow ){
+      pOut->flags = MEM_Null;
+      break;
+    }
     rc = sqlite3BtreeKeySize(pC->pCursor, &v);
     assert( rc==SQLITE_OK );  /* Always so because of CursorRestore() above */
   }
   pOut->u.i = v;
   break;
 }
 
 /* Opcode: NullRow P1 * * * *
@@ -82430,17 +82478,16 @@ SQLITE_PRIVATE int sqlite3CodeSubselect(
         ExprList *pEList;
 
         assert( !isRowid );
         sqlite3SelectDestInit(&dest, SRT_Set, pExpr->iTable);
         dest.affSdst = (u8)affinity;
         assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable );
         pSelect->iLimit = 0;
         testcase( pSelect->selFlags & SF_Distinct );
-        pSelect->selFlags &= ~SF_Distinct;
         testcase( pKeyInfo==0 ); /* Caused by OOM in sqlite3KeyInfoAlloc() */
         if( sqlite3Select(pParse, pSelect, &dest) ){
           sqlite3KeyInfoUnref(pKeyInfo);
           return 0;
         }
         pEList = pSelect->pEList;
         assert( pKeyInfo!=0 ); /* OOM will cause exit after sqlite3Select() */
         assert( pEList!=0 );
@@ -125922,41 +125969,44 @@ SQLITE_PRIVATE void sqlite3LeaveMutexAnd
   if( db->lookaside.bMalloced ){
     sqlite3_free(db->lookaside.pStart);
   }
   sqlite3_free(db);
 }
 
 /*
 ** Rollback all database files.  If tripCode is not SQLITE_OK, then
-** any open cursors are invalidated ("tripped" - as in "tripping a circuit
+** any write cursors are invalidated ("tripped" - as in "tripping a circuit
 ** breaker") and made to return tripCode if there are any further
-** attempts to use that cursor.
+** attempts to use that cursor.  Read cursors remain open and valid
+** but are "saved" in case the table pages are moved around.
 */
 SQLITE_PRIVATE void sqlite3RollbackAll(sqlite3 *db, int tripCode){
   int i;
   int inTrans = 0;
+  int schemaChange;
   assert( sqlite3_mutex_held(db->mutex) );
   sqlite3BeginBenignMalloc();
 
   /* Obtain all b-tree mutexes before making any calls to BtreeRollback(). 
   ** This is important in case the transaction being rolled back has
   ** modified the database schema. If the b-tree mutexes are not taken
   ** here, then another shared-cache connection might sneak in between
   ** the database rollback and schema reset, which can cause false
   ** corruption reports in some cases.  */
   sqlite3BtreeEnterAll(db);
+  schemaChange = (db->flags & SQLITE_InternChanges)!=0 && db->init.busy==0;
 
   for(i=0; i<db->nDb; i++){
     Btree *p = db->aDb[i].pBt;
     if( p ){
       if( sqlite3BtreeIsInTrans(p) ){
         inTrans = 1;
       }
-      sqlite3BtreeRollback(p, tripCode);
+      sqlite3BtreeRollback(p, tripCode, !schemaChange);
     }
   }
   sqlite3VtabRollback(db);
   sqlite3EndBenignMalloc();
 
   if( (db->flags&SQLITE_InternChanges)!=0 && db->init.busy==0 ){
     sqlite3ExpirePreparedStatements(db);
     sqlite3ResetAllSchemasOfConnection(db);
--- a/db/sqlite3/src/sqlite3.h
+++ b/db/sqlite3/src/sqlite3.h
@@ -102,19 +102,19 @@ extern "C" {
 ** within its configuration management system.  ^The SQLITE_SOURCE_ID
 ** string contains the date and time of the check-in (UTC) and an SHA1
 ** hash of the entire source tree.
 **
 ** See also: [sqlite3_libversion()],
 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
 ** [sqlite_version()] and [sqlite_source_id()].
 */
-#define SQLITE_VERSION        "3.8.7.1"
+#define SQLITE_VERSION        "3.8.7.2"
 #define SQLITE_VERSION_NUMBER 3008007
-#define SQLITE_SOURCE_ID      "2014-10-29 13:59:56 3b7b72c4685aa5cf5e675c2c47ebec10d9704221"
+#define SQLITE_SOURCE_ID      "2014-11-18 20:57:56 2ab564bf9655b7c7b97ab85cafc8a48329b27f93"
 
 /*
 ** CAPI3REF: Run-Time Library Version Numbers
 ** KEYWORDS: sqlite3_version, sqlite3_sourceid
 **
 ** These interfaces provide the same information as the [SQLITE_VERSION],
 ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros
 ** but are associated with the library instead of the header file.  ^(Cautious
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -2528,10 +2528,48 @@ Navigator::GetUserAgent(nsPIDOMWindow* a
     do_GetService("@mozilla.org/dom/site-specific-user-agent;1");
   if (!siteSpecificUA) {
     return NS_OK;
   }
 
   return siteSpecificUA->GetUserAgentForURIAndWindow(aURI, aWindow, aUserAgent);
 }
 
+#ifdef MOZ_EME
+already_AddRefed<Promise>
+Navigator::RequestMediaKeySystemAccess(const nsAString& aKeySystem,
+                                       const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
+                                       ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
+  nsRefPtr<Promise> p = Promise::Create(go, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  if (aKeySystem.IsEmpty() ||
+      (aOptions.WasPassed() && aOptions.Value().IsEmpty())) {
+    p->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
+    return p.forget();
+  }
+
+  if (!MediaKeySystemAccess::IsKeySystemSupported(aKeySystem)) {
+    p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return p.forget();
+  }
+
+  // TODO: Wait (async) until the CDM is downloaded, if it's not already.
+
+  if (!aOptions.WasPassed() ||
+      MediaKeySystemAccess::IsSupported(aKeySystem, aOptions.Value())) {
+    nsRefPtr<MediaKeySystemAccess> access(new MediaKeySystemAccess(mWindow, aKeySystem));
+    p->MaybeResolve(access);
+    return p.forget();
+  }
+
+  p->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+
+  return p.forget();
+}
+#endif
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -13,16 +13,19 @@
 #include "nsIDOMNavigator.h"
 #include "nsIMozNavigatorNetwork.h"
 #include "nsAutoPtr.h"
 #include "nsWrapperCache.h"
 #include "nsHashKeys.h"
 #include "nsInterfaceHashtable.h"
 #include "nsString.h"
 #include "nsTArray.h"
+#ifdef MOZ_EME
+#include "mozilla/dom/MediaKeySystemAccess.h"
+#endif
 
 class nsPluginArray;
 class nsMimeTypeArray;
 class nsPIDOMWindow;
 class nsIDOMNavigatorSystemMessages;
 class nsDOMCameraManager;
 class nsDOMDeviceStorage;
 class nsIPrincipal;
@@ -311,16 +314,23 @@ public:
 
   nsPIDOMWindow* GetParentObject() const
   {
     return GetWindow();
   }
 
   virtual JSObject* WrapObject(JSContext* cx) MOZ_OVERRIDE;
 
+#ifdef MOZ_EME
+  already_AddRefed<Promise>
+  RequestMediaKeySystemAccess(const nsAString& aKeySystem,
+                              const Optional<Sequence<MediaKeySystemOptions>>& aOptions,
+                              ErrorResult& aRv);
+#endif
+
 private:
   virtual ~Navigator();
 
   bool CheckPermission(const char* type);
   static bool CheckPermission(nsPIDOMWindow* aWindow, const char* aType);
   // GetWindowFromGlobal returns the inner window for this global, if
   // any, else null.
   static already_AddRefed<nsPIDOMWindow> GetWindowFromGlobal(JSObject* aGlobal);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -103,17 +103,16 @@
 #include "nsIDOMMozSmsMessage.h"
 #include "nsIDOMMozMmsMessage.h"
 #include "nsIDOMMozMobileMessageThread.h"
 
 #ifdef MOZ_B2G_FM
 #include "FMRadio.h"
 #endif
 
-#include "nsIDOMGlobalObjectConstructor.h"
 #include "nsDebug.h"
 
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/Likely.h"
 #include "nsIInterfaceInfoManager.h"
 
 #ifdef MOZ_TIME_MANAGER
 #include "TimeManager.h"
@@ -1346,75 +1345,16 @@ BaseStubConstructor(nsIWeakReference* aW
   } else {
     native = do_CreateInstance(*name_struct->mData->mConstructorCID, &rv);
   }
   if (NS_FAILED(rv)) {
     NS_ERROR("Failed to create the object");
     return rv;
   }
 
-  nsCOMPtr<nsIDOMGlobalObjectConstructor> constructor(do_QueryInterface(native));
-  if (constructor) {
-    // Initialize object using the current inner window, but only if
-    // the caller can access it.
-    nsCOMPtr<nsPIDOMWindow> owner = do_QueryReferent(aWeakOwner);
-    nsPIDOMWindow* outerWindow = owner ? owner->GetOuterWindow() : nullptr;
-    nsPIDOMWindow* currentInner =
-      outerWindow ? outerWindow->GetCurrentInnerWindow() : nullptr;
-    if (!currentInner ||
-        (owner != currentInner &&
-         !nsContentUtils::CanCallerAccess(currentInner))) {
-      return NS_ERROR_DOM_SECURITY_ERR;
-    }
-
-    nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(native);
-
-    JS::Rooted<JSObject*> thisObject(cx, wrappedJS->GetJSObject());
-    if (!thisObject) {
-      return NS_ERROR_UNEXPECTED;
-    }
-
-    JSAutoCompartment ac(cx, thisObject);
-
-    JS::Rooted<JS::Value> funval(cx);
-    if (!JS_GetProperty(cx, thisObject, "constructor", &funval) ||
-        !funval.isObject()) {
-      return NS_ERROR_UNEXPECTED;
-    }
-
-    // Check if the object is even callable.
-    NS_ENSURE_STATE(JS::IsCallable(&funval.toObject()));
-    {
-      // wrap parameters in the target compartment
-      // we also pass in the calling window as the first argument
-      unsigned argc = args.length() + 1;
-      JS::AutoValueVector argv(cx);
-      if (!argv.resize(argc)) {
-        return NS_ERROR_OUT_OF_MEMORY;
-      }
-
-      nsCOMPtr<nsIDOMWindow> currentWin(do_GetInterface(currentInner));
-      rv = WrapNative(cx, currentWin, &NS_GET_IID(nsIDOMWindow),
-                      true, argv[0]);
-
-      for (size_t i = 1; i < argc; ++i) {
-        argv[i].set(args[i - 1]);
-        if (!JS_WrapValue(cx, argv[i]))
-          return NS_ERROR_FAILURE;
-      }
-
-      JS::Rooted<JS::Value> frval(cx);
-      bool ret = JS_CallFunctionValue(cx, thisObject, funval, argv, &frval);
-
-      if (!ret) {
-        return NS_ERROR_FAILURE;
-      }
-    }
-  }
-
   js::AssertSameCompartment(cx, obj);
   return WrapNative(cx, native, true, args.rval());
 }
 
 static nsresult
 DefineInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj, const nsIID *aIID)
 {
   nsCOMPtr<nsIInterfaceInfoManager>
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -861,17 +861,17 @@ nsDOMWindowUtils::SendPointerEventCommon
   event.widget = widget;
   event.pressure = aPressure;
   event.inputSource = aInputSourceArg;
   event.pointerId = aPointerId;
   event.width = aWidth;
   event.height = aHeight;
   event.tiltX = aTiltX;
   event.tiltY = aTiltY;
-  event.isPrimary = aIsPrimary;
+  event.isPrimary = (nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == aInputSourceArg) ? true : aIsPrimary;
   event.clickCount = aClickCount;
   event.time = PR_IntervalNow();
   event.mFlags.mIsSynthesizedForTests = aOptionalArgCount >= 10 ? aIsSynthesized : true;
 
   nsPresContext* presContext = GetPresContext();
   if (!presContext) {
     return NS_ERROR_FAILURE;
   }
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -189,16 +189,17 @@
 #ifdef MOZ_GAMEPAD
 #include "mozilla/dom/GamepadService.h"
 #endif
 
 #include "nsRefreshDriver.h"
 
 #include "mozilla/dom/SelectionChangeEvent.h"
 
+#include "mozilla/AddonPathService.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "nsLocation.h"
 #include "nsHTMLDocument.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "prrng.h"
 #include "nsSandboxFlags.h"
@@ -646,17 +647,17 @@ public:
   virtual bool hasOwn(JSContext *cx, JS::Handle<JSObject*> proxy,
                       JS::Handle<jsid> id, bool *bp) const MOZ_OVERRIDE;
   virtual bool getOwnEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> proxy,
                                             JS::AutoIdVector &props) const MOZ_OVERRIDE;
   virtual bool getEnumerablePropertyKeys(JSContext *cx, JS::Handle<JSObject*> proxy,
                                          JS::AutoIdVector &props) const MOZ_OVERRIDE;
   virtual bool iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
                        unsigned flags,
-                       JS::MutableHandle<JS::Value> vp) const MOZ_OVERRIDE;
+                       JS::MutableHandle<JSObject*> objp) const MOZ_OVERRIDE;
   virtual const char *className(JSContext *cx,
                                 JS::Handle<JSObject*> wrapper) const MOZ_OVERRIDE;
 
   virtual void finalize(JSFreeOp *fop, JSObject *proxy) const MOZ_OVERRIDE;
 
   virtual bool isCallable(JSObject *obj) const MOZ_OVERRIDE {
     return false;
   }
@@ -937,24 +938,23 @@ nsOuterWindowProxy::getEnumerablePropert
 
   JS::AutoIdVector innerProps(cx);
   if (!js::Wrapper::getEnumerablePropertyKeys(cx, proxy, innerProps)) {
     return false;
   }
   return js::AppendUnique(cx, props, innerProps);
 }
 
-
 bool
 nsOuterWindowProxy::iterate(JSContext *cx, JS::Handle<JSObject*> proxy,
-                            unsigned flags, JS::MutableHandle<JS::Value> vp) const
+                            unsigned flags, JS::MutableHandle<JSObject*> objp) const
 {
   // BaseProxyHandler::iterate seems to do what we want here: fall
   // back on the property names returned from keys() and enumerate().
-  return js::BaseProxyHandler::iterate(cx, proxy, flags, vp);
+  return js::BaseProxyHandler::iterate(cx, proxy, flags, objp);
 }
 
 bool
 nsOuterWindowProxy::GetSubframeWindow(JSContext *cx,
                                       JS::Handle<JSObject*> proxy,
                                       JS::Handle<jsid> id,
                                       JS::MutableHandle<JS::Value> vp,
                                       bool& found) const
@@ -2265,16 +2265,24 @@ CreateNativeGlobalForInner(JSContext* aC
   nsCOMPtr<nsIExpandedPrincipal> nsEP = do_QueryInterface(aPrincipal);
   MOZ_RELEASE_ASSERT(!nsEP, "DOMWindow with nsEP is not supported");
 
   nsGlobalWindow *top = nullptr;
   if (aNewInner->GetOuterWindow()) {
     top = aNewInner->GetTop();
   }
   JS::CompartmentOptions options;
+
+  // Sometimes add-ons load their own XUL windows, either as separate top-level
+  // windows or inside a browser element. In such cases we want to tag the
+  // window's compartment with the add-on ID. See bug 1092156.
+  if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
+    options.setAddonId(MapURIToAddonID(aURI));
+  }
+
   if (top) {
     if (top->GetGlobalJSObject()) {
       options.setSameZoneAs(top->GetGlobalJSObject());
     }
   }
 
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -248,21 +248,17 @@ nsJSUtils::EvaluateString(JSContext* aCx
         script(aCx, JS::FinishOffThreadScript(aCx, JS_GetRuntime(aCx), *aOffThreadToken));
       *aOffThreadToken = nullptr; // Mark the token as having been finished.
       if (script) {
         ok = JS_ExecuteScript(aCx, scopeChain, script);
       } else {
         ok = false;
       }
     } else if (ok) {
-      if (!aCompileOptions.noScriptRval) {
-        ok = JS::Evaluate(aCx, scopeChain, aCompileOptions, aSrcBuf, aRetValue);
-      } else {
-        ok = JS::Evaluate(aCx, scopeChain, aCompileOptions, aSrcBuf);
-      }
+      ok = JS::Evaluate(aCx, scopeChain, aCompileOptions, aSrcBuf, aRetValue);
     }
 
     if (ok && aEvaluateOptions.coerceToString && !aRetValue.isUndefined()) {
       JS::Rooted<JS::Value> value(aCx, aRetValue);
       JSString* str = JS::ToString(aCx, value);
       ok = !!str;
       aRetValue.set(ok ? JS::StringValue(str) : JS::UndefinedValue());
     }
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -1730,16 +1730,22 @@ CanvasRenderingContext2D::SetTransform(d
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   Matrix matrix(m11, m12, m21, m22, dx, dy);
   mTarget->SetTransform(matrix);
 }
 
+void
+CanvasRenderingContext2D::ResetTransform(ErrorResult& error)
+{
+  SetTransform(1.0, 0.0, 0.0, 1.0, 0.0, 0.0, error);
+}
+
 static void
 MatrixToJSObject(JSContext* cx, const Matrix& matrix,
                  JS::MutableHandle<JSObject*> result, ErrorResult& error)
 {
   double elts[6] = { matrix._11, matrix._12,
                      matrix._21, matrix._22,
                      matrix._31, matrix._32 };
 
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -143,16 +143,17 @@ public:
   void Restore();
   void Scale(double x, double y, mozilla::ErrorResult& error);
   void Rotate(double angle, mozilla::ErrorResult& error);
   void Translate(double x, double y, mozilla::ErrorResult& error);
   void Transform(double m11, double m12, double m21, double m22, double dx,
                  double dy, mozilla::ErrorResult& error);
   void SetTransform(double m11, double m12, double m21, double m22, double dx,
                     double dy, mozilla::ErrorResult& error);
+  void ResetTransform(mozilla::ErrorResult& error);
 
   double GlobalAlpha()
   {
     return CurrentState().globalAlpha;
   }
 
   // Useful for silencing cast warnings
   static mozilla::gfx::Float ToFloat(double aValue) { return mozilla::gfx::Float(aValue); }
--- a/dom/canvas/test/test_canvas.html
+++ b/dom/canvas/test/test_canvas.html
@@ -19177,16 +19177,17 @@ ctx.fillRect(0, 0, 100, 50);
 
 ctx.transform(1,0, 0,1, 0,0);
 ctx.fillStyle = '#0f0';
 ctx.fillRect(0, 0, 100, 50);
 isPixel(ctx, 50,25, 0,255,0,255, 0);
 
 
 }
+
 </script>
 
 <!-- [[[ test_2d.transformation.transform.multiply.html ]]] -->
 
 <p>Canvas test: 2d.transformation.transform.multiply</p>
 <!-- Testing: transform() multiplies the CTM -->
 <canvas id="c603" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
 <script>
@@ -19553,16 +19554,19 @@ ok(ctx.scale(1, 1) === undefined, "ctx.s
 ok(ctx.rotate(0) === undefined, "ctx.rotate(0) === undefined");
 ok(ctx.translate(0, 0) === undefined, "ctx.translate(0, 0) === undefined");
 if (ctx.transform) { // (avoid spurious failures, since the aim here is not to test that all features are supported)
     ok(ctx.transform(1, 0, 0, 1, 0, 0) === undefined, "ctx.transform(1, 0, 0, 1, 0, 0) === undefined");
 }
 if (ctx.setTransform) {
     ok(ctx.setTransform(1, 0, 0, 1, 0, 0) === undefined, "ctx.setTransform(1, 0, 0, 1, 0, 0) === undefined");
 }
+if (ctx.resetTransform) {
+    ok(ctx.resetTransform() === undefined, "ctx.resetTransform() === undefined");
+}
 ok(ctx.clearRect(0, 0, 0, 0) === undefined, "ctx.clearRect(0, 0, 0, 0) === undefined");
 ok(ctx.fillRect(0, 0, 0, 0) === undefined, "ctx.fillRect(0, 0, 0, 0) === undefined");
 ok(ctx.strokeRect(0, 0, 0, 0) === undefined, "ctx.strokeRect(0, 0, 0, 0) === undefined");
 ok(ctx.beginPath() === undefined, "ctx.beginPath() === undefined");
 ok(ctx.closePath() === undefined, "ctx.closePath() === undefined");
 ok(ctx.moveTo(0, 0) === undefined, "ctx.moveTo(0, 0) === undefined");
 ok(ctx.lineTo(0, 0) === undefined, "ctx.lineTo(0, 0) === undefined");
 ok(ctx.quadraticCurveTo(0, 0, 0, 0) === undefined, "ctx.quadraticCurveTo(0, 0, 0, 0) === undefined");
@@ -21547,16 +21551,41 @@ function test_opaque() {
   ctx.fillRect(10,0,10,10);
 
   isPixel(ctx, 20, 20, 0, 0, 0, 255, 0);
   isPixel(ctx, 5, 5, 0, 128, 0, 255, 0);
   isPixel(ctx, 15, 5, 128, 0, 0, 255, 0);
 }
 </script>
 
+<p>Canvas test: 2d.transformation.transform.identity</p>
+<!-- Testing: resetTransform() changes to the identity matrix -->
+<canvas id="c689" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
+<script>
+
+
+function test_2d_transformation_reset_transform() {
+
+var canvas = document.getElementById('c689');
+var ctx = canvas.getContext('2d');
+
+ctx.fillStyle = '#f00';
+ctx.fillRect(0, 0, 100, 50);
+
+ctx.setTransform(0.1, 0.0, 0.0, 0.1, 80.0, 30.0);
+ctx.resetTransform();
+ctx.fillStyle = '#0f0';
+ctx.fillRect(0, 0, 100, 50);
+isPixel(ctx, 50,25, 0,255,0,255, 0);
+
+
+}
+
+</script>
+
 <script>
 
 function asyncTestsDone() {
 	if (isDone_test_2d_drawImage_animated_apng &&
 		isDone_test_2d_drawImage_animated_gif) {
 		SimpleTest.finish();
 	} else {
 		setTimeout(asyncTestsDone, 500);
@@ -24840,16 +24869,21 @@ function runTests() {
  }
 try {
   test_opaque();
  } catch(e) {
   throw e;
   ok(false, "unexpected exception thrown in: test_opaque");
  }
  try {
+  test_2d_transformation_reset_transform();
+ } catch (e) {
+  ok(false, "unexpected exception thrown in: test_2d_transformation_reset_transform");
+ }
+ try {
   // run this test last since it replaces the getContext method
   test_type_replace();
  } catch (e) {
   ok(false, "unexpected exception thrown in: test_type_replace");
  }
  
  //run the asynchronous tests
  try {
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -1235,32 +1235,35 @@ BackgroundDatabaseChild::RecvPBackground
   MOZ_ASSERT(mOpenRequestActor);
 
   MaybeCollectGarbageOnIPCMessage();
 
   EnsureDOMObject();
 
   auto actor = static_cast<BackgroundVersionChangeTransactionChild*>(aActor);
 
+  nsRefPtr<IDBOpenDBRequest> request = mOpenRequestActor->GetOpenDBRequest();
+  MOZ_ASSERT(request);
+
   nsRefPtr<IDBTransaction> transaction =
-    IDBTransaction::CreateVersionChange(mDatabase, actor, aNextObjectStoreId,
+    IDBTransaction::CreateVersionChange(mDatabase,
+                                        actor,
+                                        request,
+                                        aNextObjectStoreId,
                                         aNextIndexId);
   if (NS_WARN_IF(!transaction)) {
     return false;
   }
 
   transaction->AssertIsOnOwningThread();
 
   actor->SetDOMTransaction(transaction);
 
   mDatabase->EnterSetVersionTransaction(aRequestedVersion);
 
-  nsRefPtr<IDBOpenDBRequest> request = mOpenRequestActor->GetOpenDBRequest();
-  MOZ_ASSERT(request);
-
   request->SetTransaction(transaction);
 
   nsCOMPtr<nsIDOMEvent> upgradeNeededEvent =
     IDBVersionChangeEvent::Create(request,
                                   nsDependentString(kUpgradeNeededEventType),
                                   aCurrentVersion,
                                   aRequestedVersion);
   if (NS_WARN_IF(!upgradeNeededEvent)) {
@@ -1311,17 +1314,17 @@ BackgroundDatabaseChild::RecvVersionChan
         bfCacheEntry->RemoveFromBFCacheSync();
         shouldAbortAndClose = true;
       }
     }
 
     if (shouldAbortAndClose) {
       // Invalidate() doesn't close the database in the parent, so we have
       // to call Close() and AbortTransactions() manually.
-      mDatabase->AbortTransactions();
+      mDatabase->AbortTransactions(/* aShouldWarn */ false);
       mDatabase->Close();
       return true;
     }
   }
 
   // Otherwise fire a versionchange event.
   const nsDependentString type(kVersionChangeEventType);
 
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -34,19 +34,22 @@
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/nsIRemoteBlob.h"
 #include "mozilla/dom/quota/QuotaManager.h"
 #include "mozilla/ipc/FileDescriptor.h"
 #include "mozilla/ipc/InputStreamParams.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsIConsoleService.h"
 #include "nsIDocument.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
+#include "nsIScriptError.h"
 #include "nsISupportsPrimitives.h"
 #include "nsThreadUtils.h"
 #include "ProfilerHelpers.h"
 #include "ReportInternalError.h"
 
 // Include this last to avoid path problems on Windows.
 #include "ActorsChild.h"
 
@@ -309,17 +312,17 @@ IDBDatabase::CloseInternal()
 }
 
 void
 IDBDatabase::InvalidateInternal()
 {
   AssertIsOnOwningThread();
 
   InvalidateMutableFiles();
-  AbortTransactions();
+  AbortTransactions(/* aShouldWarn */ true);
 
   CloseInternal();
 }
 
 void
 IDBDatabase::EnterSetVersionTransaction(uint64_t aNewVersion)
 {
   AssertIsOnOwningThread();
@@ -746,45 +749,49 @@ IDBDatabase::UnregisterTransaction(IDBTr
   MOZ_ASSERT(aTransaction);
   aTransaction->AssertIsOnOwningThread();
   MOZ_ASSERT(mTransactions.Contains(aTransaction));
 
   mTransactions.RemoveEntry(aTransaction);
 }
 
 void
-IDBDatabase::AbortTransactions()
+IDBDatabase::AbortTransactions(bool aShouldWarn)
 {
   AssertIsOnOwningThread();
 
   class MOZ_STACK_CLASS Helper MOZ_FINAL
   {
   public:
     static void
-    AbortTransactions(nsTHashtable<nsPtrHashKey<IDBTransaction>>& aTable)
+    AbortTransactions(nsTHashtable<nsPtrHashKey<IDBTransaction>>& aTable,
+                      nsTArray<nsRefPtr<IDBTransaction>>& aAbortedTransactions)
     {
       const uint32_t count = aTable.Count();
       if (!count) {
         return;
       }
 
-      nsTArray<nsRefPtr<IDBTransaction>> transactions;
+      nsAutoTArray<nsRefPtr<IDBTransaction>, 20> transactions;
       transactions.SetCapacity(count);
 
       aTable.EnumerateEntries(Collect, &transactions);
 
       MOZ_ASSERT(transactions.Length() == count);
 
-      IDB_REPORT_INTERNAL_ERR();
-
       for (uint32_t index = 0; index < count; index++) {
-        nsRefPtr<IDBTransaction> transaction = transactions[index].forget();
+        nsRefPtr<IDBTransaction> transaction = Move(transactions[index]);
         MOZ_ASSERT(transaction);
 
         transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+        // We only care about warning for write transactions.
+        if (transaction->GetMode() != IDBTransaction::READ_ONLY) {
+          aAbortedTransactions.AppendElement(Move(transaction));
+        }
       }
     }
 
   private:
     static PLDHashOperator
     Collect(nsPtrHashKey<IDBTransaction>* aTransaction, void* aClosure)
     {
       MOZ_ASSERT(aTransaction);
@@ -793,17 +800,35 @@ IDBDatabase::AbortTransactions()
 
       auto* array = static_cast<nsTArray<nsRefPtr<IDBTransaction>>*>(aClosure);
       array->AppendElement(aTransaction->GetKey());
 
       return PL_DHASH_NEXT;
     }
   };
 
-  Helper::AbortTransactions(mTransactions);
+  nsAutoTArray<nsRefPtr<IDBTransaction>, 5> abortedTransactions;
+  Helper::AbortTransactions(mTransactions, abortedTransactions);
+
+  if (aShouldWarn && !abortedTransactions.IsEmpty()) {
+    static const char kWarningMessage[] = "IndexedDBTransactionAbortNavigation";
+
+    for (uint32_t count = abortedTransactions.Length(), index = 0;
+         index < count;
+         index++) {
+      nsRefPtr<IDBTransaction>& transaction = abortedTransactions[index];
+      MOZ_ASSERT(transaction);
+
+      nsString filename;
+      uint32_t lineNo;
+      transaction->GetCallerLocation(filename, &lineNo);
+
+      LogWarning(kWarningMessage, filename, lineNo);
+    }
+  }
 }
 
 PBackgroundIDBDatabaseFileChild*
 IDBDatabase::GetOrCreateFileActorForBlob(File* aBlob)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aBlob);
   MOZ_ASSERT(mBackgroundActor);
@@ -1150,16 +1175,75 @@ IDBDatabase::Invalidate()
 
   if (!mInvalidated) {
     mInvalidated = true;
 
     InvalidateInternal();
   }
 }
 
+void
+IDBDatabase::LogWarning(const char* aMessageName,
+                        const nsAString& aFilename,
+                        uint32_t aLineNumber)
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aMessageName);
+
+  // For now this is main-thread only.
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsXPIDLString localizedMessage;
+  if (NS_WARN_IF(NS_FAILED(
+    nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
+                                       aMessageName,
+                                       localizedMessage)))) {
+    return;
+  }
+
+  nsAutoCString category;
+  if (mFactory->IsChrome()) {
+    category.AssignLiteral("chrome ");
+  } else {
+    category.AssignLiteral("content ");
+  }
+  category.AppendLiteral("javascript");
+
+  nsCOMPtr<nsIConsoleService> consoleService =
+    do_GetService(NS_CONSOLESERVICE_CONTRACTID);
+  MOZ_ASSERT(consoleService);
+
+  nsCOMPtr<nsIScriptError> scriptError =
+    do_CreateInstance(NS_SCRIPTERROR_CONTRACTID);
+  MOZ_ASSERT(consoleService);
+
+  if (mFactory->GetParentObject()) {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      scriptError->InitWithWindowID(localizedMessage,
+                                    aFilename,
+                                    /* aSourceLine */ EmptyString(),
+                                    aLineNumber,
+                                    /* aColumnNumber */ 0,
+                                    nsIScriptError::warningFlag,
+                                    category,
+                                    mFactory->InnerWindowID())));
+  } else {
+    MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
+      scriptError->Init(localizedMessage,
+                        aFilename,
+                        /* aSourceLine */ EmptyString(),
+                        aLineNumber,
+                        /* aColumnNumber */ 0,
+                        nsIScriptError::warningFlag,
+                        category.get())));
+  }
+
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(consoleService->LogMessage(scriptError)));
+}
+
 NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache)
 NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase)
 NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase)
 
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -166,17 +166,17 @@ public:
 
   void
   RegisterTransaction(IDBTransaction* aTransaction);
 
   void
   UnregisterTransaction(IDBTransaction* aTransaction);
 
   void
-  AbortTransactions();
+  AbortTransactions(bool aShouldWarn);
 
   PBackgroundIDBDatabaseFileChild*
   GetOrCreateFileActorForBlob(File* aBlob);
 
   void
   NoteFinishedFileActor(PBackgroundIDBDatabaseFileChild* aFileActor);
 
   void
@@ -294,15 +294,20 @@ private:
   void
   RefreshSpec(bool aMayDelete);
 
   void
   ExpireFileActors(bool aExpireAll);
 
   void
   InvalidateMutableFiles();
+
+  void
+  LogWarning(const char* aMessageName,
+             const nsAString& aFilename,
+             uint32_t aLineNumber);
 };
 
 } // namespace indexedDB
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_indexeddb_idbdatabase_h__
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -24,17 +24,17 @@
 #include "nsIWebNavigation.h"
 #include "ProfilerHelpers.h"
 #include "ReportInternalError.h"
 
 // Include this last to avoid path problems on Windows.
 #include "ActorsChild.h"
 
 #ifdef DEBUG
-#include "nsContentUtils.h" // For IsCallerChrome assertions.
+#include "nsContentUtils.h" // For assertions.
 #endif
 
 namespace mozilla {
 namespace dom {
 namespace indexedDB {
 
 using namespace mozilla::dom::quota;
 using namespace mozilla::ipc;
@@ -105,16 +105,17 @@ struct IDBFactory::PendingRequestInfo
     MOZ_ASSERT(aRequest);
     MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
   }
 };
 
 IDBFactory::IDBFactory()
   : mOwningObject(nullptr)
   , mBackgroundActor(nullptr)
+  , mInnerWindowID(0)
   , mBackgroundActorFailed(false)
   , mPrivateBrowsingMode(false)
 {
 #ifdef DEBUG
   mOwningThread = PR_GetCurrentThread();
 #endif
   AssertIsOnOwningThread();
 }
@@ -178,16 +179,17 @@ IDBFactory::CreateForWindow(nsPIDOMWindo
   nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
 
   bool privateBrowsingMode = loadContext && loadContext->UsePrivateBrowsing();
 
   nsRefPtr<IDBFactory> factory = new IDBFactory();
   factory->mPrincipalInfo = Move(principalInfo);
   factory->mWindow = aWindow;
   factory->mTabChild = TabChild::GetFrom(aWindow);
+  factory->mInnerWindowID = aWindow->WindowID();
   factory->mPrivateBrowsingMode = privateBrowsingMode;
 
   factory.forget(aFactory);
   return NS_OK;
 }
 
 // static
 nsresult
@@ -280,16 +282,25 @@ void
 IDBFactory::AssertIsOnOwningThread() const
 {
   MOZ_ASSERT(mOwningThread);
   MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
 }
 
 #endif // DEBUG
 
+bool
+IDBFactory::IsChrome() const
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(mPrincipalInfo);
+
+  return mPrincipalInfo->type() == PrincipalInfo::TSystemPrincipalInfo;
+}
+
 void
 IDBFactory::SetBackgroundActor(BackgroundFactoryChild* aBackgroundActor)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aBackgroundActor);
   MOZ_ASSERT(!mBackgroundActor);
 
   mBackgroundActor = aBackgroundActor;
@@ -519,29 +530,30 @@ IDBFactory::OpenInternal(nsIPrincipal* a
       }
     }
   }
 
   AutoJSAPI autoJS;
   nsRefPtr<IDBOpenDBRequest> request;
 
   if (mWindow) {
-    if (NS_WARN_IF(!autoJS.Init(mWindow))) {
+    AutoJSContext cx;
+    if (NS_WARN_IF(!autoJS.Init(mWindow, cx))) {
       IDB_REPORT_INTERNAL_ERR();
       aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
       return nullptr;
     }
 
-    JS::Rooted<JSObject*> scriptOwner(autoJS.cx(),
+    JS::Rooted<JSObject*> scriptOwner(cx,
       static_cast<nsGlobalWindow*>(mWindow.get())->FastGetGlobalJSObject());
     MOZ_ASSERT(scriptOwner);
 
     request = IDBOpenDBRequest::CreateForWindow(this, mWindow, scriptOwner);
   } else {
-    autoJS.Init();
+    autoJS.Init(mOwningObject.get());
     JS::Rooted<JSObject*> scriptOwner(autoJS.cx(), mOwningObject);
 
     request = IDBOpenDBRequest::CreateForJS(this, scriptOwner);
   }
 
   MOZ_ASSERT(request);
 
   // If we already have a background actor then we can start this request now.
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -68,16 +68,18 @@ class IDBFactory MOZ_FINAL
   nsTArray<nsAutoPtr<PendingRequestInfo>> mPendingRequests;
 
   BackgroundFactoryChild* mBackgroundActor;
 
 #ifdef DEBUG
   PRThread* mOwningThread;
 #endif
 
+  uint64_t mInnerWindowID;
+
   bool mBackgroundActorFailed;
   bool mPrivateBrowsingMode;
 
 public:
   static nsresult
   CreateForWindow(nsPIDOMWindow* aWindow,
                   IDBFactory** aFactory);
 
@@ -125,16 +127,27 @@ public:
   PrincipalInfo*
   GetPrincipalInfo() const
   {
     AssertIsOnOwningThread();
 
     return mPrincipalInfo;
   }
 
+  uint64_t
+  InnerWindowID() const
+  {
+    AssertIsOnOwningThread();
+
+    return mInnerWindowID;
+  }
+
+  bool
+  IsChrome() const;
+
   already_AddRefed<IDBOpenDBRequest>
   Open(const nsAString& aName,
        uint64_t aVersion,
        ErrorResult& aRv);
 
   already_AddRefed<IDBOpenDBRequest>
   Open(const nsAString& aName,
        const IDBOpenDBOptions& aOptions,
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -94,22 +94,21 @@ IDBRequest::InitMembers()
 already_AddRefed<IDBRequest>
 IDBRequest::Create(IDBDatabase* aDatabase,
                    IDBTransaction* aTransaction)
 {
   MOZ_ASSERT(aDatabase);
   aDatabase->AssertIsOnOwningThread();
 
   nsRefPtr<IDBRequest> request = new IDBRequest(aDatabase);
+  CaptureCaller(request->mFilename, &request->mLineNo);
 
   request->mTransaction = aTransaction;
   request->SetScriptOwner(aDatabase->GetScriptOwner());
 
-  request->CaptureCaller();
-
   return request.forget();
 }
 
 // static
 already_AddRefed<IDBRequest>
 IDBRequest::Create(IDBObjectStore* aSourceAsObjectStore,
                    IDBDatabase* aDatabase,
                    IDBTransaction* aTransaction)
@@ -135,16 +134,36 @@ IDBRequest::Create(IDBIndex* aSourceAsIn
 
   nsRefPtr<IDBRequest> request = Create(aDatabase, aTransaction);
 
   request->mSourceAsIndex = aSourceAsIndex;
 
   return request.forget();
 }
 
+// static
+void
+IDBRequest::CaptureCaller(nsAString& aFilename, uint32_t* aLineNo)
+{
+  MOZ_ASSERT(aFilename.IsEmpty());
+  MOZ_ASSERT(aLineNo);
+
+  ThreadsafeAutoJSContext cx;
+
+  const char* filename = nullptr;
+  uint32_t lineNo = 0;
+  if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) {
+    *aLineNo = 0;
+    return;
+  }
+
+  aFilename.Assign(NS_ConvertUTF8toUTF16(filename));
+  *aLineNo = lineNo;
+}
+
 void
 IDBRequest::GetSource(
              Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor>& aSource) const
 {
   AssertIsOnOwningThread();
 
   MOZ_ASSERT_IF(mSourceAsObjectStore, !mSourceAsIndex);
   MOZ_ASSERT_IF(mSourceAsIndex, !mSourceAsObjectStore);
@@ -222,35 +241,23 @@ IDBRequest::GetErrorCode() const
   MOZ_ASSERT(mHaveResultOrErrorCode);
 
   return mErrorCode;
 }
 
 #endif // DEBUG
 
 void
-IDBRequest::CaptureCaller()
+IDBRequest::GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const
 {
-  AutoJSContext cx;
-
-  const char* filename = nullptr;
-  uint32_t lineNo = 0;
-  if (!nsJSUtils::GetCallingLocation(cx, &filename, &lineNo)) {
-    return;
-  }
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aLineNo);
 
-  mFilename.Assign(NS_ConvertUTF8toUTF16(filename));
-  mLineNo = lineNo;
-}
-
-void
-IDBRequest::FillScriptErrorEvent(ErrorEventInit& aEventInit) const
-{
-  aEventInit.mLineno = mLineNo;
-  aEventInit.mFilename = mFilename;
+  aFilename = mFilename;
+  *aLineNo = mLineNo;
 }
 
 IDBRequestReadyState
 IDBRequest::ReadyState() const
 {
   AssertIsOnOwningThread();
 
   return IsPending() ?
@@ -296,17 +303,16 @@ IDBRequest::SetResultCallback(ResultCall
   AssertIsOnOwningThread();
   MOZ_ASSERT(aCallback);
   MOZ_ASSERT(!mHaveResultOrErrorCode);
   MOZ_ASSERT(mResultVal.isUndefined());
   MOZ_ASSERT(!mError);
 
   // See if our window is still valid.
   if (NS_WARN_IF(NS_FAILED(CheckInnerWindowCorrectness()))) {
-    IDB_REPORT_INTERNAL_ERR();
     SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
     return;
   }
 
   AutoJSAPI autoJS;
   Maybe<JSAutoCompartment> ac;
 
   if (GetScriptOwner()) {
@@ -419,34 +425,34 @@ IDBOpenDBRequest::CreateForWindow(IDBFac
                                   JS::Handle<JSObject*> aScriptOwner)
 {
   MOZ_ASSERT(aFactory);
   aFactory->AssertIsOnOwningThread();
   MOZ_ASSERT(aOwner);
   MOZ_ASSERT(aScriptOwner);
 
   nsRefPtr<IDBOpenDBRequest> request = new IDBOpenDBRequest(aFactory, aOwner);
-  request->CaptureCaller();
+  CaptureCaller(request->mFilename, &request->mLineNo);
 
   request->SetScriptOwner(aScriptOwner);
 
   return request.forget();
 }
 
 // static
 already_AddRefed<IDBOpenDBRequest>
 IDBOpenDBRequest::CreateForJS(IDBFactory* aFactory,
                               JS::Handle<JSObject*> aScriptOwner)
 {
   MOZ_ASSERT(aFactory);
   aFactory->AssertIsOnOwningThread();
   MOZ_ASSERT(aScriptOwner);
 
   nsRefPtr<IDBOpenDBRequest> request = new IDBOpenDBRequest(aFactory, nullptr);
-  request->CaptureCaller();
+  CaptureCaller(request->mFilename, &request->mLineNo);
 
   request->SetScriptOwner(aScriptOwner);
 
   return request.forget();
 }
 
 void
 IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction)
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -76,16 +76,19 @@ public:
          IDBDatabase* aDatabase,
          IDBTransaction* aTransaction);
 
   static already_AddRefed<IDBRequest>
   Create(IDBIndex* aSource,
          IDBDatabase* aDatabase,
          IDBTransaction* aTransaction);
 
+  static void
+  CaptureCaller(nsAString& aFilename, uint32_t* aLineNo);
+
   // nsIDOMEventTarget
   virtual nsresult
   PreHandleEvent(EventChainPreVisitor& aVisitor) MOZ_OVERRIDE;
 
   void
   GetSource(Nullable<OwningIDBObjectStoreOrIDBIndexOrIDBCursor>& aSource) const;
 
   void
@@ -109,17 +112,17 @@ public:
     return mErrorCode;
   }
 #endif
 
   DOMError*
   GetError(ErrorResult& aRv);
 
   void
-  FillScriptErrorEvent(ErrorEventInit& aEventInit) const;
+  GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const;
 
   bool
   IsPending() const
   {
     return !mHaveResultOrErrorCode;
   }
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
@@ -184,19 +187,16 @@ protected:
   explicit IDBRequest(nsPIDOMWindow* aOwner);
   ~IDBRequest();
 
   void
   InitMembers();
 
   void
   ConstructResult();
-
-  void
-  CaptureCaller();
 };
 
 class NS_NO_VTABLE IDBRequest::ResultCallback
 {
 public:
   virtual nsresult
   GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult) = 0;
 
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -43,16 +43,17 @@ IDBTransaction::IDBTransaction(IDBDataba
                                Mode aMode)
   : IDBWrapperCache(aDatabase)
   , mDatabase(aDatabase)
   , mObjectStoreNames(aObjectStoreNames)
   , mNextObjectStoreId(0)
   , mNextIndexId(0)
   , mAbortCode(NS_OK)
   , mPendingRequestCount(0)
+  , mLineNo(0)
   , mReadyState(IDBTransaction::INITIAL)
   , mMode(aMode)
   , mCreating(false)
   , mAbortedByScript(false)
 #ifdef DEBUG
   , mSentCommitOrAbort(false)
   , mFiredCompleteOrAbort(false)
 #endif
@@ -122,29 +123,33 @@ IDBTransaction::~IDBTransaction()
   }
 }
 
 // static
 already_AddRefed<IDBTransaction>
 IDBTransaction::CreateVersionChange(
                                 IDBDatabase* aDatabase,
                                 BackgroundVersionChangeTransactionChild* aActor,
+                                IDBOpenDBRequest* aOpenRequest,
                                 int64_t aNextObjectStoreId,
                                 int64_t aNextIndexId)
 {
   MOZ_ASSERT(aDatabase);
   aDatabase->AssertIsOnOwningThread();
   MOZ_ASSERT(aActor);
+  MOZ_ASSERT(aOpenRequest);
   MOZ_ASSERT(aNextObjectStoreId > 0);
   MOZ_ASSERT(aNextIndexId > 0);
 
   nsTArray<nsString> emptyObjectStoreNames;
 
   nsRefPtr<IDBTransaction> transaction =
     new IDBTransaction(aDatabase, emptyObjectStoreNames, VERSION_CHANGE);
+  aOpenRequest->GetCallerLocation(transaction->mFilename,
+                                  &transaction->mLineNo);
 
   transaction->SetScriptOwner(aDatabase->GetScriptOwner());
   transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor;
   transaction->mNextObjectStoreId = aNextObjectStoreId;
   transaction->mNextIndexId = aNextIndexId;
 
   // XXX Fix!
   MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
@@ -170,16 +175,17 @@ IDBTransaction::Create(IDBDatabase* aDat
 {
   MOZ_ASSERT(aDatabase);
   aDatabase->AssertIsOnOwningThread();
   MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
   MOZ_ASSERT(aMode == READ_ONLY || aMode == READ_WRITE);
 
   nsRefPtr<IDBTransaction> transaction =
     new IDBTransaction(aDatabase, aObjectStoreNames, aMode);
+  IDBRequest::CaptureCaller(transaction->mFilename, &transaction->mLineNo);
 
   transaction->SetScriptOwner(aDatabase->GetScriptOwner());
 
   // XXX Fix!
   MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
 
   nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
   if (NS_WARN_IF(!appShell) ||
@@ -386,16 +392,26 @@ IDBTransaction::IsOpen() const
   if (mReadyState == IDBTransaction::LOADING &&
       (mCreating || GetCurrent() == this)) {
     return true;
   }
 
   return false;
 }
 
+void
+IDBTransaction::GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const
+{
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aLineNo);
+
+  aFilename = mFilename;
+  *aLineNo = mLineNo;
+}
+
 already_AddRefed<IDBObjectStore>
 IDBTransaction::CreateObjectStore(const ObjectStoreSpec& aSpec)
 {
   AssertIsOnOwningThread();
   MOZ_ASSERT(aSpec.metadata().id());
   MOZ_ASSERT(VERSION_CHANGE == mMode);
   MOZ_ASSERT(mBackgroundActor.mVersionChangeBackgroundActor);
   MOZ_ASSERT(IsOpen());
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -32,16 +32,17 @@ class PBlobChild;
 namespace indexedDB {
 
 class BackgroundCursorChild;
 class BackgroundRequestChild;
 class BackgroundTransactionChild;
 class BackgroundVersionChangeTransactionChild;
 class IDBDatabase;
 class IDBObjectStore;
+class IDBOpenDBRequest;
 class IDBRequest;
 class IndexMetadata;
 class ObjectStoreSpec;
 class OpenCursorParams;
 class PBackgroundIDBDatabaseFileChild;
 class RequestParams;
 
 class IDBTransaction MOZ_FINAL
@@ -89,31 +90,35 @@ private:
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
   uint64_t mSerialNumber;
 #endif
 
   nsresult mAbortCode;
   uint32_t mPendingRequestCount;
 
+  nsString mFilename;
+  uint32_t mLineNo;
+
   ReadyState mReadyState;
   Mode mMode;
 
   bool mCreating;
   bool mAbortedByScript;
 
 #ifdef DEBUG
   bool mSentCommitOrAbort;
   bool mFiredCompleteOrAbort;
 #endif
 
 public:
   static already_AddRefed<IDBTransaction>
   CreateVersionChange(IDBDatabase* aDatabase,
                       BackgroundVersionChangeTransactionChild* aActor,
+                      IDBOpenDBRequest* aOpenRequest,
                       int64_t aNextObjectStoreId,
                       int64_t aNextIndexId);
 
   static already_AddRefed<IDBTransaction>
   Create(IDBDatabase* aDatabase,
          const nsTArray<nsString>& aObjectStoreNames,
          Mode aMode);
 
@@ -179,16 +184,19 @@ public:
 
   bool
   IsAborted() const
   {
     AssertIsOnOwningThread();
     return NS_FAILED(mAbortCode);
   }
 
+  void
+  GetCallerLocation(nsAString& aFilename, uint32_t* aLineNo) const;
+
   // 'Get' prefix is to avoid name collisions with the enum
   Mode
   GetMode() const
   {
     AssertIsOnOwningThread();
     return mMode;
   }
 
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -361,17 +361,17 @@ IndexedDatabaseManager::FireWindowOnErro
 
   nsString errorName;
   if (error) {
     error->GetName(errorName);
   }
 
   ThreadsafeAutoJSContext cx;
   RootedDictionary<ErrorEventInit> init(cx);
-  request->FillScriptErrorEvent(init);
+  request->GetCallerLocation(init.mFilename, &init.mLineno);
 
   init.mMessage = errorName;
   init.mCancelable = true;
   init.mBubbles = true;
 
   nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryInterface(aOwner));
   NS_ASSERTION(sgo, "How can this happen?!");
 
--- a/dom/interfaces/base/moz.build
+++ b/dom/interfaces/base/moz.build
@@ -12,17 +12,16 @@ XPIDL_SOURCES += [
     'nsIContentPrefService.idl',
     'nsIContentPrefService2.idl',
     'nsIContentURIGrouper.idl',
     'nsIDOMChromeWindow.idl',
     'nsIDOMClientRect.idl',
     'nsIDOMClientRectList.idl',
     'nsIDOMConstructor.idl',
     'nsIDOMCrypto.idl',
-    'nsIDOMGlobalObjectConstructor.idl',
     'nsIDOMGlobalPropertyInitializer.idl',
     'nsIDOMHistory.idl',
     'nsIDOMJSWindow.idl',
     'nsIDOMLocation.idl',
     'nsIDOMModalContentWindow.idl',
     'nsIDOMNavigator.idl',
     'nsIDOMScreen.idl',
     'nsIDOMWindow.idl',
deleted file mode 100644
--- a/dom/interfaces/base/nsIDOMGlobalObjectConstructor.idl
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "domstubs.idl"
-
-[scriptable, uuid(cb439c73-0129-4289-a349-c5216e6b912a)]
-interface nsIDOMGlobalObjectConstructor : nsISupports
-{
-  /*
-   * JS use only
-   *
-   * The constructor() method will be called with any parameters passed
-   * to the object constructor.
-   * If the JS implementation returns a value, it will be ignored.
-   */
-  void constructor();
-};
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -204,8 +204,10 @@ KeyNameAppsWarning=KeyboardEvent.key val
 # LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "FastFwd" and "MediaFastForward".
 KeyNameFastFwdWarning=KeyboardEvent.key value "FastFwd" is obsolete and will be renamed to "MediaFastForward". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
 # LOCALIZATION NOTE: Do not translate "KeyboardEvent.key", "Zoom" and "ZoomToggle".
 KeyNameZoomWarning=KeyboardEvent.key value "Zoom" is obsolete and will be renamed to "ZoomToggle". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
 # LOCALIZATION NOTE: Do not translate "KeyboardEvent.key" and "Dead".
 KeyNameDeadKeysWarning=KeyboardEvent.key values starting with "Dead" are obsolete and will be merged into just "Dead". For more help https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent.key
 ImportXULIntoContentWarning=Importing XUL nodes into a content document is deprecated. This functionality may be removed soon.
 XMLDocumentLoadPrincipalMismatch=Use of document.load forbidden on Documents that come from other Windows. Only the Window in which a Document was created is allowed to call .load on that Document. Preferably, use XMLHttpRequest instead.
+# LOCALIZATION NOTE: Do not translate "IndexedDB".
+IndexedDBTransactionAbortNavigation=An IndexedDB transaction that was not yet complete has been aborted due to page navigation.
\ No newline at end of file
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -330,18 +330,19 @@ IsMP4SupportedType(const nsACString& aTy
                    const nsAString& aCodecs = EmptyString())
 {
 // Currently on B2G, FMP4 is only working for MSE playback.
 // For other normal MP4, it still uses current omx decoder.
 // Bug 1061034 is a follow-up bug to enable all MP4s with MOZ_FMP4
 #ifdef MOZ_OMX_DECODER
   return false;
 #else
+  bool haveAAC, haveMP3, haveH264;
   return Preferences::GetBool("media.fragmented-mp4.exposed", false) &&
-         MP4Decoder::CanHandleMediaType(aType, aCodecs);
+         MP4Decoder::CanHandleMediaType(aType, aCodecs, haveAAC, haveH264, haveMP3);
 #endif
 }
 #endif
 
 #ifdef MOZ_APPLEMEDIA
 static const char * const gAppleMP3Types[] = {
   "audio/mp3",
   "audio/mpeg",
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -15,16 +15,17 @@
 namespace mozilla {
 
 namespace dom {
 class TimeRanges;
 }
 
 class RequestSampleCallback;
 class MediaDecoderReader;
+class SharedDecoderManager;
 
 // Encapsulates the decoding and reading of media data. Reading can either
 // synchronous and done on the calling "decode" thread, or asynchronous and
 // performed on a background thread, with the result being returned by
 // callback. Never hold the decoder monitor when calling into this class.
 // Unless otherwise specified, methods and fields of this class can only
 // be accessed on the decode task queue.
 class MediaDecoderReader {
@@ -42,16 +43,17 @@ public:
   // True if this reader is waiting for a Content Decryption Module to become
   // available.
   virtual bool IsWaitingOnCDMResource() { return false; }
   // True when this reader need to become dormant state
   virtual bool IsDormantNeeded() { return false; }
   // Release media resources they should be released in dormant state
   // The reader can be made usable again by calling ReadMetadata().
   virtual void ReleaseMediaResources() {};
+  virtual void SetSharedDecoderManager(SharedDecoderManager* aManager) {}
   // Breaks reference-counted cycles. Called during shutdown.
   // WARNING: If you override this, you must call the base implementation
   // in your override.
   virtual void BreakCycles();
 
   // Destroys the decoding state. The reader cannot be made usable again.
   // This is different from ReleaseMediaResources() as it is irreversable,
   // whereas ReleaseMediaResources() is.
new file mode 100644
--- /dev/null
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -0,0 +1,212 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/MediaKeySystemAccess.h"
+#include "mozilla/dom/MediaKeySystemAccessBinding.h"
+#include "mozilla/Preferences.h"
+#include "nsContentTypeParser.h"
+#ifdef MOZ_FMP4
+#include "MP4Decoder.h"
+#endif
+#ifdef XP_WIN
+#include "mozilla/WindowsVersion.h"
+#endif
+#include "nsContentCID.h"
+#include "nsServiceManagerUtils.h"
+#include "mozIGeckoMediaPluginService.h"
+#include "VideoUtils.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeySystemAccess,
+                                      mParent)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccess)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccess)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaKeySystemAccess)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+MediaKeySystemAccess::MediaKeySystemAccess(nsPIDOMWindow* aParent,
+                                           const nsAString& aKeySystem)
+  : mParent(aParent)
+  , mKeySystem(aKeySystem)
+{
+}
+
+MediaKeySystemAccess::~MediaKeySystemAccess()
+{
+}
+
+JSObject*
+MediaKeySystemAccess::WrapObject(JSContext* aCx)
+{
+  return MediaKeySystemAccessBinding::Wrap(aCx, this);
+}
+
+nsPIDOMWindow*
+MediaKeySystemAccess::GetParentObject() const
+{
+  return mParent;
+}
+
+void
+MediaKeySystemAccess::GetKeySystem(nsString& aRetVal) const
+{
+  aRetVal = mKeySystem;
+}
+
+already_AddRefed<Promise>
+MediaKeySystemAccess::CreateMediaKeys(ErrorResult& aRv)
+{
+  nsRefPtr<MediaKeys> keys(new MediaKeys(mParent, mKeySystem));
+  return keys->Init(aRv);
+}
+
+static bool
+HaveGMPFor(mozIGeckoMediaPluginService* aGMPService,
+           const nsCString& aKeySystem,
+           const nsCString& aAPI,
+           const nsCString& aTag = EmptyCString())
+{
+  nsTArray<nsCString> tags;
+  tags.AppendElement(aKeySystem);
+  if (!aTag.IsEmpty()) {
+    tags.AppendElement(aTag);
+  }
+  // Note: EME plugins need a non-null nodeId here, as they must
+  // not be shared across origins.
+  bool hasPlugin = false;
+  if (NS_FAILED(aGMPService->HasPluginForAPI(aAPI,
+                                             &tags,
+                                             &hasPlugin))) {
+    return false;
+  }
+  return hasPlugin;
+}
+
+/* static */
+bool
+MediaKeySystemAccess::IsKeySystemSupported(const nsAString& aKeySystem)
+{
+  nsCOMPtr<mozIGeckoMediaPluginService> mps =
+    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  if (NS_WARN_IF(!mps)) {
+    return false;
+  }
+
+  if (aKeySystem.EqualsLiteral("org.w3.clearkey") &&
+      HaveGMPFor(mps,
+                 NS_LITERAL_CSTRING("org.w3.clearkey"),
+                 NS_LITERAL_CSTRING("eme-decrypt"))) {
+    return true;
+  }
+
+#ifdef XP_WIN
+  if (aKeySystem.EqualsLiteral("com.adobe.access") &&
+      Preferences::GetBool("media.eme.adobe-access.enabled", false) &&
+      IsVistaOrLater() && // Win Vista and later only.
+      HaveGMPFor(mps,
+                 NS_LITERAL_CSTRING("com.adobe.access"),
+                 NS_LITERAL_CSTRING("eme-decrypt"))) {
+      return true;
+  }
+#endif
+
+  return false;
+}
+
+static bool
+IsPlayableWithGMP(mozIGeckoMediaPluginService* aGMPS,
+                  const nsAString& aKeySystem,
+                  const nsAString& aContentType)
+{
+#ifdef MOZ_FMP4
+  nsContentTypeParser parser(aContentType);
+  nsAutoString mimeType;
+  nsresult rv = parser.GetType(mimeType);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  if (!mimeType.EqualsLiteral("audio/mp4") &&
+      !mimeType.EqualsLiteral("audio/x-m4a") &&
+      !mimeType.EqualsLiteral("video/mp4")) {
+    return false;
+  }
+
+  nsAutoString codecs;
+  parser.GetParameter("codecs", codecs);
+
+  NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
+  bool hasAAC = false;
+  bool hasH264 = false;
+  bool hasMP3 = false;
+  if (!MP4Decoder::CanHandleMediaType(mimeTypeUTF8,
+                                      codecs,
+                                      hasAAC,
+                                      hasH264,
+                                      hasMP3) ||
+      hasMP3) {
+    return false;
+  }
+  return (!hasAAC || !HaveGMPFor(aGMPS,
+                                 NS_ConvertUTF16toUTF8(aKeySystem),
+                                 NS_LITERAL_CSTRING("eme-decrypt"),
+                                 NS_LITERAL_CSTRING("aac"))) &&
+         (!hasH264 || !HaveGMPFor(aGMPS,
+                                  NS_ConvertUTF16toUTF8(aKeySystem),
+                                  NS_LITERAL_CSTRING("eme-decrypt"),
+                                  NS_LITERAL_CSTRING("h264")));
+#else
+  return false;
+#endif
+}
+
+/* static */
+bool
+MediaKeySystemAccess::IsSupported(const nsAString& aKeySystem,
+                                  const Sequence<MediaKeySystemOptions>& aOptions)
+{
+  nsCOMPtr<mozIGeckoMediaPluginService> mps =
+    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  if (NS_WARN_IF(!mps)) {
+    return false;
+  }
+
+  for (size_t i = 0; i < aOptions.Length(); i++) {
+    const MediaKeySystemOptions& options = aOptions[i];
+    if (!options.mInitDataType.EqualsLiteral("cenc")) {
+      continue;
+    }
+    if (!options.mAudioCapability.IsEmpty() ||
+        !options.mVideoCapability.IsEmpty()) {
+      // Don't support any capabilites until we know we have a CDM with
+      // capabilities...
+      continue;
+    }
+    if (!options.mAudioType.IsEmpty() &&
+        !IsPlayableWithGMP(mps, aKeySystem, options.mAudioType)) {
+      continue;
+    }
+    if (!options.mVideoType.IsEmpty() &&
+        !IsPlayableWithGMP(mps, aKeySystem, options.mVideoType)) {
+      continue;
+    }
+
+    // Our sandbox provides an origin specific unique identifier, and the
+    // ability to persist data. We don't yet have a way to turn those off
+    // and on for specific GMPs/CDMs, so we don't check the uniqueidentifier
+    // and stateful attributes here.
+
+    return true;
+  }
+  return false;
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/media/eme/MediaKeySystemAccess.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_MediaKeySystemAccess_h
+#define mozilla_dom_MediaKeySystemAccess_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/ErrorResult.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/MediaKeySystemAccessBinding.h"
+#include "js/TypeDecls.h"
+
+namespace mozilla {
+namespace dom {
+
+class MediaKeySystemAccess MOZ_FINAL : public nsISupports,
+                                       public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeySystemAccess)
+
+public:
+  explicit MediaKeySystemAccess(nsPIDOMWindow* aParent,
+                                const nsAString& aKeySystem);
+
+protected:
+  ~MediaKeySystemAccess();
+
+public:
+  nsPIDOMWindow* GetParentObject() const;
+
+  virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
+
+  void GetKeySystem(nsString& aRetVal) const;
+
+  already_AddRefed<Promise> CreateMediaKeys(ErrorResult& aRv);
+
+  static bool IsKeySystemSupported(const nsAString& aKeySystem);
+
+  static bool IsSupported(const nsAString& aKeySystem,
+                          const Sequence<MediaKeySystemOptions>& aOptions);
+
+private:
+  nsCOMPtr<nsPIDOMWindow> mParent;
+  const nsString mKeySystem;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MediaKeySystemAccess_h
--- a/dom/media/eme/MediaKeys.cpp
+++ b/dom/media/eme/MediaKeys.cpp
@@ -109,121 +109,16 @@ MediaKeys::SetServerCertificate(const Ar
     promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return promise.forget();
   }
 
   mProxy->SetServerCertificate(StorePromise(promise), data);
   return promise.forget();
 }
 
-static bool
-HaveGMPFor(const nsCString& aKeySystem,
-           const nsCString& aAPI,
-           const nsCString& aTag = EmptyCString())
-{
-  nsCOMPtr<mozIGeckoMediaPluginService> mps =
-    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
-  if (NS_WARN_IF(!mps)) {
-    return false;
-  }
-
-  nsTArray<nsCString> tags;
-  tags.AppendElement(aKeySystem);
-  if (!aTag.IsEmpty()) {
-    tags.AppendElement(aTag);
-  }
-  // Note: EME plugins need a non-null nodeId here, as they must
-  // not be shared across origins.
-  bool hasPlugin = false;
-  if (NS_FAILED(mps->HasPluginForAPI(aAPI,
-                                     &tags,
-                                     &hasPlugin))) {
-    return false;
-  }
-  return hasPlugin;
-}
-
-static bool
-IsPlayableMP4Type(const nsAString& aContentType)
-{
-#ifdef MOZ_FMP4
-  nsContentTypeParser parser(aContentType);
-  nsAutoString mimeType;
-  nsresult rv = parser.GetType(mimeType);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-  nsAutoString codecs;
-  parser.GetParameter("codecs", codecs);
-
-  NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
-  return MP4Decoder::CanHandleMediaType(mimeTypeUTF8, codecs);
-#else
-  return false;
-#endif
-}
-
-bool
-MediaKeys::IsTypeSupported(const nsAString& aKeySystem,
-                           const Optional<nsAString>& aInitDataType,
-                           const Optional<nsAString>& aContentType)
-{
-  if (aKeySystem.EqualsLiteral("org.w3.clearkey") &&
-      (!aInitDataType.WasPassed() || aInitDataType.Value().EqualsLiteral("cenc")) &&
-      (!aContentType.WasPassed() || IsPlayableMP4Type(aContentType.Value())) &&
-      HaveGMPFor(NS_LITERAL_CSTRING("org.w3.clearkey"),
-                 NS_LITERAL_CSTRING("eme-decrypt"))) {
-    return true;
-  }
-
-#ifdef XP_WIN
-  // Note: Adobe Access's GMP uses WMF to decode, so anything our MP4Reader
-  // thinks it can play on Windows, the Access GMP should be able to play.
-  if (aKeySystem.EqualsLiteral("com.adobe.access") &&
-      Preferences::GetBool("media.eme.adobe-access.enabled", false) &&
-      IsVistaOrLater() && // Win Vista and later only.
-      (!aInitDataType.WasPassed() || aInitDataType.Value().EqualsLiteral("cenc")) &&
-      (!aContentType.WasPassed() || IsPlayableMP4Type(aContentType.Value())) &&
-      HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
-                 NS_LITERAL_CSTRING("eme-decrypt")) &&
-      HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
-                 NS_LITERAL_CSTRING("decode-video"),
-                 NS_LITERAL_CSTRING("h264")) &&
-      HaveGMPFor(NS_LITERAL_CSTRING("com.adobe.access"),
-                 NS_LITERAL_CSTRING("decode-audio"),
-                 NS_LITERAL_CSTRING("aac"))) {
-      return true;
-  }
-#endif
-
-  return false;
-}
-
-/* static */
-IsTypeSupportedResult
-MediaKeys::IsTypeSupported(const GlobalObject& aGlobal,
-                           const nsAString& aKeySystem,
-                           const Optional<nsAString>& aInitDataType,
-                           const Optional<nsAString>& aContentType,
-                           const Optional<nsAString>& aCapability)
-{
-  // TODO: Should really get spec changed to this is async, so we can wait
-  //       for user to consent to running plugin.
-  bool supported = IsTypeSupported(aKeySystem, aInitDataType, aContentType);
-
-  EME_LOG("MediaKeys::IsTypeSupported keySystem='%s' initDataType='%s' contentType='%s' supported=%d",
-          NS_ConvertUTF16toUTF8(aKeySystem).get(),
-          (aInitDataType.WasPassed() ? NS_ConvertUTF16toUTF8(aInitDataType.Value()).get() : ""),
-          (aContentType.WasPassed() ? NS_ConvertUTF16toUTF8(aContentType.Value()).get() : ""),
-          supported);
-
-  return supported ? IsTypeSupportedResult::Probably
-                   : IsTypeSupportedResult::_empty;
-}
-
 already_AddRefed<Promise>
 MediaKeys::MakePromise(ErrorResult& aRv)
 {
   nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
   if (!global) {
     NS_WARNING("Passed non-global to MediaKeys ctor!");
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
@@ -298,47 +193,24 @@ MediaKeys::ResolvePromise(PromiseId aId)
     mPendingSessions.Remove(aId);
     mKeySessions.Put(session->GetSessionId(), session);
     promise->MaybeResolve(session);
   } else {
     promise->MaybeResolve(JS::UndefinedHandleValue);
   }
 }
 
-/* static */
-already_AddRefed<Promise>
-MediaKeys::Create(const GlobalObject& aGlobal,
-                  const nsAString& aKeySystem,
-                  ErrorResult& aRv)
-{
-  // CDMProxy keeps MediaKeys alive until it resolves the promise and thus
-  // returns the MediaKeys object to JS.
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aGlobal.GetAsSupports());
-  if (!window || !window->GetExtantDoc()) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-
-  nsRefPtr<MediaKeys> keys = new MediaKeys(window, aKeySystem);
-  return keys->Init(aRv);
-}
-
 already_AddRefed<Promise>
 MediaKeys::Init(ErrorResult& aRv)
 {
   nsRefPtr<Promise> promise(MakePromise(aRv));
   if (aRv.Failed()) {
     return nullptr;
   }
 
-  if (!IsTypeSupported(mKeySystem)) {
-    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return promise.forget();
-  }
-
   mProxy = new CDMProxy(this, mKeySystem);
 
   // Determine principal (at creation time) of the MediaKeys object.
   nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(GetParentObject());
   if (!sop) {
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
--- a/dom/media/eme/MediaKeys.h
+++ b/dom/media/eme/MediaKeys.h
@@ -48,16 +48,18 @@ class MediaKeys MOZ_FINAL : public nsISu
   ~MediaKeys();
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(MediaKeys)
 
   MediaKeys(nsPIDOMWindow* aParentWindow, const nsAString& aKeySystem);
 
+  already_AddRefed<Promise> Init(ErrorResult& aRv);
+
   nsPIDOMWindow* GetParentObject() const;
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   nsresult Bind(HTMLMediaElement* aElement);
 
   // Javascript: readonly attribute DOMString keySystem;
   void GetKeySystem(nsString& retval) const;
@@ -65,29 +67,16 @@ public:
   // JavaScript: MediaKeys.createSession()
   already_AddRefed<MediaKeySession> CreateSession(SessionType aSessionType,
                                                   ErrorResult& aRv);
 
   // JavaScript: MediaKeys.SetServerCertificate()
   already_AddRefed<Promise> SetServerCertificate(const ArrayBufferViewOrArrayBuffer& aServerCertificate,
                                                  ErrorResult& aRv);
 
-  // JavaScript: MediaKeys.create()
-  static
-  already_AddRefed<Promise> Create(const GlobalObject& aGlobal,
-                                   const nsAString& aKeySystem,
-                                   ErrorResult& aRv);
-
-  // JavaScript: MediaKeys.IsTypeSupported()
-  static IsTypeSupportedResult IsTypeSupported(const GlobalObject& aGlobal,
-                                               const nsAString& aKeySystem,
-                                               const Optional<nsAString>& aInitDataType,
-                                               const Optional<nsAString>& aContentType,
-                                               const Optional<nsAString>& aCapability);
-
   already_AddRefed<MediaKeySession> GetSession(const nsAString& aSessionId);
 
   // Called once a Create() operation succeeds.
   void OnCDMCreated(PromiseId aId, const nsACString& aNodeId);
   // Called when GenerateRequest or Load have been called on a MediaKeySession
   // and we are waiting for its initialisation to finish.
   void OnSessionPending(PromiseId aId, MediaKeySession* aSession);
   // Called once a CreateSession succeeds.
@@ -123,22 +112,17 @@ public:
   nsresult CheckPrincipals();
 
   // Returns a pointer to the bound media element's owner doc.
   // If we're not bound, this returns null.
   nsIDocument* GetOwnerDoc() const;
 
 private:
 
-  static bool IsTypeSupported(const nsAString& aKeySystem,
-                              const Optional<nsAString>& aInitDataType = Optional<nsAString>(),
-                              const Optional<nsAString>& aContentType = Optional<nsAString>());
-
   bool IsInPrivateBrowsing();
-  already_AddRefed<Promise> Init(ErrorResult& aRv);
 
   // Removes promise from mPromises, and returns it.
   already_AddRefed<Promise> RetrievePromise(PromiseId aId);
 
   // Owning ref to proxy. The proxy has a weak reference back to the MediaKeys,
   // and the MediaKeys destructor clears the proxy's reference to the MediaKeys.
   nsRefPtr<CDMProxy> mProxy;
 
--- a/dom/media/eme/moz.build
+++ b/dom/media/eme/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.dom += [
     'MediaEncryptedEvent.h',
     'MediaKeyError.h',
     'MediaKeyMessageEvent.h',
     'MediaKeys.h',
     'MediaKeySession.h',
+    'MediaKeySystemAccess.h',
 ]
 
 EXPORTS.mozilla += [
     'CDMCallbackProxy.h',
     'CDMCaps.h',
     'CDMProxy.h',
     'EMELog.h'
 ]
@@ -24,13 +25,14 @@ UNIFIED_SOURCES += [
     'CDMCaps.cpp',
     'CDMProxy.cpp',
     'EMELog.cpp',
     'MediaEncryptedEvent.cpp',
     'MediaKeyError.cpp',
     'MediaKeyMessageEvent.cpp',
     'MediaKeys.cpp',
     'MediaKeySession.cpp',
+    'MediaKeySystemAccess.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 
 FAIL_ON_WARNINGS = True
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -49,24 +49,35 @@ MP4Decoder::SetCDMProxy(CDMProxy* aProxy
       NS_NewRunnableMethod(this, &MediaDecoder::NotifyWaitingForResourcesStatusChanged));
     caps.CallOnMainThreadWhenCapsAvailable(task);
   }
   return NS_OK;
 }
 #endif
 
 static bool
-IsSupportedAudioCodec(const nsAString& aCodec)
+IsSupportedAudioCodec(const nsAString& aCodec,
+                      bool& aOutContainsAAC,
+                      bool& aOutContainsMP3)
 {
-  // AAC-LC, HE-AAC or MP3 in M4A.
-  return aCodec.EqualsASCII("mp4a.40.2") ||
+  // AAC-LC or HE-AAC in M4A.
+  aOutContainsAAC = aCodec.EqualsASCII("mp4a.40.2") ||
+                    aCodec.EqualsASCII("mp4a.40.5");
+  if (aOutContainsAAC) {
+    return true;
+  }
 #ifndef MOZ_GONK_MEDIACODEC // B2G doesn't support MP3 in MP4 yet.
-         aCodec.EqualsASCII("mp3") ||
+  aOutContainsMP3 = aCodec.EqualsASCII("mp3");
+  if (aOutContainsMP3) {
+    return true;
+  }
+#else
+  aOutContainsMP3 = false;
 #endif
-         aCodec.EqualsASCII("mp4a.40.5");
+  return false;
 }
 
 static bool
 IsSupportedH264Codec(const nsAString& aCodec)
 {
   int16_t profile = 0, level = 0;
 
   if (!ExtractH264CodecDetails(aCodec, profile, level)) {
@@ -87,48 +98,60 @@ IsSupportedH264Codec(const nsAString& aC
           profile == H264_PROFILE_MAIN ||
           profile == H264_PROFILE_EXTENDED ||
           profile == H264_PROFILE_HIGH);
 }
 
 /* static */
 bool
 MP4Decoder::CanHandleMediaType(const nsACString& aType,
-                               const nsAString& aCodecs)
+                               const nsAString& aCodecs,
+                               bool& aOutContainsAAC,
+                               bool& aOutContainsH264,
+                               bool& aOutContainsMP3)
 {
   if (!IsEnabled()) {
     return false;
   }
 
   if (aType.EqualsASCII("audio/mp4") || aType.EqualsASCII("audio/x-m4a")) {
-    return aCodecs.IsEmpty() || IsSupportedAudioCodec(aCodecs);
+    return aCodecs.IsEmpty() ||
+           IsSupportedAudioCodec(aCodecs,
+                                 aOutContainsAAC,
+                                 aOutContainsMP3);
   }
 
   if (!aType.EqualsASCII("video/mp4")) {
     return false;
   }
 
   // Verify that all the codecs specifed are ones that we expect that
   // we can play.
   nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
   bool expectMoreTokens = false;
   while (tokenizer.hasMoreTokens()) {
     const nsSubstring& token = tokenizer.nextToken();
     expectMoreTokens = tokenizer.separatorAfterCurrentToken();
-    if (IsSupportedAudioCodec(token) || IsSupportedH264Codec(token)) {
+    if (IsSupportedAudioCodec(token,
+                              aOutContainsAAC,
+                              aOutContainsMP3)) {
+      continue;
+    }
+    if (IsSupportedH264Codec(token)) {
+      aOutContainsH264 = true;
       continue;
     }
     return false;
   }
   if (expectMoreTokens) {
     // Last codec name was empty
     return false;
   }
+
   return true;
-
 }
 
 static bool
 IsFFmpegAvailable()
 {
 #ifndef MOZ_FFMPEG
   return false;
 #else
--- a/dom/media/fmp4/MP4Decoder.h
+++ b/dom/media/fmp4/MP4Decoder.h
@@ -25,19 +25,23 @@ public:
   virtual MediaDecoderStateMachine* CreateStateMachine();
 
 #ifdef MOZ_EME
   virtual nsresult SetCDMProxy(CDMProxy* aProxy) MOZ_OVERRIDE;
 #endif
 
   // Returns true if aMIMEType is a type that we think we can render with the
   // a MP4 platform decoder backend. If aCodecs is non emtpy, it is filled
-  // with a comma-delimited list of codecs to check support for.
+  // with a comma-delimited list of codecs to check support for. Notes in
+  // out params wether the codecs string contains AAC or H.264.
   static bool CanHandleMediaType(const nsACString& aMIMEType,
-                                 const nsAString& aCodecs = EmptyString());
+                                 const nsAString& aCodecs,
+                                 bool& aOutContainsAAC,
+                                 bool& aOutContainsH264,
+                                 bool& aOutContainsMP3);
 
   // Returns true if the MP4 backend is preffed on, and we're running on a
   // platform that is likely to have decoders for the contained formats.
   static bool IsEnabled();
 };
 
 } // namespace mozilla
 
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -9,16 +9,17 @@
 #include "nsSize.h"
 #include "VideoUtils.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "ImageContainer.h"
 #include "Layers.h"
 #include "SharedThreadPool.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/TimeRanges.h"
+#include "SharedDecoderManager.h"
 
 #ifdef MOZ_EME
 #include "mozilla/CDMProxy.h"
 #endif
 
 using mozilla::layers::Image;
 using mozilla::layers::LayerManager;
 using mozilla::layers::LayersBackend;
@@ -409,22 +410,31 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo
     nsresult rv = mAudio.mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (HasVideo()) {
     const VideoDecoderConfig& video = mDemuxer->VideoConfig();
     mInfo.mVideo.mDisplay =
       nsIntSize(video.display_width, video.display_height);
-    mVideo.mCallback = new  DecoderCallback(this, kVideo);
-    mVideo.mDecoder = mPlatform->CreateH264Decoder(video,
-                                                   mLayersBackendType,
-                                                   mDecoder->GetImageContainer(),
-                                                   mVideo.mTaskQueue,
-                                                   mVideo.mCallback);
+    mVideo.mCallback = new DecoderCallback(this, kVideo);
+    if (mSharedDecoderManager) {
+      mVideo.mDecoder =
+        mSharedDecoderManager->CreateH264Decoder(video,
+                                                 mLayersBackendType,
+                                                 mDecoder->GetImageContainer(),
+                                                 mVideo.mTaskQueue,
+                                                 mVideo.mCallback);
+    } else {
+      mVideo.mDecoder = mPlatform->CreateH264Decoder(video,
+                                                     mLayersBackendType,
+                                                     mDecoder->GetImageContainer(),
+                                                     mVideo.mTaskQueue,
+                                                     mVideo.mCallback);
+    }
     NS_ENSURE_TRUE(mVideo.mDecoder != nullptr, NS_ERROR_FAILURE);
     nsresult rv = mVideo.mDecoder->Init();
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // Get the duration, and report it to the decoder if we have it.
   Microseconds duration = mDemuxer->Duration();
   if (duration != -1) {
@@ -468,22 +478,16 @@ MP4Reader::HasVideo()
 
 MP4Reader::DecoderData&
 MP4Reader::GetDecoderData(TrackType aTrack)
 {
   MOZ_ASSERT(aTrack == kAudio || aTrack == kVideo);
   return (aTrack == kAudio) ? mAudio : mVideo;
 }
 
-MediaDataDecoder*
-MP4Reader::Decoder(TrackType aTrack)
-{
-  return GetDecoderData(aTrack).mDecoder;
-}
-
 MP4Sample*
 MP4Reader::PopSample(TrackType aTrack)
 {
   switch (aTrack) {
     case kAudio:
       return mDemuxer->DemuxAudioSample();
 
     case kVideo:
@@ -879,9 +883,26 @@ void MP4Reader::NotifyResourcesStatusCha
 {
 #ifdef MOZ_GONK_MEDIACODEC
   if (mDecoder) {
     mDecoder->NotifyWaitingForResourcesStatusChanged();
   }
 #endif
 }
 
+void
+MP4Reader::SetIdle()
+{
+  if (mSharedDecoderManager && mVideo.mDecoder) {
+    mSharedDecoderManager->SetIdle(mVideo.mDecoder);
+    NotifyResourcesStatusChanged();
+  }
+}
+
+void
+MP4Reader::SetSharedDecoderManager(SharedDecoderManager* aManager)
+{
+#ifdef MOZ_GONK_MEDIACODEC
+  mSharedDecoderManager = aManager;
+#endif
+}
+
 } // namespace mozilla
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -57,19 +57,22 @@ public:
   virtual void NotifyDataArrived(const char* aBuffer, uint32_t aLength,
                                  int64_t aOffset) MOZ_OVERRIDE;
 
   virtual int64_t GetEvictionOffset(double aTime) MOZ_OVERRIDE;
 
   virtual nsresult GetBuffered(dom::TimeRanges* aBuffered) MOZ_OVERRIDE;
 
   // For Media Resource Management
+  virtual void SetIdle() MOZ_OVERRIDE;
   virtual bool IsWaitingMediaResources() MOZ_OVERRIDE;
   virtual bool IsDormantNeeded() MOZ_OVERRIDE;
   virtual void ReleaseMediaResources() MOZ_OVERRIDE;
+  virtual void SetSharedDecoderManager(SharedDecoderManager* aManager)
+    MOZ_OVERRIDE;
 
   virtual nsresult ResetDecode() MOZ_OVERRIDE;
 
   virtual void Shutdown() MOZ_OVERRIDE;
 
 private:
 
   void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData);
@@ -174,27 +177,27 @@ private:
 
   // The last number of decoded output frames that we've reported to
   // MediaDecoder::NotifyDecoded(). We diff the number of output video
   // frames every time that DecodeVideoData() is called, and report the
   // delta there.
   uint64_t mLastReportedNumDecodedFrames;
 
   DecoderData& GetDecoderData(mp4_demuxer::TrackType aTrack);
-  MediaDataDecoder* Decoder(mp4_demuxer::TrackType aTrack);
 
   layers::LayersBackend mLayersBackendType;
 
   nsTArray<nsTArray<uint8_t>> mInitDataEncountered;
 
   // True if we've read the streams' metadata.
   bool mDemuxerInitialized;
 
   // Synchronized by decoder monitor.
   bool mIsEncrypted;
 
   bool mIndexReady;
   Monitor mIndexMonitor;
+  nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/fmp4/PlatformDecoderModule.h
+++ b/dom/media/fmp4/PlatformDecoderModule.h
@@ -225,15 +225,15 @@ public:
 
   // For Codec Resource Management
   virtual bool IsWaitingMediaResources() {
     return false;
   };
   virtual bool IsDormantNeeded() {
     return false;
   };
-  virtual void ReleaseMediaResources() {};
-  virtual void ReleaseDecoder() {};
+  virtual void ReleaseMediaResources() {}
+  virtual void ReleaseDecoder() {}
 };
 
 } // namespace mozilla
 
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/media/fmp4/SharedDecoderManager.cpp
@@ -0,0 +1,207 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedDecoderManager.h"
+#include "mp4_demuxer/DecoderData.h"
+
+namespace mozilla {
+
+class SharedDecoderCallback : public MediaDataDecoderCallback
+{
+public:
+  SharedDecoderCallback(SharedDecoderManager* aManager) : mManager(aManager) {}
+
+  virtual void Output(MediaData* aData) MOZ_OVERRIDE
+  {
+    if (mManager->mActiveCallback) {
+      mManager->mActiveCallback->Output(aData);
+    }
+  }
+  virtual void Error() MOZ_OVERRIDE
+  {
+    if (mManager->mActiveCallback) {
+      mManager->mActiveCallback->Error();
+    }
+  }
+  virtual void InputExhausted() MOZ_OVERRIDE
+  {
+    if (mManager->mActiveCallback) {
+      mManager->mActiveCallback->InputExhausted();
+    }
+  }
+  virtual void DrainComplete() MOZ_OVERRIDE
+  {
+    if (mManager->mActiveCallback) {
+      mManager->DrainComplete();
+    }
+  }
+  virtual void NotifyResourcesStatusChanged() MOZ_OVERRIDE
+  {
+    if (mManager->mActiveCallback) {
+      mManager->mActiveCallback->NotifyResourcesStatusChanged();
+    }
+  }
+  virtual void ReleaseMediaResources() MOZ_OVERRIDE
+  {
+    if (mManager->mActiveCallback) {
+      mManager->mActiveCallback->ReleaseMediaResources();
+    }
+  }
+
+  SharedDecoderManager* mManager;
+};
+
+SharedDecoderManager::SharedDecoderManager()
+  : mActiveProxy(nullptr)
+  , mActiveCallback(nullptr)
+  , mWaitForInternalDrain(false)
+  , mMonitor("SharedDecoderProxy")
+{
+  mCallback = new SharedDecoderCallback(this);
+}
+
+SharedDecoderManager::~SharedDecoderManager() {}
+
+already_AddRefed<MediaDataDecoder>
+SharedDecoderManager::CreateH264Decoder(
+  const mp4_demuxer::VideoDecoderConfig& aConfig,
+  layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer,
+  MediaTaskQueue* aVideoTaskQueue, MediaDataDecoderCallback* aCallback)
+{
+  if (!mDecoder) {
+    nsAutoPtr<PlatformDecoderModule> platform(PlatformDecoderModule::Create());
+    mDecoder = platform->CreateH264Decoder(
+      aConfig, aLayersBackend, aImageContainer, aVideoTaskQueue, mCallback);
+    if (!mDecoder) {
+      return nullptr;
+    }
+    nsresult rv = mDecoder->Init();
+    NS_ENSURE_SUCCESS(rv, nullptr);
+  }
+
+  nsRefPtr<SharedDecoderProxy> proxy(new SharedDecoderProxy(this, aCallback));
+  return proxy.forget();
+}
+
+void
+SharedDecoderManager::Select(SharedDecoderProxy* aProxy)
+{
+  if (mActiveProxy == aProxy) {
+    return;
+  }
+  SetIdle(mActiveProxy);
+
+  mActiveProxy = aProxy;
+  mActiveCallback = aProxy->mCallback;
+}
+
+void
+SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy)
+{
+  if (aProxy && mActiveProxy == aProxy) {
+    mWaitForInternalDrain = true;
+    mActiveProxy->Drain();
+    MonitorAutoLock mon(mMonitor);
+    while (mWaitForInternalDrain) {
+      mon.Wait();
+    }
+    mActiveProxy->Flush();
+    mActiveProxy = nullptr;
+  }
+}
+
+void
+SharedDecoderManager::DrainComplete()
+{
+  if (mWaitForInternalDrain) {
+    MonitorAutoLock mon(mMonitor);
+    mWaitForInternalDrain = false;
+    mon.NotifyAll();
+  } else {
+    mActiveCallback->DrainComplete();
+  }
+}
+
+SharedDecoderProxy::SharedDecoderProxy(
+  SharedDecoderManager* aManager, MediaDataDecoderCallback* aCallback)
+  : mManager(aManager), mCallback(aCallback)
+{
+}
+
+SharedDecoderProxy::~SharedDecoderProxy() { Shutdown(); }
+
+nsresult
+SharedDecoderProxy::Init()
+{
+  return NS_OK;
+}
+
+nsresult
+SharedDecoderProxy::Input(mp4_demuxer::MP4Sample* aSample)
+{
+  if (mManager->mActiveProxy != this) {
+    mManager->Select(this);
+  }
+  return mManager->mDecoder->Input(aSample);
+  return NS_OK;
+}
+
+nsresult
+SharedDecoderProxy::Flush()
+{
+  if (mManager->mActiveProxy == this) {
+    return mManager->mDecoder->Flush();
+  }
+  return NS_OK;
+}
+
+nsresult
+SharedDecoderProxy::Drain()
+{
+  if (mManager->mActiveProxy == this) {
+    return mManager->mDecoder->Drain();
+  }
+  return NS_OK;
+}
+
+nsresult
+SharedDecoderProxy::Shutdown()
+{
+  mManager->SetIdle(this);
+  return NS_OK;
+}
+
+bool
+SharedDecoderProxy::IsWaitingMediaResources()
+{
+  if (mManager->mActiveProxy == this) {
+    return mManager->mDecoder->IsWaitingMediaResources();
+  }
+  return mManager->mActiveProxy != nullptr;
+}
+
+bool
+SharedDecoderProxy::IsDormantNeeded()
+{
+  return mManager->mDecoder->IsDormantNeeded();
+}
+
+void
+SharedDecoderProxy::ReleaseMediaResources()
+{
+  if (mManager->mActiveProxy == this) {
+    mManager->mDecoder->ReleaseMediaResources();
+  }
+}
+
+void
+SharedDecoderProxy::ReleaseDecoder()
+{
+  if (mManager->mActiveProxy == this) {
+    mManager->mDecoder->ReleaseMediaResources();
+  }
+}
+}
new file mode 100644
--- /dev/null
+++ b/dom/media/fmp4/SharedDecoderManager.h
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef SHARED_DECODER_MANAGER_H_
+#define SHARED_DECODER_MANAGER_H_
+
+#include "PlatformDecoderModule.h"
+#include "mozilla/Monitor.h"
+
+namespace mozilla
+{
+
+class MediaDataDecoder;
+class SharedDecoderProxy;
+class SharedDecoderCallback;
+
+class SharedDecoderManager
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedDecoderManager)
+
+  SharedDecoderManager();
+
+  already_AddRefed<MediaDataDecoder> CreateH264Decoder(
+    const mp4_demuxer::VideoDecoderConfig& aConfig,
+    layers::LayersBackend aLayersBackend,
+    layers::ImageContainer* aImageContainer, MediaTaskQueue* aVideoTaskQueue,
+    MediaDataDecoderCallback* aCallback);
+
+  void SetReader(MediaDecoderReader* aReader);
+  void Select(SharedDecoderProxy* aProxy);
+  void SetIdle(MediaDataDecoder* aProxy);
+
+  friend class SharedDecoderProxy;
+  friend class SharedDecoderCallback;
+
+private:
+  virtual ~SharedDecoderManager();
+  void DrainComplete();
+
+  nsRefPtr<MediaDataDecoder> mDecoder;
+  SharedDecoderProxy* mActiveProxy;
+  MediaDataDecoderCallback* mActiveCallback;
+  nsAutoPtr<MediaDataDecoderCallback> mCallback;
+  bool mWaitForInternalDrain;
+  Monitor mMonitor;
+};
+
+class SharedDecoderProxy : public MediaDataDecoder
+{
+public:
+  SharedDecoderProxy(SharedDecoderManager* aManager,
+                     MediaDataDecoderCallback* aCallback);
+  virtual ~SharedDecoderProxy();
+
+  virtual nsresult Init() MOZ_OVERRIDE;
+  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
+  virtual nsresult Flush() MOZ_OVERRIDE;
+  virtual nsresult Drain() MOZ_OVERRIDE;
+  virtual nsresult Shutdown() MOZ_OVERRIDE;
+  virtual bool IsWaitingMediaResources() MOZ_OVERRIDE;
+  virtual bool IsDormantNeeded() MOZ_OVERRIDE;
+  virtual void ReleaseMediaResources() MOZ_OVERRIDE;
+  virtual void ReleaseDecoder() MOZ_OVERRIDE;
+
+  friend class SharedDecoderManager;
+
+private:
+  nsRefPtr<SharedDecoderManager> mManager;
+  MediaDataDecoderCallback* mCallback;
+};
+}
+
+#endif
--- a/dom/media/fmp4/android/AndroidDecoderModule.cpp
+++ b/dom/media/fmp4/android/AndroidDecoderModule.cpp
@@ -3,17 +3,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AndroidDecoderModule.h"
 #include "AndroidBridge.h"
 #include "GLImages.h"
 
 #include "MediaData.h"
 
-#include "mp4_demuxer/Adts.h"
 #include "mp4_demuxer/AnnexB.h"
 #include "mp4_demuxer/DecoderData.h"
 
 #include "nsThreadUtils.h"
 #include "nsAutoPtr.h"
 
 #include <jni.h>
 #include <string.h>
@@ -45,17 +44,17 @@ public:
     , mConfig(aConfig)
   {
 
   }
 
   nsresult Init() MOZ_OVERRIDE {
     mSurfaceTexture = AndroidSurfaceTexture::Create();
     if (!mSurfaceTexture) {
-      printf_stderr("Failed to create SurfaceTexture for video decode\n");
+      NS_WARNING("Failed to create SurfaceTexture for video decode\n");
       return NS_ERROR_FAILURE;
     }
 
     return InitDecoder(mSurfaceTexture->JavaSurface());
   }
 
   virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
     mp4_demuxer::AnnexB::ConvertSample(aSample);
@@ -99,35 +98,16 @@ protected:
 
 class AudioDataDecoder : public MediaCodecDataDecoder {
 public:
   AudioDataDecoder(const char* aMimeType, MediaFormat* aFormat, MediaDataDecoderCallback* aCallback)
   : MediaCodecDataDecoder(MediaData::Type::AUDIO_DATA, aMimeType, aFormat, aCallback)
   {
   }
 
-  virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
-    if (!strcmp(mMimeType, "audio/mp4a-latm")) {
-      uint32_t numChannels = mFormat->GetInteger(NS_LITERAL_CSTRING("channel-count"));
-      uint32_t sampleRate = mFormat->GetInteger(NS_LITERAL_CSTRING("sample-rate"));
-      uint8_t frequencyIndex =
-          mp4_demuxer::Adts::GetFrequencyIndex(sampleRate);
-      uint32_t aacProfile = mFormat->GetInteger(NS_LITERAL_CSTRING("aac-profile"));
-      bool rv = mp4_demuxer::Adts::ConvertSample(numChannels,
-                                                 frequencyIndex,
-                                                 aacProfile,
-                                                 aSample);
-      if (!rv) {
-        printf_stderr("Failed to prepend ADTS header\n");
-        return NS_ERROR_FAILURE;
-      }
-    }
-    return MediaCodecDataDecoder::Input(aSample);
-  }
-
   nsresult Output(BufferInfo* aInfo, void* aBuffer, MediaFormat* aFormat, Microseconds aDuration) {
     // The output on Android is always 16-bit signed
 
     uint32_t numChannels = aFormat->GetInteger(NS_LITERAL_CSTRING("channel-count"));
     uint32_t sampleRate = aFormat->GetInteger(NS_LITERAL_CSTRING("sample-rate"));
     uint32_t numFrames = (aInfo->getSize() / numChannels) / 2;
 
     AudioDataValue* audio = new AudioDataValue[aInfo->getSize()];
@@ -208,21 +188,16 @@ AndroidDecoderModule::CreateAudioDecoder
     csd0[1] = aConfig.audio_specific_config[1];
 
     jobject buffer = env->NewDirectByteBuffer(csd0, 2);
     format->SetByteBuffer(NS_LITERAL_CSTRING("csd-0"), buffer);
 
     env->DeleteLocalRef(buffer);
   }
 
-  if (strcmp(aConfig.mime_type, "audio/mp4a-latm") == 0) {
-    format->SetInteger(NS_LITERAL_CSTRING("is-adts"), 1);
-    format->SetInteger(NS_LITERAL_CSTRING("aac-profile"), aConfig.aac_profile);
-  }
-
   nsRefPtr<MediaDataDecoder> decoder =
     new AudioDataDecoder(aConfig.mime_type, format, aCallback);
 
   return decoder.forget();
 
 }
 
 
@@ -237,16 +212,17 @@ MediaCodecDataDecoder::MediaCodecDataDec
                                              MediaDataDecoderCallback* aCallback)
   : mType(aType)
   , mMimeType(strdup(aMimeType))
   , mFormat(aFormat)
   , mCallback(aCallback)
   , mInputBuffers(nullptr)
   , mOutputBuffers(nullptr)
   , mMonitor("MediaCodecDataDecoder::mMonitor")
+  , mFlushing(false)
   , mDraining(false)
   , mStopping(false)
 {
 
 }
 
 MediaCodecDataDecoder::~MediaCodecDataDecoder()
 {
@@ -304,125 +280,154 @@ nsresult MediaCodecDataDecoder::InitDeco
                     NS_NewRunnableMethod(this, &MediaCodecDataDecoder::DecoderLoop));
 
   return NS_OK;
 }
 
 // This is in usec, so that's 10ms
 #define DECODER_TIMEOUT 10000
 
+#define HANDLE_DECODER_ERROR() \
+  if (NS_FAILED(res)) { \
+    NS_WARNING("exiting decoder loop due to exception"); \
+    mCallback->Error(); \
+    break; \
+  }
+
 void MediaCodecDataDecoder::DecoderLoop()
 {
   bool outputDone = false;
 
+  bool draining = false;
+  bool waitingEOF = false;
+
   JNIEnv* env = GetJNIForThread();
   mp4_demuxer::MP4Sample* sample = nullptr;
 
   nsAutoPtr<MediaFormat> outputFormat;
   nsresult res;
 
   for (;;) {
     {
       MonitorAutoLock lock(mMonitor);
-      while (!mStopping && !mDraining && mQueue.empty()) {
+      while (!mStopping && !mDraining && !mFlushing && mQueue.empty()) {
         if (mQueue.empty()) {
           // We could be waiting here forever if we don't signal that we need more input
           mCallback->InputExhausted();
         }
         lock.Wait();
       }
 
       if (mStopping) {
         // Get out of the loop. This is the only exit point.
         break;
       }
 
-      if (mDraining) {
+      if (mFlushing) {
         mDecoder->Flush();
         ClearQueue();
-        mDraining =  false;
+        mFlushing =  false;
         lock.Notify();
         continue;
       }
 
+      if (mDraining && !sample && !waitingEOF) {
+        draining = true;
+      }
+
       // We're not stopping or draining, so try to get a sample
       if (!mQueue.empty()) {
         sample = mQueue.front();
       }
     }
 
+    if (draining && !waitingEOF) {
+      MOZ_ASSERT(!sample, "Shouldn't have a sample when pushing EOF frame");
+
+      int inputIndex = mDecoder->DequeueInputBuffer(DECODER_TIMEOUT, &res);
+      HANDLE_DECODER_ERROR();
+
+      if (inputIndex >= 0) {
+        mDecoder->QueueInputBuffer(inputIndex, 0, 0, 0, MediaCodec::getBUFFER_FLAG_END_OF_STREAM(), &res);
+        waitingEOF = true;
+      }
+    }
+
     if (sample) {
       // We have a sample, try to feed it to the decoder
       int inputIndex = mDecoder->DequeueInputBuffer(DECODER_TIMEOUT, &res);
-      if (NS_FAILED(res)) {
-        printf_stderr("exiting decoder loop due to exception while dequeuing input\n");
-        mCallback->Error();
-        break;
-      }
+      HANDLE_DECODER_ERROR();
 
       if (inputIndex >= 0) {
         jobject buffer = env->GetObjectArrayElement(mInputBuffers, inputIndex);
         void* directBuffer = env->GetDirectBufferAddress(buffer);
 
         // We're feeding this to the decoder, so remove it from the queue
         mMonitor.Lock();
         mQueue.pop();
         mMonitor.Unlock();
 
         MOZ_ASSERT(env->GetDirectBufferCapacity(buffer) >= sample->size,
           "Decoder buffer is not large enough for sample");
 
         PodCopy((uint8_t*)directBuffer, sample->data, sample->size);
 
         mDecoder->QueueInputBuffer(inputIndex, 0, sample->size, sample->composition_timestamp, 0, &res);
-        if (NS_FAILED(res)) {
-          printf_stderr("exiting decoder loop due to exception while queuing input\n");
-          mCallback->Error();
-          break;
-        }
+        HANDLE_DECODER_ERROR();
 
         mDurations.push(sample->duration);
 
         delete sample;
         sample = nullptr;
 
         outputDone = false;
         env->DeleteLocalRef(buffer);
       }
     }
 
     if (!outputDone) {
       BufferInfo bufferInfo;
 
       int outputStatus = mDecoder->DequeueOutputBuffer(bufferInfo.wrappedObject(), DECODER_TIMEOUT, &res);
-      if (NS_FAILED(res)) {
-        printf_stderr("exiting decoder loop due to exception while dequeuing output\n");
-        mCallback->Error();
-        break;
-      }
+      HANDLE_DECODER_ERROR();
 
       if (outputStatus == MediaCodec::getINFO_TRY_AGAIN_LATER()) {
         // We might want to call mCallback->InputExhausted() here, but there seems to be
         // some possible bad interactions here with the threading
       } else if (outputStatus == MediaCodec::getINFO_OUTPUT_BUFFERS_CHANGED()) {
         res = ResetOutputBuffers();
-        if (NS_FAILED(res)) {
-          printf_stderr("exiting decoder loop due to exception while restting output buffers\n");
-          mCallback->Error();
-          break;
-        }
+        HANDLE_DECODER_ERROR();
       } else if (outputStatus == MediaCodec::getINFO_OUTPUT_FORMAT_CHANGED()) {
         outputFormat = new MediaFormat(mDecoder->GetOutputFormat(), GetJNIForThread());
       } else if (outputStatus < 0) {
-        printf_stderr("unknown error from decoder! %d\n", outputStatus);
+        NS_WARNING("unknown error from decoder!");
         mCallback->Error();
+
+        // Don't break here just in case it's recoverable. If it's not, others stuff will fail later and
+        // we'll bail out.
       } else {
         // We have a valid buffer index >= 0 here
         if (bufferInfo.getFlags() & MediaCodec::getBUFFER_FLAG_END_OF_STREAM()) {
+          if (draining) {
+            draining = false;
+            waitingEOF = false;
+
+            mMonitor.Lock();
+            mDraining = false;
+            mMonitor.Notify();
+            mMonitor.Unlock();
+
+            mCallback->DrainComplete();
+          }
+
+          mDecoder->ReleaseOutputBuffer(outputStatus, false);
           outputDone = true;
+
+          // We only queue empty EOF frames, so we're done for now
+          continue;
         }
 
         MOZ_ASSERT(!mDurations.empty(), "Should have had a duration queued");
 
         Microseconds duration = 0;
         if (!mDurations.empty()) {
           duration = mDurations.front();
           mDurations.pop();
@@ -504,30 +509,36 @@ nsresult MediaCodecDataDecoder::ResetOut
   if (NS_FAILED(res)) {
     return res;
   }
 
   return NS_OK;
 }
 
 nsresult MediaCodecDataDecoder::Flush() {
-  Drain();
+  MonitorAutoLock lock(mMonitor);
+  mFlushing = true;
+  lock.Notify();
+
+  while (mFlushing) {
+    lock.Wait();
+  }
+
   return NS_OK;
 }
 
 nsresult MediaCodecDataDecoder::Drain() {
   MonitorAutoLock lock(mMonitor);
+  if (mDraining) {
+    return NS_OK;
+  }
+
   mDraining = true;
   lock.Notify();
 
-  while (mDraining) {
-    lock.Wait();
-  }
-
-  mCallback->DrainComplete();
   return NS_OK;
 }
 
 
 nsresult MediaCodecDataDecoder::Shutdown() {
   MonitorAutoLock lock(mMonitor);
 
   if (!mThread || mStopping) {
--- a/dom/media/fmp4/android/AndroidDecoderModule.h
+++ b/dom/media/fmp4/android/AndroidDecoderModule.h
@@ -82,16 +82,17 @@ protected:
 
   jobjectArray mInputBuffers;
   jobjectArray mOutputBuffers;
 
   nsCOMPtr<nsIThread> mThread;
 
   // Only these members are protected by mMonitor.
   Monitor mMonitor;
+  bool mFlushing;
   bool mDraining;
   bool mStopping;
 
   SampleQueue mQueue;
   std::queue<Microseconds> mDurations;
 
   virtual nsresult InitDecoder(jobject aSurface = nullptr);
 
--- a/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp
+++ b/dom/media/fmp4/gonk/GonkMediaDataDecoder.cpp
@@ -27,29 +27,31 @@ namespace mozilla {
 
 GonkMediaDataDecoder::GonkMediaDataDecoder(GonkDecoderManager* aManager,
                                            MediaTaskQueue* aTaskQueue,
                                            MediaDataDecoderCallback* aCallback)
   : mTaskQueue(aTaskQueue)
   , mCallback(aCallback)
   , mManager(aManager)
   , mSignaledEOS(false)
+  , mDrainComplete(false)
 {
   MOZ_COUNT_CTOR(GonkMediaDataDecoder);
 }
 
 GonkMediaDataDecoder::~GonkMediaDataDecoder()
 {
   MOZ_COUNT_DTOR(GonkMediaDataDecoder);
 }
 
 nsresult
 GonkMediaDataDecoder::Init()
 {
   mDecoder = mManager->Init(mCallback);
+  mDrainComplete = false;
   return mDecoder.get() ? NS_OK : NS_ERROR_UNEXPECTED;
 }
 
 nsresult
 GonkMediaDataDecoder::Shutdown()
 {
   mDecoder->stop();
   mDecoder = nullptr;
@@ -84,17 +86,17 @@ GonkMediaDataDecoder::ProcessDecode(mp4_
   ProcessOutput();
 }
 
 void
 GonkMediaDataDecoder::ProcessOutput()
 {
   nsAutoPtr<MediaData> output;
   nsresult rv;
-  while (true) {
+  while (true && !mDrainComplete) {
     rv = mManager->Output(mLastStreamOffset, output);
     if (rv == NS_OK) {
       mCallback->Output(output.forget());
       continue;
     } else if (rv == NS_ERROR_NOT_AVAILABLE && mSignaledEOS) {
       // Try to get more frames before getting EOS frame
       continue;
     }
@@ -112,16 +114,17 @@ GonkMediaDataDecoder::ProcessOutput()
     ALOG("Failed to output data");
     // GonkDecoderManangers report NS_ERROR_ABORT when EOS is reached.
     if (rv == NS_ERROR_ABORT) {
       if (output.get() != nullptr) {
         mCallback->Output(output.forget());
       }
       mCallback->DrainComplete();
       mSignaledEOS = false;
+      mDrainComplete = true;
       return;
     }
     mCallback->Error();
   }
 }
 
 nsresult
 GonkMediaDataDecoder::Flush()
--- a/dom/media/fmp4/gonk/GonkMediaDataDecoder.h
+++ b/dom/media/fmp4/gonk/GonkMediaDataDecoder.h
@@ -87,13 +87,15 @@ private:
   android::sp<android::MediaCodecProxy> mDecoder;
   nsAutoPtr<GonkDecoderManager> mManager;
 
   // The last offset into the media resource that was passed into Input().
   // This is used to approximate the decoder's position in the media resource.
   int64_t mLastStreamOffset;
   // Set it ture when there is no input data
   bool mSignaledEOS;
+  // Set if there is no more output data from decoder
+  bool mDrainComplete;
 };
 
 } // namespace mozilla
 
 #endif // GonkMediaDataDecoder_h_
--- a/dom/media/fmp4/moz.build
+++ b/dom/media/fmp4/moz.build
@@ -3,22 +3,24 @@
 # 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 += [
     'MP4Decoder.h',
     'MP4Reader.h',
     'PlatformDecoderModule.h',
+    'SharedDecoderManager.h',
 ]
 
 UNIFIED_SOURCES += [
     'BlankDecoderModule.cpp',
     'MP4Decoder.cpp',
     'PlatformDecoderModule.cpp',
+    'SharedDecoderManager.cpp',
 ]
 
 SOURCES += [
     'MP4Reader.cpp',
 ]
 
 if CONFIG['MOZ_WMF']:
     DIRS += [ 'wmf' ];
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -9,16 +9,17 @@
 #include "mozilla/dom/TimeRanges.h"
 #include "DecoderTraits.h"
 #include "MediaDataDecodedListener.h"
 #include "MediaDecoderOwner.h"
 #include "MediaSourceDecoder.h"
 #include "MediaSourceUtils.h"
 #include "SourceBufferDecoder.h"
 #include "TrackBuffer.h"
+#include "SharedDecoderManager.h"
 
 #ifdef MOZ_FMP4
 #include "MP4Decoder.h"
 #include "MP4Reader.h"
 #endif
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* GetMediaSourceLog();
@@ -57,16 +58,17 @@ MediaSourceReader::MediaSourceReader(Med
   , mSeekResult(NS_OK)
   , mTimeThreshold(-1)
   , mDropAudioBeforeThreshold(false)
   , mDropVideoBeforeThreshold(false)
   , mEnded(false)
   , mAudioIsSeeking(false)
   , mVideoIsSeeking(false)
   , mHasEssentialTrackBuffers(false)
+  , mSharedDecoderManager(new SharedDecoderManager())
 {
 }
 
 void
 MediaSourceReader::PrepareInitialization()
 {
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MSE_DEBUG("MediaSourceReader(%p)::PrepareInitialization trackBuffers=%u",
@@ -366,16 +368,17 @@ MediaSourceReader::CreateSubDecoder(cons
 
   // Set a callback on the subreader that forwards calls to this reader.
   // This reader will then forward them onto the state machine via this
   // reader's callback.
   RefPtr<MediaDataDecodedListener<MediaSourceReader>> callback =
     new MediaDataDecodedListener<MediaSourceReader>(this, GetTaskQueue());
   reader->SetCallback(callback);
   reader->SetTaskQueue(GetTaskQueue());
+  reader->SetSharedDecoderManager(mSharedDecoderManager);
   reader->Init(nullptr);
 
   MSE_DEBUG("MediaSourceReader(%p)::CreateSubDecoder subdecoder %p subreader %p",
             this, decoder.get(), reader.get());
   decoder->SetReader(reader);
 #ifdef MOZ_EME
   decoder->SetCDMProxy(mCDMProxy);
 #endif
--- a/dom/media/mediasource/MediaSourceReader.h
+++ b/dom/media/mediasource/MediaSourceReader.h
@@ -170,13 +170,14 @@ private:
   // the mDiscontinuity field set to true once we have the
   // first decoded sample. These flags are set during seeking
   // so we can detect when we have the first decoded sample
   // after a seek.
   bool mAudioIsSeeking;
   bool mVideoIsSeeking;
 
   bool mHasEssentialTrackBuffers;
+  nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
 };
 
 } // namespace mozilla
 
 #endif /* MOZILLA_MEDIASOURCEREADER_H_ */
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -187,28 +187,42 @@ function SetupEME(test, token, params)
   });
 
   var onSetKeysFail = (params && params.onSetKeysFail)
     ? params.onSetKeysFail
     : bail(token + " Failed to set MediaKeys on <video> element");
 
   v.addEventListener("encrypted", function(ev) {
     Log(token, "got encrypted event");
-    MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
-      Log(token, "created MediaKeys object ok");
-      mediaKeys.sessions = [];
-      return v.setMediaKeys(mediaKeys);
-    }, bail("failed to create MediaKeys object")).then(function() {
-      Log(token, "set MediaKeys on <video> element ok");
+    var options = [
+      {
+        initDataType: ev.initDataType,
+        videoType: test.type,
+      }
+    ];
+    navigator.requestMediaKeySystemAccess(KEYSYSTEM_TYPE, options)
+      .then(function(keySystemAccess) {
+        return keySystemAccess.createMediaKeys();
+      }, bail(token + " Failed to request key system access."))
+      
+      .then(function(mediaKeys) {
+        Log(token, "created MediaKeys object ok");
+        mediaKeys.sessions = [];
+        return v.setMediaKeys(mediaKeys);
+      }, bail("failed to create MediaKeys object"))
+      
+      .then(function() {
+        Log(token, "set MediaKeys on <video> element ok");
 
-      var session = v.mediaKeys.createSession(test.sessionType);
-      if (params && params.onsessioncreated) {
-        params.onsessioncreated(session);
-      }
-      session.addEventListener("message", UpdateSessionFunc(test, token));
-      session.generateRequest(ev.initDataType, ev.initData).then(function() {
-      }, bail(token + " Failed to initialise MediaKeySession"));
-
-    }, onSetKeysFail);
+        var session = v.mediaKeys.createSession(test.sessionType);
+        if (params && params.onsessioncreated) {
+          params.onsessioncreated(session);
+        }
+        session.addEventListener("message", UpdateSessionFunc(test, token));
+        return session.generateRequest(ev.initDataType, ev.initData);
+      }, onSetKeysFail)
+      
+      .then(function() {
+        Log(token, "generated request");
+      }, bail(token + " Failed to request key system access2."));
   });
-
   return v;
 }
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -359,16 +359,18 @@ skip-if = (toolkit == 'android' && proce
 [test_decoder_disable.html]
 [test_defaultMuted.html]
 [test_delay_load.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
 [test_eme_canvas_blocked.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_eme_playback.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+[test_eme_requestKeySystemAccess.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_eme_stream_capture_blocked.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_empty_resource.html]
 [test_error_in_video_document.html]
 skip-if = toolkit == 'android' || (os == 'win' && !debug) || (os == 'mac' && !debug) # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
--- a/dom/media/test/test_eme_playback.html
+++ b/dom/media/test/test_eme_playback.html
@@ -46,18 +46,16 @@ function startTest(test, token)
       }
     }
   );
 
   var gotEncrypted = false;
   var gotPlaying = false;
 
   v.addEventListener("encrypted", function(ev) {
-    ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
-       TimeStamp(token) + " MediaKeys should support this keysystem");
     gotEncrypted = true;
   });
 
   v.addEventListener("playing", function () { gotPlaying = true; });
 
   v.addEventListener("ended", function(ev) {
     ok(true, TimeStamp(token) + " got ended event");
 
@@ -83,33 +81,17 @@ function startTest(test, token)
     manager.finished(token);
    });
 
   v.addEventListener("error", bail(token + " got error event"));
 
   LoadTest(test, v, token).then(function(){v.play();}, bail(token + " failed to load"));
 }
 
-function testIsTypeSupported()
-{
-  var t = MediaKeys.isTypeSupported;
-  const clearkey = "org.w3.clearkey";
-  ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
-  ok(t(clearkey), "ClearKey supported.");
-  ok(!t(clearkey, "bogus"), "ClearKey bogus initDataType not supported.");
-  ok(t(clearkey, "cenc"), "ClearKey/cenc should be supported.");
-  ok(!t(clearkey, "cenc", "bogus"), "ClearKey/cenc bogus content type should be supported.");
-  ok(t(clearkey, "cenc", 'video/mp4'), "ClearKey/cenc video/mp4 supported.");
-  ok(t(clearkey, "cenc", 'video/mp4; codecs="avc1.4d4015,mp4a.40.2"'), "ClearKey/cenc H.264/AAC supported.");
-  ok(t(clearkey, "cenc", 'audio/mp4'), "ClearKey/cenc audio/mp4 supported.");
-  ok(t(clearkey, "cenc", 'audio/mp4; codecs="mp4a.40.2"'), "ClearKey/cenc AAC LC supported.");
-}
-
 function beginTest() {
-  testIsTypeSupported();
   manager.runTests(gEMETests, startTest);
 }
 
 var prefs = [
   [ "media.mediasource.enabled", true ],
   [ "media.mediasource.mp4.enabled", true ],
 ];
 
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_eme_requestKeySystemAccess.html
@@ -0,0 +1,293 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Encrypted Media Extensions</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function Test(test) {
+  var name = "'" + test.name + "'";
+  return new Promise(function(resolve, reject) {
+    var p;
+    if (test.options) {
+      p = navigator.requestMediaKeySystemAccess(test.keySystem, test.options);
+    } else {
+      p = navigator.requestMediaKeySystemAccess(test.keySystem);
+    }
+    p.then(
+      function(keySystemAccess) {
+        ok(test.shouldPass, name + " passed and was expected to " + (test.shouldPass ? "pass" : "fail"));
+        resolve();
+      },
+      function(ex) {
+        if (test.shouldPass) {
+          info(name + " failed: " + ex);
+        }
+        ok(!test.shouldPass, name + " failed and was expected to " + (test.shouldPass ? "pass" : "fail"));
+        resolve();
+      });
+  });
+}
+
+const CLEARKEY_ID = 'org.w3.clearkey';
+
+var tests = [
+  {
+    name: 'Empty keySystem string',
+    keySystem: '',
+    options: [ ],
+    shouldPass: false,
+  },
+  {
+    name: 'Empty options specified',
+    keySystem: CLEARKEY_ID,
+    options: [ ],
+    shouldPass: false,
+  },
+  {
+    name: 'Undefined options',
+    keySystem: CLEARKEY_ID,
+    shouldPass: true,
+  },
+  {
+    name: 'Basic MP4 cenc',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'Invalid keysystem failure',
+    keySystem: 'bogusKeySystem',
+    options: [],
+    shouldPass: false,
+  },
+  {
+    name: 'Invalid initDataType',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Invalid videoType',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Invalid statefulness',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+        stateful: 'bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Invalid uniqueidentifier',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+        uniqueidentifier: 'bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Audio capabilities not supported by CLEARKEY_ID',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+        audioCapability: 'bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Video capabilities not supported by CLEARKEY_ID',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+        videoCapability: 'bogus',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'Invalid option followed by valid',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        bogus: 'bogon',
+      },
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'MP4 audio container',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        audioType: 'audio/mp4',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'MP4 audio container with AAC-LC',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        audioType: 'audio/mp4; codecs="mp4a.40.2"',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'MP4 audio container with invalid codecs',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        audioType: 'audio/mp4; codecs="bogus"',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'MP4 audio container with mp3 is unsupported',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        audioType: 'audio/mp4; codecs="mp3"',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'MP4 video container with mp3 and h264 is unsupported',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4; codecs="avc1.42E01E,mp3"',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'MP4 video container with constrained baseline h.264',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        audioType: 'video/mp4; codecs="avc1.42E01E"',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'MP4 video container with invalid codecs',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4; codecs="bogus"',
+      }
+    ],
+    shouldPass: false,
+  },
+  {
+    name: 'MP4 video container with constrained baseline h.264 and AAC-LC',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4; codecs="avc1.42E01E,mp4a.40.2"',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'MP4 audio and video type both specified',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'cenc',
+        videoType: 'video/mp4; codecs="avc1.42E01E"',
+        audioType: 'audio/mp4; codecs="mp4a.40.2"',
+      }
+    ],
+    shouldPass: true,
+  },
+  {
+    name: 'WebM CLEARKEY_ID not supported',
+    keySystem: CLEARKEY_ID,
+    options: [
+      {
+        initDataType: 'webm',
+        videoType: 'video/webm',
+      }
+    ],
+    shouldPass: false,
+  },
+];
+
+function beginTest() {
+  Promise.all(tests.map(Test)).then(function() { SimpleTest.finish(); });
+}
+
+var prefs = [
+  [ "media.mediasource.enabled", true ],
+  [ "media.mediasource.mp4.enabled", true ],
+];
+
+if (/Linux/.test(navigator.userAgent) ||
+    !document.createElement('video').canPlayType("video/mp4")) {
+  // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
+  prefs.push([ "media.fragmented-mp4.exposed", true ]);
+  prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -12,16 +12,31 @@ using NPError from "npapi.h";
 using NPNVariable from "npapi.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using class mac_plugin_interposing::NSCursorInfo from "mozilla/plugins/PluginMessageUtils.h";
 using struct nsID from "nsID.h";
 
 namespace mozilla {
 namespace plugins {
 
+struct PluginSettings
+{
+  // These settings correspond to NPNVariable. They are fetched from
+  // mozilla::plugins::parent::_getvalue.
+  bool javascriptEnabled;
+  bool asdEnabled;
+  bool isOffline;
+  bool supportsXembed;
+  bool supportsWindowless;
+
+  // These settings come from elsewhere.
+  nsCString userAgent;
+  bool nativeCursorsSupported;
+};
+
 intr protocol PPluginModule
 {
   bridges PContent, PPluginModule;
 
   manages PPluginInstance;
   manages PCrashReporter;
 
 both:
@@ -29,17 +44,17 @@ both:
   // a nested event loop for the current interrupt call.
   async ProcessNativeEventsInInterruptCall();
 
 child:
   // Forces the child process to update its plugin function table.
   intr NP_GetEntryPoints()
     returns (NPError rv);
 
-  intr NP_Initialize()
+  intr NP_Initialize(PluginSettings settings)
     returns (NPError rv);
 
   intr PPluginInstance(nsCString aMimeType,
                       uint16_t aMode,
                       nsCString[] aNames,
                       nsCString[] aValues)
     returns (NPError rv);
 
@@ -64,37 +79,32 @@ child:
   async SetParentHangTimeout(uint32_t seconds);
 
   intr PCrashReporter()
     returns (NativeThreadId tid, uint32_t processType);
 
   intr GeckoGetProfile()
     returns (nsCString aProfile);
 
+  async SettingChanged(PluginSettings settings);
+
 parent:
   /**
    * This message is only used on X11 platforms.
    *
    * Send a dup of the plugin process's X socket to the parent
    * process.  In theory, this scheme keeps the plugin's X resources
    * around until after both the plugin process shuts down *and* the
    * parent process closes the dup fd.  This is used to prevent the
    * parent process from crashing on X errors if, e.g., the plugin
    * crashes *just before* a repaint and the parent process tries to
    * use the newly-invalid surface.
    */
   async BackUpXResources(FileDescriptor aXSocketFd);
 
-  intr NPN_UserAgent()
-    returns (nsCString userAgent);
-
-  intr NPN_GetValue_WithBoolReturn(NPNVariable aVariable)
-    returns (NPError aError,
-             bool aBoolVal);
-
   // Wake up and process a few native events.  Periodically called by
   // Gtk-specific code upon detecting that the plugin process has
   // entered a nested event loop.  If the browser doesn't process
   // native events, then "livelock" and some other glitches can occur.
   intr ProcessSomeEvents();
 
   // OS X Specific calls to manage the plugin's window
   // when interposing system calls.
@@ -103,17 +113,16 @@ parent:
                          size_t aWidth, size_t aHeight);
   async PluginHideWindow(uint32_t aWindowId);
 
   // OS X Specific calls to allow the plugin to manage the cursor.
   async SetCursor(NSCursorInfo cursorInfo);
   async ShowCursor(bool show);
   async PushCursor(NSCursorInfo cursorInfo);
   async PopCursor();
-  sync GetNativeCursorsSupported() returns (bool supported);
 
   sync NPN_SetException(nsCString message);
 
   async NPN_ReloadPlugins(bool aReloadPages);
 
   // Notifies the chrome process that a PluginModuleChild linked to a content
   // process was destroyed. The chrome process may choose to asynchronously shut
   // down the plugin process in response.
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -816,20 +816,17 @@ PluginModuleChild::ActorDestroy(ActorDes
 void
 PluginModuleChild::CleanUp()
 {
 }
 
 const char*
 PluginModuleChild::GetUserAgent()
 {
-    if (mUserAgent.IsVoid() && !CallNPN_UserAgent(&mUserAgent))
-        return nullptr;
-
-    return NullableStringGet(mUserAgent);
+    return NullableStringGet(Settings().userAgent());
 }
 
 //-----------------------------------------------------------------------------
 // FIXME/cjones: just getting this out of the way for the moment ...
 
 namespace mozilla {
 namespace plugins {
 namespace child {
@@ -1115,28 +1112,31 @@ NPError
         // Copied from nsNPAPIPlugin.cpp
         case NPNVToolkit:
 #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
             *static_cast<NPNToolkitType*>(aValue) = NPNVGtk2;
             return NPERR_NO_ERROR;
 #endif
             return NPERR_GENERIC_ERROR;
 
-        case NPNVjavascriptEnabledBool: // Intentional fall-through
-        case NPNVasdEnabledBool: // Intentional fall-through
-        case NPNVisOfflineBool: // Intentional fall-through
-        case NPNVSupportsXEmbedBool: // Intentional fall-through
-        case NPNVSupportsWindowless: { // Intentional fall-through
-            NPError result;
-            bool value;
-            PluginModuleChild::GetChrome()->
-                CallNPN_GetValue_WithBoolReturn(aVariable, &result, &value);
-            *(NPBool*)aValue = value ? true : false;
-            return result;
-        }
+        case NPNVjavascriptEnabledBool:
+            *(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().javascriptEnabled();
+            return NPERR_NO_ERROR;
+        case NPNVasdEnabledBool:
+            *(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().asdEnabled();
+            return NPERR_NO_ERROR;
+        case NPNVisOfflineBool:
+            *(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().isOffline();
+            return NPERR_NO_ERROR;
+        case NPNVSupportsXEmbedBool:
+            *(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().supportsXembed();
+            return NPERR_NO_ERROR;
+        case NPNVSupportsWindowless:
+            *(NPBool*)aValue = PluginModuleChild::GetChrome()->Settings().supportsWindowless();
+            return NPERR_NO_ERROR;
 #if defined(MOZ_WIDGET_GTK)
         case NPNVxDisplay: {
             if (aNPP) {
                 return InstCast(aNPP)->NPN_GetValue(aVariable, aValue);
             } 
             else {
                 *(void **)aValue = xt_client_get_display();
             }          
@@ -1830,16 +1830,23 @@ void
 
 } /* namespace child */
 } /* namespace plugins */
 } /* namespace mozilla */
 
 //-----------------------------------------------------------------------------
 
 bool
+PluginModuleChild::RecvSettingChanged(const PluginSettings& aSettings)
+{
+    mCachedSettings = aSettings;
+    return true;
+}
+
+bool
 PluginModuleChild::AnswerNP_GetEntryPoints(NPError* _retval)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
     MOZ_ASSERT(mIsChrome);
 
 #if defined(OS_LINUX) || defined(OS_BSD)
     return true;
@@ -1847,22 +1854,24 @@ PluginModuleChild::AnswerNP_GetEntryPoin
     *_retval = mGetEntryPointsFunc(&mFunctions);
     return true;
 #else
 #  error Please implement me for your platform
 #endif
 }
 
 bool
-PluginModuleChild::AnswerNP_Initialize(NPError* _retval)
+PluginModuleChild::AnswerNP_Initialize(const PluginSettings& aSettings, NPError* _retval)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
     MOZ_ASSERT(mIsChrome);
 
+    mCachedSettings = aSettings;
+
 #ifdef OS_WIN
     SetEventHooks();
 #endif
 
 #ifdef MOZ_X11
     // Send the parent our X socket to act as a proxy reference for our X
     // resources.
     int xSocketFd = ConnectionNumber(DefaultXDisplay());
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -67,19 +67,21 @@ protected:
     virtual mozilla::ipc::RacyInterruptPolicy
     MediateInterruptRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
         return MediateRace(parent, child);
     }
 
     virtual bool ShouldContinueFromReplyTimeout() MOZ_OVERRIDE;
 
+    virtual bool RecvSettingChanged(const PluginSettings& aSettings) MOZ_OVERRIDE;
+
     // Implement the PPluginModuleChild interface
     virtual bool AnswerNP_GetEntryPoints(NPError* rv) MOZ_OVERRIDE;
-    virtual bool AnswerNP_Initialize(NPError* rv) MOZ_OVERRIDE;
+    virtual bool AnswerNP_Initialize(const PluginSettings& aSettings, NPError* rv) MOZ_OVERRIDE;
 
     virtual PPluginModuleChild*
     AllocPPluginModuleChild(mozilla::ipc::Transport* aTransport,
                             base::ProcessId aOtherProcess) MOZ_OVERRIDE;
 
     virtual PPluginInstanceChild*
     AllocPPluginInstanceChild(const nsCString& aMimeType,
                               const uint16_t& aMode,
@@ -221,19 +223,17 @@ public:
         SendPushCursor(cursorInfo);
     }
 
     void PopCursor() {
         SendPopCursor();
     }
 
     bool GetNativeCursorsSupported() {
-        bool supported = false;
-        SendGetNativeCursorsSupported(&supported);
-        return supported;
+        return Settings().nativeCursorsSupported();
     }
 #endif
 
     // Quirks mode support for various plugin mime types
     enum PluginQuirks {
         QUIRKS_NOT_INITIALIZED                          = 0,
         // Silverlight assumes it is transparent in windowless mode. This quirk
         // matches the logic in nsNPAPIPluginInstance::SetWindowless.
@@ -273,16 +273,18 @@ public:
         // in CoreGraphics mode:  The Flash plugin sometimes accesses the
         // CGContextRef we pass to it in NPP_HandleEvent(NPCocoaEventDrawRect)
         // outside of that call.  See bug 804606.
         QUIRK_FLASH_AVOID_CGMODE_CRASHES                = 1 << 10,
     };
 
     int GetQuirks() { return mQuirks; }
 
+    const PluginSettings& Settings() const { return mCachedSettings; }
+
 private:
     void AddQuirk(PluginQuirks quirk) {
       if (mQuirks == QUIRKS_NOT_INITIALIZED)
         mQuirks = 0;
       mQuirks |= quirk;
     }
     void InitQuirksModes(const nsCString& aMimeType);
     bool InitGraphics();
@@ -313,16 +315,18 @@ private:
     NP_PLUGINUNIXINIT mInitializeFunc;
 #elif defined(OS_WIN) || defined(OS_MACOSX)
     NP_PLUGININIT mInitializeFunc;
     NP_GETENTRYPOINTS mGetEntryPointsFunc;
 #endif
 
     NPPluginFuncs mFunctions;
 
+    PluginSettings mCachedSettings;
+
 #if defined(MOZ_WIDGET_GTK)
     // If a plugin spins a nested glib event loop in response to a
     // synchronous IPC message from the browser, the loop might break
     // only after the browser responds to a request sent by the
     // plugin.  This can happen if a plugin uses gtk's synchronous
     // copy/paste, for example.  But because the browser is blocked on
     // a condvar, it can't respond to the request.  This situation
     // isn't technically a deadlock, but the symptoms are basically
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -247,16 +247,18 @@ PluginModuleChromeParent::PluginModuleCh
 
     Preferences::RegisterCallback(TimeoutChanged, kChildTimeoutPref, this);
     Preferences::RegisterCallback(TimeoutChanged, kParentTimeoutPref, this);
 #ifdef XP_WIN
     Preferences::RegisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
     Preferences::RegisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
 #endif
 
+    RegisterSettingsCallbacks();
+
 #ifdef MOZ_ENABLE_PROFILER_SPS
     InitPluginProfiling();
 #endif
 
     mozilla::HangMonitor::RegisterAnnotator(*this);
 }
 
 PluginModuleChromeParent::~PluginModuleChromeParent()
@@ -290,16 +292,18 @@ PluginModuleChromeParent::~PluginModuleC
 #endif
 
     Preferences::UnregisterCallback(TimeoutChanged, kChildTimeoutPref, this);
     Preferences::UnregisterCallback(TimeoutChanged, kParentTimeoutPref, this);
 #ifdef XP_WIN
     Preferences::UnregisterCallback(TimeoutChanged, kHangUITimeoutPref, this);
     Preferences::UnregisterCallback(TimeoutChanged, kHangUIMinDisplayPref, this);
 
+    UnregisterSettingsCallbacks();
+
     if (mHangUIParent) {
         delete mHangUIParent;
         mHangUIParent = nullptr;
     }
 #endif
 
     mozilla::HangMonitor::UnregisterAnnotator(*this);
 }
@@ -898,16 +902,19 @@ void
 PluginModuleChromeParent::ActorDestroy(ActorDestroyReason why)
 {
     if (why == AbnormalShutdown) {
 #ifdef MOZ_CRASHREPORTER
         ProcessFirstMinidump();
 #endif
     }
 
+    // We can't broadcast settings changes anymore.
+    UnregisterSettingsCallbacks();
+
     PluginModuleParent::ActorDestroy(why);
 }
 
 void
 PluginModuleParent::NotifyPluginCrashed()
 {
     if (!OkToCleanup()) {
         // there's still plugin code on the C++ stack.  try again
@@ -1151,23 +1158,16 @@ PluginModuleParent::NPP_URLRedirectNotif
 {
   PluginInstanceParent* i = InstCast(instance);
   if (!i)
     return;
 
   i->NPP_URLRedirectNotify(url, status, notifyData);
 }
 
-bool
-PluginModuleParent::AnswerNPN_UserAgent(nsCString* userAgent)
-{
-    *userAgent = NullableString(mNPNIface->uagent(nullptr));
-    return true;
-}
-
 PluginInstanceParent*
 PluginModuleParent::InstCast(NPP instance)
 {
     PluginInstanceParent* ip =
         static_cast<PluginInstanceParent*>(instance->pdata);
 
     // If the plugin crashed and the PluginInstanceParent was deleted,
     // instance->pdata will be nullptr.
@@ -1257,32 +1257,135 @@ PluginModuleParent::EndUpdateBackground(
 {
     PluginInstanceParent* i = InstCast(instance);
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->EndUpdateBackground(aCtx, aRect);
 }
 
+class OfflineObserver MOZ_FINAL : public nsIObserver
+{
+public:
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIOBSERVER
+
+    explicit OfflineObserver(PluginModuleChromeParent* pmp)
+      : mPmp(pmp)
+    {}
+
+private:
+    ~OfflineObserver() {}
+    PluginModuleChromeParent* mPmp;
+};
+
+NS_IMPL_ISUPPORTS(OfflineObserver, nsIObserver)
+
+NS_IMETHODIMP
+OfflineObserver::Observe(nsISupports *aSubject,
+                         const char *aTopic,
+                         const char16_t *aData)
+{
+    MOZ_ASSERT(!strcmp(aTopic, "ipc:network:set-offline"));
+    mPmp->CachedSettingChanged();
+    return NS_OK;
+}
+
+static const char* kSettingsPrefs[] =
+    {"javascript.enabled",
+     "dom.ipc.plugins.nativeCursorSupport"};
+
+void
+PluginModuleChromeParent::RegisterSettingsCallbacks()
+{
+    for (size_t i = 0; i < ArrayLength(kSettingsPrefs); i++) {
+        Preferences::RegisterCallback(CachedSettingChanged, kSettingsPrefs[i], this);
+    }
+
+    nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+    if (observerService) {
+        mOfflineObserver = new OfflineObserver(this);
+        observerService->AddObserver(mOfflineObserver, "ipc:network:set-offline", false);
+    }
+}
+
+void
+PluginModuleChromeParent::UnregisterSettingsCallbacks()
+{
+    for (size_t i = 0; i < ArrayLength(kSettingsPrefs); i++) {
+        Preferences::UnregisterCallback(CachedSettingChanged, kSettingsPrefs[i], this);
+    }
+
+    nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
+    if (observerService) {
+        observerService->RemoveObserver(mOfflineObserver, "ipc:network:set-offline");
+        mOfflineObserver = nullptr;
+    }
+}
+
+bool
+PluginModuleParent::GetSetting(NPNVariable aVariable)
+{
+    NPBool boolVal = false;
+    mozilla::plugins::parent::_getvalue(nullptr, aVariable, &boolVal);
+    return boolVal;
+}
+
+void
+PluginModuleParent::GetSettings(PluginSettings* aSettings)
+{
+    aSettings->javascriptEnabled() = GetSetting(NPNVjavascriptEnabledBool);
+    aSettings->asdEnabled() = GetSetting(NPNVasdEnabledBool);
+    aSettings->isOffline() = GetSetting(NPNVisOfflineBool);
+    aSettings->supportsXembed() = GetSetting(NPNVSupportsXEmbedBool);
+    aSettings->supportsWindowless() = GetSetting(NPNVSupportsWindowless);
+    aSettings->userAgent() = NullableString(mNPNIface->uagent(nullptr));
+
+#if defined(XP_MACOSX)
+    aSettings->nativeCursorsSupported() =
+      Preferences::GetBool("dom.ipc.plugins.nativeCursorSupport", false);
+#else
+    // Need to initialize this to satisfy IPDL.
+    aSettings->nativeCursorsSupported() = false;
+#endif
+}
+
+void
+PluginModuleChromeParent::CachedSettingChanged()
+{
+    PluginSettings settings;
+    GetSettings(&settings);
+    unused << SendSettingChanged(settings);
+}
+
+/* static */ void
+PluginModuleChromeParent::CachedSettingChanged(const char* aPref, void* aModule)
+{
+    PluginModuleChromeParent *module = static_cast<PluginModuleChromeParent*>(aModule);
+    module->CachedSettingChanged();
+}
+
 #if defined(XP_UNIX) && !defined(XP_MACOSX) && !defined(MOZ_WIDGET_GONK)
 nsresult
 PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error)
 {
     PLUGIN_LOG_DEBUG_METHOD;
 
     mNPNIface = bFuncs;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
     *error = NPERR_NO_ERROR;
     if (IsChrome()) {
-        if (!CallNP_Initialize(error)) {
+        PluginSettings settings;
+        GetSettings(&settings);
+        if (!CallNP_Initialize(settings, error)) {
             Close();
             return NS_ERROR_FAILURE;
         }
         else if (*error != NPERR_NO_ERROR) {
             Close();
             return NS_OK;
         }
     }
@@ -1310,17 +1413,19 @@ PluginModuleParent::NP_Initialize(NPNets
 
 nsresult
 PluginModuleChromeParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error)
 {
     nsresult rv = PluginModuleParent::NP_Initialize(bFuncs, error);
     if (NS_FAILED(rv))
         return rv;
 
-    if (!CallNP_Initialize(error)) {
+    PluginSettings settings;
+    GetSettings(&settings);
+    if (!CallNP_Initialize(settings, error)) {
         Close();
         return NS_ERROR_FAILURE;
     }
     if (*error != NPERR_NO_ERROR) {
         Close();
         return NS_OK;
     }
 
@@ -1534,27 +1639,16 @@ PluginModuleParent::ContentsScaleFactorC
     PluginInstanceParent* i = InstCast(instance);
     if (!i)
         return NS_ERROR_FAILURE;
 
     return i->ContentsScaleFactorChanged(aContentsScaleFactor);
 }
 #endif // #if defined(XP_MACOSX)
 
-bool
-PluginModuleParent::AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable,
-                                                      NPError* aError,
-                                                      bool* aBoolVal)
-{
-    NPBool boolVal = false;
-    *aError = mozilla::plugins::parent::_getvalue(nullptr, aVariable, &boolVal);
-    *aBoolVal = boolVal ? true : false;
-    return true;
-}
-
 #if defined(MOZ_WIDGET_QT)
 static const int kMaxtimeToProcessEvents = 30;
 bool
 PluginModuleParent::AnswerProcessSomeEvents()
 {
     PLUGIN_LOG_DEBUG(("Spinning mini nested loop ..."));
     QCoreApplication::processEvents(QEventLoop::AllEvents, kMaxtimeToProcessEvents);
 
@@ -1745,31 +1839,16 @@ PluginModuleParent::RecvPopCursor()
 #else
     NS_NOTREACHED(
         "PluginInstanceParent::RecvPopCursor not implemented!");
     return false;
 #endif
 }
 
 bool
-PluginModuleParent::RecvGetNativeCursorsSupported(bool* supported)
-{
-    PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
-#if defined(XP_MACOSX)
-    *supported =
-      Preferences::GetBool("dom.ipc.plugins.nativeCursorSupport", false);
-    return true;
-#else
-    NS_NOTREACHED(
-        "PluginInstanceParent::RecvGetNativeCursorSupportLevel not implemented!");
-    return false;
-#endif
-}
-
-bool
 PluginModuleParent::RecvNPN_SetException(const nsCString& aMessage)
 {
     PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
 
     // This function ignores its first argument.
     mozilla::plugins::parent::_setexception(nullptr, NullableStringGet(aMessage));
     return true;
 }
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -110,24 +110,16 @@ protected:
     MediateInterruptRace(const Message& parent, const Message& child) MOZ_OVERRIDE
     {
         return MediateRace(parent, child);
     }
 
     virtual bool
     RecvBackUpXResources(const FileDescriptor& aXSocketFd) MOZ_OVERRIDE;
 
-    virtual bool
-    AnswerNPN_UserAgent(nsCString* userAgent) MOZ_OVERRIDE;
-
-    virtual bool
-    AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable,
-                                      NPError* aError,
-                                      bool* aBoolVal) MOZ_OVERRIDE;
-
     virtual bool AnswerProcessSomeEvents() MOZ_OVERRIDE;
 
     virtual bool
     RecvProcessNativeEventsInInterruptCall() MOZ_OVERRIDE;
 
     virtual bool
     RecvPluginShowWindow(const uint32_t& aWindowId, const bool& aModal,
                          const int32_t& aX, const int32_t& aY,
@@ -150,19 +142,16 @@ protected:
 
     virtual bool
     RecvPushCursor(const NSCursorInfo& aCursorInfo) MOZ_OVERRIDE;
 
     virtual bool
     RecvPopCursor() MOZ_OVERRIDE;
 
     virtual bool
-    RecvGetNativeCursorsSupported(bool* supported) MOZ_OVERRIDE;
-
-    virtual bool
     RecvNPN_SetException(const nsCString& aMessage) MOZ_OVERRIDE;
 
     virtual bool
     RecvNPN_ReloadPlugins(const bool& aReloadPages) MOZ_OVERRIDE;
 
     static PluginInstanceParent* InstCast(NPP instance);
     static BrowserStreamParent* StreamCast(NPP instance, NPStream* s);
 
@@ -237,16 +226,19 @@ protected:
 #if defined(XP_MACOSX)
     virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing);
     virtual nsresult ContentsScaleFactorChanged(NPP instance, double aContentsScaleFactor);
 #endif
 
 protected:
     void NotifyPluginCrashed();
 
+    bool GetSetting(NPNVariable aVariable);
+    void GetSettings(PluginSettings* aSettings);
+
     bool mIsChrome;
     bool mShutdown;
     bool mClearSiteDataSupported;
     bool mGetSitesWithDataSupported;
     const NPNetscapeFuncs* mNPNIface;
     nsNPAPIPlugin* mPlugin;
     ScopedMethodFactory<PluginModuleParent> mTaskFactory;
     nsString mPluginDumpID;
@@ -307,16 +299,18 @@ class PluginModuleChromeParent
     /**
      * Called by Plugin Hang UI to notify that the user has clicked continue.
      * Used for chrome hang annotations.
      */
     void
     OnHangUIContinue();
 #endif // XP_WIN
 
+    void CachedSettingChanged();
+
 private:
     virtual void
     EnteredCxxStack() MOZ_OVERRIDE;
 
     void
     ExitedCxxStack() MOZ_OVERRIDE;
 
     virtual void
@@ -355,18 +349,23 @@ private:
 
     virtual void UpdatePluginTimeout() MOZ_OVERRIDE;
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     void InitPluginProfiling();
     void ShutdownPluginProfiling();
 #endif
 
+    void RegisterSettingsCallbacks();
+    void UnregisterSettingsCallbacks();
+
     virtual bool RecvNotifyContentModuleDestroyed() MOZ_OVERRIDE;
 
+    static void CachedSettingChanged(const char* aPref, void* aModule);
+
     PluginProcessParent* mSubprocess;
     uint32_t mPluginId;
 
     ScopedMethodFactory<PluginModuleChromeParent> mChromeTaskFactory;
 
     enum HangAnnotationFlags
     {
         kInPluginCall = (1u << 0),
@@ -417,14 +416,16 @@ private:
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     void InitializeInjector();
     
     void OnCrash(DWORD processID) MOZ_OVERRIDE;
 
     DWORD mFlashProcess1;
     DWORD mFlashProcess2;
 #endif
+
+    nsCOMPtr<nsIObserver> mOfflineObserver;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif // mozilla_plugins_PluginModuleParent_h
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -657,16 +657,18 @@ var interfaceNamesInGlobalScope =
     {name: "MediaKeyError", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaEncryptedEvent", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaKeys", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaKeySession", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
+    {name: "MediaKeySystemAccess", pref: "media.eme.enabled"},
+// IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "MediaKeyMessageEvent", pref: "media.eme.enabled"},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaQueryList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaRecorder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/webidl/CanvasRenderingContext2D.webidl
+++ b/dom/webidl/CanvasRenderingContext2D.webidl
@@ -42,17 +42,18 @@ interface CanvasRenderingContext2D {
   [Throws, LenientFloat]
   void rotate(double angle);
   [Throws, LenientFloat]
   void translate(double x, double y);
   [Throws, LenientFloat]
   void transform(double a, double b, double c, double d, double e, double f);
   [Throws, LenientFloat]
   void setTransform(double a, double b, double c, double d, double e, double f);
-// NOT IMPLEMENTED  void resetTransform();
+  [Throws]
+  void resetTransform();
 
   // compositing
            attribute unrestricted double globalAlpha; // (default 1.0)
            [Throws]
            attribute DOMString globalCompositeOperation; // (default source-over)
 
   // colors and styles (see also the CanvasDrawingStyles interface)
            attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle; // (default black)
new file mode 100644
--- /dev/null
+++ b/dom/webidl/MediaKeySystemAccess.webidl
@@ -0,0 +1,34 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is
+ * https://dvcs.w3.org/hg/html-media/raw-file/default/encrypted-media/encrypted-media.html
+ *
+ * Copyright © 2014 W3C® (MIT, ERCIM, Keio, Beihang), All Rights Reserved.
+ * W3C liability, trademark and document use rules apply.
+ */
+
+enum MediaKeysRequirement {
+  "required",
+  "optional",
+  "disallowed"
+};
+
+dictionary MediaKeySystemOptions {
+  DOMString            initDataType = "";
+  DOMString            audioType = "";
+  DOMString            audioCapability = "";
+  DOMString            videoType = "";
+  DOMString            videoCapability = "";
+  MediaKeysRequirement uniqueidentifier = "optional";
+  MediaKeysRequirement stateful = "optional";
+};
+
+[Pref="media.eme.enabled"]
+interface MediaKeySystemAccess {
+  readonly    attribute DOMString keySystem;
+  [NewObject, Throws]
+  Promise<MediaKeys> createMediaKeys();
+};
--- a/dom/webidl/MediaKeys.webidl
+++ b/dom/webidl/MediaKeys.webidl
@@ -17,14 +17,9 @@ enum SessionType { "temporary", "persist
 interface MediaKeys {
   readonly attribute DOMString keySystem;
 
   [NewObject, Throws]
   MediaKeySession createSession(optional SessionType sessionType = "temporary");
 
   [NewObject, Throws]
   Promise<void> setServerCertificate((ArrayBufferView or ArrayBuffer) serverCertificate);
-
-  [Throws,NewObject]
-  static Promise<MediaKeys> create(DOMString keySystem);
-  static IsTypeSupportedResult isTypeSupported(DOMString keySystem, optional DOMString initDataType, optional DOMString contentType, optional DOMString capability);
-
 };
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -392,8 +392,17 @@ partial interface Navigator {
   boolean sendBeacon(DOMString url,
                      optional (ArrayBufferView or Blob or DOMString or FormData)? data = null);
 };
 
 partial interface Navigator {
   [Pref="dom.tv.enabled", CheckPermissions="tv", Func="Navigator::HasTVSupport"]
   readonly attribute TVManager? tv;
 };
+
+#ifdef MOZ_EME
+partial interface Navigator {
+  [Pref="media.eme.enabled", Throws, NewObject]
+  Promise<MediaKeySystemAccess>
+  requestMediaKeySystemAccess(DOMString keySystem,
+                              optional sequence<MediaKeySystemOptions> supportedConfigurations);
+};
+#endif
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -793,9 +793,10 @@ if CONFIG['MOZ_BUILD_APP'] in ['browser'
 
 if CONFIG['MOZ_EME']:
     WEBIDL_FILES += [
         'MediaEncryptedEvent.webidl',
         'MediaKeyError.webidl',
         'MediaKeyMessageEvent.webidl',
         'MediaKeys.webidl',
         'MediaKeySession.webidl',
+        'MediaKeySystemAccess.webidl',
     ]
--- a/dom/workers/ScriptLoader.cpp
+++ b/dom/workers/ScriptLoader.cpp
@@ -753,17 +753,18 @@ ScriptExecutorRunnable::WorkerRun(JSCont
            .setNoScriptRval(true);
 
     JS::SourceBufferHolder srcBuf(loadInfo.mScriptTextBuf,
                                   loadInfo.mScriptTextLength,
                                   JS::SourceBufferHolder::GiveOwnership);
     loadInfo.mScriptTextBuf = nullptr;
     loadInfo.mScriptTextLength = 0;
 
-    if (!JS::Evaluate(aCx, global, options, srcBuf)) {
+    JS::Rooted<JS::Value> unused(aCx);
+    if (!JS::Evaluate(aCx, global, options, srcBuf, &unused)) {
       return true;
     }
 
     loadInfo.mExecutionResult = true;
   }
 
   return true;
 }
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -5626,18 +5626,20 @@ WorkerPrivate::RunExpiredTimeouts(JSCont
     }
     else {
       nsString expression = info->mTimeoutString;
 
       JS::CompileOptions options(aCx);
       options.setFileAndLine(info->mFilename.get(), info->mLineNumber)
              .setNoScriptRval(true);
 
+      JS::Rooted<JS::Value> unused(aCx);
       if ((expression.IsEmpty() ||
-           !JS::Evaluate(aCx, global, options, expression.get(), expression.Length())) &&
+           !JS::Evaluate(aCx, global, options,
+                         expression.get(), expression.Length(), &unused)) &&
           !JS_ReportPendingException(aCx)) {
         retval = false;
         break;
       }
     }
 
     NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
   }
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -838,17 +838,17 @@ DrawTargetSkia::Init(unsigned char* aDat
   SkBitmap bitmap;
 
   SkImageInfo info = SkImageInfo::Make(aSize.width,
                                        aSize.height,
                                        GfxFormatToSkiaColorType(aFormat),
                                        alphaType);
   bitmap.setInfo(info, aStride);
   bitmap.setPixels(aData);
-  mCanvas.adopt(new SkCanvas(new SkBitmapDevice(bitmap)));
+  mCanvas.adopt(new SkCanvas(bitmap));
 
   mSize = aSize;
   mFormat = aFormat;
 }
 
 void
 DrawTargetSkia::SetTransform(const Matrix& aTransform)
 {
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -151,18 +151,41 @@ HasCPUIDBit(unsigned int level, CPUIDReg
   return !!(unsigned(regs[reg]) & bit);
 }
 #endif
 #endif
 
 namespace mozilla {
 namespace gfx {
 
-// XXX - Need to define an API to set this.
-GFX2D_API int sGfxLogLevel = LOG_DEBUG;
+// These values we initialize with should match those in
+// PreferenceAccess::RegisterAll method.
+int32_t PreferenceAccess::sGfxLogLevel = LOG_DEFAULT;
+
+PreferenceAccess* PreferenceAccess::sAccess = nullptr;
+PreferenceAccess::~PreferenceAccess()
+{
+}
+
+// Just a placeholder, the derived class will set the variable to default
+// if the preference doesn't exist.
+void PreferenceAccess::LivePref(const char* aName, int32_t* aVar, int32_t aDef)
+{
+  *aVar = aDef;
+}
+
+// This will be called with the derived class, so we will want to register
+// the callbacks with it.
+void PreferenceAccess::SetAccess(PreferenceAccess* aAccess) {
+  sAccess = aAccess;
+  if (sAccess) {
+    RegisterAll();
+  }
+}
+
 
 #ifdef WIN32
 ID3D10Device1 *Factory::mD3D10Device;
 #ifdef USE_D2D1_1
 ID3D11Device *Factory::mD3D11Device;
 ID2D1Device *Factory::mD2D1Device;
 #endif
 #endif
@@ -790,19 +813,20 @@ LogForwarder* Factory::mLogForwarder = n
 // static
 void
 Factory::SetLogForwarder(LogForwarder* aLogFwd) {
   mLogForwarder = aLogFwd;
 }
 
 // static
 void
-CriticalLogger::OutputMessage(const std::string &aString, int aLevel)
+CriticalLogger::OutputMessage(const std::string &aString,
+                              int aLevel, bool aNoNewline)
 {
   if (Factory::GetLogForwarder()) {
     Factory::GetLogForwarder()->Log(aString);
   }
 
-  BasicLogger::OutputMessage(aString, aLevel);
+  BasicLogger::OutputMessage(aString, aLevel, aNoNewline);
 }
 
 }
 }
--- a/gfx/2d/Logging.h
+++ b/gfx/2d/Logging.h
@@ -35,61 +35,146 @@ extern "C" __declspec(dllimport) void __
 
 #if defined(PR_LOGGING)
 extern GFX2D_API PRLogModuleInfo *GetGFX2DLog();
 #endif
 
 namespace mozilla {
 namespace gfx {
 
-const int LOG_DEBUG = 1;
+// Attempting to be consistent with prlog values, but that isn't critical
+// (and note that 5 has a special meaning - see the description
+// with sGfxLogLevel)
+const int LOG_CRITICAL = 1;
 const int LOG_WARNING = 2;
-const int LOG_CRITICAL = 3;
+const int LOG_DEBUG = 3;
+const int LOG_DEBUG_PRLOG = 4;
+const int LOG_EVERYTHING = 5; // This needs to be the highest value
+
+#if defined(DEBUG)
+const int LOG_DEFAULT = LOG_EVERYTHING;
+#else
+const int LOG_DEFAULT = LOG_CRITICAL;
+#endif
 
 #if defined(PR_LOGGING)
 
 inline PRLogModuleLevel PRLogLevelForLevel(int aLevel) {
   switch (aLevel) {
+  case LOG_CRITICAL:
+    return PR_LOG_ERROR;
+  case LOG_WARNING:
+    return PR_LOG_WARNING;
   case LOG_DEBUG:
     return PR_LOG_DEBUG;
-  case LOG_WARNING:
-    return PR_LOG_WARNING;
+  case LOG_DEBUG_PRLOG:
+    return PR_LOG_DEBUG;
+  case LOG_EVERYTHING:
+    return PR_LOG_ALWAYS;
   }
   return PR_LOG_DEBUG;
 }
 
 #endif
 
-extern GFX2D_API int sGfxLogLevel;
+class PreferenceAccess
+{
+public:
+  virtual ~PreferenceAccess();
+
+  // This should connect the variable aVar to be updated whenever a preference
+  // aName is modified.  aDefault would be used if the preference is undefined,
+  // so that we always get the valid value for aVar.
+  virtual void LivePref(const char* aName, int32_t* aVar, int32_t aDefault);
+
+public:
+  static void SetAccess(PreferenceAccess* aAccess);
+
+public:
+  // For each preference that needs to be accessed in Moz2D, add a variable
+  // to hold it, as well as the call to LivePref in the RegisterAll() method
+  // below.
+
+  // Used to choose the level of logging we get.  The higher the number,
+  // the more logging we get.  Value of zero will give you no logging,
+  // 1 just errors, 2 adds warnings and 3 adds logging/debug.  4 is used to
+  // selectively enable logging on the configurations that
+  // support prlog (on other systems, 3 and 4 are the same.)  For prlog,
+  // in addition to setting the value to 4, you will need to set an
+  // environment variable NSPR_LOG_MODULES to gfx:4. See prlog.h for details.
+  static int32_t sGfxLogLevel;
+
+private:
+  static void RegisterAll() {
+    // The default values (last parameter) should match the initialization
+    // values in Factory.cpp, otherwise the standalone Moz2D will get different
+    // defaults.
+    sAccess->LivePref("gfx.logging.level", &sGfxLogLevel, LOG_DEFAULT);
+  }
+  static PreferenceAccess* sAccess;
+};
 
 struct BasicLogger
 {
-  static void OutputMessage(const std::string &aString, int aLevel) {
+  // For efficiency, this method exists and copies the logic of the
+  // OutputMessage below.  If making any changes here, also make it
+  // in the appropriate places in that method.
+  static bool ShouldOutputMessage(int aLevel) {
+    if (PreferenceAccess::sGfxLogLevel >= aLevel) {
 #if defined(WIN32) && !defined(PR_LOGGING)
-    if (aLevel >= sGfxLogLevel) {
-      ::OutputDebugStringA(aString.c_str());
-    }
-#elif defined(PR_LOGGING) && !(defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID))
-    if (PR_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
-      PR_LogPrint(aString.c_str());
-    }
+      return true;
+#elif defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID)
+      return true;
+#elif defined(PR_LOGGING)
+      if (PR_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
+        return true;
+      } else if ((PreferenceAccess::sGfxLogLevel >= LOG_DEBUG_PRLOG) ||
+                 (aLevel < LOG_DEBUG)) {
+        return true;
+      }
 #else
-    if (aLevel >= sGfxLogLevel) {
-#if defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID)
-      printf_stderr("%s", aString.c_str());
-#else
-      printf("%s", aString.c_str());
+      return true;
 #endif
     }
+    return false;
+  }
+
+  static void OutputMessage(const std::string &aString,
+                            int aLevel,
+                            bool aNoNewline) {
+    // This behavior (the higher the preference, the more we log)
+    // is consistent with what prlog does in general.  Note that if prlog
+    // is in the build, but disabled, we will printf if the preferences
+    // requires us to log something (see sGfxLogLevel for the special
+    // treatment of LOG_DEBUG and LOG_DEBUG_PRLOG)
+    //
+    // If making any logic changes to this method, you should probably
+    // make the corresponding change in the ShouldOutputMessage method
+    // above.
+    if (PreferenceAccess::sGfxLogLevel >= aLevel) {
+#if defined(WIN32) && !defined(PR_LOGGING)
+      ::OutputDebugStringA((aNoNewline ? aString : aString+"\n").c_str());
+#elif defined(MOZ_WIDGET_GONK) || defined(MOZ_WIDGET_ANDROID)
+      printf_stderr("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+#elif defined(PR_LOGGING)
+      if (PR_LOG_TEST(GetGFX2DLog(), PRLogLevelForLevel(aLevel))) {
+        PR_LogPrint("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+      } else if ((PreferenceAccess::sGfxLogLevel >= LOG_DEBUG_PRLOG) ||
+                 (aLevel < LOG_DEBUG)) {
+        printf("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
+      }
+#else
+      printf("%s%s", aString.c_str(), aNoNewline ? "" : "\n");
 #endif
+    }
   }
 };
 
 struct CriticalLogger {
-  static void OutputMessage(const std::string &aString, int aLevel);
+  static void OutputMessage(const std::string &aString, int aLevel, bool aNoNewline);
 };
 
 // Implement this interface and init the Factory with an instance to
 // forward critical logs.
 class LogForwarder {
 public:
   virtual ~LogForwarder() {}
   virtual void Log(const std::string &aString) = 0;
@@ -101,102 +186,283 @@ public:
   NoLog() {}
   ~NoLog() {}
 
   template<typename T>
   NoLog &operator <<(const T &aLogText) { return *this; }
 };
 
 MOZ_BEGIN_ENUM_CLASS(LogOptions, int)
-  NoNewline = 0x01
+  NoNewline = 0x01,
+  AutoPrefix = 0x02
 MOZ_END_ENUM_CLASS(LogOptions)
 
 template<typename T>
 struct Hexa {
   explicit Hexa(T aVal) : mVal(aVal) {}
   T mVal;
 };
 template<typename T>
 Hexa<T> hexa(T val) { return Hexa<T>(val); }
 
 template<int L, typename Logger = BasicLogger>
 class Log
 {
 public:
-  explicit Log(LogOptions aOptions = LogOptions(0)) : mOptions(aOptions) {}
+  explicit Log(int aOptions = (int)LogOptions::AutoPrefix)
+    : mOptions(aOptions)
+    , mLogIt(BasicLogger::ShouldOutputMessage(L))
+  {
+    if (mLogIt && AutoPrefix()) {
+      mMessage << "[GFX" << L << "]: ";
+    }
+  }
   ~Log() {
     Flush();
   }
 
   void Flush() {
-    if (!(int(mOptions) & int(LogOptions::NoNewline))) {
-      mMessage << '\n';
-    }
+    if (MOZ_LIKELY(!LogIt())) return;
+
     std::string str = mMessage.str();
     if (!str.empty()) {
       WriteLog(str);
     }
-    mMessage.str("");
+    if (AutoPrefix()) {
+      mMessage.str("[GFX");
+      mMessage << L << "]: ";
+    } else {
+      mMessage.str("");
+    }
     mMessage.clear();
   }
 
-  Log &operator <<(char aChar) { mMessage << aChar; return *this; }
-  Log &operator <<(const std::string &aLogText) { mMessage << aLogText; return *this; }
-  Log &operator <<(const char aStr[]) { mMessage << static_cast<const char*>(aStr); return *this; }
-  Log &operator <<(bool aBool) { mMessage << (aBool ? "true" : "false"); return *this; }
-  Log &operator <<(int aInt) { mMessage << aInt; return *this; }
-  Log &operator <<(unsigned int aInt) { mMessage << aInt; return *this; }
-  Log &operator <<(long aLong) { mMessage << aLong; return *this; }
-  Log &operator <<(unsigned long aLong) { mMessage << aLong; return *this; }
-  Log &operator <<(long long aLong) { mMessage << aLong; return *this; }
-  Log &operator <<(unsigned long long aLong) { mMessage << aLong; return *this; }
-  Log &operator <<(Float aFloat) { mMessage << aFloat; return *this; }
-  Log &operator <<(double aDouble) { mMessage << aDouble; return *this; }
+  Log &operator <<(char aChar) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << aChar;
+    }
+    return *this;
+  }
+  Log &operator <<(const std::string &aLogText) { 
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << aLogText;
+    }
+    return *this;
+  }
+  Log &operator <<(const char aStr[]) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << static_cast<const char*>(aStr);
+    }
+    return *this;
+  }
+  Log &operator <<(bool aBool) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << (aBool ? "true" : "false");
+    }
+    return *this;
+  }
+  Log &operator <<(int aInt) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << aInt;
+    }
+    return *this;
+  }
+  Log &operator <<(unsigned int aInt) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << aInt;
+    }
+    return *this;
+  }
+  Log &operator <<(long aLong) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << aLong;
+    }
+    return *this;
+  }
+  Log &operator <<(unsigned long aLong) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << aLong;
+    }
+    return *this;
+  }
+  Log &operator <<(long long aLong) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << aLong;
+    }
+    return *this;
+  }
+  Log &operator <<(unsigned long long aLong) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << aLong;
+    }
+    return *this;
+  }
+  Log &operator <<(Float aFloat) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << aFloat;
+    }
+    return *this;
+  }
+  Log &operator <<(double aDouble) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << aDouble;
+    }
+    return *this;
+  }
   template <typename T, typename Sub, typename Coord>
-  Log &operator <<(const BasePoint<T, Sub, Coord>& aPoint)
-    { mMessage << "Point" << aPoint; return *this; }
+  Log &operator <<(const BasePoint<T, Sub, Coord>& aPoint) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "Point" << aPoint;
+    }
+    return *this;
+  }
   template <typename T, typename Sub>
-  Log &operator <<(const BaseSize<T, Sub>& aSize)
-    { mMessage << "Size(" << aSize.width << "," << aSize.height << ")"; return *this; }
+  Log &operator <<(const BaseSize<T, Sub>& aSize) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "Size(" << aSize.width << "," << aSize.height << ")";
+    }
+    return *this;
+  }
   template <typename T, typename Sub, typename Point, typename SizeT, typename Margin>
-  Log &operator <<(const BaseRect<T, Sub, Point, SizeT, Margin>& aRect)
-    { mMessage << "Rect" << aRect; return *this; }
-  Log &operator<<(const Matrix& aMatrix)
-    { mMessage << "Matrix(" << aMatrix._11 << " " << aMatrix._12 << " ; " << aMatrix._21 << " " << aMatrix._22 << " ; " << aMatrix._31 << " " << aMatrix._32 << ")"; return *this; }
+  Log &operator <<(const BaseRect<T, Sub, Point, SizeT, Margin>& aRect) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "Rect" << aRect;
+    }
+    return *this;
+  }
+  Log &operator<<(const Matrix& aMatrix) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "Matrix(" << aMatrix._11 << " " << aMatrix._12 << " ; " << aMatrix._21 << " " << aMatrix._22 << " ; " << aMatrix._31 << " " << aMatrix._32 << ")";
+    }
+    return *this;
+  }
   template<typename T>
-  Log &operator<<(Hexa<T> aHex)
-    { mMessage << "0x" << std::hex << aHex.mVal << std::dec; return *this; }
+  Log &operator<<(Hexa<T> aHex) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      mMessage << "0x" << std::hex << aHex.mVal << std::dec;
+    }
+    return *this;
+  }
+
+  Log& operator<<(SurfaceFormat aFormat) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      switch(aFormat) {
+        case SurfaceFormat::B8G8R8A8:
+          mMessage << "SurfaceFormat::B8G8R8A8";
+          break;
+        case SurfaceFormat::B8G8R8X8:
+          mMessage << "SurfaceFormat::B8G8R8X8";
+          break;
+        case SurfaceFormat::R8G8B8A8:
+          mMessage << "SurfaceFormat::R8G8B8A8";
+          break;
+        case SurfaceFormat::R8G8B8X8:
+          mMessage << "SurfaceFormat::R8G8B8X8";
+          break;
+        case SurfaceFormat::R5G6B5:
+          mMessage << "SurfaceFormat::R5G6B5";
+          break;
+        case SurfaceFormat::A8:
+          mMessage << "SurfaceFormat::A8";
+          break;
+        case SurfaceFormat::YUV:
+          mMessage << "SurfaceFormat::YUV";
+          break;
+        case SurfaceFormat::UNKNOWN:
+          mMessage << "SurfaceFormat::UNKNOWN";
+          break;
+        default:
+          mMessage << "Invalid SurfaceFormat (" << (int)aFormat << ")";
+          break;
+      }
+    }
+    return *this;
+  }
+
+  Log& operator<<(SurfaceType aType) {
+    if (MOZ_UNLIKELY(LogIt())) {
+      switch(aType) {
+        case SurfaceType::DATA:
+          mMessage << "SurfaceType::DATA";
+          break;
+        case SurfaceType::D2D1_BITMAP:
+          mMessage << "SurfaceType::D2D1_BITMAP";
+          break;
+        case SurfaceType::D2D1_DRAWTARGET:
+          mMessage << "SurfaceType::D2D1_DRAWTARGET";
+          break;
+        case SurfaceType::CAIRO:
+          mMessage << "SurfaceType::CAIRO";
+          break;
+        case SurfaceType::CAIRO_IMAGE:
+          mMessage << "SurfaceType::CAIRO_IMAGE";
+          break;
+        case SurfaceType::COREGRAPHICS_IMAGE:
+          mMessage << "SurfaceType::COREGRAPHICS_IMAGE";
+          break;
+        case SurfaceType::COREGRAPHICS_CGCONTEXT:
+          mMessage << "SurfaceType::COREGRAPHICS_CGCONTEXT";
+          break;
+        case SurfaceType::SKIA:
+          mMessage << "SurfaceType::SKIA";
+          break;
+        case SurfaceType::DUAL_DT:
+          mMessage << "SurfaceType::DUAL_DT";
+          break;
+        case SurfaceType::D2D1_1_IMAGE:
+          mMessage << "SurfaceType::D2D1_1_IMAGE";
+          break;
+        case SurfaceType::RECORDING:
+          mMessage << "SurfaceType::RECORDING";
+          break;
+        case SurfaceType::TILED:
+          mMessage << "SurfaceType::TILED";
+          break;
+        default:
+          mMessage << "Invalid SurfaceType (" << (int)aType << ")";
+          break;
+      }
+    }
+    return *this;
+  }
+
+  inline bool LogIt() const { return mLogIt; }
+  inline bool NoNewline() const { return mOptions & int(LogOptions::NoNewline); }
+  inline bool AutoPrefix() const { return mOptions & int(LogOptions::AutoPrefix); }
+
 
 private:
-
   void WriteLog(const std::string &aString) {
-    Logger::OutputMessage(aString, L);
+    if (MOZ_UNLIKELY(LogIt())) {
+      Logger::OutputMessage(aString, L, NoNewline());
+    }
   }
 
   std::stringstream mMessage;
-  LogOptions mOptions;
+  int mOptions;
+  bool mLogIt;
 };
 
 typedef Log<LOG_DEBUG> DebugLog;
 typedef Log<LOG_WARNING> WarningLog;
 typedef Log<LOG_CRITICAL, CriticalLogger> CriticalLog;
 
 #ifdef GFX_LOG_DEBUG
-#define gfxDebug DebugLog
+#define gfxDebug mozilla::gfx::DebugLog
 #else
-#define gfxDebug if (1) ; else NoLog
+#define gfxDebug if (1) ; else mozilla::gfx::NoLog
 #endif
 #ifdef GFX_LOG_WARNING
-#define gfxWarning WarningLog
+#define gfxWarning mozilla::gfx::WarningLog
 #else
-#define gfxWarning if (1) ; else NoLog
+#define gfxWarning if (1) ; else mozilla::gfx::NoLog
 #endif
 
 // This log goes into crash reports, use with care.
-#define gfxCriticalError CriticalLog
+#define gfxCriticalError mozilla::gfx::CriticalLog
 
 // See nsDebug.h and the NS_WARN_IF macro
 
 #ifdef __cplusplus
 #ifdef DEBUG
 inline bool MOZ2D_warn_if_impl(bool aCondition, const char* aExpr,
                                const char* aFile, int32_t aLine)
 {
@@ -213,17 +479,17 @@ inline bool MOZ2D_warn_if_impl(bool aCon
 #endif
 
 const int INDENT_PER_LEVEL = 2;
 
 class TreeLog
 {
 public:
   explicit TreeLog(const std::string& aPrefix = "")
-        : mLog(LogOptions::NoNewline),
+        : mLog(int(LogOptions::NoNewline)),
           mPrefix(aPrefix),
           mDepth(0),
           mStartOfLine(true),
           mConditionedOnPref(false),
           mPrefFunction(nullptr) {}
 
   template <typename T>
   TreeLog& operator<<(const T& aObject) {
--- a/gfx/2d/gfx2d.vcxproj
+++ b/gfx/2d/gfx2d.vcxproj
@@ -57,17 +57,17 @@
     </Link>
     <PreBuildEvent>
       <Command>xcopy $(ProjectDir)..\..\mfbt\*.h mozilla\ /Y</Command>
       <Message>Copying MFBT files</Message>
     </PreBuildEvent>
   </ItemDefinitionGroup>
   <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
     <ClCompile>
-      <PreprocessorDefinitions>USE_SSE2;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <PreprocessorDefinitions>USE_SSE2;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions);GFX_LOG_WARNING</PreprocessorDefinitions>
       <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
       <WarningLevel>Level3</WarningLevel>
       <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
       <AdditionalIncludeDirectories>./</AdditionalIncludeDirectories>
     </ClCompile>
     <Link>
       <TargetMachine>MachineX86</TargetMachine>
       <GenerateDebugInformation>true</GenerateDebugInformation>
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -152,12 +152,19 @@ for var in ('USE_CAIRO', 'MOZ2D_HAS_MOZ_
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3', 'gonk', 'qt'):
     DEFINES['MOZ_ENABLE_FREETYPE'] = True
 
 if CONFIG['MOZ_DEBUG']:
     DEFINES['GFX_LOG_DEBUG'] = True
     DEFINES['GFX_LOG_WARNING'] = True
 
+# Define the GFX_LOG_WARNING in release builds (available, but controlled by a
+# preference), though we may want to consider only doing it in the nightly
+# build, if the size of gfxWarning() code ends up making a difference.
+# See bug 1074952.
+# if CONFIG['NIGHTLY_BUILD']:
+DEFINES['GFX_LOG_WARNING'] = True
+
 CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gtk3', 'gonk', 'qt'):
     CXXFLAGS += CONFIG['CAIRO_FT_CFLAGS']
--- a/gfx/layers/basic/BasicCompositor.cpp
+++ b/gfx/layers/basic/BasicCompositor.cpp
@@ -12,18 +12,18 @@
 #include "gfx2DGlue.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Helpers.h"
 #include "gfxUtils.h"
 #include "YCbCrUtils.h"
 #include <algorithm>
 #include "ImageContainer.h"
 #include "gfxPrefs.h"
-#define PIXMAN_DONT_DEFINE_STDINT
-#include "pixman.h"                     // for pixman_f_transform, etc
+#include "skia/SkCanvas.h"              // for SkCanvas
+#include "skia/SkBitmapDevice.h"        // for SkBitmapDevice
 
 namespace mozilla {
 using namespace mozilla::gfx;
 
 namespace layers {
 
 class DataTextureSourceBasic : public DataTextureSource
                              , public TextureSourceBasic
@@ -163,85 +163,72 @@ DrawSurfaceWithTextureCoords(DrawTarget 
   // Only use REPEAT if aTextureCoords is outside (0, 0, 1, 1).
   gfx::Rect unitRect(0, 0, 1, 1);
   ExtendMode mode = unitRect.Contains(aTextureCoords) ? ExtendMode::CLAMP : ExtendMode::REPEAT;
 
   FillRectWithMask(aDest, aDestRect, aSource, aFilter, DrawOptions(aOpacity),
                    mode, aMask, aMaskTransform, &matrix);
 }
 
-static pixman_transform
-Matrix3DToPixman(const gfx3DMatrix& aMatrix)
+static SkMatrix
+Matrix3DToSkia(const gfx3DMatrix& aMatrix)
 {
-  pixman_f_transform transform;
+  SkMatrix transform;
+  transform.setAll(aMatrix._11,
+                   aMatrix._21,
+                   aMatrix._41,
+                   aMatrix._12,
+                   aMatrix._22,
+                   aMatrix._42,
+                   aMatrix._14,
+                   aMatrix._24,
+                   aMatrix._44);
 
-  transform.m[0][0] = aMatrix._11;
-  transform.m[0][1] = aMatrix._21;
-  transform.m[0][2] = aMatrix._41;
-  transform.m[1][0] = aMatrix._12;
-  transform.m[1][1] = aMatrix._22;
-  transform.m[1][2] = aMatrix._42;
-  transform.m[2][0] = aMatrix._14;
-  transform.m[2][1] = aMatrix._24;
-  transform.m[2][2] = aMatrix._44;
-
-  pixman_transform result;
-  pixman_transform_from_pixman_f_transform(&result, &transform);
-
-  return result;
+  return transform;
 }
 
 static void
-PixmanTransform(DataSourceSurface* aDest,
-                DataSourceSurface* aSource,
-                const gfx3DMatrix& aTransform,
-                const Point& aDestOffset)
+SkiaTransform(DataSourceSurface* aDest,
+              DataSourceSurface* aSource,
+              const gfx3DMatrix& aTransform,
+              const Point& aDestOffset)
 {
+  if (aTransform.IsSingular()) {
+    return;
+  }
+
   IntSize destSize = aDest->GetSize();
-  pixman_image_t* dest = pixman_image_create_bits(PIXMAN_a8r8g8b8,
-                                                  destSize.width,
-                                                  destSize.height,
-                                                  (uint32_t*)aDest->GetData(),
-                                                  aDest->Stride());
+  SkImageInfo destInfo = SkImageInfo::Make(destSize.width,
+                                           destSize.height,
+                                           kBGRA_8888_SkColorType,
+                                           kPremul_SkAlphaType);
+  SkBitmap destBitmap;
+  destBitmap.setInfo(destInfo, aDest->Stride());
+  destBitmap.setPixels((uint32_t*)aDest->GetData());
+  SkCanvas destCanvas(destBitmap);
 
   IntSize srcSize = aSource->GetSize();
-  pixman_image_t* src = pixman_image_create_bits(PIXMAN_a8r8g8b8,
-                                                 srcSize.width,
-                                                 srcSize.height,
-                                                 (uint32_t*)aSource->GetData(),
-                                                 aSource->Stride());
-
-  NS_ABORT_IF_FALSE(src && dest, "Failed to create pixman images?");
-
-  pixman_transform pixTransform = Matrix3DToPixman(aTransform);
-  pixman_transform pixTransformInverted;
+  SkImageInfo srcInfo = SkImageInfo::Make(srcSize.width,
+                                          srcSize.height,
+                                          kBGRA_8888_SkColorType,
+                                          kPremul_SkAlphaType);
+  SkBitmap src;
+  src.setInfo(srcInfo, aSource->Stride());
+  src.setPixels((uint32_t*)aSource->GetData());
 
-  // If the transform is singular then nothing would be drawn anyway, return here
-  if (!pixman_transform_invert(&pixTransformInverted, &pixTransform)) {
-    pixman_image_unref(dest);
-    pixman_image_unref(src);
-    return;
-  }
-  pixman_image_set_transform(src, &pixTransformInverted);
+  gfx3DMatrix transform = aTransform;
+  transform.TranslatePost(Point3D(-aDestOffset.x, -aDestOffset.y, 0));
+  destCanvas.setMatrix(Matrix3DToSkia(transform));
 
-  pixman_image_composite32(PIXMAN_OP_SRC,
-                           src,
-                           nullptr,
-                           dest,
-                           aDestOffset.x,
-                           aDestOffset.y,
-                           0,
-                           0,
-                           0,
-                           0,
-                           destSize.width,
-                           destSize.height);
-
-  pixman_image_unref(dest);
-  pixman_image_unref(src);
+  SkPaint paint;
+  paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+  paint.setAntiAlias(true);
+  paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+  SkRect destRect = SkRect::MakeXYWH(0, 0, srcSize.width, srcSize.height);
+  destCanvas.drawBitmapRectToRect(src, nullptr, destRect, &paint);
 }
 
 static inline IntRect
 RoundOut(Rect r)
 {
   r.RoundOut();
   return IntRect(r.x, r.y, r.width, r.height);
 }
@@ -372,22 +359,22 @@ BasicCompositor::DrawQuad(const gfx::Rec
   }
 
   if (!aTransform.Is2D()) {
     dest->Flush();
 
     RefPtr<SourceSurface> snapshot = dest->Snapshot();
     RefPtr<DataSourceSurface> source = snapshot->GetDataSurface();
     RefPtr<DataSourceSurface> temp =
-      Factory::CreateDataSourceSurface(RoundOut(transformBounds).Size(), SurfaceFormat::B8G8R8A8);
+      Factory::CreateDataSourceSurface(RoundOut(transformBounds).Size(), SurfaceFormat::B8G8R8A8, true);
     if (NS_WARN_IF(!temp)) {
       return;
     }
 
-    PixmanTransform(temp, source, new3DTransform, transformBounds.TopLeft());
+    SkiaTransform(temp, source, new3DTransform, transformBounds.TopLeft());
 
     transformBounds.MoveTo(0, 0);
     buffer->DrawSurface(temp, transformBounds, transformBounds);
   }
 
   buffer->PopClip();
 }
 
--- a/gfx/layers/basic/BasicLayerManager.cpp
+++ b/gfx/layers/basic/BasicLayerManager.cpp
@@ -41,19 +41,18 @@
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCOMPtr.h"                   // for already_AddRefed
 #include "nsDebug.h"                    // for NS_ASSERTION, etc
 #include "nsISupportsImpl.h"            // for gfxContext::Release, etc
 #include "nsPoint.h"                    // for nsIntPoint
 #include "nsRect.h"                     // for nsIntRect
 #include "nsRegion.h"                   // for nsIntRegion, etc
 #include "nsTArray.h"                   // for nsAutoTArray
-#define PIXMAN_DONT_DEFINE_STDINT
-#include "pixman.h"                     // for pixman_f_transform, etc
-
+#include "skia/SkCanvas.h"              // for SkCanvas
+#include "skia/SkBitmapDevice.h"        // for SkBitmapDevice
 class nsIWidget;
 
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 /**
@@ -600,85 +599,72 @@ void
 BasicLayerManager::SetRoot(Layer* aLayer)
 {
   NS_ASSERTION(aLayer, "Root can't be null");
   NS_ASSERTION(aLayer->Manager() == this, "Wrong manager");
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
   mRoot = aLayer;
 }
 
-static pixman_transform
-BasicLayerManager_Matrix3DToPixman(const gfx3DMatrix& aMatrix)
+static SkMatrix
+BasicLayerManager_Matrix3DToSkia(const gfx3DMatrix& aMatrix)
 {
-  pixman_f_transform transform;
+  SkMatrix transform;
+  transform.setAll(aMatrix._11,
+                   aMatrix._21,
+                   aMatrix._41,
+                   aMatrix._12,
+                   aMatrix._22,
+                   aMatrix._42,
+                   aMatrix._14,
+                   aMatrix._24,
+                   aMatrix._44);
 
-  transform.m[0][0] = aMatrix._11;
-  transform.m[0][1] = aMatrix._21;
-  transform.m[0][2] = aMatrix._41;
-  transform.m[1][0] = aMatrix._12;
-  transform.m[1][1] = aMatrix._22;
-  transform.m[1][2] = aMatrix._42;
-  transform.m[2][0] = aMatrix._14;
-  transform.m[2][1] = aMatrix._24;
-  transform.m[2][2] = aMatrix._44;
-
-  pixman_transform result;
-  pixman_transform_from_pixman_f_transform(&result, &transform);
-
-  return result;
+  return transform;
 }
 
 static void
-PixmanTransform(const gfxImageSurface* aDest,
-                RefPtr<DataSourceSurface> aSrc,
-                const gfx3DMatrix& aTransform,
-                gfxPoint aDestOffset)
+SkiaTransform(const gfxImageSurface* aDest,
+              RefPtr<DataSourceSurface> aSrc,
+              const gfx3DMatrix& aTransform,
+              gfxPoint aDestOffset)
 {
+  if (aTransform.IsSingular()) {
+    return;
+  }
+
   IntSize destSize = ToIntSize(aDest->GetSize());
-  pixman_image_t* dest = pixman_image_create_bits(aDest->Format() == gfxImageFormat::ARGB32 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
-                                                  destSize.width,
-                                                  destSize.height,
-                                                  (uint32_t*)aDest->Data(),
-                                                  aDest->Stride());
+  SkImageInfo destInfo = SkImageInfo::Make(destSize.width,
+                                           destSize.height,
+                                           kBGRA_8888_SkColorType,
+                                           kPremul_SkAlphaType);
+  SkBitmap destBitmap;
+  destBitmap.setInfo(destInfo, aDest->Stride());
+  destBitmap.setPixels((uint32_t*)aDest->Data());
+  SkCanvas destCanvas(destBitmap);
 
   IntSize srcSize = aSrc->GetSize();
-  pixman_image_t* src = pixman_image_create_bits(aSrc->GetFormat() == SurfaceFormat::B8G8R8A8 ? PIXMAN_a8r8g8b8 : PIXMAN_x8r8g8b8,
-                                                 srcSize.width,
-                                                 srcSize.height,
-                                                 (uint32_t*)aSrc->GetData(),
-                                                 aSrc->Stride());
-
-  NS_ABORT_IF_FALSE(src && dest, "Failed to create pixman images?");
-
-  pixman_transform pixTransform = BasicLayerManager_Matrix3DToPixman(aTransform);
-  pixman_transform pixTransformInverted;
+  SkImageInfo srcInfo = SkImageInfo::Make(srcSize.width,
+                                          srcSize.height,
+                                          kBGRA_8888_SkColorType,
+                                          kPremul_SkAlphaType);
+  SkBitmap src;
+  src.setInfo(srcInfo, aSrc->Stride());
+  src.setPixels((uint32_t*)aSrc->GetData());
 
-  // If the transform is singular then nothing would be drawn anyway, return here
-  if (!pixman_transform_invert(&pixTransformInverted, &pixTransform)) {
-    pixman_image_unref(dest);
-    pixman_image_unref(src);
-    return;
-  }
-  pixman_image_set_transform(src, &pixTransformInverted);
+  gfx3DMatrix transform = aTransform;
+  transform.TranslatePost(Point3D(-aDestOffset.x, -aDestOffset.y, 0));
+  destCanvas.setMatrix(BasicLayerManager_Matrix3DToSkia(transform));
 
-  pixman_image_composite32(PIXMAN_OP_SRC,
-                           src,
-                           nullptr,
-                           dest,
-                           aDestOffset.x,
-                           aDestOffset.y,
-                           0,
-                           0,
-                           0,
-                           0,
-                           destSize.width,
-                           destSize.height);
-
-  pixman_image_unref(dest);
-  pixman_image_unref(src);
+  SkPaint paint;
+  paint.setXfermodeMode(SkXfermode::kSrc_Mode);
+  paint.setAntiAlias(true);
+  paint.setFilterLevel(SkPaint::kLow_FilterLevel);
+  SkRect destRect = SkRect::MakeXYWH(0, 0, srcSize.width, srcSize.height);
+  destCanvas.drawBitmapRectToRect(src, nullptr, destRect, &paint);
 }
 
 /**
  * Transform a surface using a gfx3DMatrix and blit to the destination if
  * it is efficient to do so.
  *
  * @param aSource       Source surface.
  * @param aDest         Desintation context.
@@ -711,17 +697,17 @@ Transform3D(RefPtr<SourceSurface> aSourc
                                                                        aDestRect.height),
                                                             gfxImageFormat::ARGB32);
   gfxPoint offset = aDestRect.TopLeft();
 
   // Include a translation to the correct origin.
   gfx3DMatrix translation = gfx3DMatrix::Translation(aBounds.x, aBounds.y, 0);
 
   // Transform the content and offset it such that the content begins at the origin.
-  PixmanTransform(destImage, aSource->GetDataSurface(), translation * aTransform, offset);
+  SkiaTransform(destImage, aSource->GetDataSurface(), translation * aTransform, offset);
 
   // If we haven't actually drawn to aDest then return our temporary image so
   // that the caller can do this.
   return destImage.forget();
 }
 
 void
 BasicLayerManager::PaintSelfOrChildren(PaintLayerContext& aPaintContext,
--- a/gfx/layers/client/TextureClientPool.cpp
+++ b/gfx/layers/client/TextureClientPool.cpp
@@ -27,16 +27,19 @@ TextureClientPool::TextureClientPool(gfx
   : mFormat(aFormat)
   , mSize(aSize)
   , mMaxTextureClients(aMaxTextureClients)
   , mShrinkTimeoutMsec(aShrinkTimeoutMsec)
   , mOutstandingClients(0)
   , mSurfaceAllocator(aAllocator)
 {
   mTimer = do_CreateInstance("@mozilla.org/timer;1");
+  if (aFormat == gfx::SurfaceFormat::UNKNOWN) {
+    gfxWarning() << "Creating texture pool for SurfaceFormat::UNKNOWN format";
+  }
 }
 
 TextureClientPool::~TextureClientPool()
 {
   mTimer->Cancel();
 }
 
 TemporaryRef<TextureClient>
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -1065,23 +1065,23 @@ CompositorD3D11::UpdateConstantBuffers()
   return true;
 }
 
 void
 CompositorD3D11::SetSamplerForFilter(Filter aFilter)
 {
   ID3D11SamplerState *sampler;
   switch (aFilter) {
+    case Filter::POINT:
+    sampler = mAttachments->mPointSamplerState;
+    break;
+  case Filter::LINEAR:
   default:
-  case Filter::LINEAR:
     sampler = mAttachments->mLinearSamplerState;
     break;
-  case Filter::POINT:
-    sampler = mAttachments->mPointSamplerState;
-    break;
   }
 
   mContext->PSSetSamplers(0, 1, &sampler);
 }
 
 void
 CompositorD3D11::PaintToTarget()
 {
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -283,17 +283,17 @@ TextureClientD3D11::Unlock()
     desc.Usage = D3D10_USAGE_STAGING;
     desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ;
     desc.MiscFlags = 0;
 
     RefPtr<ID3D10Texture2D> tex;
     HRESULT hr = device->CreateTexture2D(&desc, nullptr, byRef(tex));
 
     if (FAILED(hr)) {
-      gfx::gfxCriticalError() << "[D3D11] CreateTexture2D failure " << mSize << " Code: " << gfx::hexa(hr);
+      gfxCriticalError() << "[D3D11] CreateTexture2D failure " << mSize << " Code: " << gfx::hexa(hr);
       return;
     }
 
     if (SUCCEEDED(hr)) {
       device->CopyResource(tex, mTexture10);
 
       gfxWindowsPlatform::GetPlatform()->GetReadbackManager()->PostTask(tex, mReadbackSink);
     } else {
@@ -370,17 +370,17 @@ TextureClientD3D11::AllocateForSurface(g
       D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE);
 
     newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
 
     hr = device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture10));
   }
 
   if (FAILED(hr)) {
-    gfx::gfxCriticalError() << "[D3D11] CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
+    gfxCriticalError() << "[D3D11] CreateTexture2D failure " << aSize << " Code: " << gfx::hexa(hr);
     return false;
   }
 
   // Defer clearing to the next time we lock to avoid an extra (expensive) lock.
   mNeedsClear = aFlags & ALLOC_CLEAR_BUFFER;
   mNeedsClearWhite = aFlags & ALLOC_CLEAR_BUFFER_WHITE;
 
   return true;
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -6,16 +6,17 @@
 #include "mozilla/layers/AsyncTransactionTracker.h" // for AsyncTransactionTracker
 #include "mozilla/layers/CompositorChild.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "mozilla/layers/ImageBridgeChild.h"
 #include "mozilla/layers/SharedBufferManagerChild.h"
 #include "mozilla/layers/ISurfaceAllocator.h"     // for GfxMemoryImageReporter
 
 #include "prlog.h"
+#include "prprf.h"
 
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "gfxTextRun.h"
 
 #ifdef XP_WIN
 #include <process.h>
 #define getpid _getpid
@@ -147,31 +148,109 @@ class SRGBOverrideObserver MOZ_FINAL : p
                                        public nsSupportsWeakReference
 {
     ~SRGBOverrideObserver() {}
 public:
     NS_DECL_ISUPPORTS
     NS_DECL_NSIOBSERVER
 };
 
+/// This override of the LogForwarder, initially used for the critical graphics
+/// errors, is sending the log to the crash annotations as well, but only
+/// if the capacity set with the method below is >= 2.  We always retain the
+/// very first critical message, and the latest capacity-1 messages are
+/// rotated through. Note that we don't expect the total number of times
+/// this gets called to be large - it is meant for critical errors only.
+
 class CrashStatsLogForwarder: public mozilla::gfx::LogForwarder
 {
 public:
-    virtual void Log(const std::string& aString) MOZ_OVERRIDE {
-        if (!NS_IsMainThread()) {
-            return;
-        }
+  CrashStatsLogForwarder(const char* aKey);
+  virtual void Log(const std::string& aString) MOZ_OVERRIDE;
+
+  void SetCircularBufferSize(uint32_t aCapacity);
+
+private:
+  // Helpers for the Log()
+  bool UpdateStringsVector(const std::string& aString);
+  void UpdateCrashReport();
+
+private:
+  std::vector<std::pair<int32_t,std::string> > mBuffer;
+  nsCString mCrashCriticalKey;
+  uint32_t mMaxCapacity;
+  int32_t mIndex;
+};
+
+CrashStatsLogForwarder::CrashStatsLogForwarder(const char* aKey)
+  : mBuffer()
+  , mCrashCriticalKey(aKey)
+  , mMaxCapacity(0)
+  , mIndex(-1)
+{
+}
+
+void CrashStatsLogForwarder::SetCircularBufferSize(uint32_t aCapacity)
+{
+  mMaxCapacity = aCapacity;
+  mBuffer.reserve(static_cast<size_t>(aCapacity));
+}
+
+bool
+CrashStatsLogForwarder::UpdateStringsVector(const std::string& aString)
+{
+  // We want at least the first one and the last one.  Otherwise, no point.
+  if (mMaxCapacity < 2) {
+    return false;
+  }
+
+  mIndex += 1;
+  MOZ_ASSERT(mIndex >= 0);
+
+  // index will count 0, 1, 2, ..., max-1, 1, 2, ..., max-1, 1, 2, ...
+  int32_t index = mIndex ? (mIndex-1) % (mMaxCapacity-1) + 1 : 0;
+  MOZ_ASSERT(index >= 0 && index < (int32_t)mMaxCapacity);
+  MOZ_ASSERT(index <= mIndex && index <= (int32_t)mBuffer.size());
+
+  // Checking for index >= mBuffer.size(), rather than index == mBuffer.size()
+  // just out of paranoia, but we know index <= mBuffer.size().
+  std::pair<int32_t,std::string> newEntry(mIndex,aString);
+  if (index >= static_cast<int32_t>(mBuffer.size())) {
+    mBuffer.push_back(newEntry);
+  } else {
+    mBuffer[index] = newEntry;
+  }
+  return true;
+}
+
+void CrashStatsLogForwarder::UpdateCrashReport()
+{
+  std::stringstream message;
+  for(std::vector<std::pair<int32_t, std::string> >::iterator it = mBuffer.begin(); it != mBuffer.end(); ++it) {
+    message << "|[" << (*it).first << "]" << (*it).second;
+  }
+
 #ifdef MOZ_CRASHREPORTER
-        nsCString reportString(aString.c_str());
-        CrashReporter::AppendAppNotesToCrashReport(reportString);
+  nsCString reportString(message.str().c_str());
+  nsresult annotated = CrashReporter::AnnotateCrashReport(mCrashCriticalKey, reportString);
 #else
-        printf("GFX ERROR: %s", aString.c_str());
+  nsresult annotated = NS_ERROR_NOT_IMPLEMENTED;
 #endif
-    }
-};
+  if (annotated != NS_OK) {
+    printf("Crash Annotation %s: %s",
+           mCrashCriticalKey.get(), message.str().c_str());
+  }
+}
+  
+void CrashStatsLogForwarder::Log(const std::string& aString)
+{
+  if (UpdateStringsVector(aString)) {
+    UpdateCrashReport();
+  }
+}
 
 NS_IMPL_ISUPPORTS(SRGBOverrideObserver, nsIObserver, nsISupportsWeakReference)
 
 #define GFX_DOWNLOADABLE_FONTS_ENABLED "gfx.downloadable_fonts.enabled"
 
 #define GFX_PREF_FALLBACK_USE_CMAPS  "gfx.font_rendering.fallback.always_use_cmaps"
 
 #define GFX_PREF_OPENTYPE_SVG "gfx.font_rendering.opentype_svg.enabled"
@@ -353,21 +432,24 @@ void RecordingPrefChanged(const char *aP
 void
 gfxPlatform::Init()
 {
     if (gEverInitialized) {
         NS_RUNTIMEABORT("Already started???");
     }
     gEverInitialized = true;
 
-    mozilla::gfx::Factory::SetLogForwarder(new CrashStatsLogForwarder);
+    CrashStatsLogForwarder* logForwarder = new CrashStatsLogForwarder("GraphicsCriticalError");
+    mozilla::gfx::Factory::SetLogForwarder(logForwarder);
 
     // Initialize the preferences by creating the singleton.
     gfxPrefs::GetSingleton();
 
+    logForwarder->SetCircularBufferSize(gfxPrefs::GfxLoggingCrashLength());
+
     gGfxPlatformPrefsLock = new Mutex("gfxPlatform::gGfxPlatformPrefsLock");
 
     /* Initialize the GfxInfo service.
      * Note: we can't call functions on GfxInfo that depend
      * on gPlatform until after it has been initialized
      * below. GfxInfo initialization annotates our
      * crash reports so we want to do it before
      * we try to load any drivers and do device detection
@@ -525,16 +607,19 @@ gfxPlatform::Shutdown()
     // most platforms.  Windows is a "special snowflake", though, and has three
     // context providers available, so we have to shut all of them down.
     // We should only support the default GL provider on Windows; then, this
     // could go away. Unfortunately, we currently support WGL (the default) for
     // WebGL on Optimus.
     mozilla::gl::GLContextProviderEGL::Shutdown();
 #endif
 
+    // This is a bit iffy - we're assuming that we were the ones that set the
+    // log forwarder in the Factory, so that it's our responsibility to 
+    // delete it.
     delete mozilla::gfx::Factory::GetLogForwarder();
     mozilla::gfx::Factory::SetLogForwarder(nullptr);
 
     delete gGfxPlatformPrefsLock;
 
     gfxPrefs::DestroySingleton();
     gfxFont::DestroySingletons();
 
--- a/gfx/thebes/gfxPrefs.cpp
+++ b/gfx/thebes/gfxPrefs.cpp
@@ -2,22 +2,41 @@
  * 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 "gfxPrefs.h"
 
 #include "mozilla/Preferences.h"
 #include "MainThreadUtils.h"
+#include "mozilla/gfx/Logging.h"
 
 using namespace mozilla;
 
 gfxPrefs* gfxPrefs::sInstance = nullptr;
 bool gfxPrefs::sInstanceHasBeenDestroyed = false;
 
+class PreferenceAccessImpl : public mozilla::gfx::PreferenceAccess
+{
+public:
+  virtual ~PreferenceAccessImpl();
+  virtual void LivePref(const char* aName, int32_t* aVar, int32_t aDefault) MOZ_OVERRIDE;
+};
+
+PreferenceAccessImpl::~PreferenceAccessImpl()
+{
+}
+
+void PreferenceAccessImpl::LivePref(const char* aName,
+                                    int32_t* aVar,
+                                    int32_t aDefault)
+{
+  Preferences::AddIntVarCache(aVar, aName, aDefault);
+}
+
 void
 gfxPrefs::DestroySingleton()
 {
   if (sInstance) {
     delete sInstance;
     sInstance = nullptr;
     sInstanceHasBeenDestroyed = true;
   }
@@ -28,21 +47,29 @@ bool
 gfxPrefs::SingletonExists()
 {
   return sInstance != nullptr;
 }
 
 gfxPrefs::gfxPrefs()
 {
   gfxPrefs::AssertMainThread();
+  mMoz2DPrefAccess = new PreferenceAccessImpl;
+  mozilla::gfx::PreferenceAccess::SetAccess(mMoz2DPrefAccess);
 }
 
 gfxPrefs::~gfxPrefs()
 {
   gfxPrefs::AssertMainThread();
+
+  // gfxPrefs is a singleton, we can reset this to null once
+  // it goes away.
+  mozilla::gfx::PreferenceAccess::SetAccess(nullptr);
+  delete mMoz2DPrefAccess;
+  mMoz2DPrefAccess = nullptr;
 }
 
 void gfxPrefs::AssertMainThread()
 {
   MOZ_ASSERT(NS_IsMainThread(), "this code must be run on the main thread");
 }
 
 void gfxPrefs::PrefAddVarCache(bool* aVariable,
@@ -107,8 +134,9 @@ void gfxPrefs::PrefSet(const char* aPref
 {
   Preferences::SetUint(aPref, aValue);
 }
 
 void gfxPrefs::PrefSet(const char* aPref, float aValue)
 {
   Preferences::SetFloat(aPref, aValue);
 }
+
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -63,20 +63,25 @@ public:                                 
 static Type Name() { MOZ_ASSERT(SingletonExists()); return GetSingleton().mPref##Name.mValue; } \
 static void Set##Name(Type aVal) { MOZ_ASSERT(SingletonExists()); \
     GetSingleton().mPref##Name.Set(UpdatePolicy::Update, Get##Name##PrefName(), aVal); } \
 private:                                                                      \
 static const char* Get##Name##PrefName() { return Pref; }                     \
 static Type Get##Name##PrefDefault() { return Default; }                      \
 PrefTemplate<UpdatePolicy::Update, Type, Get##Name##PrefDefault, Get##Name##PrefName> mPref##Name
 
+class PreferenceAccessImpl;
 class gfxPrefs;
 class gfxPrefs MOZ_FINAL
 {
 private:
+  /// See Logging.h.  This lets Moz2D access preference values it owns.
+  PreferenceAccessImpl* mMoz2DPrefAccess;
+
+private:
   // Enums for the update policy.
   MOZ_BEGIN_NESTED_ENUM_CLASS(UpdatePolicy)
     Skip, // Set the value to default, skip any Preferences calls
     Once, // Evaluate the preference once, unchanged during the session
     Live  // Evaluate the preference and set callback so it stays current/live
   MOZ_END_NESTED_ENUM_CLASS(UpdatePolicy)
 
   // Since we cannot use const char*, use a function that returns it.
@@ -199,16 +204,18 @@ private:
   DECL_GFX_PREF(Live, "gfx.color_management.rendering_intent", CMSRenderingIntent, int32_t, 0);
 
   DECL_GFX_PREF(Once, "gfx.direct2d.disabled",                 Direct2DDisabled, bool, false);
   DECL_GFX_PREF(Once, "gfx.direct2d.force-enabled",            Direct2DForceEnabled, bool, false);
   DECL_GFX_PREF(Live, "gfx.direct2d.use1_1",                   Direct2DUse1_1, bool, false);
   DECL_GFX_PREF(Live, "gfx.gralloc.fence-with-readpixels",     GrallocFenceWithReadPixels, bool, false);
   DECL_GFX_PREF(Live, "gfx.layerscope.enabled",                LayerScopeEnabled, bool, false);
   DECL_GFX_PREF(Live, "gfx.layerscope.port",                   LayerScopePort, int32_t, 23456);
+  // Note that        "gfx.logging.level" is defined in Logging.h
+  DECL_GFX_PREF(Once, "gfx.logging.crash.length",              GfxLoggingCrashLength, uint32_t, 6);
   DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled",             PerfWarnings, bool, false);
   DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs",           WorkAroundDriverBugs, bool, true);
 
   DECL_GFX_PREF(Live, "gfx.draw-color-bars",                   CompositorDrawColorBars, bool, false);
 
   // Use vsync events generated by hardware
   DECL_GFX_PREF(Once, "gfx.vsync.hw-vsync.enabled",            HardwareVsyncEnabled, bool, false);
   DECL_GFX_PREF(Once, "gfx.vsync.compositor",                  VsyncAlignedCompositor, bool, false);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -1684,17 +1684,20 @@ gfxWindowsPlatform::InitD3D11Devices()
 
   if (!adapter) {
     return;
   }
 
   HRESULT hr;
 
   hr = d3d11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr,
-                         D3D11_CREATE_DEVICE_BGRA_SUPPORT,
+                         // Use
+                         // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS
+                         // to prevent bug 1092260. IE 11 also uses this flag.
+                         D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
                          featureLevels.Elements(), featureLevels.Length(),
                          D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr);
 
   if (FAILED(hr)) {
     return;
   }
 
   mD3D11Device->SetExceptionMode(0);
new file mode 100644
--- /dev/null
+++ b/image/src/DecodePool.cpp
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DecodePool.h"
+
+#include <algorithm>
+
+#include "mozilla/ClearOnShutdown.h"
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIObserverService.h"
+#include "nsIThreadPool.h"
+#include "nsXPCOMCIDInternal.h"
+#include "prsystem.h"
+
+#ifdef MOZ_NUWA_PROCESS
+#include "ipc/Nuwa.h"
+#endif
+
+#include "gfxPrefs.h"
+
+#include "Decoder.h"
+#include "RasterImage.h"
+
+using std::max;
+using std::min;
+
+namespace mozilla {
+namespace image {
+
+///////////////////////////////////////////////////////////////////////////////
+// Helper runnables.
+///////////////////////////////////////////////////////////////////////////////
+
+class NotifyProgressWorker : public nsRunnable
+{
+public:
+  /**
+   * Called by the DecodePool when it's done some significant portion of
+   * decoding, so that progress can be recorded and notifications can be sent.
+   */
+  static void Dispatch(RasterImage* aImage)
+  {
+    nsCOMPtr<nsIRunnable> worker = new NotifyProgressWorker(aImage);
+    NS_DispatchToMainThread(worker);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
+
+    mImage->FinishedSomeDecoding(ShutdownReason::DONE);
+
+    return NS_OK;
+  }
+
+private:
+  explicit NotifyProgressWorker(RasterImage* aImage)
+    : mImage(aImage)
+  { }
+
+  nsRefPtr<RasterImage> mImage;
+};
+
+class FrameNeededWorker : public nsRunnable
+{
+public:
+  /**
+   * Called when an off-main-thread decoder needs a new frame to be allocated on
+   * the main thread.
+   *
+   * After allocating the new frame, the worker will call RequestDecode to
+   * continue decoding.
+   */
+  static void Dispatch(RasterImage* aImage)
+  {
+    nsCOMPtr<nsIRunnable> worker = new FrameNeededWorker(aImage);
+    NS_DispatchToMainThread(worker);
+  }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
+    nsresult rv = NS_OK;
+
+    // If we got a synchronous decode in the mean time, we don't need to do
+    // anything.
+    if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
+      rv = mImage->mDecoder->AllocateFrame();
+    }
+
+    if (NS_SUCCEEDED(rv) && mImage->mDecoder) {
+      // By definition, we're not done decoding, so enqueue us for more decoding.
+      DecodePool::Singleton()->RequestDecode(mImage);
+    }
+
+    return NS_OK;
+  }
+
+private:
+  explicit FrameNeededWorker(RasterImage* aImage)
+    : mImage(aImage)
+  { }
+
+  nsRefPtr<RasterImage> mImage;
+};
+
+class DecodeWorker : public nsRunnable
+{
+public:
+  DecodeWorker(RasterImage* aImage)
+    : mImage(aImage)
+  { }
+
+  NS_IMETHOD Run() MOZ_OVERRIDE
+  {
+    ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
+
+    // If we were interrupted, we shouldn't do any work.
+    if (mImage->mDecodeStatus == DecodeStatus::STOPPED) {
+      NotifyProgressWorker::Dispatch(mImage);
+      return NS_OK;
+    }
+
+    // If someone came along and synchronously decoded us, there's nothing for us to do.
+    if (!mImage->mDecoder || mImage->IsDecodeFinished()) {
+      NotifyProgressWorker::Dispatch(mImage);
+      return NS_OK;
+    }
+
+    // If we're a decode job that's been enqueued since a previous decode that
+    // still needs a new frame, we can't do anything. Wait until the
+    // FrameNeededWorker enqueues another frame.
+    if (mImage->mDecoder->NeedsNewFrame()) {
+      return NS_OK;
+    }
+
+    mImage->mDecodeStatus = DecodeStatus::ACTIVE;
+
+    size_t oldByteCount = mImage->mDecoder->BytesDecoded();
+
+    // Multithreaded decoding can be disabled. If we've done so, we don't want
+    // to monopolize the main thread, and will allow a timeout.
+    DecodeUntil type = NS_IsMainThread() ? DecodeUntil::TIME
+                                         : DecodeUntil::DONE_BYTES;
+
+    size_t maxBytes = mImage->mSourceData.Length() -
+                      mImage->mDecoder->BytesDecoded();
+    DecodePool::Singleton()->DecodeSomeOfImage(mImage, DecodeStrategy::ASYNC,
+                                               type, maxBytes);
+
+    size_t bytesDecoded = mImage->mDecoder->BytesDecoded() - oldByteCount;
+
+    mImage->mDecodeStatus = DecodeStatus::WORK_DONE;
+
+    if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
+      // The decoder needs a new frame. Enqueue an event to get it; that event
+      // will enqueue another decode request when it's done.
+      FrameNeededWorker::Dispatch(mImage);
+    } else if (mImage->mDecoder &&
+               !mImage->mError &&
+               !mImage->mPendingError &&
+               !mImage->IsDecodeFinished() &&
+               bytesDecoded < maxBytes &&
+               bytesDecoded > 0) {
+      // We aren't finished decoding, and we have more data, so add this request
+      // to the back of the list.
+      DecodePool::Singleton()->RequestDecode(mImage);
+    } else {
+      // Nothing more for us to do - let everyone know what happened.
+      NotifyProgressWorker::Dispatch(mImage);
+    }
+
+    return NS_OK;
+  }
+
+protected:
+  virtual ~DecodeWorker()
+  {
+    if (gfxPrefs::ImageMTDecodingEnabled()) {
+      // Dispatch mImage to main thread to prevent mImage from being destructed by decode thread.
+      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
+      NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
+      if (mainThread) {
+        // Handle ambiguous nsISupports inheritance
+        RasterImage* rawImg = nullptr;
+        mImage.swap(rawImg);
+        DebugOnly<nsresult> rv = NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
+        MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
+      }
+    }
+  }
+
+private:
+  nsRefPtr<RasterImage> mImage;
+};
+
+#ifdef MOZ_NUWA_PROCESS
+
+class RIDThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSITHREADPOOLLISTENER
+  
+  RIDThreadPoolListener() { }
+
+private:
+  ~RIDThreadPoolListener() { }
+};
+
+NS_IMPL_ISUPPORTS(RIDThreadPoolListener, nsIThreadPoolListener)
+
+NS_IMETHODIMP
+RIDThreadPoolListener::OnThreadCreated()
+{
+    if (IsNuwaProcess()) {
+        NuwaMarkCurrentThread(static_cast<void(*)(void*)>(nullptr), nullptr);
+    }
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+RIDThreadPoolListener::OnThreadShuttingDown()
+{
+    return NS_OK;
+}
+
+#endif // MOZ_NUWA_PROCESS
+
+
+///////////////////////////////////////////////////////////////////////////////
+// DecodePool implementation.
+///////////////////////////////////////////////////////////////////////////////
+
+/* static */ StaticRefPtr<DecodePool> DecodePool::sSingleton;
+
+NS_IMPL_ISUPPORTS(DecodePool, nsIObserver)
+
+/* static */ DecodePool*
+DecodePool::Singleton()
+{
+  if (!sSingleton) {
+    MOZ_ASSERT(NS_IsMainThread());
+    sSingleton = new DecodePool();
+    ClearOnShutdown(&sSingleton);
+  }
+
+  return sSingleton;
+}
+
+already_AddRefed<nsIEventTarget>
+DecodePool::GetEventTarget()
+{
+  nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
+  return target.forget();
+}
+
+DecodePool::DecodePool()
+  : mThreadPoolMutex("Thread Pool")
+{
+  if (gfxPrefs::ImageMTDecodingEnabled()) {
+    mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
+    if (mThreadPool) {
+      mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder"));
+      int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
+      uint32_t limit;
+      if (prefLimit <= 0) {
+        limit = max(PR_GetNumberOfProcessors(), 2) - 1;
+      } else {
+        limit = static_cast<uint32_t>(prefLimit);
+      }
+
+      mThreadPool->SetThreadLimit(limit);
+      mThreadPool->SetIdleThreadLimit(limit);
+
+#ifdef MOZ_NUWA_PROCESS
+      if (IsNuwaProcess()) {
+        mThreadPool->SetListener(new RIDThreadPoolListener());
+      }
+#endif
+
+      nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
+      if (obsSvc) {
+        obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
+      }
+    }
+  }
+}
+
+DecodePool::~DecodePool()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
+}
+
+NS_IMETHODIMP
+DecodePool::Observe(nsISupports*, const char* aTopic, const char16_t*)
+{
+  MOZ_ASSERT(strcmp(aTopic, "xpcom-shutdown-threads") == 0, "Unexpected topic");
+
+  nsCOMPtr<nsIThreadPool> threadPool;
+
+  {
+    MutexAutoLock threadPoolLock(mThreadPoolMutex);
+    threadPool = mThreadPool;
+    mThreadPool = nullptr;
+  }
+
+  if (threadPool) {
+    threadPool->Shutdown();
+  }
+
+  return NS_OK;
+}
+
+void
+DecodePool::RequestDecode(RasterImage* aImage)
+{
+  MOZ_ASSERT(aImage->mDecoder);
+  aImage->mDecodingMonitor.AssertCurrentThreadIn();
+
+  // If we're currently waiting on a new frame for this image, we can't do any
+  // decoding.
+  if (!aImage->mDecoder->NeedsNewFrame()) {
+    if (aImage->mDecodeStatus == DecodeStatus::PENDING ||
+        aImage->mDecodeStatus == DecodeStatus::ACTIVE) {
+      // The image is already in our list of images to decode, or currently being
+      // decoded, so we don't have to do anything else.
+      return;
+    }
+
+    aImage->mDecodeStatus = DecodeStatus::PENDING;
+    nsCOMPtr<nsIRunnable> worker = new DecodeWorker(aImage);
+
+    MutexAutoLock threadPoolLock(mThreadPoolMutex);
+    if (!gfxPrefs::ImageMTDecodingEnabled() || !mThreadPool) {
+      NS_DispatchToMainThread(worker);
+    } else {
+      mThreadPool->Dispatch(worker, nsIEventTarget::DISPATCH_NORMAL);
+    }
+  }
+}
+
+void
+DecodePool::DecodeABitOf(RasterImage* aImage, DecodeStrategy aStrategy)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  aImage->mDecodingMonitor.AssertCurrentThreadIn();
+
+  // If the image is waiting for decode work to be notified, go ahead and do that.
+  if (aImage->mDecodeStatus == DecodeStatus::WORK_DONE) {
+    aImage->FinishedSomeDecoding();
+  }
+
+  DecodeSomeOfImage(aImage, aStrategy);
+
+  aImage->FinishedSomeDecoding();
+
+  // If the decoder needs a new frame, enqueue an event to get it; that event
+  // will enqueue another decode request when it's done.
+  if (aImage->mDecoder && aImage->mDecoder->NeedsNewFrame()) {
+    FrameNeededWorker::Dispatch(aImage);
+  } else {
+    // If we aren't yet finished decoding and we have more data in hand, add
+    // this request to the back of the priority list.
+    if (aImage->mDecoder &&
+        !aImage->mError &&
+        !aImage->IsDecodeFinished() &&
+        aImage->mSourceData.Length() > aImage->mDecoder->BytesDecoded()) {
+      RequestDecode(aImage);
+    }
+  }
+}
+
+/* static */ void
+DecodePool::StopDecoding(RasterImage* aImage)
+{
+  aImage->mDecodingMonitor.AssertCurrentThreadIn();
+
+  // If we haven't got a decode request, we're not currently decoding. (Having
+  // a decode request doesn't imply we *are* decoding, though.)
+  aImage->mDecodeStatus = DecodeStatus::STOPPED;
+}
+
+nsresult
+DecodePool::DecodeUntilSizeAvailable(RasterImage* aImage)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  ReentrantMonitorAutoEnter lock(aImage->mDecodingMonitor);
+
+  // If the image is waiting for decode work to be notified, go ahead and do that.
+  if (aImage->mDecodeStatus == DecodeStatus::WORK_DONE) {
+    nsresult rv = aImage->FinishedSomeDecoding();
+    if (NS_FAILED(rv)) {
+      aImage->DoError();
+      return rv;
+    }
+  }
+
+  // We use DecodeStrategy::ASYNC here because we just want to get the size
+  // information here and defer the rest of the work.
+  nsresult rv =
+    DecodeSomeOfImage(aImage, DecodeStrategy::ASYNC, DecodeUntil::SIZE);
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
+  // If the decoder needs a new frame, enqueue an event to get it; that event
+  // will enqueue another decode request when it's done.
+  if (aImage->mDecoder && aImage->mDecoder->NeedsNewFrame()) {
+    FrameNeededWorker::Dispatch(aImage);
+  } else {
+    rv = aImage->FinishedSomeDecoding();
+  }
+
+  return rv;
+}
+
+nsresult
+DecodePool::DecodeSomeOfImage(RasterImage* aImage,
+                              DecodeStrategy aStrategy,
+                              DecodeUntil aDecodeUntil /* = DecodeUntil::TIME */,
+                              uint32_t bytesToDecode /* = 0 */)
+{
+  MOZ_ASSERT(aImage->mInitialized, "Worker active for uninitialized container");
+  aImage->mDecodingMonitor.AssertCurrentThreadIn();
+
+  // If an error is flagged, it probably happened while we were waiting
+  // in the event queue.
+  if (aImage->mError) {
+    return NS_OK;
+  }
+
+  // If there is an error worker pending (say because the main thread has enqueued
+  // another decode request for us before processing the error worker) then bail out.
+  if (aImage->mPendingError) {
+    return NS_OK;
+  }
+
+  // If mDecoded or we don't have a decoder, we must have finished already (for
+  // example, a synchronous decode request came while the worker was pending).
+  if (!aImage->mDecoder || aImage->mDecoded) {
+    return NS_OK;
+  }
+
+  if (aImage->mDecoder->NeedsNewFrame()) {
+    if (aStrategy == DecodeStrategy::SYNC) {
+      MOZ_ASSERT(NS_IsMainThread());
+      aImage->mDecoder->AllocateFrame();
+    } else {
+      return NS_OK;
+    }
+  }
+
+  nsRefPtr<Decoder> decoderKungFuDeathGrip = aImage->mDecoder;
+
+  uint32_t maxBytes;
+  if (aImage->mDecoder->IsSizeDecode()) {
+    // Decode all available data if we're a size decode; they're cheap, and we
+    // want them to be more or less synchronous.
+    maxBytes = aImage->mSourceData.Length();
+  } else {
+    // We're only guaranteed to decode this many bytes, so in particular,
+    // gfxPrefs::ImageMemDecodeBytesAtATime should be set high enough for us
+    // to read the size from most images.
+    maxBytes = gfxPrefs::ImageMemDecodeBytesAtATime();
+  }
+
+  if (bytesToDecode == 0) {
+    bytesToDecode = aImage->mSourceData.Length() - aImage->mDecoder->BytesDecoded();
+  }
+
+  TimeStamp deadline = TimeStamp::Now() +
+                       TimeDuration::FromMilliseconds(gfxPrefs::ImageMemMaxMSBeforeYield());
+
+  // We keep decoding chunks until:
+  //  * we don't have any data left to decode,
+  //  * the decode completes,
+  //  * we're an DecodeUntil::SIZE decode and we get the size, or
+  //  * we run out of time.
+  // We also try to decode at least one "chunk" if we've allocated a new frame,
+  // even if we have no more data to send to the decoder.
+  while ((aImage->mSourceData.Length() > aImage->mDecoder->BytesDecoded() &&
+          bytesToDecode > 0 &&
+          !aImage->IsDecodeFinished() &&
+          !(aDecodeUntil == DecodeUntil::SIZE && aImage->mHasSize) &&
+          !aImage->mDecoder->NeedsNewFrame()) ||
+         aImage->mDecoder->NeedsToFlushData()) {
+    uint32_t chunkSize = min(bytesToDecode, maxBytes);
+    nsresult rv = aImage->DecodeSomeData(chunkSize, aStrategy);
+    if (NS_FAILED(rv)) {
+      aImage->DoError();
+      return rv;
+    }
+
+    bytesToDecode -= chunkSize;
+
+    // Yield if we've been decoding for too long. We check this _after_ decoding
+    // a chunk to ensure that we don't yield without doing any decoding.
+    if (aDecodeUntil == DecodeUntil::TIME && TimeStamp::Now() >= deadline) {
+      break;
+    }
+  }
+
+  return NS_OK;
+}
+
+} // namespace image
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/image/src/DecodePool.h
@@ -0,0 +1,151 @@
+/* -*- 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/. */
+
+/**
+ * DecodePool manages the threads used for decoding raster images.
+ */
+
+#ifndef MOZILLA_IMAGELIB_DECODEPOOL_H_
+#define MOZILLA_IMAGELIB_DECODEPOOL_H_
+
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticPtr.h"
+#include <mozilla/TypedEnum.h>
+#include "nsCOMPtr.h"
+#include "nsIEventTarget.h"
+#include "nsIObserver.h"
+
+class nsIThreadPool;
+
+namespace mozilla {
+namespace image {
+
+class Decoder;
+class RasterImage;
+
+MOZ_BEGIN_ENUM_CLASS(DecodeStrategy, uint8_t)
+  // DecodeStrategy::SYNC requests a synchronous decode, which will continue
+  // decoding frames as long as it has more source data. It returns to the
+  // caller only once decoding is complete (or until it needs more source data
+  // before continuing). Because DecodeStrategy::SYNC can involve allocating new
+  // imgFrames, it can only be run on the main thread.
+  SYNC,
+
+  // DecodeStrategy::ASYNC requests an asynchronous decode, which will continue
+  // decoding until it either finishes a frame or runs out of source data.
+  // Because DecodeStrategy::ASYNC does not allocate new imgFrames, it can be
+  // safely run off the main thread. (And hence workers in the decode pool
+  // always use it.)
+  ASYNC
+MOZ_END_ENUM_CLASS(DecodeStrategy)
+
+MOZ_BEGIN_ENUM_CLASS(DecodeStatus, uint8_t)
+  INACTIVE,
+  PENDING,
+  ACTIVE,
+  WORK_DONE,
+  STOPPED
+MOZ_END_ENUM_CLASS(DecodeStatus)
+
+MOZ_BEGIN_ENUM_CLASS(DecodeUntil, uint8_t)
+  TIME,
+  SIZE,
+  DONE_BYTES
+MOZ_END_ENUM_CLASS(DecodeUntil)
+
+MOZ_BEGIN_ENUM_CLASS(ShutdownReason, uint8_t)
+  DONE,
+  NOT_NEEDED,
+  FATAL_ERROR
+MOZ_END_ENUM_CLASS(ShutdownReason)
+
+
+/**
+ * DecodePool is a singleton class we use when decoding large images.
+ *
+ * When we wish to decode an image larger than
+ * image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode()
+ * for the image.  This adds the image to a queue of pending requests and posts
+ * the DecodePool singleton to the event queue, if it's not already pending
+ * there.
+ *
+ * When the DecodePool is run from the event queue, it decodes the image (and
+ * all others it's managing) in chunks, periodically yielding control back to
+ * the event loop.
+ */
+class DecodePool : public nsIObserver
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+
+  static DecodePool* Singleton();
+
+  /**
+   * Ask the DecodePool to asynchronously decode this image.
+   */
+  void RequestDecode(RasterImage* aImage);
+
+  /**
+   * Decode aImage for a short amount of time, and post the remainder to the
+   * queue.
+   */
+  void DecodeABitOf(RasterImage* aImage, DecodeStrategy aStrategy);
+
+  /**
+   * Ask the DecodePool to stop decoding this image.  Internally, we also
+   * call this function when we finish decoding an image.
+   *
+   * Since the DecodePool keeps raw pointers to RasterImages, make sure you
+   * call this before a RasterImage is destroyed!
+   */
+  static void StopDecoding(RasterImage* aImage);
+
+  /**
+   * Synchronously decode the beginning of the image until we run out of
+   * bytes or we get the image's size.  Note that this done on a best-effort
+   * basis; if the size is burried too deep in the image, we'll give up.
+   *
+   * @return NS_ERROR if an error is encountered, and NS_OK otherwise.  (Note
+   *         that we return NS_OK even when the size was not found.)
+   */
+  nsresult DecodeUntilSizeAvailable(RasterImage* aImage);
+
+  /**
+   * Returns an event target interface to the thread pool; primarily for
+   * OnDataAvailable delivery off main thread.
+   *
+   * @return An nsIEventTarget interface to mThreadPool.
+   */
+  already_AddRefed<nsIEventTarget> GetEventTarget();
+
+  /**
+   * Decode some chunks of the given image.  If aDecodeUntil is SIZE,
+   * decode until we have the image's size, then stop. If bytesToDecode is
+   * non-0, at most bytesToDecode bytes will be decoded. if aDecodeUntil is
+   * DONE_BYTES, decode until all bytesToDecode bytes are decoded.
+   */
+  nsresult DecodeSomeOfImage(RasterImage* aImage,
+                             DecodeStrategy aStrategy,
+                             DecodeUntil aDecodeUntil = DecodeUntil::TIME,
+                             uint32_t bytesToDecode = 0);
+
+private:
+  DecodePool();
+  virtual ~DecodePool();
+
+  static StaticRefPtr<DecodePool> sSingleton;
+
+  // mThreadPoolMutex protects mThreadPool. For all RasterImages R,
+  // R::mDecodingMonitor must be acquired before mThreadPoolMutex
+  // if both are acquired; the other order may cause deadlock.
+  Mutex                     mThreadPoolMutex;
+  nsCOMPtr<nsIThreadPool>   mThreadPool;
+};
+
+} // namespace image
+} // namespace mozilla
+
+#endif // MOZILLA_IMAGELIB_DECODEPOOL_H_
deleted file mode 100644
--- a/image/src/DecodeStrategy.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/* -*- 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/. */
-
-/** @file
- * An enumeration used by RasterImage and Decoder to specify which 'strategy' to
- * use for image decoding - synchronous or asynchronous.
- */
-
-#ifndef mozilla_imagelib_DecodeStrategy_h_
-#define mozilla_imagelib_DecodeStrategy_h_
-
-namespace mozilla {
-namespace image {
-
-enum DecodeStrategy {
-  // DECODE_SYNC requests a synchronous decode, which will continue decoding
-  // frames as long as it has more source data. It returns to the caller only
-  // once decoding is complete (or until it needs more source data before
-  // continuing). Because DECODE_SYNC can involve allocating new imgFrames, it
-  // can only be run on the main thread.
-  DECODE_SYNC,
-
-  // DECODE_ASYNC requests an asynchronous decode, which will continue decoding
-  // until it either finishes a frame or runs out of source data. Because
-  // DECODE_ASYNC does not allocate new imgFrames, it can be safely run off the
-  // main thread. (And hence workers in the decode pool always use it.)
-  DECODE_ASYNC
-};
-
-} // namespace image
-} // namespace mozilla
-
-#endif
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -91,17 +91,17 @@ Decoder::InitSharedDecoder(uint8_t* imag
 }
 
 void
 Decoder::Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy)
 {
   PROFILER_LABEL("ImageDecoder", "Write",
     js::ProfileEntry::Category::GRAPHICS);
 
-  MOZ_ASSERT(NS_IsMainThread() || aStrategy == DECODE_ASYNC);
+  MOZ_ASSERT(NS_IsMainThread() || aStrategy == DecodeStrategy::ASYNC);
 
   // We're strict about decoder errors
   MOZ_ASSERT(!HasDecoderError(),
              "Not allowed to make more decoder calls after error!");
 
   // Begin recording telemetry data.
   TimeStamp start = TimeStamp::Now();
   mChunkCount++;
@@ -124,31 +124,33 @@ Decoder::Write(const char* aBuffer, uint
     return;
   }
 
   // Pass the data along to the implementation
   WriteInternal(aBuffer, aCount, aStrategy);
 
   // If we're a synchronous decoder and we need a new frame to proceed, let's
   // create one and call it again.
-  while (aStrategy == DECODE_SYNC && NeedsNewFrame() && !HasDataError()) {
-    nsresult rv = AllocateFrame();
+  if (aStrategy == DecodeStrategy::SYNC) {
+    while (NeedsNewFrame() && !HasDataError()) {
+      nsresult rv = AllocateFrame();
 
-    if (NS_SUCCEEDED(rv)) {
-      // Tell the decoder to use the data it saved when it asked for a new frame.
-      WriteInternal(nullptr, 0, aStrategy);
+      if (NS_SUCCEEDED(rv)) {
+        // Use the data we saved when we asked for a new frame.
+        WriteInternal(nullptr, 0, aStrategy);
+      }
     }
   }
 
   // Finish telemetry.
   mDecodeTime += (TimeStamp::Now() - start);
 }
 
 void
-Decoder::Finish(RasterImage::eShutdownIntent aShutdownIntent)
+Decoder::Finish(ShutdownReason aReason)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Implementation-specific finalization
   if (!HasError())
     FinishInternal();
 
   // If the implementation left us mid-frame, finish that up.
@@ -175,17 +177,17 @@ Decoder::Finish(RasterImage::eShutdownIn
                          EmptyString(), 0, 0, nsIScriptError::errorFlag,
                          "Image", mImage.InnerWindowID()
                        ))) {
         consoleService->LogMessage(errorObject);
       }
     }
 
     bool usable = !HasDecoderError();
-    if (aShutdownIntent != RasterImage::eShutdownIntent_NotNeeded && !HasDecoderError()) {
+    if (aReason != ShutdownReason::NOT_NEEDED && !HasDecoderError()) {
       // If we only have a data error, we're usable if we have at least one complete frame.
       if (GetCompleteFrameCount() == 0) {
         usable = false;
       }
     }
 
     // If we're usable, do exactly what we should have when the decoder
     // completed.
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -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/. */
 
 #ifndef MOZILLA_IMAGELIB_DECODER_H_
 #define MOZILLA_IMAGELIB_DECODER_H_
 
 #include "RasterImage.h"
 #include "mozilla/RefPtr.h"
-#include "DecodeStrategy.h"
+#include "DecodePool.h"
 #include "ImageMetadata.h"
 #include "Orientation.h"
 #include "mozilla/Telemetry.h"
 
 namespace mozilla {
 
 namespace image {
 
@@ -57,17 +57,17 @@ public:
    */
   void Write(const char* aBuffer, uint32_t aCount, DecodeStrategy aStrategy);
 
   /**
    * Informs the decoder that all the data has been written.
    *
    * Notifications Sent: TODO
    */
-  void Finish(RasterImage::eShutdownIntent aShutdownIntent);
+  void Finish(ShutdownReason aReason);
 
   /**
    * Informs the shared decoder that all the data has been written.
    * Should only be used if InitSharedDecoder was useed
    *
    * Notifications Sent: TODO
    */
   void FinishSharedDecoder();
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -15,19 +15,16 @@
 #include "Decoder.h"
 #include "nsAutoPtr.h"
 #include "prenv.h"
 #include "prsystem.h"
 #include "ImageContainer.h"
 #include "ImageRegion.h"
 #include "Layers.h"
 #include "nsPresContext.h"
-#include "nsIThreadPool.h"
-#include "nsXPCOMCIDInternal.h"
-#include "nsIObserverService.h"
 #include "SurfaceCache.h"
 #include "FrameAnimator.h"
 
 #include "nsPNGDecoder.h"
 #include "nsGIFDecoder2.h"
 #include "nsJPEGDecoder.h"
 #include "nsBMPDecoder.h"
 #include "nsICODecoder.h"
@@ -46,20 +43,16 @@
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/gfx/Scale.h"
 
 #include "GeckoProfiler.h"
 #include "gfx2DGlue.h"
 #include "gfxPrefs.h"
 #include <algorithm>
 
-#ifdef MOZ_NUWA_PROCESS
-#include "ipc/Nuwa.h"
-#endif
-
 namespace mozilla {
 
 using namespace gfx;
 using namespace layers;
 
 namespace image {
 
 using std::ceil;
@@ -291,17 +284,16 @@ private:
   WeakPtr<RasterImage> mImage;
   RawAccessFrameRef    mSrcRef;
   RawAccessFrameRef    mDstRef;
   const nsIntSize      mDstSize;
   uint32_t             mImageFlags;
   ScaleState           mState;
 };
 
-/* static */ StaticRefPtr<RasterImage::DecodePool> RasterImage::DecodePool::sSingleton;
 static nsCOMPtr<nsIThread> sScaleWorkerThread = nullptr;
 
 #ifndef DEBUG
 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties)
 #else
 NS_IMPL_ISUPPORTS(RasterImage, imgIContainer, nsIProperties,
                   imgIContainerDebug)
 #endif
@@ -1540,17 +1532,17 @@ RasterImage::AddSourceData(const char *a
       mFrameBlender.ClearFrames();
     }
   }
 
   // If we're not storing source data and we've previously gotten the size,
   // write the data directly to the decoder. (If we haven't gotten the size,
   // we'll queue up the data and write it out when we do.)
   if (!StoringSourceData() && mHasSize) {
-    rv = WriteToDecoder(aBuffer, aCount, DECODE_SYNC);
+    rv = WriteToDecoder(aBuffer, aCount, DecodeStrategy::SYNC);
     CONTAINER_ENSURE_SUCCESS(rv);
 
     rv = FinishedSomeDecoding();
     CONTAINER_ENSURE_SUCCESS(rv);
   }
 
   // Otherwise, we're storing data in the source buffer
   else {
@@ -1660,17 +1652,17 @@ RasterImage::OnImageDataComplete(nsIRequ
 
   // Give precedence to Necko failure codes.
   if (NS_FAILED(aStatus))
     finalStatus = aStatus;
 
   // We just recorded OnStopRequest; we need to inform our listeners.
   {
     ReentrantMonitorAutoEnter lock(mDecodingMonitor);
-    FinishedSomeDecoding(eShutdownIntent_Done,
+    FinishedSomeDecoding(ShutdownReason::DONE,
                          LoadCompleteProgress(aLastPart, mError, finalStatus));
   }
 
   return finalStatus;
 }
 
 nsresult
 RasterImage::OnImageDataAvailable(nsIRequest*,
@@ -1740,16 +1732,22 @@ RasterImage::OnNewSourceData()
 
   // We always need the size first.
   rv = InitDecoder(/* aDoSizeDecode = */ true);
   CONTAINER_ENSURE_SUCCESS(rv);
 
   return NS_OK;
 }
 
+/* static */ already_AddRefed<nsIEventTarget>
+RasterImage::GetEventTarget()
+{
+  return DecodePool::Singleton()->GetEventTarget();
+}
+
 nsresult
 RasterImage::SetSourceSizeHint(uint32_t sizeHint)
 {
   if (sizeHint && StoringSourceData())
     return mSourceData.SetCapacity(sizeHint) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
   return NS_OK;
 }
 
@@ -1984,43 +1982,40 @@ RasterImage::InitDecoder(bool aDoSizeDec
 
   return NS_OK;
 }
 
 // Flushes, closes, and nulls-out a decoder. Cleans up any related decoding
 // state. It is an error to call this function when there is no initialized
 // decoder.
 //
-// aIntent specifies the intent of the shutdown. If aIntent is
-// eShutdownIntent_Done, an error is flagged if we didn't get what we should
-// have out of the decode. If aIntent is eShutdownIntent_NotNeeded, we don't
-// check this. If aIntent is eShutdownIntent_Error, we shut down in error mode.
+// aReason specifies why the shutdown is happening. If aReason is
+// ShutdownReason::DONE, an error is flagged if we didn't get what we should
+// have out of the decode. If aReason is ShutdownReason::NOT_NEEDED, we don't
+// check this. If aReason is ShutdownReason::FATAL_ERROR, we shut down in error
+// mode.
 nsresult
-RasterImage::ShutdownDecoder(eShutdownIntent aIntent)
+RasterImage::ShutdownDecoder(ShutdownReason aReason)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mDecodingMonitor.AssertCurrentThreadIn();
 
-  // Ensure that our intent is valid
-  NS_ABORT_IF_FALSE((aIntent >= 0) && (aIntent < eShutdownIntent_AllCount),
-                    "Invalid shutdown intent");
-
   // Ensure that the decoder is initialized
   NS_ABORT_IF_FALSE(mDecoder, "Calling ShutdownDecoder() with no active decoder!");
 
   // Figure out what kind of decode we were doing before we get rid of our decoder
   bool wasSizeDecode = mDecoder->IsSizeDecode();
 
   // Finalize the decoder
   // null out mDecoder, _then_ check for errors on the close (otherwise the
   // error routine might re-invoke ShutdownDecoder)
   nsRefPtr<Decoder> decoder = mDecoder;
   mDecoder = nullptr;
 
-  decoder->Finish(aIntent);
+  decoder->Finish(aReason);
 
   // Unlock the last frame (if we have any). Our invariant is that, while we
   // have a decoder open, the last frame is always locked.
   if (GetNumFrames() > 0) {
     nsRefPtr<imgFrame> curframe = mFrameBlender.RawGetFrame(GetNumFrames() - 1);
     curframe->UnlockImageData();
   }
 
@@ -2031,22 +2026,18 @@ RasterImage::ShutdownDecoder(eShutdownIn
   nsresult decoderStatus = decoder->GetDecoderError();
   if (NS_FAILED(decoderStatus)) {
     DoError();
     return decoderStatus;
   }
 
   // We just shut down the decoder. If we didn't get what we want, but expected
   // to, flag an error
-  bool failed = false;
-  if (wasSizeDecode && !mHasSize)
-    failed = true;
-  if (!wasSizeDecode && !mDecoded)
-    failed = true;
-  if ((aIntent == eShutdownIntent_Done) && failed) {
+  bool succeeded = wasSizeDecode ? mHasSize : mDecoded;
+  if ((aReason == ShutdownReason::DONE) && !succeeded) {
     DoError();
     return NS_ERROR_FAILURE;
   }
 
   // If we finished a full decode, and we're not meant to be storing source
   // data, stop storing it.
   if (!wasSizeDecode && !StoringSourceData()) {
     mSourceData.Clear();
@@ -2218,17 +2209,17 @@ RasterImage::RequestDecodeCore(RequestDe
   // decoded some bytes, we have nothing to do.
   if (mDecoder && !mDecoder->IsSizeDecode() && mDecoder->BytesDecoded() > 0) {
     return NS_OK;
   }
 
   // If we have a size decode open, interrupt it and shut it down; or if
   // the decoder has different flags than what we need
   if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
-    nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
+    nsresult rv = FinishedSomeDecoding(ShutdownReason::NOT_NEEDED);
     CONTAINER_ENSURE_SUCCESS(rv);
   }
 
   // If we don't have a decoder, create one
   if (!mDecoder) {
     rv = InitDecoder(/* aDoSizeDecode = */ false);
     CONTAINER_ENSURE_SUCCESS(rv);
 
@@ -2245,17 +2236,17 @@ RasterImage::RequestDecodeCore(RequestDe
 
   // If we can do decoding now, do so.  Small images will decode completely,
   // large images will decode a bit and post themselves to the event loop
   // to finish decoding.
   if (!mDecoded && mHasSourceData && aDecodeType == SYNCHRONOUS_NOTIFY_AND_SOME_DECODE) {
     PROFILER_LABEL_PRINTF("RasterImage", "DecodeABitOf",
       js::ProfileEntry::Category::GRAPHICS, "%s", GetURIString().get());
 
-    DecodePool::Singleton()->DecodeABitOf(this, DECODE_SYNC);
+    DecodePool::Singleton()->DecodeABitOf(this, DecodeStrategy::SYNC);
     return NS_OK;
   }
 
   if (!mDecoded) {
     // If we get this far, dispatch the worker. We do this instead of starting
     // any immediate decoding to guarantee that all our decode notifications are
     // dispatched asynchronously, and to ensure we stay responsive.
     DecodePool::Singleton()->RequestDecode(this);
@@ -2304,17 +2295,17 @@ RasterImage::SyncDecode()
   // not storing the source data.
   if (mDecoder && mDecoder->BytesDecoded() > mSourceData.Length()) {
     return NS_OK;
   }
 
   // If we have a decoder open with different flags than what we need, shut it
   // down
   if (mDecoder && mDecoder->GetDecodeFlags() != mFrameDecodeFlags) {
-    nsresult rv = FinishedSomeDecoding(eShutdownIntent_NotNeeded);
+    nsresult rv = FinishedSomeDecoding(ShutdownReason::NOT_NEEDED);
     CONTAINER_ENSURE_SUCCESS(rv);
 
     if (mDecoded) {
       // If we've finished decoding we need to discard so we can re-decode
       // with the new flags. If we can't discard then there isn't
       // anything we can do.
       if (!CanForciblyDiscardAndRedecode())
         return NS_ERROR_NOT_AVAILABLE;
@@ -2332,17 +2323,17 @@ RasterImage::SyncDecode()
     rv = InitDecoder(/* aDoSizeDecode = */ false);
     CONTAINER_ENSURE_SUCCESS(rv);
   }
 
   MOZ_ASSERT(mDecoder);
 
   // Write everything we have
   rv = DecodeSomeData(mSourceData.Length() - mDecoder->BytesDecoded(),
-                      DECODE_SYNC);
+                      DecodeStrategy::SYNC);
   CONTAINER_ENSURE_SUCCESS(rv);
 
   rv = FinishedSomeDecoding();
   CONTAINER_ENSURE_SUCCESS(rv);
   
   // If our decoder's still open, there's still work to be done.
   if (mDecoder) {
     DecodePool::Singleton()->RequestDecode(this);
@@ -2647,17 +2638,17 @@ RasterImage::UnlockImage()
   // decoded data around), try to cancel the decode and throw away whatever
   // we've decoded.
   if (mHasBeenDecoded && mDecoder &&
       mLockCount == 0 && CanForciblyDiscard()) {
     PR_LOG(GetCompressedImageAccountingLog(), PR_LOG_DEBUG,
            ("RasterImage[0x%p] canceling decode because image "
             "is now unlocked.", this));
     ReentrantMonitorAutoEnter lock(mDecodingMonitor);
-    FinishedSomeDecoding(eShutdownIntent_NotNeeded);
+    FinishedSomeDecoding(ShutdownReason::NOT_NEEDED);
     ForceDiscard();
     return NS_OK;
   }
 
   // Otherwise, we might still be a candidate for discarding in the future.  If
   // we are, add ourselves to the discard tracker.
   if (CanDiscard()) {
     nsresult rv = DiscardTracker::Reset(&mDiscardTrackerNode);
@@ -2768,17 +2759,17 @@ RasterImage::DoError()
     return;
   }
 
   // Calling FinishedSomeDecoding requires us to be in the decoding monitor.
   ReentrantMonitorAutoEnter lock(mDecodingMonitor);
 
   // If we're mid-decode, shut down the decoder.
   if (mDecoder) {
-    FinishedSomeDecoding(eShutdownIntent_Error);
+    FinishedSomeDecoding(ShutdownReason::FATAL_ERROR);
   }
 
   // Put the container in an error state.
   mError = true;
 
   // Log our error
   LOG_CONTAINER_ERROR;
 }
@@ -2854,25 +2845,25 @@ RasterImage::GetFramesNotified(uint32_t 
   *aFramesNotified = mFramesNotified;
 
   return NS_OK;
 }
 #endif
 
 nsresult
 RasterImage::RequestDecodeIfNeeded(nsresult aStatus,
-                                   eShutdownIntent aIntent,
+                                   ShutdownReason aReason,
                                    bool aDone,
                                    bool aWasSize)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // If we were a size decode and a full decode was requested, now's the time.
   if (NS_SUCCEEDED(aStatus) &&
-      aIntent == eShutdownIntent_Done &&
+      aReason == ShutdownReason::DONE &&
       aDone &&
       aWasSize &&
       mWantFullDecode) {
     mWantFullDecode = false;
 
     // If we're not meant to be storing source data and we just got the size,
     // we need to synchronously flush all the data we got to a full decoder.
     // When that decoder is shut down, we'll also clear our source data.
@@ -2880,17 +2871,17 @@ RasterImage::RequestDecodeIfNeeded(nsres
                                : SyncDecode();
   }
 
   // We don't need a full decode right now, so just return the existing status.
   return aStatus;
 }
 
 nsresult
-RasterImage::FinishedSomeDecoding(eShutdownIntent aIntent /* = eShutdownIntent_Done */,
+RasterImage::FinishedSomeDecoding(ShutdownReason aReason /* = ShutdownReason::DONE */,
                                   Progress aProgress /* = NoProgress */)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mDecodingMonitor.AssertCurrentThreadIn();
 
   // Ensure that, if the decoder is the last reference to the image, we don't
   // destroy it by destroying the decoder.
@@ -2912,17 +2903,17 @@ RasterImage::FinishedSomeDecoding(eShutd
     }
 
     if (!image->mHasSize && image->mDecoder->HasSize()) {
       image->mDecoder->SetSizeOnImage();
     }
 
     // If the decode finished, or we're specifically being told to shut down,
     // tell the image and shut down the decoder.
-    if (image->IsDecodeFinished() || aIntent != eShutdownIntent_Done) {
+    if (image->IsDecodeFinished() || aReason != ShutdownReason::DONE) {
       done = true;
 
       // Hold on to a reference to the decoder until we're done with it
       nsRefPtr<Decoder> decoder = image->mDecoder;
 
       wasSize = decoder->IsSizeDecode();
 
       // Do some telemetry if this isn't a size decode.
@@ -2937,17 +2928,17 @@ RasterImage::FinishedSomeDecoding(eShutd
           int32_t KBps = int32_t(decoder->BytesDecoded() /
                                  (1024 * decoder->DecodeTime().ToSeconds()));
           Telemetry::Accumulate(id, KBps);
         }
       }
 
       // We need to shut down the decoder first, in order to ensure all
       // decoding routines have been finished.
-      rv = image->ShutdownDecoder(aIntent);
+      rv = image->ShutdownDecoder(aReason);
       if (NS_FAILED(rv)) {
         image->DoError();
       }
 
       // If there were any final changes, grab them.
       invalidRect.Union(decoder->TakeInvalidRect());
       progress |= decoder->TakeProgress();
     }
@@ -2989,467 +2980,26 @@ RasterImage::FinishedSomeDecoding(eShutd
       progress = image->mProgressTracker->Difference(mNotifyProgress);
       mNotifyProgress = NoProgress;
 
       invalidRect = mNotifyInvalidRect;
       mNotifyInvalidRect = nsIntRect();
     }
   }
 
-  return RequestDecodeIfNeeded(rv, aIntent, done, wasSize);
+  return RequestDecodeIfNeeded(rv, aReason, done, wasSize);
 }
 
 already_AddRefed<imgIContainer>
 RasterImage::Unwrap()
 {
   nsCOMPtr<imgIContainer> self(this);
   return self.forget();
 }
 
-NS_IMPL_ISUPPORTS(RasterImage::DecodePool,
-                  nsIObserver)
-
-/* static */ RasterImage::DecodePool*
-RasterImage::DecodePool::Singleton()
-{
-  if (!sSingleton) {
-    MOZ_ASSERT(NS_IsMainThread());
-    sSingleton = new DecodePool();
-    ClearOnShutdown(&sSingleton);
-  }
-
-  return sSingleton;
-}
-
-already_AddRefed<nsIEventTarget>
-RasterImage::DecodePool::GetEventTarget()
-{
-  nsCOMPtr<nsIEventTarget> target = do_QueryInterface(mThreadPool);
-  return target.forget();
-}
-
-#ifdef MOZ_NUWA_PROCESS
-
-class RIDThreadPoolListener MOZ_FINAL : public nsIThreadPoolListener
-{
-public:
-    NS_DECL_THREADSAFE_ISUPPORTS
-    NS_DECL_NSITHREADPOOLLISTENER
-
-    RIDThreadPoolListener() {}
-    ~RIDThreadPoolListener() {}
-};
-
-NS_IMPL_ISUPPORTS(RIDThreadPoolListener, nsIThreadPoolListener)
-
-NS_IMETHODIMP
-RIDThreadPoolListener::OnThreadCreated()
-{
-    if (IsNuwaProcess()) {
-        NuwaMarkCurrentThread((void (*)(void *))nullptr, nullptr);
-    }
-    return NS_OK;
-}
-
-NS_IMETHODIMP
-RIDThreadPoolListener::OnThreadShuttingDown()
-{
-    return NS_OK;
-}
-
-#endif // MOZ_NUWA_PROCESS
-
-RasterImage::DecodePool::DecodePool()
- : mThreadPoolMutex("Thread Pool")
-{
-  if (gfxPrefs::ImageMTDecodingEnabled()) {
-    mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID);
-    if (mThreadPool) {
-      mThreadPool->SetName(NS_LITERAL_CSTRING("ImageDecoder"));
-      int32_t prefLimit = gfxPrefs::ImageMTDecodingLimit();
-      uint32_t limit;
-      if (prefLimit <= 0) {
-        limit = std::max(PR_GetNumberOfProcessors(), 2) - 1;
-      } else {
-        limit = static_cast<uint32_t>(prefLimit);
-      }
-
-      mThreadPool->SetThreadLimit(limit);
-      mThreadPool->SetIdleThreadLimit(limit);
-
-#ifdef MOZ_NUWA_PROCESS
-      if (IsNuwaProcess()) {
-        mThreadPool->SetListener(new RIDThreadPoolListener());
-      }
-#endif
-
-      nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
-      if (obsSvc) {
-        obsSvc->AddObserver(this, "xpcom-shutdown-threads", false);
-      }
-    }
-  }
-}
-
-RasterImage::DecodePool::~DecodePool()
-{
-  MOZ_ASSERT(NS_IsMainThread(), "Must shut down DecodePool on main thread!");
-}
-
-NS_IMETHODIMP
-RasterImage::DecodePool::Observe(nsISupports *subject, const char *topic,
-                                 const char16_t *data)
-{
-  NS_ASSERTION(strcmp(topic, "xpcom-shutdown-threads") == 0, "oops");
-
-  nsCOMPtr<nsIThreadPool> threadPool;
-
-  {
-    MutexAutoLock threadPoolLock(mThreadPoolMutex);
-    threadPool = mThreadPool;
-    mThreadPool = nullptr;
-  }
-
-  if (threadPool) {
-    threadPool->Shutdown();
-  }
-
-  return NS_OK;
-}
-
-void
-RasterImage::DecodePool::RequestDecode(RasterImage* aImg)
-{
-  MOZ_ASSERT(aImg->mDecoder);
-  aImg->mDecodingMonitor.AssertCurrentThreadIn();
-
-  // If we're currently waiting on a new frame for this image, we can't do any
-  // decoding.
-  if (!aImg->mDecoder->NeedsNewFrame()) {
-    if (aImg->mDecodeStatus == DecodeStatus::PENDING ||
-        aImg->mDecodeStatus == DecodeStatus::ACTIVE) {
-      // The image is already in our list of images to decode, or currently being
-      // decoded, so we don't have to do anything else.
-      return;
-    }
-
-    aImg->mDecodeStatus = DecodeStatus::PENDING;
-    nsRefPtr<DecodeJob> job = new DecodeJob(aImg);
-
-    MutexAutoLock threadPoolLock(mThreadPoolMutex);
-    if (!gfxPrefs::ImageMTDecodingEnabled() || !mThreadPool) {
-      NS_DispatchToMainThread(job);
-    } else {
-      mThreadPool->Dispatch(job, nsIEventTarget::DISPATCH_NORMAL);
-    }
-  }
-}
-
-void
-RasterImage::DecodePool::DecodeABitOf(RasterImage* aImg, DecodeStrategy aStrategy)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  aImg->mDecodingMonitor.AssertCurrentThreadIn();
-
-  // If the image is waiting for decode work to be notified, go ahead and do that.
-  if (aImg->mDecodeStatus == DecodeStatus::WORK_DONE) {
-    aImg->FinishedSomeDecoding();
-  }
-
-  DecodeSomeOfImage(aImg, aStrategy);
-
-  aImg->FinishedSomeDecoding();
-
-  // If the decoder needs a new frame, enqueue an event to get it; that event
-  // will enqueue another decode request when it's done.
-  if (aImg->mDecoder && aImg->mDecoder->NeedsNewFrame()) {
-    FrameNeededWorker::GetNewFrame(aImg);
-  } else {
-    // If we aren't yet finished decoding and we have more data in hand, add
-    // this request to the back of the priority list.
-    if (aImg->mDecoder &&
-        !aImg->mError &&
-        !aImg->IsDecodeFinished() &&
-        aImg->mSourceData.Length() > aImg->mDecoder->BytesDecoded()) {
-      RequestDecode(aImg);
-    }
-  }
-}
-
-/* static */ void
-RasterImage::DecodePool::StopDecoding(RasterImage* aImg)
-{
-  aImg->mDecodingMonitor.AssertCurrentThreadIn();
-
-  // If we haven't got a decode request, we're not currently decoding. (Having
-  // a decode request doesn't imply we *are* decoding, though.)
-  aImg->mDecodeStatus = DecodeStatus::STOPPED;
-}
-
-NS_IMETHODIMP
-RasterImage::DecodePool::DecodeJob::Run()
-{
-  ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
-
-  // If we were interrupted, we shouldn't do any work.
-  if (mImage->mDecodeStatus == DecodeStatus::STOPPED) {
-    DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage);
-    return NS_OK;
-  }
-
-  // If someone came along and synchronously decoded us, there's nothing for us to do.
-  if (!mImage->mDecoder || mImage->IsDecodeFinished()) {
-    DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage);
-    return NS_OK;
-  }
-
-  // If we're a decode job that's been enqueued since a previous decode that
-  // still needs a new frame, we can't do anything. Wait until the
-  // FrameNeededWorker enqueues another frame.
-  if (mImage->mDecoder->NeedsNewFrame()) {
-    return NS_OK;
-  }
-
-  mImage->mDecodeStatus = DecodeStatus::ACTIVE;
-
-  size_t oldByteCount = mImage->mDecoder->BytesDecoded();
-
-  DecodeType type = DECODE_TYPE_UNTIL_DONE_BYTES;
-
-  // Multithreaded decoding can be disabled. If we've done so, we don't want to
-  // monopolize the main thread, and will allow a timeout in DecodeSomeOfImage.
-  if (NS_IsMainThread()) {
-    type = DECODE_TYPE_UNTIL_TIME;
-  }
-
-  size_t maxBytes = mImage->mSourceData.Length() -
-                    mImage->mDecoder->BytesDecoded();
-  DecodePool::Singleton()->DecodeSomeOfImage(mImage, DECODE_ASYNC,
-                                             type, maxBytes);
-
-  size_t bytesDecoded = mImage->mDecoder->BytesDecoded() - oldByteCount;
-
-  mImage->mDecodeStatus = DecodeStatus::WORK_DONE;
-
-  // If the decoder needs a new frame, enqueue an event to get it; that event
-  // will enqueue another decode request when it's done.
-  if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
-    FrameNeededWorker::GetNewFrame(mImage);
-  }
-  // If we aren't yet finished decoding and we have more data in hand, add
-  // this request to the back of the list.
-  else if (mImage->mDecoder &&
-           !mImage->mError &&
-           !mImage->mPendingError &&
-           !mImage->IsDecodeFinished() &&
-           bytesDecoded < maxBytes &&
-           bytesDecoded > 0) {
-    DecodePool::Singleton()->RequestDecode(mImage);
-  } else {
-    // Nothing more for us to do - let everyone know what happened.
-    DecodeDoneWorker::NotifyFinishedSomeDecoding(mImage);
-  }
-
-  return NS_OK;
-}
-
-RasterImage::DecodePool::DecodeJob::~DecodeJob()
-{
-  if (gfxPrefs::ImageMTDecodingEnabled()) {
-    // Dispatch mImage to main thread to prevent mImage from being destructed by decode thread.
-    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-    NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
-    if (mainThread) {
-      // Handle ambiguous nsISupports inheritance
-      RasterImage* rawImg = nullptr;
-      mImage.swap(rawImg);
-      DebugOnly<nsresult> rv = NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
-      MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
-    }
-  }
-}
-
-nsresult
-RasterImage::DecodePool::DecodeUntilSizeAvailable(RasterImage* aImg)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ReentrantMonitorAutoEnter lock(aImg->mDecodingMonitor);
-
-  // If the image is waiting for decode work to be notified, go ahead and do that.
-  if (aImg->mDecodeStatus == DecodeStatus::WORK_DONE) {
-    nsresult rv = aImg->FinishedSomeDecoding();
-    if (NS_FAILED(rv)) {
-      aImg->DoError();
-      return rv;
-    }
-  }
-
-  // We use DECODE_ASYNC here because we just want to get the size information
-  // here and defer the rest of the work.
-  nsresult rv = DecodeSomeOfImage(aImg, DECODE_ASYNC, DECODE_TYPE_UNTIL_SIZE);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  // If the decoder needs a new frame, enqueue an event to get it; that event
-  // will enqueue another decode request when it's done.
-  if (aImg->mDecoder && aImg->mDecoder->NeedsNewFrame()) {
-    FrameNeededWorker::GetNewFrame(aImg);
-  } else {
-    rv = aImg->FinishedSomeDecoding();
-  }
-
-  return rv;
-}
-
-nsresult
-RasterImage::DecodePool::DecodeSomeOfImage(RasterImage* aImg,
-                                           DecodeStrategy aStrategy,
-                                           DecodeType aDecodeType /* = DECODE_TYPE_UNTIL_TIME */,
-                                           uint32_t bytesToDecode /* = 0 */)
-{
-  NS_ABORT_IF_FALSE(aImg->mInitialized,
-                    "Worker active for uninitialized container!");
-  aImg->mDecodingMonitor.AssertCurrentThreadIn();
-
-  // If an error is flagged, it probably happened while we were waiting
-  // in the event queue.
-  if (aImg->mError)
-    return NS_OK;
-
-  // If there is an error worker pending (say because the main thread has enqueued
-  // another decode request for us before processing the error worker) then bail out.
-  if (aImg->mPendingError)
-    return NS_OK;
-
-  // If mDecoded or we don't have a decoder, we must have finished already (for
-  // example, a synchronous decode request came while the worker was pending).
-  if (!aImg->mDecoder || aImg->mDecoded)
-    return NS_OK;
-
-  // If we're doing synchronous decodes, and we're waiting on a new frame for
-  // this image, get it now.
-  if (aStrategy == DECODE_SYNC && aImg->mDecoder->NeedsNewFrame()) {
-    MOZ_ASSERT(NS_IsMainThread());
-    aImg->mDecoder->AllocateFrame();
-  }
-
-  // If we're not synchronous, we can't allocate a frame right now.
-  else if (aImg->mDecoder->NeedsNewFrame()) {
-    return NS_OK;
-  }
-
-  nsRefPtr<Decoder> decoderKungFuDeathGrip = aImg->mDecoder;
-
-  uint32_t maxBytes;
-  if (aImg->mDecoder->IsSizeDecode()) {
-    // Decode all available data if we're a size decode; they're cheap, and we
-    // want them to be more or less synchronous.
-    maxBytes = aImg->mSourceData.Length();
-  } else {
-    // We're only guaranteed to decode this many bytes, so in particular,
-    // gfxPrefs::ImageMemDecodeBytesAtATime should be set high enough for us
-    // to read the size from most images.
-    maxBytes = gfxPrefs::ImageMemDecodeBytesAtATime();
-  }
-
-  if (bytesToDecode == 0) {
-    bytesToDecode = aImg->mSourceData.Length() - aImg->mDecoder->BytesDecoded();
-  }
-
-  TimeStamp deadline = TimeStamp::Now() +
-                       TimeDuration::FromMilliseconds(gfxPrefs::ImageMemMaxMSBeforeYield());
-
-  // We keep decoding chunks until:
-  //  * we don't have any data left to decode,
-  //  * the decode completes,
-  //  * we're an UNTIL_SIZE decode and we get the size, or
-  //  * we run out of time.
-  // We also try to decode at least one "chunk" if we've allocated a new frame,
-  // even if we have no more data to send to the decoder.
-  while ((aImg->mSourceData.Length() > aImg->mDecoder->BytesDecoded() &&
-          bytesToDecode > 0 &&
-          !aImg->IsDecodeFinished() &&
-          !(aDecodeType == DECODE_TYPE_UNTIL_SIZE && aImg->mHasSize) &&
-          !aImg->mDecoder->NeedsNewFrame()) ||
-         aImg->mDecoder->NeedsToFlushData()) {
-    uint32_t chunkSize = std::min(bytesToDecode, maxBytes);
-    nsresult rv = aImg->DecodeSomeData(chunkSize, aStrategy);
-    if (NS_FAILED(rv)) {
-      aImg->DoError();
-      return rv;
-    }
-
-    bytesToDecode -= chunkSize;
-
-    // Yield if we've been decoding for too long. We check this _after_ decoding
-    // a chunk to ensure that we don't yield without doing any decoding.
-    if (aDecodeType == DECODE_TYPE_UNTIL_TIME && TimeStamp::Now() >= deadline)
-      break;
-  }
-
-  return NS_OK;
-}
-
-RasterImage::DecodeDoneWorker::DecodeDoneWorker(RasterImage* aImage)
- : mImage(aImage)
-{ }
-
-void
-RasterImage::DecodeDoneWorker::NotifyFinishedSomeDecoding(RasterImage* aImage)
-{
-  aImage->mDecodingMonitor.AssertCurrentThreadIn();
-
-  nsCOMPtr<nsIRunnable> worker = new DecodeDoneWorker(aImage);
-  NS_DispatchToMainThread(worker);
-}
-
-NS_IMETHODIMP
-RasterImage::DecodeDoneWorker::Run()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
-
-  mImage->FinishedSomeDecoding(eShutdownIntent_Done);
-
-  return NS_OK;
-}
-
-RasterImage::FrameNeededWorker::FrameNeededWorker(RasterImage* image)
- : mImage(image)
-{}
-
-
-void
-RasterImage::FrameNeededWorker::GetNewFrame(RasterImage* image)
-{
-  nsCOMPtr<nsIRunnable> worker = new FrameNeededWorker(image);
-  NS_DispatchToMainThread(worker);
-}
-
-NS_IMETHODIMP
-RasterImage::FrameNeededWorker::Run()
-{
-  ReentrantMonitorAutoEnter lock(mImage->mDecodingMonitor);
-  nsresult rv = NS_OK;
-
-  // If we got a synchronous decode in the mean time, we don't need to do
-  // anything.
-  if (mImage->mDecoder && mImage->mDecoder->NeedsNewFrame()) {
-    rv = mImage->mDecoder->AllocateFrame();
-  }
-
-  if (NS_SUCCEEDED(rv) && mImage->mDecoder) {
-    // By definition, we're not done decoding, so enqueue us for more decoding.
-    DecodePool::Singleton()->RequestDecode(mImage);
-  }
-
-  return NS_OK;
-}
-
 nsIntSize
 RasterImage::OptimalImageSizeForDest(const gfxSize& aDest, uint32_t aWhichFrame,
                                      GraphicsFilter aFilter, uint32_t aFlags)
 {
   MOZ_ASSERT(aDest.width >= 0 || ceil(aDest.width) <= INT32_MAX ||
              aDest.height >= 0 || ceil(aDest.height) <= INT32_MAX,
              "Unexpected destination size");
 
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -20,26 +20,24 @@
 #include "Image.h"
 #include "FrameBlender.h"
 #include "nsCOMPtr.h"
 #include "imgIContainer.h"
 #include "nsIProperties.h"
 #include "nsTArray.h"
 #include "imgFrame.h"
 #include "nsThreadUtils.h"
-#include "DecodeStrategy.h"
+#include "DecodePool.h"
 #include "DiscardTracker.h"
 #include "Orientation.h"
 #include "nsIObserver.h"
 #include "mozilla/MemoryReporting.h"
-#include "mozilla/Mutex.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/TimeStamp.h"
 #include "mozilla/TypedEnum.h"
-#include "mozilla/StaticPtr.h"
 #include "mozilla/WeakPtr.h"
 #include "mozilla/UniquePtr.h"
 #ifdef DEBUG
   #include "imgIContainerDebug.h"
 #endif
 
 class nsIInputStream;
 class nsIThreadPool;
@@ -130,25 +128,16 @@ class LayerManager;
 class ImageContainer;
 class Image;
 }
 
 namespace image {
 
 class Decoder;
 class FrameAnimator;
-class ScaleRunner;
-
-MOZ_BEGIN_ENUM_CLASS(DecodeStatus, uint8_t)
-  INACTIVE,
-  PENDING,
-  ACTIVE,
-  WORK_DONE,
-  STOPPED
-MOZ_END_ENUM_CLASS(DecodeStatus)
 
 class RasterImage MOZ_FINAL : public ImageResource
                             , public nsIProperties
                             , public SupportsWeakPtr<RasterImage>
 #ifdef DEBUG
                             , public imgIContainerDebug
 #endif
 {
@@ -255,19 +244,17 @@ public:
                                         uint64_t aSourceOffset,
                                         uint32_t aCount) MOZ_OVERRIDE;
   virtual nsresult OnImageDataComplete(nsIRequest* aRequest,
                                        nsISupports* aContext,
                                        nsresult aStatus,
                                        bool aLastPart) MOZ_OVERRIDE;
   virtual nsresult OnNewSourceData() MOZ_OVERRIDE;
 
-  static already_AddRefed<nsIEventTarget> GetEventTarget() {
-    return DecodePool::Singleton()->GetEventTarget();
-  }
+  static already_AddRefed<nsIEventTarget> GetEventTarget();
 
   /**
    * A hint of the number of bytes of source data that the image contains. If
    * called early on, this can help reduce copying and reallocations by
    * appropriately preallocating the source data buffer.
    *
    * We take this approach rather than having the source data management code do
    * something more complicated (like chunklisting) because HTTP is by far the
@@ -289,188 +276,33 @@ public:
   void SetRequestedSampleSize(int requestedSampleSize) {
     mRequestedSampleSize = requestedSampleSize;
   }
 
   int GetRequestedSampleSize() {
     return mRequestedSampleSize;
   }
 
-
-
  nsCString GetURIString() {
     nsCString spec;
     if (GetURI()) {
       GetURI()->GetSpec(spec);
     }
     return spec;
   }
 
-  // Called from module startup. Sets up RasterImage to be used.
   static void Initialize();
 
-  // Decoder shutdown
-  enum eShutdownIntent {
-    eShutdownIntent_Done        = 0,
-    eShutdownIntent_NotNeeded   = 1,
-    eShutdownIntent_Error       = 2,
-    eShutdownIntent_AllCount    = 3
-  };
-
 private:
-  /*
-   * DecodePool is a singleton class we use when decoding large images.
-   *
-   * When we wish to decode an image larger than
-   * image.mem.max_bytes_for_sync_decode, we call DecodePool::RequestDecode()
-   * for the image.  This adds the image to a queue of pending requests and posts
-   * the DecodePool singleton to the event queue, if it's not already pending
-   * there.
-   *
-   * When the DecodePool is run from the event queue, it decodes the image (and
-   * all others it's managing) in chunks, periodically yielding control back to
-   * the event loop.
-   */
-  class DecodePool : public nsIObserver
-  {
-  public:
-    NS_DECL_THREADSAFE_ISUPPORTS
-    NS_DECL_NSIOBSERVER
-
-    static DecodePool* Singleton();
-
-    /**
-     * Ask the DecodePool to asynchronously decode this image.
-     */
-    void RequestDecode(RasterImage* aImg);
-
-    /**
-     * Decode aImg for a short amount of time, and post the remainder to the
-     * queue.
-     */
-    void DecodeABitOf(RasterImage* aImg, DecodeStrategy aStrategy);
-
-    /**
-     * Ask the DecodePool to stop decoding this image.  Internally, we also
-     * call this function when we finish decoding an image.
-     *
-     * Since the DecodePool keeps raw pointers to RasterImages, make sure you
-     * call this before a RasterImage is destroyed!
-     */
-    static void StopDecoding(RasterImage* aImg);
-
-    /**
-     * Synchronously decode the beginning of the image until we run out of
-     * bytes or we get the image's size.  Note that this done on a best-effort
-     * basis; if the size is burried too deep in the image, we'll give up.
-     *
-     * @return NS_ERROR if an error is encountered, and NS_OK otherwise.  (Note
-     *         that we return NS_OK even when the size was not found.)
-     */
-    nsresult DecodeUntilSizeAvailable(RasterImage* aImg);
-
-    /**
-     * Returns an event target interface to the thread pool; primarily for
-     * OnDataAvailable delivery off main thread.
-     *
-     * @return An nsIEventTarget interface to mThreadPool.
-     */
-    already_AddRefed<nsIEventTarget> GetEventTarget();
-
-  private: /* statics */
-    static StaticRefPtr<DecodePool> sSingleton;
-
-  private: /* methods */
-    DecodePool();
-    virtual ~DecodePool();
-
-    enum DecodeType {
-      DECODE_TYPE_UNTIL_TIME,
-      DECODE_TYPE_UNTIL_SIZE,
-      DECODE_TYPE_UNTIL_DONE_BYTES
-    };
+  friend class DecodePool;
+  friend class DecodeWorker;
+  friend class FrameNeededWorker;
+  friend class NotifyProgressWorker;
 
-    /* Decode some chunks of the given image.  If aDecodeType is UNTIL_SIZE,
-     * decode until we have the image's size, then stop. If bytesToDecode is
-     * non-0, at most bytesToDecode bytes will be decoded. if aDecodeType is
-     * UNTIL_DONE_BYTES, decode until all bytesToDecode bytes are decoded.
-     */
-    nsresult DecodeSomeOfImage(RasterImage* aImg,
-                               DecodeStrategy aStrategy,
-                               DecodeType aDecodeType = DECODE_TYPE_UNTIL_TIME,
-                               uint32_t bytesToDecode = 0);
-
-    /* A decode job dispatched to a thread pool by DecodePool.
-     */
-    class DecodeJob : public nsRunnable
-    {
-    public:
-      DecodeJob(RasterImage* aImage) : mImage(aImage) { }
-
-      NS_IMETHOD Run() MOZ_OVERRIDE;
-
-    protected:
-      virtual ~DecodeJob();
-
-    private:
-      nsRefPtr<RasterImage> mImage;
-    };
-
-  private: /* members */
-
-    // mThreadPoolMutex protects mThreadPool. For all RasterImages R,
-    // R::mDecodingMonitor must be acquired before mThreadPoolMutex
-    // if both are acquired; the other order may cause deadlock.
-    Mutex                     mThreadPoolMutex;
-    nsCOMPtr<nsIThreadPool>   mThreadPool;
-  };
-
-  class DecodeDoneWorker : public nsRunnable
-  {
-  public:
-    /**
-     * Called by the DecodePool with an image when it's done some significant
-     * portion of decoding that needs to be notified about.
-     *
-     * Ensures the decode state accumulated by the decoding process gets
-     * applied to the image.
-     */
-    static void NotifyFinishedSomeDecoding(RasterImage* aImage);
-
-    NS_IMETHOD Run();
-
-  private:
-    DecodeDoneWorker(RasterImage* aImage);
-
-    nsRefPtr<RasterImage> mImage;
-  };
-
-  class FrameNeededWorker : public nsRunnable
-  {
-  public:
-    /**
-     * Called by the DecodeJob with an image when it's been told by the
-     * decoder that it needs a new frame to be allocated on the main thread.
-     *
-     * Dispatches an event to do so, which will further dispatch a
-     * RequestDecode event to continue decoding.
-     */
-    static void GetNewFrame(RasterImage* image);
-
-    NS_IMETHOD Run();
-
-  private: /* methods */
-    explicit FrameNeededWorker(RasterImage* image);
-
-  private: /* members */
-
-    nsRefPtr<RasterImage> mImage;
-  };
-
-  nsresult FinishedSomeDecoding(eShutdownIntent intent = eShutdownIntent_Done,
+  nsresult FinishedSomeDecoding(ShutdownReason aReason = ShutdownReason::DONE,
                                 Progress aProgress = NoProgress);
 
   void DrawWithPreDownscaleIfNeeded(DrawableFrameRef&& aFrameRef,
                                     gfxContext* aContext,
                                     const nsIntSize& aSize,
                                     const ImageRegion& aRegion,
                                     GraphicsFilter aFilter,
                                     uint32_t aFlags);
@@ -619,32 +451,30 @@ private: // data
   bool                       mWantFullDecode:1;
 
   // Set when a decode worker detects an error off-main-thread. Once the error
   // is handled on the main thread, mError is set, but mPendingError is used to
   // stop decode work immediately.
   bool                       mPendingError:1;
 
   // Decoding
-  nsresult RequestDecodeIfNeeded(nsresult aStatus,
-                                 eShutdownIntent aIntent,
-                                 bool aDone,
-                                 bool aWasSize);
+  nsresult RequestDecodeIfNeeded(nsresult aStatus, ShutdownReason aReason,
+                                 bool aDone, bool aWasSize);
   nsresult WantDecodedFrames(uint32_t aFlags, bool aShouldSyncNotify);
   nsresult SyncDecode();
   nsresult InitDecoder(bool aDoSizeDecode);
   nsresult WriteToDecoder(const char *aBuffer, uint32_t aCount, DecodeStrategy aStrategy);
   nsresult DecodeSomeData(size_t aMaxBytes, DecodeStrategy aStrategy);
   bool     IsDecodeFinished();
   TimeStamp mDrawStartTime;
 
   // Initializes ProgressTracker and resets it on RasterImage destruction.
   nsAutoPtr<ProgressTrackerInit> mProgressTrackerInit;
 
-  nsresult ShutdownDecoder(eShutdownIntent aIntent);
+  nsresult ShutdownDecoder(ShutdownReason aReason);
 
 
   //////////////////////////////////////////////////////////////////////////////
   // Scaling.
   //////////////////////////////////////////////////////////////////////////////
 
   // Initiates an HQ scale for the given frame, if possible.
   void RequestScale(imgFrame* aFrame, uint32_t aFlags, const nsIntSize& aSize);
--- a/image/src/moz.build
+++ b/image/src/moz.build
@@ -11,16 +11,17 @@ EXPORTS += [
     'imgRequest.h',
     'imgRequestProxy.h',
     'Orientation.h',
     'SurfaceCache.h'
 ]
 
 UNIFIED_SOURCES += [
     'ClippedImage.cpp',
+    'DecodePool.cpp',
     'Decoder.cpp',
     'DiscardTracker.cpp',
     'DynamicImage.cpp',
     'FrameAnimator.cpp',
     'FrameBlender.cpp',
     'FrameSequence.cpp',
     'FrozenImage.cpp',
     'Image.cpp',
--- a/image/test/mochitest/chrome.ini
+++ b/image/test/mochitest/chrome.ini
@@ -27,16 +27,17 @@ support-files =
   ref-iframe.html
   rillybad.jpg
   transparent.gif
   transparent.png
 
 [test_animSVGImage.html]
 [test_animSVGImage2.html]
 [test_animation.html]
+skip-if = true # bug 1100497
 [test_animation2.html]
 [test_background_image_anim.html]
 [test_bullet_animation.html]
 [test_changeOfSource.html]
 [test_changeOfSource2.html]
 [test_has_transparency.html]
 [test_net_failedtoprocess.html]
 [test_removal_ondecode.html]
--- a/js/public/Debug.h
+++ b/js/public/Debug.h
@@ -284,15 +284,16 @@ onNewPromise(JSContext *cx, HandleObject
 // promise has settled (ie, it has either been fulfilled or rejected). Note that
 // this is *not* equivalent to the promise resolution (ie, the promise's fate
 // getting locked in) because you can resolve a promise with another pending
 // promise, in which case neither promise has settled yet.
 //
 // It is Gecko's responsibility to ensure that this is never called on the same
 // promise more than once (because a promise can only make the transition from
 // unsettled to settled once).
-void onPromiseSettled(JSContext *cx, HandleObject promise);
+JS_PUBLIC_API(void)
+onPromiseSettled(JSContext *cx, HandleObject promise);
 
 } // namespace dbg
 } // namespace JS
 
 
 #endif /* js_Debug_h */
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -1674,40 +1674,28 @@ BindNameToSlotHelper(ExclusiveContext *c
         MOZ_ASSERT(dn->isDefn());
         pn->pn_dflags |= (dn->pn_dflags & PND_CONST);
     } else if (pn->isDefn()) {
         dn = (Definition *) pn;
     } else {
         return true;
     }
 
-    /*
-     * Turn attempts to mutate const-declared bindings into get ops (for
-     * pre-increment and pre-decrement ops, our caller will have to emit
-     * JSOP_POS, JSOP_ONE, and JSOP_ADD as well).
-     *
-     * Turn JSOP_DELNAME into JSOP_FALSE if dn is known, as all declared
-     * bindings visible to the compiler are permanent in JS unless the
-     * declaration originates at top level in eval code.
-     */
+    // Throw an error on attempts to mutate const-declared bindings.
     switch (op) {
       case JSOP_NAME:
       case JSOP_SETCONST:
         break;
       default:
         if (pn->isConst()) {
-            if (bce->sc->needStrictChecks()) {
-                JSAutoByteString name;
-                if (!AtomToPrintableString(cx, pn->pn_atom, &name) ||
-                    !bce->reportStrictModeError(pn, JSMSG_READ_ONLY, name.ptr()))
-                {
-                    return false;
-                }
-            }
-            pn->setOp(op = JSOP_NAME);
+            JSAutoByteString name;
+            if (!AtomToPrintableString(cx, pn->pn_atom, &name))
+                return false;
+            bce->reportError(pn, JSMSG_BAD_CONST_ASSIGN, name.ptr());
+            return false;
         }
     }
 
     if (dn->pn_cookie.isFree()) {
         if (HandleScript caller = bce->evalCaller) {
             MOZ_ASSERT(bce->script->compileAndGo());
 
             /*
@@ -3312,20 +3300,16 @@ EmitDestructuringLHS(ExclusiveContext *c
         MOZ_ASSERT(pn->getOp() == JSOP_SETLOCAL || pn->getOp() == JSOP_INITLEXICAL);
         MOZ_ASSERT(pn->pn_dflags & PND_BOUND);
     } else {
         switch (pn->getKind()) {
           case PNK_NAME:
             if (!BindNameToSlot(cx, bce, pn))
                 return false;
 
-            // Allow 'const [x,y] = o', make 'const x,y; [x,y] = o' a nop.
-            if (pn->isConst() && !pn->isDefn())
-                return Emit1(cx, bce, JSOP_POP) >= 0;
-
             switch (pn->getOp()) {
               case JSOP_SETNAME:
               case JSOP_SETGNAME:
               case JSOP_SETCONST: {
                 // This is like ordinary assignment, but with one difference.
                 //
                 // In `a = b`, we first determine a binding for `a` (using
                 // JSOP_BINDNAME or JSOP_BINDGNAME), then we evaluate `b`, then
@@ -4081,23 +4065,16 @@ EmitAssignment(ExclusiveContext *cx, Byt
         }
         if (Emit1(cx, bce, op) < 0)
             return false;
     }
 
     /* Finally, emit the specialized assignment bytecode. */
     switch (lhs->getKind()) {
       case PNK_NAME:
-        if (lhs->isConst()) {
-            if (!rhs) {
-                bce->reportError(lhs, JSMSG_BAD_FOR_LEFTSIDE);
-                return false;
-            }
-            break;
-        }
         if (lhs->isOp(JSOP_SETARG) || lhs->isOp(JSOP_SETLOCAL) || lhs->isOp(JSOP_SETALIASEDVAR)) {
             if (!EmitVarOp(cx, lhs, lhs->getOp(), bce))
                 return false;
         } else {
             if (!EmitIndexOp(cx, lhs->getOp(), atomIndex, bce))
                 return false;
         }
         break;
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2433,16 +2433,21 @@ Parser<FullParseHandler>::standaloneLazy
                                                  bool strict, GeneratorKind generatorKind)
 {
     MOZ_ASSERT(checkOptionsCalled);
 
     Node pn = handler.newFunctionDefinition();
     if (!pn)
         return null();
 
+    // Our tokenStream has no current token, so pn's position is garbage.
+    // Substitute the position of the first token in our source.
+    if (!tokenStream.peekTokenPos(&pn->pn_pos))
+        return null();
+
     Directives directives(/* strict = */ strict);
     FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ nullptr, directives,
                                          generatorKind);
     if (!funbox)
         return null();
     funbox->length = fun->nargs() - fun->hasRest();
 
     Directives newDirectives = directives;
@@ -5802,17 +5807,19 @@ Parser<ParseHandler>::statement(bool can
                 return null();
             if (!report(ParseWarning, false, null(), JSMSG_USE_ASM_DIRECTIVE_FAIL))
                 return null();
         }
         return expressionStatement();
 
       case TOK_YIELD: {
         TokenKind next;
-        if (!tokenStream.peekToken(&next))
+        TokenStream::Modifier modifier = yieldExpressionsSupported() ? TokenStream::Operand
+                                                                     : TokenStream::None;
+        if (!tokenStream.peekToken(&next, modifier))
             return null();
         if (next == TOK_COLON) {
             if (!checkYieldNameValidity())
                 return null();
             return labeledStatement();
         }
         return expressionStatement();
       }
@@ -6161,17 +6168,17 @@ Parser<ParseHandler>::assignExpr()
 
     if (tt == TOK_STRING) {
         if (!tokenStream.nextTokenEndsExpr(&endsExpr))
             return null();
         if (endsExpr)
             return stringLiteral();
     }
 
-    if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()))
+    if (tt == TOK_YIELD && yieldExpressionsSupported())
         return yieldExpression();
 
     tokenStream.ungetToken();
 
     // Save the tokenizer state in case we find an arrow function and have to
     // rewind.
     TokenStream::Position start(keepAtoms);
     tokenStream.tell(&start);
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -501,16 +501,19 @@ class Parser : private JS::AutoGCRooter,
 
     bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
                                     FunctionSyntaxKind kind);
 
     // Determine whether |yield| is a valid name in the current context, or
     // whether it's prohibited due to strictness, JS version, or occurrence
     // inside a star generator.
     bool checkYieldNameValidity();
+    bool yieldExpressionsSupported() {
+        return versionNumber() >= JSVERSION_1_7 || pc->isGenerator();
+    }
 
     virtual bool strictMode() { return pc->sc->strict; }
 
     const ReadOnlyCompileOptions &options() const {
         return tokenStream.options();
     }
 
   private:
--- a/js/src/jit-test/tests/asm.js/testGlobals.js
+++ b/js/src/jit-test/tests/asm.js/testGlobals.js
@@ -8,25 +8,19 @@ assertEq(asmLink(asmCompile(USE_ASM + "v
 assertEq(asmLink(asmCompile(USE_ASM + "const i=42; function f(){ return i|0 } return f"))(), 42);
 assertEq(asmLink(asmCompile(USE_ASM + "var i=4.2; function f(){ return +i } return f"))(), 4.2);
 assertEq(asmLink(asmCompile(USE_ASM + "const i=4.2; function f(){ return +i } return f"))(), 4.2);
 assertAsmTypeFail(USE_ASM + "var i=42; function f(){ return +(i+.1) } return f");
 assertAsmTypeFail(USE_ASM + "const i=42; function f(){ return +(i+.1) } return f");
 assertAsmTypeFail(USE_ASM + "var i=1.2; function f(){ return i|0 } return f");
 assertAsmTypeFail(USE_ASM + "const i=1.2; function f(){ return i|0 } return f");
 assertAsmTypeFail(USE_ASM + "var i=0; function f(e){ e=+e; i=e } return f");
-assertAsmTypeFail(USE_ASM + "const i=0; function f(e){ e=+e; i=e } return f");
 assertAsmTypeFail(USE_ASM + "var d=0.1; function f(i){ i=i|0; d=i } return f");
-assertAsmTypeFail(USE_ASM + "const d=0.1; function f(i){ i=i|0; d=i } return f");
 assertEq(asmLink(asmCompile(USE_ASM + "var i=13; function f(j) { j=j|0; i=j; return i|0 } return f"))(42), 42);
-assertAsmTypeFail(USE_ASM + "const i=13; function f(j) { j=j|0; i=j; return i|0 } return f");
-assertAsmTypeFail(USE_ASM + "const c=0,i=13; function f(j) { j=j|0; i=j; return i|0 } return f");
 assertEq(asmLink(asmCompile(USE_ASM + "var d=.1; function f(e) { e=+e; d=e; return +e } return f"))(42.1), 42.1);
-assertAsmTypeFail(USE_ASM + "const d=.1; function f(e) { e=+e; d=e; return +e } return f");
-assertAsmTypeFail(USE_ASM + "const c=0, d=.1; function f(e) { e=+e; d=e; return +e } return f");
 assertEq(asmLink(asmCompile(USE_ASM + "var i=13; function f(i, j) { i=i|0; j=j|0; i=j; return i|0 } return f"))(42,43), 43);
 assertEq(asmLink(asmCompile(USE_ASM + "var i=13; function f(j) { j=j|0; var i=0; i=j; return i|0 } return f"))(42), 42);
 
 var f = asmLink(asmCompile(USE_ASM + "var i=13; function f(j) { j=j|0; if ((j|0) != -1) { i=j } else { return i|0 } return 0 } return f"));
 assertEq(f(-1), 13);
 assertEq(f(42), 0);
 assertEq(f(-1), 42);
 
@@ -56,17 +50,16 @@ assertAsmTypeFail('global', USE_ASM + "v
 assertAsmTypeFail('global', USE_ASM + "const i=global; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + "var i=global|0; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + "const i=global|0; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + "var j=0;var i=j.i|0; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + "var i=global.i|0; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + "const i=global.i|0; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + "var i=global.i|0; function f() { return i|0 } return f");
 assertAsmTypeFail('global', USE_ASM + 'var i=global.Infinity; function f() { i = 0.0 } return f');
-assertAsmTypeFail('global', USE_ASM + 'const i=global.Infinity; function f() { i = 0.0 } return f');
 assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), undefined);
 assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'const i=global.Infinity; function f() { return +i } return f'), undefined);
 assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), null);
 assertAsmLinkAlwaysFail(asmCompile('global', USE_ASM + 'const i=global.Infinity; function f() { return +i } return f'), null);
 assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), 3);
 assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {});
 assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {Infinity:NaN});
 assertAsmLinkFail(asmCompile('global', USE_ASM + 'var i=global.Infinity; function f() { return +i } return f'), {Infinity:-Infinity});
--- a/js/src/jit-test/tests/basic/bug673731.js
+++ b/js/src/jit-test/tests/basic/bug673731.js
@@ -1,3 +1,3 @@
 // |jit-test| error: ReferenceError
 
-const IS_TOKEN_ARRAY = [ printBugNumber && IS_TOKEN_ARRAY++ && this() ? this() : this() ];
+const IS_TOKEN_ARRAY = [ printBugNumber && this() ? this() : this() ];
--- a/js/src/jit-test/tests/basic/bug821470.js
+++ b/js/src/jit-test/tests/basic/bug821470.js
@@ -1,8 +1,8 @@
 load(libdir + "asserts.js");
 
 assertThrowsInstanceOf(function () {
     eval("function x() { 'use strict'; const x = 4; x = 3; }");
-}, TypeError);
+}, SyntaxError);
 assertThrowsInstanceOf(function () {
     Function("'use strict'; const x = x = 5;");
-}, TypeError)
+}, SyntaxError)
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/constAssignError.js
@@ -0,0 +1,23 @@
+// Can't assign to const
+
+load(libdir + 'asserts.js');
+
+function assertSyntaxError(str) {
+  assertThrowsInstanceOf(function () { eval(str); }, SyntaxError);
+}
+
+assertSyntaxError("(function() { const x = 3; (function() x++)(); return x })");
+assertSyntaxError("(function() { const x = 3; (function() x++)(); return x++ })");
+assertSyntaxError("(function() { const x = 2; (function() x++)(); return ++x })");
+assertSyntaxError("(function() { const x = 2; (function() x++)(); return x += 1 })");
+
+assertSyntaxError("(function() { const x = 3; x = 1; return (function() { return x})() })");
+assertSyntaxError("(function() { const x = 3; x = 1; return (function() { return x})() })");
+assertSyntaxError("(function() { const x = 3; x++; return (function() { return x})() })");
+assertSyntaxError("(function() { const x = 3; ++x; return (function() { return x})() })");
+assertSyntaxError("(function() { const x = 3; x--; return (function() { return x})() })");
+assertSyntaxError("(function() { const x = 3; --x; return (function() { return x})() })");
+assertSyntaxError("(function() { const x = 3; x += 1; return (function() { return x})() })");
+assertSyntaxError("(function() { const x = 3; x -= 1; return (function() { return x})() })");
+
+assertSyntaxError("(function() { const x = 3; [x] = [1]; })");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/constGlobalAssignError.js
@@ -0,0 +1,3 @@
+// |jit-test| error: SyntaxError
+
+const x = 1; x = 2;
--- a/js/src/jit-test/tests/basic/testDynamicUsage.js
+++ b/js/src/jit-test/tests/basic/testDynamicUsage.js
@@ -3,30 +3,16 @@ assertEq((function() { var g = function(
 assertEq((function() { var x; x = 3; return (function() { return x } )() })(), 3);
 assertEq((function() { x = 3; var x; return (function() { return x } )() })(), 3);
 assertEq((function() { var x; var g = function() { return x }; x = 3; return g() })(), 3);
 
 assertEq((function() { function f() { return 3 }; assertEq(f(), 3); return (function() f())() })(), 3);
 assertEq((function() { function f() { return 3 }; assertEq(f(), 3); return eval('f()') })(), 3);
 assertEq((function() { function f() { return 3 }; (function() f())(); return f() })(), 3);
 
-assertEq((function() { const x = 3; (function() x++)(); return x })(), 3);
-assertEq((function() { const x = 3; (function() x++)(); return x++ })(), 3);
-assertEq((function() { const x = 2; (function() x++)(); return ++x })(), 3);
-assertEq((function() { const x = 2; (function() x++)(); return x += 1 })(), 3);
-
-assertEq((function() { const x = 3; x = 1; return (function() { return x})() })(), 3);
-assertEq((function() { const x = 3; x = 1; return (function() { return x})() })(), 3);
-assertEq((function() { const x = 3; x++; return (function() { return x})() })(), 3);
-assertEq((function() { const x = 3; ++x; return (function() { return x})() })(), 3);
-assertEq((function() { const x = 3; x--; return (function() { return x})() })(), 3);
-assertEq((function() { const x = 3; --x; return (function() { return x})() })(), 3);
-assertEq((function() { const x = 3; x += 1; return (function() { return x})() })(), 3);
-assertEq((function() { const x = 3; x -= 1; return (function() { return x})() })(), 3);
-
 assertEq((function() { var x = 3; return eval("x") })(), 3);
 assertEq((function() { var x; x = 3; return eval("x") })(), 3);
 assertEq((function() { x = 3; var x; return eval("x") })(), 3);
 
 assertEq((function() { var x; (function() {x = 2})(); return ++x })(), 3);
 assertEq((function() { var x; (function() {x = 2})(); x++; return x })(), 3);
 assertEq((function() { var x; (function() {x = 4})(); return --x })(), 3);
 assertEq((function() { var x; (function() {x = 4})(); x--; return x })(), 3);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/execution-observability-02.js
@@ -0,0 +1,15 @@
+// Test that baseline frames are marked as debuggee when resuming from
+// throwing.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+
+var hits = 0;
+dbg.onEnterFrame = function (f) { hits++; };
+
+try {
+  g.eval("for (c in (function() { yield })()) h");
+} catch (e) {
+}
+
+assertEq(hits, 2);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onDebuggerStatement-05.js
@@ -0,0 +1,22 @@
+// For perf reasons we don't recompile all a debuggee global's scripts when
+// Debugger no longer needs to observe all execution for that global. Test that
+// things don't crash if we try to run a script with a BaselineScript that was
+// compiled with debug instrumentation when the global is no longer a debuggee.
+
+var g = newGlobal();
+var dbg = new Debugger(g);
+var counter = 0;
+dbg.onDebuggerStatement = function (frame) {
+  counter++;
+  if (counter == 15)
+    dbg.onDebuggerStatement = undefined;
+};
+
+g.eval("" + function f() {
+  {
+    let inner = 42;
+    debugger;
+    inner++;
+  }
+});
+g.eval("for (var i = 0; i < 20; i++) f()");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/debug/onExceptionUnwind-12.js
@@ -0,0 +1,14 @@
+var g = newGlobal();
+g.parent = this;
+g.hits = 0;
+g.eval("new Debugger(parent).onExceptionUnwind = function () { hits++; };");
+function f() {
+    var x = f();
+}
+try {
+  f();
+} catch (e) {
+  assertEq(e instanceof InternalError, true);
+} finally {
+  assertEq(g.hits, 0);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/generators/yield-regexp.js
@@ -0,0 +1,41 @@
+// Bug 1099956
+
+load(libdir + "asserts.js");
+
+// ES6 treating yield as an identifier except in ES6 generators introduces a
+// syntax conflict with permissible JS >= 1.7 legacy generator syntax.  Is
+// |yield /a/g| inside a function an attempt to convert the function into a
+// legacy generator, yielding a RegExp instance?  Or does it instead read as
+// |(yield / a) / g|?  Similar ambiguities exist for different textual content
+// in place of |a| -- |yield /x+17/g| or |(yield / x) + 17 / g|, and so on.
+// (And, much less importantly, is |yield /a/g| a syntax error in global code
+// as in JS >= 1.7, or is it |(yield / a) / g|.)
+//
+// For now, in JS >= 1.7, we preserve the old behavior.  In all other JS we
+// conform to ES6: |yield /a/g| is a YieldExpression inside an ES6 generator,
+// and it's an IdentifierReference divided twice when not in an ES6 generator.
+// This test will need changes if we change our JS >= 1.7 parsing to be
+// ES6-compatible.
+
+function f1() {
+  yield /abc/g;
+}
+
+var g = f1();
+var v;
+v = g.next();
+assertEq(v instanceof RegExp, true);
+assertEq(v.toString(), "/abc/g");
+assertThrowsValue(() => g.next(), StopIteration);
+
+function* f2() {
+  yield /abc/g;
+}
+
+g = f2();
+v = g.next();
+assertEq(v.done, false);
+assertEq(v.value instanceof RegExp, true);
+assertEq(v.value.toString(), "/abc/g");
+v = g.next();
+assertEq(v.done, true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/parser/lazy-parse-bad-offset.js
@@ -0,0 +1,5 @@
+// Bug 1098132: Shouldn't assert.
+
+options('strict');
+function eval() {};
+eval();
--- a/js/src/jit-test/tests/saved-stacks/gc-frame-cache.js
+++ b/js/src/jit-test/tests/saved-stacks/gc-frame-cache.js
@@ -4,17 +4,17 @@
 const FUZZ_FACTOR = 3;
 
 function assertAboutEq(actual, expected) {
   if (Math.abs(actual - expected) > FUZZ_FACTOR)
     throw new Error("Assertion failed: expected about " + expected + ", got " + actual +
                     ". FUZZ_FACTOR = " + FUZZ_FACTOR);
 }
 
-const stacks = [];
+var stacks = [];
 
 stacks.push(saveStack());
 stacks.push(saveStack());
 stacks.push(saveStack());
 stacks.push(saveStack());
 stacks.push(saveStack());
 stacks.push(saveStack());
 stacks.push(saveStack());
--- a/js/src/jit/Bailouts.cpp
+++ b/js/src/jit/Bailouts.cpp
@@ -82,17 +82,17 @@ jit::Bailout(BailoutStack *sp, BaselineB
     // might be true now. A GC might have reclaimed all the Jit code and
     // invalidated all frames which are currently on the stack. As we are
     // already in a bailout, we could not switch to an invalidation
     // bailout. When the code of an IonScript which is on the stack is
     // invalidated (see InvalidateActivation), we remove references to it and
     // increment the reference counter for each activation that appear on the
     // stack. As the bailed frame is one of them, we have to decrement it now.
     if (iter.ionScript()->invalidated())
-        iter.ionScript()->decref(cx->runtime()->defaultFreeOp());
+        iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
 
     return retval;
 }
 
 uint32_t
 jit::InvalidationBailout(InvalidationBailoutStack *sp, size_t *frameSizeOut,
                          BaselineBailoutInfo **bailoutInfo)
 {
@@ -154,17 +154,17 @@ jit::InvalidationBailout(InvalidationBai
         frame->replaceCalleeToken(nullptr);
         EnsureExitFrame(frame);
 
         JitSpew(JitSpew_IonInvalidate, "   new  calleeToken %p", (void *) frame->calleeToken());
         JitSpew(JitSpew_IonInvalidate, "   new  frameSize %u", unsigned(frame->prevFrameLocalSize()));
         JitSpew(JitSpew_IonInvalidate, "   new  ra %p", (void *) frame->returnAddress());
     }
 
-    iter.ionScript()->decref(cx->runtime()->defaultFreeOp());
+    iter.ionScript()->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
 
     return retval;
 }
 
 BailoutFrameInfo::BailoutFrameInfo(const JitActivationIterator &activations,
                                    const JitFrameIterator &frame)
   : machine_(frame.machineState())
 {
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -1772,16 +1772,17 @@ jit::FinishBailoutToBaseline(BaselineBai
       case Bailout_NonInt32Input:
       case Bailout_NonNumericInput:
       case Bailout_NonBooleanInput:
       case Bailout_NonObjectInput:
       case Bailout_NonStringInput:
       case Bailout_NonSymbolInput:
       case Bailout_GuardThreadExclusive:
       case Bailout_InitialState:
+      case Bailout_Debugger:
         // Do nothing.
         break;
 
       // Invalid assumption based on baseline code.
       case Bailout_OverflowInvalidate:
       case Bailout_NonStringInputInvalidate:
       case Bailout_DoubleOutput:
         if (!HandleBaselineInfoBailout(cx, outerScript, innerScript))
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -2975,22 +2975,20 @@ BaselineCompiler::emit_JSOP_EXCEPTION()
 
 typedef bool (*OnDebuggerStatementFn)(JSContext *, BaselineFrame *, jsbytecode *pc, bool *);
 static const VMFunction OnDebuggerStatementInfo =
     FunctionInfo<OnDebuggerStatementFn>(jit::OnDebuggerStatement);
 
 bool
 BaselineCompiler::emit_JSOP_DEBUGGER()
 {
-    if (!compileDebugInstrumentation_)
-        return true;
-
     prepareVMCall();
     pushArg(ImmPtr(pc));
 
+    frame.assertSyncedStack();
     masm.loadBaselineFramePtr(BaselineFrameReg, R0.scratchReg());
     pushArg(R0.scratchReg());
 
     if (!callVM(OnDebuggerStatementInfo))
         return false;
 
     // If the stub returns |true|, return the frame's return value.
     Label done;
@@ -3446,18 +3444,18 @@ BaselineCompiler::emit_JSOP_FINALYIELDRV
     masm.loadValue(frame.addressOfReturnValue(), JSReturnOperand);
     return emitReturn();
 }
 
 typedef bool (*InterpretResumeFn)(JSContext *, HandleObject, HandleValue, HandlePropertyName,
                                   MutableHandleValue);
 static const VMFunction InterpretResumeInfo = FunctionInfo<InterpretResumeFn>(jit::InterpretResume);
 
-typedef bool (*GeneratorThrowFn)(JSContext *, HandleObject, HandleValue, uint32_t);
-static const VMFunction GeneratorThrowInfo = FunctionInfo<GeneratorThrowFn>(js::GeneratorThrowOrClose);
+typedef bool (*GeneratorThrowFn)(JSContext *, BaselineFrame *, HandleObject, HandleValue, uint32_t);
+static const VMFunction GeneratorThrowInfo = FunctionInfo<GeneratorThrowFn>(jit::GeneratorThrowOrClose);
 
 bool
 BaselineCompiler::emit_JSOP_RESUME()
 {
     GeneratorObject::ResumeKind resumeKind = GeneratorObject::getResumeKind(pc);
 
     frame.syncStack(0);
     masm.checkStackAlignment();
@@ -3598,21 +3596,23 @@ BaselineCompiler::emit_JSOP_RESUME()
 
         // Update the frame's frameSize field.
         Register scratch3 = regs.takeAny();
         masm.computeEffectiveAddress(Address(BaselineFrameReg, BaselineFrame::FramePointerOffset),
                                      scratch2);
         masm.movePtr(scratch2, scratch3);
         masm.subPtr(BaselineStackReg, scratch2);
         masm.store32(scratch2, Address(BaselineFrameReg, BaselineFrame::reverseOffsetOfFrameSize()));
+        masm.loadBaselineFramePtr(BaselineFrameReg, scratch2);
 
         prepareVMCall();
         pushArg(Imm32(resumeKind));
         pushArg(retVal);
         pushArg(genObj);
+        pushArg(scratch2);
 
         JitCode *code = cx->runtime()->jitRuntime()->getVMWrapper(GeneratorThrowInfo);
         if (!code)
             return false;
 
         // Create the frame descriptor.
         masm.subPtr(BaselineStackReg, scratch3);
         masm.makeFrameDescriptor(scratch3, JitFrame_BaselineJS);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3894,19 +3894,19 @@ struct ScriptCountBlockState
 #ifdef DEBUG
 bool
 CodeGenerator::branchIfInvalidated(Register temp, Label *invalidated)
 {
     CodeOffsetLabel label = masm.movWithPatch(ImmWord(uintptr_t(-1)), temp);
     if (!ionScriptLabels_.append(label))
         return false;
 
-    // If IonScript::refcount != 0, the script has been invalidated.
+    // If IonScript::invalidationCount_ != 0, the script has been invalidated.
     masm.branch32(Assembler::NotEqual,
-                  Address(temp, IonScript::offsetOfRefcount()),
+                  Address(temp, IonScript::offsetOfInvalidationCount()),
                   Imm32(0),
                   invalidated);
     return true;
 }
 
 bool
 CodeGenerator::emitObjectOrStringResultChecks(LInstruction *lir, MDefinition *mir)
 {
@@ -10170,10 +10170,28 @@ CodeGenerator::visitLexicalCheck(LLexica
 }
 
 bool
 CodeGenerator::visitThrowUninitializedLexical(LThrowUninitializedLexical *ins)
 {
     return callVM(ThrowUninitializedLexicalInfo, ins);
 }
 
+bool
+CodeGenerator::visitDebugger(LDebugger *ins)
+{
+    Register cx = ToRegister(ins->getTemp(0));
+    Register temp = ToRegister(ins->getTemp(1));
+
+    // The check for cx->compartment()->isDebuggee() could be inlined, but the
+    // performance of |debugger;| does not matter.
+    masm.loadJSContext(cx);
+    masm.setupUnalignedABICall(1, temp);
+    masm.passABIArg(cx);
+    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, IsCompartmentDebuggee));
+
+    Label bail;
+    masm.branchIfTrueBool(ReturnReg, &bail);
+    return bailoutFrom(&bail, ins->snapshot());
+}
+
 } // namespace jit
 } // namespace js
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -313,16 +313,17 @@ class CodeGenerator : public CodeGenerat
     bool visitIsObjectAndBranch(LIsObjectAndBranch *lir);
     bool visitHaveSameClass(LHaveSameClass *lir);
     bool visitHasClass(LHasClass *lir);
     bool visitAsmJSParameter(LAsmJSParameter *lir);
     bool visitAsmJSReturn(LAsmJSReturn *ret);
     bool visitAsmJSVoidReturn(LAsmJSVoidReturn *ret);
     bool visitLexicalCheck(LLexicalCheck *ins);
     bool visitThrowUninitializedLexical(LThrowUninitializedLexical *ins);
+    bool visitDebugger(LDebugger *ins);
 
     bool visitCheckOverRecursed(LCheckOverRecursed *lir);
     bool visitCheckOverRecursedFailure(CheckOverRecursedFailure *ool);
 
     bool visitCheckOverRecursedPar(LCheckOverRecursedPar *lir);
 
     bool visitInterruptCheckPar(LInterruptCheckPar *lir);
     bool visitOutOfLineInterruptCheckPar(OutOfLineInterruptCheckPar *ool);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -834,17 +834,17 @@ IonScript::IonScript()
     snapshotsListSize_(0),
     snapshotsRVATableSize_(0),
     constantTable_(0),
     constantEntries_(0),
     callTargetList_(0),
     callTargetEntries_(0),
     backedgeList_(0),
     backedgeEntries_(0),
-    refcount_(0),
+    invalidationCount_(0),
     parallelAge_(0),
     recompileInfo_(),
     osrPcMismatchCounter_(0),
     dependentAsmJSModules(nullptr),
     pendingBuilder_(nullptr)
 {
 }
 
@@ -2672,17 +2672,17 @@ InvalidateActivation(FreeOp *fop, const 
         // point.
         //
         // Note: you can't simplify this mechanism to "just patch the
         // instruction immediately after the call" because things may
         // need to move into a well-defined register state (using move
         // instructions after the call) in to capture an appropriate
         // snapshot after the call occurs.
 
-        ionScript->incref();
+        ionScript->incrementInvalidationCount();
 
         JitCode *ionCode = ionScript->method();
 
         JS::Zone *zone = script->zone();
         if (zone->needsIncrementalBarrier()) {
             // We're about to remove edges from the JSScript to gcthings
             // embedded in the JitCode. Perform one final trace of the
             // JitCode for the incremental GC, as it must know about
@@ -2706,18 +2706,18 @@ InvalidateActivation(FreeOp *fop, const 
         CodeLocationLabel dataLabelToMunge(it.returnAddressToFp());
         ptrdiff_t delta = ionScript->invalidateEpilogueDataOffset() -
                           (it.returnAddressToFp() - ionCode->raw());
         Assembler::PatchWrite_Imm32(dataLabelToMunge, Imm32(delta));
 
         CodeLocationLabel osiPatchPoint = SafepointReader::InvalidationPatchPoint(ionScript, si);
         CodeLocationLabel invalidateEpilogue(ionCode, CodeOffsetLabel(ionScript->invalidateEpilogueOffset()));
 
-        JitSpew(JitSpew_IonInvalidate, "   ! Invalidate ionScript %p (ref %u) -> patching osipoint %p",
-                ionScript, ionScript->refcount(), (void *) osiPatchPoint.raw());
+        JitSpew(JitSpew_IonInvalidate, "   ! Invalidate ionScript %p (inv count %u) -> patching osipoint %p",
+                ionScript, ionScript->invalidationCount(), (void *) osiPatchPoint.raw());
         Assembler::PatchWrite_NearCall(osiPatchPoint, invalidateEpilogue);
     }
 
     JitSpew(JitSpew_IonInvalidate, "END invalidating activation");
 }
 
 void
 jit::StopAllOffThreadCompilations(JSCompartment *comp)
@@ -2766,17 +2766,17 @@ jit::Invalidate(types::TypeZone &types, 
             continue;
 
         JitSpew(JitSpew_IonInvalidate, " Invalidate %s:%u, IonScript %p",
                 co->script()->filename(), co->script()->lineno(), co->ion());
 
         // Keep the ion script alive during the invalidation and flag this
         // ionScript as being invalidated.  This increment is removed by the
         // loop after the calls to InvalidateActivation.
-        co->ion()->incref();
+        co->ion()->incrementInvalidationCount();
         numInvalidations++;
     }
 
     if (!numInvalidations) {
         JitSpew(JitSpew_IonInvalidate, " No IonScript invalidation.");
         return;
     }
 
@@ -2794,17 +2794,17 @@ jit::Invalidate(types::TypeZone &types, 
 
         ExecutionMode executionMode = co->mode();
         JSScript *script = co->script();
         IonScript *ionScript = co->ion();
         if (!ionScript)
             continue;
 
         SetIonScript(nullptr, script, executionMode, nullptr);
-        ionScript->decref(fop);
+        ionScript->decrementInvalidationCount(fop);
         co->invalidate();
         numInvalidations--;
 
         // Wait for the scripts to get warm again before doing another
         // compile, unless either:
         // (1) we are recompiling *because* a script got hot;
         //     (resetUses is false); or,
         // (2) we are invalidating a parallel script.  This is because
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1820,16 +1820,19 @@ IonBuilder::inspectOpcode(JSOp op)
         return true;
 
       case JSOP_INSTANCEOF:
         return jsop_instanceof();
 
       case JSOP_DEBUGLEAVEBLOCK:
         return true;
 
+      case JSOP_DEBUGGER:
+        return jsop_debugger();
+
       default:
 #ifdef DEBUG
         return abort("Unsupported opcode: %s (line %d)", js_CodeName[op], info().lineno(pc));
 #else
         return abort("Unsupported opcode: %d (line %d)", op, info().lineno(pc));
 #endif
     }
 }
@@ -11065,16 +11068,28 @@ IonBuilder::jsop_instanceof()
     MCallInstanceOf *ins = MCallInstanceOf::New(alloc(), obj, rhs);
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
+bool
+IonBuilder::jsop_debugger()
+{
+    MDebugger *debugger = MDebugger::New(alloc());
+    current->add(debugger);
+
+    // The |debugger;| statement will always bail out to baseline if
+    // cx->compartment()->isDebuggee(). Resume in-place and have baseline
+    // handle the details.
+    return resumeAt(debugger, pc);
+}
+
 MInstruction *
 IonBuilder::addConvertElementsToDoubles(MDefinition *elements)
 {
     MInstruction *convert = MConvertElementsToDoubles::New(alloc(), elements);
     current->add(convert);
     return convert;
 }
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -675,16 +675,17 @@ class IonBuilder
     bool jsop_itermore();
     bool jsop_isnoiter();
     bool jsop_iterend();
     bool jsop_in();
     bool jsop_in_dense();
     bool jsop_instanceof();
     bool jsop_getaliasedvar(ScopeCoordinate sc);
     bool jsop_setaliasedvar(ScopeCoordinate sc);
+    bool jsop_debugger();
 
     /* Inlining. */
 
     enum InliningStatus
     {
         InliningStatus_Error,
         InliningStatus_NotInlined,
         InliningStatus_WarmUpCountTooLow,
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -274,17 +274,17 @@ struct IonScript
     uint32_t callTargetList_;
     uint32_t callTargetEntries_;
 
     // List of patchable backedges which are threaded into the runtime's list.
     uint32_t backedgeList_;
     uint32_t backedgeEntries_;
 
     // Number of references from invalidation records.
-    uint32_t refcount_;
+    uint32_t invalidationCount_;
 
     // If this is a parallel script, the number of major GC collections it has
     // been idle, otherwise 0.
     //
     // JSScripts with parallel IonScripts are preserved across GC if the
     // parallel age is < MAX_PARALLEL_AGE.
     uint32_t parallelAge_;
 
@@ -389,18 +389,18 @@ struct IonScript
         return offsetof(IonScript, method_);
     }
     static inline size_t offsetOfOsrEntryOffset() {
         return offsetof(IonScript, osrEntryOffset_);
     }
     static inline size_t offsetOfSkipArgCheckEntryOffset() {
         return offsetof(IonScript, skipArgCheckEntryOffset_);
     }
-    static inline size_t offsetOfRefcount() {
-        return offsetof(IonScript, refcount_);
+    static inline size_t offsetOfInvalidationCount() {
+        return offsetof(IonScript, invalidationCount_);
     }
     static inline size_t offsetOfRecompiling() {
         return offsetof(IonScript, recompiling_);
     }
 
   public:
     JitCode *method() const {
         return method_;
@@ -576,32 +576,32 @@ struct IonScript
     void copyCacheEntries(const uint32_t *caches, MacroAssembler &masm);
     void copySafepoints(const SafepointWriter *writer);
     void copyCallTargetEntries(JSScript **callTargets);
     void copyPatchableBackedges(JSContext *cx, JitCode *code,
                                 PatchableBackedgeInfo *backedges,
                                 MacroAssembler &masm);
 
     bool invalidated() const {
-        return refcount_ != 0;
+        return invalidationCount_ != 0;
     }
 
     // Invalidate the current compilation.
     bool invalidate(JSContext *cx, bool resetUses, const char *reason);
 
-    size_t refcount() const {
-        return refcount_;
+    size_t invalidationCount() const {
+        return invalidationCount_;
     }
-    void incref() {
-        refcount_++;
+    void incrementInvalidationCount() {
+        invalidationCount_++;
     }
-    void decref(FreeOp *fop) {
-        MOZ_ASSERT(refcount_);
-        refcount_--;
-        if (!refcount_)
+    void decrementInvalidationCount(FreeOp *fop) {
+        MOZ_ASSERT(invalidationCount_);
+        invalidationCount_--;
+        if (!invalidationCount_)
             Destroy(fop, this);
     }
     const types::RecompileInfo& recompileInfo() const {
         return recompileInfo_;
     }
     types::RecompileInfo& recompileInfoRef() {
         return recompileInfo_;
     }
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -211,42 +211,39 @@ JitFrameIterator::script() const
     MOZ_ASSERT(isScripted());
     if (isBaselineJS())
         return baselineFrame()->script();
     JSScript *script = ScriptFromCalleeToken(calleeToken());
     MOZ_ASSERT(script);
     return script;
 }
 
-uint8_t *
-JitFrameIterator::resumeAddressToFp() const
-{
-    // If we are settled on a patched BaselineFrame due to debug mode OSR, get
-    // the real return address via the stashed DebugModeOSRInfo.
-    if (isBaselineJS() && baselineFrame()->getDebugModeOSRInfo())
-        return baselineFrame()->debugModeOSRInfo()->resumeAddr;
-    return returnAddressToFp();
-}
-
 void
 JitFrameIterator::baselineScriptAndPc(JSScript **scriptRes, jsbytecode **pcRes) const
 {
     MOZ_ASSERT(isBaselineJS());
     JSScript *script = this->script();
     if (scriptRes)
         *scriptRes = script;
-    uint8_t *retAddr = resumeAddressToFp();
 
     // If we have unwound the scope due to exception handling to a different
     // pc, the frame should behave as if it were settled on that pc.
     if (jsbytecode *overridePc = baselineFrame()->getUnwoundScopeOverridePc()) {
         *pcRes = overridePc;
         return;
     }
 
+    // If we are settled on a patched BaselineFrame due to debug mode OSR, get
+    // the stashed pc.
+    if (baselineFrame()->getDebugModeOSRInfo()) {
+        *pcRes = baselineFrame()->debugModeOSRInfo()->pc;
+        return;
+    }
+
+    uint8_t *retAddr = returnAddressToFp();
     if (pcRes) {
         // If the return address is into the prologue entry address or just
         // after the debug prologue, then assume start of script.
         if (retAddr == script->baselineScript()->prologueEntryAddr() ||
             retAddr == script->baselineScript()->postDebugPrologueAddr())
         {
             *pcRes = script->code();
             return;
@@ -729,17 +726,17 @@ HandleException(ResumeFromException *rfe
             IonScript *ionScript = nullptr;
             bool invalidated = iter.checkInvalidation(&ionScript);
 
             for (;;) {
                 HandleExceptionIon(cx, frames, rfe, &overrecursed);
 
                 if (rfe->kind == ResumeFromException::RESUME_BAILOUT) {
                     if (invalidated)
-                        ionScript->decref(cx->runtime()->defaultFreeOp());
+                        ionScript->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
                     return;
                 }
 
                 MOZ_ASSERT(rfe->kind == ResumeFromException::RESUME_ENTRY_FRAME);
 
                 // Figure out whether SPS frame was pushed for this frame or not.
                 // Even if profiler is enabled, the frame being popped might have
                 // been entered prior to SPS being enabled, and thus not have
@@ -762,17 +759,17 @@ HandleException(ResumeFromException *rfe
                     TraceLogStopEvent(logger, TraceLogger::IonMonkey);
                     TraceLogStopEvent(logger);
                     break;
                 }
                 ++frames;
             }
 
             if (invalidated)
-                ionScript->decref(cx->runtime()->defaultFreeOp());
+                ionScript->decrementInvalidationCount(cx->runtime()->defaultFreeOp());
 
         } else if (iter.isBaselineJS()) {
             // It's invalid to call DebugEpilogue twice for the same frame.
             bool calledDebugEpilogue = false;
 
             // Remember the pc we unwound the scope to.
             jsbytecode *unwoundScopeToPc = nullptr;
 
--- a/js/src/jit/IonTypes.h
+++ b/js/src/jit/IonTypes.h
@@ -105,16 +105,19 @@ enum BailoutKind
     Bailout_GuardThreadExclusive,
 
     // PJS bailout when encountering MIR unsafe for parallel execution.
     Bailout_ParallelUnsafe,
 
     // For the initial snapshot when entering a function.
     Bailout_InitialState,
 
+    // We hit a |debugger;| statement.
+    Bailout_Debugger,
+
     // END Normal bailouts
 
 
     // Bailouts caused by invalid assumptions based on Baseline code.
     // Causes immediate invalidation.
 
     // Like Bailout_Overflow, but causes immediate invalidation.
     Bailout_OverflowInvalidate,
@@ -196,16 +199,18 @@ BailoutKindString(BailoutKind kind)
       case Bailout_NonStringInput:
         return "Bailout_NonStringInput";
       case Bailout_NonSymbolInput:
         return "Bailout_NonSymbolInput";
       case Bailout_GuardThreadExclusive:
         return "Bailout_GuardThreadExclusive";
       case Bailout_InitialState:
         return "Bailout_InitialState";
+      case Bailout_Debugger:
+        return "Bailout_Debugger";
 
       // Bailouts caused by invalid assumptions.
       case Bailout_OverflowInvalidate:
         return "Bailout_OverflowInvalidate";
       case Bailout_NonStringInputInvalidate:
         return "Bailout_NonStringInputInvalidate";
       case Bailout_DoubleOutput:
         return "Bailout_DoubleOutput";
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -174,20 +174,16 @@ class JitFrameIterator
     Value *actualArgs() const;
 
     // Returns the return address of the frame above this one (that is, the
     // return address that returns back to the current frame).
     uint8_t *returnAddressToFp() const {
         return returnAddressToFp_;
     }
 
-    // Returns the resume address. As above, except taking
-    // BaselineDebugModeOSRInfo into account, if present.
-    uint8_t *resumeAddressToFp() const;
-
     // Previous frame information extracted from the current frame.
     inline size_t prevFrameLocalSize() const;
     inline FrameType prevType() const;
     uint8_t *prevFp() const;
 
     // Returns the stack space used by the current frame, in bytes. This does
     // not include the size of its fixed header.
     size_t frameSize() const {
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -5089,24 +5089,23 @@ class LClampIToUint8 : public LInstructi
   public:
     LIR_HEADER(ClampIToUint8)
 
     explicit LClampIToUint8(const LAllocation &in) {
         setOperand(0, in);
     }
 };
 
-class LClampDToUint8 : public LInstructionHelper<1, 1, 1>
+class LClampDToUint8 : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(ClampDToUint8)
 
-    LClampDToUint8(const LAllocation &in, const LDefinition &temp) {
+    explicit LClampDToUint8(const LAllocation &in) {
         setOperand(0, in);
-        setTemp(0, temp);
     }
 };
 
 class LClampVToUint8 : public LInstructionHelper<1, BOX_PIECES, 1>
 {
   public:
     LIR_HEADER(ClampVToUint8)
 
@@ -6860,12 +6859,23 @@ class LMemoryBarrier : public LInstructi
         return type_;
     }
 
     const MMemoryBarrier *mir() const {
         return mir_->toMemoryBarrier();
     }
 };
 
+class LDebugger : public LCallInstructionHelper<0, 0, 2>
+{
+  public:
+    LIR_HEADER(Debugger)
+
+    LDebugger(const LDefinition &temp1, const LDefinition &temp2) {
+        setTemp(0, temp1);
+        setTemp(1, temp2);
+    }
+};
+
 } // namespace jit
 } // namespace js
 
 #endif /* jit_LIR_Common_h */
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -335,17 +335,18 @@
     _(InterruptCheckPar)            \
     _(RecompileCheck)               \
     _(MemoryBarrier)                \
     _(AssertRangeI)                 \
     _(AssertRangeD)                 \
     _(AssertRangeF)                 \
     _(AssertRangeV)                 \
     _(LexicalCheck)                 \
-    _(ThrowUninitializedLexical)
+    _(ThrowUninitializedLexical)    \
+    _(Debugger)
 
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/LOpcodes-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/LOpcodes-x64.h"
 #elif defined(JS_CODEGEN_ARM)
 # include "jit/arm/LOpcodes-arm.h"
 #elif defined(JS_CODEGEN_MIPS)
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2969,17 +2969,17 @@ LIRGenerator::visitClampToUint8(MClampTo
     switch (in->type()) {
       case MIRType_Boolean:
         return redefine(ins, in);
 
       case MIRType_Int32:
         return defineReuseInput(new(alloc()) LClampIToUint8(useRegisterAtStart(in)), ins, 0);
 
       case MIRType_Double:
-        return define(new(alloc()) LClampDToUint8(useRegisterAtStart(in), tempCopy(in, 0)), ins);
+        return define(new(alloc()) LClampDToUint8(useRegisterAtStart(in)), ins);
 
       case MIRType_Value:
       {
         LClampVToUint8 *lir = new(alloc()) LClampVToUint8(tempDouble());
         if (!useBox(lir, LClampVToUint8::Input, in))
             return false;
         return assignSnapshot(lir, Bailout_NonPrimitiveInput)
                && define(lir, ins)
@@ -4104,16 +4104,23 @@ LIRGenerator::visitLexicalCheck(MLexical
 
 bool
 LIRGenerator::visitThrowUninitializedLexical(MThrowUninitializedLexical *ins)
 {
     LThrowUninitializedLexical *lir = new(alloc()) LThrowUninitializedLexical();
     return add(lir, ins) && assignSafepoint(lir, ins);
 }
 
+bool
+LIRGenerator::visitDebugger(MDebugger *ins)
+{
+    LDebugger *lir = new(alloc()) LDebugger(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
+    return assignSnapshot(lir, Bailout_Debugger) && add(lir, ins);
+}
+
 static void
 SpewResumePoint(MBasicBlock *block, MInstruction *ins, MResumePoint *resumePoint)
 {
     fprintf(JitSpewFile, "Current resume point %p details:\n", (void *)resumePoint);
     fprintf(JitSpewFile, "    frame count: %u\n", resumePoint->frameCount());
 
     if (ins) {
         fprintf(JitSpewFile, "    taken after: ");
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -291,14 +291,15 @@ class LIRGenerator : public LIRGenerator
     bool visitSimdReinterpretCast(MSimdReinterpretCast *ins);
     bool visitPhi(MPhi *ins);
     bool visitBeta(MBeta *ins);
     bool visitObjectState(MObjectState *ins);
     bool visitArrayState(MArrayState *ins);
     bool visitUnknownValue(MUnknownValue *ins);
     bool visitLexicalCheck(MLexicalCheck *ins);
     bool visitThrowUninitializedLexical(MThrowUninitializedLexical *ins);
+    bool visitDebugger(MDebugger *ins);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_Lowering_h */
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -11999,16 +11999,26 @@ class MAtomicTypedArrayElementBinop
     MDefinition *value() {
         return getOperand(2);
     }
     AliasSet getAliasSet() const {
         return AliasSet::Store(AliasSet::TypedArrayElement);
     }
 };
 
+class MDebugger : public MNullaryInstruction
+{
+  public:
+    INSTRUCTION_HEADER(Debugger);
+
+    static MDebugger *New(TempAllocator &alloc) {
+        return new(alloc) MDebugger();
+    }
+};
+
 class MAsmJSNeg : public MUnaryInstruction
 {
     MAsmJSNeg(MDefinition *op, MIRType type)
       : MUnaryInstruction(op)
     {
         setResultType(type);
         setMovable();
     }
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -257,17 +257,18 @@ namespace jit {
     _(ForkJoinContext)                                                      \
     _(ForkJoinGetSlice)                                                     \
     _(GuardThreadExclusive)                                                 \
     _(InterruptCheckPar)                                                    \
     _(RecompileCheck)                                                       \
     _(MemoryBarrier)                                                        \
     _(UnknownValue)                                                         \
     _(LexicalCheck)                                                         \
-    _(ThrowUninitializedLexical)
+    _(ThrowUninitializedLexical)                                            \
+    _(Debugger)
 
 // Forward declarations of MIR types.
 #define FORWARD_DECLARE(op) class M##op;
  MIR_OPCODE_LIST(FORWARD_DECLARE)
 #undef FORWARD_DECLARE
 
 class MDefinitionVisitor // interface i.e. pure abstract class
 {
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -355,16 +355,17 @@ class ParallelSafetyVisitor : public MDe
     UNSAFE_OP(AsmJSCall)
     DROP_OP(RecompileCheck)
     UNSAFE_OP(CompareExchangeTypedArrayElement)
     UNSAFE_OP(AtomicTypedArrayElementBinop)
     UNSAFE_OP(MemoryBarrier)
     UNSAFE_OP(UnknownValue)
     UNSAFE_OP(LexicalCheck)
     UNSAFE_OP(ThrowUninitializedLexical)
+    UNSAFE_OP(Debugger)
 
     // It looks like these could easily be made safe:
     UNSAFE_OP(ConvertElementsToDoubles)
     UNSAFE_OP(MaybeCopyElementsForWrite)
 };
 
 static void
 TransplantResumePoint(MInstruction *oldInstruction, MInstruction *replacementInstruction)
--- a/js/src/jit/TypePolicy.cpp
+++ b/js/src/jit/TypePolicy.cpp
@@ -731,18 +731,16 @@ InstanceOfPolicy::adjustInputs(TempAlloc
 
     return true;
 }
 
 bool
 StoreTypedArrayPolicy::adjustValueInput(TempAllocator &alloc, MInstruction *ins, int arrayType,
                                         MDefinition *value, int valueOperand)
 {
-    SingleObjectPolicy::staticAdjustInputs(alloc, ins);
-
     MDefinition *curValue = value;
     // First, ensure the value is int32, boolean, double or Value.
     // The conversion is based on TypedArrayObjectTemplate::setElementTail.
     switch (value->type()) {
       case MIRType_Int32:
       case MIRType_Double:
       case MIRType_Float32:
       case MIRType_Boolean:
@@ -814,16 +812,18 @@ StoreTypedArrayPolicy::adjustValueInput(
         ins->replaceOperand(valueOperand, value);
 
     return true;
 }
 
 bool
 StoreTypedArrayPolicy::adjustInputs(TempAllocator &alloc, MInstruction *ins)
 {
+    SingleObjectPolicy::staticAdjustInputs(alloc, ins);
+
     MStoreTypedArrayElement *store = ins->toStoreTypedArrayElement();
     MOZ_ASSERT(IsValidElementsType(store->elements(), store->offsetAdjustment()));
     MOZ_ASSERT(store->index()->type() == MIRType_Int32);
 
     return adjustValueInput(alloc, ins, store->arrayType(), store->value(), 2);
 }
 
 bool
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -913,16 +913,24 @@ DebugAfterYield(JSContext *cx, BaselineF
     // The BaselineFrame has just been constructed by JSOP_RESUME in the
     // caller. We need to set its debuggee flag as necessary.
     if (frame->script()->isDebuggee())
         frame->setIsDebuggee();
     return true;
 }
 
 bool
+GeneratorThrowOrClose(JSContext *cx, BaselineFrame *frame, HandleObject obj, HandleValue arg,
+                      uint32_t resumeKind)
+{
+    MOZ_ALWAYS_TRUE(DebugAfterYield(cx, frame));
+    return js::GeneratorThrowOrClose(cx, obj, arg, resumeKind);
+}
+
+bool
 StrictEvalPrologue(JSContext *cx, BaselineFrame *frame)
 {
     return frame->strictEvalPrologue(cx);
 }
 
 bool
 HeavyweightFunPrologue(JSContext *cx, BaselineFrame *frame)
 {
@@ -1013,41 +1021,42 @@ HandleDebugTrap(JSContext *cx, BaselineF
     return true;
 }
 
 bool
 OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn)
 {
     *mustReturn = false;
 
-    RootedScript script(cx, frame->script());
-    RootedValue rval(cx);
-
-    switch (Debugger::onDebuggerStatement(cx, frame, &rval)) {
+    switch (Debugger::onDebuggerStatement(cx, frame)) {
       case JSTRAP_ERROR:
         return false;
 
       case JSTRAP_CONTINUE:
         return true;
 
       case JSTRAP_RETURN:
-        frame->setReturnValue(rval);
         *mustReturn = true;
         return jit::DebugEpilogue(cx, frame, pc, true);
 
       case JSTRAP_THROW:
-        cx->setPendingException(rval);
         return false;
 
       default:
         MOZ_CRASH("Invalid trap status");
     }
 }
 
 bool
+IsCompartmentDebuggee(JSContext *cx)
+{
+    return cx->compartment()->isDebuggee();
+}
+
+bool
 PushBlockScope(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block)
 {
     return frame->pushBlock(cx, block);
 }
 
 bool
 PopBlockScope(JSContext *cx, BaselineFrame *frame)
 {
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -708,27 +708,30 @@ bool DebugEpilogueOnBaselineReturn(JSCon
 
 JSObject *CreateGenerator(JSContext *cx, BaselineFrame *frame);
 bool NormalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc,
                    uint32_t stackDepth);
 bool FinalSuspend(JSContext *cx, HandleObject obj, BaselineFrame *frame, jsbytecode *pc);
 bool InterpretResume(JSContext *cx, HandleObject obj, HandleValue val, HandlePropertyName kind,
                      MutableHandleValue rval);
 bool DebugAfterYield(JSContext *cx, BaselineFrame *frame);
+bool GeneratorThrowOrClose(JSContext *cx, BaselineFrame *frame, HandleObject obj, HandleValue arg,
+                           uint32_t resumeKind);
 
 bool StrictEvalPrologue(JSContext *cx, BaselineFrame *frame);
 bool HeavyweightFunPrologue(JSContext *cx, BaselineFrame *frame);
 
 bool NewArgumentsObject(JSContext *cx, BaselineFrame *frame, MutableHandleValue res);
 
 JSObject *InitRestParameter(JSContext *cx, uint32_t length, Value *rest, HandleObject templateObj,
                             HandleObject res);
 
 bool HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, bool *mustReturn);
 bool OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn);
+bool IsCompartmentDebuggee(JSContext *cx);
 
 bool EnterWith(JSContext *cx, BaselineFrame *frame, HandleValue val,
                Handle<StaticWithObject *> templ);
 bool LeaveWith(JSContext *cx, BaselineFrame *frame);
 
 bool PushBlockScope(JSContext *cx, BaselineFrame *frame, Handle<StaticBlockObject *> block);
 bool PopBlockScope(JSContext *cx, BaselineFrame *frame);
 bool DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -176,16 +176,17 @@ MSG_DEF(JSMSG_ACCESSOR_WRONG_ARGS,     3
 MSG_DEF(JSMSG_ARGUMENTS_AND_REST,      0, JSEXN_SYNTAXERR, "'arguments' object may not be used in conjunction with a rest parameter")
 MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE,     0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
 MSG_DEF(JSMSG_ARRAY_INIT_TOO_BIG,      0, JSEXN_INTERNALERR, "array initialiser too large")
 MSG_DEF(JSMSG_AS_AFTER_RESERVED_WORD,  1, JSEXN_SYNTAXERR, "missing keyword 'as' after reserved word '{0}'")
 MSG_DEF(JSMSG_BAD_ANON_GENERATOR_RETURN, 0, JSEXN_TYPEERR, "anonymous generator function returns a value")
 MSG_DEF(JSMSG_BAD_ARROW_ARGS,          0, JSEXN_SYNTAXERR, "invalid arrow-function arguments (parentheses around the arrow-function may help)")
 MSG_DEF(JSMSG_BAD_BINDING,             1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
 MSG_DEF(JSMSG_BAD_CONST_DECL,          0, JSEXN_SYNTAXERR, "missing = in const declaration")
+MSG_DEF(JSMSG_BAD_CONST_ASSIGN,        1, JSEXN_SYNTAXERR, "invalid assignment to const {0}")
 MSG_DEF(JSMSG_BAD_CONTINUE,            0, JSEXN_SYNTAXERR, "continue must be inside loop")
 MSG_DEF(JSMSG_BAD_DESTRUCT_ASS,        0, JSEXN_REFERENCEERR, "invalid destructuring assignment operator")
 MSG_DEF(JSMSG_BAD_DESTRUCT_TARGET,     0, JSEXN_SYNTAXERR, "invalid destructuring target")
 MSG_DEF(JSMSG_BAD_DESTRUCT_DECL,       0, JSEXN_SYNTAXERR, "missing = in destructuring declaration")
 MSG_DEF(JSMSG_BAD_DUP_ARGS,            0, JSEXN_SYNTAXERR, "duplicate argument names not allowed in this context")
 MSG_DEF(JSMSG_BAD_FOR_EACH_LOOP,       0, JSEXN_SYNTAXERR, "invalid for each loop")
 MSG_DEF(JSMSG_BAD_FOR_LEFTSIDE,        0, JSEXN_SYNTAXERR, "invalid for/in left-hand side")
 MSG_DEF(JSMSG_BAD_GENERATOR_RETURN,    1, JSEXN_TYPEERR, "generator function {0} returns a value")
--- a/js/src/jsapi-tests/README
+++ b/js/src/jsapi-tests/README
@@ -67,17 +67,17 @@ tests.h:
         The test framework creates these fresh for each test. The default
         environment has reasonable default settings, including
         JSOPTION_VAROBJFIX, JSOPTION_JIT, a global object of a class with
         JSCLASS_GLOBAL_FLAGS, and an error reporter that prints to stderr.
         See also "Custom test setup" below.
 
     EXEC(const char *code);
 
-        Execute some JS code in global scope, using JS_EvaluateScript. Return
+        Execute some JS code in global scope, using JS::Evaluate. Return
         false if that fails. (This means that if the code throws an uncaught JS
         exception, the test fails.)
 
     EVAL(const char *code, jsval *vp);
 
         Same as EXEC, but store the result value in *vp.
 
     CHECK(bool cond);
--- a/js/src/jsapi-tests/testArrayBufferView.cpp
+++ b/js/src/jsapi-tests/testArrayBufferView.cpp
@@ -88,17 +88,19 @@ CreateDataView(JSContext *cx)
 {
     JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
     if (!global)
         return nullptr;
 
     static const char code[] = "new DataView(new ArrayBuffer(8))";
 
     JS::Rooted<JS::Value> val(cx);
-    if (!JS_EvaluateScript(cx, global, code, strlen(code), __FILE__, __LINE__, &val))
+    JS::CompileOptions opts(cx);
+    if (!JS::Evaluate(cx, global, opts.setFileAndLine(__FILE__, __LINE__),
+                      code, strlen(code), &val))
         return nullptr;
 
     JS::Rooted<JSObject*> dv(cx, &val.toObject());
     if (!JS_IsDataViewObject(dv))
         return nullptr;
 
     return dv;
 }
--- a/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
+++ b/js/src/jsapi-tests/testFreshGlobalEvalRedefinition.cpp
@@ -36,13 +36,15 @@ BEGIN_TEST(testRedefineGlobalEval)
     if (!g)
         return false;
 
     JSAutoCompartment ac(cx, g);
     JS::Rooted<JS::Value> v(cx);
     CHECK(JS_GetProperty(cx, g, "Object", &v));
 
     static const char data[] = "Object.defineProperty(this, 'eval', { configurable: false });";
-    CHECK(JS_EvaluateScript(cx, g, data, mozilla::ArrayLength(data) - 1, __FILE__, __LINE__, &v));
+    JS::CompileOptions opts(cx);
+    CHECK(JS::Evaluate(cx, g, opts.setFileAndLine(__FILE__, __LINE__),
+                       data, mozilla::ArrayLength(data) - 1, &v));
 
     return true;
 }
 END_TEST(testRedefineGlobalEval)
--- a/js/src/jsapi-tests/testGCOutOfMemory.cpp
+++ b/js/src/jsapi-tests/testGCOutOfMemory.cpp
@@ -24,17 +24,18 @@ BEGIN_TEST(testGCOutOfMemory)
 
     static const char source[] =
         "var max = 0; (function() {"
         "    var array = [];"
         "    for (; ; ++max)"
         "        array.push({});"
         "    array = []; array.push(0);"
         "})();";
-    bool ok = JS_EvaluateScript(cx, global, source, strlen(source), "", 1, &root);
+    JS::CompileOptions opts(cx);
+    bool ok = JS::Evaluate(cx, global, opts, source, strlen(source), &root);
 
     /* Check that we get OOM. */
     CHECK(!ok);
     CHECK(!JS_IsExceptionPending(cx));
     CHECK_EQUAL(errorCount, 1u);
     JS_GC(rt);
 
     // Temporarily disabled to reopen the tree. Bug 847579.
--- a/js/src/jsapi-tests/testJSEvaluateScript.cpp
+++ b/js/src/jsapi-tests/testJSEvaluateScript.cpp
@@ -9,32 +9,35 @@ BEGIN_TEST(testJSEvaluateScript)
     JS::RootedObject obj(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), global));
     CHECK(obj);
 
     CHECK(JS::RuntimeOptionsRef(cx).varObjFix());
 
     static const char src[] = "var x = 5;";
 
     JS::RootedValue retval(cx);
-    CHECK(JS_EvaluateScript(cx, obj, src, sizeof(src) - 1, __FILE__, __LINE__, &retval));
+    JS::CompileOptions opts(cx);
+    CHECK(JS::Evaluate(cx, obj, opts.setFileAndLine(__FILE__, __LINE__),
+                       src, sizeof(src) - 1, &retval));
 
     bool hasProp = true;
     CHECK(JS_AlreadyHasOwnProperty(cx, obj, "x", &hasProp));
     CHECK(!hasProp);
 
     hasProp = false;
     CHECK(JS_HasProperty(cx, global, "x", &hasProp));
     CHECK(hasProp);
 
     // Now do the same thing, but without JSOPTION_VAROBJFIX
     JS::RuntimeOptionsRef(cx).setVarObjFix(false);
 
     static const char src2[] = "var y = 5;";
 
-    CHECK(JS_EvaluateScript(cx, obj, src2, sizeof(src2) - 1, __FILE__, __LINE__, &retval));
+    CHECK(JS::Evaluate(cx, obj, opts.setFileAndLine(__FILE__, __LINE__),
+                       src2, sizeof(src2) - 1, &retval));
 
     hasProp = false;
     CHECK(JS_AlreadyHasOwnProperty(cx, obj, "y", &hasProp));
     CHECK(hasProp);
 
     hasProp = true;
     CHECK(JS_AlreadyHasOwnProperty(cx, global, "y", &hasProp));
     CHECK(!hasProp);
--- a/js/src/jsapi-tests/testSourcePolicy.cpp
+++ b/js/src/jsapi-tests/testSourcePolicy.cpp
@@ -11,18 +11,24 @@ BEGIN_TEST(testBug795104)
     JS::CompileOptions opts(cx);
     JS::CompartmentOptionsRef(cx->compartment()).setDiscardSource(true);
     const size_t strLen = 60002;
     char *s = static_cast<char *>(JS_malloc(cx, strLen));
     CHECK(s);
     s[0] = '"';
     memset(s + 1, 'x', strLen - 2);
     s[strLen - 1] = '"';
-    CHECK(JS::Evaluate(cx, global, opts, s, strLen));
+    // We don't want an rval for our Evaluate call
+    opts.setNoScriptRval(true);
+    JS::RootedValue unused(cx);
+    CHECK(JS::Evaluate(cx, global, opts, s, strLen, &unused));
     JS::RootedFunction fun(cx);
     JS::AutoObjectVector emptyScopeChain(cx);
+    // But when compiling a function we don't want to use no-rval
+    // mode, since it's not supported for functions.
+    opts.setNoScriptRval(false);
     CHECK(JS::CompileFunction(cx, emptyScopeChain, opts, "f", 0, nullptr, s, strLen, &fun));
     CHECK(fun);
     JS_free(cx, s);
 
     return true;
 }
 END_TEST(testBug795104)
--- a/js/src/jsapi-tests/tests.cpp
+++ b/js/src/jsapi-tests/tests.cpp
@@ -49,25 +49,29 @@ void JSAPITest::uninit()
         rt = nullptr;
     }
 }
 
 bool JSAPITest::exec(const char *bytes, const char *filename, int lineno)
 {
     JS::RootedValue v(cx);
     JS::HandleObject global = JS::HandleObject::fromMarkedLocation(this->global.unsafeGet());
-    return JS_EvaluateScript(cx, global, bytes, strlen(bytes), filename, lineno, &v) ||
+    JS::CompileOptions opts(cx);
+    opts.setFileAndLine(filename, lineno);
+    return JS::Evaluate(cx, global, opts, bytes, strlen(bytes), &v) ||
         fail(JSAPITestString(bytes), filename, lineno);
 }
 
 bool JSAPITest::evaluate(const char *bytes, const char *filename, int lineno,
                          JS::MutableHandleValue vp)
 {
     JS::HandleObject global = JS::HandleObject::fromMarkedLocation(this->global.unsafeGet());
-    return JS_EvaluateScript(cx, global, bytes, strlen(bytes), filename, lineno, vp) ||
+    JS::CompileOptions opts(cx);
+    opts.setFileAndLine(filename, lineno);
+    return JS::Evaluate(cx, global, opts, bytes, strlen(bytes), vp) ||
         fail(JSAPITestString(bytes), filename, lineno);
 }
 
 bool JSAPITest::definePrint()
 {
     JS::HandleObject global = JS::HandleObject::fromMarkedLocation(this->global.unsafeGet());
     return JS_DefineFunction(cx, global, "print", (JSNative) print, 0, 0);
 }
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4759,38 +4759,38 @@ JS::CloneAndExecuteScript(JSContext *cx,
     }
     return ExecuteScript(cx, obj, script, nullptr);
 }
 
 static const unsigned LARGE_SCRIPT_LENGTH = 500*1024;
 
 static bool
 Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optionsArg,
-         SourceBufferHolder &srcBuf, JS::Value *rval)
+         SourceBufferHolder &srcBuf, MutableHandleValue rval)
 {
     CompileOptions options(cx, optionsArg);
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
 
     AutoLastFrameCheck lfc(cx);
 
     options.setCompileAndGo(obj->is<GlobalObject>());
-    options.setNoScriptRval(!rval);
     SourceCompressionTask sct(cx);
     RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(),
                                                     obj, NullPtr(), options,
                                                     srcBuf, nullptr, 0, &sct));
     if (!script)
         return false;
 
     MOZ_ASSERT(script->getVersion() == options.version);
 
-    bool result = Execute(cx, script, *obj, rval);
+    bool result = Execute(cx, script, *obj,
+                          options.noScriptRval ? nullptr : rval.address());
     if (!sct.complete())
         result = false;
 
     // After evaluation, the compiled script will not be run again.
     // script->ensureRanAnalysis allocated 1 analyze::Bytecode for every opcode
     // which for large scripts means significant memory. Perform a GC eagerly
     // to clear out this analysis data before anything happens to inhibit the
     // flushing of this memory (such as setting requestAnimationFrame).
@@ -4800,174 +4800,92 @@ Evaluate(JSContext *cx, HandleObject obj
         cx->runtime()->gc.gc(GC_NORMAL, JS::gcreason::FINISH_LARGE_EVALUATE);
     }
 
     return result;
 }
 
 static bool
 Evaluate(JSContext *cx, AutoObjectVector &scopeChain, const ReadOnlyCompileOptions &optionsArg,
-         SourceBufferHolder &srcBuf, JS::Value *rval)
+         SourceBufferHolder &srcBuf, MutableHandleValue rval)
 {
     RootedObject dynamicScope(cx);
     RootedObject unusedStaticScope(cx);
     if (!CreateScopeObjectsForScopeChain(cx, scopeChain, &dynamicScope, &unusedStaticScope))
         return false;
     return ::Evaluate(cx, dynamicScope, optionsArg, srcBuf, rval);
 }
 
 static bool
 Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optionsArg,
-         const char16_t *chars, size_t length, JS::Value *rval)
+         const char16_t *chars, size_t length, MutableHandleValue rval)
 {
   SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::NoOwnership);
   return ::Evaluate(cx, obj, optionsArg, srcBuf, rval);
 }
 
-static bool
-Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
-         const char *bytes, size_t length, JS::Value *rval)
+extern JS_PUBLIC_API(bool)
+JS::Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
+         const char *bytes, size_t length, MutableHandleValue rval)
 {
     char16_t *chars;
     if (options.utf8)
         chars = UTF8CharsToNewTwoByteCharsZ(cx, JS::UTF8Chars(bytes, length), &length).get();
     else
         chars = InflateString(cx, bytes, &length);
     if (!chars)
         return false;
 
     SourceBufferHolder srcBuf(chars, length, SourceBufferHolder::GiveOwnership);
     bool ok = ::Evaluate(cx, obj, options, srcBuf, rval);
     return ok;
 }
 
 static bool
 Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optionsArg,
-         const char *filename, JS::Value *rval)
+         const char *filename, MutableHandleValue rval)
 {
     FileContents buffer(cx);
     {
         AutoFile file;
         if (!file.open(cx, filename) || !file.readAll(cx, buffer))
             return false;
     }
 
     CompileOptions options(cx, optionsArg);
     options.setFileAndLine(filename, 1);
     return Evaluate(cx, obj, options, buffer.begin(), buffer.length(), rval);
 }
 
-extern JS_PUBLIC_API(bool)
+JS_PUBLIC_API(bool)
 JS::Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optionsArg,
              SourceBufferHolder &srcBuf, MutableHandleValue rval)
 {
-    return ::Evaluate(cx, obj, optionsArg, srcBuf, rval.address());
-}
-
-extern JS_PUBLIC_API(bool)
+    return ::Evaluate(cx, obj, optionsArg, srcBuf, rval);
+}
+
+JS_PUBLIC_API(bool)
 JS::Evaluate(JSContext *cx, AutoObjectVector &scopeChain, const ReadOnlyCompileOptions &optionsArg,
              SourceBufferHolder &srcBuf, MutableHandleValue rval)
 {
-    return ::Evaluate(cx, scopeChain, optionsArg, srcBuf, rval.address());
-}
-
-extern JS_PUBLIC_API(bool)
+    return ::Evaluate(cx, scopeChain, optionsArg, srcBuf, rval);
+}
+
+JS_PUBLIC_API(bool)
 JS::Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optionsArg,
              const char16_t *chars, size_t length, MutableHandleValue rval)
 {
-    return ::Evaluate(cx, obj, optionsArg, chars, length, rval.address());
-}
-
-extern JS_PUBLIC_API(bool)
-JS::Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
-             const char *bytes, size_t length, MutableHandleValue rval)
-{
-    return ::Evaluate(cx, obj, options, bytes, length, rval.address());
-}
-
-extern JS_PUBLIC_API(bool)
+    return ::Evaluate(cx, obj, optionsArg, chars, length, rval);
+}
+
+JS_PUBLIC_API(bool)
 JS::Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optionsArg,
              const char *filename, MutableHandleValue rval)
 {
-    return ::Evaluate(cx, obj, optionsArg, filename, rval.address());
-}
-
-extern JS_PUBLIC_API(bool)
-JS::Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optionsArg,
-             SourceBufferHolder &srcBuf)
-{
-    return ::Evaluate(cx, obj, optionsArg, srcBuf, nullptr);
-}
-
-extern JS_PUBLIC_API(bool)
-JS::Evaluate(JSContext *cx, AutoObjectVector &scopeChain, const ReadOnlyCompileOptions &optionsArg,
-             SourceBufferHolder &srcBuf)
-{
-    return ::Evaluate(cx, scopeChain, optionsArg, srcBuf, nullptr);
-}
-
-extern JS_PUBLIC_API(bool)
-JS::Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optionsArg,
-             const char16_t *chars, size_t length)
-{
-    return ::Evaluate(cx, obj, optionsArg, chars, length, nullptr);
-}
-
-extern JS_PUBLIC_API(bool)
-JS::Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
-             const char *bytes, size_t length)
-{
-    return ::Evaluate(cx, obj, options, bytes, length, nullptr);
-}
-
-extern JS_PUBLIC_API(bool)
-JS::Evaluate(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &optionsArg,
-             const char *filename)
-{
-    return ::Evaluate(cx, obj, optionsArg, filename, nullptr);
-}
-
-JS_PUBLIC_API(bool)
-JS_EvaluateUCScript(JSContext *cx, HandleObject obj, const char16_t *chars, unsigned length,
-                    const char *filename, unsigned lineno, MutableHandleValue rval)
-{
-    CompileOptions options(cx);
-    options.setFileAndLine(filename, lineno);
-
-    return ::Evaluate(cx, obj, options, chars, length, rval.address());
-}
-
-JS_PUBLIC_API(bool)
-JS_EvaluateUCScript(JSContext *cx, HandleObject obj, SourceBufferHolder &srcBuf,
-                    const char *filename, unsigned lineno, MutableHandleValue rval)
-{
-    CompileOptions options(cx);
-    options.setFileAndLine(filename, lineno);
-
-    return ::Evaluate(cx, obj, options, srcBuf, rval.address());
-}
-
-JS_PUBLIC_API(bool)
-JS_EvaluateScript(JSContext *cx, HandleObject obj, const char *bytes, unsigned nbytes,
-                  const char *filename, unsigned lineno, MutableHandleValue rval)
-{
-    CompileOptions options(cx);
-    options.setFileAndLine(filename, lineno);
-
-    return ::Evaluate(cx, obj, options, bytes, nbytes, rval.address());
-}
-
-JS_PUBLIC_API(bool)
-JS_EvaluateScript(JSContext *cx, HandleObject obj, const char *bytes, unsigned nbytes,
-                  const char *filename, unsigned lineno)
-{
-    CompileOptions options(cx);
-    options.setFileAndLine(filename, lineno);
-
-    return ::Evaluate(cx, obj, options, bytes, nbytes, nullptr);
+    return ::Evaluate(cx, obj, optionsArg, filename, rval);
 }
 
 JS_PUBLIC_API(bool)
 JS_CallFunction(JSContext *cx, HandleObject obj, HandleFunction fun, const HandleValueArray& args,
                 MutableHandleValue rval)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
@@ -6094,44 +6012,49 @@ JS_ReportPendingException(JSContext *cx)
     bool ok = js_ReportUncaughtException(cx);
     MOZ_ASSERT(!cx->isExceptionPending());
     return ok;
 }
 
 JS::AutoSaveExceptionState::AutoSaveExceptionState(JSContext *cx)
   : context(cx),
     wasPropagatingForcedReturn(cx->propagatingForcedReturn_),
+    wasOverRecursed(cx->overRecursed_),
     wasThrowing(cx->throwing),
     exceptionValue(cx)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     if (wasPropagatingForcedReturn)
         cx->clearPropagatingForcedReturn();
+    if (wasOverRecursed)
+        cx->overRecursed_ = false;
     if (wasThrowing) {
         exceptionValue = cx->unwrappedException_;
         cx->clearPendingException();
     }
 }
 
 void
 JS::AutoSaveExceptionState::restore()
 {
     context->propagatingForcedReturn_ = wasPropagatingForcedReturn;
+    context->overRecursed_ = wasOverRecursed;
     context->throwing = wasThrowing;
     context->unwrappedException_ = exceptionValue;
     drop();
 }
 
 JS::AutoSaveExceptionState::~AutoSaveExceptionState()
 {
     if (!context->isExceptionPending()) {
         if (wasPropagatingForcedReturn)
             context->setPropagatingForcedReturn();
         if (wasThrowing) {
+            context->overRecursed_ = wasOverRecursed;
             context->throwing = true;
             context->unwrappedException_ = exceptionValue;
         }
     }
 }
 
 struct JSExceptionState {
     bool throwing;
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -4001,17 +4001,17 @@ JS_DecompileScript(JSContext *cx, JS::Ha
 
 extern JS_PUBLIC_API(JSString *)
 JS_DecompileFunction(JSContext *cx, JS::Handle<JSFunction*> fun, unsigned indent);
 
 extern JS_PUBLIC_API(JSString *)
 JS_DecompileFunctionBody(JSContext *cx, JS::Handle<JSFunction*> fun, unsigned indent);
 
 /*
- * NB: JS_ExecuteScript and the JS_Evaluate*Script* quadruplets use the obj
+ * NB: JS_ExecuteScript and the JS::Evaluate APIs use the obj
  * parameter as the initial scope chain header, the 'this' keyword value, and
  * the variables object (ECMA parlance for where 'var' and 'function' bind
  * names) of the execution context for script.
  *
  * Using obj as the variables object is problematic if obj's parent (which is
  * the scope chain link; see JS_SetParent and JS_NewObject) is not null: in
  * this case, variables created by 'var x = 0', e.g., go in obj, but variables
  * created by assignment to an unbound id, 'x = 0', go in the last object on
@@ -4021,27 +4021,26 @@ JS_DecompileFunctionBody(JSContext *cx, 
  * embeddings have several such objects.  ECMA requires that "global code" be
  * executed with the variables object equal to this global object.  But these
  * JS API entry points provide freedom to execute code against a "sub-global",
  * i.e., a parented or scoped object, in which case the variables object will
  * differ from the last object on the scope chain, resulting in confusing and
  * non-ECMA explicit vs. implicit variable creation.
  *
  * Caveat embedders: unless you already depend on this buggy variables object
- * binding behavior, you should call ContextOptionsRef(cx).setVarObjFix(true)
+ * binding behavior, you should call RuntimeOptionsRef(rt).setVarObjFix(true)
  * for each context in the application, if you pass parented objects as the obj
  * parameter, or may ever pass such objects in the future.
  *
- * Why a runtime option?  The alternative is to add six or so new API entry
- * points with signatures matching the following six, and that doesn't seem
- * worth the code bloat cost.  Such new entry points would probably have less
- * obvious names, too, so would not tend to be used.  The JS_SetOption call,
- * OTOH, can be more easily hacked into existing code that does not depend on
- * the bug; such code can continue to use the familiar JS_EvaluateScript,
- * etc., entry points.
+ * Why a runtime option?  The alternative is to add APIs duplicating those below
+ * for the other value of varobjfix, and that doesn't seem worth the code bloat
+ * cost.  Such new entry points would probably have less obvious names, too, so
+ * would not tend to be used.  The RuntimeOptionsRef adjustment, OTOH, can be
+ * more easily hacked into existing code that does not depend on the bug; such
+ * code can continue to use the familiar JS::Evaluate, etc., entry points.
  */
 extern JS_PUBLIC_API(bool)
 JS_ExecuteScript(JSContext *cx, JS::HandleObject obj, JS::HandleScript script, JS::MutableHandleValue rval);
 
 extern JS_PUBLIC_API(bool)
 JS_ExecuteScript(JSContext *cx, JS::HandleObject obj, JS::HandleScript script);
 
 /*
@@ -4062,33 +4061,16 @@ namespace JS {
  * Like the above, but handles a cross-compartment script. If the script