Merge m-c to b2ginbound, a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 28 Jul 2015 18:03:56 -0700
changeset 286796 065076f45ee068376dc1f16a434dbf890a544109
parent 286795 0f6f6176462012ae4908306f1d15d0fa191808e6 (current diff)
parent 286691 2ee9895e032c492705adaf213706d4260ca172c8 (diff)
child 286797 23a59d4f3de80e8521ce0788548ae59f2ea08bd8
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.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 b2ginbound, a=merge
b2g/app/b2g.js
toolkit/devtools/DevToolsUtils.jsm
--- a/accessible/windows/msaa/Platform.cpp
+++ b/accessible/windows/msaa/Platform.cpp
@@ -44,16 +44,20 @@ a11y::ProxyCreated(ProxyAccessible* aPro
   aProxy->SetWrapper(reinterpret_cast<uintptr_t>(wrapper));
 }
 
 void
 a11y::ProxyDestroyed(ProxyAccessible* aProxy)
 {
   ProxyAccessibleWrap* wrapper =
     reinterpret_cast<ProxyAccessibleWrap*>(aProxy->GetWrapper());
+  MOZ_ASSERT(wrapper);
+  if (!wrapper)
+    return;
+
   wrapper->Shutdown();
   aProxy->SetWrapper(0);
   wrapper->Release();
 }
 
 void
 a11y::ProxyEvent(ProxyAccessible*, uint32_t)
 {
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -670,19 +670,16 @@ pref("dom.disable_window_showModalDialog
 // Enable new experimental html forms
 pref("dom.experimental_forms", true);
 pref("dom.forms.number", true);
 
 // Don't enable <input type=color> yet as we don't have a color picker
 // implemented for b2g (bug 875751)
 pref("dom.forms.color", false);
 
-// Turns on gralloc-based direct texturing for Gonk
-pref("gfx.gralloc.enabled", false);
-
 // This preference instructs the JS engine to discard the
 // source of any privileged JS after compilation. This saves
 // memory, but makes things like Function.prototype.toSource()
 // fail.
 pref("javascript.options.discardSystemSource", true);
 
 // XXXX REMOVE FOR PRODUCTION. Turns on GC and CC logging
 pref("javascript.options.mem.log", false);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -6691,16 +6691,20 @@ var gIdentityHandler = {
     delete this._overrideService;
     return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
                                      .getService(Ci.nsICertOverrideService);
   },
   get _identityIconCountryLabel () {
     delete this._identityIconCountryLabel;
     return this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
   },
+  get _identityIcons () {
+    delete this._identityIcons;
+    return this._identityIcons = document.getElementById("identity-icons");
+  },
   get _identityIcon () {
     delete this._identityIcon;
     return this._identityIcon = document.getElementById("page-proxy-favicon");
   },
   get _permissionsContainer () {
     delete this._permissionsContainer;
     return this._permissionsContainer = document.getElementById("identity-popup-permissions");
   },
@@ -6710,22 +6714,24 @@ var gIdentityHandler = {
   },
 
   /**
    * Rebuild cache of the elements that may or may not exist depending
    * on whether there's a location bar.
    */
   _cacheElements : function() {
     delete this._identityBox;
+    delete this._identityIcons;
     delete this._identityIconLabel;
     delete this._identityIconCountryLabel;
     delete this._identityIcon;
     delete this._permissionsContainer;
     delete this._permissionList;
     this._identityBox = document.getElementById("identity-box");
+    this._identityIcons = document.getElementById("identity-icons");
     this._identityIconLabel = document.getElementById("identity-icon-label");
     this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
     this._identityIcon = document.getElementById("page-proxy-favicon");
     this._permissionsContainer = document.getElementById("identity-popup-permissions");
     this._permissionList = document.getElementById("identity-popup-permission-list");
   },
 
   /**
@@ -7125,17 +7131,17 @@ var gIdentityHandler = {
     this.setPopupMessages(this._identityBox.className);
 
     this.updateSitePermissions();
 
     // Add the "open" attribute to the identity box for styling
     this._identityBox.setAttribute("open", "true");
 
     // Now open the popup, anchored off the primary chrome element
-    this._identityPopup.openPopup(this._identityIcon, "bottomcenter topleft");
+    this._identityPopup.openPopup(this._identityIcons, "bottomcenter topleft");
   },
 
   onPopupShown(event) {
     if (event.target == this._identityPopup) {
       window.addEventListener("focus", this, true);
     }
   },
 
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -758,21 +758,23 @@
                    code fires onmousedown, and hence eats our favicon drag events.
                    We only add the identity-box button to the tab order when the location bar
                    has focus, otherwise pressing F6 focuses it instead of the location bar -->
               <box id="identity-box" role="button"
                    align="center"
                    onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
                    onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
                    ondragstart="gIdentityHandler.onDragStart(event);">
-                <image id="tracking-protection-icon"/>
-                <image id="page-proxy-favicon"
-                       consumeanchor="identity-box"
-                       onclick="PageProxyClickHandler(event);"
-                       pageproxystate="invalid"/>
+                <hbox id="identity-icons"
+                      consumeanchor="identity-box">
+                  <image id="tracking-protection-icon"/>
+                  <image id="page-proxy-favicon"
+                         onclick="PageProxyClickHandler(event);"
+                         pageproxystate="invalid"/>
+                </hbox>
                 <hbox id="identity-icon-labels">
                   <label id="identity-icon-label" class="plain" flex="1"/>
                   <label id="identity-icon-country-label" class="plain"/>
                 </hbox>
               </box>
               <box id="urlbar-display-box" align="center">
                 <label class="urlbar-display urlbar-display-switchtab" value="&urlbar.switchToTab.label;"/>
               </box>
--- a/browser/components/loop/.eslintignore
+++ b/browser/components/loop/.eslintignore
@@ -6,16 +6,19 @@ modules/MozLoopWorker.js
 modules/MozLoopAPI.jsm
 # Libs we don't need to check
 content/libs
 content/shared/libs
 standalone/content/libs
 standalone/node_modules
 # Libs we don't need to check
 test/shared/vendor
+# Coverage files
+test/coverage
+test/node_modules
 # These are generated react files that we don't need to check
 content/js/contacts.js
 content/js/conversation.js
 content/js/conversationViews.js
 content/js/panel.js
 content/js/roomViews.js
 content/js/feedbackViews.js
 content/shared/js/textChatView.js
--- a/browser/components/loop/.gitignore
+++ b/browser/components/loop/.gitignore
@@ -1,1 +1,2 @@
 .module-cache
+test/coverage
--- a/browser/components/loop/README.txt
+++ b/browser/components/loop/README.txt
@@ -51,16 +51,27 @@ If you install eslint and the react plug
 
   npm install -g eslint
   npm install -g eslint-plugin-react
 
 You can also run it by hand in the browser/components/loop directory:
 
   eslint --ext .js --ext .jsx --ext .jsm .
 
+Test coverage
+=============
+Initial setup
+  cd test
+  npm install
+
+To run
+  npm run build-coverage
+
+It will create a `coverage` folder under test/
+
 Front-End Unit Tests
 ====================
 The unit tests for Loop reside in three directories:
 
 - test/desktop-local
 - test/shared
 - test/standalone
 
--- a/browser/components/loop/run-all-loop-tests.sh
+++ b/browser/components/loop/run-all-loop-tests.sh
@@ -2,31 +2,42 @@
 # Run from topsrcdir, no args
 
 if [ "$1" == "--help" ]; then
   echo "Usage: ./run-all-loop-tests.sh [options]"
   echo "    --skip-e10s  Skips the e10s tests"
   exit 0;
 fi
 
+# Causes script to abort immediately if error code is not checked.
 set -e
 
 # Main tests
 
 LOOPDIR=browser/components/loop
 ESLINT=standalone/node_modules/.bin/eslint
 if [ -x "${LOOPDIR}/${ESLINT}" ]; then
   echo 'running eslint; see http://eslint.org/docs/rules/ for error info'
   (cd ${LOOPDIR} && ./${ESLINT} --ext .js --ext .jsm --ext .jsx .)
   if [ $? != 0 ]; then
     exit 1;
   fi
   echo 'eslint run finished.'
 fi
 
+# Build tests coverage.
+MISSINGDEPSMSG="\nMake sure all dependencies are up to date by running
+'npm install' inside the 'browser/components/loop/test/' directory.\n"
+(
+cd ${LOOPDIR}/test
+if ! npm run-script build-coverage ; then
+  echo $MISSINGDEPSMSG && exit 1
+fi
+)
+
 ./mach xpcshell-test ${LOOPDIR}/
 ./mach marionette-test ${LOOPDIR}/manifest.ini
 
 # The browser_parsable_css.js can fail if we add some css that isn't parsable.
 #
 # The check to make sure that the media devices can be used in Loop without
 # prompting is in browser_devices_get_user_media_about_urls.js. It's possible
 # to mess this up with CSP handling, and probably other changes, too.
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -251,17 +251,18 @@ loop.standaloneRoomViews = (function(moz
       );
     }
   });
 
   var StandaloneRoomView = React.createClass({displayName: "StandaloneRoomView",
     mixins: [
       Backbone.Events,
       sharedMixins.MediaSetupMixin,
-      sharedMixins.RoomsAudioMixin
+      sharedMixins.RoomsAudioMixin,
+      sharedMixins.DocumentTitleMixin
     ],
 
     propTypes: {
       // We pass conversationStore here rather than use the mixin, to allow
       // easy configurability for the ui-showcase.
       activeRoomStore: React.PropTypes.oneOfType([
         React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
         React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
@@ -301,16 +302,24 @@ loop.standaloneRoomViews = (function(moz
     /**
      * Watches for when we transition to MEDIA_WAIT room state, so we can request
      * user media access.
      *
      * @param  {Object} nextProps (Unused)
      * @param  {Object} nextState Next state object.
      */
     componentWillUpdate: function(nextProps, nextState) {
+      if (this.state.roomState !== ROOM_STATES.READY &&
+          nextState.roomState === ROOM_STATES.READY) {
+        this.setTitle(mozL10n.get("standalone_title_with_room_name", {
+          roomName: nextState.roomName || this.state.roomName,
+          clientShortname: mozL10n.get("clientShortname2")
+        }));
+      }
+
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
         this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
           publisherConfig: this.getDefaultPublisherConfig({publishVideo: true})
         }));
       }
 
       // UX don't want to surface these errors (as they would imply the user
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -251,17 +251,18 @@ loop.standaloneRoomViews = (function(moz
       );
     }
   });
 
   var StandaloneRoomView = React.createClass({
     mixins: [
       Backbone.Events,
       sharedMixins.MediaSetupMixin,
-      sharedMixins.RoomsAudioMixin
+      sharedMixins.RoomsAudioMixin,
+      sharedMixins.DocumentTitleMixin
     ],
 
     propTypes: {
       // We pass conversationStore here rather than use the mixin, to allow
       // easy configurability for the ui-showcase.
       activeRoomStore: React.PropTypes.oneOfType([
         React.PropTypes.instanceOf(loop.store.ActiveRoomStore),
         React.PropTypes.instanceOf(loop.store.FxOSActiveRoomStore)
@@ -301,16 +302,24 @@ loop.standaloneRoomViews = (function(moz
     /**
      * Watches for when we transition to MEDIA_WAIT room state, so we can request
      * user media access.
      *
      * @param  {Object} nextProps (Unused)
      * @param  {Object} nextState Next state object.
      */
     componentWillUpdate: function(nextProps, nextState) {
+      if (this.state.roomState !== ROOM_STATES.READY &&
+          nextState.roomState === ROOM_STATES.READY) {
+        this.setTitle(mozL10n.get("standalone_title_with_room_name", {
+          roomName: nextState.roomName || this.state.roomName,
+          clientShortname: mozL10n.get("clientShortname2")
+        }));
+      }
+
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
         this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
           publisherConfig: this.getDefaultPublisherConfig({publishVideo: true})
         }));
       }
 
       // UX don't want to surface these errors (as they would imply the user
--- a/browser/components/loop/standalone/content/l10n/en-US/loop.properties
+++ b/browser/components/loop/standalone/content/l10n/en-US/loop.properties
@@ -110,16 +110,20 @@ rooms_unavailable_notification_message=S
 rooms_media_denied_message=We could not get access to your microphone or camera. Please reload the page to try again.
 room_information_failure_not_available=No information about this conversation is available. Please request a new link from the person who sent it to you.
 room_information_failure_unsupported_browser=Your browser cannot access any information about this conversation. Please make sure you're using the latest version.
 
 ## LOCALIZATION_NOTE(standalone_title_with_status): {{clientShortname}} will be
 ## replaced by the brand name and {{currentStatus}} will be replaced
 ## by the current call status (Connecting, Ringing, etc.)
 standalone_title_with_status={{clientShortname}} — {{currentStatus}}
+## LOCALIZATION_NOTE(standalone_title_with_room_name): {{roomName}} will be replaced
+## by the name of the conversation and {{clientShortname}} will be
+## replaced by the brand name.
+standalone_title_with_room_name={{roomName}} — {{clientShortname}}
 status_in_conversation=In conversation
 status_conversation_ended=Conversation ended
 status_error=Something went wrong
 support_link=Get Help
 
 # Text chat strings
 
 chat_textbox_placeholder=Type here…
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -73,38 +73,40 @@ describe("loop.conversation", function()
 
   afterEach(function() {
     loop.shared.mixins.setRootObject(window);
     delete navigator.mozLoop;
     sandbox.restore();
   });
 
   describe("#init", function() {
+    var OTRestore;
     beforeEach(function() {
       sandbox.stub(React, "render");
       sandbox.stub(document.mozL10n, "initialize");
 
       sandbox.stub(loop.shared.models.ConversationModel.prototype,
         "initialize");
 
       sandbox.stub(loop.Dispatcher.prototype, "dispatch");
 
       sandbox.stub(loop.shared.utils,
         "locationData").returns({
           hash: "#42",
           pathname: "/"
         });
 
+      OTRestore = window.OT;
       window.OT = {
         overrideGuidStorage: sinon.stub()
       };
     });
 
     afterEach(function() {
-      delete window.OT;
+      window.OT = OTRestore;
     });
 
     it("should initialize L10n", function() {
       loop.conversation.init();
 
       sinon.assert.calledOnce(document.mozL10n.initialize);
       sinon.assert.calledWithExactly(document.mozL10n.initialize,
         navigator.mozLoop);
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/karma.conf.base.js
@@ -0,0 +1,67 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env node */
+
+module.exports = function(config) {
+  "use strict";
+
+  return {
+
+    // Base path that will be used to resolve all patterns (eg. files, exclude).
+    basePath: "../../",
+
+    // List of files / patterns to load in the browser.
+    files: [],
+
+    // List of files to exclude.
+    exclude: [
+    ],
+
+    // Frameworks to use.
+    // Available frameworks: https://npmjs.org/browse/keyword/karma-adapter .
+    frameworks: ["mocha"],
+
+    // Test results reporter to use.
+    // Possible values: "dots", "progress".
+    // Available reporters: https://npmjs.org/browse/keyword/karma-reporter .
+    reporters: ["progress", "coverage"],
+
+    coverageReporter: {
+      type: "html",
+      dir: "test/coverage/"
+    },
+
+    // Web server port.
+    port: 9876,
+
+    // Enable / disable colors in the output (reporters and logs).
+    colors: true,
+
+    // Level of logging.
+    // Possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG.
+    logLevel: config.LOG_INFO,
+
+    // Enable / disable watching file and executing tests whenever any file changes.
+    autoWatch: false,
+
+    // Start these browsers
+    // Available browser launchers: https://npmjs.org/browse/keyword/karma-launcher .
+    browsers: ["Firefox"],
+
+    // Continuous Integration mode.
+    // If true, Karma captures browsers, runs the tests and exits.
+    singleRun: true,
+
+    // Capture console output.
+    client: {
+      captureConsole: true
+    },
+
+    plugins: [
+      "karma-coverage",
+      "karma-mocha",
+      "karma-firefox-launcher"
+    ]
+  };
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/karma.coverage.desktop.js
@@ -0,0 +1,61 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env node */
+
+module.exports = function(config) {
+  "use strict";
+
+  var baseConfig = require("./karma.conf.base.js")(config);
+
+  // List of files / patterns to load in the browser.
+  baseConfig.files = baseConfig.files.concat([
+    "content/libs/l10n.js",
+    "content/shared/libs/react-0.12.2.js",
+    "content/shared/libs/jquery-2.1.4.js",
+    "content/shared/libs/lodash-3.9.3.js",
+    "content/shared/libs/backbone-1.2.1.js",
+    "test/shared/vendor/*.js",
+    "test/karma/stubs.js", // Stub out DOM event listener due to races.
+    "content/shared/js/utils.js",
+    "content/shared/js/models.js",
+    "content/shared/js/mixins.js",
+    "content/shared/js/websocket.js",
+    "content/shared/js/actions.js",
+    "content/shared/js/otSdkDriver.js",
+    "content/shared/js/validate.js",
+    "content/shared/js/dispatcher.js",
+    "content/shared/js/store.js",
+    "content/shared/js/conversationStore.js",
+    "content/shared/js/roomStates.js",
+    "content/shared/js/fxOSActiveRoomStore.js",
+    "content/shared/js/activeRoomStore.js",
+    "content/shared/js/views.js",
+    "content/shared/js/textChatStore.js",
+    "content/shared/js/textChatView.js",
+    "content/js/feedbackViews.js",
+    "content/js/client.js",
+    "content/js/conversationAppStore.js",
+    "content/js/roomStore.js",
+    "content/js/roomViews.js",
+    "content/js/conversationViews.js",
+    "content/js/conversation.js",
+    "test/desktop-local/*.js"
+  ]);
+
+  // List of files to exclude.
+  baseConfig.exclude = baseConfig.exclude.concat([
+    "test/desktop-local/panel_test.js",
+    "test/desktop-local/contacts_test.js"
+  ]);
+
+  // Preprocess matching files before serving them to the browser.
+  // Available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor .
+  baseConfig.preprocessors = {
+    "content/js/*.js": ["coverage"]
+  };
+
+  baseConfig.coverageReporter.dir = "test/coverage/desktop";
+
+  config.set(baseConfig);
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/karma.coverage.shared_standalone.js
@@ -0,0 +1,59 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/* eslint-env node */
+
+module.exports = function(config) {
+  "use strict";
+
+  var baseConfig = require("./karma.conf.base.js")(config);
+
+  // List of files / patterns to load in the browser.
+  baseConfig.files = baseConfig.files.concat([
+    "standalone/content/libs/l10n-gaia-02ca67948fe8.js",
+    "content/shared/libs/jquery-2.1.4.js",
+    "content/shared/libs/lodash-3.9.3.js",
+    "content/shared/libs/backbone-1.2.1.js",
+    "content/shared/libs/react-0.12.2.js",
+    "content/shared/libs/sdk.js",
+    "test/shared/vendor/*.js",
+    "content/shared/js/utils.js",
+    "content/shared/js/store.js",
+    "content/shared/js/models.js",
+    "content/shared/js/mixins.js",
+    "content/shared/js/crypto.js",
+    "content/shared/js/websocket.js",
+    "content/shared/js/validate.js",
+    "content/shared/js/actions.js",
+    "content/shared/js/dispatcher.js",
+    "content/shared/js/otSdkDriver.js",
+    "content/shared/js/roomStates.js",
+    "content/shared/js/fxOSActiveRoomStore.js",
+    "content/shared/js/activeRoomStore.js",
+    "content/shared/js/conversationStore.js",
+    "content/shared/js/views.js",
+    "content/shared/js/textChatStore.js",
+    "content/shared/js/textChatView.js",
+    "standalone/content/js/multiplexGum.js",
+    "standalone/content/js/standaloneAppStore.js",
+    "standalone/content/js/standaloneClient.js",
+    "standalone/content/js/standaloneMozLoop.js",
+    "standalone/content/js/fxOSMarketplace.js",
+    "standalone/content/js/standaloneRoomViews.js",
+    "standalone/content/js/standaloneMetricsStore.js",
+    "standalone/content/js/webapp.js",
+    "test/shared/*.js",
+    "test/standalone/*.js"
+  ]);
+
+  // Preprocess matching files before serving them to the browser.
+  // Available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor .
+  baseConfig.preprocessors = {
+    "content/shared/js/*.js": ["coverage"],
+    "standalone/content/js/*.js": ["coverage"]
+  };
+
+  baseConfig.coverageReporter.dir = "test/coverage/shared_standalone";
+
+  config.set(baseConfig);
+};
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/karma/stubs.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Used for desktop coverage tests because triggering methods on
+// DOMContentLoaded proved to lead to race conditions.
+
+sinon.stub(document, "addEventListener");
+console.log("[stubs.js] addEventListener stubbed to prevent race conditions");
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/test/package.json
@@ -0,0 +1,21 @@
+{
+  "name": "FirefoxHello",
+  "version": "0.0.1",
+  "description": "Firefox Hello test coverage",
+  "main": "index.js",
+  "directories": {
+    "test": "test"
+  },
+  "devDependencies": {
+    "istanbul": "^0.3.17",
+    "karma": "^0.12.37",
+    "karma-coverage": "^0.4.2",
+    "karma-firefox-launcher": "^0.1.6",
+    "karma-mocha": "^0.2.0"
+  },
+  "scripts": {
+    "build-coverage-shared": "./node_modules/.bin/karma start karma/karma.coverage.shared_standalone.js",
+    "build-coverage-desktop": "./node_modules/.bin/karma start karma/karma.coverage.desktop.js",
+    "build-coverage": "npm run build-coverage-desktop && npm run build-coverage-shared"
+  }
+}
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -6,18 +6,18 @@ describe("loop.store.ActiveRoomStore", f
 
   var expect = chai.expect;
   var sharedActions = loop.shared.actions;
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
   var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
-  var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver;
-  var fakeMultiplexGum;
+  var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver, fakeMultiplexGum;
+  var standaloneMediaRestore;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     sandbox.useFakeTimers();
 
     dispatcher = new loop.Dispatcher();
     sandbox.stub(dispatcher, "dispatch");
     sandbox.stub(window, "close");
@@ -47,31 +47,33 @@ describe("loop.store.ActiveRoomStore", f
       forceDisconnectAll: sinon.stub().callsArg(0),
       retryPublishWithoutVideo: sinon.stub(),
       startScreenShare: sinon.stub(),
       switchAcquiredWindow: sinon.stub(),
       endScreenShare: sinon.stub().returns(true)
     };
 
     fakeMultiplexGum = {
-        reset: sandbox.spy()
+      reset: sandbox.spy()
     };
 
+    standaloneMediaRestore = loop.standaloneMedia;
     loop.standaloneMedia = {
       multiplexGum: fakeMultiplexGum
     };
 
     store = new loop.store.ActiveRoomStore(dispatcher, {
       mozLoop: fakeMozLoop,
       sdkDriver: fakeSdkDriver
     });
   });
 
   afterEach(function() {
     sandbox.restore();
+    loop.standaloneMedia = standaloneMediaRestore;
   });
 
   describe("#constructor", function() {
     it("should throw an error if mozLoop is missing", function() {
       expect(function() {
         new loop.store.ActiveRoomStore(dispatcher);
       }).to.Throw(/mozLoop/);
     });
--- a/browser/components/loop/test/shared/mixins_test.js
+++ b/browser/components/loop/test/shared/mixins_test.js
@@ -426,17 +426,18 @@ describe("loop.shared.mixins", function(
   describe("loop.shared.mixins.AudioMixin", function() {
     var view, fakeAudio, TestComp;
 
     beforeEach(function() {
       navigator.mozLoop = {
         doNotDisturb: true,
         getAudioBlob: sinon.spy(function(name, callback) {
           callback(null, new Blob([new ArrayBuffer(10)], {type: "audio/ogg"}));
-        })
+        }),
+        getLoopPref: sandbox.stub()
       };
 
       fakeAudio = {
         play: sinon.spy(),
         pause: sinon.spy(),
         removeAttribute: sinon.spy()
       };
       sandbox.stub(window, "Audio").returns(fakeAudio);
--- a/browser/components/loop/test/shared/validate_test.js
+++ b/browser/components/loop/test/shared/validate_test.js
@@ -12,16 +12,23 @@ describe("Validator", function() {
     return validator.validate.bind(validator, values);
   }
 
   // test types
   function X(){}
   function Y(){}
 
   describe("#validate", function() {
+    function mozRTCSessionDescription() {}
+    var mozRTC;
+
+    beforeEach(function() {
+      mozRTC = new mozRTCSessionDescription();
+    });
+
     it("should check for a single required dependency when no option passed",
       function() {
         expect(create({x: Number}, {}))
           .to.Throw(TypeError, /missing required x$/);
       });
 
     it("should check for a missing required dependency, undefined passed",
       function() {
@@ -62,17 +69,17 @@ describe("Validator", function() {
     });
 
     it("should check for a custom constructor dependency", function() {
       expect(create({foo: X}, {foo: null})).to.Throw(
         TypeError, /invalid dependency: foo; expected X, got null$/);
     });
 
     it("should check for a native constructor dependency", function() {
-      expect(create({foo: mozRTCSessionDescription}, {foo: "x"}))
+      expect(create({foo: mozRTC}, {foo: "x"}))
         .to.Throw(TypeError,
                   /invalid dependency: foo; expected mozRTCSessionDescription/);
     });
 
     it("should check for a null dependency", function() {
       expect(create({foo: null}, {foo: "x"})).to.Throw(
         TypeError, /invalid dependency: foo; expected null, got String$/);
     });
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js
+++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js
@@ -11,16 +11,17 @@ describe("loop.standaloneRoomViews", fun
   var ROOM_STATES = loop.store.ROOM_STATES;
   var FEEDBACK_STATES = loop.store.FEEDBACK_STATES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
 
   var sandbox, dispatcher, activeRoomStore, dispatch;
+  var fakeWindow;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     dispatcher = new loop.Dispatcher();
     dispatch = sandbox.stub(dispatcher, "dispatch");
     activeRoomStore = new loop.store.ActiveRoomStore(dispatcher, {
       mozLoop: {},
       sdkDriver: {}
@@ -29,22 +30,40 @@ describe("loop.standaloneRoomViews", fun
       sdkDriver: {}
     });
     loop.store.StoreMixin.register({
       activeRoomStore: activeRoomStore,
       textChatStore: textChatStore
     });
 
     sandbox.useFakeTimers();
+    fakeWindow = {
+      close: sandbox.stub(),
+      addEventListener: function() {},
+      document: { addEventListener: function(){} },
+      setTimeout: function(callback) { callback(); }
+    };
+    loop.shared.mixins.setRootObject(fakeWindow);
+
+
+    sandbox.stub(navigator.mozL10n, "get", function(key, args) {
+      switch(key) {
+        case "standalone_title_with_room_name":
+          return args.roomName + " — " + args.clientShortname;
+        default:
+          return key;
+      }
+    });
 
     // Prevents audio request errors in the test console.
     sandbox.useFakeXMLHttpRequest();
   });
 
   afterEach(function() {
+    loop.shared.mixins.setRootObject(window);
     sandbox.restore();
   });
 
   describe("StandaloneRoomHeader", function() {
     function mountTestComponent() {
       return TestUtils.renderIntoDocument(
         React.createElement(
           loop.standaloneRoomViews.StandaloneRoomHeader, {
@@ -78,16 +97,24 @@ describe("loop.standaloneRoomViews", fun
 
     function expectActionDispatched(view) {
       sinon.assert.calledOnce(dispatch);
       sinon.assert.calledWithExactly(dispatch,
         sinon.match.instanceOf(sharedActions.SetupStreamElements));
     }
 
     describe("#componentWillUpdate", function() {
+      it("should set document.title to roomName and brand name when the READY state is dispatched", function() {
+        activeRoomStore.setStoreState({roomName: "fakeName", roomState: ROOM_STATES.INIT});
+        var view = mountTestComponent();
+        activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
+
+        expect(fakeWindow.document.title).to.equal("fakeName — clientShortname2");
+      });
+
       it("should dispatch a `SetupStreamElements` action when the MEDIA_WAIT state " +
         "is entered", function() {
           activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
           var view = mountTestComponent();
 
           activeRoomStore.setStoreState({roomState: ROOM_STATES.MEDIA_WAIT});
 
           expectActionDispatched(view);
--- a/browser/components/loop/test/standalone/webapp_test.js
+++ b/browser/components/loop/test/standalone/webapp_test.js
@@ -42,21 +42,32 @@ describe("loop.webapp", function() {
     };
   });
 
   afterEach(function() {
     sandbox.restore();
   });
 
   describe("#init", function() {
+    var loopConfigRestore;
+
     beforeEach(function() {
       sandbox.stub(React, "render");
+      loopConfigRestore = loop.config;
+      loop.config = {
+        feedbackApiUrl: "http://fake.invalid",
+        serverUrl: "http://fake.invalid"
+      };
       sandbox.stub(loop.Dispatcher.prototype, "dispatch");
     });
 
+    afterEach(function() {
+      loop.config = loopConfigRestore;
+    });
+
     it("should create the WebappRootView", function() {
       loop.webapp.init();
 
       sinon.assert.calledOnce(React.render);
       sinon.assert.calledWith(React.render,
         sinon.match(function(value) {
           return TestUtils.isCompositeComponentElement(value,
             loop.webapp.WebappRootView);
--- a/browser/devtools/canvasdebugger/panel.js
+++ b/browser/devtools/canvasdebugger/panel.js
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const { CanvasFront } = require("devtools/server/actors/canvas");
-const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 function CanvasDebuggerPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
   this._destroyer = null;
 
   EventEmitter.decorate(this);
 };
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -97,36 +97,32 @@ Cu.import("resource://gre/modules/Servic
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
 Cu.import("resource:///modules/devtools/SimpleListWidget.jsm");
 Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
 Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
 Cu.import("resource:///modules/devtools/VariablesView.jsm");
 Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 
-const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
+const require = devtools.require;
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 const promise = require("devtools/toolkit/deprecated-sync-thenables");
 const Editor = require("devtools/sourceeditor/editor");
 const DebuggerEditor = require("devtools/sourceeditor/debugger.js");
 const {Tooltip} = require("devtools/shared/widgets/Tooltip");
 const FastListWidget = require("devtools/shared/widgets/FastListWidget");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Parser",
   "resource:///modules/devtools/Parser.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "devtools",
-  "resource://gre/modules/devtools/Loader.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "DevToolsUtils",
-  "resource://gre/modules/devtools/DevToolsUtils.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
   "resource://gre/modules/ShortcutUtils.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
   "@mozilla.org/widget/clipboardhelper;1", "nsIClipboardHelper");
 
 Object.defineProperty(this, "NetworkHelper", {
   get: function() {
--- a/browser/devtools/debugger/panel.js
+++ b/browser/devtools/debugger/panel.js
@@ -3,17 +3,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/. */
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 const EventEmitter = require("devtools/toolkit/event-emitter");
-const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 function DebuggerPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
   this._destroyer = null;
 
   this._view = this.panelWin.DebuggerView;
   this._controller = this.panelWin.DebuggerController;
--- a/browser/devtools/debugger/test/browser_dbg_source-maps-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_source-maps-04.js
@@ -5,17 +5,16 @@
  * Test that bogus source maps don't break debugging.
  */
 
 const TAB_URL = EXAMPLE_URL + "doc_minified_bogus_map.html";
 const JS_URL = EXAMPLE_URL + "code_math_bogus_map.js";
 
 // This test causes an error to be logged in the console, which appears in TBPL
 // logs, so we are disabling that here.
-let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 DevToolsUtils.reportingDisabled = true;
 
 let gPanel, gDebugger, gFrames, gSources, gPrefs;
 
 function test() {
   initDebugger(TAB_URL).then(([aTab,, aPanel]) => {
     gPanel = aPanel;
     gDebugger = gPanel.panelWin;
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -12,17 +12,17 @@ let { Services } = Cu.import("resource:/
 let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
 Services.prefs.setBoolPref("devtools.debugger.log", false);
 
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise: promise } = Cu.import("resource://gre/modules/devtools/deprecated-sync-thenables.js", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { require } = devtools;
-let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 let { BrowserToolboxProcess } = Cu.import("resource:///modules/devtools/ToolboxProcess.jsm", {});
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { DebuggerClient, ObjectClient } =
   Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {});
 let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
 let EventEmitter = require("devtools/toolkit/event-emitter");
 const { promiseInvoke } = require("devtools/async-utils");
 let TargetFactory = devtools.TargetFactory;
--- a/browser/devtools/framework/toolbox.js
+++ b/browser/devtools/framework/toolbox.js
@@ -1751,16 +1751,24 @@ Toolbox.prototype = {
       return promise.resolve();
     }
 
     let outstanding = () => {
       return Task.spawn(function*() {
         yield this.highlighterUtils.stopPicker();
         yield this._inspector.destroy();
         if (this._highlighter) {
+          // Note that if the toolbox is closed, this will work fine, but will fail
+          // in case the browser is closed and will trigger a noSuchActor message.
+          // We ignore the promise that |_hideBoxModel| returns, since we should still
+          // proceed with the rest of destruction if it fails.
+          // FF42+ now does the cleanup from the actor.
+          if (!this.highlighter.traits.autoHideOnDestroy) {
+            this.highlighterUtils.unhighlight();
+          }
           yield this._highlighter.destroy();
         }
         if (this._selection) {
           this._selection.destroy();
         }
 
         if (this.walker) {
           this.walker.off("highlighter-ready", this._highlighterReady);
--- a/browser/devtools/inspector/breadcrumbs.js
+++ b/browser/devtools/inspector/breadcrumbs.js
@@ -543,31 +543,33 @@ HTMLBreadcrumbs.prototype = {
    * probably visible. See LOW_PRIORITY_ELEMENTS.
    * @param {NodeFront} node The parent node.
    * @return {Promise} Resolves to the NodeFront.
    */
   getInterestingFirstNode: function(node) {
     let deferred = promise.defer();
 
     let fallback = null;
+    let lastNode = null;
 
     let moreChildren = () => {
       this.walker.children(node, {
-        start: fallback,
+        start: lastNode,
         maxNodes: 10,
         whatToShow: Ci.nsIDOMNodeFilter.SHOW_ELEMENT
       }).then(this.selectionGuard()).then(response => {
         for (let node of response.nodes) {
           if (!(node.tagName in LOW_PRIORITY_ELEMENTS)) {
             deferred.resolve(node);
             return;
           }
           if (!fallback) {
             fallback = node;
           }
+          lastNode = node;
         }
         if (response.hasLast) {
           deferred.resolve(fallback);
           return;
         }
         moreChildren();
       }).then(null, this.selectionGuardEnd);
     };
--- a/browser/devtools/inspector/test/browser_inspector_breadcrumbs.js
+++ b/browser/devtools/inspector/test/browser_inspector_breadcrumbs.js
@@ -8,16 +8,17 @@
 const TEST_URI = TEST_URL_ROOT + "doc_inspector_breadcrumbs.html";
 const NODES = [
   {selector: "#i1111", result: "i1 i11 i111 i1111"},
   {selector: "#i22", result: "i2 i22 i221"},
   {selector: "#i2111", result: "i2 i21 i211 i2111"},
   {selector: "#i21", result: "i2 i21 i211 i2111"},
   {selector: "#i22211", result: "i2 i22 i222 i2221 i22211"},
   {selector: "#i22", result: "i2 i22 i222 i2221 i22211"},
+  {selector: "#i3", result: "i3 i31"},
 ];
 
 add_task(function*() {
   let { inspector } = yield openInspectorForURL(TEST_URI);
   let container = inspector.panelDoc.getElementById("inspector-breadcrumbs");
 
   for (let node of NODES) {
     info("Testing node " + node.selector);
--- a/browser/devtools/inspector/test/doc_inspector_breadcrumbs.html
+++ b/browser/devtools/inspector/test/doc_inspector_breadcrumbs.html
@@ -37,11 +37,32 @@
         <div id="i222">
           <div id="i2221">
             <div id="i22211">
             </div>
           </div>
         </div>
       </div>
     </article>
+    <article id="i3">
+      <link id="i31" />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+      <link />
+    </article>
     <div id='pseudo-container'></div>
   </body>
 </html>
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -1461,21 +1461,16 @@ MarkupView.prototype = {
    */
   destroy: function() {
     if (this._destroyer) {
       return this._destroyer;
     }
 
     this._destroyer = promise.resolve();
 
-    // Note that if the toolbox is closed, this will work fine, but will fail
-    // in case the browser is closed and will trigger a noSuchActor message.
-    // We ignore the promise that |_hideBoxModel| returns, since we should still
-    // proceed with the rest of destruction if it fails.
-    this._hideBoxModel();
     this._clearBriefBoxModelTimer();
 
     this._elt.removeEventListener("click", this._onMouseClick, false);
 
     this._hoveredNode = null;
     this._inspector.toolbox.off("picker-node-hovered", this._onToolboxPickerHover);
 
     this.htmlEditor.destroy();
--- a/browser/devtools/netmonitor/panel.js
+++ b/browser/devtools/netmonitor/panel.js
@@ -3,17 +3,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/. */
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 const EventEmitter = require("devtools/toolkit/event-emitter");
-const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 function NetMonitorPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
   this._destroyer = null;
 
   this._view = this.panelWin.NetMonitorView;
   this._controller = this.panelWin.NetMonitorController;
--- a/browser/devtools/performance/test/head.js
+++ b/browser/devtools/performance/test/head.js
@@ -5,17 +5,17 @@
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 let { Preferences } = Cu.import("resource://gre/modules/Preferences.jsm", {});
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
-let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { console } = devtools.require("resource://gre/modules/devtools/Console.jsm");
 let { merge } = devtools.require("sdk/util/object");
 let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
 let { getPerformanceFront, PerformanceFront } = devtools.require("devtools/performance/front");
 let TargetFactory = devtools.TargetFactory;
 
 let mm = null;
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -45,28 +45,28 @@ const VARIABLES_VIEW_URL = "chrome://bro
 
 const require   = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 
 const Telemetry = require("devtools/shared/telemetry");
 const Editor    = require("devtools/sourceeditor/editor");
 const TargetFactory = require("devtools/framework/target").TargetFactory;
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const {DevToolsWorker} = require("devtools/toolkit/shared/worker");
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
 Cu.import("resource://gre/modules/jsdebugger.jsm");
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/reflect.jsm");
-Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "VariablesView",
   "resource:///modules/devtools/VariablesView.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "VariablesViewController",
   "resource:///modules/devtools/VariablesViewController.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "EnvironmentClient",
--- a/browser/devtools/scratchpad/test/head.js
+++ b/browser/devtools/scratchpad/test/head.js
@@ -4,19 +4,20 @@
 
 "use strict";
 
 const {NetUtil} = Cu.import("resource://gre/modules/NetUtil.jsm", {});
 const {FileUtils} = Cu.import("resource://gre/modules/FileUtils.jsm", {});
 const {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 const {console} = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
 const {require} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools;
-const {DevToolsUtils} = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
 
+
 let gScratchpadWindow; // Reference to the Scratchpad chrome window object
 
 DevToolsUtils.testing = true;
 SimpleTest.registerCleanupFunction(() => {
   DevToolsUtils.testing = false;
 });
 
 /**
--- a/browser/devtools/shadereditor/panel.js
+++ b/browser/devtools/shadereditor/panel.js
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const { Cc, Ci, Cu, Cr } = require("chrome");
 const promise = Cu.import("resource://gre/modules/Promise.jsm", {}).Promise;
 const EventEmitter = require("devtools/toolkit/event-emitter");
 const { WebGLFront } = require("devtools/server/actors/webgl");
-const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 function ShaderEditorPanel(iframeWindow, toolbox) {
   this.panelWin = iframeWindow;
   this._toolbox = toolbox;
   this._destroyer = null;
 
   EventEmitter.decorate(this);
 };
--- a/browser/devtools/shared/Parser.jsm
+++ b/browser/devtools/shared/Parser.jsm
@@ -4,17 +4,18 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-const { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
 
 XPCOMUtils.defineLazyModuleGetter(this,
   "Reflect", "resource://gre/modules/reflect.jsm");
 
 this.EXPORTED_SYMBOLS = ["Parser", "ParserHelpers", "SyntaxTreeVisitor"];
 
 /**
  * A JS parser using the reflection API.
--- a/browser/devtools/shared/SplitView.jsm
+++ b/browser/devtools/shared/SplitView.jsm
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["SplitView"];
 
 /* this must be kept in sync with CSS (ie. splitview.css) */
-const LANDSCAPE_MEDIA_QUERY = "(min-width: 551px)";
+const LANDSCAPE_MEDIA_QUERY = "(min-width: 701px)";
 
 let bindings = new WeakMap();
 
 /**
  * SplitView constructor
  *
  * Initialize the split view UI on an existing DOM element.
  *
--- a/browser/devtools/shared/splitview.css
+++ b/browser/devtools/shared/splitview.css
@@ -38,22 +38,18 @@ box,
 /* only the active details pane is shown */
 .splitview-side-details > * {
   display: none;
 }
 .splitview-side-details > .splitview-active {
   display: -moz-box;
 }
 
-.splitview-landscape-resizer {
-  cursor: ew-resize;
-}
-
 /* this is to keep in sync with SplitView.jsm's LANDSCAPE_MEDIA_QUERY */
-@media (min-width: 551px) {
+@media (min-width: 701px) {
   .splitview-root {
     -moz-box-orient: horizontal;
   }
   .splitview-controller {
     max-height: none;
   }
   .splitview-details {
     display: none;
@@ -74,26 +70,14 @@ ol.splitview-nav > li.splitview-filtered
 .splitview-nav + .splitview-nav.placeholder {
   display: none;
 }
 .splitview-nav.splitview-all-filtered ~ .splitview-nav.placeholder.all-filtered,
 .splitview-nav:empty ~ .splitview-nav.placeholder.empty {
   display: -moz-box;
 }
 
-.splitview-portrait-resizer {
-  display: none;
-}
-
 /* portrait mode */
-@media (max-width: 550px) {
-  .splitview-landscape-splitter {
-    display: none;
-  }
-
-  .splitview-portrait-resizer {
-    display: -moz-box;
-  }
-
+@media (max-width: 700px) {
   .splitview-controller {
     max-width: none;
   }
 }
--- a/browser/devtools/shared/widgets/VariablesView.jsm
+++ b/browser/devtools/shared/widgets/VariablesView.jsm
@@ -16,23 +16,21 @@ const PAGE_SIZE_SCROLL_HEIGHT_RATIO = 10
 const PAGE_SIZE_MAX_JUMPS = 30;
 const SEARCH_ACTION_MAX_DELAY = 300; // ms
 const ITEM_FLASH_DURATION = 300 // ms
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
-Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
 Cu.import("resource://gre/modules/Task.jsm");
 let {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {});
 
-XPCOMUtils.defineLazyModuleGetter(this, "devtools",
-  "resource://gre/modules/devtools/Loader.jsm");
-
 XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
   "resource://gre/modules/PluralForm.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
   "@mozilla.org/widget/clipboardhelper;1",
   "nsIClipboardHelper");
 
 Object.defineProperty(this, "WebConsoleUtils", {
--- a/browser/devtools/shared/widgets/ViewHelpers.jsm
+++ b/browser/devtools/shared/widgets/ViewHelpers.jsm
@@ -11,17 +11,18 @@ const Cu = Components.utils;
 
 const PANE_APPEARANCE_DELAY = 50;
 const PAGE_SIZE_ITEM_COUNT_RATIO = 5;
 const WIDGET_FOCUSABLE_NODES = new Set(["vbox", "hbox"]);
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
-Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
+const { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
 Cu.import("resource://gre/modules/devtools/event-emitter.js");
 
 this.EXPORTED_SYMBOLS = [
   "Heritage", "ViewHelpers", "WidgetMethods",
   "setNamedTimeout", "clearNamedTimeout",
   "setConditionalTimeout", "clearConditionalTimeout",
 ];
 
--- a/browser/devtools/styleeditor/styleeditor.xul
+++ b/browser/devtools/styleeditor/styleeditor.xul
@@ -86,17 +86,17 @@
     <xul:command id="cmd_find" oncommand="goDoCommand('cmd_find')"/>
     <xul:command id="cmd_findAgain" oncommand="goDoCommand('cmd_findAgain')"/>
   </xul:commandset>
 
   <xul:keyset id="sourceEditorKeys"/>
 
   <xul:stack id="style-editor-chrome" class="loading theme-body">
 
-    <xul:box class="splitview-root" context="sidebar-context">
+    <xul:box class="splitview-root devtools-responsive-container" context="sidebar-context">
       <xul:box class="splitview-controller">
         <xul:box class="splitview-main">
           <xul:toolbar class="devtools-toolbar">
              <xul:hbox class="devtools-toolbarbutton-group">
               <xul:toolbarbutton class="style-editor-newButton devtools-toolbarbutton"
                           accesskey="&newButton.accesskey;"
                           tooltiptext="&newButton.tooltip;"
                           label="&newButton.label;"/>
@@ -120,17 +120,17 @@
             <p><strong>&noStyleSheet.label;</strong></p>
             <p>&noStyleSheet-tip-start.label;
               <a href="#"
                 class="style-editor-newButton">&noStyleSheet-tip-action.label;</a>
               &noStyleSheet-tip-end.label;</p>
           </div>
         </xul:box> <!-- .splitview-nav-container -->
       </xul:box>   <!-- .splitview-controller -->
-      <xul:splitter class="devtools-side-splitter splitview-landscape-splitter devtools-invisible-splitter"/>
+      <xul:splitter class="devtools-side-splitter devtools-invisible-splitter"/>
       <xul:box class="splitview-side-details devtools-main-content"/>
 
       <div id="splitview-templates" hidden="true">
         <li id="splitview-tpl-summary-stylesheet" tabindex="0">
           <xul:label class="stylesheet-enabled" tabindex="0"
             tooltiptext="&visibilityToggle.tooltip;"
             accesskey="&saveButton.accesskey;"></xul:label>
           <hgroup class="stylesheet-info">
@@ -143,19 +143,16 @@
               <h3><xul:label class="stylesheet-saveButton"
                     tooltiptext="&saveButton.tooltip;"
                     accesskey="&saveButton.accesskey;">&saveButton.label;</xul:label></h3>
             </div>
           </hgroup>
         </li>
 
         <xul:box id="splitview-tpl-details-stylesheet" class="splitview-details">
-          <xul:resizer class="splitview-portrait-resizer"
-                      dir="bottom"
-                      element="splitview-resizer-target"/>
           <xul:hbox class="stylesheet-details-container">
             <xul:box class="stylesheet-editor-input textbox"
                      data-placeholder="&editorTextbox.placeholder;"/>
             <xul:splitter class="devtools-side-splitter"/>
             <xul:vbox class="stylesheet-sidebar theme-sidebar" hidden="true">
               <xul:toolbar class="devtools-toolbar">
                 &mediaRules.label;
               </xul:toolbar>
--- a/browser/devtools/webide/themes/panel-listing.css
+++ b/browser/devtools/webide/themes/panel-listing.css
@@ -9,17 +9,17 @@ html {
 }
 
 label,
 .panel-item,
 #project-panel-projects,
 #runtime-panel-projects {
   display: block;
   float: left;
-  width: auto;
+  width: 100%;
   text-align: left;
 }
 
 .project-image,
 .panel-item span {
   display: inline-block;
   float: left;
   line-height: 20px;
--- a/browser/themes/shared/controlcenter/panel.inc.css
+++ b/browser/themes/shared/controlcenter/panel.inc.css
@@ -60,17 +60,17 @@
 }
 
 #identity-popup-multiView > .panel-viewcontainer > .panel-viewstack > .panel-subviews:-moz-locale-dir(rtl) {
   border-bottom-right-radius: 0;
   border-bottom-left-radius: 3.5px;
 }
 
 .identity-popup-section:not(:first-child) {
-  border-top: 1px solid rgb(229,229,229);
+  border-top: 1px solid ThreeDShadow;
 }
 
 #identity-popup-securityView,
 #identity-popup-security-content,
 #identity-popup-permissions-content,
 #tracking-protection-content {
   padding: 0.75em 0 1em;
   -moz-padding-start: calc(2em + 24px);
@@ -121,17 +121,17 @@
     background-image: url("chrome://browser/skin/customizableui/subView-arrow-back-inverted@2x.png"),
                       linear-gradient(rgba(255,255,255,0.3), transparent);
   }
 }
 
 .identity-popup-expander > .button-box {
   padding: 0;
   -moz-appearance: none;
-  border: solid #e5e5e5;
+  border: solid ThreeDShadow;
   border-width: 0 0 0 1px;
 }
 
 .identity-popup-expander:-moz-focusring > .button-box,
 .identity-popup-expander[panel-multiview-anchor] > .button-box {
   border: 0 none;
 }
 
@@ -204,27 +204,27 @@
 }
 
 #identity-popup-securityView.mixedActiveContent,
 #identity-popup-security-content.mixedActiveContent {
   background-image: url(chrome://browser/skin/controlcenter/mcb-disabled.svg);
 }
 
 #identity-popup-securityView-header {
-  border-bottom: 1px solid #e5e5e5;
+  border-bottom: 1px solid ThreeDShadow;
   padding-bottom: 1em;
   margin-bottom: 1em;
 }
 
 #identity-popup-content-owner {
   font-weight: 700;
 }
 
 #identity-popup-content-verifier {
-  color: #636363;
+  color: Graytext;
 }
 
 #identity-popup-content-owner,
 #identity-popup-securityView > #identity-popup-securityView-connection.identity-popup-text {
   margin-top: 1em;
 }
 
 /* TRACKING PROTECTION */
@@ -272,17 +272,17 @@
 /* FOOTER BUTTONS */
 
 #identity-popup-button-container {
   background-color: hsla(210,4%,10%,.07);
 }
 
 #identity-popup-more-info-button {
   border: none;
-  border-top: 1px solid hsla(210,4%,10%,.14);
+  border-top: 1px solid ThreeDShadow;
   background: transparent;
   -moz-appearance: none;
   margin-top: 5px;
   margin: 0;
 }
 
 #identity-popup-more-info-button > .button-box {
   -moz-appearance: none;
--- a/browser/themes/shared/devtools/splitview.css
+++ b/browser/themes/shared/devtools/splitview.css
@@ -135,25 +135,8 @@
 
 .splitview-main > .devtools-toolbarbutton {
   font-size: 11px;
   padding: 0 8px;
   width: auto;
   min-width: 48px;
   min-height: 0;
 }
-
-
-/* Resizers */
-
-.splitview-portrait-resizer {
-  -moz-appearance: none;
-  background: linear-gradient(black 1px, rgba(255,255,255,0.2) 1px),
-              linear-gradient(hsl(210,11%,36%), hsl(210,11%,18%));
-  height: 12px;
-  background-size: 10px 2px, 100% 12px;
-  background-clip: content-box, border-box;
-  background-repeat: repeat-y, no-repeat;
-  background-position: center center;
-  padding: 2px 0;
-  border-top: 1px solid hsla(210,8%,5%,.5);
-  border-bottom: 1px solid hsla(210,8%,5%,.5);
-}
--- a/browser/themes/shared/identity-block/identity-block.inc.css
+++ b/browser/themes/shared/identity-block/identity-block.inc.css
@@ -106,50 +106,54 @@
 #tracking-protection-icon:not([state]) {
   margin-left: -16px;
   pointer-events: none;
   opacity: 0;
   /* Only animate the shield in, when it disappears hide it immediately. */
   transition: none;
 }
 
+#urlbar[pageproxystate="invalid"] > #identity-box > #identity-icons > #tracking-protection-icon {
+  visibility: collapse;
+}
+
 /* MAIN IDENTITY ICON */
 
 #page-proxy-favicon {
   width: 16px;
   height: 16px;
   list-style-image: url(chrome://browser/skin/identity-not-secure.svg);
 }
 
-.chromeUI > #page-proxy-favicon[pageproxystate="valid"] {
+.chromeUI > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://branding/content/identity-icons-brand.svg);
 }
 
-.verifiedDomain > #page-proxy-favicon[pageproxystate="valid"],
-.verifiedIdentity > #page-proxy-favicon[pageproxystate="valid"] {
+.verifiedDomain > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
+.verifiedIdentity > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://browser/skin/identity-secure.svg);
 }
 
-.mixedActiveContent > #page-proxy-favicon[pageproxystate="valid"] {
+.mixedActiveContent > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://browser/skin/identity-mixed-active-loaded.svg);
 }
 
-.weakCipher > #page-proxy-favicon[pageproxystate="valid"],
-.mixedDisplayContent > #page-proxy-favicon[pageproxystate="valid"],
-.mixedDisplayContentLoadedActiveBlocked > #page-proxy-favicon[pageproxystate="valid"] {
+.weakCipher > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
+.mixedDisplayContent > #identity-icons > #page-proxy-favicon[pageproxystate="valid"],
+.mixedDisplayContentLoadedActiveBlocked > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://browser/skin/identity-mixed-passive-loaded.svg);
 }
 
-.mixedActiveBlocked > #page-proxy-favicon[pageproxystate="valid"] {
+.mixedActiveBlocked > #identity-icons > #page-proxy-favicon[pageproxystate="valid"] {
   list-style-image: url(chrome://browser/skin/identity-mixed-active-blocked.svg);
 }
 
 #page-proxy-favicon[pageproxystate="invalid"] {
   opacity: 0.3;
 }
 
-#urlbar[actiontype="searchengine"] > #identity-box > #page-proxy-favicon {
+#urlbar[actiontype="searchengine"] > #identity-box > #identity-icons > #page-proxy-favicon {
   -moz-image-region: inherit;
   list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
   width: 16px;
   height: 16px;
   opacity: 1;
 }
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1216,16 +1216,17 @@ toolbarbutton[constrain-size="true"][cui
     border-radius: 2px;
   }
 }
 
 @media (-moz-windows-default-theme) {
   #urlbar,
   .searchbar-textbox {
     @navbarTextboxCustomBorder@
+    border-radius: 1px;
   }
 
   @media (-moz-os-version: windows-vista),
          (-moz-os-version: windows-win7),
          (-moz-os-version: windows-win8) {
     #urlbar:not(:-moz-lwtheme),
     .searchbar-textbox:not(:-moz-lwtheme) {
       border-color: hsla(210,54%,20%,.25) hsla(210,54%,20%,.27) hsla(210,54%,20%,.3);
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -803,8 +803,9 @@ skip-if = buildapp == 'mulet' || buildap
 [test_integer_attr_with_leading_zero.html]
 [test_getAttribute_after_createAttribute.html]
 [test_script_loader_crossorigin_data_url.html]
 [test_file_negative_date.html]
 [test_nonascii_blob_url.html]
 [test_window_element_enumeration.html]
 [test_referrer_redirect.html]
 [test_postMessages.html]
+[test_window_proto.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_window_proto.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for ...</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+  assert_throws(new TypeError, function() {
+    Object.setPrototypeOf(window, Object.create(window));
+  }, "Setting prototype via setPrototypeOf");
+
+  assert_throws(new TypeError, function() {
+    window.__proto__ = Object.create(window);
+  }, "Setting prototype via __proto__");
+}, "Setting the prototype of a window to something that has the window on its proto chain should throw");
+</script>
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -14300,16 +14300,19 @@ class CallbackMember(CGNativeMember):
         else:
             isCallbackReturnValue = "Callback"
         sourceDescription = "return value of %s" % self.getPrettyName()
         convertType = instantiateJSToNativeConversion(
             getJSToNativeConversionInfo(self.retvalType,
                                         self.descriptorProvider,
                                         exceptionCode=self.exceptionCode,
                                         isCallbackReturnValue=isCallbackReturnValue,
+                                        # Allow returning a callback type that
+                                        # allows non-callable objects.
+                                        allowTreatNonCallableAsNull=True,
                                         sourceDescription=sourceDescription),
             replacements)
         assignRetval = string.Template(
             self.getRetvalInfo(self.retvalType,
                                False)[2]).substitute(replacements)
         type = convertType.define()
         return type + assignRetval
 
--- a/dom/bindings/test/TestInterfaceJS.js
+++ b/dom/bindings/test/TestInterfaceJS.js
@@ -151,11 +151,18 @@ TestInterfaceJS.prototype = {
       throw new this._win.DOMException("We are a fourth DOMException",
                                        "TypeMismatchError");
     }
     return new this._win.Promise(function(resolve) {
       resolve(thenable)
     });
   },
 
+  get onsomething() {
+    return this.__DOM_IMPL__.getEventHandler("onsomething");
+  },
+
+  set onsomething(val) {
+    this.__DOM_IMPL__.setEventHandler("onsomething", val);
+  }
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestInterfaceJS])
--- a/dom/bindings/test/mochitest.ini
+++ b/dom/bindings/test/mochitest.ini
@@ -59,9 +59,11 @@ skip-if = debug == false
 [test_exception_options_from_jsimplemented.html]
 skip-if = debug == false
 [test_promise_rejections_from_jsimplemented.html]
 skip-if = debug == false
 [test_worker_UnwrapArg.html]
 [test_unforgeablesonexpando.html]
 [test_crossOriginWindowSymbolAccess.html]
 [test_bug1123516_maplikesetlike.html]
-skip-if = debug == false
\ No newline at end of file
+skip-if = debug == false
+[test_jsimplemented_eventhandler.html]
+skip-if = debug == false
new file mode 100644
--- /dev/null
+++ b/dom/bindings/test/test_jsimplemented_eventhandler.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1186696
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1186696</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 1186696 **/
+  SimpleTest.waitForExplicitFinish();
+
+  function doTest() {
+    var values = [ function() {}, 5, null, undefined, "some string", {} ];
+
+    while (values.length != 0) {
+      var value = values.pop();
+      var t = new TestInterfaceJS();
+      t.onsomething = value;
+      var gottenValue = t.onsomething;
+      if (typeof value == "object" || typeof value == "function") {
+        is(gottenValue, value, "Should get back the object-or-null we put in");
+      } else {
+        is(gottenValue, null, "Should get back null");
+      }
+    }
+
+    SimpleTest.finish();
+  }
+
+  SpecialPowers.pushPrefEnv({set: [['dom.expose_test_interfaces', true]]},
+                            doTest);
+  </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1186696">Mozilla Bug 1186696</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/dom/media/AudioSink.cpp
+++ b/dom/media/AudioSink.cpp
@@ -18,17 +18,17 @@ extern PRLogModuleInfo* gMediaDecoderLog
 #define SINK_LOG(msg, ...) \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
 #define SINK_LOG_V(msg, ...) \
   MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, ("AudioSink=%p " msg, this, ##__VA_ARGS__))
 
 // The amount of audio frames that is used to fuzz rounding errors.
 static const int64_t AUDIO_FUZZ_FRAMES = 1;
 
-AudioSink::AudioSink(MediaQueue<AudioData>& aAudioQueue,
+AudioSink::AudioSink(MediaQueue<MediaData>& aAudioQueue,
                      int64_t aStartTime,
                      const AudioInfo& aInfo,
                      dom::AudioChannel aChannel)
   : mAudioQueue(aAudioQueue)
   , mMonitor("AudioSink::mMonitor")
   , mState(AUDIOSINK_STATE_INIT)
   , mAudioLoopScheduled(false)
   , mStartTime(aStartTime)
@@ -412,17 +412,18 @@ AudioSink::PlaySilence(uint32_t aFrames)
   return frames;
 }
 
 uint32_t
 AudioSink::PlayFromAudioQueue()
 {
   AssertOnAudioThread();
   NS_ASSERTION(!mAudioStream->IsPaused(), "Don't play when paused");
-  nsRefPtr<AudioData> audio(AudioQueue().PopFront());
+  nsRefPtr<AudioData> audio =
+    dont_AddRef(AudioQueue().PopFront().take()->As<AudioData>());
 
   SINK_LOG_V("playing %u frames of audio at time %lld",
              audio->mFrames, audio->mTime);
   if (audio->mRate == mInfo.mRate && audio->mChannels == mInfo.mChannels) {
     mAudioStream->Write(audio->mAudioData, audio->mFrames);
   } else {
     SINK_LOG_V("mismatched sample format mInfo=[%uHz/%u channels] audio=[%uHz/%u channels]",
                mInfo.mRate, mInfo.mChannels, audio->mRate, audio->mChannels);
--- a/dom/media/AudioSink.h
+++ b/dom/media/AudioSink.h
@@ -21,17 +21,17 @@ namespace mozilla {
 class AudioData;
 class AudioStream;
 template <class T> class MediaQueue;
 
 class AudioSink {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AudioSink)
 
-  AudioSink(MediaQueue<AudioData>& aAudioQueue,
+  AudioSink(MediaQueue<MediaData>& aAudioQueue,
             int64_t aStartTime,
             const AudioInfo& aInfo,
             dom::AudioChannel aChannel);
 
   // Return a promise which will be resolved when AudioSink finishes playing,
   // or rejected if any error.
   nsRefPtr<GenericPromise> Init();
 
@@ -116,32 +116,32 @@ private:
 
   void UpdateStreamSettings();
 
   // If we have already written enough frames to the AudioStream, start the
   // playback.
   void StartAudioStreamPlaybackIfNeeded();
   void WriteSilence(uint32_t aFrames);
 
-  MediaQueue<AudioData>& AudioQueue() const {
+  MediaQueue<MediaData>& AudioQueue() const {
     return mAudioQueue;
   }
 
   ReentrantMonitor& GetReentrantMonitor() const {
     return mMonitor;
   }
 
   void AssertCurrentThreadInMonitor() const {
     GetReentrantMonitor().AssertCurrentThreadIn();
   }
 
   void AssertOnAudioThread();
   void AssertNotOnAudioThread();
 
-  MediaQueue<AudioData>& mAudioQueue;
+  MediaQueue<MediaData>& mAudioQueue;
   mutable ReentrantMonitor mMonitor;
 
   // There members are accessed on the audio thread only.
   State mState;
   Maybe<State> mPendingState;
   bool mAudioLoopScheduled;
 
   // Thread for pushing audio onto the audio hardware.
--- a/dom/media/DecodedStream.cpp
+++ b/dom/media/DecodedStream.cpp
@@ -179,18 +179,18 @@ OutputStreamData::~OutputStreamData()
 void
 OutputStreamData::Init(DecodedStream* aDecodedStream, ProcessedMediaStream* aStream)
 {
   mStream = aStream;
   mListener = new OutputStreamListener(aDecodedStream, aStream);
   aStream->AddListener(mListener);
 }
 
-DecodedStream::DecodedStream(MediaQueue<AudioData>& aAudioQueue,
-                             MediaQueue<VideoData>& aVideoQueue)
+DecodedStream::DecodedStream(MediaQueue<MediaData>& aAudioQueue,
+                             MediaQueue<MediaData>& aVideoQueue)
   : mMonitor("DecodedStream::mMonitor")
   , mPlaying(false)
   , mAudioQueue(aAudioQueue)
   , mVideoQueue(aVideoQueue)
 {
   //
 }
 
@@ -397,73 +397,75 @@ DecodedStream::InitTracks()
   }
 
   sourceStream->FinishAddTracks();
   mData->mStreamInitialized = true;
 }
 
 static void
 SendStreamAudio(DecodedStreamData* aStream, int64_t aStartTime,
-                AudioData* aAudio, AudioSegment* aOutput,
+                MediaData* aData, AudioSegment* aOutput,
                 uint32_t aRate, double aVolume)
 {
+  MOZ_ASSERT(aData);
+  AudioData* audio = aData->As<AudioData>();
   // This logic has to mimic AudioSink closely to make sure we write
   // the exact same silences
   CheckedInt64 audioWrittenOffset = aStream->mAudioFramesWritten +
                                     UsecsToFrames(aStartTime, aRate);
-  CheckedInt64 frameOffset = UsecsToFrames(aAudio->mTime, aRate);
+  CheckedInt64 frameOffset = UsecsToFrames(audio->mTime, aRate);
 
   if (!audioWrittenOffset.isValid() ||
       !frameOffset.isValid() ||
       // ignore packet that we've already processed
-      frameOffset.value() + aAudio->mFrames <= audioWrittenOffset.value()) {
+      frameOffset.value() + audio->mFrames <= audioWrittenOffset.value()) {
     return;
   }
 
   if (audioWrittenOffset.value() < frameOffset.value()) {
     int64_t silentFrames = frameOffset.value() - audioWrittenOffset.value();
     // Write silence to catch up
     AudioSegment silence;
     silence.InsertNullDataAtStart(silentFrames);
     aStream->mAudioFramesWritten += silentFrames;
     audioWrittenOffset += silentFrames;
     aOutput->AppendFrom(&silence);
   }
 
   MOZ_ASSERT(audioWrittenOffset.value() >= frameOffset.value());
 
   int64_t offset = audioWrittenOffset.value() - frameOffset.value();
-  size_t framesToWrite = aAudio->mFrames - offset;
+  size_t framesToWrite = audio->mFrames - offset;
 
-  aAudio->EnsureAudioBuffer();
-  nsRefPtr<SharedBuffer> buffer = aAudio->mAudioBuffer;
+  audio->EnsureAudioBuffer();
+  nsRefPtr<SharedBuffer> buffer = audio->mAudioBuffer;
   AudioDataValue* bufferData = static_cast<AudioDataValue*>(buffer->Data());
   nsAutoTArray<const AudioDataValue*, 2> channels;
-  for (uint32_t i = 0; i < aAudio->mChannels; ++i) {
-    channels.AppendElement(bufferData + i * aAudio->mFrames + offset);
+  for (uint32_t i = 0; i < audio->mChannels; ++i) {
+    channels.AppendElement(bufferData + i * audio->mFrames + offset);
   }
   aOutput->AppendFrames(buffer.forget(), channels, framesToWrite);
   aStream->mAudioFramesWritten += framesToWrite;
   aOutput->ApplyVolume(aVolume);
 
-  aStream->mNextAudioTime = aAudio->GetEndTime();
+  aStream->mNextAudioTime = audio->GetEndTime();
 }
 
 void
 DecodedStream::SendAudio(double aVolume, bool aIsSameOrigin)
 {
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   if (!mInfo.HasAudio()) {
     return;
   }
 
   AudioSegment output;
   uint32_t rate = mInfo.mAudio.mRate;
-  nsAutoTArray<nsRefPtr<AudioData>,10> audio;
+  nsAutoTArray<nsRefPtr<MediaData>,10> audio;
   TrackID audioTrackId = mInfo.mAudio.mTrackId;
   SourceMediaStream* sourceStream = mData->mStream;
 
   // It's OK to hold references to the AudioData because AudioData
   // is ref-counted.
   mAudioQueue.GetElementsAfter(mData->mNextAudioTime, &audio);
   for (uint32_t i = 0; i < audio.Length(); ++i) {
     SendStreamAudio(mData.get(), mStartTime.ref(), audio[i], &output, rate, aVolume);
@@ -518,25 +520,25 @@ DecodedStream::SendVideo(bool aIsSameOri
   GetReentrantMonitor().AssertCurrentThreadIn();
 
   if (!mInfo.HasVideo()) {
     return;
   }
 
   VideoSegment output;
   TrackID videoTrackId = mInfo.mVideo.mTrackId;
-  nsAutoTArray<nsRefPtr<VideoData>, 10> video;
+  nsAutoTArray<nsRefPtr<MediaData>, 10> video;
   SourceMediaStream* sourceStream = mData->mStream;
 
   // It's OK to hold references to the VideoData because VideoData
   // is ref-counted.
   mVideoQueue.GetElementsAfter(mData->mNextVideoTime, &video);
 
   for (uint32_t i = 0; i < video.Length(); ++i) {
-    VideoData* v = video[i];
+    VideoData* v = video[i]->As<VideoData>();
 
     if (mData->mNextVideoTime < v->mTime) {
       // Write last video frame to catch up. mLastVideoImage can be null here
       // which is fine, it just means there's no video.
 
       // TODO: |mLastVideoImage| should come from the last image rendered
       // by the state machine. This will avoid the black frame when capture
       // happens in the middle of playback (especially in th middle of a
--- a/dom/media/DecodedStream.h
+++ b/dom/media/DecodedStream.h
@@ -14,18 +14,17 @@
 #include "mozilla/UniquePtr.h"
 #include "mozilla/gfx/Point.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/Maybe.h"
 
 namespace mozilla {
 
-class AudioData;
-class VideoData;
+class MediaData;
 class AudioSegment;
 class MediaStream;
 class MediaInputPort;
 class SourceMediaStream;
 class ProcessedMediaStream;
 class DecodedStream;
 class DecodedStreamGraphListener;
 class OutputStreamListener;
@@ -92,18 +91,18 @@ public:
   // mPort connects DecodedStreamData::mStream to our mStream.
   nsRefPtr<MediaInputPort> mPort;
   nsRefPtr<OutputStreamListener> mListener;
 };
 
 class DecodedStream {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStream);
 public:
-  DecodedStream(MediaQueue<AudioData>& aAudioQueue,
-                MediaQueue<VideoData>& aVideoQueue);
+  DecodedStream(MediaQueue<MediaData>& aAudioQueue,
+                MediaQueue<MediaData>& aVideoQueue);
 
   // Mimic MDSM::StartAudioThread.
   // Must be called before any calls to SendData().
   void StartPlayback(int64_t aStartTime, const MediaInfo& aInfo);
   // Mimic MDSM::StopAudioThread.
   void StopPlayback();
 
   void DestroyData();
@@ -144,15 +143,15 @@ private:
   // Please move all capture-stream related code from MDSM into DecodedStream
   // and apply "dispatch + mirroring" to get rid of this monitor in the future.
   mutable ReentrantMonitor mMonitor;
 
   bool mPlaying;
   Maybe<int64_t> mStartTime;
   MediaInfo mInfo;
 
-  MediaQueue<AudioData>& mAudioQueue;
-  MediaQueue<VideoData>& mVideoQueue;
+  MediaQueue<MediaData>& mAudioQueue;
+  MediaQueue<MediaData>& mVideoQueue;
 };
 
 } // namespace mozilla
 
 #endif // DecodedStream_h_
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -110,17 +110,17 @@ IsInEmulator()
 
 VideoData::VideoData(int64_t aOffset,
                      int64_t aTime,
                      int64_t aDuration,
                      bool aKeyframe,
                      int64_t aTimecode,
                      IntSize aDisplay,
                      layers::ImageContainer::FrameID aFrameID)
-  : MediaData(VIDEO_DATA, aOffset, aTime, aDuration)
+  : MediaData(VIDEO_DATA, aOffset, aTime, aDuration, 1)
   , mDisplay(aDisplay)
   , mFrameID(aFrameID)
   , mSentToCompositor(false)
 {
   NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
   mKeyframe = aKeyframe;
   mTimecode = aTimecode;
 }
@@ -483,28 +483,28 @@ VideoData::Create(const VideoInfo& aInfo
 
 // Alignment value - 1. 0 means that data isn't aligned.
 // For 32-bytes aligned, use 31U.
 #define RAW_DATA_ALIGNMENT 31U
 
 #define RAW_DATA_DEFAULT_SIZE 4096
 
 MediaRawData::MediaRawData()
-  : MediaData(RAW_DATA)
+  : MediaData(RAW_DATA, 0)
   , mData(nullptr)
   , mSize(0)
   , mCrypto(mCryptoInternal)
   , mBuffer(new MediaByteBuffer())
   , mPadding(0)
 {
   unused << mBuffer->SetCapacity(RAW_DATA_DEFAULT_SIZE, fallible);
 }
 
 MediaRawData::MediaRawData(const uint8_t* aData, size_t aSize)
-  : MediaData(RAW_DATA)
+  : MediaData(RAW_DATA, 0)
   , mData(nullptr)
   , mSize(0)
   , mCrypto(mCryptoInternal)
   , mBuffer(new MediaByteBuffer())
   , mPadding(0)
 {
   if (!EnsureCapacity(aSize)) {
     return;
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -35,22 +35,24 @@ public:
     AUDIO_DATA = 0,
     VIDEO_DATA,
     RAW_DATA
   };
 
   MediaData(Type aType,
             int64_t aOffset,
             int64_t aTimestamp,
-            int64_t aDuration)
+            int64_t aDuration,
+            uint32_t aFrames)
     : mType(aType)
     , mOffset(aOffset)
     , mTime(aTimestamp)
     , mTimecode(aTimestamp)
     , mDuration(aDuration)
+    , mFrames(aFrames)
     , mKeyframe(false)
     , mDiscontinuity(false)
   {}
 
   // Type of contained data.
   const Type mType;
 
   // Approximate byte offset where this data was demuxed from its media.
@@ -61,36 +63,55 @@ public:
 
   // Codec specific internal time code. For Ogg based codecs this is the
   // granulepos.
   int64_t mTimecode;
 
   // Duration of sample, in microseconds.
   int64_t mDuration;
 
+  // Amount of frames for contained data.
+  const uint32_t mFrames;
+
   bool mKeyframe;
 
   // True if this is the first sample after a gap or discontinuity in
   // the stream. This is true for the first sample in a stream after a seek.
   bool mDiscontinuity;
 
   int64_t GetEndTime() const { return mTime + mDuration; }
 
   bool AdjustForStartTime(int64_t aStartTime)
   {
     mTime = mTime - aStartTime;
     return mTime >= 0;
   }
+
+  template <typename ReturnType>
+  const ReturnType* As() const
+  {
+    MOZ_ASSERT(this->mType == ReturnType::sType);
+    return static_cast<const ReturnType*>(this);
+  }
+
+  template <typename ReturnType>
+  ReturnType* As()
+  {
+    MOZ_ASSERT(this->mType == ReturnType::sType);
+    return static_cast<ReturnType*>(this);
+  }
+
 protected:
-  explicit MediaData(Type aType)
+  MediaData(Type aType, uint32_t aFrames)
     : mType(aType)
     , mOffset(0)
     , mTime(0)
     , mTimecode(0)
     , mDuration(0)
+    , mFrames(aFrames)
     , mKeyframe(false)
     , mDiscontinuity(false)
   {}
 
   virtual ~MediaData() {}
 
 };
 
@@ -100,18 +121,17 @@ public:
 
   AudioData(int64_t aOffset,
             int64_t aTime,
             int64_t aDuration,
             uint32_t aFrames,
             AudioDataValue* aData,
             uint32_t aChannels,
             uint32_t aRate)
-    : MediaData(sType, aOffset, aTime, aDuration)
-    , mFrames(aFrames)
+    : MediaData(sType, aOffset, aTime, aDuration, aFrames)
     , mChannels(aChannels)
     , mRate(aRate)
     , mAudioData(aData) {}
 
   static const Type sType = AUDIO_DATA;
   static const char* sTypeName;
 
   // Creates a new VideoData identical to aOther, but with a different
@@ -123,17 +143,16 @@ public:
                                         int64_t aTimestamp,
                                         int64_t aDuration);
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
 
   // If mAudioBuffer is null, creates it from mAudioData.
   void EnsureAudioBuffer();
 
-  const uint32_t mFrames;
   const uint32_t mChannels;
   const uint32_t mRate;
   // At least one of mAudioBuffer/mAudioData must be non-null.
   // mChannels channels, each with mFrames frames
   nsRefPtr<SharedBuffer> mAudioBuffer;
   // mFrames frames, each with mChannels values
   nsAutoArrayPtr<AudioDataValue> mAudioData;
 
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -535,17 +535,17 @@ nsresult MediaDecoder::InitializeStateMa
   nsresult rv = mDecoderStateMachine->Init(
       cloneDonor ? cloneDonor->mDecoderStateMachine : nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // If some parameters got set before the state machine got created,
   // set them now
   SetStateMachineParameters();
 
-  return ScheduleStateMachine();
+  return NS_OK;
 }
 
 void MediaDecoder::SetStateMachineParameters()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mMinimizePreroll) {
     mDecoderStateMachine->DispatchMinimizePrerollUntilPlaybackStarts();
   }
@@ -557,40 +557,27 @@ void MediaDecoder::SetMinimizePrerollUnt
   DECODER_LOG("SetMinimizePrerollUntilPlaybackStarts()");
   mMinimizePreroll = true;
 
   // This needs to be called before we init the state machine, otherwise it will
   // have no effect.
   MOZ_DIAGNOSTIC_ASSERT(!mDecoderStateMachine);
 }
 
-nsresult MediaDecoder::ScheduleStateMachine()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (mShuttingDown)
-    return NS_OK;
-
-  MOZ_ASSERT(mDecoderStateMachine);
-  NS_ENSURE_STATE(mDecoderStateMachine);
-  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  mDecoderStateMachine->ScheduleStateMachineCrossThread();
-  return NS_OK;
-}
-
 nsresult MediaDecoder::Play()
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   UpdateDormantState(false /* aDormantTimeout */, true /* aActivity */);
 
   NS_ASSERTION(mDecoderStateMachine != nullptr, "Should have state machine.");
   if (mPausedForPlaybackRateNull) {
     return NS_OK;
   }
-  ScheduleStateMachine();
+
   if (IsEnded()) {
     return Seek(0, SeekTarget::PrevSyncPoint);
   } else if (mPlayState == PLAY_STATE_LOADING) {
     mNextState = PLAY_STATE_PLAYING;
     return NS_OK;
   }
 
   ChangeState(PLAY_STATE_PLAYING);
@@ -1042,18 +1029,16 @@ void MediaDecoder::ChangeState(PlayState
   mPlayState = aState;
 
   if (mPlayState == PLAY_STATE_PLAYING) {
     ConstructMediaTracks();
   } else if (IsEnded()) {
     RemoveMediaTracks();
   }
 
-  ScheduleStateMachine();
-
   CancelDormantTimer();
   // Start dormant timer if necessary
   StartDormantTimer();
 
   GetReentrantMonitor().NotifyAll();
 }
 
 void MediaDecoder::UpdateLogicalPosition(MediaDecoderEventVisibility aEventVisibility)
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -719,20 +719,16 @@ public:
 #ifdef MOZ_WMF
   static bool IsWMFEnabled();
 #endif
 
 #ifdef MOZ_APPLEMEDIA
   static bool IsAppleMP3Enabled();
 #endif
 
-  // Schedules the state machine to run one cycle on the shared state
-  // machine thread. Main thread only.
-  nsresult ScheduleStateMachine();
-
   struct Statistics {
     // Estimate of the current playback rate (bytes/second).
     double mPlaybackRate;
     // Estimate of the current download rate (bytes/second). This
     // ignores time that the channel was paused by Gecko.
     double mDownloadRate;
     // Total length of media stream in bytes; -1 if not known
     int64_t mTotalBytes;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -282,23 +282,23 @@ MediaDecoderStateMachine::MediaDecoderSt
   // function per-process is OK, provided each call is matched by a corresponding
   // timeEndPeriod() call.
   timeBeginPeriod(1);
 #endif
 
   nsRefPtr<MediaDecoderStateMachine> self = this;
 
   AudioQueue().AddPopListener(
-    [self] (const AudioData* aSample) {
-      self->OnAudioPopped(aSample);
-    }, mTaskQueue);
+    [self] (const MediaData* aSample) {
+      self->OnAudioPopped(aSample->As<AudioData>());
+     }, mTaskQueue);
 
   VideoQueue().AddPopListener(
-    [self] (const VideoData* aSample) {
-      self->OnVideoPopped(aSample);
+    [self] (const MediaData* aSample) {
+      self->OnVideoPopped(aSample->As<VideoData>());
     }, mTaskQueue);
 }
 
 MediaDecoderStateMachine::~MediaDecoderStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   MOZ_COUNT_DTOR(MediaDecoderStateMachine);
 
@@ -379,23 +379,24 @@ void MediaDecoderStateMachine::SendStrea
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   MOZ_ASSERT(!mAudioSink, "Should've been stopped in RunStateMachine()");
 
   bool finished = mDecodedStream->SendData(mVolume, mDecoder->IsSameOriginMedia());
 
   const auto clockTime = GetClock();
   while (true) {
-    const AudioData* a = AudioQueue().PeekFront();
+    const MediaData* a = AudioQueue().PeekFront();
+
     // If we discard audio samples fed to the stream immediately, we will
     // keep decoding audio samples till the end and consume a lot of memory.
     // Therefore we only discard those behind the stream clock to throttle
     // the decoding speed.
     if (a && a->mTime <= clockTime) {
-      nsRefPtr<AudioData> releaseMe = AudioQueue().PopFront();
+      nsRefPtr<MediaData> releaseMe = AudioQueue().PopFront();
       continue;
     }
     break;
   }
 
   // To be consistent with AudioSink, |mAudioCompleted| is not set
   // until all samples are drained.
   if (finished && AudioQueue().GetSize() == 0) {
@@ -1037,17 +1038,17 @@ nsresult MediaDecoderStateMachine::Init(
 
   MediaDecoderReader* cloneReader = nullptr;
   if (aCloneDonor) {
     cloneReader = aCloneDonor->mReader;
   }
 
   nsresult rv = mReader->Init(cloneReader);
   NS_ENSURE_SUCCESS(rv, rv);
-
+  ScheduleStateMachineCrossThread();
   return NS_OK;
 }
 
 void MediaDecoderStateMachine::StopPlayback()
 {
   MOZ_ASSERT(OnTaskQueue());
   DECODER_LOG("StopPlayback()");
 
@@ -2108,21 +2109,21 @@ MediaDecoderStateMachine::SeekCompleted(
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   MOZ_ASSERT(mState == DECODER_STATE_SEEKING);
 
   int64_t seekTime = mCurrentSeek.mTarget.mTime;
   int64_t newCurrentTime = seekTime;
 
   // Setup timestamp state.
-  nsRefPtr<VideoData> video = VideoQueue().PeekFront();
+  nsRefPtr<MediaData> video = VideoQueue().PeekFront();
   if (seekTime == Duration().ToMicroseconds()) {
     newCurrentTime = seekTime;
   } else if (HasAudio()) {
-    AudioData* audio = AudioQueue().PeekFront();
+    MediaData* audio = AudioQueue().PeekFront();
     // Though we adjust the newCurrentTime in audio-based, and supplemented
     // by video. For better UX, should NOT bind the slide position to
     // the first audio data timestamp directly.
     // While seeking to a position where there's only either audio or video, or
     // seeking to a position lies before audio or video, we need to check if
     // seekTime is bounded in suitable duration. See Bug 1112438.
     int64_t videoStart = video ? video->mTime : seekTime;
     int64_t audioStart = audio ? audio->mTime : seekTime;
@@ -2511,26 +2512,26 @@ void MediaDecoderStateMachine::CheckTurn
 void MediaDecoderStateMachine::RenderVideoFrames(int32_t aMaxFrames,
                                                  int64_t aClockTime,
                                                  const TimeStamp& aClockTimeStamp)
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
-  nsAutoTArray<nsRefPtr<VideoData>,16> frames;
+  nsAutoTArray<nsRefPtr<MediaData>,16> frames;
   VideoQueue().GetFirstElements(aMaxFrames, &frames);
   if (frames.IsEmpty() || !container) {
     return;
   }
 
   nsAutoTArray<ImageContainer::NonOwningImage,16> images;
   TimeStamp lastFrameTime;
   for (uint32_t i = 0; i < frames.Length(); ++i) {
-    VideoData* frame = frames[i];
+    VideoData* frame = frames[i]->As<VideoData>();
     frame->mSentToCompositor = true;
 
     int64_t frameTime = frame->mTime;
     if (frameTime < 0) {
       // Frame times before the start time are invalid; drop such frames
       continue;
     }
 
@@ -2557,17 +2558,17 @@ void MediaDecoderStateMachine::RenderVid
     img->mProducerID = mProducerID;
 
     VERBOSE_LOG("playing video frame %lld (id=%d) (queued=%i, state-machine=%i, decoder-queued=%i)",
                 frame->mTime, frame->mFrameID,
                 VideoQueue().GetSize() + mReader->SizeOfVideoQueueInFrames(),
                 VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames());
   }
 
-  container->SetCurrentFrames(frames[0]->mDisplay, images);
+  container->SetCurrentFrames(frames[0]->As<VideoData>()->mDisplay, images);
 }
 
 void MediaDecoderStateMachine::ResyncAudioClock()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   if (IsPlaying()) {
     SetPlayStartTime(TimeStamp::Now());
@@ -2661,31 +2662,31 @@ void MediaDecoderStateMachine::UpdateRen
   TimeStamp nowTime;
   const int64_t clockTime = GetClock(&nowTime);
   // Skip frames up to the frame at the playback position, and figure out
   // the time remaining until it's time to display the next frame and drop
   // the current frame.
   NS_ASSERTION(clockTime >= 0, "Should have positive clock time.");
   int64_t remainingTime = AUDIO_DURATION_USECS;
   if (VideoQueue().GetSize() > 0) {
-    nsRefPtr<VideoData> currentFrame = VideoQueue().PopFront();
+    nsRefPtr<MediaData> currentFrame = VideoQueue().PopFront();
     int32_t framesRemoved = 0;
     while (VideoQueue().GetSize() > 0) {
-      VideoData* nextFrame = VideoQueue().PeekFront();
+      MediaData* nextFrame = VideoQueue().PeekFront();
       if (!IsRealTime() && nextFrame->mTime > clockTime) {
         remainingTime = nextFrame->mTime - clockTime;
         break;
       }
       ++framesRemoved;
-      if (!currentFrame->mSentToCompositor) {
+      if (!currentFrame->As<VideoData>()->mSentToCompositor) {
         mDecoder->NotifyDecodedFrames(0, 0, 1);
         VERBOSE_LOG("discarding video frame mTime=%lld clock_time=%lld",
                     currentFrame->mTime, clockTime);
       }
-      CheckTurningOffHardwareDecoder(currentFrame);
+      CheckTurningOffHardwareDecoder(currentFrame->As<VideoData>());
       currentFrame = VideoQueue().PopFront();
 
     }
     VideoQueue().PushFront(currentFrame);
     if (framesRemoved > 0) {
       mVideoFrameEndTime = currentFrame->GetEndTime();
       MediaDecoder::FrameStatistics& frameStats = mDecoder->GetFrameStatistics();
       frameStats.NotifyPresentedFrame();
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -401,18 +401,18 @@ protected:
 
   void OnAudioPopped(const AudioData* aSample);
   void OnVideoPopped(const VideoData* aSample);
 
   void VolumeChanged();
   void LogicalPlaybackRateChanged();
   void PreservesPitchChanged();
 
-  MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
-  MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
+  MediaQueue<MediaData>& AudioQueue() { return mAudioQueue; }
+  MediaQueue<MediaData>& VideoQueue() { return mVideoQueue; }
 
   // True if our buffers of decoded audio are not full, and we should
   // decode more.
   bool NeedToDecodeAudio();
 
   // True if our buffers of decoded video are not full, and we should
   // decode more.
   bool NeedToDecodeVideo();
@@ -874,21 +874,20 @@ private:
 
   // Time at which the last video sample was requested. If it takes too long
   // before the sample arrives, we will increase the amount of audio we buffer.
   // This is necessary for legacy synchronous decoders to prevent underruns.
   TimeStamp mVideoDecodeStartTime;
 
   // Queue of audio frames. This queue is threadsafe, and is accessed from
   // the audio, decoder, state machine, and main threads.
-  MediaQueue<AudioData> mAudioQueue;
-
+  MediaQueue<MediaData> mAudioQueue;
   // Queue of video frames. This queue is threadsafe, and is accessed from
   // the decoder, state machine, and main threads.
-  MediaQueue<VideoData> mVideoQueue;
+  MediaQueue<MediaData> mVideoQueue;
 
   // The decoder monitor must be obtained before modifying this state.
   // NotifyAll on the monitor must be called when the state is changed so
   // that interested threads can wake up and alter behaviour if appropriate
   // Accessed on state machine, audio, main, and AV thread.
   Watchable<State> mState;
 
   // The task queue in which we run decode tasks. This is referred to as
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -67,17 +67,17 @@ MediaSourceDecoder::Load(nsIStreamListen
     NS_WARNING("Failed to create state machine!");
     return NS_ERROR_FAILURE;
   }
 
   nsresult rv = GetStateMachine()->Init(nullptr);
   NS_ENSURE_SUCCESS(rv, rv);
 
   SetStateMachineParameters();
-  return ScheduleStateMachine();
+  return NS_OK;
 }
 
 media::TimeIntervals
 MediaSourceDecoder::GetSeekable()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mMediaSource) {
     NS_WARNING("MediaSource element isn't attached");
--- a/dom/webidl/EventTarget.webidl
+++ b/dom/webidl/EventTarget.webidl
@@ -27,18 +27,24 @@ interface EventTarget {
                            optional boolean capture = false);
   [Throws]
   boolean dispatchEvent(Event event);
 };
 
 // Mozilla extensions for use by JS-implemented event targets to
 // implement on* properties.
 partial interface EventTarget {
+  // The use of [TreatNonCallableAsNull] here is a bit of a hack: it just makes
+  // the codegen check whether the type involved is either
+  // [TreatNonCallableAsNull] or [TreatNonObjectAsNull] and if it is handle it
+  // accordingly.  In particular, it will NOT actually treat a non-null
+  // non-callable object as null here.
   [ChromeOnly, Throws]
-  void setEventHandler(DOMString type, EventHandler handler);
+  void setEventHandler(DOMString type,
+                       [TreatNonCallableAsNull] EventHandler handler);
 
   [ChromeOnly]
   EventHandler getEventHandler(DOMString type);
 };
 
 // Mozilla extension to make firing events on event targets from
 // chrome easier.  This returns the window which can be used to create
 // events to fire at this EventTarget, or null if there isn't one.
--- a/dom/webidl/TestInterfaceJS.webidl
+++ b/dom/webidl/TestInterfaceJS.webidl
@@ -7,17 +7,17 @@
 dictionary TestInterfaceJSUnionableDictionary {
   object objectMember;
   any anyMember;
 };
 
 [JSImplementation="@mozilla.org/dom/test-interface-js;1",
  Pref="dom.expose_test_interfaces",
  Constructor(optional any anyArg, optional object objectArg, optional TestInterfaceJSDictionary dictionaryArg)]
-interface TestInterfaceJS {
+interface TestInterfaceJS : EventTarget {
   readonly attribute any anyArg;
   readonly attribute object objectArg;
   [Cached, Pure] readonly attribute TestInterfaceJSDictionary dictionaryArg;
   attribute any anyAttr;
   attribute object objectAttr;
   [Cached, Pure] attribute TestInterfaceJSDictionary dictionaryAttr;
   any pingPongAny(any arg);
   object pingPongObject(object obj);
@@ -73,9 +73,12 @@ interface TestInterfaceJS {
   Promise<void> testPromiseWithThrowingContentPromiseInit(PromiseInit func);
   Promise<void> testPromiseWithDOMExceptionThrowingPromiseInit();
   Promise<void> testPromiseWithThrowingChromeThenFunction();
   Promise<void> testPromiseWithThrowingContentThenFunction(AnyCallback func);
   Promise<void> testPromiseWithDOMExceptionThrowingThenFunction();
   Promise<void> testPromiseWithThrowingChromeThenable();
   Promise<void> testPromiseWithThrowingContentThenable(object thenable);
   Promise<void> testPromiseWithDOMExceptionThrowingThenable();
+
+  // Event handler tests
+  attribute EventHandler onsomething;
 };
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -1044,43 +1044,16 @@ void PadDrawTargetOutFromRegion(RefPtr<D
   if (drawTarget->LockBits(&lb.data, &lb.size, &lb.stride, &lb.format)) {
     // we can only pad software targets so if we can't lock the bits don't pad
     region.VisitEdges(lb.visitor, &lb);
     drawTarget->ReleaseBits(lb.data);
   }
 }
 
 void
-ClientTiledLayerBuffer::PostValidate(const nsIntRegion& aPaintRegion,
-                                     const nsIntRegion& aDirtyRegion)
-{
-  if (gfxPrefs::TiledDrawTargetEnabled() && mMoz2DTiles.size() > 0) {
-    gfx::TileSet tileset;
-    for (size_t i = 0; i < mMoz2DTiles.size(); ++i) {
-      mMoz2DTiles[i].mTileOrigin -= mTilingOrigin;
-    }
-    tileset.mTiles = &mMoz2DTiles[0];
-    tileset.mTileCount = mMoz2DTiles.size();
-    RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
-    drawTarget->SetTransform(Matrix());
-
-    RefPtr<gfxContext> ctx = new gfxContext(drawTarget);
-    ctx->SetMatrix(
-      ctx->CurrentMatrix().Scale(mResolution, mResolution).Translate(ThebesPoint(-mTilingOrigin)));
-
-    mCallback(mPaintedLayer, ctx, aPaintRegion, aDirtyRegion,
-              DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
-    mMoz2DTiles.clear();
-    // Reset:
-    mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
-                             std::numeric_limits<int32_t>::max());
-  }
-}
-
-void
 ClientTiledLayerBuffer::UnlockTile(TileClient& aTile)
 {
   // We locked the back buffer, and flipped so we now need to unlock the front
   if (aTile.mFrontBuffer && aTile.mFrontBuffer->IsLocked()) {
     aTile.mFrontBuffer->Unlock();
     aTile.mFrontBuffer->SyncWithObject(mCompositableClient->GetForwarder()->GetSyncObject());
   }
   if (aTile.mFrontBufferOnWhite && aTile.mFrontBufferOnWhite->IsLocked()) {
@@ -1145,19 +1118,71 @@ void ClientTiledLayerBuffer::Update(cons
     }
 
     TileClient& tile = mRetainedTiles[i];
     if (!ValidateTile(tile, GetTileOffset(tilePosition), tileDrawRegion)) {
       gfxCriticalError() << "ValidateTile failed";
     }
   }
 
-  PostValidate(aPaintRegion, aDirtyRegion);
+  if (gfxPrefs::TiledDrawTargetEnabled() && mMoz2DTiles.size() > 0) {
+    gfx::TileSet tileset;
+    for (size_t i = 0; i < mMoz2DTiles.size(); ++i) {
+      mMoz2DTiles[i].mTileOrigin -= mTilingOrigin;
+    }
+    tileset.mTiles = &mMoz2DTiles[0];
+    tileset.mTileCount = mMoz2DTiles.size();
+    RefPtr<DrawTarget> drawTarget = gfx::Factory::CreateTiledDrawTarget(tileset);
+    drawTarget->SetTransform(Matrix());
+
+    RefPtr<gfxContext> ctx = new gfxContext(drawTarget);
+    ctx->SetMatrix(
+      ctx->CurrentMatrix().Scale(mResolution, mResolution).Translate(ThebesPoint(-mTilingOrigin)));
+
+    mCallback(mPaintedLayer, ctx, aPaintRegion, aDirtyRegion,
+              DrawRegionClip::DRAW, nsIntRegion(), mCallbackData);
+    mMoz2DTiles.clear();
+    // Reset:
+    mTilingOrigin = IntPoint(std::numeric_limits<int32_t>::max(),
+                             std::numeric_limits<int32_t>::max());
+  }
+
+  bool edgePaddingEnabled = gfxPrefs::TileEdgePaddingEnabled();
+
+  for (uint32_t i = 0; i < mRetainedTiles.Length(); ++i) {
+    TileClient& tile = mRetainedTiles[i];
 
-  for (TileClient& tile : mRetainedTiles) {
+    // Only worry about padding when not doing low-res because it simplifies
+    // the math and the artifacts won't be noticable
+    // Edge padding prevents sampling artifacts when compositing.
+    if (edgePaddingEnabled && mResolution == 1 &&
+        tile.mFrontBuffer && tile.mFrontBuffer->IsLocked()) {
+
+      const TileIntPoint tilePosition = newTiles.TilePosition(i);
+      IntPoint tileOffset = GetTileOffset(tilePosition);
+      // Strictly speakig we want the unscaled rect here, but it doesn't matter
+      // because we only run this code when the resolution is equal to 1.
+      IntRect tileRect = IntRect(tileOffset.x, tileOffset.y,
+                                 GetTileSize().width, GetTileSize().height);
+
+      nsIntRegion tileDrawRegion = IntRect(tileOffset, scaledTileSize);
+      tileDrawRegion.AndWith(aPaintRegion);
+
+      nsIntRegion tileValidRegion = mValidRegion;
+      tileValidRegion.OrWith(tileDrawRegion);
+
+      // We only need to pad out if the tile has area that's not valid
+      if (!tileValidRegion.Contains(tileRect)) {
+        tileValidRegion = tileValidRegion.Intersect(tileRect);
+        // translate the region into tile space and pad
+        tileValidRegion.MoveBy(-IntPoint(tileOffset.x, tileOffset.y));
+        RefPtr<DrawTarget> drawTarget = tile.mFrontBuffer->BorrowDrawTarget();
+        PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
+      }
+    }
     UnlockTile(tile);
   }
 
   mTiles = newTiles;
   mValidRegion = newValidRegion;
   mPaintedRegion.OrWith(aPaintRegion);
 }
 
@@ -1235,17 +1260,16 @@ ClientTiledLayerBuffer::ValidateTile(Til
       }
       if (backBufferOnWhite && !mCompositableClient->AddTextureClient(backBufferOnWhite)) {
         gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient (b)";
         aTile.DiscardBuffers();
         return false;
       }
     }
 
-    // prepare an array of Moz2D tiles that will be painted into in PostValidate
     gfx::Tile moz2DTile;
     RefPtr<DrawTarget> dt = backBuffer->BorrowDrawTarget();
     RefPtr<DrawTarget> dtOnWhite;
     if (backBufferOnWhite) {
       dtOnWhite = backBufferOnWhite->BorrowDrawTarget();
       moz2DTile.mDrawTarget = Factory::CreateDualDrawTarget(dt, dtOnWhite);
     } else {
       moz2DTile.mDrawTarget = dt;
@@ -1321,36 +1345,16 @@ ClientTiledLayerBuffer::ValidateTile(Til
                           drawRect.height);
     gfx::IntPoint copyTarget(NS_lroundf(drawRect.x), NS_lroundf(drawRect.y));
     drawTarget->CopySurface(source, copyRect, copyTarget);
 
     // Mark the newly updated area as invalid in the front buffer
     aTile.mInvalidFront.Or(aTile.mInvalidFront, IntRect(copyTarget.x, copyTarget.y, copyRect.width, copyRect.height));
   }
 
-  // only worry about padding when not doing low-res
-  // because it simplifies the math and the artifacts
-  // won't be noticable
-  if (mResolution == 1) {
-    IntRect unscaledTile = IntRect(aTileOrigin.x,
-                                       aTileOrigin.y,
-                                       GetTileSize().width,
-                                       GetTileSize().height);
-
-    nsIntRegion tileValidRegion = GetValidRegion();
-    tileValidRegion.Or(tileValidRegion, aDirtyRegion);
-    // We only need to pad out if the tile has area that's not valid
-    if (!tileValidRegion.Contains(unscaledTile)) {
-      tileValidRegion = tileValidRegion.Intersect(unscaledTile);
-      // translate the region into tile space and pad
-      tileValidRegion.MoveBy(-nsIntPoint(unscaledTile.x, unscaledTile.y));
-      PadDrawTargetOutFromRegion(drawTarget, tileValidRegion);
-    }
-  }
-
   // The new buffer is now validated, remove the dirty region from it.
   aTile.mInvalidBack.SubOut(offsetScaledDirtyRegion);
 
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   DrawDebugOverlay(drawTarget, aTileOrigin.x * mResolution,
                    aTileOrigin.y * GetPresShellResolution(), GetTileLength(), GetTileLength());
 #endif
 
@@ -1359,17 +1363,16 @@ ClientTiledLayerBuffer::ValidateTile(Til
 
   nsIntRegion tileRegion =
     IntRect(aTileOrigin.x, aTileOrigin.y,
               GetScaledTileSize().width, GetScaledTileSize().height);
   // Intersect this area with the portion that's invalid.
   tileRegion.SubOut(GetValidRegion());
   tileRegion.SubOut(aDirtyRegion); // Has now been validated
 
-  backBuffer->Unlock();
   backBuffer->SetWaste(tileRegion.Area() * mResolution * mResolution);
 
   if (createdTextureClient) {
     if (!mCompositableClient->AddTextureClient(backBuffer)) {
       gfxCriticalError() << "[Tiling:Client] Failed to connect a TextureClient (c)";
       aTile.DiscardBuffers();
       return false;
     }
--- a/gfx/layers/client/TiledContentClient.h
+++ b/gfx/layers/client/TiledContentClient.h
@@ -272,17 +272,16 @@ struct TileClient
   gfx::IntRect mUpdateRect;
   CompositableClient* mCompositableClient;
 #ifdef GFX_TILEDLAYER_DEBUG_OVERLAY
   TimeStamp        mLastUpdate;
 #endif
   nsIntRegion mInvalidFront;
   nsIntRegion mInvalidBack;
   nsExpirationState mExpirationState;
-
 private:
   // Copies dirty pixels from the front buffer into the back buffer,
   // and records the copied region in aAddPaintedRegion.
   void ValidateBackBufferFromFront(const nsIntRegion &aDirtyRegion,
                                    nsIntRegion& aAddPaintedRegion);
 };
 
 /**
@@ -464,19 +463,16 @@ public:
     mRetainedTiles.Clear();
   }
 
 protected:
   bool ValidateTile(TileClient& aTile,
                     const nsIntPoint& aTileRect,
                     const nsIntRegion& dirtyRect);
 
-  void PostValidate(const nsIntRegion& aPaintRegion,
-                    const nsIntRegion& aDirtyRegion);
-
   void UnlockTile(TileClient& aTile);
 
   TileClient GetPlaceholderTile() const { return TileClient(); }
 
 private:
   gfxContentType GetContentType(SurfaceMode* aMode = nullptr) const;
   ClientTiledPaintedLayer* mPaintedLayer;
   CompositableClient* mCompositableClient;
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -680,20 +680,17 @@ CompositorD3D11::DrawVRDistortion(const 
 
   // Triangle lists and same layout for both eyes
   mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
   mContext->IASetInputLayout(mAttachments->mVRDistortionInputLayout[hmdType]);
   mContext->VSSetShader(mAttachments->mVRDistortionVS[hmdType], nullptr, 0);
   mContext->PSSetShader(mAttachments->mVRDistortionPS[hmdType], nullptr, 0);
 
   // This is the source texture SRV for the pixel shader
-  // XXX, um should we cache this SRV on the source?
-  RefPtr<ID3D11ShaderResourceView> view;
-  mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
-  ID3D11ShaderResourceView* srView = view;
+  ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
   mContext->PSSetShaderResources(0, 1, &srView);
 
   gfx::IntSize vpSizeInt = mCurrentRT->GetSize();
   gfx::Size vpSize(vpSizeInt.width, vpSizeInt.height);
   ID3D11Buffer* vbuffer;
   UINT vsize, voffset;
 
   for (uint32_t eye = 0; eye < 2; eye++) {
@@ -782,26 +779,17 @@ CompositorD3D11::DrawQuad(const gfx::Rec
       static_cast<EffectMask*>(aEffectChain.mSecondaryEffects[EffectTypes::MASK].get());
     TextureSourceD3D11* source = maskEffect->mMaskTexture->AsSourceD3D11();
 
     if (!source) {
       NS_WARNING("Missing texture source!");
       return;
     }
 
-    RefPtr<ID3D11ShaderResourceView> view;
-    HRESULT hr = mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
-    if (Failed(hr)) {
-      // XXX - There's a chance we won't be able to render anything, should we
-      // just crash release builds?
-      gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in DrawQuad 1";
-      return;
-    }
-
-    ID3D11ShaderResourceView* srView = view;
+    ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
     mContext->PSSetShaderResources(3, 1, &srView);
 
     const gfx::Matrix4x4& maskTransform = maskEffect->mMaskTransform;
     NS_ASSERTION(maskTransform.Is2D(), "How did we end up with a 3D transform here?!");
     Rect bounds = Rect(Point(), Size(maskEffect->mSize));
 
     mVSConstants.maskQuad = maskTransform.As2D().TransformBounds(bounds);
   }
@@ -852,26 +840,17 @@ CompositorD3D11::DrawQuad(const gfx::Rec
 
       if (!source) {
         NS_WARNING("Missing texture source!");
         return;
       }
 
       SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, texturedEffect->mTexture->GetFormat());
 
-      RefPtr<ID3D11ShaderResourceView> view;
-      HRESULT hr = mDevice->CreateShaderResourceView(source->GetD3D11Texture(), nullptr, byRef(view));
-      if (Failed(hr)) {
-        // XXX - There's a chance we won't be able to render anything, should we
-        // just crash release builds?
-        gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in DrawQuad 2";
-        return;
-      }
-
-      ID3D11ShaderResourceView* srView = view;
+      ID3D11ShaderResourceView* srView = source->GetShaderResourceView();
       mContext->PSSetShaderResources(0, 1, &srView);
 
       if (!texturedEffect->mPremultiplied) {
         mContext->OMSetBlendState(mAttachments->mNonPremulBlendState, sBlendFactor, 0xFFFFFFFF);
         restoreBlendMode = true;
       }
 
       SetSamplerForFilter(texturedEffect->mFilter);
@@ -900,42 +879,19 @@ CompositorD3D11::DrawQuad(const gfx::Rec
         // because of unsupported dimensions (we don't tile YCbCr textures).
         return;
       }
 
       TextureSourceD3D11* sourceY  = source->GetSubSource(Y)->AsSourceD3D11();
       TextureSourceD3D11* sourceCb = source->GetSubSource(Cb)->AsSourceD3D11();
       TextureSourceD3D11* sourceCr = source->GetSubSource(Cr)->AsSourceD3D11();
 
-      HRESULT hr;
-
-      RefPtr<ID3D11ShaderResourceView> views[3];
-
-      hr = mDevice->CreateShaderResourceView(sourceY->GetD3D11Texture(),
-                                             nullptr, byRef(views[0]));
-      if (Failed(hr)) {
-        gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in DrawQuad 3";
-        return;
-      }
-
-      hr = mDevice->CreateShaderResourceView(sourceCb->GetD3D11Texture(),
-                                             nullptr, byRef(views[1]));
-      if (Failed(hr)) {
-        gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in DrawQuad 4";
-        return;
-      }
-
-      hr = mDevice->CreateShaderResourceView(sourceCr->GetD3D11Texture(),
-                                             nullptr, byRef(views[2]));
-      if (Failed(hr)) {
-        gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in DrawQuad 5";
-        return;
-      }
-
-      ID3D11ShaderResourceView* srViews[3] = { views[0], views[1], views[2] };
+      ID3D11ShaderResourceView* srViews[3] = { sourceY->GetShaderResourceView(),
+                                               sourceCb->GetShaderResourceView(),
+                                               sourceCr->GetShaderResourceView() };
       mContext->PSSetShaderResources(0, 3, srViews);
     }
     break;
   case EffectTypes::COMPONENT_ALPHA:
     {
       MOZ_ASSERT(gfxPrefs::ComponentAlphaEnabled());
       MOZ_ASSERT(mAttachments->mComponentBlendState);
       EffectComponentAlpha* effectComponentAlpha =
@@ -950,32 +906,18 @@ CompositorD3D11::DrawQuad(const gfx::Rec
       }
 
       SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, effectComponentAlpha->mOnWhite->GetFormat());
 
       SetSamplerForFilter(effectComponentAlpha->mFilter);
 
       pTexCoordRect = &effectComponentAlpha->mTextureCoords;
 
-      RefPtr<ID3D11ShaderResourceView> views[2];
-
-      HRESULT hr;
-
-      hr = mDevice->CreateShaderResourceView(sourceOnBlack->GetD3D11Texture(), nullptr, byRef(views[0]));
-      if (Failed(hr)) {
-        gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in DrawQuad 6";
-        return;
-      }
-      hr = mDevice->CreateShaderResourceView(sourceOnWhite->GetD3D11Texture(), nullptr, byRef(views[1]));
-      if (Failed(hr)) {
-        gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Failed in DrawQuad 7";
-        return;
-      }
-
-      ID3D11ShaderResourceView* srViews[2] = { views[0], views[1] };
+      ID3D11ShaderResourceView* srViews[2] = { sourceOnBlack->GetShaderResourceView(),
+                                               sourceOnWhite->GetShaderResourceView() };
       mContext->PSSetShaderResources(0, 2, srViews);
 
       mContext->OMSetBlendState(mAttachments->mComponentBlendState, sBlendFactor, 0xFFFFFFFF);
       restoreBlendMode = true;
     }
     break;
   default:
     NS_WARNING("Unknown shader type");
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TextureD3D11.h"
 #include "CompositorD3D11.h"
 #include "gfxContext.h"
 #include "Effects.h"
@@ -60,16 +60,33 @@ GetTileRectD3D11(uint32_t aID, IntSize a
   uint32_t horizontalTile = aID % horizontalTiles;
 
   return IntRect(horizontalTile * aMaxSize,
                  verticalTile * aMaxSize,
                  horizontalTile < (horizontalTiles - 1) ? aMaxSize : aSize.width % aMaxSize,
                  verticalTile < (verticalTiles - 1) ? aMaxSize : aSize.height % aMaxSize);
 }
 
+ID3D11ShaderResourceView*
+TextureSourceD3D11::GetShaderResourceView()
+{
+  MOZ_ASSERT(mTexture == GetD3D11Texture(), "You need to override GetShaderResourceView if you're overriding GetD3D11Texture!");
+
+  if (!mSRV && mTexture) {
+    RefPtr<ID3D11Device> device;
+    mTexture->GetDevice(byRef(device));
+    HRESULT hr = device->CreateShaderResourceView(mTexture, nullptr, byRef(mSRV));
+    if (FAILED(hr)) {
+      gfxCriticalError(CriticalLog::DefaultOptions(false)) << "[D3D11] TextureSourceD3D11:GetShaderResourceView CreateSRV failure " << gfx::hexa(hr);
+      return nullptr;
+    }
+  }
+  return mSRV;
+}
+
 DataTextureSourceD3D11::DataTextureSourceD3D11(SurfaceFormat aFormat,
                                                CompositorD3D11* aCompositor,
                                                TextureFlags aFlags)
   : mCompositor(aCompositor)
   , mFormat(aFormat)
   , mFlags(aFlags)
   , mCurrentTile(0)
   , mIsTiled(false)
@@ -949,16 +966,17 @@ DataTextureSourceD3D11::Update(DataSourc
 
     aSurface->Unmap();
   } else {
     mIsTiled = true;
     uint32_t tileCount = GetRequiredTilesD3D11(mSize.width, maxSize) *
                          GetRequiredTilesD3D11(mSize.height, maxSize);
 
     mTileTextures.resize(tileCount);
+    mTileSRVs.resize(tileCount);
     mTexture = nullptr;
 
     for (uint32_t i = 0; i < tileCount; i++) {
       IntRect tileRect = GetTileRect(i);
 
       desc.Width = tileRect.width;
       desc.Height = tileRect.height;
       desc.Usage = D3D11_USAGE_IMMUTABLE;
@@ -981,20 +999,44 @@ DataTextureSourceD3D11::Update(DataSourc
 
 ID3D11Texture2D*
 DataTextureSourceD3D11::GetD3D11Texture() const
 {
   return mIterating ? mTileTextures[mCurrentTile]
                     : mTexture;
 }
 
+ID3D11ShaderResourceView*
+DataTextureSourceD3D11::GetShaderResourceView()
+{
+  if (mIterating) {
+    if (!mTileSRVs[mCurrentTile]) {
+      if (!mTileTextures[mCurrentTile]) {
+        return nullptr;
+      }
+      
+      RefPtr<ID3D11Device> device;
+      mTileTextures[mCurrentTile]->GetDevice(byRef(device));
+      HRESULT hr = device->CreateShaderResourceView(mTileTextures[mCurrentTile], nullptr, byRef(mTileSRVs[mCurrentTile]));
+      if (FAILED(hr)) {
+        gfxCriticalError(CriticalLog::DefaultOptions(false)) << "[D3D11] DataTextureSourceD3D11:GetShaderResourceView CreateSRV failure " << gfx::hexa(hr);
+        return nullptr;
+      }
+    }
+    return mTileSRVs[mCurrentTile];
+  }
+
+  return TextureSourceD3D11::GetShaderResourceView();
+}
+
 void
 DataTextureSourceD3D11::Reset()
 {
   mTexture = nullptr;
+  mTileSRVs.resize(0);
   mTileTextures.resize(0);
   mIsTiled = false;
   mSize.width = 0;
   mSize.height = 0;
 }
 
 IntRect
 DataTextureSourceD3D11::GetTileRect(uint32_t aIndex) const
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef MOZILLA_GFX_TEXTURED3D11_H
 #define MOZILLA_GFX_TEXTURED3D11_H
 
 #include "mozilla/layers/Compositor.h"
@@ -149,22 +149,23 @@ private:
  */
 class TextureSourceD3D11
 {
 public:
   TextureSourceD3D11() {}
   virtual ~TextureSourceD3D11() {}
 
   virtual ID3D11Texture2D* GetD3D11Texture() const { return mTexture; }
-
+  virtual ID3D11ShaderResourceView* GetShaderResourceView();
 protected:
   virtual gfx::IntSize GetSize() const { return mSize; }
 
   gfx::IntSize mSize;
   RefPtr<ID3D11Texture2D> mTexture;
+  RefPtr<ID3D11ShaderResourceView> mSRV;
 };
 
 /**
  * A TextureSource that implements the DataTextureSource interface.
  * it can be used without a TextureHost and is able to upload texture data
  * from a gfx::DataSourceSurface.
  */
 class DataTextureSourceD3D11 : public DataTextureSource
@@ -188,16 +189,18 @@ public:
                       gfx::IntPoint* aSrcOffset = nullptr) override;
 
   // TextureSource
 
   virtual TextureSourceD3D11* AsSourceD3D11() override { return this; }
 
   virtual ID3D11Texture2D* GetD3D11Texture() const override;
 
+  virtual ID3D11ShaderResourceView* GetShaderResourceView() override;
+
   virtual DataTextureSource* AsDataTextureSource() override { return this; }
 
   virtual void DeallocateDeviceData() override { mTexture = nullptr; }
 
   virtual gfx::IntSize GetSize() const  override { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const override { return mFormat; }
 
@@ -222,16 +225,17 @@ public:
   }
 
 protected:
   gfx::IntRect GetTileRect(uint32_t aIndex) const;
 
   void Reset();
 
   std::vector< RefPtr<ID3D11Texture2D> > mTileTextures;
+  std::vector< RefPtr<ID3D11ShaderResourceView> > mTileSRVs;
   RefPtr<CompositorD3D11> mCompositor;
   gfx::SurfaceFormat mFormat;
   TextureFlags mFlags;
   uint32_t mCurrentTile;
   bool mIsTiled;
   bool mIterating;
 
 };
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -340,16 +340,17 @@ private:
   // screen size does not align nicely to the default tile size. Although layers can be any size,
   // they are often the same size as the screen, especially for width.
   DECL_GFX_PREF(Once, "layers.tile-width",                     LayersTileWidth, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tile-height",                    LayersTileHeight, int32_t, 256);
   DECL_GFX_PREF(Once, "layers.tile-max-pool-size",             LayersTileMaxPoolSize, uint32_t, (uint32_t)50);
   DECL_GFX_PREF(Once, "layers.tile-shrink-pool-timeout",       LayersTileShrinkPoolTimeout, uint32_t, (uint32_t)1000);
   DECL_GFX_PREF(Once, "layers.tiled-drawtarget.enabled",       TiledDrawTargetEnabled, bool, false);
   DECL_GFX_PREF(Once, "layers.tiles.adjust",                   LayersTilesAdjust, bool, true);
+  DECL_GFX_PREF(Once, "layers.tiles.edge-padding",             TileEdgePaddingEnabled, bool, true);
   DECL_GFX_PREF(Live, "layers.transaction.warning-ms",         LayerTransactionWarning, uint32_t, 200);
   DECL_GFX_PREF(Once, "layers.uniformity-info",                UniformityInfo, bool, false);
   DECL_GFX_PREF(Once, "layers.use-image-offscreen-surfaces",   UseImageOffscreenSurfaces, bool, false);
 
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.damping-ratio", ScrollBehaviorDampingRatio, float, 1.0f);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.enabled",    ScrollBehaviorEnabled, bool, false);
   DECL_GFX_PREF(Live, "layout.css.scroll-behavior.spring-constant", ScrollBehaviorSpringConstant, float, 250.0f);
   DECL_GFX_PREF(Live, "layout.css.scroll-snap.prediction-max-velocity", ScrollSnapPredictionMaxVelocity, int32_t, 2000);
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -618,16 +618,40 @@ class DispatchWrapper
     // we set aside when we did know the type.
     static void TraceWrapped(JSTracer* trc, JS::StaticTraceable* thingp, const char* name) {
         auto wrapper = reinterpret_cast<DispatchWrapper*>(
                            uintptr_t(thingp) - offsetof(DispatchWrapper, storage));
         wrapper->tracer(&wrapper->storage, trc);
     }
 };
 
+inline RootLists&
+RootListsForRootingContext(JSContext* cx)
+{
+    return ContextFriendFields::get(cx)->roots;
+}
+
+inline RootLists&
+RootListsForRootingContext(js::ContextFriendFields* cx)
+{
+    return cx->roots;
+}
+
+inline RootLists&
+RootListsForRootingContext(JSRuntime* rt)
+{
+    return PerThreadDataFriendFields::getMainThread(rt)->roots;
+}
+
+inline RootLists&
+RootListsForRootingContext(js::PerThreadDataFriendFields* pt)
+{
+    return pt->roots;
+}
+
 } /* namespace js */
 
 namespace JS {
 
 /*
  * Local variable of type T whose value is always rooted. This is typically
  * used for local variables, or for non-rooted values being passed to a
  * function that requires a handle, e.g. Foo(Root<T>(cx, x)).
@@ -645,46 +669,33 @@ class MOZ_STACK_CLASS Rooted : public js
     /* Note: CX is a subclass of either ContextFriendFields or PerThreadDataFriendFields. */
     void registerWithRootLists(js::RootLists& roots) {
         js::ThingRootKind kind = js::RootKind<T>::rootKind();
         this->stack = &roots.stackRoots_[kind];
         this->prev = *stack;
         *stack = reinterpret_cast<Rooted<void*>*>(this);
     }
 
-    static js::RootLists& rootListsForRootingContext(JSContext* cx) {
-        return js::ContextFriendFields::get(cx)->roots;
-    }
-    static js::RootLists& rootListsForRootingContext(js::ContextFriendFields* cx) {
-        return cx->roots;
-    }
-    static js::RootLists& rootListsForRootingContext(JSRuntime* rt) {
-        return js::PerThreadDataFriendFields::getMainThread(rt)->roots;
-    }
-    static js::RootLists& rootListsForRootingContext(js::PerThreadDataFriendFields* pt) {
-        return pt->roots;
-    }
-
   public:
     template <typename RootingContext>
     explicit Rooted(const RootingContext& cx
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(js::GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        registerWithRootLists(rootListsForRootingContext(cx));
+        registerWithRootLists(js::RootListsForRootingContext(cx));
     }
 
     template <typename RootingContext, typename S>
     Rooted(const RootingContext& cx, S&& initial
            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(mozilla::Forward<S>(initial))
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        registerWithRootLists(rootListsForRootingContext(cx));
+        registerWithRootLists(js::RootListsForRootingContext(cx));
     }
 
     ~Rooted() {
         MOZ_ASSERT(*stack == reinterpret_cast<Rooted<void*>*>(this));
         *stack = prev;
     }
 
     Rooted<T>* previous() { return reinterpret_cast<Rooted<T>*>(prev); }
@@ -988,36 +999,42 @@ class PersistentRooted : public js::Pers
     friend class mozilla::LinkedListElement<PersistentRooted>;
 
     friend struct js::gc::PersistentRootedMarker<T>;
 
     friend void js::gc::FinishPersistentRootedChains(js::RootLists&);
 
     void registerWithRootLists(js::RootLists& roots) {
         MOZ_ASSERT(!initialized());
-        roots.getPersistentRootedList<T>().insertBack(this);
+        js::ThingRootKind kind = js::RootKind<T>::rootKind();
+        roots.heapRoots_[kind].insertBack(reinterpret_cast<JS::PersistentRooted<void*>*>(this));
+        // Until marking and destruction support the full set, we assert that
+        // we don't try to add any unsupported types.
+        MOZ_ASSERT(kind == js::THING_ROOT_OBJECT ||
+                   kind == js::THING_ROOT_SCRIPT ||
+                   kind == js::THING_ROOT_STRING ||
+                   kind == js::THING_ROOT_ID ||
+                   kind == js::THING_ROOT_VALUE);
     }
 
   public:
     PersistentRooted() : ptr(js::GCMethods<T>::initial()) {}
 
-    explicit PersistentRooted(JSContext* cx) {
-        init(cx);
+    template <typename RootingContext>
+    explicit PersistentRooted(const RootingContext& cx)
+      : ptr(js::GCMethods<T>::initial())
+    {
+        registerWithRootLists(js::RootListsForRootingContext(cx));
     }
 
-    PersistentRooted(JSContext* cx, T initial) {
-        init(cx, initial);
-    }
-
-    explicit PersistentRooted(JSRuntime* rt) {
-        init(rt);
-    }
-
-    PersistentRooted(JSRuntime* rt, T initial) {
-        init(rt, initial);
+    template <typename RootingContext, typename U>
+    PersistentRooted(const RootingContext& cx, U&& initial)
+      : ptr(mozilla::Forward<U>(initial))
+    {
+        registerWithRootLists(js::RootListsForRootingContext(cx));
     }
 
     PersistentRooted(const PersistentRooted& rhs)
       : mozilla::LinkedListElement<PersistentRooted<T>>(),
         ptr(rhs.ptr)
     {
         /*
          * Copy construction takes advantage of the fact that the original
@@ -1029,32 +1046,25 @@ class PersistentRooted : public js::Pers
          */
         const_cast<PersistentRooted&>(rhs).setNext(this);
     }
 
     bool initialized() {
         return ListBase::isInList();
     }
 
-    void init(JSContext* cx) {
+    template <typename RootingContext>
+    void init(const RootingContext& cx) {
         init(cx, js::GCMethods<T>::initial());
     }
 
-    void init(JSContext* cx, T initial) {
-        ptr = initial;
-        registerWithRootLists(js::ContextFriendFields::get(cx)->roots);
-    }
-
-    void init(JSRuntime* rt) {
-        init(rt, js::GCMethods<T>::initial());
-    }
-
-    void init(JSRuntime* rt, T initial) {
-        ptr = initial;
-        registerWithRootLists(js::PerThreadDataFriendFields::getMainThread(rt)->roots);
+    template <typename RootingContext, typename U>
+    void init(const RootingContext& cx, U&& initial) {
+        ptr = mozilla::Forward<U>(initial);
+        registerWithRootLists(js::RootListsForRootingContext(cx));
     }
 
     void reset() {
         if (initialized()) {
             set(js::GCMethods<T>::initial());
             ListBase::remove();
         }
     }
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -315,27 +315,26 @@ struct PersistentRootedMarker
 };
 
 } // namespace gc
 } // namespace js
 
 void
 js::gc::MarkPersistentRootedChainsInLists(RootLists& roots, JSTracer* trc)
 {
-    PersistentRootedMarker<JSFunction*>::markChain(trc, roots.functionPersistentRooteds,
-                                                   "PersistentRooted<JSFunction*>");
-    PersistentRootedMarker<JSObject*>::markChain(trc, roots.objectPersistentRooteds,
+    PersistentRootedMarker<JSObject*>::markChain(trc, roots.getPersistentRootedList<JSObject*>(),
                                                  "PersistentRooted<JSObject*>");
-    PersistentRootedMarker<JSScript*>::markChain(trc, roots.scriptPersistentRooteds,
+    PersistentRootedMarker<JSScript*>::markChain(trc, roots.getPersistentRootedList<JSScript*>(),
                                                  "PersistentRooted<JSScript*>");
-    PersistentRootedMarker<JSString*>::markChain(trc, roots.stringPersistentRooteds,
+    PersistentRootedMarker<JSString*>::markChain(trc, roots.getPersistentRootedList<JSString*>(),
                                                  "PersistentRooted<JSString*>");
-    PersistentRootedMarker<jsid>::markChain(trc, roots.idPersistentRooteds,
+
+    PersistentRootedMarker<jsid>::markChain(trc, roots.getPersistentRootedList<jsid>(),
                                             "PersistentRooted<jsid>");
-    PersistentRootedMarker<Value>::markChain(trc, roots.valuePersistentRooteds,
+    PersistentRootedMarker<Value>::markChain(trc, roots.getPersistentRootedList<Value>(),
                                              "PersistentRooted<Value>");
 }
 
 void
 js::gc::MarkPersistentRootedChains(JSTracer* trc)
 {
     for (ContextIter cx(trc->runtime()); !cx.done(); cx.next())
         MarkPersistentRootedChainsInLists(cx->roots, trc);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1361,22 +1361,21 @@ FinishPersistentRootedChain(mozilla::Lin
 {
     while (!list.isEmpty())
         list.getFirst()->reset();
 }
 
 void
 js::gc::FinishPersistentRootedChains(RootLists& roots)
 {
-    FinishPersistentRootedChain(roots.functionPersistentRooteds);
-    FinishPersistentRootedChain(roots.idPersistentRooteds);
-    FinishPersistentRootedChain(roots.objectPersistentRooteds);
-    FinishPersistentRootedChain(roots.scriptPersistentRooteds);
-    FinishPersistentRootedChain(roots.stringPersistentRooteds);
-    FinishPersistentRootedChain(roots.valuePersistentRooteds);
+    FinishPersistentRootedChain(roots.getPersistentRootedList<JSObject*>());
+    FinishPersistentRootedChain(roots.getPersistentRootedList<JSScript*>());
+    FinishPersistentRootedChain(roots.getPersistentRootedList<JSString*>());
+    FinishPersistentRootedChain(roots.getPersistentRootedList<jsid>());
+    FinishPersistentRootedChain(roots.getPersistentRootedList<Value>());
 }
 
 void
 GCRuntime::finishRoots()
 {
     if (rootsHash.initialized())
         rootsHash.clear();
 
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2444,20 +2444,26 @@ js::SetPrototype(JSContext* cx, HandleOb
 
     /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */
     bool extensible;
     if (!IsExtensible(cx, obj, &extensible))
         return false;
     if (!extensible)
         return result.fail(JSMSG_CANT_SET_PROTO);
 
-    /* ES6 9.1.2 step 6 forbids generating cyclical prototype chains. */
+    /*
+     * ES6 9.1.2 step 6 forbids generating cyclical prototype chains. But we
+     * have to do this comparison on the observable outer objects, not on the
+     * possibly-inner object we're setting the proto on.
+     */
+    RootedObject outerObj(cx, GetOuterObject(cx, obj));
     RootedObject obj2(cx);
     for (obj2 = proto; obj2; ) {
-        if (obj2 == obj)
+        MOZ_ASSERT(GetOuterObject(cx, obj2) == obj2);
+        if (obj2 == outerObj)
             return result.fail(JSMSG_CANT_SET_PROTO_CYCLE);
 
         if (!GetPrototype(cx, obj2, &obj2))
             return false;
     }
 
     // Convert unboxed objects to their native representations before changing
     // their prototype/group, as they depend on the group for their layout.
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -340,51 +340,64 @@ class RootLists
 
     /* Allow inlining of PersistentRooted constructors and destructors. */
   private:
     template <typename Referent> friend class JS::PersistentRooted;
     friend void js::gc::MarkPersistentRootedChains(JSTracer*);
     friend void js::gc::MarkPersistentRootedChainsInLists(RootLists&, JSTracer*);
     friend void js::gc::FinishPersistentRootedChains(RootLists&);
 
-    mozilla::LinkedList<JS::PersistentRootedFunction> functionPersistentRooteds;
-    mozilla::LinkedList<JS::PersistentRootedId>       idPersistentRooteds;
-    mozilla::LinkedList<JS::PersistentRootedObject>   objectPersistentRooteds;
-    mozilla::LinkedList<JS::PersistentRootedScript>   scriptPersistentRooteds;
-    mozilla::LinkedList<JS::PersistentRootedString>   stringPersistentRooteds;
-    mozilla::LinkedList<JS::PersistentRootedValue>    valuePersistentRooteds;
+    mozilla::LinkedList<JS::PersistentRooted<void*>> heapRoots_[THING_ROOT_LIMIT];
 
     /* Specializations of this return references to the appropriate list. */
     template<typename Referent>
     inline mozilla::LinkedList<JS::PersistentRooted<Referent>>& getPersistentRootedList();
 };
 
 template<>
-inline mozilla::LinkedList<JS::PersistentRootedFunction>
-&RootLists::getPersistentRootedList<JSFunction*>() { return functionPersistentRooteds; }
+inline mozilla::LinkedList<JS::PersistentRootedFunction>&
+RootLists::getPersistentRootedList<JSFunction*>() {
+    return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<JSFunction*>>&>(
+        heapRoots_[THING_ROOT_OBJECT]);
+}
 
 template<>
-inline mozilla::LinkedList<JS::PersistentRootedId>
-&RootLists::getPersistentRootedList<jsid>() { return idPersistentRooteds; }
+inline mozilla::LinkedList<JS::PersistentRootedObject>&
+RootLists::getPersistentRootedList<JSObject*>() {
+    return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<JSObject*>>&>(
+        heapRoots_[THING_ROOT_OBJECT]);
+}
 
 template<>
-inline mozilla::LinkedList<JS::PersistentRootedObject>
-&RootLists::getPersistentRootedList<JSObject*>() { return objectPersistentRooteds; }
+inline mozilla::LinkedList<JS::PersistentRootedId>&
+RootLists::getPersistentRootedList<jsid>() {
+    return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<jsid>>&>(
+        heapRoots_[THING_ROOT_ID]);
+}
 
 template<>
-inline mozilla::LinkedList<JS::PersistentRootedScript>
-&RootLists::getPersistentRootedList<JSScript*>() { return scriptPersistentRooteds; }
+inline mozilla::LinkedList<JS::PersistentRootedScript>&
+RootLists::getPersistentRootedList<JSScript*>() {
+    return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<JSScript*>>&>(
+        heapRoots_[THING_ROOT_SCRIPT]);
+}
 
 template<>
-inline mozilla::LinkedList<JS::PersistentRootedString>
-&RootLists::getPersistentRootedList<JSString*>() { return stringPersistentRooteds; }
+inline mozilla::LinkedList<JS::PersistentRootedString>&
+RootLists::getPersistentRootedList<JSString*>() {
+    return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<JSString*>>&>(
+        heapRoots_[THING_ROOT_STRING]);
+}
 
 template<>
-inline mozilla::LinkedList<JS::PersistentRootedValue>
-&RootLists::getPersistentRootedList<JS::Value>() { return valuePersistentRooteds; }
+inline mozilla::LinkedList<JS::PersistentRootedValue>&
+RootLists::getPersistentRootedList<JS::Value>() {
+    return reinterpret_cast<mozilla::LinkedList<JS::PersistentRooted<JS::Value>>&>(
+        heapRoots_[THING_ROOT_VALUE]);
+}
 
 struct ContextFriendFields
 {
   protected:
     JSRuntime* const     runtime_;
 
     /* The current compartment. */
     JSCompartment*      compartment_;
--- a/mfbt/AlreadyAddRefed.h
+++ b/mfbt/AlreadyAddRefed.h
@@ -91,17 +91,17 @@ struct MOZ_MUST_USE already_AddRefed
    * The autoconversion allows one to omit the idiom
    *
    *    nsRefPtr<BaseClass> y = x.forget();
    *    return y.forget();
    *
    * Note that nsRefPtr is the XPCOM reference counting smart pointer class.
    */
   template <typename U>
-  already_AddRefed(already_AddRefed<U>&& aOther) : mRawPtr(aOther.take()) {}
+  MOZ_IMPLICIT already_AddRefed(already_AddRefed<U>&& aOther) : mRawPtr(aOther.take()) {}
 
   ~already_AddRefed() { MOZ_ASSERT(!mRawPtr); }
 
   // Specialize the unused operator<< for already_AddRefed, to allow
   // nsCOMPtr<nsIFoo> foo;
   // unused << foo.forget();
   // Note that nsCOMPtr is the XPCOM reference counting smart pointer class.
   friend void operator<<(const mozilla::unused_t& aUnused,
--- a/mfbt/CheckedInt.h
+++ b/mfbt/CheckedInt.h
@@ -521,17 +521,17 @@ public:
    *
    * This constructor is not explicit. Instead, the type of its argument is a
    * separate template parameter, ensuring that no conversion is performed
    * before this constructor is actually called. As explained in the above
    * documentation for class CheckedInt, this constructor checks that its
    * argument is valid.
    */
   template<typename U>
-  CheckedInt(U aValue) MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT
+  MOZ_IMPLICIT CheckedInt(U aValue) MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT
     : mValue(T(aValue)),
       mIsValid(detail::IsInRange<T>(aValue))
   {
     static_assert(detail::IsSupported<T>::value &&
                   detail::IsSupported<U>::value,
                   "This type is not supported by CheckedInt");
   }
 
--- a/mfbt/EnumeratedRange.h
+++ b/mfbt/EnumeratedRange.h
@@ -31,17 +31,17 @@ template<typename IntTypeT, typename Enu
 class EnumeratedIterator
 {
 public:
   template<typename EnumType>
   explicit EnumeratedIterator(EnumType aCurrent)
     : mCurrent(aCurrent) { }
 
   template<typename IntType, typename EnumType>
-  EnumeratedIterator(const EnumeratedIterator<IntType, EnumType>& aOther)
+  explicit EnumeratedIterator(const EnumeratedIterator<IntType, EnumType>& aOther)
     : mCurrent(aOther.mCurrent) { }
 
   EnumTypeT operator*() const { return mCurrent; }
 
   /* Increment and decrement operators */
 
   EnumeratedIterator& operator++()
   {
--- a/mfbt/IntegerRange.h
+++ b/mfbt/IntegerRange.h
@@ -21,17 +21,17 @@ template<typename IntTypeT>
 class IntegerIterator
 {
 public:
   template<typename IntType>
   explicit IntegerIterator(IntType aCurrent)
     : mCurrent(aCurrent) { }
 
   template<typename IntType>
-  IntegerIterator(const IntegerIterator<IntType>& aOther)
+  explicit IntegerIterator(const IntegerIterator<IntType>& aOther)
     : mCurrent(aOther.mCurrent) { }
 
   IntTypeT operator*() const { return mCurrent; }
 
   /* Increment and decrement operators */
 
   IntegerIterator& operator++() { ++mCurrent; return *this; }
   IntegerIterator& operator--() { --mCurrent; return *this; }
--- a/mfbt/RangedPtr.h
+++ b/mfbt/RangedPtr.h
@@ -99,17 +99,17 @@ public:
     MOZ_ASSERT(aLength <= size_t(-1) / sizeof(T));
     MOZ_ASSERT(reinterpret_cast<uintptr_t>(mRangeStart) + aLength * sizeof(T) >=
                reinterpret_cast<uintptr_t>(mRangeStart));
     checkSanity();
   }
 
   /* Equivalent to RangedPtr(aArr, aArr, N). */
   template<size_t N>
-  RangedPtr(T (&aArr)[N])
+  explicit RangedPtr(T (&aArr)[N])
     : mPtr(aArr)
 #ifdef DEBUG
     , mRangeStart(aArr), mRangeEnd(aArr + N)
 #endif
   {
     checkSanity();
   }
 
--- a/mfbt/ReentrancyGuard.h
+++ b/mfbt/ReentrancyGuard.h
@@ -21,22 +21,22 @@ class ReentrancyGuard
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 #ifdef DEBUG
   bool& mEntered;
 #endif
 
 public:
   template<class T>
 #ifdef DEBUG
-  ReentrancyGuard(T& aObj
-                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+  explicit ReentrancyGuard(T& aObj
+                           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mEntered(aObj.mEntered)
 #else
-  ReentrancyGuard(T&
-                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+  explicit ReentrancyGuard(T&
+                           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
 #endif
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 #ifdef DEBUG
     MOZ_ASSERT(!mEntered);
     mEntered = true;
 #endif
   }
--- a/mfbt/RefPtr.h
+++ b/mfbt/RefPtr.h
@@ -233,17 +233,17 @@ class RefPtr
 public:
   RefPtr() : mPtr(0) {}
   RefPtr(const RefPtr& aOther) : mPtr(ref(aOther.mPtr)) {}
   MOZ_IMPLICIT RefPtr(already_AddRefed<T>& aOther) : mPtr(aOther.take()) {}
   MOZ_IMPLICIT RefPtr(already_AddRefed<T>&& aOther) : mPtr(aOther.take()) {}
   MOZ_IMPLICIT RefPtr(T* aVal) : mPtr(ref(aVal)) {}
 
   template<typename U>
-  RefPtr(const RefPtr<U>& aOther) : mPtr(ref(aOther.get())) {}
+  MOZ_IMPLICIT RefPtr(const RefPtr<U>& aOther) : mPtr(ref(aOther.get())) {}
 
   ~RefPtr() { unref(mPtr); }
 
   RefPtr& operator=(const RefPtr& aOther)
   {
     assign(ref(aOther.mPtr));
     return *this;
   }
--- a/mfbt/ReverseIterator.h
+++ b/mfbt/ReverseIterator.h
@@ -121,21 +121,21 @@ class IteratorRange
 {
 public:
   typedef IteratorT iterator;
   typedef IteratorT const_iterator;
   typedef ReverseIterator<IteratorT> reverse_iterator;
   typedef ReverseIterator<IteratorT> const_reverse_iterator;
 
   template<typename Iterator1, typename Iterator2>
-  IteratorRange(Iterator1 aIterBegin, Iterator2 aIterEnd)
+  MOZ_IMPLICIT IteratorRange(Iterator1 aIterBegin, Iterator2 aIterEnd)
     : mIterBegin(aIterBegin), mIterEnd(aIterEnd) { }
 
   template<typename Iterator>
-  IteratorRange(const IteratorRange<Iterator>& aOther)
+  MOZ_IMPLICIT IteratorRange(const IteratorRange<Iterator>& aOther)
     : mIterBegin(aOther.mIterBegin), mIterEnd(aOther.mIterEnd) { }
 
   iterator begin() const { return mIterBegin; }
   const_iterator cbegin() const { return begin(); }
   iterator end() const { return mIterEnd; }
   const_iterator cend() const { return end(); }
   reverse_iterator rbegin() const { return reverse_iterator(mIterEnd); }
   const_reverse_iterator crbegin() const { return rbegin(); }
--- a/mfbt/UniquePtr.h
+++ b/mfbt/UniquePtr.h
@@ -233,16 +233,17 @@ public:
   UniquePtr(decltype(nullptr))
     : mTuple(nullptr, DeleterType())
   {
     static_assert(!IsPointer<D>::value, "must provide a deleter instance");
     static_assert(!IsReference<D>::value, "must provide a deleter instance");
   }
 
   template<typename U, class E>
+  MOZ_IMPLICIT
   UniquePtr(UniquePtr<U, E>&& aOther,
             typename EnableIf<IsConvertible<typename UniquePtr<U, E>::Pointer,
                                             Pointer>::value &&
                               !IsArray<U>::value &&
                               (IsReference<D>::value
                                ? IsSame<D, E>::value
                                : IsConvertible<E, D>::value),
                               int>::Type aDummy = 0)
@@ -473,19 +474,19 @@ private:
 /** A default deletion policy using plain old operator delete. */
 template<typename T>
 class DefaultDelete
 {
 public:
   MOZ_CONSTEXPR DefaultDelete() {}
 
   template<typename U>
-  DefaultDelete(const DefaultDelete<U>& aOther,
-                typename EnableIf<mozilla::IsConvertible<U*, T*>::value,
-                                  int>::Type aDummy = 0)
+  MOZ_IMPLICIT DefaultDelete(const DefaultDelete<U>& aOther,
+                             typename EnableIf<mozilla::IsConvertible<U*, T*>::value,
+                                               int>::Type aDummy = 0)
   {}
 
   void operator()(T* aPtr) const
   {
     static_assert(sizeof(T) > 0, "T must be complete");
     delete aPtr;
   }
 };
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -304,16 +304,22 @@
 
         <receiver android:name="org.mozilla.gecko.webapp.TaskKiller">
           <intent-filter>
              <action android:name="org.mozilla.webapp.TASK_REMOVED" />
              <category android:name="android.intent.category.DEFAULT" />
           </intent-filter>
         </receiver>
 
+        <receiver android:name="org.mozilla.gecko.RestrictionProvider">
+          <intent-filter>
+            <action android:name="android.intent.action.GET_RESTRICTION_ENTRIES" />
+          </intent-filter>
+        </receiver>
+
         <!-- Activity used for launching non-privileged WebApps via a URL -->
         <activity android:name="org.mozilla.gecko.Webapp"
                   android:label="@string/webapp_generic_name"
                   android:configChanges="keyboard|keyboardHidden|mcc|mnc|orientation|screenSize"
                   android:windowSoftInputMode="stateUnspecified|adjustResize"
                   android:launchMode="singleTask"
                   android:taskAffinity="org.mozilla.gecko.WEBAPP"
                   android:process=":@ANDROID_PACKAGE_NAME@.Webapp"
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -468,16 +468,19 @@ public class BrowserApp extends GeckoApp
             return;
         }
 
         Log.d(LOGTAG, "BrowserApp.onTabChanged: " + tab.getId() + ": " + msg);
         switch(msg) {
             case LOCATION_CHANGE:
                 // fall through
             case SELECTED:
+                if (mZoomedView != null) {
+                    mZoomedView.stopZoomDisplay(false);
+                }
                 if (Tabs.getInstance().isSelectedTab(tab)) {
                     updateHomePagerForTab(tab);
                 }
 
                 mHideDynamicToolbarOnActionModeEnd = false;
                 break;
             case START:
                 if (Tabs.getInstance().isSelectedTab(tab)) {
--- a/mobile/android/base/RestrictedProfiles.java
+++ b/mobile/android/base/RestrictedProfiles.java
@@ -60,55 +60,82 @@ public class RestrictedProfiles {
         add("about:config");
     }};
 
     /* This is a list of things we can restrict you from doing. Some of these are reflected in Android UserManager constants.
      * Others are specific to us.
      * These constants should be in sync with the ones from toolkit/components/parentalcontrols/nsIParentalControlServices.idl
      */
     public enum Restriction {
-        DISALLOW_DOWNLOADS(1, "no_download_files"),
-        DISALLOW_INSTALL_EXTENSION(2, "no_install_extensions"),
-        DISALLOW_INSTALL_APPS(3, "no_install_apps"), // UserManager.DISALLOW_INSTALL_APPS
-        DISALLOW_BROWSE_FILES(4, "no_browse_files"),
-        DISALLOW_SHARE(5, "no_share"),
-        DISALLOW_BOOKMARK(6, "no_bookmark"),
-        DISALLOW_ADD_CONTACTS(7, "no_add_contacts"),
-        DISALLOW_SET_IMAGE(8, "no_set_image"),
-        DISALLOW_MODIFY_ACCOUNTS(9, "no_modify_accounts"), // UserManager.DISALLOW_MODIFY_ACCOUNTS
-        DISALLOW_REMOTE_DEBUGGING(10, "no_remote_debugging"),
-        DISALLOW_IMPORT_SETTINGS(11, "no_import_settings"),
-        DISALLOW_TOOLS_MENU(12, "no_tools_menu"),
-        DISALLOW_REPORT_SITE_ISSUE(13, "no_report_site_issue");
+        // These restrictions have no strings assigned because they are only used in guest mode and not shown in the
+        // restricted profiles settings UI
+        DISALLOW_DOWNLOADS(1, "no_download_files", 0, 0),
+        DISALLOW_BROWSE_FILES(4, "no_browse_files", 0, 0),
+        DISALLOW_SHARE(5, "no_share", 0, 0),
+        DISALLOW_BOOKMARK(6, "no_bookmark", 0, 0),
+        DISALLOW_ADD_CONTACTS(7, "no_add_contacts", 0, 0),
+        DISALLOW_SET_IMAGE(8, "no_set_image", 0, 0),
+        DISALLOW_MODIFY_ACCOUNTS(9, "no_modify_accounts", 0, 0), // UserManager.DISALLOW_MODIFY_ACCOUNTS
+        DISALLOW_REMOTE_DEBUGGING(10, "no_remote_debugging", 0, 0),
+
+        // These restrictions are used for restricted profiles and therefore need to have strings assigned for the profile
+        // settings UI.
+        DISALLOW_INSTALL_EXTENSION(2, "no_install_extensions", R.string.restriction_disallow_addons_title, R.string.restriction_disallow_addons_description),
+        DISALLOW_INSTALL_APPS(3, "no_install_apps", R.string.restriction_disallow_apps_title, R.string.restriction_disallow_apps_description), // UserManager.DISALLOW_INSTALL_APPS
+        DISALLOW_IMPORT_SETTINGS(11, "no_report_site_issue", R.string.restriction_disallow_import_settings_title, R.string.restriction_disallow_import_settings_description),
+        DISALLOW_TOOLS_MENU(12, "no_tools_menu", R.string.restriction_disallow_tools_menu_title, R.string.restriction_disallow_tools_menu_description),
+        DISALLOW_REPORT_SITE_ISSUE(13, "no_report_site_issue", R.string.restriction_disallow_report_site_issue_title, R.string.restriction_disallow_report_site_issue_description);
 
         public final int id;
         public final String name;
+        public final int titleResource;
+        public final int descriptionResource;
 
-        Restriction(final int id, final String name) {
+        Restriction(final int id, final String name, int titleResource, int descriptionResource) {
             this.id = id;
             this.name = name;
+            this.titleResource = titleResource;
+            this.descriptionResource = descriptionResource;
+        }
+
+        public String getTitle(Context context) {
+            if (titleResource == 0) {
+                return toString();
+            }
+
+            return context.getResources().getString(titleResource);
+        }
+
+        public String getDescription(Context context) {
+            if (descriptionResource == 0) {
+                return name;
+            }
+
+            return context.getResources().getString(descriptionResource);
         }
     }
 
-    private static List<Restriction> restrictionsOfGuestProfile = Arrays.asList(
+    static List<Restriction> GUEST_RESTRICTIONS = Arrays.asList(
         Restriction.DISALLOW_DOWNLOADS,
         Restriction.DISALLOW_INSTALL_EXTENSION,
         Restriction.DISALLOW_INSTALL_APPS,
         Restriction.DISALLOW_BROWSE_FILES,
         Restriction.DISALLOW_SHARE,
         Restriction.DISALLOW_BOOKMARK,
         Restriction.DISALLOW_ADD_CONTACTS,
         Restriction.DISALLOW_SET_IMAGE,
         Restriction.DISALLOW_MODIFY_ACCOUNTS,
         Restriction.DISALLOW_REMOTE_DEBUGGING,
         Restriction.DISALLOW_IMPORT_SETTINGS
     );
 
     // Restricted profiles will automatically have these restrictions by default
-    private static List<Restriction> defaultRestrictionsOfRestrictedProfiles = Arrays.asList(
+    static List<Restriction> RESTRICTED_PROFILE_RESTRICTIONS = Arrays.asList(
+        Restriction.DISALLOW_INSTALL_EXTENSION,
+        Restriction.DISALLOW_INSTALL_APPS,
         Restriction.DISALLOW_TOOLS_MENU,
         Restriction.DISALLOW_REPORT_SITE_ISSUE,
         Restriction.DISALLOW_IMPORT_SETTINGS
     );
 
     private static Restriction geckoActionToRestriction(int action) {
         for (Restriction rest : Restriction.values()) {
             if (rest.id == action) {
@@ -120,45 +147,55 @@ public class RestrictedProfiles {
     }
 
     @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
     private static Bundle getRestrictions(final Context context) {
         final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE);
         return mgr.getUserRestrictions();
     }
 
+    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+    private static Bundle getAppRestrictions(final Context context) {
+        final UserManager mgr = (UserManager) context.getSystemService(Context.USER_SERVICE);
+        return mgr.getApplicationRestrictions(context.getPackageName());
+    }
+
     /**
      * This method does the system version check for you.
      *
      * Returns false if the system doesn't support restrictions,
      * or the provided value is not present in the set of user
      * restrictions.
      *
      * Returns true otherwise.
      */
-    private static boolean getRestriction(final Context context, final String name) {
+    private static boolean getRestriction(final Context context, final Restriction restriction) {
         // Early versions don't support restrictions at all,
         // so no action can be restricted.
         if (Versions.preJBMR2) {
             return false;
         }
 
-        return getRestrictions(context).getBoolean(name, false);
+        if (!isUserRestricted(context)) {
+            return false;
+        }
+
+        return getAppRestrictions(context).getBoolean(restriction.name, RESTRICTED_PROFILE_RESTRICTIONS.contains(restriction));
     }
 
     private static boolean canLoadUrl(final Context context, final String url) {
         // Null URLs are always permitted.
         if (url == null) {
             return true;
         }
 
         try {
             // If we're not in guest mode, and the system restriction isn't in place, everything is allowed.
             if (!getInGuest() &&
-                !getRestriction(context, Restriction.DISALLOW_BROWSE_FILES.name)) {
+                !getRestriction(context, Restriction.DISALLOW_BROWSE_FILES)) {
                 return true;
             }
         } catch (IllegalArgumentException ex) {
             Log.i(LOGTAG, "Invalid action", ex);
         }
 
         final Uri u = Uri.parse(url);
         final String scheme = u.getScheme();
@@ -226,26 +263,21 @@ public class RestrictedProfiles {
     }
 
     private static boolean isAllowed(final Context context, final Restriction restriction, String url) {
         if (getInGuest()) {
             if (Restriction.DISALLOW_BROWSE_FILES == restriction) {
                 return canLoadUrl(context, url);
             }
 
-            return !restrictionsOfGuestProfile.contains(restriction);
-        }
-
-        // Hardcoded restrictions. Make restrictions configurable and read from UserManager (Bug 1180653)
-        if (isUserRestricted(context) && defaultRestrictionsOfRestrictedProfiles.contains(restriction)) {
-            return false;
+            return !GUEST_RESTRICTIONS.contains(restriction);
         }
 
         // NOTE: Restrictions hold the opposite intention, so we need to flip it.
-        return !getRestriction(context, restriction.name);
+        return !getRestriction(context, restriction);
     }
 
     @WrapElementForJNI
     public static String getUserRestrictions() {
         return getUserRestrictions(GeckoAppShell.getContext());
     }
 
     private static String getUserRestrictions(final Context context) {
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/RestrictionProvider.java
@@ -0,0 +1,69 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko;
+
+import android.annotation.TargetApi;
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.RestrictionEntry;
+import android.os.Build;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Broadcast receiver providing supported restrictions to the system.
+ */
+@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
+public class RestrictionProvider extends BroadcastReceiver {
+    @Override
+    public void onReceive(final Context context, final Intent intent) {
+        if (AppConstants.Versions.preJBMR2) {
+            // This broadcast does not make any sense prior to Jelly Bean MR2.
+            return;
+        }
+
+        final PendingResult result = goAsync();
+
+        new Thread() {
+            @Override
+            public void run() {
+                final Bundle oldRestrictions = intent.getBundleExtra(Intent.EXTRA_RESTRICTIONS_BUNDLE);
+                final Bundle extras = new Bundle();
+
+                ArrayList<RestrictionEntry> entries = initRestrictions(context, oldRestrictions);
+                extras.putParcelableArrayList(Intent.EXTRA_RESTRICTIONS_LIST, entries);
+
+                result.setResult(Activity.RESULT_OK, null, extras);
+                result.finish();
+            }
+        }.start();
+    }
+
+    private ArrayList<RestrictionEntry> initRestrictions(Context context, Bundle oldRestrictions) {
+        ArrayList<RestrictionEntry> entries = new ArrayList<RestrictionEntry>();
+
+        for (RestrictedProfiles.Restriction restriction : RestrictedProfiles.RESTRICTED_PROFILE_RESTRICTIONS) {
+            RestrictionEntry entry = createRestrictionEntryWithDefaultValue(context, restriction,
+                    oldRestrictions.getBoolean(restriction.name, true));
+            entries.add(entry);
+        }
+
+        return entries;
+    }
+
+    private RestrictionEntry createRestrictionEntryWithDefaultValue(Context context, RestrictedProfiles.Restriction restriction, boolean defaultValue) {
+        RestrictionEntry entry = new RestrictionEntry(restriction.name, defaultValue);
+
+        entry.setTitle(restriction.getTitle(context));
+        entry.setDescription(restriction.getDescription(context));
+
+        return entry;
+    }
+}
--- a/mobile/android/base/ZoomedView.java
+++ b/mobile/android/base/ZoomedView.java
@@ -496,17 +496,17 @@ public class ZoomedView extends FrameLay
         // Later, it will be converted relative to the zoomed view as soon as
         // the position of the zoomed view will be calculated.
         animationStart.x = (float) leftFromGecko * metrics.zoomFactor + offset.x;
         animationStart.y = (float) topFromGecko * metrics.zoomFactor + offset.y;
 
         moveUsingGeckoPosition(leftFromGecko, topFromGecko);
     }
 
-    private void stopZoomDisplay(boolean withAnimation) {
+    public void stopZoomDisplay(boolean withAnimation) {
         if (getVisibility() == View.VISIBLE) {
             shouldSetVisibleOnUpdate = false;
             hideZoomedView(withAnimation);
             ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable);
             if (layerView != null) {
                 layerView.setOnMetricsChangedZoomedViewportListener(null);
                 layerView.removeZoomedViewListener(this);
                 layerView = null;
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -673,8 +673,23 @@ just addresses the organization to follo
 
 <!-- DevTools Authentication -->
 <!-- LOCALIZATION NOTE (devtools_auth_scan_header): This header text appears
      above a QR reader that is actively scanning for QR codes.  The expected QR
      code has already been displayed by the client trying to connect (such as
      desktop Firefox via WebIDE), so you just need to aim this device at the QR
      code. -->
 <!ENTITY devtools_auth_scan_header "Scanning for the QR code displayed on your other device">
+
+<!-- Restrictions -->
+<!-- Localization note: These are restrictions the device owner (e.g. parent) can enable for
+     a restricted profile (e.g. child). Used inside the Android settings UI. -->
+<!ENTITY restriction_disallow_tools_menu_title "Disallow Tools menu">
+<!ENTITY restriction_disallow_tools_menu_description "Hide Tools menu from UI.">
+<!ENTITY restriction_disallow_report_site_issue_title "Disallow \'Report site issue\'">
+<!ENTITY restriction_disallow_report_site_issue_description "Hide \'Report site issue\' menu item.">
+<!ENTITY restriction_disallow_import_settings_title "Disallow importing settings">
+<!ENTITY restriction_disallow_import_settings_description "Do not allow to import settings from other system browsers.">
+<!ENTITY restriction_disallow_addons_title "Disallow add-ons">
+<!ENTITY restriction_disallow_addons_description "Disallow installation of add-ons.">
+<!ENTITY restriction_disallow_apps_title "Disallow apps">
+<!ENTITY restriction_disallow_apps_description "Disallow installing apps from Firefox Marketplace.">
+
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -428,16 +428,17 @@ gbjar.sources += [
     'prompts/PromptService.java',
     'prompts/TabInput.java',
     'ReaderModeUtils.java',
     'ReadingListHelper.java',
     'RemoteClientsDialogFragment.java',
     'RemoteTabsExpandableListAdapter.java',
     'Restarter.java',
     'RestrictedProfiles.java',
+    'RestrictionProvider.java',
     'ServiceNotificationClient.java',
     'SessionParser.java',
     'SharedPreferencesHelper.java',
     'SiteIdentity.java',
     'SmsManager.java',
     'sqlite/ByteBufferInputStream.java',
     'sqlite/MatrixBlobCursor.java',
     'sqlite/SQLiteBridge.java',
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -536,16 +536,28 @@
   <string name="exit_guest_session_text">&exit_guest_session_text;</string>
 
   <string name="actionbar_menu">&actionbar_menu;</string>
   <string name="actionbar_done">&actionbar_done;</string>
 
   <!-- Voice search from the Awesome Bar -->
   <string name="voicesearch_prompt">&voicesearch_prompt;</string>
 
+  <!-- Restrictions -->
+  <string name="restriction_disallow_tools_menu_title">&restriction_disallow_tools_menu_title;</string>
+  <string name="restriction_disallow_tools_menu_description">&restriction_disallow_tools_menu_description;</string>
+  <string name="restriction_disallow_report_site_issue_title">&restriction_disallow_report_site_issue_title;</string>
+  <string name="restriction_disallow_report_site_issue_description">&restriction_disallow_report_site_issue_description;</string>
+  <string name="restriction_disallow_import_settings_title">&restriction_disallow_import_settings_title;</string>
+  <string name="restriction_disallow_import_settings_description">&restriction_disallow_import_settings_description;</string>
+  <string name="restriction_disallow_addons_title">&restriction_disallow_addons_title;</string>
+  <string name="restriction_disallow_addons_description">&restriction_disallow_addons_description;</string>
+  <string name="restriction_disallow_apps_title">&restriction_disallow_apps_title;</string>
+  <string name="restriction_disallow_apps_description">&restriction_disallow_apps_description;</string>
+
   <!-- Miscellaneous -->
   <string name="ellipsis">&ellipsis;</string>
 
   <string name="colon">&colon;</string>
 
   <string name="percent">&percent;</string>
 
   <string name="remote_tabs_last_synced">&remote_tabs_last_synced;</string>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4256,18 +4256,22 @@ pref("layers.async-pan-zoom.enabled", tr
 
 #ifdef MOZ_WIDGET_UIKIT
 pref("layers.async-pan-zoom.enabled", true);
 #endif
 
 #ifdef XP_MACOSX
 pref("layers.enable-tiles", true);
 pref("layers.tiled-drawtarget.enabled", true);
+pref("layers.tiles.edge-padding", false);
 #endif
 
+#ifdef MOZ_WIDGET_GONK
+pref("layers.tiled-drawtarget.enabled", true);
+#endif
 
 // same effect as layers.offmainthreadcomposition.enabled, but specifically for
 // use with tests.
 pref("layers.offmainthreadcomposition.testing.enabled", false);
 
 // whether to allow use of the basic compositor
 pref("layers.offmainthreadcomposition.force-basic", false);
 
--- a/mozglue/misc/TimeStamp.h
+++ b/mozglue/misc/TimeStamp.h
@@ -421,18 +421,18 @@ public:
    * Now() is trying to ensure the best possible precision on each platform,
    * at least one millisecond.
    *
    * NowLoRes() has been introduced to workaround performance problems of
    * QueryPerformanceCounter on the Windows platform.  NowLoRes() is giving
    * lower precision, usually 15.6 ms, but with very good performance benefit.
    * Use it for measurements of longer times, like >200ms timeouts.
    */
-  static MFBT_API TimeStamp Now() { return Now(true); }
-  static MFBT_API TimeStamp NowLoRes() { return Now(false); }
+  static TimeStamp Now() { return Now(true); }
+  static TimeStamp NowLoRes() { return Now(false); }
 
   /**
    * Return a timestamp representing the time when the current process was
    * created which will be comparable with other timestamps taken with this
    * class. If the actual process creation time is detected to be inconsistent
    * the @a aIsInconsistent parameter will be set to true, the returned
    * timestamp however will still be valid though inaccurate.
    *
--- a/python/mozbuild/mozbuild/backend/visualstudio.py
+++ b/python/mozbuild/mozbuild/backend/visualstudio.py
@@ -37,29 +37,45 @@ def get_id(name):
 # TODO validate mappings are correct. only 2010 confirmed so far
 def visual_studio_product_to_internal_version(version, solution=False):
     if solution:
         if version == '2010':
             return '11.00'
         elif version == '2011':
             return '12.00'
         elif version == '2012':
-            return '13.00'
+            return '12.00'
+        elif version == '2013':
+            return '12.00'
         else:
             raise Exception('Unknown version seen: %s' % version)
     else:
         if version == '2010':
             return '10.00'
         elif version == '2011':
             return '11.00'
         elif version == '2012':
             return '12.00'
+        elif version == '2013':
+            return '12.00'
         else:
             raise Exception('Unknown version seen: %s' % version)
 
+def visual_studio_product_to_platform_toolset_version(version):
+    if version == '2010':
+        return 'v100'
+    elif version == '2011':
+        return 'v110'
+    elif version == '2012':
+        return 'v120'
+    elif version == '2013':
+        return 'v120'
+    else:
+        raise Exception('Unknown version seen: %s' % version)
+
 class VisualStudioBackend(CommonBackend):
     """Generate Visual Studio project files.
 
     This backend is used to produce Visual Studio projects and a solution
     to foster developing Firefox with Visual Studio.
 
     This backend is currently considered experimental. There are many things
     not optimal about how it works.
@@ -70,18 +86,19 @@ class VisualStudioBackend(CommonBackend)
     oddly under MozillaBuild.
     """
 
     def _init(self):
         CommonBackend._init(self)
 
         # These should eventually evolve into parameters.
         self._out_dir = os.path.join(self.environment.topobjdir, 'msvc')
+        self._projsubdir = 'projects'
         # But making this one a parameter requires testing first.
-        self._version = '2010'
+        self._version = '2013'
 
         self._paths_to_sources = {}
         self._paths_to_includes = {}
         self._paths_to_defines = {}
         self._paths_to_configs = {}
         self._libs_to_paths = {}
 
         def detailed(summary):
@@ -136,21 +153,27 @@ class VisualStudioBackend(CommonBackend)
     def _process_unified_sources(self, obj):
         reldir = getattr(obj, 'relativedir', None)
 
         s = self._paths_to_sources.setdefault(reldir, set())
         s.update(obj.files)
 
     def consume_finished(self):
         out_dir = self._out_dir
+        out_proj_dir = os.path.join(self._out_dir, self._projsubdir)
         try:
             os.makedirs(out_dir)
         except OSError as e:
             if e.errno != errno.EEXIST:
                 raise
+        try:
+            os.makedirs(out_proj_dir)
+        except OSError as e:
+            if e.errno != errno.EEXIST:
+                raise
 
         projects = {}
 
         for lib, path in sorted(self._libs_to_paths.items()):
             config = self._paths_to_configs.get(path, None)
             sources = self._paths_to_sources.get(path, set())
             sources = set(os.path.join('$(TopSrcDir)', path, s) for s in sources)
             sources = set(os.path.normpath(s) for s in sources)
@@ -189,61 +212,61 @@ class VisualStudioBackend(CommonBackend)
             defines = []
             for k, v in self._paths_to_defines.get(path, {}).items():
                 if v is True:
                     defines.append(k)
                 else:
                     defines.append('%s=%s' % (k, v))
 
             basename = 'library_%s' % lib
-            project_id = self._write_vs_project(out_dir, basename, lib,
+            project_id = self._write_vs_project(out_proj_dir, basename, lib,
                 includes=includes,
                 forced_includes=['$(TopObjDir)\\dist\\include\\mozilla-config.h'],
                 defines=defines,
                 headers=headers,
                 sources=sources)
 
             projects[basename] = (project_id, basename, lib)
 
         # Generate projects that can be used to build common targets.
         for target in ('export', 'binaries', 'tools', 'full'):
             basename = 'target_%s' % target
             command = '$(SolutionDir)\\mach.bat build'
             if target != 'full':
                 command += ' %s' % target
 
-            project_id = self._write_vs_project(out_dir, basename, target,
+            project_id = self._write_vs_project(out_proj_dir, basename, target,
                 build_command=command,
                 clean_command='$(SolutionDir)\\mach.bat build clean')
 
             projects[basename] = (project_id, basename, target)
 
         # A project that can be used to regenerate the visual studio projects.
         basename = 'target_vs'
-        project_id = self._write_vs_project(out_dir, basename, 'visual-studio',
+        project_id = self._write_vs_project(out_proj_dir, basename, 'visual-studio',
             build_command='$(SolutionDir)\\mach.bat build-backend -b VisualStudio')
         projects[basename] = (project_id, basename, 'visual-studio')
 
         # A project to run the main application binary.
         app_name = self.environment.substs['MOZ_APP_NAME']
         basename = 'binary_%s' % app_name
-        project_id = self._write_vs_project(out_dir, basename, app_name,
+        project_id = self._write_vs_project(out_proj_dir, basename, app_name,
             debugger=('$(TopObjDir)\\dist\\bin\\%s.exe' % app_name,
                 '-no-remote'))
         projects[basename] = (project_id, basename, app_name)
 
         # Projects to run other common binaries.
         for app in ['js', 'xpcshell']:
             basename = 'binary_%s' % app
-            project_id = self._write_vs_project(out_dir, basename, app,
+            project_id = self._write_vs_project(out_proj_dir, basename, app,
                 debugger=('$(TopObjDir)\\dist\\bin\\%s.exe' % app, ''))
             projects[basename] = (project_id, basename, app)
 
         # Write out a shared property file with common variables.
-        props_path = os.path.join(out_dir, 'mozilla.props')
+        props_path = os.path.join(out_proj_dir, 'mozilla.props')
         with open(props_path, 'wb') as fh:
             self._write_props(fh)
 
         # Generate some wrapper scripts that allow us to invoke mach inside
         # a MozillaBuild-like environment. We currently only use the batch
         # script. We'd like to use the PowerShell script. However, it seems
         # to buffer output from within Visual Studio (surely this is
         # configurable) and the default execution policy of PowerShell doesn't
@@ -269,17 +292,17 @@ class VisualStudioBackend(CommonBackend)
             version)
         fh.write('# Visual Studio %s\r\n' % self._version)
 
         binaries_id = projects['target_binaries'][0]
 
         # Write out entries for each project.
         for key in sorted(projects):
             project_id, basename, name = projects[key]
-            path = '%s.vcxproj' % basename
+            path = os.path.join(self._projsubdir, '%s.vcxproj' % basename)
 
             fh.write('Project("{%s}") = "%s", "%s", "{%s}"\r\n' % (
                 project_type, name, path, project_id))
 
             # Make all libraries depend on the binaries target.
             if key.startswith('library_'):
                 fh.write('\tProjectSection(ProjectDependencies) = postProject\r\n')
                 fh.write('\t\t{%s} = {%s}\r\n' % (binaries_id, binaries_id))
@@ -441,36 +464,36 @@ class VisualStudioBackend(CommonBackend)
 
         # We go through mach because it has the logic for choosing the most
         # appropriate build tool.
         fh.write(b'"%%MOZILLABUILD%%\\msys\\bin\\bash" '
             b'-c "%s/mach --log-no-times %%1 %%2 %%3 %%4 %%5 %%6 %%7"' % relpath)
 
     def _write_vs_project(self, out_dir, basename, name, **kwargs):
         root = '%s.vcxproj' % basename
+        project_id = get_id(basename.encode('utf-8'))
+
         with open(os.path.join(out_dir, root), 'wb') as fh:
             project_id, name = VisualStudioBackend.write_vs_project(fh,
-                self._version, name, **kwargs)
+                self._version, project_id, name, **kwargs)
 
         with open(os.path.join(out_dir, '%s.user' % root), 'w') as fh:
             fh.write('<?xml version="1.0" encoding="utf-8"?>\r\n')
             fh.write('<Project ToolsVersion="4.0" xmlns="%s">\r\n' %
                 MSBUILD_NAMESPACE)
             fh.write('</Project>\r\n')
 
         return project_id
 
     @staticmethod
-    def write_vs_project(fh, version, name, includes=[],
+    def write_vs_project(fh, version, project_id, name, includes=[],
         forced_includes=[], defines=[],
         build_command=None, clean_command=None,
         debugger=None, headers=[], sources=[]):
 
-        project_id = get_id(name.encode('utf-8'))
-
         impl = getDOMImplementation()
         doc = impl.createDocument(MSBUILD_NAMESPACE, 'Project', None)
 
         project = doc.documentElement
         project.setAttribute('DefaultTargets', 'Build')
         project.setAttribute('ToolsVersion', '4.0')
         project.setAttribute('xmlns', MSBUILD_NAMESPACE)
 
@@ -496,16 +519,19 @@ class VisualStudioBackend(CommonBackend)
         k.appendChild(doc.createTextNode('MakeFileProj'))
 
         g = pg.appendChild(doc.createElement('ProjectGuid'))
         g.appendChild(doc.createTextNode('{%s}' % project_id))
 
         rn = pg.appendChild(doc.createElement('RootNamespace'))
         rn.appendChild(doc.createTextNode('mozilla'))
 
+        pts = pg.appendChild(doc.createElement('PlatformToolset'))
+        pts.appendChild(doc.createTextNode(visual_studio_product_to_platform_toolset_version(version)))
+
         i = project.appendChild(doc.createElement('Import'))
         i.setAttribute('Project', '$(VCTargetsPath)\\Microsoft.Cpp.Default.props')
 
         ig = project.appendChild(doc.createElement('ImportGroup'))
         ig.setAttribute('Label', 'ExtensionTargets')
 
         ig = project.appendChild(doc.createElement('ImportGroup'))
         ig.setAttribute('Label', 'ExtensionSettings')
--- a/security/sandbox/mac/Sandbox.mm
+++ b/security/sandbox/mac/Sandbox.mm
@@ -321,17 +321,18 @@ static const char contentSandboxRules[] 
   "    (allow file-read*\n"
   "        (var-folders2-regex \"/com\\.apple\\.IconServices/\")\n"
   "        (var-folders2-regex \"/[^/]+\\.mozrunner/extensions/[^/]+/chrome/[^/]+/content/[^/]+\\.j(s|ar)$\"))\n"
   "\n"
   "    (allow file-write* (var-folders2-regex \"/org\\.chromium\\.[a-zA-Z0-9]*$\"))\n"
   "    (allow file-read*\n"
   "        (home-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
   "        (resolving-regex \"/Library/Application Support/[^/]+/Extensions/[^/]/\")\n"
-  "        (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/extensions/\"))\n"
+  "        (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/extensions/\")\n"
+  "        (home-regex \"/Library/Application Support/Firefox/Profiles/[^/]+/weave/\"))\n"
   "\n"
   "; the following rules should be removed when printing and \n"
   "; opening a file from disk are brokered through the main process\n"
   "    (if\n"
   "      (< sandbox-level 2)\n"
   "      (allow file*\n"
   "          (require-not\n"
   "              (home-subpath \"/Library\")))\n"
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -414,16 +414,20 @@ XPCOMUtils.defineLazyGetter(this, "Prefs
     store.matchTitleToken = prefs.get(...PREF_MATCH_TITLE);
     store.matchURLToken = prefs.get(...PREF_MATCH_URL);
     store.suggestHistory = prefs.get(...PREF_SUGGEST_HISTORY);
     store.suggestBookmark = prefs.get(...PREF_SUGGEST_BOOKMARK);
     store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE);
     store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED);
     store.suggestSearches = prefs.get(...PREF_SUGGEST_SEARCHES);
     store.maxCharsForSearchSuggestions = prefs.get(...PREF_MAX_CHARS_FOR_SUGGEST);
+    store.keywordEnabled = true;
+    try {
+      store.keywordEnabled = Services.prefs.getBoolPref("keyword.enabled");
+    } catch (ex) {}
 
     // If history is not set, onlyTyped value should be ignored.
     if (!store.suggestHistory) {
       store.suggestTyped = false;
     }
     store.defaultBehavior = types.concat("Typed").reduce((memo, type) => {
       let prefValue = store["suggest" + type];
       return memo | (prefValue &&
@@ -468,24 +472,27 @@ XPCOMUtils.defineLazyGetter(this, "Prefs
     observe(subject, topic, data) {
       // Avoid re-entrancy when flipping linked preferences.
       if (this._ignoreNotifications)
         return;
       this._ignoreNotifications = true;
       loadPrefs(subject, topic, data);
       this._ignoreNotifications = false;
     },
-    QueryInterface: XPCOMUtils.generateQI([ Ci.nsIObserver ])
+    QueryInterface: XPCOMUtils.generateQI([
+      Ci.nsIObserver,
+      Ci.nsISupportsWeakReference ])
   };
 
   // Synchronize suggest.* prefs with autocomplete.enabled at initialization
   syncEnabledPref();
 
   loadPrefs();
   prefs.observe("", store);
+  Services.prefs.addObserver("keyword.enabled", store, true);
 
   return Object.seal(store);
 });
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Helper functions
 
 /**
@@ -1169,17 +1176,19 @@ Search.prototype = {
     // schemes we know should have a host. This allows new schemes to be
     // implemented without us accidentally blocking access to them.
     let hostExpected = new Set(["http", "https", "ftp", "chrome", "resource"]);
     if (hostExpected.has(uri.scheme) && !uri.host)
       return false;
 
     // If the result is something that looks like a single-worded hostname
     // we need to check the domain whitelist to treat it as such.
+    // We also want to return a "visit" if keyword.enabled is false.
     if (uri.asciiHost &&
+        Prefs.keywordEnabled &&
         REGEXP_SINGLEWORD_HOST.test(uri.asciiHost) &&
         !Services.uriFixup.isDomainWhitelisted(uri.asciiHost, -1)) {
       return false;
     }
 
     let value = makeActionURL("visiturl", {
       url: uri.spec,
       input: this._originalSearchString,
--- a/toolkit/components/places/tests/unifiedcomplete/test_visiturl.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_visiturl.js
@@ -56,9 +56,21 @@ add_task(function*() {
   });
 
   do_print("visit url, ipv6 literal");
   yield check_autocomplete({
     search: "[2001:db8::1]",
     searchParam: "enable-actions",
     matches: [ makeVisitMatch("[2001:db8::1]", "http://[2001:db8::1]/") ]
   });
+
+  // Setting keyword.enabled to false should always try to visit.
+  Services.prefs.setBoolPref("keyword.enabled", false);
+  do_register_cleanup(() => {
+    Services.prefs.clearUserPref("keyword.enabled");
+  });
+  do_print("visit url, keyword.enabled = false");
+  yield check_autocomplete({
+    search: "bacon",
+    searchParam: "enable-actions",
+    matches: [ makeVisitMatch("bacon", "http://bacon/") ]
+  });
 });
--- a/toolkit/components/telemetry/TelemetryController.jsm
+++ b/toolkit/components/telemetry/TelemetryController.jsm
@@ -588,19 +588,30 @@ let Impl = {
   },
 
   /**
    * Perform telemetry initialization for either chrome or content process.
    * @return {Boolean} True if Telemetry is allowed to record at least base (FHR) data,
    *                   false otherwise.
    */
   enableTelemetryRecording: function enableTelemetryRecording() {
-    const enabled = Preferences.get(PREF_ENABLED, false);
+    // The thumbnail service also runs in a content process, even with e10s off.
+    // We need to check if e10s is on so we don't submit child payloads for it.
+    // We still need xpcshell child tests to work, so we skip this if test mode is enabled.
+    if (Utils.isContentProcess && !this._testMode && !Services.appinfo.browserTabsRemoteAutostart) {
+      this._log.config("enableTelemetryRecording - not enabling Telemetry for non-e10s child process");
+      Telemetry.canRecordBase = false;
+      Telemetry.canRecordExtended = false;
+      return false;
+    }
 
-    // Enable base Telemetry recording, if needed.
+    // Configure base Telemetry recording.
+    // Unified Telemetry makes it opt-out unless the unifedOptin pref is set.
+    // If extended Telemetry is enabled, base recording is always on as well.
+    const enabled = Preferences.get(PREF_ENABLED, false);
     Telemetry.canRecordBase = enabled || (IS_UNIFIED_TELEMETRY && !IS_UNIFIED_OPTIN);
 
 #ifdef MOZILLA_OFFICIAL
     // Enable extended telemetry if:
     //  * the telemetry preference is set and
     //  * this is an official build or we are in test-mode
     // We only do the latter check for official builds so that e.g. developer builds
     // still enable Telemetry based on prefs.
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -22,23 +22,16 @@ Cu.import("resource://gre/modules/Task.j
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/TelemetrySend.jsm", this);
 Cu.import("resource://gre/modules/TelemetryUtils.jsm", this);
 
 const Utils = TelemetryUtils;
 
 const myScope = this;
 
-const IS_CONTENT_PROCESS = (function() {
-  // We cannot use Services.appinfo here because in telemetry xpcshell tests,
-  // appinfo is initially unavailable, and becomes available only later on.
-  let runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
-  return runtime.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
-})();
-
 // When modifying the payload in incompatible ways, please bump this version number
 const PAYLOAD_VERSION = 4;
 const PING_TYPE_MAIN = "main";
 const PING_TYPE_SAVED_SESSION = "saved-session";
 
 const REASON_ABORTED_SESSION = "aborted-session";
 const REASON_DAILY = "daily";
 const REASON_SAVED_SESSION = "saved-session";
@@ -53,17 +46,17 @@ const ENVIRONMENT_CHANGE_LISTENER = "Tel
 const MS_IN_ONE_HOUR  = 60 * 60 * 1000;
 const MIN_SUBSESSION_LENGTH_MS = 10 * 60 * 1000;
 
 // This is the HG changeset of the Histogram.json file, used to associate
 // submitted ping data with its histogram definition (bug 832007)
 #expand const HISTOGRAMS_FILE_VERSION = "__HISTOGRAMS_FILE_VERSION__";
 
 const LOGGER_NAME = "Toolkit.Telemetry";
-const LOGGER_PREFIX = "TelemetrySession" + (IS_CONTENT_PROCESS ? "#content::" : "::");
+const LOGGER_PREFIX = "TelemetrySession" + (Utils.isContentProcess ? "#content::" : "::");
 
 const PREF_BRANCH = "toolkit.telemetry.";
 const PREF_PREVIOUS_BUILDID = PREF_BRANCH + "previousBuildID";
 const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled";
 const PREF_ASYNC_PLUGIN_INIT = "dom.ipc.plugins.asyncInit";
 const PREF_UNIFIED = PREF_BRANCH + "unified";
 
 
@@ -533,42 +526,30 @@ let TelemetryScheduler = {
     let nowDate = Policy.now();
     let now = nowDate.getTime();
 
     if (now - this._lastTickTime > 1.1 * SCHEDULER_TICK_INTERVAL_MS) {
       this._log.trace("_schedulerTickLogic - First scheduler tick after sleep or startup.");
     }
     this._lastTickTime = now;
 
-    // Check if aborted-session ping is due.
-    let isAbortedPingDue =
-      (now - this._lastSessionCheckpointTime) >= ABORTED_SESSION_UPDATE_INTERVAL_MS;
-    // Check if daily ping is due.
-    let shouldSendDaily = this._isDailyPingDue(nowDate);
-    // We can combine the daily-ping and the aborted-session ping in the following cases:
-    // - If both the daily and the aborted session pings are due (a laptop that wakes
-    //   up after a few hours).
-    // - If either the daily ping is due and the other one would follow up shortly
-    //   (whithin the coalescence threshold).
-    let nextSessionCheckpoint =
-      this._lastSessionCheckpointTime + ABORTED_SESSION_UPDATE_INTERVAL_MS;
-    let combineActions = (shouldSendDaily && isAbortedPingDue) || (shouldSendDaily &&
-                          Utils.areTimesClose(now, nextSessionCheckpoint,
-                                                       SCHEDULER_COALESCE_THRESHOLD_MS));
+    // Check if the daily ping is due.
+    const shouldSendDaily = this._isDailyPingDue(nowDate);
 
-    if (combineActions) {
-      this._log.trace("_schedulerTickLogic - Combining pings.");
-      // Send the daily ping and also save its payload as an aborted-session ping.
-      return Impl._sendDailyPing(true).then(() => this._dailyPingSucceeded(now),
-                                            () => this._dailyPingFailed(now));
-    } else if (shouldSendDaily) {
+    if (shouldSendDaily) {
       this._log.trace("_schedulerTickLogic - Daily ping due.");
       return Impl._sendDailyPing().then(() => this._dailyPingSucceeded(now),
                                         () => this._dailyPingFailed(now));
-    } else if (isAbortedPingDue) {
+    }
+
+    // Check if the aborted-session ping is due. If a daily ping was saved above, it was
+    // already duplicated as an aborted-session ping.
+    const isAbortedPingDue =
+      (now - this._lastSessionCheckpointTime) >= ABORTED_SESSION_UPDATE_INTERVAL_MS;
+    if (isAbortedPingDue) {
       this._log.trace("_schedulerTickLogic - Aborted session ping due.");
       return this._saveAbortedPing(now);
     }
 
     // No ping is due.
     this._log.trace("_schedulerTickLogic - No ping due.");
     // It's possible, because of sleeps, that we're no longer within midnight tolerance for
     // daily pings. Because of that, daily retry attempts would not be 0 on the next midnight.
@@ -744,16 +725,22 @@ this.TelemetrySession = Object.freeze({
    * Used only for testing purposes.
    */
   setup: function() {
     return Impl.setupChromeProcess(true);
   },
   /**
    * Used only for testing purposes.
    */
+  setupContent: function() {
+    return Impl.setupContentProcess(true);
+  },
+  /**
+   * Used only for testing purposes.
+   */
   uninstall: function() {
     try {
       Impl.uninstall();
     } catch (ex) {
       // Ignore errors
     }
   },
   /**
@@ -848,17 +835,17 @@ let Impl = {
     var appTimestamps = {};
     try {
       let o = {};
       Cu.import("resource://gre/modules/TelemetryTimestamps.jsm", o);
       appTimestamps = o.TelemetryTimestamps.get();
     } catch (ex) {}
 
     // Only submit this if the extended set is enabled.
-    if (!IS_CONTENT_PROCESS && Telemetry.canRecordExtended) {
+    if (!Utils.isContentProcess && Telemetry.canRecordExtended) {
       try {
         ret.addonManager = AddonManagerPrivate.getSimpleMeasures();
         ret.UITelemetry = UITelemetry.getSimpleMeasures();
       } catch (ex) {}
     }
 
     if (si.process) {
       for each (let field in Object.keys(si)) {
@@ -877,17 +864,17 @@ let Impl = {
 
     ret.js = Cu.getJSEngineTelemetryValue();
 
     let maximalNumberOfConcurrentThreads = Telemetry.maximalNumberOfConcurrentThreads;
     if (maximalNumberOfConcurrentThreads) {
       ret.maximalNumberOfConcurrentThreads = maximalNumberOfConcurrentThreads;
     }
 
-    if (IS_CONTENT_PROCESS) {
+    if (Utils.isContentProcess) {
       return ret;
     }
 
     // Measurements specific to chrome process
 
     // Update debuggerAttached flag
     let debugService = Cc["@mozilla.org/xpcom/debug;1"].getService(Ci.nsIDebug2);
     let isDebuggerAttached = debugService.isDebuggerAttached;
@@ -1301,17 +1288,17 @@ let Impl = {
 
     // Add extended set measurements common to chrome & content processes
     if (Telemetry.canRecordExtended) {
       payloadObj.chromeHangs = Telemetry.chromeHangs;
       payloadObj.threadHangStats = this.getThreadHangStats(Telemetry.threadHangStats);
       payloadObj.log = TelemetryLog.entries();
     }
 
-    if (IS_CONTENT_PROCESS) {
+    if (Utils.isContentProcess) {
       return payloadObj;
     }
 
     // Additional payload for chrome process.
     payloadObj.info = info;
 
     // Add extended set measurements for chrome process.
     if (Telemetry.canRecordExtended) {
@@ -1359,20 +1346,20 @@ let Impl = {
     clearSubsession = false;
     const isSubsession = false;
 #else
     const isSubsession = !this._isClassicReason(reason);
 #endif
 
     let measurements =
       this.getSimpleMeasurements(reason == REASON_SAVED_SESSION, isSubsession, clearSubsession);
-    let info = !IS_CONTENT_PROCESS ? this.getMetadata(reason) : null;
+    let info = !Utils.isContentProcess ? this.getMetadata(reason) : null;
     let payload = this.assemblePayloadWithMeasurements(measurements, info, reason, clearSubsession);
 
-    if (!IS_CONTENT_PROCESS && clearSubsession) {
+    if (!Utils.isContentProcess && clearSubsession) {
       this.startNewSubsession();
       // Persist session data to disk (don't wait until it completes).
       let sessionData = this._getSessionDataObject();
       this._stateSaveSerializer.enqueueTask(() => this._saveSessionData(sessionData));
     }
 
     return payload;
   },
@@ -1523,34 +1510,35 @@ let Impl = {
 
     this._delayedInitTask.arm();
     return this._delayedInitTaskDeferred.promise;
   },
 
   /**
    * Initializes telemetry for a content process.
    */
-  setupContentProcess: function setupContentProcess() {
+  setupContentProcess: function setupContentProcess(testing) {
     this._log.trace("setupContentProcess");
 
     if (!Telemetry.canRecordBase) {
+      this._log.trace("setupContentProcess - base recording is disabled, not initializing");
       return;
     }
 
     Services.obs.addObserver(this, "content-child-shutdown", false);
     cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD, this);
 
     this.gatherStartupHistograms();
 
     let delayedTask = new DeferredTask(function* () {
       this._initialized = true;
 
       this.attachObservers();
       this.gatherMemory();
-    }.bind(this), TELEMETRY_DELAY);
+    }.bind(this), testing ? TELEMETRY_TEST_DELAY : TELEMETRY_DELAY);
 
     delayedTask.arm();
   },
 
   getFlashVersion: function getFlashVersion() {
     this._log.trace("getFlashVersion");
     let host = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
     let tags = host.getPluginTags();
@@ -1866,35 +1854,36 @@ let Impl = {
      }
 
     // This handles 2) and 3).
     return this._delayedInitTask.finalize().then(cleanup);
    },
 
   /**
    * Gather and send a daily ping.
-   * @param {Boolean} [saveAsAborted=false] Also saves the payload as an aborted-session
-   *                  ping.
    * @return {Promise} Resolved when the ping is sent.
    */
-  _sendDailyPing: function(saveAsAborted = false) {
+  _sendDailyPing: function() {
     this._log.trace("_sendDailyPing");
     let payload = this.getSessionPayload(REASON_DAILY, true);
 
     let options = {
       addClientId: true,
       addEnvironment: true,
     };
 
     let promise = TelemetryController.submitExternalPing(getPingType(payload), payload, options);
-    // If required, also save the payload as an aborted session.
-    if (saveAsAborted && IS_UNIFIED_TELEMETRY) {
+
+    // Also save the payload as an aborted session. If we delay this, aborted-session can
+    // lag behind for the profileSubsessionCounter and other state, complicating analysis.
+    if (IS_UNIFIED_TELEMETRY) {
       let abortedPromise = this._saveAbortedSessionPing(payload);
       promise = promise.then(() => abortedPromise);
     }
+
     return promise;
   },
 
   /**
    * Loads session data from the session data file.
    * @return {Promise<boolean>} A promise which is resolved with a true argument when
    *                            loading has completed, with false otherwise.
    */
--- a/toolkit/components/telemetry/TelemetryUtils.jsm
+++ b/toolkit/components/telemetry/TelemetryUtils.jsm
@@ -7,18 +7,32 @@
 this.EXPORTED_SYMBOLS = [
   "TelemetryUtils"
 ];
 
 const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
 
 const MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000;
 
+const IS_CONTENT_PROCESS = (function() {
+  // We cannot use Services.appinfo here because in telemetry xpcshell tests,
+  // appinfo is initially unavailable, and becomes available only later on.
+  let runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
+  return runtime.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
+})();
+
 this.TelemetryUtils = {
   /**
+   * True if this is a content process.
+   */
+  get isContentProcess() {
+    return IS_CONTENT_PROCESS;
+  },
+
+  /**
    * Turn a millisecond timestamp into a day timestamp.
    *
    * @param aMsec A number of milliseconds since Unix epoch.
    * @return The number of whole days since Unix epoch.
    */
   millisecondsToDays: function(aMsec) {
     return Math.floor(aMsec / MILLISECONDS_PER_DAY);
   },
--- a/toolkit/components/telemetry/docs/index.rst
+++ b/toolkit/components/telemetry/docs/index.rst
@@ -16,9 +16,10 @@ Client-side, this consists of:
    :maxdepth: 2
 
    pings
    common-ping
    environment
    main-ping
    deletion-ping
    crash-ping
+   uitour-ping
    preferences
--- a/toolkit/components/telemetry/docs/preferences.rst
+++ b/toolkit/components/telemetry/docs/preferences.rst
@@ -1,16 +1,22 @@
 Preferences
 ===========
 
 Telemetry behaviour is controlled through the preferences listed here.
 
-*Note:* On official builds (which define ``MOZILLA_OFFICIAL``), Telemetry is only initialized when ``MOZ_TELEMETRY_REPORTING`` is defined.
+Default behaviors
+-----------------
+
+On official builds (which define ``MOZILLA_OFFICIAL``), Telemetry is only initialized when ``MOZ_TELEMETRY_REPORTING`` is defined.
 Sending only happens on official builds with ``MOZ_TELEMETRY_REPORTING`` defined.
 
+Preferences
+-----------
+
 ``toolkit.telemetry.unified``
 
   This controls whether unified behavior is enabled. If true:
 
   * Telemetry is always enabled and recording *base* data.
   * Telemetry will send additional ``main`` pings.
 
 ``toolkit.telemetry.unifiedIsOptIn``
@@ -19,16 +25,20 @@ Sending only happens on official builds 
   Defaults to false & requires a restart.
 
 ``toolkit.telemetry.enabled``
 
   If ``unified`` is off, this controls whether the Telemetry module is enabled.
   If ``unified`` is on, this controls whether to record *extended* data.
   This preference is controlled through the `Preferences` dialog.
 
+  Note that the default value here of this pref depends on the define ``RELEASE_BUILD`` and the channel.
+  If ``RELEASE_BUILD`` is set, ``MOZ_TELEMETRY_ON_BY_DEFAULT`` gets set, which means this pref will default to ``true``.
+  This is overridden by the preferences code on the "beta" channel, the pref also defaults to ``true`` there.
+
 ``datareporting.healthreport.uploadEnabled``
 
   Send the data we record if user has consented to FHR. This preference is controlled through the `Preferences` dialog.
 
 ``toolkit.telemetry.archive.enabled``
 
   Allow pings to be archived locally. This can only be enabled if ``unified`` is on.
 
--- a/toolkit/components/telemetry/tests/unit/test_ChildHistograms.js
+++ b/toolkit/components/telemetry/tests/unit/test_ChildHistograms.js
@@ -1,16 +1,18 @@
 
 Cu.import("resource://gre/modules/TelemetryController.jsm", this);
 Cu.import("resource://gre/modules/TelemetrySession.jsm", this);
 Cu.import("resource://gre/modules/PromiseUtils.jsm", this);
 
 const Telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
 
 const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload";
+const MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD = "Telemetry:GetChildPayload";
+const MESSAGE_CHILD_TEST_DONE = "ChildTest:Done";
 
 const PLATFORM_VERSION = "1.9.2";
 const APP_VERSION = "1";
 const APP_ID = "xpcshell@tests.mozilla.org";
 const APP_NAME = "XPCShell";
 
 function run_child_test() {
   // Setup histograms with some fixed values.
@@ -53,34 +55,42 @@ function check_histogram_values(payload)
                "Keyed flag test histogram should have the right value.");
   Assert.equal(kh["TELEMETRY_TEST_KEYED_FLAG"]["b"].sum, 1,
                "Keyed flag test histogram should have the right value.");
 }
 
 add_task(function*() {
   if (!runningInParent) {
     TelemetryController.setupContent();
+    TelemetrySession.setupContent();
     run_child_test();
+    dump("... done with child test\n");
+    do_send_remote_message(MESSAGE_CHILD_TEST_DONE);
+    dump("... waiting for child payload collection\n");
+    yield do_await_remote_message(MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD);
     return;
   }
 
   // Setup.
   do_get_profile(true);
   loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION);
   Services.prefs.setBoolPref("toolkit.telemetry.enabled", true);
   yield TelemetryController.setup();
   yield TelemetrySession.setup();
 
-  // Run test in child and wait until it is finished.
-  yield run_test_in_child("test_ChildHistograms.js");
+  // Run test in child, don't wait for it to finish.
+  let childPromise = run_test_in_child("test_ChildHistograms.js");
+  yield do_await_remote_message(MESSAGE_CHILD_TEST_DONE);
 
   // Gather payload from child.
+  dump("... requesting child payloads\n");
   let promiseMessage = do_await_remote_message(MESSAGE_TELEMETRY_PAYLOAD);
   TelemetrySession.requestChildPayloads();
   yield promiseMessage;
+  dump("... received child payload\n");
 
   // Check child payload.
   const payload = TelemetrySession.getPayload("test-ping");
   Assert.ok("childPayloads" in payload, "Should have child payloads.");
   Assert.equal(payload.childPayloads.length, 1, "Should have received one child payload so far.");
   Assert.ok("histograms" in payload.childPayloads[0], "Child payload should have histograms.");
   Assert.ok("keyedHistograms" in payload.childPayloads[0], "Child payload should have keyed histograms.");
   check_histogram_values(payload.childPayloads[0]);
deleted file mode 100644
--- a/toolkit/devtools/DevToolsUtils.jsm
+++ /dev/null
@@ -1,18 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-"use strict";
-
-/*
- * General utilities used throughout devtools.
- *
- * When using chrome debugging, the debugger server is unable to debug itself.
- * To avoid this, it must be loaded with a custom devtools loader with the
- * invisibleToDebugger flag set to true. Everyone else, though, prefers a JSM.
- */
-
-this.EXPORTED_SYMBOLS = [ "DevToolsUtils" ];
-
-const { devtools } = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {});
-this.DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils.js");
--- a/toolkit/devtools/Loader.jsm
+++ b/toolkit/devtools/Loader.jsm
@@ -147,17 +147,17 @@ SrcdirProvider.prototype = {
     let outputParserURI = this.fileURI(OS.Path.join(toolkitDir, "output-parser"));
     let clientURI = this.fileURI(OS.Path.join(toolkitDir, "client"));
     let prettyFastURI = this.fileURI(OS.Path.join(toolkitDir), "pretty-fast.js");
     let jsBeautifyURI = this.fileURI(OS.Path.join(toolkitDir, "jsbeautify", "beautify.js"));
     let asyncUtilsURI = this.fileURI(OS.Path.join(toolkitDir), "async-utils.js");
     let contentObserverURI = this.fileURI(OS.Path.join(toolkitDir), "content-observer.js");
     let gcliURI = this.fileURI(OS.Path.join(toolkitDir, "gcli", "source", "lib", "gcli"));
     let projecteditorURI = this.fileURI(OS.Path.join(devtoolsDir, "projecteditor"));
-    let promiseURI = this.fileURI(OS.Path.join(modulesDir, "promise-backend.js"));
+    let promiseURI = this.fileURI(OS.Path.join(modulesDir, "Promise-backend.js"));
     let acornURI = this.fileURI(OS.Path.join(toolkitDir, "acorn"));
     let acornWalkURI = OS.Path.join(acornURI, "walk.js");
     let ternURI = OS.Path.join(toolkitDir, "tern");
     let sourceMapURI = this.fileURI(OS.Path.join(toolkitDir), "SourceMap.jsm");
     this.loader = new loader.Loader({
       id: "fx-devtools",
       modules: loaderModules,
       paths: {
@@ -434,17 +434,19 @@ DevToolsLoader.prototype = {
    */
   reload: function() {
     var events = this.require("sdk/system/events");
     events.emit("startupcache-invalidate", {});
     events.emit("devtools-unloaded", {});
 
     this._provider.unload("reload");
     delete this._provider;
+    delete this._mainid;
     this._chooseProvider();
+    this.main("main");
   },
 
   /**
    * Sets whether the compartments loaded by this instance should be invisible
    * to the debugger.  Invisibility is needed for loaders that support debugging
    * of chrome code.  This is true of remote target environments, like Fennec or
    * B2G.  It is not the default case for desktop Firefox because we offer the
    * Browser Toolbox for chrome debugging there, which uses its own, separate
--- a/toolkit/devtools/client/dbg-client.jsm
+++ b/toolkit/devtools/client/dbg-client.jsm
@@ -45,17 +45,17 @@ XPCOMUtils.defineLazyGetter(this, "event
 Object.defineProperty(this, "WebConsoleClient", {
   get: function () {
     return devtools.require("devtools/toolkit/webconsole/client").WebConsoleClient;
   },
   configurable: true,
   enumerable: true
 });
 
-Components.utils.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
+const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
 this.executeSoon = DevToolsUtils.executeSoon;
 this.makeInfallible = DevToolsUtils.makeInfallible;
 this.values = DevToolsUtils.values;
 
 let LOG_PREF = "devtools.debugger.log";
 let VERBOSE_PREF = "devtools.debugger.log.verbose";
 let wantLogging = Services.prefs.getBoolPref(LOG_PREF);
 let wantVerbose =
--- a/toolkit/devtools/gcli/commands/security.js
+++ b/toolkit/devtools/gcli/commands/security.js
@@ -16,23 +16,30 @@ const CSP = Cc["@mozilla.org/cspcontext;
 
 const GOOD_IMG_SRC = "chrome://browser/content/gcli_sec_good.svg";
 const MOD_IMG_SRC = "chrome://browser/content/gcli_sec_moderate.svg";
 const BAD_IMG_SRC = "chrome://browser/content/gcli_sec_bad.svg";
 
 const CONTENT_SECURITY_POLICY = "Content-Security-Policy";
 const CONTENT_SECURITY_POLICY_REPORT_ONLY = "Content-Security-Policy-Report-Only";
 
-const DIR_UNSAFE_INLINE = "'unsafe-inline'";
-const DIR_UNSAFE_EVAL = "'unsafe-eval'";
+// special handling within policy
 const POLICY_REPORT_ONLY = "report-only"
 
+// special handling of directives
+const DIR_UPGRADE_INSECURE = "upgrade-insecure-requests";
+
+// special handling of sources
+const SRC_UNSAFE_INLINE = "'unsafe-inline'";
+const SRC_UNSAFE_EVAL = "'unsafe-eval'";
+
 const WILDCARD_MSG = l10n.lookup("securityCSPRemWildCard");
 const XSS_WARNING_MSG = l10n.lookup("securityCSPPotentialXSS");
 
+
 exports.items = [
   {
     // --- General Security information
     name: "security",
     description: l10n.lookup("securityDesc"),
     manual: l10n.lookup("securityManual")
   },
   {
@@ -68,28 +75,39 @@ exports.items = [
             outHeader = curPolicy[POLICY_REPORT_ONLY] === true ?
                           CONTENT_SECURITY_POLICY_REPORT_ONLY :
                           CONTENT_SECURITY_POLICY;
             continue;
           }
 
           // loop over all the directive-sources within that directive
           var outSrcs = [];
+
+          // special case handling for upgrade-insecure-requests
+          // which does not have any srcs
+          if (dir === DIR_UPGRADE_INSECURE) {
+            outSrcs.push({
+              icon: GOOD_IMG_SRC,
+              src: "", // no src for upgrade-insecure-requests
+              desc: "" // no description for upgrade-insecure-requests
+            });
+          }
+
           for (var src in curDir) {
             var curSrc = curDir[src];
 
             // the default icon and descritpion of the directive-src
             var outIcon = GOOD_IMG_SRC;
             var outDesc = "";
 
             if (curSrc.indexOf("*") > -1) {
               outIcon = MOD_IMG_SRC;
               outDesc = WILDCARD_MSG;
             }
-            if (curSrc == DIR_UNSAFE_INLINE || curSrc == DIR_UNSAFE_EVAL) {
+            if (curSrc == SRC_UNSAFE_INLINE || curSrc == SRC_UNSAFE_EVAL) {
               outIcon = BAD_IMG_SRC;
               outDesc = XSS_WARNING_MSG;
             }
             outSrcs.push({
               icon: outIcon,
               src: curSrc,
               desc: outDesc
             });
--- a/toolkit/devtools/moz.build
+++ b/toolkit/devtools/moz.build
@@ -37,17 +37,16 @@ EXTRA_JS_MODULES.devtools += [
     'event-parsers.js',
     'output-parser.js',
     'path.js',
     'worker-loader.js',
 ]
 
 EXTRA_JS_MODULES.devtools += [
     'Console.jsm',
-    'DevToolsUtils.jsm',
     'LayoutHelpers.jsm',
     'Loader.jsm',
     'Require.jsm',
 ]
 
 EXTRA_JS_MODULES.devtools.server.actors += [
     'server/actors/highlighter.css'
 ]
--- a/toolkit/devtools/server/actors/highlighter.js
+++ b/toolkit/devtools/server/actors/highlighter.js
@@ -153,16 +153,25 @@ let HighlighterActor = exports.Highlight
     // SimpleOutlineHighlighter, and back, if the top level window changes.
     events.on(this._tabActor, "navigate", this._onNavigate);
   },
 
   get conn() {
     return this._inspector && this._inspector.conn;
   },
 
+  form: function() {
+    return {
+      actor: this.actorID,
+      traits: {
+        autoHideOnDestroy: true
+      }
+    }
+  },
+
   _createHighlighter: function() {
     this._isPreviousWindowXUL = isXUL(this._tabActor.window);
 
     if (!this._isPreviousWindowXUL) {
       this._highlighter = new BoxModelHighlighter(this._highlighterEnv,
                                                   this._inspector);
       this._highlighter.on("ready", this._highlighterReady);
       this._highlighter.on("hide", this._highlighterHidden);
@@ -194,16 +203,17 @@ let HighlighterActor = exports.Highlight
       this._destroyHighlighter();
       this._createHighlighter();
     }
   },
 
   destroy: function() {
     protocol.Actor.prototype.destroy.call(this);
 
+    this.hideBoxModel();
     this._destroyHighlighter();
     events.off(this._tabActor, "navigate", this._onNavigate);
 
     this._highlighterEnv.destroy();
     this._highlighterEnv = null;
 
     this._autohide = null;
     this._inspector = null;
@@ -411,17 +421,24 @@ let HighlighterActor = exports.Highlight
       this._highlighter.hide();
       this._stopPickerListeners();
       this._isPicking = false;
       this._hoveredNode = null;
     }
   })
 });
 
-let HighlighterFront = protocol.FrontClass(HighlighterActor, {});
+let HighlighterFront = protocol.FrontClass(HighlighterActor, {
+  // Update the object given a form representation off the wire.
+  form: function(json) {
+    this.actorID = json.actor;
+    // FF42+ HighlighterActors starts exposing custom form, with traits object
+    this.traits = json.traits || {};
+  }
+});
 
 /**
  * A generic highlighter actor class that instantiate a highlighter given its
  * type name and allows to show/hide it.
  */
 let CustomHighlighterActor = exports.CustomHighlighterActor = protocol.ActorClass({
   typeName: "customhighlighter",
 
--- a/toolkit/devtools/tests/unit/head_devtools.js
+++ b/toolkit/devtools/tests/unit/head_devtools.js
@@ -1,16 +1,16 @@
 "use strict";
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/devtools/Loader.jsm");
-Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
+const DevToolsUtils = devtools.require("devtools/toolkit/DevToolsUtils");
 
 // Register a console listener, so console messages don't just disappear
 // into the ether.
 let errorCount = 0;
 let listener = {
   observe: function (aMessage) {
     errorCount++;
     try {
--- a/toolkit/devtools/webconsole/network-helper.js
+++ b/toolkit/devtools/webconsole/network-helper.js
@@ -51,17 +51,17 @@
  *  Steven Roussey (AppCenter Inc, Network54)
  *  Mihai Sucan (Mozilla Corp.)
  */
 
 "use strict";
 
 const {components, Cc, Ci, Cu} = require("chrome");
 loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
-loader.lazyImporter(this, "DevToolsUtils", "resource://gre/modules/devtools/DevToolsUtils.jsm");
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 // The cache used in the `nsIURL` function.
 const gNSURLStore = new Map();
 
 /**
  * Helper object for networking stuff.
  *
  * Most of the following functions have been taken from the Firebug source. They
--- a/toolkit/devtools/webconsole/utils.js
+++ b/toolkit/devtools/webconsole/utils.js
@@ -13,17 +13,17 @@ Cu.import("resource://gre/modules/XPCOMU
 loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
 loader.lazyImporter(this, "LayoutHelpers", "resource://gre/modules/devtools/LayoutHelpers.jsm");
 
 // TODO: Bug 842672 - toolkit/ imports modules from browser/.
 // Note that these are only used in WebConsoleCommands, see $0 and pprint().
 loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.jsm");
 loader.lazyImporter(this, "devtools", "resource://gre/modules/devtools/Loader.jsm");
 loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
-loader.lazyImporter(this, "DevToolsUtils", "resource://gre/modules/devtools/DevToolsUtils.jsm");
+const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 // Match the function name from the result of toString() or toSource().
 //
 // Examples:
 // (function foobar(a, b) { ...
 // function foobar2(a) { ...
 // function() { ...
 const REGEX_MATCH_FUNCTION_NAME = /^\(?function\s+([^(\s]+)\s*\(/;
--- a/widget/gtk/gtk3drawing.c
+++ b/widget/gtk/gtk3drawing.c
@@ -896,19 +896,17 @@ moz_gtk_button_paint(cairo_t *cr, GdkRec
         gtk_render_background(style, cr, x, y, width, height);
         gtk_render_frame(style, cr, x, y, width, height);
         moz_gtk_button_get_default_border(&default_top, &default_left,
                                           &default_bottom, &default_right);
         x += default_left;
         y += default_top;
         width -= (default_left + default_right);
         height -= (default_top + default_bottom);
-    }
- 
-    if (relief != GTK_RELIEF_NONE || state->depressed ||
+    } else if (relief != GTK_RELIEF_NONE || state->depressed ||
         (state_flags & GTK_STATE_FLAG_PRELIGHT)) {
         /* the following line can trigger an assertion (Crux theme)
            file ../../gdk/gdkwindow.c: line 1846 (gdk_window_clear_area):
            assertion `GDK_IS_WINDOW (window)' failed */
         gtk_render_background(style, cr, x, y, width, height);
         gtk_render_frame(style, cr, x, y, width, height);
     }
 
--- a/widget/gtk/mozgtk/mozgtk.c
+++ b/widget/gtk/mozgtk/mozgtk.c
@@ -502,16 +502,17 @@ STUB(gdk_display_get_device_manager)
 STUB(gdk_error_trap_pop_ignored)
 STUB(gdk_event_get_source_device)
 STUB(gdk_window_get_type)
 STUB(gdk_x11_window_get_xid)
 STUB(gdk_x11_display_get_type)
 STUB(gtk_cairo_should_draw_window)
 STUB(gtk_cairo_transform_to_window)
 STUB(gtk_combo_box_text_append)
+STUB(gtk_drag_set_icon_surface)
 STUB(gtk_get_major_version)
 STUB(gtk_get_micro_version)
 STUB(gtk_get_minor_version)
 STUB(gtk_menu_button_new)
 STUB(gtk_offscreen_window_new)
 STUB(gtk_paned_new)
 STUB(gtk_render_activity)
 STUB(gtk_render_arrow)
@@ -541,16 +542,17 @@ STUB(gtk_style_context_remove_region)
 STUB(gtk_style_context_restore)
 STUB(gtk_style_context_save)
 STUB(gtk_style_context_set_path)
 STUB(gtk_style_context_set_state)
 STUB(gtk_tree_view_column_get_button)
 STUB(gtk_widget_get_preferred_size)
 STUB(gtk_widget_get_style_context)
 STUB(gtk_widget_path_append_type)
+STUB(gtk_widget_path_free)
 STUB(gtk_widget_path_new)
 STUB(gtk_widget_set_visual)
 STUB(gtk_app_chooser_dialog_new_for_content_type)
 STUB(gtk_app_chooser_get_type)
 STUB(gtk_app_chooser_get_app_info)
 STUB(gtk_app_chooser_dialog_get_type)
 STUB(gtk_app_chooser_dialog_set_heading)
 #endif
--- a/widget/gtk/nsDragService.cpp
+++ b/widget/gtk/nsDragService.cpp
@@ -14,16 +14,17 @@
 #include "nsIIOService.h"
 #include "nsIFileURL.h"
 #include "nsNetUtil.h"
 #include "mozilla/Logging.h"
 #include "nsTArray.h"
 #include "nsPrimitiveHelpers.h"
 #include "prtime.h"
 #include "prthread.h"
+#include <dlfcn.h>
 #include <gtk/gtk.h>
 #include <gdk/gdkx.h>
 #include "nsCRT.h"
 #include "mozilla/BasicEvents.h"
 #include "mozilla/Services.h"
 
 #include "gfxASurface.h"
 #include "gfxXlibSurface.h"
@@ -33,16 +34,17 @@
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsISelection.h"
 #include "nsViewManager.h"
 #include "nsIFrame.h"
 #include "nsGtkUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "gfxPlatform.h"
+#include "nsScreenGtk.h"
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 // This sets how opaque the drag image is
 #define DRAG_IMAGE_ALPHA_LEVEL 0.5
 
 // These values are copied from GtkDragResult (rather than using GtkDragResult
@@ -401,24 +403,24 @@ nsDragService::InvokeDragSession(nsIDOMN
 
 bool
 nsDragService::SetAlphaPixmap(SourceSurface *aSurface,
                               GdkDragContext *aContext,
                               int32_t aXOffset,
                               int32_t aYOffset,
                               const nsIntRect& dragRect)
 {
-#if (MOZ_WIDGET_GTK == 2)
     GdkScreen* screen = gtk_widget_get_screen(mHiddenWidget);
 
     // Transparent drag icons need, like a lot of transparency-related things,
     // a compositing X window manager
     if (!gdk_screen_is_composited(screen))
       return false;
 
+#if (MOZ_WIDGET_GTK == 2)
     GdkColormap* alphaColormap = gdk_screen_get_rgba_colormap(screen);
     if (!alphaColormap)
       return false;
 
     GdkPixmap* pixmap = gdk_pixmap_new(nullptr, dragRect.width, dragRect.height,
                                        gdk_colormap_get_visual(alphaColormap)->depth);
     if (!pixmap)
       return false;
@@ -449,18 +451,56 @@ nsDragService::SetAlphaPixmap(SourceSurf
                     DrawOptions(DRAG_IMAGE_ALPHA_LEVEL, CompositionOp::OP_SOURCE));
 
     // The drag transaction addrefs the pixmap, so we can just unref it from us here
     gtk_drag_set_icon_pixmap(aContext, alphaColormap, pixmap, nullptr,
                              aXOffset, aYOffset);
     g_object_unref(pixmap);
     return true;
 #else
-    // TODO GTK3
-    return false;
+#ifdef cairo_image_surface_create
+#error "Looks like we're including Mozilla's cairo instead of system cairo"
+#endif
+    // TODO: grab X11 pixmap or image data instead of expensive readback.
+    cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
+                                                       dragRect.width,
+                                                       dragRect.height);
+    if (!surf)
+        return false;
+
+    RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
+        CreateDrawTargetForData(cairo_image_surface_get_data(surf),
+                                dragRect.Size(),
+                                cairo_image_surface_get_stride(surf),
+                                SurfaceFormat::B8G8R8A8);
+    if (!dt)
+        return false;
+
+    dt->ClearRect(Rect(0, 0, dragRect.width, dragRect.height));
+    dt->DrawSurface(aSurface,
+                    Rect(0, 0, dragRect.width, dragRect.height),
+                    Rect(0, 0, dragRect.width, dragRect.height),
+                    DrawSurfaceOptions(),
+                    DrawOptions(DRAG_IMAGE_ALPHA_LEVEL, CompositionOp::OP_SOURCE));
+
+    cairo_surface_mark_dirty(surf);
+    cairo_surface_set_device_offset(surf, -aXOffset, -aYOffset);
+
+    // Ensure that the surface is drawn at the correct scale on HiDPI displays.
+    static auto sCairoSurfaceSetDeviceScalePtr =
+        (void (*)(cairo_surface_t*,double,double))
+        dlsym(RTLD_DEFAULT, "cairo_surface_set_device_scale");
+    if (sCairoSurfaceSetDeviceScalePtr) {
+        gint scale = nsScreenGtk::GetGtkMonitorScaleFactor();
+        sCairoSurfaceSetDeviceScalePtr(surf, scale, scale);
+    }
+
+    gtk_drag_set_icon_surface(aContext, surf);
+    cairo_surface_destroy(surf);
+    return true;
 #endif
 }
 
 NS_IMETHODIMP
 nsDragService::StartDragSession()
 {
     MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::StartDragSession"));
     return nsBaseDragService::StartDragSession();
--- a/widget/gtk/nsLookAndFeel.cpp
+++ b/widget/gtk/nsLookAndFeel.cpp
@@ -1182,16 +1182,18 @@ nsLookAndFeel::Init()
     GtkWidget *frame = gtk_frame_new(nullptr);
     gtk_container_add(GTK_CONTAINER(parent), frame);
 
     // TODO GTK3 - update sFrameOuterLightBorder 
     // for GTK_BORDER_STYLE_INSET/OUTSET/GROVE/RIDGE border styles (Bug 978172).
     style = gtk_widget_get_style_context(frame);
     gtk_style_context_get_border_color(style, GTK_STATE_FLAG_NORMAL, &color);
     sFrameInnerDarkBorder = sFrameOuterLightBorder = GDK_RGBA_TO_NS_RGBA(color);
+
+    gtk_widget_path_free(path);
 #endif
     // Some themes have a unified menu bar, and support window dragging on it
     gboolean supports_menubar_drag = FALSE;
     GParamSpec *param_spec =
         gtk_widget_class_find_style_property(GTK_WIDGET_GET_CLASS(menuBar),
                                              "window-dragging");
     if (param_spec) {
         if (g_type_is_a(G_PARAM_SPEC_VALUE_TYPE(param_spec), G_TYPE_BOOLEAN)) {
--- a/widget/gtk/nsScreenManagerGtk.cpp
+++ b/widget/gtk/nsScreenManagerGtk.cpp
@@ -3,42 +3,46 @@
  * 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 "nsScreenManagerGtk.h"
 #include "nsScreenGtk.h"
 #include "nsIComponentManager.h"
 #include "nsRect.h"
 #include "nsAutoPtr.h"
+#include "nsGtkUtils.h"
 
 #define SCREEN_MANAGER_LIBRARY_LOAD_FAILED ((PRLibrary*)1)
 
 #ifdef MOZ_X11
 #include <gdk/gdkx.h>
 // prototypes from Xinerama.h
 typedef Bool (*_XnrmIsActive_fn)(Display *dpy);
 typedef XineramaScreenInfo* (*_XnrmQueryScreens_fn)(Display *dpy, int *number);
 #endif
 
 #include <gtk/gtk.h>
 
+void
+monitors_changed(GdkScreen* aScreen, gpointer aClosure)
+{
+  nsScreenManagerGtk *manager = static_cast<nsScreenManagerGtk*>(aClosure);
+  manager->Init();
+}
 
 static GdkFilterReturn
 root_window_event_filter(GdkXEvent *aGdkXEvent, GdkEvent *aGdkEvent,
                          gpointer aClosure)
 {
   nsScreenManagerGtk *manager = static_cast<nsScreenManagerGtk*>(aClosure);
 #ifdef MOZ_X11
   XEvent *xevent = static_cast<XEvent*>(aGdkXEvent);
 
   // See comments in nsScreenGtk::Init below.
   switch (xevent->type) {
-    case ConfigureNotify:
-      manager->Init();
-      break;
     case PropertyNotify:
       {
         XPropertyEvent *propertyEvent = &xevent->xproperty;
         if (propertyEvent->atom == manager->NetWorkareaAtom()) {
           manager->Init();
         }
       }
       break;
@@ -58,16 +62,20 @@ nsScreenManagerGtk :: nsScreenManagerGtk
   // nothing else to do. I guess we could cache a bunch of information
   // here, but we want to ask the device at runtime in case anything
   // has changed.
 }
 
 
 nsScreenManagerGtk :: ~nsScreenManagerGtk()
 {
+  g_signal_handlers_disconnect_by_func(gdk_screen_get_default(),
+                                       FuncToGpointer(monitors_changed),
+                                       this);
+
   if (mRootWindow) {
     gdk_window_remove_filter(mRootWindow, root_window_event_filter, this);
     g_object_unref(mRootWindow);
     mRootWindow = nullptr;
   }
 
   /* XineramaIsActive() registers a callback function close_display()
    * in X, which is to be called in XCloseDisplay(). This is the case
@@ -88,24 +96,25 @@ nsresult
 nsScreenManagerGtk :: EnsureInit()
 {
   if (mCachedScreenArray.Count() > 0)
     return NS_OK;
 
   mRootWindow = gdk_get_default_root_window();
   g_object_ref(mRootWindow);
 
-  // GDK_STRUCTURE_MASK ==> StructureNotifyMask, for ConfigureNotify
   // GDK_PROPERTY_CHANGE_MASK ==> PropertyChangeMask, for PropertyNotify
   gdk_window_set_events(mRootWindow,
                         GdkEventMask(gdk_window_get_events(mRootWindow) |
-                                     GDK_STRUCTURE_MASK |
                                      GDK_PROPERTY_CHANGE_MASK));
+
+  g_signal_connect(gdk_screen_get_default(), "monitors-changed",
+                   G_CALLBACK(monitors_changed), this);
+#ifdef MOZ_X11
   gdk_window_add_filter(mRootWindow, root_window_event_filter, this);
-#ifdef MOZ_X11
   if (GDK_IS_X11_DISPLAY(gdk_display_get_default()))
       mNetWorkareaAtom =
         XInternAtom(GDK_WINDOW_XDISPLAY(mRootWindow), "_NET_WORKAREA", False);
 #endif
 
   return Init();
 }
 
--- a/xpcom/base/nsAutoPtr.h
+++ b/xpcom/base/nsAutoPtr.h
@@ -92,30 +92,30 @@ public:
   // constructor.
   nsAutoPtr(nsAutoPtr<T>& aSmartPtr)
     : mRawPtr(aSmartPtr.forget())
     // Construct by transferring ownership from another smart pointer.
   {
   }
 
   template <typename I>
-  nsAutoPtr(nsAutoPtr<I>& aSmartPtr)
+  MOZ_IMPLICIT nsAutoPtr(nsAutoPtr<I>& aSmartPtr)
     : mRawPtr(aSmartPtr.forget())
     // Construct by transferring ownership from another smart pointer.
   {
   }
 
   nsAutoPtr(nsAutoPtr<T>&& aSmartPtr)
     : mRawPtr(aSmartPtr.forget())
     // Construct by transferring ownership from another smart pointer.
   {
   }
 
   template <typename I>
-  nsAutoPtr(nsAutoPtr<I>&& aSmartPtr)
+  MOZ_IMPLICIT nsAutoPtr(nsAutoPtr<I>&& aSmartPtr)
     : mRawPtr(aSmartPtr.forget())
     // Construct by transferring ownership from another smart pointer.
   {
   }
 
   // Assignment operators
 
   nsAutoPtr<T>&
--- a/xpcom/base/nsRefPtr.h
+++ b/xpcom/base/nsRefPtr.h
@@ -96,31 +96,31 @@ public:
     : mRawPtr(aRawPtr)
   {
     if (mRawPtr) {
       mRawPtr->AddRef();
     }
   }
 
   template <typename I>
-  nsRefPtr(already_AddRefed<I>& aSmartPtr)
+  MOZ_IMPLICIT nsRefPtr(already_AddRefed<I>& aSmartPtr)
     : mRawPtr(aSmartPtr.take())
     // construct from |already_AddRefed|
   {
   }
 
   template <typename I>
-  nsRefPtr(already_AddRefed<I>&& aSmartPtr)
+  MOZ_IMPLICIT nsRefPtr(already_AddRefed<I>&& aSmartPtr)
     : mRawPtr(aSmartPtr.take())
     // construct from |otherRefPtr.forget()|
   {
   }
 
   template <typename I>
-  nsRefPtr(nsRefPtr<I>&& aSmartPtr)
+  MOZ_IMPLICIT nsRefPtr(nsRefPtr<I>&& aSmartPtr)
     : mRawPtr(aSmartPtr.forget().take())
     // construct from |Move(nsRefPtr<SomeSubclassOfT>)|.
   {
   }
 
   MOZ_IMPLICIT nsRefPtr(const nsCOMPtr_helper& aHelper);
 
   // Defined in OwningNonNull.h
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -2219,17 +2219,17 @@ protected:
 
   explicit nsAutoArrayBase(const TArrayBase &aOther)
   {
     Init();
     this->AppendElements(aOther);
   }
 
   template<typename Allocator>
-  nsAutoArrayBase(nsTArray_Impl<elem_type, Allocator>&& aOther)
+  explicit nsAutoArrayBase(nsTArray_Impl<elem_type, Allocator>&& aOther)
   {
     Init();
     this->SwapElements(aOther);
   }
 
 private:
   // nsTArray_base casts itself as an nsAutoArrayBase in order to get a pointer
   // to mAutoBuf.
--- a/xpcom/glue/nsThreadUtils.h
+++ b/xpcom/glue/nsThreadUtils.h
@@ -362,157 +362,157 @@ struct IsParameterStorageClass : public 
 
 template<typename T>
 struct StoreCopyPassByValue
 {
   typedef T stored_type;
   typedef T passed_type;
   stored_type m;
   template <typename A>
-  StoreCopyPassByValue(A&& a) : m(mozilla::Forward<A>(a)) {}
+  explicit StoreCopyPassByValue(A&& a) : m(mozilla::Forward<A>(a)) {}
   passed_type PassAsParameter() { return m; }
 };
 template<typename S>
 struct IsParameterStorageClass<StoreCopyPassByValue<S>>
   : public mozilla::TrueType {};
 
 template<typename T>
 struct StoreCopyPassByConstLRef
 {
   typedef T stored_type;
   typedef const T& passed_type;
   stored_type m;
   template <typename A>
-  StoreCopyPassByConstLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+  explicit StoreCopyPassByConstLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
   passed_type PassAsParameter() { return m; }
 };
 template<typename S>
 struct IsParameterStorageClass<StoreCopyPassByConstLRef<S>>
   : public mozilla::TrueType {};
 
 template<typename T>
 struct StoreCopyPassByLRef
 {
   typedef T stored_type;
   typedef T& passed_type;
   stored_type m;
   template <typename A>
-  StoreCopyPassByLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+  explicit StoreCopyPassByLRef(A&& a) : m(mozilla::Forward<A>(a)) {}
   passed_type PassAsParameter() { return m; }
 };
 template<typename S>
 struct IsParameterStorageClass<StoreCopyPassByLRef<S>>
   : public mozilla::TrueType {};
 
 template<typename T>
 struct StoreCopyPassByRRef
 {
   typedef T stored_type;
   typedef T&& passed_type;
   stored_type m;
   template <typename A>
-  StoreCopyPassByRRef(A&& a) : m(mozilla::Forward<A>(a)) {}
+  explicit StoreCopyPassByRRef(A&& a) : m(mozilla::Forward<A>(a)) {}
   passed_type PassAsParameter() { return mozilla::Move(m); }
 };
 template<typename S>
 struct IsParameterStorageClass<StoreCopyPassByRRef<S>>
   : public mozilla::TrueType {};
 
 template<typename T>
 struct StoreRefPassByLRef
 {
   typedef T& stored_type;
   typedef T& passed_type;
   stored_type m;
   template <typename A>
-  StoreRefPassByLRef(A& a) : m(a) {}
+  explicit StoreRefPassByLRef(A& a) : m(a) {}
   passed_type PassAsParameter() { return m; }
 };
 template<typename S>
 struct IsParameterStorageClass<StoreRefPassByLRef<S>>
   : public mozilla::TrueType {};
 
 template<typename T>
 struct StoreConstRefPassByConstLRef
 {
   typedef const T& stored_type;
   typedef const T& passed_type;
   stored_type m;
   template <typename A>
-  StoreConstRefPassByConstLRef(const A& a) : m(a) {}
+  explicit StoreConstRefPassByConstLRef(const A& a) : m(a) {}
   passed_type PassAsParameter() { return m; }
 };
 template<typename S>
 struct IsParameterStorageClass<StoreConstRefPassByConstLRef<S>>
   : public mozilla::TrueType {};
 
 template<typename T>
 struct StorensRefPtrPassByPtr
 {
   typedef nsRefPtr<T> stored_type;
   typedef T* passed_type;
   stored_type m;
   template <typename A>
-  StorensRefPtrPassByPtr(A a) : m(a) {}
+  explicit StorensRefPtrPassByPtr(A a) : m(a) {}
   passed_type PassAsParameter() { return m.get(); }
 };
 template<typename S>
 struct IsParameterStorageClass<StorensRefPtrPassByPtr<S>>
   : public mozilla::TrueType {};
 
 template<typename T>
 struct StorePtrPassByPtr
 {
   typedef T* stored_type;
   typedef T* passed_type;
   stored_type m;
   template <typename A>
-  StorePtrPassByPtr(A a) : m(a) {}
+  explicit StorePtrPassByPtr(A a) : m(a) {}
   passed_type PassAsParameter() { return m; }
 };
 template<typename S>
 struct IsParameterStorageClass<StorePtrPassByPtr<S>>
   : public mozilla::TrueType {};
 
 template<typename T>
 struct StoreConstPtrPassByConstPtr
 {
   typedef const T* stored_type;
   typedef const T* passed_type;
   stored_type m;
   template <typename A>
-  StoreConstPtrPassByConstPtr(A a) : m(a) {}
+  explicit StoreConstPtrPassByConstPtr(A a) : m(a) {}
   passed_type PassAsParameter() { return m; }
 };
 template<typename S>
 struct IsParameterStorageClass<StoreConstPtrPassByConstPtr<S>>
   : public mozilla::TrueType {};
 
 template<typename T>
 struct StoreCopyPassByConstPtr
 {
   typedef T stored_type;
   typedef const T* passed_type;
   stored_type m;
   template <typename A>
-  StoreCopyPassByConstPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
+  explicit StoreCopyPassByConstPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
   passed_type PassAsParameter() { return &m; }
 };
 template<typename S>
 struct IsParameterStorageClass<StoreCopyPassByConstPtr<S>>
   : public mozilla::TrueType {};
 
 template<typename T>
 struct StoreCopyPassByPtr
 {
   typedef T stored_type;
   typedef T* passed_type;
   stored_type m;
   template <typename A>
-  StoreCopyPassByPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
+  explicit StoreCopyPassByPtr(A&& a) : m(mozilla::Forward<A>(a)) {}
   passed_type PassAsParameter() { return &m; }
 };
 template<typename S>
 struct IsParameterStorageClass<StoreCopyPassByPtr<S>>
   : public mozilla::TrueType {};
 
 namespace detail {
 
@@ -621,17 +621,17 @@ struct nsRunnableMethodArguments<>
     ((*o).*m)();
   }
 };
 template <typename T0>
 struct nsRunnableMethodArguments<T0>
 {
   typename ::detail::ParameterStorage<T0>::Type m0;
   template<typename A0>
-  nsRunnableMethodArguments(A0&& a0)
+  explicit nsRunnableMethodArguments(A0&& a0)
     : m0(mozilla::Forward<A0>(a0))
   {}
   template<class C, typename M> void apply(C* o, M m)
   {
     ((*o).*m)(m0.PassAsParameter());
   }
 };
 template <typename T0, typename T1>
--- a/xpcom/string/nsTLiteralString.h
+++ b/xpcom/string/nsTLiteralString.h
@@ -23,17 +23,17 @@ public:
 
 public:
 
   /**
    * constructor
    */
 
   template<size_type N>
-  nsTLiteralString_CharT(const char_type (&aStr)[N])
+  explicit nsTLiteralString_CharT(const char_type (&aStr)[N])
     : string_type(const_cast<char_type*>(aStr), N - 1, F_TERMINATED | F_LITERAL)
   {
   }
 
 private:
 
   // NOT TO BE IMPLEMENTED
   template<size_type N>