Merge m-c to fx-team. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 29 May 2015 17:02:15 -0400
changeset 246407 4c9fbec390f16673d0ef1e2f88d68aae8dc12133
parent 246406 7ee8b48771e2f74e16b820943ee4822b0c349378 (current diff)
parent 246371 45a4d6336c7326d3f7541c62b98437fd0fcb4245 (diff)
child 246408 027644ad089b185d69bc76aa7bcf66b7d43e5e6b
push id28829
push usercbook@mozilla.com
push dateMon, 01 Jun 2015 12:18:22 +0000
treeherdermozilla-central@2fa4bb097f03 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to fx-team. a=merge
--- 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/app/Makefile.in
+++ b/b2g/app/Makefile.in
@@ -1,25 +1,15 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 USE_RCS_MK := 1
 include $(topsrcdir)/config/makefiles/rcs.mk
 
-UA_UPDATE_FILE = ua-update.json
-
-$(UA_UPDATE_FILE): % : %.in
-	# strip out comment lines, which are not valid in JSON
-	sed -e '/^ *\/\//d' -e '/^ *$$/d' $^ > $@
-
-UA_UPDATE_FILES = $(UA_UPDATE_FILE)
-UA_UPDATE_DEST  = $(FINAL_TARGET)
-INSTALL_TARGETS += UA_UPDATE
-
 # Make sure the standalone glue doesn't try to get libxpcom.so from b2g/app.
 NSDISTMODE = copy
 
 include $(topsrcdir)/config/rules.mk
 
 APP_ICON = app
 
 ifeq ($(OS_ARCH),WINNT)
--- a/b2g/app/moz.build
+++ b/b2g/app/moz.build
@@ -76,8 +76,11 @@ if CONFIG['OS_ARCH'] == 'WINNT':
     ]
 
 FAIL_ON_WARNINGS = True
 
 JS_PREFERENCE_FILES += [
     'b2g.js',
 ]
 
+DIST_FILES += [
+  'ua-update.json.in',
+]
--- a/b2g/app/ua-update.json.in
+++ b/b2g/app/ua-update.json.in
@@ -1,12 +1,11 @@
-// Comments must be on their own lines and must start with "//"
-
-// Send these sites a custom user-agent. Bugs to remove each override after
-// evangelism are included.
+#filter slashslash
+// Everything after the first // on a line will be removed by the preproccesor.
+// Send these sites a custom user-agent. Bugs should be included with an entry.
 {
   // bug 826347, msn.com
   "msn.com": "Mozilla/5.0 (Linux; Android 4.0.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.133 Mobile Safari/535.19",
   // bug 826353, itau.com.br
   "itau.com.br": "\\(Mobile#(Android; Mobile",
   // bug 826510, r7.com
   "r7.com": "\\(Mobile#(Android; Mobile",
   // bug 826514, estadao.com.br
--- 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/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="a0d4c222c1f1a8d2954539dac1537a1166aab320"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6dc0f4c583407a4a52a66ce7cb11f058302a762"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="04c307a8a8439adf38c0f14d065275f571a7b486"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="a0d4c222c1f1a8d2954539dac1537a1166aab320"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6dc0f4c583407a4a52a66ce7cb11f058302a762"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="04c307a8a8439adf38c0f14d065275f571a7b486"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a0d4c222c1f1a8d2954539dac1537a1166aab320"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e6dc0f4c583407a4a52a66ce7cb11f058302a762"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="22664edc4c73e5fe8f5095ff1d5549db78a2bc10"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="218a5637399d023f4326e12c8a486dad95403b6c"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="a0d4c222c1f1a8d2954539dac1537a1166aab320"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6dc0f4c583407a4a52a66ce7cb11f058302a762"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="04c307a8a8439adf38c0f14d065275f571a7b486"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="a0d4c222c1f1a8d2954539dac1537a1166aab320"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6dc0f4c583407a4a52a66ce7cb11f058302a762"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="04c307a8a8439adf38c0f14d065275f571a7b486"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="a0d4c222c1f1a8d2954539dac1537a1166aab320"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6dc0f4c583407a4a52a66ce7cb11f058302a762"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="04c307a8a8439adf38c0f14d065275f571a7b486"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a0d4c222c1f1a8d2954539dac1537a1166aab320"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="e6dc0f4c583407a4a52a66ce7cb11f058302a762"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="22664edc4c73e5fe8f5095ff1d5549db78a2bc10"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="218a5637399d023f4326e12c8a486dad95403b6c"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="a0d4c222c1f1a8d2954539dac1537a1166aab320"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6dc0f4c583407a4a52a66ce7cb11f058302a762"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="04c307a8a8439adf38c0f14d065275f571a7b486"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "a0d4c222c1f1a8d2954539dac1537a1166aab320", 
+        "git_revision": "e6dc0f4c583407a4a52a66ce7cb11f058302a762", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "e67700d565f5f15c985a5e108622845bb23aec4b", 
+    "revision": "51be40eaad6a141d3741dbae10948968ebfc07a6", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="a0d4c222c1f1a8d2954539dac1537a1166aab320"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6dc0f4c583407a4a52a66ce7cb11f058302a762"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="04c307a8a8439adf38c0f14d065275f571a7b486"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="a0d4c222c1f1a8d2954539dac1537a1166aab320"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="e6dc0f4c583407a4a52a66ce7cb11f058302a762"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="fffc68521ebb1501d6b015c6d1c4a17a04fdb2e2"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="46da1a05ac04157669685246d70ac59d48699c9e"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="04c307a8a8439adf38c0f14d065275f571a7b486"/>
--- 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)
--- a/dom/secureelement/gonk/ACEService.js
+++ b/dom/secureelement/gonk/ACEService.js
@@ -4,16 +4,17 @@
 
 /* Copyright © 2015, Deutsche Telekom, Inc. */
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "DOMApplicationRegistry",
                                   "resource://gre/modules/Webapps.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SEUtils",
                                   "resource://gre/modules/SEUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "SE", function() {
@@ -115,16 +116,21 @@ function ACEService() {
     Cc["@mozilla.org/secureelement/access-control/rules-manager;1"]
       .createInstance(Ci.nsIAccessRulesManager));
 }
 
 ACEService.prototype = {
   _rulesManagers: null,
 
   isAccessAllowed: function isAccessAllowed(localId, seType, aid) {
+    if(!Services.prefs.getBoolPref("devtools.debugger.forbid-certified-apps")) {
+      debug("Certified apps debug enabled, allowing access");
+      return Promise.resolve(true);
+    }
+
     let manifestURL = DOMApplicationRegistry.getManifestURLByLocalId(localId);
     if (!manifestURL) {
       return Promise.reject(Error("Missing manifest for app: " + localId));
     }
 
     let rulesManager = this._rulesManagers.get(seType);
     if (!rulesManager) {
       debug("App " + manifestURL + " tried to access '" + seType + "' SE" +
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/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_debug.yml
+++ b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_debug.yml
@@ -1,12 +1,12 @@
 $inherits:
   from: 'tasks/builds/b2g_aries_spark_ota_base.yml'
   variables:
-    build_name: 'aries'
+    build_name: 'aries-ota'
     build_type: 'debug'
 task:
   metadata:
     name: '[TC] B2G Aries OTA (userdebug)'
   scopes:
     - 'docker-worker:cache:build-aries-spark-ota-debug'
   payload:
     cache:
--- a/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_opt.yml
+++ b/testing/taskcluster/tasks/builds/b2g_aries_spark_ota_opt.yml
@@ -1,12 +1,12 @@
 $inherits:
   from: 'tasks/builds/b2g_aries_spark_ota_base.yml'
   variables:
-    build_name: 'aries'
+    build_name: 'aries-ota'
     build_type: 'opt'
 task:
   metadata:
     name: '[TC] B2G Aries OTA (user)'
   scopes:
     - 'docker-worker:cache:build-aries-spark-ota-user'
   payload:
     cache:
--- a/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_debug.yml
+++ b/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_debug.yml
@@ -1,12 +1,12 @@
 $inherits:
   from: 'tasks/builds/b2g_flame_kk_ota_base.yml'
   variables:
-    build_name: 'flame-kk'
+    build_name: 'flame-kk-ota'
     build_type: 'debug'
 task:
   metadata:
     name: '[TC] B2G Flame KK OTA (userdebug)'
   scopes:
     - 'docker-worker:cache:build-flame-kk-ota-debug'
 
   payload:
--- a/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_opt.yml
+++ b/testing/taskcluster/tasks/builds/b2g_flame_kk_ota_opt.yml
@@ -1,12 +1,12 @@
 $inherits:
   from: 'tasks/builds/b2g_flame_kk_ota_base.yml'
   variables:
-    build_name: 'flame-kk'
+    build_name: 'flame-kk-ota'
     build_type: 'opt'
 task:
   metadata:
     name: '[TC] B2G Flame KK OTA (user)'
   scopes:
     - 'docker-worker:cache:build-flame-kk-ota-user'
 
   payload:
--- 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())
       };