Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 29 May 2015 16:58:30 -0400
changeset 277142 45a4d6336c7326d3f7541c62b98437fd0fcb4245
parent 277072 164a9a5ab7c94b5c5d6f6fc415ca0e3642810971 (current diff)
parent 277141 e592b2411157f1bd0fe110233247e7a361821ad8 (diff)
child 277143 90f12f541628f654b3bc066b77decc044ecdeb67
child 277149 881a9db28198032b5ffdd9fa8b3991ce857e3c4d
child 277178 4c9fbec390f16673d0ef1e2f88d68aae8dc12133
child 277211 a89b9240f50a63fdfc66e4356f2b79f005e3973b
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone41.0a1
first release with
nightly linux32
45a4d6336c73 / 41.0a1 / 20150530030205 / files
nightly linux64
45a4d6336c73 / 41.0a1 / 20150530030205 / files
nightly mac
45a4d6336c73 / 41.0a1 / 20150530030205 / files
nightly win32
45a4d6336c73 / 41.0a1 / 20150530030205 / files
nightly win64
45a4d6336c73 / 41.0a1 / 20150530030205 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c. a=merge
browser/installer/package-manifest.in
toolkit/devtools/server/actors/inspector.js
--- a/accessible/base/DocManager.cpp
+++ b/accessible/base/DocManager.cpp
@@ -27,17 +27,17 @@
 #include "nsIDOMDocument.h"
 #include "nsIDOMWindow.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIWebNavigation.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIWebProgress.h"
 #include "nsCoreUtils.h"
 #include "nsXULAppAPI.h"
-#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/TabChild.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 using namespace mozilla::dom;
 
 StaticAutoPtr<nsTArray<DocAccessibleParent*>> DocManager::sRemoteDocuments;
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -452,18 +452,20 @@ DocManager::CreateDocOrRootAccessible(ns
     // Note: don't use AccReorderEvent to avoid coalsecense and special reorder
     // events processing.
     docAcc->FireDelayedEvent(nsIAccessibleEvent::EVENT_REORDER,
                              ApplicationAcc());
 
     if (IPCAccessibilityActive()) {
       DocAccessibleChild* ipcDoc = new DocAccessibleChild(docAcc);
       docAcc->SetIPCDoc(ipcDoc);
-    auto contentChild = dom::ContentChild::GetSingleton();
-    contentChild->SendPDocAccessibleConstructor(ipcDoc, nullptr, 0);
+      nsCOMPtr<nsITabChild> tabChild =
+        do_GetInterface(aDocument->GetDocShell());
+    static_cast<TabChild*>(tabChild.get())->
+      SendPDocAccessibleConstructor(ipcDoc, nullptr, 0);
     }
   } else {
     parentDocAcc->BindChildDocument(docAcc);
   }
 
 #ifdef A11Y_LOG
   if (logging::IsEnabled(logging::eDocCreate)) {
     logging::DocCreate("document creation finished", aDocument);
--- a/accessible/base/NotificationController.cpp
+++ b/accessible/base/NotificationController.cpp
@@ -5,17 +5,17 @@
 
 #include "NotificationController.h"
 
 #include "DocAccessible-inl.h"
 #include "DocAccessibleChild.h"
 #include "TextLeafAccessible.h"
 #include "TextUpdater.h"
 
-#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
 // NotificationCollector
@@ -287,18 +287,20 @@ NotificationController::WillRefresh(mozi
       DocAccessibleChild* ipcDoc = childDoc->IPCDoc();
       if (ipcDoc) {
         parentIPCDoc->SendBindChildDoc(ipcDoc, id);
         continue;
       }
 
       ipcDoc = new DocAccessibleChild(childDoc);
       childDoc->SetIPCDoc(ipcDoc);
-      auto contentChild = dom::ContentChild::GetSingleton();
-      contentChild->SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id);
+      nsCOMPtr<nsITabChild> tabChild =
+        do_GetInterface(mDocument->DocumentNode()->GetDocShell());
+      static_cast<TabChild*>(tabChild.get())->
+        SendPDocAccessibleConstructor(ipcDoc, parentIPCDoc, id);
     }
   }
 
   mObservingState = eRefreshObserving;
   if (!mDocument)
     return;
 
   // Stop further processing if there are no new notifications of any kind or
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -27,17 +27,17 @@ class DocAccessibleParent : public Proxy
 public:
   DocAccessibleParent() :
     ProxyAccessible(this), mParentDoc(nullptr), mShutdown(false)
   { MOZ_COUNT_CTOR_INHERITED(DocAccessibleParent, ProxyAccessible); }
   ~DocAccessibleParent()
   {
     MOZ_COUNT_DTOR_INHERITED(DocAccessibleParent, ProxyAccessible);
     MOZ_ASSERT(mChildDocs.Length() == 0);
-    MOZ_ASSERT(!mParentDoc);
+    MOZ_ASSERT(!ParentDoc());
   }
 
   /*
    * Called when a message from a document in a child process notifies the main
    * process it is firing an event.
    */
   virtual bool RecvEvent(const uint64_t& aID, const uint32_t& aType)
     override;
@@ -50,47 +50,47 @@ public:
 
   virtual bool RecvCaretMoveEvent(const uint64_t& aID, const int32_t& aOffset)
     override final;
 
   virtual bool RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID) override;
   void Unbind()
   {
     mParent = nullptr;
-    mParentDoc->mChildDocs.RemoveElement(this);
+    ParentDoc()->mChildDocs.RemoveElement(this);
     mParentDoc = nullptr;
   }
 
   void Destroy();
   virtual void ActorDestroy(ActorDestroyReason aWhy) override
   {
     if (!mShutdown)
       Destroy();
   }
 
   /*
    * Return the main processes representation of the parent document (if any)
    * of the document this object represents.
    */
-  DocAccessibleParent* Parent() const { return mParentDoc; }
+  DocAccessibleParent* ParentDoc() const { return mParentDoc; }
 
   /*
    * Called when a document in a content process notifies the main process of a
    * new child document.
    */
   bool AddChildDoc(DocAccessibleParent* aChildDoc, uint64_t aParentID,
                    bool aCreating = true);
 
   /*
    * Called when the document in the content process this object represents
    * notifies the main process a child document has been removed.
    */
   void RemoveChildDoc(DocAccessibleParent* aChildDoc)
   {
-    aChildDoc->mParent->SetChildDoc(nullptr);
+    aChildDoc->Parent()->SetChildDoc(nullptr);
     mChildDocs.RemoveElement(aChildDoc);
     aChildDoc->mParentDoc = nullptr;
     MOZ_ASSERT(aChildDoc->mChildDocs.Length() == 0);
   }
 
   void RemoveAccessible(ProxyAccessible* aAccessible)
   {
     MOZ_ASSERT(mAccessibles.GetEntry(aAccessible->ID()));
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-include protocol PContent;
+include protocol PFileDescriptorSet;
+include protocol PBrowser;
 
 include "mozilla/GfxMessageUtils.h";
 
 using nsIntRect from "nsRect.h";
 using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
 using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
 
 namespace mozilla {
@@ -39,17 +40,17 @@ struct Attribute
 struct RelationTargets
 {
   uint32_t Type;
   uint64_t[] Targets;
 };
 
 prio(normal upto high) sync protocol PDocAccessible
 {
-  manager PContent;
+  manager PBrowser;
 
 parent:
   __delete__();
 
   /*
    * Notify the parent process the document in the child process is firing an
    * event.
    */
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -41,17 +41,17 @@ public:
 
   void AddChildAt(uint32_t aIdx, ProxyAccessible* aChild)
   { mChildren.InsertElementAt(aIdx, aChild); }
 
   uint32_t ChildrenCount() const { return mChildren.Length(); }
   ProxyAccessible* ChildAt(uint32_t aIdx) const { return mChildren[aIdx]; }
 
   // XXX evaluate if this is fast enough.
-  size_t IndexInParent() const { return mParent->mChildren.IndexOf(this); }
+  size_t IndexInParent() const { return Parent()->mChildren.IndexOf(this); }
   int32_t IndexOfEmbeddedChild(const ProxyAccessible*);
   bool MustPruneChildren() const;
 
   void Shutdown();
 
   void SetChildDoc(DocAccessibleParent*);
 
   /**
--- a/accessible/jsat/EventManager.jsm
+++ b/accessible/jsat/EventManager.jsm
@@ -169,20 +169,27 @@ this.EventManager.prototype = {
 
         break;
       }
       case Events.STATE_CHANGE:
       {
         let event = aEvent.QueryInterface(Ci.nsIAccessibleStateChangeEvent);
         let state = Utils.getState(event);
         if (state.contains(States.CHECKED)) {
-          this.present(
-            Presentation.
-              actionInvoked(aEvent.accessible,
-                            event.isEnabled ? 'check' : 'uncheck'));
+          if (aEvent.accessible.role === Roles.SWITCH) {
+            this.present(
+              Presentation.
+                actionInvoked(aEvent.accessible,
+                              event.isEnabled ? 'on' : 'off'));
+          } else {
+            this.present(
+              Presentation.
+                actionInvoked(aEvent.accessible,
+                              event.isEnabled ? 'check' : 'uncheck'));
+          }
         } else if (state.contains(States.SELECTED)) {
           this.present(
             Presentation.
               actionInvoked(aEvent.accessible,
                             event.isEnabled ? 'select' : 'unselect'));
         }
         break;
       }
--- a/accessible/jsat/OutputGenerator.jsm
+++ b/accessible/jsat/OutputGenerator.jsm
@@ -205,17 +205,17 @@ let OutputGenerator = {
     let typeName = Utils.getAttributes(aAccessible)['text-input-type'];
     // Ignore the the input type="text" case.
     if (!typeName || typeName === 'text') {
       return;
     }
     aOutput.push({string: 'textInputType_' + typeName});
   },
 
-  _addState: function _addState(aOutput, aState) {}, // jshint ignore:line
+  _addState: function _addState(aOutput, aState, aRoleStr) {}, // jshint ignore:line
 
   _addRole: function _addRole(aOutput, aRoleStr) {}, // jshint ignore:line
 
   get outputOrder() {
     if (!this._utteranceOrder) {
       this._utteranceOrder = new PrefCache('accessibility.accessfu.utterance');
     }
     return typeof this._utteranceOrder.value === 'number' ?
@@ -247,16 +247,17 @@ let OutputGenerator = {
     'link': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'helpballoon': NAME_FROM_SUBTREE_RULE,
     'list': INCLUDE_DESC | INCLUDE_NAME,
     'listitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'outline': INCLUDE_DESC,
     'outlineitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'pagetab': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'graphic': INCLUDE_DESC,
+    'switch': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'pushbutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'checkbutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'radiobutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
     'buttondropdown': NAME_FROM_SUBTREE_RULE,
     'combobox': INCLUDE_DESC | INCLUDE_VALUE,
     'droplist': INCLUDE_DESC,
     'progressbar': INCLUDE_DESC | INCLUDE_VALUE,
     'slider': INCLUDE_DESC | INCLUDE_VALUE,
@@ -302,17 +303,17 @@ let OutputGenerator = {
     'app root': IGNORE_EXPLICIT_NAME },
 
   objectOutputFunctions: {
     _generateBaseOutput:
       function _generateBaseOutput(aAccessible, aRoleStr, aState, aFlags) {
         let output = [];
 
         if (aFlags & INCLUDE_DESC) {
-          this._addState(output, aState);
+          this._addState(output, aState, aRoleStr);
           this._addType(output, aAccessible, aRoleStr);
           this._addRole(output, aRoleStr);
         }
 
         if (aFlags & INCLUDE_VALUE && aAccessible.value.trim()) {
           output[this.outputOrder === OUTPUT_DESC_FIRST ? 'push' : 'unshift'](
             aAccessible.value);
         }
@@ -408,23 +409,25 @@ let OutputGenerator = {
  *
  * An utterance is ordered from the least to the most important. Speaking the
  * last string usually makes sense, but speaking the first often won't.
  * For example {@link genForAction} might return ['button', 'clicked'] for a
  * clicked event. Speaking only 'clicked' makes sense. Speaking 'button' does
  * not.
  */
 this.UtteranceGenerator = {  // jshint ignore:line
-  __proto__: OutputGenerator,
+  __proto__: OutputGenerator, // jshint ignore:line
 
   gActionMap: {
     jump: 'jumpAction',
     press: 'pressAction',
     check: 'checkAction',
     uncheck: 'uncheckAction',
+    on: 'onAction',
+    off: 'offAction',
     select: 'selectAction',
     unselect: 'unselectAction',
     open: 'openAction',
     close: 'closeAction',
     switch: 'switchAction',
     click: 'clickAction',
     collapse: 'collapseAction',
     expand: 'expandAction',
@@ -470,17 +473,17 @@ this.UtteranceGenerator = {  // jshint i
   },
 
   genForEditingMode: function genForEditingMode(aIsEditing) {
     return [{string: aIsEditing ? 'editingMode' : 'navigationMode'}];
   },
 
   objectOutputFunctions: {
 
-    __proto__: OutputGenerator.objectOutputFunctions,
+    __proto__: OutputGenerator.objectOutputFunctions, // jshint ignore:line
 
     defaultFunc: function defaultFunc() {
       return this.objectOutputFunctions._generateBaseOutput.apply(
         this, arguments);
     },
 
     heading: function heading(aAccessible, aRoleStr, aState, aFlags) {
       let level = {};
@@ -592,34 +595,39 @@ this.UtteranceGenerator = {  // jshint i
   _getContextStart: function _getContextStart(aContext) {
     return aContext.newAncestry;
   },
 
   _addRole: function _addRole(aOutput, aRoleStr) {
     aOutput.push({string: this._getOutputName(aRoleStr)});
   },
 
-  _addState: function _addState(aOutput, aState) {
+  _addState: function _addState(aOutput, aState, aRoleStr) {
 
     if (aState.contains(States.UNAVAILABLE)) {
       aOutput.push({string: 'stateUnavailable'});
     }
 
     if (aState.contains(States.READONLY)) {
       aOutput.push({string: 'stateReadonly'});
     }
 
     // Don't utter this in Jelly Bean, we let TalkBack do it for us there.
     // This is because we expose the checked information on the node itself.
     // XXX: this means the checked state is always appended to the end,
     // regardless of the utterance ordering preference.
     if ((Utils.AndroidSdkVersion < 16 || Utils.MozBuildApp === 'browser') &&
       aState.contains(States.CHECKABLE)) {
-      let statetr = aState.contains(States.CHECKED) ?
-        'stateChecked' : 'stateNotChecked';
+      let checked = aState.contains(States.CHECKED);
+      let statetr;
+      if (aRoleStr === 'switch') {
+        statetr = checked ? 'stateOn' : 'stateOff';
+      } else {
+        statetr = checked ? 'stateChecked' : 'stateNotChecked';
+      }
       aOutput.push({string: statetr});
     }
 
     if (aState.contains(States.PRESSED)) {
       aOutput.push({string: 'statePressed'});
     }
 
     if (aState.contains(States.EXPANDABLE)) {
@@ -657,17 +665,17 @@ this.UtteranceGenerator = {  // jshint i
       this._addName(utterance, aAccessible, aFlags);
       this._addLandmark(utterance, aAccessible);
 
       return utterance;
     }
 };
 
 this.BrailleGenerator = {  // jshint ignore:line
-  __proto__: OutputGenerator,
+  __proto__: OutputGenerator, // jshint ignore:line
 
   genForContext: function genForContext(aContext) {
     let output = OutputGenerator.genForContext.apply(this, arguments);
 
     let acc = aContext.accessible;
 
     // add the static text indicating a list item; do this for both listitems or
     // direct first children of listitems, because these are both common
@@ -694,17 +702,17 @@ this.BrailleGenerator = {  // jshint ign
       }
     }
 
     return output;
   },
 
   objectOutputFunctions: {
 
-    __proto__: OutputGenerator.objectOutputFunctions,
+    __proto__: OutputGenerator.objectOutputFunctions, // jshint ignore:line
 
     defaultFunc: function defaultFunc() {
       return this.objectOutputFunctions._generateBaseOutput.apply(
         this, arguments);
     },
 
     listitem: function listitem(aAccessible, aRoleStr, aState, aFlags) {
       let braille = [];
@@ -755,23 +763,27 @@ this.BrailleGenerator = {  // jshint ign
       }
 
       return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
     },
 
     _useStateNotRole:
       function _useStateNotRole(aAccessible, aRoleStr, aState, aFlags) {
         let braille = [];
-        this._addState(braille, aState, aAccessible.role);
+        this._addState(braille, aState, aRoleStr);
         this._addName(braille, aAccessible, aFlags);
         this._addLandmark(braille, aAccessible);
 
         return braille;
       },
 
+    switch: function braille_generator_object_output_functions_switch() {
+      return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
+    },
+
     checkbutton: function checkbutton() {
       return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
     },
 
     radiobutton: function radiobutton() {
       return this.objectOutputFunctions._useStateNotRole.apply(this, arguments);
     },
 
@@ -791,25 +803,25 @@ this.BrailleGenerator = {  // jshint ign
   _getOutputName: function _getOutputName(aName) {
     return OutputGenerator._getOutputName(aName) + 'Abbr';
   },
 
   _addRole: function _addRole(aBraille, aRoleStr) {
     aBraille.push({string: this._getOutputName(aRoleStr)});
   },
 
-  _addState: function _addState(aBraille, aState, aRole) {
+  _addState: function _addState(aBraille, aState, aRoleStr) {
     if (aState.contains(States.CHECKABLE)) {
       aBraille.push({
         string: aState.contains(States.CHECKED) ?
           this._getOutputName('stateChecked') :
           this._getOutputName('stateUnchecked')
       });
     }
-    if (aRole === Roles.TOGGLE_BUTTON) {
+    if (aRoleStr === 'toggle button') {
       aBraille.push({
         string: aState.contains(States.PRESSED) ?
           this._getOutputName('statePressed') :
           this._getOutputName('stateUnpressed')
       });
     }
   }
 };
--- a/accessible/jsat/TraversalRules.jsm
+++ b/accessible/jsat/TraversalRules.jsm
@@ -1,22 +1,20 @@
 /* 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/. */
 
 /* global PrefCache, Roles, Prefilters, States, Filters, Utils,
-   TraversalRules */
+   TraversalRules, Components, XPCOMUtils */
 /* exported TraversalRules */
 
 'use strict';
 
-const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
-const Cr = Components.results;
 
 this.EXPORTED_SYMBOLS = ['TraversalRules']; // jshint ignore:line
 
 Cu.import('resource://gre/modules/accessibility/Utils.jsm');
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Roles',  // jshint ignore:line
   'resource://gre/modules/accessibility/Constants.jsm');
 XPCOMUtils.defineLazyModuleGetter(this, 'Filters',  // jshint ignore:line
@@ -98,17 +96,18 @@ var gSimpleTraversalRoles =
    Roles.HEADING,
    Roles.SLIDER,
    Roles.SPINBUTTON,
    Roles.OPTION,
    Roles.LISTITEM,
    Roles.GRID_CELL,
    Roles.COLUMNHEADER,
    Roles.ROWHEADER,
-   Roles.STATUSBAR];
+   Roles.STATUSBAR,
+   Roles.SWITCH];
 
 var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
   // An object is simple, if it either has a single child lineage,
   // or has a flat subtree.
   function isSingleLineage(acc) {
     for (let child = acc; child; child = child.firstChild) {
       if (Utils.visibleChildCount(child) > 1) {
         return false;
@@ -235,17 +234,18 @@ this.TraversalRules = { // jshint ignore
      Roles.LISTBOX,
      Roles.ENTRY,
      Roles.PASSWORD_TEXT,
      Roles.PAGETAB,
      Roles.RADIOBUTTON,
      Roles.RADIO_MENU_ITEM,
      Roles.SLIDER,
      Roles.CHECKBUTTON,
-     Roles.CHECK_MENU_ITEM]),
+     Roles.CHECK_MENU_ITEM,
+     Roles.SWITCH]),
 
   Graphic: new BaseTraversalRule(
     [Roles.GRAPHIC],
     function Graphic_match(aAccessible) {
       return TraversalRules._shouldSkipImage(aAccessible);
     }),
 
   Heading: new BaseTraversalRule(
@@ -297,17 +297,18 @@ this.TraversalRules = { // jshint ignore
   Separator: new BaseTraversalRule(
     [Roles.SEPARATOR]),
 
   Table: new BaseTraversalRule(
     [Roles.TABLE]),
 
   Checkbox: new BaseTraversalRule(
     [Roles.CHECKBUTTON,
-     Roles.CHECK_MENU_ITEM]),
+     Roles.CHECK_MENU_ITEM,
+     Roles.SWITCH /* A type of checkbox that represents on/off values */]),
 
   _shouldSkipImage: function _shouldSkipImage(aAccessible) {
     if (gSkipEmptyImages.value && aAccessible.name === '') {
       return Filters.IGNORE;
     }
     return Filters.MATCH;
   }
 };
--- a/accessible/tests/mochitest/jsat/doc_content_integration.html
+++ b/accessible/tests/mochitest/jsat/doc_content_integration.html
@@ -50,16 +50,22 @@
     }
 
     function changeSliderValue() {
       document.getElementById('slider').setAttribute('aria-valuenow', '5');
       document.getElementById('slider').setAttribute(
         'aria-valuetext', 'medium');
     }
 
+    function toggleLight() {
+      var lightSwitch = document.getElementById('light');
+      lightSwitch.setAttribute('aria-checked',
+        lightSwitch.getAttribute('aria-checked') === 'true' ? 'false' : 'true');
+    }
+
   </script>
   <style>
     #windows {
       position: relative;
       width: 320px;
       height: 480px;
     }
 
@@ -95,14 +101,15 @@
       <p>Do you agree?</p>
       <button onclick="setTimeout(hideAlert, 500)">Yes</button>
       <button onclick="hideAlert()">No</button>
     </div>
     <div id="appframe"></div>
   </div>
   <button id="home">Home</button>
   <button id="fruit" aria-label="apple"></button>
+  <span id="light" role="switch" aria-label="Light" aria-checked="false" onclick="toggleLight()"></span>
   <div id="live" aria-live="polite" aria-label="live">
     <div id="slider" role="slider" aria-label="slider" aria-valuemin="0"
       aria-valuemax="10"  aria-valuenow="0"></div>
   </div>
 </body>
 </html>
--- a/accessible/tests/mochitest/jsat/doc_traversal.html
+++ b/accessible/tests/mochitest/jsat/doc_traversal.html
@@ -138,10 +138,12 @@
       <tr>
         <td>Dirt</td>
         <td>Messy Stuff</td>
       </tr>
     </tbody>
   </table>
   <div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
   <div aria-label="Last sync: 30min ago" id="statusbar-2" role="status"></div>
+
+  <span id="switch-1" role="switch" aria-checked="false" aria-label="Light switch"></span>
 </body>
 </html>
--- a/accessible/tests/mochitest/jsat/jsatcommon.js
+++ b/accessible/tests/mochitest/jsat/jsatcommon.js
@@ -612,16 +612,28 @@ function ExpectedCheckAction(aChecked, a
   }, [{
     eventType: AndroidEvent.VIEW_CLICKED,
     checked: aChecked
   }], aOptions);
 }
 
 ExpectedCheckAction.prototype = Object.create(ExpectedPresent.prototype);
 
+function ExpectedSwitchAction(aSwitched, aOptions) {
+  ExpectedPresent.call(this, {
+    eventType: 'action',
+    data: [{ string: aSwitched ? 'onAction' : 'offAction' }]
+  }, [{
+    eventType: AndroidEvent.VIEW_CLICKED,
+    checked: aSwitched
+  }], aOptions);
+}
+
+ExpectedSwitchAction.prototype = Object.create(ExpectedPresent.prototype);
+
 function ExpectedNameChange(aName, aOptions) {
   ExpectedPresent.call(this, {
     eventType: 'name-change',
     data: aName
   }, null, aOptions);
 }
 
 ExpectedNameChange.prototype = Object.create(ExpectedPresent.prototype);
--- a/accessible/tests/mochitest/jsat/test_content_integration.html
+++ b/accessible/tests/mochitest/jsat/test_content_integration.html
@@ -53,21 +53,33 @@
            new ExpectedCursorChange(['much range', {'string': 'label'}])],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['much range', '5', {'string': 'slider'}])],
           [ContentMessages.moveOrAdjustUp(), new ExpectedValueChange('6')],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
           [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
+          [ContentMessages.simpleMoveNext,
+           new ExpectedCursorChange(['Light', {"string": "stateOff"}, {'string': 'switch'}])],
+          // switch on
+          [ContentMessages.activateCurrent(),
+           new ExpectedClickAction({ no_android: true }),
+           new ExpectedSwitchAction(true)],
            [ContentMessages.simpleMoveNext,
            new ExpectedCursorChange(['slider', '0', {'string': 'slider'}])],
 
           // Simple traversal backward
           [ContentMessages.simpleMovePrevious,
+           new ExpectedCursorChange(['Light', {"string": "stateOn"}, {'string': 'switch'}])],
+          // switch off
+          [ContentMessages.activateCurrent(),
+           new ExpectedClickAction({ no_android: true }),
+           new ExpectedSwitchAction(false)],
+          [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['apple', {'string': 'pushbutton'}])],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['Home', {'string': 'pushbutton'}])],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['much range', '6', {'string': 'slider'}, 'such app'])],
           [ContentMessages.moveOrAdjustDown(), new ExpectedValueChange('5')],
           [ContentMessages.simpleMovePrevious,
            new ExpectedCursorChange(['much range', {'string': 'label'}])],
--- a/accessible/tests/mochitest/jsat/test_output.html
+++ b/accessible/tests/mochitest/jsat/test_output.html
@@ -483,16 +483,31 @@ https://bugzilla.mozilla.org/show_bug.cg
                               ["Last sync:", "2 days ago"]],
           expectedBraille: [["Last sync:", "2 days ago"],
                             ["Last sync:", "2 days ago"]]
         }, {
           accOrElmOrID: "statusbar-2",
           expectedUtterance: [["Last sync: 30min ago"],
                               ["Last sync: 30min ago"]],
           expectedBraille: [["Last sync: 30min ago"], ["Last sync: 30min ago"]]
+        }, {
+          accOrElmOrID: "switch-1",
+          expectedUtterance: [[{"string": "stateOn"}, {"string": "switch"},
+            "Simple switch"], ["Simple switch", {"string": "stateOn"},
+            {"string": "switch"}]],
+          expectedBraille: [[{"string": "stateCheckedAbbr"}, "Simple switch"],
+            ["Simple switch", {"string": "stateCheckedAbbr"}]]
+        }, {
+          accOrElmOrID: "switch-2",
+          expectedUtterance: [[{"string": "stateOff"},
+            {"string": "switch"}, "Another switch"], ["Another switch",
+            {"string": "stateOff"}, {"string": "switch"}]],
+          expectedBraille: [
+            [{"string": "stateUncheckedAbbr"}, "Another switch"],
+            ["Another switch", {"string": "stateUncheckedAbbr"}]]
         }];
 
         // Test all possible utterance order preference values.
         tests.forEach(function run(test) {
           var utteranceOrderValues = [0, 1];
           utteranceOrderValues.forEach(
             function testUtteranceOrder(utteranceOrder) {
               SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, utteranceOrder);
@@ -640,11 +655,13 @@ https://bugzilla.mozilla.org/show_bug.cg
       </select>
       <select id="labelled-combobox" aria-label="Intervals">
         <option value="15">Every 15 min</option>
         <option value="30">Every 30 min</option>
         <option value="null" selected>Never</option>
       </select>
       <div id="statusbar-1" role="status">Last sync:<span>2 days ago</span></div>
       <div aria-label="Last sync: 30min ago" id="statusbar-2" role="status"></div>
+      <span id="switch-1" role="switch" aria-label="Simple switch" aria-checked="true"></span>
+      <span id="switch-2" role="switch" aria-label="Another switch" aria-checked="false"></span>
     </div>
   </body>
 </html>
--- a/accessible/tests/mochitest/jsat/test_traversal.html
+++ b/accessible/tests/mochitest/jsat/test_traversal.html
@@ -51,28 +51,29 @@
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.FormElement, null,
                              ['input-1-1', 'label-1-2', 'button-1-1',
                               'radio-1-1', 'radio-1-2', 'input-1-3',
                               'input-1-4', 'button-1-2', 'checkbox-1-1',
                               'select-1-1', 'select-1-2', 'checkbox-1-2',
                               'select-1-3', 'input-1-5', 'button-1-3',
                               'button-2-1', 'button-2-2', 'button-2-3',
-                              'button-2-4', 'checkbox-1-5']);
+                              'button-2-4', 'checkbox-1-5', 'switch-1']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Button, null,
                              ['button-1-1', 'button-1-2', 'button-1-3',
                               'button-2-1', 'button-2-2', 'button-2-3',
                               'button-2-4']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.RadioButton, null,
                              ['radio-1-1', 'radio-1-2']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Checkbox, null,
-                             ['checkbox-1-1', 'checkbox-1-2', 'checkbox-1-5']);
+                             ['checkbox-1-1', 'checkbox-1-2', 'checkbox-1-5',
+                              'switch-1']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.Combobox, null,
                              ['select-1-1', 'select-1-2', 'select-1-3']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.List, null,
                              ['list-1', 'list-2', 'list-3']);
 
       queueTraversalSequence(gQueue, docAcc, TraversalRules.ListItem, null,
@@ -117,17 +118,18 @@
                               '4. Standard Lisp', 'link-0', ' Lisp',
                               'checkbox-1-5', ' LeLisp', '• JavaScript',
                               'heading-5', 'image-2', 'image-3',
                               'Not actually an image', 'link-1', 'anchor-1',
                               'link-2', 'anchor-2', 'link-3', '3', '1', '4',
                               '1', 'Sunday', 'M', 'Week 1', '3', '4', '7', '2',
                               '5 8', 'gridcell4', 'Just an innocuous separator',
                               'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
-                              'Dirt', 'Messy Stuff', 'statusbar-1', 'statusbar-2']);
+                              'Dirt', 'Messy Stuff', 'statusbar-1', 'statusbar-2',
+                              'switch-1']);
 
       gQueue.invoke();
     }
 
     SimpleTest.waitForExplicitFinish();
     addLoadEvent(function () {
       /* We open a new browser because we need to test with a top-level content
          document. */
--- a/accessible/windows/ia2/ia2AccessibleAction.cpp
+++ b/accessible/windows/ia2/ia2AccessibleAction.cpp
@@ -19,17 +19,18 @@ using namespace mozilla::a11y;
 STDMETHODIMP
 ia2AccessibleAction::QueryInterface(REFIID iid, void** ppv)
 {
   if (!ppv)
     return E_INVALIDARG;
 
   *ppv = nullptr;
 
-  if (IID_IAccessibleAction == iid) {
+  if (IID_IAccessibleAction == iid &&
+      !static_cast<AccessibleWrap*>(this)->IsProxy()) {
     *ppv = static_cast<IAccessibleAction*>(this);
     (reinterpret_cast<IUnknown*>(*ppv))->AddRef();
     return S_OK;
   }
 
   return E_NOINTERFACE;
 }
 
--- a/accessible/windows/ia2/ia2AccessibleHyperlink.cpp
+++ b/accessible/windows/ia2/ia2AccessibleHyperlink.cpp
@@ -50,17 +50,19 @@ ia2AccessibleHyperlink::get_anchor(long 
   VariantInit(aAnchor);
 
   Accessible* thisObj = static_cast<AccessibleWrap*>(this);
   if (thisObj->IsProxy()) {
     ProxyAccessible* anchor = thisObj->Proxy()->AnchorAt(aIndex);
     if (!anchor)
       return S_FALSE;
 
-    aAnchor->punkVal = static_cast<IAccessibleHyperlink*>(WrapperFor(anchor));
+    IUnknown* tmp = static_cast<IAccessibleHyperlink*>(WrapperFor(anchor));
+    tmp->AddRef();
+    aAnchor->punkVal = tmp;
     aAnchor->vt = VT_UNKNOWN;
     return S_OK;
   }
 
   if (thisObj->IsDefunct())
     return CO_E_OBJNOTCONNECTED;
 
   if (aIndex < 0 || aIndex >= static_cast<long>(thisObj->AnchorCount()))
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -110,56 +110,52 @@ AccessibleWrap::QueryInterface(REFIID ii
 
   if (!ppv)
     return E_INVALIDARG;
 
   *ppv = nullptr;
 
   if (IID_IUnknown == iid)
     *ppv = static_cast<IAccessible*>(this);
-
-  if (!*ppv && IsProxy())
-    return E_NOINTERFACE;
-
-  if (IID_IDispatch == iid || IID_IAccessible == iid)
+  else if (IID_IDispatch == iid || IID_IAccessible == iid)
     *ppv = static_cast<IAccessible*>(this);
-  else if (IID_IEnumVARIANT == iid) {
+  else if (IID_IEnumVARIANT == iid && !IsProxy()) {
     // Don't support this interface for leaf elements.
     if (!HasChildren() || nsAccUtils::MustPrune(this))
       return E_NOINTERFACE;
 
     *ppv = static_cast<IEnumVARIANT*>(new ChildrenEnumVariant(this));
-  } else if (IID_IServiceProvider == iid)
+  } else if (IID_IServiceProvider == iid && !IsProxy())
     *ppv = new ServiceProvider(this);
-  else if (IID_ISimpleDOMNode == iid) {
+  else if (IID_ISimpleDOMNode == iid && !IsProxy()) {
     if (IsDefunct() || (!HasOwnContent() && !IsDoc()))
       return E_NOINTERFACE;
 
     *ppv = static_cast<ISimpleDOMNode*>(new sdnAccessible(GetNode()));
   }
 
   if (nullptr == *ppv) {
     HRESULT hr = ia2Accessible::QueryInterface(iid, ppv);
     if (SUCCEEDED(hr))
       return hr;
   }
 
-  if (nullptr == *ppv) {
+  if (nullptr == *ppv && !IsProxy()) {
     HRESULT hr = ia2AccessibleComponent::QueryInterface(iid, ppv);
     if (SUCCEEDED(hr))
       return hr;
   }
 
   if (nullptr == *ppv) {
     HRESULT hr = ia2AccessibleHyperlink::QueryInterface(iid, ppv);
     if (SUCCEEDED(hr))
       return hr;
   }
 
-  if (nullptr == *ppv) {
+  if (nullptr == *ppv && !IsProxy()) {
     HRESULT hr = ia2AccessibleValue::QueryInterface(iid, ppv);
     if (SUCCEEDED(hr))
       return hr;
   }
 
   if (nullptr == *ppv)
     return E_NOINTERFACE;
 
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -345,16 +345,17 @@ var shell = {
 
     window.addEventListener('MozApplicationManifest', this);
     window.addEventListener('MozAfterPaint', this);
     window.addEventListener('sizemodechange', this);
     window.addEventListener('unload', this);
     this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.addEventListener('mozbrowserselectionstatechanged', this, true);
     this.contentBrowser.addEventListener('mozbrowserscrollviewchange', this, true);
+    this.contentBrowser.addEventListener('mozbrowsercaretstatechanged', this);
 
     CustomEventManager.init();
     WebappsHelper.init();
     UserAgentOverrides.init();
     CaptivePortalLoginHelper.init();
 
     this.contentBrowser.src = homeURL;
     this.isHomeLoaded = false;
@@ -375,16 +376,17 @@ var shell = {
     window.removeEventListener('unload', this);
     window.removeEventListener('keydown', this, true);
     window.removeEventListener('keyup', this, true);
     window.removeEventListener('MozApplicationManifest', this);
     window.removeEventListener('sizemodechange', this);
     this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
     this.contentBrowser.removeEventListener('mozbrowserselectionstatechanged', this, true);
     this.contentBrowser.removeEventListener('mozbrowserscrollviewchange', this, true);
+    this.contentBrowser.removeEventListener('mozbrowsercaretstatechanged', this);
     ppmm.removeMessageListener("content-handler", this);
 
     UserAgentOverrides.uninit();
   },
 
   // If this key event represents a hardware button which needs to be send as
   // a message, broadcasts it with the message set to 'xxx-button-press' or
   // 'xxx-button-release'.
@@ -485,16 +487,38 @@ var shell = {
         data.offsetY = offsetY;
 
         DoCommandHelper.setEvent(evt);
         shell.sendChromeEvent({
           type: 'selectionstatechanged',
           detail: data,
         });
         break;
+      case 'mozbrowsercaretstatechanged':
+        {
+          let elt = evt.target;
+          let win = elt.ownerDocument.defaultView;
+          let offsetX = win.mozInnerScreenX - window.mozInnerScreenX;
+          let offsetY = win.mozInnerScreenY - window.mozInnerScreenY;
+
+          let rect = elt.getBoundingClientRect();
+          offsetX += rect.left;
+          offsetY += rect.top;
+
+          let data = evt.detail;
+          data.offsetX = offsetX;
+          data.offsetY = offsetY;
+          data.sendDoCommandMsg = null;
+
+          shell.sendChromeEvent({
+            type: 'caretstatechanged',
+            detail: data,
+          });
+        }
+        break;
 
       case 'MozApplicationManifest':
         try {
           if (!Services.prefs.getBoolPref('browser.cache.offline.enable'))
             return;
 
           let contentWindow = evt.originalTarget.defaultView;
           let documentElement = contentWindow.document.documentElement;
@@ -713,16 +737,20 @@ var CustomEventManager = {
       case 'inputmethod-update-layouts':
       case 'inputregistry-add':
       case 'inputregistry-remove':
         KeyboardHelper.handleEvent(detail);
         break;
       case 'do-command':
         DoCommandHelper.handleEvent(detail.cmd);
         break;
+      case 'copypaste-do-command':
+        Services.obs.notifyObservers({ wrappedJSObject: shell.contentBrowser },
+                                     'ask-children-to-execute-copypaste-command', detail.cmd);
+        break;
     }
   }
 }
 
 let DoCommandHelper = {
   _event: null,
   setEvent: function docommand_setEvent(evt) {
     this._event = evt;
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1193,19 +1193,20 @@ pref("browser.tabs.remote.desktopbehavio
 pref("security.sandbox.windows.log", false);
 
 // Controls whether and how the Windows NPAPI plugin process is sandboxed.
 // To get a different setting for a particular plugin replace "default", with
 // the plugin's nice file name, see: nsPluginTag::GetNiceFileName.
 // On windows these levels are:
 // 0 - no sandbox
 // 1 - sandbox with USER_NON_ADMIN access token level
-// 2 - a more strict sandbox, which might cause functionality issues
+// 2 - a more strict sandbox, which might cause functionality issues. This now
+//     includes running at low integrity.
 // 3 - the strongest settings we seem to be able to use without breaking
-//     everything, but will definitely cause some functionality restrictions
+//     everything, but will probably cause some functionality restrictions
 pref("dom.ipc.plugins.sandbox-level.default", 0);
 
 #if defined(MOZ_CONTENT_SANDBOX)
 // This controls the strength of the Windows content process sandbox for testing
 // purposes. This will require a restart.
 // On windows these levels are:
 // 0 - sandbox with USER_NON_ADMIN access token level
 // 1 - level 0 plus low integrity
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -524,36 +524,28 @@ AutoJSAPI::StealException(JS::MutableHan
 AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
                                  const char *aReason,
                                  bool aIsMainThread,
                                  JSContext* aCx)
   : AutoJSAPI(aGlobalObject, aIsMainThread,
               aCx ? aCx : FindJSContext(aGlobalObject))
   , ScriptSettingsStackEntry(aGlobalObject, /* aCandidate = */ true)
   , mWebIDLCallerPrincipal(nullptr)
-  , mIsMainThread(aIsMainThread)
 {
   MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT_IF(!aCx, aIsMainThread); // cx is mandatory off-main-thread.
   MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == FindJSContext(aGlobalObject));
-  if (aIsMainThread) {
-    nsContentUtils::EnterMicroTask();
-  }
 
   if (aIsMainThread && gRunToCompletionListeners > 0) {
     mDocShellEntryMonitor.emplace(cx(), aReason);
   }
 }
 
 AutoEntryScript::~AutoEntryScript()
 {
-  if (mIsMainThread) {
-    nsContentUtils::LeaveMicroTask();
-  }
-
   // GC when we pop a script entry point. This is a useful heuristic that helps
   // us out on certain (flawed) benchmarks like sunspider, because it lets us
   // avoid GCing during the timing loop.
   JS_MaybeGC(cx());
 }
 
 AutoEntryScript::DocshellEntryMonitor::DocshellEntryMonitor(JSContext* aCx,
                                                             const char* aReason)
@@ -585,17 +577,20 @@ AutoEntryScript::DocshellEntryMonitor::E
   nsCOMPtr<nsIDocShell> docShellForJSRunToCompletion = window->GetDocShell();
   nsString filename;
   uint32_t lineNumber = 0;
 
   js::AutoStableStringChars functionName(aCx);
   if (rootedFunction) {
     JS::Rooted<JSString*> displayId(aCx, JS_GetFunctionDisplayId(rootedFunction));
     if (displayId) {
-      functionName.initTwoByte(aCx, displayId);
+      if (!functionName.initTwoByte(aCx, displayId)) {
+        JS_ClearPendingException(aCx);
+        return;
+      }
     }
   }
 
   if (!rootedScript) {
     rootedScript = JS_GetFunctionScript(aCx, rootedFunction);
   }
   if (rootedScript) {
     filename = NS_ConvertUTF8toUTF16(JS_GetScriptFilename(rootedScript));
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -370,18 +370,16 @@ private:
   // the aIsJSImplementedWebIDL case.  And in that case, the subject principal
   // is the principal of the callee function that is part of the CallArgs just a
   // bit up the stack, and which will outlive us.  So we know the principal
   // can't go away until then either.
   nsIPrincipal* MOZ_NON_OWNING_REF mWebIDLCallerPrincipal;
   friend nsIPrincipal* GetWebIDLCallerPrincipal();
 
   Maybe<DocshellEntryMonitor> mDocShellEntryMonitor;
-
-  bool mIsMainThread;
 };
 
 /*
  * A class that can be used to force a particular incumbent script on the stack.
  */
 class AutoIncumbentScript : protected ScriptSettingsStackEntry {
 public:
   explicit AutoIncumbentScript(nsIGlobalObject* aGlobalObject);
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -214,16 +214,17 @@ nsJSUtils::EvaluateString(JSContext* aCx
   // Unfortunately, the JS engine actually compiles scripts with a return value
   // in a different, less efficient way.  Furthermore, it can't JIT them in many
   // cases.  So we need to be explicitly told whether the caller cares about the
   // return value.  Callers can do this by calling the other overload of
   // EvaluateString() which calls this function with
   // aCompileOptions.noScriptRval set to true.
   aRetValue.setUndefined();
 
+  nsAutoMicroTask mt;
   nsresult rv = NS_OK;
 
   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   NS_ENSURE_TRUE(ssm->ScriptAllowed(aEvaluationGlobal), NS_OK);
 
   mozilla::Maybe<AutoDontReportUncaught> dontReport;
   if (!aEvaluateOptions.reportUncaught) {
     // We need to prevent AutoLastFrameCheck from reporting and clearing
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -1205,24 +1205,28 @@ nsScriptLoader::ProcessPendingRequests()
   }
 
   while (!mPendingChildLoaders.IsEmpty() && ReadyToExecuteScripts()) {
     nsRefPtr<nsScriptLoader> child = mPendingChildLoaders[0];
     mPendingChildLoaders.RemoveElementAt(0);
     child->RemoveExecuteBlocker();
   }
 
+  if (mDocumentParsingDone && mDocument && !mParserBlockingRequest &&
+      mNonAsyncExternalScriptInsertedRequests.isEmpty() &&
+      mXSLTRequests.isEmpty() && mDeferRequests.isEmpty() &&
+      MaybeRemovedDeferRequests()) {
+    return ProcessPendingRequests();
+  }
+
   if (mDocumentParsingDone && mDocument &&
       !mParserBlockingRequest && mLoadingAsyncRequests.isEmpty() &&
       mLoadedAsyncRequests.isEmpty() &&
       mNonAsyncExternalScriptInsertedRequests.isEmpty() &&
       mXSLTRequests.isEmpty() && mDeferRequests.isEmpty()) {
-    if (MaybeRemovedDeferRequests()) {
-      return ProcessPendingRequests();
-    }
     // No more pending scripts; time to unblock onload.
     // OK to unblock onload synchronously here, since callers must be
     // prepared for the world changing anyway.
     mDocumentParsingDone = false;
     mDocument->UnblockOnload(true);
   }
 }
 
--- a/dom/base/nsStyleLinkElement.cpp
+++ b/dom/base/nsStyleLinkElement.cpp
@@ -45,17 +45,17 @@ nsStyleLinkElement::nsStyleLinkElement()
 nsStyleLinkElement::~nsStyleLinkElement()
 {
   nsStyleLinkElement::SetStyleSheet(nullptr);
 }
 
 void
 nsStyleLinkElement::Unlink()
 {
-  mStyleSheet = nullptr;
+  nsStyleLinkElement::SetStyleSheet(nullptr);
 }
 
 void
 nsStyleLinkElement::Traverse(nsCycleCollectionTraversalCallback &cb)
 {
   nsStyleLinkElement* tmp = this;
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStyleSheet);
 }
--- a/dom/base/test/TestCSPParser.cpp
+++ b/dom/base/test/TestCSPParser.cpp
@@ -206,17 +206,19 @@ nsresult TestDirectives() {
       "font-src http://www.example.com" },
     { "connect-src http://www.example.com",
       "connect-src http://www.example.com" },
     { "report-uri http://www.example.com",
       "report-uri http://www.example.com/" },
     { "script-src 'nonce-correctscriptnonce'",
       "script-src 'nonce-correctscriptnonce'" },
     { "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='",
-      "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" }
+      "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" },
+    { "referrer no-referrer",
+      "referrer no-referrer" }
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
   return runTestSuite(policies, policyCount, 1);
 }
 
 // ============================= TestKeywords ========================
 
@@ -268,17 +270,19 @@ nsresult TestIgnoreUpperLowerCasePolicie
       "default-src https://*.example.com" },
     { "script-src 'none' test.com;",
       "script-src http://test.com" },
     { "script-src 'NoNCE-correctscriptnonce'",
       "script-src 'nonce-correctscriptnonce'" },
     { "script-src 'NoncE-NONCENEEDSTOBEUPPERCASE'",
       "script-src 'nonce-NONCENEEDSTOBEUPPERCASE'" },
     { "script-src 'SHA256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='",
-      "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" }
+      "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" },
+    { "refERRer No-refeRRer",
+      "referrer No-refeRRer" }
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
   return runTestSuite(policies, policyCount, 1);
 }
 
 // ============================= TestPaths ========================
 
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1451,16 +1451,21 @@ DOMInterfaces = {
     'headerFile': 'WebGLExtensions.h'
 },
 
 'WebGLExtensionBlendMinMax': {
     'nativeType': 'mozilla::WebGLExtensionBlendMinMax',
     'headerFile': 'WebGLExtensions.h'
 },
 
+'WebGLExtensionDisjointTimerQuery': {
+    'nativeType': 'mozilla::WebGLExtensionDisjointTimerQuery',
+    'headerFile': 'WebGLExtensions.h'
+},
+
 'WebGLFramebuffer': {
     'nativeType': 'mozilla::WebGLFramebuffer',
     'headerFile': 'WebGLFramebuffer.h'
 },
 
 'WebGLProgram': {
     'nativeType': 'mozilla::WebGLProgram',
     'headerFile': 'WebGLProgram.h'
@@ -1509,16 +1514,21 @@ DOMInterfaces = {
     'headerFile': 'WebGLSync.h'
 },
 
 'WebGLTexture': {
     'nativeType': 'mozilla::WebGLTexture',
     'headerFile': 'WebGLTexture.h'
 },
 
+'WebGLTimerQuery': {
+    'nativeType': 'mozilla::WebGLTimerQuery',
+    'headerFile': 'WebGLTimerQuery.h'
+},
+
 'WebGLTransformFeedback': {
     'nativeType': 'mozilla::WebGLTransformFeedback',
     'headerFile': 'WebGLTransformFeedback.h'
 },
 
 'WebGLUniformLocation': {
     'nativeType': 'mozilla::WebGLUniformLocation',
     'headerFile': 'WebGLUniformLocation.h'
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -57,16 +57,20 @@ CallbackObject::CallSetup::CallSetup(Cal
                                      JSCompartment* aCompartment,
                                      bool aIsJSImplementedWebIDL)
   : mCx(nullptr)
   , mCompartment(aCompartment)
   , mErrorResult(aRv)
   , mExceptionHandling(aExceptionHandling)
   , mIsMainThread(NS_IsMainThread())
 {
+  if (mIsMainThread) {
+    nsContentUtils::EnterMicroTask();
+  }
+
   // Compute the caller's subject principal (if necessary) early, before we
   // do anything that might perturb the relevant state.
   nsIPrincipal* webIDLCallerPrincipal = nullptr;
   if (aIsJSImplementedWebIDL) {
     webIDLCallerPrincipal = nsContentUtils::SubjectPrincipal();
   }
 
   // We need to produce a useful JSContext here.  Ideally one that the callback
@@ -297,16 +301,22 @@ CallbackObject::CallSetup::~CallSetup()
       if (saved) {
         JS_RestoreFrameChain(mCx);
       }
     }
   }
 
   mAutoIncumbentScript.reset();
   mAutoEntryScript.reset();
+
+  // It is important that this is the last thing we do, after leaving the
+  // compartment and undoing all our entry/incumbent script changes
+  if (mIsMainThread) {
+    nsContentUtils::LeaveMicroTask();
+  }
 }
 
 already_AddRefed<nsISupports>
 CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
                                           const nsIID& aIID) const
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!aCallback) {
--- a/dom/browser-element/BrowserElementChild.js
+++ b/dom/browser-element/BrowserElementChild.js
@@ -29,22 +29,25 @@ function isTopBrowserElement(docShell) {
     if (docShell && docShell.isBrowserOrApp) {
       return false;
     }
   }
   return true;
 }
 
 if (!('BrowserElementIsPreloaded' in this)) {
-  if (isTopBrowserElement(docShell) &&
-      Services.prefs.getBoolPref("dom.mozInputMethod.enabled")) {
-    try {
-      Services.scriptloader.loadSubScript("chrome://global/content/forms.js");
-    } catch (e) {
+  if (isTopBrowserElement(docShell)) {
+    if (Services.prefs.getBoolPref("dom.mozInputMethod.enabled")) {
+      try {
+        Services.scriptloader.loadSubScript("chrome://global/content/forms.js");
+      } catch (e) {
+      }
     }
+
+    Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementCopyPaste.js");
   }
 
   if (Services.prefs.getIntPref("dom.w3c_touch_events.enabled") == 1) {
     if (docShell.asyncPanZoomEnabled === false) {
       Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanningAPZDisabled.js");
       ContentPanningAPZDisabled.init();
     }
 
new file mode 100644
--- /dev/null
+++ b/dom/browser-element/BrowserElementCopyPaste.js
@@ -0,0 +1,90 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- /
+/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
+/* 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/. */
+
+"use strict";
+
+dump("###################################### BrowserElementCopyPaste.js loaded\n");
+
+let CopyPasteAssistent = {
+  COMMAND_MAP: {
+    'cut': 'cmd_cut',
+    'copy': 'cmd_copyAndCollapseToEnd',
+    'paste': 'cmd_paste',
+    'selectall': 'cmd_selectAll'
+  },
+
+  init: function() {
+    addEventListener('mozcaretstatechanged',
+                     this._caretStateChangedHandler.bind(this),
+                     /* useCapture = */ true,
+                     /* wantsUntrusted = */ false);
+    addMessageListener('browser-element-api:call', this._browserAPIHandler.bind(this));
+  },
+
+  _browserAPIHandler: function(e) {
+    switch (e.data.msg_name) {
+      case 'copypaste-do-command':
+        if (this._isCommandEnabled(e.data.command)) {
+          docShell.doCommand(COMMAND_MAP[e.data.command]);
+        }
+        break;
+    }
+  },
+
+  _isCommandEnabled: function(cmd) {
+    let command = this.COMMAND_MAP[cmd];
+    if (!command) {
+      return false;
+    }
+
+    return docShell.isCommandEnabled(command);
+  },
+
+  _caretStateChangedHandler: function(e) {
+    e.stopPropagation();
+
+    let boundingClientRect = e.boundingClientRect;
+    let canPaste = this._isCommandEnabled("paste");
+    let zoomFactor = content.innerWidth == 0 ? 1 : content.screen.width / content.innerWidth;
+
+    let detail = {
+      rect: {
+        width: boundingClientRect ? boundingClientRect.width : 0,
+        height: boundingClientRect ? boundingClientRect.height : 0,
+        top: boundingClientRect ? boundingClientRect.top : 0,
+        bottom: boundingClientRect ? boundingClientRect.bottom : 0,
+        left: boundingClientRect ? boundingClientRect.left : 0,
+        right: boundingClientRect ? boundingClientRect.right : 0,
+      },
+      commands: {
+        canSelectAll: this._isCommandEnabled("selectall"),
+        canCut: this._isCommandEnabled("cut"),
+        canCopy: this._isCommandEnabled("copy"),
+        canPaste: this._isCommandEnabled("paste"),
+      },
+      zoomFactor: zoomFactor,
+      reason: e.reason,
+      collapsed: e.collapsed,
+      caretVisible: e.caretVisible,
+      selectionVisible: e.selectionVisible
+    };
+
+    // Get correct geometry information if we have nested iframe.
+    let currentWindow = e.target.defaultView;
+    while (currentWindow.realFrameElement) {
+      let currentRect = currentWindow.realFrameElement.getBoundingClientRect();
+      detail.rect.top += currentRect.top;
+      detail.rect.bottom += currentRect.top;
+      detail.rect.left += currentRect.left;
+      detail.rect.right += currentRect.left;
+      currentWindow = currentWindow.realFrameElement.ownerDocument.defaultView;
+    }
+
+    sendAsyncMsg('caretstatechanged', detail);
+  },
+};
+
+CopyPasteAssistent.init();
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -82,16 +82,17 @@ function BrowserElementParent() {
   this._domRequestReady = false;
   this._pendingAPICalls = [];
   this._pendingDOMRequests = {};
   this._pendingSetInputMethodActive = [];
   this._nextPaintListeners = [];
 
   Services.obs.addObserver(this, 'oop-frameloader-crashed', /* ownsWeak = */ true);
   Services.obs.addObserver(this, 'copypaste-docommand', /* ownsWeak = */ true);
+  Services.obs.addObserver(this, 'ask-children-to-execute-copypaste-command', /* ownsWeak = */ true);
 }
 
 BrowserElementParent.prototype = {
 
   classDescription: "BrowserElementAPI implementation",
   classID: Components.ID("{9f171ac4-0939-4ef8-b360-3408aedc3060}"),
   contractID: "@mozilla.org/dom/browser-element-api;1",
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserElementAPI,
@@ -198,16 +199,17 @@ BrowserElementParent.prototype = {
       "entered-dom-fullscreen": this._enteredDomFullscreen,
       "fullscreen-origin-change": this._fullscreenOriginChange,
       "exited-dom-fullscreen": this._exitedDomFullscreen,
       "got-visible": this._gotDOMRequestResult,
       "visibilitychange": this._childVisibilityChange,
       "got-set-input-method-active": this._gotDOMRequestResult,
       "selectionstatechanged": this._handleSelectionStateChanged,
       "scrollviewchange": this._handleScrollViewChange,
+      "caretstatechanged": this._handleCaretStateChanged,
     };
 
     let mmSecuritySensitiveCalls = {
       "showmodalprompt": this._handleShowModalPrompt,
       "contextmenu": this._fireCtxMenuEvent,
       "securitychange": this._fireEventFromMsg,
       "locationchange": this._fireEventFromMsg,
       "iconchange": this._fireEventFromMsg,
@@ -433,16 +435,44 @@ BrowserElementParent.prototype = {
   },
 
   _handleSelectionStateChanged: function(data) {
     let evt = this._createEvent('selectionstatechanged', data.json,
                                 /* cancelable = */ false);
     this._frameElement.dispatchEvent(evt);
   },
 
+  // Called when state of accessible caret in child has changed.
+  // The fields of data is as following:
+  //  - rect: Contains bounding rectangle of selection, Include width, height,
+  //          top, bottom, left and right.
+  //  - commands: Describe what commands can be executed in child. Include canSelectAll,
+  //              canCut, canCopy and canPaste. For example: if we want to check if cut
+  //              command is available, using following code, if (data.commands.canCut) {}.
+  //  - zoomFactor: Current zoom factor in child frame.
+  //  - reason: The reason causes the state changed. Include "visibilitychange",
+  //            "updateposition", "longpressonemptycontent", "taponcaret", "presscaret",
+  //            "releasecaret".
+  //  - collapsed: Indicate current selection is collapsed or not.
+  //  - caretVisible: Indicate the caret visiibility.
+  //  - selectionVisible: Indicate current selection is visible or not.
+  _handleCaretStateChanged: function(data) {
+    let evt = this._createEvent('caretstatechanged', data.json,
+                                /* cancelable = */ false);
+
+    let self = this;
+    function sendDoCommandMsg(cmd) {
+      let data = { command: cmd };
+      self._sendAsyncMsg('copypaste-do-command', data);
+    }
+    Cu.exportFunction(sendDoCommandMsg, evt.detail, { defineAs: 'sendDoCommandMsg' });
+
+    this._frameElement.dispatchEvent(evt);
+  },
+
   _handleScrollViewChange: function(data) {
     let evt = this._createEvent("scrollviewchange", data.json,
                                 /* cancelable = */ false);
     this._frameElement.dispatchEvent(evt);
   },
 
   _createEvent: function(evtName, detail, cancelable) {
     // This will have to change if we ever want to send a CustomEvent with null
@@ -974,16 +1004,21 @@ BrowserElementParent.prototype = {
         this._fireFatalError();
       }
       break;
     case 'copypaste-docommand':
       if (this._isAlive() && this._frameElement.isEqualNode(subject.wrappedJSObject)) {
         this._sendAsyncMsg('do-command', { command: data });
       }
       break;
+    case 'ask-children-to-execute-copypaste-command':
+      if (this._isAlive() && this._frameElement == subject.wrappedJSObject) {
+        this._sendAsyncMsg('copypaste-do-command', { command: data });
+      }
+      break;
     default:
       debug('Unknown topic: ' + topic);
       break;
     };
   },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([BrowserElementParent]);
--- a/dom/browser-element/mochitest/browserElementTestHelpers.js
+++ b/dom/browser-element/mochitest/browserElementTestHelpers.js
@@ -60,16 +60,20 @@ const browserElementTestHelpers = {
   setEnabledPref: function(value) {
     this._setPref('dom.mozBrowserFramesEnabled', value);
   },
 
   setSelectionChangeEnabledPref: function(value) {
     this._setPref('selectioncaret.enabled', value);
   },
 
+  setAccessibleCaretEnabledPref: function(value) {
+    this._setPref('layout.accessiblecaret.enabled', value);
+  },
+
   getOOPByDefaultPref: function() {
     return this._getBoolPref("dom.ipc.browser_frames.oop_by_default");
   },
 
   addPermission: function() {
     this.lockTestReady();
     SpecialPowers.pushPermissions(
       [{'type': "browser", 'allow': 1, 'context': document}],
--- a/dom/browser-element/mochitest/browserElement_CopyPaste.js
+++ b/dom/browser-element/mochitest/browserElement_CopyPaste.js
@@ -1,31 +1,34 @@
 /* Any copyright is dedicated to the public domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test that "cut, copy, paste, selectall" and selectionstatechanged event works from inside an <iframe mozbrowser>.
 "use strict";
 
 SimpleTest.waitForExplicitFinish();
 SimpleTest.requestFlakyTimeout("untriaged");
+SimpleTest.requestLongerTimeout(2); // slow on android
 browserElementTestHelpers.setEnabledPref(true);
-browserElementTestHelpers.setSelectionChangeEnabledPref(true);
+browserElementTestHelpers.setSelectionChangeEnabledPref(false);
+browserElementTestHelpers.setAccessibleCaretEnabledPref(true);
 browserElementTestHelpers.addPermission();
 const { Services } = SpecialPowers.Cu.import('resource://gre/modules/Services.jsm');
 
 var gTextarea = null;
 var mm;
 var iframeOuter;
 var iframeInner;
 var state = 0;
 var stateMeaning;
 var defaultData;
 var pasteData;
 var focusScript;
 var createEmbededFrame = false;
+var testSelectionChange = false;
 
 function copyToClipboard(str) {
   gTextarea.value = str;
   SpecialPowers.wrap(gTextarea).editor.selectAll();
   SpecialPowers.wrap(gTextarea).editor.copy();
 }
 
 function getScriptForGetContent() {
@@ -84,16 +87,24 @@ function runTest() {
   });
 }
 
 function doCommand(cmd) {
   Services.obs.notifyObservers({wrappedJSObject: SpecialPowers.unwrap(iframeInner)},
                                'copypaste-docommand', cmd);
 }
 
+function rerunTest() {
+  // clean up and run test again.
+  document.body.removeChild(iframeOuter);
+  document.body.removeChild(gTextarea);
+  state = 0;
+  runTest();
+}
+
 function dispatchTest(e) {
   iframeInner.addEventListener("mozbrowserloadend", function onloadend2(e) {
     iframeInner.removeEventListener("mozbrowserloadend", onloadend2);
     iframeInner.focus();
     SimpleTest.executeSoon(function() { testSelectAll(e); });
   });
 
   switch (state) {
@@ -155,47 +166,58 @@ function dispatchTest(e) {
                    "</body>" +
                    "<script>document.designMode='on';</script>" +
                    "</html>";
       stateMeaning = " (test: normal div with designMode:on)";
       focusScript = "var elt=content.document.getElementById('text');elt.focus();";
       break;
     default:
       if (createEmbededFrame || browserElementTestHelpers.getOOPByDefaultPref()) {
-        SimpleTest.finish();
+        if (testSelectionChange) {
+          SimpleTest.finish();
+          return;
+        } else {
+          testSelectionChange = true;
+          createEmbededFrame = false;
+          SpecialPowers.pushPrefEnv(
+            {'set':
+              [['selectioncaret.enabled', true],
+               ['layout.accessiblecaret.enabled', false]]},
+            function() {
+              rerunTest();
+          });
+        }
       } else {
         createEmbededFrame = true;
-
-        // clean up and run test again.
-        document.body.removeChild(iframeOuter);
-        document.body.removeChild(gTextarea);
-        state = 0;
-        runTest();
+        rerunTest();
       }
       break;
   }
 }
 
 function isChildProcess() {
   return SpecialPowers.Cc["@mozilla.org/xre/app-info;1"]
                          .getService(SpecialPowers.Ci.nsIXULRuntime)
                          .processType != SpecialPowers.Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
 }
 
 function testSelectAll(e) {
   // Skip mozbrowser test if we're at child process.
   if (!isChildProcess()) {
-    iframeOuter.addEventListener("mozbrowserselectionstatechanged", function selectchangeforselectall(e) {
-      if (e.detail.states.indexOf('selectall') == 0) {
-        iframeOuter.removeEventListener("mozbrowserselectionstatechanged", selectchangeforselectall, true);
+    let eventName = testSelectionChange ? "mozbrowserselectionstatechanged" : "mozbrowsercaretstatechanged";
+    iframeOuter.addEventListener(eventName, function selectchangeforselectall(e) {
+      if (!e.detail.states || e.detail.states.indexOf('selectall') == 0) {
+        iframeOuter.removeEventListener(eventName, selectchangeforselectall, true);
         ok(true, "got mozbrowserselectionstatechanged event." + stateMeaning);
         ok(e.detail, "event.detail is not null." + stateMeaning);
         ok(e.detail.width != 0, "event.detail.width is not zero" + stateMeaning);
         ok(e.detail.height != 0, "event.detail.height is not zero" + stateMeaning);
-        ok(e.detail.states, "event.detail.state " + e.detail.states);
+        if (testSelectionChange) {
+          ok(e.detail.states, "event.detail.state " + e.detail.states);
+        }
         SimpleTest.executeSoon(function() { testCopy1(e); });
       }
     }, true);
   }
 
   mm.addMessageListener('content-focus', function messageforfocus(msg) {
     mm.removeMessageListener('content-focus', messageforfocus);
     // test selectall command, after calling this the selectionstatechanged event should be fired.
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -165,16 +165,17 @@ class WebGLContext
 {
     friend class WebGL2Context;
     friend class WebGLContextUserData;
     friend class WebGLExtensionCompressedTextureATC;
     friend class WebGLExtensionCompressedTextureETC1;
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionDepthTexture;
+    friend class WebGLExtensionDisjointTimerQuery;
     friend class WebGLExtensionDrawBuffers;
     friend class WebGLExtensionLoseContext;
     friend class WebGLExtensionVertexArray;
     friend class WebGLMemoryTracker;
     friend class WebGLObserver;
 
     enum {
         UNPACK_FLIP_Y_WEBGL = 0x9240,
@@ -1596,16 +1597,17 @@ public:
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
     friend class WebGLBuffer;
     friend class WebGLSampler;
     friend class WebGLShader;
     friend class WebGLSync;
+    friend class WebGLTimerQuery;
     friend class WebGLTransformFeedback;
     friend class WebGLUniformLocation;
     friend class WebGLVertexArray;
     friend class WebGLVertexArrayFake;
     friend class WebGLVertexArrayGL;
 };
 
 // used by DOM bindings in conjunction with GetParentObject
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -31,16 +31,17 @@ WebGLContext::GetExtensionString(WebGLEx
 
         WEBGL_EXTENSION_IDENTIFIER(ANGLE_instanced_arrays)
         WEBGL_EXTENSION_IDENTIFIER(EXT_blend_minmax)
         WEBGL_EXTENSION_IDENTIFIER(EXT_color_buffer_half_float)
         WEBGL_EXTENSION_IDENTIFIER(EXT_frag_depth)
         WEBGL_EXTENSION_IDENTIFIER(EXT_shader_texture_lod)
         WEBGL_EXTENSION_IDENTIFIER(EXT_sRGB)
         WEBGL_EXTENSION_IDENTIFIER(EXT_texture_filter_anisotropic)
+        WEBGL_EXTENSION_IDENTIFIER(EXT_disjoint_timer_query)
         WEBGL_EXTENSION_IDENTIFIER(OES_element_index_uint)
         WEBGL_EXTENSION_IDENTIFIER(OES_standard_derivatives)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_vertex_array_object)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_color_buffer_float)
@@ -174,23 +175,23 @@ WebGLContext::IsExtensionSupported(WebGL
     default:
         // For warnings-as-errors.
         break;
     }
 
     if (Preferences::GetBool("webgl.enable-draft-extensions", false) ||
         IsWebGL2())
     {
-        /* None for now.
         switch (ext) {
+        case WebGLExtensionID::EXT_disjoint_timer_query:
+            return WebGLExtensionDisjointTimerQuery::IsSupported(this);
         default:
             // For warnings-as-errors.
             break;
         }
-        */
     }
 
     return false;
 }
 
 static bool
 CompareWebGLExtensionName(const nsACString& name, const char* other)
 {
@@ -307,16 +308,19 @@ WebGLContext::EnableExtension(WebGLExten
 
     // EXT_
     case WebGLExtensionID::EXT_blend_minmax:
         obj = new WebGLExtensionBlendMinMax(this);
         break;
     case WebGLExtensionID::EXT_color_buffer_half_float:
         obj = new WebGLExtensionColorBufferHalfFloat(this);
         break;
+    case WebGLExtensionID::EXT_disjoint_timer_query:
+        obj = new WebGLExtensionDisjointTimerQuery(this);
+        break;
     case WebGLExtensionID::EXT_frag_depth:
         obj = new WebGLExtensionFragDepth(this);
         break;
     case WebGLExtensionID::EXT_shader_texture_lod:
         obj = new WebGLExtensionShaderTextureLod(this);
         break;
     case WebGLExtensionID::EXT_sRGB:
         obj = new WebGLExtensionSRGB(this);
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -172,16 +172,31 @@ WebGLContext::GetParameter(JSContext* cx
             if (mBoundVertexArray == mDefaultVertexArray){
                 return WebGLObjectAsJSValue(cx, (WebGLVertexArray *) nullptr, rv);
             }
 
             return WebGLObjectAsJSValue(cx, mBoundVertexArray.get(), rv);
         }
     }
 
+    if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
+        if (pname == LOCAL_GL_TIMESTAMP_EXT) {
+            GLuint64 iv = 0;
+            gl->fGetInteger64v(pname, (GLint64*) &iv);
+            return JS::NumberValue(uint64_t(iv));
+        } else if (pname == LOCAL_GL_GPU_DISJOINT_EXT) {
+            // When disjoint isn't supported, leave as false.
+            realGLboolean disjoint = 0;
+            if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) {
+                gl->fGetBooleanv(pname, &disjoint);
+            }
+            return JS::BooleanValue(bool(disjoint));
+        }
+    }
+
     if (IsWebGL2()) {
         switch (pname) {
             case LOCAL_GL_MAX_SAMPLES:
             case LOCAL_GL_MAX_UNIFORM_BLOCK_SIZE:
             case LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS: {
                 GLint val;
                 gl->fGetIntegerv(pname, &val);
                 return JS::NumberValue(uint32_t(val));
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLExtensions.h"
+
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "GLContext.h"
+#include "WebGLContext.h"
+#include "WebGLTimerQuery.h"
+
+namespace mozilla {
+
+WebGLExtensionDisjointTimerQuery::WebGLExtensionDisjointTimerQuery(WebGLContext* webgl)
+  : WebGLExtensionBase(webgl)
+  , mActiveQuery(nullptr)
+{
+  MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
+}
+
+WebGLExtensionDisjointTimerQuery::~WebGLExtensionDisjointTimerQuery()
+{
+}
+
+already_AddRefed<WebGLTimerQuery>
+WebGLExtensionDisjointTimerQuery::CreateQueryEXT()
+{
+  if (mIsLost)
+    return nullptr;
+
+  nsRefPtr<WebGLTimerQuery> query = WebGLTimerQuery::Create(mContext);
+  return query.forget();
+}
+
+void
+WebGLExtensionDisjointTimerQuery::DeleteQueryEXT(WebGLTimerQuery* query)
+{
+  if (mIsLost)
+    return;
+
+  if (!mContext->ValidateObject("deleteQueryEXT", query))
+    return;
+
+  query->RequestDelete();
+}
+
+bool
+WebGLExtensionDisjointTimerQuery::IsQueryEXT(WebGLTimerQuery* query)
+{
+  if (!query)
+    return false;
+
+  if (!mContext->ValidateObjectAllowDeleted("isQueryEXT", query))
+    return false;
+
+  if (query->IsDeleted())
+    return false;
+
+  return true;
+}
+
+void
+WebGLExtensionDisjointTimerQuery::BeginQueryEXT(GLenum target,
+                                                WebGLTimerQuery* query)
+{
+  if (mIsLost)
+    return;
+
+  if (!mContext->ValidateObject("beginQueryEXT", query))
+    return;
+
+  if (query->HasEverBeenBound() && query->Target() != target) {
+    mContext->ErrorInvalidOperation("beginQueryEXT: Query is already bound"
+                                    " to a different target.");
+    return;
+  }
+
+  if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
+    mContext->ErrorInvalidEnumInfo("beginQueryEXT: Can only begin on target"
+                                   " TIME_ELAPSED_EXT.", target);
+    return;
+  }
+
+  if (mActiveQuery) {
+    mContext->ErrorInvalidOperation("beginQueryEXT: A query is already"
+                                    " active.");
+    return;
+  }
+
+  mContext->MakeContextCurrent();
+  gl::GLContext* gl = mContext->GL();
+  gl->fBeginQuery(target, query->GLName());
+  query->BindTo(LOCAL_GL_TIME_ELAPSED_EXT);
+  mActiveQuery = query;
+}
+
+void
+WebGLExtensionDisjointTimerQuery::EndQueryEXT(GLenum target)
+{
+  if (mIsLost)
+    return;
+
+  if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
+    mContext->ErrorInvalidEnumInfo("endQueryEXT: Can only end on"
+                                   " TIME_ELAPSED_EXT.", target);
+    return;
+  }
+
+  if (!mActiveQuery) {
+    mContext->ErrorInvalidOperation("endQueryEXT: A query is not active.");
+    return;
+  }
+
+  mContext->MakeContextCurrent();
+  mContext->GL()->fEndQuery(target);
+  mActiveQuery = nullptr;
+}
+
+void
+WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLTimerQuery* query,
+                                                  GLenum target)
+{
+  if (mIsLost)
+    return;
+
+  if (!mContext->ValidateObject("queryCounterEXT", query))
+    return;
+
+  if (target != LOCAL_GL_TIMESTAMP_EXT) {
+    mContext->ErrorInvalidEnumInfo("queryCounterEXT: requires"
+                                   " TIMESTAMP_EXT.", target);
+    return;
+  }
+
+  mContext->MakeContextCurrent();
+  mContext->GL()->fQueryCounter(query->GLName(), target);
+  query->BindTo(LOCAL_GL_TIMESTAMP_EXT);
+}
+
+void
+WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target,
+                                              GLenum pname,
+                                              JS::MutableHandle<JS::Value> retval)
+{
+  if (mIsLost)
+    return;
+
+  mContext->MakeContextCurrent();
+  switch (pname) {
+  case LOCAL_GL_CURRENT_QUERY_EXT: {
+    if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
+      mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query target.",
+                                     target);
+      return;
+    }
+    if (mActiveQuery) {
+      JS::Rooted<JS::Value> v(cx);
+      dom::GetOrCreateDOMReflector(cx, mActiveQuery.get(), &v);
+      retval.set(v);
+    } else {
+      retval.set(JS::NullValue());
+    }
+    break;
+  }
+  case LOCAL_GL_QUERY_COUNTER_BITS_EXT: {
+    if (target != LOCAL_GL_TIME_ELAPSED_EXT &&
+        target != LOCAL_GL_TIMESTAMP_EXT) {
+      mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query target.",
+                                     target);
+      return;
+    }
+    GLint bits = 0;
+    mContext->GL()->fGetQueryiv(target, pname, &bits);
+    retval.set(JS::Int32Value(int32_t(bits)));
+    break;
+  }
+  default:
+    mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query property.",
+                                   pname);
+    break;
+  }
+}
+
+void
+WebGLExtensionDisjointTimerQuery::GetQueryObjectEXT(JSContext* cx,
+                                                    WebGLTimerQuery* query,
+                                                    GLenum pname,
+                                                    JS::MutableHandle<JS::Value> retval)
+{
+  if (mIsLost)
+    return;
+
+  if (!mContext->ValidateObject("getQueryObjectEXT", query))
+    return;
+
+  if (query == mActiveQuery.get()) {
+    mContext->ErrorInvalidOperation("getQueryObjectEXT: Query must not be"
+                                    " active.");
+  }
+
+  mContext->MakeContextCurrent();
+  // XXX: Note that the query result *may change* within the same task!
+  // This does not follow the specification, which states that all calls
+  // checking query results must return the same value until the event loop
+  // is empty.
+  switch (pname) {
+  case LOCAL_GL_QUERY_RESULT_EXT: {
+    GLuint64 result = 0;
+    mContext->GL()->fGetQueryObjectui64v(query->GLName(),
+                                         LOCAL_GL_QUERY_RESULT_EXT,
+                                         &result);
+    retval.set(JS::NumberValue(result));
+    break;
+  }
+  case LOCAL_GL_QUERY_RESULT_AVAILABLE_EXT: {
+    GLuint avail = 0;
+    mContext->GL()->fGetQueryObjectuiv(query->GLName(),
+                                       LOCAL_GL_QUERY_RESULT_AVAILABLE_EXT,
+                                       &avail);
+    retval.set(JS::BooleanValue(bool(avail)));
+    break;
+  }
+  default:
+    mContext->ErrorInvalidEnumInfo("getQueryObjectEXT: Invalid query"
+                                   " property.", pname);
+    break;
+  }
+}
+
+bool
+WebGLExtensionDisjointTimerQuery::IsSupported(const WebGLContext* webgl)
+{
+  webgl->MakeContextCurrent();
+  gl::GLContext* gl = webgl->GL();
+  return gl->IsSupported(gl::GLFeature::query_objects) &&
+         gl->IsSupported(gl::GLFeature::get_query_object_i64v) &&
+         gl->IsSupported(gl::GLFeature::query_counter); // provides GL_TIMESTAMP
+}
+
+
+IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDisjointTimerQuery)
+
+} // namespace mozilla
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -11,16 +11,18 @@
 #include "nsWrapperCache.h"
 #include "WebGLObjectModel.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
 class WebGLContext;
 class WebGLShader;
+class WebGLQuery;
+class WebGLTimerQuery;
 class WebGLVertexArray;
 
 class WebGLExtensionBase
     : public nsWrapperCache
     , public WebGLContextBoundObject
 {
 public:
     explicit WebGLExtensionBase(WebGLContext* webgl);
@@ -324,11 +326,41 @@ public:
     explicit WebGLExtensionBlendMinMax(WebGLContext* webgl);
     virtual ~WebGLExtensionBlendMinMax();
 
     static bool IsSupported(const WebGLContext*);
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
+class WebGLExtensionDisjointTimerQuery
+    : public WebGLExtensionBase
+{
+public:
+    explicit WebGLExtensionDisjointTimerQuery(WebGLContext* webgl);
+    virtual ~WebGLExtensionDisjointTimerQuery();
+
+    already_AddRefed<WebGLTimerQuery> CreateQueryEXT();
+    void DeleteQueryEXT(WebGLTimerQuery* query);
+    bool IsQueryEXT(WebGLTimerQuery* query);
+    void BeginQueryEXT(GLenum target, WebGLTimerQuery* query);
+    void EndQueryEXT(GLenum target);
+    void QueryCounterEXT(WebGLTimerQuery* query, GLenum target);
+    void GetQueryEXT(JSContext *cx, GLenum target, GLenum pname,
+                     JS::MutableHandle<JS::Value> retval);
+    void GetQueryObjectEXT(JSContext *cx, WebGLTimerQuery* query,
+                           GLenum pname,
+                           JS::MutableHandle<JS::Value> retval);
+
+    static bool IsSupported(const WebGLContext*);
+
+    DECL_WEBGL_EXTENSION_GOOP
+
+private:
+    /**
+     * An active TIME_ELAPSED query participating in a begin/end block.
+     */
+    WebGLRefPtr<WebGLTimerQuery> mActiveQuery;
+};
+
 } // namespace mozilla
 
 #endif // WEBGL_EXTENSIONS_H_
--- a/dom/canvas/WebGLStrongTypes.h
+++ b/dom/canvas/WebGLStrongTypes.h
@@ -449,9 +449,15 @@ STRONG_GLENUM_BEGIN(BufferBinding)
     STRONG_GLENUM_VALUE(ARRAY_BUFFER),              // 0x8892
     STRONG_GLENUM_VALUE(ELEMENT_ARRAY_BUFFER),      // 0x8893
     STRONG_GLENUM_VALUE(PIXEL_PACK_BUFFER),         // 0x88EB
     STRONG_GLENUM_VALUE(PIXEL_UNPACK_BUFFER),       // 0x88EC
     STRONG_GLENUM_VALUE(UNIFORM_BUFFER),            // 0x8A11
     STRONG_GLENUM_VALUE(TRANSFORM_FEEDBACK_BUFFER), // 0x8C8E
 STRONG_GLENUM_END(BufferBinding)
 
+STRONG_GLENUM_BEGIN(QueryBinding)
+    STRONG_GLENUM_VALUE(NONE),
+    STRONG_GLENUM_VALUE(TIME_ELAPSED_EXT),
+    STRONG_GLENUM_VALUE(TIMESTAMP_EXT),
+STRONG_GLENUM_END(QueryBinding)
+
 #endif // WEBGL_STRONG_TYPES_H_
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLTimerQuery.cpp
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLTimerQuery.h"
+
+#include "GLContext.h"
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "nsContentUtils.h"
+#include "WebGLContext.h"
+
+namespace mozilla {
+
+JSObject*
+WebGLTimerQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+{
+  return dom::WebGLTimerQueryBinding::Wrap(cx, this, aGivenProto);
+}
+
+WebGLTimerQuery::WebGLTimerQuery(WebGLContext* webgl, GLuint aName)
+  : WebGLBindableName<QueryBinding>(aName)
+  , WebGLContextBoundObject(webgl)
+{
+}
+
+WebGLTimerQuery*
+WebGLTimerQuery::Create(WebGLContext* webgl)
+{
+  GLuint name = 0;
+  webgl->MakeContextCurrent();
+  webgl->gl->fGenQueries(1, &name);
+  return new WebGLTimerQuery(webgl, name);
+}
+
+void
+WebGLTimerQuery::Delete()
+{
+  mContext->MakeContextCurrent();
+  mContext->gl->fDeleteQueries(1, &mGLName);
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTimerQuery)
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTimerQuery, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTimerQuery, Release)
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLTimerQuery.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef WEBGL_TIMER_QUERY_H_
+#define WEBGL_TIMER_QUERY_H_
+
+#include "nsWrapperCache.h"
+#include "WebGLObjectModel.h"
+
+namespace mozilla {
+
+class WebGLTimerQuery final
+  : public nsWrapperCache
+  , public WebGLBindableName<QueryBinding>
+  , public WebGLRefCountedObject<WebGLTimerQuery>
+  , public WebGLContextBoundObject
+{
+public:
+  static WebGLTimerQuery* Create(WebGLContext* webgl);
+
+  // WebGLRefCountedObject
+  void Delete();
+
+  // nsWrapperCache
+  WebGLContext* GetParentObject() const {
+    return Context();
+  }
+
+  // NS
+  virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTimerQuery)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTimerQuery)
+
+private:
+  explicit WebGLTimerQuery(WebGLContext* webgl, GLuint aName);
+  ~WebGLTimerQuery() {
+    DeleteOnce();
+  }
+
+  friend class WebGLExtensionDisjointTimerQuery;
+};
+
+} // namespace mozilla
+
+#endif // WEBGL_TIMER_QUERY_H_
--- a/dom/canvas/WebGLTypes.h
+++ b/dom/canvas/WebGLTypes.h
@@ -146,16 +146,17 @@ enum class WebGLTexDimensions : uint8_t 
 enum class WebGLExtensionID : uint8_t {
     ANGLE_instanced_arrays,
     EXT_blend_minmax,
     EXT_color_buffer_half_float,
     EXT_frag_depth,
     EXT_sRGB,
     EXT_shader_texture_lod,
     EXT_texture_filter_anisotropic,
+    EXT_disjoint_timer_query,
     OES_element_index_uint,
     OES_standard_derivatives,
     OES_texture_float,
     OES_texture_float_linear,
     OES_texture_half_float,
     OES_texture_half_float_linear,
     OES_vertex_array_object,
     WEBGL_color_buffer_float,
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -88,16 +88,17 @@ UNIFIED_SOURCES += [
     'WebGLExtensionColorBufferHalfFloat.cpp',
     'WebGLExtensionCompressedTextureATC.cpp',
     'WebGLExtensionCompressedTextureETC1.cpp',
     'WebGLExtensionCompressedTexturePVRTC.cpp',
     'WebGLExtensionCompressedTextureS3TC.cpp',
     'WebGLExtensionDebugRendererInfo.cpp',
     'WebGLExtensionDebugShaders.cpp',
     'WebGLExtensionDepthTexture.cpp',
+    'WebGLExtensionDisjointTimerQuery.cpp',
     'WebGLExtensionDrawBuffers.cpp',
     'WebGLExtensionElementIndexUint.cpp',
     'WebGLExtensionFragDepth.cpp',
     'WebGLExtensionInstancedArrays.cpp',
     'WebGLExtensionLoseContext.cpp',
     'WebGLExtensionShaderTextureLod.cpp',
     'WebGLExtensionSRGB.cpp',
     'WebGLExtensionStandardDerivatives.cpp',
@@ -115,16 +116,17 @@ UNIFIED_SOURCES += [
     'WebGLRenderbuffer.cpp',
     'WebGLSampler.cpp',
     'WebGLShader.cpp',
     'WebGLShaderPrecisionFormat.cpp',
     'WebGLShaderValidator.cpp',
     'WebGLSync.cpp',
     'WebGLTexelConversions.cpp',
     'WebGLTexture.cpp',
+    'WebGLTimerQuery.cpp',
     'WebGLTransformFeedback.cpp',
     'WebGLUniformLocation.cpp',
     'WebGLValidateStrings.cpp',
     'WebGLVertexArray.cpp',
     'WebGLVertexArrayFake.cpp',
     'WebGLVertexArrayGL.cpp',
 ]
 LOCAL_INCLUDES += [
--- a/dom/canvas/test/webgl-mochitest.ini
+++ b/dom/canvas/test/webgl-mochitest.ini
@@ -25,16 +25,17 @@ skip-if = android_version == '10' || and
 [webgl-mochitest/test_texsubimage_float.html]
 [webgl-mochitest/test_uninit_data.html]
 [webgl-mochitest/test_webgl_available.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 #[webgl-mochitest/test_webgl_color_buffer_float.html]
 # We haven't cleaned up the Try results yet, but let's get this on the books first.
 [webgl-mochitest/test_webgl_conformance.html]
 skip-if = buildapp == 'mulet' || toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
+[webgl-mochitest/test_webgl_disjoint_timer_query.html]
 [webgl-mochitest/test_webgl_request_context.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [webgl-mochitest/test_webgl_request_mismatch.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [webgl-mochitest/test_webgl2_not_exposed.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
 [webgl-mochitest/test_webgl2_invalidate_framebuffer.html]
 skip-if = toolkit == 'android' #bug 865443- seperate suite - the non_conf* tests pass except for one on armv6 tests
new file mode 100644
--- /dev/null
+++ b/dom/canvas/test/webgl-mochitest/test_webgl_disjoint_timer_query.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset='UTF-8'>
+<title>WebGL test: Test EXT_disjoint_timer_query.</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script src="webgl-util.js"></script>
+</head>
+<body>
+<canvas id="c"></canvas>
+
+<script>
+
+function doTest() {
+  var gl = WebGLUtil.getWebGL('c', true);
+  var ext = gl.getExtension('EXT_disjoint_timer_query');
+  if (!ext) {
+    ok(true, "EXT_disjoint_timer_query may be unsupported.");
+    SimpleTest.finish();
+    return;
+  }
+
+  ok(!ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT),
+     "No query is active initially.");
+
+  var elapsedQuery = ext.createQueryEXT();
+  ok(elapsedQuery, "Query creation works.");
+  ok(ext.isQueryEXT(elapsedQuery), "New query is valid after creation.");
+
+  ext.beginQueryEXT(ext.TIME_ELAPSED_EXT, elapsedQuery);
+  is(ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT), elapsedQuery,
+     "Query is active after beginQueryEXT.");
+  ext.endQueryEXT(ext.TIME_ELAPSED_EXT);
+  gl.flush();
+
+  ok(!ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.CURRENT_QUERY_EXT),
+     "Query is inactive after endQueryEXT.");
+  ok(ext.getQueryObjectEXT(elapsedQuery, ext.QUERY_RESULT_AVAILABLE_EXT),
+     "Time elapsed query is available immediately after flush.");
+
+  ext.deleteQueryEXT(elapsedQuery);
+  ok(!ext.isQueryEXT(elapsedQuery), "Query is no longer valid after deletion.");
+
+  var timestampQuery = ext.createQueryEXT();
+  ext.queryCounterEXT(timestampQuery, ext.TIMESTAMP_EXT);
+  gl.flush();
+  ok(ext.getQueryObjectEXT(timestampQuery, ext.QUERY_RESULT_AVAILABLE_EXT),
+     "Timestamp query should be available immediately after flush.");
+
+  ok(ext.getQueryEXT(ext.TIMESTAMP_EXT, ext.QUERY_COUNTER_BITS_EXT) >= 30,
+     "Timestamp must be at least 30 bits to hold at least 1 second of timing.");
+  ok(ext.getQueryEXT(ext.TIME_ELAPSED_EXT, ext.QUERY_COUNTER_BITS_EXT) >= 30,
+     "Time elapsed must be at least 30 bits to hold at least 1 second of timing.");
+
+  SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({"set": [['webgl.enable-draft-extensions', true]]}, doTest);
+
+</script>
+</body>
+</html>
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -15,19 +15,16 @@
 #include "ContentChild.h"
 
 #include "BlobChild.h"
 #include "CrashReporterChild.h"
 #include "GeckoProfiler.h"
 #include "TabChild.h"
 
 #include "mozilla/Attributes.h"
-#ifdef ACCESSIBILITY
-#include "mozilla/a11y/DocAccessibleChild.h"
-#endif
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/ContentBridgeChild.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
@@ -852,32 +849,16 @@ ContentChild::InitXPCOM()
     // This object is held alive by the observer service.
     nsRefPtr<SystemMessageHandledObserver> sysMsgObserver =
         new SystemMessageHandledObserver();
     sysMsgObserver->Init();
 
     InitOnContentProcessCreated();
 }
 
-a11y::PDocAccessibleChild*
-ContentChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&)
-{
-  MOZ_ASSERT(false, "should never call this!");
-  return nullptr;
-}
-
-bool
-ContentChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild)
-{
-#ifdef ACCESSIBILITY
-  delete static_cast<mozilla::a11y::DocAccessibleChild*>(aChild);
-#endif
-  return true;
-}
-
 PMemoryReportRequestChild*
 ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
                                              const bool &aAnonymize,
                                              const bool &aMinimizeMemoryUsage,
                                              const MaybeFileDesc& aDMDFile)
 {
     MemoryReportRequestChild *actor =
         new MemoryReportRequestChild(aAnonymize, aDMDFile);
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -441,18 +441,16 @@ public:
 
     virtual bool RecvPBrowserConstructor(PBrowserChild* aCctor,
                                          const TabId& aTabId,
                                          const IPCTabContext& aContext,
                                          const uint32_t& aChromeFlags,
                                          const ContentParentId& aCpID,
                                          const bool& aIsForApp,
                                          const bool& aIsForBrowser) override;
-    virtual PDocAccessibleChild* AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&) override;
-    virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) override;
 
     void GetAvailableDictionaries(InfallibleTArray<nsString>& aDictionaries);
 
     PBrowserOrId
     GetBrowserOrId(TabChild* aTabChild);
 
     virtual POfflineCacheUpdateChild* AllocPOfflineCacheUpdateChild(
             const URIParams& manifestURI,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -27,20 +27,16 @@
 #include "AppProcessChecker.h"
 #include "AudioChannelService.h"
 #include "BlobParent.h"
 #include "CrashReporterParent.h"
 #include "GMPServiceParent.h"
 #include "IHistory.h"
 #include "imgIContainer.h"
 #include "mozIApplication.h"
-#ifdef ACCESSIBILITY
-#include "mozilla/a11y/DocAccessibleParent.h"
-#include "nsAccessibilityService.h"
-#endif
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
@@ -3150,52 +3146,16 @@ ContentParent::Observe(nsISupports* aSub
                 pse->AddSubProfile(result.get());
             }
         }
     }
 #endif
     return NS_OK;
 }
 
-  a11y::PDocAccessibleParent*
-ContentParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent, const uint64_t&)
-{
-#ifdef ACCESSIBILITY
-  return new a11y::DocAccessibleParent();
-#else
-  return nullptr;
-#endif
-}
-
-bool
-ContentParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent)
-{
-#ifdef ACCESSIBILITY
-  delete static_cast<a11y::DocAccessibleParent*>(aParent);
-#endif
-  return true;
-}
-
-bool
-ContentParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc, const uint64_t& aParentID)
-{
-#ifdef ACCESSIBILITY
-  auto doc = static_cast<a11y::DocAccessibleParent*>(aDoc);
-  if (aParentDoc) {
-    MOZ_ASSERT(aParentID);
-    auto parentDoc = static_cast<a11y::DocAccessibleParent*>(aParentDoc);
-    return parentDoc->AddChildDoc(doc, aParentID);
-  } else {
-    MOZ_ASSERT(!aParentID);
-    a11y::DocManager::RemoteDocAdded(doc);
-  }
-#endif
-  return true;
-}
-
 PGMPServiceParent*
 ContentParent::AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
                                       base::ProcessId aOtherProcess)
 {
     return GMPServiceParent::Create(aTransport, aOtherProcess);
 }
 
 PCompositorParent*
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -840,21 +840,16 @@ private:
                           const nsCString& aOrigin,
                           const nsString& aDatabaseName,
                           const int64_t& aFileId,
                           int32_t* aRefCnt,
                           int32_t* aDBRefCnt,
                           int32_t* aSliceRefCnt,
                           bool* aResult) override;
 
-    virtual PDocAccessibleParent* AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&) override;
-    virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) override;
-    virtual bool RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
-                                               PDocAccessibleParent* aParentDoc, const uint64_t& aParentID) override;
-
     virtual PWebrtcGlobalParent* AllocPWebrtcGlobalParent() override;
     virtual bool DeallocPWebrtcGlobalParent(PWebrtcGlobalParent *aActor) override;
 
 
     virtual bool RecvUpdateDropEffect(const uint32_t& aDragAction,
                                       const uint32_t& aDropEffect) override;
 
     virtual bool RecvGetBrowserConfiguration(const nsCString& aURI, BrowserConfiguration* aConfig) override;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -4,16 +4,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PBlob;
 include protocol PColorPicker;
 include protocol PContent;
 include protocol PContentBridge;
+include protocol PDocAccessible;
 include protocol PDocumentRenderer;
 include protocol PFilePicker;
 include protocol PIndexedDBPermissionRequest;
 include protocol PRenderFrame;
 include protocol PPluginWidget;
 include DOMTypes;
 include JavaScriptTypes;
 include URIParams;
@@ -93,16 +94,17 @@ struct FrameScriptInfo
     bool runInGlobalScope;
 };
 
 prio(normal upto urgent) sync protocol PBrowser
 {
     manager PContent or PContentBridge;
 
     manages PColorPicker;
+    manages PDocAccessible;
     manages PDocumentRenderer;
     manages PFilePicker;
     manages PIndexedDBPermissionRequest;
     manages PRenderFrame;
     manages PPluginWidget;
 
 both:
     AsyncMessage(nsString aMessage, ClonedMessageData aData, CpowEntry[] aCpows,
@@ -110,16 +112,24 @@ both:
 
     /**
      * Create a layout frame (encapsulating a remote layer tree) for
      * the page that is currently loaded in the <browser>.
      */
     PRenderFrame();
 
 parent:
+    /**
+     * Tell the parent process a new accessible document has been created.
+     * aParentDoc is the accessible document it was created in if any, and
+     * aParentAcc is the id of the accessible in that document the new document
+     * is a child of.
+     */
+    PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc);
+
     /*
      * Creates a new remoted nsIWidget connection for windowed plugins
      * in e10s mode. This is always initiated from the child in response
      * to windowed plugin creation.
      */
     sync PPluginWidget();
 
     /**
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -10,17 +10,16 @@ include protocol PBlob;
 include protocol PBluetooth;
 include protocol PBrowser;
 include protocol PCellBroadcast;
 include protocol PCompositor;
 include protocol PContentBridge;
 include protocol PContentPermissionRequest;
 include protocol PCycleCollectWithLogs;
 include protocol PCrashReporter;
-include protocol PDocAccessible;
 include protocol PPSMContentDownloader;
 include protocol PExternalHelperApp;
 include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
 include protocol PFMRadio;
 include protocol PFileSystemRequest;
 include protocol PHal;
 include protocol PIcc;
@@ -417,17 +416,16 @@ prio(normal upto urgent) sync protocol P
     manages PAsmJSCacheEntry;
     manages PBlob;
     manages PBluetooth;
     manages PBrowser;
     manages PCellBroadcast;
     manages PContentPermissionRequest;
     manages PCrashReporter;
     manages PCycleCollectWithLogs;
-    manages PDocAccessible;
     manages PDeviceStorageRequest;
     manages PFileSystemRequest;
     manages PPSMContentDownloader;
     manages PExternalHelperApp;
     manages PFileDescriptorSet;
     manages PFMRadio;
     manages PHal;
     manages PIcc;
@@ -650,24 +648,16 @@ child:
     async UpdateWindow(uintptr_t aChildId);
 
     /**
      * Send gamepad status update to child.
      */
     GamepadUpdate(GamepadChangeEvent aGamepadEvent);
 parent:
     /**
-     * Tell the parent process a new accessible document has been created.
-     * aParentDoc is the accessible document it was created in if any, and
-     * aParentAcc is the id of the accessible in that document the new document
-     * is a child of.
-     */
-    PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc);
-
-    /**
      * Tell the content process some attributes of itself.  This is
      * among the first information queried by content processes after
      * startup.  (The message is sync to allow the content process to
      * control when it receives the information.)
      *
      * |id| is a unique ID among all subprocesses.  When |isForApp &&
      * isForBrowser|, we're loading <browser> for an app.  When
      * |isForBrowser|, we're loading <browser>.  When |!isForApp &&
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -3,16 +3,19 @@
 /* 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 "base/basictypes.h"
 
 #include "TabChild.h"
 
+#ifdef ACCESSIBILITY
+#include "mozilla/a11y/DocAccessibleChild.h"
+#endif
 #include "Layers.h"
 #include "ContentChild.h"
 #include "TabParent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
@@ -197,27 +200,50 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildBase)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TabChildBase)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TabChildBase)
 
+// For the root frame, Screen and ParentLayer pixels are interchangeable.
+// nsViewportInfo stores zoom values as CSSToScreenScale (because it's a
+// data structure specific to the root frame), while FrameMetrics and
+// ZoomConstraints store zoom values as CSSToParentLayerScale (because they
+// are not specific to the root frame). We define convenience functions for
+// converting between the two. As the name suggests, they should only be used
+// when dealing with the root frame!
+CSSToScreenScale ConvertScaleForRoot(CSSToParentLayerScale aScale)
+{
+  return ViewTargetAs<ScreenPixel>(aScale, PixelCastJustification::ScreenIsParentLayerForRoot);
+}
+CSSToParentLayerScale ConvertScaleForRoot(CSSToScreenScale aScale)
+{
+  return ViewTargetAs<ParentLayerPixel>(aScale, PixelCastJustification::ScreenIsParentLayerForRoot);
+}
+
+// Calculate the scale needed to fit the given viewport into the given display.
+CSSToScreenScale CalculateIntrinsicScale(const ScreenIntSize& aDisplaySize, const CSSSize& aViewportSize)
+{
+  return MaxScaleRatio(ScreenSize(aDisplaySize), aViewportSize);
+}
+
 void
 TabChildBase::InitializeRootMetrics()
 {
   // Calculate a really simple resolution that we probably won't
   // be keeping, as well as putting the scroll offset back to
   // the top-left of the page.
   mLastRootMetrics.SetViewport(CSSRect(CSSPoint(), kDefaultViewportSize));
   mLastRootMetrics.SetCompositionBounds(ParentLayerRect(
       ParentLayerPoint(),
       ParentLayerSize(ViewAs<ParentLayerPixel>(mInnerSize, PixelCastJustification::ScreenIsParentLayerForRoot))));
-  mLastRootMetrics.SetZoom(CSSToParentLayerScale2D(mLastRootMetrics.CalculateIntrinsicScale()));
+  mLastRootMetrics.SetZoom(CSSToParentLayerScale2D(
+      ConvertScaleForRoot(CalculateIntrinsicScale(mInnerSize, kDefaultViewportSize))));
   mLastRootMetrics.SetDevPixelsPerCSSPixel(WebWidget()->GetDefaultScale());
   // We use ParentLayerToLayerScale(1) below in order to turn the
   // async zoom amount into the gecko zoom amount.
   mLastRootMetrics.SetCumulativeResolution(mLastRootMetrics.GetZoom() / mLastRootMetrics.GetDevPixelsPerCSSPixel() * ParentLayerToLayerScale(1));
   // This is the root layer, so the cumulative resolution is the same
   // as the resolution.
   mLastRootMetrics.SetPresShellResolution(mLastRootMetrics.GetCumulativeResolution().ToScaleFactor().scale);
   mLastRootMetrics.SetScrollOffset(CSSPoint(0, 0));
@@ -259,30 +285,16 @@ TabChildBase::GetPageSize(nsCOMPtr<nsIDo
   if (bodyDOMElement) {
     bodyWidth = bodyDOMElement->ScrollWidth();
     bodyHeight = bodyDOMElement->ScrollHeight();
   }
   return CSSSize(std::max(htmlWidth, bodyWidth),
                  std::max(htmlHeight, bodyHeight));
 }
 
-// For the root frame, Screen and ParentLayer pixels are interchangeable.
-// nsViewportInfo stores zoom values as CSSToScreenScale (because it's a
-// data structure specific to the root frame), while FrameMetrics and
-// ZoomConstraints store zoom values as CSSToParentLayerScale (because they
-// are not specific to the root frame). We define convenience functions for
-// converting between the two. As the name suggests, they should only be used
-// when dealing with the root frame!
-CSSToScreenScale ConvertScaleForRoot(CSSToParentLayerScale aScale) {
-  return ViewTargetAs<ScreenPixel>(aScale, PixelCastJustification::ScreenIsParentLayerForRoot);
-}
-CSSToParentLayerScale ConvertScaleForRoot(CSSToScreenScale aScale) {
-  return ViewTargetAs<ParentLayerPixel>(aScale, PixelCastJustification::ScreenIsParentLayerForRoot);
-}
-
 bool
 TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
 {
   if (!gfxPrefs::AsyncPanZoomEnabled()) {
     return false;
   }
 
   TABC_LOG("HandlePossibleViewportChange aOldScreenSize=%s mInnerSize=%s\n",
@@ -378,33 +390,32 @@ TabChildBase::HandlePossibleViewportChan
   //    viewport)
   // 3. screen size remains constant, but CSS viewport changes (meta viewport
   //    tag is added or removed)
   // 4. neither screen size nor CSS viewport changes
   //
   // In all of these cases, we maintain how much actual content is visible
   // within the screen width. Note that "actual content" may be different with
   // respect to CSS pixels because of the CSS viewport size changing.
-  float oldIntrinsicScale =
-      std::max(oldScreenSize.width / oldBrowserSize.width,
-               oldScreenSize.height / oldBrowserSize.height);
-  metrics.ZoomBy(metrics.CalculateIntrinsicScale().scale / oldIntrinsicScale);
+  CSSToScreenScale oldIntrinsicScale = CalculateIntrinsicScale(oldScreenSize, oldBrowserSize);
+  CSSToScreenScale newIntrinsicScale = CalculateIntrinsicScale(mInnerSize, viewport);
+  metrics.ZoomBy(newIntrinsicScale.scale / oldIntrinsicScale.scale);
 
   // Changing the zoom when we're not doing a first paint will get ignored
   // by AsyncPanZoomController and causes a blurry flash.
   bool isFirstPaint = true;
   if (shell) {
     isFirstPaint = shell->GetIsFirstPaint();
   }
   if (isFirstPaint) {
     // FIXME/bug 799585(?): GetViewportInfo() returns a defaultZoom of
     // 0.0 to mean "did not calculate a zoom".  In that case, we default
     // it to the intrinsic scale.
     if (viewportInfo.GetDefaultZoom().scale < 0.01f) {
-      viewportInfo.SetDefaultZoom(ConvertScaleForRoot(metrics.CalculateIntrinsicScale()));
+      viewportInfo.SetDefaultZoom(newIntrinsicScale);
     }
 
     CSSToScreenScale defaultZoom = viewportInfo.GetDefaultZoom();
     MOZ_ASSERT(viewportInfo.GetMinZoom() <= defaultZoom &&
                defaultZoom <= viewportInfo.GetMaxZoom());
     metrics.SetZoom(CSSToParentLayerScale2D(ConvertScaleForRoot(defaultZoom)));
 
     metrics.SetScrollId(viewId);
@@ -2567,16 +2578,32 @@ bool
 TabChild::RecvSelectionEvent(const WidgetSelectionEvent& event)
 {
   WidgetSelectionEvent localEvent(event);
   localEvent.widget = mWidget;
   APZCCallbackHelper::DispatchWidgetEvent(localEvent);
   return true;
 }
 
+a11y::PDocAccessibleChild*
+TabChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&)
+{
+  MOZ_ASSERT(false, "should never call this!");
+  return nullptr;
+}
+
+bool
+TabChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild)
+{
+#ifdef ACCESSIBILITY
+  delete static_cast<mozilla::a11y::DocAccessibleChild*>(aChild);
+#endif
+  return true;
+}
+
 PDocumentRendererChild*
 TabChild::AllocPDocumentRendererChild(const nsRect& documentRect,
                                       const mozilla::gfx::Matrix& transform,
                                       const nsString& bgcolor,
                                       const uint32_t& renderFlags,
                                       const bool& flushLayout,
                                       const nsIntSize& renderSize)
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -386,16 +386,20 @@ public:
                                   const ClonedMessageData& aData,
                                   InfallibleTArray<CpowEntry>&& aCpows,
                                   const IPC::Principal& aPrincipal) override;
 
     virtual bool RecvAppOfflineStatus(const uint32_t& aId, const bool& aOffline) override;
 
     virtual bool RecvSwappedWithOtherRemoteLoader() override;
 
+    virtual PDocAccessibleChild* AllocPDocAccessibleChild(PDocAccessibleChild*,
+                                                          const uint64_t&)
+      override;
+    virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) override;
     virtual PDocumentRendererChild*
     AllocPDocumentRendererChild(const nsRect& documentRect, const gfx::Matrix& transform,
                                 const nsString& bgcolor,
                                 const uint32_t& renderFlags, const bool& flushLayout,
                                 const nsIntSize& renderSize) override;
     virtual bool DeallocPDocumentRendererChild(PDocumentRendererChild* actor) override;
     virtual bool RecvPDocumentRendererConstructor(PDocumentRendererChild* actor,
                                                   const nsRect& documentRect,
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -5,16 +5,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "base/basictypes.h"
 
 #include "TabParent.h"
 
 #include "AppProcessChecker.h"
 #include "mozIApplication.h"
+#ifdef ACCESSIBILITY
+#include "mozilla/a11y/DocAccessibleParent.h"
+#include "nsAccessibilityService.h"
+#endif
 #include "mozilla/BrowserElementParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/plugins/PluginWidgetParent.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/Hal.h"
@@ -1102,16 +1106,55 @@ TabParent::GetState(uint32_t *aState)
 NS_IMETHODIMP
 TabParent::SetDocShell(nsIDocShell *aDocShell)
 {
   NS_ENSURE_ARG(aDocShell);
   NS_WARNING("No mDocShell member in TabParent so there is no docShell to set");
   return NS_OK;
 }
 
+  a11y::PDocAccessibleParent*
+TabParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent,
+                                     const uint64_t&)
+{
+#ifdef ACCESSIBILITY
+  return new a11y::DocAccessibleParent();
+#else
+  return nullptr;
+#endif
+}
+
+bool
+TabParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent)
+{
+#ifdef ACCESSIBILITY
+  delete static_cast<a11y::DocAccessibleParent*>(aParent);
+#endif
+  return true;
+}
+
+bool
+TabParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
+                                         PDocAccessibleParent* aParentDoc,
+                                         const uint64_t& aParentID)
+{
+#ifdef ACCESSIBILITY
+  auto doc = static_cast<a11y::DocAccessibleParent*>(aDoc);
+  if (aParentDoc) {
+    MOZ_ASSERT(aParentID);
+    auto parentDoc = static_cast<a11y::DocAccessibleParent*>(aParentDoc);
+    return parentDoc->AddChildDoc(doc, aParentID);
+  } else {
+    MOZ_ASSERT(!aParentID);
+    a11y::DocManager::RemoteDocAdded(doc);
+  }
+#endif
+  return true;
+}
+
 PDocumentRendererParent*
 TabParent::AllocPDocumentRendererParent(const nsRect& documentRect,
                                         const gfx::Matrix& transform,
                                         const nsString& bgcolor,
                                         const uint32_t& renderFlags,
                                         const bool& flushLayout,
                                         const nsIntSize& renderSize)
 {
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -242,16 +242,24 @@ public:
     virtual bool RecvDispatchWheelEvent(const mozilla::WidgetWheelEvent& aEvent) override;
     virtual bool RecvDispatchMouseEvent(const mozilla::WidgetMouseEvent& aEvent) override;
     virtual bool RecvDispatchKeyboardEvent(const mozilla::WidgetKeyboardEvent& aEvent) override;
 
     virtual PColorPickerParent*
     AllocPColorPickerParent(const nsString& aTitle, const nsString& aInitialColor) override;
     virtual bool DeallocPColorPickerParent(PColorPickerParent* aColorPicker) override;
 
+    virtual PDocAccessibleParent*
+    AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&) override;
+    virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) override;
+    virtual bool
+    RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
+                                  PDocAccessibleParent* aParentDoc,
+                                  const uint64_t& aParentID) override;
+
     void LoadURL(nsIURI* aURI);
     // XXX/cjones: it's not clear what we gain by hiding these
     // message-sending functions under a layer of indirection and
     // eating the return values
     void Show(const ScreenIntSize& size, bool aParentIsActive);
     void UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size);
     void UpdateFrame(const layers::FrameMetrics& aFrameMetrics);
     void UIResolutionChanged();
--- a/dom/ipc/jar.mn
+++ b/dom/ipc/jar.mn
@@ -2,13 +2,14 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 toolkit.jar:
         content/global/test-ipc.xul (test.xul)
         content/global/remote-test-ipc.js (remote-test.js)
         content/global/BrowserElementChild.js (../browser-element/BrowserElementChild.js)
         content/global/BrowserElementChildPreload.js (../browser-element/BrowserElementChildPreload.js)
+        content/global/BrowserElementCopyPaste.js (../browser-element/BrowserElementCopyPaste.js)
         content/global/BrowserElementPanning.js (../browser-element/BrowserElementPanning.js)
 *       content/global/BrowserElementPanningAPZDisabled.js (../browser-element/BrowserElementPanningAPZDisabled.js)
         content/global/manifestMessages.js (manifestMessages.js)
         content/global/PushServiceChildPreload.js (../push/PushServiceChildPreload.js)
         content/global/preload.js (preload.js)
--- a/dom/ipc/preload.js
+++ b/dom/ipc/preload.js
@@ -98,16 +98,17 @@ const BrowserElementIsPreloaded = true;
         Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanningAPZDisabled.js", global);
       }
     } catch (e) {
     }
 
     Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementPanning.js", global);
   }
 
+  Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementCopyPaste.js", global);
   Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementChildPreload.js", global);
 
   Services.io.getProtocolHandler("app");
   Services.io.getProtocolHandler("default");
 
   docShell.isActive = false;
   docShell.createAboutBlankContentViewer(null);
 
--- a/dom/locales/en-US/chrome/accessibility/AccessFu.properties
+++ b/dom/locales/en-US/chrome/accessibility/AccessFu.properties
@@ -27,16 +27,17 @@ cell           =       cell
 link           =       link
 list           =       list
 listitem       =       list item
 outline        =       outline
 outlineitem    =       outline item
 pagetab        =       tab
 propertypage   =       property page
 graphic        =       graphic
+switch         =       switch
 pushbutton     =       button
 checkbutton    =       check button
 radiobutton    =       radio button
 combobox       =       combo box
 progressbar    =       progress bar
 slider         =       slider
 spinbutton     =       spin button
 diagram        =       diagram
@@ -122,16 +123,18 @@ rowInfo = Row %S
 spansColumns = spans %S columns
 spansRows = spans %S rows
 
 # Invoked actions
 jumpAction     =      jumped
 pressAction    =      pressed
 checkAction    =      checked
 uncheckAction  =      unchecked
+onAction       =      on
+offAction      =      off
 selectAction   =      selected
 unselectAction =      unselected
 openAction     =      opened
 closeAction    =      closed
 switchAction   =      switched
 clickAction    =      clicked
 collapseAction =      collapsed
 expandAction   =      expanded
@@ -146,17 +149,19 @@ hidden         =      hidden
 tabLoading     =      loading
 tabLoaded      =      loaded
 tabNew         =      new tab
 tabLoadStopped =      loading stopped
 tabReload      =      reloading
 
 # Object states
 stateChecked     =    checked
+stateOn          =    on
 stateNotChecked  =    not checked
+stateOff         =    off
 statePressed     =    pressed
 # No string for a not pressed toggle button
 stateExpanded    =    expanded
 stateCollapsed   =    collapsed
 stateUnavailable =    unavailable
 stateReadonly    =    readonly
 stateRequired    =    required
 stateTraversed   =    visited
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -119,16 +119,18 @@ using dom::MediaTrackConstraintSet;
 using dom::MediaTrackConstraints;
 using dom::MediaStreamError;
 using dom::GetUserMediaRequest;
 using dom::Sequence;
 using dom::OwningBooleanOrMediaTrackConstraints;
 using dom::SupportedAudioConstraints;
 using dom::SupportedVideoConstraints;
 
+static Atomic<bool> sInShutdown;
+
 static bool
 HostInDomain(const nsCString &aHost, const nsCString &aPattern)
 {
   int32_t patternOffset = 0;
   int32_t hostOffset = 0;
 
   // Act on '*.' wildcard in the left-most position in a domain pattern.
   if (aPattern.Length() > 2 && aPattern[0] == '*' && aPattern[1] == '.') {
@@ -941,25 +943,26 @@ public:
         branch->GetIntPref("media.getusermedia.playout_delay", &playout_delay);
       }
     }
 #endif
     // Create a media stream.
     nsRefPtr<nsDOMUserMediaStream> trackunion =
       nsDOMUserMediaStream::CreateTrackUnionStream(window, mListener,
                                                    mAudioSource, mVideoSource);
-    if (!trackunion) {
+    if (!trackunion || sInShutdown) {
       nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure = mOnFailure.forget();
       LOG(("Returning error for getUserMedia() - no stream"));
 
       nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowID);
       if (window) {
         nsRefPtr<MediaStreamError> error = new MediaStreamError(window,
             NS_LITERAL_STRING("InternalError"),
-            NS_LITERAL_STRING("No stream."));
+            sInShutdown ? NS_LITERAL_STRING("In shutdown") :
+                          NS_LITERAL_STRING("No stream."));
         onFailure->OnError(error);
       }
       return NS_OK;
     }
     trackunion->AudioConfig(aec_on, (uint32_t) aec,
                             agc_on, (uint32_t) agc,
                             noise_on, (uint32_t) noise,
                             playout_delay);
@@ -1004,17 +1007,17 @@ public:
                            agc_on, (uint32_t) agc,
                            noise_on, (uint32_t) noise,
                            playout_delay);
 
     // Dispatch to the media thread to ask it to start the sources,
     // because that can take a while.
     // Pass ownership of trackunion to the MediaOperationTask
     // to ensure it's kept alive until the MediaOperationTask runs (at least).
-    MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+    MediaManager::PostTask(FROM_HERE,
       new MediaOperationTask(MEDIA_START, mListener, trackunion,
                              tracksAvailableCallback,
                              mAudioSource, mVideoSource, false, mWindowID,
                              mOnFailure.forget()));
 
     // We won't need mOnFailure now.
     mOnFailure = nullptr;
     return NS_OK;
@@ -1607,17 +1610,17 @@ MediaManager::Get() {
     if (!sSingleton->mMediaThread->StartWithOptions(options)) {
       MOZ_CRASH();
     }
 
     LOG(("New Media thread for gum"));
 
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
-      obs->AddObserver(sSingleton, "xpcom-shutdown", false);
+      obs->AddObserver(sSingleton, "xpcom-will-shutdown", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:allow", false);
       obs->AddObserver(sSingleton, "getUserMedia:response:deny", false);
       obs->AddObserver(sSingleton, "getUserMedia:revoke", false);
       obs->AddObserver(sSingleton, "phone-state-changed", false);
     }
     // else MediaManager won't work properly and will leak (see bug 837874)
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (prefs) {
@@ -1639,22 +1642,27 @@ MediaManager::GetIfExists() {
 MediaManager::GetInstance()
 {
   // so we can have non-refcounted getters
   nsRefPtr<MediaManager> service = MediaManager::Get();
   return service.forget();
 }
 
 /* static */
-MessageLoop*
-MediaManager::GetMessageLoop()
+void
+MediaManager::PostTask(const tracked_objects::Location& from_here, Task* task)
 {
+  if (sInShutdown) {
+    // Can't safely delete task here since it may have items with specific
+    // thread-release requirements.
+    return;
+  }
   NS_ASSERTION(Get(), "MediaManager singleton?");
   NS_ASSERTION(Get()->mMediaThread, "No thread yet");
-  return Get()->mMediaThread->message_loop();
+  Get()->mMediaThread->message_loop()->PostTask(from_here, task);
 }
 
 /* static */ nsresult
 MediaManager::NotifyRecordingStatusChange(nsPIDOMWindow* aWindow,
                                           const nsString& aMsg,
                                           const bool& aIsAudio,
                                           const bool& aIsVideo)
 {
@@ -1778,16 +1786,19 @@ MediaManager::GetUserMedia(
     // Fake stream from default backend.
     task = new GetUserMediaTask(c, onSuccess.forget(),
       onFailure.forget(), windowID, listener, mPrefs, new MediaEngineDefault(c.mFakeTracks));
   } else {
     // Stream from default device from WebRTC backend.
     task = new GetUserMediaTask(c, onSuccess.forget(),
       onFailure.forget(), windowID, listener, mPrefs);
   }
+  if (sInShutdown) {
+    return task->Denied(NS_LITERAL_STRING("In shutdown"));
+  }
 
   nsIURI* docURI = aWindow->GetDocumentURI();
 
   bool isLoop = false;
   nsCOMPtr<nsIURI> loopURI;
   nsresult rv = NS_NewURI(getter_AddRefs(loopURI), "about:loopconversation");
   NS_ENSURE_SUCCESS(rv, rv);
   rv = docURI->EqualsExceptRef(loopURI, &isLoop);
@@ -1884,17 +1895,17 @@ MediaManager::GetUserMedia(
   if (mCameraManager == nullptr) {
     mCameraManager = nsDOMCameraManager::CreateInstance(aWindow);
   }
 #endif
 
   // XXX No full support for picture in Desktop yet (needs proper UI)
   if (privileged ||
       (fake && !Preferences::GetBool("media.navigator.permission.fake"))) {
-    MediaManager::GetMessageLoop()->PostTask(FROM_HERE, task.forget());
+    MediaManager::PostTask(FROM_HERE, task.forget());
   } else {
     bool isHTTPS = false;
     if (docURI) {
       docURI->SchemeIs("https", &isHTTPS);
     }
 
     // Check if this site has persistent permissions.
     nsresult rv;
@@ -1969,16 +1980,17 @@ MediaManager::GetUserMediaDevices(nsPIDO
   nsIDOMGetUserMediaErrorCallback* aOnFailure,
   uint64_t aInnerWindowID,
   bool aPrivileged)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 
   NS_ENSURE_TRUE(aOnFailure, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(aOnSuccess, NS_ERROR_NULL_POINTER);
+  NS_ENSURE_TRUE(!sInShutdown, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onSuccess(aOnSuccess);
   nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onFailure(aOnFailure);
 
   // Check if the preference for using loopback devices is enabled.
   nsAdoptingCString loopbackAudioDevice =
     Preferences::GetCString("media.audio_loopback_dev");
   nsAdoptingCString loopbackVideoDevice =
@@ -1989,17 +2001,17 @@ MediaManager::GetUserMediaDevices(nsPIDO
   nsCString origin;
   nsPrincipal::GetOriginForURI(aWindow->GetDocumentURI(), origin);
   bool inPrivateBrowsing;
   {
     nsCOMPtr<nsIDocument> doc = aWindow->GetDoc();
     nsCOMPtr<nsILoadContext> loadContext = doc->GetLoadContext();
     inPrivateBrowsing = loadContext && loadContext->UsePrivateBrowsing();
   }
-  MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+  MediaManager::PostTask(FROM_HERE,
     new GetUserMediaDevicesTask(
       aConstraints, onSuccess.forget(), onFailure.forget(),
       (aInnerWindowID ? aInnerWindowID : aWindow->WindowID()),
       loopbackAudioDevice, loopbackVideoDevice, aPrivileged, origin,
       inPrivateBrowsing, useFakeStreams));
 
   return NS_OK;
 }
@@ -2022,16 +2034,17 @@ MediaManager::EnumerateDevices(nsPIDOMWi
 MediaEngine*
 MediaManager::GetBackend(uint64_t aWindowId)
 {
   // Plugin backends as appropriate. The default engine also currently
   // includes picture support for Android.
   // This IS called off main-thread.
   MutexAutoLock lock(mMutex);
   if (!mBackend) {
+    MOZ_RELEASE_ASSERT(!sInShutdown);  // we should never create a new backend in shutdown
 #if defined(MOZ_WEBRTC)
     mBackend = new MediaEngineWebRTC(mPrefs);
 #else
     mBackend = new MediaEngineDefault();
 #endif
   }
   return mBackend;
 }
@@ -2197,69 +2210,97 @@ MediaManager::Observe(nsISupports* aSubj
 
   if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
     nsCOMPtr<nsIPrefBranch> branch( do_QueryInterface(aSubject) );
     if (branch) {
       GetPrefs(branch,NS_ConvertUTF16toUTF8(aData).get());
       LOG(("%s: %dx%d @%dfps (min %d)", __FUNCTION__,
            mPrefs.mWidth, mPrefs.mHeight, mPrefs.mFPS, mPrefs.mMinFPS));
     }
-  } else if (!strcmp(aTopic, "xpcom-shutdown")) {
-    obs->RemoveObserver(this, "xpcom-shutdown");
+  } else if (!strcmp(aTopic, "xpcom-will-shutdown")) {
+    sInShutdown = true;
+
+    obs->RemoveObserver(this, "xpcom-will-shutdown");
     obs->RemoveObserver(this, "getUserMedia:response:allow");
     obs->RemoveObserver(this, "getUserMedia:response:deny");
     obs->RemoveObserver(this, "getUserMedia:revoke");
 
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (prefs) {
       prefs->RemoveObserver("media.navigator.video.default_width", this);
       prefs->RemoveObserver("media.navigator.video.default_height", this);
       prefs->RemoveObserver("media.navigator.video.default_fps", this);
       prefs->RemoveObserver("media.navigator.video.default_minfps", this);
     }
 
+    // Close off any remaining active windows.
+    GetActiveWindows()->Clear();
+    mActiveCallbacks.Clear();
+    mCallIds.Clear();
+    {
+      MutexAutoLock lock(mMutex);
+      if (mBackend) {
+        mBackend->Shutdown(); // ok to invoke multiple times
+      }
+    }
+
     // Because mMediaThread is not an nsThread, we must dispatch to it so it can
     // clean up BackgroundChild. Continue stopping thread once this is done.
 
     class ShutdownTask : public Task
     {
     public:
-      explicit ShutdownTask(nsRunnable* aReply) : mReply(aReply) {}
+      ShutdownTask(TemporaryRef<MediaEngine> aBackend,
+                   nsRunnable* aReply)
+        : mReply(aReply)
+        , mBackend(aBackend) {}
     private:
       virtual void
       Run()
       {
+        LOG(("MediaManager Thread Shutdown"));
         MOZ_ASSERT(MediaManager::IsInMediaThread());
         mozilla::ipc::BackgroundChild::CloseForCurrentThread();
-        NS_DispatchToMainThread(mReply);
+        // must explicitly do this before dispatching the reply, since the reply may kill us with Stop()
+        mBackend = nullptr; // last reference, will invoke Shutdown() again
+
+        if (NS_FAILED(NS_DispatchToMainThread(mReply))) {
+          LOG(("Will leak thread: DispatchToMainthread of reply runnable failed in MediaManager shutdown"));
+        }
       }
       nsRefPtr<nsRunnable> mReply;
+      RefPtr<MediaEngine> mBackend;
     };
 
     // Post ShutdownTask to execute on mMediaThread and pass in a lambda
     // callback to be executed back on this thread once it is done.
     //
     // The lambda callback "captures" the 'this' pointer for member access.
     // This is safe since this is guaranteed to be here since sSingleton isn't
     // cleared until the lambda function clears it.
 
-    MediaManager::GetMessageLoop()->PostTask(FROM_HERE, new ShutdownTask(
-        media::NewRunnableFrom([this]() mutable {
-      // Close off any remaining active windows.
+    // note that this == sSingleton
+    nsRefPtr<MediaManager> that(sSingleton);
+    // Release the backend (and call Shutdown()) from within the MediaManager thread
+    RefPtr<MediaEngine> temp;
+    {
       MutexAutoLock lock(mMutex);
-      GetActiveWindows()->Clear();
-      mActiveCallbacks.Clear();
-      mCallIds.Clear();
-      LOG(("Releasing MediaManager singleton and thread"));
-      // Note: won't be released immediately as the Observer has a ref to us
-      sSingleton = nullptr;
+      temp = mBackend.forget();
+    }
+    // Don't use MediaManager::PostTask() because we're sInShutdown=true here!
+    mMediaThread->message_loop()->PostTask(FROM_HERE, new ShutdownTask(
+        temp.forget(),
+        media::NewRunnableFrom([this, that]() mutable {
+      LOG(("MediaManager shutdown lambda running, releasing MediaManager singleton and thread"));
       if (mMediaThread) {
         mMediaThread->Stop();
       }
-      mBackend = nullptr;
+      // we hold a ref to 'that' which is the same as sSingleton
+      sSingleton = nullptr;
+
       return NS_OK;
     })));
     return NS_OK;
 
   } else if (!strcmp(aTopic, "getUserMedia:response:allow")) {
     nsString key(aData);
     nsAutoPtr<GetUserMediaTask> task;
     mActiveCallbacks.RemoveAndForget(key, task);
@@ -2294,18 +2335,21 @@ MediaManager::Observe(nsISupports* aSubj
             task->SetAudioDevice(static_cast<AudioDevice*>(device.get()));
           } else {
             NS_WARNING("Unknown device type in getUserMedia");
           }
         }
       }
     }
 
+    if (sInShutdown) {
+      return task->Denied(NS_LITERAL_STRING("In shutdown"));
+    }
     // Reuse the same thread to save memory.
-    MediaManager::GetMessageLoop()->PostTask(FROM_HERE, task.forget());
+    MediaManager::PostTask(FROM_HERE, task.forget());
     return NS_OK;
 
   } else if (!strcmp(aTopic, "getUserMedia:response:deny")) {
     nsString errorMessage(NS_LITERAL_STRING("PermissionDeniedError"));
 
     if (aSubject) {
       nsCOMPtr<nsISupportsString> msg(do_QueryInterface(aSubject));
       MOZ_ASSERT(msg);
@@ -2493,18 +2537,17 @@ MediaManager::MediaCaptureWindowState(ns
 }
 
 NS_IMETHODIMP
 MediaManager::SanitizeDeviceIds(int64_t aSinceWhen)
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   LOG(("%s: sinceWhen = %llu", __FUNCTION__, aSinceWhen));
 
-  MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
-    new SanitizeDeviceIdsTask(aSinceWhen));
+  MediaManager::PostTask(FROM_HERE, new SanitizeDeviceIdsTask(aSinceWhen));
   return NS_OK;
 }
 
 static void
 StopScreensharingCallback(MediaManager *aThis,
                           uint64_t aWindowID,
                           StreamListeners *aListeners,
                           void *aData)
@@ -2611,35 +2654,33 @@ void
 GetUserMediaCallbackMediaStreamListener::AudioConfig(bool aEchoOn,
               uint32_t aEcho,
               bool aAgcOn, uint32_t aAGC,
               bool aNoiseOn, uint32_t aNoise,
               int32_t aPlayoutDelay)
 {
   if (mAudioSource) {
 #ifdef MOZ_WEBRTC
-    mMediaThread->message_loop()->PostTask(FROM_HERE,
+    MediaManager::PostTask(FROM_HERE,
       NewRunnableMethod(mAudioSource.get(), &MediaEngineSource::Config,
                         aEchoOn, aEcho, aAgcOn, aAGC, aNoiseOn,
                         aNoise, aPlayoutDelay));
-#else
-    unused << mMediaThread;
 #endif
   }
 }
 
 // Can be invoked from EITHER MainThread or MSG thread
 void
 GetUserMediaCallbackMediaStreamListener::Invalidate()
 {
   // We can't take a chance on blocking here, so proxy this to another
   // thread.
   // Pass a ref to us (which is threadsafe) so it can query us for the
   // source stream info.
-  MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+  MediaManager::PostTask(FROM_HERE,
     new MediaOperationTask(MEDIA_STOP,
                            this, nullptr, nullptr,
                            mAudioSource, mVideoSource,
                            mFinished, mWindowID, nullptr));
 }
 
 // Doesn't kill audio
 // XXX refactor to combine with Invalidate()?
@@ -2647,17 +2688,17 @@ void
 GetUserMediaCallbackMediaStreamListener::StopScreenWindowSharing()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   if (mVideoSource && !mStopped &&
       (mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Screen ||
        mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Application ||
        mVideoSource->GetMediaSource() == dom::MediaSourceEnum::Window)) {
     // Stop the whole stream if there's no audio; just the video track if we have both
-    MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+    MediaManager::PostTask(FROM_HERE,
       new MediaOperationTask(mAudioSource ? MEDIA_STOP_TRACK : MEDIA_STOP,
                              this, nullptr, nullptr,
                              nullptr, mVideoSource,
                              mFinished, mWindowID, nullptr));
   }
 }
 
 // Stop backend for track
@@ -2665,17 +2706,17 @@ GetUserMediaCallbackMediaStreamListener:
 void
 GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aID, bool aIsAudio)
 {
   if (((aIsAudio && mAudioSource) ||
        (!aIsAudio && mVideoSource)) && !mStopped)
   {
     // XXX to support multiple tracks of a type in a stream, this should key off
     // the TrackID and not just the type
-    MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+    MediaManager::PostTask(FROM_HERE,
       new MediaOperationTask(MEDIA_STOP_TRACK,
                              this, nullptr, nullptr,
                              aIsAudio  ? mAudioSource : nullptr,
                              !aIsAudio ? mVideoSource : nullptr,
                              mFinished, mWindowID, nullptr));
   } else {
     LOG(("gUM track %d ended, but we don't have type %s",
          aID, aIsAudio ? "audio" : "video"));
@@ -2691,17 +2732,17 @@ GetUserMediaCallbackMediaStreamListener:
   NS_DispatchToMainThread(new GetUserMediaListenerRemove(mWindowID, this));
 }
 
 // Called from the MediaStreamGraph thread
 void
 GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph* aGraph,
                                                                bool aHasListeners)
 {
-  MediaManager::GetMessageLoop()->PostTask(FROM_HERE,
+  MediaManager::PostTask(FROM_HERE,
     new MediaOperationTask(MEDIA_DIRECT_LISTENERS,
                            this, nullptr, nullptr,
                            mAudioSource, mVideoSource,
                            aHasListeners, mWindowID, nullptr));
 }
 
 // Called from the MediaStreamGraph thread
 // this can be in response to our own RemoveListener() (via ::Remove()), or
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -66,16 +66,17 @@ public:
     , mWindowID(aWindowID)
     , mStopped(false)
     , mFinished(false)
     , mLock("mozilla::GUMCMSL")
     , mRemoved(false) {}
 
   ~GetUserMediaCallbackMediaStreamListener()
   {
+    unused << mMediaThread;
     // It's OK to release mStream on any thread; they have thread-safe
     // refcounts.
   }
 
   void Activate(already_AddRefed<SourceMediaStream> aStream,
     MediaEngineSource* aAudioSource,
     MediaEngineSource* aVideoSource)
   {
@@ -509,17 +510,17 @@ class MediaManager final : public nsIMed
 public:
   static already_AddRefed<MediaManager> GetInstance();
 
   // NOTE: never Dispatch(....,NS_DISPATCH_SYNC) to the MediaManager
   // thread from the MainThread, as we NS_DISPATCH_SYNC to MainThread
   // from MediaManager thread.
   static MediaManager* Get();
   static MediaManager* GetIfExists();
-  static MessageLoop* GetMessageLoop();
+  static void PostTask(const tracked_objects::Location& from_here, Task* task);
 #ifdef DEBUG
   static bool IsInMediaThread();
 #endif
 
   static bool Exists()
   {
     return !!sSingleton;
   }
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -62,31 +62,31 @@ GMPChild::GMPChild()
 
 GMPChild::~GMPChild()
 {
   LOGD("GMPChild dtor");
 }
 
 static bool
 GetFileBase(const std::string& aPluginPath,
-#if defined(XP_MACOSX)
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
             nsCOMPtr<nsIFile>& aLibDirectory,
 #endif
             nsCOMPtr<nsIFile>& aFileBase,
             nsAutoString& aBaseName)
 {
   nsDependentCString pluginPath(aPluginPath.c_str());
 
   nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(pluginPath),
                                 true, getter_AddRefs(aFileBase));
   if (NS_FAILED(rv)) {
     return false;
   }
 
-#if defined(XP_MACOSX)
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   if (NS_FAILED(aFileBase->Clone(getter_AddRefs(aLibDirectory)))) {
     return false;
   }
 #endif
 
   nsCOMPtr<nsIFile> parent;
   rv = aFileBase->GetParent(getter_AddRefs(parent));
   if (NS_FAILED(rv)) {
@@ -102,23 +102,23 @@ GetFileBase(const std::string& aPluginPa
   aBaseName = Substring(parentLeafName,
                         4,
                         parentLeafName.Length() - 1);
   return true;
 }
 
 static bool
 GetPluginFile(const std::string& aPluginPath,
-#if defined(XP_MACOSX)
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
               nsCOMPtr<nsIFile>& aLibDirectory,
 #endif
               nsCOMPtr<nsIFile>& aLibFile)
 {
   nsAutoString baseName;
-#ifdef XP_MACOSX
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   GetFileBase(aPluginPath, aLibDirectory, aLibFile, baseName);
 #else
   GetFileBase(aPluginPath, aLibFile, baseName);
 #endif
 
 #if defined(XP_MACOSX)
   nsAutoString binaryName = NS_LITERAL_STRING("lib") + baseName + NS_LITERAL_STRING(".dylib");
 #elif defined(OS_POSIX)
@@ -587,17 +587,17 @@ GMPChild::ShutdownComplete()
   SendAsyncShutdownComplete();
 }
 
 static bool
 GetPluginVoucherFile(const std::string& aPluginPath,
                      nsCOMPtr<nsIFile>& aOutVoucherFile)
 {
   nsAutoString baseName;
-#if defined(XP_MACOSX)
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   nsCOMPtr<nsIFile> libDir;
   GetFileBase(aPluginPath, aOutVoucherFile, libDir, baseName);
 #else
   GetFileBase(aPluginPath, aOutVoucherFile, baseName);
 #endif
   nsAutoString infoFileName = baseName + NS_LITERAL_STRING(".voucher");
   aOutVoucherFile->AppendRelativePath(infoFileName);
   return true;
--- a/dom/media/gmp/GMPLoader.cpp
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -75,17 +75,17 @@ public:
                     const GMPPlatformAPI* aPlatformAPI) override;
 
   virtual GMPErr GetAPI(const char* aAPIName,
                         void* aHostAPI,
                         void** aPluginAPI) override;
 
   virtual void Shutdown() override;
 
-#if defined(XP_MACOSX)
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   virtual void SetSandboxInfo(MacSandboxInfo* aSandboxInfo) override;
 #endif
 
 private:
   PRLibrary* mLib;
   GMPGetAPIFunc mGetAPIFunc;
   SandboxStarter* mSandboxStarter;
 };
@@ -270,17 +270,17 @@ GMPLoaderImpl::Shutdown()
     if (shutdownFunc) {
       shutdownFunc();
     }
     PR_UnloadLibrary(mLib);
     mLib = nullptr;
   }
 }
 
-#if defined(XP_MACOSX)
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
 void
 GMPLoaderImpl::SetSandboxInfo(MacSandboxInfo* aSandboxInfo)
 {
   if (mSandboxStarter) {
     mSandboxStarter->SetSandboxInfo(aSandboxInfo);
   }
 }
 #endif
--- a/dom/media/gmp/GMPLoader.h
+++ b/dom/media/gmp/GMPLoader.h
@@ -5,28 +5,28 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GMP_LOADER_H__
 #define GMP_LOADER_H__
 
 #include <stdint.h>
 #include "gmp-entrypoints.h"
 
-#if defined(XP_MACOSX)
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
 #include "mozilla/Sandbox.h"
 #endif
 
 namespace mozilla {
 namespace gmp {
 
 class SandboxStarter {
 public:
   virtual ~SandboxStarter() {}
   virtual bool Start(const char* aLibPath) = 0;
-#if defined(XP_MACOSX)
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   // On OS X we need to set Mac-specific sandbox info just before we start the
   // sandbox, which we don't yet know when the GMPLoader and SandboxStarter
   // objects are created.
   virtual void SetSandboxInfo(MacSandboxInfo* aSandboxInfo) = 0;
 #endif
 };
 
 // Encapsulates generating the device-bound node id, activating the sandbox,
@@ -60,17 +60,17 @@ public:
 
   // Retrieves an interface pointer from the GMP.
   virtual GMPErr GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI) = 0;
 
   // Calls the GMPShutdown function exported by the GMP lib, and unloads the
   // plugin library.
   virtual void Shutdown() = 0;
 
-#if defined(XP_MACOSX)
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
   // On OS X we need to set Mac-specific sandbox info just before we start the
   // sandbox, which we don't yet know when the GMPLoader and SandboxStarter
   // objects are created.
   virtual void SetSandboxInfo(MacSandboxInfo* aSandboxInfo) = 0;
 #endif
 };
 
 // On Desktop, this function resides in plugin-container.
--- a/dom/media/webrtc/MediaEngine.h
+++ b/dom/media/webrtc/MediaEngine.h
@@ -59,16 +59,18 @@ public:
   virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
                                      nsTArray<nsRefPtr<MediaEngineVideoSource> >*) = 0;
 
   /* Populate an array of audio sources in the nsTArray. Also include devices
    * that are currently unavailable. */
   virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
                                      nsTArray<nsRefPtr<MediaEngineAudioSource> >*) = 0;
 
+  virtual void Shutdown() = 0;
+
 protected:
   virtual ~MediaEngine() {}
 };
 
 /**
  * Common abstract base class for audio and video sources.
  */
 class MediaEngineSource : public nsISupports
@@ -76,16 +78,18 @@ class MediaEngineSource : public nsISupp
 public:
   // code inside webrtc.org assumes these sizes; don't use anything smaller
   // without verifying it's ok
   static const unsigned int kMaxDeviceNameLength = 128;
   static const unsigned int kMaxUniqueIdLength = 256;
 
   virtual ~MediaEngineSource() {}
 
+  virtual void Shutdown() = 0;
+
   /* Populate the human readable name of this device in the nsAString */
   virtual void GetName(nsAString&) = 0;
 
   /* Populate the UUID of this device in the nsAString */
   virtual void GetUUID(nsAString&) = 0;
 
   /* Release the device back to the system. */
   virtual nsresult Deallocate() = 0;
@@ -240,16 +244,17 @@ protected:
 class MediaEngineAudioSource : public MediaEngineSource
 {
 public:
   virtual ~MediaEngineAudioSource() {}
 
   /* This call reserves but does not start the device. */
   virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs) = 0;
+
 protected:
   explicit MediaEngineAudioSource(MediaEngineState aState)
     : MediaEngineSource(aState) {}
   MediaEngineAudioSource()
     : MediaEngineSource(kReleased) {}
 
 };
 
--- a/dom/media/webrtc/MediaEngineCameraVideoSource.h
+++ b/dom/media/webrtc/MediaEngineCameraVideoSource.h
@@ -56,16 +56,18 @@ public:
   virtual nsresult TakePhoto(PhotoCallback* aCallback) override
   {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   uint32_t GetBestFitnessDistance(
       const nsTArray<const dom::MediaTrackConstraintSet*>& aConstraintSets) override;
 
+  virtual void Shutdown() override {};
+
 protected:
   struct CapabilityCandidate {
     explicit CapabilityCandidate(uint8_t index, uint32_t distance = 0)
     : mIndex(index), mDistance(distance) {}
 
     size_t mIndex;
     uint32_t mDistance;
   };
--- a/dom/media/webrtc/MediaEngineDefault.h
+++ b/dom/media/webrtc/MediaEngineDefault.h
@@ -31,16 +31,18 @@ class MediaEngineDefault;
  * The default implementation of the MediaEngine interface.
  */
 class MediaEngineDefaultVideoSource : public nsITimerCallback,
                                       public MediaEngineVideoSource
 {
 public:
   MediaEngineDefaultVideoSource();
 
+  virtual void Shutdown() override {};
+
   virtual void GetName(nsAString&) override;
   virtual void GetUUID(nsAString&) override;
 
   virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs) override;
   virtual nsresult Deallocate() override;
   virtual nsresult Start(SourceMediaStream*, TrackID) override;
   virtual nsresult Stop(SourceMediaStream*, TrackID) override;
@@ -99,16 +101,18 @@ protected:
 class SineWaveGenerator;
 
 class MediaEngineDefaultAudioSource : public nsITimerCallback,
                                       public MediaEngineAudioSource
 {
 public:
   MediaEngineDefaultAudioSource();
 
+  virtual void Shutdown() override {};
+
   virtual void GetName(nsAString&) override;
   virtual void GetUUID(nsAString&) override;
 
   virtual nsresult Allocate(const dom::MediaTrackConstraints &aConstraints,
                             const MediaEnginePrefs &aPrefs) override;
   virtual nsresult Deallocate() override;
   virtual nsresult Start(SourceMediaStream*, TrackID) override;
   virtual nsresult Stop(SourceMediaStream*, TrackID) override;
@@ -153,25 +157,33 @@ class MediaEngineDefault : public MediaE
 {
 public:
   explicit MediaEngineDefault(bool aHasFakeTracks = false)
     : mHasFakeTracks(aHasFakeTracks)
     , mMutex("mozilla::MediaEngineDefault")
   {}
 
   virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
-                                     nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
+                                     nsTArray<nsRefPtr<MediaEngineVideoSource> >*) override;
   virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
-                                     nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
+                                     nsTArray<nsRefPtr<MediaEngineAudioSource> >*) override;
+  virtual void Shutdown() override {
+    MutexAutoLock lock(mMutex);
+
+    mVSources.Clear();
+    mASources.Clear();
+  };
 
 protected:
   bool mHasFakeTracks;
 
 private:
-  ~MediaEngineDefault() {}
+  ~MediaEngineDefault() {
+    Shutdown();
+  }
 
   Mutex mMutex;
   // protected with mMutex:
 
   nsTArray<nsRefPtr<MediaEngineVideoSource> > mVSources;
   nsTArray<nsRefPtr<MediaEngineAudioSource> > mASources;
 };
 
--- a/dom/media/webrtc/MediaEngineTabVideoSource.h
+++ b/dom/media/webrtc/MediaEngineTabVideoSource.h
@@ -13,16 +13,17 @@ namespace mozilla {
 
 class MediaEngineTabVideoSource : public MediaEngineVideoSource, nsIDOMEventListener, nsITimerCallback {
   public:
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIDOMEVENTLISTENER
     NS_DECL_NSITIMERCALLBACK
     MediaEngineTabVideoSource();
 
+    virtual void Shutdown() override {};
     virtual void GetName(nsAString_internal&) override;
     virtual void GetUUID(nsAString_internal&) override;
     virtual nsresult Allocate(const dom::MediaTrackConstraints &,
                               const mozilla::MediaEnginePrefs&) override;
     virtual nsresult Deallocate() override;
     virtual nsresult Start(mozilla::SourceMediaStream*, mozilla::TrackID) override;
     virtual void SetDirectListeners(bool aHasDirectListeners) override {};
     virtual void NotifyPull(mozilla::MediaStreamGraph*, mozilla::SourceMediaStream*, mozilla::TrackID, mozilla::StreamTime) override;
--- a/dom/media/webrtc/MediaEngineWebRTC.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTC.cpp
@@ -269,20 +269,19 @@ MediaEngineWebRTC::EnumerateVideoDevices
       aVSources->AppendElement(vSource.get());
     } else {
       vSource = new MediaEngineWebRTCVideoSource(videoEngine, i, aMediaSource);
       mVideoSources.Put(uuid, vSource); // Hashtable takes ownership.
       aVSources->AppendElement(vSource);
     }
   }
 
-  if (mHasTabVideoSource || dom::MediaSourceEnum::Browser == aMediaSource)
+  if (mHasTabVideoSource || dom::MediaSourceEnum::Browser == aMediaSource) {
     aVSources->AppendElement(new MediaEngineTabVideoSource());
-
-  return;
+  }
 #endif
 }
 
 void
 MediaEngineWebRTC::EnumerateAudioDevices(dom::MediaSourceEnum aMediaSource,
                                          nsTArray<nsRefPtr<MediaEngineAudioSource> >* aASources)
 {
   ScopedCustomReleasePtr<webrtc::VoEBase> ptrVoEBase;
@@ -367,25 +366,53 @@ MediaEngineWebRTC::EnumerateAudioDevices
         mThread, mVoiceEngine, i, deviceName, uniqueId
       );
       mAudioSources.Put(uuid, aSource); // Hashtable takes ownership.
       aASources->AppendElement(aSource);
     }
   }
 }
 
+static PLDHashOperator
+ClearVideoSource (const nsAString&, // unused
+                  MediaEngineVideoSource* aData,
+                  void *userArg)
+{
+  if (aData) {
+    aData->Shutdown();
+  }
+  return PL_DHASH_NEXT;
+}
+
+static PLDHashOperator
+ClearAudioSource (const nsAString&, // unused
+                  MediaEngineWebRTCAudioSource* aData,
+                  void *userArg)
+{
+  if (aData) {
+    aData->Shutdown();
+  }
+  return PL_DHASH_NEXT;
+}
+
 void
 MediaEngineWebRTC::Shutdown()
 {
   // This is likely paranoia
   MutexAutoLock lock(mMutex);
 
-  // Clear callbacks before we go away since the engines may outlive us
+  LOG(("%s", __FUNCTION__));
+  // Shutdown all the sources, since we may have dangling references to the
+  // sources in nsDOMUserMediaStreams waiting for GC/CC
+  mVideoSources.EnumerateRead(ClearVideoSource, nullptr);
+  mAudioSources.EnumerateRead(ClearAudioSource, nullptr);
   mVideoSources.Clear();
   mAudioSources.Clear();
+
+  // Clear callbacks before we go away since the engines may outlive us
   if (mVideoEngine) {
     mVideoEngine->SetTraceCallback(nullptr);
     webrtc::VideoEngine::Delete(mVideoEngine);
   }
 
   if (mScreenEngine) {
     mScreenEngine->SetTraceCallback(nullptr);
     webrtc::VideoEngine::Delete(mScreenEngine);
--- a/dom/media/webrtc/MediaEngineWebRTC.h
+++ b/dom/media/webrtc/MediaEngineWebRTC.h
@@ -104,29 +104,30 @@ public:
   }
   virtual nsresult TakePhoto(PhotoCallback* aCallback) override
   {
     return NS_ERROR_NOT_IMPLEMENTED;
   }
 
   void Refresh(int aIndex);
 
+  virtual void Shutdown() override;
+
 protected:
   ~MediaEngineWebRTCVideoSource() { Shutdown(); }
 
 private:
   // Initialize the needed Video engine interfaces.
   void Init();
-  void Shutdown();
 
   // Engine variables.
   webrtc::VideoEngine* mVideoEngine; // Weak reference, don't free.
-  webrtc::ViEBase* mViEBase;
-  webrtc::ViECapture* mViECapture;
-  webrtc::ViERender* mViERender;
+  ScopedCustomReleasePtr<webrtc::ViEBase> mViEBase;
+  ScopedCustomReleasePtr<webrtc::ViECapture> mViECapture;
+  ScopedCustomReleasePtr<webrtc::ViERender> mViERender;
 
   int mMinFps; // Min rate we want to accept
   dom::MediaSourceEnum mMediaSource; // source of media (camera | application | screen)
 
   size_t NumCapabilities() override;
   void GetCapability(size_t aIndex, webrtc::CaptureCapability& aOut) override;
 };
 
@@ -190,22 +191,23 @@ public:
 
   // VoEMediaProcess.
   void Process(int channel, webrtc::ProcessingTypes type,
                int16_t audio10ms[], int length,
                int samplingFreq, bool isStereo) override;
 
   NS_DECL_THREADSAFE_ISUPPORTS
 
+  virtual void Shutdown() override;
+
 protected:
   ~MediaEngineWebRTCAudioSource() { Shutdown(); }
 
 private:
   void Init();
-  void Shutdown();
 
   webrtc::VoiceEngine* mVoiceEngine;
   ScopedCustomReleasePtr<webrtc::VoEBase> mVoEBase;
   ScopedCustomReleasePtr<webrtc::VoEExternalMedia> mVoERender;
   ScopedCustomReleasePtr<webrtc::VoENetwork> mVoENetwork;
   ScopedCustomReleasePtr<webrtc::VoEAudioProcessing> mVoEProcessing;
 
   // mMonitor protects mSources[] access/changes, and transitions of mState
@@ -234,22 +236,22 @@ private:
 
 class MediaEngineWebRTC : public MediaEngine
 {
 public:
   explicit MediaEngineWebRTC(MediaEnginePrefs& aPrefs);
 
   // Clients should ensure to clean-up sources video/audio sources
   // before invoking Shutdown on this class.
-  void Shutdown();
+  void Shutdown() override;
 
   virtual void EnumerateVideoDevices(dom::MediaSourceEnum,
-                                    nsTArray<nsRefPtr<MediaEngineVideoSource> >*);
+                                     nsTArray<nsRefPtr<MediaEngineVideoSource>>*) override;
   virtual void EnumerateAudioDevices(dom::MediaSourceEnum,
-                                    nsTArray<nsRefPtr<MediaEngineAudioSource> >*);
+                                     nsTArray<nsRefPtr<MediaEngineAudioSource>>*) override;
 private:
   ~MediaEngineWebRTC() {
     Shutdown();
 #if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
     AsyncLatencyLogger::Get()->Release();
 #endif
     gFarendObserver = nullptr;
   }
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -473,21 +473,22 @@ MediaEngineWebRTCAudioSource::Init()
   }
 }
 
 void
 MediaEngineWebRTCAudioSource::Shutdown()
 {
   if (!mInitDone) {
     // duplicate these here in case we failed during Init()
-    if (mChannel != -1) {
+    if (mChannel != -1 && mVoENetwork) {
       mVoENetwork->DeRegisterExternalTransport(mChannel);
     }
 
     delete mNullTransport;
+    mNullTransport = nullptr;
     return;
   }
 
   if (mState == kStarted) {
     SourceMediaStream *source;
     bool empty;
 
     while (1) {
@@ -509,16 +510,17 @@ MediaEngineWebRTCAudioSource::Shutdown()
   }
 
   mVoEBase->Terminate();
   if (mChannel != -1) {
     mVoENetwork->DeRegisterExternalTransport(mChannel);
   }
 
   delete mNullTransport;
+  mNullTransport = nullptr;
 
   mVoEProcessing = nullptr;
   mVoENetwork = nullptr;
   mVoERender = nullptr;
   mVoEBase = nullptr;
 
   mState = kReleased;
   mInitDone = false;
--- a/dom/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -261,17 +261,17 @@ MediaEngineWebRTCVideoSource::Deallocate
     // event isn't needed).
     // XXX Note if MainThread Dispatch()es NS_DISPATCH_SYNC to us we can deadlock.
     // XXX It might be nice to only do this if we're in shutdown...  Hard to be
     // sure when that is though.
     // Thread safety: a) we call this synchronously, and don't use ViECapture from
     // another thread anywhere else, b) ViEInputManager::DestroyCaptureDevice() grabs
     // an exclusive object lock and deletes it in a critical section, so all in all
     // this should be safe threadwise.
-    NS_DispatchToMainThread(WrapRunnable(mViECapture,
+    NS_DispatchToMainThread(WrapRunnable(mViECapture.get(),
                                          &webrtc::ViECapture::ReleaseCaptureDevice,
                                          mCaptureIndex),
                             NS_DISPATCH_SYNC);
 #else
     mViECapture->ReleaseCaptureDevice(mCaptureIndex);
 #endif
     mState = kReleased;
     LOG(("Video device %d deallocated", mCaptureIndex));
@@ -417,19 +417,20 @@ MediaEngineWebRTCVideoSource::Shutdown()
       Stop(source, kVideoTrack); // XXX change to support multiple tracks
     }
     MOZ_ASSERT(mState == kStopped);
   }
 
   if (mState == kAllocated || mState == kStopped) {
     Deallocate();
   }
-  mViECapture->Release();
-  mViERender->Release();
-  mViEBase->Release();
+  mViECapture = nullptr;
+  mViERender = nullptr;
+  mViEBase = nullptr;
+
   mState = kReleased;
   mInitDone = false;
 }
 
 void MediaEngineWebRTCVideoSource::Refresh(int aIndex) {
   // NOTE: mCaptureIndex might have changed when allocated!
   // Use aIndex to update information, but don't change mCaptureIndex!!
   // Caller looked up this source by uniqueId, so it shouldn't change
--- a/dom/network/PUDPSocket.ipdl
+++ b/dom/network/PUDPSocket.ipdl
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
 
 /* 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 protocol PNecko;
+include protocol PBackground;
 include protocol PBlob;
 include InputStreamParams;
 
 include "mozilla/net/NeckoMessageUtils.h";
 include "mozilla/net/DNS.h";
 include "prio.h";
 
 using mozilla::net::NetAddr from "mozilla/net/DNS.h";
@@ -32,17 +33,17 @@ union UDPData {
 };
 
 namespace mozilla {
 namespace net {
 
 //-------------------------------------------------------------------
 protocol PUDPSocket
 {
-  manager PNecko;
+  manager PNecko or PBackground;
 
 parent:
   Bind(UDPAddressInfo addressInfo, bool addressReuse, bool loopback);
 
   OutgoingData(UDPData data, UDPSocketAddr addr);
 
   JoinMulticast(nsCString multicastAddress, nsCString iface);
   LeaveMulticast(nsCString multicastAddress, nsCString iface);
--- a/dom/network/UDPSocket.cpp
+++ b/dom/network/UDPSocket.cpp
@@ -313,18 +313,20 @@ UDPSocket::Send(const StringOrBlobOrArra
 
   MOZ_ASSERT(mSocket || mSocketChild);
 
   // If the remote address and port were not specified in the constructor or as arguments,
   // throw InvalidAccessError.
   nsCString remoteAddress;
   if (aRemoteAddress.WasPassed()) {
     remoteAddress = NS_ConvertUTF16toUTF8(aRemoteAddress.Value());
+    UDPSOCKET_LOG(("%s: Send to %s", __FUNCTION__, remoteAddress.get()));
   } else if (!mRemoteAddress.IsVoid()) {
     remoteAddress = mRemoteAddress;
+    UDPSOCKET_LOG(("%s: Send to %s", __FUNCTION__, remoteAddress.get()));
   } else {
     aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
     return false;
   }
 
   uint16_t remotePort;
   if (aRemotePort.WasPassed() && !aRemotePort.Value().IsNull()) {
     remotePort = aRemotePort.Value().Value();
@@ -408,16 +410,17 @@ UDPSocket::InitLocal(const nsAString& aL
 
   if (aLocalAddress.IsEmpty()) {
     rv = sock->Init(aLocalPort, /* loopback = */ false, principal,
                     mAddressReuse, /* optionalArgc = */ 1);
   } else {
     PRNetAddr prAddr;
     PR_InitializeNetAddr(PR_IpAddrAny, aLocalPort, &prAddr);
     PR_StringToNetAddr(NS_ConvertUTF16toUTF8(aLocalAddress).BeginReading(), &prAddr);
+    UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, NS_ConvertUTF16toUTF8(aLocalAddress).get(), aLocalPort));
 
     mozilla::net::NetAddr addr;
     PRNetAddrToNetAddr(&prAddr, &addr);
     rv = sock->InitWithAddress(&addr, principal, mAddressReuse,
                                /* optionalArgc = */ 1);
   }
   if (NS_FAILED(rv)) {
     return rv;
--- a/dom/network/UDPSocket.h
+++ b/dom/network/UDPSocket.h
@@ -13,16 +13,25 @@
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/SocketCommonBinding.h"
 #include "nsIUDPSocket.h"
 #include "nsIUDPSocketChild.h"
 #include "nsTArray.h"
 
 struct JSContext;
 
+#if defined(PR_LOGGING)
+//
+// set NSPR_LOG_MODULES=UDPSocket:5
+//
+extern PRLogModuleInfo *gUDPSocketLog;
+#endif
+#define UDPSOCKET_LOG(args)     PR_LOG(gUDPSocketLog, PR_LOG_DEBUG, args)
+#define UDPSOCKET_LOG_ENABLED() PR_LOG_TEST(gUDPSocketLog, PR_LOG_DEBUG)
+
 namespace mozilla {
 namespace dom {
 
 struct UDPOptions;
 class StringOrBlobOrArrayBufferOrArrayBufferView;
 
 class UDPSocket final : public DOMEventTargetHelper
                       , public nsIUDPSocketListener
--- a/dom/network/UDPSocketChild.cpp
+++ b/dom/network/UDPSocketChild.cpp
@@ -4,19 +4,33 @@
  * 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 "UDPSocketChild.h"
 #include "mozilla/unused.h"
 #include "mozilla/ipc/InputStreamUtils.h"
 #include "mozilla/net/NeckoChild.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
 
 using mozilla::net::gNeckoChild;
 
+#if defined(PR_LOGGING)
+//
+// set NSPR_LOG_MODULES=UDPSocket:5
+//
+extern PRLogModuleInfo *gUDPSocketLog;
+#endif
+#define UDPSOCKET_LOG(args)     PR_LOG(gUDPSocketLog, PR_LOG_DEBUG, args)
+#define UDPSOCKET_LOG_ENABLED() PR_LOG_TEST(gUDPSocketLog, PR_LOG_DEBUG)
+
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(UDPSocketChildBase, nsIUDPSocketChild)
 
 UDPSocketChildBase::UDPSocketChildBase()
 : mIPCOpen(false)
 {
@@ -49,41 +63,139 @@ NS_IMETHODIMP_(MozExternalRefCountType) 
   if (refcnt == 1 && mIPCOpen) {
     PUDPSocketChild::SendRequestDelete();
     return 1;
   }
   return refcnt;
 }
 
 UDPSocketChild::UDPSocketChild()
-:mLocalPort(0)
+:mBackgroundManager(nullptr)
+,mLocalPort(0)
 {
 }
 
 UDPSocketChild::~UDPSocketChild()
 {
 }
 
+class UDPSocketBackgroundChildCallback final :
+  public nsIIPCBackgroundChildCreateCallback
+{
+  bool* mDone;
+
+public:
+  explicit UDPSocketBackgroundChildCallback(bool* aDone)
+  : mDone(aDone)
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(mDone);
+    MOZ_ASSERT(!*mDone);
+  }
+
+  NS_DECL_ISUPPORTS
+
+private:
+  ~UDPSocketBackgroundChildCallback()
+  { }
+
+  virtual void
+  ActorCreated(PBackgroundChild* aActor) override
+  {
+    *mDone = true;
+  }
+
+  virtual void
+  ActorFailed() override
+  {
+    *mDone = true;
+  }
+};
+
+NS_IMPL_ISUPPORTS(UDPSocketBackgroundChildCallback, nsIIPCBackgroundChildCreateCallback)
+
+nsresult
+UDPSocketChild::CreatePBackgroundSpinUntilDone()
+{
+  using mozilla::ipc::BackgroundChild;
+
+  // Spinning the event loop in MainThread would be dangerous
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
+
+  bool done = false;
+  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
+    new UDPSocketBackgroundChildCallback(&done);
+
+  if (NS_WARN_IF(!BackgroundChild::GetOrCreateForCurrentThread(callback))) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsIThread* thread = NS_GetCurrentThread();
+  while (!done) {
+    if (NS_WARN_IF(!NS_ProcessNextEvent(thread, true /* aMayWait */))) {
+      return NS_ERROR_FAILURE;
+    }
+  }
+
+  if (NS_WARN_IF(!BackgroundChild::GetForCurrentThread())) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
 // nsIUDPSocketChild Methods
 
 NS_IMETHODIMP
+UDPSocketChild::SetBackgroundSpinsEvents()
+{
+  using mozilla::ipc::BackgroundChild;
+
+  PBackgroundChild* existingBackgroundChild =
+    BackgroundChild::GetForCurrentThread();
+  // If it's not spun up yet, block until it is, and retry
+  if (!existingBackgroundChild) {
+    nsresult rv = CreatePBackgroundSpinUntilDone();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    existingBackgroundChild =
+      BackgroundChild::GetForCurrentThread();
+    MOZ_ASSERT(existingBackgroundChild);
+  }
+  // By now PBackground is guaranteed to be/have-been up
+  mBackgroundManager = existingBackgroundChild;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 UDPSocketChild::Bind(nsIUDPSocketInternal* aSocket,
                      nsIPrincipal* aPrincipal,
                      const nsACString& aHost,
                      uint16_t aPort,
                      bool aAddressReuse,
                      bool aLoopback)
 {
+  UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort));
+
   NS_ENSURE_ARG(aSocket);
 
   mSocket = aSocket;
   AddIPDLReference();
 
-  gNeckoChild->SendPUDPSocketConstructor(this, IPC::Principal(aPrincipal),
-                                         mFilterName);
+  if (mBackgroundManager) {
+    // If we want to support a passed-in principal here we'd need to
+    // convert it to a PrincipalInfo
+    MOZ_ASSERT(!aPrincipal);
+    mBackgroundManager->SendPUDPSocketConstructor(this, void_t(), mFilterName);
+  } else {
+    gNeckoChild->SendPUDPSocketConstructor(this, IPC::Principal(aPrincipal),
+                                           mFilterName);
+  }
 
   SendBind(UDPAddressInfo(nsCString(aHost), aPort), aAddressReuse, aLoopback);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UDPSocketChild::Close()
 {
@@ -94,42 +206,45 @@ UDPSocketChild::Close()
 NS_IMETHODIMP
 UDPSocketChild::Send(const nsACString& aHost,
                      uint16_t aPort,
                      const uint8_t* aData,
                      uint32_t aByteLength)
 {
   NS_ENSURE_ARG(aData);
 
+  UDPSOCKET_LOG(("%s: %s:%u - %u bytes", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort, aByteLength));
   return SendDataInternal(UDPSocketAddr(UDPAddressInfo(nsCString(aHost), aPort)),
                           aData, aByteLength);
 }
 
 NS_IMETHODIMP
 UDPSocketChild::SendWithAddr(nsINetAddr* aAddr,
                              const uint8_t* aData,
                              uint32_t aByteLength)
 {
   NS_ENSURE_ARG(aAddr);
   NS_ENSURE_ARG(aData);
 
   NetAddr addr;
   aAddr->GetNetAddr(&addr);
 
+  UDPSOCKET_LOG(("%s: %u bytes", __FUNCTION__, aByteLength));
   return SendDataInternal(UDPSocketAddr(addr), aData, aByteLength);
 }
 
 NS_IMETHODIMP
 UDPSocketChild::SendWithAddress(const NetAddr* aAddr,
                                 const uint8_t* aData,
                                 uint32_t aByteLength)
 {
   NS_ENSURE_ARG(aAddr);
   NS_ENSURE_ARG(aData);
 
+  UDPSOCKET_LOG(("%s: %u bytes", __FUNCTION__, aByteLength));
   return SendDataInternal(UDPSocketAddr(*aAddr), aData, aByteLength);
 }
 
 nsresult
 UDPSocketChild::SendDataInternal(const UDPSocketAddr& aAddr,
                                  const uint8_t* aData,
                                  const uint32_t aByteLength)
 {
@@ -156,16 +271,17 @@ UDPSocketChild::SendBinaryStream(const n
   NS_ENSURE_ARG(aStream);
 
   OptionalInputStreamParams stream;
   nsTArray<mozilla::ipc::FileDescriptor> fds;
   SerializeInputStream(aStream, stream, fds);
 
   MOZ_ASSERT(fds.IsEmpty());
 
+  UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, PromiseFlatCString(aHost).get(), aPort));
   SendOutgoingData(UDPData(stream), UDPSocketAddr(UDPAddressInfo(nsCString(aHost), aPort)));
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 UDPSocketChild::JoinMulticast(const nsACString& aMulticastAddress,
                               const nsACString& aInterface)
@@ -218,16 +334,17 @@ UDPSocketChild::GetFilterName(nsACString
 
 // PUDPSocketChild Methods
 bool
 UDPSocketChild::RecvCallbackOpened(const UDPAddressInfo& aAddressInfo)
 {
   mLocalAddress = aAddressInfo.addr();
   mLocalPort = aAddressInfo.port();
 
+  UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, mLocalAddress.get(), mLocalPort));
   nsresult rv = mSocket->CallListenerOpened();
   mozilla::unused << NS_WARN_IF(NS_FAILED(rv));
 
   return true;
 }
 
 bool
 UDPSocketChild::RecvCallbackClosed()
@@ -237,28 +354,31 @@ UDPSocketChild::RecvCallbackClosed()
 
   return true;
 }
 
 bool
 UDPSocketChild::RecvCallbackReceivedData(const UDPAddressInfo& aAddressInfo,
                                          InfallibleTArray<uint8_t>&& aData)
 {
+  UDPSOCKET_LOG(("%s: %s:%u length %u", __FUNCTION__,
+                 aAddressInfo.addr().get(), aAddressInfo.port(), aData.Length()));
   nsresult rv = mSocket->CallListenerReceivedData(aAddressInfo.addr(), aAddressInfo.port(),
                                                   aData.Elements(), aData.Length());
   mozilla::unused << NS_WARN_IF(NS_FAILED(rv));
 
   return true;
 }
 
 bool
 UDPSocketChild::RecvCallbackError(const nsCString& aMessage,
                                   const nsCString& aFilename,
                                   const uint32_t& aLineNumber)
 {
+  UDPSOCKET_LOG(("%s: %s:%s:%u", __FUNCTION__, aMessage.get(), aFilename.get(), aLineNumber));
   nsresult rv = mSocket->CallListenerError(aMessage, aFilename, aLineNumber);
   mozilla::unused << NS_WARN_IF(NS_FAILED(rv));
 
   return true;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/network/UDPSocketChild.h
+++ b/dom/network/UDPSocketChild.h
@@ -37,29 +37,32 @@ class UDPSocketChild : public mozilla::n
 {
 public:
   NS_DECL_NSIUDPSOCKETCHILD
   NS_IMETHOD_(MozExternalRefCountType) Release() override;
 
   UDPSocketChild();
   virtual ~UDPSocketChild();
 
+  nsresult CreatePBackgroundSpinUntilDone();
+
   virtual bool RecvCallbackOpened(const UDPAddressInfo& aAddressInfo) override;
   virtual bool RecvCallbackClosed() override;
   virtual bool RecvCallbackReceivedData(const UDPAddressInfo& aAddressInfo,
                                         InfallibleTArray<uint8_t>&& aData) override;
   virtual bool RecvCallbackError(const nsCString& aMessage,
                                  const nsCString& aFilename,
                                  const uint32_t& aLineNumber) override;
 
 private:
   nsresult SendDataInternal(const UDPSocketAddr& aAddr,
                             const uint8_t* aData,
                             const uint32_t aByteLength);
 
+  mozilla::ipc::PBackgroundChild* mBackgroundManager;
   uint16_t mLocalPort;
   nsCString mLocalAddress;
   nsCString mFilterName;
 };
 
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/network/UDPSocketParent.cpp
+++ b/dom/network/UDPSocketParent.cpp
@@ -15,24 +15,44 @@
 #include "mozilla/net/DNS.h"
 #include "mozilla/net/NeckoCommon.h"
 #include "mozilla/net/PNeckoParent.h"
 #include "nsNetUtil.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabParent.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptSecurityManager.h"
+#include "mozilla/ipc/PBackgroundParent.h"
+
+#if defined(PR_LOGGING)
+//
+// set NSPR_LOG_MODULES=UDPSocket:5
+//
+extern PRLogModuleInfo *gUDPSocketLog;
+#endif
+#define UDPSOCKET_LOG(args)     PR_LOG(gUDPSocketLog, PR_LOG_DEBUG, args)
+#define UDPSOCKET_LOG_ENABLED() PR_LOG_TEST(gUDPSocketLog, PR_LOG_DEBUG)
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ISUPPORTS(UDPSocketParent, nsIUDPSocketListener)
 
-UDPSocketParent::UDPSocketParent()
-  : mIPCOpen(true)
+UDPSocketParent::UDPSocketParent(PBackgroundParent* aManager)
+  : mBackgroundManager(aManager)
+  , mNeckoManager(nullptr)
+  , mIPCOpen(true)
+{
+  mObserver = new mozilla::net::OfflineObserver(this);
+}
+
+UDPSocketParent::UDPSocketParent(PNeckoParent* aManager)
+  : mBackgroundManager(nullptr)
+  , mNeckoManager(aManager)
+  , mIPCOpen(true)
 {
   mObserver = new mozilla::net::OfflineObserver(this);
 }
 
 UDPSocketParent::~UDPSocketParent()
 {
   if (mObserver) {
     mObserver->RemoveObserver();
@@ -73,22 +93,32 @@ UDPSocketParent::GetAppId()
   }
   return appId;
 }
 
 bool
 UDPSocketParent::Init(const IPC::Principal& aPrincipal,
                       const nsACString& aFilter)
 {
+  MOZ_ASSERT_IF(mBackgroundManager, !aPrincipal);
+  // will be used once we move all UDPSocket to PBackground, or
+  // if we add in Principal checking for mtransport
+  unused << mBackgroundManager;
+  
   mPrincipal = aPrincipal;
   if (net::UsingNeckoIPCSecurity() &&
       mPrincipal &&
       !ContentParent::IgnoreIPCPrincipal()) {
-    if (!AssertAppPrincipal(Manager()->Manager(), mPrincipal)) {
-      return false;
+    if (mNeckoManager) {
+      if (!AssertAppPrincipal(mNeckoManager->Manager(), mPrincipal)) {
+        return false;
+      }
+    } else {
+      // PBackground is (for now) using a STUN filter for verification
+      // it's not being used for DoS
     }
 
     nsCOMPtr<nsIPermissionManager> permMgr =
       services::GetPermissionManager();
     if (!permMgr) {
       NS_WARNING("No PermissionManager available!");
       return false;
     }
@@ -116,29 +146,31 @@ UDPSocketParent::Init(const IPC::Princip
     } else {
       printf_stderr("Content doesn't have a valid filter. "
                     "filter name: %s.", aFilter.BeginReading());
       return false;
     }
   }
   // We don't have browser actors in xpcshell, and hence can't run automated
   // tests without this loophole.
-  if (net::UsingNeckoIPCSecurity() && !mFilter && 
+  if (net::UsingNeckoIPCSecurity() && !mFilter &&
       (!mPrincipal || ContentParent::IgnoreIPCPrincipal())) {
     return false;
   }
   return true;
 }
 
 // PUDPSocketParent methods
 
 bool
 UDPSocketParent::RecvBind(const UDPAddressInfo& aAddressInfo,
                           const bool& aAddressReuse, const bool& aLoopback)
 {
+  UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, aAddressInfo.addr().get(), aAddressInfo.port()));
+
   if (NS_FAILED(BindInternal(aAddressInfo.addr(), aAddressInfo.port(), aAddressReuse, aLoopback))) {
     FireInternalError(__LINE__);
     return true;
   }
 
   nsCOMPtr<nsINetAddr> localAddr;
   mSocket->GetLocalAddr(getter_AddRefs(localAddr));
 
@@ -149,27 +181,30 @@ UDPSocketParent::RecvBind(const UDPAddre
   }
 
   uint16_t port;
   if (NS_FAILED(localAddr->GetPort(&port))) {
     FireInternalError(__LINE__);
     return true;
   }
 
+  UDPSOCKET_LOG(("%s: SendCallbackOpened: %s:%u", __FUNCTION__, addr.get(), port));
   mozilla::unused << SendCallbackOpened(UDPAddressInfo(addr, port));
 
   return true;
 }
 
 nsresult
 UDPSocketParent::BindInternal(const nsCString& aHost, const uint16_t& aPort,
                               const bool& aAddressReuse, const bool& aLoopback)
 {
   nsresult rv;
 
+  UDPSOCKET_LOG(("%s: %s:%u", __FUNCTION__, nsCString(aHost).get(), aPort));
+
   nsCOMPtr<nsIUDPSocket> sock =
       do_CreateInstance("@mozilla.org/network/udp-socket;1", &rv);
 
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   if (aHost.IsEmpty()) {
@@ -393,27 +428,31 @@ UDPSocketParent::OnPacketReceived(nsIUDP
   fromAddr->GetPort(&port);
   fromAddr->GetAddress(ip);
 
   nsCString data;
   aMessage->GetData(data);
 
   const char* buffer = data.get();
   uint32_t len = data.Length();
+  UDPSOCKET_LOG(("%s: %s:%u, length %u", __FUNCTION__, ip.get(), port, len));
 
   if (mFilter) {
     bool allowed;
     mozilla::net::NetAddr addr;
     fromAddr->GetNetAddr(&addr);
     nsresult rv = mFilter->FilterPacket(&addr,
                                         (const uint8_t*)buffer, len,
                                         nsIUDPSocketFilter::SF_INCOMING,
                                         &allowed);
     // Receiving unallowed data, drop.
     if (NS_WARN_IF(NS_FAILED(rv)) || !allowed) {
+      if (!allowed) {
+        UDPSOCKET_LOG(("%s: not allowed", __FUNCTION__));
+      }
       return NS_OK;
     }
   }
 
   FallibleTArray<uint8_t> fallibleArray;
   if (!fallibleArray.InsertElementsAt(0, buffer, len)) {
     FireInternalError(__LINE__);
     return NS_ERROR_OUT_OF_MEMORY;
--- a/dom/network/UDPSocketParent.h
+++ b/dom/network/UDPSocketParent.h
@@ -10,27 +10,32 @@
 #include "mozilla/net/PUDPSocketParent.h"
 #include "nsCOMPtr.h"
 #include "nsIUDPSocket.h"
 #include "nsIUDPSocketFilter.h"
 #include "mozilla/net/OfflineObserver.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 
 namespace mozilla {
+namespace net {
+class PNeckoParent;
+}
+
 namespace dom {
 
 class UDPSocketParent : public mozilla::net::PUDPSocketParent
                       , public nsIUDPSocketListener
                       , public mozilla::net::DisconnectableParent
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIUDPSOCKETLISTENER
 
-  UDPSocketParent();
+  explicit UDPSocketParent(PBackgroundParent* aManager);
+  explicit UDPSocketParent(PNeckoParent* aManager);
 
   bool Init(const IPC::Principal& aPrincipal, const nsACString& aFilter);
 
   virtual bool RecvBind(const UDPAddressInfo& aAddressInfo,
                         const bool& aAddressReuse, const bool& aLoopback) override;
 
   virtual bool RecvOutgoingData(const UDPData& aData, const UDPSocketAddr& aAddr) override;
 
@@ -49,16 +54,20 @@ private:
   virtual void ActorDestroy(ActorDestroyReason why) override;
   void Send(const InfallibleTArray<uint8_t>& aData, const UDPSocketAddr& aAddr);
   void Send(const InputStreamParams& aStream, const UDPSocketAddr& aAddr);
   nsresult BindInternal(const nsCString& aHost, const uint16_t& aPort,
                         const bool& aAddressReuse, const bool& aLoopback);
 
   void FireInternalError(uint32_t aLineNo);
 
+  // One of these will be null and the other non-null.
+  PBackgroundParent* mBackgroundManager;
+  PNeckoParent* mNeckoManager;
+
   bool mIPCOpen;
   nsCOMPtr<nsIUDPSocket> mSocket;
   nsCOMPtr<nsIUDPSocketFilter> mFilter;
   nsRefPtr<mozilla::net::OfflineObserver> mObserver;
   nsCOMPtr<nsIPrincipal> mPrincipal;
 };
 
 } // namespace dom
--- a/dom/network/interfaces/nsIUDPSocketChild.idl
+++ b/dom/network/interfaces/nsIUDPSocketChild.idl
@@ -14,23 +14,26 @@ namespace mozilla {
 namespace net {
 union NetAddr;
 }
 }
 %}
 native NetAddr(mozilla::net::NetAddr);
 [ptr] native NetAddrPtr(mozilla::net::NetAddr);
 
-[scriptable, uuid(dd636fc5-05a0-401c-832f-2c07f3477c60)]
+[scriptable, uuid(481f15ce-224a-40b6-9927-7effbc326776)]
 interface nsIUDPSocketChild : nsISupports
 {
   readonly attribute unsigned short localPort;
   readonly attribute AUTF8String localAddress;
   attribute AUTF8String filterName;
 
+  // Allow hosting this over PBackground instead of PNecko
+  [noscript] void setBackgroundSpinsEvents();
+
   // Tell the chrome process to bind the UDP socket to a given local host and port
   void bind(in nsIUDPSocketInternal socket, in nsIPrincipal principal,
             in AUTF8String host, in unsigned short port,
             in bool addressReuse, in bool loopback);
 
   // Tell the chrome process to perform equivalent operations to all following methods
   void send(in AUTF8String host, in unsigned short port,
             [const, array, size_is(byteLength)] in uint8_t bytes,
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -1949,17 +1949,17 @@ NPError
 #endif
     return NPERR_GENERIC_ERROR;
   }
 
   case NPNVxtAppContext:
     return NPERR_GENERIC_ERROR;
 #endif
 
-#if defined(XP_WIN) || (MOZ_WIDGET_GTK == 2) || defined(MOZ_WIDGET_QT)
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
   case NPNVnetscapeWindow: {
     if (!npp || !npp->ndata)
       return NPERR_INVALID_INSTANCE_ERROR;
 
     nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *) npp->ndata;
 
     nsRefPtr<nsPluginInstanceOwner> owner = inst->GetOwner();
     NS_ENSURE_TRUE(owner, NPERR_NO_ERROR);
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -476,17 +476,18 @@ PluginModuleChromeParent::LoadModule(con
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
     nsAutoCString sandboxPref("dom.ipc.plugins.sandbox-level.");
     sandboxPref.Append(aPluginTag->GetNiceFileName());
     if (NS_FAILED(Preferences::GetInt(sandboxPref.get(), &sandboxLevel))) {
       sandboxLevel = Preferences::GetInt("dom.ipc.plugins.sandbox-level.default");
     }
 #endif
 
-    nsAutoPtr<PluginModuleChromeParent> parent(new PluginModuleChromeParent(aFilePath, aPluginId));
+    nsAutoPtr<PluginModuleChromeParent> parent(new PluginModuleChromeParent(aFilePath, aPluginId,
+                                                                            sandboxLevel));
     UniquePtr<LaunchCompleteTask> onLaunchedRunnable(new LaunchedTask(parent));
     parent->mSubprocess->SetCallRunnableImmediately(!parent->mIsStartingAsync);
     TimeStamp launchStart = TimeStamp::Now();
     bool launched = parent->mSubprocess->Launch(Move(onLaunchedRunnable),
                                                 sandboxLevel);
     if (!launched) {
         // We never reached open
         parent->mShutdown = true;
@@ -559,19 +560,21 @@ PluginModuleChromeParent::OnProcessLaunc
 #ifdef XP_WIN
     { // Scope for lock
         mozilla::MutexAutoLock lock(mCrashReporterMutex);
         mCrashReporter = CrashReporter();
     }
 #endif
 #endif
 
-#ifdef XP_WIN
+#if defined(XP_WIN) && defined(_X86_)
+    // Protected mode only applies to Windows and only to x86.
     if (!mIsBlocklisted && mIsFlashPlugin &&
-        Preferences::GetBool("dom.ipc.plugins.flash.disable-protected-mode", false)) {
+        (Preferences::GetBool("dom.ipc.plugins.flash.disable-protected-mode", false) ||
+         mSandboxLevel >= 2)) {
         SendDisableFlashProtectedMode();
     }
 #endif
 
     if (mInitOnAsyncConnect) {
         mInitOnAsyncConnect = false;
 #if defined(XP_WIN)
         mAsyncInitRv = NP_GetEntryPoints(mNPPIface,
@@ -657,28 +660,31 @@ PluginModuleContentParent::PluginModuleC
 
 PluginModuleContentParent::~PluginModuleContentParent()
 {
     Preferences::UnregisterCallback(TimeoutChanged, kContentTimeoutPref, this);
 }
 
 bool PluginModuleChromeParent::sInstantiated = false;
 
-PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId)
+PluginModuleChromeParent::PluginModuleChromeParent(const char* aFilePath,
+                                                   uint32_t aPluginId,
+                                                   int32_t aSandboxLevel)
     : PluginModuleParent(true)
     , mSubprocess(new PluginProcessParent(aFilePath))
     , mPluginId(aPluginId)
     , mChromeTaskFactory(this)
     , mHangAnnotationFlags(0)
     , mHangAnnotatorMutex("PluginModuleChromeParent::mHangAnnotatorMutex")
 #ifdef XP_WIN
     , mPluginCpuUsageOnHang()
     , mHangUIParent(nullptr)
     , mHangUIEnabled(true)
     , mIsTimerReset(true)
+    , mSandboxLevel(aSandboxLevel)
 #ifdef MOZ_CRASHREPORTER
     , mCrashReporterMutex("PluginModuleChromeParent::mCrashReporterMutex")
     , mCrashReporter(nullptr)
 #endif
 #endif
 #ifdef MOZ_CRASHREPORTER_INJECTOR
     , mFlashProcess1(0)
     , mFlashProcess2(0)
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -438,17 +438,18 @@ private:
 
 #if defined(XP_WIN) || defined(XP_MACOSX)
     virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) override;
 #endif
 
     virtual void ActorDestroy(ActorDestroyReason why) override;
 
     // aFilePath is UTF8, not native!
-    explicit PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId);
+    explicit PluginModuleChromeParent(const char* aFilePath, uint32_t aPluginId,
+                                      int32_t aSandboxLevel);
 
     CrashReporterParent* CrashReporter();
 
     void CleanupFromTimeout(const bool aByHangUI);
 
     virtual void UpdatePluginTimeout() override;
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
@@ -478,16 +479,17 @@ private:
     Atomic<uint32_t> mHangAnnotationFlags;
     mozilla::Mutex mHangAnnotatorMutex;
     InfallibleTArray<mozilla::ipc::IProtocol*> mProtocolCallStack;
 #ifdef XP_WIN
     InfallibleTArray<float> mPluginCpuUsageOnHang;
     PluginHangUIParent *mHangUIParent;
     bool mHangUIEnabled;
     bool mIsTimerReset;
+    int32_t mSandboxLevel;
 #ifdef MOZ_CRASHREPORTER
     /**
      * This mutex protects the crash reporter when the Plugin Hang UI event
      * handler is executing off main thread. It is intended to protect both
      * the mCrashReporter variable in addition to the CrashReporterParent object
      * that mCrashReporter refers to.
      */
     mozilla::Mutex mCrashReporterMutex;
--- a/dom/plugins/ipc/PluginProcessParent.cpp
+++ b/dom/plugins/ipc/PluginProcessParent.cpp
@@ -71,37 +71,47 @@ AddSandboxAllowedFile(vector<std::wstrin
     return;
 }
 
 static void
 AddSandboxAllowedFiles(int32_t aSandboxLevel,
                        vector<std::wstring>& aAllowedFilesRead,
                        vector<std::wstring>& aAllowedFilesReadWrite)
 {
-    if (aSandboxLevel < 3) {
+    if (aSandboxLevel < 2) {
         return;
     }
 
     nsresult rv;
     nsCOMPtr<nsIProperties> dirSvc =
         do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv);
     if (NS_WARN_IF(NS_FAILED(rv))) {
         return;
     }
 
-    AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR);
-    AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR,
-                          NS_LITERAL_STRING("\\*"));
+    // Higher than level 2 currently removes the users own rights.
+    if (aSandboxLevel > 2) {
+        AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR);
+        AddSandboxAllowedFile(aAllowedFilesRead, dirSvc, NS_WIN_HOME_DIR,
+                              NS_LITERAL_STRING("\\*"));
+    }
 
+    // Level 2 and above is now using low integrity, so we need to give write
+    // access to the Flash directories.
     AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
                           NS_LITERAL_STRING("\\Macromedia\\Flash Player\\*"));
     AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_WIN_APPDATA_DIR,
                           NS_LITERAL_STRING("\\Adobe\\Flash Player\\*"));
+
+#if defined(_X86_)
+    // Write access to the Temp directory should only be needed for 32-bit as
+    // it is used to turn off protected mode, which only applies to x86.
     AddSandboxAllowedFile(aAllowedFilesReadWrite, dirSvc, NS_OS_TEMP_DIR,
                           NS_LITERAL_STRING("\\*"));
+#endif
 }
 #endif
 
 bool
 PluginProcessParent::Launch(mozilla::UniquePtr<LaunchCompleteTask> aLaunchCompleteTask,
                             int32_t aSandboxLevel)
 {
 #if defined(XP_WIN) && defined(MOZ_SANDBOX)
new file mode 100644
--- /dev/null
+++ b/dom/webidl/CaretStateChangedEvent.webidl
@@ -0,0 +1,32 @@
+/* -*- 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/.
+ */
+
+enum CaretChangedReason {
+  "visibilitychange",
+  "updateposition",
+  "longpressonemptycontent",
+  "taponcaret",
+  "presscaret",
+  "releasecaret"
+};
+
+dictionary CaretStateChangedEventInit : EventInit {
+  boolean collapsed = true;
+  DOMRectReadOnly? boundingClientRect = null;
+  CaretChangedReason reason = "visibilitychange";
+  boolean caretVisible = false;
+  boolean selectionVisible = false;
+};
+
+[Constructor(DOMString type, optional CaretStateChangedEventInit eventInit),
+ ChromeOnly]
+interface CaretStateChangedEvent : Event {
+  readonly attribute boolean collapsed;
+  readonly attribute DOMRectReadOnly? boundingClientRect;
+  readonly attribute CaretChangedReason reason;
+  readonly attribute boolean caretVisible;
+  readonly attribute boolean selectionVisible;
+};
--- a/dom/webidl/WebGL2RenderingContext.webidl
+++ b/dom/webidl/WebGL2RenderingContext.webidl
@@ -3,17 +3,16 @@
  * 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 source for this IDL is found at https://www.khronos.org/registry/webgl/specs/latest/2.0
  * This IDL depends on WebGLRenderingContext.webidl
  */
 
 typedef long long GLint64; // Should this be int64?
-typedef unsigned long long GLuint64; // Should this be uint64?
 
 [Pref="webgl.enable-prototype-webgl2"]
 interface WebGLQuery {
 };
 
 [Pref="webgl.enable-prototype-webgl2"]
 interface WebGLSampler {
 };
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -23,16 +23,17 @@ typedef short          GLshort;
 typedef long           GLint;
 typedef long           GLsizei;
 typedef long long      GLintptr;
 typedef long long      GLsizeiptr;
 // Ideally the typedef below would use 'unsigned byte', but that doesn't currently exist in Web IDL.
 typedef octet          GLubyte;        /* 'octet' should be an unsigned 8 bit type. */
 typedef unsigned short GLushort;
 typedef unsigned long  GLuint;
+typedef unsigned long long GLuint64;
 typedef unrestricted float GLfloat;
 typedef unrestricted float GLclampf;
 
 dictionary WebGLContextAttributes {
     // boolean alpha = true;
     // We deviate from the spec here.
     // If alpha isn't specified, we rely on a pref ("webgl.default-no-alpha")
     boolean alpha;
@@ -970,8 +971,34 @@ interface WebGLExtensionInstancedArrays 
     void vertexAttribDivisorANGLE(GLuint index, GLuint divisor);
 };
 
 [NoInterfaceObject]
 interface WebGLExtensionBlendMinMax {
     const GLenum MIN_EXT = 0x8007;
     const GLenum MAX_EXT = 0x8008;
 };
+
+// FIXME: Spec interface name is WebGLTimerQueryEXT.
+[NoInterfaceObject]
+interface WebGLTimerQuery {
+};
+
+// FIXME: Spec interface name is EXT_disjoint_timer_query.
+[NoInterfaceObject]
+interface WebGLExtensionDisjointTimerQuery {
+    const GLenum QUERY_COUNTER_BITS_EXT = 0x8864;
+    const GLenum CURRENT_QUERY_EXT = 0x8865;
+    const GLenum QUERY_RESULT_EXT = 0x8866;
+    const GLenum QUERY_RESULT_AVAILABLE_EXT = 0x8867;
+    const GLenum TIME_ELAPSED_EXT = 0x88BF;
+    const GLenum TIMESTAMP_EXT = 0x8E28;
+    const GLenum GPU_DISJOINT_EXT = 0x8FBB;
+
+    WebGLTimerQuery? createQueryEXT();
+    void deleteQueryEXT(WebGLTimerQuery? query);
+    [WebGLHandlesContextLoss] boolean isQueryEXT(WebGLTimerQuery? query);
+    void beginQueryEXT(GLenum target, WebGLTimerQuery? query);
+    void endQueryEXT(GLenum target);
+    void queryCounterEXT(WebGLTimerQuery? query, GLenum target);
+    any getQueryEXT(GLenum target, GLenum pname);
+    any getQueryObjectEXT(WebGLTimerQuery? query, GLenum pname);
+};
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -722,16 +722,17 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'AutocompleteErrorEvent.webidl',
     'BlobEvent.webidl',
     'CallEvent.webidl',
     'CallGroupErrorEvent.webidl',
     'CameraClosedEvent.webidl',
     'CameraConfigurationEvent.webidl',
     'CameraFacesDetectedEvent.webidl',
     'CameraStateChangeEvent.webidl',
+    'CaretStateChangedEvent.webidl',
     'CFStateChangeEvent.webidl',
     'CloseEvent.webidl',
     'CSSFontFaceLoadEvent.webidl',
     'DataErrorEvent.webidl',
     'DataStoreChangeEvent.webidl',
     'DeviceLightEvent.webidl',
     'DeviceOrientationEvent.webidl',
     'DeviceProximityEvent.webidl',
--- a/dom/xbl/nsXBLProtoImplField.cpp
+++ b/dom/xbl/nsXBLProtoImplField.cpp
@@ -388,16 +388,18 @@ nsXBLProtoImplField::InstallField(JS::Ha
 
   *aDidInstall = false;
 
   // Empty fields are treated as not actually present.
   if (IsEmpty()) {
     return NS_OK;
   }
 
+  nsAutoMicroTask mt;
+
   nsAutoCString uriSpec;
   aBindingDocURI->GetSpec(uriSpec);
 
   nsIGlobalObject* globalObject = xpc::WindowGlobalOrNull(aBoundNode);
   if (!globalObject) {
     return NS_OK;
   }
 
--- a/dom/xbl/nsXBLProtoImplMethod.cpp
+++ b/dom/xbl/nsXBLProtoImplMethod.cpp
@@ -288,16 +288,18 @@ nsXBLProtoImplAnonymousMethod::Execute(n
   nsIDocument* document = aBoundElement->OwnerDoc();
 
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(document->GetInnerWindow());
   if (!global) {
     return NS_OK;
   }
 
+  nsAutoMicroTask mt;
+
   // We are going to run script via JS::Call, so we need a script entry point,
   // but as this is XBL related it does not appear in the HTML spec.
   dom::AutoEntryScript aes(global, "XBL <constructor>/<destructor> invocation");
   aes.TakeOwnershipOfErrorReporting();
   JSContext* cx = aes.cx();
 
   JS::Rooted<JSObject*> globalObject(cx, global->GetGlobalJSObject());
 
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -3532,17 +3532,19 @@ XULDocument::ExecuteScript(nsXULPrototyp
 
     nsresult rv;
     rv = mScriptGlobalObject->EnsureScriptEnvironment();
     NS_ENSURE_SUCCESS(rv, rv);
 
     JS::HandleScript scriptObject = aScript->GetScriptObject();
     NS_ENSURE_TRUE(scriptObject, NS_ERROR_UNEXPECTED);
 
-    // Execute the precompiled script with the given version.
+    // Execute the precompiled script with the given version
+    nsAutoMicroTask mt;
+
     // We're about to run script via JS::CloneAndExecuteScript, so we need an
     // AutoEntryScript. This is Gecko specific and not in any spec.
     AutoEntryScript aes(mScriptGlobalObject, "precompiled XUL <script> element");
     aes.TakeOwnershipOfErrorReporting();
     JSContext* cx = aes.cx();
     JS::Rooted<JSObject*> baseGlobal(cx, JS::CurrentGlobalOrNull(cx));
     NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(baseGlobal), NS_OK);
 
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -521,20 +521,79 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *a
     mTextRenderingParams = params;
   }
 
   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
 
   AutoDWriteGlyphRun autoRun;
   DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun);
 
+  bool needsRepushedLayers = false;
+  if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && mFormat != SurfaceFormat::B8G8R8X8) {
+    D2D1_RECT_F rect;
+    bool isAligned;
+    needsRepushedLayers = mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
+
+    // If we have a complex clip in our stack and we have a transparent
+    // background, and subpixel AA is permitted, we need to repush our layer
+    // stack limited by the glyph run bounds initializing our layers for
+    // subpixel AA.
+    if (needsRepushedLayers) {
+      mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
+                                  DWRITE_MEASURING_MODE_NATURAL, &rect);
+      rect.left = std::floor(rect.left);
+      rect.right = std::ceil(rect.right);
+      rect.top = std::floor(rect.top);
+      rect.bottom = std::ceil(rect.bottom);
+
+      PopAllClips();
+
+      if (!mTransform.IsRectilinear()) {
+        // We must limit the pixels we touch to the -user space- bounds of
+        // the glyphs being drawn. In order not to get transparent pixels
+        // copied up in our pushed layer stack.
+        D2D1_RECT_F userRect;
+        mDC->SetTransform(D2D1::IdentityMatrix());
+        mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
+                                    DWRITE_MEASURING_MODE_NATURAL, &userRect);
+
+        RefPtr<ID2D1PathGeometry> path;
+        D2DFactory()->CreatePathGeometry(byRef(path));
+        RefPtr<ID2D1GeometrySink> sink;
+        path->Open(byRef(sink));
+        sink->BeginFigure(D2D1::Point2F(userRect.left, userRect.top), D2D1_FIGURE_BEGIN_FILLED);
+        sink->AddLine(D2D1::Point2F(userRect.right, userRect.top));
+        sink->AddLine(D2D1::Point2F(userRect.right, userRect.bottom));
+        sink->AddLine(D2D1::Point2F(userRect.left, userRect.bottom));
+        sink->EndFigure(D2D1_FIGURE_END_CLOSED);
+        sink->Close();
+
+        mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), path, D2D1_ANTIALIAS_MODE_ALIASED,
+                                              D2DMatrix(mTransform), 1.0f, nullptr,
+                                              D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND |
+                                              D2D1_LAYER_OPTIONS1_IGNORE_ALPHA), nullptr);
+      }
+
+      PushClipsToDC(mDC, true, rect);
+      mDC->SetTransform(D2DMatrix(mTransform));
+    }
+  }
+
   if (brush) {
     mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
   }
 
+  if (needsRepushedLayers) {
+    PopClipsFromDC(mDC);
+
+    if (!mTransform.IsRectilinear()) {
+      mDC->PopLayer();
+    }
+  }
+
   FinalizeDrawing(aOptions.mCompositionOp, aPattern);
 }
 
 void
 DrawTargetD2D1::Mask(const Pattern &aSource,
                      const Pattern &aMask,
                      const DrawOptions &aOptions)
 {
@@ -1256,25 +1315,25 @@ DrawTargetD2D1::PushAllClips()
   if (!mClipsArePushed) {
     PushClipsToDC(mDC);
   
     mClipsArePushed = true;
   }
 }
 
 void
-DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC)
+DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
 {
   mDC->SetTransform(D2D1::IdentityMatrix());
   mTransformDirty = true;
 
   for (std::vector<PushedClip>::iterator iter = mPushedClips.begin();
         iter != mPushedClips.end(); iter++) {
     if (iter->mPath) {
-      PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform);
+      PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform, aForceIgnoreAlpha, aMaxRect);
     } else {
       mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
     }
   }
 }
 
 void
 DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC)
@@ -1488,23 +1547,24 @@ DrawTargetD2D1::OptimizeSourceSurface(So
   if (!bitmap) {
     return data.forget();
   }
 
   return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), mDC, data->GetFormat(), data->GetSize());
 }
 
 void
-DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
+DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
+                             bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
 {
   D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
 
-  if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) {
+  if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE || aForceIgnoreAlpha) {
     options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
   }
 
-  mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry,
+  mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry,
                                         D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform,
                                         1.0, nullptr, options), nullptr);
 }
 
 }
 }
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -174,24 +174,25 @@ private:
   TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
 
   TemporaryRef<ID2D1Geometry> GetInverseClippedGeometry();
 
   bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
 
   void PopAllClips();
   void PushAllClips();
-  void PushClipsToDC(ID2D1DeviceContext *aDC);
+  void PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha = false, const D2D1_RECT_F& aMaxRect = D2D1::InfiniteRect());
   void PopClipsFromDC(ID2D1DeviceContext *aDC);
 
   TemporaryRef<ID2D1Brush> CreateTransparentBlackBrush();
   TemporaryRef<ID2D1SolidColorBrush> GetSolidColorBrush(const D2D_COLOR_F& aColor);
   TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
 
-  void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform);
+  void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
+                    bool aForceIgnoreAlpha = false, const D2D1_RECT_F& aLayerRect = D2D1::InfiniteRect());
 
   IntSize mSize;
 
   RefPtr<ID3D11Device> mDevice;
   RefPtr<ID3D11Texture2D> mTexture;
   RefPtr<ID2D1Geometry> mCurrentClippedGeometry;
   // This is only valid if mCurrentClippedGeometry is non-null. And will
   // only be the intersection of all pixel-aligned retangular clips. This is in
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -65,16 +65,17 @@ static const char *sExtensionNames[] = {
     "NO_EXTENSION",
     "GL_AMD_compressed_ATC_texture",
     "GL_ANGLE_depth_texture",
     "GL_ANGLE_framebuffer_blit",
     "GL_ANGLE_framebuffer_multisample",
     "GL_ANGLE_instanced_arrays",
     "GL_ANGLE_texture_compression_dxt3",
     "GL_ANGLE_texture_compression_dxt5",
+    "GL_ANGLE_timer_query",
     "GL_APPLE_client_storage",
     "GL_APPLE_texture_range",
     "GL_APPLE_vertex_array_object",
     "GL_ARB_ES2_compatibility",
     "GL_ARB_ES3_compatibility",
     "GL_ARB_color_buffer_float",
     "GL_ARB_copy_buffer",
     "GL_ARB_depth_texture",
@@ -92,24 +93,26 @@ static const char *sExtensionNames[] = {
     "GL_ARB_sampler_objects",
     "GL_ARB_sync",
     "GL_ARB_texture_compression",
     "GL_ARB_texture_float",
     "GL_ARB_texture_non_power_of_two",
     "GL_ARB_texture_rectangle",
     "GL_ARB_texture_storage",
     "GL_ARB_texture_swizzle",
+    "GL_ARB_timer_query",
     "GL_ARB_transform_feedback2",
     "GL_ARB_uniform_buffer_object",
     "GL_ARB_vertex_array_object",
     "GL_EXT_bgra",
     "GL_EXT_blend_minmax",
     "GL_EXT_color_buffer_float",
     "GL_EXT_color_buffer_half_float",
     "GL_EXT_copy_texture",
+    "GL_EXT_disjoint_timer_query",
     "GL_EXT_draw_buffers",
     "GL_EXT_draw_buffers2",
     "GL_EXT_draw_instanced",
     "GL_EXT_draw_range_elements",
     "GL_EXT_frag_depth",
     "GL_EXT_framebuffer_blit",
     "GL_EXT_framebuffer_multisample",
     "GL_EXT_framebuffer_object",
@@ -124,16 +127,17 @@ static const char *sExtensionNames[] = {
     "GL_EXT_shader_texture_lod",
     "GL_EXT_texture3D",
     "GL_EXT_texture_compression_dxt1",
     "GL_EXT_texture_compression_s3tc",
     "GL_EXT_texture_filter_anisotropic",
     "GL_EXT_texture_format_BGRA8888",
     "GL_EXT_texture_sRGB",
     "GL_EXT_texture_storage",
+    "GL_EXT_timer_query",
     "GL_EXT_transform_feedback",
     "GL_EXT_unpack_subimage",
     "GL_IMG_read_format",
     "GL_IMG_texture_compression_pvrtc",
     "GL_IMG_texture_npot",
     "GL_KHR_debug",
     "GL_NV_draw_instanced",
     "GL_NV_fence",
@@ -1038,61 +1042,105 @@ GLContext::InitWithPrefix(const char *pr
             if (!LoadSymbols(useCore ? coreSymbols : extSymbols, trygl, prefix)) {
                 NS_ERROR("GL supports BindBufferOffset without supplying its function.");
 
                 MarkUnsupported(GLFeature::bind_buffer_offset);
                 ClearSymbols(coreSymbols);
             }
         }
 
+        if (IsSupported(GLFeature::query_counter)) {
+            SymLoadStruct coreSymbols[] = {
+                { (PRFuncPtr*) &mSymbols.fQueryCounter, { "QueryCounter", nullptr } },
+                END_SYMBOLS
+            };
+            SymLoadStruct extSymbols[] = {
+                { (PRFuncPtr*) &mSymbols.fQueryCounter, { "QueryCounterEXT", "QueryCounterANGLE", nullptr } },
+                END_SYMBOLS
+            };
+
+            bool useCore = IsFeatureProvidedByCoreSymbols(GLFeature::query_counter);
+
+            if (!LoadSymbols(useCore ? coreSymbols : extSymbols, trygl, prefix)) {
+                NS_ERROR("GL supports query counters without supplying its functions.");
+
+                MarkUnsupported(GLFeature::query_counter);
+                ClearSymbols(coreSymbols);
+            }
+        }
+
         if (IsSupported(GLFeature::query_objects)) {
             SymLoadStruct coreSymbols[] = {
                 { (PRFuncPtr*) &mSymbols.fBeginQuery, { "BeginQuery", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fGenQueries, { "GenQueries", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fDeleteQueries, { "DeleteQueries", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fEndQuery, { "EndQuery", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fGetQueryiv, { "GetQueryiv", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fGetQueryObjectuiv, { "GetQueryObjectuiv", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fIsQuery, { "IsQuery", nullptr } },
                 END_SYMBOLS
             };
 
             SymLoadStruct extSymbols[] = {
-                { (PRFuncPtr*) &mSymbols.fBeginQuery, { "BeginQueryEXT", nullptr } },
-                { (PRFuncPtr*) &mSymbols.fGenQueries, { "GenQueriesEXT", nullptr } },
-                { (PRFuncPtr*) &mSymbols.fDeleteQueries, { "DeleteQueriesEXT", nullptr } },
-                { (PRFuncPtr*) &mSymbols.fEndQuery, { "EndQueryEXT", nullptr } },
-                { (PRFuncPtr*) &mSymbols.fGetQueryiv, { "GetQueryivEXT", nullptr } },
-                { (PRFuncPtr*) &mSymbols.fGetQueryObjectuiv, { "GetQueryObjectuivEXT", nullptr } },
-                { (PRFuncPtr*) &mSymbols.fIsQuery, { "IsQueryEXT", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fBeginQuery, { "BeginQueryEXT", "BeginQueryANGLE", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fGenQueries, { "GenQueriesEXT", "GenQueriesANGLE", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fDeleteQueries, { "DeleteQueriesEXT", "DeleteQueriesANGLE", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fEndQuery, { "EndQueryEXT", "EndQueryANGLE", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fGetQueryiv, { "GetQueryivEXT", "GetQueryivANGLE", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fGetQueryObjectuiv, { "GetQueryObjectuivEXT", "GetQueryObjectuivANGLE", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fIsQuery, { "IsQueryEXT", "IsQueryANGLE", nullptr } },
                 END_SYMBOLS
             };
 
             bool useCore = IsFeatureProvidedByCoreSymbols(GLFeature::query_objects);
 
             if (!LoadSymbols(useCore ? coreSymbols : extSymbols, trygl, prefix)) {
                 NS_ERROR("GL supports query objects without supplying its functions.");
 
                 MarkUnsupported(GLFeature::query_objects);
+                MarkUnsupported(GLFeature::get_query_object_i64v);
                 MarkUnsupported(GLFeature::get_query_object_iv);
                 MarkUnsupported(GLFeature::occlusion_query);
                 MarkUnsupported(GLFeature::occlusion_query_boolean);
                 MarkUnsupported(GLFeature::occlusion_query2);
                 ClearSymbols(coreSymbols);
             }
         }
 
+        if (IsSupported(GLFeature::get_query_object_i64v)) {
+            SymLoadStruct coreSymbols[] = {
+                { (PRFuncPtr*) &mSymbols.fGetQueryObjecti64v, { "GetQueryObjecti64v", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fGetQueryObjectui64v, { "GetQueryObjectui64v", nullptr } },
+                END_SYMBOLS
+            };
+
+            SymLoadStruct extSymbols[] = {
+                { (PRFuncPtr*) &mSymbols.fGetQueryObjecti64v, { "GetQueryObjecti64vEXT", "GetQueryObjecti64vANGLE", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fGetQueryObjectui64v, { "GetQueryObjectui64vEXT", "GetQueryObjectui64vANGLE", nullptr } },
+                END_SYMBOLS
+            };
+
+            bool useCore = IsFeatureProvidedByCoreSymbols(GLFeature::get_query_object_i64v);
+            if (!LoadSymbols(useCore ? coreSymbols : extSymbols, trygl, prefix)) {
+                NS_ERROR("GL supports 64 bit query object getters without supplying its functions.");
+
+                MarkUnsupported(GLFeature::get_query_object_i64v);
+                MarkUnsupported(GLFeature::query_counter);
+                ClearSymbols(coreSymbols);
+            }
+        }
+
         if (IsSupported(GLFeature::get_query_object_iv)) {
             SymLoadStruct coreSymbols[] = {
                 { (PRFuncPtr*) &mSymbols.fGetQueryObjectiv, { "GetQueryObjectiv", nullptr } },
                 END_SYMBOLS
             };
 
             SymLoadStruct extSymbols[] = {
-                { (PRFuncPtr*) &mSymbols.fGetQueryObjectiv, { "GetQueryObjectivEXT", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fGetQueryObjectiv, { "GetQueryObjectivEXT", "GetQueryObjectivANGLE", nullptr } },
                 END_SYMBOLS
             };
 
             bool useCore = IsFeatureProvidedByCoreSymbols(GLFeature::get_query_object_iv);
 
             if (!LoadSymbols(useCore ? coreSymbols : extSymbols, trygl, prefix)) {
                 NS_ERROR("GL supports query objects iv getter without supplying its function.");
 
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -94,28 +94,31 @@ enum class GLFeature {
     ES3_compatibility,
     frag_color_float,
     frag_depth,
     framebuffer_blit,
     framebuffer_multisample,
     framebuffer_object,
     get_integer_indexed,
     get_integer64_indexed,
+    get_query_object_i64v,
     get_query_object_iv,
     get_string_indexed,
     gpu_shader4,
     instanced_arrays,
     instanced_non_arrays,
     invalidate_framebuffer,
     map_buffer_range,
     occlusion_query,
     occlusion_query_boolean,
     occlusion_query2,
     packed_depth_stencil,
+    query_counter,
     query_objects,
+    query_time_elapsed,
     read_buffer,
     renderbuffer_color_float,
     renderbuffer_color_half_float,
     robustness,
     sRGB_framebuffer,
     sRGB_texture,
     sampler_objects,
     standard_derivatives,
@@ -359,16 +362,17 @@ public:
         Extension_None = 0,
         AMD_compressed_ATC_texture,
         ANGLE_depth_texture,
         ANGLE_framebuffer_blit,
         ANGLE_framebuffer_multisample,
         ANGLE_instanced_arrays,
         ANGLE_texture_compression_dxt3,
         ANGLE_texture_compression_dxt5,
+        ANGLE_timer_query,
         APPLE_client_storage,
         APPLE_texture_range,
         APPLE_vertex_array_object,
         ARB_ES2_compatibility,
         ARB_ES3_compatibility,
         ARB_color_buffer_float,
         ARB_copy_buffer,
         ARB_depth_texture,
@@ -386,24 +390,26 @@ public:
         ARB_sampler_objects,
         ARB_sync,
         ARB_texture_compression,
         ARB_texture_float,
         ARB_texture_non_power_of_two,
         ARB_texture_rectangle,
         ARB_texture_storage,
         ARB_texture_swizzle,
+        ARB_timer_query,
         ARB_transform_feedback2,
         ARB_uniform_buffer_object,
         ARB_vertex_array_object,
         EXT_bgra,
         EXT_blend_minmax,
         EXT_color_buffer_float,
         EXT_color_buffer_half_float,
         EXT_copy_texture,
+        EXT_disjoint_timer_query,
         EXT_draw_buffers,
         EXT_draw_buffers2,
         EXT_draw_instanced,
         EXT_draw_range_elements,
         EXT_frag_depth,
         EXT_framebuffer_blit,
         EXT_framebuffer_multisample,
         EXT_framebuffer_object,
@@ -418,16 +424,17 @@ public:
         EXT_shader_texture_lod,
         EXT_texture3D,
         EXT_texture_compression_dxt1,
         EXT_texture_compression_s3tc,
         EXT_texture_filter_anisotropic,
         EXT_texture_format_BGRA8888,
         EXT_texture_sRGB,
         EXT_texture_storage,
+        EXT_timer_query,
         EXT_transform_feedback,
         EXT_unpack_subimage,
         IMG_read_format,
         IMG_texture_compression_pvrtc,
         IMG_texture_npot,
         KHR_debug,
         NV_draw_instanced,
         NV_fence,
@@ -2612,16 +2619,32 @@ public:
     void fVertexAttribDivisor(GLuint index, GLuint divisor)
     {
         BEFORE_GL_CALL;
         ASSERT_SYMBOL_PRESENT(fVertexAttribDivisor);
         mSymbols.fVertexAttribDivisor(index, divisor);
         AFTER_GL_CALL;
     }
 
+// -----------------------------------------------------------------------------
+// Package XXX_query_counter
+/**
+ * XXX_query_counter:
+ *  - depends on XXX_query_objects
+ *  - provide all followed entry points
+ *  - provide GL_TIMESTAMP
+ */
+public:
+    void fQueryCounter(GLuint id, GLenum target) {
+        BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fQueryCounter);
+        mSymbols.fQueryCounter(id, target);
+        AFTER_GL_CALL;
+    }
+
 
 // -----------------------------------------------------------------------------
 // Package XXX_query_objects
 /**
  * XXX_query_objects:
  *  - provide all followed entry points
  *
  * XXX_occlusion_query2:
@@ -2666,16 +2689,38 @@ public:
     realGLboolean fIsQuery(GLuint query) {
         BEFORE_GL_CALL;
         ASSERT_SYMBOL_PRESENT(fIsQuery);
         realGLboolean retval = mSymbols.fIsQuery(query);
         AFTER_GL_CALL;
         return retval;
     }
 
+// -----------------------------------------------------------------------------
+// Package XXX_get_query_object_i64v
+/**
+ * XXX_get_query_object_i64v:
+ *  - depends on XXX_query_objects
+ *  - provide the followed entry point
+ */
+public:
+    void fGetQueryObjecti64v(GLuint id, GLenum pname, GLint64* params) {
+        BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fGetQueryObjecti64v);
+        mSymbols.fGetQueryObjecti64v(id, pname, params);
+        AFTER_GL_CALL;
+    }
+
+    void fGetQueryObjectui64v(GLuint id, GLenum pname, GLuint64* params) {
+        BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fGetQueryObjectui64v);
+        mSymbols.fGetQueryObjectui64v(id, pname, params);
+        AFTER_GL_CALL;
+    }
+
 
 // -----------------------------------------------------------------------------
 // Package XXX_get_query_object_iv
 /**
  * XXX_get_query_object_iv:
  *  - depends on XXX_query_objects
  *  - provide the followed entry point
  *
--- a/gfx/gl/GLContextFeatures.cpp
+++ b/gfx/gl/GLContextFeatures.cpp
@@ -256,16 +256,28 @@ static const FeatureInfo sFeatureInfoArr
         GLVersion::GL3_2,
         GLESVersion::ES3,
         GLContext::Extension_None,
         {
             GLContext::Extensions_End
         }
     },
     {
+        "get_query_object_i64v",
+        GLVersion::GL3_3,
+        GLESVersion::NONE,
+        GLContext::ARB_timer_query,
+        {
+            GLContext::ANGLE_timer_query,
+            GLContext::EXT_disjoint_timer_query,
+            GLContext::EXT_timer_query,
+            GLContext::Extensions_End
+        }
+    },
+    {
         "get_query_object_iv",
         GLVersion::GL2,
         GLESVersion::NONE,
         GLContext::Extension_None,
         {
             GLContext::Extensions_End
         }
         /*
@@ -388,31 +400,58 @@ static const FeatureInfo sFeatureInfoArr
         GLContext::Extension_None,
         {
             GLContext::EXT_packed_depth_stencil,
             GLContext::OES_packed_depth_stencil,
             GLContext::Extensions_End
         }
     },
     {
+        "query_counter",
+        GLVersion::GL3_3,
+        GLESVersion::NONE,
+        GLContext::ARB_timer_query,
+        {
+            GLContext::ANGLE_timer_query,
+            GLContext::EXT_disjoint_timer_query,
+            // EXT_timer_query does NOT support GL_TIMESTAMP retrieval with
+            // QueryCounter.
+            GLContext::Extensions_End
+        }
+    },
+    {
         "query_objects",
         GLVersion::GL2,
         GLESVersion::ES3,
         GLContext::Extension_None,
         {
+            GLContext::ANGLE_timer_query,
+            GLContext::EXT_disjoint_timer_query,
             GLContext::EXT_occlusion_query_boolean,
             GLContext::Extensions_End
         }
         /*
          * XXX_query_objects only provide entry points commonly supported by
-         * ARB_occlusion_query (added in OpenGL 2.0) and EXT_occlusion_query_boolean
-         * (added in OpenGL ES 3.0)
+         * ARB_occlusion_query (added in OpenGL 2.0), EXT_occlusion_query_boolean
+         * (added in OpenGL ES 3.0), and ARB_timer_query (added in OpenGL 3.3)
          */
     },
     {
+        "query_time_elapsed",
+        GLVersion::GL3_3,
+        GLESVersion::NONE,
+        GLContext::ARB_timer_query,
+        {
+            GLContext::ANGLE_timer_query,
+            GLContext::EXT_disjoint_timer_query,
+            GLContext::EXT_timer_query,
+            GLContext::Extensions_End
+        }
+    },
+    {
         "read_buffer",
         GLVersion::GL2,
         GLESVersion::ES3,
         GLContext::Extension_None,
         {
             GLContext::Extensions_End
         }
     },
--- a/gfx/gl/GLContextSymbols.h
+++ b/gfx/gl/GLContextSymbols.h
@@ -140,16 +140,22 @@ struct GLContextSymbols
     typedef void (GLAPIENTRY * PFNGLGETPROGRAMINFOLOGPROC) (GLuint program, GLsizei bufSize, GLsizei* length, GLchar* infoLog);
     PFNGLGETPROGRAMINFOLOGPROC fGetProgramInfoLog;
     typedef void (GLAPIENTRY * PFNGLGETQUERYIVPROC) (GLenum target, GLenum pname, GLint* params);
     PFNGLGETQUERYIVPROC fGetQueryiv;
     typedef void (GLAPIENTRY * PFNGLGETQUERYOBJECTIVPROC) (GLuint id, GLenum pname, GLint* params);
     PFNGLGETQUERYOBJECTIVPROC fGetQueryObjectiv;
     typedef void (GLAPIENTRY * PFNGLGETQUERYOBJECTUIVPROC) (GLuint id, GLenum pname, GLuint* params);
     PFNGLGETQUERYOBJECTUIVPROC fGetQueryObjectuiv;
+    typedef void (GLAPIENTRY * PFNGLGETQUERYOBJECTI64VPROC) (GLuint id, GLenum pname, GLint64* params);
+    PFNGLGETQUERYOBJECTI64VPROC fGetQueryObjecti64v;
+    typedef void (GLAPIENTRY * PFNGLGETQUERYOBJECTUI64VPROC) (GLuint id, GLenum pname, GLuint64* params);
+    PFNGLGETQUERYOBJECTUI64VPROC fGetQueryObjectui64v;
+    typedef void (GLAPIENTRY * PFNGLQUERYCOUNTERPROC) (GLuint id, GLenum target);
+    PFNGLQUERYCOUNTERPROC fQueryCounter;
     typedef void (GLAPIENTRY * PFNGLTEXPARAMETERIPROC) (GLenum target, GLenum pname, GLint param);
     PFNGLTEXPARAMETERIPROC fTexParameteri;
     typedef void (GLAPIENTRY * PFNGLTEXPARAMETERIVPROC) (GLenum target, GLenum pname, const GLint* param);
     PFNGLTEXPARAMETERIVPROC fTexParameteriv;
     typedef void (GLAPIENTRY * PFNGLTEXPARAMETERFPROC) (GLenum target, GLenum pname, GLfloat param);
     PFNGLTEXPARAMETERFPROC fTexParameterf;
     typedef GLubyte* (GLAPIENTRY * PFNGLGETSTRINGPROC) (GLenum);
     PFNGLGETSTRINGPROC fGetString;
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -170,25 +170,16 @@ public:
       scrollableRect.y = std::max(0.f,
                                   scrollableRect.y - (compSize.height - scrollableRect.height));
       scrollableRect.height = compSize.height;
     }
 
     return scrollableRect;
   }
 
-  // Return the scale factor needed to fit the viewport
-  // into its composition bounds.
-  CSSToParentLayerScale CalculateIntrinsicScale() const
-  {
-    return CSSToParentLayerScale(
-        std::max(mCompositionBounds.width / mViewport.width,
-                 mCompositionBounds.height / mViewport.height));
-  }
-
   CSSSize CalculateCompositedSizeInCssPixels() const
   {
     return mCompositionBounds.Size() / GetZoom();
   }
 
   CSSRect CalculateCompositedRectInCssPixels() const
   {
     return mCompositionBounds / GetZoom();
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -569,33 +569,43 @@ APZCTreeManager::ReceiveInputEvent(Input
   nsEventStatus result = nsEventStatus_eIgnore;
   HitTestResult hitResult = HitNothing;
   switch (aEvent.mInputType) {
     case MULTITOUCH_INPUT: {
       MultiTouchInput& touchInput = aEvent.AsMultiTouchInput();
       result = ProcessTouchInput(touchInput, aOutTargetGuid, aOutInputBlockId);
       break;
     } case SCROLLWHEEL_INPUT: {
+      FlushRepaintsToClearScreenToGeckoTransform();
+
       ScrollWheelInput& wheelInput = aEvent.AsScrollWheelInput();
       nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(wheelInput.mOrigin,
                                                             &hitResult);
       if (apzc) {
         MOZ_ASSERT(hitResult == HitLayer || hitResult == HitDispatchToContentRegion);
 
+        // For wheel events, the call to ReceiveInputEvent below may result in
+        // scrolling, which changes the async transform. However, the event we
+        // want to pass to gecko should be the pre-scroll event coordinates,
+        // transformed into the gecko space. (pre-scroll because the mouse
+        // cursor is stationary during wheel scrolling, unlike touchmove
+        // events). Also, since we just flushed the pending repaints the
+        // transform to gecko space is a no-op so we can just skip it.
+        MOZ_ASSERT(
+          (GetScreenToApzcTransform(apzc) * GetApzcToGeckoTransform(apzc))
+          .NudgeToIntegersFixedEpsilon()
+          .IsIdentity());
+
         result = mInputQueue->ReceiveInputEvent(
           apzc,
           /* aTargetConfirmed = */ hitResult == HitLayer,
           wheelInput, aOutInputBlockId);
 
         // Update the out-parameters so they are what the caller expects.
         apzc->GetGuid(aOutTargetGuid);
-        Matrix4x4 transformToGecko = GetScreenToApzcTransform(apzc)
-                                   * GetApzcToGeckoTransform(apzc);
-        wheelInput.mOrigin =
-          TransformTo<ScreenPixel>(transformToGecko, wheelInput.mOrigin);
       }
       break;
     } case PANGESTURE_INPUT: {
       PanGestureInput& panInput = aEvent.AsPanGestureInput();
       nsRefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(panInput.mPanStartPoint,
                                                             &hitResult);
       if (apzc) {
         MOZ_ASSERT(hitResult == HitLayer || hitResult == HitDispatchToContentRegion);
@@ -663,27 +673,17 @@ already_AddRefed<AsyncPanZoomController>
 APZCTreeManager::GetTouchInputBlockAPZC(const MultiTouchInput& aEvent,
                                         HitTestResult* aOutHitResult)
 {
   nsRefPtr<AsyncPanZoomController> apzc;
   if (aEvent.mTouches.Length() == 0) {
     return apzc.forget();
   }
 
-  { // In this block we flush repaint requests for the entire APZ tree. We need to do this
-    // at the start of an input block for a number of reasons. One of the reasons is so that
-    // after we untransform the event into gecko space, it doesn't end up under something
-    // else. Another reason is that if we hit-test this event and end up on a layer's
-    // dispatch-to-content region we cannot be sure we actually got the correct layer. We
-    // have to fall back to the gecko hit-test to handle this case, but we can't untransform
-    // the event we send to gecko because we don't know the layer to untransform with
-    // respect to.
-    MonitorAutoLock lock(mTreeLock);
-    FlushRepaintsRecursively(mRootNode);
-  }
+  FlushRepaintsToClearScreenToGeckoTransform();
 
   apzc = GetTargetAPZC(aEvent.mTouches[0].mScreenPoint, aOutHitResult);
   for (size_t i = 1; i < aEvent.mTouches.Length(); i++) {
     nsRefPtr<AsyncPanZoomController> apzc2 = GetTargetAPZC(aEvent.mTouches[i].mScreenPoint, aOutHitResult);
     apzc = GetMultitouchTarget(apzc, apzc2);
     APZCTM_LOG("Using APZC %p as the root APZC for multi-touch\n", apzc.get());
   }
 
@@ -765,16 +765,22 @@ APZCTreeManager::ProcessTouchInput(Multi
         aInput, aOutInputBlockId);
 
     // For computing the event to pass back to Gecko, use up-to-date transforms
     // (i.e. not anything cached in an input block).
     // This ensures that transformToApzc and transformToGecko are in sync.
     Matrix4x4 transformToApzc = GetScreenToApzcTransform(mApzcForInputBlock);
     Matrix4x4 transformToGecko = GetApzcToGeckoTransform(mApzcForInputBlock);
     Matrix4x4 outTransform = transformToApzc * transformToGecko;
+    if (aInput.mType == MultiTouchInput::MULTITOUCH_START) {
+      // For touch-start events we should have flushed all pending repaints
+      // above as part of the GetTouchInputBlockAPZC call, and so we expect
+      // the apzc-to-gecko transform to be empty.
+      MOZ_ASSERT(outTransform.NudgeToIntegersFixedEpsilon().IsIdentity());
+    }
     for (size_t i = 0; i < aInput.mTouches.Length(); i++) {
       SingleTouchData& touchData = aInput.mTouches[i];
       touchData.mScreenPoint = TransformTo<ScreenPixel>(
           outTransform, touchData.mScreenPoint);
     }
   }
 
   if (aInput.mType == MultiTouchInput::MULTITOUCH_END) {
@@ -1053,16 +1059,42 @@ APZCTreeManager::UpdateZoomConstraintsRe
     if (child->GetApzc() && child->GetApzc()->IsRootForLayersId()) {
       continue;
     }
     UpdateZoomConstraintsRecursively(child, aConstraints);
   }
 }
 
 void
+APZCTreeManager::FlushRepaintsToClearScreenToGeckoTransform()
+{
+  // As the name implies, we flush repaint requests for the entire APZ tree in
+  // order to clear the screen-to-gecko transform (aka the "untransform" applied
+  // to incoming input events before they can be passed on to Gecko).
+  //
+  // The primary reason we do this is to avoid the problem where input events,
+  // after being untransformed, end up hit-testing differently in Gecko. This
+  // might happen in cases where the input event lands on content that is async-
+  // scrolled into view, but Gecko still thinks it is out of view given the
+  // visible area of a scrollframe.
+  //
+  // Another reason we want to clear the untransform is that if our APZ hit-test
+  // hits a dispatch-to-content region then that's an ambiguous result and we
+  // need to ask Gecko what actually got hit. In order to do this we need to
+  // untransform the input event into Gecko space - but to do that we need to
+  // know which APZC got hit! This leads to a circular dependency; the only way
+  // to get out of it is to make sure that the untransform for all the possible
+  // matched APZCs is the same. It is simplest to ensure that by flushing the
+  // pending repaint requests, which makes all of the untransforms empty (and
+  // therefore equal).
+  MonitorAutoLock lock(mTreeLock);
+  FlushRepaintsRecursively(mRootNode);
+}
+
+void
 APZCTreeManager::FlushRepaintsRecursively(HitTestingTreeNode* aNode)
 {
   mTreeLock.AssertCurrentThreadOwns();
 
   for (HitTestingTreeNode* node = aNode; node; node = node->GetPrevSibling()) {
     if (node->IsPrimaryHolder()) {
       MOZ_ASSERT(node->GetApzc());
       node->GetApzc()->FlushRepaintForNewInputBlock();
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -424,16 +424,17 @@ private:
                                   ScrollableLayerGuid* aOutTargetGuid,
                                   uint64_t* aOutInputBlockId);
   nsEventStatus ProcessEvent(WidgetInputEvent& inputEvent,
                              ScrollableLayerGuid* aOutTargetGuid,
                              uint64_t* aOutInputBlockId);
   void UpdateWheelTransaction(WidgetInputEvent& aEvent);
   void UpdateZoomConstraintsRecursively(HitTestingTreeNode* aNode,
                                         const ZoomConstraints& aConstraints);
+  void FlushRepaintsToClearScreenToGeckoTransform();
   void FlushRepaintsRecursively(HitTestingTreeNode* aNode);
 
   already_AddRefed<HitTestingTreeNode> RecycleOrCreateNode(TreeBuildingState& aState,
                                                            AsyncPanZoomController* aApzc);
   HitTestingTreeNode* PrepareNodeForLayer(const LayerMetricsWrapper& aLayer,
                                           const FrameMetrics& aMetrics,
                                           uint64_t aLayersId,
                                           const gfx::Matrix4x4& aAncestorTransform,
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1500,16 +1500,17 @@ AsyncPanZoomController::AllowScrollHando
 {
   WheelBlockState* block = mInputQueue->CurrentWheelBlock();
   return block->AllowScrollHandoff();
 }
 
 nsEventStatus AsyncPanZoomController::OnScrollWheel(const ScrollWheelInput& aEvent)
 {
   LayoutDevicePoint delta = GetScrollWheelDelta(aEvent);
+  APZC_LOG("%p got a scroll-wheel with delta %s\n", this, Stringify(delta).c_str());
 
   if ((delta.x || delta.y) &&
       !CanScrollWithWheel(delta) &&
       mInputQueue->GetCurrentWheelTransaction())
   {
     // We can't scroll this apz anymore, so we simply drop the event.
     if (gfxPrefs::MouseScrollTestingEnabled()) {
       if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -120,26 +120,25 @@ void Axis::StartTouch(ParentLayerCoord a
   mStartPos = aPos;
   mPos = aPos;
   mPosTimeMs = aTimestampMs;
   mAxisLocked = false;
 }
 
 bool Axis::AdjustDisplacement(ParentLayerCoord aDisplacement,
                               /* ParentLayerCoord */ float& aDisplacementOut,
-                              /* ParentLayerCoord */ float&
-                              aOverscrollAmountOut,
-                              bool forceOverscroll /* = false */)
+                              /* ParentLayerCoord */ float& aOverscrollAmountOut,
+                              bool aForceOverscroll /* = false */)
 {
   if (mAxisLocked) {
     aOverscrollAmountOut = 0;
     aDisplacementOut = 0;
     return false;
   }
-  if (forceOverscroll) {
+  if (aForceOverscroll) {
     aOverscrollAmountOut = aDisplacement;
     aDisplacementOut = 0;
     return false;
   }
 
   ClearOverscrollAnimationState();
 
   ParentLayerCoord displacement = aDisplacement;
--- a/gfx/layers/apz/src/Axis.h
+++ b/gfx/layers/apz/src/Axis.h
@@ -74,17 +74,17 @@ public:
    * ocurred, its amount is written to |aOverscrollAmountOut|.
    * The |aDisplacementOut| parameter is set to the adjusted
    * displacement, and the function returns true iff internal overscroll amounts
    * were changed.
    */
   bool AdjustDisplacement(ParentLayerCoord aDisplacement,
                           /* ParentLayerCoord */ float& aDisplacementOut,
                           /* ParentLayerCoord */ float& aOverscrollAmountOut,
-                          bool forceOverscroll = false);
+                          bool aForceOverscroll = false);
 
   /**
    * Overscrolls this axis by the requested amount in the requested direction.
    * The axis must be at the end of its scroll range in this direction.
    */
   void OverscrollBy(ParentLayerCoord aOverscroll);
 
   /**
--- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp
+++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp
@@ -1894,16 +1894,18 @@ protected:
                                         CSSRect aScrollableRect = CSSRect(-1, -1, -1, -1)) {
     FrameMetrics metrics;
     metrics.SetScrollId(aScrollId);
     IntRect layerBound = aLayer->GetVisibleRegion().GetBounds();
     metrics.SetCompositionBounds(ParentLayerRect(layerBound.x, layerBound.y,
                                                  layerBound.width, layerBound.height));
     metrics.SetScrollableRect(aScrollableRect);
     metrics.SetScrollOffset(CSSPoint(0, 0));
+    metrics.SetPageScrollAmount(LayoutDeviceIntSize(50, 100));
+    metrics.SetAllowVerticalScrollWithWheel();
     aLayer->SetFrameMetrics(metrics);
     aLayer->SetClipRect(Some(ViewAs<ParentLayerPixel>(layerBound)));
     if (!aScrollableRect.IsEqualEdges(CSSRect(-1, -1, -1, -1))) {
       // The purpose of this is to roughly mimic what layout would do in the
       // case of a scrollable frame with the event regions and clip. This lets
       // us exercise the hit-testing code in APZCTreeManager
       EventRegions er = aLayer->GetEventRegions();
       IntRect scrollRect = LayerIntRect::ToUntyped(RoundedToInt(aScrollableRect * metrics.LayersPixelsPerCSSPixel()));
@@ -2419,16 +2421,47 @@ TEST_F(APZHitTestingTester, TestRepaintF
 
   mti.mType = MultiTouchInput::MULTITOUCH_END;
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(mti, nullptr, nullptr));
   EXPECT_EQ(touchPoint, mti.mTouches[0].mScreenPoint);
 
   mcc->RunThroughDelayedTasks();
 }
 
+TEST_F(APZHitTestingTester, TestRepaintFlushOnWheelEvents) {
+  // The purpose of this test is to ensure that wheel events trigger a repaint
+  // flush as per bug 1166871, and that the wheel event untransform is a no-op.
+
+  CreateSimpleScrollingLayer();
+  ScopedLayerTreeRegistration registration(0, root, mcc);
+  manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
+  TestAsyncPanZoomController* apzcroot = ApzcOf(root);
+
+  EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(AtLeast(3));
+  ScreenPoint origin(100, 50);
+  for (int i = 0; i < 3; i++) {
+    ScrollWheelInput swi(MillisecondsSinceStartup(mTime), mTime, 0,
+      ScrollWheelInput::SCROLLMODE_INSTANT, ScrollWheelInput::SCROLLDELTA_PIXEL,
+      origin, 0, 10);
+    EXPECT_EQ(nsEventStatus_eConsumeDoDefault, manager->ReceiveInputEvent(swi, nullptr, nullptr));
+    EXPECT_EQ(origin, swi.mOrigin);
+
+    ViewTransform viewTransform;
+    ParentLayerPoint point;
+    apzcroot->SampleContentTransformForFrame(mTime, &viewTransform, point);
+    EXPECT_EQ(0, point.x);
+    EXPECT_EQ((i + 1) * 10, point.y);
+    EXPECT_EQ(0, viewTransform.mTranslation.x);
+    EXPECT_EQ((i + 1) * -10, viewTransform.mTranslation.y);
+
+    mTime += TimeDuration::FromMilliseconds(5);
+  }
+  mcc->RunThroughDelayedTasks();
+}
+
 TEST_F(APZHitTestingTester, Bug1148350) {
   CreateBug1148350LayerTree();
   ScopedLayerTreeRegistration registration(0, root, mcc);
   manager->UpdateHitTestingTree(nullptr, root, false, 0, 0);
 
   MockFunction<void(std::string checkPointName)> check;
   {
     InSequence s;
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -196,16 +196,22 @@ private:
                 WheelSmoothScrollMaxDurationMs, int32_t, 400);
   DECL_GFX_PREF(Live, "general.smoothScroll.mouseWheel.durationMinMS",
                 WheelSmoothScrollMinDurationMs, int32_t, 200);
 
   DECL_GFX_PREF(Once, "gfx.android.rgb16.force",               AndroidRGB16Force, bool, false);
 #if defined(ANDROID)
   DECL_GFX_PREF(Once, "gfx.apitrace.enabled",                  UseApitrace, bool, false);
 #endif
+#if defined(RELEASE_BUILD)
+  // "Skip" means this is locked to the default value in beta and release.
+  DECL_GFX_PREF(Skip, "gfx.blocklist.all",                     BlocklistAll, int32_t, 0);
+#else
+  DECL_GFX_PREF(Once, "gfx.blocklist.all",                     BlocklistAll, int32_t, 0);
+#endif
   DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_calls",  CanvasAutoAccelerateMinCalls, int32_t, 4);
   DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_frames", CanvasAutoAccelerateMinFrames, int32_t, 30);
   DECL_GFX_PREF(Live, "gfx.canvas.auto_accelerate.min_seconds", CanvasAutoAccelerateMinSeconds, float, 5.0f);
   DECL_GFX_PREF(Live, "gfx.canvas.azure.accelerated",          CanvasAzureAccelerated, bool, false);
   // 0x7fff is the maximum supported xlib surface size and is more than enough for canvases.
   DECL_GFX_PREF(Live, "gfx.canvas.max-size",                   MaxCanvasSize, int32_t, 0x7fff);
   DECL_GFX_PREF(Once, "gfx.canvas.skiagl.cache-items",         CanvasSkiaGLCacheItems, int32_t, 256);
   DECL_GFX_PREF(Once, "gfx.canvas.skiagl.cache-size",          CanvasSkiaGLCacheSize, int32_t, 96);
--- a/hal/gonk/GonkHal.cpp
+++ b/hal/gonk/GonkHal.cpp
@@ -759,18 +759,21 @@ SetScreenEnabled(bool aEnabled)
   GetGonkDisplay()->SetEnabled(aEnabled);
   sScreenEnabled = aEnabled;
 }
 
 bool
 GetKeyLightEnabled()
 {
   LightConfiguration config;
-  GetLight(eHalLightID_Buttons, &config);
-  return (config.color != 0x00000000);
+  bool ok = GetLight(eHalLightID_Buttons, &config);
+  if (ok) {
+    return (config.color != 0x00000000);
+  }
+  return false;
 }
 
 void
 SetKeyLightEnabled(bool aEnabled)
 {
   LightConfiguration config;
   config.mode = eHalLightMode_User;
   config.flash = eHalLightFlash_None;
@@ -793,20 +796,25 @@ SetKeyLightEnabled(bool aEnabled)
 }
 
 double
 GetScreenBrightness()
 {
   LightConfiguration config;
   LightType light = eHalLightID_Backlight;
 
-  GetLight(light, &config);
-  // backlight is brightness only, so using one of the RGB elements as value.
-  int brightness = config.color & 0xFF;
-  return brightness / 255.0;
+  bool ok = GetLight(light, &config);
+  if (ok) {
+    // backlight is brightness only, so using one of the RGB elements as value.
+    int brightness = config.color & 0xFF;
+    return brightness / 255.0;
+  }
+  // If GetLight fails, it's because the light doesn't exist.  So return
+  // a value corresponding to "off".
+  return 0;
 }
 
 void
 SetScreenBrightness(double brightness)
 {
   // Don't use De Morgan's law to push the ! into this expression; we want to
   // catch NaN too.
   if (!(0 <= brightness && brightness <= 1)) {
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -10,16 +10,18 @@
 #include "mozilla/media/MediaChild.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/dom/PBlobChild.h"
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/ipc/PBackgroundTestChild.h"
 #include "mozilla/layout/VsyncChild.h"
+#include "mozilla/net/PUDPSocketChild.h"
+#include "mozilla/dom/network/UDPSocketChild.h"
 #include "nsID.h"
 #include "nsTraceRefcnt.h"
 
 namespace {
 
 class TestChild final : public mozilla::ipc::PBackgroundTestChild
 {
   friend class mozilla::ipc::BackgroundChildImpl;
@@ -43,16 +45,19 @@ public:
   Recv__delete__(const nsCString& aTestArg) override;
 };
 
 } // anonymous namespace
 
 namespace mozilla {
 namespace ipc {
 
+using mozilla::dom::UDPSocketChild;
+using mozilla::net::PUDPSocketChild;
+
 using mozilla::dom::cache::PCacheChild;
 using mozilla::dom::cache::PCacheStorageChild;
 using mozilla::dom::cache::PCacheStreamControlChild;
 
 // -----------------------------------------------------------------------------
 // BackgroundChildImpl::ThreadLocal
 // -----------------------------------------------------------------------------
 
@@ -208,16 +213,33 @@ BackgroundChildImpl::DeallocPVsyncChild(
   MOZ_ASSERT(aActor);
 
   // This actor already has one ref-count. Please check AllocPVsyncChild().
   nsRefPtr<mozilla::layout::VsyncChild> actor =
       dont_AddRef(static_cast<mozilla::layout::VsyncChild*>(aActor));
   return true;
 }
 
+PUDPSocketChild*
+BackgroundChildImpl::AllocPUDPSocketChild(const OptionalPrincipalInfo& aPrincipalInfo,
+                                          const nsCString& aFilter)
+{
+  MOZ_CRASH("AllocPUDPSocket should not be called");
+  return nullptr;
+}
+
+bool
+BackgroundChildImpl::DeallocPUDPSocketChild(PUDPSocketChild* child)
+{
+
+  UDPSocketChild* p = static_cast<UDPSocketChild*>(child);
+  p->ReleaseIPDLReference();
+  return true;
+}
+
 // -----------------------------------------------------------------------------
 // BroadcastChannel API
 // -----------------------------------------------------------------------------
 
 dom::PBroadcastChannelChild*
 BackgroundChildImpl::AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
                                                  const nsString& aOrigin,
                                                  const nsString& aChannel,
--- a/ipc/glue/BackgroundChildImpl.h
+++ b/ipc/glue/BackgroundChildImpl.h
@@ -78,16 +78,22 @@ protected:
   DeallocPMediaChild(PMediaChild* aActor) override;
 
   virtual PVsyncChild*
   AllocPVsyncChild() override;
 
   virtual bool
   DeallocPVsyncChild(PVsyncChild* aActor) override;
 
+  virtual PUDPSocketChild*
+  AllocPUDPSocketChild(const OptionalPrincipalInfo& aPrincipalInfo,
+                       const nsCString& aFilter) override;
+  virtual bool
+  DeallocPUDPSocketChild(PUDPSocketChild* aActor) override;
+
   virtual PBroadcastChannelChild*
   AllocPBroadcastChannelChild(const PrincipalInfo& aPrincipalInfo,
                               const nsString& aOrigin,
                               const nsString& aChannel,
                               const bool& aPrivateBrowsing) override;
 
   virtual bool
   DeallocPBroadcastChannelChild(PBroadcastChannelChild* aActor) override;
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/dom/cache/ActorUtils.h"
 #include "mozilla/dom/indexedDB/ActorsParent.h"
 #include "mozilla/dom/ipc/BlobParent.h"
 #include "mozilla/ipc/BackgroundParent.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "mozilla/ipc/PBackgroundSharedTypes.h"
 #include "mozilla/ipc/PBackgroundTestParent.h"
 #include "mozilla/layout/VsyncParent.h"
+#include "mozilla/dom/network/UDPSocketParent.h"
 #include "nsIAppsService.h"
 #include "nsNetUtil.h"
 #include "nsRefPtr.h"
 #include "nsThreadUtils.h"
 #include "nsTraceRefcnt.h"
 #include "nsXULAppAPI.h"
 
 #ifdef DISABLE_ASSERTS_FOR_FUZZING
@@ -32,16 +33,17 @@
 #else
 #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false)
 #endif
 
 using mozilla::ipc::AssertIsOnBackgroundThread;
 using mozilla::dom::cache::PCacheParent;
 using mozilla::dom::cache::PCacheStorageParent;
 using mozilla::dom::cache::PCacheStreamControlParent;
+using mozilla::dom::UDPSocketParent;
 
 namespace {
 
 void
 AssertIsInMainProcess()
 {
   MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
 }
@@ -247,16 +249,102 @@ BackgroundParentImpl::DeallocPVsyncParen
   MOZ_ASSERT(aActor);
 
   // This actor already has one ref-count. Please check AllocPVsyncParent().
   nsRefPtr<mozilla::layout::VsyncParent> actor =
       dont_AddRef(static_cast<mozilla::layout::VsyncParent*>(aActor));
   return true;
 }
 
+namespace {
+
+class InitUDPSocketParentCallback final : public nsRunnable
+{
+public:
+  InitUDPSocketParentCallback(UDPSocketParent* aActor,
+                              const nsACString& aFilter)
+    : mActor(aActor)
+    , mFilter(aFilter)
+  {
+    AssertIsInMainProcess();
+    AssertIsOnBackgroundThread();
+  }
+
+  NS_IMETHODIMP
+  Run()
+  {
+    AssertIsInMainProcess();
+
+    IPC::Principal principal;
+    if (!mActor->Init(principal, mFilter)) {
+      MOZ_CRASH("UDPSocketCallback - failed init");
+    }
+    return NS_OK;
+  }
+
+private:
+  ~InitUDPSocketParentCallback() {};
+
+  nsRefPtr<UDPSocketParent> mActor;
+  nsCString mFilter;
+};
+
+}
+
+auto
+BackgroundParentImpl::AllocPUDPSocketParent(const OptionalPrincipalInfo& /* unused */,
+                                            const nsCString& /* unused */)
+  -> PUDPSocketParent*
+{
+  nsRefPtr<UDPSocketParent> p = new UDPSocketParent(this);
+
+  return p.forget().take();
+}
+
+bool
+BackgroundParentImpl::RecvPUDPSocketConstructor(PUDPSocketParent* aActor,
+                                                const OptionalPrincipalInfo& aOptionalPrincipal,
+                                                const nsCString& aFilter)
+{
+  AssertIsInMainProcess();
+  AssertIsOnBackgroundThread();
+
+  if (aOptionalPrincipal.type() == OptionalPrincipalInfo::TPrincipalInfo) {
+    // Support for checking principals (for non-mtransport use) will be handled in
+    // bug 1167039
+    return false;
+  }
+  // No principal - This must be from mtransport (WebRTC/ICE) - We'd want
+  // to DispatchToMainThread() here, but if we do we must block RecvBind()
+  // until Init() gets run.  Since we don't have a principal, and we verify
+  // we have a filter, we can safely skip the Dispatch and just invoke Init()
+  // to install the filter.
+
+  // For mtransport, this will always be "stun", which doesn't allow outbound packets if
+  // they aren't STUN packets until a STUN response is seen.
+  if (!aFilter.EqualsASCII("stun")) {
+    return false;
+  }
+
+  IPC::Principal principal;
+  if (!static_cast<UDPSocketParent*>(aActor)->Init(principal, aFilter)) {
+    MOZ_CRASH("UDPSocketCallback - failed init");
+  }
+
+  return true;
+}
+
+bool
+BackgroundParentImpl::DeallocPUDPSocketParent(PUDPSocketParent* actor)
+{
+  UDPSocketParent* p = static_cast<UDPSocketParent*>(actor);
+  p->Release();
+  return true;
+}
+
 mozilla::dom::PBroadcastChannelParent*
 BackgroundParentImpl::AllocPBroadcastChannelParent(
                                             const PrincipalInfo& aPrincipalInfo,
                                             const nsString& aOrigin,
                                             const nsString& aChannel,
                                             const bool& aPrivateBrowsing)
 {
   AssertIsInMainProcess();
@@ -477,18 +565,20 @@ public:
       AssertAppPrincipal(mContentParent, principal);
       mContentParent = nullptr;
 
       mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL);
       return NS_OK;
     }
 
     AssertIsOnBackgroundThread();
-    mCallback->Run();
-    mCallback = nullptr;
+    if (mCallback) {
+      mCallback->Run();
+      mCallback = nullptr;
+    }
 
     return NS_OK;
   }
 
 private:
   nsRefPtr<ContentParent> mContentParent;
   PrincipalInfo mPrincipalInfo;
   nsRefPtr<nsRunnable> mCallback;
--- a/ipc/glue/BackgroundParentImpl.h
+++ b/ipc/glue/BackgroundParentImpl.h
@@ -114,15 +114,26 @@ protected:
 
   virtual bool
   DeallocPCacheParent(dom::cache::PCacheParent* aActor) override;
 
   virtual dom::cache::PCacheStreamControlParent*
   AllocPCacheStreamControlParent() override;
 
   virtual bool
-  DeallocPCacheStreamControlParent(dom::cache::PCacheStreamControlParent* aActor) override;
+  DeallocPCacheStreamControlParent(dom::cache::PCacheStreamControlParent* aActor)
+                                   override;
+
+  virtual PUDPSocketParent*
+  AllocPUDPSocketParent(const OptionalPrincipalInfo& pInfo,
+                        const nsCString& aFilter) override;
+  virtual bool
+  RecvPUDPSocketConstructor(PUDPSocketParent*,
+                            const OptionalPrincipalInfo& aPrincipalInfo,
+                            const nsCString& aFilter) override;
+  virtual bool
+  DeallocPUDPSocketParent(PUDPSocketParent*) override;
 };
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_backgroundparentimpl_h__
--- a/ipc/glue/PBackground.ipdl
+++ b/ipc/glue/PBackground.ipdl
@@ -5,18 +5,19 @@
 include protocol PBackgroundIDBFactory;
 include protocol PBackgroundTest;
 include protocol PBlob;
 include protocol PBroadcastChannel;
 include protocol PCache;
 include protocol PCacheStorage;
 include protocol PCacheStreamControl;
 include protocol PFileDescriptorSet;
+include protocol PMedia;
+include protocol PUDPSocket;
 include protocol PVsync;
-include protocol PMedia;
 
 include DOMTypes;
 include PBackgroundSharedTypes;
 include PBackgroundIDBSharedTypes;
 include ServiceWorkerRegistrarTypes;
 
 using mozilla::dom::cache::Namespace from "mozilla/dom/cache/Types.h";
 include "mozilla/dom/cache/IPCUtils.h";
@@ -29,28 +30,30 @@ sync protocol PBackground
   manages PBackgroundIDBFactory;
   manages PBackgroundTest;
   manages PBlob;
   manages PBroadcastChannel;
   manages PCache;
   manages PCacheStorage;
   manages PCacheStreamControl;
   manages PFileDescriptorSet;
+  manages PMedia;
+  manages PUDPSocket;
   manages PVsync;
-  manages PMedia;
 
 parent:
   // Only called at startup during mochitests to check the basic infrastructure.
   PBackgroundTest(nsCString testArg);
 
   PBackgroundIDBFactory(LoggingInfo loggingInfo);
 
   PVsync();
   PMedia();
 
+  PUDPSocket(OptionalPrincipalInfo pInfo, nsCString filter);
   PBroadcastChannel(PrincipalInfo pInfo, nsString origin, nsString channel,
                     bool privateBrowsing);
 
   RegisterServiceWorker(ServiceWorkerRegistrationData data);
   UnregisterServiceWorker(PrincipalInfo principalInfo,
                           nsString scope);
   ShutdownServiceWorkerRegistrar();
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1322,20 +1322,20 @@ class MOZ_STACK_CLASS AutoStableStringCh
     bool ownsChars_;
 
   public:
     explicit AutoStableStringChars(JSContext* cx)
       : s_(cx), state_(Uninitialized), ownsChars_(false)
     {}
     ~AutoStableStringChars();
 
-    bool init(JSContext* cx, JSString* s);
+    bool init(JSContext* cx, JSString* s) MOZ_WARN_UNUSED_RESULT;
 
     /* Like init(), but Latin1 chars are inflated to TwoByte. */
-    bool initTwoByte(JSContext* cx, JSString* s);
+    bool initTwoByte(JSContext* cx, JSString* s) MOZ_WARN_UNUSED_RESULT;
 
     bool isLatin1() const { return state_ == Latin1; }
     bool isTwoByte() const { return state_ == TwoByte; }
 
     const char16_t* twoByteChars() const {
         MOZ_ASSERT(state_ == TwoByte);
         return twoByteChars_;
     }
--- a/js/xpconnect/src/XPCShellImpl.cpp
+++ b/js/xpconnect/src/XPCShellImpl.cpp
@@ -27,16 +27,17 @@
 #include "nsAutoPtr.h"
 #include "nsJSPrincipals.h"
 #include "xpcpublic.h"
 #include "xpcprivate.h"
 #include "BackstagePass.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 #include "nsJSUtils.h"
+#include "gfxPrefs.h"
 
 #include "base/histogram.h"
 
 #ifdef ANDROID
 #include <android/log.h>
 #endif
 
 #ifdef XP_WIN
@@ -1483,16 +1484,19 @@ XRE_XPCShellMain(int argc, char** argv, 
                                                   static_cast<nsIGlobalObject*>(backstagePass),
                                                   systemprincipal,
                                                   0,
                                                   options,
                                                   getter_AddRefs(holder));
         if (NS_FAILED(rv))
             return 1;
 
+        // Initialize graphics prefs on the main thread, if not already done
+        gfxPrefs::GetSingleton();
+
         {
             JS::Rooted<JSObject*> glob(cx, holder->GetJSObject());
             if (!glob) {
                 return 1;
             }
 
             // Even if we're building in a configuration where source is
             // discarded, there's no reason to do that on XPCShell, and doing so
--- a/js/xpconnect/src/xpcObjectHelper.h
+++ b/js/xpconnect/src/xpcObjectHelper.h
@@ -115,20 +115,24 @@ protected:
       , mObject(aObject)
       , mCache(aCache)
     {
         if (!mCache && aObject)
             CallQueryInterface(aObject, &mCache);
     }
 
     nsCOMPtr<nsISupports>    mCanonicalStrong;
-    nsISupports*             mCanonical;
+    nsISupports* MOZ_UNSAFE_REF("xpcObjectHelper has been specifically optimized "
+                                "to avoid unnecessary AddRefs and Releases. "
+                                "(see bug 565742)") mCanonical;
 
 private:
     xpcObjectHelper(xpcObjectHelper& aOther) = delete;
 
-    nsISupports*             mObject;
+    nsISupports* MOZ_UNSAFE_REF("xpcObjectHelper has been specifically optimized "
+                                "to avoid unnecessary AddRefs and Releases. "
+                                "(see bug 565742)") mObject;
     nsWrapperCache*          mCache;
     nsCOMPtr<nsIClassInfo>   mClassInfo;
     nsRefPtr<nsXPCClassInfo> mXPCClassInfo;
 };
 
 #endif
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -85,16 +85,17 @@ AccessibleCaretManager::OnSelectionChang
 
 void
 AccessibleCaretManager::HideCarets()
 {
   if (mFirstCaret->IsLogicallyVisible() || mSecondCaret->IsLogicallyVisible()) {
     AC_LOG("%s", __FUNCTION__);
     mFirstCaret->SetAppearance(Appearance::None);
     mSecondCaret->SetAppearance(Appearance::None);
+    DispatchCaretStateChangedEvent(CaretChangedReason::Visibilitychange);
     CancelCaretTimeoutTimer();
   }
 }
 
 void
 AccessibleCaretManager::UpdateCarets()
 {
   mCaretMode = GetCaretMode();
@@ -149,26 +150,32 @@ AccessibleCaretManager::UpdateCaretsForC
   if (!editingHost) {
     HideCarets();
     return;
   }
 
   // No need to consider whether the caret's position is out of scrollport.
   // According to the spec, we need to explicitly hide it after the scrolling is
   // ended.
-  mFirstCaret->SetPosition(frame, offset);
+  bool oldSecondCaretVisible = mSecondCaret->IsLogicallyVisible();
+  PositionChangedResult caretResult = mFirstCaret->SetPosition(frame, offset);
   mFirstCaret->SetSelectionBarEnabled(false);
   if (nsContentUtils::HasNonEmptyTextContent(
         editingHost, nsContentUtils::eRecurseIntoChildren)) {
     mFirstCaret->SetAppearance(Appearance::Normal);
     LaunchCaretTimeoutTimer();
   } else {
     mFirstCaret->SetAppearance(Appearance::NormalNotShown);
   }
   mSecondCaret->SetAppearance(Appearance::None);
+
+  if ((caretResult == PositionChangedResult::Changed ||
+      oldSecondCaretVisible) && !mActiveCaret) {
+    DispatchCaretStateChangedEvent(CaretChangedReason::Updateposition);
+  }
 }
 
 void
 AccessibleCaretManager::UpdateCaretsForSelectionMode()
 {
   AC_LOG("%s, selection: %p", __FUNCTION__, GetSelection());
 
   int32_t startOffset = 0;
@@ -209,16 +216,24 @@ AccessibleCaretManager::UpdateCaretsForS
 
   if (firstCaretResult == PositionChangedResult::Changed ||
       secondCaretResult == PositionChangedResult::Changed) {
     // Flush layout to make the carets intersection correct.
     mPresShell->FlushPendingNotifications(Flush_Layout);
   }
 
   UpdateCaretsForTilt();
+
+  if ((firstCaretResult == PositionChangedResult::Changed ||
+       secondCaretResult == PositionChangedResult::Changed ||
+       firstCaretResult == PositionChangedResult::Invisible ||
+       secondCaretResult == PositionChangedResult::Invisible) &&
+      !mActiveCaret) {
+    DispatchCaretStateChangedEvent(CaretChangedReason::Updateposition);
+  }
 }
 
 void
 AccessibleCaretManager::UpdateCaretsForTilt()
 {
   if (mFirstCaret->IsVisuallyVisible() && mSecondCaret->IsVisuallyVisible()) {
     if (mFirstCaret->Intersects(*mSecondCaret)) {
       if (mFirstCaret->LogicalPosition().x <=
@@ -248,16 +263,17 @@ AccessibleCaretManager::PressCaret(const
     mActiveCaret = mSecondCaret.get();
     SetSelectionDirection(eDirNext);
   }
 
   if (mActiveCaret) {
     mOffsetYToCaretLogicalPosition =
       mActiveCaret->LogicalPosition().y - aPoint.y;
     SetSelectionDragState(true);
+    DispatchCaretStateChangedEvent(CaretChangedReason::Presscaret);
     CancelCaretTimeoutTimer();
     rv = NS_OK;
   }
 
   return rv;
 }
 
 nsresult
@@ -274,28 +290,30 @@ AccessibleCaretManager::DragCaret(const 
 
 nsresult
 AccessibleCaretManager::ReleaseCaret()
 {
   MOZ_ASSERT(mActiveCaret);
 
   mActiveCaret = nullptr;
   SetSelectionDragState(false);
+  DispatchCaretStateChangedEvent(CaretChangedReason::Releasecaret);
   LaunchCaretTimeoutTimer();
   return NS_OK;
 }
 
 nsresult
 AccessibleCaretManager::TapCaret(const nsPoint& aPoint)
 {
   MOZ_ASSERT(GetCaretMode() != CaretMode::None);
 
   nsresult rv = NS_ERROR_FAILURE;
 
   if (GetCaretMode() == CaretMode::Cursor) {
+    DispatchCaretStateChangedEvent(CaretChangedReason::Taponcaret);
     rv = NS_OK;
   }
 
   return rv;
 }
 
 nsresult
 AccessibleCaretManager::SelectWordOrShortcut(const nsPoint& aPoint)
@@ -326,16 +344,17 @@ AccessibleCaretManager::SelectWordOrShor
   nsLayoutUtils::TransformPoint(rootFrame, ptFrame, ptInFrame);
 
   nsIContent* editingHost = ptFrame->GetContent()->GetEditingHost();
   if (ChangeFocus(ptFrame) &&
       (editingHost && !nsContentUtils::HasNonEmptyTextContent(
                          editingHost, nsContentUtils::eRecurseIntoChildren))) {
     // Content is empty. No need to select word.
     AC_LOG("%s, Cannot select word bacause content is empty", __FUNCTION__);
+    DispatchCaretStateChangedEvent(CaretChangedReason::Longpressonemptycontent);
     return NS_OK;
   }
 
   nsresult rv = SelectWord(ptFrame, ptInFrame);
   UpdateCarets();
   return rv;
 }
 
@@ -861,9 +880,80 @@ AccessibleCaretManager::LaunchCaretTimeo
 void
 AccessibleCaretManager::CancelCaretTimeoutTimer()
 {
   if (mCaretTimeoutTimer) {
     mCaretTimeoutTimer->Cancel();
   }
 }
 
+void
+AccessibleCaretManager::DispatchCaretStateChangedEvent(CaretChangedReason aReason) const
+{
+  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+  // Holding PresShell to prevent AccessibleCaretManager to be destroyed.
+  nsCOMPtr<nsIPresShell> presShell = mPresShell;
+  // XXX: Do we need to flush layout?
+  presShell->FlushPendingNotifications(Flush_Layout);
+  if (presShell->IsDestroying()) {
+    return;
+  }
+
+  Selection* sel = GetSelection();
+  if (!sel) {
+    return;
+  }
+
+  nsIDocument* doc = mPresShell->GetDocument();
+  MOZ_ASSERT(doc);
+
+  CaretStateChangedEventInit init;
+  init.mBubbles = true;
+
+  const nsRange* range = sel->GetAnchorFocusRange();
+  nsINode* commonAncestorNode = nullptr;
+  if (range) {
+    commonAncestorNode = range->GetCommonAncestor();
+  }
+
+  if (!commonAncestorNode) {
+    commonAncestorNode = sel->GetFrameSelection()->GetAncestorLimiter();
+  }
+
+  nsRefPtr<DOMRect> domRect = new DOMRect(ToSupports(doc));
+  nsRect rect = nsContentUtils::GetSelectionBoundingRect(sel);
+
+  nsIFrame* commonAncestorFrame = nullptr;
+  nsIFrame* rootFrame = mPresShell->GetRootFrame();
+
+  if (commonAncestorNode && commonAncestorNode->IsContent()) {
+    commonAncestorFrame = commonAncestorNode->AsContent()->GetPrimaryFrame();
+  }
+
+  if (commonAncestorFrame && rootFrame) {
+    nsLayoutUtils::TransformRect(rootFrame, commonAncestorFrame, rect);
+    nsRect clampedRect = nsLayoutUtils::ClampRectToScrollFrames(commonAncestorFrame,
+                                                                rect);
+    nsLayoutUtils::TransformRect(commonAncestorFrame, rootFrame, clampedRect);
+    domRect->SetLayoutRect(clampedRect);
+    init.mSelectionVisible = !clampedRect.IsEmpty();
+    init.mBoundingClientRect = domRect;
+  } else {
+    domRect->SetLayoutRect(rect);
+    init.mSelectionVisible = true;
+  }
+
+  init.mBoundingClientRect = domRect;
+  init.mReason = aReason;
+  init.mCollapsed = sel->IsCollapsed();
+  init.mCaretVisible = mFirstCaret->IsLogicallyVisible() ||
+                       mSecondCaret->IsLogicallyVisible();
+
+  nsRefPtr<CaretStateChangedEvent> event =
+    CaretStateChangedEvent::Constructor(doc, NS_LITERAL_STRING("mozcaretstatechanged"), init);
+
+  event->SetTrusted(true);
+  event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
+  bool ret;
+  doc->DispatchEvent(event, &ret);
+}
+
 } // namespace mozilla
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.h
@@ -8,16 +8,17 @@
 #define AccessibleCaretManager_h
 
 #include "nsCOMPtr.h"
 #include "nsCoord.h"
 #include "nsIFrame.h"
 #include "nsISelectionListener.h"
 #include "nsRefPtr.h"
 #include "nsWeakReference.h"
+#include "mozilla/dom/CaretStateChangedEvent.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 
 class nsFrameSelection;
 class nsIContent;
 class nsIPresShell;
 struct nsPoint;
@@ -127,16 +128,20 @@ protected:
   nsresult DragCaretInternal(const nsPoint& aPoint);
   nsPoint AdjustDragBoundary(const nsPoint& aPoint) const;
   void ClearMaintainedSelection() const;
 
   dom::Selection* GetSelection() const;
   already_AddRefed<nsFrameSelection> GetFrameSelection() const;
   nsIContent* GetFocusedContent() const;
 
+  // This function will call FlushPendingNotifications. So caller must ensure
+  // everything exists after calling this method.
+  void DispatchCaretStateChangedEvent(dom::CaretChangedReason aReason) const;
+
   // If we're dragging the first caret, we do not want to drag it over the
   // previous character of the second caret. Same as the second caret. So we
   // check if content offset exceeds the previous/next character of second/first
   // caret base the active caret.
   bool CompareRangeWithContentOffset(nsIFrame::ContentOffsets& aOffsets);
 
   // Timeout in milliseconds to hide the AccessibleCaret under cursor mode while
   // no one touches it.
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -1290,16 +1290,26 @@ public:
   nsIntRegion mRegionToInvalidate;
 
   // The offset between the active scrolled root of this layer
   // and the root of the container for the previous and current
   // paints respectively.
   nsPoint mLastAnimatedGeometryRootOrigin;
   nsPoint mAnimatedGeometryRootOrigin;
 
+  // If mIgnoreInvalidationsOutsideRect is set, this contains the bounds of the
+  // layer's old visible region, in layer pixels.
+  nsIntRect mOldVisibleBounds;
+
+  // If set, invalidations that fall outside of this rect should not result in
+  // calls to layer->InvalidateRegion during DLBI. Instead, the parts outside
+  // this rectangle will be invalidated in InvalidateVisibleBoundsChangesForScrolledLayer.
+  // See the comment in ComputeAndSetIgnoreInvalidationRect for more information.
+  Maybe<nsIntRect> mIgnoreInvalidationsOutsideRect;
+
   nsRefPtr<ColorLayer> mColorLayer;
   nsRefPtr<ImageLayer> mImageLayer;
 };
 
 /*
  * User data for layers which will be used as masks.
  */
 struct MaskLayerUserData : public LayerUserData
@@ -1466,47 +1476,53 @@ AppendToString(nsACString& s, const nsIn
   return s += sfx;
 }
 
 /**
  * Invalidate aRegion in aLayer. aLayer is in the coordinate system
  * *after* aTranslation has been applied, so we need to
  * apply the inverse of that transform before calling InvalidateRegion.
  */
-static void
-InvalidatePostTransformRegion(PaintedLayer* aLayer, const nsIntRegion& aRegion,
-                              const nsIntPoint& aTranslation)
+template<typename RegionOrRect> void
+InvalidatePostTransformRegion(PaintedLayer* aLayer, const RegionOrRect& aRegion,
+                              const nsIntPoint& aTranslation,
+                              PaintedDisplayItemLayerUserData* aData)
 {
   // Convert the region from the coordinates of the container layer
   // (relative to the snapped top-left of the display list reference frame)
   // to the PaintedLayer's own coordinates
-  nsIntRegion rgn = aRegion;
+  RegionOrRect rgn = aRegion;
   rgn.MoveBy(-aTranslation);
-  aLayer->InvalidateRegion(rgn);
+  if (aData->mIgnoreInvalidationsOutsideRect) {
+    rgn = rgn.Intersect(*aData->mIgnoreInvalidationsOutsideRect);
+  }
+  if (!rgn.IsEmpty()) {
+    aLayer->InvalidateRegion(rgn);
 #ifdef MOZ_DUMP_PAINTING
-  if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
-    nsAutoCString str;
-    AppendToString(str, rgn);
-    printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get());
-  }
+    if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+      nsAutoCString str;
+      AppendToString(str, rgn);
+      printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get());
+    }
 #endif
+  }
 }
 
 static void
 InvalidatePostTransformRegion(PaintedLayer* aLayer, const nsRect& aRect,
                               const DisplayItemClip& aClip,
                               const nsIntPoint& aTranslation)
 {
   PaintedDisplayItemLayerUserData* data =
       static_cast<PaintedDisplayItemLayerUserData*>(aLayer->GetUserData(&gPaintedDisplayItemLayerUserData));
 
   nsRect rect = aClip.ApplyNonRoundedIntersection(aRect);
 
   nsIntRect pixelRect = rect.ScaleToOutsidePixels(data->mXScale, data->mYScale, data->mAppUnitsPerDevPixel);
-  InvalidatePostTransformRegion(aLayer, pixelRect, aTranslation);
+  InvalidatePostTransformRegion(aLayer, pixelRect, aTranslation, data);
 }
 
 
 static nsIntPoint
 GetTranslationForPaintedLayer(PaintedLayer* aLayer)
 {
   PaintedDisplayItemLayerUserData* data =
     static_cast<PaintedDisplayItemLayerUserData*>
@@ -2101,16 +2117,99 @@ ContainerState::RecyclePaintedLayer(Pain
       printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get());
     }
 #endif
     data->mRegionToInvalidate.SetEmpty();
   }
   return data;
 }
 
+static void
+ComputeAndSetIgnoreInvalidationRect(PaintedLayer* aLayer,
+                                    PaintedDisplayItemLayerUserData* aData,
+                                    const nsIFrame* aAnimatedGeometryRoot,
+                                    nsDisplayListBuilder* aBuilder,
+                                    const nsIntPoint& aLayerTranslation)
+{
+  if (!aLayer->Manager()->IsWidgetLayerManager()) {
+    // This optimization is only useful for layers with retained content.
+    return;
+  }
+
+  const nsIFrame* parentFrame = aAnimatedGeometryRoot->GetParent();
+
+  // GetDirtyRectForScrolledContents will return an empty rect if parentFrame
+  // is not a scrollable frame.
+  nsRect dirtyRect = aBuilder->GetDirtyRectForScrolledContents(parentFrame);
+
+  if (dirtyRect.IsEmpty()) {
+    // parentFrame is not a scrollable frame, or we didn't encounter it during
+    // display list building (though this shouldn't happen), or it's empty.
+    // In all those cases this optimization is not needed.
+    return;
+  }
+
+  // parentFrame is a scrollable frame, and aLayer contains the scrolled
+  // contents of that frame.
+
+  // maxNewVisibleBounds is a conservative approximation of the new visible
+  // region of aLayer.
+  nsIntRect maxNewVisibleBounds =
+    dirtyRect.ScaleToOutsidePixels(aData->mXScale, aData->mYScale,
+                                   aData->mAppUnitsPerDevPixel) - aLayerTranslation;
+  aData->mOldVisibleBounds = aLayer->GetVisibleRegion().GetBounds();
+
+  // When the visible region of aLayer changes (e.g. due to scrolling),
+  // three distinct types of invalidations need to be triggered:
+  //  (1) Items (or parts of items) that have left the visible region need
+  //      to be invalidated so that the pixels they painted are no longer
+  //      part of the layer's valid region.
+  //  (2) Items (or parts of items) that weren't in the old visible region
+  //      but are in the new visible region need to be invalidated. This
+  //      invalidation isn't required for painting the right layer
+  //      contents, because these items weren't part of the layer's valid
+  //      region, so they'd be painted anyway. It is, however, necessary in
+  //      order to get an accurate invalid region for the layer tree that
+  //      aLayer is in, for example for partial compositing.
+  //  (3) Any changes that happened in the intersection of the old and the
+  //      new visible region need to be invalidated. There shouldn't be any
+  //      of these when scrolling static content.
+  //
+  // We'd like to guarantee that we won't invalidate anything in the
+  // intersection area of the old and the new visible region if all
+  // invalidation are of type (1) and (2). However, if we just call
+  // aLayer->InvalidateRegion for the invalidations of type (1) and (2),
+  // at some point we'll hit the complexity limit of the layer's invalid
+  // region. And the resulting region simplification can cause the region
+  // to intersect with the intersection of the old and the new visible
+  // region.
+  // In order to get around this problem, we're using the following approach:
+  //  - aData->mIgnoreInvalidationsOutsideRect is set to a conservative
+  //    approximation of the intersection of the old and the new visible
+  //    region. At this point we don't know the layer's new visible region.
+  //  - As long as we don't know the layer's new visible region, we ignore all
+  //    invalidations outside that rectangle, so roughly some of the
+  //    invalidations of type (1) and (2).
+  //  - Once we know the layer's new visible region, which happens at some
+  //    point during PostprocessRetainedLayers, we invalidate a conservative
+  //    approximation of (1) and (2). Specifically, we invalidate the region
+  //    union of the old visible bounds and the new visible bounds, minus
+  //    aData->mIgnoreInvalidationsOutsideRect. That region is simple enough
+  //    that it will never be simplified on its own.
+  //    We unset mIgnoreInvalidationsOutsideRect at this point.
+  //  - Any other invalidations that happen on the layer after this point, e.g.
+  //    during WillEndTransaction, will just happen regularly. If they are of
+  //    type (1) or (2), they won't change the layer's invalid region because
+  //    they fall inside the region we invalidated in the previous step.
+  // Consequently, aData->mIgnoreInvalidationsOutsideRect is safe from
+  // invalidations as long as there are no invalidations of type (3).
+  aData->mIgnoreInvalidationsOutsideRect =
+    Some(maxNewVisibleBounds.Intersect(aData->mOldVisibleBounds));
+}
+
 void
 ContainerState::PreparePaintedLayerForUse(PaintedLayer* aLayer,
                                           PaintedDisplayItemLayerUserData* aData,
                                           const nsIFrame* aAnimatedGeometryRoot,
                                           const nsIFrame* aReferenceFrame,
                                           const nsPoint& aTopLeft,
                                           bool didResetScrollPositionForLayerPixelAlignment)
 {
@@ -2134,16 +2233,18 @@ ContainerState::PreparePaintedLayerForUs
   // is close to aData->mAnimatedGeometryRootPosition if possible.
   nsIntPoint pixOffset(RoundToMatchResidual(scaledOffset.x, aData->mAnimatedGeometryRootPosition.x),
                        RoundToMatchResidual(scaledOffset.y, aData->mAnimatedGeometryRootPosition.y));
   aData->mTranslation = pixOffset;
   pixOffset += mParameters.mOffset;
   Matrix matrix = Matrix::Translation(pixOffset.x, pixOffset.y);
   aLayer->SetBaseTransform(Matrix4x4::From2D(matrix));
 
+  ComputeAndSetIgnoreInvalidationRect(aLayer, aData, aAnimatedGeometryRoot, mBuilder, pixOffset);
+
   // FIXME: Temporary workaround for bug 681192 and bug 724786.
 #ifndef MOZ_WIDGET_ANDROID
   // Calculate exact position of the top-left of the active scrolled root.
   // This might not be 0,0 due to the snapping in ScaleToNearestPixels.
   gfxPoint animatedGeometryRootTopLeft = scaledOffset - ThebesPoint(matrix.GetTranslation()) + mParameters.mOffset;
   // If it has changed, then we need to invalidate the entire layer since the
   // pixels in the layer buffer have the content at a (subpixel) offset
   // from what we need.
@@ -3943,17 +4044,18 @@ FrameLayerBuilder::ComputeGeometryChange
 #endif
   }
   if (!combined.IsEmpty()) {
     if (notifyRenderingChanged) {
       item->NotifyRenderingChanged();
     }
     InvalidatePostTransformRegion(paintedLayer,
         combined.ScaleToOutsidePixels(layerData->mXScale, layerData->mYScale, layerData->mAppUnitsPerDevPixel),
-        layerData->mTranslation);
+        layerData->mTranslation,
+        layerData);
   }
 
   aData->EndUpdate(geometry);
 }
 
 void
 FrameLayerBuilder::AddPaintedDisplayItem(PaintedLayerData* aLayerData,
                                         nsDisplayItem* aItem,
@@ -4061,17 +4163,18 @@ FrameLayerBuilder::AddPaintedDisplayItem
 #endif
         invalid.ScaleRoundOut(paintedData->mXScale, paintedData->mYScale);
 
         if (hasClip) {
           invalid.And(invalid, intClip);
         }
 
         InvalidatePostTransformRegion(layer, invalid,
-                                      GetTranslationForPaintedLayer(layer));
+                                      GetTranslationForPaintedLayer(layer),
+                                      paintedData);
       }
     }
     ClippedDisplayItem* cdi =
       entry->mItems.AppendElement(ClippedDisplayItem(aItem,
                                                      mContainerLayerGeneration));
     cdi->mInactiveLayerManager = tempManager;
   }
 }
@@ -4267,16 +4370,49 @@ ContainerState::SetupScrollingMetadata(N
 
     scrollFrame->ComputeFrameMetrics(aEntry->mLayer, mContainerReferenceFrame,
                                      mParameters, &metricsArray);
   }
   // Watch out for FrameMetrics copies in profiles
   aEntry->mLayer->SetFrameMetrics(metricsArray);
 }
 
+static void
+InvalidateVisibleBoundsChangesForScrolledLayer(PaintedLayer* aLayer)
+{
+  PaintedDisplayItemLayerUserData* data =
+    static_cast<PaintedDisplayItemLayerUserData*>(aLayer->GetUserData(&gPaintedDisplayItemLayerUserData));
+
+  if (data->mIgnoreInvalidationsOutsideRect) {
+    // We haven't invalidated anything outside *data->mIgnoreInvalidationsOutsideRect
+    // during DLBI. Now is the right time to do that, because at this point aLayer
+    // knows its new visible region.
+    // We use the visible regions' bounds here (as opposed to the true region)
+    // in order to limit rgn's complexity. The only possible disadvantage of
+    // this is that it might cause us to unnecessarily recomposite parts of the
+    // window that are in the visible region's bounds but not in the visible
+    // region itself, but that is acceptable for scrolled layers.
+    nsIntRegion rgn;
+    rgn.Or(data->mOldVisibleBounds, aLayer->GetVisibleRegion().GetBounds());
+    rgn.Sub(rgn, *data->mIgnoreInvalidationsOutsideRect);
+    if (!rgn.IsEmpty()) {
+      aLayer->InvalidateRegion(rgn);
+#ifdef MOZ_DUMP_PAINTING
+      if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
+        printf_stderr("Invalidating changes of the visible region bounds of the scrolled contents\n");
+        nsAutoCString str;
+        AppendToString(str, rgn);
+        printf_stderr("Invalidating layer %p: %s\n", aLayer, str.get());
+      }
+#endif
+    }
+    data->mIgnoreInvalidationsOutsideRect = Nothing();
+  }
+}
+
 void
 ContainerState::PostprocessRetainedLayers(nsIntRegion* aOpaqueRegionForContainer)
 {
   nsAutoTArray<OpaqueRegionEntry,4> opaqueRegions;
   bool hideAll = false;
   int32_t opaqueRegionForContainer = -1;
 
   for (int32_t i = mNewChildLayers.Length() - 1; i >= 0; --i) {
@@ -4305,16 +4441,21 @@ ContainerState::PostprocessRetainedLayer
       } else if (data) {
         e->mVisibleRegion.Sub(e->mVisibleRegion, data->mOpaqueRegion);
       }
     }
 
     SetOuterVisibleRegionForLayer(e->mLayer, e->mVisibleRegion,
       e->mLayerContentsVisibleRect.width >= 0 ? &e->mLayerContentsVisibleRect : nullptr);
 
+    PaintedLayer* p = e->mLayer->AsPaintedLayer();
+    if (p) {
+      InvalidateVisibleBoundsChangesForScrolledLayer(p);
+    }
+
     if (!e->mOpaqueRegion.IsEmpty()) {
       const nsIFrame* animatedGeometryRootToCover = animatedGeometryRootForOpaqueness;
       if (e->mOpaqueForAnimatedGeometryRootParent &&
           nsLayoutUtils::GetAnimatedGeometryRootForFrame(mBuilder, e->mAnimatedGeometryRoot->GetParent(),
                                                          mContainerAnimatedGeometryRoot)
             == mContainerAnimatedGeometryRoot) {
         animatedGeometryRootToCover = mContainerAnimatedGeometryRoot;
         data = FindOpaqueRegionEntry(opaqueRegions,
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -571,11 +571,26 @@ gfx::MarginTyped<dst> operator*(const gf
 template<class src, class dst>
 gfx::MarginTyped<dst> operator/(const gfx::MarginTyped<src>& aMargin, const gfx::ScaleFactors2D<dst, src>& aScale) {
   return gfx::MarginTyped<dst>(aMargin.top / aScale.yScale,
                                aMargin.right / aScale.xScale,
                                aMargin.bottom / aScale.yScale,
                                aMargin.left / aScale.xScale);
 }
 
+// Calculate the max or min or the ratios of the widths and heights of two
+// sizes, returning a scale factor in the correct units.
+
+template<class src, class dst>
+gfx::ScaleFactor<src, dst> MaxScaleRatio(const gfx::SizeTyped<dst>& aDestSize, const gfx::SizeTyped<src>& aSrcSize) {
+  return gfx::ScaleFactor<src, dst>(std::max(aDestSize.width / aSrcSize.width,
+                                             aDestSize.height / aSrcSize.height));
+}
+
+template<class src, class dst>
+gfx::ScaleFactor<src, dst> MinScaleRatio(const gfx::SizeTyped<dst>& aDestSize, const gfx::SizeTyped<src>& aSrcSize) {
+  return gfx::ScaleFactor<src, dst>(std::min(aDestSize.width / aSrcSize.width,
+                                             aDestSize.height / aSrcSize.height));
+}
+
 }
 
 #endif
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -1264,16 +1264,34 @@ nsDisplayListBuilder::ExitSVGEffectsCont
 void
 nsDisplayListBuilder::AppendNewScrollInfoItemForHoisting(nsDisplayScrollInfoLayer* aScrollInfoItem)
 {
   MOZ_ASSERT(ShouldBuildScrollInfoItemsForHoisting());
   MOZ_ASSERT(mScrollInfoItemsForHoisting);
   mScrollInfoItemsForHoisting->AppendNewToTop(aScrollInfoItem);
 }
 
+void
+nsDisplayListBuilder::StoreDirtyRectForScrolledContents(const nsIFrame* aScrollableFrame,
+                                                        const nsRect& aDirty)
+{
+  mDirtyRectForScrolledContents.Put(const_cast<nsIFrame*>(aScrollableFrame),
+                                    aDirty + ToReferenceFrame(aScrollableFrame));
+}
+
+nsRect
+nsDisplayListBuilder::GetDirtyRectForScrolledContents(const nsIFrame* aScrollableFrame) const
+{
+  nsRect result;
+  if (!mDirtyRectForScrolledContents.Get(const_cast<nsIFrame*>(aScrollableFrame), &result)) {
+    return nsRect();
+  }
+  return result;
+}
+
 void nsDisplayListSet::MoveTo(const nsDisplayListSet& aDestination) const
 {
   aDestination.BorderBackground()->AppendToTop(BorderBackground());
   aDestination.BlockBorderBackgrounds()->AppendToTop(BlockBorderBackgrounds());
   aDestination.Floats()->AppendToTop(Floats());
   aDestination.Content()->AppendToTop(Content());
   aDestination.PositionedDescendants()->AppendToTop(PositionedDescendants());
   aDestination.Outlines()->AppendToTop(Outlines());
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -849,16 +849,32 @@ public:
   void EnterSVGEffectsContents(nsDisplayList* aHoistedItemsStorage);
   void ExitSVGEffectsContents();
 
   bool ShouldBuildScrollInfoItemsForHoisting() const
   { return mSVGEffectsBuildingDepth > 0; }
 
   void AppendNewScrollInfoItemForHoisting(nsDisplayScrollInfoLayer* aScrollInfoItem);
 
+  /**
+   * Store the dirty rect of the scrolled contents of aScrollableFrame. This
+   * is a bound for the extents of the new visible region of the scrolled
+   * layer.
+   * @param aScrollableFrame the scrollable frame
+   * @param aDirty           the dirty rect, relative to aScrollableFrame
+   */
+  void StoreDirtyRectForScrolledContents(const nsIFrame* aScrollableFrame, const nsRect& aDirty);
+
+  /**
+   * Retrieve the stored dirty rect for the scrolled contents of aScrollableFrame.
+   * @param  aScrollableFrame the scroll frame
+   * @return                  the dirty rect, relative to aScrollableFrame's *reference frame*
+   */
+  nsRect GetDirtyRectForScrolledContents(const nsIFrame* aScrollableFrame) const;
+
 private:
   void MarkOutOfFlowFrameForDisplay(nsIFrame* aDirtyFrame, nsIFrame* aFrame,
                                     const nsRect& aDirtyRect);
 
   struct PresShellState {
     nsIPresShell* mPresShell;
     nsIFrame*     mCaretFrame;
     nsRect        mCaretRect;
@@ -925,16 +941,20 @@ private:
   // Cache for storing animated geometry roots for arbitrary frames
   nsDataHashtable<nsGenericHashKey<AnimatedGeometryRootLookup>, nsIFrame*>
                                  mAnimatedGeometryRootCache;
   // will-change budget tracker
   nsDataHashtable<nsPtrHashKey<nsPresContext>, DocumentWillChangeBudget>
                                  mWillChangeBudget;
   // Assert that we never check the budget before its fully calculated.
   mutable mozilla::DebugOnly<bool> mWillChangeBudgetCalculated;
+
+  // rects are relative to the frame's reference frame
+  nsDataHashtable<nsPtrHashKey<nsIFrame>, nsRect> mDirtyRectForScrolledContents;
+
   // Relative to mCurrentFrame.
   nsRect                         mDirtyRect;
   nsRegion                       mWindowExcludeGlassRegion;
   nsRegion                       mWindowOpaqueRegion;
   nsIntRegion                    mWindowDraggingRegion;
   // The display item for the Windows window glass background, if any
   nsDisplayItem*                 mGlassDisplayItem;
   // A temporary list that we append scroll info items to while building
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2967,16 +2967,17 @@ ScrollFrameHelper::BuildDisplayList(nsDi
       // the corners where we have a scrollbar.
       if (mClipAllDescendants) {
         clipState.ClipContentDescendants(clip, haveRadii ? radii : nullptr);
       } else {
         clipState.ClipContainingBlockDescendants(clip, haveRadii ? radii : nullptr);
       }
     }
 
+    aBuilder->StoreDirtyRectForScrolledContents(mOuter, dirtyRect);
     mOuter->BuildDisplayListForChild(aBuilder, mScrolledFrame, dirtyRect, scrolledContent);
 
     if (idSetter.ShouldForceLayerForScrollParent() &&
         !gfxPrefs::LayoutUseContainersForRootFrames())
     {
       // Note that forcing layerization of scroll parents follows the scroll
       // handoff chain which is subject to the out-of-flow-frames caveat noted
       // above (where the idSetter variable is created).
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -150,16 +150,17 @@ protected:
   }
 
   uint64_t mLastSequenceNumber;
   nsPluginFrame* mFrame;
 };
 
 nsPluginFrame::nsPluginFrame(nsStyleContext* aContext)
   : nsPluginFrameSuper(aContext)
+  , mInstanceOwner(nullptr)
   , mReflowCallbackPosted(false)
 {
   MOZ_LOG(GetObjectFrameLog(), PR_LOG_DEBUG,
          ("Created new nsPluginFrame %p\n", this));
 }
 
 nsPluginFrame::~nsPluginFrame()
 {
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1153845-1-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>There should be a 200x200 lime square on this page.</title>
+
+<style>
+
+svg {
+  width: 200px;
+  height: 200px;
+  background-color: lime;
+}
+
+</style>
+
+<svg></svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1153845-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<title>There should be a 200x200 lime square on this page.</title>
+
+<style>
+
+svg {
+  width: 200px;
+  height: 200px;
+  background-color: lime;
+  filter: brightness(100%);
+}
+
+</style>
+
+<svg></svg>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1918,9 +1918,10 @@ skip-if(!asyncPanZoom) fuzzy-if(B2G,22,1
 skip-if(!asyncPanZoom) fuzzy-if(B2G,62,175) == 1133905-2-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(!asyncPanZoom) fuzzy-if(B2G,23,174) == 1133905-3-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(!asyncPanZoom) == 1133905-4-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(!asyncPanZoom) fuzzy-if(B2G,102,545) == 1133905-5-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(!asyncPanZoom) fuzzy-if(B2G,101,887) == 1133905-6-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(B2G||Mulet) == 1150021-1.xul 1150021-1-ref.xul
 == 1151145-1.html 1151145-1-ref.html
 == 1151306-1.html 1151306-1-ref.html
+== 1153845-1.html 1153845-1-ref.html
 == 1156129-1.html 1156129-1-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/invalidation/fast-scrolling.html
@@ -0,0 +1,109 @@
+<!DOCTYPE html>
+<html lang="en" class="reftest-wait">
+<meta charset="utf-8">
+<title>Bug 1164227 - Testcase for the invalid region simplification bug</title>
+
+<style>
+
+#scrollbox {
+  width: 400px;
+  height: 500px;
+  overflow: auto;
+  margin: 80px;
+  border: 1px solid black;
+}
+
+.contents {
+  height: 600px;
+  background: white;
+  padding: 20px;
+  position: relative;
+}
+
+.boxes > div {
+  box-sizing: border-box;
+  width: 10px;
+  height: 10px;
+  border: 1px solid black;
+  float: left;
+  margin-left: -2px;
+}
+
+.boxes > div:nth-child(odd) {
+  transform: translateY(500px);
+}
+
+.reftest-no-paint {
+  position: absolute;
+  top: 250px;
+  left: 30px;
+  width: 200px;
+  height: 50px;
+  border: 1px solid red;
+}
+</style>
+
+<div id="scrollbox">
+
+  <div class="contents">
+
+    <div class="boxes">
+      <div style="margin-top: 0px"></div>
+      <div style="margin-top: 1px"></div>
+      <div style="margin-top: 2px"></div>
+      <div style="margin-top: 3px"></div>
+      <div style="margin-top: 4px"></div>
+      <div style="margin-top: 5px"></div>
+      <div style="margin-top: 6px"></div>
+      <div style="margin-top: 7px"></div>
+      <div style="margin-top: 8px"></div>
+      <div style="margin-top: 9px"></div>
+      <div style="margin-top: 10px"></div>
+      <div style="margin-top: 11px"></div>
+      <div style="margin-top: 12px"></div>
+      <div style="margin-top: 13px"></div>
+      <div style="margin-top: 14px"></div>
+      <div style="margin-top: 15px"></div>
+      <div style="margin-top: 16px"></div>
+      <div style="margin-top: 17px"></div>
+      <div style="margin-top: 18px"></div>
+      <div style="margin-top: 19px"></div>
+      <div style="margin-top: 20px"></div>
+      <div style="margin-top: 21px"></div>
+      <div style="margin-top: 22px"></div>
+      <div style="margin-top: 23px"></div>
+      <div style="margin-top: 24px"></div>
+      <div style="margin-top: 25px"></div>
+      <div style="margin-top: 26px"></div>
+      <div style="margin-top: 27px"></div>
+      <div style="margin-top: 28px"></div>
+      <div style="margin-top: 29px"></div>
+      <div style="margin-top: 30px"></div>
+      <div style="margin-top: 31px"></div>
+      <div style="margin-top: 32px"></div>
+      <div style="margin-top: 33px"></div>
+      <div style="margin-top: 34px"></div>
+      <div style="margin-top: 35px"></div>
+      <div style="margin-top: 36px"></div>
+      <div style="margin-top: 37px"></div>
+      <div style="margin-top: 38px"></div>
+      <div style="margin-top: 39px"></div>
+    </div>
+
+    <div class="reftest-no-paint"></div>
+
+  </div>
+
+</div>
+
+<script>
+
+var scrollbox = document.querySelector("#scrollbox");
+scrollbox.scrollTop = 100;
+
+window.addEventListener("MozReftestInvalidate", function (e) {
+  scrollbox.scrollTop = 0;
+  document.documentElement.removeAttribute("class");
+});
+
+</script>
--- a/layout/reftests/invalidation/reftest.list
+++ b/layout/reftests/invalidation/reftest.list
@@ -61,8 +61,9 @@ pref(layout.animated-image-layers.enable
 != layer-splitting-2.html about:blank
 != layer-splitting-3.html about:blank
 != layer-splitting-4.html about:blank
 != layer-splitting-5.html about:blank
 != layer-splitting-6.html about:blank
 != layer-splitting-7.html about:blank
 fuzzy-if(gtk2Widget,2,4) == image-scrolling-zoom-1.html image-scrolling-zoom-1-ref.html
 != image-scrolling-zoom-1-ref.html image-scrolling-zoom-1-notref.html
+!= fast-scrolling.html about:blank
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -9996,30 +9996,32 @@ CSSParserImpl::ParseProperty(nsCSSProper
                                                PropertyEnabledState()) {
             nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
             tokenStream->mPropertyID = *p;
             tokenStream->mShorthandPropertyID = aPropID;
             tokenStream->mTokenStream = propertyValue;
             tokenStream->mBaseURI = mBaseURI;
             tokenStream->mSheetURI = mSheetURI;
             tokenStream->mSheetPrincipal = mSheetPrincipal;
-            tokenStream->mSheet = mSheet;
+            // XXX Should store sheet here (see bug 952338).
+            // tokenStream->mSheet = mSheet;
             tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
             tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
             value.SetTokenStreamValue(tokenStream);
             AppendValue(*p, value);
           }
         } else {
           nsCSSValueTokenStream* tokenStream = new nsCSSValueTokenStream;
           tokenStream->mPropertyID = aPropID;
           tokenStream->mTokenStream = propertyValue;
           tokenStream->mBaseURI = mBaseURI;
           tokenStream->mSheetURI = mSheetURI;
           tokenStream->mSheetPrincipal = mSheetPrincipal;
-          tokenStream->mSheet = mSheet;
+          // XXX Should store sheet here (see bug 952338).
+          // tokenStream->mSheet = mSheet;
           tokenStream->mLineNumber = stateBeforeProperty.mPosition.LineNumber();
           tokenStream->mLineOffset = stateBeforeProperty.mPosition.LineOffset();
           value.SetTokenStreamValue(tokenStream);
           AppendValue(aPropID, value);
         }
       }
       result = true;
     } else {
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -2321,34 +2321,27 @@ css::URLValue::URLValue(nsIURI* aURI, ns
                         nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal)
   : mURI(aURI),
     mString(aString),
     mReferrer(aReferrer),
     mOriginPrincipal(aOriginPrincipal),
     mURIResolved(true)
 {
   MOZ_ASSERT(aOriginPrincipal, "Must have an origin principal");
-  mString->AddRef();
 }
 
 css::URLValue::URLValue(nsStringBuffer* aString, nsIURI* aBaseURI,
                         nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal)
   : mURI(aBaseURI),
     mString(aString),
     mReferrer(aReferrer),
     mOriginPrincipal(aOriginPrincipal),
     mURIResolved(false)
 {
   MOZ_ASSERT(aOriginPrincipal, "Must have an origin principal");
-  mString->AddRef();
-}
-
-css::URLValue::~URLValue()
-{
-  mString->Release();
 }
 
 bool
 css::URLValue::operator==(const URLValue& aOther) const
 {
   bool eq;
   return NS_strcmp(nsCSSValue::GetBufferValue(mString),
                    nsCSSValue::GetBufferValue(aOther.mString)) == 0 &&
--- a/layout/style/nsCSSValue.h
+++ b/layout/style/nsCSSValue.h
@@ -82,17 +82,17 @@ struct URLValue {
   // aString and aBaseURI.
   URLValue(nsStringBuffer* aString, nsIURI* aBaseURI, nsIURI* aReferrer,
            nsIPrincipal* aOriginPrincipal);
   // Construct with the actual URI.
   URLValue(nsIURI* aURI, nsStringBuffer* aString, nsIURI* aReferrer,
            nsIPrincipal* aOriginPrincipal);
 
 protected:
-  ~URLValue();
+  ~URLValue() {};
 
 public:
   bool operator==(const URLValue& aOther) const;
 
   // URIEquals only compares URIs and principals (unlike operator==, which
   // also compares the original strings).  URIEquals also assumes that the
   // mURI member of both URL objects is non-null.  Do NOT call this method
   // unless you're sure this is the case.
@@ -103,18 +103,17 @@ public:
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
 private:
   // If mURIResolved is false, mURI stores the base URI.
   // If mURIResolved is true, mURI stores the URI we resolve to; this may be
   // null if the URI is invalid.
   mutable nsCOMPtr<nsIURI> mURI;
 public:
-  nsStringBuffer* mString; // Could use nsRefPtr, but it'd add useless
-                           // null-checks; this is never null.
+  nsRefPtr<nsStringBuffer> mString;
   nsCOMPtr<nsIURI> mReferrer;
   nsCOMPtr<nsIPrincipal> mOriginPrincipal;
 
   NS_INLINE_DECL_REFCOUNTING(URLValue)
 
 private:
   mutable bool mURIResolved;
 
@@ -745,34 +744,34 @@ private:
                            Serialization aValueSerialization) const;
 protected:
   nsCSSUnit mUnit;
   union {
     int32_t    mInt;
     float      mFloat;
     // Note: the capacity of the buffer may exceed the length of the string.
     // If we're of a string type, mString is not null.
-    nsStringBuffer* mString;
+    nsStringBuffer* MOZ_OWNING_REF mString;
     nscolor    mColor;
-    Array*     mArray;
-    mozilla::css::URLValue* mURL;
-    mozilla::css::ImageValue* mImage;
-    mozilla::css::GridTemplateAreasValue* mGridTemplateAreas;
-    nsCSSValueGradient* mGradient;
-    nsCSSValueTokenStream* mTokenStream;
-    nsCSSValuePair_heap* mPair;
-    nsCSSRect_heap* mRect;
-    nsCSSValueTriplet_heap* mTriplet;
-    nsCSSValueList_heap* mList;
+    Array* MOZ_OWNING_REF mArray;
+    mozilla::css::URLValue* MOZ_OWNING_REF mURL;
+    mozilla::css::ImageValue* MOZ_OWNING_REF mImage;
+    mozilla::css::GridTemplateAreasValue* MOZ_OWNING_REF mGridTemplateAreas;
+    nsCSSValueGradient* MOZ_OWNING_REF mGradient;
+    nsCSSValueTokenStream* MOZ_OWNING_REF mTokenStream;
+    nsCSSValuePair_heap* MOZ_OWNING_REF mPair;
+    nsCSSRect_heap* MOZ_OWNING_REF mRect;
+    nsCSSValueTriplet_heap* MOZ_OWNING_REF mTriplet;
+    nsCSSValueList_heap* MOZ_OWNING_REF mList;
     nsCSSValueList* mListDependent;
-    nsCSSValueSharedList* mSharedList;
-    nsCSSValuePairList_heap* mPairList;
+    nsCSSValueSharedList* MOZ_OWNING_REF mSharedList;
+    nsCSSValuePairList_heap* MOZ_OWNING_REF mPairList;
     nsCSSValuePairList* mPairListDependent;
-    nsCSSValueFloatColor* mFloatColor;
-    mozilla::css::FontFamilyListRefCnt* mFontFamilyList;
+    nsCSSValueFloatColor* MOZ_OWNING_REF mFloatColor;
+    mozilla::css::FontFamilyListRefCnt* MOZ_OWNING_REF mFontFamilyList;
   } mValue;
 };
 
 struct nsCSSValue::Array final {
 
   // return |Array| with reference count of zero
   static Array* Create(size_t aItemCount) {
     return new (aItemCount) Array(aItemCount);
@@ -1533,17 +1532,18 @@ public:
   // When the value of a shorthand property has a variable reference, the
   // same mTokenStream value is used on each of the nsCSSValueTokenStream
   // objects that will be set by parsing the shorthand.
   nsString mTokenStream;
 
   nsCOMPtr<nsIURI> mBaseURI;
   nsCOMPtr<nsIURI> mSheetURI;
   nsCOMPtr<nsIPrincipal> mSheetPrincipal;
-  mozilla::CSSStyleSheet* mSheet;
+  // XXX Should store sheet here (see Bug 952338)
+  // mozilla::CSSStyleSheet* mSheet;
   uint32_t mLineNumber;
   uint32_t mLineOffset;
 
   // This table is used to hold a reference on to any ImageValue that results
   // from re-parsing this token stream at computed value time.  When properties
   // like background-image contain a normal url(), the Declaration's data block
   // will hold a reference to the ImageValue.  When a token stream is used,
   // the Declaration only holds on to this nsCSSValueTokenStream object, and
--- a/layout/svg/nsCSSFilterInstance.cpp
+++ b/layout/svg/nsCSSFilterInstance.cpp
@@ -25,21 +25,21 @@ static float ClampFactor(float aFactor)
     return 0;
   }
 
   return aFactor;
 }
 
 nsCSSFilterInstance::nsCSSFilterInstance(const nsStyleFilter& aFilter,
                                          nscolor aShadowFallbackColor,
-                                         const nsIntRect& aTargetBBoxInFilterSpace,
+                                         const nsIntRect& aTargetBoundsInFilterSpace,
                                          const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform)
   : mFilter(aFilter)
   , mShadowFallbackColor(aShadowFallbackColor)
-  , mTargetBBoxInFilterSpace(aTargetBBoxInFilterSpace)
+  , mTargetBoundsInFilterSpace(aTargetBoundsInFilterSpace)
   , mFrameSpaceInCSSPxToFilterSpaceTransform(aFrameSpaceInCSSPxToFilterSpaceTransform)
 {
 }
 
 nsresult
 nsCSSFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
 {
   FilterPrimitiveDescription descr;
@@ -380,17 +380,17 @@ nsCSSFilterInstance::GetLastResultIndex(
 }
 
 void
 nsCSSFilterInstance::SetBounds(FilterPrimitiveDescription& aDescr,
                                const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs)
 {
   int32_t inputIndex = GetLastResultIndex(aPrimitiveDescrs);
   nsIntRect inputBounds = (inputIndex < 0) ?
-    mTargetBBoxInFilterSpace : aPrimitiveDescrs[inputIndex].PrimitiveSubregion();
+    mTargetBoundsInFilterSpace : aPrimitiveDescrs[inputIndex].PrimitiveSubregion();
 
   nsTArray<nsIntRegion> inputExtents;
   inputExtents.AppendElement(inputBounds);
 
   nsIntRegion outputExtents =
     FilterSupport::PostFilterExtentsForPrimitive(aDescr, inputExtents);
   IntRect outputBounds = outputExtents.GetBounds();
 
--- a/layout/svg/nsCSSFilterInstance.h
+++ b/layout/svg/nsCSSFilterInstance.h
@@ -31,24 +31,24 @@ class nsCSSFilterInstance
 
 public:
   /**
    * @param aFilter The CSS filter from the style system. This class stores
    *   aFilter by reference, so callers should avoid modifying or deleting
    *   aFilter during the lifetime of nsCSSFilterInstance.
    * @param aShadowFallbackColor The color that should be used for
    *   drop-shadow() filters that don't specify a shadow color.
-   * @param aTargetBBoxInFilterSpace The frame of element being filtered, in
-   *   filter space.
+   * @param aTargetBoundsInFilterSpace The pre-filter visual overflow rect of
+   *   the frame being filtered, in filter space.
    * @param aFrameSpaceInCSSPxToFilterSpaceTransform The transformation from
    *   the filtered element's frame space in CSS pixels to filter space.
    */
   nsCSSFilterInstance(const nsStyleFilter& aFilter,
                       nscolor aShadowFallbackColor,
-                      const nsIntRect& aTargetBBoxInFilterSpace,
+                      const nsIntRect& aTargetBoundsInFilterSpace,
                       const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform);
 
   /**
    * Creates at least one new FilterPrimitiveDescription based on the filter
    * from the style system. Appends the new FilterPrimitiveDescription(s) to the
    * aPrimitiveDescrs list.
    */
   nsresult BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs);
@@ -114,20 +114,20 @@ private:
 
   /**
    * The color that should be used for drop-shadow() filters that don't
    * specify a shadow color.
    */
   nscolor mShadowFallbackColor;
 
   /**
-   * The bounding box of the element being filtered, in filter space. Used for
-   * input bounds if this CSS filter is the first in the filter chain.
+   * The pre-filter overflow rect of the frame being filtered, in filter space.
+   * Used for input bounds if this CSS filter is the first in the filter chain.
    */
-  nsIntRect mTargetBBoxInFilterSpace;
+  nsIntRect mTargetBoundsInFilterSpace;
 
   /**
    * The transformation from the filtered element's frame space in CSS pixels to
    * filter space. Used to transform style values to filter space.
    */
   gfxMatrix mFrameSpaceInCSSPxToFilterSpaceTransform;
 };
 
--- a/layout/svg/nsFilterInstance.cpp
+++ b/layout/svg/nsFilterInstance.cpp
@@ -197,41 +197,41 @@ nsFilterInstance::nsFilterInstance(nsIFr
 
   mFilterSpaceToFrameSpaceInCSSPxTransform =
     filterToUserSpace * GetUserSpaceToFrameSpaceInCSSPxTransform();
   // mFilterSpaceToFrameSpaceInCSSPxTransform is always invertible
   mFrameSpaceInCSSPxToFilterSpaceTransform =
     mFilterSpaceToFrameSpaceInCSSPxTransform;
   mFrameSpaceInCSSPxToFilterSpaceTransform.Invert();
 
+  nsIntRect targetBounds;
+  if (aPreFilterVisualOverflowRectOverride) {
+    targetBounds =
+      FrameSpaceToFilterSpace(aPreFilterVisualOverflowRectOverride);
+  } else if (mTargetFrame) {
+    nsRect preFilterVOR = mTargetFrame->GetPreEffectsVisualOverflowRect();
+    targetBounds = FrameSpaceToFilterSpace(&preFilterVOR);
+  }
+  mTargetBounds.UnionRect(mTargetBBoxInFilterSpace, targetBounds);
+
   // Build the filter graph.
   rv = BuildPrimitives(aFilterChain);
   if (NS_FAILED(rv)) {
     return;
   }
 
   if (mPrimitiveDescriptions.IsEmpty()) {
     // Nothing should be rendered.
     return;
   }
 
   // Convert the passed in rects from frame space to filter space:
   mPostFilterDirtyRegion = FrameSpaceToFilterSpace(aPostFilterDirtyRegion);
   mPreFilterDirtyRegion = FrameSpaceToFilterSpace(aPreFilterDirtyRegion);
 
-  nsIntRect targetBounds;
-  if (aPreFilterVisualOverflowRectOverride) {
-    targetBounds =
-      FrameSpaceToFilterSpace(aPreFilterVisualOverflowRectOverride);
-  } else if (mTargetFrame) {
-    nsRect preFilterVOR = mTargetFrame->GetPreEffectsVisualOverflowRect();
-    targetBounds = FrameSpaceToFilterSpace(&preFilterVOR);
-  }
-  mTargetBounds.UnionRect(mTargetBBoxInFilterSpace, targetBounds);
-
   mInitialized = true;
 }
 
 nsresult
 nsFilterInstance::ComputeUserSpaceToFilterSpaceScale()
 {
   gfxMatrix canvasTransform;
   if (mTargetFrame) {
@@ -313,17 +313,17 @@ nsFilterInstance::BuildPrimitivesForFilt
   // Build primitives for a CSS filter.
 
   // If we don't have a frame, use opaque black for shadows with unspecified
   // shadow colors.
   nscolor shadowFallbackColor =
     mTargetFrame ? mTargetFrame->StyleColor()->mColor : NS_RGB(0,0,0);
 
   nsCSSFilterInstance cssFilterInstance(aFilter, shadowFallbackColor,
-                                        mTargetBBoxInFilterSpace,
+                                        mTargetBounds,
                                         mFrameSpaceInCSSPxToFilterSpaceTransform);
   return cssFilterInstance.BuildPrimitives(mPrimitiveDescriptions);
 }
 
 void
 nsFilterInstance::ComputeNeededBoxes()
 {
   if (mPrimitiveDescriptions.IsEmpty())
--- a/media/mtransport/nr_socket_prsock.cpp
+++ b/media/mtransport/nr_socket_prsock.cpp
@@ -115,16 +115,99 @@ extern "C" {
 #include "stun_hint.h"
 }
 #include "nr_socket_prsock.h"
 #include "simpletokenbucket.h"
 
 // Implement the nsISupports ref counting
 namespace mozilla {
 
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+class SingletonThreadHolder final
+{
+private:
+  ~SingletonThreadHolder()
+  {
+    r_log(LOG_GENERIC,LOG_DEBUG,"Deleting SingletonThreadHolder");
+    if (NS_WARN_IF(mThread)) {
+      mThread->Shutdown();
+      mThread = nullptr;
+    }
+  }
+
+  DISALLOW_COPY_ASSIGN(SingletonThreadHolder);
+
+public:
+  // Must be threadsafe for ClearOnShutdown
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SingletonThreadHolder)
+
+  explicit SingletonThreadHolder(const nsCSubstring& aName)
+    : mName(aName)
+  {
+    mParentThread = NS_GetCurrentThread();
+  }
+
+  nsIThread* GetThread() {
+    return mThread;
+  }
+
+  /*
+   * Keep track of how many instances are using a SingletonThreadHolder.
+   * When no one is using it, shut it down
+   */
+  MozExternalRefCountType AddUse()
+  {
+    MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
+    MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
+    nsrefcnt count = ++mUseCount;
+    if (count == 1) {
+      // idle -> in-use
+      nsresult rv = NS_NewThread(getter_AddRefs(mThread));
+      MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv) && mThread,
+                         "Should successfully create mtransport I/O thread");
+      NS_SetThreadName(mThread, mName);
+      r_log(LOG_GENERIC,LOG_DEBUG,"Created wrapped SingletonThread %p",
+            mThread.get());
+    }
+    r_log(LOG_GENERIC,LOG_DEBUG,"AddUse: %lu", (unsigned long) count);
+    return count;
+  }
+
+  MozExternalRefCountType ReleaseUse()
+  {
+    MOZ_ASSERT(mParentThread == NS_GetCurrentThread());
+    nsrefcnt count = --mUseCount;
+    MOZ_ASSERT(int32_t(mUseCount) >= 0, "illegal refcnt");
+    if (count == 0) {
+      // in-use -> idle -- no one forcing it to remain instantiated
+      r_log(LOG_GENERIC,LOG_DEBUG,"Shutting down wrapped SingletonThread %p",
+            mThread.get());
+      mThread->Shutdown();
+      mThread = nullptr;
+      // It'd be nice to use a timer instead...
+    }
+    r_log(LOG_GENERIC,LOG_DEBUG,"ReleaseUse: %lu", (unsigned long) count);
+    return count;
+  }
+
+private:
+  nsCString mName;
+  nsAutoRefCnt mUseCount;
+  nsCOMPtr<nsIThread> mParentThread;
+  nsCOMPtr<nsIThread> mThread;
+};
+
+static StaticRefPtr<SingletonThreadHolder> sThread;
+
+static void ClearSingletonOnShutdown()
+{
+  ClearOnShutdown(&sThread);
+}
+#endif
+
 static TimeStamp nr_socket_short_term_violation_time;
 static TimeStamp nr_socket_long_term_violation_time;
 
 TimeStamp NrSocketBase::short_term_violation_time() {
   return nr_socket_short_term_violation_time;
 }
 
 TimeStamp NrSocketBase::long_term_violation_time() {
@@ -739,46 +822,83 @@ NS_IMETHODIMP NrSocketIpcProxy::CallList
 }
 
 // callback while UDP socket is closed
 NS_IMETHODIMP NrSocketIpcProxy::CallListenerClosed() {
   return socket_->CallListenerClosed();
 }
 
 // NrSocketIpc Implementation
-NrSocketIpc::NrSocketIpc(const nsCOMPtr<nsIEventTarget> &main_thread)
+NrSocketIpc::NrSocketIpc()
     : err_(false),
       state_(NR_INIT),
-      main_thread_(main_thread),
+      io_thread_(GetIOThreadAndAddUse_s()),
       monitor_("NrSocketIpc") {
 }
 
+NrSocketIpc::~NrSocketIpc()
+{
+  // also guarantees socket_child_ is released from the io_thread, and
+  // tells the SingletonThreadHolder we're done with it
+
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+  // close(), but transfer the socket_child_ reference to die as well
+  RUN_ON_THREAD(io_thread_,
+                mozilla::WrapRunnableNM(&NrSocketIpc::release_child_i,
+                                        socket_child_.forget().take(),
+                                        sts_thread_),
+                NS_DISPATCH_NORMAL);
+#endif
+}
+
+/* static */
+nsIThread* NrSocketIpc::GetIOThreadAndAddUse_s()
+{
+  // Always runs on STS thread!
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+  // We need to safely release this on shutdown to avoid leaks
+  if (!sThread) {
+    sThread = new SingletonThreadHolder(NS_LITERAL_CSTRING("mtransport"));
+    NS_DispatchToMainThread(mozilla::WrapRunnableNM(&ClearSingletonOnShutdown));
+  }
+  // Mark that we're using the shared thread and need it to stick around
+  sThread->AddUse();
+  return sThread->GetThread();
+#else
+  static nsCOMPtr<nsIThread> sThread;
+  if (!sThread) {
+    (void) NS_NewNamedThread("mtransport", getter_AddRefs(sThread));
+  }
+  return sThread;
+#endif
+}
+
 // IUDPSocketInternal interfaces
 // callback while error happened in UDP socket operation
 NS_IMETHODIMP NrSocketIpc::CallListenerError(const nsACString &message,
                                              const nsACString &filename,
                                              uint32_t line_number) {
-  ASSERT_ON_THREAD(main_thread_);
+  ASSERT_ON_THREAD(io_thread_);
 
   r_log(LOG_GENERIC, LOG_ERR, "UDP socket error:%s at %s:%d",
         message.BeginReading(), filename.BeginReading(), line_number );
 
   ReentrantMonitorAutoEnter mon(monitor_);
   err_ = true;
   monitor_.NotifyAll();
 
   return NS_OK;
 }
 
 // callback while receiving UDP packet
 NS_IMETHODIMP NrSocketIpc::CallListenerReceivedData(const nsACString &host,
                                                     uint16_t port,
                                                     const uint8_t *data,
                                                     uint32_t data_length) {
-  ASSERT_ON_THREAD(main_thread_);
+  ASSERT_ON_THREAD(io_thread_);
 
   PRNetAddr addr;
   memset(&addr, 0, sizeof(addr));
 
   {
     ReentrantMonitorAutoEnter mon(monitor_);
 
     if (PR_SUCCESS != PR_StringToNetAddr(host.BeginReading(), &addr)) {
@@ -803,17 +923,17 @@ NS_IMETHODIMP NrSocketIpc::CallListenerR
                                       &NrSocketIpc::recv_callback_s,
                                       msg),
                 NS_DISPATCH_NORMAL);
   return NS_OK;
 }
 
 // callback while UDP socket is opened
 NS_IMETHODIMP NrSocketIpc::CallListenerOpened() {
-  ASSERT_ON_THREAD(main_thread_);
+  ASSERT_ON_THREAD(io_thread_);
   ReentrantMonitorAutoEnter mon(monitor_);
 
   uint16_t port;
   if (NS_FAILED(socket_child_->GetLocalPort(&port))) {
     err_ = true;
     MOZ_ASSERT(false, "Failed to get local port");
     return NS_OK;
   }
@@ -857,17 +977,17 @@ NS_IMETHODIMP NrSocketIpc::CallListenerO
 
   mon.NotifyAll();
 
   return NS_OK;
 }
 
 // callback while UDP socket is closed
 NS_IMETHODIMP NrSocketIpc::CallListenerClosed() {
-  ASSERT_ON_THREAD(main_thread_);
+  ASSERT_ON_THREAD(io_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   MOZ_ASSERT(state_ == NR_CONNECTED || state_ == NR_CLOSING);
   state_ = NR_CLOSED;
 
   return NS_OK;
 }
@@ -905,19 +1025,19 @@ int NrSocketIpc::create(nr_transport_add
 
   // wildcard address will be resolved at NrSocketIpc::CallListenerVoid
   if ((r=nr_transport_addr_copy(&my_addr_, addr))) {
     ABORT(r);
   }
 
   state_ = NR_CONNECTING;
 
-  RUN_ON_THREAD(main_thread_,
+  RUN_ON_THREAD(io_thread_,
                 mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this),
-                                      &NrSocketIpc::create_m,
+                                      &NrSocketIpc::create_i,
                                       host, static_cast<uint16_t>(port)),
                 NS_DISPATCH_NORMAL);
 
   // Wait until socket creation complete.
   mon.Wait();
 
   if (err_) {
     ABORT(R_INTERNAL);
@@ -936,49 +1056,45 @@ int NrSocketIpc::sendto(const void *msg,
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   //If send err happened before, simply return the error.
   if (err_) {
     return R_IO_ERROR;
   }
 
-  if (!socket_child_) {
-    return R_EOD;
-  }
-
   if (state_ != NR_CONNECTED) {
     return R_INTERNAL;
   }
 
   int r;
   net::NetAddr addr;
   if ((r=nr_transport_addr_to_netaddr(to, &addr))) {
     return r;
   }
 
   nsAutoPtr<DataBuffer> buf(new DataBuffer(static_cast<const uint8_t*>(msg), len));
 
-  RUN_ON_THREAD(main_thread_,
+  RUN_ON_THREAD(io_thread_,
                 mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this),
-                                      &NrSocketIpc::sendto_m,
+                                      &NrSocketIpc::sendto_i,
                                       addr, buf),
                 NS_DISPATCH_NORMAL);
   return 0;
 }
 
 void NrSocketIpc::close() {
   ASSERT_ON_THREAD(sts_thread_);
 
   ReentrantMonitorAutoEnter mon(monitor_);
   state_ = NR_CLOSING;
 
-  RUN_ON_THREAD(main_thread_,
+  RUN_ON_THREAD(io_thread_,
                 mozilla::WrapRunnable(nsRefPtr<NrSocketIpc>(this),
-                                      &NrSocketIpc::close_m),
+                                      &NrSocketIpc::close_i),
                 NS_DISPATCH_NORMAL);
 
   //remove all enqueued messages
   std::queue<RefPtr<nr_udp_message> > empty;
   std::swap(received_msgs_, empty);
 }
 
 int NrSocketIpc::recvfrom(void *buf, size_t maxlen, size_t *len, int flags,
@@ -1047,75 +1163,106 @@ int NrSocketIpc::write(const void *msg, 
   return R_INTERNAL;
 }
 
 int NrSocketIpc::read(void* buf, size_t maxlen, size_t *len) {
   MOZ_ASSERT(false);
   return R_INTERNAL;
 }
 
-// Main thread executors
-void NrSocketIpc::create_m(const nsACString &host, const uint16_t port) {
-  ASSERT_ON_THREAD(main_thread_);
-
-  ReentrantMonitorAutoEnter mon(monitor_);
+// IO thread executors
+void NrSocketIpc::create_i(const nsACString &host, const uint16_t port) {
+  ASSERT_ON_THREAD(io_thread_);
 
   nsresult rv;
   nsCOMPtr<nsIUDPSocketChild> socketChild = do_CreateInstance("@mozilla.org/udp-socket-child;1", &rv);
   if (NS_FAILED(rv)) {
     err_ = true;
     MOZ_ASSERT(false, "Failed to create UDPSocketChild");
-    mon.NotifyAll();
     return;
   }
 
-  socket_child_ = new nsMainThreadPtrHolder<nsIUDPSocketChild>(socketChild);
-  socket_child_->SetFilterName(nsCString("stun"));
+  // This can spin the event loop; don't do that with the monitor held
+  socketChild->SetBackgroundSpinsEvents();
+
+  ReentrantMonitorAutoEnter mon(monitor_);
+  if (!socket_child_) {
+    socket_child_ = socketChild;
+    socket_child_->SetFilterName(nsCString("stun"));
+  } else {
+    socketChild = nullptr;
+  }
 
   nsRefPtr<NrSocketIpcProxy> proxy(new NrSocketIpcProxy);
   rv = proxy->Init(this);
   if (NS_FAILED(rv)) {
     err_ = true;
     mon.NotifyAll();
     return;
   }
 
+  // XXX bug 1126232 - don't use null Principal!
   if (NS_FAILED(socket_child_->Bind(proxy, nullptr, host, port,
                                     /* reuse = */ false,
                                     /* loopback = */ false))) {
     err_ = true;
     MOZ_ASSERT(false, "Failed to create UDP socket");
     mon.NotifyAll();
     return;
   }
 }
 
-void NrSocketIpc::sendto_m(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf) {
-  ASSERT_ON_THREAD(main_thread_);
+void NrSocketIpc::sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf) {
+  ASSERT_ON_THREAD(io_thread_);
 
-  MOZ_ASSERT(socket_child_);
+  if (!socket_child_) {
+    MOZ_ASSERT(false);
+    err_ = true;
+    return;
+  }
 
   ReentrantMonitorAutoEnter mon(monitor_);
 
   if (NS_FAILED(socket_child_->SendWithAddress(&addr,
                                                buf->data(),
                                                buf->len()))) {
     err_ = true;
   }
 }
 
-void NrSocketIpc::close_m() {
-  ASSERT_ON_THREAD(main_thread_);
+void NrSocketIpc::close_i() {
+  ASSERT_ON_THREAD(io_thread_);
 
   if (socket_child_) {
     socket_child_->Close();
     socket_child_ = nullptr;
   }
 }
 
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+// close(), but transfer the socket_child_ reference to die as well
+// static
+void NrSocketIpc::release_child_i(nsIUDPSocketChild* aChild,
+                                  nsCOMPtr<nsIEventTarget> sts_thread) {
+  nsRefPtr<nsIUDPSocketChild> socket_child_ref =
+    already_AddRefed<nsIUDPSocketChild>(aChild);
+  if (socket_child_ref) {
+    socket_child_ref->Close();
+  }
+  // Tell SingletonThreadHolder we're done with it
+  RUN_ON_THREAD(sts_thread,
+                mozilla::WrapRunnableNM(&NrSocketIpc::release_use_s),
+                NS_DISPATCH_NORMAL);
+}
+
+void NrSocketIpc::release_use_s() {
+  sThread->ReleaseUse();
+}
+#endif
+
 void NrSocketIpc::recv_callback_s(RefPtr<nr_udp_message> msg) {
   ASSERT_ON_THREAD(sts_thread_);
 
   {
     ReentrantMonitorAutoEnter mon(monitor_);
     if (state_ != NR_CONNECTED) {
       return;
     }
@@ -1164,19 +1311,17 @@ static nr_socket_vtbl nr_socket_local_vt
 
 int nr_socket_local_create(nr_transport_addr *addr, nr_socket **sockp) {
   RefPtr<NrSocketBase> sock;
 
   // create IPC bridge for content process
   if (XRE_GetProcessType() == GeckoProcessType_Default) {
     sock = new NrSocket();
   } else {
-    nsCOMPtr<nsIThread> main_thread;
-    NS_GetMainThread(getter_AddRefs(main_thread));
-    sock = new NrSocketIpc(main_thread.get());
+    sock = new NrSocketIpc();
   }
 
   int r, _status;
 
   r = sock->create(addr);
   if (r)
     ABORT(r);
 
@@ -1282,9 +1427,8 @@ int NR_async_cancel(NR_SOCKET sock,int h
   NrSocketBase *s = static_cast<NrSocketBase *>(sock);
 
   return s->cancel(how);
 }
 
 nr_socket_vtbl* NrSocketBase::vtbl() {
   return &nr_socket_local_vtbl;
 }
-
--- a/media/mtransport/nr_socket_prsock.h
+++ b/media/mtransport/nr_socket_prsock.h
@@ -54,22 +54,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsASocketHandler.h"
 #include "nsISocketTransportService.h"
 #include "nsXPCOM.h"
 #include "nsIEventTarget.h"
 #include "nsIUDPSocketChild.h"
 #include "nsProxyRelease.h"
+#include "nsThreadUtils.h"
 
 #include "databuffer.h"
 #include "m_cpp_utils.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/ClearOnShutdown.h"
 
 // Stub declaration for nICEr type
 typedef struct nr_socket_vtbl_ nr_socket_vtbl;
 
 namespace mozilla {
 
 namespace net {
   union NetAddr;
@@ -206,50 +208,56 @@ public:
                                   uint32_t line_number);
   NS_IMETHODIMP CallListenerReceivedData(const nsACString &host,
                                          uint16_t port,
                                          const uint8_t *data,
                                          uint32_t data_length);
   NS_IMETHODIMP CallListenerOpened();
   NS_IMETHODIMP CallListenerClosed();
 
-  explicit NrSocketIpc(const nsCOMPtr<nsIEventTarget> &main_thread);
+  NrSocketIpc();
 
   // Implementations of the NrSocketBase APIs
   virtual int create(nr_transport_addr *addr) override;
   virtual int sendto(const void *msg, size_t len,
                      int flags, nr_transport_addr *to) override;
   virtual int recvfrom(void * buf, size_t maxlen,
                        size_t *len, int flags,
                        nr_transport_addr *from) override;
   virtual int getaddr(nr_transport_addr *addrp) override;
   virtual void close() override;
   virtual int connect(nr_transport_addr *addr) override;
   virtual int write(const void *msg, size_t len, size_t *written) override;
   virtual int read(void* buf, size_t maxlen, size_t *len) override;
 
 private:
-  virtual ~NrSocketIpc() {};
+  virtual ~NrSocketIpc();
 
   DISALLOW_COPY_ASSIGN(NrSocketIpc);
 
-  // Main thread executors of the NrSocketBase APIs
-  void create_m(const nsACString &host, const uint16_t port);
-  void sendto_m(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf);
-  void close_m();
+  static nsIThread* GetIOThreadAndAddUse_s();
+
+  // Main or private thread executors of the NrSocketBase APIs
+  void create_i(const nsACString &host, const uint16_t port);
+  void sendto_i(const net::NetAddr &addr, nsAutoPtr<DataBuffer> buf);
+  void close_i();
+#if defined(MOZILLA_INTERNAL_API) && !defined(MOZILLA_XPCOMRT_API)
+  static void release_child_i(nsIUDPSocketChild* aChild, nsCOMPtr<nsIEventTarget> ststhread);
+  static void release_use_s();
+#endif
   // STS thread executor
   void recv_callback_s(RefPtr<nr_udp_message> msg);
 
   bool err_;
   NrSocketIpcState state_;
   std::queue<RefPtr<nr_udp_message> > received_msgs_;
 
-  nsMainThreadPtrHandle<nsIUDPSocketChild> socket_child_;
+  nsRefPtr<nsIUDPSocketChild> socket_child_; // only accessed from the io_thread
   nsCOMPtr<nsIEventTarget> sts_thread_;
-  const nsCOMPtr<nsIEventTarget> main_thread_;
+  const nsCOMPtr<nsIEventTarget> io_thread_;
   ReentrantMonitor monitor_;
 };
 
 // The socket child holds onto one of these, which just passes callbacks
 // through and makes sure the ref to the NrSocketIpc is released on STS.
 class NrSocketIpcProxy : public nsIUDPSocketInternal {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
--- a/media/mtransport/stun_udp_socket_filter.cpp
+++ b/media/mtransport/stun_udp_socket_filter.cpp
@@ -81,16 +81,17 @@ class PendingSTUNRequest {
 };
 
 class STUNUDPSocketFilter : public nsIUDPSocketFilter {
  public:
   STUNUDPSocketFilter()
     : white_list_(),
       pending_requests_() {}
 
+  // Allocated/freed and used on the PBackground IPC thread
   NS_DECL_ISUPPORTS
   NS_DECL_NSIUDPSOCKETFILTER
 
  private:
   virtual ~STUNUDPSocketFilter() {}
 
   bool filter_incoming_packet(const mozilla::net::NetAddr *remote_addr,
                               const uint8_t *data,
--- a/media/mtransport/stun_udp_socket_filter.h
+++ b/media/mtransport/stun_udp_socket_filter.h
@@ -7,16 +7,18 @@
 #include "nsIUDPSocketFilter.h"
 
 #define NS_STUN_UDP_SOCKET_FILTER_HANDLER_CONTRACTID NS_NETWORK_UDP_SOCKET_FILTER_HANDLER_PREFIX "stun"
 #define NS_STUN_UDP_SOCKET_FILTER_HANDLER_CID { 0x3e43ee93, 0x829e, 0x4ea6, \
       { 0xa3, 0x4e, 0x62, 0xd9, 0xe4, 0xc9, 0xf9, 0x93 } };
 
 class nsStunUDPSocketFilterHandler : public nsIUDPSocketFilterHandler {
 public:
-  NS_DECL_ISUPPORTS
+  // Threadsafe because we create off-main-thread, but destroy on MainThread
+  // via FreeFactoryEntries()
+  NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIUDPSOCKETFILTERHANDLER
 private:
   virtual ~nsStunUDPSocketFilterHandler() {}
 };
 
 
 #endif // stun_udp_socket_filter_h__
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp
@@ -299,17 +299,17 @@ nsresult PeerConnectionMedia::Init(const
     return NS_ERROR_FAILURE;
   }
 #endif // defined(MOZILLA_XPCOMRT_API)
 
   // TODO(ekr@rtfm.com): need some way to set not offerer later
   // Looks like a bug in the NrIceCtx API.
   mIceCtx = NrIceCtx::Create("PC:" + mParentName,
                              true, // Offerer
-                             true, // Trickle
+                             true, // Explicitly set priorities
                              mAllowIceLoopback);
   if(!mIceCtx) {
     CSFLogError(logTag, "%s: Failed to create Ice Context", __FUNCTION__);
     return NS_ERROR_FAILURE;
   }
 
   if (NS_FAILED(rv = mIceCtx->SetStunServers(stun_servers))) {
     CSFLogError(logTag, "%s: Failed to set stun servers", __FUNCTION__);
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -904,8 +904,15 @@ pref("touchcaret.expiration.time", 0);
 // Touch caret stays visible under a wider range of conditions
 // than the default b2g. We can display the caret in empty editables
 // for example, and do not auto-hide until loss of focus.
 pref("touchcaret.extendedvisibility", true);
 
 // The TouchCaret and the SelectionCarets will indicate when the
 // TextSelection actionbar is to be openned or closed.
 pref("caret.manages-android-actionbar", true);
+
+// Disable sending console to logcat on release builds.
+#ifdef RELEASE_BUILD
+pref("consoleservice.logcat", false);
+#else
+pref("consoleservice.logcat", true);
+#endif
--- a/netwerk/base/nsIOService.h
+++ b/netwerk/base/nsIOService.h
@@ -73,16 +73,17 @@ public:
 
     // Called by channels before a redirect happens. This notifies the global
     // redirect observers.
     nsresult AsyncOnChannelRedirect(nsIChannel* oldChan, nsIChannel* newChan,
                                     uint32_t flags,
                                     nsAsyncRedirectVerifyHelper *helper);
 
     bool IsOffline() { return mOffline; }
+    bool IsShutdown() { return mShutdown; }
     bool IsLinkUp();
 
     // Should only be called from NeckoChild. Use SetAppOffline instead.
     void SetAppOfflineInternal(uint32_t appId, int32_t status);
 
 private:
     // These shouldn't be called directly:
     // - construct using GetInstance
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -1212,16 +1212,19 @@ nsSocketTransport::InitiateSocket()
             crashOnNonLocalConnections = 0;
         }
     }
 
     nsresult rv;
     bool isLocal;
     IsLocal(&isLocal);
 
+    if (gIOService->IsShutdown()) {
+        return NS_ERROR_ABORT;
+    }
     if (gIOService->IsOffline()) {
         if (!isLocal)
             return NS_ERROR_OFFLINE;
     } else if (!isLocal) {
 
 #ifdef DEBUG
         // all IP networking has to be done from the parent
         if (NS_SUCCEEDED(mCondition) &&
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -25,16 +25,17 @@
 #include "mozilla/Telemetry.h"
 #include "nsThreadUtils.h"
 #include "nsIFile.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
 PRLogModuleInfo *gSocketTransportLog = nullptr;
+PRLogModuleInfo *gUDPSocketLog = nullptr;
 
 nsSocketTransportService *gSocketTransportService = nullptr;
 PRThread                 *gSocketThread           = nullptr;
 
 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
 #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
 #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
 #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
@@ -106,16 +107,17 @@ nsSocketTransportService::nsSocketTransp
     , mKeepaliveEnabledPref(false)
     , mServeMultipleEventsPerPollIter(true)
     , mServingPendingQueue(false)
     , mMaxTimePerPollIter(100)
     , mTelemetryEnabledPref(false)
     , mProbedMaxCount(false)
 {
     gSocketTransportLog = PR_NewLogModule("nsSocketTransport");
+    gUDPSocketLog = PR_NewLogModule("UDPSocket");
 
     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
     PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
     mActiveList = (SocketContext *)
         moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
     mIdleList = (SocketContext *)
         moz_xmalloc(sizeof(SocketContext) * mIdleListSize);
--- a/netwerk/base/nsSocketTransportService2.h
+++ b/netwerk/base/nsSocketTransportService2.h
@@ -27,16 +27,23 @@ struct PRPollDesc;
 
 //
 // set NSPR_LOG_MODULES=nsSocketTransport:5
 //
 extern PRLogModuleInfo *gSocketTransportLog;
 #define SOCKET_LOG(args)     MOZ_LOG(gSocketTransportLog, PR_LOG_DEBUG, args)
 #define SOCKET_LOG_ENABLED() PR_LOG_TEST(gSocketTransportLog, PR_LOG_DEBUG)
 
+//
+// set NSPR_LOG_MODULES=UDPSocket:5
+//
+extern PRLogModuleInfo *gUDPSocketLog;
+#define UDPSOCKET_LOG(args)     PR_LOG(gUDPSocketLog, PR_LOG_DEBUG, args)
+#define UDPSOCKET_LOG_ENABLED() PR_LOG_TEST(gUDPSocketLog, PR_LOG_DEBUG)
+
 //-----------------------------------------------------------------------------
 
 #define NS_SOCKET_POLL_TIMEOUT PR_INTERVAL_NO_TIMEOUT
 
 //-----------------------------------------------------------------------------
 
 namespace mozilla {
 namespace net {
--- a/netwerk/base/nsUDPSocket.cpp
+++ b/netwerk/base/nsUDPSocket.cpp
@@ -482,17 +482,17 @@ nsUDPSocket::AddOutputBytes(uint64_t aBy
 {
   mByteWriteCount += aBytes;
   SaveNetworkStats(false);
 }
 
 void
 nsUDPSocket::OnMsgClose()
 {
-  SOCKET_LOG(("nsUDPSocket::OnMsgClose [this=%p]\n", this));
+  UDPSOCKET_LOG(("nsUDPSocket::OnMsgClose [this=%p]\n", this));
 
   if (NS_FAILED(mCondition))
     return;
 
   // tear down socket.  this signals the STS to detach our socket handler.
   mCondition = NS_BINDING_ABORTED;
 
   // if we are attached, then socket transport service will call our
@@ -500,17 +500,17 @@ nsUDPSocket::OnMsgClose()
   // (and thus close the socket) manually.
   if (!mAttached)
     OnSocketDetached(mFD);
 }
 
 void
 nsUDPSocket::OnMsgAttach()
 {
-  SOCKET_LOG(("nsUDPSocket::OnMsgAttach [this=%p]\n", this));
+  UDPSOCKET_LOG(("nsUDPSocket::OnMsgAttach [this=%p]\n", this));
 
   if (NS_FAILED(mCondition))
     return;
 
   mCondition = TryAttach();
 
   // if we hit an error while trying to attach then bail...
   if (NS_FAILED(mCondition))
@@ -573,17 +573,17 @@ namespace {
 class UDPMessageProxy final : public nsIUDPMessage
 {
 public:
   UDPMessageProxy(NetAddr* aAddr,
                   nsIOutputStream* aOutputStream,
                   FallibleTArray<uint8_t>& aData)
   : mOutputStream(aOutputStream)
   {
-    memcpy(&mAddr, aAddr, sizeof(NetAddr));
+    memcpy(&mAddr, aAddr, sizeof(mAddr));
     aData.SwapElements(mData);
   }
 
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIUDPMESSAGE
 
 private:
   ~UDPMessageProxy() {}
@@ -606,17 +606,18 @@ UDPMessageProxy::GetFromAddr(nsINetAddr 
 
   return NS_OK;
 }
 
 /* readonly attribute ACString data; */
 NS_IMETHODIMP
 UDPMessageProxy::GetData(nsACString & aData)
 {
-  return NS_ERROR_NOT_IMPLEMENTED;
+  aData.Assign(reinterpret_cast<const char*>(mData.Elements()), mData.Length());
+  return NS_OK;
 }
 
 /* [noscript, notxpcom, nostdcall] Uint8TArrayRef getDataAsTArray(); */
 FallibleTArray<uint8_t>&
 UDPMessageProxy::GetDataAsTArray()
 {
   return mData;
 }
@@ -1052,16 +1053,123 @@ SocketListenerProxy::OnPacketReceivedRun
 
 NS_IMETHODIMP
 SocketListenerProxy::OnStopListeningRunnable::Run()
 {
   mListener->OnStopListening(mSocket, mStatus);
   return NS_OK;
 }
 
+
+class SocketListenerProxyBackground final : public nsIUDPSocketListener
+{
+  ~SocketListenerProxyBackground() {}
+
+public:
+  explicit SocketListenerProxyBackground(nsIUDPSocketListener* aListener)
+    : mListener(aListener)
+    , mTargetThread(do_GetCurrentThread())
+  { }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIUDPSOCKETLISTENER
+
+  class OnPacketReceivedRunnable : public nsRunnable
+  {
+  public:
+    OnPacketReceivedRunnable(const nsCOMPtr<nsIUDPSocketListener>& aListener,
+                             nsIUDPSocket* aSocket,
+                             nsIUDPMessage* aMessage)
+      : mListener(aListener)
+      , mSocket(aSocket)
+      , mMessage(aMessage)
+    { }
+
+    NS_DECL_NSIRUNNABLE
+
+  private:
+    nsCOMPtr<nsIUDPSocketListener> mListener;
+    nsCOMPtr<nsIUDPSocket> mSocket;
+    nsCOMPtr<nsIUDPMessage> mMessage;
+  };
+
+  class OnStopListeningRunnable : public nsRunnable
+  {
+  public:
+    OnStopListeningRunnable(const nsCOMPtr<nsIUDPSocketListener>& aListener,
+                            nsIUDPSocket* aSocket,
+                            nsresult aStatus)
+      : mListener(aListener)
+      , mSocket(aSocket)
+      , mStatus(aStatus)
+    { }
+
+    NS_DECL_NSIRUNNABLE
+
+  private:
+    nsCOMPtr<nsIUDPSocketListener> mListener;
+    nsCOMPtr<nsIUDPSocket> mSocket;
+    nsresult mStatus;
+  };
+
+private:
+  nsCOMPtr<nsIUDPSocketListener> mListener;
+  nsCOMPtr<nsIEventTarget> mTargetThread;
+};
+
+NS_IMPL_ISUPPORTS(SocketListenerProxyBackground,
+                  nsIUDPSocketListener)
+
+NS_IMETHODIMP
+SocketListenerProxyBackground::OnPacketReceived(nsIUDPSocket* aSocket,
+                                                nsIUDPMessage* aMessage)
+{
+  nsRefPtr<OnPacketReceivedRunnable> r =
+    new OnPacketReceivedRunnable(mListener, aSocket, aMessage);
+  return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
+}
+
+NS_IMETHODIMP
+SocketListenerProxyBackground::OnStopListening(nsIUDPSocket* aSocket,
+                                               nsresult aStatus)
+{
+  nsRefPtr<OnStopListeningRunnable> r =
+    new OnStopListeningRunnable(mListener, aSocket, aStatus);
+  return mTargetThread->Dispatch(r, NS_DISPATCH_NORMAL);
+}
+
+NS_IMETHODIMP
+SocketListenerProxyBackground::OnPacketReceivedRunnable::Run()
+{
+  NetAddr netAddr;
+  nsCOMPtr<nsINetAddr> nsAddr;
+  mMessage->GetFromAddr(getter_AddRefs(nsAddr));
+  nsAddr->GetNetAddr(&netAddr);
+
+  nsCOMPtr<nsIOutputStream> outputStream;
+  mMessage->GetOutputStream(getter_AddRefs(outputStream));
+
+  FallibleTArray<uint8_t>& data = mMessage->GetDataAsTArray();
+
+  UDPSOCKET_LOG(("%s [this=%p], len %u", __FUNCTION__, this, data.Length()));
+  nsCOMPtr<nsIUDPMessage> message = new UDPMessageProxy(&netAddr,
+                                                        outputStream,
+                                                        data);
+  mListener->OnPacketReceived(mSocket, message);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SocketListenerProxyBackground::OnStopListeningRunnable::Run()
+{
+  mListener->OnStopListening(mSocket, mStatus);
+  return NS_OK;
+}
+
+
 class PendingSend : public nsIDNSListener
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIDNSLISTENER
 
   PendingSend(nsUDPSocket *aSocket, uint16_t aPort,
               FallibleTArray<uint8_t> &aData)
@@ -1175,18 +1283,24 @@ SendRequestRunnable::Run()
 NS_IMETHODIMP
 nsUDPSocket::AsyncListen(nsIUDPSocketListener *aListener)
 {
   // ensuring mFD implies ensuring mLock
   NS_ENSURE_TRUE(mFD, NS_ERROR_NOT_INITIALIZED);
   NS_ENSURE_TRUE(mListener == nullptr, NS_ERROR_IN_PROGRESS);
   {
     MutexAutoLock lock(mLock);
-    mListener = new SocketListenerProxy(aListener);
     mListenerTarget = NS_GetCurrentThread();
+    if (NS_IsMainThread()) {
+      // PNecko usage
+      mListener = new SocketListenerProxy(aListener);
+    } else {
+      // PBackground usage from media/mtransport
+      mListener = new SocketListenerProxyBackground(aListener);
+    }
   }
   return PostEvent(this, &nsUDPSocket::OnMsgAttach);
 }
 
 NS_IMETHODIMP
 nsUDPSocket::Send(const nsACString &aHost, uint16_t aPort,
                   const uint8_t *aData, uint32_t aDataLength,
                   uint32_t *_retval)
@@ -1309,17 +1423,17 @@ nsUDPSocket::SetSocketOption(const PRSoc
     return NS_OK;
   }
 
   if (NS_WARN_IF(!mFD)) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   if (PR_SetSocketOption(mFD, &aOpt) != PR_SUCCESS) {
-    SOCKET_LOG(("nsUDPSocket::SetSocketOption [this=%p] failed for type %d, "
+    UDPSOCKET_LOG(("nsUDPSocket::SetSocketOption [this=%p] failed for type %d, "
       "error %d\n", this, aOpt.option, PR_GetError()));
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -478,17 +478,17 @@ NeckoParent::DeallocPTCPServerSocketPare
    p->ReleaseIPDLReference();
   return true;
 }
 
 PUDPSocketParent*
 NeckoParent::AllocPUDPSocketParent(const Principal& /* unused */,
                                    const nsCString& /* unused */)
 {
-  nsRefPtr<UDPSocketParent> p = new UDPSocketParent();
+  nsRefPtr<UDPSocketParent> p = new UDPSocketParent(this);
 
   return p.forget().take();
 }
 
 bool
 NeckoParent::RecvPUDPSocketConstructor(PUDPSocketParent* aActor,
                                        const Principal& aPrincipal,
                                        const nsCString& aFilter)
new file mode 100644
--- /dev/null
+++ b/parser/htmlparser/tests/mochitest/file_async_bug1104732.sjs
@@ -0,0 +1,14 @@
+var timer = null;
+
+function handleRequest(request, response)
+{
+  response.processAsync();
+  response.setHeader("Content-Type", "application/javascript", false);
+  response.write("asyncState = 'mid-async';\n");
+
+  timer = Components.classes["@mozilla.org/timer;1"].createInstance(Components.interfaces.nsITimer);
+  timer.initWithCallback(function() {
+    response.write("asyncState = 'loaded';\n");
+    response.finish();
+  }, 5 * 1000 /* milliseconds */, timer.TYPE_ONE_SHOT);
+}
new file mode 100644
--- /dev/null
+++ b/parser/htmlparser/tests/mochitest/file_defer_bug1104732.js
@@ -0,0 +1,3 @@
+is(document.readyState, "interactive", "readyState should be interactive during defer.");
+state = "defer";
+
--- a/parser/htmlparser/tests/mochitest/mochitest.ini
+++ b/parser/htmlparser/tests/mochitest/mochitest.ini
@@ -124,16 +124,21 @@ support-files =
 [test_bug672453.html]
 [test_bug688580.html]
 [test_bug688580.xhtml]
 [test_bug709083.html]
 [test_bug715112.html]
 [test_bug715739.html]
 [test_bug716579.html]
 [test_bug717180.html]
+[test_bug1104732.html]
+support-files =
+  file_defer_bug1104732.js
+  file_async_bug1104732.sjs
+
 [test_compatmode.html]
 [test_html5_tree_construction.html]
 skip-if = toolkit == 'android' #TIMED_OUT
 [test_html5_tree_construction_part2.html]
 skip-if = toolkit == 'android' || e10s #TIMED_OUT
 [test_img_picture_preload.html]
 [test_viewsource.html]
 [test_xml_mislabeled.html]
new file mode 100644
--- /dev/null
+++ b/parser/htmlparser/tests/mochitest/test_bug1104732.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1104732
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1104732</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <script type="application/javascript">
+
+  /** Test for Bug 1104732 **/
+
+  // Expected order of the values of the "state" variable:
+  // Test starting
+  // defer
+  // DOMContentLoaded
+  // load
+
+  // In the meantime, the async script load should happen before load.
+  // Ideally, we would want to assert that it happens after DOMContentLoaded,
+  // because it shouldn't be holding up DOMContentLoaded, but there is
+  // no *guarantee* that this is the case, so we cannot assert it.
+
+  var state = "Test starting";
+  var asyncState = "not loaded";
+  SimpleTest.waitForExplicitFinish();
+  is(document.readyState, "loading", "Document should have been loading.");
+  document.addEventListener("DOMContentLoaded", function () {
+    is(document.readyState, "interactive", "readyState should be interactive during DOMContentLoaded.");
+    is(state, "defer", "Bad state upon DOMContentLoaded");
+    state = "DOMContentLoaded";
+    // This doesn't work (see above), but we can't "todo" this assertion either, because
+    // it will sometimes be the case...
+    // isnot(asyncState, "loaded", "Async script should not have delayed domcontentloaded");
+  });
+  window.addEventListener("load", function () {
+    is(document.readyState, "complete", "readyState should be complete during load.");
+    is(state, "DOMContentLoaded", "Bad state upon load")
+    state = "load";
+    is(asyncState, "loaded", "Async script should be loaded upon document load event");
+    SimpleTest.finish();
+  });
+  </script>
+  <script defer src="file_defer_bug1104732.js"></script>
+  <script async src="file_async_bug1104732.sjs"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1104732">Mozilla Bug 1104732</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
+
--- a/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
+++ b/security/sandbox/win/src/sandboxbroker/sandboxBroker.cpp
@@ -157,84 +157,94 @@ SandboxBroker::SetSecurityLevelForConten
 
 bool
 SandboxBroker::SetSecurityLevelForPluginProcess(int32_t aSandboxLevel)
 {
   if (!mPolicy) {
     return false;
   }
 
-  sandbox::ResultCode result;
-  bool ret;
-  if (aSandboxLevel >= 2) {
-    result = mPolicy->SetJobLevel(sandbox::JOB_UNPROTECTED,
-                                     0 /* ui_exceptions */);
-    ret = (sandbox::SBOX_ALL_OK == result);
-
-    sandbox::TokenLevel tokenLevel;
-    if (aSandboxLevel >= 3) {
-      tokenLevel = sandbox::USER_LIMITED;
-    } else {
-      tokenLevel = sandbox::USER_INTERACTIVE;
-    }
-
-    result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
-                                    tokenLevel);
-    ret = ret && (sandbox::SBOX_ALL_OK == result);
+  sandbox::JobLevel jobLevel;
+  sandbox::TokenLevel accessTokenLevel;
+  sandbox::IntegrityLevel initialIntegrityLevel;
+  sandbox::IntegrityLevel delayedIntegrityLevel;
 
-    sandbox::MitigationFlags mitigations =
-      sandbox::MITIGATION_BOTTOM_UP_ASLR |
-      sandbox::MITIGATION_HEAP_TERMINATE |
-      sandbox::MITIGATION_SEHOP |
-      sandbox::MITIGATION_DEP_NO_ATL_THUNK |
-      sandbox::MITIGATION_DEP;
-
-    result = mPolicy->SetProcessMitigations(mitigations);
-    ret = ret && (sandbox::SBOX_ALL_OK == result);
-
-    mitigations =
-      sandbox::MITIGATION_STRICT_HANDLE_CHECKS;
-
-    result = mPolicy->SetDelayedProcessMitigations(mitigations);
-    ret = ret && (sandbox::SBOX_ALL_OK == result);
-
-    // The following is required for the Java plugin.
-    result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
-                              sandbox::TargetPolicy::FILES_ALLOW_ANY,
-                              L"\\??\\pipe\\jpi2_pid*_pipe*");
-    ret = ret && (sandbox::SBOX_ALL_OK == result);
-
+  if (aSandboxLevel > 2) {
+    jobLevel = sandbox::JOB_UNPROTECTED;
+    accessTokenLevel = sandbox::USER_LIMITED;
+    initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+    delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+  } else if (aSandboxLevel == 2) {
+    jobLevel = sandbox::JOB_UNPROTECTED;
+    accessTokenLevel = sandbox::USER_INTERACTIVE;
+    initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
+    delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_LOW;
   } else {
-    result = mPolicy->SetJobLevel(sandbox::JOB_NONE,
-                                     0 /* ui_exceptions */);
-    ret = (sandbox::SBOX_ALL_OK == result);
-
-    result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
-                                    sandbox::USER_NON_ADMIN);
-    ret = ret && (sandbox::SBOX_ALL_OK == result);
+    jobLevel = sandbox::JOB_NONE;
+    accessTokenLevel = sandbox::USER_NON_ADMIN;
+    initialIntegrityLevel = sandbox::INTEGRITY_LEVEL_MEDIUM;
+    delayedIntegrityLevel = sandbox::INTEGRITY_LEVEL_MEDIUM;
   }
 
-  result = mPolicy->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_MEDIUM);
+  sandbox::ResultCode result = mPolicy->SetJobLevel(jobLevel,
+                                                    0 /* ui_exceptions */);
+  bool ret = (sandbox::SBOX_ALL_OK == result);
+
+  result = mPolicy->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
+                                  accessTokenLevel);
+  ret = ret && (sandbox::SBOX_ALL_OK == result);
+
+  result = mPolicy->SetIntegrityLevel(initialIntegrityLevel);
+  ret = ret && (sandbox::SBOX_ALL_OK == result);
+  result = mPolicy->SetDelayedIntegrityLevel(delayedIntegrityLevel);
+  ret = ret && (sandbox::SBOX_ALL_OK == result);
+
+  sandbox::MitigationFlags mitigations =
+    sandbox::MITIGATION_BOTTOM_UP_ASLR |
+    sandbox::MITIGATION_HEAP_TERMINATE |
+    sandbox::MITIGATION_SEHOP |
+    sandbox::MITIGATION_DEP_NO_ATL_THUNK |
+    sandbox::MITIGATION_DEP;
+
+  result = mPolicy->SetProcessMitigations(mitigations);
+  ret = ret && (sandbox::SBOX_ALL_OK == result);
+
+  mitigations =
+    sandbox::MITIGATION_STRICT_HANDLE_CHECKS;
+
+  result = mPolicy->SetDelayedProcessMitigations(mitigations);
   ret = ret && (sandbox::SBOX_ALL_OK == result);
 
   // Add the policy for the client side of a pipe. It is just a file
   // in the \pipe\ namespace. We restrict it to pipes that start with
   // "chrome." so the sandboxed process cannot connect to system services.
   result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
                             sandbox::TargetPolicy::FILES_ALLOW_ANY,
                             L"\\??\\pipe\\chrome.*");
   ret = ret && (sandbox::SBOX_ALL_OK == result);
 
   // The NPAPI process needs to be able to duplicate shared memory to the
-  // content process, which are Section type handles.
+  // content process and broker process, which are Section type handles.
+  // Content and broker are for e10s and non-e10s cases.
   result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
                             sandbox::TargetPolicy::HANDLES_DUP_ANY,
                             L"Section");
   ret = ret && (sandbox::SBOX_ALL_OK == result);
 
+  result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_HANDLES,
+                            sandbox::TargetPolicy::HANDLES_DUP_BROKER,
+                            L"Section");
+  ret = ret && (sandbox::SBOX_ALL_OK == result);
+
+  // The following is required for the Java plugin.
+  result = mPolicy->AddRule(sandbox::TargetPolicy::SUBSYS_FILES,
+                            sandbox::TargetPolicy::FILES_ALLOW_ANY,
+                            L"\\??\\pipe\\jpi2_pid*_pipe*");
+  ret = ret && (sandbox::SBOX_ALL_OK == result);
+
   return ret;
 }
 
 bool
 SandboxBroker::SetSecurityLevelForIPDLUnitTestProcess()
 {
   if (!mPolicy) {
     return false;
--- a/testing/marionette/client/requirements.txt
+++ b/testing/marionette/client/requirements.txt
@@ -1,14 +1,14 @@
 marionette-transport >= 0.4
 marionette-driver >= 0.7
 browsermob-proxy >= 0.6.0
 manifestparser >= 1.1
 mozhttpd >= 0.7
-mozinfo >= 0.7
+mozinfo >= 0.8
 mozprocess >= 0.9
 mozrunner >= 6.2
 mozdevice >= 0.44
 mozlog >= 2.7
 moznetwork >= 0.21
 mozcrash >= 0.5
 mozprofile >= 0.7
 moztest >= 0.7
--- a/toolkit/devtools/DevToolsUtils.js
+++ b/toolkit/devtools/DevToolsUtils.js
@@ -497,28 +497,32 @@ exports.fetch = function fetch(aURL, aOp
         channel = Services.io.newChannel2(url,
                                           null,
                                           null,
                                           null,      // aLoadingNode
                                           Services.scriptSecurityManager.getSystemPrincipal(),
                                           null,      // aTriggeringPrincipal
                                           Ci.nsILoadInfo.SEC_NORMAL,
                                           aOptions.policy);
-      } catch (e if e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {
-        // On Windows xpcshell tests, c:/foo/bar can pass as a valid URL, but
-        // newChannel won't be able to handle it.
-        url = "file:///" + url;
-        channel = Services.io.newChannel2(url,
-                                          null,
-                                          null,
-                                          null,      // aLoadingNode
-                                          Services.scriptSecurityManager.getSystemPrincipal(),
-                                          null,      // aTriggeringPrincipal
-                                          Ci.nsILoadInfo.SEC_NORMAL,
-                                          aOptions.policy);
+      } catch (e) {
+        if (e.name == "NS_ERROR_UNKNOWN_PROTOCOL") {
+          // On Windows xpcshell tests, c:/foo/bar can pass as a valid URL, but
+          // newChannel won't be able to handle it.
+          url = "file:///" + url;
+          channel = Services.io.newChannel2(url,
+                                            null,
+                                            null,
+                                            null,      // aLoadingNode
+                                            Services.scriptSecurityManager.getSystemPrincipal(),
+                                            null,      // aTriggeringPrincipal
+                                            Ci.nsILoadInfo.SEC_NORMAL,
+                                            aOptions.policy);
+        } else {
+          throw e;
+        }
       }
       let chunks = [];
       let streamListener = {
         onStartRequest: function(aRequest, aContext, aStatusCode) {
           if (!components.isSuccessCode(aStatusCode)) {
             deferred.reject(new Error("Request failed with status code = "
                                       + aStatusCode
                                       + " in onStartRequest handler for url = "
--- a/toolkit/devtools/client/connection-manager.js
+++ b/toolkit/devtools/client/connection-manager.js
@@ -95,17 +95,17 @@ let ConnectionManager = {
     if (this._connections.has(connection)) {
       this._connections.delete(connection);
       if (connection.status != Connection.Status.DESTROYED) {
         connection.destroy();
       }
     }
   },
   get connections() {
-    return [c for (c of this._connections)];
+    return [...this._connections];
   },
   getFreeTCPPort: function () {
     let serv = Cc['@mozilla.org/network/server-socket;1']
                  .createInstance(Ci.nsIServerSocket);
     serv.init(-1, true, -1);
     let port = serv.port;
     serv.close();
     return port;
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -184,17 +184,17 @@ function eventSource(aProto) {
   aProto.emit = function () {
     if (!this._listeners) {
       return;
     }
 
     let name = arguments[0];
     let listeners = this._getListeners(name).slice(0);
 
-    for each (let listener in listeners) {
+    for (let listener of listeners) {
       try {
         listener.apply(null, arguments);
       } catch (e) {
         // Prevent a bad listener from interfering with the others.
         DevToolsUtils.reportException("notify event '" + name + "'", e);
       }
     }
   }
@@ -2073,18 +2073,18 @@ ThreadClient.prototype = {
 
   /**
    * Clear and invalidate all the grip clients from the given cache.
    *
    * @param aGripCacheName
    *        The property name of the grip cache we want to clear.
    */
   _clearObjectClients: function (aGripCacheName) {
-    for each (let grip in this[aGripCacheName]) {
-      grip.valid = false;
+    for (let id in this[aGripCacheName]) {
+      this[aGripCacheName][id].valid = false;
     }
     this[aGripCacheName] = {};
   },
 
   /**
    * Invalidate pause-lifetime grip clients and clear the list of current grip
    * clients.
    */
@@ -2264,19 +2264,25 @@ function ObjectClient(aClient, aGrip)
 }
 
 ObjectClient.prototype = {
   get actor() { return this._grip.actor },
   get _transport() { return this._client._transport; },
 
   valid: true,
 
-  get isFrozen() this._grip.frozen,
-  get isSealed() this._grip.sealed,
-  get isExtensible() this._grip.extensible,
+  get isFrozen() {
+    return this._grip.frozen;
+  },
+  get isSealed() {
+    return this._grip.sealed;
+  },
+  get isExtensible() {
+    return this._grip.extensible;
+  },
 
   getDefinitionSite: DebuggerClient.requester({
     type: "definitionSite"
   }, {
     before: function (aPacket) {
       if (this._grip.class != "Function") {
         throw new Error("getDefinitionSite is only valid for function grips.");
       }
@@ -2535,22 +2541,34 @@ function SourceClient(aClient, aForm) {
   this._form = aForm;
   this._isBlackBoxed = aForm.isBlackBoxed;
   this._isPrettyPrinted = aForm.isPrettyPrinted;
   this._activeThread = aClient;
   this._client = aClient.client;
 }
 
 SourceClient.prototype = {
-  get _transport() this._client._transport,
-  get isBlackBoxed() this._isBlackBoxed,
-  get isPrettyPrinted() this._isPrettyPrinted,
-  get actor() this._form.actor,
-  get request() this._client.request,
-  get url() this._form.url,
+  get _transport() {
+    return this._client._transport;
+  },
+  get isBlackBoxed() {
+    return this._isBlackBoxed;
+  },
+  get isPrettyPrinted() {
+    return this._isPrettyPrinted;
+  },
+  get actor() {
+    return this._form.actor;
+  },
+  get request() {
+    return this._client.request;
+  },
+  get url() {
+    return this._form.url;
+  },
 
   /**
    * Black box this SourceClient's source.
    *
    * @param aCallback Function
    *        The callback function called when we receive the response from the server.
    */
   blackBox: DebuggerClient.requester({
@@ -2890,17 +2908,19 @@ eventSource(BreakpointClient.prototype);
 function EnvironmentClient(aClient, aForm) {
   this._client = aClient;
   this._form = aForm;
   this.request = this._client.request;
 }
 
 EnvironmentClient.prototype = {
 
-  get actor() this._form.actor,
+  get actor() {
+    return this._form.actor;
+  },
   get _transport() { return this._client._transport; },
 
   /**
    * Fetches the bindings introduced by this lexical environment.
    */
   getBindings: DebuggerClient.requester({
     type: "bindings"
   }, {
--- a/toolkit/devtools/event-emitter.js
+++ b/toolkit/devtools/event-emitter.js
@@ -137,17 +137,17 @@ EventEmitter.prototype = {
       // emitting.
       if (!this._eventEmitterListeners) {
         break;
       }
 
       // If listeners were removed during emission, make sure the
       // event handler we're going to fire wasn't removed.
       if (originalListeners === this._eventEmitterListeners.get(aEvent) ||
-          this._eventEmitterListeners.get(aEvent).some(function(l) l === listener)) {
+          this._eventEmitterListeners.get(aEvent).some(l => l === listener)) {
         try {
           listener.apply(null, arguments);
         }
         catch (ex) {
           // Prevent a bad listener from interfering with the others.
           let msg = ex + ": " + ex.stack;
           Cu.reportError(msg);
           dump(msg + "\n");
--- a/toolkit/devtools/gcli/commands/calllog.js
+++ b/toolkit/devtools/gcli/commands/calllog.js
@@ -94,17 +94,19 @@ exports.items = [
       return l10n.lookupFormat("calllogStopReply", [ numDebuggers ]);
     }
   },
   {
     item: "command",
     runAt: "client",
     name: "calllog chromestart",
     description: l10n.lookup("calllogChromeStartDesc"),
-    get hidden() gcli.hiddenByChromePref(),
+    get hidden() {
+      return gcli.hiddenByChromePref();
+    },
     params: [
       {
         name: "sourceType",
         type: {
           name: "selection",
           data: ["content-variable", "chrome-variable", "jsm", "javascript"]
         }
       },
@@ -194,17 +196,19 @@ exports.items = [
       return name + "(" + args + ")";
     }
   },
   {
     item: "command",
     runAt: "client",
     name: "calllog chromestop",
     description: l10n.lookup("calllogChromeStopDesc"),
-    get hidden() gcli.hiddenByChromePref(),
+    get hidden() {
+      return gcli.hiddenByChromePref();
+    },
     exec: function(args, context) {
       let numDebuggers = chromeDebuggers.length;
       if (numDebuggers == 0) {
         return l10n.lookup("calllogChromeStopNoLogging");
       }
 
       for (let dbg of chromeDebuggers) {
         dbg.onEnterFrame = undefined;
--- a/toolkit/devtools/gcli/commands/paintflashing.js
+++ b/toolkit/devtools/gcli/commands/paintflashing.js
@@ -98,17 +98,19 @@ exports.items = [
     description: l10n.lookup("paintflashingOnDesc"),
     manual: l10n.lookup("paintflashingManual"),
     params: [{
       group: "options",
       params: [
         {
           type: "boolean",
           name: "chrome",
-          get hidden() gcli.hiddenByChromePref(),
+          get hidden() {
+            return gcli.hiddenByChromePref();
+          },
           description: l10n.lookup("paintflashingChromeDesc"),
         }
       ]
     }],
     exec: function*(args, context) {
       if (!args.chrome) {
         const value = yield context.updateExec("paintflashing_server --state on");
         isContentPaintFlashing = value;
@@ -126,22 +128,24 @@ exports.items = [
     description: l10n.lookup("paintflashingOffDesc"),
     manual: l10n.lookup("paintflashingManual"),
     params: [{
       group: "options",
       params: [
         {
           type: "boolean",
           name: "chrome",
-          get hidden() gcli.hiddenByChromePref(),
+          get hidden() {
+            return gcli.hiddenByChromePref();
+          },
           description: l10n.lookup("paintflashingChromeDesc"),
         }
       ]
     }],
-    exec: function(args, context) {
+    exec: function*(args, context) {
       if (!args.chrome) {
         const value = yield context.updateExec("paintflashing_server --state off");
         isContentPaintFlashing = value;
         onPaintFlashingChanged(context.environment.target, value);
       }
       else {
         setPaintFlashing(context.environment.chromeWindow, "off");
       }
@@ -157,17 +161,17 @@ exports.items = [
     state: {
       isChecked: () => isContentPaintFlashing,
       onChange: (_, handler) => eventEmitter.on("changed", handler),
       offChange: (_, handler) => eventEmitter.off("changed", handler),
     },
     tooltipText: l10n.lookup("paintflashingTooltip"),
     description: l10n.lookup("paintflashingToggleDesc"),
     manual: l10n.lookup("paintflashingManual"),
-    exec: function(args, context) {
+    exec: function*(args, context) {
       const value = yield context.updateExec("paintflashing_server --state toggle");
       isContentPaintFlashing = value;
       onPaintFlashingChanged(context.environment.target, value);
     }
   },
   {
     item: "command",
     runAt: "server",
--- a/toolkit/devtools/gcli/commands/tools.js
+++ b/toolkit/devtools/gcli/commands/tools.js
@@ -16,25 +16,29 @@ const BRAND_SHORT_NAME = Cc["@mozilla.or
                            .createBundle("chrome://branding/locale/brand.properties")
                            .GetStringFromName("brandShortName");
 
 exports.items = [
   {
     name: "tools",
     description: l10n.lookupFormat("toolsDesc2", [ BRAND_SHORT_NAME ]),
     manual: l10n.lookupFormat("toolsManual2", [ BRAND_SHORT_NAME ]),
-    get hidden() gcli.hiddenByChromePref(),
+    get hidden() {
+      return gcli.hiddenByChromePref();
+    }
   },
   {
     item: "command",
     runAt: "client",
     name: "tools srcdir",
     description: l10n.lookup("toolsSrcdirDesc"),
     manual: l10n.lookupFormat("toolsSrcdirManual2", [ BRAND_SHORT_NAME ]),
-    get hidden() gcli.hiddenByChromePref(),
+    get hidden() {
+      return gcli.hiddenByChromePref();
+    },
     params: [
       {
         name: "srcdir",
         type: "string" /* {
           name: "file",
           filetype: "directory",
           existing: "yes"
         } */,
@@ -62,17 +66,19 @@ exports.items = [
     }
   },
   {
     item: "command",
     runAt: "client",
     name: "tools builtin",
     description: l10n.lookup("toolsBuiltinDesc"),
     manual: l10n.lookup("toolsBuiltinManual"),
-    get hidden() gcli.hiddenByChromePref(),
+    get hidden() {
+      return gcli.hiddenByChromePref();
+    },
     returnType: "string",
     exec: function(args, context) {
       Services.prefs.clearUserPref("devtools.loader.srcdir");
       devtools.reload();
       return l10n.lookup("toolsBuiltinReloaded");
     }
   },
   {
--- a/toolkit/devtools/security/auth.js
+++ b/toolkit/devtools/security/auth.js
@@ -389,17 +389,17 @@ OOBCert.Client.prototype = {
     };
   }),
 
   _createRandom() {
     const length = 16; // 16 bytes / 128 bits
     let rng = Cc["@mozilla.org/security/random-generator;1"]
               .createInstance(Ci.nsIRandomGenerator);
     let bytes = rng.generateRandomBytes(length);
-    return [for (byte of bytes) byte.toString(16)].join("");
+    return bytes.map(byte => byte.toString(16)).join("");
   },
 
   /**
    * Send data across the OOB channel to the server to authenticate the devices.
    *
    * @param host string
    *        The host name or IP address of the debugger server.
    * @param port number
--- a/toolkit/devtools/security/tests/unit/testactors.js
+++ b/toolkit/devtools/security/tests/unit/testactors.js
@@ -41,17 +41,17 @@ function TestTabList(aConnection) {
   }
 
   aConnection.addActorPool(this._tabActorPool);
 }
 
 TestTabList.prototype = {
   constructor: TestTabList,
   getList: function () {
-    return promise.resolve([tabActor for (tabActor of this._tabActors)]);
+    return promise.resolve([...this._tabActors]);
   }
 };
 
 function createRootActor(aConnection) {
   let root = new RootActor(aConnection, {
     tabList: new TestTabList(aConnection),
     globalActorFactories: DebuggerServer.globalActorFactories
   });
--- a/toolkit/devtools/server/actors/child-process.js
+++ b/toolkit/devtools/server/actors/child-process.js
@@ -33,17 +33,19 @@ function ChildProcessActor(aConnection) 
   let sandbox = Cu.Sandbox(systemPrincipal);
   this._consoleScope = sandbox;
 }
 exports.ChildProcessActor = ChildProcessActor;
 
 ChildProcessActor.prototype = {
   actorPrefix: "process",
 
-  get isRootActor() true,
+  get isRootActor() {
+    return true;
+  },
 
   get exited() {
     return !this._contextPool;
   },
 
   get url() {
     return undefined;
   },
--- a/toolkit/devtools/server/actors/common.js
+++ b/toolkit/devtools/server/actors/common.js
@@ -275,18 +275,18 @@ ActorPool.prototype = {
   unmanage: function(aActor) {
     return this.removeActor(aActor);
   },
 
   /**
    * Run all actor cleanups.
    */
   cleanup: function AP_cleanup() {
-    for each (let actor in this._cleanups) {
-      actor.disconnect();
+    for (let id in this._cleanups) {
+      this._cleanups[id].disconnect();
     }
     this._cleanups = {};
   },
 
   forEach: function(callback) {
     for (let name in this._actors) {
       callback(this._actors[name]);
     }
--- a/toolkit/devtools/server/actors/director-manager.js
+++ b/toolkit/devtools/server/actors/director-manager.js
@@ -478,21 +478,21 @@ const DirectorManagerActor = exports.Dir
     protocol.Actor.prototype.destroy.call(this, conn);
     this.finalize();
   },
 
   /**
    * Retrieves the list of installed director-scripts.
    */
   list: method(function () {
-    var enabled_script_ids = [for (id of this._directorScriptActorsMap.keys()) id];
+    let enabledScriptIds = [...this._directorScriptActorsMap.keys()];
 
     return {
       installed: DirectorRegistry.list(),
-      enabled: enabled_script_ids
+      enabled: enabledScriptIds
     };
   }, {
     response: {
       directorScripts: RetVal("json")
     }
   }),
 
   /**
--- a/toolkit/devtools/server/actors/gcli.js
+++ b/toolkit/devtools/server/actors/gcli.js
@@ -255,19 +255,27 @@ const GcliActor = ActorClass({
         get chromeWindow() {
           throw new Error("environment.chromeWindow is not available in runAt:server commands");
         },
 
         get chromeDocument() {
           throw new Error("environment.chromeDocument is not available in runAt:server commands");
         },
 
-        get window() tabActor.window,
-        get document() tabActor.window.document,
-        get __deprecatedTabActor() tabActor,
+        get window() {
+          return tabActor.window;
+        },
+
+        get document() {
+          return tabActor.window.document;
+        },
+
+        get __deprecatedTabActor() {
+          return tabActor;
+        }
       };
 
       return new Requisition(this._system, { environment: environment });
     });
 
     return this._requisitionPromise;
   },
 
--- a/toolkit/devtools/server/actors/highlighter.js
+++ b/toolkit/devtools/server/actors/highlighter.js
@@ -138,17 +138,19 @@ let HighlighterActor = exports.Highlight
     this._layoutHelpers = new LayoutHelpers(this._tabActor.window);
     this._createHighlighter();
 
     // Listen to navigation events to switch from the BoxModelHighlighter to the
     // SimpleOutlineHighlighter, and back, if the top level window changes.
     events.on(this._tabActor, "navigate", this._onNavigate);
   },
 
-  get conn() this._inspector && this._inspector.conn,
+  get conn() {
+    return this._inspector && this._inspector.conn;
+  },
 
   _createHighlighter: function() {
     this._isPreviousWindowXUL = isXUL(this._tabActor);
 
     if (!this._isPreviousWindowXUL) {
       this._highlighter = new BoxModelHighlighter(this._tabActor,
                                                           this._inspector);
       this._highlighter.on("ready", this._highlighterReady);
@@ -429,17 +431,19 @@ let CustomHighlighterActor = exports.Cus
       this._highlighter = new constructor(inspector.tabActor);
     } else {
       throw new Error("Custom " + typeName +
         "highlighter cannot be created in a XUL window");
       return;
     }
   },
 
-  get conn() this._inspector && this._inspector.conn,
+  get conn() {
+    return this._inspector && this._inspector.conn;
+  },
 
   destroy: function() {
     protocol.Actor.prototype.destroy.call(this);
     this.finalize();
   },
 
   /**
    * Show the highlighter.
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
@@ -208,17 +208,19 @@ var NodeActor = exports.NodeActor = prot
   toString: function() {
     return "[NodeActor " + this.actorID + " for " + this.rawNode.toString() + "]";
   },
 
   /**
    * Instead of storing a connection object, the NodeActor gets its connection
    * from its associated walker.
    */
-  get conn() this.walker.conn,
+  get conn() {
+    return this.walker.conn;
+  },
 
   isDocumentElement: function() {
     return this.rawNode.ownerDocument &&
            this.rawNode.ownerDocument.documentElement === this.rawNode;
   },
 
   // Returns the JSON representation of this object over the wire.
   form: function(detail) {
@@ -376,18 +378,19 @@ var NodeActor = exports.NodeActor = prot
     }
     return false;
   },
 
   writeAttrs: function() {
     if (!this.rawNode.attributes) {
       return undefined;
     }
-    return [{namespace: attr.namespace, name: attr.name, value: attr.value }
-            for (attr of this.rawNode.attributes)];
+    return [...this.rawNode.attributes].map(attr => {
+      return {namespace: attr.namespace, name: attr.name, value: attr.value };
+    });
   },
 
   writePseudoClassLocks: function() {
     if (this.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
       return undefined;
     }
     let ret = undefined;
     for (let pseudo of PSEUDO_CLASSES) {
@@ -826,65 +829,107 @@ let NodeFront = protocol.FrontClass(Node
       this._form.incompleteValue = change.incompleteValue;
     } else if (change.type === "pseudoClassLock") {
       this._form.pseudoClassLocks = change.pseudoClassLocks;
     }
   },
 
   // Some accessors to make NodeFront feel more like an nsIDOMNode
 
-  get id() this.getAttribute("id"),
-
-  get nodeType() this._form.nodeType,
-  get namespaceURI() this._form.namespaceURI,
-  get nodeName() this._form.nodeName,
-
-  get baseURI() this._form.baseURI,
+  get id() {
+    return this.getAttribute("id");
+  },
+
+  get nodeType() {
+    return this._form.nodeType;
+  },
+  get namespaceURI() {
+    return this._form.namespaceURI;
+  },
+  get nodeName() {
+    return this._form.nodeName;
+  },
+
+  get baseURI() {
+    return this._form.baseURI;
+  },
 
   get className() {
     return this.getAttribute("class") || '';
   },
 
-  get hasChildren() this._form.numChildren > 0,
-  get numChildren() this._form.numChildren,
-  get hasEventListeners() this._form.hasEventListeners,
-
-  get isBeforePseudoElement() this._form.isBeforePseudoElement,
-  get isAfterPseudoElement() this._form.isAfterPseudoElement,
-  get isPseudoElement() this.isBeforePseudoElement || this.isAfterPseudoElement,
-  get isAnonymous() this._form.isAnonymous,
-
-  get tagName() this.nodeType === Ci.nsIDOMNode.ELEMENT_NODE ? this.nodeName : null,
-  get shortValue() this._form.shortValue,
-  get incompleteValue() !!this._form.incompleteValue,
-
-  get isDocumentElement() !!this._form.isDocumentElement,
+  get hasChildren() {
+    return this._form.numChildren > 0;
+  },
+  get numChildren() {
+    return this._form.numChildren;
+  },
+  get hasEventListeners() {
+    return this._form.hasEventListeners;
+  },
+
+  get isBeforePseudoElement() {
+    return this._form.isBeforePseudoElement;
+  },
+  get isAfterPseudoElement() {
+    return this._form.isAfterPseudoElement;
+  },
+  get isPseudoElement() {
+    return this.isBeforePseudoElement || this.isAfterPseudoElement;
+  },
+  get isAnonymous() {
+    return this._form.isAnonymous;
+  },
+
+  get tagName() {
+    return this.nodeType === Ci.nsIDOMNode.ELEMENT_NODE ? this.nodeName : null;
+  },
+  get shortValue() {
+    return this._form.shortValue;
+  },
+  get incompleteValue() {
+    return !!this._form.incompleteValue;
+  },
+
+  get isDocumentElement() {
+    return !!this._form.isDocumentElement;
+  },
 
   // doctype properties
-  get name() this._form.name,
-  get publicId() this._form.publicId,
-  get systemId() this._form.systemId,
+  get name() {
+    return this._form.name;
+  },
+  get publicId() {
+    return this._form.publicId;
+  },
+  get systemId() {
+    return this._form.systemId;
+  },
 
   getAttribute: function(name) {
     let attr = this._getAttribute(name);
     return attr ? attr.value : null;
   },
   hasAttribute: function(name) {
     this._cacheAttributes();
     return (name in this._attrMap);
   },
 
   get hidden() {
     let cls = this.getAttribute("class");
     return cls && cls.indexOf(HIDDEN_CLASS) > -1;
   },
 
-  get attributes() this._form.attrs,
-
-  get pseudoClassLocks() this._form.pseudoClassLocks || [],
+  get attributes() {
+    return this._form.attrs;
+  },
+
+  get pseudoClassLocks() {
+    return this._form.pseudoClassLocks || [];
+  },
   hasPseudoClassLock: function(pseudo) {
     return this.pseudoClassLocks.some(locked => locked === pseudo);
   },
 
   get isDisplayed() {
     // The NodeActor's form contains the isDisplayed information as a boolean
     // starting from FF32. Before that, the property is missing
     return "isDisplayed" in this._form ? this._form.isDisplayed : true;
@@ -1079,17 +1124,17 @@ var NodeListActor = exports.NodeListActo
     request: { item: Arg(0) },
     response: RetVal("disconnectedNode")
   }),
 
   /**
    * Get a range of the items from the node list.
    */
   items: method(function(start=0, end=this.nodeList.length) {
-    let items = [this.walker._ref(item) for (item of Array.prototype.slice.call(this.nodeList, start, end))];
+    let items = Array.prototype.slice.call(this.nodeList, start, end).map(item => this.walker._ref(item));
     return this.walker.attachElements(items);
   }, {
     request: {
       start: Arg(0, "nullable:number"),
       end: Arg(1, "nullable:number")
     },
     response: RetVal("disconnectedNodeArray")
   }),
@@ -3454,17 +3499,19 @@ var InspectorActor = exports.InspectorAc
   },
 
   // Forces destruction of the actor and all its children
   // like highlighter, walker and style actors.
   disconnect: function() {
     this.destroy();
   },
 
-  get window() this.tabActor.window,
+  get window() {
+    return this.tabActor.window;
+  },
 
   getWalker: method(function(options={}) {
     if (this._walkerPromise) {
       return this._walkerPromise;
     }
 
     let deferred = promise.defer();
     this._walkerPromise = deferred.promise;
@@ -3735,20 +3782,28 @@ function DocumentWalker(node, rootWin, w
   this.walker.showSubDocuments = true;
   this.walker.showDocumentsAsNodes = true;
   this.walker.init(rootWin.document, whatToShow);
   this.walker.currentNode = node;
   this.filter = filter;
 }
 
 DocumentWalker.prototype = {
-  get node() this.walker.node,
-  get whatToShow() this.walker.whatToShow,
-  get currentNode() this.walker.currentNode,
-  set currentNode(aVal) this.walker.currentNode = aVal,
+  get node() {
+    return this.walker.node;
+  },
+  get whatToShow() {
+    return this.walker.whatToShow;
+  },
+  get currentNode() {
+    return this.walker.currentNode;
+  },
+  set currentNode(aVal) {
+    this.walker.currentNode = aVal;
+  },
 
   parentNode: function() {
     return this.walker.parentNode();
   },
 
   firstChild: function() {
     let node = this.walker.currentNode;
     if (!node)
--- a/toolkit/devtools/server/actors/root.js
+++ b/toolkit/devtools/server/actors/root.js
@@ -255,17 +255,17 @@ RootActor.prototype = {
         this.conn.removeActorPool(this._tabActorPool);
       }
       this._tabActorPool = newActorPool;
       this.conn.addActorPool(this._tabActorPool);
 
       let reply = {
         "from": this.actorID,
         "selected": selected || 0,
-        "tabs": [actor.form() for (actor of tabActorList)],
+        "tabs": tabActorList.map(actor => actor.form())
       };
 
       /* If a root window is accessible, include its URL. */
       if (this.url) {
         reply.url = this.url;
       }
 
       /* DebuggerServer.addGlobalActor support: name actors in 'listTabs' reply. */
@@ -333,17 +333,17 @@ RootActor.prototype = {
       }
       this._addonActorPool = addonActorPool;
       this.conn.addActorPool(this._addonActorPool);
 
       addonList.onListChanged = this._onAddonListChanged;
 
       return {
         "from": this.actorID,
-        "addons": [addonActor.form() for (addonActor of addonActors)]
+        "addons": addonActors.map(addonActor => addonActor.form())
       };
     });
   },
 
   onAddonListChanged: function () {
     this.conn.send({ from: this.actorID, type: "addonListChanged" });
     this._parameters.addonList.onListChanged = null;
   },
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -492,20 +492,25 @@ ThreadActor.prototype = {
     }
     return this._dbg;
   },
 
   get globalDebugObject() {
     return this.dbg.makeGlobalObjectReference(this._parent.window);
   },
 
-  get state() { return this._state; },
-  get attached() this.state == "attached" ||
-                 this.state == "running" ||
-                 this.state == "paused",
+  get state() {
+    return this._state;
+  },
+
+  get attached() {
+    return this.state == "attached" ||
+           this.state == "running" ||
+           this.state == "paused";
+  },
 
   get threadLifetimePool() {
     if (!this._threadLifetimePool) {
       this._threadLifetimePool = new ActorPool(this.conn);
       this.conn.addActorPool(this._threadLifetimePool);
       this._threadLifetimePool.objectActors = new WeakMap();
     }
     return this._threadLifetimePool;
@@ -884,17 +889,17 @@ ThreadActor.prototype = {
    * Define the JS hook functions for stepping.
    */
   _makeSteppingHooks: function (aStartLocation, steppingType) {
     // Bind these methods and state because some of the hooks are called
     // with 'this' set to the current frame. Rather than repeating the
     // binding in each _makeOnX method, just do it once here and pass it
     // in to each function.
     const steppingHookState = {
-      pauseAndRespond: (aFrame, onPacket=(k)=>k) => {
+      pauseAndRespond: (aFrame, onPacket=k=>k) => {
         return this._pauseAndRespond(aFrame, { type: "resumeLimit" }, onPacket);
       },
       createValueGrip: this.createValueGrip.bind(this),
       thread: this,
       startFrame: this.youngestFrame,
       startLocation: aStartLocation,
       steppingType: steppingType
     };
@@ -1301,17 +1306,17 @@ ThreadActor.prototype = {
 
   onReleaseMany: function (aRequest) {
     if (!aRequest.actors) {
       return { error: "missingParameter",
                message: "no actors were specified" };
     }
 
     let res;
-    for each (let actorID in aRequest.actors) {
+    for (let actorID of aRequest.actors) {
       let actor = this.threadLifetimePool.get(actorID);
       if (!actor) {
         if (!res) {
           res = { error: "notReleasable",
                   message: "Only thread-lifetime actors can be released." };
         }
         continue;
       }
@@ -1329,24 +1334,25 @@ ThreadActor.prototype = {
     const scripts = this.scripts.getAllScripts();
     for (let i = 0, len = scripts.length; i < len; i++) {
       let s = scripts[i];
       if (s.source) {
         sourcesToScripts.set(s.source, s);
       }
     }
 
-    return all([this.sources.createSourceActors(script.source)
-                for (script of sourcesToScripts.values())]);
+    return all([...sourcesToScripts.values()].map(script => {
+      return this.sources.createSourceActors(script.source);
+    }));
   },
 
   onSources: function (aRequest) {
     return this._discoverSources().then(() => {
       return {
-        sources: [s.form() for (s of this.sources.iter())]
+        sources: this.sources.iter().map(s => s.form())
       };
     });
   },
 
   /**
    * Disassociate all breakpoint actors from their scripts and clear the
    * breakpoint handlers. This method can be used when the thread actor intends
    * to keep the breakpoint store, but needs to clear any actual breakpoints,
@@ -1583,17 +1589,17 @@ ThreadActor.prototype = {
    */
   _updateFrames: function () {
     let popped = [];
 
     // Create the actor pool that will hold the still-living frames.
     let framePool = new ActorPool(this.conn);
     let frameList = [];
 
-    for each (let frameActor in this._frameActors) {
+    for (let frameActor of this._frameActors) {
       if (frameActor.frame.live) {
         framePool.addActor(frameActor);
         frameList.push(frameActor);
       } else {
         popped.push(frameActor.actorID);
       }
     }
 
@@ -2427,17 +2433,17 @@ SourceActor.prototype = {
   getExecutableLines: function () {
     // Check if the original source is source mapped
     let packet = {
       from: this.actorID
     };
 
     function sortLines(lines) {
       // Converting the Set into an array
-      lines = [line for (line of lines)];
+      lines = [...lines];
       lines.sort((a, b) => {
         return a - b;
       });
       return lines;
     }
 
     if (this.generatedSource) {
       return this.threadActor.sources.getSourceMap(this.generatedSource).then(sm => {
@@ -4831,18 +4837,17 @@ FrameActor.prototype = {
     return form;
   },
 
   _args: function () {
     if (!this.frame.arguments) {
       return [];
     }
 
-    return [this.threadActor.createValueGrip(arg)
-            for each (arg in this.frame.arguments)];
+    return this.frame.arguments.map(arg => this.threadActor.createValueGrip(arg));
   },
 
   /**
    * Handle a protocol request to pop this frame from the stack.
    *
    * @param aRequest object
    *        The protocol request object.
    */
@@ -5106,18 +5111,20 @@ EnvironmentActor.prototype = {
     if (typeof this.obj.getVariable != "function") {
     //if (typeof this.obj.getVariableDescriptor != "function") {
       return bindings;
     }
 
     let parameterNames;
     if (this.obj.callee) {
       parameterNames = this.obj.callee.parameterNames;
-    }
-    for each (let name in parameterNames) {
+    } else {
+      parameterNames = [];
+    }
+    for (let name of parameterNames) {
       let arg = {};
       let value = this.obj.getVariable(name);
 
       // TODO: this part should be removed in favor of the commented-out part
       // below when getVariableDescriptor lands (bug 725815).
       let desc = {
         value: value,
         configurable: false,
@@ -5136,17 +5143,17 @@ EnvironmentActor.prototype = {
       } else {
         descForm.get = this.threadActor.createValueGrip(desc.get);
         descForm.set = this.threadActor.createValueGrip(desc.set);
       }
       arg[name] = descForm;
       bindings.arguments.push(arg);
     }
 
-    for each (let name in this.obj.names()) {
+    for (let name of this.obj.names()) {
       if (bindings.arguments.some(function exists(element) {
                                     return !!element[name];
                                   })) {
         continue;
       }
 
       let value = this.obj.getVariable(name);
 
@@ -5195,20 +5202,24 @@ EnvironmentActor.prototype = {
     if (!desc.writable) {
       return { error: "immutableBinding",
                message: "Changing the value of an immutable binding is not " +
                         "allowed" };
     }*/
 
     try {
       this.obj.setVariable(aRequest.name, aRequest.value);
-    } catch (e if e instanceof Debugger.DebuggeeWouldRun) {
+    } catch (e) {
+      if (e instanceof Debugger.DebuggeeWouldRun) {
         return { error: "threadWouldRun",
                  cause: e.cause ? e.cause : "setter",
                  message: "Assigning a value would cause the debuggee to run" };
+      } else {
+        throw e;
+      }
     }
     return { from: this.actorID };
   },
 
   /**
    * Handle a protocol request to fully enumerate the bindings introduced by the
    * lexical environment.
    *
--- a/toolkit/devtools/server/actors/storage.js
+++ b/toolkit/devtools/server/actors/storage.js
@@ -880,17 +880,17 @@ function ObjectStoreMetadata(objectStore
 }
 ObjectStoreMetadata.prototype = {
   toObject: function() {
     return {
       name: this._name,
       keyPath: this._keyPath,
       autoIncrement: this._autoIncrement,
       indexes: JSON.stringify(
-        [index.toObject() for (index of this._indexes.values())]
+        [...this._indexes.values()].map(index => index.toObject())
       )
     };
   }
 };
 
 /**
  * Meta data object for a particular indexed db in a host.
  *
--- a/toolkit/devtools/server/actors/styleeditor.js
+++ b/toolkit/devtools/server/actors/styleeditor.js
@@ -40,22 +40,26 @@ types.addActorType("old-stylesheet");
  * stylesheets of a document.
  */
 let StyleEditorActor = exports.StyleEditorActor = protocol.ActorClass({
   typeName: "styleeditor",
 
   /**
    * The window we work with, taken from the parent actor.
    */
-  get window() this.parentActor.window,
+  get window() {
+    return this.parentActor.window;
+  },
 
   /**
    * The current content document of the window we work with.
    */
-  get document() this.window.document,
+  get document() {
+    return this.window.document;
+  },
 
   events: {
     "document-load" : {
       type: "documentLoad",
       styleSheets: Arg(0, "array:old-stylesheet")
     }
   },
 
@@ -287,27 +291,33 @@ let OldStyleSheetActor = protocol.ActorC
 
   toString: function() {
     return "[OldStyleSheetActor " + this.actorID + "]";
   },
 
   /**
    * Window of target
    */
-  get window() this._window || this.parentActor.window,
+  get window() {
+    return this._window || this.parentActor.window;
+  },
 
   /**
    * Document of target.
    */
-  get document() this.window.document,
+  get document() {
+    return this.window.document;
+  },
 
   /**
    * URL of underlying stylesheet.
    */
-  get href() this.rawSheet.href,
+  get href() {
+    return this.rawSheet.href;
+  },
 
   /**
    * Retrieve the index (order) of stylesheet in the document.
    *
    * @return number
    */
   get styleSheetIndex()
   {
@@ -613,23 +623,37 @@ var OldStyleSheetFront = protocol.FrontC
 
     return deferred.promise;
   },
 
   getOriginalSources: function() {
     return promise.resolve([]);
   },
 
-  get href() this._form.href,
-  get nodeHref() this._form.nodeHref,
-  get disabled() !!this._form.disabled,
-  get title() this._form.title,
-  get isSystem() this._form.system,
-  get styleSheetIndex() this._form.styleSheetIndex,
-  get ruleCount() this._form.ruleCount
+  get href() {
+    return this._form.href;
+  },
+  get nodeHref() {
+    return this._form.nodeHref;
+  },
+  get disabled() {
+    return !!this._form.disabled;
+  },
+  get title() {
+    return this._form.title;
+  },
+  get isSystem() {
+    return this._form.system;
+  },
+  get styleSheetIndex() {
+    return this._form.styleSheetIndex;
+  },
+  get ruleCount() {
+    return this._form.ruleCount;
+  }
 });
 
 XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
 
 exports.StyleEditorActor = StyleEditorActor;
 exports.StyleEditorFront = StyleEditorFront;
--- a/toolkit/devtools/server/actors/styles.js
+++ b/toolkit/devtools/server/actors/styles.js
@@ -132,17 +132,19 @@ var PageStyleActor = protocol.ActorClass
 
     // Stores the association of DOM objects -> actors
     this.refMap = new Map;
 
     this.onFrameUnload = this.onFrameUnload.bind(this);
     events.on(this.inspector.tabActor, "will-navigate", this.onFrameUnload);
   },
 
-  get conn() this.inspector.conn,
+  get conn() {
+    return this.inspector.conn;
+  },
 
   form: function(detail) {
     if (detail === "actorid") {
       return this.actorID;
     }
 
     return {
       actor: this.actorID,
@@ -969,40 +971,44 @@ var StyleRuleActor = protocol.ActorClass
         this.column = DOMUtils.getRuleColumn(this.rawRule);
       }
     } else {
       // Fake a rule
       this.type = ELEMENT_STYLE;
       this.rawNode = item;
       this.rawRule = {
         style: item.style,
-        toString: function() "[element rule " + this.style + "]"
+        toString: function() { return "[element rule " + this.style + "]"; }
       }
     }
   },
 
-  get conn() this.pageStyle.conn,
+  get conn() {
+    return this.pageStyle.conn;
+  },
 
   // Objects returned by this actor are owned by the PageStyleActor
   // to which this rule belongs.
-  get marshallPool() this.pageStyle,
+  get marshallPool() {
+    return this.pageStyle;
+  },
 
   getDocument: function(sheet) {
     let document;
 
     if (sheet.ownerNode instanceof Ci.nsIDOMHTMLDocument) {
       document = sheet.ownerNode;
     } else {
       document = sheet.ownerNode.ownerDocument;
     }
 
     return document;
   },
 
-  toString: function() "[StyleRuleActor for " + this.rawRule + "]",
+  toString: function() { return "[StyleRuleActor for " + this.rawRule + "]" },
 
   form: function(detail) {
     if (detail === "actorid") {
       return this.actorID;
     }
 
     let form = {
       actor: this.actorID,
@@ -1272,19 +1278,25 @@ var StyleRuleFront = protocol.FrontClass
 
   /**
    * Return a new RuleModificationList for this node.
    */
   startModifyingProperties: function() {
     return new RuleModificationList(this);
   },
 
-  get type() this._form.type,
-  get line() this._form.line || -1,
-  get column() this._form.column || -1,
+  get type() {
+    return this._form.type;
+  },
+  get line() {
+    return this._form.line || -1;
+  },
+  get column() {
+    return this._form.column || -1;
+  },
   get cssText() {
     return this._form.cssText;
   },
   get keyText() {
     return this._form.keyText;
   },
   get name() {
     return this._form.name;
--- a/toolkit/devtools/server/actors/stylesheets.js
+++ b/toolkit/devtools/server/actors/stylesheets.js
@@ -44,22 +44,26 @@ types.addActorType("originalsource");
  * stylesheets of a document.
  */
 let StyleSheetsActor = exports.StyleSheetsActor = protocol.ActorClass({
   typeName: "stylesheets",
 
   /**
    * The window we work with, taken from the parent actor.
    */
-  get window() this.parentActor.window,
+  get window() {
+    return this.parentActor.window;
+  },
 
   /**
    * The current content document of the window we work with.
    */
-  get document() this.window.document,
+  get document() {
+    return this.window.document;
+  },
 
   form: function()
   {
     return { actor: this.actorID };
   },
 
   initialize: function (conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, null);
@@ -264,19 +268,23 @@ let MediaRuleActor = protocol.ActorClass
 
   events: {
     "matches-change" : {
       type: "matchesChange",
       matches: Arg(0, "boolean"),
     }
   },
 
-  get window() this.parentActor.window,
+  get window() {
+    return this.parentActor.window;
+  },
 
-  get document() this.window.document,
+  get document() {
+    return this.window.document;
+  },
 
   get matches() {
     return this.mql ? this.mql.matches : null;
   },
 
   initialize: function(aMediaRule, aParentActor) {
     protocol.Actor.prototype.initialize.call(this, null);
 
@@ -350,21 +358,31 @@ let MediaRuleFront = protocol.FrontClass
     if (detail === "actorid") {
       this.actorID = form;
       return;
     }
     this.actorID = form.actor;
     this._form = form;
   },
 
-  get mediaText() this._form.mediaText,
-  get conditionText() this._form.conditionText,
-  get matches() this._form.matches,
-  get line() this._form.line || -1,
-  get column() this._form.column || -1,
+  get mediaText() {
+    return this._form.mediaText;
+  },
+  get conditionText() {
+    return this._form.conditionText;
+  },
+  get matches() {
+    return this._form.matches;
+  },
+  get line() {
+    return this._form.line || -1;
+  },
+  get column() {
+    return this._form.column || -1;
+  },
   get parentStyleSheet() {
     return this.conn.getActor(this._form.parentStyleSheet);
   }
 });
 
 /**
  * A StyleSheetActor represents a stylesheet on the server.
  */
@@ -391,29 +409,37 @@ let StyleSheetActor = protocol.ActorClas
 
   toString: function() {
     return "[StyleSheetActor " + this.actorID + "]";
   },
 
   /**
    * Window of target
    */
-  get window() this._window || this.parentActor.window,
+  get window() {
+    return this._window || this.parentActor.window;
+  },
 
   /**
    * Document of target.
    */
-  get document() this.window.document,
+  get document() {
+    return this.window.document;
+  },
 
-  get ownerNode() this.rawSheet.ownerNode,
+  get ownerNode() {
+    return this.rawSheet.ownerNode;
+  },
 
   /**
    * URL of underlying stylesheet.
    */
-  get href() this.rawSheet.href,
+  get href() {
+    return this.rawSheet.href;
+  },
 
   /**
    * Retrieve the index (order) of stylesheet in the document.
    *
    * @return number
    */
   get styleSheetIndex()
   {
@@ -988,23 +1014,37 @@ var StyleSheetFront = protocol.FrontClas
     if (detail === "actorid") {
       this.actorID = form;
       return;
     }
     this.actorID = form.actor;
     this._form = form;
   },
 
-  get href() this._form.href,
-  get nodeHref() this._form.nodeHref,
-  get disabled() !!this._form.disabled,
-  get title() this._form.title,
-  get isSystem() this._form.system,
-  get styleSheetIndex() this._form.styleSheetIndex,
-  get ruleCount() this._form.ruleCount
+  get href() {
+    return this._form.href;
+  },
+  get nodeHref() {
+    return this._form.nodeHref;
+  },
+  get disabled() {
+    return !!this._form.disabled;
+  },
+  get title() {
+    return this._form.title;
+  },
+  get isSystem() {
+    return this._form.system;
+  },
+  get styleSheetIndex() {
+    return this._form.styleSheetIndex;
+  },
+  get ruleCount() {
+    return this._form.ruleCount;
+  }
 });
 
 /**
  * Actor representing an original source of a style sheet that was specified
  * in a source map.
  */
 let OriginalSourceActor = protocol.ActorClass({
   typeName: "originalsource",
@@ -1075,18 +1115,22 @@ let OriginalSourceFront = protocol.Front
     if (detail === "actorid") {
       this.actorID = form;
       return;
     }
     this.actorID = form.actor;
     this._form = form;
   },
 
-  get href() this._form.url,
-  get url() this._form.url
+  get href() {
+    return this._form.url;
+  },
+  get url() {
+    return this._form.url;
+  }
 });
 
 
 XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
 
 exports.StyleSheetsActor = StyleSheetsActor;
--- a/toolkit/devtools/server/actors/utils/TabSources.js
+++ b/toolkit/devtools/server/actors/utils/TabSources.js
@@ -326,20 +326,19 @@ TabSources.prototype = {
   _createSourceMappedActors: function (aSource) {
     if (!this._useSourceMaps || !aSource.sourceMapURL) {
       return resolve(null);
     }