Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 28 Oct 2014 16:10:23 +0100
changeset 212744 7155fda159c1ae2db49155002dcf0b31ef04e034
parent 212743 6f87f4d252253da4b247841301e572e3d3e61d38 (current diff)
parent 212646 c0ddb1b098ec15b8d8cc68c08c1b20f65465c9f4 (diff)
child 212745 5f5fe4c2f7069fab3373ff69d5214c3dec0b840a
push idunknown
push userunknown
push dateunknown
milestone36.0a1
Merge mozilla-central to b2g-inbound
CLOBBER
dom/media/test/test_encryptedMediaExtensions.html
testing/web-platform/meta/media-source/mediasource-config-change-webm-a-bitrate.html.ini
testing/web-platform/meta/media-source/mediasource-config-change-webm-av-audio-bitrate.html.ini
testing/web-platform/meta/media-source/mediasource-config-change-webm-av-video-bitrate.html.ini
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1381,16 +1381,18 @@ pref("devtools.timeline.enabled", false)
 
 // Enable perftools via build command
 #ifdef MOZ_DEVTOOLS_PERFTOOLS
   pref("devtools.performance_dev.enabled", true);
 #else
   pref("devtools.performance_dev.enabled", false);
 #endif
 
+pref("devtools.performance.ui.show-timeline-memory", false);
+
 // The default Profiler UI settings
 pref("devtools.profiler.ui.show-platform-data", false);
 
 // The default cache UI setting
 pref("devtools.cache.disabled", false);
 
 // Enable the Network Monitor
 pref("devtools.netmonitor.enabled", true);
--- a/browser/devtools/main.js
+++ b/browser/devtools/main.js
@@ -380,17 +380,17 @@ Tools.webAudioEditor = {
   icon: "chrome://browser/skin/devtools/tool-webaudio.svg",
   invertIconForLightTheme: true,
   url: "chrome://browser/content/devtools/webaudioeditor.xul",
   label: l10n("ToolboxWebAudioEditor1.label", webAudioEditorStrings),
   panelLabel: l10n("ToolboxWebAudioEditor1.panelLabel", webAudioEditorStrings),
   tooltip: l10n("ToolboxWebAudioEditor1.tooltip", webAudioEditorStrings),
 
   isTargetSupported: function(target) {
-    return !target.isAddon && !target.chrome;
+    return !target.isAddon && !target.chrome && target.hasActor("webaudio");
   },
 
   build: function(iframeWindow, toolbox) {
     return new WebAudioEditorPanel(iframeWindow, toolbox);
   }
 };
 
 Tools.scratchpad = {
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/modules/front.js
@@ -0,0 +1,322 @@
+/* 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 { extend } = require("sdk/util/object");
+const { Task } = require("resource://gre/modules/Task.jsm");
+
+loader.lazyRequireGetter(this, "Services");
+loader.lazyRequireGetter(this, "promise");
+loader.lazyRequireGetter(this, "EventEmitter",
+  "devtools/toolkit/event-emitter");
+loader.lazyRequireGetter(this, "TimelineFront",
+  "devtools/server/actors/timeline", true);
+loader.lazyRequireGetter(this, "DevToolsUtils",
+  "devtools/toolkit/DevToolsUtils");
+
+loader.lazyImporter(this, "gDevTools",
+  "resource:///modules/devtools/gDevTools.jsm");
+
+let showTimelineMemory = () => Services.prefs.getBoolPref("devtools.performance.ui.show-timeline-memory");
+
+/**
+ * A cache of all PerformanceActorsConnection instances. The keys are Target objects.
+ */
+let SharedPerformanceActors = new WeakMap();
+
+/**
+ * Instantiates a shared PerformanceActorsConnection for the specified target.
+ * Consumers must yield on `open` to make sure the connection is established.
+ *
+ * @param Target target
+ *        The target owning this connection.
+ */
+SharedPerformanceActors.forTarget = function(target) {
+  if (this.has(target)) {
+    return this.get(target);
+  }
+
+  let instance = new PerformanceActorsConnection(target);
+  this.set(target, instance);
+  return instance;
+};
+
+/**
+ * A connection to underlying actors (profiler, memory, framerate, etc)
+ * shared by all tools in a target.
+ *
+ * Use `SharedPerformanceActors.forTarget` to make sure you get the same
+ * instance every time, and the `PerformanceFront` to start/stop recordings.
+ *
+ * @param Target target
+ *        The target owning this connection.
+ */
+function PerformanceActorsConnection(target) {
+  EventEmitter.decorate(this);
+
+  this._target = target;
+  this._client = this._target.client;
+  this._request = this._request.bind(this);
+
+  Services.obs.notifyObservers(null, "performance-actors-connection-created", null);
+}
+
+PerformanceActorsConnection.prototype = {
+
+  /**
+   * Initializes a connection to the profiler and other miscellaneous actors.
+   * If already open, nothing happens.
+   *
+   * @return object
+   *         A promise that is resolved once the connection is established.
+   */
+  open: Task.async(function*() {
+    if (this._connected) {
+      return;
+    }
+
+    // Local debugging needs to make the target remote.
+    yield this._target.makeRemote();
+
+    // Sets `this._profiler`
+    yield this._connectProfilerActor();
+
+    // Sets or shims `this._timeline`
+    yield this._connectTimelineActor();
+
+    this._connected = true;
+
+    Services.obs.notifyObservers(null, "performance-actors-connection-opened", null);
+  }),
+
+  /**
+   * Destroys this connection.
+   */
+  destroy: function () {
+    this._disconnectActors();
+    this._connected = false;
+  },
+
+  /**
+   * Initializes a connection to the profiler actor.
+   */
+  _connectProfilerActor: Task.async(function*() {
+    // Chrome debugging targets have already obtained a reference
+    // to the profiler actor.
+    if (this._target.chrome) {
+      this._profiler = this._target.form.profilerActor;
+    }
+    // Or when we are debugging content processes, we already have the tab
+    // specific one. Use it immediately.
+    else if (this._target.form && this._target.form.profilerActor) {
+      this._profiler = this._target.form.profilerActor;
+    }
+    // Check if we already have a grip to the `listTabs` response object
+    // and, if we do, use it to get to the profiler actor.
+    else if (this._target.root && this._target.root.profilerActor) {
+      this._profiler = this._target.root.profilerActor;
+    }
+    // Otherwise, call `listTabs`.
+    else {
+      this._profiler = (yield listTabs(this._client)).profilerActor;
+    }
+  }),
+
+  /**
+   * Initializes a connection to a timeline actor.
+   */
+  _connectTimelineActor: function() {
+    // Only initialize the timeline front if the respective actor is available.
+    // Older Gecko versions don't have an existing implementation, in which case
+    // all the methods we need can be easily mocked.
+    //
+    // If the timeline actor exists, all underlying actors (memory, framerate) exist,
+    // with the expected methods and behaviour. If using the Performance tool,
+    // and timeline actor does not exist (FxOS devices < Gecko 35),
+    // then just use the mocked actor and do not display timeline data.
+    //
+    // TODO use framework level feature detection from bug 1069673
+    if (this._target.form && this._target.form.timelineActor) {
+      this._timeline = new TimelineFront(this._target.client, this._target.form);
+    } else {
+      this._timeline = {
+        start: () => {},
+        stop: () => {},
+        isRecording: () => false,
+        on: () => {},
+        off: () => {},
+        destroy: () => {}
+      };
+    }
+  },
+
+  /**
+   * Closes the connections to non-profiler actors.
+   */
+  _disconnectActors: function () {
+    this._timeline.destroy();
+  },
+
+  /**
+   * Sends the request over the remote debugging protocol to the
+   * specified actor.
+   *
+   * @param string actor
+   *        The designated actor. Currently supported: "profiler", "timeline".
+   * @param string method
+   *        Method to call on the backend.
+   * @param any args [optional]
+   *        Additional data or arguments to send with the request.
+   * @return object
+   *         A promise resolved with the response once the request finishes.
+   */
+  _request: function(actor, method, ...args) {
+    // Handle requests to the profiler actor.
+    if (actor == "profiler") {
+      let deferred = promise.defer();
+      let data = args[0] || {};
+      data.to = this._profiler;
+      data.type = method;
+      this._client.request(data, deferred.resolve);
+      return deferred.promise;
+    }
+
+    // Handle requests to the timeline actor.
+    if (actor == "timeline") {
+      return this._timeline[method].apply(this._timeline, args);
+    }
+  }
+};
+
+/**
+ * A thin wrapper around a shared PerformanceActorsConnection for the parent target.
+ * Handles manually starting and stopping a recording.
+ *
+ * @param PerformanceActorsConnection connection
+ *        The shared instance for the parent target.
+ */
+function PerformanceFront(connection) {
+  EventEmitter.decorate(this);
+
+  this._request = connection._request;
+
+  // Pipe events from TimelineActor to the PerformanceFront
+  connection._timeline.on("markers", markers => this.emit("markers", markers));
+  connection._timeline.on("memory", (delta, measurement) => this.emit("memory", delta, measurement));
+  connection._timeline.on("ticks", (delta, timestamps) => this.emit("ticks", delta, timestamps));
+}
+
+PerformanceFront.prototype = {
+  /**
+   * Manually begins a recording session.
+   *
+   * @return object
+   *         A promise that is resolved once recording has started.
+   */
+  startRecording: Task.async(function*() {
+    let { isActive, currentTime } = yield this._request("profiler", "isActive");
+
+    // Start the profiler only if it wasn't already active. The built-in
+    // nsIPerformance module will be kept recording, because it's the same instance
+    // for all targets and interacts with the whole platform, so we don't want
+    // to affect other clients by stopping (or restarting) it.
+    if (!isActive) {
+      // Extend the options so that protocol.js doesn't modify
+      // the source object.
+      let options = extend({}, this._customPerformanceOptions);
+      yield this._request("profiler", "startProfiler", options);
+      this._profilingStartTime = 0;
+      this.emit("profiler-activated");
+    } else {
+      this._profilingStartTime = currentTime;
+      this.emit("profiler-already-active");
+    }
+
+    // The timeline actor is target-dependent, so just make sure
+    // it's recording.
+    let withMemory = showTimelineMemory();
+    yield this._request("timeline", "start", { withTicks: true, withMemory: withMemory });
+  }),
+
+  /**
+   * Manually ends the current recording session.
+   *
+   * @return object
+   *         A promise that is resolved once recording has stopped,
+   *         with the profiler and timeline data.
+   */
+  stopRecording: Task.async(function*() {
+    // We'll need to filter out all samples that fall out of current profile's
+    // range. This is necessary because the profiler is continuously running.
+    let profilerData = yield this._request("profiler", "getProfile");
+    filterSamples(profilerData, this._profilingStartTime);
+    offsetSampleTimes(profilerData, this._profilingStartTime);
+
+    yield this._request("timeline", "stop");
+
+    // Join all the acquired data and return it for outside consumers.
+    return {
+      recordingDuration: profilerData.currentTime - this._profilingStartTime,
+      profilerData: profilerData
+    };
+  }),
+
+  /**
+   * Overrides the options sent to the built-in profiler module when activating,
+   * such as the maximum entries count, the sampling interval etc.
+   *
+   * Used in tests and for older backend implementations.
+   */
+  _customPerformanceOptions: {
+    entries: 1000000,
+    interval: 1,
+    features: ["js"]
+  }
+};
+
+/**
+ * Filters all the samples in the provided profiler data to be more recent
+ * than the specified start time.
+ *
+ * @param object profilerData
+ *        The profiler data received from the backend.
+ * @param number profilingStartTime
+ *        The earliest acceptable sample time (in milliseconds).
+ */
+function filterSamples(profilerData, profilingStartTime) {
+  let firstThread = profilerData.profile.threads[0];
+
+  firstThread.samples = firstThread.samples.filter(e => {
+    return e.time >= profilingStartTime;
+  });
+}
+
+/**
+ * Offsets all the samples in the provided profiler data by the specified time.
+ *
+ * @param object profilerData
+ *        The profiler data received from the backend.
+ * @param number timeOffset
+ *        The amount of time to offset by (in milliseconds).
+ */
+function offsetSampleTimes(profilerData, timeOffset) {
+  let firstThreadSamples = profilerData.profile.threads[0].samples;
+
+  for (let sample of firstThreadSamples) {
+    sample.time -= timeOffset;
+  }
+}
+
+/**
+ * A collection of small wrappers promisifying functions invoking callbacks.
+ */
+function listTabs(client) {
+  let deferred = promise.defer();
+  client.listTabs(deferred.resolve);
+  return deferred.promise;
+}
+
+exports.getPerformanceActorsConnection = target => SharedPerformanceActors.forTarget(target);
+exports.PerformanceFront = PerformanceFront;
--- a/browser/devtools/performance/moz.build
+++ b/browser/devtools/performance/moz.build
@@ -1,8 +1,11 @@
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXTRA_JS_MODULES.devtools.performance += [
+    'modules/front.js',
     'panel.js'
 ]
+
+BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
--- a/browser/devtools/performance/panel.js
+++ b/browser/devtools/performance/panel.js
@@ -1,16 +1,17 @@
 /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const {Cc, Ci, Cu, Cr} = require("chrome");
+const { PerformanceFront, getPerformanceActorsConnection } = require("devtools/performance/front");
 
 Cu.import("resource://gre/modules/Task.jsm");
 
 loader.lazyRequireGetter(this, "promise");
 loader.lazyRequireGetter(this, "EventEmitter",
   "devtools/toolkit/event-emitter");
 
 function PerformancePanel(iframeWindow, toolbox) {
@@ -22,26 +23,27 @@ function PerformancePanel(iframeWindow, 
 
 exports.PerformancePanel = PerformancePanel;
 
 PerformancePanel.prototype = {
   /**
    * Open is effectively an asynchronous constructor.
    *
    * @return object
-   *         A promise that is resolved when the Profiler completes opening.
+   *         A promise that is resolved when the Performance tool
+   *         completes opening.
    */
   open: Task.async(function*() {
     this.panelWin.gToolbox = this._toolbox;
     this.panelWin.gTarget = this.target;
 
-    // Mock Front for now
-    let gFront = {};
-    EventEmitter.decorate(gFront);
-    this.panelWin.gFront = gFront;
+    this._connection = getPerformanceActorsConnection(this.target);
+    yield this._connection.open();
+
+    this.panelWin.gFront = new PerformanceFront(this._connection);
 
     yield this.panelWin.startupPerformance();
 
     this.isReady = true;
     this.emit("ready");
     return this;
   }),
 
@@ -50,13 +52,16 @@ PerformancePanel.prototype = {
   get target() this._toolbox.target,
 
   destroy: Task.async(function*() {
     // Make sure this panel is not already destroyed.
     if (this._destroyed) {
       return;
     }
 
+    // Destroy the connection to ensure packet handlers are removed from client.
+    this._connection.destroy();
+
     yield this.panelWin.shutdownPerformance();
     this.emit("destroyed");
     this._destroyed = true;
   })
 };
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser.ini
@@ -0,0 +1,30 @@
+[DEFAULT]
+skip-if = e10s # Handle in Bug 1077464 for profiler
+subsuite = devtools
+support-files =
+  doc_simple-test.html
+  head.js
+
+# Commented out tests are profiler tests
+# that need to be moved over to performance tool
+
+[browser_perf-aaa-run-first-leaktest.js]
+[browser_perf-front-basic-timeline-01.js]
+[browser_perf-front-basic-profiler-01.js]
+# bug 1077464
+#[browser_perf-front-profiler-01.js]
+[browser_perf-front-profiler-02.js]
+[browser_perf-front-profiler-03.js]
+[browser_perf-front-profiler-04.js]
+# bug 1077464
+#[browser_perf-front-profiler-05.js]
+# bug 1077464
+#[browser_perf-front-profiler-06.js]
+# needs shared connection with profiler's shared connection
+#[browser_perf-shared-connection-01.js]
+[browser_perf-shared-connection-02.js]
+[browser_perf-shared-connection-03.js]
+# bug 1077464
+#[browser_perf-shared-connection-04.js]
+[browser_perf-data-samples.js]
+[browser_perf-data-massaging-01.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-aaa-run-first-leaktest.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the performance tool leaks on initialization and sudden destruction.
+ * You can also use this initialization format as a template for other tests.
+ */
+
+function spawnTest () {
+  let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
+
+  ok(target, "Should have a target available.");
+  ok(toolbox, "Should have a toolbox available.");
+  ok(panel, "Should have a panel available.");
+
+  ok(panel.panelWin.gToolbox, "Should have a toolbox reference on the panel window.");
+  ok(panel.panelWin.gTarget, "Should have a target reference on the panel window.");
+  ok(panel.panelWin.gFront, "Should have a front reference on the panel window.");
+
+  yield teardown(panel);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-data-massaging-01.js
@@ -0,0 +1,63 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the retrieved profiler data samples are correctly filtered and
+ * normalized before passed to consumers.
+ */
+
+const WAIT_TIME = 1000; // ms
+
+function spawnTest () {
+  let { panel } = yield initPerformance(SIMPLE_URL);
+  let front = panel.panelWin.gFront;
+
+  // Perform the first recording...
+
+  yield front.startRecording();
+  let profilingStartTime = front._profilingStartTime;
+  info("Started profiling at: " + profilingStartTime);
+
+  busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
+
+  let firstRecordingData = yield front.stopRecording();
+  let firstRecordingFinishTime = firstRecordingData.profilerData.currentTime;
+
+  is(profilingStartTime, 0,
+    "The profiling start time should be 0 for the first recording.");
+  ok(firstRecordingData.recordingDuration >= WAIT_TIME,
+    "The first recording duration is correct.");
+  ok(firstRecordingFinishTime >= WAIT_TIME,
+    "The first recording finish time is correct.");
+
+  // Perform the second recording...
+
+  yield front.startRecording();
+  profilingStartTime = front._profilingStartTime;
+  info("Started profiling at: " + profilingStartTime);
+
+  busyWait(WAIT_TIME); // allow the profiler module to sample more cpu activity
+
+  let secondRecordingData = yield front.stopRecording();
+  let secondRecordingFinishTime = secondRecordingData.profilerData.currentTime;
+  let secondRecordingProfile = secondRecordingData.profilerData.profile;
+  let secondRecordingSamples = secondRecordingProfile.threads[0].samples;
+
+  isnot(profilingStartTime, 0,
+    "The profiling start time should not be 0 on the second recording.");
+  ok(secondRecordingData.recordingDuration >= WAIT_TIME,
+    "The second recording duration is correct.");
+  ok(secondRecordingFinishTime - firstRecordingFinishTime >= WAIT_TIME,
+    "The second recording finish time is correct.");
+
+  ok(secondRecordingSamples[0].time < profilingStartTime,
+    "The second recorded sample times were normalized.");
+  ok(secondRecordingSamples[0].time > 0,
+    "The second recorded sample times were normalized correctly.");
+  ok(!secondRecordingSamples.find(e => e.time + profilingStartTime <= firstRecordingFinishTime),
+    "There should be no samples from the first recording in the second one, " +
+    "even though the total number of frames did not overflow.");
+
+  yield teardown(panel);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-data-samples.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the retrieved profiler data samples always have a (root) node.
+ * If this ever changes, the |ThreadNode.prototype.insert| function in
+ * browser/devtools/profiler/utils/tree-model.js will have to be changed.
+ */
+
+const WAIT_TIME = 1000; // ms
+
+function spawnTest () {
+  let { panel } = yield initPerformance(SIMPLE_URL);
+  let front = panel.panelWin.gFront;
+
+  yield front.startRecording();
+  busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
+
+  let recordingData = yield front.stopRecording();
+  let profile = recordingData.profilerData.profile;
+
+  for (let thread of profile.threads) {
+    info("Checking thread: " + thread.name);
+
+    for (let sample of thread.samples) {
+      if (sample.frames[0].location != "(root)") {
+        ok(false, "The sample " + sample.toSource() + " doesn't have a root node.");
+      }
+    }
+  }
+
+  yield teardown(panel);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-front-basic-profiler-01.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test basic functionality of PerformanceFront
+ */
+
+let WAIT = 1000;
+
+function spawnTest () {
+  let { target, front } = yield initBackend(SIMPLE_URL);
+
+  yield front.startRecording();
+
+  yield busyWait(WAIT);
+
+  let { recordingDuration, profilerData } = yield front.stopRecording();
+
+  ok(recordingDuration > 500, "recordingDuration exists");
+  ok(profilerData, "profilerData exists");
+
+  yield removeTab(target.tab);
+  finish();
+
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-front-basic-timeline-01.js
@@ -0,0 +1,84 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Test basic functionality of PerformanceFront, retrieving timeline data.
+ */
+
+function spawnTest () {
+  Services.prefs.setBoolPref("devtools.performance.ui.show-timeline-memory", true);
+
+  let { target, front } = yield initBackend(SIMPLE_URL);
+
+  let lastMemoryDelta = 0;
+  let lastTickDelta = 0;
+
+  let counters = {
+    markers: [],
+    memory: [],
+    ticks: []
+  };
+
+  let deferreds = {
+    markers: Promise.defer(),
+    memory: Promise.defer(),
+    ticks: Promise.defer()
+  }
+
+  front.on("markers", handler);
+  front.on("memory", handler);
+  front.on("ticks", handler);
+
+  yield front.startRecording();
+
+  yield Promise.all(Object.keys(deferreds).map(type => deferreds[type].promise));
+
+  yield front.stopRecording();
+
+  is(counters.markers.length, 1, "one marker event fired.");
+  is(counters.memory.length, 3, "three memory events fired.");
+  is(counters.ticks.length, 3, "three ticks events fired.");
+
+  yield removeTab(target.tab);
+  finish();
+
+  function handler (name, ...args) {
+    if (name === "memory") {
+      let [delta, measurement] = args;
+      is(typeof delta, "number", "received `delta` in memory event");
+      ok(delta > lastMemoryDelta, "received `delta` in memory event");
+      ok(measurement.total, "received `total` in memory event");
+      ok(measurement.domSize, "received `domSize` in memory event");
+      ok(measurement.jsObjectsSize, "received `jsObjectsSize` in memory event");
+
+      counters.memory.push({ delta: delta, measurement: measurement });
+      lastMemoryDelta = delta;
+    } else if (name === "ticks") {
+      let [delta, timestamps] = args;
+      ok(delta > lastTickDelta, "received `delta` in ticks event");
+
+      // First tick doesn't contain any timestamps
+      if (counters.ticks.length) {
+        ok(timestamps.length, "received `timestamps` in ticks event");
+      }
+
+      counters.ticks.push({ delta: delta, timestamps: timestamps});
+      lastTickDelta = delta;
+    } else if (name === "markers") {
+      let [markers] = args;
+      ok(markers[0].start, "received atleast one marker with `start`");
+      ok(markers[0].end, "received atleast one marker with `end`");
+      ok(markers[0].name, "received atleast one marker with `name`");
+      counters.markers.push(markers);
+      front.off(name, handler);
+      deferreds[name].resolve();
+    } else {
+      throw new Error("unknown event");
+    }
+
+    if (name !== "markers" && counters[name].length === 3) {
+      front.off(name, handler);
+      deferreds[name].resolve();
+    }
+  };
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-front-profiler-02.js
@@ -0,0 +1,37 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the profiler connection front does not activate the built-in
+ * profiler module if not necessary, and doesn't deactivate it when
+ * a recording is stopped.
+ */
+
+let test = Task.async(function*() {
+  let { target, panel, toolbox } = yield initPerformance(SIMPLE_URL);
+  let front = panel.panelWin.gFront;
+
+  ok(!nsIProfilerModule.IsActive(),
+    "The built-in profiler module should not have been automatically started.");
+
+  let activated = front.once("profiler-activated");
+  yield front.startRecording();
+  yield activated;
+  yield front.stopRecording();
+  ok(nsIProfilerModule.IsActive(),
+    "The built-in profiler module should still be active (1).");
+
+  let alreadyActive = front.once("profiler-already-active");
+  yield front.startRecording();
+  yield alreadyActive;
+  yield front.stopRecording();
+  ok(nsIProfilerModule.IsActive(),
+    "The built-in profiler module should still be active (2).");
+
+  yield teardown(panel);
+
+  ok(!nsIProfilerModule.IsActive(),
+    "The built-in profiler module should have been automatically stoped.");
+
+  finish();
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-front-profiler-03.js
@@ -0,0 +1,33 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the built-in profiler module doesn't deactivate when the toolbox
+ * is destroyed if there are other consumers using it.
+ */
+
+let test = Task.async(function*() {
+  let { panel: firstPanel } = yield initPerformance(SIMPLE_URL);
+  let firstFront = firstPanel.panelWin.gFront;
+
+  let activated = firstFront.once("profiler-activated");
+  yield firstFront.startRecording();
+  yield activated;
+
+  let { panel: secondPanel } = yield initPerformance(SIMPLE_URL);
+  let secondFront = secondPanel.panelWin.gFront;
+
+  let alreadyActive = secondFront.once("profiler-already-active");
+  yield secondFront.startRecording();
+  yield alreadyActive;
+
+  yield teardown(firstPanel);
+  ok(nsIProfilerModule.IsActive(),
+    "The built-in profiler module should still be active.");
+
+  yield teardown(secondPanel);
+  ok(!nsIProfilerModule.IsActive(),
+    "The built-in profiler module should have been automatically stoped.");
+
+  finish();
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-front-profiler-04.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the built-in profiler module is not reactivated if no other
+ * consumer was using it over the remote debugger protocol, and ensures
+ * that the actor will work properly even in such cases (e.g. the Gecko Profiler
+ * addon was installed and automatically activated the profiler module).
+ */
+
+let test = Task.async(function*() {
+  // Ensure the profiler is already running when the test starts.
+  let ENTRIES = 1000000;
+  let INTERVAL = 1;
+  let FEATURES = ["js"];
+  nsIProfilerModule.StartProfiler(ENTRIES, INTERVAL, FEATURES, FEATURES.length);
+
+  let { panel: firstPanel } = yield initPerformance(SIMPLE_URL);
+  let firstFront = firstPanel.panelWin.gFront;
+
+  let alredyActive = firstFront.once("profiler-already-active");
+  yield firstFront.startRecording();
+  yield alredyActive;
+  ok(firstFront._profilingStartTime > 0, "The profiler was not restarted.");
+
+  let { panel: secondPanel } = yield initPerformance(SIMPLE_URL);
+  let secondFront = secondPanel.panelWin.gFront;
+
+  let alreadyActive = secondFront.once("profiler-already-active");
+  yield secondFront.startRecording();
+  yield alreadyActive;
+  ok(secondFront._profilingStartTime > 0, "The profiler was not restarted.");
+
+  yield teardown(firstPanel);
+  ok(nsIProfilerModule.IsActive(),
+    "The built-in profiler module should still be active.");
+
+  yield teardown(secondPanel);
+  ok(!nsIProfilerModule.IsActive(),
+    "The built-in profiler module should have been automatically stoped.");
+
+  finish();
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-shared-connection-02.js
@@ -0,0 +1,39 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the shared PerformanceActorsConnection is only opened once.
+ */
+
+let gProfilerConnectionsOpened = 0;
+Services.obs.addObserver(profilerConnectionObserver, "performance-actors-connection-opened", false);
+
+function spawnTest () {
+  let { target, panel } = yield initPerformance(SIMPLE_URL);
+
+  is(gProfilerConnectionsOpened, 1,
+    "Only one profiler connection was opened.");
+
+  let sharedConnection = getPerformanceActorsConnection(target);
+
+  ok(sharedConnection,
+    "A shared profiler connection for the current toolbox was retrieved.");
+  is(sharedConnection._request, panel.panelWin.gFront._request,
+    "The same shared profiler connection is used by the panel's front.");
+
+  yield sharedConnection.open();
+  is(gProfilerConnectionsOpened, 1,
+    "No additional profiler connections were opened.");
+
+  yield teardown(panel);
+  finish();
+}
+
+function profilerConnectionObserver(subject, topic, data) {
+  is(topic, "performance-actors-connection-opened", "The correct topic was observed.");
+  gProfilerConnectionsOpened++;
+}
+
+registerCleanupFunction(() => {
+  Services.obs.removeObserver(profilerConnectionObserver, "performance-actors-connection-opened");
+});
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-shared-connection-03.js
@@ -0,0 +1,29 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests if the shared PerformanceActorsConnection can properly send requests.
+ */
+
+function spawnTest () {
+  let { panel } = yield initPerformance(SIMPLE_URL);
+  let front = panel.panelWin.gFront;
+
+  ok(!nsIProfilerModule.IsActive(),
+    "The built-in profiler module should not have been automatically started.");
+
+  let result = yield front._request("profiler", "startProfiler");
+  is(result.started, true,
+    "The request finished successfully and the profiler should've been started.");
+  ok(nsIProfilerModule.IsActive(),
+    "The built-in profiler module should now be active.");
+
+  result = yield front._request("profiler", "stopProfiler");
+  is(result.started, false,
+    "The request finished successfully and the profiler should've been stopped.");
+  ok(!nsIProfilerModule.IsActive(),
+    "The built-in profiler module should now be inactive.");
+
+  yield teardown(panel);
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/doc_simple-test.html
@@ -0,0 +1,22 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<!doctype html>
+
+<html>
+  <head>
+    <meta charset="utf-8"/>
+    <title>Profiler test page</title>
+  </head>
+
+  <body>
+    <script type="text/javascript">
+      function test() {
+        var a = "Hello world!";
+      }
+
+      // Prevent this script from being garbage collected.
+      window.setInterval(test, 1);
+    </script>
+  </body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/head.js
@@ -0,0 +1,209 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
+
+let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+// Enable logging for all the tests. Both the debugger server and frontend will
+// be affected by this pref.
+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 } = Cu.import("resource://gre/modules/Promise.jsm", {});
+let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
+let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
+let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
+let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
+let { getPerformanceActorsConnection, PerformanceFront } = devtools.require("devtools/performance/front");
+let nsIProfilerModule = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
+let TargetFactory = devtools.TargetFactory;
+let mm = null;
+
+const FRAME_SCRIPT_UTILS_URL = "chrome://browser/content/devtools/frame-script-utils.js"
+const EXAMPLE_URL = "http://example.com/browser/browser/devtools/performance/test/";
+const SIMPLE_URL = EXAMPLE_URL + "doc_simple-test.html";
+
+// All tests are asynchronous.
+waitForExplicitFinish();
+
+let gToolEnabled = Services.prefs.getBoolPref("devtools.performance_dev.enabled");
+let gShowTimelineMemory = Services.prefs.getBoolPref("devtools.performance.ui.show-timeline-memory");
+
+gDevTools.testing = true;
+
+/**
+ * Call manually in tests that use frame script utils after initializing
+ * the tool. Must be called after initializing so we can detect
+ * whether or not `content` is a CPOW or not. Call after init but before navigating
+ * to different pages.
+ */
+function loadFrameScripts () {
+  mm = gBrowser.selectedBrowser.messageManager;
+  mm.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
+}
+
+registerCleanupFunction(() => {
+  gDevTools.testing = false;
+  info("finish() was called, cleaning up...");
+
+  Services.prefs.setBoolPref("devtools.performance.ui.show-timeline-memory", gShowTimelineMemory);
+  Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
+  Services.prefs.setBoolPref("devtools.performance_dev.enabled", gToolEnabled);
+  // Make sure the profiler module is stopped when the test finishes.
+  nsIProfilerModule.StopProfiler();
+
+  Cu.forceGC();
+});
+
+function addTab(aUrl, aWindow) {
+  info("Adding tab: " + aUrl);
+
+  let deferred = Promise.defer();
+  let targetWindow = aWindow || window;
+  let targetBrowser = targetWindow.gBrowser;
+
+  targetWindow.focus();
+  let tab = targetBrowser.selectedTab = targetBrowser.addTab(aUrl);
+  let linkedBrowser = tab.linkedBrowser;
+
+  linkedBrowser.addEventListener("load", function onLoad() {
+    linkedBrowser.removeEventListener("load", onLoad, true);
+    info("Tab added and finished loading: " + aUrl);
+    deferred.resolve(tab);
+  }, true);
+
+  return deferred.promise;
+}
+
+function removeTab(aTab, aWindow) {
+  info("Removing tab.");
+
+  let deferred = Promise.defer();
+  let targetWindow = aWindow || window;
+  let targetBrowser = targetWindow.gBrowser;
+  let tabContainer = targetBrowser.tabContainer;
+
+  tabContainer.addEventListener("TabClose", function onClose(aEvent) {
+    tabContainer.removeEventListener("TabClose", onClose, false);
+    info("Tab removed and finished closing.");
+    deferred.resolve();
+  }, false);
+
+  targetBrowser.removeTab(aTab);
+  return deferred.promise;
+}
+
+function handleError(aError) {
+  ok(false, "Got an error: " + aError.message + "\n" + aError.stack);
+  finish();
+}
+
+function once(aTarget, aEventName, aUseCapture = false) {
+  info("Waiting for event: '" + aEventName + "' on " + aTarget + ".");
+
+  let deferred = Promise.defer();
+
+  for (let [add, remove] of [
+    ["on", "off"], // Use event emitter before DOM events for consistency
+    ["addEventListener", "removeEventListener"],
+    ["addListener", "removeListener"]
+  ]) {
+    if ((add in aTarget) && (remove in aTarget)) {
+      aTarget[add](aEventName, function onEvent(...aArgs) {
+        aTarget[remove](aEventName, onEvent, aUseCapture);
+        deferred.resolve(...aArgs);
+      }, aUseCapture);
+      break;
+    }
+  }
+
+  return deferred.promise;
+}
+
+function test () {
+  Task.spawn(spawnTest).then(finish, handleError);
+}
+
+function initBackend(aUrl) {
+  info("Initializing a performance front.");
+
+  if (!DebuggerServer.initialized) {
+    DebuggerServer.init(() => true);
+    DebuggerServer.addBrowserActors();
+  }
+
+  return Task.spawn(function*() {
+    let tab = yield addTab(aUrl);
+    let target = TargetFactory.forTab(tab);
+
+    yield target.makeRemote();
+
+    yield gDevTools.showToolbox(target, "performance");
+
+    let connection = getPerformanceActorsConnection(target);
+    yield connection.open();
+    let front = new PerformanceFront(connection);
+    return { target, front };
+  });
+}
+
+function initPerformance(aUrl) {
+  info("Initializing a performance pane.");
+
+  return Task.spawn(function*() {
+    let tab = yield addTab(aUrl);
+    let target = TargetFactory.forTab(tab);
+
+    yield target.makeRemote();
+
+    Services.prefs.setBoolPref("devtools.performance_dev.enabled", true);
+    let toolbox = yield gDevTools.showToolbox(target, "performance");
+    let panel = toolbox.getCurrentPanel();
+    return { target, panel, toolbox };
+  });
+}
+
+function* teardown(panel) {
+  info("Destroying the performance tool.");
+
+  let tab = panel.target.tab;
+  yield panel._toolbox.destroy();
+  yield removeTab(tab);
+}
+
+function idleWait(time) {
+  return DevToolsUtils.waitForTime(time);
+}
+
+function consoleMethod (...args) {
+  if (!mm) {
+    throw new Error("`loadFrameScripts()` must be called before using frame scripts.");
+  }
+  mm.sendAsyncMessage("devtools:test:console", args);
+}
+
+function* consoleProfile(connection, label) {
+  let notified = connection.once("profile");
+  consoleMethod("profile", label);
+  yield notified;
+}
+
+function* consoleProfileEnd(connection) {
+  let notified = connection.once("profileEnd");
+  consoleMethod("profileEnd");
+  yield notified;
+}
+
+function busyWait(time) {
+  let start = Date.now();
+  let stack;
+  while (Date.now() - start < time) { stack = Components.stack; }
+}
+
+function idleWait(time) {
+  return DevToolsUtils.waitForTime(time);
+}
+
--- a/browser/devtools/shared/frame-script-utils.js
+++ b/browser/devtools/shared/frame-script-utils.js
@@ -12,11 +12,16 @@ addMessageListener("devtools:test:naviga
   content.location = data.location;
 });
 
 addMessageListener("devtools:test:reload", function ({ data }) {
   data = data || {};
   content.location.reload(data.forceget);
 });
 
+addMessageListener("devtools:test:console", function ({ data }) {
+  let method = data.shift();
+  content.console[method].apply(content.console, data);
+});
+
 addEventListener("load", function() {
   sendAsyncMessage("devtools:test:load");
 }, true);
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -68,17 +68,17 @@
   background-clip: padding-box;
   margin-top: -1px; /* Move up into the TabsToolbar for the inner highlight at the top of the nav-bar */
   /* Position the toolbar above the bottom of background tabs */
   position: relative;
   z-index: 1;
 }
 
 #nav-bar {
-  background-image: linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
+  background-image: linear-gradient(@toolbarHighlight@, transparent);
   box-shadow: 0 1px 0 @toolbarHighlight@ inset;
   padding-top: 2px;
   padding-bottom: 2px;
 }
 
 #nav-bar-overflow-button {
   -moz-image-region: rect(-5px, 12px, 11px, -4px);
 }
@@ -1535,71 +1535,66 @@ richlistitem[type~="action"][actiontype=
   list-style-image: url("chrome://browser/skin/reload-stop-go.png");
 }
 
 #urlbar-reload-button {
   -moz-image-region: rect(0, 14px, 14px, 0);
 }
 
 #urlbar-reload-button:not([disabled]):hover {
-  background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.2), hsla(200,100%,70%,0));
+  background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.2), transparent);
   -moz-image-region: rect(14px, 14px, 28px, 0);
 }
 
 #urlbar-reload-button:not([disabled]):hover:active {
-  background-image: radial-gradient(circle closest-side, hsla(200,100%,60%,.1), hsla(200,100%,60%,0));
+  background-image: radial-gradient(circle closest-side, hsla(200,100%,60%,.1), transparent);
   -moz-image-region: rect(28px, 14px, 42px, 0);
 }
 
 #urlbar-reload-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 #urlbar-go-button {
   -moz-image-region: rect(0, 42px, 14px, 28px);
 }
 
 #urlbar-go-button:hover {
-  background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.2), hsla(110,70%,50%,0));
+  background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.2), transparent);
   -moz-image-region: rect(14px, 42px, 28px, 28px);
 }
 
 #urlbar-go-button:hover:active {
-  background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.1), hsla(110,70%,50%,0));
+  background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.1), transparent);
   -moz-image-region: rect(28px, 42px, 42px, 28px);
 }
 
 #urlbar-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 #urlbar-stop-button {
   -moz-image-region: rect(0, 28px, 14px, 14px);
 }
 
 #urlbar-stop-button:not([disabled]):hover {
-  background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.3), hsla(5,100%,75%,0));
+  background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.3), transparent);
   -moz-image-region: rect(14px, 28px, 28px, 14px);
 }
 
 #urlbar-stop-button:hover:active {
-  background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.1), hsla(5,100%,75%,0));
+  background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.1), transparent);
   -moz-image-region: rect(28px, 28px, 42px, 14px);
 }
 
 /* Popup blocker button */
 #page-report-button {
   list-style-image: url("chrome://browser/skin/Info.png");
 }
 
-/* Loop */
-#loop-call-button {
-  list-style-image: url("chrome://global/skin/loop/loop-call.png");
-}
-
 /* social share panel */
 
 .social-share-frame {
   background: linear-gradient(to bottom, rgba(242,242,242,.99), rgba(242,242,242,.95));
   border-left: 1px solid #f8f8f8;
   width: 330px;
   height: 150px;
   /* we resize our panels dynamically, make it look nice */
@@ -2118,40 +2113,40 @@ toolbarbutton.chevron > .toolbarbutton-i
 .social-panel > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
 
 %include ../shared/social/chat.inc.css
 
 .chat-titlebar {
   background-color: #d9d9d9;
-  background-image: linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
+  background-image: linear-gradient(@toolbarHighlight@, transparent);
 }
 
 .chat-titlebar[selected] {
   background-color: #f0f0f0;
 }
 
 .chatbar-button {
   -moz-appearance: none;
   background-color: #d9d9d9;
-  background-image: linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
+  background-image: linear-gradient(@toolbarHighlight@, transparent);
 }
 
 .chatbar-button > .toolbarbutton-icon {
   -moz-margin-end: 0;
 }
 
 .chatbar-button:hover,
 .chatbar-button[open="true"] {
   background-color: #f0f0f0;
 }
 
 .chatbar-button[activity] {
-  background-image: radial-gradient(circle farthest-corner at center 3px, rgb(233,242,252) 3%, rgba(172,206,255,0.75) 40%, rgba(87,151,201,0.5) 80%, rgba(87,151,201,0));
+  background-image: radial-gradient(circle farthest-corner at center 3px, rgb(233,242,252) 3%, rgba(172,206,255,0.75) 40%, rgba(87,151,201,0.5) 80%, transparent);
 }
 
 chatbox {
   border-top-left-radius: 2.5px;
   border-top-right-radius: 2.5px;
 }
 
 /* Customization mode */
--- a/browser/themes/linux/downloads/downloads.css
+++ b/browser/themes/linux/downloads/downloads.css
@@ -164,17 +164,17 @@ richlistitem[type="download"]:last-child
 
 /*** Highlighted list items ***/
 
 #downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover {
   border-radius: 3px;
   border-top: 1px solid hsla(0,0%,100%,.3);
   border-bottom: 1px solid hsla(0,0%,0%,.2);
   background-color: Highlight;
-  background-image: linear-gradient(hsla(0,0%,100%,.1), hsla(0,0%,100%,0));
+  background-image: linear-gradient(hsla(0,0%,100%,.1), transparent);
   color: HighlightText;
   cursor: pointer;
 }
 
 /*** Button icons ***/
 
 .downloadButton.downloadCancel {
   -moz-image-region: rect(0px, 16px, 16px, 0px);
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -20,17 +20,17 @@
   --space-above-tabbar: 9px;
 
   --tabs-toolbar-color: #333;
   --toolbarbutton-hover-background: hsla(0,0%,100%,.1) linear-gradient(hsla(0,0%,100%,.3), hsla(0,0%,100%,.1)) padding-box;
   --toolbarbutton-hover-bordercolor: hsla(0,0%,0%,.2);
   --toolbarbutton-hover-boxshadow: 0 1px 0 hsla(0,0%,100%,.5),
                                    0 1px 0 hsla(0,0%,100%,.5) inset;
 
-  --toolbarbutton-active-background: hsla(0,0%,0%,.02) linear-gradient(hsla(0,0%,0%,.12), hsla(0,0%,0%,0)) border-box;
+  --toolbarbutton-active-background: hsla(0,0%,0%,.02) linear-gradient(hsla(0,0%,0%,.12), transparent) border-box;
   --toolbarbutton-active-boxshadow: 0 1px 0 hsla(0,0%,100%,.5),
                                     0 1px 0 hsla(0,0%,0%,.05) inset,
                                     0 1px 1px hsla(0,0%,0%,.2) inset;
 
   --toolbarbutton-checkedhover-backgroundcolor: hsla(0,0%,0%,.09);
 
   --toolbarbutton-combined-boxshadow: 0 0 0 1px hsla(0,0%,100%,.15);
   --toolbarbutton-combined-backgroundimage: linear-gradient(hsla(0,0%,0%,.15) 0, hsla(0,0%,0%,.15) 18px);
@@ -2123,17 +2123,17 @@ toolbarbutton[sdk-button="true"][cui-are
   padding: 0 3px;
   list-style-image: var(--urlbar-dropmarker-url);
   -moz-image-region: var(--urlbar-dropmarker-region);
 }
 
 .urlbar-history-dropmarker[open="true"],
 .urlbar-history-dropmarker:hover:active {
   -moz-image-region: var(--urlbar-dropmarker-active-region);
-  background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), hsla(205,100%,70%,0));
+  background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), transparent);
 }
 
 @media (min-resolution: 2dppx) {
   .urlbar-history-dropmarker {
     list-style-image: var(--urlbar-dropmarker-2x-url);
     -moz-image-region: var(--urlbar-dropmarker-2x-region);
   }
 
@@ -2152,17 +2152,17 @@ toolbarbutton[sdk-button="true"][cui-are
 }
 
 .urlbar-icon {
   padding: 0 3px;
 }
 
 .urlbar-icon[open="true"],
 .urlbar-icon:hover:active {
-  background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), hsla(205,100%,70%,0));
+  background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), transparent);
 }
 
 #urlbar-search-splitter {
   min-width: 8px;
   width: 8px;
   background-image: none;
   margin: 0 -4px;
   position: relative;
@@ -2368,17 +2368,17 @@ richlistitem[type~="action"][actiontype=
   margin: 0;
   -moz-padding-start: 2px;
   -moz-padding-end: 1px;
   background-origin: border-box;
   list-style-image: url("chrome://browser/skin/reload-stop-go.png");
 }
 
 #urlbar > toolbarbutton:not([disabled]):hover:active {
-  background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), hsla(205,100%,70%,0));
+  background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), transparent);
 }
 
 #urlbar-go-button {
   -moz-image-region: rect(0, 42px, 14px, 28px);
 }
 
 #urlbar-go-button:hover:active {
   -moz-image-region: rect(14px, 42px, 28px, 28px);
@@ -4518,26 +4518,26 @@ menulist.translate-infobar-element > .me
 }
 
 /* === end of social toolbar provider menu === */
 
 %include ../shared/social/chat.inc.css
 
 .chat-titlebar {
   background-color: #d9d9d9;
-  background-image: linear-gradient(rgba(255,255,255,.43), rgba(255,255,255,0));
+  background-image: linear-gradient(rgba(255,255,255,.43), transparent);
 }
 
 .chat-titlebar[selected] {
   background-color: #f0f0f0;
 }
 
 .chatbar-button {
   background-color: #d9d9d9;
-  background-image: linear-gradient(rgba(255,255,255,.43), rgba(255,255,255,0));
+  background-image: linear-gradient(rgba(255,255,255,.43), transparent);
   border-top-left-radius: @toolbarbuttonCornerRadius@;
   border-top-right-radius: @toolbarbuttonCornerRadius@;
 }
 
 .chatbar-button:hover,
 .chatbar-button[open="true"] {
   background-color: #f0f0f0;
 }
--- a/browser/themes/osx/customizableui/panelUIOverlay.css
+++ b/browser/themes/osx/customizableui/panelUIOverlay.css
@@ -7,24 +7,24 @@
 .panel-subviews {
   background-color: hsla(0,0%,100%,.97);
 }
 
 @media (min-resolution: 2dppx) {
   #PanelUI-help[panel-multiview-anchor="true"]::after,
   toolbarbutton[panel-multiview-anchor="true"] {
     background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted@2x.png),
-                      linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
+                      linear-gradient(rgba(255,255,255,0.3), transparent);
     background-size: 16px, auto;
   }
 
   #PanelUI-help[panel-multiview-anchor="true"]:-moz-locale-dir(rtl)::after,
   toolbarbutton[panel-multiview-anchor="true"]:-moz-locale-dir(rtl) {
     background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted-rtl@2x.png),
-                      linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
+                      linear-gradient(rgba(255,255,255,0.3), transparent);
   }
 
   #PanelUI-fxa-status {
     list-style-image: url(chrome://browser/skin/sync-horizontalbar@2x.png);
   }
 
   #PanelUI-fxa-status[status="active"] {
     list-style-image: url(chrome://browser/skin/syncProgress-horizontalbar@2x.png);
--- a/browser/themes/shared/customizableui/customizeMode.inc.css
+++ b/browser/themes/shared/customizableui/customizeMode.inc.css
@@ -347,32 +347,32 @@ toolbarpaletteitem[place="toolbar"] {
 }
 
 #customization-lwtheme-menu-recommended {
   border-top: 1px solid hsla(210,4%,10%,.05);
   border-bottom: 1px solid hsla(210,4%,10%,.05);
 }
 
 #customization-lwtheme-menu-footer {
-  background: linear-gradient(hsla(210,4%,10%,.05) 60%, hsla(210,4%,10%,0)) border-box;
+  background: linear-gradient(hsla(210,4%,10%,.05) 60%, transparent) border-box;
   border-top: 1px solid hsla(210,4%,10%,.05);
   margin-bottom: -10px;
 }
 
 .customization-lwtheme-menu-footeritem {
   -moz-appearance: none;
   -moz-box-flex: 1;
   color: hsl(0,0%,50%);
   border-style: none;
   padding: 10px;
   margin-left: 0;
   margin-right: 0;
 }
 
 .customization-lwtheme-menu-footeritem:hover {
-  background: linear-gradient(hsla(210,4%,10%,.08) 40%, hsla(210,4%,10%,0)) padding-box;
+  background: linear-gradient(hsla(210,4%,10%,.08) 40%, transparent) padding-box;
 }
 
 .customization-lwtheme-menu-footeritem:first-child {
   -moz-border-end: 1px solid hsla(210,4%,10%,.15);
 }
 
 %include customizeTip.inc.css
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -82,19 +82,19 @@
   from { margin-top: -.5em; } to { margin-top: calc(64px - .5em); }
 }
 
 @keyframes whimsyRotate {
   to { transform: perspective(5000px) rotateY(360deg); }
 }
 
 #PanelUI-button {
-  background-image: linear-gradient(to bottom, hsla(0,0%,100%,0), hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, hsla(0,0%,100%,0)),
-                    linear-gradient(to bottom, hsla(210,54%,20%,0), hsla(210,54%,20%,.3) 30%, hsla(210,54%,20%,.3) 70%, hsla(210,54%,20%,0)),
-                    linear-gradient(to bottom, hsla(0,0%,100%,0), hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, hsla(0,0%,100%,0));
+  background-image: linear-gradient(to bottom, transparent, hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, transparent),
+                    linear-gradient(to bottom, transparent, hsla(210,54%,20%,.3) 30%, hsla(210,54%,20%,.3) 70%, transparent),
+                    linear-gradient(to bottom, transparent, hsla(0,0%,100%,.3) 30%, hsla(0,0%,100%,.3) 70%, transparent);
   background-size: 1px calc(100% - 1px), 1px calc(100% - 1px), 1px  calc(100% - 1px) !important;
   background-position: 0px 0px, 1px 0px, 2px 0px;
   background-repeat: no-repeat;
 }
 
 #PanelUI-button:-moz-locale-dir(rtl) {
   background-position: 100% 0, calc(100% - 1px) 0, calc(100% - 2px) 0;
 }
@@ -869,49 +869,49 @@ toolbarbutton[panel-multiview-anchor="tr
   background-color: Highlight;
 }
 
 #PanelUI-help[panel-multiview-anchor="true"] + toolbarseparator {
   display: none;
 }
 
 #PanelUI-help[panel-multiview-anchor="true"] {
-  background-image: linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
+  background-image: linear-gradient(rgba(255,255,255,0.3), transparent);
   background-position: 0;
 }
 
 #PanelUI-help[panel-multiview-anchor="true"]::after {
   content: "";
   position: absolute;
   top: 0;
   height: 100%;
   width: @exitSubviewGutterWidth@;
   background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted.png),
-                    linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
+                    linear-gradient(rgba(255,255,255,0.3), transparent);
   background-repeat: no-repeat;
   background-color: Highlight;
   background-position: left 10px center, 0;
 }
 
 #PanelUI-help[panel-multiview-anchor="true"]:-moz-locale-dir(rtl)::after {
   background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted-rtl.png),
-                    linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
+                    linear-gradient(rgba(255,255,255,0.3), transparent);
   background-position: right 10px center, 0;
 }
 
 toolbarbutton[panel-multiview-anchor="true"] {
   background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted.png),
-                    linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
+                    linear-gradient(rgba(255,255,255,0.3), transparent);
   background-position: right calc(@menuPanelButtonWidth@ / 2 - @exitSubviewGutterWidth@ + 2px) center;
   background-repeat: no-repeat, repeat;
 }
 
 toolbarbutton[panel-multiview-anchor="true"]:-moz-locale-dir(rtl) {
   background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted-rtl.png),
-                    linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
+                    linear-gradient(rgba(255,255,255,0.3), transparent);
   background-position: left calc(@menuPanelButtonWidth@ / 2 - @exitSubviewGutterWidth@ + 2px) center;
 }
 
 toolbarpaletteitem[place="palette"] > .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
 #bookmarks-menu-button[cui-areatype="menu-panel"] > .toolbarbutton-menubutton-dropmarker {
   display: none;
 }
 
@@ -1000,19 +1000,19 @@ toolbarpaletteitem[haswideitem][place="p
   border-top-left-radius: 0;
   border-bottom-left-radius: 0;
 }
 
 .toolbaritem-combined-buttons > separator {
   -moz-appearance: none;
   width: 3px;
   -moz-box-align: stretch;
-  background-image: linear-gradient(to bottom, hsla(0,0%,100%,0), hsla(0,0%,100%,.3) 40%, hsla(0,0%,100%,.3) 60%, hsla(0,0%,100%,0)),
-                    linear-gradient(to bottom, hsla(210,54%,20%,0), hsla(210,54%,20%,.15) 40%, hsla(210,54%,20%,.15) 60%, hsla(210,54%,20%,0)),
-                    linear-gradient(to bottom, hsla(0,0%,100%,0), hsla(0,0%,100%,.3) 40%, hsla(0,0%,100%,.3) 60%, hsla(0,0%,100%,0));
+  background-image: linear-gradient(to bottom, transparent, hsla(0,0%,100%,.3) 40%, hsla(0,0%,100%,.3) 60%, transparent),
+                    linear-gradient(to bottom, transparent, hsla(210,54%,20%,.15) 40%, hsla(210,54%,20%,.15) 60%, transparent),
+                    linear-gradient(to bottom, transparent, hsla(0,0%,100%,.3) 40%, hsla(0,0%,100%,.3) 60%, transparent);
   background-size: 1px, 1px, 1px;
   background-position: 0 0, 1px 0, 2px 0;
   background-repeat: no-repeat;
 }
 
 .toolbaritem-combined-buttons@inAnyPanel@ > separator {
   margin: .5em 0;
   width: 1px;
--- a/browser/themes/shared/devtools/app-manager/device.css
+++ b/browser/themes/shared/devtools/app-manager/device.css
@@ -298,17 +298,17 @@ button {
   float: right;
   padding: 0 5px;
 }
 
 /*****************     HEADER      *****************/
 
 header {
   padding-top: 140px;
-  background-image: linear-gradient(to bottom, rgba(0,0,0,0), rgba(0,0,0,0.7));
+  background-image: linear-gradient(to bottom, transparent, rgba(0,0,0,0.7));
   color: #FFF;
   text-shadow: 0 1px 2px rgba(0,0,0,0.8);
   padding: 10px;
 }
 
 
 
 /*****************      APPS & BROWSER TABS      *****************/
--- a/browser/themes/shared/devtools/toolbars.inc.css
+++ b/browser/themes/shared/devtools/toolbars.inc.css
@@ -519,19 +519,19 @@
 #toolbox-dock-bottom:hover,
 #toolbox-dock-side:hover {
   opacity: 1;
 }
 
 .devtools-separator {
   margin: 0 2px;
   width: 2px;
-  background-image: linear-gradient(hsla(204,45%,98%,0), hsla(204,45%,98%,.1), hsla(204,45%,98%,0)),
-                    linear-gradient(hsla(206,37%,4%,0), hsla(206,37%,4%,.6), hsla(206,37%,4%,0)),
-                    linear-gradient(hsla(204,45%,98%,0), hsla(204,45%,98%,.1), hsla(204,45%,98%,0));
+  background-image: linear-gradient(transparent, hsla(204,45%,98%,.1), transparent),
+                    linear-gradient(transparent, hsla(206,37%,4%,.6), transparent),
+                    linear-gradient(transparent, hsla(204,45%,98%,.1), transparent);
   background-size: 1px 100%;
   background-repeat: no-repeat;
   background-position: 0, 1px, 2px;
 }
 
 #toolbox-buttons:empty + .devtools-separator,
 .devtools-separator[invisible] {
   visibility: hidden;
--- a/browser/themes/shared/social/chat.inc.css
+++ b/browser/themes/shared/social/chat.inc.css
@@ -121,17 +121,17 @@ chatbar > chatbox > .chat-titlebar > .ch
   margin-right: 2px;
 }
 
 .chat-titlebar[minimized="true"] {
   border-bottom: none;
 }
 
 .chat-titlebar[activity] {
-  background-image: radial-gradient(ellipse closest-side at center, rgb(255,255,255), rgba(255,255,255,0));
+  background-image: radial-gradient(ellipse closest-side at center, rgb(255,255,255), transparent);
   background-repeat: no-repeat;
   background-size: 100% 20px;
   background-position: 0 -10px;
 }
 
 chatbox[dark=true] > .chat-titlebar,
 chatbox[dark=true] > .chat-titlebar[selected] {
   border-bottom: none;
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -289,17 +289,17 @@
 
 /* Pinned tab separators need position: absolute when positioned (during overflow). */
 #tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned]::before {
   height: 100%;
   position: absolute;
 }
 
 .tabbrowser-tab[pinned][titlechanged]:not([selected="true"]) > .tab-stack > .tab-content {
-  background-image: radial-gradient(farthest-corner at center bottom, rgb(255,255,255) 3%, rgba(186,221,251,0.75) 20%, rgba(127,179,255,0.25) 40%, rgba(127,179,255,0) 70%);
+  background-image: radial-gradient(farthest-corner at center bottom, rgb(255,255,255) 3%, rgba(186,221,251,0.75) 20%, rgba(127,179,255,0.25) 40%, transparent 70%);
   background-position: center bottom @tabToolbarNavbarOverlap@;
   background-repeat: no-repeat;
   background-size: 85% 100%;
 }
 
 /* Background tab separators (3px wide).
    Also show separators beside the selected tab when dragging it. */
 #tabbrowser-tabs[movingtab] > .tabbrowser-tab[beforeselected]:not([last-visible-tab])::after,
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -282,17 +282,17 @@
   /* Position the toolbar above the bottom of background tabs */
   position: relative;
   z-index: 1;
 }
 
 #nav-bar {
   border-top: 1px solid @toolbarShadowColor@ !important;
   background-clip: padding-box;
-  background-image: linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
+  background-image: linear-gradient(@toolbarHighlight@, transparent);
   box-shadow: 0 1px 0 @toolbarHighlight@ inset;
 }
 
 %ifdef WINDOWS_AERO
 @media not all and (-moz-windows-compositor) {
 %endif
   #TabsToolbar[collapsed="true"] + #nav-bar {
     border-top-style: none !important;
@@ -872,17 +872,17 @@ toolbarbutton[sdk-button="true"][cui-are
   margin-bottom: @tabToolbarNavbarOverlap@;
 }
 
 #TabsToolbar .toolbarbutton-1:not([disabled=true]):hover,
 #TabsToolbar .toolbarbutton-1[open],
 #TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled=true]):hover,
 .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled=true]):hover {
-  background-image: linear-gradient(rgba(255,255,255,0), rgba(255,255,255,.5)),
+  background-image: linear-gradient(transparent, rgba(255,255,255,.5)),
                     linear-gradient(transparent, rgba(0,0,0,.25) 30%),
                     linear-gradient(transparent, rgba(0,0,0,.25) 30%);
   background-position: 1px -1px, 0 -1px, 100% -1px;
   background-size: calc(100% - 2px) 100%, 1px 100%, 1px 100%;
   background-repeat: no-repeat;
 }
 
 /* unified back/forward button */
@@ -1218,22 +1218,22 @@ html|*.urlbar-input:-moz-lwtheme::-moz-p
 }
 
 .searchbar-engine-button,
 .search-go-container {
   padding: 2px 2px;
 }
 
 .urlbar-icon:hover {
-  background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.3), hsla(200,100%,70%,0));
+  background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.3), transparent);
 }
 
 .urlbar-icon[open="true"],
 .urlbar-icon:hover:active {
-  background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.1), hsla(200,100%,70%,0));
+  background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.1), transparent);
 }
 
 #urlbar-search-splitter {
   min-width: 6px;
   -moz-margin-start: -3px;
   border: none;
   background: transparent;
 }
@@ -1338,23 +1338,23 @@ html|*.urlbar-input:-moz-lwtheme::-moz-p
   background-color: transparent;
   border: none;
   width: auto;
   list-style-image: url("chrome://browser/skin/urlbar-history-dropmarker.png");
   -moz-image-region: rect(0px, 11px, 14px, 0px);
 }
 
 .urlbar-history-dropmarker:hover {
-  background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), hsla(205,100%,70%,0));
+  background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.3), transparent);
   -moz-image-region: rect(0px, 22px, 14px, 11px);
 }
 
 .urlbar-history-dropmarker:hover:active,
 .urlbar-history-dropmarker[open="true"] {
-  background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.1), hsla(205,100%,70%,0));
+  background-image: radial-gradient(circle closest-side, hsla(205,100%,70%,.1), transparent);
   -moz-image-region: rect(0px, 33px, 14px, 22px);
 }
 
 /* page proxy icon */
 
 %include ../shared/identity-block.inc.css
 
 #page-proxy-favicon {
@@ -1490,58 +1490,58 @@ richlistitem[type~="action"][actiontype=
   list-style-image: url("chrome://browser/skin/reload-stop-go.png");
 }
 
 #urlbar-reload-button {
   -moz-image-region: rect(0, 14px, 14px, 0);
 }
 
 #urlbar-reload-button:not([disabled]):hover {
-  background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.2), hsla(200,100%,70%,0));
+  background-image: radial-gradient(circle closest-side, hsla(200,100%,70%,.2), transparent);
   -moz-image-region: rect(14px, 14px, 28px, 0);
 }
 
 #urlbar-reload-button:not([disabled]):hover:active {
-  background-image: radial-gradient(circle closest-side, hsla(200,100%,60%,.1), hsla(200,100%,60%,0));
+  background-image: radial-gradient(circle closest-side, hsla(200,100%,60%,.1), transparent);
   -moz-image-region: rect(28px, 14px, 42px, 0);
 }
 
 #urlbar-reload-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 #urlbar-go-button {
   -moz-image-region: rect(0, 42px, 14px, 28px);
 }
 
 #urlbar-go-button:hover {
-  background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.2), hsla(110,70%,50%,0));
+  background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.2), transparent);
   -moz-image-region: rect(14px, 42px, 28px, 28px);
 }
 
 #urlbar-go-button:hover:active {
-  background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.1), hsla(110,70%,50%,0));
+  background-image: radial-gradient(circle closest-side, hsla(110,70%,50%,.1), transparent);
   -moz-image-region: rect(28px, 42px, 42px, 28px);
 }
 
 #urlbar-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
   transform: scaleX(-1);
 }
 
 #urlbar-stop-button {
   -moz-image-region: rect(0, 28px, 14px, 14px);
 }
 
 #urlbar-stop-button:not([disabled]):hover {
-  background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.3), hsla(5,100%,75%,0));
+  background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.3), transparent);
   -moz-image-region: rect(14px, 28px, 28px, 14px);
 }
 
 #urlbar-stop-button:hover:active {
-  background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.1), hsla(5,100%,75%,0));
+  background-image: radial-gradient(circle closest-side, hsla(5,100%,75%,.1), transparent);
   -moz-image-region: rect(28px, 28px, 42px, 14px);
 }
 
 /* popup blocker button */
 
 #page-report-button {
   list-style-image: url("chrome://browser/skin/urlbar-popup-blocked.png");
   -moz-image-region: rect(0, 16px, 16px, 0);
@@ -2767,27 +2767,27 @@ toolbarpaletteitem[place="palette"] > #s
 .social-panel-frame {
   border-radius: inherit;
 }
 
 %include ../shared/social/chat.inc.css
 
 .chat-titlebar {
   background-color: #c4cfde;
-  background-image: linear-gradient(rgba(255,255,255,.5), rgba(255,255,255,0));
+  background-image: linear-gradient(rgba(255,255,255,.5), transparent);
 }
 
 .chat-titlebar[selected] {
   background-color: #dae3f0;
 }
 
 .chatbar-button {
   -moz-appearance: none;
   background-color: #c4cfde;
-  background-image: linear-gradient(rgba(255,255,255,.5), rgba(255,255,255,0));
+  background-image: linear-gradient(rgba(255,255,255,.5), transparent);
 }
 
 .chatbar-button > .toolbarbutton-icon {
   -moz-margin-end: 0;
 }
 
 .chatbar-button:hover,
 .chatbar-button[open="true"] {
--- a/browser/themes/windows/downloads/allDownloadsViewOverlay-aero.css
+++ b/browser/themes/windows/downloads/allDownloadsViewOverlay-aero.css
@@ -16,20 +16,20 @@
 
   This styling should be kept in sync with the style from the above file.
   */
   #downloadsRichListBox > richlistitem.download[selected] {
     color: inherit;
     background-color: transparent;
     /* four gradients for the bevel highlights on each edge, one for blue background */
     background-image:
-      linear-gradient(to bottom, rgba(255,255,255,0.9) 3px, rgba(255,255,255,0) 3px),
-      linear-gradient(to right, rgba(255,255,255,0.5) 3px, rgba(255,255,255,0) 3px),
-      linear-gradient(to left, rgba(255,255,255,0.5) 3px, rgba(255,255,255,0) 3px),
-      linear-gradient(to top, rgba(255,255,255,0.4) 3px, rgba(255,255,255,0) 3px),
+      linear-gradient(to bottom, rgba(255,255,255,0.9) 3px, transparent 3px),
+      linear-gradient(to right, rgba(255,255,255,0.5) 3px, transparent 3px),
+      linear-gradient(to left, rgba(255,255,255,0.5) 3px, transparent 3px),
+      linear-gradient(to top, rgba(255,255,255,0.4) 3px, transparent 3px),
       linear-gradient(to bottom, rgba(163,196,247,0.3), rgba(122,180,246,0.3));
     background-clip: content-box;
     border-radius: 6px;
     outline: 1px solid rgb(124,163,206);
     -moz-outline-radius: 3px;
     outline-offset: -2px;
   }
 }
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -79,17 +79,17 @@ browser.jar:
         skin/classic/browser/searchbar-dropdown-arrow.png
         skin/classic/browser/Secure24.png
         skin/classic/browser/setDesktopBackground.css
         skin/classic/browser/slowStartup-16.png
         skin/classic/browser/theme-switcher-icon.png
         skin/classic/browser/Toolbar.png                             (Toolbar-XP.png)
         skin/classic/browser/Toolbar-inverted.png
         skin/classic/browser/Toolbar-lunaSilver.png
-        skin/classic/browser/toolbarbutton-dropdown-arrow.png
+        skin/classic/browser/toolbarbutton-dropdown-arrow.png        (toolbarbutton-dropdown-arrow-XPVista7.png)
         skin/classic/browser/toolbarbutton-dropdown-arrow-inverted.png
         skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
         skin/classic/browser/undoCloseTab@2x.png                     (../shared/undoCloseTab@2x.png)
         skin/classic/browser/urlbar-arrow.png
         skin/classic/browser/urlbar-popup-blocked.png
         skin/classic/browser/urlbar-history-dropmarker.png
         skin/classic/browser/notification-pluginNormal.png           (../shared/plugins/notification-pluginNormal.png)
         skin/classic/browser/notification-pluginAlert.png            (../shared/plugins/notification-pluginAlert.png)
@@ -187,23 +187,23 @@ browser.jar:
         skin/classic/browser/preferences/in-content/icons@2x.png     (../shared/incontentprefs/icons@2x.png)
         skin/classic/browser/preferences/applications.css            (preferences/applications.css)
         skin/classic/browser/preferences/aboutPermissions.css        (preferences/aboutPermissions.css)
         skin/classic/browser/social/services-16.png                  (social/services-16.png)
         skin/classic/browser/social/services-64.png                  (social/services-64.png)
         skin/classic/browser/social/chat-icons.svg                   (../shared/social/chat-icons.svg)
         skin/classic/browser/social/gear_default.png                 (../shared/social/gear_default.png)
         skin/classic/browser/social/gear_clicked.png                 (../shared/social/gear_clicked.png)
-        skin/classic/browser/tabbrowser/newtab.png                   (tabbrowser/newtab.png)
+        skin/classic/browser/tabbrowser/newtab.png                   (tabbrowser/newtab-XPVista7.png)
         skin/classic/browser/tabbrowser/newtab-inverted.png          (tabbrowser/newtab-inverted.png)
         skin/classic/browser/tabbrowser/connecting.png               (tabbrowser/connecting.png)
         skin/classic/browser/tabbrowser/loading.png                  (tabbrowser/loading.png)
         skin/classic/browser/tabbrowser/tab-active-middle.png        (tabbrowser/tab-active-middle.png)
         skin/classic/browser/tabbrowser/tab-active-middle@2x.png     (tabbrowser/tab-active-middle@2x.png)
-        skin/classic/browser/tabbrowser/tab-arrow-left.png           (tabbrowser/tab-arrow-left.png)
+        skin/classic/browser/tabbrowser/tab-arrow-left.png           (tabbrowser/tab-arrow-left-XPVista7.png)
         skin/classic/browser/tabbrowser/tab-arrow-left-inverted.png  (tabbrowser/tab-arrow-left-inverted.png)
         skin/classic/browser/tabbrowser/tab-background-start.png     (tabbrowser/tab-background-start.png)
         skin/classic/browser/tabbrowser/tab-background-start@2x.png  (tabbrowser/tab-background-start@2x.png)
         skin/classic/browser/tabbrowser/tab-background-middle.png    (tabbrowser/tab-background-middle.png)
         skin/classic/browser/tabbrowser/tab-background-middle@2x.png (tabbrowser/tab-background-middle@2x.png)
         skin/classic/browser/tabbrowser/tab-background-end.png       (tabbrowser/tab-background-end.png)
         skin/classic/browser/tabbrowser/tab-background-end@2x.png    (tabbrowser/tab-background-end@2x.png)
         skin/classic/browser/tabbrowser/tab-overflow-indicator.png   (../shared/tabbrowser/tab-overflow-indicator.png)
@@ -511,16 +511,17 @@ browser.jar:
         skin/classic/aero/browser/setDesktopBackground.css
         skin/classic/aero/browser/slowStartup-16.png
         skin/classic/aero/browser/theme-switcher-icon.png
         skin/classic/aero/browser/theme-switcher-icon-aero.png
         skin/classic/aero/browser/Toolbar.png
         skin/classic/aero/browser/Toolbar-inverted.png
         skin/classic/aero/browser/Toolbar-aero.png
         skin/classic/aero/browser/toolbarbutton-dropdown-arrow.png
+        skin/classic/aero/browser/toolbarbutton-dropdown-arrow-XPVista7.png
         skin/classic/aero/browser/toolbarbutton-dropdown-arrow-inverted.png
         skin/classic/aero/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
         skin/classic/aero/browser/undoCloseTab@2x.png                     (../shared/undoCloseTab@2x.png)
         skin/classic/aero/browser/urlbar-arrow.png
         skin/classic/aero/browser/urlbar-popup-blocked.png
         skin/classic/aero/browser/urlbar-history-dropmarker.png
         skin/classic/aero/browser/notification-pluginNormal.png     (../shared/plugins/notification-pluginNormal.png)
         skin/classic/aero/browser/notification-pluginAlert.png      (../shared/plugins/notification-pluginAlert.png)
@@ -621,22 +622,24 @@ browser.jar:
         skin/classic/aero/browser/preferences/applications.css       (preferences/applications.css)
         skin/classic/aero/browser/preferences/aboutPermissions.css   (preferences/aboutPermissions.css)
         skin/classic/aero/browser/social/services-16.png             (social/services-16.png)
         skin/classic/aero/browser/social/services-64.png             (social/services-64.png)
         skin/classic/aero/browser/social/chat-icons.svg              (../shared/social/chat-icons.svg)
         skin/classic/aero/browser/social/gear_default.png            (../shared/social/gear_default.png)
         skin/classic/aero/browser/social/gear_clicked.png            (../shared/social/gear_clicked.png)
         skin/classic/aero/browser/tabbrowser/newtab.png              (tabbrowser/newtab.png)
+        skin/classic/aero/browser/tabbrowser/newtab-XPVista7.png     (tabbrowser/newtab-XPVista7.png)
         skin/classic/aero/browser/tabbrowser/newtab-inverted.png     (tabbrowser/newtab-inverted.png)
         skin/classic/aero/browser/tabbrowser/connecting.png          (tabbrowser/connecting.png)
         skin/classic/aero/browser/tabbrowser/loading.png             (tabbrowser/loading.png)
         skin/classic/aero/browser/tabbrowser/tab-active-middle.png   (tabbrowser/tab-active-middle.png)
         skin/classic/aero/browser/tabbrowser/tab-active-middle@2x.png (tabbrowser/tab-active-middle@2x.png)
         skin/classic/aero/browser/tabbrowser/tab-arrow-left.png      (tabbrowser/tab-arrow-left.png)
+        skin/classic/aero/browser/tabbrowser/tab-arrow-left-XPVista7.png (tabbrowser/tab-arrow-left-XPVista7.png)
         skin/classic/aero/browser/tabbrowser/tab-arrow-left-inverted.png (tabbrowser/tab-arrow-left-inverted.png)
         skin/classic/aero/browser/tabbrowser/tab-background-start.png    (tabbrowser/tab-background-start.png)
         skin/classic/aero/browser/tabbrowser/tab-background-start@2x.png (tabbrowser/tab-background-start@2x.png)
         skin/classic/aero/browser/tabbrowser/tab-background-middle.png   (tabbrowser/tab-background-middle.png)
         skin/classic/aero/browser/tabbrowser/tab-background-middle@2x.png (tabbrowser/tab-background-middle@2x.png)
         skin/classic/aero/browser/tabbrowser/tab-background-end.png      (tabbrowser/tab-background-end.png)
         skin/classic/aero/browser/tabbrowser/tab-background-end@2x.png   (tabbrowser/tab-background-end@2x.png)
         skin/classic/aero/browser/tabbrowser/tab-overflow-indicator.png  (../shared/tabbrowser/tab-overflow-indicator.png)
@@ -870,10 +873,19 @@ browser.jar:
 % override chrome://browser/skin/menuPanel-small.png       chrome://browser/skin/menuPanel-small-aero.png          os=WINNT osversion=6.1
 
 % override chrome://browser/skin/theme-switcher-icon.png   chrome://browser/skin/theme-switcher-icon-aero.png      os=WINNT osversion=6
 % override chrome://browser/skin/theme-switcher-icon.png   chrome://browser/skin/theme-switcher-icon-aero.png      os=WINNT osversion=6.1
 
 % override chrome://browser/skin/sync-horizontalbar.png          chrome://browser/skin/sync-horizontalbar-XPVista7.png          os=WINNT osversion<6.2
 % override chrome://browser/skin/syncProgress-horizontalbar.png  chrome://browser/skin/syncProgress-horizontalbar-XPVista7.png  os=WINNT osversion<6.2
 
+% override chrome://browser/skin/toolbarbutton-dropdown-arrow.png  chrome://browser/skin/toolbarbutton-dropdown-arrow-XPVista7.png  os=WINNT osversion=6
+% override chrome://browser/skin/toolbarbutton-dropdown-arrow.png  chrome://browser/skin/toolbarbutton-dropdown-arrow-XPVista7.png  os=WINNT osversion=6.1
+
+% override chrome://browser/skin/tabbrowser/newtab.png     chrome://browser/skin/tabbrowser/newtab-XPVista7.png    os=WINNT osversion=6
+% override chrome://browser/skin/tabbrowser/newtab.png     chrome://browser/skin/tabbrowser/newtab-XPVista7.png    os=WINNT osversion=6.1
+
+% override chrome://browser/skin/tabbrowser/tab-arrow-left.png     chrome://browser/skin/tabbrowser/tab-arrow-left-XPVista7.png    os=WINNT osversion=6
+% override chrome://browser/skin/tabbrowser/tab-arrow-left.png     chrome://browser/skin/tabbrowser/tab-arrow-left-XPVista7.png    os=WINNT osversion=6.1
+
 % override chrome://browser/skin/loop/toolbar.png          chrome://browser/skin/loop/toolbar-XPVista7.png         os=WINNT osversion=6
 % override chrome://browser/skin/loop/toolbar.png          chrome://browser/skin/loop/toolbar-XPVista7.png         os=WINNT osversion=6.1
--- a/browser/themes/windows/places/organizer-aero.css
+++ b/browser/themes/windows/places/organizer-aero.css
@@ -27,17 +27,17 @@
 }
 
 @media (-moz-windows-compositor) {
   #placesToolbox {
     border-top: none;
   }
 
   #placesToolbar {
-    background-image: linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
+    background-image: linear-gradient(@toolbarHighlight@, transparent);
   }
 }
 
 @media (-moz-windows-default-theme) {
   #placesView > splitter {
     border: 0;
     -moz-border-end: 1px solid #A9B7C9;
     min-width: 0;
copy from browser/themes/windows/tabbrowser/newtab.png
copy to browser/themes/windows/tabbrowser/newtab-XPVista7.png
index 7cea7bdd8f7f073156e7fe505fa842ded15195c0..f4cf55ffce71954c594d5bed27666d7bcc0ef9ac
GIT binary patch
literal 105
zc%17D@N?(olHy`uVBq!ia0vp^0zfRp!3HFQtmCqPl!2#<V@SoV<RAb4+q+gwkaS>D
zIL9C^KK;Lf%_N?J!or{aJj-r0T#Pti(!gY+!^6PvKHYfRq}vWTK)no}u6{1-oD!M<
DXL26&
copy from browser/themes/windows/tabbrowser/tab-arrow-left.png
copy to browser/themes/windows/tabbrowser/tab-arrow-left-XPVista7.png
index 951d5cbe57e67709286b7bd7a1aec7ccec0ed5fe..1dd8a226f753f38eb6a40bbdeeef0e35af7d32b8
GIT binary patch
literal 122
zc%17D@N?(olHy`uVBq!ia0vp^{6H+o$P6Scj-)&SQfvV}A+9nqGA=GIe`X(+2Z}J3
z1o;IsI6S+N2IMGtx;Tb#Tu)ALU}Kk%;5RTZH`rio@S#y~0^<`V*2W+M28N_REXHYe
Srw##CGkCiCxvX<aXaWE<EFI_o
copy from browser/themes/windows/toolbarbutton-dropdown-arrow.png
copy to browser/themes/windows/toolbarbutton-dropdown-arrow-XPVista7.png
index 5f892f532efdb3742322674f4ee9b4adbb5cd93a..08d9da1d1245845502678a3b126b79be0c23b2d8
GIT binary patch
literal 91
zc%17D@N?(olHy`uVBq!ia0vp^oIuRZ!3HF6%}!4MQc9jKjv*Ss$qG_CcI^0bymg_M
pLc;<k35695Y#dq(7z_*;7-aX!Y-HteDFdoz@O1TaS?83{1ORpU6|MjP
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -2868,17 +2868,17 @@ nsDocShell::PopProfileTimelineMarkers(JS
   // docShell level but we can only be sure that a paint did happen in a
   // docShell if an Layer marker type was recorded too.
 
   nsTArray<mozilla::dom::ProfileTimelineMarker> profileTimelineMarkers;
 
   // If we see an unpaired START, we keep it around for the next call
   // to PopProfileTimelineMarkers.  We store the kept START objects in
   // this array.
-  decltype(mProfileTimelineMarkers) keptMarkers;
+  nsTArray<InternalProfileTimelineMarker*> keptMarkers;
 
   for (uint32_t i = 0; i < mProfileTimelineMarkers.Length(); ++i) {
     ProfilerMarkerTracing* startPayload = static_cast<ProfilerMarkerTracing*>(
       mProfileTimelineMarkers[i]->mPayload);
     const char* startMarkerName = mProfileTimelineMarkers[i]->mName.get();
 
     bool hasSeenPaintedLayer = false;
 
--- a/docshell/shistory/src/nsSHistory.cpp
+++ b/docshell/shistory/src/nsSHistory.cpp
@@ -1077,18 +1077,19 @@ void
 nsSHistory::GloballyEvictContentViewers()
 {
   // First, collect from each SHistory object the transactions which have a
   // cached content viewer.  Associate with each transaction its distance from
   // its SHistory's current index.
 
   nsTArray<TransactionAndDistance> transactions;
 
-  nsSHistory *shist = static_cast<nsSHistory*>(PR_LIST_HEAD(&gSHistoryList));
-  while (shist != &gSHistoryList) {
+  PRCList* listEntry = PR_LIST_HEAD(&gSHistoryList);
+  while (listEntry != &gSHistoryList) {
+    nsSHistory* shist = static_cast<nsSHistory*>(listEntry);
 
     // Maintain a list of the transactions which have viewers and belong to
     // this particular shist object.  We'll add this list to the global list,
     // |transactions|, eventually.
     nsTArray<TransactionAndDistance> shTransactions;
 
     // Content viewers are likely to exist only within shist->mIndex -/+
     // gHistoryMaxViewers, so only search within that range.
@@ -1137,17 +1138,17 @@ nsSHistory::GloballyEvictContentViewers(
 
       nsISHTransaction *temp = trans;
       temp->GetNext(getter_AddRefs(trans));
     }
 
     // We've found all the transactions belonging to shist which have viewers.
     // Add those transactions to our global list and move on.
     transactions.AppendElements(shTransactions);
-    shist = static_cast<nsSHistory*>(PR_NEXT_LINK(shist));
+    listEntry = PR_NEXT_LINK(shist);
   }
 
   // We now have collected all cached content viewers.  First check that we
   // have enough that we actually need to evict some.
   if ((int32_t)transactions.Length() <= sHistoryMaxTotalViewers) {
     return;
   }
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2546,16 +2546,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       if (!outerObject) {
         NS_ERROR("out of memory");
         return NS_ERROR_FAILURE;
       }
 
       JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
 
       js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr));
+      js::SetProxyExtra(outerObject, 0, js::PrivateValue(nullptr));
 
       outerObject = xpc::TransplantObject(cx, obj, outerObject);
       if (!outerObject) {
         NS_ERROR("unable to transplant wrappers, probably OOM");
         return NS_ERROR_FAILURE;
       }
 
       js::SetProxyExtra(outerObject, 0, js::PrivateValue(ToSupports(this)));
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -169,16 +169,34 @@ UnwrapDOMObject(JSObject* obj)
 {
   MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
              "Don't pass non-DOM objects to this function");
 
   JS::Value val = js::GetReservedOrProxyPrivateSlot(obj, DOM_OBJECT_SLOT);
   return static_cast<T*>(val.toPrivate());
 }
 
+template <class T>
+inline T*
+UnwrapPossiblyNotInitializedDOMObject(JSObject* obj)
+{
+  // This is used by the OjectMoved JSClass hook which can be called before
+  // JS_NewObject has returned and so before we have a chance to set
+  // DOM_OBJECT_SLOT to anything useful.
+
+  MOZ_ASSERT(IsDOMClass(js::GetObjectClass(obj)),
+             "Don't pass non-DOM objects to this function");
+
+  JS::Value val = js::GetReservedOrProxyPrivateSlot(obj, DOM_OBJECT_SLOT);
+  if (val.isUndefined()) {
+    return nullptr;
+  }
+  return static_cast<T*>(val.toPrivate());
+}
+
 inline const DOMJSClass*
 GetDOMClass(const js::Class* clasp)
 {
   return IsDOMClass(clasp) ? DOMJSClass::FromJSClass(clasp) : nullptr;
 }
 
 inline const DOMJSClass*
 GetDOMClass(JSObject* obj)
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -1523,17 +1523,17 @@ class CGAbstractClassHook(CGAbstractStat
     Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw
     'this' unwrapping as it assumes that the unwrapped type is always known.
     """
     def __init__(self, descriptor, name, returnType, args):
         CGAbstractStaticMethod.__init__(self, descriptor, name, returnType,
                                         args)
 
     def definition_body_prologue(self):
-        return ("%s* self = UnwrapDOMObject<%s>(obj);\n" %
+        return ("%s* self = UnwrapPossiblyNotInitializedDOMObject<%s>(obj);\n" %
                 (self.descriptor.nativeType, self.descriptor.nativeType))
 
     def definition_body(self):
         return self.definition_body_prologue() + self.generate_code()
 
     def generate_code(self):
         assert False  # Override me!
 
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -664,16 +664,22 @@ CreateOffscreen(GLContext* gl,
     baseCaps.depth = options.depth;
     baseCaps.premultAlpha = options.premultipliedAlpha;
     baseCaps.preserve = options.preserveDrawingBuffer;
     baseCaps.stencil = options.stencil;
 
     if (!baseCaps.alpha)
         baseCaps.premultAlpha = true;
 
+    if (gl->IsANGLE()) {
+        // We can't use no-alpha formats on ANGLE yet because of:
+        // https://code.google.com/p/angleproject/issues/detail?id=764
+        baseCaps.alpha = true;
+    }
+
     // we should really have this behind a
     // |gfxPlatform::GetPlatform()->GetScreenDepth() == 16| check, but
     // for now it's just behind a pref for testing/evaluation.
     baseCaps.bpp16 = Preferences::GetBool("webgl.prefer-16bpp", false);
 
 #ifdef MOZ_WIDGET_GONK
     baseCaps.surfaceAllocator = surfAllocator;
 #endif
@@ -898,54 +904,56 @@ WebGLContext::SetDimensions(int32_t sWid
 #endif
 
     mResetLayer = true;
     mOptionsFrozen = true;
 
     // increment the generation number
     ++mGeneration;
 
+    // Update our internal stuff:
+    if (gl->WorkAroundDriverBugs()) {
+        if (!mOptions.alpha && gl->Caps().alpha) {
+            mNeedsFakeNoAlpha = true;
+        }
+    }
+
+    // Update mOptions.
+    mOptions.depth = gl->Caps().depth;
+    mOptions.stencil = gl->Caps().stencil;
+    mOptions.antialias = gl->Caps().antialias;
+
     MakeContextCurrent();
 
     gl->fViewport(0, 0, mWidth, mHeight);
     mViewportWidth = mWidth;
     mViewportHeight = mHeight;
 
-    // Update mOptions.
-    mOptions.depth = gl->Caps().depth;
-    mOptions.stencil = gl->Caps().stencil;
-    mOptions.antialias = gl->Caps().antialias;
-
     // Make sure that we clear this out, otherwise
     // we'll end up displaying random memory
     gl->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
 
     AssertCachedBindings();
     AssertCachedState();
 
     // Clear immediately, because we need to present the cleared initial
     // buffer.
     mBackbufferNeedsClear = true;
     ClearBackbufferIfNeeded();
 
     mShouldPresent = true;
 
     MOZ_ASSERT(gl->Caps().color);
-    MOZ_ASSERT(gl->Caps().alpha == mOptions.alpha);
+    MOZ_ASSERT_IF(!mNeedsFakeNoAlpha, gl->Caps().alpha == mOptions.alpha);
+    MOZ_ASSERT_IF(mNeedsFakeNoAlpha, !mOptions.alpha && gl->Caps().alpha);
     MOZ_ASSERT(gl->Caps().depth == mOptions.depth);
     MOZ_ASSERT(gl->Caps().stencil == mOptions.stencil);
     MOZ_ASSERT(gl->Caps().antialias == mOptions.antialias);
     MOZ_ASSERT(gl->Caps().preserve == mOptions.preserveDrawingBuffer);
 
-    if (gl->WorkAroundDriverBugs() && gl->IsANGLE()) {
-        if (!mOptions.alpha) {
-            mNeedsFakeNoAlpha = true;
-        }
-    }
-
     AssertCachedBindings();
     AssertCachedState();
 
     reporter.SetSuccessful();
     return NS_OK;
 }
 
 void
@@ -1247,22 +1255,20 @@ void
 WebGLContext::GetContextAttributes(Nullable<dom::WebGLContextAttributes> &retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
     dom::WebGLContextAttributes& result = retval.SetValue();
 
-    const PixelBufferFormat& format = gl->GetPixelFormat();
-
-    result.mAlpha.Construct(format.alpha > 0);
-    result.mDepth = format.depth > 0;
-    result.mStencil = format.stencil > 0;
-    result.mAntialias = format.samples > 1;
+    result.mAlpha.Construct(mOptions.alpha);
+    result.mDepth = mOptions.depth;
+    result.mStencil = mOptions.stencil;
+    result.mAntialias = mOptions.antialias;
     result.mPremultipliedAlpha = mOptions.premultipliedAlpha;
     result.mPreserveDrawingBuffer = mOptions.preserveDrawingBuffer;
 }
 
 /* [noscript] DOMString mozGetUnderlyingParamString(in GLenum pname); */
 NS_IMETHODIMP
 WebGLContext::MozGetUnderlyingParamString(uint32_t pname, nsAString& retval)
 {
@@ -1840,17 +1846,16 @@ bool WebGLContext::TexImageFromVideoElem
     }
     srcImage = nullptr;
     container->UnlockCurrentImage();
     return ok;
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
-
 WebGLContext::ScopedMaskWorkaround::ScopedMaskWorkaround(WebGLContext& webgl)
     : mWebGL(webgl)
     , mNeedsChange(NeedsChange(webgl))
 {
     if (mNeedsChange) {
         mWebGL.gl->fColorMask(mWebGL.mColorWriteMask[0],
                               mWebGL.mColorWriteMask[1],
                               mWebGL.mColorWriteMask[2],
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -2099,16 +2099,68 @@ WebGLContext::PixelStorei(GLenum pname, 
             MakeContextCurrent();
             gl->fPixelStorei(pname, param);
             break;
         default:
             return ErrorInvalidEnumInfo("pixelStorei: parameter", pname);
     }
 }
 
+// `width` in pixels.
+// `stride` in bytes.
+static bool
+SetFullAlpha(void* data, GLenum format, GLenum type, size_t width,
+             size_t height, size_t stride)
+{
+    if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) {
+        // Just memset the rows.
+        for (size_t j = 0; j < height; ++j) {
+            uint8_t* row = static_cast<uint8_t*>(data) + j*stride;
+            memset(row, 0xff, width);
+            row += stride;
+        }
+
+        return true;
+    }
+
+    if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) {
+        for (size_t j = 0; j < height; ++j) {
+            uint8_t* row = static_cast<uint8_t*>(data) + j*stride;
+
+            uint8_t* pAlpha = row + 3;
+            uint8_t* pAlphaEnd = pAlpha + 4*width;
+            while (pAlpha != pAlphaEnd) {
+                *pAlpha = 0xff;
+                pAlpha += 4;
+            }
+        }
+
+        return true;
+    }
+
+    if (format == LOCAL_GL_RGBA && type == LOCAL_GL_FLOAT) {
+        for (size_t j = 0; j < height; ++j) {
+            uint8_t* rowBytes = static_cast<uint8_t*>(data) + j*stride;
+            float* row = reinterpret_cast<float*>(rowBytes);
+
+            float* pAlpha = row + 3;
+            float* pAlphaEnd = pAlpha + 4*width;
+            while (pAlpha != pAlphaEnd) {
+                *pAlpha = 1.0f;
+                pAlpha += 4;
+            }
+        }
+
+        return true;
+    }
+
+    MOZ_ASSERT(false, "Unhandled case, how'd we get here?");
+    return false;
+}
+
 void
 WebGLContext::ReadPixels(GLint x, GLint y, GLsizei width,
                          GLsizei height, GLenum format,
                          GLenum type, const Nullable<ArrayBufferView> &pixels,
                          ErrorResult& rv)
 {
     if (IsContextLost())
         return;
@@ -2342,71 +2394,35 @@ WebGLContext::ReadPixels(GLint x, GLint 
                      + bytesPerPixel * subrect_x_in_dest_buffer, // destination
                    subrect_data.get() + subrect_alignedRowSize * y_inside_subrect, // source
                    subrect_plainRowSize); // size
         }
     }
 
     // if we're reading alpha, we may need to do fixup.  Note that we don't allow
     // GL_ALPHA to readpixels currently, but we had the code written for it already.
-    if (format == LOCAL_GL_ALPHA ||
-        format == LOCAL_GL_RGBA)
-    {
-        bool needAlphaFixup;
-        if (mBoundFramebuffer) {
-            needAlphaFixup = !mBoundFramebuffer->ColorAttachment(0).HasAlpha();
-        } else {
-            needAlphaFixup = gl->GetPixelFormat().alpha == 0;
-        }
-
-        if (needAlphaFixup) {
-            if (format == LOCAL_GL_ALPHA && type == LOCAL_GL_UNSIGNED_BYTE) {
-                // this is easy; it's an 0xff memset per row
-                uint8_t *row = static_cast<uint8_t*>(data);
-                for (GLint j = 0; j < height; ++j) {
-                    memset(row, 0xff, checked_plainRowSize.value());
-                    row += checked_alignedRowSize.value();
-                }
-            } else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_UNSIGNED_BYTE) {
-                // this is harder, we need to just set the alpha byte here
-                uint8_t *row = static_cast<uint8_t*>(data);
-                for (GLint j = 0; j < height; ++j) {
-                    uint8_t *rowp = row;
-#if MOZ_LITTLE_ENDIAN
-                    // offset to get the alpha byte; we're always going to
-                    // move by 4 bytes
-                    rowp += 3;
-#endif
-                    uint8_t *endrowp = rowp + 4 * width;
-                    while (rowp != endrowp) {
-                        *rowp = 0xff;
-                        rowp += 4;
-                    }
-
-                    row += checked_alignedRowSize.value();
-                }
-            } else if (format == LOCAL_GL_RGBA && type == LOCAL_GL_FLOAT) {
-                float* row = static_cast<float*>(data);
-
-                for (GLint j = 0; j < height; ++j) {
-                    float* pAlpha = row + 3;
-                    float* pAlphaEnd = pAlpha + 4*width;
-
-                    while (pAlpha != pAlphaEnd) {
-                        *pAlpha = 1.0f;
-                        pAlpha += 4;
-                    }
-
-                    row += checked_alignedRowSize.value();
-                }
-            } else {
-                NS_WARNING("Unhandled case, how'd we get here?");
-                return rv.Throw(NS_ERROR_FAILURE);
-            }
-        }
+
+    const bool formatHasAlpha = format == LOCAL_GL_ALPHA ||
+                                format == LOCAL_GL_RGBA;
+    if (!formatHasAlpha)
+        return;
+
+    bool needAlphaFilled;
+    if (mBoundFramebuffer) {
+        needAlphaFilled = !mBoundFramebuffer->ColorAttachment(0).HasAlpha();
+    } else {
+        needAlphaFilled = !mOptions.alpha;
+    }
+
+    if (!needAlphaFilled)
+        return;
+
+    size_t stride = checked_alignedRowSize.value(); // In bytes!
+    if (!SetFullAlpha(data, format, type, width, height, stride)) {
+        return rv.Throw(NS_ERROR_FAILURE);
     }
 }
 
 void
 WebGLContext::RenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
 {
     if (IsContextLost())
         return;
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -307,22 +307,28 @@ WebGLContext::GetParameter(JSContext* cx
         case LOCAL_GL_SAMPLES:
         case LOCAL_GL_MAX_VERTEX_ATTRIBS:
         case LOCAL_GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
         case LOCAL_GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS:
         case LOCAL_GL_MAX_TEXTURE_IMAGE_UNITS:
         case LOCAL_GL_RED_BITS:
         case LOCAL_GL_GREEN_BITS:
         case LOCAL_GL_BLUE_BITS:
-        case LOCAL_GL_ALPHA_BITS:
         case LOCAL_GL_DEPTH_BITS: {
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             return JS::Int32Value(i);
         }
+        case LOCAL_GL_ALPHA_BITS: {
+            GLint i = 0;
+            if (!mNeedsFakeNoAlpha) {
+                gl->fGetIntegerv(pname, &i);
+            }
+            return JS::Int32Value(i);
+        }
         case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT: {
             if (IsExtensionEnabled(WebGLExtensionID::OES_standard_derivatives)) {
                 GLint i = 0;
                 gl->fGetIntegerv(pname, &i);
                 return JS::Int32Value(i);
             } else {
                 break;
             }
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -1095,17 +1095,17 @@ WebGLContext::ValidateTexInputData(GLenu
 bool
 WebGLContext::ValidateCopyTexImage(GLenum format,
                                    WebGLTexImageFunc func,
                                    WebGLTexDimensions dims)
 {
     MOZ_ASSERT(IsCopyFunc(func));
 
     // Default framebuffer format
-    GLenum fboFormat = bool(gl->GetPixelFormat().alpha > 0) ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
+    GLenum fboFormat = mOptions.alpha ? LOCAL_GL_RGBA : LOCAL_GL_RGB;
 
     if (mBoundFramebuffer) {
         if (!mBoundFramebuffer->CheckAndInitializeAttachments()) {
             ErrorInvalidFramebufferOperation("%s: incomplete framebuffer", InfoFrom(func, dims));
             return false;
         }
 
         GLenum readPlaneBits = LOCAL_GL_COLOR_BUFFER_BIT;
--- a/dom/canvas/test/webgl-conformance/mochi-single.html
+++ b/dom/canvas/test/webgl-conformance/mochi-single.html
@@ -49,18 +49,19 @@ if (!window.ok) {
 }
 if (!window.todo) {
   window.todo = function(status, message) {
     console.log('todo(' + status + ', \'' + message + '\')');
   }
 }
 if (!window.SimpleTest) {
   window.SimpleTest = {
+    finish: function(){},
+    requestLongerTimeout: function(){},
     waitForExplicitFinish: function(){},
-    finish: function(){},
   };
 }
 
 ////////////////////////////////////////////////////////////////////////
 // Implement our own version of `fail-if` expected failure handling.
 // `fail-if` in mochitest.ini doesn't work yet. (bug 987849)
 
 var OS_VERSION_WIN7 = 6.1;
@@ -238,16 +239,23 @@ function OnTestComplete() {
   SimpleTest.finish();
 }
 
 ////////////////////////////////////////////////////////////////////////
 // Begin execution
 
 SimpleTest.waitForExplicitFinish();
 
+var isAndroid2_3 = (DriverInfo.getOS() == DriverInfo.OS.ANDROID &&
+                    DriverInfo.getOSVersion() < OS_VERSION_ANDROID_ICS);
+if (isAndroid2_3) {
+  var timeoutLengthMultiplier = 2.0;
+  SimpleTest.requestLongerTimeout(timeoutLengthMultiplier);
+}
+
 do {
   var arg = location.search.substr(1);
   if (arg == 'dump') {
     statusElem.innerHTML = 'Dumping';
 
     ok(true, 'OS:' + DriverInfo.getOS());
     ok(true, 'OS version:' + DriverInfo.getOSVersion());
     ok(true, 'Driver:' + DriverInfo.getDriver());
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1787,17 +1787,21 @@ NS_IMETHODIMP HTMLMediaElement::SetMuted
 
 already_AddRefed<DOMMediaStream>
 HTMLMediaElement::CaptureStreamInternal(bool aFinishWhenEnded)
 {
   nsIDOMWindow* window = OwnerDoc()->GetInnerWindow();
   if (!window) {
     return nullptr;
   }
-
+#ifdef MOZ_EME
+  if (ContainsRestrictedContent()) {
+    return nullptr;
+  }
+#endif
   OutputMediaStream* out = mOutputStreams.AppendElement();
 #ifdef DEBUG
   // Estimate hints based on the type of the media element
   // under the preference media.capturestream_hints for the
   // debug builds only. This allows WebRTC Peer Connection
   // to behave appropriately when media streams generated
   // via mozCaptureStream*() are added to the Peer Connection.
   // This functionality is planned to be used as part of Audio
@@ -3992,20 +3996,31 @@ NS_IMETHODIMP HTMLMediaElement::CanPlayC
 
 #ifdef MOZ_EME
 MediaKeys*
 HTMLMediaElement::GetMediaKeys() const
 {
   return mMediaKeys;
 }
 
+bool
+HTMLMediaElement::ContainsRestrictedContent()
+{
+  return GetMediaKeys() != nullptr;
+}
+
 already_AddRefed<Promise>
 HTMLMediaElement::SetMediaKeys(mozilla::dom::MediaKeys* aMediaKeys,
                                ErrorResult& aRv)
 {
+  if (MozAudioCaptured()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+
   nsCOMPtr<nsIGlobalObject> global =
     do_QueryInterface(OwnerDoc()->GetInnerWindow());
   if (!global) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
     return nullptr;
   }
   nsRefPtr<Promise> promise = Promise::Create(global, aRv);
   if (aRv.Failed()) {
@@ -4030,16 +4045,18 @@ HTMLMediaElement::SetMediaKeys(mozilla::
     if (NS_FAILED(mMediaKeys->Bind(this))) {
       promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
       mMediaKeys = nullptr;
       return promise.forget();
     }
     if (mDecoder) {
       mDecoder->SetCDMProxy(mMediaKeys->GetCDMProxy());
     }
+    // Update the same-origin status.
+    NotifyDecoderPrincipalChanged();
   }
   promise->MaybeResolve(JS::UndefinedHandleValue);
   return promise.forget();
 }
 
 MediaWaitingFor
 HTMLMediaElement::WaitingFor() const
 {
--- a/dom/html/HTMLMediaElement.h
+++ b/dom/html/HTMLMediaElement.h
@@ -545,16 +545,17 @@ public:
 
 
   bool IsEventAttributeName(nsIAtom* aName) MOZ_OVERRIDE;
 
   // Returns the principal of the "top level" document; the origin displayed
   // in the URL bar of the browser window.
   already_AddRefed<nsIPrincipal> GetTopLevelPrincipal();
 
+  bool ContainsRestrictedContent();
 #endif // MOZ_EME
 
   bool MozAutoplayEnabled() const
   {
     return mAutoplayEnabled;
   }
 
   already_AddRefed<DOMMediaStream> MozCaptureStream(ErrorResult& aRv);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1406,16 +1406,23 @@ ContentParent::ShutDownProcess(bool aClo
     // with aCloseWithError = true.  It's important that we call
     // CloseWithError() in this case; see bug 895204.
 
     if (!aCloseWithError && !mCalledClose) {
         // Close() can only be called once: It kicks off the destruction
         // sequence.
         mCalledClose = true;
         Close();
+#ifdef MOZ_NUWA_PROCESS
+        // Kill Nuwa process forcibly to break its IPC channels and finalize
+        // corresponding parents.
+        if (IsNuwaProcess()) {
+            KillHard();
+        }
+#endif
     }
 
     if (aCloseWithError && !mCalledCloseWithError) {
         MessageChannel* channel = GetIPCChannel();
         if (channel) {
             mCalledCloseWithError = true;
             channel->CloseWithError();
         }
@@ -2926,27 +2933,23 @@ ContentParent::KillHard()
     mForceKillTask = nullptr;
     // This ensures the process is eventually killed, but doesn't
     // immediately KILLITWITHFIRE because we want to get a minidump if
     // possible.  After a timeout though, the process is forceably
     // killed.
     if (!KillProcess(OtherProcess(), 1, false)) {
         NS_WARNING("failed to kill subprocess!");
     }
-    mSubprocess->SetAlreadyDead();
+    if (mSubprocess) {
+        mSubprocess->SetAlreadyDead();
+    }
     XRE_GetIOMessageLoop()->PostTask(
         FROM_HERE,
         NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated,
                             OtherProcess(), /*force=*/true));
-    //We do clean-up here
-    MessageLoop::current()->PostDelayedTask(
-        FROM_HERE,
-        NewRunnableMethod(this, &ContentParent::ShutDownProcess,
-                          /* closeWithError */ true),
-        3000);
     // We've now closed the OtherProcess() handle, so must set it to null to
     // prevent our dtor closing it twice.
     SetOtherProcess(0);
 }
 
 bool
 ContentParent::IsPreallocated()
 {
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -160,17 +160,17 @@ static_assert(QUICK_BUFFERING_LOW_DATA_U
 
 // The amount of instability we tollerate in calls to
 // MediaDecoderStateMachine::UpdateEstimatedDuration(); changes of duration
 // less than this are ignored, as they're assumed to be the result of
 // instability in the duration estimation.
 static const int64_t ESTIMATED_DURATION_FUZZ_FACTOR_USECS = USECS_PER_S / 2;
 
 static TimeDuration UsecsToDuration(int64_t aUsecs) {
-  return TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
+  return TimeDuration::FromMicroseconds(aUsecs);
 }
 
 static int64_t DurationToUsecs(TimeDuration aDuration) {
   return static_cast<int64_t>(aDuration.ToSeconds() * USECS_PER_S);
 }
 
 MediaDecoderStateMachine::MediaDecoderStateMachine(MediaDecoder* aDecoder,
                                                    MediaDecoderReader* aReader,
@@ -2564,16 +2564,17 @@ void MediaDecoderStateMachine::RenderVid
   }
 
   VERBOSE_LOG("playing video frame %lld", aData->mTime);
 
   VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
   if (container) {
     container->SetCurrentFrame(ThebesIntSize(aData->mDisplay), aData->mImage,
                                aTarget);
+    MOZ_ASSERT(container->GetFrameDelay() >= 0 || mScheduler->IsRealTime());
   }
 }
 
 int64_t
 MediaDecoderStateMachine::GetAudioClock()
 {
   // We must hold the decoder monitor while using the audio stream off the
   // audio sink to ensure that it doesn't get destroyed on the audio sink
@@ -2650,16 +2651,17 @@ void MediaDecoderStateMachine::AdvanceFr
 
   // If playbackRate is 0.0, we should stop the progress, but not be in paused
   // state, per spec.
   if (mPlaybackRate == 0.0) {
     return;
   }
 
   int64_t clock_time = GetClock();
+  TimeStamp nowTime = TimeStamp::Now();
   // 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.
   int64_t remainingTime = AUDIO_DURATION_USECS;
   NS_ASSERTION(clock_time >= mStartTime, "Should have positive clock time.");
   nsAutoPtr<VideoData> currentFrame;
   if (VideoQueue().GetSize() > 0) {
     VideoData* frame = VideoQueue().PeekFront();
 #ifdef PR_LOGGING
@@ -2716,18 +2718,18 @@ void MediaDecoderStateMachine::AdvanceFr
   // We've got enough data to keep playing until at least the next frame.
   // Start playing now if need be.
   if (!IsPlaying() && ((mFragmentEndTime >= 0 && clock_time < mFragmentEndTime) || mFragmentEndTime < 0)) {
     StartPlayback();
   }
 
   if (currentFrame) {
     // Decode one frame and display it.
-    TimeStamp presTime = mPlayStartTime - UsecsToDuration(mPlayDuration) +
-                          UsecsToDuration(currentFrame->mTime - mStartTime);
+    int64_t delta = currentFrame->mTime - clock_time;
+    TimeStamp presTime = nowTime + TimeDuration::FromMicroseconds(delta / mPlaybackRate);
     NS_ASSERTION(currentFrame->mTime >= mStartTime, "Should have positive frame time");
     // Filter out invalid frames by checking the frame time. FrameTime could be
     // zero if it's a initial frame.
     int64_t frameTime = currentFrame->mTime - mStartTime;
     if (frameTime > 0  || (frameTime == 0 && mPlayDuration == 0) ||
         mScheduler->IsRealTime()) {
       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
       // If we have video, we want to increment the clock in steps of the frame
@@ -2763,17 +2765,17 @@ void MediaDecoderStateMachine::AdvanceFr
   }
 
   // If the number of audio/video frames queued has changed, either by
   // this function popping and playing a video frame, or by the audio
   // thread popping and playing an audio frame, we may need to update our
   // ready state. Post an update to do so.
   UpdateReadyState();
 
-  ScheduleStateMachine(remainingTime);
+  ScheduleStateMachine(remainingTime / mPlaybackRate);
 }
 
 nsresult
 MediaDecoderStateMachine::DropVideoUpToSeekTarget(VideoData* aSample)
 {
   nsAutoPtr<VideoData> video(aSample);
   MOZ_ASSERT(video);
   DECODER_LOG("DropVideoUpToSeekTarget() frame [%lld, %lld] dup=%d",
--- a/dom/media/fmp4/apple/AppleATDecoder.cpp
+++ b/dom/media/fmp4/apple/AppleATDecoder.cpp
@@ -33,17 +33,17 @@ AppleATDecoder::AppleATDecoder(const mp4
   , mConverter(nullptr)
   , mStream(nullptr)
   , mCurrentAudioTimestamp(0)
   , mSamplePosition(0)
   , mHaveOutput(false)
   , mFlushed(false)
 {
   MOZ_COUNT_CTOR(AppleATDecoder);
-  LOG("Creating Apple AudioToolbox Audio decoder");
+  LOG("Creating Apple AudioToolbox decoder");
   LOG("Audio Decoder configuration: %s %d Hz %d channels %d bits per channel",
       mConfig.mime_type,
       mConfig.samples_per_second,
       mConfig.channel_count,
       mConfig.bits_per_sample);
 
   if (!strcmp(aConfig.mime_type, "audio/mpeg")) {
     mFileType = kAudioFileMP3Type;
@@ -87,17 +87,17 @@ static void
 
 nsresult
 AppleATDecoder::Init()
 {
   if (!mFileType) {
     NS_ERROR("Non recognised format");
     return NS_ERROR_FAILURE;
   }
-  LOG("Initializing Apple AudioToolbox Audio decoder");
+  LOG("Initializing Apple AudioToolbox decoder");
   OSStatus rv = AudioFileStreamOpen(this,
                                     _MetadataCallback,
                                     _SampleCallback,
                                     mFileType,
                                     &mStream);
   if (rv) {
     NS_ERROR("Couldn't open AudioFileStream");
     return NS_ERROR_FAILURE;
@@ -310,20 +310,20 @@ AppleATDecoder::SampleCallback(uint32_t 
       break;
     }
   } while (true);
 }
 
 void
 AppleATDecoder::SetupDecoder()
 {
-  AudioStreamBasicDescription inputFormat;
-
+  LOG("Setting up Apple AudioToolbox decoder.");
   mHaveOutput = false;
 
+  AudioStreamBasicDescription inputFormat;
   nsresult rv = AppleUtils::GetRichestDecodableFormat(mStream, inputFormat);
   if (NS_FAILED(rv)) {
     mCallback->Error();
     return;
   }
 
   // Fill in the output format manually.
   PodZero(&mOutputFormat);
--- a/dom/media/gmp/GMPService.cpp
+++ b/dom/media/gmp/GMPService.cpp
@@ -1196,16 +1196,19 @@ GeckoMediaPluginService::ClearStorage()
   }
 
   nsCOMPtr<nsIFile> path; // $profileDir/gmp/
   nsresult rv = GetStorageDir(getter_AddRefs(path));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
-  if (NS_FAILED(path->Remove(true))) {
+  bool exists = false;
+  if (NS_SUCCEEDED(path->Exists(&exists)) &&
+      exists &&
+      NS_FAILED(path->Remove(true))) {
     NS_WARNING("Failed to delete GMP storage directory");
   }
   NS_DispatchToMainThread(new StorageClearedTask(), NS_DISPATCH_NORMAL);
 }
 
 } // namespace gmp
 } // namespace mozilla
--- a/dom/media/mediasource/SourceBufferDecoder.h
+++ b/dom/media/mediasource/SourceBufferDecoder.h
@@ -79,16 +79,28 @@ public:
   }
 
   void SetTaskQueue(MediaTaskQueue* aTaskQueue)
   {
     MOZ_ASSERT((!mTaskQueue && aTaskQueue) || (mTaskQueue && !aTaskQueue));
     mTaskQueue = aTaskQueue;
   }
 
+  void BreakCycles()
+  {
+    if (mReader) {
+      mReader->BreakCycles();
+      mReader = nullptr;
+    }
+    mTaskQueue = nullptr;
+#ifdef MOZ_EME
+    mCDMProxy = nullptr;
+#endif
+  }
+
 #ifdef MOZ_EME
   virtual nsresult SetCDMProxy(CDMProxy* aProxy)
   {
     MOZ_ASSERT(NS_IsMainThread());
     ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
     mCDMProxy = aProxy;
     return NS_OK;
   }
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -386,17 +386,17 @@ TrackBuffer::ContainsTime(int64_t aTime)
 }
 
 void
 TrackBuffer::BreakCycles()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   for (uint32_t i = 0; i < mDecoders.Length(); ++i) {
-    mDecoders[i]->GetReader()->BreakCycles();
+    mDecoders[i]->BreakCycles();
   }
   mDecoders.Clear();
 
   // These are cleared in Shutdown()
   MOZ_ASSERT(mInitializedDecoders.IsEmpty());
   MOZ_ASSERT(!mParentDecoder);
 }
 
new file mode 100644
--- /dev/null
+++ b/dom/media/test/eme.js
@@ -0,0 +1,178 @@
+const KEYSYSTEM_TYPE = "org.w3.clearkey";
+
+function bail(message)
+{
+  return function(err) {
+    ok(false, message);
+    if (err) {
+      info(err);
+    }
+    SimpleTest.finish();
+  }
+}
+
+function ArrayBufferToString(arr)
+{
+  var str = '';
+  var view = new Uint8Array(arr);
+  for (var i = 0; i < view.length; i++) {
+    str += String.fromCharCode(view[i]);
+  }
+  return str;
+}
+
+function StringToArrayBuffer(str)
+{
+  var arr = new ArrayBuffer(str.length);
+  var view = new Uint8Array(arr);
+  for (var i = 0; i < str.length; i++) {
+    view[i] = str.charCodeAt(i);
+  }
+  return arr;
+}
+
+function Base64ToHex(str)
+{
+  var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
+  var res = "";
+  for (var i = 0; i < bin.length; i++) {
+    res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+  }
+  return res;
+}
+
+function HexToBase64(hex)
+{
+  var bin = "";
+  for (var i = 0; i < hex.length; i += 2) {
+    bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+  }
+  return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
+}
+
+function UpdateSessionFunc(test) {
+  return function(ev) {
+    var msgStr = ArrayBufferToString(ev.message);
+    var msg = JSON.parse(msgStr);
+
+    info("got message from CDM: " + msgStr);
+    is(msg.type, test.sessionType, "Key session type should match");
+    ok(msg.kids, "message event should contain key ID array");
+
+    var outKeys = [];
+
+    for (var i = 0; i < msg.kids.length; i++) {
+      var id64 = msg.kids[i];
+      var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
+      var key = test.keys[idHex];
+
+      if (key) {
+        info("found key " + key + " for key id " + idHex);
+        outKeys.push({
+          "kty":"oct",
+          "alg":"A128KW",
+          "kid":id64,
+          "k":HexToBase64(key)
+        });
+      } else {
+        bail("Couldn't find key for key id " + idHex);
+      }
+    }
+
+    var update = JSON.stringify({
+      "keys" : outKeys,
+      "type" : msg.type
+    });
+    info("sending update message to CDM: " + update);
+
+    ev.target.update(StringToArrayBuffer(update)).then(function() {
+      info("MediaKeySession update ok!");
+    }, bail("MediaKeySession update failed"));
+  }
+}
+
+function PlayFragmented(test, elem)
+{
+  return new Promise(function(resolve, reject) {
+    var ms = new MediaSource();
+    elem.src = URL.createObjectURL(ms);
+
+    var sb;
+    var curFragment = 0;
+
+    function addNextFragment() {
+      if (curFragment >= test.fragments.length) {
+        ms.endOfStream();
+        resolve();
+        return;
+      }
+
+      var fragmentFile = test.fragments[curFragment++];
+
+      var req = new XMLHttpRequest();
+      req.open("GET", fragmentFile);
+      req.responseType = "arraybuffer";
+
+      req.addEventListener("load", function() {
+        sb.appendBuffer(new Uint8Array(req.response));
+      });
+
+      info("fetching resource " + fragmentFile);
+      req.send(null);
+    }
+
+    ms.addEventListener("sourceopen", function () {
+      sb = ms.addSourceBuffer(test.type);
+      sb.addEventListener("updateend", addNextFragment);
+
+      addNextFragment();
+    })
+  });
+}
+
+// Returns a promise that is resovled when the media element is ready to have
+// its play() function called; when it's loaded MSE fragments, or once the load
+// has started for non-MSE video.
+function LoadTest(test, elem)
+{
+  if (test.fragments) {
+    return PlayFragmented(test, elem);
+  }
+
+  // This file isn't fragmented; set the media source normally.
+  return new Promise(function(resolve, reject) {
+    elem.src = test.name;
+    resolve();
+  });
+}
+
+function SetupEME(test, token, params)
+{
+  var v = document.createElement("video");
+
+  var onSetKeysFail = (params && params.onSetKeysFail)
+    ? params.onSetKeysFail
+    : bail(token + " Failed to set MediaKeys on <video> element");
+  
+  v.addEventListener("encrypted", function(ev) {
+    info(token + " got encrypted event");
+    MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
+      info(token + " created MediaKeys object ok");
+      mediaKeys.sessions = [];
+      return v.setMediaKeys(mediaKeys);
+    }, bail("failed to create MediaKeys object")).then(function() {
+      info(token + " set MediaKeys on <video> element ok");
+
+      var session = v.mediaKeys.createSession(test.sessionType);
+      if (params && params.onsessioncreated) {
+        params.onsessioncreated(session);
+      }
+      session.addEventListener("message", UpdateSessionFunc(test));
+      session.generateRequest(ev.initDataType, ev.initData).then(function() {
+      }, bail(token + " Failed to initialise MediaKeySession"));
+
+    }, onSetKeysFail);
+  });
+
+  return v;
+}
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -135,16 +135,17 @@ support-files =
   detodos.opus
   detodos.opus^headers^
   detodos.webm
   detodos.webm^headers^
   dirac.ogg
   dirac.ogg^headers^
   dynamic_redirect.sjs
   dynamic_resource.sjs
+  eme.js
   gizmo-frag-cenc1.m4s
   gizmo-frag-cenc2.m4s
   gizmo-frag-cencinit.mp4
   file_access_controls.html
   fragment_noplay.js
   fragment_play.js
   gizmo.mp4
   gizmo.mp4^headers^
@@ -355,17 +356,21 @@ skip-if = (toolkit == 'android' && proce
 [test_contentDuration7.html]
 [test_controls.html]
 [test_currentTime.html]
 [test_decode_error.html]
 [test_decoder_disable.html]
 [test_defaultMuted.html]
 [test_delay_load.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
-[test_encryptedMediaExtensions.html]
+[test_eme_canvas_blocked.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+[test_eme_playback.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+[test_eme_stream_capture_blocked.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_error_in_video_document.html]
 skip-if = toolkit == 'android' # bug 608634
 [test_error_on_404.html]
 [test_fastSeek.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_fastSeek-forwards.html]
 [test_imagecapture.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_eme_canvas_blocked.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Encrypted Media Extensions</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var manager = new MediaTestManager;
+
+function startTest(test, token)
+{
+  manager.started(token);
+
+  var sessions = [];
+
+  var v = SetupEME(test, token);
+  v.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+
+  v.addEventListener("canplay", function(ev) {
+    var video = ev.target;
+    var canvas = document.createElement("canvas");
+    canvas.width = video.videoWidth;
+    canvas.height = video.videoHeight;
+    document.body.appendChild(canvas);
+    var ctx = canvas.getContext("2d");
+    var threwError = false;
+    try {
+      ctx.drawImage(video, 0, 0);
+    } catch (ex) {
+      threwError = true;
+    }
+    ok(threwError, token + " - Should throw an error when trying to draw EME video to canvas.");
+    manager.finished(token);
+  });
+
+  v.addEventListener("error", bail(token + " got error event"));
+
+  LoadTest(test, v);
+}
+
+function beginTest() {
+  manager.runTests(gEMETests, startTest);
+}
+
+var prefs = [
+  [ "media.mediasource.enabled", true ],
+  [ "media.mediasource.ignore_codecs", true ],
+];
+
+if (/Linux/.test(navigator.userAgent) ||
+    !document.createElement('video').canPlayType("video/mp4")) {
+  // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
+  prefs.push([ "media.fragmented-mp4.exposed", true ]);
+  prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+</script>
+</pre>
+</body>
+</html>
rename from dom/media/test/test_encryptedMediaExtensions.html
rename to dom/media/test/test_eme_playback.html
--- a/dom/media/test/test_encryptedMediaExtensions.html
+++ b/dom/media/test/test_eme_playback.html
@@ -1,246 +1,96 @@
 <!DOCTYPE HTML>
 <html>
 <head>
   <title>Test Encrypted Media Extensions</title>
   <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
   <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 var manager = new MediaTestManager;
 
-const KEYSYSTEM_TYPE = "org.w3.clearkey";
 
-function bail(message)
-{
-  return function(err) {
-    ok(false, message);
-    if (err) {
-      info(err);
-    }
-    SimpleTest.finish();
-  }
-}
-
-function ArrayBufferToString(arr)
-{
-  var str = '';
-  var view = new Uint8Array(arr);
-  for (var i = 0; i < view.length; i++) {
-    str += String.fromCharCode(view[i]);
-  }
-  return str;
-}
-
-function StringToArrayBuffer(str)
-{
-  var arr = new ArrayBuffer(str.length);
-  var view = new Uint8Array(arr);
-  for (var i = 0; i < str.length; i++) {
-    view[i] = str.charCodeAt(i);
-  }
-  return arr;
-}
-
-function Base64ToHex(str)
-{
-  var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
-  var res = "";
-  for (var i = 0; i < bin.length; i++) {
-    res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
-  }
-  return res;
-}
-
-function HexToBase64(hex)
-{
-  var bin = "";
-  for (var i = 0; i < hex.length; i += 2) {
-    bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
-  }
-  return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
-}
-
-function UpdateSessionFunc(test) {
-  return function(ev) {
-    var msgStr = ArrayBufferToString(ev.message);
-    var msg = JSON.parse(msgStr);
-
-    info("got message from CDM: " + msgStr);
-    is(msg.type, test.sessionType, "Key session type should match");
-    ok(msg.kids, "message event should contain key ID array");
-
-    var outKeys = [];
-
-    for (var i = 0; i < msg.kids.length; i++) {
-      var id64 = msg.kids[i];
-      var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
-      var key = test.keys[idHex];
-
-      if (key) {
-        info("found key " + key + " for key id " + idHex);
-        outKeys.push({
-          "kty":"oct",
-          "alg":"A128KW",
-          "kid":id64,
-          "k":HexToBase64(key)
-        });
-      } else {
-        bail("Couldn't find key for key id " + idHex);
-      }
-    }
-
-    var update = JSON.stringify({
-      "keys" : outKeys,
-      "type" : msg.type
-    });
-    info("sending update message to CDM: " + update);
-
-    ev.target.update(StringToArrayBuffer(update)).then(function() {
-      info("MediaKeySession update ok!");
-    }, bail("MediaKeySession update failed"));
-  }
-}
-
-function PlayFragmented(test, elem)
-{
-  var ms = new MediaSource();
-  elem.src = URL.createObjectURL(ms);
-
-  var sb;
-  var curFragment = 0;
-
-  function addNextFragment() {
-    if (curFragment >= test.fragments.length) {
-      ms.endOfStream();
-      elem.play();
-      return;
-    }
-
-    var fragmentFile = test.fragments[curFragment++];
-
-    var req = new XMLHttpRequest();
-    req.open("GET", fragmentFile);
-    req.responseType = "arraybuffer";
-
-    req.addEventListener("load", function() {
-      sb.appendBuffer(new Uint8Array(req.response));
-    });
-
-    info("fetching resource " + fragmentFile);
-    req.send(null);
-  }
-
-  ms.addEventListener("sourceopen", function () {
-    sb = ms.addSourceBuffer(test.type);
-    sb.addEventListener("updateend", addNextFragment);
-
-    addNextFragment();
-  });
-}
-
-function PlayTest(test, elem)
-{
-  if (test.fragments) {
-    PlayFragmented(test, elem);
-    return;
-  }
-
-  // This file isn't fragmented; set the media source normally.
-  elem.src = test.name;
-  elem.play();
-}
-
-function KeysChangeFunc(session, keys) {
+function KeysChangeFunc(session, keys, token) {
   session.keyIdsReceived = [];
   for (var keyid in keys) {
     info("Set " + keyid + " to false in session.keyIdsReceived");
     session.keyIdsReceived[keyid] = false;
   }
   return function(ev) {
     var session = ev.target;
     session.gotKeysChanged = true;
     session.getUsableKeyIds().then(function(keyIds) {
       for (var k = 0; k < keyIds.length; k++) {
         var kid = Base64ToHex(window.btoa(ArrayBufferToString(keyIds[k])));
-        ok(kid in session.keyIdsReceived, "session.keyIdsReceived contained " + kid + " as expected.");
+        ok(kid in session.keyIdsReceived, token + " session.keyIdsReceived contained " + kid + " as expected.");
         session.keyIdsReceived[kid] = true;
       }
     }, bail("Failed to get keyIds"));
   }
 }
 
 function startTest(test, token)
 {
-  manager.started(test._token);
+  manager.started(token);
+
+  var sessions = [];
 
-  var v = document.createElement("video");
+  var v = SetupEME(test, token,
+    {
+      onsessioncreated: function(session) {
+        sessions.push(session);
+        session.addEventListener("keyschange", KeysChangeFunc(session, test.keys, token), false);
+      }
+    }
+  );
+
   var gotEncrypted = false;
   var gotPlaying = false;
 
   v.addEventListener("encrypted", function(ev) {
-    gotEncrypted = true;
-
-    info(token + " got encrypted event");
     ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
        token + " MediaKeys should support this keysystem");
-
-    MediaKeys.create(KEYSYSTEM_TYPE).then(function(mediaKeys) {
-      info(token + " created MediaKeys object ok");
-      mediaKeys.sessions = [];
-      return v.setMediaKeys(mediaKeys);
-    }, bail("failed to create MediaKeys object")).then(function() {
-      info(token + " set MediaKeys on <video> element ok");
-
-      ok(MediaKeys.isTypeSupported(KEYSYSTEM_TYPE, ev.initDataType, test.type),
-         "MediaKeys should still support keysystem after CDM created...");
-
-      var session = v.mediaKeys.createSession(test.sessionType);
-      v.mediaKeys.sessions.push(session);
-      session.addEventListener("keyschange", KeysChangeFunc(session, test.keys), false);
-      session.addEventListener("message", UpdateSessionFunc(test));
-      session.generateRequest(ev.initDataType, ev.initData).then(function() {
-      }, bail(token + " Failed to initialise MediaKeySession"));
-
-    }, bail(token + " Failed to set MediaKeys on <video> element"));
+    gotEncrypted = true;
   });
 
   v.addEventListener("playing", function () { gotPlaying = true; });
 
   v.addEventListener("ended", function(ev) {
     ok(true, token + " got ended event");
-    manager.finished(test._token);
+    manager.finished(token);
 
     ok(gotEncrypted, token + " encrypted event should have fired");
     ok(gotPlaying, token + " playing event should have fired");
 
     ok(Math.abs(test.duration - v.duration) < 0.1,
        token + " Duration of video should be corrrect");
     ok(Math.abs(test.duration - v.currentTime) < 0.1,
        token + " Current time should be same as duration");
+
     // Verify all sessions had all keys went sent the to the CDM usable, and thus
     // that we received keyschange event(s).
-    var sessions = v.mediaKeys.sessions;
     is(sessions.length, 1, "should have 1 session");
     for (var i = 0; i < sessions.length; i++) {
       var session = sessions[i];
-      ok(session.gotKeysChanged, "Should have received at least one keychange event");
+      ok(session.gotKeysChanged, token + " should have received at least one keychange event");
       for (var kid in session.keyIdsReceived) {
-        ok(session.keyIdsReceived[kid], "key with id " + kid + " was usable as expected");
+        ok(session.keyIdsReceived[kid], token + " key with id " + kid + " was usable as expected");
       }
     }
-  });
+
+   });
 
   v.addEventListener("error", bail(token + " got error event"));
 
-  PlayTest(test, v);
+  LoadTest(test, v).then(function(){v.play();}, bail(token + " failed to load"));
 }
 
 function testIsTypeSupported()
 {
   var t = MediaKeys.isTypeSupported;
   const clearkey = "org.w3.clearkey";
   ok(!t("bogus", "bogon", "video/bogus"), "Invalid type.");
   ok(t(clearkey), "ClearKey supported.");
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_eme_stream_capture_blocked.html
@@ -0,0 +1,102 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Encrypted Media Extensions</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var manager = new MediaTestManager;
+
+function startTest(test, token)
+{
+  // Three cases:
+  // 1. setting MediaKeys on an element captured by MediaElementSource should fail, and
+  // 2. creating a MediaElementSource on a media element with a MediaKeys should fail, and
+  // 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
+
+  // Case 1. setting MediaKeys on an element captured by MediaElementSource should fail.
+  var case1token = token + "_case1";
+  var setKeysFailed = function() {
+    ok(true, case1token + " setMediaKeys failed as expected.");
+    manager.finished(case1token);
+  };
+  var v1 = SetupEME(test, case1token,  { onSetKeysFail: setKeysFailed });
+  var context = new AudioContext();
+  var node = context.createMediaElementSource(v1);
+  v1.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+  v1.addEventListener("error", bail(case1token + " got error event"));
+  v1.addEventListener("canplay", function(ev) {
+    ok(false, case1token + " should never reach canplay, as setMediaKeys should fail");
+  });
+  manager.started(case1token);
+  LoadTest(test, v1);
+
+
+  // Case 2. creating a MediaElementSource on a media element with a MediaKeys should fail.
+  var case2token = token + "_case2";
+  var v2 = SetupEME(test, case2token);
+  v2.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+  v2.addEventListener("error", bail(case2token + " got error event"));
+  v2.addEventListener("canplay", function(ev) {
+    ok(true, case2token + " should reach canplay");
+    var threw = false;
+    try {
+      var context = new AudioContext();
+      var node = context.createMediaElementSource(v2);
+    } catch (e) {
+      threw = true;
+    }
+    ok(threw, "Should throw an error creating a MediaElementSource on an EME video.");
+    manager.finished(case2token);
+  });
+  manager.started(case2token);
+  LoadTest(test, v2);
+
+
+  // Case 3. capturing a media element with mozCaptureStream that has a MediaKeys should fail.
+  var case3token = token + "_case3";
+  var v3 = SetupEME(test, case3token);
+  v3.preload = "auto"; // Required due to "canplay" not firing for MSE unless we do this.
+  v3.addEventListener("error", bail(case3token + " got error event"));
+  v3.addEventListener("canplay", function(ev) {
+    ok(true, case3token + " should reach canplay");
+    var threw = false;
+    try {
+      var stream = v3.mozCaptureStreamUntilEnded();
+    } catch (e) {
+      threw = true;
+    }
+    ok(threw, "Should throw an error calling mozCaptureStreamUntilEnded an EME video.");
+    manager.finished(case3token);
+  });
+  manager.started(case3token);
+  LoadTest(test, v3);
+}
+
+function beginTest() {
+  manager.runTests(gEMETests, startTest);
+}
+
+var prefs = [
+  [ "media.mediasource.enabled", true ],
+  [ "media.mediasource.ignore_codecs", true ],
+];
+
+if (/Linux/.test(navigator.userAgent) ||
+    !document.createElement('video').canPlayType("video/mp4")) {
+  // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
+  prefs.push([ "media.fragmented-mp4.exposed", true ]);
+  prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/tests/mochitest/pc.js
+++ b/dom/media/tests/mochitest/pc.js
@@ -1298,16 +1298,18 @@ DataChannelTest.prototype = Object.creat
       function dataChannelConnected(channel) {
         // in case the switch statement below had called onSuccess already we
         // don't want to call it again
         if (!dcOpened) {
           clearTimeout(dcConnectionTimeout);
           is(channel.readyState, "open", peer + " dataChannels[0] switched to state: 'open'");
           dcOpened = true;
           onSuccess();
+        } else {
+          info("dataChannelConnected() called, but data channel was open already");
         }
       }
 
       // TODO: drno: convert dataChannels into an object and make
       //             registerDataChannelOpenEvent a generic function
       if (peer == this.pcLocal) {
         peer.dataChannels[0].onopen = dataChannelConnected;
       } else {
@@ -2601,21 +2603,22 @@ PeerConnectionWrapper.prototype = {
 
   /**
    * Register all events during the setup of the data channel
    *
    * @param {Function} onDataChannelOpened
    *        Callback to execute when the data channel has been opened
    */
   registerDataChannelOpenEvents : function (onDataChannelOpened) {
-    info(this + ": Register callbacks for 'ondatachannel' and 'onopen'");
+    info(this + ": Register callback for 'ondatachannel'");
 
     this.ondatachannel = function (targetChannel) {
+      this.dataChannels.push(targetChannel);
+      info(this + ": 'ondatachannel' fired, registering 'onopen' callback");
       targetChannel.onopen = onDataChannelOpened;
-      this.dataChannels.push(targetChannel);
     };
   },
 
   /**
    * Returns the string representation of the class
    *
    * @returns {String} The string representation
    */
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -280,16 +280,22 @@ AudioContext::CreateAnalyser()
 already_AddRefed<MediaElementAudioSourceNode>
 AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
                                        ErrorResult& aRv)
 {
   if (mIsOffline) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
+#ifdef MOZ_EME
+  if (aMediaElement.ContainsRestrictedContent()) {
+    aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return nullptr;
+  }
+#endif
   nsRefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   nsRefPtr<MediaElementAudioSourceNode> mediaElementAudioSourceNode =
     new MediaElementAudioSourceNode(this, stream);
   return mediaElementAudioSourceNode.forget();
 }
--- a/gfx/angle/src/libGLESv2/validationES2.cpp
+++ b/gfx/angle/src/libGLESv2/validationES2.cpp
@@ -586,17 +586,18 @@ bool ValidateES2CopyTexImageParameters(C
                 context->recordError(Error(GL_INVALID_OPERATION));
                 return false;
             }
             break;
           case GL_LUMINANCE_ALPHA:
           case GL_RGBA:
             if (colorbufferFormat != GL_RGBA4 &&
                 colorbufferFormat != GL_RGB5_A1 &&
-                colorbufferFormat != GL_RGBA8_OES)
+                colorbufferFormat != GL_RGBA8_OES &&
+                colorbufferFormat != GL_BGRA8_EXT)
             {
                 context->recordError(Error(GL_INVALID_OPERATION));
                 return false;
             }
             break;
           case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
           case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
           case GL_COMPRESSED_RGBA_S3TC_DXT3_ANGLE:
--- a/gfx/gl/GLConsts.h
+++ b/gfx/gl/GLConsts.h
@@ -5182,18 +5182,16 @@
 #define LOCAL_EGL_CONTEXT_PRIORITY_MEDIUM_IMG                0x3102
 #define LOCAL_EGL_CORE_NATIVE_ENGINE                         0x305B
 #define LOCAL_EGL_COVERAGE_BUFFERS_NV                        0x30E0
 #define LOCAL_EGL_COVERAGE_SAMPLES_NV                        0x30E1
 #define LOCAL_EGL_COVERAGE_SAMPLE_RESOLVE_DEFAULT_NV         0x3132
 #define LOCAL_EGL_COVERAGE_SAMPLE_RESOLVE_NONE_NV            0x3133
 #define LOCAL_EGL_COVERAGE_SAMPLE_RESOLVE_NV                 0x3131
 #define LOCAL_EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE          0x3200
-#define LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE              ((EGLNativeDisplayType)-2)
-#define LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE                   ((EGLNativeDisplayType)-3)
 #define LOCAL_EGL_DEFAULT_DISPLAY                            ((EGLNativeDisplayType)0)
 #define LOCAL_EGL_DEPTH_ENCODING_NONE_NV                     0
 #define LOCAL_EGL_DEPTH_ENCODING_NONLINEAR_NV                0x30E3
 #define LOCAL_EGL_DEPTH_ENCODING_NV                          0x30E2
 #define LOCAL_EGL_DEPTH_SIZE                                 0x3025
 #define LOCAL_EGL_DISCARD_SAMPLES_ARM                        0x3286
 #define LOCAL_EGL_DISPLAY_SCALING                            10000
 #define LOCAL_EGL_DMA_BUF_PLANE0_FD_EXT                      0x3272
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -1807,16 +1807,25 @@ GLContext::ChooseGLFormats(const Surface
             formats.color_rbFormat  = LOCAL_GL_RGBA8;
         } else {
             formats.color_texInternalFormat = IsGLES() ? LOCAL_GL_RGB : LOCAL_GL_RGB8;
             formats.color_texFormat = LOCAL_GL_RGB;
             formats.color_rbFormat  = LOCAL_GL_RGB8;
         }
     }
 
+    if (WorkAroundDriverBugs() &&
+        IsANGLE() &&
+        formats.color_rbFormat == LOCAL_GL_RGBA8)
+    {
+        formats.color_texInternalFormat = LOCAL_GL_BGRA;
+        formats.color_texFormat = LOCAL_GL_BGRA;
+        formats.color_rbFormat = LOCAL_GL_BGRA8_EXT;
+    }
+
     uint32_t msaaLevel = gfxPrefs::MSAALevel();
     GLsizei samples = msaaLevel * msaaLevel;
     samples = std::min(samples, mMaxSamples);
 
     // Bug 778765.
     if (WorkAroundDriverBugs() && samples == 1) {
         samples = 0;
     }
--- a/gfx/gl/GLDefs.h
+++ b/gfx/gl/GLDefs.h
@@ -50,9 +50,13 @@
 
 #define LOCAL_GL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
 #define LOCAL_GL_CONTEXT_LOST                           0x9242
 #define LOCAL_GL_CONTEXT_FLAGS_ARB                      0x2094
 #define LOCAL_GL_CONTEXT_CORE_PROFILE_BIT_ARB           0x00000001
 #define LOCAL_GL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB  0x00000002
 #define LOCAL_GL_CONTEXT_ROBUST_ACCESS_BIT_ARB          0x00000004
 
+
+#define LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE         ((EGLNativeDisplayType)-2)
+#define LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE              ((EGLNativeDisplayType)-3)
+
 #endif
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -94,16 +94,29 @@ LoadLibraryForEGLOnWindows(const nsAStri
         nsPrintfCString msg("Failed to load %s - Expect EGL initialization to fail",
                             NS_LossyConvertUTF16toASCII(filename).get());
         NS_WARNING(msg.get());
     }
     return lib;
 }
 #endif // XP_WIN
 
+static EGLDisplay
+GetAndInitDisplay(GLLibraryEGL& egl, void* displayType)
+{
+    EGLDisplay display = egl.fGetDisplay(displayType);
+    if (display == EGL_NO_DISPLAY)
+        return EGL_NO_DISPLAY;
+
+    if (!egl.fInitialize(display, nullptr, nullptr))
+        return EGL_NO_DISPLAY;
+
+    return display;
+}
+
 bool
 GLLibraryEGL::EnsureInitialized()
 {
     if (mInitialized) {
         return true;
     }
 
     mozilla::ScopedGfxFeatureReporter reporter("EGL");
@@ -180,16 +193,17 @@ GLLibraryEGL::EnsureInitialized()
 
 #endif // !Windows
 
 #define SYMBOL(name) \
 { (PRFuncPtr*) &mSymbols.f##name, { "egl" #name, nullptr } }
 
     GLLibraryLoader::SymLoadStruct earlySymbols[] = {
         SYMBOL(GetDisplay),
+        SYMBOL(Terminate),
         SYMBOL(GetCurrentSurface),
         SYMBOL(GetCurrentContext),
         SYMBOL(MakeCurrent),
         SYMBOL(DestroyContext),
         SYMBOL(CreateContext),
         SYMBOL(DestroySurface),
         SYMBOL(CreateWindowSurface),
         SYMBOL(CreatePbufferSurface),
@@ -227,45 +241,51 @@ GLLibraryEGL::EnsureInitialized()
 
     GLLibraryLoader::LoadSymbols(mEGLLibrary, &optionalSymbols[0]);
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18
     MOZ_RELEASE_ASSERT(mSymbols.fQueryStringImplementationANDROID,
                        "Couldn't find eglQueryStringImplementationANDROID");
 #endif
 
-    mEGLDisplay = nullptr;
+    mEGLDisplay = GetAndInitDisplay(*this, EGL_DEFAULT_DISPLAY);
 
-#ifdef XP_WIN
-    // XXX we have no way of knowing if this is ANGLE, or if we're just using
-    // a native EGL on windows.  We don't really do the latter right now, so
-    // let's assume it is ANGLE, and try our special types.
-
-    // D3D11 ANGLE only works with OMTC; there's a bug in the non-OMTC layer
-    // manager, and it's pointless to try to fix it.  We also don't try D3D11
-    // ANGLE if the layer manager is prefering D3D9 (hrm, do we care?)
-    if (gfxPrefs::LayersOffMainThreadCompositionEnabled() &&
-        !gfxPrefs::LayersPreferD3D9())
+    const char* vendor = (char*)fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
+    if (vendor && (strstr(vendor, "TransGaming") != 0 ||
+                   strstr(vendor, "Google Inc.") != 0))
     {
-        if (gfxPrefs::WebGLANGLEForceD3D11()) {
-            mEGLDisplay = fGetDisplay(LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
-        } else if (gfxPrefs::WebGLANGLETryD3D11()) {
-            mEGLDisplay = fGetDisplay(LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE);
-        }
+        mIsANGLE = true;
     }
-#endif
+
+    if (mIsANGLE) {
+        EGLDisplay newDisplay = EGL_NO_DISPLAY;
 
-    if (!mEGLDisplay)
-        mEGLDisplay = fGetDisplay(EGL_DEFAULT_DISPLAY);
-    if (!fInitialize(mEGLDisplay, nullptr, nullptr))
-        return false;
+        // D3D11 ANGLE only works with OMTC; there's a bug in the non-OMTC layer
+        // manager, and it's pointless to try to fix it.  We also don't try
+        // D3D11 ANGLE if the layer manager is prefering D3D9 (hrm, do we care?)
+        if (gfxPrefs::LayersOffMainThreadCompositionEnabled() &&
+            !gfxPrefs::LayersPreferD3D9())
+        {
+            if (gfxPrefs::WebGLANGLEForceD3D11()) {
+                newDisplay = GetAndInitDisplay(*this,
+                                               LOCAL_EGL_D3D11_ONLY_DISPLAY_ANGLE);
+            } else if (gfxPrefs::WebGLANGLETryD3D11()) {
+                newDisplay = GetAndInitDisplay(*this,
+                                               LOCAL_EGL_D3D11_ELSE_D3D9_DISPLAY_ANGLE);
+            }
+        }
 
-    const char *vendor = (const char*) fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
-    if (vendor && (strstr(vendor, "TransGaming") != 0 || strstr(vendor, "Google Inc.") != 0)) {
-        mIsANGLE = true;
+        if (newDisplay != EGL_NO_DISPLAY) {
+            DebugOnly<EGLBoolean> success = fTerminate(mEGLDisplay);
+            MOZ_ASSERT(success == LOCAL_EGL_TRUE);
+
+            mEGLDisplay = newDisplay;
+
+            vendor = (char*)fQueryString(mEGLDisplay, LOCAL_EGL_VENDOR);
+        }
     }
 
     InitExtensions();
 
     GLLibraryLoader::PlatformLookupFunction lookupFunction =
             (GLLibraryLoader::PlatformLookupFunction)mSymbols.fGetProcAddress;
 
     if (IsExtensionSupported(KHR_lock_surface)) {
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -99,17 +99,17 @@ namespace gl {
 #define BEFORE_GL_CALL
 #endif
 #define AFTER_GL_CALL
 #endif
 
 class GLLibraryEGL
 {
 public:
-    GLLibraryEGL() 
+    GLLibraryEGL()
         : mInitialized(false),
           mEGLLibrary(nullptr),
           mIsANGLE(false)
     {
     }
 
     void InitExtensions();
 
@@ -149,16 +149,24 @@ public:
     EGLDisplay fGetDisplay(void* display_id)
     {
         BEFORE_GL_CALL;
         EGLDisplay disp = mSymbols.fGetDisplay(display_id);
         AFTER_GL_CALL;
         return disp;
     }
 
+    EGLBoolean fTerminate(EGLDisplay display)
+    {
+        BEFORE_GL_CALL;
+        EGLBoolean ret = mSymbols.fTerminate(display);
+        AFTER_GL_CALL;
+        return ret;
+    }
+
     EGLSurface fGetCurrentSurface(EGLint id)
     {
         BEFORE_GL_CALL;
         EGLSurface surf = mSymbols.fGetCurrentSurface(id);
         AFTER_GL_CALL;
         return surf;
     }
 
@@ -464,16 +472,18 @@ public:
     bool EnsureInitialized();
 
     void DumpEGLConfig(EGLConfig cfg);
     void DumpEGLConfigs();
 
     struct {
         typedef EGLDisplay (GLAPIENTRY * pfnGetDisplay)(void *display_id);
         pfnGetDisplay fGetDisplay;
+        typedef EGLBoolean (GLAPIENTRY * pfnTerminate)(EGLDisplay dpy);
+        pfnTerminate fTerminate;
         typedef EGLSurface (GLAPIENTRY * pfnGetCurrentSurface)(EGLint);
         pfnGetCurrentSurface fGetCurrentSurface;
         typedef EGLContext (GLAPIENTRY * pfnGetCurrentContext)(void);
         pfnGetCurrentContext fGetCurrentContext;
         typedef EGLBoolean (GLAPIENTRY * pfnMakeCurrent)(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx);
         pfnMakeCurrent fMakeCurrent;
         typedef EGLBoolean (GLAPIENTRY * pfnDestroyContext)(EGLDisplay dpy, EGLContext ctx);
         pfnDestroyContext fDestroyContext;
--- a/gfx/gl/SharedSurfaceANGLE.cpp
+++ b/gfx/gl/SharedSurfaceANGLE.cpp
@@ -284,34 +284,26 @@ ChooseConfig(GLContext* gl, GLLibraryEGL
         return EGL_NO_CONFIG;
     }
 
     // The requests passed to ChooseConfig are treated as minimums. If you ask
     // for 0 bits of alpha, we might still get 8 bits.
     EGLConfig config = EGL_NO_CONFIG;
     for (int i = 0; i < foundConfigs; i++) {
         EGLConfig cur = configs[i];
-        if (!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_DEPTH_SIZE,
+        if (!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_ALPHA_SIZE,
+                                        caps.alpha) ||
+            !DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_DEPTH_SIZE,
                                         caps.depth) ||
             !DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_STENCIL_SIZE,
                                         caps.stencil))
         {
             continue;
         }
 
-        // We can't enforce alpha on ANGLE yet because of:
-        // https://code.google.com/p/angleproject/issues/detail?id=764
-        if (!gl->IsANGLE()) {
-            if (!DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_ALPHA_SIZE,
-                                            caps.alpha))
-            {
-                continue;
-            }
-        }
-
         config = cur;
         break;
     }
 
     if (config == EGL_NO_CONFIG) {
         NS_WARNING("No acceptable EGLConfig found.");
         return EGL_NO_CONFIG;
     }
--- a/gfx/gl/SharedSurfaceGL.cpp
+++ b/gfx/gl/SharedSurfaceGL.cpp
@@ -43,16 +43,18 @@ SharedSurface_Basic::Create(GLContext* g
     case LOCAL_GL_RGB8:
         if (formats.color_texType == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
             format = SurfaceFormat::R5G6B5;
         else
             format = SurfaceFormat::B8G8R8X8;
         break;
     case LOCAL_GL_RGBA:
     case LOCAL_GL_RGBA8:
+    case LOCAL_GL_BGRA:
+    case LOCAL_GL_BGRA8_EXT:
         format = SurfaceFormat::B8G8R8A8;
         break;
     default:
         MOZ_CRASH("Unhandled Tex format.");
     }
 
     ret.reset( new SharedSurface_Basic(gl, size, hasAlpha, format, tex) );
     return Move(ret);
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -188,10 +188,180 @@ Compositor::DrawDiagnosticsInternal(Diag
                  aTransform);
   // bottom
   this->DrawQuad(gfx::Rect(aVisibleRect.x + lWidth, aVisibleRect.y + aVisibleRect.height-lWidth,
                            aVisibleRect.width - 2 * lWidth, lWidth),
                  aClipRect, effects, opacity,
                  aTransform);
 }
 
-} // namespace
-} // namespace
+static float
+WrapTexCoord(float v)
+{
+    // fmodf gives negative results for negative numbers;
+    // that is, fmodf(0.75, 1.0) == 0.75, but
+    // fmodf(-0.75, 1.0) == -0.75.  For the negative case,
+    // the result we need is 0.25, so we add 1.0f.
+    if (v < 0.0f) {
+        return 1.0f + fmodf(v, 1.0f);
+    }
+
+    return fmodf(v, 1.0f);
+}
+
+static void
+SetRects(size_t n,
+         decomposedRectArrayT* aLayerRects,
+         decomposedRectArrayT* aTextureRects,
+         float x0, float y0, float x1, float y1,
+         float tx0, float ty0, float tx1, float ty1,
+         bool flip_y)
+{
+  if (flip_y) {
+    std::swap(ty0, ty1);
+  }
+  (*aLayerRects)[n] = gfx::Rect(x0, y0, x1 - x0, y1 - y0);
+  (*aTextureRects)[n] = gfx::Rect(tx0, ty0, tx1 - tx0, ty1 - ty0);
+}
+
+#ifdef DEBUG
+static inline bool
+FuzzyEqual(float a, float b)
+{
+	return fabs(a - b) < 0.0001f;
+}
+static inline bool
+FuzzyLTE(float a, float b)
+{
+	return a <= b + 0.0001f;
+}
+#endif
+
+size_t
+DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
+                           const gfx::Rect& aTexCoordRect,
+                           decomposedRectArrayT* aLayerRects,
+                           decomposedRectArrayT* aTextureRects)
+{
+  gfx::Rect texCoordRect = aTexCoordRect;
+
+  // If the texture should be flipped, it will have negative height. Detect that
+  // here and compensate for it. We will flip each rect as we emit it.
+  bool flipped = false;
+  if (texCoordRect.height < 0) {
+    flipped = true;
+    texCoordRect.y += texCoordRect.height;
+    texCoordRect.height = -texCoordRect.height;
+  }
+
+  // Wrap the texture coordinates so they are within [0,1] and cap width/height
+  // at 1. We rely on this below.
+  texCoordRect = gfx::Rect(gfx::Point(WrapTexCoord(texCoordRect.x),
+                                      WrapTexCoord(texCoordRect.y)),
+                           gfx::Size(std::min(texCoordRect.width, 1.0f),
+                                     std::min(texCoordRect.height, 1.0f)));
+
+  NS_ASSERTION(texCoordRect.x >= 0.0f && texCoordRect.x <= 1.0f &&
+               texCoordRect.y >= 0.0f && texCoordRect.y <= 1.0f &&
+               texCoordRect.width >= 0.0f && texCoordRect.width <= 1.0f &&
+               texCoordRect.height >= 0.0f && texCoordRect.height <= 1.0f &&
+               texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f &&
+               texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f,
+               "We just wrapped the texture coordinates, didn't we?");
+
+  // Get the top left and bottom right points of the rectangle. Note that
+  // tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2].
+  gfx::Point tl = texCoordRect.TopLeft();
+  gfx::Point br = texCoordRect.BottomRight();
+
+  NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f &&
+               tl.y >= 0.0f && tl.y <= 1.0f &&
+               br.x >= tl.x && br.x <= 2.0f &&
+               br.y >= tl.y && br.y <= 2.0f &&
+               FuzzyLTE(br.x - tl.x, 1.0f) &&
+               FuzzyLTE(br.y - tl.y, 1.0f),
+               "Somehow generated invalid texture coordinates");
+
+  // Then check if we wrap in either the x or y axis.
+  bool xwrap = br.x > 1.0f;
+  bool ywrap = br.y > 1.0f;
+
+  // If xwrap is false, the texture will be sampled from tl.x .. br.x.
+  // If xwrap is true, then it will be split into tl.x .. 1.0, and
+  // 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination
+  // rectangle is also split appropriately, according to the calculated
+  // xmid/ymid values.
+  if (!xwrap && !ywrap) {
+    SetRects(0, aLayerRects, aTextureRects,
+             aRect.x, aRect.y, aRect.XMost(), aRect.YMost(),
+             tl.x, tl.y, br.x, br.y,
+             flipped);
+    return 1;
+  }
+
+  // If we are dealing with wrapping br.x and br.y are greater than 1.0 so
+  // wrap them here as well.
+  br = gfx::Point(xwrap ? WrapTexCoord(br.x) : br.x,
+                  ywrap ? WrapTexCoord(br.y) : br.y);
+
+  // If we wrap around along the x axis, we will draw first from
+  // tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above).
+  // The same applies for the Y axis. The midpoints we calculate here are
+  // only valid if we actually wrap around.
+  GLfloat xmid = aRect.x + (1.0f - tl.x) / texCoordRect.width * aRect.width;
+  GLfloat ymid = aRect.y + (1.0f - tl.y) / texCoordRect.height * aRect.height;
+
+  NS_ASSERTION(!xwrap ||
+               (xmid > aRect.x &&
+                xmid < aRect.XMost() &&
+                FuzzyEqual((xmid - aRect.x) + (aRect.XMost() - xmid), aRect.width)),
+               "xmid should be within [x,XMost()] and the wrapped rect should have the same width");
+  NS_ASSERTION(!ywrap ||
+               (ymid > aRect.y &&
+                ymid < aRect.YMost() &&
+                FuzzyEqual((ymid - aRect.y) + (aRect.YMost() - ymid), aRect.height)),
+               "ymid should be within [y,YMost()] and the wrapped rect should have the same height");
+
+  if (!xwrap && ywrap) {
+    SetRects(0, aLayerRects, aTextureRects,
+             aRect.x, aRect.y, aRect.XMost(), ymid,
+             tl.x, tl.y, br.x, 1.0f,
+             flipped);
+    SetRects(1, aLayerRects, aTextureRects,
+             aRect.x, ymid, aRect.XMost(), aRect.YMost(),
+             tl.x, 0.0f, br.x, br.y,
+             flipped);
+    return 2;
+  }
+
+  if (xwrap && !ywrap) {
+    SetRects(0, aLayerRects, aTextureRects,
+             aRect.x, aRect.y, xmid, aRect.YMost(),
+             tl.x, tl.y, 1.0f, br.y,
+             flipped);
+    SetRects(1, aLayerRects, aTextureRects,
+             xmid, aRect.y, aRect.XMost(), aRect.YMost(),
+             0.0f, tl.y, br.x, br.y,
+             flipped);
+    return 2;
+  }
+
+  SetRects(0, aLayerRects, aTextureRects,
+           aRect.x, aRect.y, xmid, ymid,
+           tl.x, tl.y, 1.0f, 1.0f,
+           flipped);
+  SetRects(1, aLayerRects, aTextureRects,
+           xmid, aRect.y, aRect.XMost(), ymid,
+           0.0f, tl.y, br.x, 1.0f,
+           flipped);
+  SetRects(2, aLayerRects, aTextureRects,
+           aRect.x, ymid, xmid, aRect.YMost(),
+           tl.x, 0.0f, 1.0f, br.y,
+           flipped);
+  SetRects(3, aLayerRects, aTextureRects,
+           xmid, ymid, aRect.XMost(), aRect.YMost(),
+           0.0f, 0.0f, br.x, br.y,
+           flipped);
+  return 4;
+}
+
+} // namespace layers
+} // namespace mozilla
--- a/gfx/layers/Compositor.h
+++ b/gfx/layers/Compositor.h
@@ -535,12 +535,19 @@ protected:
   RefPtr<gfx::DrawTarget> mTarget;
   nsIntRect mTargetBounds;
 
 private:
   static LayersBackend sBackend;
 
 };
 
+// Returns the number of rects. (Up to 4)
+typedef gfx::Rect decomposedRectArrayT[4];
+size_t DecomposeIntoNoRepeatRects(const gfx::Rect& aRect,
+                                  const gfx::Rect& aTexCoordRect,
+                                  decomposedRectArrayT* aLayerRects,
+                                  decomposedRectArrayT* aTextureRects);
+
 } // namespace layers
 } // namespace mozilla
 
 #endif /* MOZILLA_GFX_COMPOSITOR_H */
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -205,17 +205,16 @@ CompositorD3D11::Initialize()
     rastDesc.ScissorEnable = TRUE;
 
     hr = mDevice->CreateRasterizerState(&rastDesc, byRef(mAttachments->mRasterizerState));
     if (FAILED(hr)) {
       return false;
     }
 
     CD3D11_SAMPLER_DESC samplerDesc(D3D11_DEFAULT);
-    samplerDesc.AddressU = samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
     hr = mDevice->CreateSamplerState(&samplerDesc, byRef(mAttachments->mLinearSamplerState));
     if (FAILED(hr)) {
       return false;
     }
 
     samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
     hr = mDevice->CreateSamplerState(&samplerDesc, byRef(mAttachments->mPointSamplerState));
     if (FAILED(hr)) {
@@ -569,17 +568,16 @@ CompositorD3D11::DrawQuad(const gfx::Rec
                           gfx::Float aOpacity,
                           const gfx::Matrix4x4& aTransform)
 {
   MOZ_ASSERT(mCurrentRT, "No render target");
   memcpy(&mVSConstants.layerTransform, &aTransform._11, 64);
   IntPoint origin = mCurrentRT->GetOrigin();
   mVSConstants.renderTargetOffset[0] = origin.x;
   mVSConstants.renderTargetOffset[1] = origin.y;
-  mVSConstants.layerQuad = aRect;
 
   mPSConstants.layerOpacity[0] = aOpacity;
 
   bool restoreBlendMode = false;
 
   MaskType maskType = MaskType::MaskNone;
 
   if (aEffectChain.mSecondaryEffects[EffectTypes::MASK]) {
@@ -622,16 +620,17 @@ CompositorD3D11::DrawQuad(const gfx::Rec
   scissor.left = aClipRect.x;
   scissor.right = aClipRect.XMost();
   scissor.top = aClipRect.y;
   scissor.bottom = aClipRect.YMost();
   mContext->RSSetScissorRects(1, &scissor);
   mContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
   mContext->VSSetShader(mAttachments->mVSQuadShader[maskType], nullptr, 0);
 
+  const Rect* pTexCoordRect = nullptr;
 
   switch (aEffectChain.mPrimaryEffect->mType) {
   case EffectTypes::SOLID_COLOR: {
       SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, SurfaceFormat::UNKNOWN);
 
       Color color =
         static_cast<EffectSolidColor*>(aEffectChain.mPrimaryEffect.get())->mColor;
       mPSConstants.layerColor[0] = color.r * color.a * aOpacity;
@@ -641,17 +640,17 @@ CompositorD3D11::DrawQuad(const gfx::Rec
     }
     break;
   case EffectTypes::RGB:
   case EffectTypes::RENDER_TARGET:
     {
       TexturedEffect* texturedEffect =
         static_cast<TexturedEffect*>(aEffectChain.mPrimaryEffect.get());
 
-      mVSConstants.textureCoords = texturedEffect->mTextureCoords;
+      pTexCoordRect = &texturedEffect->mTextureCoords;
 
       TextureSourceD3D11* source = texturedEffect->mTexture->AsSourceD3D11();
 
       if (!source) {
         NS_WARNING("Missing texture source!");
         return;
       }
 
@@ -677,17 +676,17 @@ CompositorD3D11::DrawQuad(const gfx::Rec
     }
     break;
   case EffectTypes::YCBCR: {
       EffectYCbCr* ycbcrEffect =
         static_cast<EffectYCbCr*>(aEffectChain.mPrimaryEffect.get());
 
       SetSamplerForFilter(Filter::LINEAR);
 
-      mVSConstants.textureCoords = ycbcrEffect->mTextureCoords;
+      pTexCoordRect = &ycbcrEffect->mTextureCoords;
 
       const int Y = 0, Cb = 1, Cr = 2;
       TextureSource* source = ycbcrEffect->mTexture;
 
       if (!source) {
         NS_WARNING("No texture to composite");
         return;
       }
@@ -744,17 +743,18 @@ CompositorD3D11::DrawQuad(const gfx::Rec
         NS_WARNING("Missing texture source(s)!");
         return;
       }
 
       SetPSForEffect(aEffectChain.mPrimaryEffect, maskType, effectComponentAlpha->mOnWhite->GetFormat());
 
       SetSamplerForFilter(effectComponentAlpha->mFilter);
 
-      mVSConstants.textureCoords = effectComponentAlpha->mTextureCoords;
+      pTexCoordRect = &effectComponentAlpha->mTextureCoords;
+
       RefPtr<ID3D11ShaderResourceView> views[2];
 
       HRESULT hr;
 
       hr = mDevice->CreateShaderResourceView(sourceOnBlack->GetD3D11Texture(), nullptr, byRef(views[0]));
       if (Failed(hr)) {
         return;
       }
@@ -769,22 +769,44 @@ CompositorD3D11::DrawQuad(const gfx::Rec
       mContext->OMSetBlendState(mAttachments->mComponentBlendState, sBlendFactor, 0xFFFFFFFF);
       restoreBlendMode = true;
     }
     break;
   default:
     NS_WARNING("Unknown shader type");
     return;
   }
-  if (!UpdateConstantBuffers()) {
-    NS_WARNING("Failed to update shader constant buffers");
-    return;
+
+  if (pTexCoordRect) {
+    Rect layerRects[4];
+    Rect textureRects[4];
+    size_t rects = DecomposeIntoNoRepeatRects(aRect,
+                                              *pTexCoordRect,
+                                              &layerRects,
+                                              &textureRects);
+    for (size_t i = 0; i < rects; i++) {
+      mVSConstants.layerQuad = layerRects[i];
+      mVSConstants.textureCoords = textureRects[i];
+
+      if (!UpdateConstantBuffers()) {
+        NS_WARNING("Failed to update shader constant buffers");
+        break;
+      }
+      mContext->Draw(4, 0);
+    }
+  } else {
+    mVSConstants.layerQuad = aRect;
+
+    if (!UpdateConstantBuffers()) {
+      NS_WARNING("Failed to update shader constant buffers");
+    } else {
+      mContext->Draw(4, 0);
+    }
   }
 
-  mContext->Draw(4, 0);
   if (restoreBlendMode) {
     mContext->OMSetBlendState(mAttachments->mPremulBlendState, sBlendFactor, 0xFFFFFFFF);
   }
 }
 
 void
 CompositorD3D11::BeginFrame(const nsIntRegion& aInvalidRegion,
                             const Rect* aClipRectIn,
@@ -855,17 +877,17 @@ CompositorD3D11::EndFrame()
   EnsureSize();
   if (oldSize == mSize) {
     mSwapChain->Present(0, mDisableSequenceForNextFrame ? DXGI_PRESENT_DO_NOT_SEQUENCE : 0);
     mDisableSequenceForNextFrame = false;
     if (mTarget) {
       PaintToTarget();
     }
   }
-  
+
   mCurrentRT = nullptr;
 }
 
 void
 CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize)
 {
   D3D11_VIEWPORT viewport;
   viewport.MaxDepth = 1.0f;
--- a/gfx/layers/opengl/CompositorOGL.cpp
+++ b/gfx/layers/opengl/CompositorOGL.cpp
@@ -378,199 +378,34 @@ CompositorOGL::Initialize()
       msg += NS_LITERAL_STRING("TEXTURE_RECTANGLE");
     console->LogStringMessage(msg.get());
   }
 
   reporter.SetSuccessful();
   return true;
 }
 
-static GLfloat
-WrapTexCoord(GLfloat v)
-{
-    // fmodf gives negative results for negative numbers;
-    // that is, fmodf(0.75, 1.0) == 0.75, but
-    // fmodf(-0.75, 1.0) == -0.75.  For the negative case,
-    // the result we need is 0.25, so we add 1.0f.
-    if (v < 0.0f) {
-        return 1.0f + fmodf(v, 1.0f);
-    }
-
-    return fmodf(v, 1.0f);
-}
-
-static void
-SetRects(int n,
-         Rect* aLayerRects,
-         Rect* aTextureRects,
-         GLfloat x0, GLfloat y0, GLfloat x1, GLfloat y1,
-         GLfloat tx0, GLfloat ty0, GLfloat tx1, GLfloat ty1,
-         bool flip_y /* = false */)
-{
-  if (flip_y) {
-    std::swap(ty0, ty1);
-  }
-  aLayerRects[n] = Rect(x0, y0, x1 - x0, y1 - y0);
-  aTextureRects[n] = Rect(tx0, ty0, tx1 - tx0, ty1 - ty0);
-}
-
-#ifdef DEBUG
-static inline bool
-FuzzyEqual(float a, float b)
-{
-  return fabs(a - b) < 0.0001f;
-}
-#endif
-
-static int
-DecomposeIntoNoRepeatRects(const Rect& aRect,
-                           const Rect& aTexCoordRect,
-                           Rect* aLayerRects,
-                           Rect* aTextureRects)
-{
-  Rect texCoordRect = aTexCoordRect;
-
-  // If the texture should be flipped, it will have negative height. Detect that
-  // here and compensate for it. We will flip each rect as we emit it.
-  bool flipped = false;
-  if (texCoordRect.height < 0) {
-    flipped = true;
-    texCoordRect.y += texCoordRect.height;
-    texCoordRect.height = -texCoordRect.height;
-  }
-
-  // Wrap the texture coordinates so they are within [0,1] and cap width/height
-  // at 1. We rely on this below.
-  texCoordRect = Rect(Point(WrapTexCoord(texCoordRect.x),
-                            WrapTexCoord(texCoordRect.y)),
-                      Size(std::min(texCoordRect.width, 1.0f),
-                           std::min(texCoordRect.height, 1.0f)));
-
-  NS_ASSERTION(texCoordRect.x >= 0.0f && texCoordRect.x <= 1.0f &&
-               texCoordRect.y >= 0.0f && texCoordRect.y <= 1.0f &&
-               texCoordRect.width >= 0.0f && texCoordRect.width <= 1.0f &&
-               texCoordRect.height >= 0.0f && texCoordRect.height <= 1.0f &&
-               texCoordRect.XMost() >= 0.0f && texCoordRect.XMost() <= 2.0f &&
-               texCoordRect.YMost() >= 0.0f && texCoordRect.YMost() <= 2.0f,
-               "We just wrapped the texture coordinates, didn't we?");
-
-  // Get the top left and bottom right points of the rectangle. Note that
-  // tl.x/tl.y are within [0,1] but br.x/br.y are within [0,2].
-  Point tl = texCoordRect.TopLeft();
-  Point br = texCoordRect.BottomRight();
-
-  NS_ASSERTION(tl.x >= 0.0f && tl.x <= 1.0f &&
-               tl.y >= 0.0f && tl.y <= 1.0f &&
-               br.x >= tl.x && br.x <= 2.0f &&
-               br.y >= tl.y && br.y <= 2.0f &&
-               br.x - tl.x <= 1.0f &&
-               br.y - tl.y <= 1.0f,
-               "Somehow generated invalid texture coordinates");
-
-  // Then check if we wrap in either the x or y axis.
-  bool xwrap = br.x > 1.0f;
-  bool ywrap = br.y > 1.0f;
-
-  // If xwrap is false, the texture will be sampled from tl.x .. br.x.
-  // If xwrap is true, then it will be split into tl.x .. 1.0, and
-  // 0.0 .. WrapTexCoord(br.x). Same for the Y axis. The destination
-  // rectangle is also split appropriately, according to the calculated
-  // xmid/ymid values.
-  if (!xwrap && !ywrap) {
-    SetRects(0, aLayerRects, aTextureRects,
-             aRect.x, aRect.y, aRect.XMost(), aRect.YMost(),
-             tl.x, tl.y, br.x, br.y,
-             flipped);
-    return 1;
-  }
-
-  // If we are dealing with wrapping br.x and br.y are greater than 1.0 so
-  // wrap them here as well.
-  br = Point(xwrap ? WrapTexCoord(br.x) : br.x,
-             ywrap ? WrapTexCoord(br.y) : br.y);
-
-  // If we wrap around along the x axis, we will draw first from
-  // tl.x .. 1.0 and then from 0.0 .. br.x (which we just wrapped above).
-  // The same applies for the Y axis. The midpoints we calculate here are
-  // only valid if we actually wrap around.
-  GLfloat xmid = aRect.x + (1.0f - tl.x) / texCoordRect.width * aRect.width;
-  GLfloat ymid = aRect.y + (1.0f - tl.y) / texCoordRect.height * aRect.height;
-
-  NS_ASSERTION(!xwrap ||
-               (xmid > aRect.x &&
-                xmid < aRect.XMost() &&
-                FuzzyEqual((xmid - aRect.x) + (aRect.XMost() - xmid), aRect.width)),
-               "xmid should be within [x,XMost()] and the wrapped rect should have the same width");
-  NS_ASSERTION(!ywrap ||
-               (ymid > aRect.y &&
-                ymid < aRect.YMost() &&
-                FuzzyEqual((ymid - aRect.y) + (aRect.YMost() - ymid), aRect.height)),
-               "ymid should be within [y,YMost()] and the wrapped rect should have the same height");
-
-  if (!xwrap && ywrap) {
-    SetRects(0, aLayerRects, aTextureRects,
-             aRect.x, aRect.y, aRect.XMost(), ymid,
-             tl.x, tl.y, br.x, 1.0f,
-             flipped);
-    SetRects(1, aLayerRects, aTextureRects,
-             aRect.x, ymid, aRect.XMost(), aRect.YMost(),
-             tl.x, 0.0f, br.x, br.y,
-             flipped);
-    return 2;
-  }
-
-  if (xwrap && !ywrap) {
-    SetRects(0, aLayerRects, aTextureRects,
-             aRect.x, aRect.y, xmid, aRect.YMost(),
-             tl.x, tl.y, 1.0f, br.y,
-             flipped);
-    SetRects(1, aLayerRects, aTextureRects,
-             xmid, aRect.y, aRect.XMost(), aRect.YMost(),
-             0.0f, tl.y, br.x, br.y,
-             flipped);
-    return 2;
-  }
-
-  SetRects(0, aLayerRects, aTextureRects,
-           aRect.x, aRect.y, xmid, ymid,
-           tl.x, tl.y, 1.0f, 1.0f,
-           flipped);
-  SetRects(1, aLayerRects, aTextureRects,
-           xmid, aRect.y, aRect.XMost(), ymid,
-           0.0f, tl.y, br.x, 1.0f,
-           flipped);
-  SetRects(2, aLayerRects, aTextureRects,
-           aRect.x, ymid, xmid, aRect.YMost(),
-           tl.x, 0.0f, 1.0f, br.y,
-           flipped);
-  SetRects(3, aLayerRects, aTextureRects,
-           xmid, ymid, aRect.XMost(), aRect.YMost(),
-           0.0f, 0.0f, br.x, br.y,
-           flipped);
-  return 4;
-}
-
 // |aRect| is the rectangle we want to draw to. We will draw it with
 // up to 4 draw commands if necessary to avoid wrapping.
 // |aTexCoordRect| is the rectangle from the texture that we want to
 // draw using the given program.
 // |aTexture| is the texture we are drawing. Its actual size can be
 // larger than the rectangle given by |texCoordRect|.
 void
 CompositorOGL::BindAndDrawQuadWithTextureRect(ShaderProgramOGL *aProg,
                                               const Rect& aRect,
                                               const Rect& aTexCoordRect,
                                               TextureSource *aTexture)
 {
   Rect layerRects[4];
   Rect textureRects[4];
-  int rects = DecomposeIntoNoRepeatRects(aRect,
-                                         aTexCoordRect,
-                                         layerRects,
-                                         textureRects);
+  size_t rects = DecomposeIntoNoRepeatRects(aRect,
+                                            aTexCoordRect,
+                                            &layerRects,
+                                            &textureRects);
   BindAndDrawQuads(aProg, rects, layerRects, textureRects);
 }
 
 void
 CompositorOGL::PrepareViewport(const gfx::IntSize& aSize)
 {
   // Set the viewport correctly.
   mGLContext->fViewport(0, 0, aSize.width, aSize.height);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -387,17 +387,19 @@ gfxWindowsPlatform::UpdateRenderMode()
 
     // Do not ever try if d2d is explicitly disabled,
     // or if we're not using DWrite fonts.
     if (d2dDisabled || mUsingGDIFonts) {
         tryD2D = false;
     }
 
     ID3D11Device *device = GetD3D11Device();
-    if (isVistaOrHigher && !safeMode && tryD2D && device &&
+    if (isVistaOrHigher && !safeMode && tryD2D &&
+        device &&
+        device->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0 &&
         DoesD3D11DeviceSupportResourceSharing(device)) {
 
         VerifyD2DDevice(d2dForceEnabled);
         if (mD2DDevice) {
             mRenderMode = RENDER_DIRECT2D;
             mUseDirectWrite = true;
         }
     } else {
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -346,17 +346,21 @@ struct ClassExtension
      */
     JSWeakmapKeyDelegateOp weakmapKeyDelegateOp;
 
     /*
      * Optional hook called when an object is moved by a compacting GC.
      *
      * There may exist weak pointers to an object that are not traced through
      * when the normal trace APIs are used, for example objects in the wrapper
-     * cache.  This hook allows these pointers to be updated.
+     * cache. This hook allows these pointers to be updated.
+     *
+     * Note that this hook can be called before JS_NewObject() returns if a GC
+     * is triggered during construction of the object. This can happen for
+     * global objects for example.
      */
     JSObjectMovedOp objectMovedOp;
 };
 
 #define JS_NULL_CLASS_SPEC  {nullptr,nullptr,nullptr,nullptr,nullptr,nullptr}
 #define JS_NULL_CLASS_EXT   {nullptr,nullptr,nullptr,false,nullptr,nullptr}
 
 struct ObjectOps
--- a/js/src/devtools/rootAnalysis/annotations.js
+++ b/js/src/devtools/rootAnalysis/annotations.js
@@ -59,16 +59,17 @@ var ignoreClasses = {
     "JSStringFinalizer" : true,
     "SprintfState" : true,
     "SprintfStateStr" : true,
     "JSLocaleCallbacks" : true,
     "JSC::ExecutableAllocator" : true,
     "PRIOMethods": true,
     "XPCOMFunctions" : true, // I'm a little unsure of this one
     "_MD_IOVector" : true,
+    "malloc_table_t": true, // replace_malloc
 };
 
 // Ignore calls through TYPE.FIELD, where TYPE is the class or struct name containing
 // a function pointer field named FIELD.
 var ignoreCallees = {
     "js::Class.trace" : true,
     "js::Class.finalize" : true,
     "JSRuntime.destroyPrincipals" : true,
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -155,16 +155,17 @@ BytecodeEmitter::init()
 }
 
 static ptrdiff_t
 EmitCheck(ExclusiveContext *cx, BytecodeEmitter *bce, ptrdiff_t delta)
 {
     ptrdiff_t offset = bce->code().length();
 
     // Start it off moderately large to avoid repeated resizings early on.
+    // ~98% of cases fit within 1024 bytes.
     if (bce->code().capacity() == 0 && !bce->code().reserve(1024))
         return -1;
 
     jsbytecode dummy = 0;
     if (!bce->code().appendN(dummy, delta)) {
         js_ReportOutOfMemory(cx);
         return -1;
     }
@@ -2220,18 +2221,16 @@ EmitFinishIteratorResult(ExclusiveContex
         return UINT_MAX;
 
     if (!EmitIndex32(cx, JSOP_INITPROP, value_id, bce))
         return false;
     if (Emit1(cx, bce, done ? JSOP_TRUE : JSOP_FALSE) < 0)
         return false;
     if (!EmitIndex32(cx, JSOP_INITPROP, done_id, bce))
         return false;
-    if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
-        return false;
     return true;
 }
 
 static bool
 EmitNameOp(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, bool callContext)
 {
     if (!BindNameToSlot(cx, bce, pn))
         return false;
@@ -3382,18 +3381,16 @@ EmitDestructuringOpsArrayHelper(Exclusiv
             SET_UINT24(pc, 0);
 
             if (!EmitNumberOp(cx, 0, bce))                             // ... OBJ? ITER ARRAY INDEX
                 return false;
             if (!EmitSpread(cx, bce))                                  // ... OBJ? ARRAY INDEX
                 return false;
             if (Emit1(cx, bce, JSOP_POP) < 0)                          // ... OBJ? ARRAY
                 return false;
-            if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
-                return false;
             needToPopIterator = false;
         } else {
             if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ... OBJ? ITER ITER
                 return false;
             if (!EmitIteratorNext(cx, bce, pattern))                   // ... OBJ? ITER RESULT
                 return false;
             if (Emit1(cx, bce, JSOP_DUP) < 0)                          // ... OBJ? ITER RESULT RESULT
                 return false;
@@ -6416,19 +6413,16 @@ EmitObject(ExclusiveContext *cx, Bytecod
                     obj = nullptr;
             }
 
             if (!EmitIndex32(cx, op, index, bce))
                 return false;
         }
     }
 
-    if (Emit1(cx, bce, JSOP_ENDINIT) < 0)
-        return false;
-
     if (obj) {
         /*
          * The object survived and has a predictable shape: update the original
          * bytecode.
          */
         ObjectBox *objbox = bce->parser->newObjectBox(obj);
         if (!objbox)
             return false;
@@ -6461,18 +6455,17 @@ EmitArrayComp(ExclusiveContext *cx, Byte
      */
     MOZ_ASSERT(bce->stackDepth > 0);
     uint32_t saveDepth = bce->arrayCompDepth;
     bce->arrayCompDepth = (uint32_t) (bce->stackDepth - 1);
     if (!EmitTree(cx, bce, pn->pn_head))
         return false;
     bce->arrayCompDepth = saveDepth;
 
-    /* Emit the usual op needed for decompilation. */
-    return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
+    return true;
 }
 
 /**
  * EmitSpread expects the current index (I) of the array, the array itself and the iterator to be
  * on the stack in that order (iterator on the bottom).
  * It will pop the iterator and I, then iterate over the iterator by calling |.next()|
  * and put the results into the I-th element of array with incrementing I, then
  * push the result I (it will be original I + iteration count).
@@ -6550,19 +6543,17 @@ EmitArray(ExclusiveContext *cx, Bytecode
             SET_UINT24(bce->code(off), atomIndex);
         }
     }
     MOZ_ASSERT(atomIndex == count);
     if (afterSpread) {
         if (Emit1(cx, bce, JSOP_POP) < 0)                                // ARRAY
             return false;
     }
-
-    /* Emit an op to finish the array and aid in decompilation. */
-    return Emit1(cx, bce, JSOP_ENDINIT) >= 0;
+    return true;
 }
 
 static bool
 EmitUnary(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
 {
     if (!UpdateSourceCoordNotes(cx, bce, pn->pn_pos.begin))
         return false;
     /* Unary op, including unary +/-. */
@@ -7077,17 +7068,18 @@ frontend::EmitTree(ExclusiveContext *cx,
 
     return ok;
 }
 
 static int
 AllocSrcNote(ExclusiveContext *cx, SrcNotesVector &notes)
 {
     // Start it off moderately large to avoid repeated resizings early on.
-    if (notes.capacity() == 0 && !notes.reserve(1024))
+    // ~99% of cases fit within 256 bytes.
+    if (notes.capacity() == 0 && !notes.reserve(256))
         return -1;
 
     jssrcnote dummy = 0;
     if (!notes.append(dummy)) {
         js_ReportOutOfMemory(cx);
         return -1;
     }
     return notes.length() - 1;
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -21,16 +21,18 @@
 
 /* Perform validation of incremental marking in debug builds but not on B2G. */
 #if defined(DEBUG) && !defined(MOZ_B2G)
 #define JS_GC_MARKING_VALIDATION
 #endif
 
 namespace js {
 
+class AutoLockGC;
+
 namespace gc {
 
 typedef Vector<JS::Zone *, 4, SystemAllocPolicy> ZoneVector;
 
 struct FinalizePhase;
 class MarkingValidator;
 struct AutoPrepareForTracing;
 class AutoTraceSession;
@@ -59,16 +61,34 @@ class ChunkPool
         inline void popFront();
         inline void removeAndPopFront();
       private:
         ChunkPool &pool;
         Chunk **chunkp;
     };
 };
 
+// Performs extra allocation off the main thread so that when memory is
+// required on the main thread it will already be available and waiting.
+class BackgroundAllocTask : public GCParallelTask
+{
+    // Guarded by the GC lock.
+    JSRuntime *runtime;
+    ChunkPool &chunkPool_;
+
+    const bool enabled_;
+
+  public:
+    BackgroundAllocTask(JSRuntime *rt, ChunkPool &pool);
+    bool enabled() const { return enabled_; }
+
+  protected:
+    virtual void run() MOZ_OVERRIDE;
+};
+
 /*
  * Encapsulates all of the GC tunables. These are effectively constant and
  * should only be modified by setParameter.
  */
 class GCSchedulingTunables
 {
     /*
      * Soft limit on the number of bytes we are allowed to allocate in the GC
@@ -252,17 +272,17 @@ class GCRuntime
 
     bool triggerGC(JS::gcreason::Reason reason);
     bool triggerZoneGC(Zone *zone, JS::gcreason::Reason reason);
     bool maybeGC(Zone *zone);
     void maybePeriodicFullGC();
     void minorGC(JS::gcreason::Reason reason);
     void minorGC(JSContext *cx, JS::gcreason::Reason reason);
     void evictNursery(JS::gcreason::Reason reason = JS::gcreason::EVICT_NURSERY) { minorGC(reason); }
-    void gcIfNeeded(JSContext *cx);
+    bool gcIfNeeded(JSContext *cx = nullptr);
     void gc(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
     void gcSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason, int64_t millis = 0);
     void gcFinalSlice(JSGCInvocationKind gckind, JS::gcreason::Reason reason);
     void gcDebugSlice(bool limit, int64_t objCount);
 
     void runDebugGC();
     inline void poke();
 
@@ -296,18 +316,24 @@ class GCRuntime
 
     size_t maxMallocBytesAllocated() { return maxMallocBytes; }
 
   public:
     // Internal public interface
     js::gc::State state() { return incrementalState; }
     bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); }
     void waitBackgroundSweepEnd() { helperState.waitBackgroundSweepEnd(); }
-    void waitBackgroundSweepOrAllocEnd() { helperState.waitBackgroundSweepOrAllocEnd(); }
-    void startBackgroundAllocationIfIdle() { helperState.startBackgroundAllocationIfIdle(); }
+    void waitBackgroundSweepOrAllocEnd() {
+        helperState.waitBackgroundSweepEnd();
+        allocTask.cancel(GCParallelTask::CancelAndWait);
+    }
+
+#ifdef JSGC_GENERATIONAL
+    void requestMinorGC(JS::gcreason::Reason reason);
+#endif
 
 #ifdef DEBUG
 
     bool onBackgroundThread() { return helperState.onBackgroundThread(); }
 
     bool currentThreadOwnsGCLock() {
         return lockOwner == PR_GetCurrentThread();
     }
@@ -409,17 +435,17 @@ class GCRuntime
     bool isIncrementalGc() { return isIncremental; }
     bool isFullGc() { return isFull; }
 
     bool shouldCleanUpEverything() { return cleanUpEverything; }
 
     bool areGrayBitsValid() { return grayBitsValid; }
     void setGrayBitsInvalid() { grayBitsValid = false; }
 
-    bool isGcNeeded() { return isNeeded; }
+    bool isGcNeeded() { return minorGCRequested || majorGCRequested; }
 
     double computeHeapGrowthFactor(size_t lastBytes);
     size_t computeTriggerBytes(double growthFactor, size_t lastBytes);
 
     JSGCMode gcMode() const { return mode; }
     void setGCMode(JSGCMode m) {
         mode = m;
         marker.setGCMode(mode);
@@ -446,17 +472,18 @@ class GCRuntime
 
     template <AllowGC allowGC>
     static void *refillFreeListFromAnyThread(ThreadSafeContext *cx, AllocKind thingKind);
     static void *refillFreeListInGC(Zone *zone, AllocKind thingKind);
 
   private:
     // For ArenaLists::allocateFromArena()
     friend class ArenaLists;
-    Chunk *pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);
+    Chunk *pickChunk(const AutoLockGC &lock, Zone *zone,
+                     AutoMaybeStartBackgroundAllocation &maybeStartBGAlloc);
     inline void arenaAllocatedDuringGC(JS::Zone *zone, ArenaHeader *arena);
 
     template <AllowGC allowGC>
     static void *refillFreeListFromMainThread(JSContext *cx, AllocKind thingKind);
     static void *refillFreeListOffMainThread(ExclusiveContext *cx, AllocKind thingKind);
     static void *refillFreeListPJS(ForkJoinContext *cx, AllocKind thingKind);
 
     /*
@@ -464,20 +491,23 @@ class GCRuntime
      * Must be called either during the GC or with the GC lock taken.
      */
     Chunk *expireChunkPool(bool shrinkBuffers, bool releaseAll);
     void expireAndFreeChunkPool(bool releaseAll);
     void freeChunkList(Chunk *chunkListHead);
     void prepareToFreeChunk(ChunkInfo &info);
     void releaseChunk(Chunk *chunk);
 
-    inline bool wantBackgroundAllocation() const;
+    friend class BackgroundAllocTask;
+    friend class AutoMaybeStartBackgroundAllocation;
+    inline bool wantBackgroundAllocation(const AutoLockGC &lock) const;
+    void startBackgroundAllocTaskIfIdle();
 
     bool initZeal();
-    void requestInterrupt(JS::gcreason::Reason reason);
+    void requestMajorGC(JS::gcreason::Reason reason);
     void collect(bool incremental, int64_t budget, JSGCInvocationKind gckind,
                  JS::gcreason::Reason reason);
     bool gcCycle(bool incremental, int64_t budget, JSGCInvocationKind gckind,
                  JS::gcreason::Reason reason);
     gcstats::ZoneGCStats scanZonesBeforeGC();
     void budgetIncrementalGC(int64_t *budget);
     void resetIncrementalGC(const char *reason);
     void incrementalCollectSlice(int64_t budget, JS::gcreason::Reason reason);
@@ -600,22 +630,23 @@ class GCRuntime
     bool                  cleanUpEverything;
 
     /*
      * The gray bits can become invalid if UnmarkGray overflows the stack. A
      * full GC will reset this bit, since it fills in all the gray bits.
      */
     bool                  grayBitsValid;
 
-    /*
-     * These flags must be kept separate so that a thread requesting a
-     * compartment GC doesn't cancel another thread's concurrent request for a
-     * full GC.
-     */
-    volatile uintptr_t    isNeeded;
+    volatile uintptr_t    majorGCRequested;
+    JS::gcreason::Reason  majorGCTriggerReason;
+
+#ifdef JSGC_GENERATIONAL
+    bool                  minorGCRequested;
+    JS::gcreason::Reason  minorGCTriggerReason;
+#endif
 
     /* Incremented at the start of every major GC. */
     uint64_t              majorGCNumber;
 
     /* The major GC number at which to release observed type information. */
     uint64_t              jitReleaseNumber;
 
     /* Incremented on every GC slice. */
@@ -628,19 +659,16 @@ class GCRuntime
     bool                  isIncremental;
 
     /* Whether all compartments are being collected in first GC slice. */
     bool                  isFull;
 
     /* The invocation kind of the current GC, taken from the first slice. */
     JSGCInvocationKind    invocationKind;
 
-    /* The reason that an interrupt-triggered GC should be called. */
-    JS::gcreason::Reason  triggerReason;
-
     /*
      * If this is 0, all cross-compartment proxies must be registered in the
      * wrapper map. This checking must be disabled temporarily while creating
      * new wrappers. When non-zero, this records the recursion depth of wrapper
      * creation.
      */
     mozilla::DebugOnly<uintptr_t>  disableStrictProxyCheckingCount;
 
@@ -835,16 +863,17 @@ class GCRuntime
 #ifdef DEBUG
     size_t                noGCOrAllocationCheck;
 #endif
 
     /* Synchronize GC heap access between main thread and GCHelperState. */
     PRLock                *lock;
     mozilla::DebugOnly<PRThread *>   lockOwner;
 
+    BackgroundAllocTask allocTask;
     GCHelperState helperState;
 
     /*
      * During incremental sweeping, this field temporarily holds the arenas of
      * the current AllocKind being swept in order of increasing free space.
      */
     SortedArenaList incrementalSweepList;
 
--- a/js/src/gc/Statistics.cpp
+++ b/js/src/gc/Statistics.cpp
@@ -761,17 +761,18 @@ Statistics::beginSlice(const ZoneGCStats
 {
     this->zoneStats = zoneStats;
 
     bool first = runtime->gc.state() == gc::NO_INCREMENTAL;
     if (first)
         beginGC();
 
     SliceData data(reason, PRMJ_Now(), GetPageFaultCount());
-    (void) slices.append(data); /* Ignore any OOMs here. */
+    if (!slices.append(data))
+        CrashAtUnhandlableOOM("Failed to allocate statistics slice.");
 
     if (JSAccumulateTelemetryDataCallback cb = runtime->telemetryCallback)
         (*cb)(JS_TELEMETRY_GC_REASON, reason);
 
     // Slice callbacks should only fire for the outermost level
     if (++gcDepth == 1) {
         bool wasFullGC = zoneStats.isCollectingAllZones();
         if (sliceCallback)
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -298,17 +298,17 @@ StoreBuffer::markAll(JSTracer *trc)
     bufferRelocCell.mark(this, trc);
     bufferGeneric.mark(this, trc);
 }
 
 void
 StoreBuffer::setAboutToOverflow()
 {
     aboutToOverflow_ = true;
-    runtime_->requestInterrupt(JSRuntime::RequestInterruptMainThread);
+    runtime_->gc.requestMinorGC(JS::gcreason::FULL_STORE_BUFFER);
 }
 
 bool
 StoreBuffer::inParallelSection() const
 {
     return InParallelSection();
 }
 
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -1849,22 +1849,16 @@ BaselineCompiler::emit_JSOP_INITPROP()
     frame.push(R0);
     frame.syncStack(0);
 
     // Call IC.
     ICSetProp_Fallback::Compiler compiler(cx);
     return emitOpIC(compiler.getStub(&stubSpace_));
 }
 
-bool
-BaselineCompiler::emit_JSOP_ENDINIT()
-{
-    return true;
-}
-
 typedef bool (*NewbornArrayPushFn)(JSContext *, HandleObject, const Value &);
 static const VMFunction NewbornArrayPushInfo = FunctionInfo<NewbornArrayPushFn>(NewbornArrayPush);
 
 bool
 BaselineCompiler::emit_JSOP_ARRAYPUSH()
 {
     // Keep value in R0, object in R1.
     frame.popRegsAndSync(2);
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -99,17 +99,16 @@ namespace jit {
     _(JSOP_INITELEM)           \
     _(JSOP_INITELEM_GETTER)    \
     _(JSOP_INITELEM_SETTER)    \
     _(JSOP_INITELEM_INC)       \
     _(JSOP_MUTATEPROTO)        \
     _(JSOP_INITPROP)           \
     _(JSOP_INITPROP_GETTER)    \
     _(JSOP_INITPROP_SETTER)    \
-    _(JSOP_ENDINIT)            \
     _(JSOP_ARRAYPUSH)          \
     _(JSOP_GETELEM)            \
     _(JSOP_SETELEM)            \
     _(JSOP_CALLELEM)           \
     _(JSOP_DELELEM)            \
     _(JSOP_IN)                 \
     _(JSOP_GETGNAME)           \
     _(JSOP_BINDGNAME)          \
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -1634,19 +1634,16 @@ IonBuilder::inspectOpcode(JSOp op)
         PropertyName *name = info().getAtom(pc)->asPropertyName();
         return jsop_initprop_getter_setter(name);
       }
 
       case JSOP_INITELEM_GETTER:
       case JSOP_INITELEM_SETTER:
         return jsop_initelem_getter_setter();
 
-      case JSOP_ENDINIT:
-        return true;
-
       case JSOP_FUNCALL:
         return jsop_funcall(GET_ARGC(pc));
 
       case JSOP_FUNAPPLY:
         return jsop_funapply(GET_ARGC(pc));
 
       case JSOP_CALL:
       case JSOP_NEW:
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1064,28 +1064,39 @@ GCRuntime::moveChunkToFreePool(Chunk *ch
 {
     MOZ_ASSERT(chunk->unused());
     MOZ_ASSERT(chunkSet.has(chunk));
     chunkSet.remove(chunk);
     emptyChunks.put(chunk);
 }
 
 inline bool
-GCRuntime::wantBackgroundAllocation() const
-{
-    /*
-     * To minimize memory waste we do not want to run the background chunk
-     * allocation if we have empty chunks or when the runtime needs just few
-     * of them.
-     */
-    return helperState.canBackgroundAllocate() &&
+GCRuntime::wantBackgroundAllocation(const AutoLockGC &lock) const
+{
+    // To minimize memory waste, we do not want to run the background chunk
+    // allocation if we already have some empty chunks or when the runtime has
+    // a small heap size (and therefore likely has a small growth rate).
+    return allocTask.enabled() &&
            emptyChunks.count() < tunables.minEmptyChunkCount() &&
            chunkSet.count() >= 4;
 }
 
+void
+GCRuntime::startBackgroundAllocTaskIfIdle()
+{
+    AutoLockHelperThreadState helperLock;
+    if (allocTask.isRunning())
+        return;
+
+    // Join the previous invocation of the task. This will return immediately
+    // if the thread has never been started.
+    allocTask.joinWithLockHeld();
+    allocTask.startWithLockHeld();
+}
+
 class js::gc::AutoMaybeStartBackgroundAllocation
 {
   private:
     JSRuntime *runtime;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit AutoMaybeStartBackgroundAllocation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
@@ -1094,27 +1105,24 @@ class js::gc::AutoMaybeStartBackgroundAl
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     void tryToStartBackgroundAllocation(JSRuntime *rt) {
         runtime = rt;
     }
 
     ~AutoMaybeStartBackgroundAllocation() {
-        if (runtime && !runtime->currentThreadOwnsInterruptLock()) {
-            AutoLockHelperThreadState helperLock;
-            AutoLockGC lock(runtime);
-            runtime->gc.startBackgroundAllocationIfIdle();
-        }
+        if (runtime && !runtime->currentThreadOwnsInterruptLock())
+            runtime->gc.startBackgroundAllocTaskIfIdle();
     }
 };
 
-/* The caller must hold the GC lock. */
 Chunk *
-GCRuntime::pickChunk(Zone *zone, AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
+GCRuntime::pickChunk(const AutoLockGC &lock, Zone *zone,
+                     AutoMaybeStartBackgroundAllocation &maybeStartBackgroundAllocation)
 {
     Chunk **listHeadp = getAvailableChunkList(zone);
     Chunk *chunk = *listHeadp;
     if (chunk)
         return chunk;
 
     chunk = emptyChunks.get(rt);
     if (!chunk) {
@@ -1122,17 +1130,17 @@ GCRuntime::pickChunk(Zone *zone, AutoMay
         if (!chunk)
             return nullptr;
         MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
     }
 
     MOZ_ASSERT(chunk->unused());
     MOZ_ASSERT(!chunkSet.has(chunk));
 
-    if (wantBackgroundAllocation())
+    if (wantBackgroundAllocation(lock))
         maybeStartBackgroundAllocation.tryToStartBackgroundAllocation(rt);
 
     chunkAllocationSinceLastGC = true;
 
     /*
      * FIXME bug 583732 - chunk is newly allocated and cannot be present in
      * the table so using ordinary lookupForAdd is suboptimal here.
      */
@@ -1168,23 +1176,27 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     verifyPostData(nullptr),
     chunkAllocationSinceLastGC(false),
     nextFullGCTime(0),
     lastGCTime(0),
     mode(JSGC_MODE_INCREMENTAL),
     decommitThreshold(32 * 1024 * 1024),
     cleanUpEverything(false),
     grayBitsValid(false),
-    isNeeded(0),
+    majorGCRequested(0),
+    majorGCTriggerReason(JS::gcreason::NO_REASON),
+#ifdef JSGC_GENERATIONAL
+    minorGCRequested(false),
+    minorGCTriggerReason(JS::gcreason::NO_REASON),
+#endif
     majorGCNumber(0),
     jitReleaseNumber(0),
     number(0),
     startNumber(0),
     isFull(false),
-    triggerReason(JS::gcreason::NO_REASON),
 #ifdef DEBUG
     disableStrictProxyCheckingCount(0),
 #endif
     incrementalState(gc::NO_INCREMENTAL),
     lastMarkSlice(false),
     sweepOnBackgroundThread(false),
     foundBlackGrayEdges(false),
     sweepingZones(nullptr),
@@ -1224,16 +1236,17 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     inUnsafeRegion(0),
 #endif
     alwaysPreserveCode(false),
 #ifdef DEBUG
     noGCOrAllocationCheck(0),
 #endif
     lock(nullptr),
     lockOwner(nullptr),
+    allocTask(rt, emptyChunks),
     helperState(rt)
 {
     setGCMode(JSGC_MODE_GLOBAL);
 }
 
 #ifdef JS_GC_ZEAL
 
 const char *gc::ZealModeHelpText =
@@ -1965,17 +1978,17 @@ ArenaLists::allocateFromArena(JS::Zone *
         return allocateFromArenaInner<HasFreeThings>(zone, aheader, thingKind);
     }
 
     // Parallel threads have their own ArenaLists, but chunks are shared;
     // if we haven't already, take the GC lock now to avoid racing.
     if (maybeLock.isNothing())
         maybeLock.emplace(rt);
 
-    Chunk *chunk = rt->gc.pickChunk(zone, maybeStartBGAlloc);
+    Chunk *chunk = rt->gc.pickChunk(maybeLock.ref(), zone, maybeStartBGAlloc);
     if (!chunk)
         return nullptr;
 
     // Although our chunk should definitely have enough space for another arena,
     // there are other valid reasons why Chunk::allocateArena() may fail.
     aheader = chunk->allocateArena(zone, thingKind);
     if (!aheader)
         return nullptr;
@@ -2932,23 +2945,35 @@ SliceBudget::checkOverBudget()
 
 void
 js::MarkCompartmentActive(InterpreterFrame *fp)
 {
     fp->script()->compartment()->zone()->active = true;
 }
 
 void
-GCRuntime::requestInterrupt(JS::gcreason::Reason reason)
-{
-    if (isNeeded)
+GCRuntime::requestMajorGC(JS::gcreason::Reason reason)
+{
+    if (majorGCRequested)
         return;
 
-    isNeeded = true;
-    triggerReason = reason;
+    majorGCRequested = true;
+    majorGCTriggerReason = reason;
+    rt->requestInterrupt(JSRuntime::RequestInterruptMainThread);
+}
+
+void
+GCRuntime::requestMinorGC(JS::gcreason::Reason reason)
+{
+    MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
+    if (minorGCRequested)
+        return;
+
+    minorGCRequested = true;
+    minorGCTriggerReason = reason;
     rt->requestInterrupt(JSRuntime::RequestInterruptMainThread);
 }
 
 bool
 GCRuntime::triggerGC(JS::gcreason::Reason reason)
 {
     /* Wait till end of parallel section to trigger GC. */
     if (InParallelSection()) {
@@ -2967,17 +2992,17 @@ GCRuntime::triggerGC(JS::gcreason::Reaso
     if (rt->currentThreadOwnsInterruptLock())
         return false;
 
     /* GC is already running. */
     if (rt->isHeapCollecting())
         return false;
 
     JS::PrepareForFullGC(rt);
-    requestInterrupt(reason);
+    requestMajorGC(reason);
     return true;
 }
 
 bool
 GCRuntime::triggerZoneGC(Zone *zone, JS::gcreason::Reason reason)
 {
     /*
      * If parallel threads are running, wait till they
@@ -3009,37 +3034,35 @@ GCRuntime::triggerZoneGC(Zone *zone, JS:
 
     if (rt->isAtomsZone(zone)) {
         /* We can't do a zone GC of the atoms compartment. */
         triggerGC(reason);
         return true;
     }
 
     PrepareZoneForGC(zone);
-    requestInterrupt(reason);
+    requestMajorGC(reason);
     return true;
 }
 
 bool
 GCRuntime::maybeGC(Zone *zone)
 {
     MOZ_ASSERT(CurrentThreadCanAccessRuntime(rt));
 
 #ifdef JS_GC_ZEAL
     if (zealMode == ZealAllocValue || zealMode == ZealPokeValue) {
         JS::PrepareForFullGC(rt);
         gc(GC_NORMAL, JS::gcreason::MAYBEGC);
         return true;
     }
 #endif
 
-    if (isNeeded) {
-        gcSlice(GC_NORMAL, JS::gcreason::MAYBEGC);
+    if (gcIfNeeded())
         return true;
-    }
 
     double factor = schedulingState.inHighFrequencyGCMode() ? 0.85 : 0.9;
     if (zone->usage.gcBytes() > 1024 * 1024 &&
         zone->usage.gcBytes() >= factor * zone->threshold.gcTriggerBytes() &&
         incrementalState == NO_INCREMENTAL &&
         !isBackgroundSweeping())
     {
         PrepareZoneForGC(zone);
@@ -3264,22 +3287,18 @@ js::GetCPUCount()
 }
 
 bool
 GCHelperState::init()
 {
     if (!(done = PR_NewCondVar(rt->gc.lock)))
         return false;
 
-    if (CanUseExtraThreads()) {
-        backgroundAllocation = (GetCPUCount() >= 2);
+    if (CanUseExtraThreads())
         HelperThreadState().ensureInitialized();
-    } else {
-        backgroundAllocation = false;
-    }
 
     return true;
 }
 
 void
 GCHelperState::finish()
 {
     if (!rt->gc.lock) {
@@ -3353,46 +3372,50 @@ GCHelperState::work()
 
       case SWEEPING: {
         AutoTraceLog logSweeping(logger, TraceLogger::GCSweeping);
         doSweep();
         MOZ_ASSERT(state() == SWEEPING);
         break;
       }
 
-      case ALLOCATING: {
-        AutoTraceLog logAllocation(logger, TraceLogger::GCAllocation);
-        do {
-            Chunk *chunk;
-            {
-                AutoUnlockGC unlock(rt);
-                chunk = Chunk::allocate(rt);
-            }
-
-            /* OOM stops the background allocation. */
-            if (!chunk)
-                break;
-            MOZ_ASSERT(chunk->info.numArenasFreeCommitted == 0);
-            rt->gc.emptyChunks.put(chunk);
-        } while (state() == ALLOCATING && rt->gc.wantBackgroundAllocation());
-
-        MOZ_ASSERT(state() == ALLOCATING || state() == CANCEL_ALLOCATION);
-        break;
-      }
-
-      case CANCEL_ALLOCATION:
-        break;
     }
 
     setState(IDLE);
     thread = nullptr;
 
     PR_NotifyAllCondVar(done);
 }
 
+BackgroundAllocTask::BackgroundAllocTask(JSRuntime *rt, ChunkPool &pool)
+  : runtime(rt),
+    chunkPool_(pool),
+    enabled_(CanUseExtraThreads() && GetCPUCount() >= 2)
+{
+}
+
+/* virtual */ void
+BackgroundAllocTask::run()
+{
+    TraceLogger *logger = TraceLoggerForCurrentThread();
+    AutoTraceLog logAllocation(logger, TraceLogger::GCAllocation);
+
+    AutoLockGC lock(runtime);
+    while (!cancel_ && runtime->gc.wantBackgroundAllocation(lock)) {
+        Chunk *chunk;
+        {
+            AutoUnlockGC unlock(runtime);
+            chunk = Chunk::allocate(runtime);
+            if (!chunk)
+                break;
+        }
+        chunkPool_.put(chunk);
+    }
+}
+
 void
 GCHelperState::startBackgroundSweep(bool shouldShrink)
 {
     MOZ_ASSERT(CanUseExtraThreads());
 
     AutoLockHelperThreadState helperLock;
     AutoLockGC lock(rt);
     MOZ_ASSERT(state() == IDLE);
@@ -3411,56 +3434,31 @@ GCHelperState::startBackgroundShrink()
       case IDLE:
         MOZ_ASSERT(!sweepFlag);
         shrinkFlag = true;
         startBackgroundThread(SWEEPING);
         break;
       case SWEEPING:
         shrinkFlag = true;
         break;
-      case ALLOCATING:
-      case CANCEL_ALLOCATION:
-        /*
-         * If we have started background allocation there is nothing to
-         * shrink.
-         */
-        break;
+      default:
+        MOZ_CRASH("Invalid GC helper thread state.");
     }
 }
 
 void
 GCHelperState::waitBackgroundSweepEnd()
 {
     AutoLockGC lock(rt);
     while (state() == SWEEPING)
         waitForBackgroundThread();
     if (rt->gc.incrementalState == NO_INCREMENTAL)
         rt->gc.assertBackgroundSweepingFinished();
 }
 
-void
-GCHelperState::waitBackgroundSweepOrAllocEnd()
-{
-    AutoLockGC lock(rt);
-    if (state() == ALLOCATING)
-        setState(CANCEL_ALLOCATION);
-    while (state() == SWEEPING || state() == CANCEL_ALLOCATION)
-        waitForBackgroundThread();
-    if (rt->gc.incrementalState == NO_INCREMENTAL)
-        rt->gc.assertBackgroundSweepingFinished();
-}
-
-/* Must be called with the GC lock taken. */
-inline void
-GCHelperState::startBackgroundAllocationIfIdle()
-{
-    if (state_ == IDLE)
-        startBackgroundThread(ALLOCATING);
-}
-
 /* Must be called with the GC lock taken. */
 void
 GCHelperState::doSweep()
 {
     if (sweepFlag) {
         sweepFlag = false;
         AutoUnlockGC unlock(rt);
 
@@ -5809,39 +5807,44 @@ GCRuntime::gcCycle(bool incremental, int
     /*
      * Marking can trigger many incidental post barriers, some of them for
      * objects which are not going to be live after the GC.
      */
     AutoDisableStoreBuffer adsb(this);
 
     AutoTraceSession session(rt, MajorCollecting);
 
-    isNeeded = false;
+    majorGCRequested = false;
     interFrameGC = true;
 
     number++;
     if (incrementalState == NO_INCREMENTAL)
         majorGCNumber++;
 
     // It's ok if threads other than the main thread have suppressGC set, as
     // they are operating on zones which will not be collected from here.
     MOZ_ASSERT(!rt->mainThread.suppressGC);
 
     // Assert if this is a GC unsafe region.
     JS::AutoAssertOnGC::VerifyIsSafeToGC(rt);
 
-    /*
-     * As we about to purge caches and clear the mark bits we must wait for
-     * any background finalization to finish. We must also wait for the
-     * background allocation to finish so we can avoid taking the GC lock
-     * when manipulating the chunks during the GC.
-     */
     {
         gcstats::AutoPhase ap(stats, gcstats::PHASE_WAIT_BACKGROUND_THREAD);
-        waitBackgroundSweepOrAllocEnd();
+
+        // As we are about to purge caches and clear the mark bits, wait for
+        // background finalization to finish. It cannot run between slices
+        // so we only need to wait on the first slice.
+        if (incrementalState == NO_INCREMENTAL)
+            waitBackgroundSweepEnd();
+
+        // We must also wait for background allocation to finish so we can
+        // avoid taking the GC lock when manipulating the chunks during the GC.
+        // The background alloc task can run between slices, so we must wait
+        // for it at the start of every slice.
+        allocTask.cancel(GCParallelTask::CancelAndWait);
     }
 
     State prevState = incrementalState;
 
     if (!incremental) {
         // Reset any in progress incremental GC if this was triggered via the
         // API. This isn't required for correctness, but sometimes during tests
         // the caller expects this GC to collect certain objects, and we need
@@ -6140,29 +6143,31 @@ GCRuntime::shrinkBuffers()
     else
         expireChunksAndArenas(true);
 }
 
 void
 GCRuntime::minorGC(JS::gcreason::Reason reason)
 {
 #ifdef JSGC_GENERATIONAL
+    minorGCRequested = false;
     TraceLogger *logger = TraceLoggerForMainThread(rt);
     AutoTraceLog logMinorGC(logger, TraceLogger::MinorGC);
     nursery.collect(rt, reason, nullptr);
     MOZ_ASSERT_IF(!rt->mainThread.suppressGC, nursery.isEmpty());
 #endif
 }
 
 void
 GCRuntime::minorGC(JSContext *cx, JS::gcreason::Reason reason)
 {
     // Alternate to the runtime-taking form above which allows marking type
     // objects as needing pretenuring.
 #ifdef JSGC_GENERATIONAL
+    minorGCRequested = false;
     TraceLogger *logger = TraceLoggerForMainThread(rt);
     AutoTraceLog logMinorGC(logger, TraceLogger::MinorGC);
     Nursery::TypeObjectList pretenureTypes;
     nursery.collect(rt, reason, &pretenureTypes);
     for (size_t i = 0; i < pretenureTypes.length(); i++) {
         if (pretenureTypes[i]->canPreTenure())
             pretenureTypes[i]->setShouldPreTenure(cx);
     }
@@ -6191,30 +6196,36 @@ GCRuntime::enableGenerationalGC()
 #ifdef JSGC_GENERATIONAL
     if (generationalDisabled == 0) {
         nursery.enable();
         storeBuffer.enable();
     }
 #endif
 }
 
-void
-GCRuntime::gcIfNeeded(JSContext *cx)
-{
+bool
+GCRuntime::gcIfNeeded(JSContext *cx /* = nullptr */)
+{
+    // This method returns whether a major GC was performed.
+
 #ifdef JSGC_GENERATIONAL
-    /*
-     * In case of store buffer overflow perform minor GC first so that the
-     * correct reason is seen in the logs.
-     */
-    if (storeBuffer.isAboutToOverflow())
-        minorGC(cx, JS::gcreason::FULL_STORE_BUFFER);
+    if (minorGCRequested) {
+        if (cx)
+            minorGC(cx, minorGCTriggerReason);
+        else
+            minorGC(minorGCTriggerReason);
+    }
 #endif
 
-    if (isNeeded)
-        gcSlice(GC_NORMAL, rt->gc.triggerReason, 0);
+    if (majorGCRequested) {
+        gcSlice(GC_NORMAL, rt->gc.majorGCTriggerReason);
+        return true;
+    }
+
+    return false;
 }
 
 AutoFinishGC::AutoFinishGC(JSRuntime *rt)
 {
     if (JS::IsIncrementalGCInProgress(rt)) {
         JS::PrepareForIncrementalGC(rt);
         JS::FinishIncrementalGC(rt, JS::gcreason::API);
     }
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1000,19 +1000,17 @@ NotifyGCPostSwap(JSObject *a, JSObject *
  * In non-threadsafe builds, all actual sweeping and allocation is performed
  * on the main thread, but GCHelperState encapsulates this from clients as
  * much as possible.
  */
 class GCHelperState
 {
     enum State {
         IDLE,
-        SWEEPING,
-        ALLOCATING,
-        CANCEL_ALLOCATION
+        SWEEPING
     };
 
     // Associated runtime.
     JSRuntime *const rt;
 
     // Condvar for notifying the main thread when work has finished. This is
     // associated with the runtime's GC lock --- the worker thread state
     // condvars can't be used here due to lock ordering issues.
@@ -1028,18 +1026,16 @@ class GCHelperState
     void waitForBackgroundThread();
 
     State state();
     void setState(State state);
 
     bool              sweepFlag;
     bool              shrinkFlag;
 
-    bool              backgroundAllocation;
-
     friend class js::gc::ArenaLists;
 
     static void freeElementsAndArray(void **array, void **end) {
         MOZ_ASSERT(array <= end);
         for (void **p = array; p != end; ++p)
             js_free(*p);
         js_free(array);
     }
@@ -1049,48 +1045,33 @@ class GCHelperState
 
   public:
     explicit GCHelperState(JSRuntime *rt)
       : rt(rt),
         done(nullptr),
         state_(IDLE),
         thread(nullptr),
         sweepFlag(false),
-        shrinkFlag(false),
-        backgroundAllocation(true)
+        shrinkFlag(false)
     { }
 
     bool init();
     void finish();
 
     void work();
 
     /* Must be called with the GC lock taken. */
     void startBackgroundSweep(bool shouldShrink);
 
     /* Must be called with the GC lock taken. */
     void startBackgroundShrink();
 
     /* Must be called without the GC lock taken. */
     void waitBackgroundSweepEnd();
 
-    /* Must be called without the GC lock taken. */
-    void waitBackgroundSweepOrAllocEnd();
-
-    /* Must be called with the GC lock taken. */
-    void startBackgroundAllocationIfIdle();
-
-    bool canBackgroundAllocate() const {
-        return backgroundAllocation;
-    }
-
-    void disableBackgroundAllocation() {
-        backgroundAllocation = false;
-    }
-
     bool onBackgroundThread();
 
     /*
      * Outside the GC lock may give true answer when in fact the sweeping has
      * been done.
      */
     bool isBackgroundSweeping() const {
         return state_ == SWEEPING;
@@ -1113,35 +1094,50 @@ class GCParallelTask
         Dispatched,
         Finished,
     } state;
 
     // Amount of time this task took to execute.
     uint64_t duration_;
 
   protected:
+    // A flag to signal a request for early completion of the off-thread task.
+    mozilla::Atomic<bool> cancel_;
+
     virtual void run() = 0;
 
   public:
     GCParallelTask() : state(NotStarted), duration_(0) {}
 
+    // Time spent in the most recent invocation of this task.
     int64_t duration() const { return duration_; }
 
     // The simple interface to a parallel task works exactly like pthreads.
     bool start();
     void join();
 
     // If multiple tasks are to be started or joined at once, it is more
     // efficient to take the helper thread lock once and use these methods.
     bool startWithLockHeld();
     void joinWithLockHeld();
 
     // Instead of dispatching to a helper, run the task on the main thread.
     void runFromMainThread(JSRuntime *rt);
 
+    // Dispatch a cancelation request.
+    enum CancelMode { CancelNoWait, CancelAndWait};
+    void cancel(CancelMode mode = CancelNoWait) {
+        cancel_ = true;
+        if (mode == CancelAndWait)
+            join();
+    }
+
+    // Check if a task is actively running.
+    bool isRunning() const;
+
     // This should be friended to HelperThread, but cannot be because it
     // would introduce several circular dependencies.
   public:
     void runFromHelperThread();
 };
 
 struct GCChunkHasher {
     typedef gc::Chunk *Lookup;
--- a/js/src/jsopcode.cpp
+++ b/js/src/jsopcode.cpp
@@ -799,16 +799,20 @@ js_Disassemble(JSContext *cx, HandleScri
 JS_FRIEND_API(bool)
 js_DumpPC(JSContext *cx)
 {
     js::gc::AutoSuppressGC suppressGC(cx);
     Sprinter sprinter(cx);
     if (!sprinter.init())
         return false;
     ScriptFrameIter iter(cx);
+    if (iter.done()) {
+        fprintf(stdout, "Empty stack.\n");
+        return true;
+    }
     RootedScript script(cx, iter.script());
     bool ok = js_DisassembleAtPC(cx, script, true, iter.pc(), false, &sprinter);
     fprintf(stdout, "%s", sprinter.string());
     return ok;
 }
 
 JS_FRIEND_API(bool)
 js_DumpScript(JSContext *cx, JSScript *scriptArg)
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -21,27 +21,17 @@
 /*
  * JS operation bytecodes.
  */
 typedef enum JSOp {
 #define ENUMERATE_OPCODE(op, val, ...) op = val,
 FOR_EACH_OPCODE(ENUMERATE_OPCODE)
 #undef ENUMERATE_OPCODE
 
-    JSOP_LIMIT,
-
-    /*
-     * These pseudo-ops help js_DecompileValueGenerator decompile JSOP_SETPROP,
-     * JSOP_SETELEM, and comprehension-tails, respectively.  They are never
-     * stored in bytecode, so they don't preempt valid opcodes.
-     */
-    JSOP_GETPROP2 = JSOP_LIMIT,
-    JSOP_GETELEM2 = JSOP_LIMIT + 1,
-    JSOP_FORLOCAL = JSOP_LIMIT + 2,
-    JSOP_FAKE_LIMIT = JSOP_FORLOCAL
+    JSOP_LIMIT
 } JSOp;
 
 /*
  * JS bytecode formats.
  */
 #define JOF_BYTE          0       /* single bytecode, no immediates */
 #define JOF_JUMP          1       /* signed 16-bit jump offset immediate */
 #define JOF_ATOM          2       /* unsigned 16-bit constant index */
--- a/js/src/jsweakmap.cpp
+++ b/js/src/jsweakmap.cpp
@@ -347,33 +347,20 @@ TryPreserveReflector(JSContext *cx, Hand
     }
     return true;
 }
 
 static inline void
 WeakMapPostWriteBarrier(JSRuntime *rt, ObjectValueMap *weakMap, JSObject *key)
 {
 #ifdef JSGC_GENERATIONAL
-    /*
-     * Strip the barriers from the type before inserting into the store buffer.
-     * This will automatically ensure that barriers do not fire during GC.
-     *
-     * Some compilers complain about instantiating the WeakMap class for
-     * unbarriered type arguments, so we cast to a HashMap instead.  Because of
-     * WeakMap's multiple inheritace, We need to do this in two stages, first to
-     * the HashMap base class and then to the unbarriered version.
-     */
-    ObjectValueMap::Base *baseHashMap = static_cast<ObjectValueMap::Base *>(weakMap);
-
-    typedef HashMap<JSObject *, Value> UnbarrieredMap;
-    UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
-
-    typedef HashKeyRef<UnbarrieredMap, JSObject *> Ref;
+    // Strip the barriers from the type before inserting into the store buffer.
+    // This will automatically ensure that barriers do not fire during GC.
     if (key && IsInsideNursery(key))
-        rt->gc.storeBuffer.putGeneric(Ref((unbarrieredMap), key));
+        rt->gc.storeBuffer.putGeneric(UnbarrieredRef(weakMap, key));
 #endif
 }
 
 static MOZ_ALWAYS_INLINE bool
 SetWeakMapEntryInternal(JSContext *cx, Handle<WeakMapObject*> mapObj,
                         HandleObject key, HandleValue value)
 {
     ObjectValueMap *map = mapObj->getMap();
--- a/js/src/jsweakmap.h
+++ b/js/src/jsweakmap.h
@@ -7,16 +7,17 @@
 #ifndef jsweakmap_h
 #define jsweakmap_h
 
 #include "jscompartment.h"
 #include "jsfriendapi.h"
 #include "jsobj.h"
 
 #include "gc/Marking.h"
+#include "gc/StoreBuffer.h"
 #include "js/HashTable.h"
 
 namespace js {
 
 // A subclass template of js::HashMap whose keys and values may be garbage-collected. When
 // a key is collected, the table entry disappears, dropping its reference to the value.
 //
 // More precisely:
@@ -274,16 +275,38 @@ protected:
             MOZ_ASSERT(!gc::IsAboutToBeFinalized(&k));
             MOZ_ASSERT(!gc::IsAboutToBeFinalized(&r.front().value()));
             MOZ_ASSERT(k == r.front().key());
         }
 #endif
     }
 };
 
+/*
+ * At times, you will need to ignore barriers when accessing WeakMap entries.
+ * Localize the templatized casting craziness here.
+ */
+template <class Key, class Value>
+static inline gc::HashKeyRef<HashMap<Key, Value, DefaultHasher<Key>, RuntimeAllocPolicy>, Key>
+UnbarrieredRef(WeakMap<PreBarriered<Key>, RelocatablePtr<Value>> *map, Key key)
+{
+    /*
+     * Some compilers complain about instantiating the WeakMap class for
+     * unbarriered type arguments, so we cast to a HashMap instead. Because of
+     * WeakMap's multiple inheritance, we need to do this in two stages, first
+     * to the HashMap base class and then to the unbarriered version.
+     */
+
+    typedef typename WeakMap<PreBarriered<Key>, RelocatablePtr<Value>>::Base BaseMap;
+    auto baseMap = static_cast<BaseMap*>(map);
+    typedef HashMap<Key, Value, DefaultHasher<Key>, RuntimeAllocPolicy> UnbarrieredMap;
+    typedef gc::HashKeyRef<UnbarrieredMap, Key> UnbarrieredKeyRef;
+    return UnbarrieredKeyRef(reinterpret_cast<UnbarrieredMap*>(baseMap), key);
+}
+
 /* WeakMap methods exposed so they can be installed in the self-hosting global. */
 
 extern JSObject *
 InitBareWeakMapCtor(JSContext *cx, js::HandleObject obj);
 
 extern bool
 WeakMap_has(JSContext *cx, unsigned argc, Value *vp);
 
--- a/js/src/tests/js1_8_5/extensions/findReferences-01.js
+++ b/js/src/tests/js1_8_5/extensions/findReferences-01.js
@@ -17,32 +17,32 @@ if (typeof findReferences == "function")
     assertEq(referencesVia(o, 'objectElements[42]', o[42]), true);
     assertEq(referencesVia(o, '123456789', o[123456789]), true);
     assertEq(referencesVia(o, 'myself', o), true);
     assertEq(referencesVia(o, 'alsoMyself', o), true);
 
     function g() { return 42; }
     function s(v) { }
     var p = Object.defineProperty({}, 'a', { get:g, set:s });
-    assertEq(referencesVia(p, 'shape; base; getter', g), true);
-    assertEq(referencesVia(p, 'shape; base; setter', s), true);
+    assertEq(referencesVia(p, 'shape; getter', g), true);
+    assertEq(referencesVia(p, 'shape; setter', s), true);
 
     // If there are multiple objects with the same shape referring to a getter
     // or setter, findReferences should get all of them, even though the shape
     // gets 'marked' the first time we visit it.
     var q = Object.defineProperty({}, 'a', { get:g, set:s });
-    assertEq(referencesVia(p, 'shape; base; getter', g), true);
-    assertEq(referencesVia(q, 'shape; base; getter', g), true);
+    assertEq(referencesVia(p, 'shape; getter', g), true);
+    assertEq(referencesVia(q, 'shape; getter', g), true);
 
     // If we extend each object's shape chain, both should still be able to
     // reach the getter, even though the two shapes are each traversed twice.
     p.b = 9;
     q.b = 9;
-    assertEq(referencesVia(p, 'shape; parent; base; getter', g), true);
-    assertEq(referencesVia(q, 'shape; parent; base; getter', g), true);
+    assertEq(referencesVia(p, 'shape; parent; getter', g), true);
+    assertEq(referencesVia(q, 'shape; parent; getter', g), true);
 
     // These are really just ordinary own property references.
     assertEq(referencesVia(C, 'prototype', Object.getPrototypeOf(o)), true);
     assertEq(referencesVia(Object.getPrototypeOf(o), 'constructor', C), true);
 
     // Dense arrays should work, too.
     a = [];
     a[1] = o;
--- a/js/src/vm/GlobalObject.cpp
+++ b/js/src/vm/GlobalObject.cpp
@@ -235,16 +235,21 @@ GlobalObject::create(JSContext *cx, cons
     MOZ_ASSERT(clasp->trace == JS_GlobalObjectTraceHook);
 
     JSObject *obj = NewObjectWithGivenProto(cx, clasp, nullptr, nullptr, SingletonObject);
     if (!obj)
         return nullptr;
 
     Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>());
 
+    // Initialize the private slot to null if present, as GC can call class
+    // hooks before the caller gets to set this to a non-garbage value.
+    if (clasp->flags & JSCLASS_HAS_PRIVATE)
+        global->setPrivate(nullptr);
+
     cx->compartment()->initGlobal(*global);
 
     if (!global->setQualifiedVarObj(cx))
         return nullptr;
     if (!global->setUnqualifiedVarObj(cx))
         return nullptr;
     if (!global->setDelegate(cx))
         return nullptr;
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -184,18 +184,18 @@ static const JSClass parseTaskGlobalClas
     JS_GlobalObjectTraceHook
 };
 
 ParseTask::ParseTask(ExclusiveContext *cx, JSObject *exclusiveContextGlobal, JSContext *initCx,
                      const char16_t *chars, size_t length,
                      JS::OffThreadCompileCallback callback, void *callbackData)
   : cx(cx), options(initCx), chars(chars), length(length),
     alloc(JSRuntime::TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE),
-    exclusiveContextGlobal(initCx, exclusiveContextGlobal), optionsElement(initCx),
-    optionsIntroductionScript(initCx), callback(callback), callbackData(callbackData),
+    exclusiveContextGlobal(initCx, exclusiveContextGlobal),
+    callback(callback), callbackData(callbackData),
     script(nullptr), errors(cx), overRecursed(false)
 {
 }
 
 bool
 ParseTask::init(JSContext *cx, const ReadOnlyCompileOptions &options)
 {
     return this->options.copy(cx, options);
@@ -756,16 +756,17 @@ js::GCParallelTask::joinWithLockHeld()
     MOZ_ASSERT(HelperThreadState().isLocked());
 
     if (state == NotStarted)
         return;
 
     while (state != Finished)
         HelperThreadState().wait(GlobalHelperThreadState::CONSUMER);
     state = NotStarted;
+    cancel_ = false;
 }
 
 void
 js::GCParallelTask::join()
 {
     AutoLockHelperThreadState helperLock;
     joinWithLockHeld();
 }
@@ -791,16 +792,23 @@ js::GCParallelTask::runFromHelperThread(
         run();
         duration_ = PRMJ_Now() - timeStart;
     }
 
     state = Finished;
     HelperThreadState().notifyAll(GlobalHelperThreadState::CONSUMER);
 }
 
+bool
+js::GCParallelTask::isRunning() const
+{
+    MOZ_ASSERT(HelperThreadState().isLocked());
+    return state == Dispatched;
+}
+
 void
 HelperThread::handleGCParallelWorkload()
 {
     MOZ_ASSERT(HelperThreadState().isLocked());
     MOZ_ASSERT(HelperThreadState().canStartGCParallelTask());
     MOZ_ASSERT(idle());
 
     MOZ_ASSERT(!gcParallelTask);
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -452,24 +452,16 @@ struct ParseTask
     OwningCompileOptions options;
     const char16_t *chars;
     size_t length;
     LifoAlloc alloc;
 
     // Rooted pointer to the global object used by 'cx'.
     PersistentRootedObject exclusiveContextGlobal;
 
-    // Saved GC-managed CompileOptions fields that will populate slots in
-    // the ScriptSourceObject. We create the ScriptSourceObject in the
-    // compilation's temporary compartment, so storing these values there
-    // at that point would create cross-compartment references. Instead we
-    // hold them here, and install them after merging the compartments.
-    PersistentRootedObject optionsElement;
-    PersistentRootedScript optionsIntroductionScript;
-
     // Callback invoked off the main thread when the parse finishes.
     JS::OffThreadCompileCallback callback;
     void *callbackData;
 
     // Holds the final script between the invocation of the callback and the
     // point where FinishOffThreadScript is called, which will destroy the
     // ParseTask.
     JSScript *script;
--- a/js/src/vm/Interpreter-inl.h
+++ b/js/src/vm/Interpreter-inl.h
@@ -565,29 +565,29 @@ static MOZ_ALWAYS_INLINE bool
 InitArrayElemOperation(JSContext *cx, jsbytecode *pc, HandleObject obj, uint32_t index, HandleValue val)
 {
     JSOp op = JSOp(*pc);
     MOZ_ASSERT(op == JSOP_INITELEM_ARRAY || op == JSOP_INITELEM_INC);
 
     MOZ_ASSERT(obj->is<ArrayObject>());
 
     /*
-     * If val is a hole, do not call JSObject::defineElement. In this case,
-     * if the current op is the last element initialiser, set the array length
-     * to one greater than id.
+     * If val is a hole, do not call JSObject::defineElement.
      *
-     * If val is a hole and current op is JSOP_INITELEM_INC, always call
+     * Furthermore, if the current op is JSOP_INITELEM_INC, always call
      * SetLengthProperty even if it is not the last element initialiser,
      * because it may be followed by JSOP_SPREAD, which will not set the array
-     * length if nothing is spreaded.
+     * length if nothing is spread.
+     *
+     * Alternatively, if the current op is JSOP_INITELEM_ARRAY, the length will
+     * have already been set by the earlier JSOP_NEWARRAY; JSOP_INITELEM_ARRAY
+     * cannot follow JSOP_SPREAD.
      */
     if (val.isMagic(JS_ELEMENTS_HOLE)) {
-        JSOp next = JSOp(*GetNextPc(pc));
-
-        if ((op == JSOP_INITELEM_ARRAY && next == JSOP_ENDINIT) || op == JSOP_INITELEM_INC) {
+        if (op == JSOP_INITELEM_INC) {
             if (!SetLengthProperty(cx, obj, index + 1))
                 return false;
         }
     } else {
         if (!JSObject::defineElement(cx, obj, index, val, nullptr, nullptr, JSPROP_ENUMERATE))
             return false;
     }
 
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -1606,16 +1606,17 @@ CASE(JSOP_UNUSED46)
 CASE(JSOP_UNUSED47)
 CASE(JSOP_UNUSED48)
 CASE(JSOP_UNUSED49)
 CASE(JSOP_UNUSED50)
 CASE(JSOP_UNUSED51)
 CASE(JSOP_UNUSED52)
 CASE(JSOP_UNUSED57)
 CASE(JSOP_UNUSED83)
+CASE(JSOP_UNUSED92)
 CASE(JSOP_UNUSED103)
 CASE(JSOP_UNUSED104)
 CASE(JSOP_UNUSED105)
 CASE(JSOP_UNUSED107)
 CASE(JSOP_UNUSED124)
 CASE(JSOP_UNUSED125)
 CASE(JSOP_UNUSED126)
 CASE(JSOP_UNUSED146)
@@ -3115,24 +3116,16 @@ CASE(JSOP_NEWOBJECT)
     obj = CopyInitializerObject(cx, baseobj, newKind);
     if (!obj || !SetInitializerObjectType(cx, script, REGS.pc, obj, newKind))
         goto error;
 
     PUSH_OBJECT(*obj);
 }
 END_CASE(JSOP_NEWOBJECT)
 
-CASE(JSOP_ENDINIT)
-{
-    /* FIXME remove JSOP_ENDINIT bug 588522 */
-    MOZ_ASSERT(REGS.stackDepth() >= 1);
-    MOZ_ASSERT(REGS.sp[-1].isObject() || REGS.sp[-1].isUndefined());
-}
-END_CASE(JSOP_ENDINIT)
-
 CASE(JSOP_MUTATEPROTO)
 {
     MOZ_ASSERT(REGS.stackDepth() >= 2);
 
     if (REGS.sp[-1].isObjectOrNull()) {
         RootedObject &newProto = rootObject1;
         rootObject1 = REGS.sp[-1].toObjectOrNull();
 
--- a/js/src/vm/Opcodes.h
+++ b/js/src/vm/Opcodes.h
@@ -765,27 +765,19 @@ 1234567890123456789012345678901234567890
      * This opcode takes an object with the final shape, which can be set at
      * the start and slots then filled in directly.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t baseobjIndex
      *   Stack: => obj
      */ \
     macro(JSOP_NEWOBJECT, 91, "newobject",  NULL,         5,  0,  1, JOF_OBJECT) \
-    /*
-     * A no-operation bytecode.
-     *
-     * Indicates the end of object/array initialization, and used for
-     * Type-Inference, decompile, etc.
-     *   Category: Literals
-     *   Type: Object
-     *   Operands:
-     *   Stack: =>
-     */ \
-    macro(JSOP_ENDINIT,   92, "endinit",    NULL,         1,  0,  0, JOF_BYTE) \
+    \
+    macro(JSOP_UNUSED92,  92, "unused92",   NULL,         1,  0,  0, JOF_BYTE) \
+    \
     /*
      * Initialize a named property in an object literal, like '{a: x}'.
      *
      * Pops the top two values on the stack as 'val' and 'obj', defines
      * 'nameIndex' property of 'obj' as 'val', pushes 'obj' onto the stack.
      *   Category: Literals
      *   Type: Object
      *   Operands: uint32_t nameIndex
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1841,33 +1841,18 @@ js_IsDebugScopeSlow(ProxyObject *proxy)
 
 /*****************************************************************************/
 
 /* static */ MOZ_ALWAYS_INLINE void
 DebugScopes::proxiedScopesPostWriteBarrier(JSRuntime *rt, ObjectWeakMap *map,
                                            const PreBarrieredObject &key)
 {
 #ifdef JSGC_GENERATIONAL
-    /*
-     * Strip the barriers from the type before inserting into the store buffer.
-     * This will automatically ensure that barriers do not fire during GC.
-     *
-     * Some compilers complain about instantiating the WeakMap class for
-     * unbarriered type arguments, so we cast to a HashMap instead.  Because of
-     * WeakMap's multiple inheritace, We need to do this in two stages, first to
-     * the HashMap base class and then to the unbarriered version.
-     */
-    ObjectWeakMap::Base *baseHashMap = static_cast<ObjectWeakMap::Base *>(map);
-
-    typedef HashMap<JSObject *, JSObject *> UnbarrieredMap;
-    UnbarrieredMap *unbarrieredMap = reinterpret_cast<UnbarrieredMap *>(baseHashMap);
-
-    typedef gc::HashKeyRef<UnbarrieredMap, JSObject *> Ref;
     if (key && IsInsideNursery(key))
-        rt->gc.storeBuffer.putGeneric(Ref(unbarrieredMap, key.get()));
+        rt->gc.storeBuffer.putGeneric(UnbarrieredRef(map, key.get()));
 #endif
 }
 
 #ifdef JSGC_GENERATIONAL
 class DebugScopes::MissingScopesRef : public gc::BufferableRef
 {
     MissingScopeMap *map;
     ScopeIterKey key;
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -23,17 +23,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 186);
+static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 187);
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext *cx)
       : context(cx), base(nullptr), cursor(nullptr), limit(nullptr) { }
 
     JSContext *cx() const {
         return context;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -6087,16 +6087,22 @@ nsLayoutUtils::SurfaceFromElementResult
 nsLayoutUtils::SurfaceFromElement(HTMLVideoElement* aElement,
                                   uint32_t aSurfaceFlags,
                                   DrawTarget* aTarget)
 {
   SurfaceFromElementResult result;
 
   NS_WARN_IF_FALSE((aSurfaceFlags & SFE_PREFER_NO_PREMULTIPLY_ALPHA) == 0, "We can't support non-premultiplied alpha for video!");
 
+#ifdef MOZ_EME
+  if (aElement->ContainsRestrictedContent()) {
+    return result;
+  }
+#endif
+
   uint16_t readyState;
   if (NS_SUCCEEDED(aElement->GetReadyState(&readyState)) &&
       (readyState == nsIDOMHTMLMediaElement::HAVE_NOTHING ||
        readyState == nsIDOMHTMLMediaElement::HAVE_METADATA)) {
     result.mIsStillLoading = true;
     return result;
   }
 
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -2458,16 +2458,32 @@ MaxZIndexInList(nsDisplayList* aList, ns
 {
   int32_t maxZIndex = 0;
   for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
     maxZIndex = std::max(maxZIndex, item->ZIndex());
   }
   return maxZIndex;
 }
 
+// Finds the max z-index of the items in aList that meet the following conditions
+//   1) have z-index auto or z-index >= 0.
+//   2) aFrame is a proper ancestor of the item's frame.
+// Returns -1 if there is no such item.
+static int32_t
+MaxZIndexInListOfItemsContainedInFrame(nsDisplayList* aList, nsIFrame* aFrame)
+{
+  int32_t maxZIndex = -1;
+  for (nsDisplayItem* item = aList->GetBottom(); item; item = item->GetAbove()) {
+    if (nsLayoutUtils::IsProperAncestorFrame(aFrame, item->Frame())) {
+      maxZIndex = std::max(maxZIndex, item->ZIndex());
+    }
+  }
+  return maxZIndex;
+}
+
 static void
 AppendToTop(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists,
             nsDisplayList* aSource, nsIFrame* aSourceFrame, bool aOwnLayer,
             bool aPositioned)
 {
   if (aSource->IsEmpty())
     return;
 
@@ -2992,28 +3008,35 @@ ScrollFrameHelper::BuildDisplayList(nsDi
       wrapper.WrapListsInPlace(aBuilder, mOuter, scrolledContent);
     }
 
     // In case we are not using displayport or the nsDisplayScrollLayers are
     // flattened during visibility computation, we still need to export the
     // metadata about this scroll box to the compositor process.
     nsDisplayScrollInfoLayer* layerItem = new (aBuilder) nsDisplayScrollInfoLayer(
       aBuilder, mScrolledFrame, mOuter);
+    nsDisplayList* positionedDescendants = scrolledContent.PositionedDescendants();
     if (BuildScrollContainerLayers()) {
       // We process display items from bottom to top, so if we need to flatten after
       // the scroll layer items have been processed we need to be on the top.
-      nsDisplayList* positionedDescendants = scrolledContent.PositionedDescendants();
       if (!positionedDescendants->IsEmpty()) {
         layerItem->SetOverrideZIndex(MaxZIndexInList(positionedDescendants, aBuilder));
         positionedDescendants->AppendNewToTop(layerItem);
       } else {
         aLists.Outlines()->AppendNewToTop(layerItem);
       }
     } else {
-      scrolledContent.BorderBackground()->AppendNewToBottom(layerItem);
+      int32_t zindex =
+        MaxZIndexInListOfItemsContainedInFrame(positionedDescendants, mOuter);
+      if (zindex >= 0) {
+        layerItem->SetOverrideZIndex(zindex);
+        positionedDescendants->AppendNewToTop(layerItem);
+      } else {
+        scrolledContent.Outlines()->AppendNewToTop(layerItem);
+      }
     }
   }
   // Now display overlay scrollbars and the resizer, if we have one.
   AppendScrollPartsTo(aBuilder, aDirtyRect, scrolledContent, usingDisplayport,
                       createLayersForScrollbars, true);
   scrolledContent.MoveTo(aLists);
 }
 
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
@@ -74,18 +74,17 @@ private:
 
   GMPDecryptorCallback* mCallback;
   GMPThread* mThread;
 
   Key mKey;
 };
 
 
-ClearKeyDecryptionManager::ClearKeyDecryptionManager(GMPDecryptorHost* aHost)
-  : mHost(aHost)
+ClearKeyDecryptionManager::ClearKeyDecryptionManager()
 {
   CK_LOGD("ClearKeyDecryptionManager ctor");
 }
 
 ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
 {
   CK_LOGD("ClearKeyDecryptionManager dtor");
 }
@@ -127,17 +126,17 @@ ClearKeyDecryptionManager::CreateSession
     mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
                              nullptr /* message */, 0 /* messageLen */);
     return;
   }
 
   string sessionId = GetNewSessionId();
   assert(mSessions.find(sessionId) == mSessions.end());
 
-  ClearKeySession* session = new ClearKeySession(sessionId, mHost, mCallback);
+  ClearKeySession* session = new ClearKeySession(sessionId, mCallback);
   session->Init(aPromiseId, aInitData, aInitDataSize);
   mSessions[sessionId] = session;
 
   const vector<KeyId>& sessionKeys = session->GetKeyIds();
   vector<KeyId> neededKeys;
   for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) {
     if (mDecryptors.find(*it) == mDecryptors.end()) {
       // Need to request this key ID from the client.
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
@@ -14,17 +14,17 @@
 #include "gmp-api/gmp-decryption.h"
 #include "ScopedNSSTypes.h"
 
 class ClearKeyDecryptor;
 
 class ClearKeyDecryptionManager MOZ_FINAL : public GMPDecryptor
 {
 public:
-  explicit ClearKeyDecryptionManager(GMPDecryptorHost* aHost);
+  ClearKeyDecryptionManager();
   ~ClearKeyDecryptionManager();
 
   virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE;
 
   virtual void CreateSession(uint32_t aPromiseId,
                              const char* aInitDataType,
                              uint32_t aInitDataTypeSize,
                              const uint8_t* aInitData,
@@ -55,15 +55,14 @@ public:
 
   virtual void Decrypt(GMPBuffer* aBuffer,
                        GMPEncryptedBufferMetadata* aMetadata) MOZ_OVERRIDE;
 
   virtual void DecryptingComplete() MOZ_OVERRIDE;
 
 private:
   GMPDecryptorCallback* mCallback;
-  GMPDecryptorHost* mHost;
 
   std::map<KeyId, ClearKeyDecryptor*> mDecryptors;
   std::map<std::string, ClearKeySession*> mSessions;
 };
 
 #endif // __ClearKeyDecryptor_h__
--- a/media/gmp-clearkey/0.1/ClearKeySession.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp
@@ -7,20 +7,18 @@
 
 #include "gmp-api/gmp-decryption.h"
 #include "mozilla/Endian.h"
 #include "pk11pub.h"
 
 using namespace mozilla;
 
 ClearKeySession::ClearKeySession(const std::string& aSessionId,
-                                 GMPDecryptorHost* aHost,
                                  GMPDecryptorCallback* aCallback)
   : mSessionId(aSessionId)
-  , mHost(aHost)
   , mCallback(aCallback)
 {
   CK_LOGD("ClearKeySession ctor %p", this);
 }
 
 ClearKeySession::~ClearKeySession()
 {
   CK_LOGD("ClearKeySession dtor %p", this);
--- a/media/gmp-clearkey/0.1/ClearKeySession.h
+++ b/media/gmp-clearkey/0.1/ClearKeySession.h
@@ -16,25 +16,24 @@ class GMPEncryptedBufferMetadata;
  * Currently useless; will be fleshed out later with support for persistent
  * key sessions.
  */
 
 class ClearKeySession
 {
 public:
   ClearKeySession(const std::string& aSessionId,
-                  GMPDecryptorHost* aHost, GMPDecryptorCallback *aCallback);
+                  GMPDecryptorCallback* aCallback);
 
   ~ClearKeySession();
 
   const std::vector<KeyId>& GetKeyIds() { return mKeyIds; }
 
   void Init(uint32_t aPromiseId,
             const uint8_t* aInitData, uint32_t aInitDataSize);
 private:
   std::string mSessionId;
   std::vector<KeyId> mKeyIds;
 
   GMPDecryptorCallback* mCallback;
-  GMPDecryptorHost* mHost;
 };
 
 #endif // __ClearKeySession_h__
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
@@ -5,16 +5,18 @@
 #include <algorithm>
 #include <assert.h>
 #include <ctype.h>
 #include <stdarg.h>
 #include <stdint.h>
 #include <vector>
 
 #include "ClearKeyUtils.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Assertions.h"
 #include "mozilla/Endian.h"
 #include "mozilla/NullPtr.h"
 #include "openaes/oaes_lib.h"
 
 using namespace std;
 
 #define FOURCC(a,b,c,d) ((a << 24) + (b << 16) + (c << 8) + d)
 
@@ -90,28 +92,33 @@ EncodeBase64Web(vector<uint8_t> aBinary,
   // Pad binary data in case there's rubbish past the last byte.
   aBinary.push_back(0);
 
   // Number of bytes not consumed in the previous character
   uint32_t shift = 0;
 
   auto out = aEncoded.begin();
   auto data = aBinary.begin();
-  for (int i = 0; i < aEncoded.length(); i++) {
+  for (string::size_type i = 0; i < aEncoded.length(); i++) {
     if (shift) {
       out[i] = (*data << (6 - shift)) & sMask;
       data++;
     } else {
       out[i] = 0;
     }
 
     out[i] += (*data >> (shift + 2)) & sMask;
     shift = (shift + 2) % 8;
 
-    out[i] = sAlphabet[out[i]];
+    // Cast idx to size_t before using it as an array-index,
+    // to pacify clang 'Wchar-subscripts' warning:
+    size_t idx = static_cast<size_t>(out[i]);
+    MOZ_ASSERT(idx < MOZ_ARRAY_LENGTH(sAlphabet),
+               "out of bounds index for 'sAlphabet'");
+    out[i] = sAlphabet[idx];
   }
 
   return true;
 }
 
 /* static */ void
 ClearKeyUtils::ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize,
                              vector<KeyId>& aOutKeys)
--- a/media/gmp-clearkey/0.1/gmp-clearkey.cpp
+++ b/media/gmp-clearkey/0.1/gmp-clearkey.cpp
@@ -30,17 +30,17 @@ GMPInit(GMPPlatformAPI* aPlatformAPI)
 
 MOZ_EXPORT GMPErr
 GMPGetAPI(const char* aApiName, void* aHostAPI, void** aPluginAPI)
 {
   if (strcmp(aApiName, "eme-decrypt")) {
     return GMPNotImplementedErr;
   }
 
-  *aPluginAPI = new ClearKeyDecryptionManager(static_cast<GMPDecryptorHost*>(aHostAPI));
+  *aPluginAPI = new ClearKeyDecryptionManager();
 
   return GMPNoErr;
 }
 
 MOZ_EXPORT GMPErr
 GMPShutdown(void)
 {
   return GMPNoErr;
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/aec/aec_core.c
@@ -731,17 +731,17 @@ int WebRtcAec_GetDelayMetricsCore(AecCor
       break;
     }
   }
   // Account for lookahead.
   *median = (my_median - kLookaheadBlocks) * kMsPerBlock;
 
   // Calculate the L1 norm, with median value as central moment.
   for (i = 0; i < kHistorySizeBlocks; i++) {
-    l1_norm += (float)(fabs(i - my_median) * self->delay_histogram[i]);
+    l1_norm += (float)abs(i - my_median) * self->delay_histogram[i];
   }
   *std = (int)(l1_norm / (float)num_delay_values + 0.5f) * kMsPerBlock;
 
   // Reset histogram.
   memset(self->delay_histogram, 0, sizeof(self->delay_histogram));
 
   return 0;
 }
--- a/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.c
+++ b/media/webrtc/trunk/webrtc/modules/audio_processing/agc/digital_agc.c
@@ -284,17 +284,17 @@ int32_t WebRtcAgc_InitDigital(DigitalAgc
 
     return 0;
 }
 
 int32_t WebRtcAgc_AddFarendToDigital(DigitalAgc_t *stt, const int16_t *in_far,
                                      int16_t nrSamples)
 {
     // Check for valid pointer
-    if (&stt->vadFarend == NULL)
+    if (stt == NULL)
     {
         return -1;
     }
 
     // VAD for far end
     WebRtcAgc_ProcessVad(&stt->vadFarend, in_far, nrSamples);
 
     return 0;
--- a/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc
+++ b/media/webrtc/trunk/webrtc/modules/remote_bitrate_estimator/overuse_detector.cc
@@ -244,20 +244,20 @@ void OveruseDetector::UpdateKalman(int64
 
   const double h[2] = {fs_delta, 1.0};
   const double Eh[2] = {E_[0][0]*h[0] + E_[0][1]*h[1],
                         E_[1][0]*h[0] + E_[1][1]*h[1]};
 
   const double residual = t_ts_delta - slope_*h[0] - offset_;
 
   const bool stable_state =
-      (BWE_MIN(num_of_deltas_, 60) * fabsf(offset_) < threshold_);
+      (BWE_MIN(num_of_deltas_, 60) * fabs(offset_) < threshold_);
   // We try to filter out very late frames. For instance periodic key
   // frames doesn't fit the Gaussian model well.
-  if (fabsf(residual) < 3 * sqrt(var_noise_)) {
+  if (fabs(residual) < 3 * sqrt(var_noise_)) {
     UpdateNoiseEstimate(residual, min_frame_period, stable_state);
   } else {
     UpdateNoiseEstimate(3 * sqrt(var_noise_), min_frame_period, stable_state);
   }
 
   const double denom = var_noise_ + h[0]*Eh[0] + h[1]*Eh[1];
 
   const double K[2] = {Eh[0] / denom,
@@ -353,17 +353,17 @@ void OveruseDetector::UpdateNoiseEstimat
   }
 }
 
 BandwidthUsage OveruseDetector::Detect(double ts_delta) {
   if (num_of_deltas_ < 2) {
     return kBwNormal;
   }
   const double T = BWE_MIN(num_of_deltas_, 60) * offset_;
-  if (fabsf(T) > threshold_) {
+  if (fabs(T) > threshold_) {
     if (offset_ > 0) {
       if (time_over_using_ == -1) {
         // Initialize the timer. Assume that we've been
         // over-using half of the time since the previous
         // sample.
         time_over_using_ = ts_delta / 2;
       } else {
         // Increment timer
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/jitter_estimator.cc
@@ -157,17 +157,17 @@ VCMJitterEstimator::UpdateEstimate(int64
     _prevFrameSize = frameSizeBytes;
 
     // Only update the Kalman filter if the sample is not considered
     // an extreme outlier. Even if it is an extreme outlier from a
     // delay point of view, if the frame size also is large the
     // deviation is probably due to an incorrect line slope.
     double deviation = DeviationFromExpectedDelay(frameDelayMS, deltaFS);
 
-    if (abs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
+    if (fabs(deviation) < _numStdDevDelayOutlier * sqrt(_varNoise) ||
         frameSizeBytes > _avgFrameSize + _numStdDevFrameSizeOutlier * sqrt(_varFrameSize))
     {
         // Update the variance of the deviation from the
         // line given by the Kalman filter
         EstimateRandomJitter(deviation, incompleteFrame);
         // Prevent updating with frames which have been congested by a large
         // frame, and therefore arrives almost at the same time as that frame.
         // This can occur when we receive a large frame (key frame) which
@@ -252,17 +252,17 @@ VCMJitterEstimator::KalmanEstimateChanne
     Mh[0] = _thetaCov[0][0] * deltaFSBytes + _thetaCov[0][1];
     Mh[1] = _thetaCov[1][0] * deltaFSBytes + _thetaCov[1][1];
     // sigma weights measurements with a small deltaFS as noisy and
     // measurements with large deltaFS as good
     if (_maxFrameSize < 1.0)
     {
         return;
     }
-    double sigma = (300.0 * exp(-abs(static_cast<double>(deltaFSBytes)) /
+    double sigma = (300.0 * exp(-fabs(static_cast<double>(deltaFSBytes)) /
                    (1e0 * _maxFrameSize)) + 1) * sqrt(_varNoise);
     if (sigma < 1.0)
     {
         sigma = 1.0;
     }
     hMh_sigma = deltaFSBytes * Mh[0] + Mh[1] + sigma;
     if ((hMh_sigma < 1e-9 && hMh_sigma >= 0) || (hMh_sigma > -1e-9 && hMh_sigma <= 0))
     {
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/receiver.cc
@@ -154,22 +154,22 @@ VCMEncodedFrame* VCMReceiver::FrameForDe
   const int64_t now_ms = clock_->TimeInMilliseconds();
   timing_->UpdateCurrentDelay(frame_timestamp);
   next_render_time_ms = timing_->RenderTimeMs(frame_timestamp, now_ms);
   // Check render timing.
   bool timing_error = false;
   // Assume that render timing errors are due to changes in the video stream.
   if (next_render_time_ms < 0) {
     timing_error = true;
-  } else if (abs(next_render_time_ms - now_ms) > max_video_delay_ms_) {
+  } else if (std::abs(next_render_time_ms - now_ms) > max_video_delay_ms_) {
     WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding,
                  VCMId(vcm_id_, receiver_id_),
                  "This frame is out of our delay bounds, resetting jitter "
                  "buffer: %d > %d",
-                 static_cast<int>(abs(next_render_time_ms - now_ms)),
+                 static_cast<int>(std::abs(next_render_time_ms - now_ms)),
                  max_video_delay_ms_);
     timing_error = true;
   } else if (static_cast<int>(timing_->TargetVideoDelay()) >
              max_video_delay_ms_) {
     WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding,
                  VCMId(vcm_id_, receiver_id_),
                  "More than %u ms target delay. Flushing jitter buffer and"
                  "resetting timing.", max_video_delay_ms_);
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/rtt_filter.cc
@@ -109,17 +109,17 @@ VCMRttFilter::Update(uint32_t rttMs)
                "RttFilter Update: sample=%u avgRtt=%f varRtt=%f maxRtt=%u",
                rttMs, _avgRtt, _varRtt, _maxRtt);
 }
 
 bool
 VCMRttFilter::JumpDetection(uint32_t rttMs)
 {
     double diffFromAvg = _avgRtt - rttMs;
-    if (abs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt))
+    if (fabs(diffFromAvg) > _jumpStdDevs * sqrt(_varRtt))
     {
         int diffSign = (diffFromAvg >= 0) ? 1 : -1;
         int jumpCountSign = (_jumpCount >= 0) ? 1 : -1;
         if (diffSign != jumpCountSign)
         {
             // Since the signs differ the samples currently
             // in the buffer is useless as they represent a
             // jump in a different direction.
--- a/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender_unittest.cc
+++ b/media/webrtc/trunk/webrtc/modules/video_coding/main/source/video_sender_unittest.cc
@@ -47,17 +47,17 @@ enum {
 struct Vp8StreamInfo {
   float framerate_fps[kMaxNumberOfTemporalLayers];
   int bitrate_kbps[kMaxNumberOfTemporalLayers];
 };
 
 MATCHER_P(MatchesVp8StreamInfo, expected, "") {
   bool res = true;
   for (int tl = 0; tl < kMaxNumberOfTemporalLayers; ++tl) {
-    if (abs(expected.framerate_fps[tl] - arg.framerate_fps[tl]) > 0.5) {
+    if (fabs(expected.framerate_fps[tl] - arg.framerate_fps[tl]) > 0.5) {
       *result_listener << " framerate_fps[" << tl
                        << "] = " << arg.framerate_fps[tl] << " (expected "
                        << expected.framerate_fps[tl] << ") ";
       res = false;
     }
     if (abs(expected.bitrate_kbps[tl] - arg.bitrate_kbps[tl]) > 10) {
       *result_listener << " bitrate_kbps[" << tl
                        << "] = " << arg.bitrate_kbps[tl] << " (expected "
--- a/media/webrtc/trunk/webrtc/video/call_perf_tests.cc
+++ b/media/webrtc/trunk/webrtc/video/call_perf_tests.cc
@@ -179,17 +179,17 @@ class VideoRtcpAndSyncObserver : public 
     ss << stream_offset;
     webrtc::test::PrintResult(
         "stream_offset", "", "synchronization", ss.str(), "ms", false);
     int64_t time_since_creation = now_ms - creation_time_ms_;
     // During the first couple of seconds audio and video can falsely be
     // estimated as being synchronized. We don't want to trigger on those.
     if (time_since_creation < kStartupTimeMs)
       return;
-    if (abs(latest_audio_ntp - latest_video_ntp) < kInSyncThresholdMs) {
+    if (std::abs(latest_audio_ntp - latest_video_ntp) < kInSyncThresholdMs) {
       if (first_time_in_sync_ == -1) {
         first_time_in_sync_ = now_ms;
         webrtc::test::PrintResult("sync_convergence_time",
                                   "",
                                   "synchronization",
                                   time_since_creation,
                                   "ms",
                                   false);
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -98,16 +98,17 @@ import android.graphics.drawable.Drawabl
 import android.net.Uri;
 import android.nfc.NdefMessage;
 import android.nfc.NdefRecord;
 import android.nfc.NfcAdapter;
 import android.nfc.NfcEvent;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.StrictMode;
+import android.support.v4.app.DialogFragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.content.LocalBroadcastManager;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.InputDevice;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -147,16 +148,17 @@ public class BrowserApp extends GeckoApp
     private static final int TABS_ANIMATION_DURATION = 450;
 
     private static final String ADD_SHORTCUT_TOAST = "add_shortcut_toast";
     public static final String GUEST_BROWSING_ARG = "--guest";
 
     private static final String STATE_ABOUT_HOME_TOP_PADDING = "abouthome_top_padding";
 
     private static final String BROWSER_SEARCH_TAG = "browser_search";
+    private static final String ONBOARD_STARTPANE_TAG = "startpane_dialog";
 
     // Request ID for startActivityForResult.
     private static final int ACTIVITY_REQUEST_PREFERENCES = 1001;
 
     public static final String PREF_STARTPANE_ENABLED = "startpane_enabled";
 
     private BrowserSearch mBrowserSearch;
     private View mBrowserSearchContainer;
@@ -623,26 +625,26 @@ public class BrowserApp extends GeckoApp
     private void checkStartPane(Context context, String intentAction) {
         final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
 
         try {
             final SharedPreferences prefs = GeckoSharedPrefs.forProfile(this);
 
             if (prefs.getBoolean(PREF_STARTPANE_ENABLED, false)) {
                 if (!Intent.ACTION_VIEW.equals(intentAction)) {
-                    final Intent startIntent = new Intent(this, StartPane.class);
-                    context.startActivity(startIntent);
+                    final DialogFragment dialog = new StartPane();
+                    dialog.show(getSupportFragmentManager(), ONBOARD_STARTPANE_TAG);
                 }
                 // Don't bother trying again to show the v1 minimal first run.
                 prefs.edit().putBoolean(PREF_STARTPANE_ENABLED, false).apply();
             }
         } finally {
             StrictMode.setThreadPolicy(savedPolicy);
         }
-      }
+    }
 
     private Class<?> getMediaPlayerManager() {
         if (AppConstants.MOZ_MEDIA_PLAYER) {
             try {
                 return Class.forName("org.mozilla.gecko.MediaPlayerManager");
             } catch(Exception ex) {
                 // Ignore failures
                 Log.e(LOGTAG, "No native casting support", ex);
--- a/mobile/android/base/ReadingListHelper.java
+++ b/mobile/android/base/ReadingListHelper.java
@@ -22,23 +22,18 @@ import android.content.ContentValues;
 import android.content.Context;
 import android.net.Uri;
 import android.util.Log;
 import android.widget.Toast;
 
 public final class ReadingListHelper implements GeckoEventListener, NativeEventListener {
     private static final String LOGTAG = "ReadingListHelper";
 
-    private static final int READER_ADD_SUCCESS = 0;
-    private static final int READER_ADD_FAILED = 1;
-    private static final int READER_ADD_DUPLICATE = 2;
-
     protected final Context context;
 
-
     public ReadingListHelper(Context context) {
         this.context = context;
 
         EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener) this,
             "Reader:AddToList", "Reader:FaviconRequest");
         EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener) this,
             "Reader:ListStatusRequest", "Reader:RemoveFromList");
     }
@@ -81,26 +76,16 @@ public final class ReadingListHelper imp
         }
     }
 
     /**
      * A page can be added to the ReadingList by long-tap of the page-action
      * icon, or by tapping the readinglist-add icon in the ReaderMode banner.
      */
     private void handleAddToList(JSONObject message) {
-        final int result = message.optInt("result", READER_ADD_FAILED);
-        if (result != READER_ADD_SUCCESS) {
-            if (result == READER_ADD_FAILED) {
-                showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT);
-            } else if (result == READER_ADD_DUPLICATE) {
-                showToast(R.string.reading_list_duplicate, Toast.LENGTH_SHORT);
-            }
-            return;
-        }
-
         final ContentValues values = new ContentValues();
         final String url = message.optString("url");
 
         values.put(ReadingListItems.URL, url);
         values.put(ReadingListItems.TITLE, message.optString("title"));
         values.put(ReadingListItems.LENGTH, message.optInt("length"));
         values.put(ReadingListItems.EXCERPT, message.optString("excerpt"));
 
--- a/mobile/android/base/StartPane.java
+++ b/mobile/android/base/StartPane.java
@@ -1,77 +1,75 @@
 package org.mozilla.gecko;
 
 import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
-import org.mozilla.gecko.util.HardwareUtils;
 
-import android.app.Activity;
 import android.content.Intent;
 import android.os.Bundle;
+import android.support.v4.app.DialogFragment;
 import android.view.GestureDetector;
+import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
 import android.widget.Button;
 
-public class StartPane extends Activity {
+public class StartPane extends DialogFragment {
 
     @Override
-    public void onCreate(Bundle bundle) {
-        super.onCreate(bundle);
-        setContentView(R.layout.onboard_start_pane);
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setStyle(DialogFragment.STYLE_NO_TITLE, 0);
+    }
 
-        final Button accountButton = (Button) findViewById(R.id.button_account);
-        accountButton.setOnClickListener(new OnClickListener() {
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle) {
+        final View view = inflater.inflate(R.layout.onboard_start_pane, container, false);
+        final Button browserButton = (Button) view.findViewById(R.id.button_browser);
+        browserButton.setOnClickListener(new OnClickListener() {
 
             @Override
             public void onClick(View v) {
                 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "firstrun-sync");
-                showAccountSetup();
+
+                // StartPane is on the stack above the browser, so just dismiss this Fragment.
+                StartPane.this.dismiss();
             }
         });
 
-        final Button browserButton = (Button) findViewById(R.id.button_browser);
-        browserButton.setOnClickListener(new OnClickListener() {
+        final Button accountButton = (Button) view.findViewById(R.id.button_account);
+        accountButton.setOnClickListener(new OnClickListener() {
 
             @Override
             public void onClick(View v) {
                 Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "firstrun-browser");
-                showBrowser();
+
+                final Intent intent = new Intent(getActivity(), FxAccountGetStartedActivity.class);
+                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+                startActivity(intent);
+                StartPane.this.dismiss();
             }
         });
 
-        if (!HardwareUtils.isTablet() && !HardwareUtils.isTelevision()) {
-            addDismissHandler();
-        }
-    }
-
-    private void showBrowser() {
-        // StartPane is on the stack above the browser, so just kill this activity.
-        finish();
-    }
-
-    private void showAccountSetup() {
-        final Intent intent = new Intent(this, FxAccountGetStartedActivity.class);
-        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        startActivity(intent);
-        finish();
+        addDismissHandler(view);
+        return view;
     }
 
     // Add handler for dismissing the StartPane on a single click.
-    private void addDismissHandler() {
-        final GestureDetector gestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() {
+    private void addDismissHandler(View view) {
+        final GestureDetector gestureDetector = new GestureDetector(getActivity(), new GestureDetector.SimpleOnGestureListener() {
             @Override
             public boolean onSingleTapUp(MotionEvent e) {
-                StartPane.this.finish();
+                StartPane.this.dismiss();
                 return true;
             }
         });
 
-        findViewById(R.id.onboard_content).setOnTouchListener(new OnTouchListener() {
+        view.findViewById(R.id.onboard_content).setOnTouchListener(new OnTouchListener() {
             @Override
             public boolean onTouch(View v, MotionEvent event) {
                 return gestureDetector.onTouchEvent(event);
             }
         });
     }
 }
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -354,18 +354,16 @@ size. -->
 <!-- Localization note (site_settings_*) : These strings are used in the "Site Settings"
      dialog that appears after selecting the "Edit Site Settings" context menu item. -->
 <!ENTITY site_settings_title3       "Site Settings">
 <!ENTITY site_settings_cancel       "Cancel">
 <!ENTITY site_settings_clear        "Clear">
 <!ENTITY site_settings_no_settings  "There are no settings to clear.">
 
 <!ENTITY reading_list_added "Page added to your Reading List">
-<!ENTITY reading_list_failed "Failed to add page to your Reading List">
-<!ENTITY reading_list_duplicate "Page already in your Reading List">
 
 <!-- Localization note (reading_list_time_minutes) : This string is used in the "Reading List"
      panel on the home page to give the user an estimate of how many minutes it will take to
      read an article. The word "minute" should be abbreviated if possible. -->
 <!ENTITY reading_list_time_minutes "&formatD;min">
 <!ENTITY reading_list_time_over_an_hour "Over an hour">
 
 <!-- Localization note : These strings are used as alternate text for accessibility.
--- a/mobile/android/base/resources/drawable/edit_text_default.xml
+++ b/mobile/android/base/resources/drawable/edit_text_default.xml
@@ -5,18 +5,20 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
 
     <!-- Make sure the border only appears at the bottom of the background -->
     <item
         android:top="-2dp"
         android:right="-2dp"
         android:left="-2dp">
         <shape>
-            <!-- Padding creates vertical space between the text and the underline -->
+            <!-- Padding creates vertical space between the text and the underline,
+                 as well as right padding for search icon/clear button -->
             <padding
                 android:top="@dimen/search_bar_padding_y"
-                android:bottom="@dimen/search_bar_padding_y"/>
+                android:bottom="@dimen/search_bar_padding_y"
+                android:right="@dimen/search_bar_padding_right"/>
             <solid android:color="@android:color/transparent"/>
             <stroke android:width="1dp" android:color="@color/edit_text_default"/>
         </shape>
     </item>
 
 </layer-list>
--- a/mobile/android/base/resources/drawable/edit_text_focused.xml
+++ b/mobile/android/base/resources/drawable/edit_text_focused.xml
@@ -5,19 +5,21 @@
 <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
 
     <!-- Make sure the border only appears at the bottom of the background -->
     <item
         android:top="-3dp"
         android:right="-3dp"
         android:left="-3dp">
         <shape>
-            <!-- Padding creates vertical space between the text and the underline -->
+            <!-- Padding creates vertical space between the text and the underline,
+                 as well as right padding for search icon/clear button -->
             <padding
                 android:top="@dimen/search_bar_padding_y"
-                android:bottom="@dimen/search_bar_padding_y"/>
+                android:bottom="@dimen/search_bar_padding_y"
+                android:right="@dimen/search_bar_padding_right"/>
             <solid android:color="@android:color/transparent"/>
             <!-- We apply a color filter to set the color for the selected search engine -->
             <stroke android:width="2dp" android:color="@android:color/white"/>
         </shape>
     </item>
 
 </layer-list>
--- a/mobile/android/base/resources/layout/onboard_start_pane.xml
+++ b/mobile/android/base/resources/layout/onboard_start_pane.xml
@@ -1,37 +1,37 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
 
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              style="@style/OnboardStartLayout"
+              android:layout_height="match_parent"
+              android:layout_width="wrap_content"
               android:orientation="vertical"
-              android:background="@color/onboard_start"
-              android:windowIsFloating="true">
+              android:background="@color/onboard_start">
 
     <ScrollView android:id="@+id/onboard_content"
                 android:layout_width="match_parent"
                 android:layout_height="0dp"
                 android:layout_weight="1"
                 android:fillViewport="true" >
 
         <LinearLayout android:orientation="vertical"
                       android:layout_width="match_parent"
                       android:layout_height="wrap_content">
 
             <!-- Empty spacer view -->
             <View android:layout_width="0dp"
                   android:layout_height="0dp"
                   android:layout_weight="1"/>
 
-            <RelativeLayout android:layout_width="match_parent"
-                            android:layout_height="wrap_content"
+            <RelativeLayout android:layout_width="wrap_content"
+                            android:layout_height="match_parent"
                             android:paddingTop="15dp">
 
                 <ImageView android:id="@+id/image_shield"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:layout_alignParentTop="true"
                            android:layout_centerHorizontal="true"
                            android:paddingRight="92dp"
@@ -54,44 +54,45 @@
                            android:layout_below="@id/image_shield"
                            android:layout_marginTop="23dp"
                            android:src="@drawable/large_icon"
                            android:contentDescription="@string/onboard_empty_contentDescription"/>
 
                 <ImageView android:id="@+id/image_sync"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
+                           android:layout_centerHorizontal="true"
                            android:layout_below="@id/image_shield"
-                           android:layout_toLeftOf="@id/image_logo"
-                           android:layout_marginRight="30dp"
+                           android:paddingRight="200dp"
                            android:src="@drawable/onboard_start_sync"
                            android:contentDescription="@string/onboard_empty_contentDescription"/>
 
                 <ImageView android:id="@+id/image_addon"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
-                           android:layout_below="@id/image_private"
-                           android:layout_toRightOf="@id/image_logo"
-                           android:layout_marginLeft="30dp"
+                           android:layout_centerHorizontal="true"
+                           android:layout_below="@id/image_shield"
+                           android:paddingLeft="200dp"
                            android:src="@drawable/onboard_start_addon"
                            android:contentDescription="@string/onboard_empty_contentDescription"/>
 
                 <TextView android:id="@+id/text_message"
-                          android:layout_width="wrap_content"
+                          android:layout_width="match_parent"
                           android:layout_height="wrap_content"
                           android:layout_below="@id/image_logo"
                           android:layout_centerHorizontal="true"
                           android:layout_marginTop="30dp"
+                          android:gravity="center"
                           android:padding="10sp"
                           android:text="@string/onboard_start_message"
                           android:textAppearance="@style/OnboardStartTextAppearance"
                           android:textSize="23sp" />
 
                 <TextView android:layout_width="295dp"
-                          android:layout_height="wrap_content"
+                          android:layout_height="match_parent"
                           android:layout_gravity="center"
                           android:gravity="center"
                           android:layout_below="@id/text_message"
                           android:layout_centerHorizontal="true"
                           android:lineSpacingExtra="12sp"
                           android:padding="10sp"
                           android:text="@string/onboard_start_subtext"
                           android:textAppearance="@style/OnboardStartTextAppearance.Subtext" />
@@ -119,9 +120,9 @@
                 android:text="@string/onboard_start_button_account"/>
 
         <Button android:id="@+id/button_browser"
                 style="@style/Widget.Onboard.Start.Button"
                 android:layout_marginTop="3px"
                 android:text="@string/onboard_start_button_browser"/>
 
     </LinearLayout>
-</LinearLayout>
+</LinearLayout>
\ No newline at end of file
--- a/mobile/android/base/resources/values-large-v11/styles.xml
+++ b/mobile/android/base/resources/values-large-v11/styles.xml
@@ -163,18 +163,13 @@
     </style>
 
     <style name="TabsPanelItem.TextAppearance.Linkified.LearnMore">
         <item name="android:layout_height">match_parent</item>
         <item name="android:gravity">center</item>
         <item name="android:layout_gravity">center</item>
     </style>
 
-    <style name="OnboardStartLayout">
-        <item name="android:layout_height">wrap_content</item>
-        <item name="android:layout_width">400dp</item>
-    </style>
-
     <style name="TextAppearance.UrlBar.Title" parent="TextAppearance.Medium">
         <item name="android:textSize">16sp</item>
     </style>
 
 </resources>
--- a/mobile/android/base/resources/values/search_dimens.xml
+++ b/mobile/android/base/resources/values/search_dimens.xml
@@ -9,16 +9,19 @@
     <dimen name="progress_bar_height">3dp</dimen>
 
     <!-- Size of the text for query input and suggestions -->
     <dimen name="query_text_size">16sp</dimen>
 
     <dimen name="search_row_padding">15dp</dimen>
     <dimen name="search_bar_padding_y">10dp</dimen>
 
+    <!-- Padding to account for search engine icon/clear button -->
+    <dimen name="search_bar_padding_right">25dp</dimen>
+
     <dimen name="search_history_drawable_padding">10dp</dimen>
 
     <!-- Widget Buttons -->
     <dimen name="widget_header_height">70dp</dimen>
     <dimen name="widget_button_offset">-50dp</dimen>
     <dimen name="widget_button_padding">45dp</dimen>
     <dimen name="widget_text_size">14sp</dimen>
     <dimen name="widget_padding">7dp</dimen>
--- a/mobile/android/base/resources/values/styles.xml
+++ b/mobile/android/base/resources/values/styles.xml
@@ -830,20 +830,16 @@
 
     <!-- Make the share overlay activity appear like an overlay. -->
     <style name="ShareOverlayActivity">
         <item name="android:windowBackground">@android:color/transparent</item>
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowIsTranslucent">true</item>
         <item name="android:backgroundDimEnabled">true</item>
     </style>
-    <style name="OnboardStartLayout">
-        <item name="android:layout_width">match_parent</item>
-        <item name="android:layout_height">match_parent</item>
-    </style>
 
     <style name="OnboardStartTextAppearance">
         <item name="android:textColor">#5F636B</item>
     </style>
 
     <style name="OnboardStartTextAppearance.Subtext">
         <item name="android:textSize">18sp</item>
     </style>
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -289,18 +289,16 @@
   <string name="edit_mode_cancel">&edit_mode_cancel;</string>
 
   <string name="site_settings_title">&site_settings_title3;</string>
   <string name="site_settings_cancel">&site_settings_cancel;</string>
   <string name="site_settings_clear">&site_settings_clear;</string>
   <string name="site_settings_no_settings">&site_settings_no_settings;</string>
 
   <string name="reading_list_added">&reading_list_added;</string>
-  <string name="reading_list_failed">&reading_list_failed;</string>
-  <string name="reading_list_duplicate">&reading_list_duplicate;</string>
   <string name="reading_list_time_minutes">&reading_list_time_minutes;</string>
   <string name="reading_list_time_over_an_hour">&reading_list_time_over_an_hour;</string>
 
   <string name="page_action_dropmarker_description">&page_action_dropmarker_description;</string>
 
   <string name="contextmenu_open_new_tab">&contextmenu_open_new_tab;</string>
   <string name="contextmenu_open_private_tab">&contextmenu_open_private_tab;</string>
   <string name="contextmenu_remove">&contextmenu_remove;</string>
--- a/mobile/android/chrome/content/Reader.js
+++ b/mobile/android/chrome/content/Reader.js
@@ -5,20 +5,16 @@
 "use strict";
 
 let Reader = {
   // Version of the cache database schema
   DB_VERSION: 1,
 
   DEBUG: 0,
 
-  READER_ADD_SUCCESS: 0,
-  READER_ADD_FAILED: 1,
-  READER_ADD_DUPLICATE: 2,
-
   // Don't try to parse the page if it has too many elements (for memory and
   // performance reasons)
   MAX_ELEMS_TO_PARSE: 3000,
 
   _requests: {},
 
   get isEnabledForParseOnLoad() {
     delete this.isEnabledForParseOnLoad;
@@ -33,17 +29,17 @@ let Reader = {
     readerModeCallback: function(tabID) {
       Messaging.sendRequest({
         type: "Reader:Toggle",
         tabID: tabID
       });
     },
 
     readerModeActiveCallback: function(tabID) {
-      Reader.addTabToReadingList(tabID);
+      Reader._addTabToReadingList(tabID);
       UITelemetry.addEvent("save.1", "pageaction", null, "reader");
     },
   },
 
   updatePageAction: function(tab) {
     if (this.pageAction.id) {
       PageActions.remove(this.pageAction.id);
       delete this.pageAction.id;
@@ -88,60 +84,59 @@ let Reader = {
         if (aData.startsWith("reader.parse-on-load.")) {
           this.isEnabledForParseOnLoad = this.getStateForParseOnLoad();
         }
         break;
       }
     }
   },
 
-  addTabToReadingList: function(tabID) {
+  _addTabToReadingList: function(tabID) {
     let tab = BrowserApp.getTabForId(tabID);
     let currentURI = tab.browser.currentURI;
-    let url = currentURI.spec;
     let urlWithoutRef = currentURI.specIgnoringRef;
 
-    let sendResult = function(result, article) {
-      article = article || {};
-
-      Messaging.sendRequest({
-        type: "Reader:AddToList",
-        result: result,
-        title: truncate(article.title, MAX_TITLE_LENGTH),
-        url: truncate(url, MAX_URI_LENGTH),
-        length: article.length,
-        excerpt: article.excerpt
-      });
-    }.bind(this);
-
-    let handleArticle = function(article) {
-      if (!article) {
-        sendResult(this.READER_ADD_FAILED, null);
+    this.getArticleFromCache(urlWithoutRef, (article) => {
+      // If the article is already in the cache, just use that.
+      if (article) {
+        this.addArticleToReadingList(article);
         return;
       }
 
-      this.storeArticleInCache(article, function(success) {
-        let result = (success ? this.READER_ADD_SUCCESS : this.READER_ADD_FAILED);
-        sendResult(result, article);
-      }.bind(this));
-    }.bind(this);
+      // Otherwise, get the article data from the tab.
+      this.getArticleForTab(tabID, urlWithoutRef, (article) => {
+        if (article) {
+          this.addArticleToReadingList(article);
+        } else {
+          // If there was a problem getting the article, just store the
+          // URL and title from the tab.
+          this.addArticleToReadingList({
+            url: urlWithoutRef,
+            title: tab.browser.contentDocument.title,
+          });
+        }
+      });
+    });
+  },
 
-    this.getArticleFromCache(urlWithoutRef, function (article) {
-      // If the article is already in reading list, bail
-      if (article) {
-        sendResult(this.READER_ADD_DUPLICATE, null);
-        return;
-      }
+  addArticleToReadingList: function(article) {
+    if (!article || !article.url) {
+      Cu.reportError("addArticleToReadingList requires article with valid URL");
+      return;
+    }
 
-      if (tabID != null) {
-        this.getArticleForTab(tabID, urlWithoutRef, handleArticle);
-      } else {
-        this.parseDocumentFromURL(urlWithoutRef, handleArticle);
-      }
-    }.bind(this));
+    Messaging.sendRequest({
+      type: "Reader:AddToList",
+      url: truncate(article.url, MAX_URI_LENGTH),
+      title: truncate(article.title || "", MAX_TITLE_LENGTH),
+      length: article.length || 0,
+      excerpt: article.excerpt || "",
+    });
+
+    this.storeArticleInCache(article);
   },
 
   getStateForParseOnLoad: function Reader_getStateForParseOnLoad() {
     let isEnabled = Services.prefs.getBoolPref("reader.parse-on-load.enabled");
     let isForceEnabled = Services.prefs.getBoolPref("reader.parse-on-load.force-enabled");
     // For low-memory devices, don't allow reader mode since it takes up a lot of memory.
     // See https://bugzilla.mozilla.org/show_bug.cgi?id=792603 for details.
     return isForceEnabled || (isEnabled && !BrowserApp.isOnLowMemoryPlatform);
@@ -256,36 +251,33 @@ let Reader = {
 
       request.onsuccess = function(event) {
         this.log("Got article from the cache DB: " + event.target.result);
         callback(event.target.result);
       }.bind(this);
     }.bind(this));
   },
 
-  storeArticleInCache: function Reader_storeArticleInCache(article, callback) {
+  storeArticleInCache: function Reader_storeArticleInCache(article) {
     this._getCacheDB(function(cacheDB) {
       if (!cacheDB) {
-        callback(false);
         return;
       }
 
       let transaction = cacheDB.transaction(cacheDB.objectStoreNames, "readwrite");
       let articles = transaction.objectStore(cacheDB.objectStoreNames[0]);
 
       let request = articles.add(article);
 
       request.onerror = function(event) {
         this.log("Error storing article in the cache DB: " + article.url);
-        callback(false);
       }.bind(this);
 
       request.onsuccess = function(event) {
         this.log("Stored article in the cache DB: " + article.url);
-        callback(true);
       }.bind(this);
     }.bind(this));
   },
 
   removeArticleFromCache: function Reader_removeArticleFromCache(url) {
     this._getCacheDB(function(cacheDB) {
       if (!cacheDB) {
         return;
--- a/mobile/android/chrome/content/WebcompatReporter.js
+++ b/mobile/android/chrome/content/WebcompatReporter.js
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
+Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 var WebcompatReporter = {
   menuItem: null,
   menuItemEnabled: null,
   init: function() {
     Services.obs.addObserver(this, "DesktopMode:Change", false);
@@ -73,15 +74,19 @@ var WebcompatReporter = {
     };
     NativeWindow.toast.show(message, "long", options);
   },
 
   reportIssue: function(url) {
     let webcompatURL = new URL("http://webcompat.com/");
     webcompatURL.searchParams.append("open", "1");
     webcompatURL.searchParams.append("url", url);
-    BrowserApp.addTab(webcompatURL.href);
+    if (PrivateBrowsingUtils.isBrowserPrivate(BrowserApp.selectedTab.browser)) {
+      BrowserApp.addTab(webcompatURL.href, {parentId: BrowserApp.selectedTab.id, isPrivate: true});
+    } else {
+      BrowserApp.addTab(webcompatURL.href);
+    }
   }
 };
 
 XPCOMUtils.defineLazyGetter(WebcompatReporter, "strings", function() {
   return Services.strings.createBundle("chrome://browser/locale/webcompatReporter.properties");
 });
--- a/mobile/android/chrome/content/aboutReader.js
+++ b/mobile/android/chrome/content/aboutReader.js
@@ -315,35 +315,19 @@ AboutReader.prototype = {
     });
   },
 
   _onReaderToggle: function Reader_onToggle() {
     if (!this._article)
       return;
 
     if (this._isReadingListItem == 0) {
-      let uptime = UITelemetry.uptimeMillis();
-      gChromeWin.Reader.storeArticleInCache(this._article, function(success) {
-        let result = gChromeWin.Reader.READER_ADD_FAILED;
-        if (success) {
-          result = gChromeWin.Reader.READER_ADD_SUCCESS;
-          UITelemetry.addEvent("save.1", "button", uptime, "reader");
-        }
-
-        let json = JSON.stringify({ fromAboutReader: true, url: this._article.url });
+      gChromeWin.Reader.addArticleToReadingList(this._article);
 
-        Messaging.sendRequest({
-          type: "Reader:AddToList",
-          result: result,
-          title: this._article.title,
-          url: this._article.url,
-          length: this._article.length,
-          excerpt: this._article.excerpt
-        });
-      }.bind(this));
+      UITelemetry.addEvent("save.1", "button", null, "reader");
     } else {
       Messaging.sendRequest({
         type: "Reader:RemoveFromList",
         url: this._article.url
       });
 
       UITelemetry.addEvent("unsave.1", "button", null, "reader");
     }
--- a/security/manager/ssl/public/nsICertOverrideService.idl
+++ b/security/manager/ssl/public/nsICertOverrideService.idl
@@ -13,17 +13,17 @@ interface nsIX509Cert;
 #define NS_CERTOVERRIDE_CONTRACTID "@mozilla.org/security/certoverride;1"
 %}
 
 /**
  * This represents the global list of triples
  *   {host:port, cert-fingerprint, allowed-overrides} 
  * that the user wants to accept without further warnings. 
  */
-[scriptable, uuid(be019e47-22fc-4355-9f16-9ab047d6742d)]
+[scriptable, uuid(31738d2a-77d3-4359-84c9-4be2f38fb8c5)]
 interface nsICertOverrideService : nsISupports {
 
   /**
    *  Override Untrusted
    */
   const short ERROR_UNTRUSTED = 1;
 
   /**
@@ -104,21 +104,23 @@ interface nsICertOverrideService : nsISu
    *               If it is -1, then it is internaly treated as 443.
    *               If it is 0 and aHostName is "all:temporary-certificates",
    *               then all temporary certificates should be cleared.
    */
   void clearValidityOverride(in ACString aHostName,
                              in int32_t aPort);
 
   /**
-   *  Count and return the number of permanent overrides.
+   *  Obtain the full list of hostname:port for which overrides are known.
    *
-   *  @param aCount The number of permanent overrides
+   *  @param aCount The number of host:port entries returned
+   *  @param aHostsWithPortsArray The array of host:port entries returned
    */
-  void getPermanentOverrideCount(out uint32_t aCount);
+  void getAllOverrideHostsWithPorts(out uint32_t aCount, 
+                                    [array, size_is(aCount)] out wstring aHostsWithPortsArray);
 
   /**
    *  Is the given cert used in rules?
    *
    *  @param aCert The cert we're looking for
    *  @return how many override entries are currently on file
    *          for the given certificate
    */
--- a/security/manager/ssl/src/nsCertOverrideService.cpp
+++ b/security/manager/ssl/src/nsCertOverrideService.cpp
@@ -1,33 +1,39 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsCertOverrideService.h"
 
+#include "pkix/pkixtypes.h"
+#include "nsIX509Cert.h"
 #include "NSSCertDBTrustDomain.h"
-#include "ScopedNSSTypes.h"
-#include "SharedSSLState.h"
+#include "nsNSSCertificate.h"
+#include "nsNSSCertHelper.h"
+#include "nsCRT.h"
 #include "nsAppDirectoryServiceDefs.h"
-#include "nsCRT.h"
+#include "nsStreamUtils.h"
+#include "nsNetUtil.h"
 #include "nsILineInputStream.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
-#include "nsIX509Cert.h"
-#include "nsNSSCertHelper.h"
-#include "nsNSSCertificate.h"
-#include "nsNSSComponent.h"
-#include "nsNetUtil.h"
+#include "nsISupportsPrimitives.h"
 #include "nsPromiseFlatString.h"
-#include "nsStreamUtils.h"
+#include "nsThreadUtils.h"
 #include "nsStringBuffer.h"
-#include "nsThreadUtils.h"
+#include "ScopedNSSTypes.h"
+#include "SharedSSLState.h"
+
+#include "nspr.h"
+#include "pk11pub.h"
+#include "certdb.h"
+#include "sechash.h"
 #include "ssl.h" // For SSL_ClearSessionCache
 
 using namespace mozilla;
 using namespace mozilla::psm;
 
 static const char kCertOverrideFileName[] = "cert_override.txt";
 
 void
@@ -95,21 +101,28 @@ nsCertOverrideService::~nsCertOverrideSe
 nsresult
 nsCertOverrideService::Init()
 {
   if (!NS_IsMainThread()) {
     NS_NOTREACHED("nsCertOverrideService initialized off main thread");
     return NS_ERROR_NOT_SAME_THREAD;
   }
 
-  // Note that the names of these variables would seem to indicate that at one
-  // point another hash algorithm was used and is still supported for backwards
-  // compatibility. This is not the case. It has always been SHA256.
   mOidTagForStoringNewHashes = SEC_OID_SHA256;
-  mDottedOidForStoringNewHashes.Assign("OID.2.16.840.1.101.3.4.2.1");
+
+  SECOidData *od = SECOID_FindOIDByTag(mOidTagForStoringNewHashes);
+  if (!od)
+    return NS_ERROR_FAILURE;
+
+  char *dotted_oid = CERT_GetOidString(&od->oid);
+  if (!dotted_oid)
+    return NS_ERROR_FAILURE;
+
+  mDottedOidForStoringNewHashes = dotted_oid;
+  PR_smprintf_free(dotted_oid);
 
   nsCOMPtr<nsIObserverService> observerService =
       mozilla::services::GetObserverService();
 
   // If we cannot add ourselves as a profile change observer, then we will not
   // attempt to read/write any settings file. Otherwise, we would end up
   // reading/writing the wrong settings file after a profile change.
   if (observerService) {
@@ -380,16 +393,52 @@ GetCertFingerprintByOidTag(nsIX509Cert *
 
   ScopedCERTCertificate nsscert(aCert->GetCert());
   if (!nsscert) {
     return NS_ERROR_FAILURE;
   }
   return GetCertFingerprintByOidTag(nsscert.get(), aOidTag, fp);
 }
 
+static nsresult
+GetCertFingerprintByDottedOidString(CERTCertificate* nsscert,
+                                    const nsCString &dottedOid, 
+                                    nsCString &fp)
+{
+  SECItem oid;
+  oid.data = nullptr;
+  oid.len = 0;
+  SECStatus srv = SEC_StringToOID(nullptr, &oid, 
+                    dottedOid.get(), dottedOid.Length());
+  if (srv != SECSuccess)
+    return NS_ERROR_FAILURE;
+
+  SECOidTag oid_tag = SECOID_FindOIDTag(&oid);
+  SECITEM_FreeItem(&oid, false);
+
+  if (oid_tag == SEC_OID_UNKNOWN)
+    return NS_ERROR_FAILURE;
+
+  return GetCertFingerprintByOidTag(nsscert, oid_tag, fp);
+}
+
+static nsresult
+GetCertFingerprintByDottedOidString(nsIX509Cert *aCert,
+                                    const nsCString &dottedOid,
+                                    nsCString &fp)
+{
+
+  ScopedCERTCertificate nsscert(aCert->GetCert());
+  if (!nsscert) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return GetCertFingerprintByDottedOidString(nsscert.get(), dottedOid, fp);
+}
+
 NS_IMETHODIMP
 nsCertOverrideService::RememberValidityOverride(const nsACString& aHostName,
                                                 int32_t aPort,
                                                 nsIX509Cert* aCert,
                                                 uint32_t aOverrideBits,
                                                 bool aTemporary)
 {
   NS_ENSURE_ARG_POINTER(aCert);
@@ -492,27 +541,24 @@ nsCertOverrideService::HasMatchingOverri
   }
 
   *aOverrideBits = settings.mOverrideBits;
   *aIsTemporary = settings.mIsTemporary;
 
   nsAutoCString fpStr;
   nsresult rv;
 
-  // This code was originally written in a way that suggested that other hash
-  // algorithms are supported for backwards compatibility. However, this was
-  // always unnecessary, because only SHA256 has ever been used here.
   if (settings.mFingerprintAlgOID.Equals(mDottedOidForStoringNewHashes)) {
     rv = GetCertFingerprintByOidTag(aCert, mOidTagForStoringNewHashes, fpStr);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-  } else {
-    return NS_ERROR_UNEXPECTED;
   }
+  else {
+    rv = GetCertFingerprintByDottedOidString(aCert, settings.mFingerprintAlgOID, fpStr);
+  }
+  if (NS_FAILED(rv))
+    return rv;
 
   *_retval = settings.mFingerprint.Equals(fpStr);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsCertOverrideService::GetValidityOverride(const nsACString & aHostName, int32_t aPort,
                                            nsACString & aHashAlg, 
@@ -598,48 +644,25 @@ nsCertOverrideService::ClearValidityOver
   }
   nsAutoCString hostPort;
   GetHostWithPort(aHostName, aPort, hostPort);
   {
     ReentrantMonitorAutoEnter lock(monitor);
     mSettingsTable.RemoveEntry(hostPort.get());
     Write();
   }
-
-  if (EnsureNSSInitialized(nssEnsure)) {
-    SSL_ClearSessionCache();
-  } else {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
+  SSL_ClearSessionCache();
   return NS_OK;
 }
 
-static PLDHashOperator
-CountPermanentEntriesCallback(nsCertOverrideEntry* aEntry, void* aArg)
+NS_IMETHODIMP
+nsCertOverrideService::GetAllOverrideHostsWithPorts(uint32_t *aCount, 
+                                                        char16_t ***aHostsWithPortsArray)
 {
-  uint32_t* overrideCount = reinterpret_cast<uint32_t*>(aArg);
-  if (aEntry && !aEntry->mSettings.mIsTemporary) {
-    *overrideCount = *overrideCount + 1;
-    return PL_DHASH_NEXT;
-  }
-
-  return PL_DHASH_NEXT;
-}
-
-NS_IMETHODIMP
-nsCertOverrideService::GetPermanentOverrideCount(uint32_t* aOverrideCount)
-{
-  NS_ENSURE_ARG(aOverrideCount);
-  *aOverrideCount = 0;
-
-  ReentrantMonitorAutoEnter lock(monitor);
-  mSettingsTable.EnumerateEntries(CountPermanentEntriesCallback, aOverrideCount);
-
-  return NS_OK;
+  return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 static bool
 matchesDBKey(nsIX509Cert *cert, const char *match_dbkey)
 {
   char *dbkey = nullptr;
   nsresult rv = cert->GetDbKey(&dbkey);
   if (NS_FAILED(rv) || !dbkey)
@@ -710,21 +733,25 @@ FindMatchingCertCallback(nsCertOverrideE
     if ((settings.mIsTemporary && !cai->aCheckTemporaries)
         ||
         (!settings.mIsTemporary && !cai->aCheckPermanents)) {
       still_ok = false;
     }
 
     if (still_ok && matchesDBKey(cai->cert, settings.mDBKey.get())) {
       nsAutoCString cert_fingerprint;
-      nsresult rv = NS_ERROR_UNEXPECTED;
+      nsresult rv;
       if (settings.mFingerprintAlgOID.Equals(cai->mDottedOidForStoringNewHashes)) {
         rv = GetCertFingerprintByOidTag(cai->cert,
                cai->mOidTagForStoringNewHashes, cert_fingerprint);
       }
+      else {
+        rv = GetCertFingerprintByDottedOidString(cai->cert,
+               settings.mFingerprintAlgOID, cert_fingerprint);
+      }
       if (NS_SUCCEEDED(rv) &&
           settings.mFingerprint.Equals(cert_fingerprint)) {
         cai->counter++;
       }
     }
   }
 
   return PL_DHASH_NEXT;
@@ -776,21 +803,25 @@ EnumerateCertOverridesCallback(nsCertOve
     const nsCertOverride &settings = aEntry->mSettings;
 
     if (!capac->cert) {
       (*capac->enumerator)(settings, capac->userdata);
     }
     else {
       if (matchesDBKey(capac->cert, settings.mDBKey.get())) {
         nsAutoCString cert_fingerprint;
-        nsresult rv = NS_ERROR_UNEXPECTED;
+        nsresult rv;
         if (settings.mFingerprintAlgOID.Equals(capac->mDottedOidForStoringNewHashes)) {
           rv = GetCertFingerprintByOidTag(capac->cert,
                  capac->mOidTagForStoringNewHashes, cert_fingerprint);
         }
+        else {
+          rv = GetCertFingerprintByDottedOidString(capac->cert,
+                 settings.mFingerprintAlgOID, cert_fingerprint);
+        }
         if (NS_SUCCEEDED(rv) &&
             settings.mFingerprint.Equals(cert_fingerprint)) {
           (*capac->enumerator)(settings, capac->userdata);
         }
       }
     }
   }
 
@@ -824,8 +855,9 @@ nsCertOverrideService::GetHostWithPort(c
     aPort = 443;
   }
   if (!hostPort.IsEmpty()) {
     hostPort.Append(':');
     hostPort.AppendInt(aPort);
   }
   _retval.Assign(hostPort);
 }
+
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -1044,33 +1044,16 @@ nsNSSComponent::InitializeNSS()
   // Initialize the site security service
   nsCOMPtr<nsISiteSecurityService> sssService =
     do_GetService(NS_SSSERVICE_CONTRACTID);
   if (!sssService) {
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Cannot initialize site security service\n"));
     return NS_ERROR_FAILURE;
   }
 
-  // This can happen during startup and is a bit expensive, so only instantiate
-  // the certificate override service if telemetry is actually enabled.
-  if (Telemetry::CanRecord()) {
-    nsCOMPtr<nsICertOverrideService> overrideService(
-      do_GetService(NS_CERTOVERRIDE_CONTRACTID));
-    if (!overrideService) {
-      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("Failed to initialize cert override service\n"));
-      return NS_ERROR_FAILURE;
-    }
-    uint32_t overrideCount = 0;
-    rv = overrideService->GetPermanentOverrideCount(&overrideCount);
-    if (NS_FAILED(rv)) {
-      return rv;
-    }
-    Telemetry::Accumulate(Telemetry::SSL_PERMANENT_CERT_ERROR_OVERRIDES,
-                          overrideCount);
-  }
 
   PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("NSS Initialization done\n"));
   return NS_OK;
 }
 
 void
 nsNSSComponent::ShutdownNSS()
 {
--- a/security/manager/ssl/src/nsNSSModule.cpp
+++ b/security/manager/ssl/src/nsNSSModule.cpp
@@ -194,24 +194,24 @@ NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEn
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCertPicker)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nssEnsure, nsNTLMAuthModule, InitTest)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCryptoHash)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsCryptoHMAC)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsStreamCipher)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsKeyObject)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsKeyObjectFactory)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsDataSignatureVerifier)
+NS_NSS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nssEnsure, nsCertOverrideService, Init)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsure, nsRandomGenerator)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureOnChromeOnly, nsSSLStatus)
 NS_NSS_GENERIC_FACTORY_CONSTRUCTOR(nssEnsureOnChromeOnly, TransportSecurityInfo)
 
 typedef mozilla::psm::NSSErrorsService NSSErrorsService;
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NSSErrorsService, Init)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsNSSVersion)
-NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsCertOverrideService, Init)
 
 NS_DEFINE_NAMED_CID(NS_NSSCOMPONENT_CID);
 NS_DEFINE_NAMED_CID(NS_SSLSOCKETPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_STARTTLSSOCKETPROVIDER_CID);
 NS_DEFINE_NAMED_CID(NS_SDR_CID);
 NS_DEFINE_NAMED_CID(NS_PK11TOKENDB_CID);
 NS_DEFINE_NAMED_CID(NS_PKCS11MODULEDB_CID);
 NS_DEFINE_NAMED_CID(NS_PSMCONTENTLISTEN_CID);
--- a/testing/marionette/client/marionette/runner/base.py
+++ b/testing/marionette/client/marionette/runner/base.py
@@ -15,16 +15,17 @@ import time
 import traceback
 import unittest
 import xml.dom.minidom as dom
 
 from manifestparser import TestManifest
 from marionette import Marionette
 from mixins.b2g import B2GTestResultMixin, get_b2g_pid, get_dm
 from mozhttpd import MozHttpd
+from mozlog.structured.structuredlog import get_default_logger
 from moztest.adapters.unit import StructuredTestRunner, StructuredTestResult
 from moztest.results import TestResultCollection, TestResult, relevant_line
 
 class MarionetteTest(TestResult):
 
     @property
     def test_name(self):
         if self.test_class is not None:
@@ -217,17 +218,18 @@ class MarionetteTextTestRunner(Structure
 
     def _makeResult(self):
         return self.resultclass(self.stream,
                                 self.descriptions,
                                 self.verbosity,
                                 marionette=self.marionette,
                                 b2g_pid=self.b2g_pid,
                                 logger=self.logger,
-                                logcat_stdout=self.logcat_stdout)
+                                logcat_stdout=self.logcat_stdout,
+                                result_callbacks=self.result_callbacks)
 
     def run(self, test):
         "Run the given test case or test suite."
         for pre_run_func in self.pre_run_functions:
             pre_run_func()
 
         result = super(MarionetteTextTestRunner, self).run(test)
         result.printLogs(test)
@@ -445,17 +447,18 @@ class BaseMarionetteTestRunner(object):
     def __init__(self, address=None, emulator=None, emulator_binary=None,
                  emulator_img=None, emulator_res='480x800', homedir=None,
                  app=None, app_args=None, binary=None, profile=None,
                  logger=None, no_window=False, logdir=None, logcat_stdout=False,
                  xml_output=None, repeat=0, testvars=None, tree=None, type=None,
                  device_serial=None, symbols_path=None, timeout=None,
                  shuffle=False, shuffle_seed=random.randint(0, sys.maxint),
                  sdcard=None, this_chunk=1, total_chunks=1, sources=None,
-                 server_root=None, gecko_log=None, **kwargs):
+                 server_root=None, gecko_log=None, result_callbacks=None,
+                 **kwargs):
         self.address = address
         self.emulator = emulator
         self.emulator_binary = emulator_binary
         self.emulator_img = emulator_img
         self.emulator_res = emulator_res
         self.homedir = homedir
         self.app = app
         self.app_args = app_args or []
@@ -485,16 +488,32 @@ class BaseMarionetteTestRunner(object):
         self.sources = sources
         self.server_root = server_root
         self.this_chunk = this_chunk
         self.total_chunks = total_chunks
         self.gecko_log = gecko_log
         self.mixin_run_tests = []
         self.manifest_skipped_tests = []
         self.tests = []
+        self.result_callbacks = result_callbacks if result_callbacks is not None else []
+
+        def gather_debug(test, status):
+            rv = {}
+            marionette = test._marionette_weakref()
+            try:
+                marionette.set_context(marionette.CONTEXT_CHROME)
+                rv['screenshot'] = marionette.screenshot()
+                marionette.set_context(marionette.CONTEXT_CONTENT)
+                rv['source'] = marionette.page_source
+            except:
+                logger = get_default_logger()
+                logger.warning('Failed to gather test failure debug.', exc_info=True)
+            return rv
+
+        self.result_callbacks.append(gather_debug)
 
         if testvars:
             if not os.path.exists(testvars):
                 raise IOError('--testvars file does not exist')
 
             try:
                 with open(testvars) as f:
                     self.testvars = json.loads(f.read())
@@ -784,17 +803,18 @@ class BaseMarionetteTestRunner(object):
                                            self.testvars,
                                            **self.test_kwargs)
                 break
 
         if suite.countTestCases():
             runner = self.textrunnerclass(logger=self.logger,
                                           marionette=self.marionette,
                                           capabilities=self.capabilities,
-                                          logcat_stdout=self.logcat_stdout)
+                                          logcat_stdout=self.logcat_stdout,
+                                          result_callbacks=self.result_callbacks)
 
             results = runner.run(suite)
             self.results.append(results)
 
             self.failed += len(results.failures) + len(results.errors)
             if hasattr(results, 'skipped'):
                 self.skipped += len(results.skipped)
                 self.todo += len(results.skipped)
--- a/testing/mozbase/mozlog/mozlog/structured/formatters/html/html.py
+++ b/testing/mozbase/mozlog/mozlog/structured/formatters/html/html.py
@@ -119,17 +119,17 @@ class HTMLFormatter(base.BaseFormatter):
                     class_='screenshot'))
             for name, content in debug.items():
                 if 'screenshot' in name:
                     href = '#'
                 else:
                     # use base64 to avoid that some browser (such as Firefox, Opera)
                     # treats '#' as the start of another link if the data URL contains.
                     # use 'charset=utf-8' to show special characters like Chinese.
-                    href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode(content)
+                    href = 'data:text/plain;charset=utf-8;base64,%s' % base64.b64encode(content.encode('utf-8'))
                 links_html.append(html.a(
                     name.title(),
                     class_=name,
                     href=href,
                     target='_blank'))
                 links_html.append(' ')
 
             log = html.div(class_='log')
--- a/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-append-buffer.html.ini
@@ -5,11 +5,8 @@
     expected: FAIL
 
   [Test appending an empty ArrayBufferView.]
     expected: FAIL
 
   [Test appending an empty ArrayBuffer.]
     expected: FAIL
 
-  [Test SourceBuffer.appendBuffer() triggering an 'ended' to 'open' transition.]
-    disabled:
-      if not debug and (os == "win") and (version == "5.1.2600") and (processor == "x86") and (bits == 32): Unstable
--- a/testing/web-platform/meta/media-source/mediasource-buffered.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-buffered.html.ini
@@ -1,3 +1,15 @@
 [mediasource-buffered.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Demuxed content with different lengths]
+    expected: FAIL
+
+  [Muxed tracks with different lengths]
+    expected: FAIL
+
+  [Demuxed content with an empty buffered range on one SourceBuffer]
+    expected: FAIL
+
+  [Muxed content empty buffered ranges.]
+    expected: FAIL
+
deleted file mode 100644
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-a-bitrate.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[mediasource-config-change-webm-a-bitrate.html]
-  type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
deleted file mode 100644
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-av-audio-bitrate.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[mediasource-config-change-webm-av-audio-bitrate.html]
-  type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
deleted file mode 100644
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-av-video-bitrate.html.ini
+++ /dev/null
@@ -1,3 +0,0 @@
-[mediasource-config-change-webm-av-video-bitrate.html]
-  type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
--- a/testing/web-platform/meta/media-source/mediasource-config-change-webm-v-bitrate.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-config-change-webm-v-bitrate.html.ini
@@ -1,6 +1,5 @@
 [mediasource-config-change-webm-v-bitrate.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
   [Tests webm video-only bitrate changes.]
     expected: FAIL
 
--- a/testing/web-platform/meta/media-source/mediasource-duration.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-duration.html.ini
@@ -1,3 +1,15 @@
 [mediasource-duration.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test seek starts on duration truncation below currentTime]
+    expected: TIMEOUT
+
+  [Test appendBuffer completes previous seek to truncated duration]
+    disabled: TIMEOUT or FAIL https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+
+  [Test endOfStream completes previous seek to truncated duration]
+    expected: TIMEOUT
+
+  [Test setting same duration multiple times does not fire duplicate durationchange]
+    expected: FAIL
+
--- a/testing/web-platform/meta/media-source/mediasource-play-then-seek-back.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-play-then-seek-back.html.ini
@@ -1,3 +1,6 @@
 [mediasource-play-then-seek-back.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test playing then seeking back.]
+    expected: TIMEOUT
+
--- a/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-redundant-seek.html.ini
@@ -1,3 +1,6 @@
 [mediasource-redundant-seek.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test redundant fully prebuffered seek]
+    expected: TIMEOUT
+
--- a/testing/web-platform/meta/media-source/mediasource-seek-beyond-duration.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-seek-beyond-duration.html.ini
@@ -1,3 +1,9 @@
 [mediasource-seek-beyond-duration.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test seeking beyond updated media duration.]
+    expected: TIMEOUT
+
+  [Test seeking beyond media duration.]
+    disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+
--- a/testing/web-platform/meta/media-source/mediasource-seek-during-pending-seek.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-seek-during-pending-seek.html.ini
@@ -1,3 +1,9 @@
 [mediasource-seek-during-pending-seek.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test seeking to a new location before transitioning beyond HAVE_METADATA.]
+    expected: TIMEOUT
+
+  [Test seeking to a new location during a pending seek.]
+    expected: TIMEOUT
+
--- a/testing/web-platform/meta/media-source/mediasource-sourcebuffer-mode.html.ini
+++ b/testing/web-platform/meta/media-source/mediasource-sourcebuffer-mode.html.ini
@@ -1,3 +1,6 @@
 [mediasource-sourcebuffer-mode.html]
   type: testharness
-  disabled: https://bugzilla.mozilla.org/show_bug.cgi?id=1066467
+  expected: TIMEOUT
+  [Test setting SourceBuffer.mode and SourceBuffer.timestampOffset while parsing media segment.]
+    expected: FAIL
+
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -6473,23 +6473,16 @@
     "description": "For non-stapling cases, is OCSP fetching a possibility? (0=yes, 1=no because missing/invalid OCSP URI, 2=no because fetching disabled, 3=no because both)"
   },
   "SSL_CERT_ERROR_OVERRIDES": {
     "expires_in_version": "never",
     "kind": "enumerated",
     "n_values": 24,
     "description": "Was a certificate error overridden on this handshake? What was it? (0=unknown error (indicating bug), 1=no, >1=a specific error)"
   },
-  "SSL_PERMANENT_CERT_ERROR_OVERRIDES": {
-    "expires_in_version": "never",
-    "kind": "exponential",
-    "high": 1024,
-    "n_buckets": 10,
-    "description": "How many permanent certificate overrides a user has stored."
-  },
   "TELEMETRY_TEST_EXPIRED": {
     "expires_in_version": "4.0a1",
     "kind": "flag",
     "description": "a testing histogram; not meant to be touched"
   },
   "CERT_OCSP_ENABLED": {
     "expires_in_version": "never",
     "kind": "boolean",
--- a/toolkit/devtools/server/actors/timeline.js
+++ b/toolkit/devtools/server/actors/timeline.js
@@ -207,17 +207,17 @@ let TimelineActor = exports.TimelineActo
     this._startTime = this.docShells[0].now();
 
     for (let docShell of this.docShells) {
       docShell.recordProfileTimelineMarkers = true;
     }
 
     if (withMemory) {
       this._memoryActor = new MemoryActor(this.conn, this.tabActor);
-      events.emit(this, "memory", Date.now(), this._memoryActor.measure());
+      events.emit(this, "memory", this._startTime, this._memoryActor.measure());
     }
     if (withTicks) {
       this._framerateActor = new FramerateActor(this.conn, this.tabActor);
       this._framerateActor.startRecording();
     }
 
     this._pullTimelineData();
     return this._startTime;
--- a/toolkit/themes/osx/global/tabprompts.css
+++ b/toolkit/themes/osx/global/tabprompts.css
@@ -31,17 +31,17 @@ tabmodalprompt {
 button {
   -moz-appearance: none;
   padding: 2px 0;
   margin: 0;
   -moz-margin-start: 8px;
   border-radius: 2px;
   color: black !important;
   background-color: hsl(0,0%,90%);
-  background-image: linear-gradient(hsla(0,0%,100%,.7), hsla(0,0%,100%,0));
+  background-image: linear-gradient(hsla(0,0%,100%,.7), transparent);
   background-clip: padding-box;
   border: 1px solid;
   border-color: hsl(0,0%,65%) hsl(0,0%,60%) hsl(0,0%,50%);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.9) inset,
               0 1px 2px hsla(0,0%,0%,.1);
 }
 
 
@@ -49,17 +49,17 @@ button[default=true] {
   background-color: hsl(0,0%,79%);
 }
 
 button:hover {
   background-color: hsl(0,0%,96%);
 }
 
 button:hover:active {
-  background-image: linear-gradient(hsla(0,0%,100%,.2), hsla(0,0%,100%,0));
+  background-image: linear-gradient(hsla(0,0%,100%,.2), transparent);
   background-color: hsl(0,0%,70%);
   box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
               0 1px 3px hsla(0,0%,0%,.2);
 }
 
 button:focus {
   box-shadow: 0 0 1px -moz-mac-focusring inset,
               0 0 4px 1px -moz-mac-focusring,
--- a/toolkit/themes/osx/mozapps/extensions/extensions.css
+++ b/toolkit/themes/osx/mozapps/extensions/extensions.css
@@ -792,25 +792,25 @@
 #detail-screenshot {
   -moz-margin-end: 2em;
   max-width: 300px;
   max-height: 300px;
 }
 
 #detail-screenshot[loading] {
   background-image: url("chrome://global/skin/icons/loading_16.png"),
-                    linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0));
+                    linear-gradient(rgba(255, 255, 255, 0.5), transparent);
   background-position: 50% 50%;
   background-repeat: no-repeat;
   border-radius: 3px;
 }
 
 #detail-screenshot[loading="error"] {
   background-image: url("chrome://global/skin/media/error.png"),
-                    linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0));
+                    linear-gradient(rgba(255, 255, 255, 0.5), transparent);
 }
 
 #detail-desc-container {
   margin-bottom: 2em;
 }
 
 #detail-desc, #detail-fulldesc {
   -moz-margin-start: 6px;
@@ -1147,33 +1147,33 @@ button.button-link:not([disabled="true"]
 .header-button {
   -moz-appearance: none;
   padding: 0 4px;
   margin: 0;
   height: 22px;
   border: 1px solid rgba(60,73,97,0.5);
   border-radius: @toolbarbuttonCornerRadius@;
   box-shadow: inset 0 1px rgba(255,255,255,0.25), 0 1px rgba(255,255,255,0.25);
-  background: linear-gradient(rgba(255,255,255,0.45), rgba(255,255,255,0));
+  background: linear-gradient(rgba(255,255,255,0.45), transparent);
   background-clip: padding-box;
 }
 
 .header-button .toolbarbutton-text {
   display: none;
 }
 
 .header-button[disabled="true"] .toolbarbutton-icon {
   opacity: 0.4;
 }
 
 .header-button:not([disabled="true"]):active:hover,
 .header-button[open="true"] {
   border-color: rgba(45,54,71,0.7);
   box-shadow: inset 0 0 4px rgb(45,54,71), 0 1px rgba(255,255,255,0.25);
-  background-image: linear-gradient(rgba(45,54,71,0.6), rgba(45,54,71,0));
+  background-image: linear-gradient(rgba(45,54,71,0.6), transparent);
 }
 
 /*** telemetry experiments ***/
 
 #detail-experiment-container {
   font-size: 80%;
   margin-bottom: 1em;
 }
--- a/toolkit/themes/windows/global/autocomplete.css
+++ b/toolkit/themes/windows/global/autocomplete.css
@@ -111,20 +111,20 @@ treechildren.autocomplete-treebody::-moz
   -moz-appearance: menuitem is almost right, but the hover effect is not
   transparent and is lighter than desired.
   */
   .autocomplete-richlistitem[selected="true"] {
     color: inherit;
     background-color: transparent;
     /* four gradients for the bevel highlights on each edge, one for blue background */
     background-image:
-      linear-gradient(to bottom, rgba(255,255,255,0.9) 3px, rgba(255,255,255,0) 3px),
-      linear-gradient(to right, rgba(255,255,255,0.5) 3px, rgba(255,255,255,0) 3px),
-      linear-gradient(to left, rgba(255,255,255,0.5) 3px, rgba(255,255,255,0) 3px),
-      linear-gradient(to top, rgba(255,255,255,0.4) 3px, rgba(255,255,255,0) 3px),
+      linear-gradient(to bottom, rgba(255,255,255,0.9) 3px, transparent 3px),
+      linear-gradient(to right, rgba(255,255,255,0.5) 3px, transparent 3px),
+      linear-gradient(to left, rgba(255,255,255,0.5) 3px, transparent 3px),
+      linear-gradient(to top, rgba(255,255,255,0.4) 3px, transparent 3px),
       linear-gradient(to bottom, rgba(163,196,247,0.3), rgba(122,180,246,0.3));
     background-clip: content-box;
     border-radius: 6px;
     outline: 1px solid rgb(124,163,206);
     -moz-outline-radius: 3px;
     outline-offset: -2px;
   }
 }
--- a/toolkit/themes/windows/global/inContentUI.css
+++ b/toolkit/themes/windows/global/inContentUI.css
@@ -33,32 +33,32 @@ html|html {
   }
 }
 
 @media (-moz-windows-glass) {
   *|*:root {
     /* Blame shorlander for this monstrosity. */
     background-image: /* Side gradients */
                       linear-gradient(to right,
-                                      rgba(255,255,255,0.2), rgba(255,255,255,0) 40%,
-                                      rgba(255,255,255,0) 60%, rgba(255,255,255,0.2)),
+                                      rgba(255,255,255,0.2), transparent 40%,
+                                      transparent 60%, rgba(255,255,255,0.2)),
                       /* Aero-style light beams */
                       -moz-linear-gradient(left 32deg,
                                            /* First light beam */
-                                           rgba(255,255,255,0) 19.5%, rgba(255,255,255,0.1) 20%,
+                                           transparent 19.5%, rgba(255,255,255,0.1) 20%,
                                            rgba(255,255,255,0.1) 21.5%, rgba(255,255,255,0.2) 22%,
                                            rgba(255,255,255,0.2) 25.5%, rgba(255,255,255,0.1) 26%,
-                                           rgba(255,255,255,0.1) 27.5%, rgba(255,255,255,0) 28%,
+                                           rgba(255,255,255,0.1) 27.5%, transparent 28%,
                                            /* Second light beam */
-                                           rgba(255,255,255,0) 49.5%, rgba(255,255,255,0.1) 50%,
+                                           transparent 49.5%, rgba(255,255,255,0.1) 50%,
                                            rgba(255,255,255,0.1) 52.5%, rgba(255,255,255,0.2) 53%,
                                            rgba(255,255,255,0.2) 54.5%, rgba(255,255,255,0.1) 55%,
-                                           rgba(255,255,255,0.1) 57.5%, rgba(255,255,255,0) 58%,
+                                           rgba(255,255,255,0.1) 57.5%, transparent 58%,
                                            /* Third light beam */
-                                           rgba(255,255,255,0) 87%, rgba(255,255,255,0.2) 90%),
+                                           transparent 87%, rgba(255,255,255,0.2) 90%),
                       /* Texture */
                       url("chrome://global/skin/inContentUI/background-texture.png");
   }
 }
 %endif
 
 /* Content */
 *|*.main-content {
@@ -83,17 +83,17 @@ html|html {
 @media (-moz-windows-glass) {
   /* Buttons */
   *|button,
   menulist,
   colorpicker[type="button"] {
     -moz-appearance: none;
     color: black;
     padding: 0 5px;
-    background: linear-gradient(rgba(251, 252, 253, 0.95), rgba(246, 247, 248, 0) 49%, 
+    background: linear-gradient(rgba(251, 252, 253, 0.95), transparent 49%, 
                                 rgba(211, 212, 213, 0.45) 51%, rgba(225, 226, 229, 0.3));
     background-clip: padding-box;
     border-radius: 3px;
     border: 1px solid rgba(31, 64, 100, 0.4);
     border-top-color: rgba(31, 64, 100, 0.3);
     box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.25) inset,
                 0 0 2px 1px rgba(255, 255, 255, 0.25) inset;
   }
--- a/toolkit/themes/windows/mozapps/extensions/extensions.css
+++ b/toolkit/themes/windows/mozapps/extensions/extensions.css
@@ -785,25 +785,25 @@
 #detail-screenshot {
   -moz-margin-end: 2em;
   max-width: 300px;
   max-height: 300px;
 }
 
 #detail-screenshot[loading] {
   background-image: url("chrome://global/skin/icons/loading_16.png"),
-                    linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0));
+                    linear-gradient(rgba(255, 255, 255, 0.5), transparent);
   background-position: 50% 50%;
   background-repeat: no-repeat;
   border-radius: 3px;
 }
 
 #detail-screenshot[loading="error"] {
   background-image: url("chrome://global/skin/media/error.png"),
-                    linear-gradient(rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0));
+                    linear-gradient(rgba(255, 255, 255, 0.5), transparent);
 }
 
 #detail-desc-container {
   margin-bottom: 2em;
 }
 
 #detail-desc, #detail-fulldesc {
   -moz-margin-start: 6px;
@@ -1156,17 +1156,17 @@ button.button-link:not([disabled="true"]
   box-shadow: none;
 }
 
 .header-button {
   -moz-appearance: none;
   padding: 1px 3px;
   color: #444;
   text-shadow: 0 0 3px white;
-  background: linear-gradient(rgba(251, 252, 253, 0.95), rgba(246, 247, 248, 0) 49%, 
+  background: linear-gradient(rgba(251, 252, 253, 0.95), transparent 49%, 
                               rgba(211, 212, 213, 0.45) 51%, rgba(225, 226, 229, 0.3));
   background-clip: padding-box;
   border: 1px solid rgba(31, 64, 100, 0.4);
   border-top-color: rgba(31, 64, 100, 0.3);
   box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.25) inset,
               0 0 2px 1px rgba(255, 255, 255, 0.25) inset;
 }
 
--- a/tools/profiler/platform.h
+++ b/tools/profiler/platform.h
@@ -259,17 +259,20 @@ class TickSample {
       :
         pc(NULL),
         sp(NULL),
         fp(NULL),
 #ifdef ENABLE_ARM_LR_SAVING
         lr(NULL),
 #endif
         context(NULL),
-        isSamplingCurrentThread(false) {}
+        isSamplingCurrentThread(false),
+        threadProfile(nullptr),
+        rssMemory(0),
+        ussMemory(0) {}
 
   void PopulateContext(void* aContext);
 
   Address pc;  // Instruction pointer.
   Address sp;  // Stack pointer.
   Address fp;  // Frame pointer.
 #ifdef ENABLE_ARM_LR_SAVING
   Address lr;  // ARM link register
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -175,16 +175,18 @@
 
 #include "nsIContent.h"
 
 #include "mozilla/HangMonitor.h"
 #include "WinIMEHandler.h"
 
 #include "npapi.h"
 
+#include <d3d11.h>
+
 #if !defined(SM_CONVERTIBLESLATEMODE)
 #define SM_CONVERTIBLESLATEMODE 0x2003
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
@@ -6730,17 +6732,19 @@ nsWindow::GetPreferredCompositorBackends
   // want to support this case. See bug 593471
   if (!(prefs.mDisableAcceleration ||
         mTransparencyMode == eTransparencyTransparent)) {
     if (prefs.mPreferOpenGL) {
       aHints.AppendElement(LayersBackend::LAYERS_OPENGL);
     }
 
     ID3D11Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D11Device();
-    if (device && !DoesD3D11DeviceSupportResourceSharing(device)) {
+    if (device &&
+        device->GetFeatureLevel() >= D3D_FEATURE_LEVEL_10_0 &&
+        !DoesD3D11DeviceSupportResourceSharing(device)) {
       // bug 1083071 - bad things - fall back to basic layers
       // This should not happen aside from driver bugs, and in particular
       // should not happen on our test machines, so let's NS_ERROR to ensure
       // that we would catch it as a test failure.
       NS_ERROR("Can't use Direct3D 11 because of a driver bug "
         "causing resource sharing to fail");
     } else {
       if (!prefs.mPreferD3D9) {