Merge m-c to b2ginbound, a=merge
authorWes Kocher <wkocher@mozilla.com>
Thu, 03 Dec 2015 16:11:31 -0800
changeset 309646 e12a20f4efd2ea41befada9b5cfd522cf4c77817
parent 309645 84127f4ae4e58846d242a7c7bcd7935e58c85e5d (current diff)
parent 309643 528fc18a86d3f6e6bec37190522a46853a846665 (diff)
child 309647 b3f792eaa33835998fcb56187bd2343c16adb3ab
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone45.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2ginbound, a=merge
dom/media/test/test_eme_key_ids_initdata.html
mobile/android/base/resources/drawable-hdpi/menu_panel_bg.9.png
mobile/android/base/resources/drawable-xhdpi/menu_panel_bg.9.png
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1082,16 +1082,19 @@ pref("layout.accessiblecaret.use_long_ta
 
 // Enable sync and mozId with Firefox Accounts.
 pref("services.sync.fxaccounts.enabled", true);
 pref("identity.fxaccounts.enabled", true);
 
 // Mobile Identity API.
 pref("services.mobileid.server.uri", "https://msisdn.services.mozilla.com");
 
+pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
+pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox.com/v1");
+
 // Enable mapped array buffer.
 #ifndef XP_WIN
 pref("dom.mapped_arraybuffer.enabled", true);
 #endif
 
 // SystemUpdate API
 pref("dom.system_update.enabled", true);
 
--- a/browser/components/sessionstore/RunState.jsm
+++ b/browser/components/sessionstore/RunState.jsm
@@ -14,24 +14,16 @@ const STATE_STOPPED = 0;
 const STATE_RUNNING = 1;
 const STATE_QUITTING = 2;
 const STATE_CLOSING = 3;
 const STATE_CLOSED = 4;
 
 // We're initially stopped.
 var state = STATE_STOPPED;
 
-function observer(subj, topic) {
-  Services.obs.removeObserver(observer, topic);
-  state = STATE_QUITTING;
-}
-
-// Listen for when the application is quitting.
-Services.obs.addObserver(observer, "quit-application-granted", false);
-
 /**
  * This module keeps track of SessionStore's current run state. We will
  * always start out at STATE_STOPPED. After the sessionw as read from disk and
  * the initial browser window has loaded we switch to STATE_RUNNING. On the
  * first notice that a browser shutdown was granted we switch to STATE_QUITTING.
  */
 this.RunState = Object.freeze({
   // If we're stopped then SessionStore hasn't been initialized yet. As soon
@@ -90,10 +82,19 @@ this.RunState = Object.freeze({
 
   // Switch the run state to STATE_CLOSED. This must be called by SessionFile
   // after the last write to disk was accepted and no further writes will be
   // allowed. Any writes after this stage will cause exceptions.
   setClosed() {
     if (this.isClosing) {
       state = STATE_CLOSED;
     }
-  }
+  },
+
+  // Switch the run state to STATE_QUITTING. This should be called once we're
+  // certain that the browser is going away and before we start collecting the
+  // final window states to save in the session file.
+  setQuitting() {
+    if (this.isRunning) {
+      state = STATE_QUITTING;
+    }
+  },
 });
--- a/browser/components/sessionstore/SessionStore.jsm
+++ b/browser/components/sessionstore/SessionStore.jsm
@@ -26,17 +26,17 @@ const NOTIFY_TAB_RESTORED = "sessionstor
 
 // Maximum number of tabs to restore simultaneously. Previously controlled by
 // the browser.sessionstore.max_concurrent_tabs pref.
 const MAX_CONCURRENT_TAB_RESTORES = 3;
 
 // global notifications observed
 const OBSERVING = [
   "browser-window-before-show", "domwindowclosed",
-  "quit-application-requested", "browser-lastwindow-close-granted",
+  "quit-application-granted", "browser-lastwindow-close-granted",
   "quit-application", "browser:purge-session-history",
   "browser:purge-domain-data",
   "idle-daily",
 ];
 
 // XUL Window properties to (re)store
 // Restored in restoreDimensions()
 const WINDOW_ATTRIBUTES = ["width", "height", "screenX", "screenY", "sizemode"];
@@ -175,16 +175,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "TabStateCache",
   "resource:///modules/sessionstore/TabStateCache.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "TabStateFlusher",
   "resource:///modules/sessionstore/TabStateFlusher.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Utils",
   "resource:///modules/sessionstore/Utils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ViewSourceBrowser",
   "resource://gre/modules/ViewSourceBrowser.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
+  "resource://gre/modules/AsyncShutdown.jsm");
 
 /**
  * |true| if we are in debug mode, |false| otherwise.
  * Debug mode is controlled by preference browser.sessionstore.debug
  */
 var gDebuggingEnabled = false;
 function debug(aMsg) {
   if (gDebuggingEnabled) {
@@ -622,18 +624,18 @@ var SessionStoreInternal = {
   observe: function ssi_observe(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "browser-window-before-show": // catch new windows
         this.onBeforeBrowserWindowShown(aSubject);
         break;
       case "domwindowclosed": // catch closed windows
         this.onClose(aSubject);
         break;
-      case "quit-application-requested":
-        this.onQuitApplicationRequested();
+      case "quit-application-granted":
+        this.onQuitApplicationGranted();
         break;
       case "browser-lastwindow-close-granted":
         this.onLastWindowCloseGranted();
         break;
       case "quit-application":
         this.onQuitApplication(aData);
         break;
       case "browser:purge-session-history": // catch sanitization
@@ -832,16 +834,17 @@ var SessionStoreInternal = {
         break;
       case "SessionStore:crashedTabRevived":
         // The browser was revived by navigating to a different page
         // manually, so we remove it from the ignored browser set.
         this._crashedBrowsers.delete(browser.permanentKey);
         break;
       case "SessionStore:error":
         this.reportInternalError(data);
+        TabStateFlusher.resolveAll(browser, false, "Received error from the content process");
         break;
       default:
         throw new Error(`received unknown message '${aMessage.name}'`);
         break;
     }
   },
 
   /**
@@ -1205,16 +1208,20 @@ var SessionStoreInternal = {
     aWindow.dispatchEvent(event);
 
     if (this.windowToFocus && this.windowToFocus == aWindow) {
       delete this.windowToFocus;
     }
 
     var tabbrowser = aWindow.gBrowser;
 
+    // The tabbrowser binding will go away once the window is closed,
+    // so we'll hold a reference to the browsers in the closure here.
+    let browsers = tabbrowser.browsers;
+
     TAB_EVENTS.forEach(function(aEvent) {
       tabbrowser.tabContainer.removeEventListener(aEvent, this, true);
     }, this);
 
     aWindow.gBrowser.removeEventListener("XULFrameLoaderCreated", this);
 
     let winData = this._windows[aWindow.__SSi];
 
@@ -1276,20 +1283,16 @@ var SessionStoreInternal = {
       // 3) When the flush is complete, revisit our decision to store the window
       //    in _closedWindows, and add/remove as necessary.
       if (!winData.isPrivate) {
         // Remove any open private tabs the window may contain.
         PrivacyFilter.filterPrivateTabs(winData);
         this.maybeSaveClosedWindow(winData, isLastWindow);
       }
 
-      // The tabbrowser binding will go away once the window is closed,
-      // so we'll hold a reference to the browsers in the closure here.
-      let browsers = tabbrowser.browsers;
-
       TabStateFlusher.flushWindow(aWindow).then(() => {
         // At this point, aWindow is closed! You should probably not try to
         // access any DOM elements from aWindow within this callback unless
         // you're holding on to them in the closure.
 
         // We can still access tabbrowser.browsers, thankfully.
         for (let browser of browsers) {
           if (this._closedWindowTabs.has(browser.permanentKey)) {
@@ -1305,23 +1308,23 @@ var SessionStoreInternal = {
           // It's possible that a tab switched its privacy state at some point
           // before our flush, so we need to filter again.
           PrivacyFilter.filterPrivateTabs(winData);
           this.maybeSaveClosedWindow(winData, isLastWindow);
         }
 
         // Update the tabs data now that we've got the most
         // recent information.
-        this.cleanUpWindow(aWindow, winData);
+        this.cleanUpWindow(aWindow, winData, browsers);
 
         // save the state without this window to disk
         this.saveStateDelayed();
       });
     } else {
-      this.cleanUpWindow(aWindow, winData);
+      this.cleanUpWindow(aWindow, winData, browsers);
     }
 
     for (let i = 0; i < tabbrowser.tabs.length; i++) {
       this.onTabRemove(aWindow, tabbrowser.tabs[i], true);
     }
   },
 
   /**
@@ -1331,17 +1334,23 @@ var SessionStoreInternal = {
    *
    * @param aWindow
    *        The browser window we're cleaning up.
    * @param winData
    *        The data for the window that we should hold in the
    *        DyingWindowCache in case anybody is still holding a
    *        reference to it.
    */
-  cleanUpWindow(aWindow, winData) {
+  cleanUpWindow(aWindow, winData, browsers) {
+    // Any leftover TabStateFlusher Promises need to be resolved now,
+    // since we're about to remove the message listeners.
+    for (let browser of browsers) {
+      TabStateFlusher.resolveAll(browser);
+    }
+
     // Cache the window state until it is completely gone.
     DyingWindowCache.set(aWindow, winData);
 
     let mm = aWindow.getGroupMessageManager("browsers");
     MESSAGES.forEach(msg => mm.removeMessageListener(msg, this));
 
     delete aWindow.__SSi;
   },
@@ -1390,33 +1399,86 @@ var SessionStoreInternal = {
         this._capClosedWindows();
       } else if (!shouldStore && alreadyStored) {
         this._closedWindows.splice(winIndex, 1);
       }
     }
   },
 
   /**
-   * On quit application requested
+   * On quit application granted
    */
-  onQuitApplicationRequested: function ssi_onQuitApplicationRequested() {
-    // get a current snapshot of all windows
-    this._forEachBrowserWindow(function(aWindow) {
-      // Flush all data queued in the content script to not lose it when
-      // shutting down.
-      TabState.flushWindow(aWindow);
-      this._collectWindowData(aWindow);
+  onQuitApplicationGranted: function ssi_onQuitApplicationGranted() {
+    // Collect an initial snapshot of window data before we do the flush
+    this._forEachBrowserWindow((win) => {
+      this._collectWindowData(win);
     });
-    // we must cache this because _getMostRecentBrowserWindow will always
-    // return null by the time quit-application occurs
+
+    // Now add an AsyncShutdown blocker that'll spin the event loop
+    // until the windows have all been flushed.
+
+    // This progress object will track the state of async window flushing
+    // and will help us debug things that go wrong with our AsyncShutdown
+    // blocker.
+    let progress = { total: -1, current: -1 };
+
+    // We're going down! Switch state so that we treat closing windows and
+    // tabs correctly.
+    RunState.setQuitting();
+
+    AsyncShutdown.quitApplicationGranted.addBlocker(
+      "SessionStore: flushing all windows",
+      this.flushAllWindowsAsync(progress),
+      () => progress);
+  },
+
+  /**
+   * An async Task that iterates all open browser windows and flushes
+   * any outstanding messages from their tabs. This will also close
+   * all of the currently open windows while we wait for the flushes
+   * to complete.
+   *
+   * @param progress (Object)
+   *        Optional progress object that will be updated as async
+   *        window flushing progresses. flushAllWindowsSync will
+   *        write to the following properties:
+   *
+   *        total (int):
+   *          The total number of windows to be flushed.
+   *        current (int):
+   *          The current window that we're waiting for a flush on.
+   *
+   * @return Promise
+   */
+  flushAllWindowsAsync: Task.async(function*(progress={}) {
+    let windowPromises = [];
+    // We collect flush promises and close each window immediately so that
+    // the user can't start changing any window state while we're waiting
+    // for the flushes to finish.
+    this._forEachBrowserWindow((win) => {
+      windowPromises.push(TabStateFlusher.flushWindow(win));
+      win.close();
+    });
+
+    progress.total = windowPromises.length;
+
+    // We'll iterate through the Promise array, yielding each one, so as to
+    // provide useful progress information to AsyncShutdown.
+    for (let i = 0; i < windowPromises.length; ++i) {
+      progress.current = i;
+      yield windowPromises[i];
+    };
+
+    // We must cache this because _getMostRecentBrowserWindow will always
+    // return null by the time quit-application occurs.
     var activeWindow = this._getMostRecentBrowserWindow();
     if (activeWindow)
       this.activeWindowSSiCache = activeWindow.__SSi || "";
     DirtyWindows.clear();
-  },
+  }),
 
   /**
    * On last browser window close
    */
   onLastWindowCloseGranted: function ssi_onLastWindowCloseGranted() {
     // last browser window is quitting.
     // remember to restore the last window when another browser window is opened
     // do not account for pref(resume_session_once) at this point, as it might be
--- a/browser/components/sessionstore/TabStateFlusher.jsm
+++ b/browser/components/sessionstore/TabStateFlusher.jsm
@@ -32,29 +32,47 @@ this.TabStateFlusher = Object.freeze({
    * that will resolve when we've heard back from all browsers.
    */
   flushWindow(window) {
     return TabStateFlusherInternal.flushWindow(window);
   },
 
   /**
    * Resolves the flush request with the given flush ID.
+   *
+   * @param browser (<xul:browser>)
+   *        The browser for which the flush is being resolved.
+   * @param flushID (int)
+   *        The ID of the flush that was sent to the browser.
+   * @param success (bool, optional)
+   *        Whether or not the flush succeeded.
+   * @param message (string, optional)
+   *        An error message that will be sent to the Console in the
+   *        event that a flush failed.
    */
-  resolve(browser, flushID) {
-    TabStateFlusherInternal.resolve(browser, flushID);
+  resolve(browser, flushID, success=true, message="") {
+    TabStateFlusherInternal.resolve(browser, flushID, success, message);
   },
 
   /**
    * Resolves all active flush requests for a given browser. This should be
    * used when the content process crashed or the final update message was
    * seen. In those cases we can't guarantee to ever hear back from the frame
    * script so we just resolve all requests instead of discarding them.
+   *
+   * @param browser (<xul:browser>)
+   *        The browser for which all flushes are being resolved.
+   * @param success (bool, optional)
+   *        Whether or not the flushes succeeded.
+   * @param message (string, optional)
+   *        An error message that will be sent to the Console in the
+   *        event that the flushes failed.
    */
-  resolveAll(browser) {
-    TabStateFlusherInternal.resolveAll(browser);
+  resolveAll(browser, success=true, message="") {
+    TabStateFlusherInternal.resolveAll(browser, success, message);
   }
 });
 
 var TabStateFlusherInternal = {
   // Stores the last request ID.
   _lastRequestID: 0,
 
   // A map storing all active requests per browser.
@@ -90,51 +108,77 @@ var TabStateFlusherInternal = {
   flushWindow(window) {
     let browsers = window.gBrowser.browsers;
     let promises = browsers.map((browser) => this.flush(browser));
     return Promise.all(promises);
   },
 
   /**
    * Resolves the flush request with the given flush ID.
+   *
+   * @param browser (<xul:browser>)
+   *        The browser for which the flush is being resolved.
+   * @param flushID (int)
+   *        The ID of the flush that was sent to the browser.
+   * @param success (bool, optional)
+   *        Whether or not the flush succeeded.
+   * @param message (string, optional)
+   *        An error message that will be sent to the Console in the
+   *        event that a flush failed.
    */
-  resolve(browser, flushID) {
+  resolve(browser, flushID, success=true, message="") {
     // Nothing to do if there are no pending flushes for the given browser.
     if (!this._requests.has(browser.permanentKey)) {
       return;
     }
 
     // Retrieve active requests for given browser.
     let perBrowserRequests = this._requests.get(browser.permanentKey);
     if (!perBrowserRequests.has(flushID)) {
       return;
     }
 
+    if (!success) {
+      Cu.reportError("Failed to flush browser: " + message);
+    }
+
     // Resolve the request with the given id.
     let resolve = perBrowserRequests.get(flushID);
     perBrowserRequests.delete(flushID);
-    resolve();
+    resolve(success);
   },
 
   /**
    * Resolves all active flush requests for a given browser. This should be
    * used when the content process crashed or the final update message was
    * seen. In those cases we can't guarantee to ever hear back from the frame
    * script so we just resolve all requests instead of discarding them.
+   *
+   * @param browser (<xul:browser>)
+   *        The browser for which all flushes are being resolved.
+   * @param success (bool, optional)
+   *        Whether or not the flushes succeeded.
+   * @param message (string, optional)
+   *        An error message that will be sent to the Console in the
+   *        event that the flushes failed.
    */
-  resolveAll(browser) {
+  resolveAll(browser, success=true, message="") {
     // Nothing to do if there are no pending flushes for the given browser.
     if (!this._requests.has(browser.permanentKey)) {
       return;
     }
 
     // Retrieve active requests for given browser.
     let perBrowserRequests = this._requests.get(browser.permanentKey);
 
+    if (!success) {
+      Cu.reportError("Failed to flush browser: " + message);
+    }
+
     // Resolve all requests.
     for (let resolve of perBrowserRequests.values()) {
-      resolve();
+      resolve(success);
     }
 
     // Clear active requests.
     perBrowserRequests.clear();
   }
 };
--- a/browser/components/sessionstore/test/browser_broadcast.js
+++ b/browser/components/sessionstore/test/browser_broadcast.js
@@ -18,39 +18,16 @@ add_task(function flush_on_tabclose() {
 
   let [{state: {storage}}] = JSON.parse(ss.getClosedTabData(window));
   is(storage["http://example.com"].test, "on-tab-close",
     "sessionStorage data has been flushed on TabClose");
 });
 
 /**
  * This test ensures we won't lose tab data queued in the content script when
- * the application tries to quit.
- */
-add_task(function flush_on_quit_requested() {
-  let tab = yield createTabWithStorageData(["http://example.com"]);
-  let browser = tab.linkedBrowser;
-
-  yield modifySessionStorage(browser, {test: "on-quit-requested"});
-
-  // Note that sending quit-application-requested should not interfere with
-  // other tests and code. We're just notifying about a shutdown request but
-  // we will not send quit-application-granted. Observers will thus assume
-  // that some other observer has canceled the request.
-  sendQuitApplicationRequested();
-
-  let {storage} = JSON.parse(ss.getTabState(tab));
-  is(storage["http://example.com"].test, "on-quit-requested",
-    "sessionStorage data has been flushed when a quit is requested");
-
-  gBrowser.removeTab(tab);
-});
-
-/**
- * This test ensures we won't lose tab data queued in the content script when
  * duplicating a tab.
  */
 add_task(function flush_on_duplicate() {
   let tab = yield createTabWithStorageData(["http://example.com"]);
   let browser = tab.linkedBrowser;
 
   yield modifySessionStorage(browser, {test: "on-duplicate"});
   let tab2 = ss.duplicateTab(window, tab);
@@ -147,14 +124,8 @@ function createTabWithStorageData(urls, 
       browser.loadURI(url);
       yield promiseBrowserLoaded(browser);
       yield modifySessionStorage(browser, {test: INITIAL_VALUE});
     }
 
     throw new Task.Result(tab);
   });
 }
-
-function sendQuitApplicationRequested() {
-  let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
-                     .createInstance(Ci.nsISupportsPRBool);
-  Services.obs.notifyObservers(cancelQuit, "quit-application-requested", null);
-}
--- a/browser/components/sessionstore/test/browser_send_async_message_oom.js
+++ b/browser/components/sessionstore/test/browser_send_async_message_oom.js
@@ -46,17 +46,21 @@ add_task(function*() {
 
 
   let promiseReported = new Promise(resolve => {
     browser.messageManager.addMessageListener("SessionStore:error", resolve);
   });
 
   // Attempt to flush. This should fail.
   let promiseFlushed = TabStateFlusher.flush(browser);
-  promiseFlushed.then(() => {throw new Error("Flush should have failed")});
+  promiseFlushed.then((success) => {
+    if (success) {
+      throw new Error("Flush should have failed")
+    }
+  });
 
   // The frame script should report an error.
   yield promiseReported;
 
   // Give us some time to handle that error.
   yield new Promise(resolve => setTimeout(resolve, 10));
 
   // By now, Telemetry should have been updated.
--- a/browser/extensions/loop/.eslintrc
+++ b/browser/extensions/loop/.eslintrc
@@ -54,28 +54,31 @@
     "comma-spacing": 2,
     "comma-style": 2,
     "computed-property-spacing": [2, "never"],
     "consistent-return": 2,
     "curly": [2, "all"],
     "dot-location": [2, "property"],
     "eol-last": 2,
     "eqeqeq": [2, "smart"],
+    "generator-star-spacing": [2, {"before": false, "after": true}],
     "jsx-quotes": [2, "prefer-double"],
     "key-spacing": [2, {"beforeColon": false, "afterColon": true }],
     "linebreak-style": [2, "unix"],
     "new-cap": 0,                 // TODO: set to 2
     "new-parens": 2,
     "no-alert": 2,
     "no-array-constructor": 2,
     "no-caller": 2,
     "no-catch-shadow": 2,
     "no-class-assign": 2,
     "no-const-assign": 2,
     "no-console": 0,              // Leave as 0. We use console logging in content code.
+    "no-duplicate-case": 2,
+    "no-else-return": 2,
     "no-empty": 2,
     "no-empty-label": 2,
     "no-eval": 2,
     "no-extend-native": 2, // XXX
     "no-extra-bind": 0,           // Leave as 0
     "no-extra-parens": 0,         // TODO: (bug?) [2, "functions"],
     "no-extra-semi": 2,
     "no-implied-eval": 2,
@@ -94,16 +97,17 @@
     "no-new-func": 2,
     "no-new-object": 2,
     "no-new-wrappers": 2,
     "no-octal-escape": 2,
     "no-process-exit": 2,
     "no-proto": 2,
     "no-return-assign": 2,
     "no-script-url": 2,
+    "no-self-compare": 2,
     "no-sequences": 2,
     "no-shadow": 2,
     "no-shadow-restricted-names": 2,
     "no-spaced-func": 2,
     "no-trailing-spaces": 2,
     "no-undef-init": 2,
     "no-underscore-dangle": 0,    // Leave as 0. Commonly used for private variables.
     "no-unexpected-multiline": 2,
@@ -126,28 +130,29 @@
     "space-in-parens": [2, "never"],
     "space-return-throw-case": 2,
     "space-unary-ops": [2, {"words": true, "nonwords": false}],
     "spaced-comment": [2, "always"],
     "strict": [2, "function"],
     "yoda": [2, "never"],
     // eslint-plugin-react rules. These are documented at
     // <https://github.com/yannickcr/eslint-plugin-react#list-of-supported-rules>
+    "react/jsx-curly-spacing": [2, "never"],
+    "react/jsx-no-bind": 2,
+    "react/jsx-no-duplicate-props": 2,
     "react/jsx-no-undef": 2,
     "react/jsx-sort-props": 2,
     "react/jsx-sort-prop-types": 2,
     "react/jsx-uses-vars": 2,
-    "react/jsx-no-duplicate-props": 2,
     "react/no-did-mount-set-state": 2,
     "react/no-did-update-set-state": 2,
     "react/no-unknown-property": 2,
     "react/prop-types": 2,
     "react/self-closing-comp": 2,
     "react/wrap-multilines": 2,
-    "react/jsx-curly-spacing": [2, "never"],
     // Not worth it: React is defined globally
     "react/jsx-uses-react": 0,
     "react/react-in-jsx-scope": 0,
     // These ones we don't want to ever enable
     "react/display-name": 0,
     "react/jsx-boolean-value": 0,
     "react/no-danger": 0,
     "react/no-multi-comp": 0
--- a/browser/extensions/loop/bootstrap.js
+++ b/browser/extensions/loop/bootstrap.js
@@ -794,22 +794,18 @@ function startup() {
   let sheets = ["chrome://loop-shared/skin/loop.css"];
 
   if (AppConstants.platform != "linux") {
     sheets.push("chrome://loop/skin/platform.css");
   }
 
   for (let sheet of sheets) {
     let styleSheetURI = Services.io.newURI(sheet, null, null);
-    // XXX We would love to specify AUTHOR_SHEET here and in shutdown, however
-    // bug 1228542 prevents us from doing that as we'd cause a lot of assertions
-    // in debug mode for tests. Once that is fixed, we should be able to change
-    // this, and remove the !important attributes from our syle sheets.
     styleSheetService.loadAndRegisterSheet(styleSheetURI,
-                                           styleSheetService.USER_SHEET);
+                                           styleSheetService.AUTHOR_SHEET);
   }
 }
 
 /**
  * Called when the add-on is shutting down, could be for re-installation
  * or just uninstall.
  */
 function shutdown() {
@@ -841,19 +837,19 @@ function shutdown() {
   // Unload stylesheets.
   let styleSheetService = Cc["@mozilla.org/content/style-sheet-service;1"]
     .getService(Components.interfaces.nsIStyleSheetService);
   let sheets = ["chrome://loop/content/addon/css/loop.css",
                 "chrome://loop/skin/platform.css"];
   for (let sheet of sheets) {
     let styleSheetURI = Services.io.newURI(sheet, null, null);
     if (styleSheetService.sheetRegistered(styleSheetURI,
-                                          styleSheetService.USER_SHEET)) {
+                                          styleSheetService.AUTHOR_SHEET)) {
       styleSheetService.unregisterSheet(styleSheetURI,
-                                        styleSheetService.USER_SHEET);
+                                        styleSheetService.AUTHOR_SHEET);
     }
   }
 
   // Unload modules.
   Cu.unload("chrome://loop/content/modules/MozLoopAPI.jsm");
   Cu.unload("chrome://loop/content/modules/LoopRooms.jsm");
   Cu.unload("chrome://loop/content/modules/MozLoopService.jsm");
 }
--- a/browser/extensions/loop/content/panels/js/otconfig.js
+++ b/browser/extensions/loop/content/panels/js/otconfig.js
@@ -1,12 +1,12 @@
 /* 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/. */
 
 window.OTProperties = {
-  cdnURL: "loop/"
+  cdnURL: ""
 };
 window.OTProperties.assetURL = window.OTProperties.cdnURL + "sdk-content/";
 window.OTProperties.configURL = window.OTProperties.assetURL + "js/dynamic_config.min.js";
 
 // We don't use the SDK's CSS. This will prevent spurious 404 errors.
 window.OTProperties.cssURL = "about:blank";
--- a/browser/extensions/loop/content/panels/js/panel.js
+++ b/browser/extensions/loop/content/panels/js/panel.js
@@ -375,19 +375,19 @@ loop.panel = (function(_, mozL10n) {
         )
       );
     },
 
     render: function() {
       var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
       if (roomUrl && roomUrl.location) {
         return this._renderIcon(roomUrl);
-      } else {
-        return this._renderDefaultIcon();
       }
+
+      return this._renderDefaultIcon();
     }
   });
 
   /**
    * Room list entry.
    *
    * Active Room means there are participants in the room.
    * Opened Room means the user is in the room.
--- a/browser/extensions/loop/content/panels/js/panel.jsx
+++ b/browser/extensions/loop/content/panels/js/panel.jsx
@@ -375,19 +375,19 @@ loop.panel = (function(_, mozL10n) {
         </div>
       );
     },
 
     render: function() {
       var roomUrl = this.props.roomUrls && this.props.roomUrls[0];
       if (roomUrl && roomUrl.location) {
         return this._renderIcon(roomUrl);
-      } else {
-        return this._renderDefaultIcon();
       }
+
+      return this._renderDefaultIcon();
     }
   });
 
   /**
    * Room list entry.
    *
    * Active Room means there are participants in the room.
    * Opened Room means the user is in the room.
--- a/browser/extensions/loop/content/panels/js/roomViews.js
+++ b/browser/extensions/loop/content/panels/js/roomViews.js
@@ -2,17 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var loop = loop || {};
 loop.roomViews = (function(mozL10n) {
   "use strict";
 
   var ROOM_STATES = loop.store.ROOM_STATES;
-  var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var sharedActions = loop.shared.actions;
   var sharedMixins = loop.shared.mixins;
   var sharedUtils = loop.shared.utils;
   var sharedViews = loop.shared.views;
 
   /**
    * ActiveRoomStore mixin.
@@ -617,16 +616,22 @@ loop.roomViews = (function(mozL10n) {
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
         this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
           publisherConfig: this.getDefaultPublisherConfig({
             publishVideo: !this.state.videoMuted
           })
         }));
       }
+
+      // Automatically start sharing a tab now we're ready to share.
+      if (this.state.roomState !== ROOM_STATES.SESSION_CONNECTED &&
+          nextState.roomState === ROOM_STATES.SESSION_CONNECTED) {
+        this.props.dispatcher.dispatch(new sharedActions.StartBrowserShare());
+      }
     },
 
     /**
      * User clicked on the "Leave" button.
      */
     leaveRoom: function() {
       if (this.state.used) {
         this.props.dispatcher.dispatch(new sharedActions.LeaveRoom());
@@ -762,21 +767,16 @@ loop.roomViews = (function(mozL10n) {
     render: function() {
       if (this.state.roomName || this.state.roomContextUrls) {
         var roomTitle = this.state.roomName ||
                         this.state.roomContextUrls[0].description ||
                         this.state.roomContextUrls[0].location;
         this.setTitle(roomTitle);
       }
 
-      var screenShareData = {
-        state: this.state.screenSharingState || SCREEN_SHARE_STATES.INACTIVE,
-        visible: true
-      };
-
       var shouldRenderInvitationOverlay = this._shouldRenderInvitationOverlay();
       var shouldRenderEditContextView = this.state.showEditContext;
       var roomData = this.props.roomStore.getStoreState("activeRoom");
 
       switch (this.state.roomState) {
         case ROOM_STATES.FAILED:
         case ROOM_STATES.FULL: {
           // Note: While rooms are set to hold a maximum of 2 participants, the
@@ -822,17 +822,16 @@ loop.roomViews = (function(mozL10n) {
                 screenSharePosterUrl: null, 
                 showInitialContext: false, 
                 useDesktopPaths: true}, 
                 React.createElement(sharedViews.ConversationToolbar, {
                   audio: { enabled: !this.state.audioMuted, visible: true}, 
                   dispatcher: this.props.dispatcher, 
                   hangup: this.leaveRoom, 
                   publishStream: this.publishStream, 
-                  screenShare: screenShareData, 
                   settingsMenuItems: settingsMenuItems, 
                   show: !shouldRenderEditContextView, 
                   showHangup: this.props.chatWindowDetached, 
                   video: { enabled: !this.state.videoMuted, visible: true}}), 
                 React.createElement(DesktopRoomInvitationView, {
                   dispatcher: this.props.dispatcher, 
                   error: this.state.error, 
                   onAddContextClick: this.handleAddContextClick, 
--- a/browser/extensions/loop/content/panels/js/roomViews.jsx
+++ b/browser/extensions/loop/content/panels/js/roomViews.jsx
@@ -2,17 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var loop = loop || {};
 loop.roomViews = (function(mozL10n) {
   "use strict";
 
   var ROOM_STATES = loop.store.ROOM_STATES;
-  var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var sharedActions = loop.shared.actions;
   var sharedMixins = loop.shared.mixins;
   var sharedUtils = loop.shared.utils;
   var sharedViews = loop.shared.views;
 
   /**
    * ActiveRoomStore mixin.
@@ -617,16 +616,22 @@ loop.roomViews = (function(mozL10n) {
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
         this.props.dispatcher.dispatch(new sharedActions.SetupStreamElements({
           publisherConfig: this.getDefaultPublisherConfig({
             publishVideo: !this.state.videoMuted
           })
         }));
       }
+
+      // Automatically start sharing a tab now we're ready to share.
+      if (this.state.roomState !== ROOM_STATES.SESSION_CONNECTED &&
+          nextState.roomState === ROOM_STATES.SESSION_CONNECTED) {
+        this.props.dispatcher.dispatch(new sharedActions.StartBrowserShare());
+      }
     },
 
     /**
      * User clicked on the "Leave" button.
      */
     leaveRoom: function() {
       if (this.state.used) {
         this.props.dispatcher.dispatch(new sharedActions.LeaveRoom());
@@ -762,21 +767,16 @@ loop.roomViews = (function(mozL10n) {
     render: function() {
       if (this.state.roomName || this.state.roomContextUrls) {
         var roomTitle = this.state.roomName ||
                         this.state.roomContextUrls[0].description ||
                         this.state.roomContextUrls[0].location;
         this.setTitle(roomTitle);
       }
 
-      var screenShareData = {
-        state: this.state.screenSharingState || SCREEN_SHARE_STATES.INACTIVE,
-        visible: true
-      };
-
       var shouldRenderInvitationOverlay = this._shouldRenderInvitationOverlay();
       var shouldRenderEditContextView = this.state.showEditContext;
       var roomData = this.props.roomStore.getStoreState("activeRoom");
 
       switch (this.state.roomState) {
         case ROOM_STATES.FAILED:
         case ROOM_STATES.FULL: {
           // Note: While rooms are set to hold a maximum of 2 participants, the
@@ -822,17 +822,16 @@ loop.roomViews = (function(mozL10n) {
                 screenSharePosterUrl={null}
                 showInitialContext={false}
                 useDesktopPaths={true}>
                 <sharedViews.ConversationToolbar
                   audio={{ enabled: !this.state.audioMuted, visible: true }}
                   dispatcher={this.props.dispatcher}
                   hangup={this.leaveRoom}
                   publishStream={this.publishStream}
-                  screenShare={screenShareData}
                   settingsMenuItems={settingsMenuItems}
                   show={!shouldRenderEditContextView}
                   showHangup={this.props.chatWindowDetached}
                   video={{ enabled: !this.state.videoMuted, visible: true }} />
                 <DesktopRoomInvitationView
                   dispatcher={this.props.dispatcher}
                   error={this.state.error}
                   onAddContextClick={this.handleAddContextClick}
--- a/browser/extensions/loop/content/shared/css/conversation.css
+++ b/browser/extensions/loop/content/shared/css/conversation.css
@@ -155,34 +155,16 @@ html[dir="rtl"] .conversation-toolbar-bt
   background-color: transparent;
 }
 
 .btn-settings:hover,
 .btn-settings:active {
   background-image: url("../img/settings-hover.svg");
 }
 
-.btn-screen-share {
-  background-image: url("../img/sharing.svg");
-}
-
-.btn-screen-share:hover,
-.btn-screen-share:active {
-  background-image: url("../img/sharing-hover.svg");
-}
-
-.btn-screen-share.active {
-  background-image: url("../img/sharing-active.svg");
-}
-
-.btn-screen-share.disabled {
-  /* The screen share button is in its pending state when its disabled. */
-  background-image: url("../img/sharing-pending.svg");
-}
-
 /* General Call (incoming or outgoing). */
 
 .call-action-group {
   display: flex;
   padding: 0 4px;
   width: 100%;
 }
 
@@ -314,37 +296,30 @@ html[dir="rtl"] .room-failure > .setting
   margin: 0.25rem 0;
   flex: none;
 }
 
 .failure-info-extra-failure {
   color: #f00;
 }
 
-.screen-share-menu.dropdown-menu,
 .settings-menu.dropdown-menu {
   bottom: 3.1rem;
 }
 
 .settings-menu.dropdown-menu {
   left: auto;
   /*offset dropdown menu to be above menu button*/
   right: 14px;
 }
 
-html[dir="rtl"] .screen-share-menu.dropdown-menu,
 html[dir="rtl"] .settings-menu.dropdown-menu {
   right: auto;
 }
 
-html[dir="rtl"] .screen-share-menu.dropdown-menu {
-  /*offset dropdown menu to be above menu button*/
-  left: 40px;
-}
-
 html[dir="rtl"] .settings-menu.dropdown-menu {
   /*offset dropdown menu to be above menu button*/
   left: 14px;
 }
 
 .settings-menu.dropdown-menu.menu-below {
   top: 11.5rem;
   bottom: auto;
--- a/browser/extensions/loop/content/shared/img/sharing-active.svg
+++ b/browser/extensions/loop/content/shared/img/sharing-active.svg
@@ -1,1 +0,0 @@
-<svg width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg"><g fill="none"><rect opacity=".95" fill="#fff" x="2" y="2" width="24" height="24" rx="40"/><path d="M2 14c0 6.629 5.373 12 12 12 6.629 0 12-5.373 12-12 0-6.629-5.373-12-12-12-6.629 0-12 5.373-12 12zm-2 0c0-7.732 6.267-14 14-14 7.732 0 14 6.267 14 14 0 7.732-6.267 14-14 14-7.732 0-14-6.267-14-14z" fill-opacity=".2" fill="#000"/><rect fill="#00A9DC" x="11" y="12" width="9" height="7" rx="1"/><path d="M17 11v-.997c0-.565-.447-1.003-.998-1.003h-7.005c-.551 0-.998.449-.998 1.003v4.994c0 .565.447 1.003.998 1.003h1.002v-3.997c0-.554.446-1.003.998-1.003h6.002z" fill="#00A9DC"/></g></svg>
--- a/browser/extensions/loop/content/shared/img/sharing-hover.svg
+++ b/browser/extensions/loop/content/shared/img/sharing-hover.svg
@@ -1,10 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <defs></defs>
-    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <rect id="Rectangle-1264-Copy" opacity="0.95" fill="#5CCCEE" x="2" y="2" width="24" height="24" rx="40"></rect>
-        <path d="M2,14 L2,14 C2,20.6288742 7.372583,26 14,26 L14,26 C20.6288742,26 26,20.627417 26,14 L26,14 C26,7.37112582 20.627417,2 14,2 L14,2 C7.37112582,2 2,7.372583 2,14 L2,14 Z M0,14 L0,14 C0,6.26754774 6.26702203,0 14,0 C21.7324523,0 28,6.26702203 28,14 C28,21.7324523 21.732978,28 14,28 C6.26754774,28 0,21.732978 0,14 L0,14 Z" id="Shape" fill-opacity="0.2" fill="#000000"></path>
-        <rect id="Rectangle-170-Copy-4" fill="#FFFFFF" x="11" y="12" width="9" height="7" rx="1"></rect>
-        <path d="M17,11 L17,10.0029293 C17,9.43788135 16.553384,9 16.0024554,9 L8.99754465,9 C8.4463114,9 8,9.44902676 8,10.0029293 L8,14.9970707 C8,15.5621186 8.44661595,16 8.99754465,16 L10,16 L10,12.0029293 C10,11.4490268 10.4463114,11 10.9975446,11 L17,11 Z" id="Rectangle-170-Copy" fill="#FFFFFF"></path>
-    </g>
-</svg>
\ No newline at end of file
--- a/browser/extensions/loop/content/shared/img/sharing-pending.svg
+++ b/browser/extensions/loop/content/shared/img/sharing-pending.svg
@@ -1,1 +0,0 @@
-<svg width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg"><g fill="none"><rect opacity=".95" fill="#999999" x="2" y="2" width="24" height="24" rx="40"/><path d="M2 14c0 6.629 5.373 12 12 12 6.629 0 12-5.373 12-12 0-6.629-5.373-12-12-12-6.629 0-12 5.373-12 12zm-2 0c0-7.732 6.267-14 14-14 7.732 0 14 6.267 14 14 0 7.732-6.267 14-14 14-7.732 0-14-6.267-14-14z" fill-opacity=".2" fill="#000"/><rect fill="#4A4A4A" x="11" y="12" width="9" height="7" rx="1"/><path d="M17 11v-.997c0-.565-.447-1.003-.998-1.003h-7.005c-.551 0-.998.449-.998 1.003v4.994c0 .565.447 1.003.998 1.003h1.002v-3.997c0-.554.446-1.003.998-1.003h6.002z" fill="#4A4A4A"/></g></svg>
--- a/browser/extensions/loop/content/shared/img/sharing.svg
+++ b/browser/extensions/loop/content/shared/img/sharing.svg
@@ -1,1 +0,0 @@
-<svg width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg"><g fill="none"><rect opacity=".95" fill="#fff" x="2" y="2" width="24" height="24" rx="40"/><path d="M2 14c0 6.629 5.373 12 12 12 6.629 0 12-5.373 12-12 0-6.629-5.373-12-12-12-6.629 0-12 5.373-12 12zm-2 0c0-7.732 6.267-14 14-14 7.732 0 14 6.267 14 14 0 7.732-6.267 14-14 14-7.732 0-14-6.267-14-14z" fill-opacity=".2" fill="#000"/><rect fill="#4A4A4A" x="11" y="12" width="9" height="7" rx="1"/><path d="M17 11v-.997c0-.565-.447-1.003-.998-1.003h-7.005c-.551 0-.998.449-.998 1.003v4.994c0 .565.447 1.003.998 1.003h1.002v-3.997c0-.554.446-1.003.998-1.003h6.002z" fill="#4A4A4A"/></g></svg>
\ No newline at end of file
--- a/browser/extensions/loop/content/shared/js/actions.js
+++ b/browser/extensions/loop/content/shared/js/actions.js
@@ -12,18 +12,19 @@ loop.shared.actions = (function() {
    * or by an async event, e.g. status received.
    *
    * They should be dispatched to stores via the dispatcher.
    */
 
   function Action(name, schema, values) {
     var validatedData = new loop.validate.Validator(schema || {})
                                          .validate(values || {});
-    for (var prop in validatedData)
+    for (var prop in validatedData) {
       this[prop] = validatedData[prop];
+    }
 
     this.name = name;
   }
 
   Action.define = function(name, schema) {
     return Action.bind(null, name, schema);
   };
 
@@ -200,21 +201,19 @@ loop.shared.actions = (function() {
     SetMute: Action.define("setMute", {
       // The part of the stream to enable, e.g. "audio" or "video"
       type: String,
       // Whether or not to enable the stream.
       enabled: Boolean
     }),
 
     /**
-     * Used to start a screen share.
+     * Used to start a browser tab share.
      */
-    StartScreenShare: Action.define("startScreenShare", {
-      // The part of the screen to share, e.g. "window" or "browser".
-      type: String
+    StartBrowserShare: Action.define("startBrowserShare", {
     }),
 
     /**
      * Used to end a screen share.
      */
     EndScreenShare: Action.define("endScreenShare", {
     }),
 
--- a/browser/extensions/loop/content/shared/js/activeRoomStore.js
+++ b/browser/extensions/loop/content/shared/js/activeRoomStore.js
@@ -252,17 +252,17 @@ loop.store.ActiveRoomStore = (function()
         "remotePeerConnected",
         "windowUnload",
         "leaveRoom",
         "feedbackComplete",
         "mediaStreamCreated",
         "mediaStreamDestroyed",
         "remoteVideoStatus",
         "videoDimensionsChanged",
-        "startScreenShare",
+        "startBrowserShare",
         "endScreenShare",
         "updateSocialShareInfo",
         "connectionStatus",
         "mediaConnected"
       ];
       // Register actions that are only used on Desktop.
       if (this._isDesktop) {
         // 'receivedTextChatMessage' and  'sendTextChatMessage' actions are only
@@ -915,43 +915,40 @@ loop.store.ActiveRoomStore = (function()
         // Just update the current share.
         this._sdkDriver.switchAcquiredWindow(windowId);
       } else {
         console.error("Unexpectedly received windowId for browser sharing when pending");
       }
     },
 
     /**
-     * Initiates a screen sharing publisher.
+     * Initiates a browser tab sharing publisher.
      *
-     * @param {sharedActions.StartScreenShare} actionData
+     * @param {sharedActions.StartBrowserShare} actionData
      */
-    startScreenShare: function(actionData) {
+    startBrowserShare: function(actionData) {
       // For the unit test we already set the state here, instead of indirectly
       // via an action, because actions are queued thus depending on the
       // asynchronous nature of `loop.request`.
       this.setStoreState({ screenSharingState: SCREEN_SHARE_STATES.PENDING });
       this.dispatchAction(new sharedActions.ScreenSharingState({
         state: SCREEN_SHARE_STATES.PENDING
       }));
 
       var options = {
-        videoSource: actionData.type
+        videoSource: "browser"
       };
-      if (options.videoSource === "browser") {
-        this._browserSharingListener = this._handleSwitchBrowserShare.bind(this);
+      this._browserSharingListener = this._handleSwitchBrowserShare.bind(this);
 
-        // Set up a listener for watching screen shares. This will get notified
-        // with the first windowId when it is added, so we start off the sharing
-        // from within the listener.
-        loop.request("AddBrowserSharingListener").then(this._browserSharingListener);
-        loop.subscribe("BrowserSwitch", this._browserSharingListener);
-      } else {
-        this._sdkDriver.startScreenShare(options);
-      }
+      // Set up a listener for watching screen shares. This will get notified
+      // with the first windowId when it is added, so we start off the sharing
+      // from within the listener.
+      loop.request("AddBrowserSharingListener", this.getStoreState().windowId)
+        .then(this._browserSharingListener);
+      loop.subscribe("BrowserSwitch", this._browserSharingListener);
     },
 
     /**
      * Ends an active screenshare session.
      */
     endScreenShare: function() {
       if (this._browserSharingListener) {
         // Remove the browser sharing listener as we don't need it now.
--- a/browser/extensions/loop/content/shared/js/views.js
+++ b/browser/extensions/loop/content/shared/js/views.js
@@ -5,17 +5,16 @@
 var loop = loop || {};
 loop.shared = loop.shared || {};
 loop.shared.views = (function(_, mozL10n) {
   "use strict";
 
   var sharedActions = loop.shared.actions;
   var sharedModels = loop.shared.models;
   var sharedMixins = loop.shared.mixins;
-  var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
 
   /**
    * Hang-up control button.
    *
    * Required props:
    * - {Function} action  Function to be executed on click.
    * - {String}   title   Tooltip functionality.
    */
@@ -100,124 +99,16 @@ loop.shared.views = (function(_, mozL10n
         React.createElement("button", {className: this._getClasses(), 
                 onClick: this.handleClick, 
                 title: this._getTitle()})
       );
     }
   });
 
   /**
-   * Screen sharing control button.
-   *
-   * Required props:
-   * - {loop.Dispatcher} dispatcher  The dispatcher instance
-   * - {Boolean}         visible     Set to true to display the button
-   * - {String}          state       One of the screen sharing states, see
-   *                                 loop.shared.utils.SCREEN_SHARE_STATES
-   */
-  var ScreenShareControlButton = React.createClass({displayName: "ScreenShareControlButton",
-    mixins: [sharedMixins.DropdownMenuMixin()],
-
-    propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      state: React.PropTypes.string.isRequired,
-      visible: React.PropTypes.bool.isRequired
-    },
-
-    getInitialState: function() {
-      var os = loop.shared.utils.getOS();
-      var osVersion = loop.shared.utils.getOSVersion();
-      // Disable screensharing on older OSX and Windows versions.
-      if ((os.indexOf("mac") > -1 && osVersion.major <= 10 && osVersion.minor <= 6) ||
-          (os.indexOf("win") > -1 && osVersion.major <= 5 && osVersion.minor <= 2)) {
-        return { windowSharingDisabled: true };
-      }
-      return { windowSharingDisabled: false };
-    },
-
-    handleClick: function() {
-      if (this.props.state === SCREEN_SHARE_STATES.ACTIVE) {
-        this.props.dispatcher.dispatch(
-          new sharedActions.EndScreenShare({}));
-      } else {
-        this.toggleDropdownMenu();
-      }
-    },
-
-    _startScreenShare: function(type) {
-      this.props.dispatcher.dispatch(new sharedActions.StartScreenShare({
-        type: type
-      }));
-    },
-
-    _handleShareTabs: function() {
-      this._startScreenShare("browser");
-      this.hideDropdownMenu();
-    },
-
-    _handleShareWindows: function() {
-      this._startScreenShare("window");
-      this.hideDropdownMenu();
-    },
-
-    _getTitle: function() {
-      var prefix = this.props.state === SCREEN_SHARE_STATES.ACTIVE ?
-        "active" : "inactive";
-
-      return mozL10n.get(prefix + "_screenshare_button_title");
-    },
-
-    render: function() {
-      if (!this.props.visible) {
-        return null;
-      }
-
-      var cx = classNames;
-
-      var isActive = this.props.state === SCREEN_SHARE_STATES.ACTIVE;
-      var screenShareClasses = cx({
-        "btn": true,
-        "btn-screen-share": true,
-        "transparent-button": true,
-        "menu-showing": this.state.showMenu,
-        "active": isActive,
-        "disabled": this.props.state === SCREEN_SHARE_STATES.PENDING
-      });
-      var dropdownMenuClasses = cx({
-        "screen-share-menu": true,
-        "dropdown-menu": true,
-        "hide": !this.state.showMenu
-      });
-      var windowSharingClasses = cx({
-        "dropdown-menu-item": true,
-        "disabled": this.state.windowSharingDisabled
-      });
-
-      return (
-        React.createElement("div", null, 
-          React.createElement("button", {className: screenShareClasses, 
-                  onClick: this.handleClick, 
-                  ref: "anchor", 
-                  title: this._getTitle()}, 
-            isActive ? null : React.createElement("span", {className: "chevron"})
-          ), 
-          React.createElement("ul", {className: dropdownMenuClasses, ref: "menu"}, 
-            React.createElement("li", {className: "dropdown-menu-item", onClick: this._handleShareTabs}, 
-              mozL10n.get("share_tabs_button_title2")
-            ), 
-            React.createElement("li", {className: windowSharingClasses, onClick: this._handleShareWindows}, 
-              mozL10n.get("share_windows_button_title")
-            )
-          )
-        )
-      );
-    }
-  });
-
-  /**
    * Settings control button.
    */
   var SettingsControlButton = React.createClass({displayName: "SettingsControlButton",
     propTypes: {
       // Set to true if the menu should be below the button rather than above.
       menuBelow: React.PropTypes.bool,
       menuItems: React.PropTypes.array
     },
@@ -386,34 +277,32 @@ loop.shared.views = (function(_, mozL10n
   /**
    * Conversation controls.
    */
   var ConversationToolbar = React.createClass({displayName: "ConversationToolbar",
     getDefaultProps: function() {
       return {
         video: { enabled: true, visible: true },
         audio: { enabled: true, visible: true },
-        screenShare: { state: SCREEN_SHARE_STATES.INACTIVE, visible: false },
         settingsMenuItems: null,
         showHangup: true
       };
     },
 
     getInitialState: function() {
       return {
         idle: false
       };
     },
 
     propTypes: {
       audio: React.PropTypes.object.isRequired,
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       hangup: React.PropTypes.func.isRequired,
       publishStream: React.PropTypes.func.isRequired,
-      screenShare: React.PropTypes.object,
       settingsMenuItems: React.PropTypes.array,
       show: React.PropTypes.bool.isRequired,
       showHangup: React.PropTypes.bool,
       video: React.PropTypes.object.isRequired
     },
 
     handleClickHangup: function() {
       this.props.hangup();
@@ -520,21 +409,16 @@ loop.shared.views = (function(_, mozL10n
                                     scope: "local", type: "video", 
                                     visible: this.props.video.visible}), 
                 React.createElement(MediaControlButton, {action: this.handleToggleAudio, 
                                     enabled: this.props.audio.enabled, 
                                     scope: "local", type: "audio", 
                                     visible: this.props.audio.visible})
             )
           ), 
-          React.createElement("li", {className: "conversation-toolbar-btn-box"}, 
-            React.createElement(ScreenShareControlButton, {dispatcher: this.props.dispatcher, 
-                                      state: this.props.screenShare.state, 
-                                      visible: this.props.screenShare.visible})
-          ), 
           React.createElement("li", {className: "conversation-toolbar-btn-box btn-edit-entry"}, 
             React.createElement(SettingsControlButton, {menuItems: this.props.settingsMenuItems})
           )
         )
       );
     }
   });
 
@@ -1136,12 +1020,11 @@ loop.shared.views = (function(_, mozL10n
     Checkbox: Checkbox,
     ContextUrlView: ContextUrlView,
     ConversationToolbar: ConversationToolbar,
     MediaControlButton: MediaControlButton,
     MediaLayoutView: MediaLayoutView,
     MediaView: MediaView,
     LoadingView: LoadingView,
     SettingsControlButton: SettingsControlButton,
-    ScreenShareControlButton: ScreenShareControlButton,
     NotificationListView: NotificationListView
   };
 })(_, navigator.mozL10n || document.mozL10n);
--- a/browser/extensions/loop/content/shared/js/views.jsx
+++ b/browser/extensions/loop/content/shared/js/views.jsx
@@ -5,17 +5,16 @@
 var loop = loop || {};
 loop.shared = loop.shared || {};
 loop.shared.views = (function(_, mozL10n) {
   "use strict";
 
   var sharedActions = loop.shared.actions;
   var sharedModels = loop.shared.models;
   var sharedMixins = loop.shared.mixins;
-  var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
 
   /**
    * Hang-up control button.
    *
    * Required props:
    * - {Function} action  Function to be executed on click.
    * - {String}   title   Tooltip functionality.
    */
@@ -100,124 +99,16 @@ loop.shared.views = (function(_, mozL10n
         <button className={this._getClasses()}
                 onClick={this.handleClick}
                 title={this._getTitle()}></button>
       );
     }
   });
 
   /**
-   * Screen sharing control button.
-   *
-   * Required props:
-   * - {loop.Dispatcher} dispatcher  The dispatcher instance
-   * - {Boolean}         visible     Set to true to display the button
-   * - {String}          state       One of the screen sharing states, see
-   *                                 loop.shared.utils.SCREEN_SHARE_STATES
-   */
-  var ScreenShareControlButton = React.createClass({
-    mixins: [sharedMixins.DropdownMenuMixin()],
-
-    propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      state: React.PropTypes.string.isRequired,
-      visible: React.PropTypes.bool.isRequired
-    },
-
-    getInitialState: function() {
-      var os = loop.shared.utils.getOS();
-      var osVersion = loop.shared.utils.getOSVersion();
-      // Disable screensharing on older OSX and Windows versions.
-      if ((os.indexOf("mac") > -1 && osVersion.major <= 10 && osVersion.minor <= 6) ||
-          (os.indexOf("win") > -1 && osVersion.major <= 5 && osVersion.minor <= 2)) {
-        return { windowSharingDisabled: true };
-      }
-      return { windowSharingDisabled: false };
-    },
-
-    handleClick: function() {
-      if (this.props.state === SCREEN_SHARE_STATES.ACTIVE) {
-        this.props.dispatcher.dispatch(
-          new sharedActions.EndScreenShare({}));
-      } else {
-        this.toggleDropdownMenu();
-      }
-    },
-
-    _startScreenShare: function(type) {
-      this.props.dispatcher.dispatch(new sharedActions.StartScreenShare({
-        type: type
-      }));
-    },
-
-    _handleShareTabs: function() {
-      this._startScreenShare("browser");
-      this.hideDropdownMenu();
-    },
-
-    _handleShareWindows: function() {
-      this._startScreenShare("window");
-      this.hideDropdownMenu();
-    },
-
-    _getTitle: function() {
-      var prefix = this.props.state === SCREEN_SHARE_STATES.ACTIVE ?
-        "active" : "inactive";
-
-      return mozL10n.get(prefix + "_screenshare_button_title");
-    },
-
-    render: function() {
-      if (!this.props.visible) {
-        return null;
-      }
-
-      var cx = classNames;
-
-      var isActive = this.props.state === SCREEN_SHARE_STATES.ACTIVE;
-      var screenShareClasses = cx({
-        "btn": true,
-        "btn-screen-share": true,
-        "transparent-button": true,
-        "menu-showing": this.state.showMenu,
-        "active": isActive,
-        "disabled": this.props.state === SCREEN_SHARE_STATES.PENDING
-      });
-      var dropdownMenuClasses = cx({
-        "screen-share-menu": true,
-        "dropdown-menu": true,
-        "hide": !this.state.showMenu
-      });
-      var windowSharingClasses = cx({
-        "dropdown-menu-item": true,
-        "disabled": this.state.windowSharingDisabled
-      });
-
-      return (
-        <div>
-          <button className={screenShareClasses}
-                  onClick={this.handleClick}
-                  ref="anchor"
-                  title={this._getTitle()}>
-            {isActive ? null : <span className="chevron"/>}
-          </button>
-          <ul className={dropdownMenuClasses} ref="menu">
-            <li className="dropdown-menu-item" onClick={this._handleShareTabs}>
-              {mozL10n.get("share_tabs_button_title2")}
-            </li>
-            <li className={windowSharingClasses} onClick={this._handleShareWindows}>
-              {mozL10n.get("share_windows_button_title")}
-            </li>
-          </ul>
-        </div>
-      );
-    }
-  });
-
-  /**
    * Settings control button.
    */
   var SettingsControlButton = React.createClass({
     propTypes: {
       // Set to true if the menu should be below the button rather than above.
       menuBelow: React.PropTypes.bool,
       menuItems: React.PropTypes.array
     },
@@ -386,34 +277,32 @@ loop.shared.views = (function(_, mozL10n
   /**
    * Conversation controls.
    */
   var ConversationToolbar = React.createClass({
     getDefaultProps: function() {
       return {
         video: { enabled: true, visible: true },
         audio: { enabled: true, visible: true },
-        screenShare: { state: SCREEN_SHARE_STATES.INACTIVE, visible: false },
         settingsMenuItems: null,
         showHangup: true
       };
     },
 
     getInitialState: function() {
       return {
         idle: false
       };
     },
 
     propTypes: {
       audio: React.PropTypes.object.isRequired,
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
       hangup: React.PropTypes.func.isRequired,
       publishStream: React.PropTypes.func.isRequired,
-      screenShare: React.PropTypes.object,
       settingsMenuItems: React.PropTypes.array,
       show: React.PropTypes.bool.isRequired,
       showHangup: React.PropTypes.bool,
       video: React.PropTypes.object.isRequired
     },
 
     handleClickHangup: function() {
       this.props.hangup();
@@ -520,21 +409,16 @@ loop.shared.views = (function(_, mozL10n
                                     scope="local" type="video"
                                     visible={this.props.video.visible}/>
                 <MediaControlButton action={this.handleToggleAudio}
                                     enabled={this.props.audio.enabled}
                                     scope="local" type="audio"
                                     visible={this.props.audio.visible}/>
             </div>
           </li>
-          <li className="conversation-toolbar-btn-box">
-            <ScreenShareControlButton dispatcher={this.props.dispatcher}
-                                      state={this.props.screenShare.state}
-                                      visible={this.props.screenShare.visible} />
-          </li>
           <li className="conversation-toolbar-btn-box btn-edit-entry">
             <SettingsControlButton menuItems={this.props.settingsMenuItems} />
           </li>
         </ul>
       );
     }
   });
 
@@ -1136,12 +1020,11 @@ loop.shared.views = (function(_, mozL10n
     Checkbox: Checkbox,
     ContextUrlView: ContextUrlView,
     ConversationToolbar: ConversationToolbar,
     MediaControlButton: MediaControlButton,
     MediaLayoutView: MediaLayoutView,
     MediaView: MediaView,
     LoadingView: LoadingView,
     SettingsControlButton: SettingsControlButton,
-    ScreenShareControlButton: ScreenShareControlButton,
     NotificationListView: NotificationListView
   };
 })(_, navigator.mozL10n || document.mozL10n);
--- a/browser/extensions/loop/skin/osx/platform.css
+++ b/browser/extensions/loop/skin/osx/platform.css
@@ -1,47 +1,41 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 /* Only apply to browser.xul documents */
 @-moz-document url("chrome://browser/content/browser.xul") {
-  /**
-   * XXX Due to bug 1228542, anything in this file that overrides a browser style
-   * must specify !important. Otherwise the style won't get applied correctly
-   * due to the limitations caused by the bug.
-   */
-
   notification[value="loop-sharing-notification"] {
-    background: #00a9dc !important;
-    padding: 0 !important;
-    border: 0 !important;
+    background: #00a9dc;
+    padding: 0;
+    border: 0;
   }
 
   notification[value="loop-sharing-notification"].paused {
-    background: #ebebeb !important;
+    background: #ebebeb;
   }
 
   notification[value="loop-sharing-notification"] .notification-button {
-    background: #fff !important;
-    border-radius: 0 !important;
+    background: #fff;
+    border-radius: 0;
   }
 
   notification[value="loop-sharing-notification"].paused .notification-button {
-    background: #57bd35 !important;
+    background: #57bd35;
   }
 
   notification[value="loop-sharing-notification"].paused .notification-button:hover {
-    background: #39a017 !important;
+    background: #39a017;
   }
 
   notification[value="loop-sharing-notification"] .notification-button:hover,
   notification[value="loop-sharing-notification"].paused .notification-button-default:hover {
-    background: #ebebeb !important;
+    background: #ebebeb;
   }
 
   notification[value="loop-sharing-notification"] .notification-button-default,
   notification[value="loop-sharing-notification"].paused .notification-button-default {
-    background: #fff !important;
+    background: #fff;
   }
 }
--- a/browser/extensions/loop/skin/shared/loop.css
+++ b/browser/extensions/loop/skin/shared/loop.css
@@ -1,30 +1,24 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
 
 /* Only apply to browser.xul documents */
 @-moz-document url("chrome://browser/content/browser.xul") {
-  /**
-   * XXX Due to bug 1228542, anything in this file that overrides a browser style
-   * must specify !important. Otherwise the style won't get applied correctly
-   * due to the limitations caused by the bug.
-   */
-
   /*
      XXX Copied from browser/themes/<platform>/browser.css. Should really be
      changing the sizes of icons in files to 16px x 16px and no borders.
    */
   :-moz-any(toolbar, .widget-overflow-list) #loop-button > .toolbarbutton-icon,
   :-moz-any(toolbar, .widget-overflow-list) #loop-button > :-moz-any(.toolbarbutton-menubutton-button, .toolbarbutton-badge-stack) > .toolbarbutton-icon {
-    max-width: 18px !important;
-    margin: 0 !important;
+    max-width: 18px;
+    margin: 0;
   }
 
   #loop-button {
     list-style-image: url(chrome://loop/skin/toolbar.png);
     -moz-image-region: rect(0, 18px, 18px, 0);
   }
 
   toolbar[brighttext] #loop-button {
@@ -94,17 +88,17 @@
     #loop-button[cui-areatype="menu-panel"],
     toolbarpaletteitem[place="palette"] > #loop-button {
       list-style-image: url(chrome://loop/skin/menuPanel@2x.png);
       -moz-image-region: rect(0, 64px, 64px, 0);
     }
 
     /* Make sure that the state icons are not shown in the customization palette. */
     toolbarpaletteitem[place="palette"] > #loop-button {
-      -moz-image-region: rect(0, 64px, 64px, 0) !important;
+      -moz-image-region: rect(0, 64px, 64px, 0);
     }
 
     #loop-button[cui-areatype="menu-panel"][state="disabled"],
     #loop-button[cui-areatype="menu-panel"][disabled="true"] {
       -moz-image-region: rect(0, 128px, 64px, 64px);
     }
 
     #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] {
@@ -132,17 +126,17 @@
     #loop-button[cui-areatype="menu-panel"],
     toolbarpaletteitem[place="palette"] > #loop-button {
       list-style-image: url(chrome://loop/skin/menuPanel.png);
       -moz-image-region: rect(0, 32px, 32px, 0);
     }
 
     /* Make sure that the state icons are not shown in the customization palette. */
     toolbarpaletteitem[place="palette"] > #loop-button {
-      -moz-image-region: rect(0, 32px, 32px, 0) !important;
+      -moz-image-region: rect(0, 32px, 32px, 0);
     }
 
     #loop-button[cui-areatype="menu-panel"][state="disabled"],
     #loop-button[cui-areatype="menu-panel"][disabled="true"] {
       -moz-image-region: rect(0, 64px, 32px, 32px);
     }
 
     #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] {
@@ -162,125 +156,125 @@
     }
 
     #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) {
       -moz-image-region: rect(0, 224px, 32px, 192px);
     }
   }
 
   notification[value="loop-sharing-notification"] {
-    -moz-appearance: none !important;
-    height: 40px !important;
-    background-color: #00a9dc !important;
-    box-shadow: 0 40px 1px rgba(0,0,0,.5) inset !important;
+    -moz-appearance: none;
+    height: 40px;
+    background-color: #00a9dc;
+    box-shadow: 0 40px 1px rgba(0,0,0,.5) inset;
   }
 
   notification[value="loop-sharing-notification"].paused {
-    background-color: #ebebeb !important;
+    background-color: #ebebeb;
   }
 
   notification[value="loop-sharing-notification"] .notification-inner {
-    color: #fff !important;
-    padding: 0 !important;
+    color: #fff;
+    padding: 0;
   }
 
   notification[value="loop-sharing-notification"].paused .notification-inner {
-    color: #00a9dc !important;
+    color: #00a9dc;
   }
 
   notification[value="loop-sharing-notification"] .notification-button {
-    -moz-appearance: none !important;
-    background-color: #fff !important;
-    border: 0 !important;
-    border-right: solid 1px #ebebeb !important;
-    width: 100px !important;
-    height: 40px !important;
-    margin: 0 !important;
-    list-style-image: url(chrome://loop/content/shared/img/pause-12x12.svg) !important;
-    box-shadow: 0 40px 1px rgba(0,0,0,.5) inset !important;
-    text-shadow: none !important;
+    -moz-appearance: none;
+    background-color: #fff;
+    border: 0;
+    border-right: solid 1px #ebebeb;
+    width: 100px;
+    height: 40px;
+    margin: 0;
+    list-style-image: url(chrome://loop/content/shared/img/pause-12x12.svg);
+    box-shadow: 0 40px 1px rgba(0,0,0,.5) inset;
+    text-shadow: none;
   }
 
   notification[value="loop-sharing-notification"] .notification-button:-moz-locale-dir(rtl) {
-    border-right: 0 !important;
-    border-left: solid 1px #ebebeb !important;
+    border-right: 0;
+    border-left: solid 1px #ebebeb;
   }
 
   notification[value="loop-sharing-notification"].paused .notification-button {
-    background-color: #57bd35 !important;
-    color: #fff !important;
-    list-style-image: url(chrome://loop/content/shared/img/play-12x12.svg) !important;
+    background-color: #57bd35;
+    color: #fff;
+    list-style-image: url(chrome://loop/content/shared/img/play-12x12.svg);
   }
 
   notification[value="loop-sharing-notification"].paused .notification-button:hover {
-    background-color: #39a017 !important;
+    background-color: #39a017;
   }
 
   notification[value="loop-sharing-notification"] .notification-button:hover,
   notification[value="loop-sharing-notification"].paused .notification-button-default:hover {
-    background-color: #ebebeb !important;
+    background-color: #ebebeb;
   }
 
   notification[value="loop-sharing-notification"] .notification-button-default,
   notification[value="loop-sharing-notification"].paused .notification-button-default {
-    color: #d92215 !important;
-    background-color: #fff !important;
-    border-right: 0 !important;
-    list-style-image: url(chrome://loop/content/shared/img/stop-12x12.svg) !important;
+    color: #d92215;
+    background-color: #fff;
+    border-right: 0;
+    list-style-image: url(chrome://loop/content/shared/img/stop-12x12.svg);
   }
 
   notification[value="loop-sharing-notification"] .notification-button .button-icon {
-    display: block !important;
-    -moz-margin-end: 6px !important;
+    display: block;
+    -moz-margin-end: 6px;
   }
 
   notification[value="loop-sharing-notification"] .button-menubutton-button {
-    min-width: 0 !important;
+    min-width: 0;
   }
 
   notification[value="loop-sharing-notification"] .messageImage {
-    list-style-image: url(chrome://loop/content/shared/img/icons-16x16.svg#loop-icon-white) !important;
-    margin-inline-start: 14px !important;
+    list-style-image: url(chrome://loop/content/shared/img/icons-16x16.svg#loop-icon-white);
+    margin-inline-start: 14px;
   }
 
   notification[value="loop-sharing-notification"].paused .messageImage {
-    list-style-image: url(chrome://loop/content/shared/img/icons-16x16.svg#loop-icon-still) !important;
+    list-style-image: url(chrome://loop/content/shared/img/icons-16x16.svg#loop-icon-still);
   }
 
   notification[value="loop-sharing-notification"] .close-icon {
-    display: none !important;
+    display: none;
   }
 
   chatbox[src^="about:loopconversation#"] > .chat-titlebar {
-    background-color: #00a9dc !important;
-    border-color: #00a9dc !important;
+    background-color: #00a9dc;
+    border-color: #00a9dc;
   }
 
   chatbox[src^="about:loopconversation#"] .chat-title {
-    color: white !important;
+    color: white;
   }
 
   chatbox[src^="about:loopconversation#"] .chat-minimize-button {
-    list-style-image: url("chrome://browser/skin/social/chat-icons.svg#minimize-white") !important;
+    list-style-image: url("chrome://browser/skin/social/chat-icons.svg#minimize-white");
   }
 
   chatbox[src^="about:loopconversation#"] .chat-swap-button {
-    list-style-image: url("chrome://browser/skin/social/chat-icons.svg#expand-white") !important;
+    list-style-image: url("chrome://browser/skin/social/chat-icons.svg#expand-white");
   }
 
   .chat-loop-hangup {
-    list-style-image: url("chrome://browser/skin/social/chat-icons.svg#exit-white") !important;
-    background-color: #d13f1a !important;
-    border: 1px solid #d13f1a !important;
-    border-top-right-radius: 4px !important;
-    width: 32px !important;
-    height: 26px !important;
-    margin-top: -6px !important;
-    margin-bottom: -5px !important;
-    -moz-margin-start: 6px !important;
-    -moz-margin-end: -5px !important;
+    list-style-image: url("chrome://browser/skin/social/chat-icons.svg#exit-white");
+    background-color: #d13f1a;
+    border: 1px solid #d13f1a;
+    border-top-right-radius: 4px;
+    width: 32px;
+    height: 26px;
+    margin-top: -6px;
+    margin-bottom: -5px;
+    -moz-margin-start: 6px;
+    -moz-margin-end: -5px;
   }
 
   .chat-toolbarbutton.chat-loop-hangup:-moz-any(:hover,:hover:active) {
-    background-color: #ef6745 !important;
-    border-color: #ef6745 !important;
+    background-color: #ef6745;
+    border-color: #ef6745;
   }
 }
--- a/browser/extensions/loop/standalone/content/index.html
+++ b/browser/extensions/loop/standalone/content/index.html
@@ -161,17 +161,17 @@
           disconnect: function() {},
           observe: function () {},
           takeRecords: function() {}
         };
         window.MutationObserver = myMutationObserver;
       }
 
       window.OTProperties = {
-        cdnURL: "shared/libs/"
+        cdnURL: "shared/vendor/"
       };
       window.OTProperties.assetURL = window.OTProperties.cdnURL + "sdk-content/";
       window.OTProperties.configURL = window.OTProperties.assetURL + "js/dynamic_config.min.js";
 
       // We don't use the SDK's CSS. This will prevent spurious 404 errors.
       window.OTProperties.cssURL = "about:blank";
     </script>
 
--- a/browser/extensions/loop/standalone/content/l10n/en-US/loop.properties
+++ b/browser/extensions/loop/standalone/content/l10n/en-US/loop.properties
@@ -7,18 +7,16 @@ generic_failure_no_reason2=Would you lik
 tos_failure_message={{clientShortname}} is not available in your country.
 
 retry_call_button=Retry
 unable_retrieve_call_info=Unable to retrieve conversation information.
 mute_local_audio_button_title=Mute your audio
 unmute_local_audio_button_title=Unmute your audio
 mute_local_video_button_title2=Disable video
 unmute_local_video_button_title2=Enable video
-active_screenshare_button_title=Stop sharing
-inactive_screenshare_button_title=Share your screen
 
 welcome=Welcome to the {{clientShortname}} web client.
 incompatible_browser_heading=Oops!
 incompatible_browser_message=Firefox Hello only works in browsers that support WebRTC
 unsupported_platform_heading=Sorry!
 unsupported_platform_message={{platform}} does not currently support {{clientShortname}}
 unsupported_platform_ios=iOS
 unsupported_platform_windows_phone=Windows Phone
--- a/browser/extensions/loop/standalone/package.json
+++ b/browser/extensions/loop/standalone/package.json
@@ -9,19 +9,19 @@
   "engines": {
     "node": "0.10.x",
     "npm": "2.14.x"
   },
   "dependencies": {},
   "devDependencies": {
     "classnames": "2.2.x",
     "compression": "1.5.x",
-    "eslint": "1.6.x",
+    "eslint": "1.10.x",
     "eslint-plugin-mozilla": "../../../../testing/eslint-plugin-mozilla",
-    "eslint-plugin-react": "3.5.x",
+    "eslint-plugin-react": "3.10.x",
     "exports-loader": "0.6.x",
     "expose-loader": "0.7.x",
     "express": "4.x",
     "imports-loader": "0.6.x",
     "react": "0.13.3",
     "script-loader": "0.6.x",
     "webpack": "1.12.x"
   },
--- a/browser/extensions/loop/test/desktop-local/roomViews_test.js
+++ b/browser/extensions/loop/test/desktop-local/roomViews_test.js
@@ -395,21 +395,19 @@ describe("loop.roomViews", function() {
 
       view.setState({ audioMuted: true });
 
       var muteBtn = view.getDOMNode().querySelector(".btn-mute-audio");
 
       expect(muteBtn.classList.contains("muted")).eql(true);
     });
 
-    it("should dispatch a `StartScreenShare` action when sharing is not active and the screen share button is pressed", function() {
+    it("should dispatch a `SetMute` action when the mute button is pressed", function() {
       view = mountTestComponent();
 
-      view.setState({ screenSharingState: SCREEN_SHARE_STATES.INACTIVE });
-
       var muteBtn = view.getDOMNode().querySelector(".btn-mute-video");
 
       React.addons.TestUtils.Simulate.click(muteBtn);
 
       sinon.assert.calledWithMatch(dispatcher.dispatch,
         sinon.match.hasOwn("name", "setMute"));
     });
 
@@ -432,16 +430,25 @@ describe("loop.roomViews", function() {
       it("should dispatch a `SetupStreamElements` action on MEDIA_WAIT state is re-entered", function() {
           activeRoomStore.setStoreState({ roomState: ROOM_STATES.ENDED });
           var component = mountTestComponent();
 
           activeRoomStore.setStoreState({ roomState: ROOM_STATES.MEDIA_WAIT });
 
           expectActionDispatched(component);
         });
+
+      it("should dispatch a `StartBrowserShare` action when the SESSION_CONNECTED state is entered", function() {
+        activeRoomStore.setStoreState({ roomState: ROOM_STATES.READY });
+        var component = mountTestComponent();
+
+        activeRoomStore.setStoreState({ roomState: ROOM_STATES.SESSION_CONNECTED });
+
+        expectActionDispatched("startBrowserShare");
+      });
     });
 
     describe("#render", function() {
       it("should set document.title to store.serverData.roomName", function() {
         mountTestComponent();
 
         activeRoomStore.setStoreState({ roomName: "fakeName" });
 
--- a/browser/extensions/loop/test/functional/test_1_browser_call.py
+++ b/browser/extensions/loop/test/functional/test_1_browser_call.py
@@ -181,22 +181,16 @@ class Test1BrowserCall(MarionetteTestCas
 
         # Then send a message using the standalone.
         self.send_chat_message("test2")
 
         # Finally check the link generator got it.
         self.switch_to_chatbox()
         self.check_received_message("test2")
 
-    def local_enable_screenshare(self):
-        self.switch_to_chatbox()
-        button = self.marionette.find_element(By.CLASS_NAME, "btn-screen-share")
-
-        button.click()
-
     def standalone_check_remote_screenshare(self):
         self.switch_to_standalone()
         self.check_video(".screen-share-video")
 
     def remote_leave_room(self):
         self.switch_to_standalone()
         button = self.marionette.find_element(By.CLASS_NAME, "btn-hangup")
 
@@ -264,49 +258,47 @@ class Test1BrowserCall(MarionetteTestCas
 
         self.assertGreater(noted_calls, 0,
                            "OTSdkDriver._connectionLengthNotedCalls should be "
                            "> 0, noted_calls = " + str(noted_calls))
 
     def test_1_browser_call(self):
         self.switch_to_panel()
 
-        self.local_start_a_conversation()
+        # self.local_start_a_conversation()
 
-        # Check the self video in the conversation window
-        self.local_check_room_self_video()
+        # # Check the self video in the conversation window
+        # self.local_check_room_self_video()
 
-        # make sure that the media start time is not initialized
-        self.local_check_media_start_time_uninitialized()
+        # # make sure that the media start time is not initialized
+        # self.local_check_media_start_time_uninitialized()
 
-        room_url = self.local_get_and_verify_room_url()
+        # room_url = self.local_get_and_verify_room_url()
 
-        # load the link clicker interface into the current content browser
-        self.standalone_load_and_join_room(room_url)
+        # # load the link clicker interface into the current content browser
+        # self.standalone_load_and_join_room(room_url)
 
-        # Check we get the video streams
-        self.standalone_check_remote_video()
-        self.local_check_remote_video()
-
-        # Check text messaging
-        self.check_text_messaging()
+        # # Check we get the video streams
+        # self.standalone_check_remote_video()
+        # self.local_check_remote_video()
 
-        # since bi-directional media is connected, make sure we've set
-        # the start time
-        self.local_check_media_start_time_initialized()
+        # # Check text messaging
+        # self.check_text_messaging()
 
-        # XXX To enable this, we either need to navigate the permissions prompt
-        # or have a route where we don't need the permissions prompt.
-        # self.local_enable_screenshare()
+        # # since bi-directional media is connected, make sure we've set
+        # # the start time
+        # self.local_check_media_start_time_initialized()
+
+        # # Check that screenshare was automatically started
         # self.standalone_check_remote_screenshare()
 
-        # We hangup on the remote (standalone) side, because this also leaves
-        # the local chatbox with the local publishing media still connected,
-        # which means that the local_check_connection_length below
-        # verifies that the connection is noted at the time the remote media
-        # drops, rather than waiting until the window closes.
-        self.remote_leave_room()
+        # # We hangup on the remote (standalone) side, because this also leaves
+        # # the local chatbox with the local publishing media still connected,
+        # # which means that the local_check_connection_length below
+        # # verifies that the connection is noted at the time the remote media
+        # # drops, rather than waiting until the window closes.
+        # self.remote_leave_room()
 
-        self.local_check_connection_length_noted()
+        # self.local_check_connection_length_noted()
 
     def tearDown(self):
         self.loop_test_servers.shutdown()
         MarionetteTestCase.tearDown(self)
--- a/browser/extensions/loop/test/shared/activeRoomStore_test.js
+++ b/browser/extensions/loop/test/shared/activeRoomStore_test.js
@@ -194,19 +194,17 @@ describe("loop.store.ActiveRoomStore", f
         failedJoinRequest: false
       }));
 
       sinon.assert.calledOnce(clearTimeout);
     });
 
     it("should remove the sharing listener", function() {
       // Setup the listener.
-      store.startScreenShare(new sharedActions.StartScreenShare({
-        type: "browser"
-      }));
+      store.startBrowserShare(new sharedActions.StartBrowserShare());
 
       // Now simulate room failure.
       store.roomFailure(new sharedActions.RoomFailure({
         error: fakeError,
         failedJoinRequest: false
       }));
 
       sinon.assert.calledOnce(requestStubs.RemoveBrowserSharingListener);
@@ -1252,19 +1250,17 @@ describe("loop.store.ActiveRoomStore", f
 
       sinon.assert.calledOnce(requestStubs["Rooms:Leave"]);
       sinon.assert.calledWithExactly(requestStubs["Rooms:Leave"],
         "fakeToken", "1627384950");
     });
 
     it("should remove the sharing listener", function() {
       // Setup the listener.
-      store.startScreenShare(new sharedActions.StartScreenShare({
-        type: "browser"
-      }));
+      store.startBrowserShare(new sharedActions.StartBrowserShare());
 
       // Now simulate connection failure.
       store.connectionFailure(connectionFailureAction);
 
       sinon.assert.calledOnce(requestStubs.RemoveBrowserSharingListener);
     });
 
     it("should set the state to `FAILED`", function() {
@@ -1534,73 +1530,54 @@ describe("loop.store.ActiveRoomStore", f
       }));
 
       expect(store.getStoreState().remoteVideoDimensions).eql({
         camera: { fake: 20 }
       });
     });
   });
 
-  describe("#startScreenShare", function() {
+  describe("#startBrowserShare", function() {
     afterEach(function() {
       store.endScreenShare();
     });
 
     it("should set the state to 'pending'", function() {
-      store.startScreenShare(new sharedActions.StartScreenShare({
-        type: "window"
-      }));
+      store.startBrowserShare(new sharedActions.StartBrowserShare());
 
       sinon.assert.calledOnce(dispatcher.dispatch);
       sinon.assert.calledWith(dispatcher.dispatch,
         new sharedActions.ScreenSharingState({
           state: SCREEN_SHARE_STATES.PENDING
         }));
     });
 
-    it("should invoke the SDK driver with the correct options for window sharing", function() {
-      store.startScreenShare(new sharedActions.StartScreenShare({
-        type: "window"
-      }));
-
-      sinon.assert.calledOnce(fakeSdkDriver.startScreenShare);
-      sinon.assert.calledWith(fakeSdkDriver.startScreenShare, {
-        videoSource: "window"
-      });
-    });
-
     it("should add a browser sharing listener for tab sharing", function() {
-      store.startScreenShare(new sharedActions.StartScreenShare({
-        type: "browser"
-      }));
+      store.startBrowserShare(new sharedActions.StartBrowserShare());
 
       sinon.assert.calledOnce(requestStubs.AddBrowserSharingListener);
     });
 
     it("should invoke the SDK driver with the correct options for tab sharing", function() {
-      store.startScreenShare(new sharedActions.StartScreenShare({
-        type: "browser"
-      }));
+      store.startBrowserShare(new sharedActions.StartBrowserShare());
 
       sinon.assert.calledOnce(fakeSdkDriver.startScreenShare);
       sinon.assert.calledWith(fakeSdkDriver.startScreenShare, {
         videoSource: "browser",
         constraints: {
           browserWindow: 42,
           scrollWithPage: true
         }
       });
     });
   });
 
   describe("Screen share Events", function() {
     beforeEach(function() {
-      store.startScreenShare(new sharedActions.StartScreenShare({
-        type: "browser"
-      }));
+      store.startBrowserShare(new sharedActions.StartBrowserShare());
 
       store.setStoreState({
         screenSharingState: SCREEN_SHARE_STATES.ACTIVE
       });
 
       // Stub to prevent errors surfacing in the console.
       sandbox.stub(window.console, "error");
     });
@@ -1647,19 +1624,17 @@ describe("loop.store.ActiveRoomStore", f
       sinon.assert.calledWith(dispatcher.dispatch,
         new sharedActions.ScreenSharingState({
           state: SCREEN_SHARE_STATES.INACTIVE
         }));
     });
 
     it("should remove the sharing listener", function() {
       // Setup the listener.
-      store.startScreenShare(new sharedActions.StartScreenShare({
-        type: "browser"
-      }));
+      store.startBrowserShare(new sharedActions.StartBrowserShare());
 
       // Now stop the screen share.
       store.endScreenShare();
 
       sinon.assert.calledOnce(requestStubs.RemoveBrowserSharingListener);
     });
   });
 
@@ -1802,19 +1777,17 @@ describe("loop.store.ActiveRoomStore", f
 
         sinon.assert.calledOnce(requestStubs["Rooms:Leave"]);
         sinon.assert.calledWithExactly(requestStubs["Rooms:Leave"],
           "fakeToken", "1627384950");
       });
 
     it("should remove the sharing listener", function() {
       // Setup the listener.
-      store.startScreenShare(new sharedActions.StartScreenShare({
-        type: "browser"
-      }));
+      store.startBrowserShare(new sharedActions.StartBrowserShare());
 
       // Now unload the window.
       store.windowUnload();
 
       sinon.assert.calledOnce(requestStubs.RemoveBrowserSharingListener);
     });
 
     it("should set the state to CLOSING", function() {
@@ -1859,19 +1832,17 @@ describe("loop.store.ActiveRoomStore", f
 
       sinon.assert.calledOnce(requestStubs["Rooms:Leave"]);
       sinon.assert.calledWithExactly(requestStubs["Rooms:Leave"],
         "fakeToken", "1627384950");
     });
 
     it("should remove the sharing listener", function() {
       // Setup the listener.
-      store.startScreenShare(new sharedActions.StartScreenShare({
-        type: "browser"
-      }));
+      store.startBrowserShare(new sharedActions.StartBrowserShare());
 
       // Now leave the room.
       store.leaveRoom();
 
       sinon.assert.calledOnce(requestStubs.RemoveBrowserSharingListener);
     });
 
     it("should set the state to ENDED", function() {
--- a/browser/extensions/loop/test/shared/views_test.js
+++ b/browser/extensions/loop/test/shared/views_test.js
@@ -6,17 +6,16 @@ describe("loop.shared.views", function()
   "use strict";
 
   var expect = chai.expect;
   var l10n = navigator.mozL10n || document.mozL10n;
   var TestUtils = React.addons.TestUtils;
   var sharedActions = loop.shared.actions;
   var sharedModels = loop.shared.models;
   var sharedViews = loop.shared.views;
-  var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
   var getReactElementByClass = TestUtils.findRenderedDOMComponentWithClass;
   var sandbox, fakeAudioXHR, dispatcher, OS, OSVersion;
 
   beforeEach(function() {
     sandbox = LoopMochaUtils.createSandbox();
     sandbox.useFakeTimers(); // exposes sandbox.clock as a fake timer
     sandbox.stub(l10n, "get", function(x) {
       return "translated:" + x;
@@ -104,196 +103,16 @@ describe("loop.shared.views", function()
           action: function() {},
           enabled: false
         }));
 
       expect(comp.getDOMNode().classList.contains("muted")).eql(true);
     });
   });
 
-  describe("ScreenShareControlButton", function() {
-    it("should render a visible share button", function() {
-      var comp = TestUtils.renderIntoDocument(
-        React.createElement(sharedViews.ScreenShareControlButton, {
-          dispatcher: dispatcher,
-          visible: true,
-          state: SCREEN_SHARE_STATES.INACTIVE
-        }));
-
-      expect(comp.getDOMNode().classList.contains("active")).eql(false);
-      expect(comp.getDOMNode().classList.contains("disabled")).eql(false);
-    });
-
-    it("should render a disabled share button when share is pending", function() {
-      var comp = TestUtils.renderIntoDocument(
-        React.createElement(sharedViews.ScreenShareControlButton, {
-          dispatcher: dispatcher,
-          visible: true,
-          state: SCREEN_SHARE_STATES.PENDING
-        }));
-
-      var node = comp.getDOMNode().querySelector(".btn-screen-share");
-      expect(node.classList.contains("active")).eql(false);
-      expect(node.classList.contains("disabled")).eql(true);
-    });
-
-    it("should render an active share button", function() {
-      var comp = TestUtils.renderIntoDocument(
-        React.createElement(sharedViews.ScreenShareControlButton, {
-          dispatcher: dispatcher,
-          visible: true,
-          state: SCREEN_SHARE_STATES.ACTIVE
-        }));
-
-      var node = comp.getDOMNode().querySelector(".btn-screen-share");
-      expect(node.classList.contains("active")).eql(true);
-      expect(node.classList.contains("disabled")).eql(false);
-    });
-
-    it("should show the screenshare dropdown on click when the state is not active",
-       function() {
-        var comp = TestUtils.renderIntoDocument(
-          React.createElement(sharedViews.ScreenShareControlButton, {
-            dispatcher: dispatcher,
-            visible: true,
-            state: SCREEN_SHARE_STATES.INACTIVE
-          }));
-
-        expect(comp.state.showMenu).eql(false);
-
-        TestUtils.Simulate.click(comp.getDOMNode().querySelector(".btn-screen-share"));
-
-        expect(comp.state.showMenu).eql(true);
-      });
-
-    it("should dispatch a 'browser' StartScreenShare action on option click",
-      function() {
-        var comp = TestUtils.renderIntoDocument(
-          React.createElement(sharedViews.ScreenShareControlButton, {
-            dispatcher: dispatcher,
-            visible: true,
-            state: SCREEN_SHARE_STATES.INACTIVE
-          }));
-
-        TestUtils.Simulate.click(comp.getDOMNode().querySelector(
-          ".screen-share-menu > li"));
-
-        sinon.assert.calledOnce(dispatcher.dispatch);
-        sinon.assert.calledWithExactly(dispatcher.dispatch,
-          new sharedActions.StartScreenShare({ type: "browser" }));
-      });
-
-    it("should close the dropdown on 'browser' option click", function() {
-      var comp = TestUtils.renderIntoDocument(
-        React.createElement(sharedViews.ScreenShareControlButton, {
-          dispatcher: dispatcher,
-          visible: true,
-          state: SCREEN_SHARE_STATES.INACTIVE
-        }));
-
-      sandbox.stub(comp, "hideDropdownMenu");
-
-      TestUtils.Simulate.click(comp.getDOMNode().querySelector(
-        ".screen-share-menu > li"));
-
-      sinon.assert.calledOnce(comp.hideDropdownMenu);
-    });
-
-    it("should dispatch a 'window' StartScreenShare action on option click",
-      function() {
-        var comp = TestUtils.renderIntoDocument(
-          React.createElement(sharedViews.ScreenShareControlButton, {
-            dispatcher: dispatcher,
-            visible: true,
-            state: SCREEN_SHARE_STATES.INACTIVE
-          }));
-
-        TestUtils.Simulate.click(comp.getDOMNode().querySelector(
-          ".screen-share-menu > li:last-child"));
-
-        sinon.assert.calledOnce(dispatcher.dispatch);
-        sinon.assert.calledWithExactly(dispatcher.dispatch,
-          new sharedActions.StartScreenShare({ type: "window" }));
-      });
-
-    it("should close the dropdown on 'window' option click", function() {
-      var comp = TestUtils.renderIntoDocument(
-        React.createElement(sharedViews.ScreenShareControlButton, {
-          dispatcher: dispatcher,
-          visible: true,
-          state: SCREEN_SHARE_STATES.INACTIVE
-        }));
-
-      sandbox.stub(comp, "hideDropdownMenu");
-
-      TestUtils.Simulate.click(comp.getDOMNode().querySelector(
-        ".screen-share-menu > li:last-child"));
-
-      sinon.assert.calledOnce(comp.hideDropdownMenu);
-    });
-
-    it("should have the 'window' option enabled", function() {
-      var comp = TestUtils.renderIntoDocument(
-        React.createElement(sharedViews.ScreenShareControlButton, {
-          dispatcher: dispatcher,
-          visible: true,
-          state: SCREEN_SHARE_STATES.INACTIVE
-        }));
-
-      var node = comp.getDOMNode().querySelector(".screen-share-menu > li:last-child");
-      expect(node.classList.contains("disabled")).eql(false);
-    });
-
-    it("should disable the 'window' option on Windows XP", function() {
-      OS = "win";
-      OSVersion = { major: 5, minor: 1 };
-
-      var comp = TestUtils.renderIntoDocument(
-        React.createElement(sharedViews.ScreenShareControlButton, {
-          dispatcher: dispatcher,
-          visible: true,
-          state: SCREEN_SHARE_STATES.INACTIVE
-        }));
-
-      var node = comp.getDOMNode().querySelector(".screen-share-menu > li:last-child");
-      expect(node.classList.contains("disabled")).eql(true);
-    });
-
-    it("should disable the 'window' option on OSX 10.6", function() {
-      OS = "mac";
-      OSVersion = { major: 10, minor: 6 };
-
-      var comp = TestUtils.renderIntoDocument(
-        React.createElement(sharedViews.ScreenShareControlButton, {
-          dispatcher: dispatcher,
-          visible: true,
-          state: SCREEN_SHARE_STATES.INACTIVE
-        }));
-
-      var node = comp.getDOMNode().querySelector(".screen-share-menu > li:last-child");
-      expect(node.classList.contains("disabled")).eql(true);
-    });
-
-    it("should dispatch a EndScreenShare action on click when the state is active",
-      function() {
-        var comp = TestUtils.renderIntoDocument(
-          React.createElement(sharedViews.ScreenShareControlButton, {
-            dispatcher: dispatcher,
-            visible: true,
-            state: SCREEN_SHARE_STATES.ACTIVE
-          }));
-
-        TestUtils.Simulate.click(comp.getDOMNode().querySelector(".btn-screen-share"));
-
-        sinon.assert.calledOnce(dispatcher.dispatch);
-        sinon.assert.calledWithExactly(dispatcher.dispatch,
-          new sharedActions.EndScreenShare({}));
-      });
-  });
-
   describe("SettingsControlButton", function() {
     var requestStubs;
     var support_url = "https://support.com";
 
     beforeEach(function() {
       LoopMochaUtils.stubLoopRequest(requestStubs = {
         OpenURL: sandbox.stub(),
         SetLoopPref: sandbox.stub(),
--- a/browser/extensions/loop/ui/ui-showcase.js
+++ b/browser/extensions/loop/ui/ui-showcase.js
@@ -33,17 +33,16 @@
   var FeedbackView = loop.feedbackViews.FeedbackView;
   var Checkbox = loop.shared.views.Checkbox;
   var TextChatView = loop.shared.views.chat.TextChatView;
 
   // Store constants
   var ROOM_STATES = loop.store.ROOM_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
-  var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
 
   // Local helpers
   function returnTrue() {
     return true;
   }
 
   function returnFalse() {
     return false;
@@ -738,47 +737,44 @@
                              height: 56, 
                              summary: "Default", 
                              width: 300}, 
                 React.createElement("div", {className: "fx-embedded"}, 
                   React.createElement(ConversationToolbar, {audio: { enabled: true, visible: true}, 
                                        dispatcher: dispatcher, 
                                        hangup: noop, 
                                        publishStream: noop, 
-                                       screenShare: { state: SCREEN_SHARE_STATES.INACTIVE, visible: true}, 
                                        settingsMenuItems: [{ id: "feedback" }], 
                                        show: true, 
                                        video: { enabled: true, visible: true}})
                 )
               ), 
               React.createElement(FramedExample, {dashed: true, 
                              height: 56, 
-                             summary: "Video muted, Screen share pending", 
+                             summary: "Video muted", 
                              width: 300}, 
                 React.createElement("div", {className: "fx-embedded"}, 
                   React.createElement(ConversationToolbar, {audio: { enabled: true, visible: true}, 
                                        dispatcher: dispatcher, 
                                        hangup: noop, 
                                        publishStream: noop, 
-                                       screenShare: { state: SCREEN_SHARE_STATES.PENDING, visible: true}, 
                                        settingsMenuItems: [{ id: "feedback" }], 
                                        show: true, 
                                        video: { enabled: false, visible: true}})
                 )
               ), 
               React.createElement(FramedExample, {dashed: true, 
                              height: 56, 
-                             summary: "Audio muted, Screen share active", 
+                             summary: "Audio muted", 
                              width: 300}, 
                 React.createElement("div", {className: "fx-embedded"}, 
                   React.createElement(ConversationToolbar, {audio: { enabled: false, visible: true}, 
                                        dispatcher: dispatcher, 
                                        hangup: noop, 
                                        publishStream: noop, 
-                                       screenShare: { state: SCREEN_SHARE_STATES.ACTIVE, visible: true}, 
                                        settingsMenuItems: [{ id: "feedback" }], 
                                        show: true, 
                                        video: { enabled: true, visible: true}})
                 )
               )
             )
           ), 
 
--- a/browser/extensions/loop/ui/ui-showcase.jsx
+++ b/browser/extensions/loop/ui/ui-showcase.jsx
@@ -33,17 +33,16 @@
   var FeedbackView = loop.feedbackViews.FeedbackView;
   var Checkbox = loop.shared.views.Checkbox;
   var TextChatView = loop.shared.views.chat.TextChatView;
 
   // Store constants
   var ROOM_STATES = loop.store.ROOM_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
-  var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
 
   // Local helpers
   function returnTrue() {
     return true;
   }
 
   function returnFalse() {
     return false;
@@ -738,47 +737,44 @@
                              height={56}
                              summary="Default"
                              width={300}>
                 <div className="fx-embedded">
                   <ConversationToolbar audio={{ enabled: true, visible: true }}
                                        dispatcher={dispatcher}
                                        hangup={noop}
                                        publishStream={noop}
-                                       screenShare={{ state: SCREEN_SHARE_STATES.INACTIVE, visible: true }}
                                        settingsMenuItems={[{ id: "feedback" }]}
                                        show={true}
                                        video={{ enabled: true, visible: true }} />
                 </div>
               </FramedExample>
               <FramedExample dashed={true}
                              height={56}
-                             summary="Video muted, Screen share pending"
+                             summary="Video muted"
                              width={300}>
                 <div className="fx-embedded">
                   <ConversationToolbar audio={{ enabled: true, visible: true }}
                                        dispatcher={dispatcher}
                                        hangup={noop}
                                        publishStream={noop}
-                                       screenShare={{ state: SCREEN_SHARE_STATES.PENDING, visible: true }}
                                        settingsMenuItems={[{ id: "feedback" }]}
                                        show={true}
                                        video={{ enabled: false, visible: true }} />
                 </div>
               </FramedExample>
               <FramedExample dashed={true}
                              height={56}
-                             summary="Audio muted, Screen share active"
+                             summary="Audio muted"
                              width={300}>
                 <div className="fx-embedded">
                   <ConversationToolbar audio={{ enabled: false, visible: true }}
                                        dispatcher={dispatcher}
                                        hangup={noop}
                                        publishStream={noop}
-                                       screenShare={{ state: SCREEN_SHARE_STATES.ACTIVE, visible: true }}
                                        settingsMenuItems={[{ id: "feedback" }]}
                                        show={true}
                                        video={{ enabled: true, visible: true }} />
                 </div>
               </FramedExample>
             </div>
           </Section>
 
--- a/browser/locales/en-US/chrome/browser/loop/loop.properties
+++ b/browser/locales/en-US/chrome/browser/loop/loop.properties
@@ -99,20 +99,16 @@ initiate_call_button_label2=Ready to sta
 incoming_call_title2=Conversation Request
 incoming_call_block_button=Block
 hangup_button_title=Hang up
 hangup_button_caption2=Exit
 mute_local_audio_button_title=Mute your audio
 unmute_local_audio_button_title=Unmute your audio
 mute_local_video_button_title2=Disable video
 unmute_local_video_button_title2=Enable video
-active_screenshare_button_title=Stop sharing
-inactive_screenshare_button_title=Share your screen
-share_tabs_button_title2=Share your Tabs
-share_windows_button_title=Share other Windows
 self_view_hidden_message=Self-view hidden but still being sent; resize window to show
 
 
 ## LOCALIZATION NOTE (call_with_contact_title): The title displayed
 ## when calling a contact. Don't translate the part between {{..}} because
 ## this will be replaced by the contact's name.
 call_with_contact_title=Conversation with {{contactName}}
 
--- a/build/autoconf/icu.m4
+++ b/build/autoconf/icu.m4
@@ -15,20 +15,20 @@ MOZ_ARG_WITH_BOOL(system-icu,
 [  --with-system-icu
                           Use system ICU (located with pkgconfig)],
     MOZ_NATIVE_ICU=1)
 
 if test -n "$MOZ_NATIVE_ICU"; then
     PKG_CHECK_MODULES(MOZ_ICU, icu-i18n >= 50.1)
     MOZ_SHARED_ICU=1
 else
-    MOZ_ICU_CFLAGS="-I$_topsrcdir/intl/icu/source/common -I$_topsrcdir/intl/icu/source/i18n"
-    AC_SUBST_LIST(MOZ_ICU_CFLAGS)
+    MOZ_ICU_INCLUDES="/intl/icu/source/common /intl/icu/source/i18n"
 fi
 
+AC_SUBST_LIST(MOZ_ICU_INCLUDES)
 AC_SUBST(MOZ_NATIVE_ICU)
 
 MOZ_ARG_WITH_STRING(intl-api,
 [  --with-intl-api, --with-intl-api=build, --without-intl-api
     Determine the status of the ECMAScript Internationalization API.  The first
     (or lack of any of these) builds and exposes the API.  The second builds it
     but doesn't use ICU or expose the API to script.  The third doesn't build
     ICU at all.],
--- a/config/system-headers
+++ b/config/system-headers
@@ -178,16 +178,17 @@ Appearance.h
 AppFileInfo.h
 AppKit.h
 AppleEvents.h
 Application.h
 app/Message.h
 app/MessageRunner.h
 arpa/inet.h
 arpa/nameser.h
+array
 asm/page.h
 asm/sigcontext.h
 asm/signal.h
 ASRegistry.h
 assert.h
 atk/atk.h
 atlcom.h
 atlconv.h
--- a/devtools/.eslintrc
+++ b/devtools/.eslintrc
@@ -249,19 +249,18 @@
     // Allow the use of ternary operators.
     "no-ternary": 0,
     // Disallow throwing literals (eg. throw "error" instead of
     // throw new Error("error")).
     "no-throw-literal": 2,
     // Disallow trailing whitespace at the end of lines.
     "no-trailing-spaces": 2,
     // Disallow use of undeclared variables unless mentioned in a /*global */
-    // block.
-    // This should really be a 2, but until we define all globals in comments
-    // and .eslintrc, keeping this as a 1.
+    // block. Note that globals from head.js are automatically imported in tests
+    // by the import-headjs-globals rule form the mozilla eslint plugin.
     "no-undef": 2,
     // Allow dangling underscores in identifiers (for privates).
     "no-underscore-dangle": 0,
     // Allow use of undefined variable.
     "no-undefined": 0,
     // Disallow the use of Boolean literals in conditional expressions.
     "no-unneeded-ternary": 2,
     // Disallow unreachable statements after a return, throw, continue, or break
--- a/devtools/client/aboutdebugging/aboutdebugging.js
+++ b/devtools/client/aboutdebugging/aboutdebugging.js
@@ -59,26 +59,30 @@ var AboutDebugging = {
     }
     // Show the corresponding tab and hide the others.
     document.querySelector(".tab.active").classList.remove("active");
     document.querySelector("#tab-" + category).classList.add("active");
     document.querySelector(".category[selected]").removeAttribute("selected");
     document.querySelector(".category[value=" + category + "]")
       .setAttribute("selected", "true");
     location.hash = "#" + category;
+
+    if (category == "addons") {
+      React.render(React.createElement(AddonsComponent, { client: this.client }),
+        document.querySelector("#addons"));
+    } else if (category == "workers") {
+      React.render(React.createElement(WorkersComponent, { client: this.client }),
+        document.querySelector("#workers"));
+    }
   },
 
   init() {
     let telemetry = this._telemetry = new Telemetry();
     telemetry.toolOpened("aboutdebugging");
 
-    // Show the first available tab.
-    this.showTab();
-    window.addEventListener("hashchange", () => this.showTab());
-
     // Link checkboxes to prefs.
     let elements = document.querySelectorAll("input[type=checkbox][data-pref]");
     Array.map(elements, element => {
       let pref = element.dataset.pref;
       let updatePref = () => {
         Services.prefs.setBoolPref(pref, element.checked);
       };
       element.addEventListener("change", updatePref, false);
@@ -94,23 +98,22 @@ var AboutDebugging = {
     let loadAddonButton = document.getElementById("load-addon-from-file");
     loadAddonButton.addEventListener("click", this.loadAddonFromFile);
 
     if (!DebuggerServer.initialized) {
       DebuggerServer.init();
       DebuggerServer.addBrowserActors();
     }
     DebuggerServer.allowChromeProcess = true;
-    let client = new DebuggerClient(DebuggerServer.connectPipe());
+    this.client = new DebuggerClient(DebuggerServer.connectPipe());
 
-    client.connect(() => {
-      React.render(React.createElement(AddonsComponent, { client }),
-        document.querySelector("#addons"));
-      React.render(React.createElement(WorkersComponent, { client }),
-        document.querySelector("#workers"));
+    this.client.connect(() => {
+      // Show the first available tab.
+      this.showTab();
+      window.addEventListener("hashchange", () => this.showTab());
     });
   },
 
   loadAddonFromFile() {
     let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
     fp.init(window,
       Strings.GetStringFromName("selectAddonFromFile"),
       Ci.nsIFilePicker.modeOpen);
@@ -139,16 +142,19 @@ var AboutDebugging = {
 
     this._prefListeners.forEach(([pref, listener]) => {
       Services.prefs.removeObserver(pref, listener);
     });
     this._prefListeners = [];
 
     React.unmountComponentAtNode(document.querySelector("#addons"));
     React.unmountComponentAtNode(document.querySelector("#workers"));
+
+    this.client.close();
+    this.client = null;
   },
 };
 
 window.addEventListener("DOMContentLoaded", function load() {
   window.removeEventListener("DOMContentLoaded", load);
   AboutDebugging.init();
 });
 
--- a/devtools/client/aboutdebugging/components/target-list.js
+++ b/devtools/client/aboutdebugging/components/target-list.js
@@ -22,16 +22,16 @@ exports.TargetListComponent = React.crea
   displayName: "TargetListComponent",
 
   render() {
     let client = this.props.client;
     let targets = this.props.targets.sort(LocaleCompare).map(target => {
       return React.createElement(TargetComponent, { client, target });
     });
     return (
-      React.createElement("div", { className: "targets" },
+      React.createElement("div", { id: this.props.id, className: "targets" },
         React.createElement("h4", null, this.props.name),
         targets.length > 0 ? targets :
           React.createElement("p", null, Strings.GetStringFromName("nothing"))
       )
     );
   },
 });
--- a/devtools/client/aboutdebugging/components/workers.js
+++ b/devtools/client/aboutdebugging/components/workers.js
@@ -9,16 +9,18 @@
 loader.lazyRequireGetter(this, "Ci",
   "chrome", true);
 loader.lazyRequireGetter(this, "React",
   "devtools/client/shared/vendor/react");
 loader.lazyRequireGetter(this, "TargetListComponent",
   "devtools/client/aboutdebugging/components/target-list", true);
 loader.lazyRequireGetter(this, "Services");
 
+loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm");
+
 const Strings = Services.strings.createBundle(
   "chrome://devtools/locale/aboutdebugging.properties");
 const WorkerIcon = "chrome://devtools/skin/images/debugging-workers.svg";
 
 exports.WorkersComponent = React.createClass({
   displayName: "WorkersComponent",
 
   getInitialState() {
@@ -27,45 +29,50 @@ exports.WorkersComponent = React.createC
         service: [],
         shared: [],
         other: []
       }
     };
   },
 
   componentDidMount() {
-    this.props.client.addListener("workerListChanged", this.update);
+    let client = this.props.client;
+    client.addListener("workerListChanged", this.update);
+    client.addListener("processListChanged", this.update);
     this.update();
   },
 
   componentWillUnmount() {
-    this.props.client.removeListener("workerListChanged", this.update);
+    let client = this.props.client;
+    client.removeListener("processListChanged", this.update);
+    client.removeListener("workerListChanged", this.update);
   },
 
   render() {
     let client = this.props.client;
     let workers = this.state.workers;
     return React.createElement("div", { className: "inverted-icons" },
       React.createElement(TargetListComponent, {
+        id: "service-workers",
         name: Strings.GetStringFromName("serviceWorkers"),
         targets: workers.service, client }),
       React.createElement(TargetListComponent, {
+        id: "shared-workers",
         name: Strings.GetStringFromName("sharedWorkers"),
         targets: workers.shared, client }),
       React.createElement(TargetListComponent, {
+        id: "other-workers",
         name: Strings.GetStringFromName("otherWorkers"),
         targets: workers.other, client })
     );
   },
 
   update() {
-    let client = this.props.client;
     let workers = this.getInitialState().workers;
-    client.mainRoot.listWorkers(response => {
-      let forms = response.workers;
+    this.getWorkerForms().then(forms => {
       forms.forEach(form => {
         let worker = {
           name: form.url,
           icon: WorkerIcon,
           actorID: form.actor
         };
         switch (form.type) {
           case Ci.nsIWorkerDebugger.TYPE_SERVICE:
@@ -78,10 +85,34 @@ exports.WorkersComponent = React.createC
             break;
           default:
             worker.type = "worker";
             workers.other.push(worker);
         }
       });
       this.setState({ workers });
     });
-  }
+  },
+
+  getWorkerForms: Task.async(function*() {
+    let client = this.props.client;
+
+    // List workers from the Parent process
+    let result = yield client.mainRoot.listWorkers();
+    let forms = result.workers;
+
+    // And then from the Child processes
+    let { processes } = yield client.mainRoot.listProcesses();
+    for (let process of processes) {
+      // Ignore parent process
+      if (process.parent) {
+        continue;
+      }
+      let { form } = yield client.getProcess(process.id);
+      let processActor = form.actor;
+      let { workers } = yield client.request({to: processActor,
+                                              type: "listWorkers"});
+      forms = forms.concat(workers);
+    }
+
+    return forms;
+  }),
 });
--- a/devtools/client/aboutdebugging/test/browser.ini
+++ b/devtools/client/aboutdebugging/test/browser.ini
@@ -1,9 +1,12 @@
 [DEFAULT]
 tags = devtools
 subsuite = devtools
 support-files =
   head.js
   addons/unpacked/bootstrap.js
   addons/unpacked/install.rdf
+  service-workers/empty-sw.html
+  service-workers/empty-sw.js
 
 [browser_addons_install.js]
+[browser_service_workers.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/browser_service_workers.js
@@ -0,0 +1,66 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Service workers can't be loaded from chrome://,
+// but http:// is ok with dom.serviceWorkers.testing.enabled turned on.
+const HTTP_ROOT = CHROME_ROOT.replace("chrome://mochitests/content/",
+                                      "http://mochi.test:8888/");
+const SERVICE_WORKER = HTTP_ROOT + "service-workers/empty-sw.js";
+const TAB_URL = HTTP_ROOT + "service-workers/empty-sw.html";
+
+function waitForWorkersUpdate(document) {
+  return new Promise(done => {
+    var observer = new MutationObserver(function(mutations) {
+      observer.disconnect();
+      done();
+    });
+    var target = document.getElementById("service-workers");
+    observer.observe(target, { childList: true });
+  });
+}
+
+add_task(function *() {
+  yield new Promise(done => {
+    let options = {"set": [
+                    ["dom.serviceWorkers.testing.enabled", true],
+                  ]};
+    SpecialPowers.pushPrefEnv(options, done);
+  });
+
+  let { tab, document } = yield openAboutDebugging("workers");
+
+  let swTab = yield addTab(TAB_URL);
+
+  yield waitForWorkersUpdate(document);
+
+  // Check that the service worker appears in the UI
+  let names = [...document.querySelectorAll("#service-workers .target-name")];
+  names = names.map(element => element.textContent);
+  ok(names.includes(SERVICE_WORKER), "The service worker url appears in the list: " + names);
+
+  // Use message manager to work with e10s
+  let frameScript = function () {
+    // Retrieve the `sw` promise created in the html page
+    let { sw } = content.wrappedJSObject;
+    sw.then(function (registration) {
+      registration.unregister().then(function (success) {
+        dump("SW unregistered: " + success + "\n");
+      },
+      function (e) {
+        dump("SW not unregistered; " + e + "\n");
+      });
+    });
+  };
+  swTab.linkedBrowser.messageManager.loadFrameScript("data:,(" + encodeURIComponent(frameScript) + ")()", true);
+
+  yield waitForWorkersUpdate(document);
+
+  // Check that the service worker disappeared from the UI
+  names = [...document.querySelectorAll("#service-workers .target-name")];
+  names = names.map(element => element.textContent);
+  ok(!names.includes(SERVICE_WORKER), "The service worker url is no longer in the list: " + names);
+
+  yield removeTab(swTab);
+  yield closeAboutDebugging(tab);
+});
--- a/devtools/client/aboutdebugging/test/head.js
+++ b/devtools/client/aboutdebugging/test/head.js
@@ -11,19 +11,23 @@ const DevToolsUtils = require("devtools/
 DevToolsUtils.testing = true;
 
 const CHROME_ROOT = gTestPath.substr(0, gTestPath.lastIndexOf("/") + 1);
 
 registerCleanupFunction(() => {
   DevToolsUtils.testing = false;
 });
 
-function openAboutDebugging() {
+function openAboutDebugging(page) {
   info("opening about:debugging");
-  return addTab("about:debugging").then(tab => {
+  let url = "about:debugging";
+  if (page) {
+    url += "#" + page;
+  }
+  return addTab(url).then(tab => {
     let browser = tab.linkedBrowser;
     return {
       tab,
       document: browser.contentDocument,
       window: browser.contentWindow
     };
   });
 }
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/service-workers/empty-sw.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="UTF-8">
+  <title>Service worker test</title>
+</head>
+<body>
+<script type="text/javascript">
+var sw = navigator.serviceWorker.register("empty-sw.js");
+sw.then(
+  function (registration) {
+    dump("SW registered\n");
+  },
+  function (e) {
+    dump("SW not registered: " + e + "\n");
+  }
+);
+</script>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/devtools/client/aboutdebugging/test/service-workers/empty-sw.js
@@ -0,0 +1,1 @@
+// Empty, just test registering.
--- a/devtools/client/commandline/test/browser_cmd_commands.js
+++ b/devtools/client/commandline/test/browser_cmd_commands.js
@@ -27,27 +27,23 @@ function* spawnTest() {
   subject.QueryInterface(Ci.nsISupportsString);
   let hud = HUDService.getHudReferenceById(subject.data);
   ok(hud, "console open");
 
   let msg = yield hud.jsterm.execute("pprint(window)");
 
   ok(msg, "output for pprint(window)");
 
-  let oncePromise = hud.jsterm.once("messages-cleared");
-
-  helpers.audit(options, [
+  yield helpers.audit(options, [
     {
       setup: "console clear",
       exec: { output: "" }
     }
   ]);
 
-  yield oncePromise;
-
   let labels = hud.outputNode.querySelectorAll(".message");
   is(labels.length, 0, "no output in console");
 
   yield helpers.audit(options, [
     {
       setup: "console close",
       exec: { output: "" }
     }
--- a/devtools/client/framework/toolbox-hosts.js
+++ b/devtools/client/framework/toolbox-hosts.js
@@ -5,16 +5,18 @@
 "use strict";
 
 const {Cu} = require("chrome");
 const EventEmitter = require("devtools/shared/event-emitter");
 const promise = require("promise");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://devtools/client/shared/DOMHelpers.jsm");
 
+loader.lazyRequireGetter(this, "system", "devtools/shared/system");
+
 /* A host should always allow this much space for the page to be displayed.
  * There is also a min-height on the browser, but we still don't want to set
  * frame.height to be larger than that, since it can cause problems with
  * resizing the toolbox and panel layout. */
 const MIN_PAGE_SIZE = 25;
 
 /**
  * A toolbox host represents an object that contains a toolbox (e.g. the
@@ -278,16 +280,25 @@ WindowHost.prototype = {
 
     let flags = "chrome,centerscreen,resizable,dialog=no";
     let win = Services.ww.openWindow(null, this.WINDOW_URL, "_blank",
                                      flags, null);
 
     let frameLoad = () => {
       win.removeEventListener("load", frameLoad, true);
       win.focus();
+
+      let key;
+      if (system.constants.platform === "macosx") {
+        key = win.document.getElementById("toolbox-key-toggle-osx");
+      } else {
+        key = win.document.getElementById("toolbox-key-toggle");
+      }
+      key.removeAttribute("disabled");
+
       this.frame = win.document.getElementById("toolbox-iframe");
       this.emit("ready", this.frame);
 
       deferred.resolve(this.frame);
     };
 
     win.addEventListener("load", frameLoad, true);
     win.addEventListener("unload", this._boundUnload);
--- a/devtools/client/framework/toolbox-window.xul
+++ b/devtools/client/framework/toolbox-window.xul
@@ -24,22 +24,23 @@
   <keyset id="toolbox-keyset">
     <key id="toolbox-key-close"
          key="&closeCmd.key;"
          command="toolbox-cmd-close"
          modifiers="accel"/>
     <key id="toolbox-key-toggle"
          key="&toggleToolbox.key;"
          command="toolbox-cmd-close"
-#ifdef XP_MACOSX
+         modifiers="accel,shift"
+         disabled="true"/>
+    <key id="toolbox-key-toggle-osx"
+         key="&toggleToolbox.key;"
+         command="toolbox-cmd-close"
          modifiers="accel,alt"
-#else
-         modifiers="accel,shift"
-#endif
-        />
+         disabled="true"/>
     <key id="toolbox-key-toggle-F12"
          keycode="&toggleToolboxF12.keycode;"
          keytext="&toggleToolboxF12.keytext;"
          command="toolbox-cmd-close"/>
   </keyset>
 
   <iframe id="toolbox-iframe" flex="1" forceOwnRefreshDriver=""></iframe>
 </window>
--- a/devtools/client/inspector/inspector-search.js
+++ b/devtools/client/inspector/inspector-search.js
@@ -385,29 +385,30 @@ SelectorAutocompleter.prototype = {
    * Populates the suggestions list and show the suggestion popup.
    */
   _showPopup: function(list, firstPart, aState) {
     let total = 0;
     let query = this.searchBox.value;
     let items = [];
 
     for (let [value, /*count*/, state] of list) {
-      // for cases like 'div ' or 'div >' or 'div+'
       if (query.match(/[\s>+]$/)) {
+        // for cases like 'div ' or 'div >' or 'div+'
         value = query + value;
-      }
-      // for cases like 'div #a' or 'div .a' or 'div > d' and likewise
-      else if (query.match(/[\s>+][\.#a-zA-Z][^\s>+\.#]*$/)) {
-        let lastPart = query.match(/[\s>+][\.#a-zA-Z][^>\s+\.#]*$/)[0];
+      } else if (query.match(/[\s>+][\.#a-zA-Z][^\s>+\.#\[]*$/)) {
+        // for cases like 'div #a' or 'div .a' or 'div > d' and likewise
+        let lastPart = query.match(/[\s>+][\.#a-zA-Z][^\s>+\.#\[]*$/)[0];
         value = query.slice(0, -1 * lastPart.length + 1) + value;
-      }
-      // for cases like 'div.class' or '#foo.bar' and likewise
-      else if (query.match(/[a-zA-Z][#\.][^#\.\s+>]*$/)) {
-        let lastPart = query.match(/[a-zA-Z][#\.][^#\.\s>+]*$/)[0];
+      } else if (query.match(/[a-zA-Z][#\.][^#\.\s+>]*$/)) {
+        // for cases like 'div.class' or '#foo.bar' and likewise
+        let lastPart = query.match(/[a-zA-Z][#\.][^#\.\s+>]*$/)[0];
         value = query.slice(0, -1 * lastPart.length + 1) + value;
+      } else if (query.match(/[a-zA-Z]\[[^\]]*\]?$/)) {
+        // for cases like 'div[foo=bar]' and likewise
+        value = query;
       }
 
       let item = {
         preLabel: query,
         label: value
       };
 
       // In case of tagNames, change the case to small
@@ -452,16 +453,23 @@ SelectorAutocompleter.prototype = {
    * Suggests classes,ids and tags based on the user input as user types in the
    * searchbox.
    */
   showSuggestions: function() {
     let query = this.searchBox.value;
     let state = this.state;
     let firstPart = "";
 
+    if (query.endsWith("*")) {
+      // Hide the popup if the query ends with * because we don't want to
+      // suggest all nodes.
+      this.hidePopup();
+      return;
+    }
+
     if (state === this.States.TAG) {
       // gets the tag that is being completed. For ex. 'div.foo > s' returns 's',
       // 'di' returns 'di' and likewise.
       firstPart = (query.match(/[\s>+]?([a-zA-Z]*)$/) || ["", query])[1];
       query = query.slice(0, query.length - firstPart.length);
     }
     else if (state === this.States.CLASS) {
       // gets the class that is being completed. For ex. '.foo.b' returns 'b'
--- a/devtools/client/inspector/test/browser_inspector_search-03.js
+++ b/devtools/client/inspector/test/browser_inspector_search-03.js
@@ -155,16 +155,49 @@ var TEST_DATA = [
       {label: "#p3"},
       {label: "#s1"},
       {label: "#s2"}
     ]
   },
   {
     key: "VK_BACK_SPACE",
     suggestions: []
+  },
+  {
+    key: "p",
+    suggestions: [
+      {label: "p"},
+      {label: "#p1"},
+      {label: "#p2"},
+      {label: "#p3"}
+    ]
+  },
+  {
+    key: "[", suggestions: []
+  },
+  {
+    key: "i", suggestions: []
+  },
+  {
+    key: "d", suggestions: []
+  },
+  {
+    key: "*", suggestions: []
+  },
+  {
+    key: "=", suggestions: []
+  },
+  {
+    key: "p", suggestions: []
+  },
+  {
+    key: "]",
+    suggestions: [
+      {label: "p[id*=p]"}
+    ]
   }
 ];
 
 add_task(function* () {
   let { inspector } = yield openInspectorForURL(TEST_URL);
   let searchBox = inspector.searchBox;
   let popup = inspector.searchSuggestions.searchPopup;
 
--- a/devtools/client/jar.mn
+++ b/devtools/client/jar.mn
@@ -112,17 +112,17 @@ devtools.jar:
     content/memory/memory.xhtml (memory/memory.xhtml)
     content/memory/initializer.js (memory/initializer.js)
     content/promisedebugger/promise-controller.js (promisedebugger/promise-controller.js)
     content/promisedebugger/promise-panel.js (promisedebugger/promise-panel.js)
     content/promisedebugger/promise-debugger.xhtml (promisedebugger/promise-debugger.xhtml)
     content/commandline/commandline.css (commandline/commandline.css)
     content/commandline/commandlineoutput.xhtml (commandline/commandlineoutput.xhtml)
     content/commandline/commandlinetooltip.xhtml (commandline/commandlinetooltip.xhtml)
-*   content/framework/toolbox-window.xul (framework/toolbox-window.xul)
+    content/framework/toolbox-window.xul (framework/toolbox-window.xul)
     content/framework/toolbox-options.xul (framework/toolbox-options.xul)
     content/framework/toolbox-options.js (framework/toolbox-options.js)
     content/framework/toolbox.xul (framework/toolbox.xul)
     content/framework/options-panel.css (framework/options-panel.css)
     content/framework/toolbox-process-window.xul (framework/toolbox-process-window.xul)
 *   content/framework/toolbox-process-window.js (framework/toolbox-process-window.js)
     content/framework/dev-edition-promo/dev-edition-promo.xul (framework/dev-edition-promo/dev-edition-promo.xul)
 *   content/framework/dev-edition-promo/dev-edition-promo.css (framework/dev-edition-promo/dev-edition-promo.css)
--- a/devtools/client/styleinspector/rule-view.js
+++ b/devtools/client/styleinspector/rule-view.js
@@ -564,21 +564,22 @@ Rule.prototype = {
    *
    * @return {Promise}
    *         Promise which resolves with location as an object containing
    *         both the full and short version of the source string.
    */
   getOriginalSourceStrings: function() {
     return this.domRule.getOriginalLocation().then(({href, line, mediaText}) => {
       let mediaString = mediaText ? " @" + mediaText : "";
+      let linePart = line > 0 ? (":" + line) : "";
 
       let sourceStrings = {
-        full: (href || CssLogic.l10n("rule.sourceInline")) + ":" +
-          line + mediaString,
-        short: CssLogic.shortSource({href: href}) + ":" + line + mediaString
+        full: (href || CssLogic.l10n("rule.sourceInline")) + linePart +
+          mediaString,
+        short: CssLogic.shortSource({href: href}) + linePart + mediaString
       };
 
       return sourceStrings;
     });
   },
 
   /**
    * Returns true if the rule matches the creation options
--- a/devtools/client/styleinspector/test/browser.ini
+++ b/devtools/client/styleinspector/test/browser.ini
@@ -6,16 +6,17 @@ support-files =
   doc_content_stylesheet.xul
   doc_content_stylesheet_imported.css
   doc_content_stylesheet_imported2.css
   doc_content_stylesheet_linked.css
   doc_content_stylesheet_script.css
   doc_content_stylesheet_xul.css
   doc_copystyles.css
   doc_copystyles.html
+  doc_cssom.html
   doc_custom.html
   doc_filter.html
   doc_frame_script.js
   doc_keyframeanimation.html
   doc_keyframeanimation.css
   doc_keyframeLineNumbers.html
   doc_matched_selectors.html
   doc_media_queries.html
@@ -84,16 +85,17 @@ support-files =
 [browser_ruleview_completion-popup-hidden-after-navigation.js]
 [browser_ruleview_content_01.js]
 [browser_ruleview_content_02.js]
 skip-if = e10s # Bug 1039528: "inspect element" contextual-menu doesn't work with e10s
 [browser_ruleview_context-menu-show-mdn-docs-01.js]
 [browser_ruleview_context-menu-show-mdn-docs-02.js]
 [browser_ruleview_context-menu-show-mdn-docs-03.js]
 [browser_ruleview_copy_styles.js]
+[browser_ruleview_cssom.js]
 [browser_ruleview_cubicbezier-appears-on-swatch-click.js]
 [browser_ruleview_cubicbezier-commit-on-ENTER.js]
 [browser_ruleview_cubicbezier-revert-on-ESC.js]
 [browser_ruleview_custom.js]
 [browser_ruleview_cycle-color.js]
 [browser_ruleview_edit-property-cancel.js]
 [browser_ruleview_edit-property-commit.js]
 [browser_ruleview_edit-property-computed.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/styleinspector/test/browser_ruleview_cssom.js
@@ -0,0 +1,22 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test to ensure that CSSOM doesn't make the rule view blow up.
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1224121
+
+const TEST_URI = TEST_URL_ROOT + "doc_cssom.html";
+
+add_task(function*() {
+  yield addTab(TEST_URI);
+  let {inspector, view} = yield openRuleView();
+  yield selectNode("#target", inspector);
+
+  let elementStyle = view._elementStyle;
+  let rule = elementStyle.rules[1];
+
+  is(rule.textProps.length, 1, "rule should have one property");
+  is(rule.textProps[0].name, "color", "the property should be 'color'");
+});
new file mode 100644
--- /dev/null
+++ b/devtools/client/styleinspector/test/doc_cssom.html
@@ -0,0 +1,21 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+<html>
+<head>
+  <title>CSSOM test</title>
+
+  <script>
+    window.onload = function() {
+      let x = document.styleSheets[0];
+      x.insertRule("div { color: seagreen; }", 1);
+    };
+  </script>
+
+  <style>
+    span { }
+  </style>
+</head>
+<body>
+  <div id="target"> the ocean </div>
+</body>
+</html>
--- a/devtools/client/webconsole/console-commands.js
+++ b/devtools/client/webconsole/console-commands.js
@@ -67,17 +67,19 @@ exports.items = [
         return;
       }
 
       let panel = toolbox.getPanel("webconsole");
       if (panel == null) {
         return;
       }
 
+      let onceMessagesCleared = panel.hud.jsterm.once("messages-cleared");
       panel.hud.jsterm.clearOutput();
+      return onceMessagesCleared;
     }
   },
   {
     item: "command",
     runAt: "client",
     name: "console close",
     description: l10n.lookup("consolecloseDesc"),
     exec: function(args, context) {
--- a/devtools/server/actors/child-process.js
+++ b/devtools/server/actors/child-process.js
@@ -9,16 +9,18 @@ const { Cc, Ci, Cu } = require("chrome")
 const { ChromeDebuggerActor } = require("devtools/server/actors/script");
 const { WebConsoleActor } = require("devtools/server/actors/webconsole");
 const makeDebugger = require("devtools/server/actors/utils/make-debugger");
 const { ActorPool } = require("devtools/server/main");
 const Services = require("Services");
 const { assert } = require("devtools/shared/DevToolsUtils");
 const { TabSources } = require("./utils/TabSources");
 
+loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker", true);
+
 function ChildProcessActor(aConnection) {
   this.conn = aConnection;
   this._contextPool = new ActorPool(this.conn);
   this.conn.addActorPool(this._contextPool);
   this.threadActor = null;
 
   // Use a see-everything debugger
   this.makeDebugger = makeDebugger.bind(null, {
@@ -27,16 +29,20 @@ function ChildProcessActor(aConnection) 
   });
 
   // Scope into which the webconsole executes:
   // An empty sandbox with chrome privileges
   let systemPrincipal = Cc["@mozilla.org/systemprincipal;1"]
     .createInstance(Ci.nsIPrincipal);
   let sandbox = Cu.Sandbox(systemPrincipal);
   this._consoleScope = sandbox;
+
+  this._workerList = null;
+  this._workerActorPool = null;
+  this._onWorkerListChanged = this._onWorkerListChanged.bind(this);
 }
 exports.ChildProcessActor = ChildProcessActor;
 
 ChildProcessActor.prototype = {
   actorPrefix: "process",
 
   get isRootActor() {
     return true;
@@ -82,25 +88,59 @@ ChildProcessActor.prototype = {
 
       traits: {
         highlightable: false,
         networkMonitor: false,
       },
     };
   },
 
+  onListWorkers: function () {
+    if (!this._workerList) {
+      this._workerList = new WorkerActorList({});
+    }
+    return this._workerList.getList().then(actors => {
+      let pool = new ActorPool(this.conn);
+      for (let actor of actors) {
+        pool.addActor(actor);
+      }
+
+      this.conn.removeActorPool(this._workerActorPool);
+      this._workerActorPool = pool;
+      this.conn.addActorPool(this._workerActorPool);
+
+      this._workerList.onListChanged = this._onWorkerListChanged;
+
+      return {
+        "from": this.actorID,
+        "workers": actors.map(actor => actor.form())
+      };
+    });
+  },
+
+  _onWorkerListChanged: function () {
+    this.conn.send({ from: this.actorID, type: "workerListChanged" });
+    this._workerList.onListChanged = null;
+  },
+
   disconnect: function() {
     this.conn.removeActorPool(this._contextPool);
     this._contextPool = null;
+
+    // Tell the live lists we aren't watching any more.
+    if (this._workerList) {
+      this._workerList.onListChanged = null;
+    }
   },
 
   preNest: function() {
     // TODO: freeze windows
     // window mediator doesn't work in child.
     // it doesn't throw, but doesn't return any window
   },
 
   postNest: function() {
   },
 };
 
 ChildProcessActor.prototype.requestTypes = {
+  "listWorkers": ChildProcessActor.prototype.onListWorkers,
 };
--- a/devtools/server/actors/moz.build
+++ b/devtools/server/actors/moz.build
@@ -35,16 +35,17 @@ DevToolsModules(
     'memprof.js',
     'monitor.js',
     'object.js',
     'performance-entries.js',
     'performance-recording.js',
     'performance.js',
     'preference.js',
     'pretty-print-worker.js',
+    'process.js',
     'profiler.js',
     'promises.js',
     'root.js',
     'script.js',
     'settings.js',
     'storage.js',
     'string.js',
     'styleeditor.js',
new file mode 100644
--- /dev/null
+++ b/devtools/server/actors/process.js
@@ -0,0 +1,83 @@
+/* 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";
+
+var { Cc, Ci } = require("chrome");
+
+loader.lazyGetter(this, "ppmm", () => {
+  return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIMessageBroadcaster);
+});
+
+function ProcessActorList() {
+  this._actors = new Map();
+  this._onListChanged = null;
+  this._mustNotify = false;
+
+  this._onMessage = this._onMessage.bind(this);
+  this._processScript = "data:text/javascript,sendAsyncMessage('debug:new-process');";
+}
+
+ProcessActorList.prototype = {
+  getList: function () {
+    let processes = [];
+    for (let i = 0; i < ppmm.childCount; i++) {
+      processes.push({
+        id: i, // XXX: may not be a perfect id, but process message manager doesn't expose anything...
+        parent: i == 0, // XXX Weak, but appear to be stable
+        tabCount: undefined, // TODO: exposes process message manager on frameloaders in order to compute this
+      });
+    }
+    this._mustNotify = true;
+    this._checkListening();
+
+    return processes;
+  },
+
+  get onListChanged() {
+    return this._onListChanged;
+  },
+
+  set onListChanged(onListChanged) {
+    if (typeof onListChanged !== "function" && onListChanged !== null) {
+      throw new Error("onListChanged must be either a function or null.");
+    }
+    if (onListChanged === this._onListChanged) {
+      return;
+    }
+
+    this._onListChanged = onListChanged;
+    this._checkListening();
+  },
+
+  _checkListening: function () {
+    if (this._onListChanged !== null && this._mustNotify) {
+      this._knownProcesses = [];
+      for (let i = 0; i < ppmm.childCount; i++) {
+        this._knownProcesses.push(ppmm.getChildAt(i));
+      }
+      ppmm.addMessageListener('debug:new-process', this._onMessage);
+      ppmm.loadProcessScript(this._processScript, true);
+    } else {
+      ppmm.removeMessageListener('debug:new-process', this._onMessage);
+      ppmm.removeDelayedProcessScript(this._processScript);
+    }
+  },
+
+  _notifyListChanged: function () {
+    if (this._mustNotify) {
+      this._onListChanged();
+      this._mustNotify = false;
+    }
+  },
+
+  _onMessage: function ({ target }) {
+    if (this._knownProcesses.includes(target)) {
+      return;
+    }
+    this._notifyListChanged();
+  },
+};
+
+exports.ProcessActorList = ProcessActorList;
--- a/devtools/server/actors/root.js
+++ b/devtools/server/actors/root.js
@@ -91,16 +91,17 @@ loader.lazyGetter(this, "ppmm", () => {
  */
 function RootActor(aConnection, aParameters) {
   this.conn = aConnection;
   this._parameters = aParameters;
   this._onTabListChanged = this.onTabListChanged.bind(this);
   this._onAddonListChanged = this.onAddonListChanged.bind(this);
   this._onWorkerListChanged = this.onWorkerListChanged.bind(this);
   this._onServiceWorkerRegistrationListChanged = this.onServiceWorkerRegistrationListChanged.bind(this);
+  this._onProcessListChanged = this.onProcessListChanged.bind(this);
   this._extraActors = {};
 
   this._globalActorPool = new ActorPool(this.conn);
   this.conn.addActorPool(this._globalActorPool);
 
   this._chromeActor = null;
 }
 
@@ -201,16 +202,19 @@ RootActor.prototype = {
   disconnect: function() {
     /* Tell the live lists we aren't watching any more. */
     if (this._parameters.tabList) {
       this._parameters.tabList.onListChanged = null;
     }
     if (this._parameters.addonList) {
       this._parameters.addonList.onListChanged = null;
     }
+    if (this._parameters.workerList) {
+      this._parameters.workerList.onListChanged = null;
+    }
     if (typeof this._parameters.onShutdown === 'function') {
       this._parameters.onShutdown();
     }
     this._extraActors = null;
     this.conn = null;
     this._tabActorPool = null;
     this._globalActorPool = null;
     this._parameters = null;
@@ -414,25 +418,30 @@ RootActor.prototype = {
   },
 
   onServiceWorkerRegistrationListChanged: function () {
     this.conn.send({ from: this.actorID, type: "serviceWorkerRegistrationListChanged" });
     this._parameters.serviceWorkerRegistrationList.onListChanged = null;
   },
 
   onListProcesses: function () {
-    let processes = [];
-    for (let i = 0; i < ppmm.childCount; i++) {
-      processes.push({
-        id: i, // XXX: may not be a perfect id, but process message manager doesn't expose anything...
-        parent: i == 0, // XXX Weak, but appear to be stable
-        tabCount: undefined, // TODO: exposes process message manager on frameloaders in order to compute this
-      });
+    let { processList } = this._parameters;
+    if (!processList) {
+      return { from: this.actorID, error: "noProcesses",
+               message: "This root actor has no processes." };
     }
-    return { processes: processes };
+    processList.onListChanged = this._onProcessListChanged;
+    return {
+      processes: processList.getList()
+    };
+  },
+
+  onProcessListChanged: function () {
+    this.conn.send({ from: this.actorID, type: "processListChanged" });
+    this._parameters.processList.onListChanged = null;
   },
 
   onGetProcess: function (aRequest) {
     if (!DebuggerServer.allowChromeProcess) {
       return { error: "forbidden",
                message: "You are not allowed to debug chrome." };
     }
     if (("id" in aRequest) && typeof(aRequest.id) != "number") {
--- a/devtools/server/actors/styles.js
+++ b/devtools/server/actors/styles.js
@@ -1149,16 +1149,21 @@ var StyleRuleActor = protocol.ActorClass
   // True if this rule supports as-authored styles, meaning that the
   // rule text can be rewritten using setRuleText.
   get canSetRuleText() {
     // Special case about:PreferenceStyleSheet, as it is
     // generated on the fly and the URI is not registered with the
     // about: handler.
     // https://bugzilla.mozilla.org/show_bug.cgi?id=935803#c37
     return !!(this._parentSheet &&
+              // If a rule does not have source, then it has been
+              // modified via CSSOM; and we should fall back to
+              // non-authored editing.
+              // https://bugzilla.mozilla.org/show_bug.cgi?id=1224121
+              this.sheetActor.allRulesHaveSource() &&
               this._parentSheet.href !== "about:PreferenceStyleSheet");
   },
 
   getDocument: function(sheet) {
     let document;
 
     if (sheet.ownerNode instanceof Ci.nsIDOMHTMLDocument) {
       document = sheet.ownerNode;
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -478,16 +478,40 @@ var StyleSheetActor = protocol.ActorClas
     // text and index are unknown until source load
     this.text = null;
     this._styleSheetIndex = -1;
 
     this._transitionRefCount = 0;
   },
 
   /**
+   * Test whether all the rules in this sheet have associated source.
+   * @return {Boolean} true if all the rules have source; false if
+   *         some rule was created via CSSOM.
+   */
+  allRulesHaveSource: function() {
+    let rules;
+    try {
+      rules = this.rawSheet.cssRules;
+    } catch (e) {
+      // sheet isn't loaded yet
+      return true;
+    }
+
+    for (let i = 0; i < rules.length; i++) {
+      let rule = rules[i];
+      if (DOMUtils.getRelativeRuleLine(rule) === 0) {
+        return false;
+      }
+    }
+
+    return true;
+  },
+
+  /**
    * Get the raw stylesheet's cssRules once the sheet has been loaded.
    *
    * @return {Promise}
    *         Promise that resolves with a CSSRuleList
    */
   getCSSRules: function() {
     let rules;
     try {
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -19,16 +19,17 @@ var makeDebugger = require("./utils/make
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 loader.lazyRequireGetter(this, "RootActor", "devtools/server/actors/root", true);
 loader.lazyRequireGetter(this, "ThreadActor", "devtools/server/actors/script", true);
 loader.lazyRequireGetter(this, "unwrapDebuggerObjectGlobal", "devtools/server/actors/script", true);
 loader.lazyRequireGetter(this, "BrowserAddonActor", "devtools/server/actors/addon", true);
 loader.lazyRequireGetter(this, "WorkerActorList", "devtools/server/actors/worker", true);
 loader.lazyRequireGetter(this, "ServiceWorkerRegistrationActorList", "devtools/server/actors/worker", true);
+loader.lazyRequireGetter(this, "ProcessActorList", "devtools/server/actors/process", true);
 loader.lazyImporter(this, "AddonManager", "resource://gre/modules/AddonManager.jsm");
 
 // Assumptions on events module:
 // events needs to be dispatched synchronously,
 // by calling the listeners in the order or registration.
 loader.lazyRequireGetter(this, "events", "sdk/event/core");
 
 loader.lazyRequireGetter(this, "StyleSheetActor", "devtools/server/actors/stylesheets", true);
@@ -127,16 +128,17 @@ exports.sendShutdownEvent = sendShutdown
 function createRootActor(aConnection)
 {
   return new RootActor(aConnection,
                        {
                          tabList: new BrowserTabList(aConnection),
                          addonList: new BrowserAddonList(aConnection),
                          workerList: new WorkerActorList({}),
                          serviceWorkerRegistrationList: new ServiceWorkerRegistrationActorList(),
+                         processList: new ProcessActorList(),
                          globalActorFactories: DebuggerServer.globalActorFactories,
                          onShutdown: sendShutdownEvent
                        });
 }
 
 /**
  * A live list of BrowserTabActors representing the current browser tabs,
  * to be provided to the root actor to answer 'listTabs' requests.
--- a/devtools/server/actors/worker.js
+++ b/devtools/server/actors/worker.js
@@ -188,16 +188,19 @@ WorkerActorList.prototype = {
   get onListChanged() {
     return this._onListChanged;
   },
 
   set onListChanged(onListChanged) {
     if (typeof onListChanged !== "function" && onListChanged !== null) {
       throw new Error("onListChanged must be either a function or null.");
     }
+    if (onListChanged === this._onListChanged) {
+      return;
+    }
 
     if (this._mustNotify) {
       if (this._onListChanged === null && onListChanged !== null) {
         wdm.addListener(this);
       }
       if (this._onListChanged !== null && onListChanged === null) {
         wdm.removeListener(this);
       }
--- a/devtools/server/content-server.jsm
+++ b/devtools/server/content-server.jsm
@@ -8,24 +8,17 @@ const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
 const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 const { DevToolsLoader } = Cu.import("resource://devtools/shared/Loader.jsm", {});
 
 this.EXPORTED_SYMBOLS = ["init"];
 
-var started = false;
-
 function init(msg) {
-  if (started) {
-    return;
-  }
-  started = true;
-
   // Init a custom, invisible DebuggerServer, in order to not pollute
   // the debugger with all devtools modules, nor break the debugger itself with using it
   // in the same process.
   let devtools = new DevToolsLoader();
   devtools.invisibleToDebugger = true;
   devtools.main("devtools/server/main");
   let { DebuggerServer, ActorPool } = devtools;
 
@@ -56,11 +49,10 @@ function init(msg) {
 
   let response = {actor: actor.form()};
   mm.sendAsyncMessage("debug:content-process-actor", response);
 
   mm.addMessageListener("debug:content-process-destroy", function onDestroy() {
     mm.removeMessageListener("debug:content-process-destroy", onDestroy);
 
     DebuggerServer.destroy();
-    started = false;
   });
 }
--- a/devtools/server/tests/mochitest/test_getProcess.html
+++ b/devtools/server/tests/mochitest/test_getProcess.html
@@ -23,76 +23,98 @@ let {DebuggerServer} = require("devtools
 
 window.onload = function() {
   SimpleTest.waitForExplicitFinish();
 
   SpecialPowers.pushPrefEnv({
     "set": [
       // Always log packets when running tests.
       ["devtools.debugger.log", true],
-      ["dom.mozBrowserFramesEnabled", true]
+      // Enabled mozbrowser frame to support remote=true
+      ["dom.mozBrowserFramesEnabled", true],
+      // Allows creating a branch new process when creation the iframe
+      ["dom.ipc.processCount", 10],
     ]
   }, runTests);
 }
 
 function runTests() {
-  // Create a remote iframe with a message manager
-  let iframe = document.createElement("iframe");
-  iframe.mozbrowser = true;
-  iframe.setAttribute("remote", "true");
-  iframe.setAttribute("src", "data:text/html,foo");
-  document.body.appendChild(iframe);
-
-  let mm = iframe.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.messageManager;
-
   // Instantiate a minimal server
   if (!DebuggerServer.initialized) {
     DebuggerServer.init();
   }
   DebuggerServer.allowChromeProcess = true;
   if (!DebuggerServer.createRootActor) {
     DebuggerServer.addBrowserActors();
   }
 
-  function firstClient() {
+  let client, iframe, processCount;
+
+  function connect() {
     // Fake a first connection to the content process
     let transport = DebuggerServer.connectPipe();
-    let client = new DebuggerClient(transport);
-    client.connect(() => {
+    client = new DebuggerClient(transport);
+    client.connect(listProcess);
+  }
+
+  function listProcess() {
+    // Call listProcesses in order to start receiving new process notifications
+    client.addListener("processListChanged", function listener() {
+      client.removeListener("processListChanged", listener);
+      ok(true, "Received processListChanged event");
+      getProcess();
+    });
+    client.mainRoot.listProcesses(response => {
+      processCount = response.processes.length;
+      // Create a remote iframe to spawn a new process
+      createRemoteIframe();
+    });
+  }
+
+  function createRemoteIframe() {
+    iframe = document.createElement("iframe");
+    iframe.mozbrowser = true;
+    iframe.setAttribute("remote", "true");
+    iframe.setAttribute("src", "data:text/html,foo");
+    document.body.appendChild(iframe);
+  }
+
+  function getProcess() {
     client.mainRoot.listProcesses(response => {
       ok(response.processes.length >= 2, "Got at least the parent process and one child");
+      is(response.processes.length, processCount+1 , "Got one additional process on the second call to listProcesses");
 
       // Connect to the first content processe available
       let content = response.processes.filter(p => (!p.parent))[0];
 
       client.getProcess(content.id).then(response => {
         let actor = response.form;
         ok(actor.consoleActor, "Got the console actor");
         ok(actor.chromeDebugger, "Got the thread actor");
 
         // Ensure sending at least one request to an actor...
         client.request({
           to: actor.consoleActor,
           type: "evaluateJS",
           text: "var a = 42; a"
         }, function (response) {
           ok(response.result, 42, "console.eval worked");
-
-          client.close(cleanup);
+          cleanup();
         });
       });
     });
-    });
   }
 
   function cleanup() {
-    DebuggerServer.destroy();
-    iframe.remove();
-    SimpleTest.finish()
+    client.close(function () {
+      DebuggerServer.destroy();
+      iframe.remove();
+      SimpleTest.finish()
+    });
   }
 
-  firstClient();
+  connect();
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/devtools/shared/client/main.js
+++ b/devtools/shared/client/main.js
@@ -238,16 +238,19 @@ const DebuggerClient = exports.DebuggerC
  *        The function to call before sending the packet. Is passed the packet,
  *        and the return value is used as the new packet. The |this| context is
  *        the instance of the client object we are defining a method for.
  * @param after
  *        The function to call after the response is received. It is passed the
  *        response, and the return value is considered the new response that
  *        will be passed to the callback. The |this| context is the instance of
  *        the client object we are defining a method for.
+ * @return Request
+ *         The `Request` object that is a Promise object and resolves once
+ *         we receive the response. (See request method for more details)
  */
 DebuggerClient.requester = function (aPacketSkeleton,
                                      { telemetry, before, after }) {
   return DevToolsUtils.makeInfallible(function (...args) {
     let histogram, startTime;
     if (telemetry) {
       let transportType = this._transport.onOutputStreamReady === undefined
         ? "LOCAL_"
@@ -271,17 +274,17 @@ DebuggerClient.requester = function (aPa
         outgoingPacket[k] = aPacketSkeleton[k];
       }
     }
 
     if (before) {
       outgoingPacket = before.call(this, outgoingPacket);
     }
 
-    this.request(outgoingPacket, DevToolsUtils.makeInfallible((aResponse) => {
+    return this.request(outgoingPacket, DevToolsUtils.makeInfallible((aResponse) => {
       if (after) {
         let { from } = aResponse;
         aResponse = after.call(this, aResponse);
         if (!aResponse.from) {
           aResponse.from = from;
         }
       }
 
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/DominatorTreeNode.js
@@ -0,0 +1,100 @@
+/* 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 DEFAULT_MAX_DEPTH = 3;
+const DEFAULT_MAX_SIBLINGS = 15;
+
+/**
+ * A single node in a dominator tree.
+ *
+ * @param {NodeId} nodeId
+ * @param {NodeSize} retainedSize
+ */
+function DominatorTreeNode(nodeId, retainedSize) {
+  // The id of this node.
+  this.nodeId = nodeId;
+
+  // The retained size of this node.
+  this.retainedSize = retainedSize;
+
+  // The id of this node's parent or undefined if this node is the root.
+  this.parentId = undefined;
+
+  // An array of immediately dominated child `DominatorTreeNode`s, or undefined.
+  this.children = undefined;
+
+  // True iff the `children` property does not contain every immediately
+  // dominated node.
+  //
+  // * If children is an array and this property is true: the array does not
+  //   contain the complete set of immediately dominated children.
+  // * If children is an array and this property is false: the array contains
+  //   the complete set of immediately dominated children.
+  // * If children is undefined and this property is true: there exist
+  //   immediately dominated children for this node, but they have not been
+  //   loaded yet.
+  // * If children is undefined and this property is false: this node does not
+  //   dominate any others and therefore has no children.
+  this.moreChildrenAvailable = true;
+}
+
+DominatorTreeNode.prototype = null;
+
+module.exports = DominatorTreeNode;
+
+/**
+ * Add `child` to the `parent`'s set of children.
+ *
+ * @param {DominatorTreeNode} parent
+ * @param {DominatorTreeNode} child
+ */
+DominatorTreeNode.addChild = function (parent, child) {
+  if (parent.children === undefined) {
+    parent.children = [];
+  }
+
+  parent.children.push(child);
+  child.parentId = parent.nodeId;
+};
+
+/**
+ * Do a partial traversal of the given dominator tree and convert it into a tree
+ * of `DominatorTreeNode`s. Dominator trees have a node for every node in the
+ * snapshot's heap graph, so we must not allocate a JS object for every node. It
+ * would be way too many and the node count is effectively unbounded.
+ *
+ * Go no deeper down the tree than `maxDepth` and only consider at most
+ * `maxSiblings` within any single node's children.
+ *
+ * @param {DominatorTree} dominatorTree
+ * @param {Number} maxDepth
+ * @param {Number} maxSiblings
+ *
+ * @returns {DominatorTreeNode}
+ */
+DominatorTreeNode.partialTraversal = function (dominatorTree,
+                                               maxDepth = DEFAULT_MAX_DEPTH,
+                                               maxSiblings = DEFAULT_MAX_SIBLINGS) {
+  function dfs(nodeId, depth) {
+    const size = dominatorTree.getRetainedSize(nodeId);
+    const node = new DominatorTreeNode(nodeId, size);
+    const childNodeIds = dominatorTree.getImmediatelyDominated(nodeId);
+
+    const newDepth = depth + 1;
+    if (newDepth < maxDepth) {
+      const endIdx = Math.min(childNodeIds.length, maxSiblings);
+      for (let i = 0; i < endIdx; i++) {
+        DominatorTreeNode.addChild(node, dfs(childNodeIds[i], newDepth));
+      }
+      node.moreChildrenAvailable = childNodeIds.length < endIdx;
+    } else {
+      node.moreChildrenAvailable = childNodeIds.length > 0;
+    }
+
+    return node;
+  }
+
+  return dfs(dominatorTree.root, 0);
+};
--- a/devtools/shared/heapsnapshot/HeapAnalysesClient.js
+++ b/devtools/shared/heapsnapshot/HeapAnalysesClient.js
@@ -47,16 +47,32 @@ HeapAnalysesClient.prototype.destroy = f
  *          does not happen, eg due to a bad file path or malformed heap
  *          snapshot file.
  */
 HeapAnalysesClient.prototype.readHeapSnapshot = function (snapshotFilePath) {
   return this._worker.performTask("readHeapSnapshot", { snapshotFilePath });
 };
 
 /**
+ * Request the creation time given a snapshot file path. Returns `null`
+ * if snapshot does not exist.
+ *
+ * @param {String} snapshotFilePath
+ *        The path to the snapshot.
+ * @return {Number?}
+ *        The unix timestamp of the creation time of the snapshot, or null if
+ *        snapshot does not exist.
+ */
+HeapAnalysesClient.prototype.getCreationTime = function (snapshotFilePath) {
+  return this._worker.performTask("getCreationTime", snapshotFilePath);
+};
+
+/*** Censuses *****************************************************************/
+
+/**
  * Ask the worker to perform a census analysis on the heap snapshot with the
  * given path. The heap snapshot at the given path must have already been read
  * into memory by the worker (see `readHeapSnapshot`).
  *
  * @param {String} snapshotFilePath
  *
  * @param {Object} censusOptions
  *        A structured-cloneable object specifying the requested census's
@@ -131,21 +147,70 @@ HeapAnalysesClient.prototype.takeCensusD
   return this._worker.performTask("takeCensusDiff", {
     firstSnapshotFilePath,
     secondSnapshotFilePath,
     censusOptions,
     requestOptions
   });
 };
 
+/*** Dominator Trees **********************************************************/
+
 /**
- * Request the creation time given a snapshot file path. Returns `null`
- * if snapshot does not exist.
+ * Compute the dominator tree of the heap snapshot loaded from the given file
+ * path. Returns the id of the computed dominator tree.
  *
  * @param {String} snapshotFilePath
- *        The path to the snapshot.
- * @return {Number?}
- *        The unix timestamp of the creation time of the snapshot, or null if
- *        snapshot does not exist.
+ *
+ * @returns {Promise<DominatorTreeId>}
+ */
+HeapAnalysesClient.prototype.computeDominatorTree = function (snapshotFilePath) {
+  return this._worker.performTask("computeDominatorTree", snapshotFilePath);
+};
+
+/**
+ * Get the initial, partial view of the dominator tree with the given id.
+ *
+ * @param {Object} opts
+ *        An object specifying options for this request.
+ *        - {DominatorTreeId} dominatorTreeId
+ *          The id of the dominator tree.
+ *        - {Number} maxDepth
+ *          The maximum depth to traverse down the tree to create this initial
+ *          view.
+ *        - {Number} maxSiblings
+ *          The maximum number of siblings to visit within each traversed node's
+ *          children.
+ *
+ * @returns {Promise<DominatorTreeNode>}
  */
-HeapAnalysesClient.prototype.getCreationTime = function (snapshotFilePath) {
-  return this._worker.performTask("getCreationTime", snapshotFilePath);
+HeapAnalysesClient.prototype.getDominatorTree = function (opts) {
+  return this._worker.performTask("getDominatorTree", opts);
 };
+
+/**
+ * Get a subset of a nodes children in the dominator tree.
+ *
+ * @param {Object} opts
+ *        An object specifying options for this request.
+ *        - {DominatorTreeId} dominatorTreeId
+ *          The id of the dominator tree.
+ *        - {NodeId} nodeId
+ *          The id of the node whose children are being found.
+ *        - {Number} startIndex
+ *          The starting index within the full set of immediately dominated
+ *          children of the children being requested. Children are always sorted
+ *          by greatest to least retained size.
+ *        - {Number} maxCount
+ *          The maximum number of children to return.
+ *
+ * @returns {Promise<Object>}
+ *          A promise of an object with the following properties:
+ *          - {Array<DominatorTreeNode>} nodes
+ *            The requested nodes that are immediately dominated by the node
+ *            identified by `opts.nodeId`.
+ *          - {Boolean} moreChildrenAvailable
+ *            True iff there are more children available after the returned
+ *            nodes.
+ */
+HeapAnalysesClient.prototype.getImmediatelyDominated = function (opts) {
+  return this._worker.performTask("getImmediatelyDominated", opts);
+};
--- a/devtools/shared/heapsnapshot/HeapAnalysesWorker.js
+++ b/devtools/shared/heapsnapshot/HeapAnalysesWorker.js
@@ -8,18 +8,22 @@
 // HeapAnalysesWorker is owned and communicated with by a HeapAnalysesClient
 // instance. See HeapAnalysesClient.js.
 
 "use strict";
 
 importScripts("resource://gre/modules/workers/require.js");
 importScripts("resource://devtools/shared/worker/helper.js");
 const { censusReportToCensusTreeNode } = require("resource://devtools/shared/heapsnapshot/census-tree-node.js");
+const DominatorTreeNode = require("resource://devtools/shared/heapsnapshot/DominatorTreeNode.js");
 const CensusUtils = require("resource://devtools/shared/heapsnapshot/CensusUtils.js");
 
+const DEFAULT_START_INDEX = 0;
+const DEFAULT_MAX_COUNT = 50;
+
 // The set of HeapSnapshot instances this worker has read into memory. Keyed by
 // snapshot file path.
 const snapshots = Object.create(null);
 
 /**
  * @see HeapAnalysesClient.prototype.readHeapSnapshot
  */
 workerHelper.createTask(self, "readHeapSnapshot", ({ snapshotFilePath }) => {
@@ -81,12 +85,97 @@ workerHelper.createTask(self, "takeCensu
   }
 
   return delta;
 });
 
 /**
  * @see HeapAnalysesClient.prototype.getCreationTime
  */
-workerHelper.createTask(self, "getCreationTime", (snapshotFilePath) => {
+workerHelper.createTask(self, "getCreationTime", snapshotFilePath => {
   let snapshot = snapshots[snapshotFilePath];
   return snapshot ? snapshot.creationTime : null;
 });
+
+/**
+ * The set of `DominatorTree`s that have been computed, mapped by their id (aka
+ * the index into this array).
+ *
+ * @see /dom/webidl/DominatorTree.webidl
+ */
+const dominatorTrees = [];
+
+/**
+ * @see HeapAnalysesClient.prototype.computeDominatorTree
+ */
+workerHelper.createTask(self, "computeDominatorTree", snapshotFilePath => {
+  const snapshot = snapshots[snapshotFilePath];
+  if (!snapshot) {
+    throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
+  }
+
+  const id = dominatorTrees.length;
+  dominatorTrees.push(snapshot.computeDominatorTree());
+  return id;
+});
+
+/**
+ * @see HeapAnalysesClient.prototype.getDominatorTree
+ */
+workerHelper.createTask(self, "getDominatorTree", request => {
+  const {
+    dominatorTreeId,
+    maxDepth,
+    maxSiblings
+  } = request;
+
+  if (!(0 <= dominatorTreeId && dominatorTreeId < dominatorTrees.length)) {
+    throw new Error(
+      `There does not exist a DominatorTree with the id ${dominatorTreeId}`);
+  }
+
+  return DominatorTreeNode.partialTraversal(dominatorTrees[dominatorTreeId],
+                                            maxDepth,
+                                            maxSiblings);
+});
+
+/**
+ * @see HeapAnalysesClient.prototype.getImmediatelyDominated
+ */
+workerHelper.createTask(self, "getImmediatelyDominated", request => {
+  const {
+    dominatorTreeId,
+    nodeId,
+    startIndex,
+    maxCount
+  } = request;
+
+  if (!(0 <= dominatorTreeId && dominatorTreeId < dominatorTrees.length)) {
+    throw new Error(
+      `There does not exist a DominatorTree with the id ${dominatorTreeId}`);
+  }
+
+  const dominatorTree = dominatorTrees[dominatorTreeId];
+  const childIds = dominatorTree.getImmediatelyDominated(nodeId);
+  if (!childIds) {
+    throw new Error(`${nodeId} is not a node id in the dominator tree`);
+  }
+
+  const start = startIndex || DEFAULT_START_INDEX;
+  const count = maxCount || DEFAULT_MAX_COUNT;
+  const end = start + count;
+
+  const nodes = childIds
+    .slice(start, end)
+    .map(id => {
+      const size = dominatorTree.getRetainedSize(id);
+      const node = new DominatorTreeNode(id, size);
+      node.parentId = nodeId;
+      // DominatorTree.getImmediatelyDominated will always return non-null here
+      // because we got the id directly from the dominator tree.
+      node.moreChildrenAvailable = dominatorTree.getImmediatelyDominated(id).length > 0;
+      return node;
+    });
+
+  const moreChildrenAvailable = childIds.length > end;
+
+  return { nodes, moreChildrenAvailable };
+});
--- a/devtools/shared/heapsnapshot/moz.build
+++ b/devtools/shared/heapsnapshot/moz.build
@@ -43,12 +43,13 @@ SOURCES += [
 # Disable RTTI in google protocol buffer
 DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
 
 FINAL_LIBRARY = 'xul'
 
 DevToolsModules(
     'census-tree-node.js',
     'CensusUtils.js',
+    'DominatorTreeNode.js',
     'HeapAnalysesClient.js',
     'HeapAnalysesWorker.js',
     'HeapSnapshotFileUtils.js',
 )
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_computeDominatorTree_01.js
@@ -0,0 +1,22 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the HeapAnalyses{Client,Worker} "computeDominatorTree" request.
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* () {
+  const client = new HeapAnalysesClient();
+
+  const snapshotFilePath = saveNewHeapSnapshot();
+  yield client.readHeapSnapshot(snapshotFilePath);
+  ok(true, "Should have read the heap snapshot");
+
+  const dominatorTreeId = yield client.computeDominatorTree(snapshotFilePath);
+  equal(typeof dominatorTreeId, "number",
+        "should get a dominator tree id, and it should be a number");
+
+  client.destroy();
+});
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_computeDominatorTree_02.js
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the HeapAnalyses{Client,Worker} "computeDominatorTree" request with bad
+// file paths.
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* () {
+  const client = new HeapAnalysesClient();
+
+  let threw = false;
+  try {
+    yield client.computeDominatorTree("/etc/passwd");
+  } catch (_) {
+    threw = true;
+  }
+  ok(threw, "should throw when given a bad path");
+
+  client.destroy();
+});
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_01.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the HeapAnalyses{Client,Worker} "getDominatorTree" request.
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* () {
+  const client = new HeapAnalysesClient();
+
+  const snapshotFilePath = saveNewHeapSnapshot();
+  yield client.readHeapSnapshot(snapshotFilePath);
+  ok(true, "Should have read the heap snapshot");
+
+  const dominatorTreeId = yield client.computeDominatorTree(snapshotFilePath);
+  equal(typeof dominatorTreeId, "number",
+        "should get a dominator tree id, and it should be a number");
+
+  const partialTree = yield client.getDominatorTree({ dominatorTreeId });
+  ok(partialTree, "Should get a partial tree");
+  equal(typeof partialTree, "object",
+        "partialTree should be an object");
+
+  function checkTree(node) {
+    equal(typeof node.nodeId, "number", "each node should have an id");
+
+    if (node === partialTree) {
+      equal(node.parentId, undefined, "the root has no parent");
+    } else {
+      equal(typeof node.parentId, "number", "each node should have a parent id");
+    }
+
+    equal(typeof node.retainedSize, "number",
+          "each node should have a retained size");
+
+    ok(node.children === undefined || Array.isArray(node.children),
+       "each node either has a list of children, or undefined meaning no children loaded");
+    equal(typeof node.moreChildrenAvailable, "boolean",
+          "each node should indicate if there are more children available or not");
+
+    if (node.children) {
+      node.children.forEach(checkTree);
+    }
+  }
+
+  checkTree(partialTree);
+
+  client.destroy();
+});
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getDominatorTree_02.js
@@ -0,0 +1,23 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the HeapAnalyses{Client,Worker} "getDominatorTree" request with bad
+// dominator tree ids.
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* () {
+  const client = new HeapAnalysesClient();
+
+  let threw = false;
+  try {
+    yield client.getDominatorTree({ dominatorTreeId: 42 });
+  } catch (_) {
+    threw = true;
+  }
+  ok(threw, "should throw when given a bad id");
+
+  client.destroy();
+});
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/unit/test_HeapAnalyses_getImmediatelyDominated_01.js
@@ -0,0 +1,46 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test the HeapAnalyses{Client,Worker} "getImmediatelyDominated" request.
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* () {
+  const client = new HeapAnalysesClient();
+
+  const snapshotFilePath = saveNewHeapSnapshot();
+  yield client.readHeapSnapshot(snapshotFilePath);
+  const dominatorTreeId = yield client.computeDominatorTree(snapshotFilePath);
+
+  const partialTree = yield client.getDominatorTree({ dominatorTreeId });
+  ok(partialTree.children.length > 0,
+     "root should immediately dominate some nodes");
+
+  // First, test getting a subset of children available.
+  const response = yield client.getImmediatelyDominated({
+    dominatorTreeId,
+    nodeId: partialTree.nodeId,
+    startIndex: 0,
+    maxCount: partialTree.children.length - 1
+  });
+
+  ok(Array.isArray(response.nodes));
+  ok(response.nodes.every(node => node.parentId === partialTree.nodeId));
+  ok(response.moreChildrenAvailable);
+
+  // Next, test getting a subset of children available.
+  const secondResponse = yield client.getImmediatelyDominated({
+    dominatorTreeId,
+    nodeId: partialTree.nodeId,
+    startIndex: 0,
+    maxCount: Infinity
+  });
+
+  ok(Array.isArray(secondResponse.nodes));
+  ok(secondResponse.nodes.every(node => node.parentId === partialTree.nodeId));
+  ok(!secondResponse.moreChildrenAvailable);
+
+  client.destroy();
+});
--- a/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
+++ b/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
@@ -28,17 +28,22 @@ support-files =
 [test_census-tree-node-06.js]
 [test_census-tree-node-07.js]
 [test_census-tree-node-08.js]
 [test_DominatorTree_01.js]
 [test_DominatorTree_02.js]
 [test_DominatorTree_03.js]
 [test_DominatorTree_04.js]
 [test_DominatorTree_05.js]
+[test_HeapAnalyses_computeDominatorTree_01.js]
+[test_HeapAnalyses_computeDominatorTree_02.js]
 [test_HeapAnalyses_getCreationTime_01.js]
+[test_HeapAnalyses_getDominatorTree_01.js]
+[test_HeapAnalyses_getDominatorTree_02.js]
+[test_HeapAnalyses_getImmediatelyDominated_01.js]
 [test_HeapAnalyses_readHeapSnapshot_01.js]
 [test_HeapAnalyses_takeCensusDiff_01.js]
 [test_HeapAnalyses_takeCensusDiff_02.js]
 [test_HeapAnalyses_takeCensus_01.js]
 [test_HeapAnalyses_takeCensus_02.js]
 [test_HeapAnalyses_takeCensus_03.js]
 [test_HeapAnalyses_takeCensus_04.js]
 [test_HeapAnalyses_takeCensus_05.js]
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -370,29 +370,21 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
     }
 
     if (callbacks->mDetachedCallback.WasPassed()) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
         "mCustomDefinitions->mCallbacks->mDetachedCallback");
       cb.NoteXPCOMChild(callbacks->mDetachedCallback.Value());
     }
   }
-  for (auto iter = tmp->mCandidatesMap.Iter(); !iter.Done(); iter.Next()) {
-    nsTArray<RefPtr<Element>>* elems = iter.UserData();
-    for (size_t i = 0; i < elems->Length(); ++i) {
-      NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCandidatesMap->Element");
-      cb.NoteXPCOMChild(elems->ElementAt(i));
-    }
-  }
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Registry)
   tmp->mCustomDefinitions.Clear();
-  tmp->mCandidatesMap.Clear();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Registry)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(Registry)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(Registry)
@@ -5777,26 +5769,26 @@ nsDocument::RegisterUnresolvedElement(El
     typeName = info->NameAtom();
   }
 
   CustomElementHashKey key(info->NamespaceID(), typeName);
   if (mRegistry->mCustomDefinitions.Get(&key)) {
     return NS_OK;
   }
 
-  nsTArray<RefPtr<Element>>* unresolved;
+  nsTArray<nsWeakPtr>* unresolved;
   mRegistry->mCandidatesMap.Get(&key, &unresolved);
   if (!unresolved) {
-    unresolved = new nsTArray<RefPtr<Element>>();
+    unresolved = new nsTArray<nsWeakPtr>();
     // Ownership of unresolved is taken by mCandidatesMap.
     mRegistry->mCandidatesMap.Put(&key, unresolved);
   }
 
-  RefPtr<Element>* elem = unresolved->AppendElement();
-  *elem = aElement;
+  nsWeakPtr* elem = unresolved->AppendElement();
+  *elem = do_GetWeakReference(aElement);
   aElement->AddStates(NS_EVENT_STATE_UNRESOLVED);
 
   return NS_OK;
 }
 
 void
 nsDocument::EnqueueLifecycleCallback(nsIDocument::ElementCallbackType aType,
                                      Element* aCustomElement,
@@ -6170,21 +6162,24 @@ nsDocument::RegisterElement(JSContext* a
                                 typeAtom,
                                 nameAtom,
                                 callbacks,
                                 namespaceID,
                                 0 /* TODO dependent on HTML imports. Bug 877072 */);
   definitions.Put(&key, definition);
 
   // Do element upgrade.
-  nsAutoPtr<nsTArray<RefPtr<Element>>> candidates;
+  nsAutoPtr<nsTArray<nsWeakPtr>> candidates;
   mRegistry->mCandidatesMap.RemoveAndForget(&key, candidates);
   if (candidates) {
     for (size_t i = 0; i < candidates->Length(); ++i) {
-      Element *elem = candidates->ElementAt(i);
+      nsCOMPtr<Element> elem = do_QueryReferent(candidates->ElementAt(i));
+      if (!elem) {
+        continue;
+      }
 
       elem->RemoveStates(NS_EVENT_STATE_UNRESOLVED);
 
       // Make sure that the element name matches the name in the definition.
       // (e.g. a definition for x-button extending button should match
       // <button is="x-button"> but not <x-button>.
       if (elem->NodeInfo()->NameAtom() != nameAtom) {
         //Skip over this element because definition does not apply.
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -422,17 +422,17 @@ public:
 
 protected:
   virtual ~Registry();
 
   typedef nsClassHashtable<mozilla::dom::CustomElementHashKey,
                            mozilla::dom::CustomElementDefinition>
     DefinitionMap;
   typedef nsClassHashtable<mozilla::dom::CustomElementHashKey,
-                           nsTArray<RefPtr<mozilla::dom::Element>>>
+                           nsTArray<nsWeakPtr>>
     CandidateMap;
 
   // Hashtable for custom element definitions in web components.
   // Custom prototypes are stored in the compartment where
   // registerElement was called.
   DefinitionMap mCustomDefinitions;
 
   // The "upgrade candidates map" from the web components spec. Maps from a
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -577,17 +577,17 @@ skip-if = toolkit == 'android' #TIMED_OU
 [test_bug545644.xhtml]
 [test_bug548463.html]
 [test_bug553896.xhtml]
 [test_bug557892.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug558726.html]
 [test_bug559526.html]
 [test_bug560780.html]
-skip-if = android_version == '18' # Android 4.3 intermittent, Bug 1154497
+skip-if = (os == "android") # Failure with AccessibleCarets on Android, bug 1230229
 [test_bug562137.html]
 [test_bug562169-1.html]
 [test_bug562169-2.html]
 [test_bug562652.html]
 [test_bug564047.html]
 [test_bug564863.xhtml]
 [test_bug567350.html]
 [test_bug578096.html]
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -405,61 +405,27 @@ class CGDOMJSClass(CGThing):
     def __init__(self, descriptor):
         CGThing.__init__(self)
         self.descriptor = descriptor
 
     def declare(self):
         return ""
 
     def define(self):
-        traceHook = 'nullptr'
         callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
         objectMovedHook = OBJECT_MOVED_HOOK_NAME if self.descriptor.wrapperCache else 'nullptr'
         slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
         classFlags = "JSCLASS_IS_DOMJSCLASS | "
-        classExtensionAndObjectOps = fill(
-            """
-            {
-              false,   /* isWrappedNative */
-              nullptr, /* weakmapKeyDelegateOp */
-              ${objectMoved} /* objectMovedOp */
-            },
-            JS_NULL_OBJECT_OPS
-            """,
-            objectMoved=objectMovedHook)
         if self.descriptor.isGlobal():
             classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS)"
             traceHook = "JS_GlobalObjectTraceHook"
             reservedSlots = "JSCLASS_GLOBAL_APPLICATION_SLOTS"
-            if self.descriptor.interface.identifier.name == "Window":
-                classExtensionAndObjectOps = fill(
-                    """
-                    {
-                      false,   /* isWrappedNative */
-                      nullptr, /* weakmapKeyDelegateOp */
-                      ${objectMoved} /* objectMovedOp */
-                    },
-                    {
-                      nullptr, /* lookupProperty */
-                      nullptr, /* defineProperty */
-                      nullptr, /* hasProperty */
-                      nullptr, /* getProperty */
-                      nullptr, /* setProperty */
-                      nullptr, /* getOwnPropertyDescriptor */
-                      nullptr, /* deleteProperty */
-                      nullptr, /* watch */
-                      nullptr, /* unwatch */
-                      nullptr, /* getElements */
-                      nullptr, /* enumerate */
-                      nullptr, /* funToString */
-                    }
-                    """,
-                    objectMoved=objectMovedHook)
         else:
             classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
+            traceHook = 'nullptr'
             reservedSlots = slotCount
         if self.descriptor.interface.getExtendedAttribute("NeedResolve"):
             resolveHook = RESOLVE_HOOK_NAME
             mayResolveHook = MAY_RESOLVE_HOOK_NAME
             enumerateHook = ENUMERATE_HOOK_NAME
         elif self.descriptor.isGlobal():
             resolveHook = "mozilla::dom::ResolveGlobal"
             mayResolveHook = "mozilla::dom::MayResolveGlobal"
@@ -482,17 +448,22 @@ class CGDOMJSClass(CGThing):
                 ${resolve}, /* resolve */
                 ${mayResolve}, /* mayResolve */
                 ${finalize}, /* finalize */
                 ${call}, /* call */
                 nullptr,               /* hasInstance */
                 nullptr,               /* construct */
                 ${trace}, /* trace */
                 JS_NULL_CLASS_SPEC,
-                $*{classExtensionAndObjectOps}
+                {
+                  false,   /* isWrappedNative */
+                  nullptr, /* weakmapKeyDelegateOp */
+                  ${objectMoved} /* objectMovedOp */
+                },
+                JS_NULL_OBJECT_OPS
               },
               $*{descriptor}
             };
             static_assert(${instanceReservedSlots} == DOM_INSTANCE_RESERVED_SLOTS,
                           "Must have the right minimal number of reserved slots.");
             static_assert(${reservedSlots} >= ${slotCount},
                           "Must have enough reserved slots.");
             """,
@@ -500,17 +471,17 @@ class CGDOMJSClass(CGThing):
             flags=classFlags,
             addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'nullptr',
             enumerate=enumerateHook,
             resolve=resolveHook,
             mayResolve=mayResolveHook,
             finalize=FINALIZE_HOOK_NAME,
             call=callHook,
             trace=traceHook,
-            classExtensionAndObjectOps=classExtensionAndObjectOps,
+            objectMoved=objectMovedHook,
             descriptor=DOMClass(self.descriptor),
             instanceReservedSlots=INSTANCE_RESERVED_SLOTS,
             reservedSlots=reservedSlots,
             slotCount=slotCount)
 
 
 class CGDOMProxyJSClass(CGThing):
     """
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -83,17 +83,18 @@ skip-if = (toolkit == 'gonk')
 [test_browserElement_oop_PurgeHistory.html]
 [test_browserElement_oop_Reload.html]
 [test_browserElement_oop_ReloadPostRequest.html]
 [test_browserElement_oop_RemoveBrowserElement.html]
 [test_browserElement_oop_ScrollEvent.html]
 [test_browserElement_oop_SecurityChange.html]
 skip-if = toolkit == 'android' || (toolkit == 'gonk' && !debug) #TIMED_OUT, bug 766586
 [test_browserElement_oop_SelectionStateBlur.html]
-skip-if = (toolkit == 'gonk') # Disabled on b2g due to bug 1097419
+skip-if = (os == "android" || toolkit == 'gonk') # Disabled on b2g due to bug 1097419
+# Disabled on Android, see bug 1230230
 [test_browserElement_oop_SendEvent.html]
 [test_browserElement_oop_SetInputMethodActive.html]
 skip-if = (os == "android")
 [test_browserElement_oop_SetVisible.html]
 [test_browserElement_oop_SetVisibleFrames.html]
 [test_browserElement_oop_SetVisibleFrames2.html]
 [test_browserElement_oop_Stop.html]
 [test_browserElement_oop_TargetBlank.html]
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -218,17 +218,18 @@ skip-if = (toolkit == 'gonk' && !debug)
 skip-if = (toolkit == 'gonk')
 [test_browserElement_inproc_PurgeHistory.html]
 [test_browserElement_inproc_ReloadPostRequest.html]
 [test_browserElement_inproc_RemoveBrowserElement.html]
 [test_browserElement_inproc_ScrollEvent.html]
 [test_browserElement_inproc_SecurityChange.html]
 skip-if = toolkit == 'android' || (toolkit == 'gonk' && !debug) # android(TIMED_OUT, bug 766586) androidx86(TIMED_OUT, bug 766586)
 [test_browserElement_inproc_SelectionStateBlur.html]
-skip-if = (toolkit == 'gonk') # Disabled on b2g due to bug 1097419
+skip-if = (os == "android" || toolkit == 'gonk') # Disabled on b2g due to bug 1097419
+# Disabled on Android, see bug 1230230
 [test_browserElement_inproc_SendEvent.html]
 # The setInputMethodActive() tests will timed out on Android
 [test_browserElement_inproc_SetInputMethodActive.html]
 skip-if = (os == "android")
 [test_browserElement_inproc_SetVisible.html]
 [test_browserElement_inproc_SetVisibleFrames.html]
 [test_browserElement_inproc_SetVisibleFrames2.html]
 [test_browserElement_inproc_Stop.html]
--- a/dom/indexedDB/moz.build
+++ b/dom/indexedDB/moz.build
@@ -102,8 +102,9 @@ LOCAL_INCLUDES += [
     '/dom/workers',
     '/ipc/glue',
     '/xpcom/build',
     '/xpcom/threads',
 ]
 
 if CONFIG['ENABLE_INTL_API']:
     CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+    LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2960,16 +2960,25 @@ ContentChild::RecvShutdown()
     nsCOMPtr<nsIObserverService> os = services::GetObserverService();
     if (os) {
         os->NotifyObservers(static_cast<nsIContentChild*>(this),
                             "content-child-shutdown", nullptr);
     }
 
     GetIPCChannel()->SetAbortOnError(false);
 
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    if (profiler_is_active()) {
+        // We're shutting down while we were profiling. Send the
+        // profile up to the parent so that we don't lose this
+        // information.
+        Unused << RecvGatherProfile();
+    }
+#endif
+
     // Ignore errors here. If this fails, the parent will kill us after a
     // timeout.
     Unused << SendFinishShutdown();
     return true;
 }
 
 PBrowserOrId
 ContentChild::GetBrowserOrId(TabChild* aTabChild)
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -1564,16 +1564,21 @@ ContentParent::Init()
     DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
 
     if (profilerActive) {
         nsCOMPtr<nsIProfilerStartParams> currentProfilerParams;
         rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams));
         MOZ_ASSERT(NS_SUCCEEDED(rv));
 
+        nsCOMPtr<nsISupports> gatherer;
+        rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer));
+        MOZ_ASSERT(NS_SUCCEEDED(rv));
+        mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
+
         StartProfiler(currentProfilerParams);
     }
 #endif
 }
 
 void
 ContentParent::ForwardKnownInfo()
 {
@@ -2084,16 +2089,22 @@ ContentParent::ActorDestroy(ActorDestroy
         sNuwaPrefUpdates = nullptr;
     }
 #endif
 
     RecvRemoveGeolocationListener();
 
     mConsoleService = nullptr;
 
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    if (mGatherer && !mProfile.IsEmpty()) {
+        mGatherer->OOPExitProfile(mProfile);
+    }
+#endif
+
     if (obs) {
         RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
 
         props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID);
 
         if (AbnormalShutdown == why) {
             Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
                                   NS_LITERAL_CSTRING("content"), 1);
@@ -3288,28 +3299,30 @@ ContentParent::Observe(nsISupports* aSub
         Unused << SendOnAppThemeChanged();
     }
 #ifdef MOZ_ENABLE_PROFILER_SPS
     else if (!strcmp(aTopic, "profiler-started")) {
         nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
         StartProfiler(params);
     }
     else if (!strcmp(aTopic, "profiler-stopped")) {
+        mGatherer = nullptr;
         Unused << SendStopProfiler();
     }
     else if (!strcmp(aTopic, "profiler-paused")) {
         Unused << SendPauseProfiler(true);
     }
     else if (!strcmp(aTopic, "profiler-resumed")) {
         Unused << SendPauseProfiler(false);
     }
     else if (!strcmp(aTopic, "profiler-subprocess-gather")) {
-        mGatherer = static_cast<ProfileGatherer*>(aSubject);
-        mGatherer->WillGatherOOPProfile();
-        Unused << SendGatherProfile();
+        if (mGatherer) {
+            mGatherer->WillGatherOOPProfile();
+            Unused << SendGatherProfile();
+        }
     }
     else if (!strcmp(aTopic, "profiler-subprocess")) {
         nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
         if (pse) {
             if (!mProfile.IsEmpty()) {
                 pse->AddSubProfile(mProfile.get());
                 mProfile.Truncate();
             }
@@ -5651,17 +5664,16 @@ bool
 ContentParent::RecvProfile(const nsCString& aProfile)
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
     if (NS_WARN_IF(!mGatherer)) {
         return true;
     }
     mProfile = aProfile;
     mGatherer->GatheredOOPProfile();
-    mGatherer = nullptr;
 #endif
     return true;
 }
 
 bool
 ContentParent::RecvGetGraphicsDeviceInitData(DeviceInitData* aOut)
 {
   gfxPlatform::GetPlatform()->GetDeviceInitData(aOut);
@@ -5742,16 +5754,24 @@ ContentParent::StartProfiler(nsIProfiler
 
     ipcParams.enabled() = true;
     aParams->GetEntries(&ipcParams.entries());
     aParams->GetInterval(&ipcParams.interval());
     ipcParams.features() = aParams->GetFeatures();
     ipcParams.threadFilters() = aParams->GetThreadFilterNames();
 
     Unused << SendStartProfiler(ipcParams);
+
+    nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
+    if (NS_WARN_IF(!profiler)) {
+        return;
+    }
+    nsCOMPtr<nsISupports> gatherer;
+    profiler->GetProfileGatherer(getter_AddRefs(gatherer));
+    mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
 #endif
 }
 
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 
--- a/dom/media/AudioStream.h
+++ b/dom/media/AudioStream.h
@@ -83,17 +83,17 @@ public:
     : mBuffer(nullptr), mCapacity(0), mStart(0), mCount(0)
   {}
 
   // Set the capacity of the buffer in bytes.  Must be called before any
   // call to append or pop elements.
   void SetCapacity(uint32_t aCapacity) {
     MOZ_ASSERT(!mBuffer, "Buffer allocated.");
     mCapacity = aCapacity;
-    mBuffer = new uint8_t[mCapacity];
+    mBuffer = MakeUnique<uint8_t[]>(mCapacity);
   }
 
   uint32_t Length() {
     return mCount;
   }
 
   uint32_t Capacity() {
     return mCapacity;
@@ -132,22 +132,22 @@ public:
     mCount -= *aSize1 + *aSize2;
     mStart += *aSize1 + *aSize2;
     mStart %= mCapacity;
   }
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
   {
     size_t amount = 0;
-    amount += mBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    amount += aMallocSizeOf(mBuffer.get());
     return amount;
   }
 
 private:
-  nsAutoArrayPtr<uint8_t> mBuffer;
+  UniquePtr<uint8_t[]> mBuffer;
   uint32_t mCapacity;
   uint32_t mStart;
   uint32_t mCount;
 };
 
 // Access to a single instance of this class must be synchronized by
 // callers, or made from a single thread.  One exception is that access to
 // GetPosition, GetPositionInFrames, SetVolume, and Get{Rate,Channels},
--- a/dom/media/FileBlockCache.h
+++ b/dom/media/FileBlockCache.h
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef FILE_BLOCK_CACHE_H_
 #define FILE_BLOCK_CACHE_H_
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Monitor.h"
+#include "mozilla/UniquePtr.h"
 #include "nsTArray.h"
 #include "MediaCache.h"
 #include "nsDeque.h"
 #include "nsThreadUtils.h"
 
 struct PRFileDesc;
 
 namespace mozilla {
@@ -91,26 +92,26 @@ public:
 
     NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlockChange)
 
     // This block is waiting in memory to be written.
     // Stores a copy of the block, so we can write it asynchronously.
     explicit BlockChange(const uint8_t* aData)
       : mSourceBlockIndex(-1)
     {
-      mData = new uint8_t[BLOCK_SIZE];
+      mData = MakeUnique<uint8_t[]>(BLOCK_SIZE);
       memcpy(mData.get(), aData, BLOCK_SIZE);
     }
 
     // This block's contents are located in another file
     // block, i.e. this block has been moved.
     explicit BlockChange(int32_t aSourceBlockIndex)
       : mSourceBlockIndex(aSourceBlockIndex) {}
 
-    nsAutoArrayPtr<uint8_t> mData;
+    UniquePtr<uint8_t[]> mData;
     const int32_t mSourceBlockIndex;
 
     bool IsMove() const {
       return mSourceBlockIndex != -1;
     }
     bool IsWrite() const {
       return mSourceBlockIndex == -1 &&
              mData.get() != nullptr;
--- a/dom/media/MediaCache.cpp
+++ b/dom/media/MediaCache.cpp
@@ -374,31 +374,31 @@ MediaCacheStream::MediaCacheStream(Chann
     mChannelEnded(false),
     mChannelOffset(0),
     mStreamLength(-1),
     mStreamOffset(0),
     mPlaybackBytesPerSecond(10000),
     mPinCount(0),
     mCurrentMode(MODE_PLAYBACK),
     mMetadataInPartialBlockBuffer(false),
-    mPartialBlockBuffer(new int64_t[BLOCK_SIZE/sizeof(int64_t)])
+    mPartialBlockBuffer(MakeUnique<int64_t[]>(BLOCK_SIZE/sizeof(int64_t)))
 {
 }
 
 size_t MediaCacheStream::SizeOfExcludingThis(
                                 MallocSizeOf aMallocSizeOf) const
 {
   // Looks like these are not owned:
   // - mClient
   // - mPrincipal
   size_t size = mBlocks.ShallowSizeOfExcludingThis(aMallocSizeOf);
   size += mReadaheadBlocks.SizeOfExcludingThis(aMallocSizeOf);
   size += mMetadataBlocks.SizeOfExcludingThis(aMallocSizeOf);
   size += mPlayedBlocks.SizeOfExcludingThis(aMallocSizeOf);
-  size += mPartialBlockBuffer.SizeOfExcludingThis(aMallocSizeOf);
+  size += aMallocSizeOf(mPartialBlockBuffer.get());
 
   return size;
 }
 
 size_t MediaCacheStream::BlockList::SizeOfExcludingThis(
                                 MallocSizeOf aMallocSizeOf) const
 {
   return mEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
@@ -1837,17 +1837,17 @@ MediaCacheStream::FlushPartialBlockInter
                "mStreamOffset [%lld] mChannelOffset[%lld] mStreamLength [%lld] "
                "notifying: [%s]",
                this, blockOffset, mStreamOffset, mChannelOffset, mStreamLength,
                aNotifyAll ? "yes" : "no"));
 
     // Write back the partial block
     memset(reinterpret_cast<char*>(mPartialBlockBuffer.get()) + blockOffset, 0,
            BLOCK_SIZE - blockOffset);
-    gMediaCache->AllocateAndWriteBlock(this, mPartialBlockBuffer,
+    gMediaCache->AllocateAndWriteBlock(this, mPartialBlockBuffer.get(),
         mMetadataInPartialBlockBuffer ? MODE_METADATA : MODE_PLAYBACK);
   }
 
   // |mChannelOffset == 0| means download ends with no bytes received.
   // We should also wake up those readers who are waiting for data
   // that will never come.
   if ((blockOffset > 0 || mChannelOffset == 0) && aNotifyAll) {
     // Wake up readers who may be waiting for this data
--- a/dom/media/MediaCache.h
+++ b/dom/media/MediaCache.h
@@ -7,16 +7,17 @@
 #ifndef MediaCache_h_
 #define MediaCache_h_
 
 #include "nsTArray.h"
 #include "nsCOMPtr.h"
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
 #include "Intervals.h"
+#include "mozilla/UniquePtr.h"
 
 class nsIPrincipal;
 
 namespace mozilla {
 // defined in MediaResource.h
 class ChannelMediaResource;
 typedef media::IntervalSet<int64_t> MediaByteRangeSet;
 class MediaResource;
@@ -505,14 +506,14 @@ private:
 
   // Data received for the block containing mChannelOffset. Data needs
   // to wait here so we can write back a complete block. The first
   // mChannelOffset%BLOCK_SIZE bytes have been filled in with good data,
   // the rest are garbage.
   // Use int64_t so that the data is well-aligned.
   // Heap allocate this buffer since the exact power-of-2 will cause allocation
   // slop when combined with the rest of the object members.
-  nsAutoArrayPtr<int64_t> mPartialBlockBuffer;
+  UniquePtr<int64_t[]> mPartialBlockBuffer;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/MediaData.cpp
+++ b/dom/media/MediaData.cpp
@@ -7,16 +7,17 @@
 #include "MediaData.h"
 #include "MediaInfo.h"
 #ifdef MOZ_OMX_DECODER
 #include "GrallocImages.h"
 #include "mozilla/layers/TextureClient.h"
 #endif
 #include "VideoUtils.h"
 #include "ImageContainer.h"
+#include "mozilla/UniquePtrExtensions.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include <cutils/properties.h>
 #endif
 #include <stdint.h>
 
 namespace mozilla {
 
@@ -528,30 +529,29 @@ MediaRawData::Clone() const
 bool
 MediaRawData::EnsureCapacity(size_t aSize)
 {
   const size_t sizeNeeded = aSize + RAW_DATA_ALIGNMENT * 2;
 
   if (mData && mCapacity >= sizeNeeded) {
     return true;
   }
-  nsAutoArrayPtr<uint8_t> newBuffer;
-  newBuffer = new (fallible) uint8_t[sizeNeeded];
+  auto newBuffer = MakeUniqueFallible<uint8_t[]>(sizeNeeded);
   if (!newBuffer) {
     return false;
   }
 
   // Find alignment address.
   const uintptr_t alignmask = RAW_DATA_ALIGNMENT;
   uint8_t* newData = reinterpret_cast<uint8_t*>(
     (reinterpret_cast<uintptr_t>(newBuffer.get()) + alignmask) & ~alignmask);
   MOZ_ASSERT(uintptr_t(newData) % (RAW_DATA_ALIGNMENT+1) == 0);
   memcpy(newData, mData, mSize);
 
-  mBuffer = newBuffer.forget();
+  mBuffer = Move(newBuffer);
   mCapacity = sizeNeeded;
   mData = newData;
 
   return true;
 }
 
 MediaRawData::~MediaRawData()
 {
--- a/dom/media/MediaData.h
+++ b/dom/media/MediaData.h
@@ -420,17 +420,17 @@ private:
   // Ensure that the backend buffer can hold aSize data. Will update mData.
   // Will enforce that the start of allocated data is always 32 bytes
   // aligned and that it has sufficient end padding to allow for 32 bytes block
   // read as required by some data decoders.
   // Returns false if memory couldn't be allocated.
   bool EnsureCapacity(size_t aSize);
   uint8_t* mData;
   size_t mSize;
-  nsAutoArrayPtr<uint8_t> mBuffer;
+  UniquePtr<uint8_t[]> mBuffer;
   uint32_t mCapacity;
   CryptoSample mCryptoInternal;
   MediaRawData(const MediaRawData&); // Not implemented
 };
 
   // MediaByteBuffer is a ref counted infallible TArray.
 class MediaByteBuffer : public nsTArray<uint8_t> {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaByteBuffer);
--- a/dom/media/MediaDecoder.h
+++ b/dom/media/MediaDecoder.h
@@ -572,17 +572,17 @@ private:
   // so recompute it. The monitor must be held.
   virtual void UpdatePlaybackRate();
 
   // The actual playback rate computation. The monitor must be held.
   void ComputePlaybackRate();
 
   // Returns true if we can play the entire media through without stopping
   // to buffer, given the current download and playback rates.
-  bool CanPlayThrough();
+  virtual bool CanPlayThrough();
 
   void SetAudioChannel(dom::AudioChannel aChannel) { mAudioChannel = aChannel; }
   dom::AudioChannel GetAudioChannel() { return mAudioChannel; }
 
   /******
    * The following methods must only be called on the main
    * thread.
    ******/
--- a/dom/media/RtspMediaResource.cpp
+++ b/dom/media/RtspMediaResource.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/DebugOnly.h"
 
 #include "RtspMediaResource.h"
 
 #include "MediaDecoder.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/UniquePtr.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIStreamingProtocolService.h"
 #include "nsServiceManagerUtils.h"
 #ifdef NECKO_PROTOCOL_rtsp
 #include "mozilla/net/RtspChannelChild.h"
 #endif
 using namespace mozilla::net;
 using namespace mozilla::media;
@@ -67,30 +68,30 @@ public:
   , mFrameType(0)
   , mIsStarted(false)
   , mDuringPlayoutDelay(false)
   , mPlayoutDelayMs(kPlayoutDelayMs)
   , mPlayoutDelayTimer(nullptr) {
     MOZ_COUNT_CTOR(RtspTrackBuffer);
     mTrackIdx = aTrackIdx;
     MOZ_ASSERT(mSlotSize < UINT32_MAX / BUFFER_SLOT_NUM);
-    mRingBuffer = new uint8_t[mTotalBufferSize];
+    mRingBuffer = MakeUnique<uint8_t[]>(mTotalBufferSize);
     Reset();
   };
   ~RtspTrackBuffer() {
     MOZ_COUNT_DTOR(RtspTrackBuffer);
     mRingBuffer = nullptr;
   };
 
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
     // including this
     size_t size = aMallocSizeOf(this);
 
     // excluding this
-    size += mRingBuffer.SizeOfExcludingThis(aMallocSizeOf);
+    size += aMallocSizeOf(mRingBuffer.get());
 
     return size;
   }
 
   void Start() {
     MonitorAutoLock monitor(mMonitor);
     mIsStarted = true;
     mFrameType = 0;
@@ -174,17 +175,17 @@ private:
   // The value in mBufferSlotData[index].mLength represents:
   // -1(BUFFER_SLOT_INVALID): The index of slot data is invalid, mConsumerIdx
   //                          should go forward.
   // 0(BUFFER_SLOT_EMPTY): The index slot is empty. mConsumerIdx should wait here.
   // positive value: The index slot contains valid data and the value is data size.
   BufferSlotData mBufferSlotData[BUFFER_SLOT_NUM];
 
   // The ring buffer pointer.
-  nsAutoArrayPtr<uint8_t> mRingBuffer;
+  UniquePtr<uint8_t[]> mRingBuffer;
   // Each slot's size.
   uint32_t mSlotSize;
   // Total mRingBuffer's total size.
   uint32_t mTotalBufferSize;
   // A flag that that indicate the incoming data should be dropped or stored.
   // When we are seeking, the incoming data should be dropped.
   // Bit definition in |nsIStreamingProtocolController.h|
   uint32_t mFrameType;
--- a/dom/media/VideoSegment.cpp
+++ b/dom/media/VideoSegment.cpp
@@ -63,17 +63,17 @@ VideoFrame::CreateBlackImage(const gfx::
   const uint8_t lumaBpp = 8;
   const uint8_t chromaBpp = 4;
 
   layers::PlanarYCbCrData data;
   data.mYChannel = frame.rwget();
   data.mYSize = gfx::IntSize(aSize.width, aSize.height);
   data.mYStride = (int32_t) (aSize.width * lumaBpp / 8.0);
   data.mCbCrStride = (int32_t) (aSize.width * chromaBpp / 8.0);
-  data.mCbChannel = frame.rwget() + aSize.height * data.mYStride;
+  data.mCbChannel = frame.get() + aSize.height * data.mYStride;
   data.mCrChannel = data.mCbChannel + aSize.height * data.mCbCrStride / 2;
   data.mCbCrSize = gfx::IntSize(aSize.width / 2, aSize.height / 2);
   data.mPicX = 0;
   data.mPicY = 0;
   data.mPicSize = gfx::IntSize(aSize.width, aSize.height);
   data.mStereoMode = StereoMode::MONO;
 
   // SetData copies data, so we can free data.
--- a/dom/media/android/AndroidMediaResourceServer.cpp
+++ b/dom/media/android/AndroidMediaResourceServer.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "mozilla/Assertions.h"
 #include "mozilla/Base64.h"
 #include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/UniquePtr.h"
 #include "nsThreadUtils.h"
 #include "nsIServiceManager.h"
 #include "nsISocketTransport.h"
 #include "nsIOutputStream.h"
 #include "nsIInputStream.h"
 #include "nsIRandomGenerator.h"
 #include "nsReadLine.h"
 #include "nsNetCID.h"
@@ -258,63 +259,63 @@ ServeResourceEvent::Run() {
   rv = WriteAll(response_line, strlen(response_line));
   if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
 
   // Buffer used for reading from the input stream and writing to
   // the output stream. The buffer size should be big enough for the
   // HTTP response headers sent below. A static_assert ensures
   // this where the buffer is used.
   const int buffer_size = 32768;
-  nsAutoArrayPtr<char> b(new char[buffer_size]);
+  auto b = MakeUnique<char[]>(buffer_size);
 
   // If we know the length of the resource, send a Content-Length header.
   int64_t contentlength = resource->GetLength() - start;
   if (contentlength > 0) {
     static_assert (buffer_size > 1024,
                    "buffer_size must be large enough "
                    "to hold response headers");
-    snprintf(b, buffer_size, "Content-Length: %" PRId64 "\r\n", contentlength);
-    rv = WriteAll(b, strlen(b));
+    snprintf(b.get(), buffer_size, "Content-Length: %" PRId64 "\r\n", contentlength);
+    rv = WriteAll(b.get(), strlen(b.get()));
     if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
   }
 
   // If the request was a byte range request, respond with a Content-Range
   // header which details the extent of the data returned.
   if (start > 0) {
     static_assert (buffer_size > 1024,
                    "buffer_size must be large enough "
                    "to hold response headers");
-    snprintf(b, buffer_size, "Content-Range: "
+    snprintf(b.get(), buffer_size, "Content-Range: "
              "bytes %" PRId64 "-%" PRId64 "/%" PRId64 "\r\n",
              start, resource->GetLength() - 1, resource->GetLength());
-    rv = WriteAll(b, strlen(b));
+    rv = WriteAll(b.get(), strlen(b.get()));
     if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
   }
 
   rv = WriteAll(response_end, strlen(response_end));
   if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
 
   rv = mOutput->Flush();
   if (NS_FAILED(rv)) { Shutdown(); return NS_OK; }
 
   // Read data from media resource
   uint32_t bytesRead = 0; // Number of bytes read/written to streams
-  rv = resource->ReadAt(start, b, buffer_size, &bytesRead);
+  rv = resource->ReadAt(start, b.get(), buffer_size, &bytesRead);
   while (NS_SUCCEEDED(rv) && bytesRead != 0) {
     // Keep track of what we think the starting position for the next read
     // is. This is used in subsequent ReadAt calls to ensure we are reading
     // from the correct offset in the case where another thread is reading
     // from th same MediaResource.
     start += bytesRead;
 
     // Write data obtained from media resource to output stream
-    rv = WriteAll(b, bytesRead);
+    rv = WriteAll(b.get(), bytesRead);
     if (NS_FAILED (rv)) break;
 
-    rv = resource->ReadAt(start, b, 32768, &bytesRead);
+    rv = resource->ReadAt(start, b.get(), 32768, &bytesRead);
   }
 
   Shutdown();
   return NS_OK;
 }
 
 void
 ServeResourceEvent::Shutdown()
--- a/dom/media/eme/MediaKeySession.cpp
+++ b/dom/media/eme/MediaKeySession.cpp
@@ -204,21 +204,22 @@ MediaKeySession::GenerateRequest(const n
   nsAutoCString base64InitData(ToBase64(data));
   PromiseId pid = mKeys->StorePromise(promise);
   mKeys->GetCDMProxy()->CreateSession(Token(),
                                       mSessionType,
                                       pid,
                                       aInitDataType, data);
 
   EME_LOG("MediaKeySession[%p,'%s'] GenerateRequest() sent, "
-          "promiseId=%d initData(base64)='%s'",
+          "promiseId=%d initData(base64)='%s' initDataType='%s'",
           this,
           NS_ConvertUTF16toUTF8(mSessionId).get(),
           pid,
-          base64InitData.get());
+          base64InitData.get(),
+          NS_ConvertUTF16toUTF8(aInitDataType).get());
 
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 MediaKeySession::Load(const nsAString& aSessionId, ErrorResult& aRv)
 {
   RefPtr<DetailedPromise> promise(MakePromise(aRv,
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -436,19 +436,22 @@ GetSupportedConfig(mozIGeckoMediaPluginS
                    const MediaKeySystemConfiguration& aCandidate,
                    MediaKeySystemConfiguration& aOutConfig)
 {
   MediaKeySystemConfiguration config;
   config.mLabel = aCandidate.mLabel;
   if (aCandidate.mInitDataTypes.WasPassed()) {
     nsTArray<nsString> initDataTypes;
     for (const nsString& candidate : aCandidate.mInitDataTypes.Value()) {
+      // All supported keySystems can handle "cenc" initDataType.
+      // ClearKey also supports "keyids" and "webm" initDataTypes.
       if (candidate.EqualsLiteral("cenc")) {
         initDataTypes.AppendElement(candidate);
-      } else if (candidate.EqualsLiteral("keyids") &&
+      } else if ((candidate.EqualsLiteral("keyids") ||
+                  candidate.EqualsLiteral("webm)")) &&
                  aKeySystem.EqualsLiteral("org.w3.clearkey")) {
         initDataTypes.AppendElement(candidate);
       }
     }
     if (initDataTypes.IsEmpty()) {
       return false;
     }
     config.mInitDataTypes.Construct();
--- a/dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp
+++ b/dom/media/encoder/fmp4_muxer/ISOMediaBoxes.cpp
@@ -122,17 +122,17 @@ TrackRunBox::fillSampleTable()
   nsTArray<RefPtr<EncodedFrame>> frames;
   FragmentBuffer* frag = mControl->GetFragment(mTrackType);
 
   rv = frag->GetFirstFragment(frames);
   if (NS_FAILED(rv)) {
     return 0;
   }
   uint32_t len = frames.Length();
-  sample_info_table = new tbl[len];
+  sample_info_table = MakeUnique<tbl[]>(len);
   // Create sample table according to 14496-12 8.8.8.2.
   for (uint32_t i = 0; i < len; i++) {
     // Sample size.
     sample_info_table[i].sample_size = 0;
     if (flags.to_ulong() & flags_sample_size_present) {
       sample_info_table[i].sample_size = frames.ElementAt(i)->GetFrameData().Length();
       mAllSampleSize += sample_info_table[i].sample_size;
       table_size += sizeof(uint32_t);
--- a/dom/media/encoder/fmp4_muxer/ISOMediaBoxes.h
+++ b/dom/media/encoder/fmp4_muxer/ISOMediaBoxes.h
@@ -6,16 +6,17 @@
 #ifndef ISOMediaBoxes_h_
 #define ISOMediaBoxes_h_
 
 #include <bitset>
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "MuxerOperation.h"
+#include "mozilla/UniquePtr.h"
 
 #define WRITE_FULLBOX(_compositor, _size)       \
   BoxSizeChecker checker(_compositor, _size);   \
   FullBox::Write();
 
 #define FOURCC(a, b, c, d) ( ((a) << 24) | ((b) << 16) | ((c) << 8) | (d) )
 
 namespace mozilla {
@@ -257,17 +258,17 @@ public:
     uint32_t sample_flags;
     uint32_t sample_composition_time_offset;
   } tbl;
 
   uint32_t sample_count;
   // the following are optional fields
   uint32_t data_offset; // data offset exists when audio/video are present in file.
   uint32_t first_sample_flags;
-  nsAutoArrayPtr<tbl> sample_info_table;
+  UniquePtr<tbl[]> sample_info_table;
 
   // MuxerOperation methods
   nsresult Generate(uint32_t* aBoxSize) override;
   nsresult Write() override;
 
   // TrackRunBox methods
   uint32_t GetAllSampleSize() { return mAllSampleSize; }
   nsresult SetDataOffset(uint32_t aOffset);
@@ -399,17 +400,17 @@ public:
 class ChunkOffsetBox : public FullBox {
 public:
   // ISO BMFF members
   typedef struct {
     uint32_t chunk_offset;
   } tbl;
 
   uint32_t entry_count;
-  nsAutoArrayPtr<tbl> sample_tbl;
+  UniquePtr<tbl[]> sample_tbl;
 
   // MuxerOperation methods
   nsresult Generate(uint32_t* aBoxSize) override;
   nsresult Write() override;
 
   // ChunkOffsetBox methods
   ChunkOffsetBox(uint32_t aType, ISOControl* aControl);
   ~ChunkOffsetBox();
@@ -425,17 +426,17 @@ public:
   // ISO BMFF members
   typedef struct {
     uint32_t first_chunk;
     uint32_t sample_per_chunk;
     uint32_t sample_description_index;
   } tbl;
 
   uint32_t entry_count;
-  nsAutoArrayPtr<tbl> sample_tbl;
+  UniquePtr<tbl[]> sample_tbl;
 
   // MuxerOperation methods
   nsresult Generate(uint32_t* aBoxSize) override;
   nsresult Write() override;
 
   // SampleToChunkBox methods
   SampleToChunkBox(uint32_t aType, ISOControl* aControl);
   ~SampleToChunkBox();
@@ -450,17 +451,17 @@ class TimeToSampleBox : public FullBox {
 public:
   // ISO BMFF members
   typedef struct {
     uint32_t sample_count;
     uint32_t sample_delta;
   } tbl;
 
   uint32_t entry_count;
-  nsAutoArrayPtr<tbl> sample_tbl;
+  UniquePtr<tbl[]> sample_tbl;
 
   // MuxerOperation methods
   nsresult Generate(uint32_t* aBoxSize) override;
   nsresult Write() override;
 
   // TimeToSampleBox methods
   TimeToSampleBox(uint32_t aType, ISOControl* aControl);
   ~TimeToSampleBox();
--- a/dom/media/fmp4/MP4Stream.cpp
+++ b/dom/media/fmp4/MP4Stream.cpp
@@ -39,17 +39,17 @@ MP4Stream::BlockingReadIntoCache(int64_t
     if (NS_FAILED(rv)) {
       return false;
     }
   }
 
   MOZ_ASSERT(block.mCount >= bytesRead);
   block.mCount = bytesRead;
 
-  mCache.AppendElement(block);
+  mCache.AppendElement(Move(block));
   return true;
 }
 
 // We surreptitiously reimplement the supposedly-blocking ReadAt as a non-
 // blocking CachedReadAt, and record when it fails. This allows MP4Reader
 // to retry the read as an actual blocking read without holding the lock.
 bool
 MP4Stream::ReadAt(int64_t aOffset, void* aBuffer, size_t aCount,
--- a/dom/media/fmp4/MP4Stream.h
+++ b/dom/media/fmp4/MP4Stream.h
@@ -8,16 +8,17 @@
 #define MP4_STREAM_H_
 
 #include "mp4_demuxer/Stream.h"
 
 #include "MediaResource.h"
 
 #include "mozilla/Maybe.h"
 #include "mozilla/Monitor.h"
+#include "mozilla/UniquePtrExtensions.h"
 
 namespace mozilla {
 
 class Monitor;
 
 class MP4Stream : public mp4_demuxer::Stream {
 public:
   explicit MP4Stream(MediaResource* aResource);
@@ -69,29 +70,38 @@ private:
   uint32_t mPinCount;
 
   struct CacheBlock {
     CacheBlock(int64_t aOffset, size_t aCount)
       : mOffset(aOffset), mCount(aCount), mBuffer(nullptr) {}
     int64_t mOffset;
     size_t mCount;
 
+    CacheBlock(CacheBlock&& aOther)
+      : mOffset(aOther.mOffset)
+      , mCount(aOther.mCount)
+      , mBuffer(Move(aOther.mBuffer))
+    {}
+
     bool Init()
     {
-      mBuffer = new (fallible) char[mCount];
+      mBuffer = MakeUniqueFallible<char[]>(mCount);
       return !!mBuffer;
     }
 
     char* Buffer()
     {
       MOZ_ASSERT(mBuffer.get());
       return mBuffer.get();
     }
 
   private:
-    nsAutoArrayPtr<char> mBuffer;
+    CacheBlock(const CacheBlock&) = delete;
+    CacheBlock& operator=(const CacheBlock&) = delete;
+
+    UniquePtr<char[]> mBuffer;
   };
   nsTArray<CacheBlock> mCache;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/gmp/GMPLoader.cpp
+++ b/dom/media/gmp/GMPLoader.cpp
@@ -2,16 +2,17 @@
  * vim: sw=4 ts=4 et :
  * 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 "GMPLoader.h"
 #include <stdio.h>
 #include "mozilla/Attributes.h"
+#include "mozilla/UniquePtr.h"
 #include "gmp-entrypoints.h"
 #include "prlink.h"
 #include "prenv.h"
 #include "nsAutoPtr.h"
 
 #include <string>
 
 #ifdef XP_WIN
@@ -242,22 +243,22 @@ GMPLoaderImpl::Load(const char* aUTF8Lib
   // Load the GMP.
   PRLibSpec libSpec;
 #ifdef XP_WIN
   int pathLen = MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, nullptr, 0);
   if (pathLen == 0) {
     return false;
   }
 
-  nsAutoArrayPtr<wchar_t> widePath(new wchar_t[pathLen]);
-  if (MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, widePath, pathLen) == 0) {
+  auto widePath = MakeUnique<wchar_t[]>(pathLen);
+  if (MultiByteToWideChar(CP_UTF8, 0, aUTF8LibPath, -1, widePath.get(), pathLen) == 0) {
     return false;
   }
 
-  libSpec.value.pathname_u = widePath;
+  libSpec.value.pathname_u = widePath.get();
   libSpec.type = PR_LibSpec_PathnameU;
 #else
   libSpec.value.pathname = aUTF8LibPath;
   libSpec.type = PR_LibSpec_Pathname;
 #endif
   mLib = PR_LoadLibraryWithFlags(libSpec, 0);
   if (!mLib) {
     return false;
--- a/dom/media/mediasource/MediaSourceDecoder.cpp
+++ b/dom/media/mediasource/MediaSourceDecoder.cpp
@@ -11,16 +11,17 @@
 #include "MediaDecoderStateMachine.h"
 #include "MediaSource.h"
 #include "MediaSourceResource.h"
 #include "MediaSourceUtils.h"
 #include "VideoUtils.h"
 #include "MediaFormatReader.h"
 #include "MediaSourceDemuxer.h"
 #include "SourceBufferList.h"
+#include <algorithm>
 
 extern mozilla::LogModule* GetMediaSourceLog();
 
 #define MSE_DEBUG(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Debug, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 #define MSE_DEBUGV(arg, ...) MOZ_LOG(GetMediaSourceLog(), mozilla::LogLevel::Verbose, ("MediaSourceDecoder(%p)::%s: " arg, this, __func__, ##__VA_ARGS__))
 
 using namespace mozilla::media;
 
@@ -245,12 +246,53 @@ MediaSourceDecoder::GetMozDebugReaderDat
 
 double
 MediaSourceDecoder::GetDuration()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return ExplicitDuration();
 }
 
+MediaDecoderOwner::NextFrameStatus
+MediaSourceDecoder::NextFrameBufferedStatus()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  // Next frame hasn't been decoded yet.
+  // Use the buffered range to consider if we have the next frame available.
+  TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition());
+  TimeInterval interval(currentPosition,
+                        currentPosition + media::TimeUnit::FromMicroseconds(DEFAULT_NEXT_FRAME_AVAILABLE_BUFFERED),
+                        MediaSourceDemuxer::EOS_FUZZ);
+  return GetBuffered().Contains(interval)
+    ? MediaDecoderOwner::NEXT_FRAME_AVAILABLE
+    : MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
+}
+
+bool
+MediaSourceDecoder::CanPlayThrough()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (IsNaN(mMediaSource->Duration())) {
+    // Don't have any data yet.
+    return false;
+  }
+  TimeUnit duration = TimeUnit::FromSeconds(mMediaSource->Duration());
+  TimeUnit currentPosition = TimeUnit::FromMicroseconds(CurrentPosition());
+  if (duration.IsInfinite()) {
+    // We can't make an informed decision and just assume that it's a live stream
+    return true;
+  } else if (duration <= currentPosition) {
+    return true;
+  }
+  // If we have data up to the mediasource's duration or 30s ahead, we can
+  // assume that we can play without interruption.
+  TimeUnit timeAhead =
+    std::min(duration, currentPosition + TimeUnit::FromSeconds(30));
+  TimeInterval interval(currentPosition,
+                        timeAhead,
+                        MediaSourceDemuxer::EOS_FUZZ);
+  return GetBuffered().Contains(interval);
+}
+
 #undef MSE_DEBUG
 #undef MSE_DEBUGV
 
 } // namespace mozilla
--- a/dom/media/mediasource/MediaSourceDecoder.h
+++ b/dom/media/mediasource/MediaSourceDecoder.h
@@ -72,16 +72,19 @@ public:
   }
 
   // Returns a string describing the state of the MediaSource internal
   // buffered data. Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
   void AddSizeOfResources(ResourceSizes* aSizes) override;
 
+  MediaDecoderOwner::NextFrameStatus NextFrameBufferedStatus() override;
+  bool CanPlayThrough() override;
+
 private:
   void DoSetMediaSourceDuration(double aDuration);
 
   // The owning MediaSource holds a strong reference to this decoder, and
   // calls Attach/DetachMediaSource on this decoder to set and clear
   // mMediaSource.
   dom::MediaSource* mMediaSource;
   RefPtr<MediaSourceDemuxer> mDemuxer;
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -14,29 +14,29 @@
 #include "nsPrintfCString.h"
 
 namespace mozilla {
 
 typedef TrackInfo::TrackType TrackType;
 using media::TimeUnit;
 using media::TimeIntervals;
 
-// Gap allowed between frames. Due to inaccuracies in determining buffer end
-// frames (Bug 1065207). This value is based on the end of frame
-// default value used in Blink, kDefaultBufferDurationInMs.
-#define EOS_FUZZ_US 125000
-
 MediaSourceDemuxer::MediaSourceDemuxer()
   : mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK),
                              /* aSupportsTailDispatch = */ true))
   , mMonitor("MediaSourceDemuxer")
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
+// Due to inaccuracies in determining buffer end
+// frames (Bug 1065207). This value is based on the end of frame
+// default value used in Blink, kDefaultBufferDurationInMs.
+const TimeUnit MediaSourceDemuxer::EOS_FUZZ = media::TimeUnit::FromMicroseconds(125000);
+
 RefPtr<MediaSourceDemuxer::InitPromise>
 MediaSourceDemuxer::Init()
 {
   return InvokeAsync(GetTaskQueue(), this, __func__,
                      &MediaSourceDemuxer::AttemptInit);
 }
 
 RefPtr<MediaSourceDemuxer::InitPromise>
@@ -370,39 +370,40 @@ MediaSourceTrackDemuxer::BreakCycles()
     } );
   mParent->GetTaskQueue()->Dispatch(task.forget());
 }
 
 RefPtr<MediaSourceTrackDemuxer::SeekPromise>
 MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
 {
   TimeIntervals buffered = mManager->Buffered(mType);
-  buffered.SetFuzz(TimeUnit::FromMicroseconds(EOS_FUZZ_US));
+  buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
 
   if (!buffered.Contains(aTime)) {
     // We don't have the data to seek to.
     return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA,
                                         __func__);
   }
   TimeUnit seekTime =
-    mManager->Seek(mType, aTime, TimeUnit::FromMicroseconds(EOS_FUZZ_US));
+    mManager->Seek(mType, aTime, MediaSourceDemuxer::EOS_FUZZ);
   {
     MonitorAutoLock mon(mMonitor);
     mNextRandomAccessPoint = mManager->GetNextRandomAccessPoint(mType);
   }
   return SeekPromise::CreateAndResolve(seekTime, __func__);
 }
 
 RefPtr<MediaSourceTrackDemuxer::SamplesPromise>
 MediaSourceTrackDemuxer::DoGetSamples(int32_t aNumSamples)
 {
   bool error;
-  RefPtr<MediaRawData> sample = mManager->GetSample(mType,
-                                                      TimeUnit::FromMicroseconds(EOS_FUZZ_US),
-                                                      error);
+  RefPtr<MediaRawData> sample =
+    mManager->GetSample(mType,
+                        MediaSourceDemuxer::EOS_FUZZ,
+                        error);
   if (!sample) {
     if (error) {
       return SamplesPromise::CreateAndReject(DemuxerFailureReason::DEMUXER_ERROR, __func__);
     }
     return SamplesPromise::CreateAndReject(
       mManager->IsEnded() ? DemuxerFailureReason::END_OF_STREAM :
                             DemuxerFailureReason::WAITING_FOR_DATA, __func__);
   }
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -50,16 +50,19 @@ public:
   TaskQueue* GetTaskQueue() { return mTaskQueue; }
 
   // Returns a string describing the state of the MediaSource internal
   // buffered data. Used for debugging purposes.
   void GetMozDebugReaderData(nsAString& aString);
 
   void AddSizeOfResources(MediaSourceDecoder::ResourceSizes* aSizes);
 
+  // Gap allowed between frames.
+  static const media::TimeUnit EOS_FUZZ;
+
 private:
   ~MediaSourceDemuxer();
   friend class MediaSourceTrackDemuxer;
   // Scan source buffers and update information.
   bool ScanSourceBuffersForContent();
   RefPtr<InitPromise> AttemptInit();
   TrackBuffersManager* GetManager(TrackInfo::TrackType aType);
   TrackInfo* GetTrackInfo(TrackInfo::TrackType);
--- a/dom/media/mediasource/test/mochitest.ini
+++ b/dom/media/mediasource/test/mochitest.ini
@@ -58,16 +58,18 @@ skip-if = ((os == "win" && os_version ==
 [test_MediaSource.html]
 [test_MediaSource_memory_reporting.html]
 [test_MediaSource_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_MediaSource_disabled.html]
 [test_MultipleInitSegments.html]
 [test_MultipleInitSegments_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
+[test_PlayEvents.html]
+skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableAfterEndOfStream.html]
 [test_SeekableAfterEndOfStream_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableAfterEndOfStreamSplit.html]
 [test_SeekableAfterEndOfStreamSplit_mp4.html]
 skip-if = ((os == "win" && os_version == "5.1") || (os != "win" && os != "mac")) # Only supported on osx and vista+
 [test_SeekableBeforeEndOfStream.html]
 [test_SeekableBeforeEndOfStream_mp4.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/mediasource/test/test_PlayEvents.html
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>MSE: basic functionality</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="mediasource.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+// This test checks that readyState is properly set and the appropriate events are being fired accordingly:
+// 1. Load 1.6s of data and ensure that canplay event is fired.
+// 2. Load data to have a complete buffered range from 0 to duration and ensure that canplaythrough is fired.
+// 3. Seek to an area with no buffered data, and ensure that readyState goes back to HAVE_METADATA
+// 4. Load 1.6s of data at the seek position and ensure that canplay is fired and that readyState is now HAVE_FUTURE_DATA
+// 5. Start playing video and check that once it reaches a position with no data, readyState goes back to HAVE_CURRENT_DATA and waiting event is fired.
+// 6. Add 1.6s of data once video element fired waiting, that canplay is fired once readyState is HAVE_FUTURE_DATA.
+// 7. Finally load data to the end and ensure that canplaythrough is fired and that readyState is now HAVE_ENOUGH_DATA
+
+runWithMSE(function(ms, el) {
+  el.controls = true;
+  once(ms, 'sourceopen').then(function() {
+    ok(true, "Receive a sourceopen event");
+    var videosb = ms.addSourceBuffer("video/mp4");
+    el.addEventListener("error", function(e) {
+      ok(false, "should not fire '" + e + "' event");
+    });
+    is(el.readyState, el.HAVE_NOTHING, "readyState is HAVE_NOTHING");
+    fetchAndLoad(videosb, 'bipbop/bipbop_video', ['init'], '.mp4')
+    .then(once.bind(null, el, 'loadedmetadata'))
+    .then(function() {
+       ok(true, "got loadedmetadata event");
+       var promises = [];
+       promises.push(once(el, 'loadeddata'));
+       promises.push(once(el, 'canplay'));
+       // Load [0, 1.601666). We must ensure that we load over 25 frames as the
+       // windows H264 decoder will not produce a sample until then
+       // (bug 1191138).
+       promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(1, 3), '.m4s'));
+       return Promise.all(promises);
+    })
+    .then(function() {
+      ok(true, "got canplay event");
+      // set element duration to 3.203333s. We do so in order to guarantee that
+      // the end of the buffered range will be equal to duration, causing
+      // canplaythrough to be fired later.
+      ms.duration = 3.203333;
+      return once(el, 'durationchange');
+    })
+    .then(function() {
+      ok(true, "got durationchange event");
+      var promises = [];
+      promises.push(once(el, 'canplaythrough'));
+      // Load [0.801666, 3.203333]
+      promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(3, 5), '.m4s'));
+      return Promise.all(promises);
+    })
+    .then(function() {
+      ok(true, "got canplaythrough event");
+     // set element duration to 9.203333s, this value is set to coincide with
+     // data added later (we now have an empty range from 6s to 9.203333s).
+     ms.duration = 9.203333;
+     return once(el, 'durationchange');
+    })
+    .then(function() {
+      ok(true, "got durationchange event");
+      // An arbitrary value, so we are guaranteed to be in a range with no data.
+      el.currentTime = 6;
+      videosb.timestampOffset = 6;
+      ok(el.seeking, "seeking started");
+      return once(el, 'seeking');
+    })
+    .then(function() {
+      ok(true, "got seeking event");
+      is(el.readyState, el.HAVE_METADATA, "readyState is HAVE_METADATA");
+      var promises = [];
+      promises.push(once(el, 'seeked'));
+      promises.push(once(el, 'canplay'));
+      // Load [6+0, 6+1.601666)
+      promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(1, 3), '.m4s'));
+      return Promise.all(promises);
+    })
+    .then(function() {
+      ok(true, "got seeked and canplay event");
+      is(el.currentTime, 6, "seeked to 6s");
+      is(el.readyState, el.HAVE_FUTURE_DATA, "readyState is HAVE_FUTURE_DATA");
+      var promises = [];
+      promises.push(once(el, 'canplaythrough'));
+      // Load [6+1.60166, 6+3.203333]
+      promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(3, 5), '.m4s'));
+      return Promise.all(promises);
+    })
+    .then(function() {
+      ok(true, "got canplaythrough event");
+      // set element duration to 19.805s, this value is set to coincide with
+      // data added later (we now have an empty range from 15 to 19.805).
+      ms.duration = 19.805;
+      return once(el, 'durationchange');
+    })
+    .then(function() {
+      ok(true, "got durationchange event");
+      el.currentTime = 15;
+      videosb.timestampOffset = 15;
+      ok(el.seeking, "seeking started");
+      return once(el, 'seeking');
+    })
+    .then(function() {
+      ok(true, "got seeking event");
+      var promises = [];
+      promises.push(once(el, 'seeked'));
+      // Load [15+0, 15+1.601666)
+      promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(1, 3), '.m4s'));
+      return Promise.all(promises);
+    })
+    .then(function() {
+      ok(true, "got seeked event");
+      // Load [15+1.60166, 15+3.203333]
+      return fetchAndLoad(videosb, 'bipbop/bipbop_video', range(3, 5), '.m4s');
+    })
+    .then(function() {
+      ok(true, "data loaded");
+      // Playback we play for a little while then stall.
+      var promises = [];
+      promises.push(once(el, 'playing'));
+      promises.push(once(el, 'waiting'));
+      el.play();
+      return Promise.all(promises);
+    })
+    .then(function() {
+      ok(true, "got playing and waiting event");
+      // Playback has stalled, readyState is back to HAVE_CURRENT_DATA.
+      is(el.readyState, el.HAVE_CURRENT_DATA, "readyState is HAVE_CURRENT_DATA");
+      var promises = [];
+      promises.push(once(el, 'playing'));
+      promises.push(once(el, 'canplay'));
+      promises.push(once(el, 'canplaythrough'));
+      // Load [15+3.203333, 15+4.805)
+      // Our final buffered range will now be [0, 3.203333)[6, 9.203333)[15, 19.805)
+      promises.push(fetchAndLoad(videosb, 'bipbop/bipbop_video', range(5, 7), '.m4s'));
+      return Promise.all(promises);
+    })
+    .then(function() {
+      ok(true, "got playing, canplay and canplaythrough event");
+      SimpleTest.finish();
+    })
+  });
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp
@@ -6,16 +6,17 @@
 
 #include "ImageContainer.h"
 #include "MediaDecoderReader.h"
 #include "MediaInfo.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/mozalloc.h" // for operator new, and new (fallible)
 #include "mozilla/RefPtr.h"
 #include "mozilla/TaskQueue.h"
+#include "mozilla/UniquePtr.h"
 #include "nsRect.h"
 #include "PlatformDecoderModule.h"
 #include "TimeUnits.h"
 #include "VideoUtils.h"
 
 namespace mozilla {
 
 // Decoder that uses a passed in object's Create function to create blank
@@ -113,38 +114,38 @@ public:
 
   already_AddRefed<MediaData>
   Create(const media::TimeUnit& aDTS, const media::TimeUnit& aDuration, int64_t aOffsetInStream)
   {
     // Create a fake YUV buffer in a 420 format. That is, an 8bpp Y plane,
     // with a U and V plane that are half the size of the Y plane, i.e 8 bit,
     // 2x2 subsampled. Have the data pointers of each frame point to the
     // first plane, they'll always be zero'd memory anyway.
-    nsAutoArrayPtr<uint8_t> frame(new uint8_t[mFrameWidth * mFrameHeight]);
-    memset(frame, 0, mFrameWidth * mFrameHeight);
+    auto frame = MakeUnique<uint8_t[]>(mFrameWidth * mFrameHeight);
+    memset(frame.get(), 0, mFrameWidth * mFrameHeight);
     VideoData::YCbCrBuffer buffer;
 
     // Y plane.
-    buffer.mPlanes[0].mData = frame;
+    buffer.mPlanes[0].mData = frame.get();
     buffer.mPlanes[0].mStride = mFrameWidth;
     buffer.mPlanes[0].mHeight = mFrameHeight;
     buffer.mPlanes[0].mWidth = mFrameWidth;
     buffer.mPlanes[0].mOffset = 0;
     buffer.mPlanes[0].mSkip = 0;
 
     // Cb plane.
-    buffer.mPlanes[1].mData = frame;
+    buffer.mPlanes[1].mData = frame.get();
     buffer.mPlanes[1].mStride = mFrameWidth / 2;
     buffer.mPlanes[1].mHeight = mFrameHeight / 2;
     buffer.mPlanes[1].mWidth = mFrameWidth / 2;
     buffer.mPlanes[1].mOffset = 0;
     buffer.mPlanes[1].mSkip = 0;
 
     // Cr plane.
-    buffer.mPlanes[2].mData = frame;
+    buffer.mPlanes[2].mData = frame.get();
     buffer.mPlanes[2].mStride = mFrameWidth / 2;
     buffer.mPlanes[2].mHeight = mFrameHeight / 2;
     buffer.mPlanes[2].mWidth = mFrameWidth / 2;
     buffer.mPlanes[2].mOffset = 0;
     buffer.mPlanes[2].mSkip = 0;
 
     return VideoData::Create(mInfo,
                              mImageContainer,
--- a/dom/media/platforms/apple/AppleATDecoder.cpp
+++ b/dom/media/platforms/apple/AppleATDecoder.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AppleUtils.h"
 #include "MP4Decoder.h"
 #include "mp4_demuxer/Adts.h"
 #include "MediaInfo.h"
 #include "AppleATDecoder.h"
 #include "mozilla/Logging.h"
+#include "mozilla/UniquePtr.h"
 
 extern mozilla::LogModule* GetPDMLog();
 #define LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 #define FourCC2Str(n) ((char[5]){(char)(n >> 24), (char)(n >> 16), (char)(n >> 8), (char)(n), 0})
 
 namespace mozilla {
 
 AppleATDecoder::AppleATDecoder(const AudioInfo& aConfig,
@@ -206,26 +207,25 @@ AppleATDecoder::DecodeSample(MediaRawDat
   nsTArray<AudioDataValue> outputData;
   UInt32 channels = mOutputFormat.mChannelsPerFrame;
   // Pick a multiple of the frame size close to a power of two
   // for efficient allocation.
   const uint32_t MAX_AUDIO_FRAMES = 128;
   const uint32_t maxDecodedSamples = MAX_AUDIO_FRAMES * channels;
 
   // Descriptions for _decompressed_ audio packets. ignored.
-  nsAutoArrayPtr<AudioStreamPacketDescription>
-    packets(new AudioStreamPacketDescription[MAX_AUDIO_FRAMES]);
+  auto packets = MakeUnique<AudioStreamPacketDescription[]>(MAX_AUDIO_FRAMES);
 
   // This API insists on having packets spoon-fed to it from a callback.
   // This structure exists only to pass our state.
   PassthroughUserData userData =
     { channels, (UInt32)aSample->Size(), aSample->Data() };
 
   // Decompressed audio buffer
-  nsAutoArrayPtr<AudioDataValue> decoded(new AudioDataValue[maxDecodedSamples]);
+  auto decoded = MakeUnique<AudioDataValue[]>(maxDecodedSamples);
 
   do {
     AudioBufferList decBuffer;
     decBuffer.mNumberBuffers = 1;
     decBuffer.mBuffers[0].mNumberChannels = channels;
     decBuffer.mBuffers[0].mDataByteSize =
       maxDecodedSamples * sizeof(AudioDataValue);
     decBuffer.mBuffers[0].mData = decoded.get();
@@ -323,37 +323,36 @@ AppleATDecoder::GetInputAudioDescription
   rv = AudioFormatGetPropertyInfo(kAudioFormatProperty_FormatList,
                                   sizeof(formatInfo),
                                   &formatInfo,
                                   &formatListSize);
   if (rv || (formatListSize % sizeof(AudioFormatListItem))) {
     return NS_OK;
   }
   size_t listCount = formatListSize / sizeof(AudioFormatListItem);
-  nsAutoArrayPtr<AudioFormatListItem> formatList(
-    new AudioFormatListItem[listCount]);
+  auto formatList = MakeUnique<AudioFormatListItem[]>(listCount);
 
   rv = AudioFormatGetProperty(kAudioFormatProperty_FormatList,
                               sizeof(formatInfo),
                               &formatInfo,
                               &formatListSize,
-                              formatList);
+                              formatList.get());
   if (rv) {
     return NS_OK;
   }
   LOG("found %u available audio stream(s)",
       formatListSize / sizeof(AudioFormatListItem));
   // Get the index number of the first playable format.
   // This index number will be for the highest quality layer the platform
   // is capable of playing.
   UInt32 itemIndex;
   UInt32 indexSize = sizeof(itemIndex);
   rv = AudioFormatGetProperty(kAudioFormatProperty_FirstPlayableFormatFromList,
                               formatListSize,
-                              formatList,
+                              formatList.get(),
                               &indexSize,
                               &itemIndex);
   if (rv) {
     return NS_OK;
   }
 
   aDesc = formatList[itemIndex].mASBD;
 
@@ -433,19 +432,19 @@ static void
                                                  &size,
                                                  &writeable);
     if (rv) {
       LOG("Couldn't get property info for '%s' (%s)",
           FourCC2Str(aProperty), FourCC2Str(rv));
       decoder->mFileStreamError = true;
       return;
     }
-    nsAutoArrayPtr<uint8_t> data(new uint8_t[size]);
+    auto data = MakeUnique<uint8_t[]>(size);
     rv = AudioFileStreamGetProperty(aStream, aProperty,
-                                    &size, data);
+                                    &size, data.get());
     if (rv) {
       LOG("Couldn't get property '%s' (%s)",
           FourCC2Str(aProperty), FourCC2Str(rv));
       decoder->mFileStreamError = true;
       return;
     }
     decoder->mMagicCookie.AppendElements(data.get(), size);
   }
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.cpp
@@ -664,18 +664,17 @@ uint8_t *
 GonkVideoDecoderManager::GetColorConverterBuffer(int32_t aWidth, int32_t aHeight)
 {
   // Allocate a temporary YUV420Planer buffer.
   size_t yuv420p_y_size = aWidth * aHeight;
   size_t yuv420p_u_size = ((aWidth + 1) / 2) * ((aHeight + 1) / 2);
   size_t yuv420p_v_size = yuv420p_u_size;
   size_t yuv420p_size = yuv420p_y_size + yuv420p_u_size + yuv420p_v_size;
   if (mColorConverterBufferSize != yuv420p_size) {
-    mColorConverterBuffer = nullptr; // release the previous buffer first
-    mColorConverterBuffer = new uint8_t[yuv420p_size];
+    mColorConverterBuffer = MakeUnique<uint8_t[]>(yuv420p_size);
     mColorConverterBufferSize = yuv420p_size;
   }
   return mColorConverterBuffer.get();
 }
 
 /* static */
 void
 GonkVideoDecoderManager::RecycleCallback(TextureClient* aClient, void* aClosure)
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.h
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.h
@@ -10,16 +10,17 @@
 #include "nsRect.h"
 #include "GonkMediaDataDecoder.h"
 #include "mozilla/RefPtr.h"
 #include "I420ColorConverterHelper.h"
 #include "MediaCodecProxy.h"
 #include "GonkNativeWindow.h"
 #include "GonkNativeWindowClient.h"
 #include "mozilla/layers/FenceUtils.h"
+#include "mozilla/UniquePtr.h"
 #include <ui/Fence.h>
 
 using namespace android;
 
 namespace android {
 class MediaBuffer;
 struct MOZ_EXPORT AString;
 class GonkNativeWindow;
@@ -107,17 +108,17 @@ private:
   RefPtr<layers::TextureClientRecycleAllocator> mCopyAllocator;
 
   MediaInfo mInfo;
   MozPromiseRequestHolder<android::MediaCodecProxy::CodecPromise> mVideoCodecRequest;
   FrameInfo mFrameInfo;
 
   // color converter
   android::I420ColorConverterHelper mColorConverter;
-  nsAutoArrayPtr<uint8_t> mColorConverterBuffer;
+  UniquePtr<uint8_t[]> mColorConverterBuffer;
   size_t mColorConverterBufferSize;
 
   android::sp<android::GonkNativeWindow> mNativeWindow;
 #if ANDROID_VERSION >= 21
   android::sp<android::IGraphicBufferProducer> mGraphicBufferProducer;
 #endif
 
   enum {
--- a/dom/media/raw/RawReader.cpp
+++ b/dom/media/raw/RawReader.cpp
@@ -5,16 +5,17 @@
 
 #include "MediaDecoderStateMachine.h"
 #include "AbstractMediaDecoder.h"
 #include "RawReader.h"
 #include "RawDecoder.h"
 #include "VideoUtils.h"
 #include "nsISeekableStream.h"
 #include "gfx2DGlue.h"
+#include "mozilla/UniquePtr.h"
 
 using namespace mozilla;
 using namespace mozilla::media;
 
 RawReader::RawReader(AbstractMediaDecoder* aDecoder)
   : MediaDecoderReader(aDecoder),
     mCurrentFrame(0), mFrameSize(0), mResource(aDecoder->GetResource())
 {
@@ -133,52 +134,52 @@ bool RawReader::DecodeVideoFrame(bool &a
   AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder);
 
   if (!mFrameSize)
     return false; // Metadata read failed.  We should refuse to play.
 
   int64_t currentFrameTime = USECS_PER_S * mCurrentFrame / mFrameRate;
   uint32_t length = mFrameSize - sizeof(RawPacketHeader);
 
-  nsAutoArrayPtr<uint8_t> buffer(new uint8_t[length]);
+  auto buffer = MakeUnique<uint8_t[]>(length);
 
   // We're always decoding one frame when called
   while(true) {
     RawPacketHeader header;
 
     // Read in a packet header and validate
     if (!(ReadFromResource(reinterpret_cast<uint8_t*>(&header),
                            sizeof(header))) ||
         !(header.packetID == 0xFF && header.codecID == RAW_ID /* "YUV" */)) {
       return false;
     }
 
-    if (!ReadFromResource(buffer, length)) {
+    if (!ReadFromResource(buffer.get(), length)) {
       return false;
     }
 
     a.mParsed++;
 
     if (currentFrameTime >= aTimeThreshold)
       break;
 
     mCurrentFrame++;
     currentFrameTime += static_cast<double>(USECS_PER_S) / mFrameRate;
   }
 
   VideoData::YCbCrBuffer b;
-  b.mPlanes[0].mData = buffer;
+  b.mPlanes[0].mData = buffer.get();
   b.mPlanes[0].mStride = mMetadata.frameWidth * mMetadata.lumaChannelBpp / 8.0;
   b.mPlanes[0].mHeight = mMetadata.frameHeight;
   b.mPlanes[0].mWidth = mMetadata.frameWidth;
   b.mPlanes[0].mOffset = b.mPlanes[0].mSkip = 0;
 
   uint32_t cbcrStride = mMetadata.frameWidth * mMetadata.chromaChannelBpp / 8.0;
 
-  b.mPlanes[1].mData = buffer + mMetadata.frameHeight * b.mPlanes[0].mStride;
+  b.mPlanes[1].mData = buffer.get() + mMetadata.frameHeight * b.mPlanes[0].mStride;
   b.mPlanes[1].mStride = cbcrStride;
   b.mPlanes[1].mHeight = mMetadata.frameHeight / 2;
   b.mPlanes[1].mWidth = mMetadata.frameWidth / 2;
   b.mPlanes[1].mOffset = b.mPlanes[1].mSkip = 0;
 
   b.mPlanes[2].mData = b.mPlanes[1].mData + mMetadata.frameHeight * cbcrStride / 2;
   b.mPlanes[2].mStride = cbcrStride;
   b.mPlanes[2].mHeight = mMetadata.frameHeight / 2;
--- a/dom/media/systemservices/CamerasChild.cpp
+++ b/dom/media/systemservices/CamerasChild.cpp
@@ -123,27 +123,26 @@ public:
     // We will be returning the resulting pointer (synchronously) to our caller.
     mCamerasChild =
       static_cast<mozilla::camera::CamerasChild*>(existingBackgroundChild->SendPCamerasConstructor());
 
     return NS_OK;
   }
 
   CamerasChild* GetCamerasChild() {
-    MOZ_ASSERT(mCamerasChild);
     return mCamerasChild;
   }
 
 private:
   CamerasChild* mCamerasChild;
 };
 
 static CamerasChild*
-Cameras() {
-  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+GetCamerasChild() {
+  CamerasSingleton::Mutex().AssertCurrentThreadOwns();
   if (!CamerasSingleton::Child()) {
     MOZ_ASSERT(!NS_IsMainThread(), "Should not be on the main Thread");
     MOZ_ASSERT(!CamerasSingleton::Thread());
     LOG(("No sCameras, setting up IPC Thread"));
     nsresult rv = NS_NewNamedThread("Cameras IPC",
                                     getter_AddRefs(CamerasSingleton::Thread()));
     if (NS_FAILED(rv)) {
       LOG(("Error launching IPC Thread"));
@@ -157,17 +156,19 @@ Cameras() {
     // We block until the following happens in the Cameras IPC thread:
     // 1) Creation of PBackground finishes
     // 2) Creation of PCameras finishes by sending a message to the parent
     RefPtr<InitializeIPCThread> runnable = new InitializeIPCThread();
     RefPtr<SyncRunnable> sr = new SyncRunnable(runnable);
     sr->DispatchToThread(CamerasSingleton::Thread());
     CamerasSingleton::Child() = runnable->GetCamerasChild();
   }
-  MOZ_ASSERT(CamerasSingleton::Child());
+  if (!CamerasSingleton::Child()) {
+    LOG(("Failed to set up CamerasChild, are we in shutdown?"));
+  }
   return CamerasSingleton::Child();
 }
 
 bool
 CamerasChild::RecvReplyFailure(void)
 {
   LOG((__PRETTY_FUNCTION__));
   MonitorAutoLock monitor(mReplyMonitor);
@@ -185,17 +186,23 @@ CamerasChild::RecvReplySuccess(void)
   mReceivedReply = true;
   mReplySuccess = true;
   monitor.Notify();
   return true;
 }
 
 int NumberOfCapabilities(CaptureEngine aCapEngine, const char* deviceUniqueIdUTF8)
 {
-  return Cameras()->NumberOfCapabilities(aCapEngine, deviceUniqueIdUTF8);
+  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+  CamerasChild* child = GetCamerasChild();
+  if (child) {
+    return child->NumberOfCapabilities(aCapEngine, deviceUniqueIdUTF8);
+  } else {
+    return 0;
+  }
 }
 
 bool
 CamerasChild::RecvReplyNumberOfCapabilities(const int& numdev)
 {
   LOG((__PRETTY_FUNCTION__));
   MonitorAutoLock monitor(mReplyMonitor);
   mReceivedReply = true;
@@ -204,20 +211,18 @@ CamerasChild::RecvReplyNumberOfCapabilit
   monitor.Notify();
   return true;
 }
 
 bool
 CamerasChild::DispatchToParent(nsIRunnable* aRunnable,
                                MonitorAutoLock& aMonitor)
 {
-  {
-    OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
-    CamerasSingleton::Thread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
-  }
+  CamerasSingleton::Mutex().AssertCurrentThreadOwns();
+  CamerasSingleton::Thread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
   // We can't see if the send worked, so we need to be able to bail
   // out on shutdown (when it failed and we won't get a reply).
   if (!mIPCIsAlive) {
     return false;
   }
   // Guard against spurious wakeups.
   mReceivedReply = false;
   // Wait for a reply
@@ -255,17 +260,23 @@ CamerasChild::NumberOfCapabilities(Captu
     return 0;
   }
   LOG(("Capture capability count: %d", mReplyInteger));
   return mReplyInteger;
 }
 
 int NumberOfCaptureDevices(CaptureEngine aCapEngine)
 {
-  return Cameras()->NumberOfCaptureDevices(aCapEngine);
+  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+  CamerasChild* child = GetCamerasChild();
+  if (child) {
+    return child->NumberOfCaptureDevices(aCapEngine);
+  } else {
+    return 0;
+  }
 }
 
 int
 CamerasChild::NumberOfCaptureDevices(CaptureEngine aCapEngine)
 {
   MutexAutoLock requestLock(mRequestMutex);
   LOG((__PRETTY_FUNCTION__));
   nsCOMPtr<nsIRunnable> runnable =
@@ -295,20 +306,26 @@ CamerasChild::RecvReplyNumberOfCaptureDe
   monitor.Notify();
   return true;
 }
 
 int GetCaptureCapability(CaptureEngine aCapEngine, const char* unique_idUTF8,
                          const unsigned int capability_number,
                          webrtc::CaptureCapability& capability)
 {
-  return Cameras()->GetCaptureCapability(aCapEngine,
-                                         unique_idUTF8,
-                                         capability_number,
-                                         capability);
+  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+  CamerasChild* child = GetCamerasChild();
+  if (child) {
+    return child->GetCaptureCapability(aCapEngine,
+                                       unique_idUTF8,
+                                       capability_number,
+                                       capability);
+  } else {
+    return -1;
+  }
 }
 
 int
 CamerasChild::GetCaptureCapability(CaptureEngine aCapEngine,
                                    const char* unique_idUTF8,
                                    const unsigned int capability_number,
                                    webrtc::CaptureCapability& capability)
 {
@@ -350,22 +367,28 @@ CamerasChild::RecvReplyGetCaptureCapabil
 
 
 int GetCaptureDevice(CaptureEngine aCapEngine,
                      unsigned int list_number, char* device_nameUTF8,
                      const unsigned int device_nameUTF8Length,
                      char* unique_idUTF8,
                      const unsigned int unique_idUTF8Length)
 {
-  return Cameras()->GetCaptureDevice(aCapEngine,
-                                     list_number,
-                                     device_nameUTF8,
-                                     device_nameUTF8Length,
-                                     unique_idUTF8,
-                                     unique_idUTF8Length);
+  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+  CamerasChild* child = GetCamerasChild();
+  if (child) {
+    return child->GetCaptureDevice(aCapEngine,
+                                   list_number,
+                                   device_nameUTF8,
+                                   device_nameUTF8Length,
+                                   unique_idUTF8,
+                                   unique_idUTF8Length);
+  } else {
+    return -1;
+  }
 }
 
 int
 CamerasChild::GetCaptureDevice(CaptureEngine aCapEngine,
                                unsigned int list_number, char* device_nameUTF8,
                                const unsigned int device_nameUTF8Length,
                                char* unique_idUTF8,
                                const unsigned int unique_idUTF8Length)
@@ -404,20 +427,26 @@ CamerasChild::RecvReplyGetCaptureDevice(
   return true;
 }
 
 int AllocateCaptureDevice(CaptureEngine aCapEngine,
                           const char* unique_idUTF8,
                           const unsigned int unique_idUTF8Length,
                           int& capture_id)
 {
-  return Cameras()->AllocateCaptureDevice(aCapEngine,
-                                          unique_idUTF8,
-                                          unique_idUTF8Length,
-                                          capture_id);
+  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+  CamerasChild* child = GetCamerasChild();
+  if (child) {
+    return child->AllocateCaptureDevice(aCapEngine,
+                                        unique_idUTF8,
+                                        unique_idUTF8Length,
+                                        capture_id);
+  } else {
+    return -1;
+  }
 }
 
 int
 CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine,
                                     const char* unique_idUTF8,
                                     const unsigned int unique_idUTF8Length,
                                     int& capture_id)
 {
@@ -451,17 +480,23 @@ CamerasChild::RecvReplyAllocateCaptureDe
   mReplySuccess = true;
   mReplyInteger = numdev;
   monitor.Notify();
   return true;
 }
 
 int ReleaseCaptureDevice(CaptureEngine aCapEngine, const int capture_id)
 {
-  return Cameras()->ReleaseCaptureDevice(aCapEngine, capture_id);
+  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+  CamerasChild* child = GetCamerasChild();
+  if (child) {
+    return child->ReleaseCaptureDevice(aCapEngine, capture_id);
+  } else {
+    return -1;
+  }
 }
 
 int
 CamerasChild::ReleaseCaptureDevice(CaptureEngine aCapEngine,
                                    const int capture_id)
 {
   MutexAutoLock requestLock(mRequestMutex);
   LOG((__PRETTY_FUNCTION__));
@@ -504,20 +539,26 @@ CamerasChild::RemoveCallback(const Captu
   }
 }
 
 int StartCapture(CaptureEngine aCapEngine,
                  const int capture_id,
                  webrtc::CaptureCapability& webrtcCaps,
                  webrtc::ExternalRenderer* cb)
 {
-  return Cameras()->StartCapture(aCapEngine,
-                                 capture_id,
-                                 webrtcCaps,
-                                 cb);
+  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+  CamerasChild* child = GetCamerasChild();
+  if (child) {
+    return child->StartCapture(aCapEngine,
+                               capture_id,
+                               webrtcCaps,
+                               cb);
+  } else {
+    return -1;
+  }
 }
 
 int
 CamerasChild::StartCapture(CaptureEngine aCapEngine,
                            const int capture_id,
                            webrtc::CaptureCapability& webrtcCaps,
                            webrtc::ExternalRenderer* cb)
 {
@@ -542,17 +583,23 @@ CamerasChild::StartCapture(CaptureEngine
   if (!DispatchToParent(runnable, monitor)) {
     return -1;
   }
   return 0;
 }
 
 int StopCapture(CaptureEngine aCapEngine, const int capture_id)
 {
-  return Cameras()->StopCapture(aCapEngine, capture_id);
+  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+  CamerasChild* child = GetCamerasChild();
+  if (child) {
+    return child->StopCapture(aCapEngine, capture_id);
+  } else {
+    return -1;
+  }
 }
 
 int
 CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id)
 {
   MutexAutoLock requestLock(mRequestMutex);
   LOG((__PRETTY_FUNCTION__));
   nsCOMPtr<nsIRunnable> runnable =
@@ -568,26 +615,24 @@ CamerasChild::StopCapture(CaptureEngine 
   }
   RemoveCallback(aCapEngine, capture_id);
   return 0;
 }
 
 void
 Shutdown(void)
 {
-  {
-    OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
-    if (!CamerasSingleton::Child()) {
-      // We don't want to cause everything to get fired up if we're
-      // really already shut down.
-      LOG(("Shutdown when already shut down"));
-      return;
-    }
+  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+  if (!CamerasSingleton::Child()) {
+    // We don't want to cause everything to get fired up if we're
+    // really already shut down.
+    LOG(("Shutdown when already shut down"));
+    return;
   }
-  Cameras()->Shutdown();
+  GetCamerasChild()->Shutdown();
 }
 
 class ShutdownRunnable : public nsRunnable {
 public:
   ShutdownRunnable(RefPtr<nsRunnable> aReplyEvent,
                    nsIThread* aReplyThread)
     : mReplyEvent(aReplyEvent), mReplyThread(aReplyThread) {};
 
@@ -610,17 +655,16 @@ void
 CamerasChild::Shutdown()
 {
   {
     MonitorAutoLock monitor(mReplyMonitor);
     mIPCIsAlive = false;
     monitor.NotifyAll();
   }
 
-  OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
   if (CamerasSingleton::Thread()) {
     LOG(("Dispatching actor deletion"));
     // Delete the parent actor.
     RefPtr<nsRunnable> deleteRunnable =
       // CamerasChild (this) will remain alive and is only deleted by the
       // IPC layer when SendAllDone returns.
       media::NewRunnableFrom([this]() -> nsresult {
         Unused << this->SendAllDone();
@@ -703,17 +747,20 @@ CamerasChild::CamerasChild()
 
   MOZ_COUNT_CTOR(CamerasChild);
 }
 
 CamerasChild::~CamerasChild()
 {
   LOG(("~CamerasChild: %p", this));
 
-  Shutdown();
+  {
+    OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex());
+    Shutdown();
+  }
 
   MOZ_COUNT_DTOR(CamerasChild);
 }
 
 webrtc::ExternalRenderer* CamerasChild::Callback(CaptureEngine aCapEngine,
                                                  int capture_id)
 {
   for (unsigned int i = 0; i < mCallbacks.Length(); i++) {
--- a/dom/media/systemservices/CamerasParent.cpp
+++ b/dom/media/systemservices/CamerasParent.cpp
@@ -181,16 +181,17 @@ CamerasParent::DispatchToVideoCaptureThr
   mVideoCaptureThread->message_loop()->PostTask(FROM_HERE,
                                                 new RunnableTask(event));
   return NS_OK;
 }
 
 void
 CamerasParent::StopVideoCapture()
 {
+  LOG((__PRETTY_FUNCTION__));
   // We are called from the main thread (xpcom-shutdown) or
   // from PBackground (when the Actor shuts down).
   // Shut down the WebRTC stack (on the capture thread)
   RefPtr<CamerasParent> self(this);
   RefPtr<nsRunnable> webrtc_runnable =
     media::NewRunnableFrom([self]() -> nsresult {
       MonitorAutoLock lock(self->mThreadMonitor);
       self->CloseEngines();
@@ -402,16 +403,17 @@ CamerasParent::SetupEngine(CaptureEngine
   }
 
   return true;
 }
 
 void
 CamerasParent::CloseEngines()
 {
+  LOG((__PRETTY_FUNCTION__));
   if (!mWebRTCAlive) {
     return;
   }
   MOZ_ASSERT(mVideoCaptureThread->thread_id() == PlatformThread::CurrentId());
 
   // Stop the callers
   while (mCallbacks.Length()) {
     auto capEngine = mCallbacks[0]->mCapEngine;
@@ -695,20 +697,22 @@ CamerasParent::RecvReleaseCaptureDevice(
 
   RefPtr<CamerasParent> self(this);
   RefPtr<nsRunnable> webrtc_runnable =
     media::NewRunnableFrom([self, aCapEngine, numdev]() -> nsresult {
       int error = self->ReleaseCaptureDevice(aCapEngine, numdev);
       RefPtr<nsIRunnable> ipc_runnable =
         media::NewRunnableFrom([self, error, numdev]() -> nsresult {
           if (self->IsShuttingDown()) {
+            LOG(("In Shutdown, not Releasing"));
             return NS_ERROR_FAILURE;
           }
           if (error) {
             Unused << self->SendReplyFailure();
+            LOG(("Failed to free device nr %d", numdev));
             return NS_ERROR_FAILURE;
           } else {
             Unused << self->SendReplySuccess();
             LOG(("Freed device nr %d", numdev));
             return NS_OK;
           }
         });
       self->mPBackgroundThread->Dispatch(ipc_runnable, NS_DISPATCH_NORMAL);
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -614,17 +614,17 @@ skip-if = (toolkit == 'android' && proce
 [test_defaultMuted.html]
 [test_delay_load.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
 [test_dormant_playback.html]
 skip-if = (os == 'win' && os_version == '5.1') || (os != 'win' && toolkit != 'gonk')
 [test_eme_session_callable_value.html]
 [test_eme_canvas_blocked.html]
 skip-if = toolkit == 'android' # bug 1149374
-[test_eme_key_ids_initdata.html]
+[test_eme_initDataTypes.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_non_mse_fails.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_request_notifications.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_persistent_sessions.html]
 skip-if = toolkit == 'android' # bug 1149374
 [test_eme_playback.html]
rename from dom/media/test/test_eme_key_ids_initdata.html
rename to dom/media/test/test_eme_initDataTypes.html
--- a/dom/media/test/test_eme_key_ids_initdata.html
+++ b/dom/media/test/test_eme_initDataTypes.html
@@ -9,92 +9,125 @@
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var tests = [
   {
     name: "One keyId",
+    initDataType: 'keyids',
     initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"]}',
     expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"],"type":"temporary"}',
     sessionType: 'temporary',
     expectPass: true,
   },
   {
     name: "Two keyIds",
+    initDataType: 'keyids',
     initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A", "0DdtU9od-Bh5L3xbv0Xf_A"]}',
     expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A","0DdtU9od-Bh5L3xbv0Xf_A"],"type":"temporary"}',
     sessionType: 'temporary',
     expectPass: true,
   },
   {
     name: "Two keyIds, temporary session",
+    initDataType: 'keyids',
     initData: '{"type":"temporary", "kids":["LwVHf8JLtPrv2GUXFW2v_A", "0DdtU9od-Bh5L3xbv0Xf_A"]}',
     expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A","0DdtU9od-Bh5L3xbv0Xf_A"],"type":"temporary"}',
     sessionType: 'temporary',
     expectPass: true,
   },
   {
     name: "Two keyIds, persistent session, type before kids",
+    initDataType: 'keyids',
     initData: '{"type":"persistent", "kids":["LwVHf8JLtPrv2GUXFW2v_A", "0DdtU9od-Bh5L3xbv0Xf_A"]}',
     expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A","0DdtU9od-Bh5L3xbv0Xf_A"],"type":"persistent"}',
     sessionType: 'persistent',
     expectPass: true,
   },
   {
     name: "Invalid keyId",
+    initDataType: 'keyids',
     initData: '{"kids":["0"]}',
     sessionType: 'temporary',
     expectPass: false,
   },
   {
     name: "Empty keyId",
+    initDataType: 'keyids',
     initData: '{"kids":[""]}',
     sessionType: 'temporary',
     expectPass: false,
   },
   {
     name: "SessionType in license doesn't match MediaKeySession's sessionType",
+    initDataType: 'keyids',
     initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"]}',
     sessionType: 'persistent',
     expectPass: false,
   },
   {
     name: "One valid and one invalid kid",
+    initDataType: 'keyids',
     initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A", "invalid"]}',
     expectedRequest: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"],"type":"temporary"}',
     sessionType: 'temporary',
     expectPass: true,
   },
   {
     name: "Invalid initData",
+    initDataType: 'keyids',
     initData: 'invalid initData',
     sessionType: 'temporary',
     expectPass: false,
   },
+  {
+    name: "'webm' initDataType",
+    initDataType: 'webm',
+    initData: 'YAYeAX5Hfod+V9ANHtANHg==',
+    expectedRequest: '{"kids":["YAYeAX5Hfod-V9ANHtANHg"],"type":"temporary"}',
+    sessionType: 'temporary',
+    expectPass: true,
+  },
+  {
+    name: "'webm' initDataType with non 16 byte keyid",
+    initDataType: 'webm',
+    initData: 'YAYeAX5Hfod',
+    sessionType: 'temporary',
+    expectPass: false,
+  },
 ];
 
+function PrepareInitData(initDataType, initData)
+{
+  if (initDataType == "keyids") {
+    return new TextEncoder().encode(initData);
+  } else if (initDataType == "webm") {
+    return StringToArrayBuffer(atob(initData));
+  }
+}
+
 function Test(test) {
   return new Promise(function(resolve, reject) {
     navigator.requestMediaKeySystemAccess('org.w3.clearkey', [{initDataTypes: ['keyids']}]).then(
       (access) => access.createMediaKeys()
       ).then(
         (mediaKeys) => {
           var session = mediaKeys.createSession(test.sessionType);
-          var initData = new TextEncoder().encode(test.initData);
           session.addEventListener("message", function(event) {
             is(event.messageType, "license-request", "'" + test.name + "' MediaKeyMessage type should be license-request.");
             var text = new TextDecoder().decode(event.message);
             is(text, test.expectedRequest, "'" + test.name + "' got expected response.");
             is(text == test.expectedRequest, test.expectPass,
                "'" + test.name + "' expected to " + (test.expectPass ? "pass" : "fail"));
             resolve();
           });
-          return session.generateRequest('keyids', initData);
+          var initData = PrepareInitData(test.initDataType, test.initData);
+          return session.generateRequest(test.initDataType, initData);
         }
       ).catch((x) => {
         ok(!test.expectPass, "'" + test.name + "' expected to fail.");
         resolve();
       });
   });
 }
 
--- a/dom/media/wave/WaveReader.cpp
+++ b/dom/media/wave/WaveReader.cpp
@@ -9,16 +9,17 @@
 #include "MediaDecoderStateMachine.h"
 #include "VideoUtils.h"
 #include "nsISeekableStream.h"
 
 #include <stdint.h>
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/CheckedInt.h"
 #include "mozilla/Endian.h"
+#include "mozilla/UniquePtr.h"
 #include <algorithm>
 
 using namespace mozilla::media;
 
 namespace mozilla {
 
 // Un-comment to enable logging of seek bisections.
 //#define SEEK_LOGGING
@@ -186,19 +187,19 @@ bool WaveReader::DecodeAudioData()
   static_assert(uint64_t(BLOCK_SIZE) < UINT_MAX /
                 sizeof(AudioDataValue) / MAX_CHANNELS,
                 "bufferSize calculation could overflow.");
   const size_t bufferSize = static_cast<size_t>(frames * mChannels);
   auto sampleBuffer = MakeUnique<AudioDataValue[]>(bufferSize);
 
   static_assert(uint64_t(BLOCK_SIZE) < UINT_MAX / sizeof(char),
                 "BLOCK_SIZE too large for enumerator.");
-  nsAutoArrayPtr<char> dataBuffer(new char[static_cast<size_t>(readSize)]);
+  auto dataBuffer = MakeUnique<char[]>(static_cast<size_t>(readSize));
 
-  if (!ReadAll(dataBuffer, readSize)) {
+  if (!ReadAll(dataBuffer.get(), readSize)) {
     return false;
   }
 
   // convert data to samples
   const char* d = dataBuffer.get();
   AudioDataValue* s = sampleBuffer.get();
   for (int i = 0; i < frames; ++i) {
     for (unsigned int j = 0; j < mChannels; ++j) {
@@ -401,17 +402,17 @@ WaveReader::LoadFormatChunk(uint32_t aCh
       NS_WARNING("Invalid extended format chunk size");
       return false;
     }
     extra += extra % 2;
 
     if (extra > 0) {
       static_assert(UINT16_MAX + (UINT16_MAX % 2) < UINT_MAX / sizeof(char),
                     "chunkExtension array too large for iterator.");
-      nsAutoArrayPtr<char> chunkExtension(new char[extra]);
+      auto chunkExtension = MakeUnique<char[]>(extra);
       if (!ReadAll(chunkExtension.get(), extra)) {
         return false;
       }
     }
   }
 
   // RIFF chunks are always word (two byte) aligned.
   MOZ_ASSERT(mResource.Tell() % 2 == 0,
@@ -523,17 +524,17 @@ WaveReader::LoadListChunk(uint32_t aChun
   static const unsigned int MAX_CHUNK_SIZE = 1 << 16;
   static_assert(uint64_t(MAX_CHUNK_SIZE) < UINT_MAX / sizeof(char),
                 "MAX_CHUNK_SIZE too large for enumerator.");
 
   if (aChunkSize > MAX_CHUNK_SIZE || aChunkSize < 4) {
     return false;
   }
 
-  nsAutoArrayPtr<char> chunk(new char[aChunkSize]);
+  auto chunk = MakeUnique<char[]>(aChunkSize);
   if (!ReadAll(chunk.get(), aChunkSize)) {
     return false;
   }
 
   static const uint32_t INFO_LIST_MAGIC = 0x494e464f;
   const char* p = chunk.get();
   if (ReadUint32BE(&p) != INFO_LIST_MAGIC) {
     return false;
@@ -647,17 +648,17 @@ WaveReader::LoadAllChunks(nsAutoPtr<dom:
 
     if (!forward.isValid() || forward.value() < 0) {
       return false;
     }
 
     static const int64_t MAX_CHUNK_SIZE = 1 << 16;
     static_assert(uint64_t(MAX_CHUNK_SIZE) < UINT_MAX / sizeof(char),
                   "MAX_CHUNK_SIZE too large for enumerator.");
-    nsAutoArrayPtr<char> chunk(new char[MAX_CHUNK_SIZE]);
+    auto chunk = MakeUnique<char[]>(MAX_CHUNK_SIZE);
     while (forward.value() > 0) {
       int64_t size = std::min(forward.value(), MAX_CHUNK_SIZE);
       if (!ReadAll(chunk.get(), size)) {
         return false;
       }
       forward -= size;
     }
   }
--- a/dom/media/webaudio/BiquadFilterNode.cpp
+++ b/dom/media/webaudio/BiquadFilterNode.cpp
@@ -7,16 +7,17 @@
 #include "BiquadFilterNode.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "AudioDestinationNode.h"
 #include "PlayingRefChangeHandler.h"
 #include "WebAudioUtils.h"
 #include "blink/Biquad.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/UniquePtr.h"
 #include "AudioParamTimeline.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_INHERITED(BiquadFilterNode, AudioNode,
                                    mFrequency, mDetune, mQ, mGain)
 
@@ -311,17 +312,17 @@ BiquadFilterNode::GetFrequencyResponse(c
   aPhaseResponse.ComputeLengthAndData();
 
   uint32_t length = std::min(std::min(aFrequencyHz.Length(), aMagResponse.Length()),
                              aPhaseResponse.Length());
   if (!length) {
     return;
   }
 
-  nsAutoArrayPtr<float> frequencies(new float[length]);
+  auto frequencies = MakeUnique<float[]>(length);
   float* frequencyHz = aFrequencyHz.Data();
   const double nyquist = Context()->SampleRate() * 0.5;
 
   // Normalize the frequencies
   for (uint32_t i = 0; i < length; ++i) {
     if (frequencyHz[i] >= 0 && frequencyHz[i] <= nyquist) {
         frequencies[i] = static_cast<float>(frequencyHz[i] / nyquist);
     } else {
@@ -333,13 +334,13 @@ BiquadFilterNode::GetFrequencyResponse(c
 
   double freq = mFrequency->GetValueAtTime(currentTime);
   double q = mQ->GetValueAtTime(currentTime);
   double gain = mGain->GetValueAtTime(currentTime);
   double detune = mDetune->GetValueAtTime(currentTime);
 
   WebCore::Biquad biquad;
   SetParamsOnBiquad(biquad, Context()->SampleRate(), mType, freq, q, gain, detune);
-  biquad.getFrequencyResponse(int(length), frequencies, aMagResponse.Data(), aPhaseResponse.Data());
+  biquad.getFrequencyResponse(int(length), frequencies.get(), aMagResponse.Data(), aPhaseResponse.Data());
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/webaudio/blink/DynamicsCompressor.cpp
+++ b/dom/media/webaudio/blink/DynamicsCompressor.cpp
@@ -64,18 +64,18 @@ size_t DynamicsCompressor::sizeOfIncludi
 
     amount += m_postFilterPacks.ShallowSizeOfExcludingThis(aMallocSizeOf);
     for (size_t i = 0; i < m_postFilterPacks.Length(); i++) {
         if (m_postFilterPacks[i]) {
             amount += m_postFilterPacks[i]->sizeOfIncludingThis(aMallocSizeOf);
         }
     }
 
-    amount += m_sourceChannels.SizeOfExcludingThis(aMallocSizeOf);
-    amount += m_destinationChannels.SizeOfExcludingThis(aMallocSizeOf);
+    amount += aMallocSizeOf(m_sourceChannels.get());
+    amount += aMallocSizeOf(m_destinationChannels.get());
     amount += m_compressor.sizeOfExcludingThis(aMallocSizeOf);
     return amount;
 }
 
 void DynamicsCompressor::setParameterValue(unsigned parameterID, float value)
 {
     MOZ_ASSERT(parameterID < ParamLast);
     if (parameterID < ParamLast)
@@ -303,16 +303,16 @@ void DynamicsCompressor::setNumberOfChan
 
     m_preFilterPacks.Clear();
     m_postFilterPacks.Clear();
     for (unsigned i = 0; i < numberOfChannels; ++i) {
         m_preFilterPacks.AppendElement(new ZeroPoleFilterPack4());
         m_postFilterPacks.AppendElement(new ZeroPoleFilterPack4());
     }
 
-    m_sourceChannels = new const float* [numberOfChannels];
-    m_destinationChannels = new float* [numberOfChannels];
+    m_sourceChannels = mozilla::MakeUnique<const float* []>(numberOfChannels);
+    m_destinationChannels = mozilla::MakeUnique<float* []>(numberOfChannels);
 
     m_compressor.setNumberOfChannels(numberOfChannels);
     m_numberOfChannels = numberOfChannels;
 }
 
 } // namespace WebCore
--- a/dom/media/webaudio/blink/DynamicsCompressor.h
+++ b/dom/media/webaudio/blink/DynamicsCompressor.h
@@ -30,16 +30,17 @@
 #define DynamicsCompressor_h
 
 #include "DynamicsCompressorKernel.h"
 #include "ZeroPole.h"
 
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
 
 namespace mozilla {
 class AudioBlock;
 } // namespace mozilla
 
 namespace WebCore {
 
 using mozilla::AudioBlock;
@@ -110,18 +111,18 @@ protected:
             return aMallocSizeOf(this);
         }
     } ZeroPoleFilterPack4;
 
     // Per-channel emphasis filters.
     nsTArray<nsAutoPtr<ZeroPoleFilterPack4> > m_preFilterPacks;
     nsTArray<nsAutoPtr<ZeroPoleFilterPack4> > m_postFilterPacks;
 
-    nsAutoArrayPtr<const float*> m_sourceChannels;
-    nsAutoArrayPtr<float*> m_destinationChannels;
+    mozilla::UniquePtr<const float*[]> m_sourceChannels;
+    mozilla::UniquePtr<float*[]> m_destinationChannels;
 
     void setEmphasisStageParameters(unsigned stageIndex, float gain, float normalizedFrequency /* 0 -> 1 */);
     void setEmphasisParameters(float gain, float anchorFreq, float filterStageRatio);
 
     // The core compressor.
     DynamicsCompressorKernel m_compressor;
 };
 
--- a/dom/media/webaudio/blink/DynamicsCompressorKernel.cpp
+++ b/dom/media/webaudio/blink/DynamicsCompressorKernel.cpp
@@ -35,16 +35,17 @@
 #include "mozilla/Constants.h"
 #include "WebAudioUtils.h"
 
 using namespace std;
 
 using namespace mozilla::dom; // for WebAudioUtils
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
+using mozilla::MakeUnique;
 
 namespace WebCore {
 
 
 // Metering hits peaks instantly, but releases this fast (in seconds).
 const float meteringReleaseTimeConstant = 0.325f;
 
 const float uninitializedValue = -1;
@@ -73,43 +74,43 @@ DynamicsCompressorKernel::DynamicsCompre
         static_cast<float>(WebAudioUtils::DiscreteTimeConstantForSampleRate(meteringReleaseTimeConstant, sampleRate));
 }
 
 size_t DynamicsCompressorKernel::sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
     size_t amount = 0;
     amount += m_preDelayBuffers.ShallowSizeOfExcludingThis(aMallocSizeOf);
     for (size_t i = 0; i < m_preDelayBuffers.Length(); i++) {
-        amount += m_preDelayBuffers[i].SizeOfExcludingThis(aMallocSizeOf);
+        amount += aMallocSizeOf(m_preDelayBuffers[i].get());
     }
 
     return amount;
 }
 
 void DynamicsCompressorKernel::setNumberOfChannels(unsigned numberOfChannels)
 {
     if (m_preDelayBuffers.Length() == numberOfChannels)
         return;
 
     m_preDelayBuffers.Clear();
     for (unsigned i = 0; i < numberOfChannels; ++i)
-        m_preDelayBuffers.AppendElement(new float[MaxPreDelayFrames]);
+      m_preDelayBuffers.AppendElement(MakeUnique<float[]>(MaxPreDelayFrames));
 }
 
 void DynamicsCompressorKernel::setPreDelayTime(float preDelayTime)
 {
     // Re-configure look-ahead section pre-delay if delay time has changed.
     unsigned preDelayFrames = preDelayTime * sampleRate();
     if (preDelayFrames > MaxPreDelayFrames - 1)
         preDelayFrames = MaxPreDelayFrames - 1;
 
     if (m_lastPreDelayFrames != preDelayFrames) {
         m_lastPreDelayFrames = preDelayFrames;
         for (unsigned i = 0; i < m_preDelayBuffers.Length(); ++i)
-            memset(m_preDelayBuffers[i], 0, sizeof(float) * MaxPreDelayFrames);
+	  memset(m_preDelayBuffers[i].get(), 0, sizeof(float) * MaxPreDelayFrames);
 
         m_preDelayReadIndex = 0;
         m_preDelayWriteIndex = preDelayFrames;
     }
 }
 
 // Exponential curve for the knee.
 // It is 1st derivative matched at m_linearThreshold and asymptotically approaches the value m_linearThreshold + 1 / k.
@@ -382,17 +383,17 @@ void DynamicsCompressorKernel::process(f
             float compressorGain = m_compressorGain;
 
             int loopFrames = nDivisionFrames;
             while (loopFrames--) {
                 float compressorInput = 0;
 
                 // Predelay signal, computing compression amount from un-delayed version.
                 for (unsigned i = 0; i < numberOfChannels; ++i) {
-                    float* delayBuffer = m_preDelayBuffers[i];
+                    float* delayBuffer = m_preDelayBuffers[i].get();
                     float undelayedSource = sourceChannels[i][frameIndex];
                     delayBuffer[preDelayWriteIndex] = undelayedSource;
 
                     float absUndelayedSource = undelayedSource > 0 ? undelayedSource : -undelayedSource;
                     if (compressorInput < absUndelayedSource)
                         compressorInput = absUndelayedSource;
                 }
 
@@ -448,17 +449,17 @@ void DynamicsCompressorKernel::process(f
                 float dbRealGain = 20 * log10(postWarpCompressorGain);
                 if (dbRealGain < m_meteringGain)
                     m_meteringGain = dbRealGain;
                 else
                     m_meteringGain += (dbRealGain - m_meteringGain) * m_meteringReleaseK;
 
                 // Apply final gain.
                 for (unsigned i = 0; i < numberOfChannels; ++i) {
-                    float* delayBuffer = m_preDelayBuffers[i];
+                    float* delayBuffer = m_preDelayBuffers[i].get();
                     destinationChannels[i][frameIndex] = delayBuffer[preDelayReadIndex] * totalGain;
                 }
 
                 frameIndex++;
                 preDelayReadIndex = (preDelayReadIndex + 1) & MaxPreDelayFramesMask;
                 preDelayWriteIndex = (preDelayWriteIndex + 1) & MaxPreDelayFramesMask;
             }
 
@@ -474,17 +475,17 @@ void DynamicsCompressorKernel::process(f
 void DynamicsCompressorKernel::reset()
 {
     m_detectorAverage = 0;
     m_compressorGain = 1;
     m_meteringGain = 1;
 
     // Predelay section.
     for (unsigned i = 0; i < m_preDelayBuffers.Length(); ++i)
-        memset(m_preDelayBuffers[i], 0, sizeof(float) * MaxPreDelayFrames);
+        memset(m_preDelayBuffers[i].get(), 0, sizeof(float) * MaxPreDelayFrames);
 
     m_preDelayReadIndex = 0;
     m_preDelayWriteIndex = DefaultPreDelayFrames;
 
     m_maxAttackCompressionDiffDb = -1; // uninitialized state
 }
 
 } // namespace WebCore
--- a/dom/media/webaudio/blink/DynamicsCompressorKernel.h
+++ b/dom/media/webaudio/blink/DynamicsCompressorKernel.h
@@ -27,16 +27,17 @@
  */
 
 #ifndef DynamicsCompressorKernel_h
 #define DynamicsCompressorKernel_h
 
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "mozilla/MemoryReporting.h"
+#include "mozilla/UniquePtr.h"
 
 namespace WebCore {
 
 class DynamicsCompressorKernel {
 public:
     DynamicsCompressorKernel(float sampleRate, unsigned numberOfChannels);
 
     void setNumberOfChannels(unsigned);
@@ -84,17 +85,17 @@ protected:
 
     // Lookahead section.
     enum { MaxPreDelayFrames = 1024 };
     enum { MaxPreDelayFramesMask = MaxPreDelayFrames - 1 };
     enum { DefaultPreDelayFrames = 256 }; // setPreDelayTime() will override this initial value
     unsigned m_lastPreDelayFrames;
     void setPreDelayTime(float);
 
-    nsTArray<nsAutoArrayPtr<float> > m_preDelayBuffers;
+    nsTArray<mozilla::UniquePtr<float[]>> m_preDelayBuffers;
     int m_preDelayReadIndex;
     int m_preDelayWriteIndex;
 
     float m_maxAttackCompressionDiffDb;
 
     // Static compression curve.
     float kneeCurve(float x, float k);
     float saturate(float x, float k);
--- a/dom/media/webaudio/blink/PeriodicWave.cpp
+++ b/dom/media/webaudio/blink/PeriodicWave.cpp
@@ -24,16 +24,17 @@
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
 #include "PeriodicWave.h"
 #include <algorithm>
 #include <cmath>
+#include <limits>
 #include "mozilla/FFTBlock.h"
 
 const unsigned MinPeriodicWaveSize = 4096; // This must be a power of two.
 const unsigned MaxPeriodicWaveSize = 8192; // This must be a power of two.
 const float CentsPerRange = 1200 / 3; // 1/3 Octave.
 
 using namespace mozilla;
 using mozilla::dom::OscillatorType;
@@ -46,17 +47,29 @@ PeriodicWave::create(float sampleRate,
                      const float* imag,
                      size_t numberOfComponents)
 {
     bool isGood = real && imag && numberOfComponents > 0;
     MOZ_ASSERT(isGood);
     if (isGood) {
         RefPtr<PeriodicWave> periodicWave =
             new PeriodicWave(sampleRate, numberOfComponents);
-        periodicWave->createBandLimitedTables(real, imag, numberOfComponents);
+
+        // Limit the number of components used to those for frequencies below the
+        // Nyquist of the fixed length inverse FFT.
+        size_t halfSize = periodicWave->m_periodicWaveSize / 2;
+        numberOfComponents = std::min(numberOfComponents, halfSize);
+        periodicWave->m_numberOfComponents = numberOfComponents;
+        periodicWave->m_realComponents = new AudioFloatArray(numberOfComponents);
+        periodicWave->m_imagComponents = new AudioFloatArray(numberOfComponents);
+        memcpy(periodicWave->m_realComponents->Elements(), real,
+               numberOfComponents * sizeof(float));
+        memcpy(periodicWave->m_imagComponents->Elements(), imag,
+               numberOfComponents * sizeof(float));
+
         return periodicWave.forget();
     }
     return nullptr;
 }
 
 already_AddRefed<PeriodicWave>
 PeriodicWave::createSine(float sampleRate)
 {
@@ -91,27 +104,29 @@ PeriodicWave::createTriangle(float sampl
         new PeriodicWave(sampleRate, MinPeriodicWaveSize);
     periodicWave->generateBasicWaveform(OscillatorType::Triangle);
     return periodicWave.forget();
 }
 
 PeriodicWave::PeriodicWave(float sampleRate, size_t numberOfComponents)
     : m_sampleRate(sampleRate)
     , m_centsPerRange(CentsPerRange)
+    , m_lowestRequestedFundamentalFrequency(std::numeric_limits<float>::max())
 {
     float nyquist = 0.5 * m_sampleRate;
 
     if (numberOfComponents <= MinPeriodicWaveSize) {
         m_periodicWaveSize = MinPeriodicWaveSize;
     } else {
         unsigned npow2 = powf(2.0f, floorf(logf(numberOfComponents - 1.0)/logf(2.0f) + 1.0f));
         m_periodicWaveSize = std::min(MaxPeriodicWaveSize, npow2);
     }
 
     m_numberOfRanges = (unsigned)(3.0f*logf(m_periodicWaveSize)/logf(2.0f));
+    m_bandLimitedTables.SetLength(m_numberOfRanges);
     m_lowestFundamentalFrequency = nyquist / maxNumberOfPartials();
     m_rateScale = m_periodicWaveSize / m_sampleRate;
 }
 
 size_t PeriodicWave::sizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
 {
     size_t amount = aMallocSizeOf(this);
 
@@ -122,20 +137,32 @@ size_t PeriodicWave::sizeOfIncludingThis
         }
     }
 
     return amount;
 }
 
 void PeriodicWave::waveDataForFundamentalFrequency(float fundamentalFrequency, float* &lowerWaveData, float* &higherWaveData, float& tableInterpolationFactor)
 {
+
     // Negative frequencies are allowed, in which case we alias
     // to the positive frequency.
     fundamentalFrequency = fabsf(fundamentalFrequency);
 
+    if (fundamentalFrequency < m_lowestRequestedFundamentalFrequency) {
+        for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
+            m_bandLimitedTables[rangeIndex] = 0;
+        }
+
+        // We need to create the first table to determine the normalization
+        // constant.
+        createBandLimitedTables(fundamentalFrequency, 0);
+        m_lowestRequestedFundamentalFrequency = fundamentalFrequency;
+    }
+
     // Calculate the pitch range.
     float ratio = fundamentalFrequency > 0 ? fundamentalFrequency / m_lowestFundamentalFrequency : 0.5;
     float centsAboveLowestFrequency = logf(ratio)/logf(2.0f) * 1200;
 
     // Add one to round-up to the next range just in time to truncate
     // partials before aliasing occurs.
     float pitchRange = 1 + centsAboveLowestFrequency / m_centsPerRange;
 
@@ -144,16 +171,22 @@ void PeriodicWave::waveDataForFundamenta
 
     // The words "lower" and "higher" refer to the table data having
     // the lower and higher numbers of partials. It's a little confusing
     // since the range index gets larger the more partials we cull out.
     // So the lower table data will have a larger range index.
     unsigned rangeIndex1 = static_cast<unsigned>(pitchRange);
     unsigned rangeIndex2 = rangeIndex1 < m_numberOfRanges - 1 ? rangeIndex1 + 1 : rangeIndex1;
 
+    if (!m_bandLimitedTables[rangeIndex1].get())
+        createBandLimitedTables(fundamentalFrequency, rangeIndex1);
+
+    if (!m_bandLimitedTables[rangeIndex2].get())
+        createBandLimitedTables(fundamentalFrequency, rangeIndex2);
+
     lowerWaveData = m_bandLimitedTables[rangeIndex2]->Elements();
     higherWaveData = m_bandLimitedTables[rangeIndex1]->Elements();
 
     // Ranges from 0 -> 1 to interpolate between lower -> higher.
     tableInterpolationFactor = rangeIndex2 - pitchRange;
 }
 
 unsigned PeriodicWave::maxNumberOfPartials() const
@@ -174,88 +207,87 @@ unsigned PeriodicWave::numberOfPartialsF
 
     return numberOfPartials;
 }
 
 // Convert into time-domain wave buffers.
 // One table is created for each range for non-aliasing playback
 // at different playback rates. Thus, higher ranges have more
 // high-frequency partials culled out.
-void PeriodicWave::createBandLimitedTables(const float* realData, const float* imagData, unsigned numberOfComponents)
+void PeriodicWave::createBandLimitedTables(float fundamentalFrequency,
+                                           unsigned rangeIndex)
 {
-    float normalizationScale = 1;
-
     unsigned fftSize = m_periodicWaveSize;
-    unsigned halfSize = fftSize / 2;
     unsigned i;
 
-    // Limit the number of components used to those for frequencies below the
-    // Nyquist of the fixed length inverse FFT.
-    numberOfComponents = std::min(numberOfComponents, halfSize);
+    const float *realData = m_realComponents->Elements();
+    const float *imagData = m_imagComponents->Elements();
 
-    m_bandLimitedTables.SetCapacity(m_numberOfRanges);
-
-    for (unsigned rangeIndex = 0; rangeIndex < m_numberOfRanges; ++rangeIndex) {
-        // This FFTBlock is used to cull partials (represented by frequency bins).
-        FFTBlock frame(fftSize);
+    // This FFTBlock is used to cull partials (represented by frequency bins).
+    FFTBlock frame(fftSize);
 
-        // Find the starting bin where we should start culling the aliasing
-        // partials for this pitch range.  We need to clear out the highest
-        // frequencies to band-limit the waveform.
-        unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
-        // Also limit to the number of components that are provided.
-        numberOfPartials = std::min(numberOfPartials, numberOfComponents - 1);
+    // Find the starting bin where we should start culling the aliasing
+    // partials for this pitch range.  We need to clear out the highest
+    // frequencies to band-limit the waveform.
+    unsigned numberOfPartials = numberOfPartialsForRange(rangeIndex);
+    // Also limit to the number of components that are provided.
+    numberOfPartials = std::min(numberOfPartials, m_numberOfComponents - 1);
 
-        // Copy from loaded frequency data and generate complex conjugate
-        // because of the way the inverse FFT is defined.
-        // The coefficients of higher partials remain zero, as initialized in
-        // the FFTBlock constructor.
-        for (i = 0; i < numberOfPartials + 1; ++i) {
-            frame.RealData(i) = realData[i];
-            frame.ImagData(i) = -imagData[i];
-        }
+    // Limit number of partials to those below Nyquist frequency
+    float nyquist = 0.5 * m_sampleRate;
+    numberOfPartials = std::min(numberOfPartials,
+                                (unsigned)(nyquist / fundamentalFrequency));
 
-        // Clear any DC-offset.
-        frame.RealData(0) = 0;
-        // Clear value which has no effect.
-        frame.ImagData(0) = 0;
+    // Copy from loaded frequency data and generate complex conjugate
+    // because of the way the inverse FFT is defined.
+    // The coefficients of higher partials remain zero, as initialized in
+    // the FFTBlock constructor.
+    for (i = 0; i < numberOfPartials + 1; ++i) {
+        frame.RealData(i) = realData[i];
+        frame.ImagData(i) = -imagData[i];
+    }
 
-        // Create the band-limited table.
-        AlignedAudioFloatArray* table = new AlignedAudioFloatArray(m_periodicWaveSize);
-        m_bandLimitedTables.AppendElement(table);
-
-        // Apply an inverse FFT to generate the time-domain table data.
-        float* data = m_bandLimitedTables[rangeIndex]->Elements();
-        frame.GetInverseWithoutScaling(data);
+    // Clear any DC-offset.
+    frame.RealData(0) = 0;
+    // Clear value which has no effect.
+    frame.ImagData(0) = 0;
 
-        // For the first range (which has the highest power), calculate
-        // its peak value then compute normalization scale.
-        if (!rangeIndex) {
-            float maxValue;
-            maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
+    // Create the band-limited table.
+    AlignedAudioFloatArray* table = new AlignedAudioFloatArray(m_periodicWaveSize);
+    m_bandLimitedTables[rangeIndex] = table;
+
+    // Apply an inverse FFT to generate the time-domain table data.
+    float* data = m_bandLimitedTables[rangeIndex]->Elements();
+    frame.GetInverseWithoutScaling(data);
 
-            if (maxValue)
-                normalizationScale = 1.0f / maxValue;
-        }
+    // For the first range (which has the highest power), calculate
+    // its peak value then compute normalization scale.
+    if (!rangeIndex) {
+        float maxValue;
+        maxValue = AudioBufferPeakValue(data, m_periodicWaveSize);
 
-        // Apply normalization scale.
-        AudioBufferInPlaceScale(data, normalizationScale, m_periodicWaveSize);
+        if (maxValue)
+            m_normalizationScale = 1.0f / maxValue;
     }
+
+    // Apply normalization scale.
+    AudioBufferInPlaceScale(data, m_normalizationScale, m_periodicWaveSize);
 }
 
 void PeriodicWave::generateBasicWaveform(OscillatorType shape)
 {
     const float piFloat = float(M_PI);
     unsigned fftSize = periodicWaveSize();
     unsigned halfSize = fftSize / 2;
 
-    AudioFloatArray real(halfSize);
-    AudioFloatArray imag(halfSize);
-    float* realP = real.Elements();
-    float* imagP = imag.Elements();
+    m_numberOfComponents = halfSize;
+    m_realComponents = new AudioFloatArray(halfSize);
+    m_imagComponents = new AudioFloatArray(halfSize);
+    float* realP = m_realComponents->Elements();
+    float* imagP = m_imagComponents->Elements();
 
     // Clear DC and imag value which is ignored.
     realP[0] = 0;
     imagP[0] = 0;
 
     for (unsigned n = 1; n < halfSize; ++n) {
         float omega = 2 * piFloat * n;
         float invOmega = 1 / omega;
@@ -300,13 +332,11 @@ void PeriodicWave::generateBasicWaveform
             a = 0;
             b = 0;
             break;
         }
 
         realP[n] = a;
         imagP[n] = b;
     }
-
-    createBandLimitedTables(realP, imagP, halfSize);
 }
 
 } // namespace WebCore
--- a/dom/media/webaudio/blink/PeriodicWave.h
+++ b/dom/media/webaudio/blink/PeriodicWave.h
@@ -80,32 +80,37 @@ private:
     ~PeriodicWave() {}
 
     void generateBasicWaveform(mozilla::dom::OscillatorType);
 
     float m_sampleRate;
     unsigned m_periodicWaveSize;
     unsigned m_numberOfRanges;
     float m_centsPerRange;
+    unsigned m_numberOfComponents;
+    nsAutoPtr<AudioFloatArray> m_realComponents;
+    nsAutoPtr<AudioFloatArray> m_imagComponents;
 
     // The lowest frequency (in Hertz) where playback will include all of the
     // partials.  Playing back lower than this frequency will gradually lose
     // more high-frequency information.
     // This frequency is quite low (~10Hz @ // 44.1KHz)
     float m_lowestFundamentalFrequency;
 
     float m_rateScale;
 
     unsigned numberOfRanges() const { return m_numberOfRanges; }
 
     // Maximum possible number of partials (before culling).
     unsigned maxNumberOfPartials() const;
 
     unsigned numberOfPartialsForRange(unsigned rangeIndex) const;
 
-    // Creates tables based on numberOfComponents Fourier coefficients.
-    void createBandLimitedTables(const float* real, const float* imag, unsigned numberOfComponents);
+    // Creates table for specified index based on fundamental frequency.
+    void createBandLimitedTables(float fundamentalFrequency, unsigned rangeIndex);
+    float m_lowestRequestedFundamentalFrequency;
+    float m_normalizationScale;
     nsTArray<nsAutoPtr<AlignedAudioFloatArray> > m_bandLimitedTables;
 };
 
 } // namespace WebCore
 
 #endif // PeriodicWave_h
--- a/dom/media/webm/EbmlComposer.cpp
+++ b/dom/media/webm/EbmlComposer.cpp
@@ -16,18 +16,18 @@ static const unsigned long TIME_CODE_SCA
 // The WebM header size without audio CodecPrivateData
 static const int32_t DEFAULT_HEADER_SIZE = 1024;
 
 void EbmlComposer::GenerateHeader()
 {
   // Write the EBML header.
   EbmlGlobal ebml;
   // The WEbM header default size usually smaller than 1k.
-  nsAutoArrayPtr<uint8_t> buffer(new uint8_t[DEFAULT_HEADER_SIZE +
-                                             mCodecPrivateData.Length()]);
+  auto buffer = MakeUnique<uint8_t[]>(DEFAULT_HEADER_SIZE +
+                                             mCodecPrivateData.Length());
   ebml.buf = buffer.get();
   ebml.offset = 0;
   writeHeader(&ebml);
   {
     EbmlLoc segEbmlLoc, ebmlLocseg, ebmlLoc;
     Ebml_StartSubElement(&ebml, &segEbmlLoc, Segment);
     {
       Ebml_StartSubElement(&ebml, &ebmlLocseg, SeekHead);
--- a/dom/media/webrtc/MediaEngineDefault.cpp
+++ b/dom/media/webrtc/MediaEngineDefault.cpp
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaEngineDefault.h"
 
 #include "nsCOMPtr.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/UniquePtr.h"
 #include "nsILocalFile.h"
 #include "Layers.h"
 #include "ImageContainer.h"
 #include "ImageTypes.h"
 #include "prmem.h"
 #include "nsContentUtils.h"
 
 #include "nsIFilePicker.h"
@@ -309,17 +310,17 @@ public:
   static const int millisecondsPerSecond = PR_MSEC_PER_SEC;
 
   explicit SineWaveGenerator(uint32_t aSampleRate, uint32_t aFrequency) :
     mTotalLength(aSampleRate / aFrequency),
     mReadLength(0) {
     // If we allow arbitrary frequencies, there's no guarantee we won't get rounded here
     // We could include an error term and adjust for it in generation; not worth the trouble
     //MOZ_ASSERT(mTotalLength * aFrequency == aSampleRate);
-    mAudioBuffer = new int16_t[mTotalLength];
+    mAudioBuffer = MakeUnique<int16_t[]>(mTotalLength);
     for (int i = 0; i < mTotalLength; i++) {
       // Set volume to -20db. It's from 32768.0 * 10^(-20/20) = 3276.8
       mAudioBuffer[i] = (3276.8f * sin(2 * M_PI * i / mTotalLength));
     }
   }
 
   // NOTE: only safely called from a single thread (MSG callback)
   void generate(int16_t* aBuffer, int16_t aLengthInSamples) {
@@ -328,28 +329,28 @@ public:
     while (remaining) {
       int16_t processSamples = 0;
 
       if (mTotalLength - mReadLength >= remaining) {
         processSamples = remaining;
       } else {
         processSamples = mTotalLength - mReadLength;
       }
-      memcpy(aBuffer, mAudioBuffer + mReadLength, processSamples * bytesPerSample);
+      memcpy(aBuffer, &mAudioBuffer[mReadLength], processSamples * bytesPerSample);
       aBuffer += processSamples;
       mReadLength += processSamples;
       remaining -= processSamples;
       if (mReadLength == mTotalLength) {
         mReadLength = 0;
       }
     }
   }
 
 private:
-  nsAutoArrayPtr<int16_t> mAudioBuffer;
+  UniquePtr<int16_t[]> mAudioBuffer;
   int16_t mTotalLength;
   int16_t mReadLength;
 };
 
 /**
  * Default audio source.
  */
 NS_IMPL_ISUPPORTS(MediaEngineDefaultAudioSource, nsITimerCallback)
--- a/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
+++ b/dom/media/webspeech/synth/test/nsFakeSynthServices.cpp
@@ -147,18 +147,18 @@ FakeDirectAudioSynth::Speak(const nsAStr
     NS_IMETHOD Run() override
     {
       RefPtr<FakeSynthCallback> cb = new FakeSynthCallback(nullptr);
       mTask->Setup(cb, CHANNELS, SAMPLERATE, 2);
 
       // Just an arbitrary multiplier. Pretend that each character is
       // synthesized to 40 frames.
       uint32_t frames_length = 40 * mText.Length();
-      nsAutoArrayPtr<int16_t> frames(new int16_t[frames_length]());
-      mTask->SendAudioNative(frames, frames_length);
+      auto frames = MakeUnique<int16_t[]>(frames_length);
+      mTask->SendAudioNative(frames.get(), frames_length);
 
       mTask->SendAudioNative(nullptr, 0);
 
       return NS_OK;
     }
 
   private:
     nsCOMPtr<nsISpeechTask> mTask;
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -622,16 +622,35 @@ PluginModuleChromeParent::OnProcessLaunc
 
 #if defined(XP_MACOSX)
         if (NS_SUCCEEDED(mAsyncInitRv)) {
             mAsyncInitRv = NP_GetEntryPoints(mNPPIface,
                                              &mAsyncInitError);
         }
 #endif
     }
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+    nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
+    bool profilerActive = false;
+    DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
+    MOZ_ASSERT(NS_SUCCEEDED(rv));
+    if (profilerActive) {
+        nsCOMPtr<nsIProfilerStartParams> currentProfilerParams;
+        rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams));
+        MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+        nsCOMPtr<nsISupports> gatherer;
+        rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer));
+        MOZ_ASSERT(NS_SUCCEEDED(rv));
+        mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
+
+        StartProfiler(currentProfilerParams);
+    }
+#endif
 }
 
 bool
 PluginModuleChromeParent::WaitForIPCConnection()
 {
     PluginProcessParent* process = Process();
     MOZ_ASSERT(process);
     process->SetCallRunnableImmediately(true);
@@ -3157,36 +3176,21 @@ NS_IMPL_ISUPPORTS(PluginProfilerObserver
 
 NS_IMETHODIMP
 PluginProfilerObserver::Observe(nsISupports *aSubject,
                                 const char *aTopic,
                                 const char16_t *aData)
 {
     if (!strcmp(aTopic, "profiler-started")) {
         nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
-        uint32_t entries;
-        double interval;
-        params->GetEntries(&entries);
-        params->GetInterval(&interval);
-        const nsTArray<nsCString>& features = params->GetFeatures();
-        const nsTArray<nsCString>& threadFilterNames = params->GetThreadFilterNames();
-
-        ProfilerInitParams ipcParams;
-        ipcParams.enabled() = true;
-        ipcParams.entries() = entries;
-        ipcParams.interval() = interval;
-        ipcParams.features() = features;
-        ipcParams.threadFilters() = threadFilterNames;
-
-        Unused << mPmp->SendStartProfiler(ipcParams);
+        mPmp->StartProfiler(params);
     } else if (!strcmp(aTopic, "profiler-stopped")) {
-        Unused << mPmp->SendStopProfiler();
+        mPmp->StopProfiler();
     } else if (!strcmp(aTopic, "profiler-subprocess-gather")) {
-        RefPtr<ProfileGatherer> gatherer = static_cast<ProfileGatherer*>(aSubject);
-        mPmp->GatherAsyncProfile(gatherer);
+        mPmp->GatherAsyncProfile();
     } else if (!strcmp(aTopic, "profiler-subprocess")) {
         nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
         mPmp->GatheredAsyncProfile(pse);
     }
     return NS_OK;
 }
 
 void
@@ -3210,19 +3214,54 @@ PluginModuleChromeParent::ShutdownPlugin
         observerService->RemoveObserver(mProfilerObserver, "profiler-started");
         observerService->RemoveObserver(mProfilerObserver, "profiler-stopped");
         observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess-gather");
         observerService->RemoveObserver(mProfilerObserver, "profiler-subprocess");
     }
 }
 
 void
-PluginModuleChromeParent::GatherAsyncProfile(ProfileGatherer* aGatherer)
+PluginModuleChromeParent::StartProfiler(nsIProfilerStartParams* aParams)
 {
-    mGatherer = aGatherer;
+    if (NS_WARN_IF(!aParams)) {
+        return;
+    }
+
+    ProfilerInitParams ipcParams;
+
+    ipcParams.enabled() = true;
+    aParams->GetEntries(&ipcParams.entries());
+    aParams->GetInterval(&ipcParams.interval());
+    ipcParams.features() = aParams->GetFeatures();
+    ipcParams.threadFilters() = aParams->GetThreadFilterNames();
+
+    Unused << SendStartProfiler(ipcParams);
+
+    nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
+    if (NS_WARN_IF(!profiler)) {
+        return;
+    }
+    nsCOMPtr<nsISupports> gatherer;
+    profiler->GetProfileGatherer(getter_AddRefs(gatherer));
+    mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
+}
+
+void
+PluginModuleChromeParent::StopProfiler()
+{
+    mGatherer = nullptr;
+    Unused << SendStopProfiler();
+}
+
+void
+PluginModuleChromeParent::GatherAsyncProfile()
+{
+    if (NS_WARN_IF(!mGatherer)) {
+        return;
+    }
     mGatherer->WillGatherOOPProfile();
     Unused << SendGatherProfile();
 }
 
 void
 PluginModuleChromeParent::GatheredAsyncProfile(nsIProfileSaveEvent* aSaveEvent)
 {
     if (aSaveEvent && !mProfile.IsEmpty()) {
@@ -3237,14 +3276,13 @@ PluginModuleChromeParent::RecvProfile(co
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
     if (NS_WARN_IF(!mGatherer)) {
         return true;
     }
 
     mProfile = aProfile;
     mGatherer->GatheredOOPProfile();
-    mGatherer = nullptr;
 #endif
     return true;
 }
 
 
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -451,18 +451,20 @@ class PluginModuleChromeParent
     void CachedSettingChanged();
 
     void OnEnteredCall() override;
     void OnExitedCall() override;
     void OnEnteredSyncSend() override;
     void OnExitedSyncSend() override;
 
 #ifdef  MOZ_ENABLE_PROFILER_SPS
-    void GatherAsyncProfile(mozilla::ProfileGatherer* aGatherer);
+    void GatherAsyncProfile();
     void GatheredAsyncProfile(nsIProfileSaveEvent* aSaveEvent);
+    void StartProfiler(nsIProfilerStartParams* aParams);
+    void StopProfiler();
 #endif
 
     virtual bool
     RecvProfile(const nsCString& aProfile) override;
 
 private:
     virtual void
     EnteredCxxStack() override;
--- a/dom/presentation/PresentationSessionInfo.cpp
+++ b/dom/presentation/PresentationSessionInfo.cpp
@@ -33,24 +33,21 @@
 #include "nsINetworkInterface.h"
 #include "nsINetworkManager.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::services;
 
-inline static PRLogModuleInfo*
-GetPresentationSessionInfoLog()
-{
-  static PRLogModuleInfo* log = PR_NewLogModule("PresentationSessionInfo");
-  return log;
-}
+
+static LazyLogModule gPresentationSessionInfoLog("PresentationSessionInfo");
+
 #undef LOG
-#define LOG(...) MOZ_LOG(GetPresentationSessionInfoLog(), mozilla::LogLevel::Error, (__VA_ARGS__))
+#define LOG(...) MOZ_LOG(gPresentationSessionInfoLog, mozilla::LogLevel::Error, (__VA_ARGS__))
 
 
 /*
  * Implementation of PresentationChannelDescription
  */
 
 namespace mozilla {
 namespace dom {
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -106,21 +106,24 @@ skip-if = e10s || buildapp == 'mulet' ||
 skip-if = toolkit == 'android' #CRASH_SUTAGENT
 [test_windowProperties.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_windowedhistoryframes.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_navigation_timing.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet'
 [test_bug1012662_editor.html]
+skip-if = (toolkit == 'android') # Disabled on Android, see bug 1230231
 [test_bug1012662_noeditor.html]
+skip-if = (toolkit == 'android') # Disabled on Android, see bug 1230231
 [test_bug1161721.html]
 [test_bug1170911.html]
 [test_storagePermissionsAccept.html]
 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g
 [test_storagePermissionsRejectForeign.html]
 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g
 [test_storagePermissionsReject.html]
 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g
 [test_storagePermissionsLimitForeign.html]
 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g
 [test_selectevents.html]
-skip-if = buildapp == 'b2g' || buildapp == 'mulet' # Mouse doesn't select in the same way on b2g
+skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' # Mouse doesn't select in the same way
+# Disabled on Android, see bug 1230232
new file mode 100644
--- /dev/null
+++ b/dom/xslt/crashtests/1205163.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/xsl" href="1205163.xsl"?>
+<result>
+  <Title>Example</Title>
+  <Error>Error</Error>
+</result>
new file mode 100644
--- /dev/null
+++ b/dom/xslt/crashtests/1205163.xsl
@@ -0,0 +1,11 @@
+<?xml version="1.0"?>
+<xsl:stylesheet version="1.0"
+   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+  <xsl:output method="text"/>
+  <xsl:template match="node()|@*">
+    <xsl:copy>
+      <xsl:apply-templates select="node()|@*"/>
+    </xsl:copy>
+  </xsl:template>
+  <xsl:template match="Error"/>
+</xsl:stylesheet>
--- a/dom/xslt/crashtests/crashtests.list
+++ b/dom/xslt/crashtests/crashtests.list
@@ -11,8 +11,9 @@ load 528300.xml
 load 528488.xml
 load 528963.xml
 load 545927.html
 load 601543.html
 load 602115.html
 load 603844.html
 load 667315.xml
 load 1089049.html
+load 1205163.xml
--- a/dom/xslt/xslt/txMozillaTextOutput.cpp
+++ b/dom/xslt/xslt/txMozillaTextOutput.cpp
@@ -76,16 +76,21 @@ txMozillaTextOutput::endDocument(nsresul
     NS_ENSURE_TRUE(mDocument && mTextParent, NS_ERROR_FAILURE);
 
     RefPtr<nsTextNode> text = new nsTextNode(mDocument->NodeInfoManager());
     
     text->SetText(mText, false);
     nsresult rv = mTextParent->AppendChildTo(text, true);
     NS_ENSURE_SUCCESS(rv, rv);
 
+    // This should really be handled by nsIDocument::EndLoad
+    MOZ_ASSERT(mDocument->GetReadyStateEnum() ==
+               nsIDocument::READYSTATE_LOADING, "Bad readyState");
+    mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_INTERACTIVE);
+
     if (NS_SUCCEEDED(aResult)) {
         nsCOMPtr<nsITransformObserver> observer = do_QueryReferent(mObserver);
         if (observer) {
             observer->OnTransformDone(aResult, mDocument);
         }
     }
 
     return NS_OK;
@@ -129,16 +134,20 @@ txMozillaTextOutput::createResultDocumen
      *
      * <transformiix:result> * The text comes here * </transformiix:result>
      */
 
     // Create the document
     nsresult rv = NS_NewXMLDocument(getter_AddRefs(mDocument),
                                     aLoadedAsData);
     NS_ENSURE_SUCCESS(rv, rv);
+    // This should really be handled by nsIDocument::BeginLoad
+    MOZ_ASSERT(mDocument->GetReadyStateEnum() ==
+               nsIDocument::READYSTATE_UNINITIALIZED, "Bad readyState");
+    mDocument->SetReadyStateInternal(nsIDocument::READYSTATE_LOADING);
     nsCOMPtr<nsIDocument> source = do_QueryInterface(aSourceDocument);
     NS_ENSURE_STATE(source);
     bool hasHadScriptObject = false;
     nsIScriptGlobalObject* sgo =
       source->GetScriptHandlingObject(hasHadScriptObject);
     NS_ENSURE_STATE(sgo || !hasHadScriptObject);
     mDocument->SetScriptHandlingObject(sgo);
 
--- a/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp
+++ b/gfx/angle/src/libANGLE/renderer/d3d/ProgramD3D.cpp
@@ -204,16 +204,26 @@ void GetUniformBlockInfo(const std::vect
             bool isRowMajorMatrix = (gl::IsMatrixType(field.type) && inRowMajorLayout);
             (*blockInfoOut)[fieldName] =
                 encoder->encodeType(field.type, field.arraySize, isRowMajorMatrix);
         }
     }
 }
 
 template <typename T>
+static inline void SetIfDirty(T *dest, const T &source, bool *dirtyFlag)
+{
+    ASSERT(dest != NULL);
+    ASSERT(dirtyFlag != NULL);
+
+    *dirtyFlag = *dirtyFlag || (memcmp(dest, &source, sizeof(T)) != 0);
+    *dest      = source;
+}
+
+template <typename T>
 bool TransposeMatrix(T *target,
                      const GLfloat *value,
                      int targetWidth,
                      int targetHeight,
                      int srcWidth,
                      int srcHeight)
 {
     bool dirty = false;
@@ -1907,26 +1917,16 @@ void ProgramD3D::defineUniform(GLenum sh
         if (uniform.isArray())
         {
             encoder->exitAggregateType();
         }
     }
 }
 
 template <typename T>
-static inline void SetIfDirty(T *dest, const T &source, bool *dirtyFlag)
-{
-    ASSERT(dest != NULL);
-    ASSERT(dirtyFlag != NULL);
-
-    *dirtyFlag = *dirtyFlag || (memcmp(dest, &source, sizeof(T)) != 0);
-    *dest      = source;
-}
-
-template <typename T>
 void ProgramD3D::setUniform(GLint location, GLsizei countIn, const T *v, GLenum targetUniformType)
 {
     const int components        = gl::VariableComponentCount(targetUniformType);
     const GLenum targetBoolType = gl::VariableBoolVectorType(targetUniformType);
 
     D3DUniform *targetUniform = getD3DUniformFromLocation(location);
 
     unsigned int elementCount = targetUniform->elementCount();
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -1914,16 +1914,29 @@ nsEventStatus AsyncPanZoomController::On
 nsEventStatus AsyncPanZoomController::OnPanEnd(const PanGestureInput& aEvent) {
   APZC_LOG("%p got a pan-end in state %d\n", this, mState);
 
   // Call into OnPan in order to process any delta included in this event.
   OnPan(aEvent, true);
 
   mX.EndTouch(aEvent.mTime);
   mY.EndTouch(aEvent.mTime);
+
+  // Drop any velocity on axes where we don't have room to scroll anyways
+  // (in this APZC, or an APZC further in the handoff chain).
+  // This ensures that we don't enlarge the display port unnecessarily.
+  RefPtr<const OverscrollHandoffChain> overscrollHandoffChain =
+    CurrentPanGestureBlock()->GetOverscrollHandoffChain();
+  if (!overscrollHandoffChain->CanScrollInDirection(this, Layer::HORIZONTAL)) {
+    mX.SetVelocity(0);
+  }
+  if (!overscrollHandoffChain->CanScrollInDirection(this, Layer::VERTICAL)) {
+    mY.SetVelocity(0);
+  }
+
   SetState(NOTHING);
   RequestContentRepaint();
 
   return nsEventStatus_eConsumeNoDefault;
 }
 
 nsEventStatus AsyncPanZoomController::OnPanMomentumStart(const PanGestureInput& aEvent) {
   APZC_LOG("%p got a pan-momentumstart in state %d\n", this, mState);
--- a/gfx/layers/apz/src/Axis.cpp
+++ b/gfx/layers/apz/src/Axis.cpp
@@ -71,17 +71,17 @@ void Axis::UpdateWithTouchAtDevicePoint(
     // This could be a duplicate event, or it could be a legitimate event
     // on some platforms that generate events really fast. As a compromise
     // update mPos so we don't run into problems like bug 1042734, even though
     // that means the velocity will be stale. Better than doing a divide-by-zero.
     mPos = aPos;
     return;
   }
 
-  float newVelocity = mAxisLocked ? 0.0f : (float)(mPos - aPos - aAdditionalDelta) / (float)(aTimestampMs - mPosTimeMs);
+  float newVelocity = mAxisLocked ? 0.0f : (float)(mPos - aPos + aAdditionalDelta) / (float)(aTimestampMs - mPosTimeMs);
   if (gfxPrefs::APZMaxVelocity() > 0.0f) {
     bool velocityIsNegative = (newVelocity < 0);
     newVelocity = fabs(newVelocity);
 
     float maxVelocity = ToLocalVelocity(gfxPrefs::APZMaxVelocity());
     newVelocity = std::min(newVelocity, maxVelocity);
 
     if (gfxPrefs::APZCurveThreshold() > 0.0f && gfxPrefs::APZCurveThreshold() < gfxPrefs::APZMaxVelocity()) {
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -712,34 +712,16 @@ ClientLayerManager::GetTexturePool(Surfa
                             gfxPrefs::LayersTileMaxPoolSize(),
                             gfxPrefs::LayersTileShrinkPoolTimeout(),
                             mForwarder));
 
   return mTexturePools.LastElement();
 }
 
 void
-ClientLayerManager::ReturnTextureClientDeferred(TextureClient& aClient) {
-  GetTexturePool(aClient.GetFormat(),
-                 aClient.GetFlags())->ReturnTextureClientDeferred(&aClient);
-}
-
-void
-ClientLayerManager::ReturnTextureClient(TextureClient& aClient) {
-  GetTexturePool(aClient.GetFormat(),
-                 aClient.GetFlags())->ReturnTextureClient(&aClient);
-}
-
-void
-ClientLayerManager::ReportClientLost(TextureClient& aClient) {
-  GetTexturePool(aClient.GetFormat(),
-                 aClient.GetFlags())->ReportClientLost();
-}
-
-void
 ClientLayerManager::ClearCachedResources(Layer* aSubtree)
 {
   if (mDestroyed) {
     // ClearCachedResource was already called by ClientLayerManager::Destroy
     return;
   }
   MOZ_ASSERT(!HasShadowManager() || !aSubtree);
   mForwarder->ClearCachedResources();
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -118,21 +118,16 @@ public:
 
   virtual bool IsCompositingCheap() override;
   virtual bool HasShadowManagerInternal() const override { return HasShadowManager(); }
 
   virtual void SetIsFirstPaint() override;
 
   TextureClientPool* GetTexturePool(gfx::SurfaceFormat aFormat, TextureFlags aFlags);
 
-  /// Utility methods for managing texture clients.
-  void ReturnTextureClientDeferred(TextureClient& aClient);
-  void ReturnTextureClient(TextureClient& aClient);
-  void ReportClientLost(TextureClient& aClient);
-
   /**
    * Pass through call to the forwarder for nsPresContext's
    * CollectPluginGeometryUpdates. Passes widget configuration information
    * to the compositor for transmission to the chrome process. This
    * configuration gets set when the window paints.
    */
   void StorePluginWidgetConfigurations(const nsTArray<nsIWidget::Configuration>&
                                        aConfigurations) override;
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -251,16 +251,17 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'coco
         'gfxMacPlatformFontList.mm',
     ]
 
 # We prefer to use ICU for normalization functions, but currently it is only
 # available if we're building with the Intl API enabled:
 if CONFIG['ENABLE_INTL_API']:
     CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
     CFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+    LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
     USE_LIBS += [
         'icu',
     ]
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']
 
 include('/ipc/chromium/chromium-config.mozbuild')
--- a/intl/build/moz.build
+++ b/intl/build/moz.build
@@ -5,16 +5,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SOURCES += [
     'nsI18nModule.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
 LOCAL_INCLUDES += [
     '../locale',
     '../lwbrk',
     '../strres',
     '../uconv',
     '../unicharutil',
 ]
 
new file mode 100644
--- /dev/null
+++ b/intl/icu-patches/bug-1228227-libc++-gcc_hidden.diff
@@ -0,0 +1,23 @@
+Limit libstdc++ workaround to not upset libc++ with pragma visibility.
+
+diff --git a/intl/icu/source/common/unicode/std_string.h b/intl/icu/source/common/unicode/std_string.h
+--- a/intl/icu/source/common/unicode/std_string.h
++++ b/intl/icu/source/common/unicode/std_string.h
+@@ -22,16 +22,16 @@
+  * \brief C++ API: Central ICU header for including the C++ standard &lt;string&gt;
+  *                 header and for related definitions.
+  */
+ 
+ #include "unicode/utypes.h"
+ 
+ #if U_HAVE_STD_STRING
+ 
+-#if !defined(_MSC_VER)
++#if defined(__GLIBCXX__)
+ namespace std { class type_info; } // WORKAROUND: http://llvm.org/bugs/show_bug.cgi?id=13364
+ #endif
+ #include <string>
+ 
+ #endif  // U_HAVE_STD_STRING
+ 
+ #endif  // __STD_STRING_H__
--- a/intl/icu/source/common/unicode/std_string.h
+++ b/intl/icu/source/common/unicode/std_string.h
@@ -22,16 +22,16 @@
  * \brief C++ API: Central ICU header for including the C++ standard &lt;string&gt;
  *                 header and for related definitions.
  */
 
 #include "unicode/utypes.h"
 
 #if U_HAVE_STD_STRING
 
-#if !defined(_MSC_VER)
+#if defined(__GLIBCXX__)
 namespace std { class type_info; } // WORKAROUND: http://llvm.org/bugs/show_bug.cgi?id=13364
 #endif
 #include <string>
 
 #endif  // U_HAVE_STD_STRING
 
 #endif  // __STD_STRING_H__
--- a/intl/locale/mac/moz.build
+++ b/intl/locale/mac/moz.build
@@ -7,11 +7,12 @@
 UNIFIED_SOURCES += [
     'nsCollationMacUC.cpp',
     'nsDateTimeFormatMac.cpp',
     'nsMacCharset.cpp',
 ]
 
 FINAL_LIBRARY = 'xul'
 CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
 LOCAL_INCLUDES += [
     '..',
 ]
--- a/intl/unicharutil/util/internal/moz.build
+++ b/intl/unicharutil/util/internal/moz.build
@@ -15,10 +15,11 @@ UNIFIED_SOURCES += intl_unicharutil_util
 FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '..',
 ]
 
 if CONFIG['ENABLE_INTL_API']:
     CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+    LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
 
 DIST_INSTALL = True
--- a/intl/unicharutil/util/moz.build
+++ b/intl/unicharutil/util/moz.build
@@ -36,11 +36,12 @@ USE_STATIC_LIBS = True
 if CONFIG['_MSC_VER']:
     DEFINES['_USE_ANSI_CPP'] = True
     # Don't include directives about which CRT to use
     CFLAGS += ['-Zl']
     CXXFLAGS += ['-Zl']
 
 if CONFIG['ENABLE_INTL_API']:
     CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+    LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
     USE_LIBS += ['icu']
 
 DIST_INSTALL = True
--- a/intl/unicharutil/util/standalone/moz.build
+++ b/intl/unicharutil/util/standalone/moz.build
@@ -8,16 +8,17 @@ if CONFIG['OS_TARGET'] != 'WINNT' and CO
     Library('unicharutil_standalone')
 
 UNIFIED_SOURCES += [
     '../nsUnicodeProperties.cpp',
 ]
 
 if CONFIG['ENABLE_INTL_API']:
     CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+    LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
     USE_LIBS += ['icu']
 
 for var in ('MOZILLA_INTERNAL_API', 'MOZILLA_XPCOMRT_API', 'MOZILLA_EXTERNAL_LINKAGE',
             'NR_SOCKET_IS_VOID_PTR', 'HAVE_STRDUP'):
     DEFINES[var] = True
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']
--- a/intl/update-icu.sh
+++ b/intl/update-icu.sh
@@ -53,14 +53,15 @@ rm ${icu_dir}/source/data/unit/*.txt
 svn info $1 | grep -v '^Revision: [[:digit:]]\+$' > ${icu_dir}/SVN-INFO
 
 patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/bug-915735
 patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/suppress-warnings.diff
 patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/bug-1172609-icu-fix.diff
 patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/bug-1172609-timezone-recreateDefault.diff
 patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/bug-1198952-workaround-make-3.82-bug.diff
 patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/icu-release-56-1-flagparser-fix.patch
+patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/bug-1228227-libc++-gcc_hidden.diff
 
 # NOTE: If you're updating this script for a new ICU version, you have to rerun
 # js/src/tests/ecma_6/String/make-normalize-generateddata-input.py for any
 # normalization changes the new ICU implements.
 
 hg addremove ${icu_dir}
--- a/js/src/asmjs/AsmJSModule.cpp
+++ b/js/src/asmjs/AsmJSModule.cpp
@@ -1794,22 +1794,19 @@ AsmJSModule::setProfilingEnabled(bool en
         Instruction* callerInsn = reinterpret_cast<Instruction*>(caller);
         BOffImm calleeOffset;
         callerInsn->as<InstBLImm>()->extractImm(&calleeOffset);
         void* callee = calleeOffset.getDest(callerInsn);
 #elif defined(JS_CODEGEN_ARM64)
         MOZ_CRASH();
         void* callee = nullptr;
         (void)callerRetAddr;
-#elif defined(JS_CODEGEN_MIPS32)
-        Instruction* instr = (Instruction*)(callerRetAddr - 4 * sizeof(uint32_t));
-        void* callee = (void*)Assembler::ExtractLuiOriValue(instr, instr->next());
-#elif defined(JS_CODEGEN_MIPS64)
-        Instruction* instr = (Instruction*)(callerRetAddr - 6 * sizeof(uint32_t));
-        void* callee = (void*)Assembler::ExtractLoad64Value(instr);
+#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
+        uint8_t* instr = callerRetAddr - Assembler::PatchWrite_NearCallSize();
+        void* callee = (void*)Assembler::ExtractInstructionImmediate(instr);
 #elif defined(JS_CODEGEN_NONE)
         MOZ_CRASH();
         void* callee = nullptr;
 #else
 # error "Missing architecture"
 #endif
 
         const CodeRange* codeRange = lookupCodeRange(callee);
@@ -1824,23 +1821,18 @@ AsmJSModule::setProfilingEnabled(bool en
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
         X86Encoding::SetRel32(callerRetAddr, newCallee);
 #elif defined(JS_CODEGEN_ARM)
         new (caller) InstBLImm(BOffImm(newCallee - caller), Assembler::Always);
 #elif defined(JS_CODEGEN_ARM64)
         (void)newCallee;
         MOZ_CRASH();
-#elif defined(JS_CODEGEN_MIPS32)
-        Assembler::WriteLuiOriInstructions(instr, instr->next(),
-                                           ScratchRegister, (uint32_t)newCallee);
-        instr[2] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
-#elif defined(JS_CODEGEN_MIPS64)
-        Assembler::WriteLoad64Instructions(instr, ScratchRegister, (uint64_t)newCallee);
-        instr[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr);
+#elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
+        Assembler::PatchInstructionImmediate(instr, PatchedImmPtr(newCallee));
 #elif defined(JS_CODEGEN_NONE)
         MOZ_CRASH();
 #else
 # error "Missing architecture"
 #endif
     }
 
     // Update all the addresses in the function-pointer tables to point to the
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -134,23 +134,16 @@ CompareExchange(Scalar::Type viewType, i
       }
       case Scalar::Uint8: {
         uint8_t oldval = (uint8_t)oldCandidate;
         uint8_t newval = (uint8_t)newCandidate;
         oldval = jit::AtomicOperations::compareExchangeSeqCst(viewData.cast<uint8_t*>() + offset,
                                                               oldval, newval);
         return oldval;
       }
-      case Scalar::Uint8Clamped: {
-        uint8_t oldval = ClampIntForUint8Array(oldCandidate);
-        uint8_t newval = ClampIntForUint8Array(newCandidate);
-        oldval = jit::AtomicOperations::compareExchangeSeqCst(viewData.cast<uint8_t*>() + offset,
-                                                              oldval, newval);
-        return oldval;
-      }
       case Scalar::Int16: {
         int16_t oldval = (int16_t)oldCandidate;
         int16_t newval = (int16_t)newCandidate;
         oldval = jit::AtomicOperations::compareExchangeSeqCst(viewData.cast<int16_t*>() + offset,
                                                               oldval, newval);
         return oldval;
       }
       case Scalar::Uint16: {
@@ -230,18 +223,17 @@ js::atomics_load(JSContext* cx, unsigned
     if (!GetSharedTypedArray(cx, objv, &view))
         return false;
     uint32_t offset;
     if (!GetTypedArrayIndex(cx, idxv, view, &offset))
         return false;
 
     SharedMem<void*> viewData = view->viewDataShared();
     switch (view->type()) {
-      case Scalar::Uint8:
-      case Scalar::Uint8Clamped: {
+      case Scalar::Uint8: {
         uint8_t v = jit::AtomicOperations::loadSeqCst(viewData.cast<uint8_t*>() + offset);
         r.setInt32(v);
         return true;
       }
       case Scalar::Int8: {
         int8_t v = jit::AtomicOperations::loadSeqCst(viewData.cast<uint8_t*>() + offset);
         r.setInt32(v);
         return true;
@@ -295,21 +287,16 @@ ExchangeOrStore(Scalar::Type viewType, i
         INT_OP(viewData.cast<int8_t*>() + offset, value);
         return value;
       }
       case Scalar::Uint8: {
         uint8_t value = (uint8_t)numberValue;
         INT_OP(viewData.cast<uint8_t*>() + offset, value);
         return value;
       }
-      case Scalar::Uint8Clamped: {
-        uint8_t value = ClampIntForUint8Array(numberValue);
-        INT_OP(viewData.cast<uint8_t*>() + offset, value);
-        return value;
-      }
       case Scalar::Int16: {
         int16_t value = (int16_t)numberValue;
         INT_OP(viewData.cast<int16_t*>() + offset, value);
         return value;
       }
       case Scalar::Uint16: {
         uint16_t value = (uint16_t)numberValue;
         INT_OP(viewData.cast<uint16_t*>() + offset, value);
@@ -401,36 +388,16 @@ AtomicsBinop(JSContext* cx, HandleValue 
         r.setInt32(T::operate(viewData.cast<int8_t*>() + offset, v));
         return true;
       }
       case Scalar::Uint8: {
         uint8_t v = (uint8_t)numberValue;
         r.setInt32(T::operate(viewData.cast<uint8_t*>() + offset, v));
         return true;
       }
-      case Scalar::Uint8Clamped: {
-        // Spec says:
-        //  - clamp the input value
-        //  - perform the operation
-        //  - clamp the result
-        //  - store the result
-        // This requires a CAS loop.
-        int32_t value = ClampIntForUint8Array(numberValue);
-        SharedMem<uint8_t*> loc = viewData.cast<uint8_t*>() + offset;
-        for (;;) {
-            uint8_t old = jit::AtomicOperations::loadSafeWhenRacy(loc);
-            uint8_t result = (uint8_t)ClampIntForUint8Array(T::perform(old, value));
-            uint8_t tmp = jit::AtomicOperations::compareExchangeSeqCst(loc, old, result);
-            if (tmp == old) {
-                r.setInt32(old);
-                break;
-            }
-        }
-        return true;
-      }
       case Scalar::Int16: {
         int16_t v = (int16_t)numberValue;
         r.setInt32(T::operate(viewData.cast<int16_t*>() + offset, v));
         return true;
       }
       case Scalar::Uint16: {
         uint16_t v = (uint16_t)numberValue;
         r.setInt32(T::operate(viewData.cast<uint16_t*>() + offset, v));
--- a/js/src/jit-test/tests/atomics/basic-tests.js
+++ b/js/src/jit-test/tests/atomics/basic-tests.js
@@ -404,16 +404,29 @@ function testIsLockFree() {
     assertEq(Atomics.isLockFree(7), false);
     assertEq(Atomics.isLockFree(8), saved8);
     assertEq(Atomics.isLockFree(9), false);
     assertEq(Atomics.isLockFree(10), false);
     assertEq(Atomics.isLockFree(11), false);
     assertEq(Atomics.isLockFree(12), false);
 }
 
+function testUint8Clamped(sab) {
+    var ta = new Uint8ClampedArray(sab);
+    var thrown = false;
+    try {
+	CLONE(testMethod)(ta, 0);
+    }
+    catch (e) {
+	thrown = true;
+	assertEq(e instanceof TypeError, true);
+    }
+    assertEq(thrown, true);
+}
+
 function isLittleEndian() {
     var xxx = new ArrayBuffer(2);
     var xxa = new Int16Array(xxx);
     var xxb = new Int8Array(xxx);
     xxa[0] = 37;
     var is_little = xxb[0] == 37;
     return is_little;
 }
@@ -435,17 +448,16 @@ function runTests() {
 	assertEq(t2[0], 37);
     else
 	assertEq(t2[0], 37 << 16);
     t1[0] = 0;
 
     // Test that invoking as Atomics.whatever() works, on correct arguments.
     CLONE(testMethod)(new Int8Array(sab), 0, 42, 4095);
     CLONE(testMethod)(new Uint8Array(sab), 0, 42, 4095);
-    CLONE(testMethod)(new Uint8ClampedArray(sab), 0, 42, 4095);
     CLONE(testMethod)(new Int16Array(sab), 0, 42, 2047);
     CLONE(testMethod)(new Uint16Array(sab), 0, 42, 2047);
     CLONE(testMethod)(new Int32Array(sab), 0, 42, 1023);
     CLONE(testMethod)(new Uint32Array(sab), 0, 42, 1023);
 
     // Test that invoking as v = Atomics.whatever; v() works, on correct arguments.
     gAtomics_compareExchange = Atomics.compareExchange;
     gAtomics_exchange = Atomics.exchange;
@@ -455,17 +467,16 @@ function runTests() {
     gAtomics_add = Atomics.add;
     gAtomics_sub = Atomics.sub;
     gAtomics_and = Atomics.and;
     gAtomics_or = Atomics.or;
     gAtomics_xor = Atomics.xor;
 
     CLONE(testFunction)(new Int8Array(sab), 0, 42, 4095);
     CLONE(testFunction)(new Uint8Array(sab), 0, 42, 4095);
-    CLONE(testFunction)(new Uint8ClampedArray(sab), 0, 42, 4095);
     CLONE(testFunction)(new Int16Array(sab), 0, 42, 2047);
     CLONE(testFunction)(new Uint16Array(sab), 0, 42, 2047);
     CLONE(testFunction)(new Int32Array(sab), 0, 42, 1023);
     CLONE(testFunction)(new Uint32Array(sab), 0, 42, 1023);
 
     // Test various range and type conditions
     var v8 = new Int8Array(sab);
     var v32 = new Int32Array(sab);
@@ -492,16 +503,19 @@ function runTests() {
     CLONE(testRangeCAS)(v32);
 
     // Test extreme values
     testInt8Extremes(new Int8Array(sab));
     testUint8Extremes(new Uint8Array(sab));
     testInt16Extremes(new Int16Array(sab));
     testUint32(new Uint32Array(sab));
 
+    // Test that Uint8ClampedArray is not accepted.
+    testUint8Clamped(sab);
+
     // Misc ad-hoc tests
     adHocExchange();
 
     // Misc
     testIsLockFree();
 }
 
 if (this.Atomics && this.SharedArrayBuffer)
--- a/js/src/jit/ExecutableAllocator.h
+++ b/js/src/jit/ExecutableAllocator.h
@@ -403,17 +403,33 @@ class ExecutableAllocator
 #elif defined(JS_SIMULATOR_ARM) || defined(JS_SIMULATOR_MIPS32) || defined(JS_SIMULATOR_MIPS64)
     static void cacheFlush(void* code, size_t size)
     {
         js::jit::Simulator::FlushICache(code, size);
     }
 #elif defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     static void cacheFlush(void* code, size_t size)
     {
-#if defined(__GNUC__)
+#if defined(_MIPS_ARCH_LOONGSON3A)
+        // On Loongson3-CPUs, The cache flushed automatically
+        // by hardware. Just need to execute an instruction hazard.
+        uintptr_t tmp;
+        asm volatile (
+            ".set   push \n"
+            ".set   noreorder \n"
+            "move   %[tmp], $ra \n"
+            "bal    1f \n"
+            "daddiu $ra, 8 \n"
+            "1: \n"
+            "jr.hb  $ra \n"
+            "move   $ra, %[tmp] \n"
+            ".set   pop\n"
+            :[tmp]"=&r"(tmp)
+        );
+#elif defined(__GNUC__)
         intptr_t end = reinterpret_cast<intptr_t>(code) + size;
         __builtin___clear_cache(reinterpret_cast<char*>(code), reinterpret_cast<char*>(end));
 #else
         _flush_cache(reinterpret_cast<char*>(code), size, BCACHE);
 #endif
     }
 #elif defined(JS_CODEGEN_ARM) && (defined(__FreeBSD__) || defined(__NetBSD__))
     static void cacheFlush(void* code, size_t size)
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -2970,17 +2970,17 @@ IonBuilder::atomicsMeetsPreconditions(Ca
       case Scalar::Int32:
         return checkResult == DontCheckAtomicResult || getInlineReturnType() == MIRType_Int32;
       case Scalar::Uint32:
         // Bug 1077305: it would be attractive to allow inlining even
         // if the inline return type is Int32, which it will frequently
         // be.
         return checkResult == DontCheckAtomicResult || getInlineReturnType() == MIRType_Double;
       default:
-        // Excludes floating types and Uint8Clamped
+        // Excludes floating types and Uint8Clamped.
         return false;
     }
 }
 
 void
 IonBuilder::atomicsCheckBounds(CallInfo& callInfo, MInstruction** elements, MDefinition** index)
 {
     // Perform bounds checking and extract the elements vector.
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -13190,18 +13190,17 @@ class MCompareExchangeTypedArrayElement
     static MCompareExchangeTypedArrayElement* New(TempAllocator& alloc, MDefinition* elements,
                                                   MDefinition* index, Scalar::Type arrayType,
                                                   MDefinition* oldval, MDefinition* newval)
     {
         return new(alloc) MCompareExchangeTypedArrayElement(elements, index, arrayType, oldval, newval);
     }
     bool isByteArray() const {
         return (arrayType_ == Scalar::Int8 ||
-                arrayType_ == Scalar::Uint8 ||
-                arrayType_ == Scalar::Uint8Clamped);
+                arrayType_ == Scalar::Uint8);
     }
     MDefinition* elements() {
         return getOperand(0);
     }
     MDefinition* index() {
         return getOperand(1);
     }
     MDefinition* oldval() {
@@ -13245,18 +13244,17 @@ class MAtomicExchangeTypedArrayElement
                                                  MDefinition* index, MDefinition* value,
                                                  Scalar::Type arrayType)
     {
         return new(alloc) MAtomicExchangeTypedArrayElement(elements, index, value, arrayType);
     }
 
     bool isByteArray() const {
         return (arrayType_ == Scalar::Int8 ||
-                arrayType_ == Scalar::Uint8 ||
-                arrayType_ == Scalar::Uint8Clamped);
+                arrayType_ == Scalar::Uint8);
     }
     MDefinition* elements() {
         return getOperand(0);
     }
     MDefinition* index() {
         return getOperand(1);
     }
     MDefinition* value() {
@@ -13297,18 +13295,17 @@ class MAtomicTypedArrayElementBinop
                                               MDefinition* elements, MDefinition* index,
                                               Scalar::Type arrayType, MDefinition* value)
     {
         return new(alloc) MAtomicTypedArrayElementBinop(op, elements, index, arrayType, value);
     }
 
     bool isByteArray() const {
         return (arrayType_ == Scalar::Int8 ||
-                arrayType_ == Scalar::Uint8 ||
-                arrayType_ == Scalar::Uint8Clamped);
+                arrayType_ == Scalar::Uint8);
     }
     AtomicOp operation() const {
         return op_;
     }
     Scalar::Type arrayType() const {
         return arrayType_;
     }
     MDefinition* elements() {
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -1737,17 +1737,16 @@ template<typename S, typename T>
 void
 CodeGeneratorARM::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType,
                                              const S& value, const T& mem, Register flagTemp,
                                              Register outTemp, AnyRegister output)
 {
     MOZ_ASSERT(flagTemp != InvalidReg);
     MOZ_ASSERT_IF(arrayType == Scalar::Uint32, outTemp != InvalidReg);
 
-    // Uint8Clamped is explicitly not supported here
     switch (arrayType) {
       case Scalar::Int8:
         switch (op) {
           case AtomicFetchAddOp:
             masm.atomicFetchAdd8SignExtend(value, mem, flagTemp, output.gpr());
             break;
           case AtomicFetchSubOp:
             masm.atomicFetchSub8SignExtend(value, mem, flagTemp, output.gpr());
@@ -1903,17 +1902,16 @@ CodeGeneratorARM::atomicBinopToTypedIntA
 // Binary operation for effect, result discarded.
 template<typename S, typename T>
 void
 CodeGeneratorARM::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
                                              const T& mem, Register flagTemp)
 {
     MOZ_ASSERT(flagTemp != InvalidReg);
 
-    // Uint8Clamped is explicitly not supported here
     switch (arrayType) {
       case Scalar::Int8:
       case Scalar::Uint8:
         switch (op) {
           case AtomicFetchAddOp:
             masm.atomicAdd8(value, mem, flagTemp);
             break;
           case AtomicFetchSubOp:
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -4823,19 +4823,16 @@ MacroAssemblerARMCompat::compareExchange
 {
     switch (arrayType) {
       case Scalar::Int8:
         compareExchange8SignExtend(mem, oldval, newval, output.gpr());
         break;
       case Scalar::Uint8:
         compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
         break;
-      case Scalar::Uint8Clamped:
-        compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
-        break;
       case Scalar::Int16:
         compareExchange16SignExtend(mem, oldval, newval, output.gpr());
         break;
       case Scalar::Uint16:
         compareExchange16ZeroExtend(mem, oldval, newval, output.gpr());
         break;
       case Scalar::Int32:
         compareExchange32(mem, oldval, newval, output.gpr());
@@ -4868,19 +4865,16 @@ MacroAssemblerARMCompat::atomicExchangeT
 {
     switch (arrayType) {
       case Scalar::Int8:
         atomicExchange8SignExtend(mem, value, output.gpr());
         break;
       case Scalar::Uint8:
         atomicExchange8ZeroExtend(mem, value, output.gpr());
         break;
-      case Scalar::Uint8Clamped:
-        atomicExchange8ZeroExtend(mem, value, output.gpr());
-        break;
       case Scalar::Int16:
         atomicExchange16SignExtend(mem, value, output.gpr());
         break;
       case Scalar::Uint16:
         atomicExchange16ZeroExtend(mem, value, output.gpr());
         break;
       case Scalar::Int32:
         atomicExchange32(mem, value, output.gpr());
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -268,19 +268,16 @@ MacroAssemblerCompat::compareExchangeToT
 {
     switch (arrayType) {
       case Scalar::Int8:
         compareExchange8SignExtend(mem, oldval, newval, output.gpr());
         break;
       case Scalar::Uint8:
         compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
         break;
-      case Scalar::Uint8Clamped:
-        compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
-        break;
       case Scalar::Int16:
         compareExchange16SignExtend(mem, oldval, newval, output.gpr());
         break;
       case Scalar::Uint16:
         compareExchange16ZeroExtend(mem, oldval, newval, output.gpr());
         break;
       case Scalar::Int32:
         compareExchange32(mem, oldval, newval, output.gpr());
@@ -313,19 +310,16 @@ MacroAssemblerCompat::atomicExchangeToTy
 {
     switch (arrayType) {
       case Scalar::Int8:
         atomicExchange8SignExtend(mem, value, output.gpr());
         break;
       case Scalar::Uint8:
         atomicExchange8ZeroExtend(mem, value, output.gpr());
         break;
-      case Scalar::Uint8Clamped:
-        atomicExchange8ZeroExtend(mem, value, output.gpr());
-        break;
       case Scalar::Int16:
         atomicExchange16SignExtend(mem, value, output.gpr());
         break;
       case Scalar::Uint16:
         atomicExchange16ZeroExtend(mem, value, output.gpr());
         break;
       case Scalar::Int32:
         atomicExchange32(mem, value, output.gpr());
--- a/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
+++ b/js/src/jit/mips-shared/CodeGenerator-mips-shared.cpp
@@ -2037,17 +2037,16 @@ CodeGeneratorMIPSShared::atomicBinopToTy
                                                     const S& value, const T& mem, Register flagTemp,
                                                     Register outTemp, Register valueTemp,
                                                     Register offsetTemp, Register maskTemp,
                                                     AnyRegister output)
 {
     MOZ_ASSERT(flagTemp != InvalidReg);
     MOZ_ASSERT_IF(arrayType == Scalar::Uint32, outTemp != InvalidReg);
 
-    // Uint8Clamped is explicitly not supported here
     switch (arrayType) {
       case Scalar::Int8:
         switch (op) {
           case AtomicFetchAddOp:
             masm.atomicFetchAdd8SignExtend(value, mem, flagTemp, valueTemp, offsetTemp, maskTemp, output.gpr());
             break;
           case AtomicFetchSubOp:
             masm.atomicFetchSub8SignExtend(value, mem, flagTemp, valueTemp, offsetTemp, maskTemp, output.gpr());
@@ -2208,17 +2207,16 @@ CodeGeneratorMIPSShared::atomicBinopToTy
 template<typename S, typename T>
 void
 CodeGeneratorMIPSShared::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
                                                     const T& mem, Register flagTemp, Register valueTemp,
                                                     Register offsetTemp, Register maskTemp)
 {
     MOZ_ASSERT(flagTemp != InvalidReg);
 
-    // Uint8Clamped is explicitly not supported here
     switch (arrayType) {
       case Scalar::Int8:
       case Scalar::Uint8:
         switch (op) {
           case AtomicFetchAddOp:
             masm.atomicAdd8(value, mem, flagTemp, valueTemp, offsetTemp, maskTemp);
             break;
           case AtomicFetchSubOp:
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -1146,18 +1146,19 @@ MacroAssembler::callWithPatch()
     addLongJump(nextOffset());
     ma_liPatchable(ScratchRegister, ImmWord(0));
     return call(ScratchRegister);
 }
 
 void
 MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
 {
-    BufferOffset li(callerOffset - 6 * sizeof(uint32_t));
-    Assembler::UpdateLoad64Value(editSrc(li), calleeOffset);
+    BufferOffset li(callerOffset - Assembler::PatchWrite_NearCallSize());
+    Assembler::PatchInstructionImmediate((uint8_t*)editSrc(li),
+                                         PatchedImmPtr((const void*)calleeOffset));
 }
 
 void
 MacroAssembler::call(wasm::SymbolicAddress target)
 {
     movePtr(target, CallReg);
     call(CallReg);
 }
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -2432,19 +2432,16 @@ MacroAssemblerMIPSCompat::compareExchang
 {
     switch (arrayType) {
       case Scalar::Int8:
         compareExchange8SignExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Uint8:
         compareExchange8ZeroExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
-      case Scalar::Uint8Clamped:
-        compareExchange8ZeroExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
-        break;
       case Scalar::Int16:
         compareExchange16SignExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Uint16:
         compareExchange16ZeroExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Int32:
         compareExchange32(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
@@ -2481,19 +2478,16 @@ MacroAssemblerMIPSCompat::atomicExchange
 {
     switch (arrayType) {
       case Scalar::Int8:
         atomicExchange8SignExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Uint8:
         atomicExchange8ZeroExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
-      case Scalar::Uint8Clamped:
-        atomicExchange8ZeroExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
-        break;
       case Scalar::Int16:
         atomicExchange16SignExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Uint16:
         atomicExchange16ZeroExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Int32:
         atomicExchange32(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
--- a/js/src/jit/mips64/Assembler-mips64.cpp
+++ b/js/src/jit/mips64/Assembler-mips64.cpp
@@ -269,31 +269,32 @@ Assembler::bind(InstImm* inst, uintptr_t
         addLongJump(BufferOffset(branch));
         Assembler::WriteLoad64Instructions(inst, ScratchRegister, target);
         inst[4] = InstReg(op_special, ScratchRegister, zero, ra, ff_jalr).encode();
         // There is 1 nop after this.
         return;
     }
 
     if (BOffImm16::IsInRange(offset)) {
+#ifdef _MIPS_ARCH_LOONGSON3A
+        // Don't skip trailing nops can imporve performance
+        // on Loongson3 platform.
+        bool skipNops = false;
+#else
+        bool skipNops = (inst[0].encode() != inst_bgezal.encode() &&
+                         inst[0].encode() != inst_beq.encode());
+#endif
+
         inst[0].setBOffImm16(BOffImm16(offset));
         inst[1].makeNop();
 
-        // Don't skip trailing nops can imporve performance
-        // on Loongson3 platform.
-#ifndef _MIPS_ARCH_LOONGSON3A
-        bool conditional = (inst[0].encode() != inst_bgezal.encode() &&
-                            inst[0].encode() != inst_beq.encode());
-
-        // Skip the trailing nops in conditional branches.
-        if (conditional) {
+        if (skipNops) {
             inst[2] = InstImm(op_regimm, zero, rt_bgez, BOffImm16(5 * sizeof(uint32_t))).encode();
             // There are 4 nops after this
         }
-#endif
         return;
     }
 
     if (inst[0].encode() == inst_beq.encode()) {
         // Handle long unconditional jump.
         addLongJump(BufferOffset(branch));
         Assembler::WriteLoad64Instructions(inst, ScratchRegister, target);
         inst[4] = InstReg(op_special, ScratchRegister, zero, zero, ff_jr).encode();
--- a/js/src/jit/mips64/MacroAssembler-mips64.cpp
+++ b/js/src/jit/mips64/MacroAssembler-mips64.cpp
@@ -2535,19 +2535,16 @@ MacroAssemblerMIPS64Compat::compareExcha
 {
     switch (arrayType) {
       case Scalar::Int8:
         compareExchange8SignExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Uint8:
         compareExchange8ZeroExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
-      case Scalar::Uint8Clamped:
-        compareExchange8ZeroExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
-        break;
       case Scalar::Int16:
         compareExchange16SignExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Uint16:
         compareExchange16ZeroExtend(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Int32:
         compareExchange32(mem, oldval, newval, valueTemp, offsetTemp, maskTemp, output.gpr());
@@ -2584,19 +2581,16 @@ MacroAssemblerMIPS64Compat::atomicExchan
 {
     switch (arrayType) {
       case Scalar::Int8:
         atomicExchange8SignExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Uint8:
         atomicExchange8ZeroExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
-      case Scalar::Uint8Clamped:
-        atomicExchange8ZeroExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
-        break;
       case Scalar::Int16:
         atomicExchange16SignExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Uint16:
         atomicExchange16ZeroExtend(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
         break;
       case Scalar::Int32:
         atomicExchange32(mem, value, valueTemp, offsetTemp, maskTemp, output.gpr());
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -3361,17 +3361,16 @@ CodeGeneratorX86Shared::visitAtomicExcha
     }
 }
 
 template<typename S, typename T>
 void
 CodeGeneratorX86Shared::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
                                                    const T& mem, Register temp1, Register temp2, AnyRegister output)
 {
-    // Uint8Clamped is explicitly not supported here
     switch (arrayType) {
       case Scalar::Int8:
         switch (op) {
           case AtomicFetchAddOp:
             masm.atomicFetchAdd8SignExtend(value, mem, temp1, output.gpr());
             break;
           case AtomicFetchSubOp:
             masm.atomicFetchSub8SignExtend(value, mem, temp1, output.gpr());
@@ -3521,17 +3520,16 @@ CodeGeneratorX86Shared::atomicBinopToTyp
                                                     Register temp1, Register temp2, AnyRegister output);
 
 // Binary operation for effect, result discarded.
 template<typename S, typename T>
 void
 CodeGeneratorX86Shared::atomicBinopToTypedIntArray(AtomicOp op, Scalar::Type arrayType, const S& value,
                                                     const T& mem)
 {
-    // Uint8Clamped is explicitly not supported here
     switch (arrayType) {
       case Scalar::Int8:
       case Scalar::Uint8:
         switch (op) {
           case AtomicFetchAddOp:
             masm.atomicAdd8(value, mem);
             break;
           case AtomicFetchSubOp:
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
@@ -151,19 +151,16 @@ MacroAssemblerX86Shared::compareExchange
 {
     switch (arrayType) {
       case Scalar::Int8:
         compareExchange8SignExtend(mem, oldval, newval, output.gpr());
         break;
       case Scalar::Uint8:
         compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
         break;
-      case Scalar::Uint8Clamped:
-        compareExchange8ZeroExtend(mem, oldval, newval, output.gpr());
-        break;
       case Scalar::Int16:
         compareExchange16SignExtend(mem, oldval, newval, output.gpr());
         break;
       case Scalar::Uint16:
         compareExchange16ZeroExtend(mem, oldval, newval, output.gpr());
         break;
       case Scalar::Int32:
         compareExchange32(mem, oldval, newval, output.gpr());
@@ -196,19 +193,16 @@ MacroAssemblerX86Shared::atomicExchangeT
 {
     switch (arrayType) {
       case Scalar::Int8:
         atomicExchange8SignExtend(mem, value, output.gpr());
         break;
       case Scalar::Uint8:
         atomicExchange8ZeroExtend(mem, value, output.gpr());
         break;
-      case Scalar::Uint8Clamped:
-        atomicExchange8ZeroExtend(mem, value, output.gpr());
-        break;
       case Scalar::Int16:
         atomicExchange16SignExtend(mem, value, output.gpr());
         break;
       case Scalar::Uint16:
         atomicExchange16ZeroExtend(mem, value, output.gpr());
         break;
       case Scalar::Int32:
         atomicExchange32(mem, value, output.gpr());
--- a/js/src/make-source-package.sh
+++ b/js/src/make-source-package.sh
@@ -8,21 +8,21 @@ set -e
 : ${MAKE:=make}
 : ${MKDIR:=mkdir}
 : ${TAR:=tar}
 : ${SRCDIR:=$(cd $(dirname $0); pwd 2>/dev/null)}
 : ${MOZJS_NAME:=mozjs}
 : ${DIST:=/tmp/mozjs-src-pkg}
 
 if [[ -f "$SRCDIR/../../config/milestone.txt" ]]; then
-    MILESTONE="$(tail -1 $SRCDIR/../../config/milestone.txt)"
-    IFS=. read -a VERSION < <(echo "$MILESTONE")
-    MOZJS_MAJOR_VERSION=${MOZJS_MAJOR_VERSION:-${VERSION[0]}}
-    MOZJS_MINOR_VERSION=${MOZJS_MINOR_VERSION:-${VERSION[1]}}
-    MOZJS_PATCH_VERSION=${MOZJS_PATCH_VERSION:-${VERSION[2]}}
+	MILESTONE="$(tail -1 $SRCDIR/../../config/milestone.txt)"
+	IFS=. read -a VERSION < <(echo "$MILESTONE")
+	MOZJS_MAJOR_VERSION=${MOZJS_MAJOR_VERSION:-${VERSION[0]}}
+	MOZJS_MINOR_VERSION=${MOZJS_MINOR_VERSION:-${VERSION[1]}}
+	MOZJS_PATCH_VERSION=${MOZJS_PATCH_VERSION:-${VERSION[2]}}
 fi
 
 cmd=${1:-build}
 pkg="${MOZJS_NAME}-${MOZJS_MAJOR_VERSION}.${MOZJS_MINOR_VERSION}.${MOZJS_PATCH_VERSION:-${MOZJS_ALPHA:-0}}.tar.bz2"
 pkgpath=${pkg%.tar*}
 tgtpath=${DIST}/${pkgpath}
 taropts="-jcf"
 
@@ -43,97 +43,106 @@ echo ""
 TOPSRCDIR=${SRCDIR}/../..
 
 case $cmd in
 "clean")
 	echo "Cleaning ${pkg} and ${tgtpath} ..."
 	rm -rf ${pkg} ${tgtpath}
 	;;
 "build")
-        echo -n "Press enter to build $pkg> "
-        read
+	echo -n "Press enter to build $pkg> "
+	read
 
-        # Ensure that the configure script is newer than the configure.in script.
-        if [ ${SRCDIR}/configure.in -nt ${SRCDIR}/configure ]; then
-            echo "error: js/src/configure is out of date. Please regenerate before packaging." >&2
-            exit 1
-        fi
+	# Ensure that the configure script is newer than the configure.in script.
+	if [ ${SRCDIR}/configure.in -nt ${SRCDIR}/configure ]; then
+		echo "error: js/src/configure is out of date. Please regenerate before packaging." >&2
+		exit 1
+	fi
 
 	echo "Packaging source tarball ${pkg}..."
 	if [ -d ${tgtpath} ]; then
 		echo "WARNING - dist tree ${tgtpath} already exists!"
 	fi
 	${MKDIR} -p ${tgtpath}/js/src
 
 	# copy the embedded icu
 	${MKDIR} -p ${tgtpath}/intl
-	cp -t ${tgtpath}/intl -dRp ${TOPSRCDIR}/intl/icu
+	cp -pPR ${TOPSRCDIR}/intl/icu ${tgtpath}/intl
 
 	# copy main moz.build and Makefile.in
-	cp -t ${tgtpath} -dRp ${TOPSRCDIR}/Makefile.in ${TOPSRCDIR}/moz.build
+	cp -pPR ${TOPSRCDIR}/Makefile.in ${TOPSRCDIR}/moz.build ${tgtpath}
 
-        # copy nspr.
-        cp -t ${tgtpath} -dRP ${SRCDIR}/../../nsprpub
+	# copy nspr.
+	cp -pPR ${SRCDIR}/../../nsprpub ${tgtpath}
 
 	# copy build and config directory.
-	cp -t ${tgtpath} -dRp ${TOPSRCDIR}/build ${TOPSRCDIR}/config
+	cp -pPR ${TOPSRCDIR}/build ${TOPSRCDIR}/config ${tgtpath}
 
 	# put in js itself
-	cp -t ${tgtpath} -dRp ${TOPSRCDIR}/mfbt
-	cp -t ${tgtpath}/js -dRp ${SRCDIR}/../public
+	cp -pPR ${TOPSRCDIR}/mfbt ${tgtpath}
+	cp -pPR ${SRCDIR}/../public ${tgtpath}/js
 	find ${SRCDIR} -mindepth 1 -maxdepth 1 -not -path ${DIST} -a -not -name ${pkg} \
-		-exec cp -t ${tgtpath}/js/src -dRp {} +
+		-exec cp -pPR {} ${tgtpath}/js/src \;
 
 	# distclean if necessary
 	if [ -e ${tgtpath}/js/src/Makefile ]; then
 		${MAKE} -C ${tgtpath}/js/src distclean
 	fi
 
-	cp -t ${tgtpath} -dRp \
-		${TOPSRCDIR}/python
+	cp -pPR \
+		${TOPSRCDIR}/python \
+		${tgtpath}
 	${MKDIR} -p ${tgtpath}/dom/bindings
-	cp -t ${tgtpath}/dom/bindings -dRp \
-		${TOPSRCDIR}/dom/bindings/mozwebidlcodegen
+	cp -pPR \
+		${TOPSRCDIR}/dom/bindings/mozwebidlcodegen \
+		${tgtpath}/dom/bindings
 	${MKDIR} -p ${tgtpath}/media/webrtc/trunk/tools
-	cp -t ${tgtpath}/media/webrtc/trunk/tools -dRp \
-		${TOPSRCDIR}/media/webrtc/trunk/tools/gyp
+	cp -pPR \
+		${TOPSRCDIR}/media/webrtc/trunk/tools/gyp \
+		${tgtpath}/media/webrtc/trunk/tools
 	${MKDIR} -p ${tgtpath}/testing
-	cp -t ${tgtpath}/testing -dRp \
-		${TOPSRCDIR}/testing/mozbase
+	cp -pPR \
+		${TOPSRCDIR}/testing/mozbase \
+		${tgtpath}/testing
 	${MKDIR} -p ${tgtpath}/modules
-	cp -t ${tgtpath}/modules -dRp \
-		${TOPSRCDIR}/modules/zlib/src
+	cp -pPR \
+		${TOPSRCDIR}/modules/zlib/src \
+		${tgtpath}/modules
 	${MKDIR} -p ${tgtpath}/layout/tools/reftest
-	cp -t ${tgtpath}/layout/tools/reftest -dRp \
-	        ${TOPSRCDIR}/layout/tools/reftest/reftest
+	cp -pPR \
+		${TOPSRCDIR}/layout/tools/reftest/reftest \
+		${tgtpath}/layout/tools/reftest
 	${MKDIR} -p ${tgtpath}/toolkit/mozapps/installer
-	cp -t ${tgtpath}/toolkit/mozapps/installer -dRp \
-	        ${TOPSRCDIR}/toolkit/mozapps/installer/package-name.mk \
-	        ${TOPSRCDIR}/toolkit/mozapps/installer/upload-files.mk
+	cp -pPR \
+		${TOPSRCDIR}/toolkit/mozapps/installer/package-name.mk \
+		${TOPSRCDIR}/toolkit/mozapps/installer/upload-files.mk \
+		${tgtpath}/toolkit/mozapps/installer
 	${MKDIR} -p ${tgtpath}/mozglue
-	cp -t ${tgtpath}/mozglue -dRp \
-	        ${TOPSRCDIR}/mozglue/build \
-	        ${TOPSRCDIR}/mozglue/crt \
-	        ${TOPSRCDIR}/mozglue/misc \
-	        ${TOPSRCDIR}/mozglue/moz.build
-        ${MKDIR} -p ${tgtpath}/memory
-        cp -t ${tgtpath}/memory -dRp \
-                ${TOPSRCDIR}/memory/moz.build \
-                ${TOPSRCDIR}/memory/build \
-                ${TOPSRCDIR}/memory/fallible \
-                ${TOPSRCDIR}/memory/jemalloc \
-                ${TOPSRCDIR}/memory/mozalloc \
-                ${TOPSRCDIR}/memory/mozjemalloc
+	cp -pPR \
+		${TOPSRCDIR}/mozglue/build \
+		${TOPSRCDIR}/mozglue/crt \
+		${TOPSRCDIR}/mozglue/misc \
+		${TOPSRCDIR}/mozglue/moz.build \
+		${tgtpath}/mozglue
+	${MKDIR} -p ${tgtpath}/memory
+	cp -pPR \
+		${TOPSRCDIR}/memory/moz.build \
+		${TOPSRCDIR}/memory/build \
+		${TOPSRCDIR}/memory/fallible \
+		${TOPSRCDIR}/memory/jemalloc \
+		${TOPSRCDIR}/memory/mozalloc \
+		${TOPSRCDIR}/memory/mozjemalloc \
+		${tgtpath}/memory
 
 	# remove *.pyc and *.pyo files if any
 	find ${tgtpath} -type f -name "*.pyc" -o -name "*.pyo" |xargs rm -f
 
 	# copy or create INSTALL
 	if [ -e ${DIST}/INSTALL ]; then
-		cp -t ${tgtpath} ${DIST}/INSTALL
+		cp ${DIST}/INSTALL ${tgtpath}
 	else
 		cat <<INSTALL_EOF >${tgtpath}/INSTALL
 Full build documentation for SpiderMonkey is hosted on MDN:
   https://developer.mozilla.org/en-US/docs/SpiderMonkey/Build_Documentation
 
 Note that the libraries produced by the build system include symbols,
 causing the binaries to be extremely large. It is highly suggested that \`strip\`
 be run over the binaries before deploying them.
@@ -144,17 +153,17 @@ Building with default options may be per
   cd obj
   ../configure
   make # or mozmake on Windows
 INSTALL_EOF
 	fi
 
 	# copy or create README
 	if [ -e ${DIST}/README ]; then
-		cp -t ${tgtpath} ${DIST}/README
+		cp ${DIST}/README ${tgtpath}
 	else
 		cat <<README_EOF >${tgtpath}/README
 This directory contains SpiderMonkey ${MOZJS_MAJOR_VERSION}.
 
 This release is based on a revision of Mozilla ${MOZJS_MAJOR_VERSION}:
   http://hg.mozilla.org/releases/
 The changes in the patches/ directory were applied.
 
@@ -167,19 +176,19 @@ README_EOF
 	if [ -e ${TOPSRCDIR}/b2g/LICENSE ]; then
 		cp ${TOPSRCDIR}/b2g/LICENSE ${tgtpath}/
 	else
 		cp ${TOPSRCDIR}/LICENSE ${tgtpath}/
 	fi
 
 	# copy patches dir, if it currently exists in DIST
 	if [ -d ${DIST}/patches ]; then
-		cp -t ${tgtpath} -dRp ${DIST}/patches
+		cp -pPR ${DIST}/patches ${tgtpath}
 	elif [ -d ${TOPSRCDIR}/patches ]; then
-		cp -t ${tgtpath} -dRp ${TOPSRCDIR}/patches
+		cp -pPR ${TOPSRCDIR}/patches ${tgtpath}
 	fi
 
 	# Roll the tarball
 	${TAR} $taropts ${DIST}/../${pkg} -C ${DIST} ${pkgpath}
 	echo "Wrote $(cd ${DIST}/..; echo $PWD)/${pkg}"
 	;;
 *)
 	echo "Unrecognized command: $cmd"
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -662,16 +662,17 @@ if CONFIG['OS_ARCH'] == 'SunOS':
         'posix4',
         'dl',
         'nsl',
         'socket',
     ]
 
 CFLAGS += CONFIG['MOZ_ICU_CFLAGS']
 CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
 
 if not CONFIG['GNU_CXX']:
     ALLOW_COMPILER_WARNINGS = True
 
 NO_EXPAND_LIBS = True
 
 DIST_INSTALL = True
 
--- a/js/src/tests/js1_8_5/extensions/sharedtypedarray.js
+++ b/js/src/tests/js1_8_5/extensions/sharedtypedarray.js
@@ -186,15 +186,49 @@ function testClone2() {
     var ia2 = deserialize(blob);
     assertEq(ia1.length, ia2.length);
     assertEq(ia1.buffer instanceof SharedArrayBuffer, true);
     assertEq(sharedAddress(ia1.buffer), sharedAddress(ia2.buffer));
     ia1[10] = 37;
     assertEq(ia2[10], 37);
 }
 
+function testApplicable() {
+    var sab = b;
+    var x;
+
+    // Just make sure we can create all the view types on shared memory.
+
+    x = new Int32Array(sab);
+    assertEq(x.length, sab.byteLength / Int32Array.BYTES_PER_ELEMENT);
+    x = new Uint32Array(sab);
+    assertEq(x.length, sab.byteLength / Uint32Array.BYTES_PER_ELEMENT);
+
+    x = new Int16Array(sab);
+    assertEq(x.length, sab.byteLength / Int16Array.BYTES_PER_ELEMENT);
+    x = new Uint16Array(sab);
+    assertEq(x.length, sab.byteLength / Uint16Array.BYTES_PER_ELEMENT);
+
+    x = new Int8Array(sab);
+    assertEq(x.length, sab.byteLength / Int8Array.BYTES_PER_ELEMENT);
+    x = new Uint8Array(sab);
+    assertEq(x.length, sab.byteLength / Uint8Array.BYTES_PER_ELEMENT);
+
+    // Though the atomic operations are illegal on Uint8ClampedArray and the
+    // float arrays, they can still be used to create views on shared memory.
+
+    x = new Uint8ClampedArray(sab);
+    assertEq(x.length, sab.byteLength / Uint8ClampedArray.BYTES_PER_ELEMENT);
+
+    x = new Float32Array(sab);
+    assertEq(x.length, sab.byteLength / Float32Array.BYTES_PER_ELEMENT);
+    x = new Float64Array(sab);
+    assertEq(x.length, sab.byteLength / Float64Array.BYTES_PER_ELEMENT);
+}
+
 testSharedArrayBuffer();
 testSharedTypedArray();
 testSharedTypedArrayMethods();
 testClone1();
 testClone2();
+testApplicable();
 
 reportCompare(0, 0, 'ok');
--- a/layout/forms/moz.build
+++ b/layout/forms/moz.build
@@ -52,8 +52,9 @@ LOCAL_INCLUDES += [
     '/dom/base',
     '/dom/html',
 ]
 
 if CONFIG['ENABLE_INTL_API']:
     # nsNumberControlFrame.cpp requires ICUUtils.h which in turn requires
     # i18n/unum.h
     CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+    LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -313,17 +313,17 @@ inDOMUtils::GetRelativeRuleLine(nsIDOMCS
 
   Rule* rule = aRule->GetCSSRule();
   if (!rule) {
     return NS_ERROR_FAILURE;
   }
 
   uint32_t lineNumber = rule->GetLineNumber();
   CSSStyleSheet* sheet = rule->GetStyleSheet();
-  if (sheet) {
+  if (sheet && lineNumber != 0) {
     nsINode* owningNode = sheet->GetOwnerNode();
     if (owningNode) {
       nsCOMPtr<nsIStyleSheetLinkingElement> link =
         do_QueryInterface(owningNode);
       if (link) {
         lineNumber -= link->GetLineNumber() - 1;
       }
     }
--- a/layout/inspector/tests/test_getRelativeRuleLine.html
+++ b/layout/inspector/tests/test_getRelativeRuleLine.html
@@ -29,20 +29,22 @@
   let utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
                            .getService(SpecialPowers.Ci.inIDOMUtils);
 
   let tests = [
     { sheetNo: 0, ruleNo: 0, lineNo: 1, columnNo: 1 },
     { sheetNo: 1, ruleNo: 0, lineNo: 2, columnNo: 15 },
     { sheetNo: 1, ruleNo: 1, lineNo: 8, columnNo: 5 },
     { sheetNo: 2, ruleNo: 0, lineNo: 1, columnNo: 1 },
+    { sheetNo: 2, ruleNo: 1, lineNo: 0, columnNo: 1 },
     { sheetNo: 3, ruleNo: 0, lineNo: 5, columnNo: 6 },
   ];
 
   function doTest() {
+    document.styleSheets[2].insertRule("body{}", 1);
     for (let test of tests) {
       let sheet = document.styleSheets[test.sheetNo];
       let rule = sheet.cssRules[test.ruleNo];
       let line = utils.getRelativeRuleLine(rule);
       let column = utils.getRuleColumn(rule);
       info("testing sheet " + test.sheetNo + ", rule " + test.ruleNo);
       is(line, test.lineNo, "line number is correct");
       is(column, test.columnNo, "column number is correct");
--- a/media/gmp-clearkey/0.1/ClearKeySession.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp
@@ -64,16 +64,21 @@ ClearKeySession::Init(uint32_t aCreateSe
   } else if (aInitDataType == "keyids") {
     std::string sessionType;
     ClearKeyUtils::ParseKeyIdsInitData(aInitData, aInitDataSize, mKeyIds, sessionType);
     if (sessionType != ClearKeyUtils::SessionTypeToString(mSessionType)) {
       const char message[] = "Session type specified in keyids init data doesn't match session type.";
       mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message));
       return;
     }
+  } else if (aInitDataType == "webm" && aInitDataSize == 16) {
+    // "webm" initData format is simply the raw bytes of the keyId.
+    vector<uint8_t> keyId;
+    keyId.assign(aInitData, aInitData+aInitDataSize);
+    mKeyIds.push_back(keyId);
   }
 
   if (!mKeyIds.size()) {
     const char message[] = "Couldn't parse init data";
     mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message));
     return;
   }
 
--- a/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySessionManager.cpp
@@ -97,20 +97,23 @@ ClearKeySessionManager::CreateSession(ui
                                       uint32_t aInitDataTypeSize,
                                       const uint8_t* aInitData,
                                       uint32_t aInitDataSize,
                                       GMPSessionType aSessionType)
 {
   CK_LOGD("ClearKeySessionManager::CreateSession type:%s", aInitDataType);
 
   string initDataType(aInitDataType, aInitDataType + aInitDataTypeSize);
-  // initDataType must be "cenc" or "keyids".
-  if (initDataType != "cenc" && initDataType != "keyids") {
+  // initDataType must be "cenc", "keyids", or "webm".
+  if (initDataType != "cenc" &&
+      initDataType != "keyids" &&
+      initDataType != "webm") {
+    string message = "'" + initDataType + "' is an initDataType unsupported by ClearKey";
     mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
-                             nullptr /* message */, 0 /* messageLen */);
+                             message.c_str(), message.size());
     return;
   }
 
   if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this,
                                                         aCreateSessionToken,
                                                         aPromiseId,
                                                         initDataType,
                                                         aInitData,
--- a/media/webrtc/trunk/webrtc/modules/video_capture/mac/qtkit/video_capture_qtkit.mm
+++ b/media/webrtc/trunk/webrtc/modules/video_capture/mac/qtkit/video_capture_qtkit.mm
@@ -56,18 +56,22 @@ VideoCaptureMacQTKit::~VideoCaptureMacQT
 {
 
     nsAutoreleasePool localPool;
     WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCapture, _id,
                  "~VideoCaptureMacQTKit() called");
     if(_captureDevice)
     {
         [_captureDevice registerOwner:nil];
-        [_captureDevice stopCapture];
-        [_captureDevice release];
+        [_captureDevice performSelectorOnMainThread:@selector(stopCapture)
+                        withObject:nil
+                        waitUntilDone:NO];
+        [_captureDevice performSelectorOnMainThread:@selector(release)
+                        withObject:nil
+                        waitUntilDone:NO];
     }
 
     if(_captureInfo)
     {
         [_captureInfo release];
     }
 }
 
--- a/memory/build/jemalloc_config.cpp
+++ b/memory/build/jemalloc_config.cpp
@@ -6,18 +6,22 @@
 
 #define MOZ_JEMALLOC_IMPL
 
 /* mozmemory_wrap.h needs to be included before MFBT headers */
 #include "mozmemory_wrap.h"
 #include <mozilla/Assertions.h>
 #include "mozilla/Types.h"
 
+#if defined(MOZ_NATIVE_JEMALLOC)
+#include MALLOC_H
+#else
 #define DLLEXPORT
 #include "jemalloc/jemalloc.h"
+#endif
 
 #ifdef XP_WIN
 #include <windows.h>
 #endif
 #ifdef XP_DARWIN
 #include <sys/mman.h>
 #endif
 
--- a/memory/build/mozjemalloc_compat.c
+++ b/memory/build/mozjemalloc_compat.c
@@ -10,26 +10,17 @@
 
 #include "mozmemory_wrap.h"
 #include "jemalloc_types.h"
 #include "mozilla/Types.h"
 
 #include <stdbool.h>
 
 #if defined(MOZ_NATIVE_JEMALLOC)
-
-MOZ_IMPORT_API int
-je_(mallctl)(const char*, void*, size_t*, void*, size_t);
-MOZ_IMPORT_API int
-je_(mallctlnametomib)(const char *name, size_t *mibp, size_t *miblenp);
-MOZ_IMPORT_API int
-je_(mallctlbymib)(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp, void *newp, size_t newlen);
-MOZ_IMPORT_API int
-je_(nallocx)(size_t size, int flags);
-
+#  include MALLOC_H
 #else
 #  include "jemalloc/jemalloc.h"
 #endif
 
 /*
  *  CTL_* macros are from memory/jemalloc/src/src/stats.c with changes:
  *  - drop `t' argument to avoid redundancy in calculating type size
  *  - require `i' argument for arena number explicitly
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -893,17 +893,21 @@ pref("reader.toolbar.vertical", false);
 // Whether or not to display buttons related to reading list in reader view.
 pref("browser.readinglist.enabled", true);
 
 // Telemetry settings.
 // Whether to use the unified telemetry behavior, requires a restart.
 pref("toolkit.telemetry.unified", false);
 
 // Unified AccessibleCarets (touch-caret and selection-carets).
+#ifdef NIGHTLY_BUILD
+pref("layout.accessiblecaret.enabled", true);
+#else
 pref("layout.accessiblecaret.enabled", false);
+#endif
 // Android generates long tap (mouse) events.
 pref("layout.accessiblecaret.use_long_tap_injector", false);
 
 // Android tries to maintain extended visibility of the AccessibleCarets
 // during Selection change notifications generated by Javascript,
 // or misc internal events.
 pref("layout.accessiblecaret.extendedvisibility", true);
 
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -26,16 +26,17 @@ import org.mozilla.gecko.util.GeckoEvent
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.OnAccountsUpdateListener;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
+import android.database.sqlite.SQLiteException;
 import android.graphics.Color;
 import android.net.Uri;
 import android.os.Handler;
 import android.provider.Browser;
 import android.util.Log;
 import android.content.SharedPreferences;
 
 public class Tabs implements GeckoEventListener {
@@ -84,17 +85,21 @@ public class Tabs implements GeckoEventL
         public PersistTabsRunnable(final Context context, Iterable<Tab> tabsInOrder) {
             this.context = context;
             this.db = GeckoProfile.get(context).getDB();
             this.tabs = tabsInOrder;
         }
 
         @Override
         public void run() {
-            db.getTabsAccessor().persistLocalTabs(context.getContentResolver(), tabs);
+            try {
+                db.getTabsAccessor().persistLocalTabs(context.getContentResolver(), tabs);
+            } catch(SQLiteException e) {
+                Log.w(LOGTAG, "Error persisting local tabs", e);
+            }
         }
     };
 
     private Tabs() {
         EventDispatcher.getInstance().registerGeckoThreadListener(this,
             "Tab:Added",
             "Tab:Close",
             "Tab:Select",
--- a/mobile/android/base/preferences/SetHomepagePreference.java
+++ b/mobile/android/base/preferences/SetHomepagePreference.java
@@ -31,20 +31,16 @@ public class SetHomepagePreference exten
     private EditText homepageEditText;
 
     // This is the url that 1) was loaded from prefs or, 2) stored
     // when the user pressed the "default homepage" checkbox.
     private String storedUrl;
 
     public SetHomepagePreference(final Context context, final AttributeSet attrs) {
         super(context, attrs);
-
-        setPersistent(false);
-        setDialogLayoutResource(R.layout.preference_set_homepage);
-
         prefs = GeckoSharedPrefs.forProfile(context);
     }
 
     @Override
     protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
         // Without this GB devices have a black background to the dialog.
         builder.setInverseBackgroundForced(true);
     }
deleted file mode 100644
index 6c3bda221e198bc1e35d9c2393bb2728bd02cca6..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 0076357d7b5c4762ee085197675f4eafd6e69928..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/mobile/android/base/resources/values-v11/themes.xml
+++ b/mobile/android/base/resources/values-v11/themes.xml
@@ -36,17 +36,16 @@
         GeckoAppBase from res/values/themes.xml on API 11+ devices.
     -->
     <style name="GeckoAppBase" parent="Gecko">
         <item name="android:actionButtonStyle">@style/GeckoActionBar.Button</item>
         <item name="android:actionModeCopyDrawable">@drawable/ab_copy</item>
         <item name="android:actionModeCutDrawable">@drawable/ab_cut</item>
         <item name="android:actionModePasteDrawable">@drawable/ab_paste</item>
         <item name="android:listViewStyle">@style/Widget.ListView</item>
-        <item name="android:panelBackground">@drawable/menu_panel_bg</item>
         <item name="android:spinnerDropDownItemStyle">@style/Widget.DropDownItem.Spinner</item>
         <item name="android:spinnerItemStyle">@style/Widget.TextView.SpinnerItem</item>
         <item name="menuItemSwitcherLayoutStyle">@style/Widget.MenuItemSwitcherLayout</item>
         <item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
         <item name="menuItemSecondaryActionBarStyle">@style/Widget.MenuItemSecondaryActionBar</item>
         <item name="tabGridLayoutViewStyle">@style/Widget.TabsGridLayout</item>
     </style>
 
--- a/mobile/android/base/resources/values-v14/themes.xml
+++ b/mobile/android/base/resources/values-v14/themes.xml
@@ -12,17 +12,16 @@
     <style name="GeckoAppBase" parent="Gecko">
         <item name="android:actionButtonStyle">@style/GeckoActionBar.Button</item>
         <item name="android:actionModeCopyDrawable">@drawable/ab_copy</item>
         <item name="android:actionModeCutDrawable">@drawable/ab_cut</item>
         <item name="android:actionModePasteDrawable">@drawable/ab_paste</item>
         <item name="android:actionModeSelectAllDrawable">@drawable/ab_select_all</item>
         <item name="android:actionModeStyle">@style/GeckoActionBar</item>
         <item name="android:listViewStyle">@style/Widget.ListView</item>
-        <item name="android:panelBackground">@drawable/menu_panel_bg</item>
         <item name="android:spinnerDropDownItemStyle">@style/Widget.DropDownItem.Spinner</item>
         <item name="android:spinnerItemStyle">@style/Widget.TextView.SpinnerItem</item>
         <item name="menuItemSwitcherLayoutStyle">@style/Widget.MenuItemSwitcherLayout</item>
         <item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
         <item name="menuItemSecondaryActionBarStyle">@style/Widget.MenuItemSecondaryActionBar</item>
     </style>
 
 </resources>
--- a/mobile/android/base/resources/values-v21/themes.xml
+++ b/mobile/android/base/resources/values-v21/themes.xml
@@ -32,17 +32,16 @@
 
     <style name="ActionBar.GeckoPreferences" parent="@android:style/Widget.Material.ActionBar.Solid">
         <item name="android:titleTextStyle">@style/ActionBarTitleTextStyle</item>
     </style>
 
     <style name="GeckoAppBase" parent="Gecko">
         <item name="android:actionButtonStyle">@style/GeckoActionBar.Button</item>
         <item name="android:listViewStyle">@style/Widget.ListView</item>
-        <item name="android:panelBackground">@drawable/menu_panel_bg</item>
         <item name="android:spinnerDropDownItemStyle">@style/Widget.DropDownItem.Spinner</item>
         <item name="android:spinnerItemStyle">@style/Widget.TextView.SpinnerItem</item>
         <item name="menuItemSwitcherLayoutStyle">@style/Widget.MenuItemSwitcherLayout</item>
         <item name="menuItemDefaultStyle">@style/Widget.MenuItemDefault</item>
         <item name="menuItemSecondaryActionBarStyle">@style/Widget.MenuItemSecondaryActionBar</item>
         <item name="tabGridLayoutViewStyle">@style/Widget.TabsGridLayout</item>
     </style>
 
--- a/mobile/android/base/resources/values/themes.xml
+++ b/mobile/android/base/resources/values/themes.xml
@@ -7,23 +7,16 @@
 
     <!--
         Base application theme. This could be overridden by GeckoBaseTheme
         in other res/values-XXX/themes.xml.
     -->
     <style name="GeckoBase" parent="Theme.AppCompat.Light.DarkActionBar">
         <item name="windowNoTitle">true</item>
         <item name="android:windowContentOverlay">@null</item>
-
-        <!-- Gingerbread "More" menu background color; AppCompat sets this to
-             transparent by default: http://stackoverflow.com/a/31777488.
-             This color is set anecdotally to make all text colors, which change
-             by device, visible. Standard colors are #8D8D8D & #C8C8C8 (disabled)
-             and #000000 has also been seen. -->
-        <item name="android:panelBackground">@color/text_and_tabs_tray_grey</item>
     </style>
 
     <style name="GeckoDialogBase" parent="@android:style/Theme.Dialog">
         <item name="android:windowNoTitle">true</item>
         <item name="android:windowContentOverlay">@null</item>
     </style>
 
     <style name="GeckoTitleDialogBase" parent="@android:style/Theme.Dialog" />
--- a/mobile/android/base/resources/xml/preferences_home.xml
+++ b/mobile/android/base/resources/xml/preferences_home.xml
@@ -7,17 +7,19 @@
                   android:title="@string/pref_category_home"
                   android:enabled="false">
 
     <PreferenceCategory android:title="@string/pref_category_home_homepage"
                         android:key="android.not_a_preference.category_homepage">
 
         <org.mozilla.gecko.preferences.SetHomepagePreference
                 android:key="android.not_a_preference.homepage"
-                android:title="@string/home_homepage_title" />
+                android:title="@string/home_homepage_title"
+                android:persistent="false"
+                android:dialogLayout="@layout/preference_set_homepage"/>
 
     </PreferenceCategory>
 
     <org.mozilla.gecko.preferences.PanelsPreferenceCategory
         android:title="@string/pref_category_home_panels"/>
 
     <PreferenceCategory android:title="@string/pref_category_home_content_settings">
 
--- a/mobile/android/tests/browser/robocop/robocop.ini
+++ b/mobile/android/tests/browser/robocop/robocop.ini
@@ -139,24 +139,11 @@ skip-if = android_version == "10" || and
 [src/org/mozilla/gecko/tests/testReaderModeTitle.java]
 [src/org/mozilla/gecko/tests/testSessionHistory.java]
 # disabled on Android 4.3, bug 1144879
 skip-if = android_version == "18"
 [src/org/mozilla/gecko/tests/testStateWhileLoading.java]
 
 [src/org/mozilla/gecko/tests/testAccessibleCarets.java]
 
-# testSelectionHandler disabled on Android 2.3 by trailing skip-if, due to bug 980074
-# also disabled on Android 4.3, bug 1144882
-[src/org/mozilla/gecko/tests/testSelectionHandler.java]
-skip-if = android_version == "10" || android_version == "18"
-
-# testInputSelections disabled on Android 2.3 by trailing skip-if, due to bug 980074
-[src/org/mozilla/gecko/tests/testInputSelections.java]
-skip-if = android_version == "10"
-
-# testTextareaSelections disabled on Android 2.3 by trailing skip-if, due to bug 980074
-[src/org/mozilla/gecko/tests/testTextareaSelections.java]
-skip-if = android_version == "10"
-
 # testStumblerSetting disabled on Android 4.3, bug 1145846
 [src/org/mozilla/gecko/tests/testStumblerSetting.java]
 skip-if = android_version == "10" || android_version == "18"
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/BaseTest.java
@@ -527,32 +527,42 @@ abstract class BaseTest extends BaseRobo
                     button.performClick();
                 }
             });
        } catch (Throwable throwable) {
            mAsserter.ok(false, "Unable to click the button","Was unable to click button ");
        }
     }
 
+    private void waitForAnimationsToFinish() {
+        // Ideally we'd actually wait for animations to finish but since we have
+        // no good way of doing that, we just wait an arbitrary unit of time.
+        mSolo.sleep(3500);
+    }
+
     public void addTab() {
         mSolo.clickOnView(mSolo.getView(R.id.tabs));
+        waitForAnimationsToFinish();
+
         // wait for addTab to appear (this is usually immediate)
         boolean success = waitForCondition(new Condition() {
             @Override
             public boolean isSatisfied() {
                 View addTabView = mSolo.getView(R.id.add_tab);
                 if (addTabView == null) {
                     return false;
                 }
                 return true;
             }
         }, MAX_WAIT_MS);
         mAsserter.ok(success, "waiting for add tab view", "add tab view available");
         final Actions.RepeatedEventExpecter pageShowExpecter = mActions.expectGeckoEvent("Content:PageShow");
         mSolo.clickOnView(mSolo.getView(R.id.add_tab));
+        waitForAnimationsToFinish();
+
         // Wait until we get a PageShow event for a new tab ID
         for(;;) {
             try {
                 JSONObject data = new JSONObject(pageShowExpecter.blockForEventData());
                 int tabID = data.getInt("tabID");
                 if (tabID == 0) {
                     mAsserter.dumpLog("addTab ignoring PageShow for tab 0");
                     continue;
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testGeckoProfile.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testGeckoProfile.java
@@ -246,16 +246,18 @@ public class testGeckoProfile extends Pi
             mAsserter.is(name, iniName, "Section with name found");
             mAsserter.is(dir.getName(), iniPath, "Section has correct path");
         }
 
         mAsserter.is(found, shouldFind, "Found profile where expected");
     }
 
     // Tries to remove a profile from Gecko profile. Verifies that it's removed from profiles.ini and its directory is deleted.
+    // TODO: Reconsider profile removal. Firefox would not normally remove a
+    // profile. Outstanding tasks may still try to access files in the profile.
     private void removeProfile(GeckoProfile profile, boolean inProfilesIni) {
         findInProfilesIni(profile, inProfilesIni);
         File dir = profile.getDir();
         mAsserter.ok(dir.exists(), "Profile dir exists before removing", dir.toString());
         mAsserter.is(inProfilesIni, GeckoProfile.removeProfile(getActivity(), profile.getName()), "Remove was successful");
         mAsserter.ok(!dir.exists(), "Profile dir was deleted when it was removed", dir.toString());
         findInProfilesIni(profile, false);
     }
--- a/netwerk/base/LoadInfo.cpp
+++ b/netwerk/base/LoadInfo.cpp
@@ -128,16 +128,17 @@ LoadInfo::LoadInfo(nsIPrincipal* aLoadin
                    bool aIsThirdPartyContext,
                    const NeckoOriginAttributes& aOriginAttributes,
                    nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChainIncludingInternalRedirects,
                    nsTArray<nsCOMPtr<nsIPrincipal>>& aRedirectChain)
   : mLoadingPrincipal(aLoadingPrincipal)
   , mTriggeringPrincipal(aTriggeringPrincipal)
   , mSecurityFlags(aSecurityFlags)
   , mInternalContentPolicyType(aContentPolicyType)
+  , mTainting(LoadTainting::Basic)
   , mUpgradeInsecureRequests(aUpgradeInsecureRequests)
   , mUpgradeInsecurePreloads(aUpgradeInsecurePreloads)
   , mInnerWindowID(aInnerWindowID)
   , mOuterWindowID(aOuterWindowID)
   , mParentOuterWindowID(aParentOuterWindowID)
   , mEnforceSecurity(aEnforceSecurity)
   , mInitialSecurityCheckDone(aInitialSecurityCheckDone)
   , mIsThirdPartyContext(aIsThirdPartyContext)
--- a/netwerk/dns/moz.build
+++ b/netwerk/dns/moz.build
@@ -68,13 +68,14 @@ LOCAL_INCLUDES += [
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' and CONFIG['ANDROID_VERSION'] > '19':
     LOCAL_INCLUDES += ['%' + '%s/bionic/libc/dns/include' % CONFIG['ANDROID_SOURCE']]
 
 if CONFIG['ENABLE_INTL_API']:
     DEFINES['IDNA2008'] = True
     CXXFLAGS += CONFIG['MOZ_ICU_CFLAGS']
     CFLAGS += CONFIG['MOZ_ICU_CFLAGS']
+    LOCAL_INCLUDES += CONFIG['MOZ_ICU_INCLUDES']
     USE_LIBS += ['icu']
 else:
     UNIFIED_SOURCES += [
         'nameprep.c',
     ]
--- a/parser/htmlparser/nsExpatDriver.cpp
+++ b/parser/htmlparser/nsExpatDriver.cpp
@@ -24,16 +24,17 @@
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
 #include "nsError.h"
 #include "nsXPCOMCIDInternal.h"
 #include "nsUnicharInputStream.h"
 #include "nsContentUtils.h"
+#include "nsNullPrincipal.h"
 
 #include "mozilla/Logging.h"
 
 using mozilla::LogLevel;
 
 #define kExpatSeparatorChar 0xFFFF
 
 static const char16_t kUTF16[] = { 'U', 'T', 'F', '-', '1', '6', '\0' };
@@ -784,24 +785,31 @@ nsExpatDriver::OpenInputStreamFromExtern
                        nsContentUtils::GetSystemPrincipal(),
                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                        nsIContentPolicy::TYPE_DTD);
   }
   else {
     NS_ASSERTION(mSink == nsCOMPtr<nsIExpatSink>(do_QueryInterface(mOriginalSink)),
                  "In nsExpatDriver::OpenInputStreamFromExternalDTD: "
                  "mOriginalSink not the same object as mSink?");
-    nsCOMPtr<nsIDocument> doc;
+    nsCOMPtr<nsIPrincipal> loadingPrincipal;
     if (mOriginalSink) {
+      nsCOMPtr<nsIDocument> doc;
       doc = do_QueryInterface(mOriginalSink->GetTarget());
+      if (doc) {
+        loadingPrincipal = doc->NodePrincipal();
+      }
     }
-    NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
+    if (!loadingPrincipal) {
+      loadingPrincipal = nsNullPrincipal::Create();
+      NS_ENSURE_TRUE(loadingPrincipal, NS_ERROR_FAILURE);
+    }
     rv = NS_NewChannel(getter_AddRefs(channel),
                        uri,
-                       doc,
+                       loadingPrincipal,
                        nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS |
                        nsILoadInfo::SEC_ALLOW_CHROME,
                        nsIContentPolicy::TYPE_DTD);
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsAutoCString absURL;
   uri->GetSpec(absURL);
--- a/python/compare-locales/mach_commands.py
+++ b/python/compare-locales/mach_commands.py
@@ -9,17 +9,17 @@ from mach.decorators import (
     CommandProvider,
     Command,
 )
 
 from mozbuild.base import (
     MachCommandBase,
 )
 
-import mozpack
+import mozpack.path as mozpath
 
 
 MERGE_HELP = '''Directory to merge to. Will be removed to before running
 the comparison. Default: $(OBJDIR)/($MOZ_BUILD_APP)/locales/merge-$(AB_CD)
 '''.lstrip()
 
 
 @CommandProvider
--- a/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.cpp
+++ b/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.cpp
@@ -56,22 +56,22 @@ LogBlocked(const char* aFunctionName, co
     // Skip an extra frame to allow for this function.
     LogBlocked(aFunctionName, base::WideToUTF8(aContext).c_str(),
                /* aFramesToSkip */ 3);
   }
 }
 
 void
 LogBlocked(const char* aFunctionName, const wchar_t* aContext,
-           uint16_t aLength)
+           uint16_t aLengthInBytes)
 {
   if (sLogFunction) {
     // Skip an extra frame to allow for this function.
     LogBlocked(aFunctionName,
-               base::WideToUTF8(std::wstring(aContext, aLength)).c_str(),
+               base::WideToUTF8(std::wstring(aContext, aLengthInBytes / sizeof(wchar_t))).c_str(),
                /* aFramesToSkip */ 3);
   }
 }
 
 void
 LogAllowed(const char* aFunctionName, const char* aContext)
 {
   if (sLogFunction) {
@@ -85,18 +85,18 @@ LogAllowed(const char* aFunctionName, co
 {
   if (sLogFunction) {
     LogAllowed(aFunctionName, base::WideToUTF8(aContext).c_str());
   }
 }
 
 void
 LogAllowed(const char* aFunctionName, const wchar_t* aContext,
-           uint16_t aLength)
+           uint16_t aLengthInBytes)
 {
   if (sLogFunction) {
     LogAllowed(aFunctionName,
-               base::WideToUTF8(std::wstring(aContext, aLength)).c_str());
+               base::WideToUTF8(std::wstring(aContext, aLengthInBytes / sizeof(wchar_t))).c_str());
   }
 }
 
 } // sandboxing
 } // mozilla
--- a/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.h
+++ b/security/sandbox/chromium-shim/sandbox/win/sandboxLogging.h
@@ -38,23 +38,23 @@ void ApplyLoggingPolicy(sandbox::TargetP
 // will start from our caller's caller, which should normally be the function
 // that triggered the interception.
 void LogBlocked(const char* aFunctionName, const char* aContext = nullptr,
                 uint32_t aFramesToSkip = 2);
 
 // Convenience functions to convert to char*.
 void LogBlocked(const char* aFunctionName, const wchar_t* aContext);
 void LogBlocked(const char* aFunctionName, const wchar_t* aContext,
-                uint16_t aLength);
+                uint16_t aLengthInBytes);
 
 // Log a "ALLOWED" msg to the browser console and, if DEBUG build, stderr.
 void LogAllowed(const char* aFunctionName, const char* aContext = nullptr);
 
 // Convenience functions to convert to char*.
 void LogAllowed(const char* aFunctionName, const wchar_t* aContext);
 void LogAllowed(const char* aFunctionName, const wchar_t* aContext,
-                uint16_t aLength);
+                uint16_t aLengthInBytes);
 
 
 } // sandboxing
 } // mozilla
 
 #endif // security_sandbox_sandboxLogging_h__
--- a/services/fxaccounts/FxAccountsManager.jsm
+++ b/services/fxaccounts/FxAccountsManager.jsm
@@ -57,17 +57,18 @@ this.FxAccountsManager = {
   // We only expose the email and the verified status so far.
   get _user() {
     if (!this._activeSession || !this._activeSession.email) {
       return null;
     }
 
     return {
       email: this._activeSession.email,
-      verified: this._activeSession.verified
+      verified: this._activeSession.verified,
+      profile: this._activeSession.profile,
     }
   },
 
   _error: function(aError, aDetails) {
     log.error(aError);
     let reason = {
       error: aError
     };
@@ -160,22 +161,32 @@ this.FxAccountsManager = {
             // There is no way to obtain the key fetch token afterwards
             // without login out the user and asking her to log in again.
             // Also, key fetch tokens are designed to be short-lived, so
             // we need to fetch kB as soon as we have the key fetch token.
             if (aFetchKeys) {
               this._fxAccounts.getKeys();
             }
 
-            return Promise.resolve({
-              accountCreated: aMethod === "signUp",
-              user: this._user
+            return this._fxAccounts.getSignedInUserProfile().catch(error => {
+              // Not fetching the profile is sad but the FxA logs will already
+              // have noise.
+              return null;
             });
           }
-        );
+        ).then(profile => {
+          if (profile) {
+            this._activeSession.profile = profile;
+          }
+
+          return Promise.resolve({
+            accountCreated: aMethod === "signUp",
+            user: this._user
+          });
+        });
       },
       reason => { return this._serverError(reason); }
     );
   },
 
   /**
    * Determine whether the incoming error means that the current account
    * has new server-side state via deletion or password change, and if so,
@@ -415,20 +426,33 @@ this.FxAccountsManager = {
           return Promise.resolve(null);
         }
 
         this._activeSession = user;
         // If we get a stored information of a not yet verified account,
         // we kick off verification before returning what we have.
         if (!user.verified) {
           this.verificationStatus(user);
+          // Trying to get the profile for unverified users will fail, so we
+          // don't even try in that case.
+          log.debug("Account ", this._user);
+          return Promise.resolve(this._user);
         }
 
-        log.debug("Account " + JSON.stringify(this._user));
-        return Promise.resolve(this._user);
+        return this._fxAccounts.getSignedInUserProfile().then(profile => {
+          if (profile) {
+            this._activeSession.profile = profile;
+          }
+          log.debug("Account ", this._user);
+          return Promise.resolve(this._user);
+        }).catch(error => {
+          // FxAccounts logs already inform about the error.
+          log.debug("Account ", this._user);
+          return Promise.resolve(this._user);
+        });
       }
     );
   },
 
   queryAccount: function(aEmail) {
     log.debug("queryAccount " + aEmail);
     if (Services.io.offline) {
       return this._error(ERROR_OFFLINE);
@@ -481,16 +505,23 @@ this.FxAccountsManager = {
         let error = this._getError(data);
         if (error) {
           this._error(error, data);
         }
         // If the verification status has changed, update state.
         if (this._activeSession.verified != data.verified) {
           this._activeSession.verified = data.verified;
           this._fxAccounts.setSignedInUser(this._activeSession);
+          this._fxAccounts.getSignedInUserProfile().then(profile => {
+            if (profile) {
+              this._activeSession.profile = profile;
+            }
+          }).catch(error => {
+            // FxAccounts logs already inform about the error.
+          });
         }
         log.debug(JSON.stringify(this._user));
       },
       reason => { this._serverError(reason); }
     );
   },
 
   /*
--- a/services/fxaccounts/tests/xpcshell/test_manager.js
+++ b/services/fxaccounts/tests/xpcshell/test_manager.js
@@ -97,16 +97,17 @@ const kFxAccounts = fxAccounts;
 FxAccountsManager._fxAccounts = {
   _reject: false,
   _getSignedInUserCalled: false,
   _setSignedInUserCalled: false,
 
   _error: 'error',
   _assertion: 'assertion',
   _keys: 'keys',
+  _profile: 'aprofile',
   _signedInUser: null,
 
   _reset: function() {
     this._getSignedInUserCalled = false;
     this._setSignedInUserCalled = false;
     this._reject = false;
   },
 
@@ -135,16 +136,23 @@ FxAccountsManager._fxAccounts = {
   getSignedInUser: function() {
     this._getSignedInUserCalled = true;
     let deferred = Promise.defer();
     this._reject ? deferred.reject(this._error)
                  : deferred.resolve(this._signedInUser);
     return deferred.promise;
   },
 
+  getSignedInUserProfile: function() {
+    let deferred = Promise.defer();
+    this._reject ? deferred.reject(this._error)
+                 : deferred.resolve(this._profile);
+    return deferred.promise;
+  },
+
   getKeys: function() {
     let deferred = Promise.defer();
     this._reject ? deferred.reject(this._error)
                  : deferred.resolve(this._keys);
     return deferred.promise;
   },
 
   resendVerificationEmail: function() {
@@ -631,21 +639,23 @@ add_test(function(test_getAssertion_refr
     error => {
       do_throw("Unexpected error: " + error);
     }
   );
 });
 
 add_test(function(test_getAccount_existing_verified_session) {
   do_print("= getAccount, existing verified session =");
+  FxAccountsManager._activeSession = null;
   FxAccountsManager.getAccount().then(
     result => {
-      do_check_false(FxAccountsManager._fxAccounts._getSignedInUserCalled);
+      do_check_true(FxAccountsManager._fxAccounts._getSignedInUserCalled);
       do_check_eq(result.email, FxAccountsManager._user.email);
       do_check_eq(result.verified, FxAccountsManager._user.verified);
+      do_check_eq(result.profile, "aprofile");
       run_next_test();
     },
     error => {
       do_throw("Unexpected error: " + error);
     }
   );
 });
 
--- a/testing/eslint-plugin-mozilla/lib/helpers.js
+++ b/testing/eslint-plugin-mozilla/lib/helpers.js
@@ -124,16 +124,23 @@ module.exports = {
   addVarToScope: function(name, context) {
     var scope = context.getScope();
     var variables = scope.variables;
     var variable = new escope.Variable(name, scope);
 
     variable.eslintExplicitGlobal = false;
     variable.writeable = true;
     variables.push(variable);
+
+    // Since eslint 1.10.3, scope variables are now duplicated in the scope.set
+    // map, so we need to store them there too if it exists.
+    // See https://groups.google.com/forum/#!msg/eslint/Y4_oHMWwP-o/5S57U8jXd8kJ
+    if (scope.set) {
+      scope.set.set(name, variable);
+    }
   },
 
   /**
    * Get the single line text represented by a particular AST node.
    *
    * @param  {ASTNode} node
    *         The AST node representing the source text.
    * @param  {String} text
--- a/testing/eslint-plugin-mozilla/lib/rules/no-cpows-in-tests.js
+++ b/testing/eslint-plugin-mozilla/lib/rules/no-cpows-in-tests.js
@@ -42,24 +42,26 @@ module.exports = function(context) {
   return {
     MemberExpression: function(node) {
       if (!helpers.getIsBrowserMochitest(this)) {
         return;
       }
 
       var expression = context.getSource(node);
 
-      cpows.some(function(cpow) {
+      // Only report a single CPOW error per node -- so if checking
+      // |cpows| reports one, don't report another below.
+      var someCpowFound = cpows.some(function(cpow) {
         if (cpow.test(expression)) {
           showError(node, expression);
           return true;
         }
         return false;
       });
-      if (helpers.getIsGlobalScope(context)) {
+      if (!someCpowFound && helpers.getIsGlobalScope(context)) {
         if (/^content\./.test(expression)) {
           showError(node, expression);
           return;
         }
       }
     },
 
     Identifier: function(node) {
--- a/testing/mozharness/configs/mediatests/buildbot_posix_config.py
+++ b/testing/mozharness/configs/mediatests/buildbot_posix_config.py
@@ -36,20 +36,20 @@ config = {
          "https://blobupload.elasticbeanstalk.com",
     ],
     "blob_uploader_auth_file" : os.path.join(os.getcwd(), "oauth.txt"),
     "download_minidump_stackwalk": True,
     "download_symbols": "ondemand",
 
     "firefox_media_repo": 'https://github.com/mjzffr/firefox-media-tests.git',
     "firefox_media_branch": 'master',
-    "firefox_media_rev": 'b11d6c3d7f6af166be314d2ac6673e78c1edb566',
+    "firefox_media_rev": '49b500b30b80372a6c678ec7d0a2b074844f5e84',
     "firefox_ui_repo": 'https://github.com/mozilla/firefox-ui-tests.git',
     "firefox_ui_branch": 'mozilla-central',
-    "firefox_ui_rev": '6d6d57917f85399e903ac69b7e4297091b2d474c',
+    "firefox_ui_rev": '32be49d74e1d10c6bf087235b1d6753c1b840bc4',
 
     "suite_definitions": {
         "media-tests": {
             "options": [],
         },
         "media-youtube-tests": {
             "options": [
                 "%(test_manifest)s"
--- a/testing/mozharness/configs/mediatests/buildbot_windows_config.py
+++ b/testing/mozharness/configs/mediatests/buildbot_windows_config.py
@@ -47,20 +47,20 @@ config = {
     ],
     "blob_uploader_auth_file" : os.path.join(os.getcwd(), "oauth.txt"),
     "in_tree_config": "config/mozharness/marionette.py",
     "download_minidump_stackwalk": True,
     "download_symbols": "ondemand",
 
     "firefox_media_repo": 'https://github.com/mjzffr/firefox-media-tests.git',
     "firefox_media_branch": 'master',
-    "firefox_media_rev": 'b11d6c3d7f6af166be314d2ac6673e78c1edb566',
+    "firefox_media_rev": '49b500b30b80372a6c678ec7d0a2b074844f5e84',
     "firefox_ui_repo": 'https://github.com/mozilla/firefox-ui-tests.git',
     "firefox_ui_branch": 'mozilla-central',
-    "firefox_ui_rev": '6d6d57917f85399e903ac69b7e4297091b2d474c',
+    "firefox_ui_rev": '32be49d74e1d10c6bf087235b1d6753c1b840bc4',
 
     "suite_definitions": {
         "media-tests": {
             "options": [],
         },
         "media-youtube-tests": {
             "options": [
                 "%(test_manifest)s"
--- a/testing/mozharness/configs/merge_day/b2g_branch_repos.py
+++ b/testing/mozharness/configs/merge_day/b2g_branch_repos.py
@@ -21,16 +21,20 @@ config = {
         "android-sdk",
         "platform_external_wpa_supplicant_8",
         "platform_prebuilts_misc",
         "hardware_qcom_display",
         "device_generic_goldfish",
         "platform_frameworks_av",
         "platform_frameworks_base",
         "android-development",
+        "device-hammerhead",
+        "device_lge_hammerhead-kernel",
+        "platform_bionic",
+        "platform_build",
     ],
     "extra_branch_manifest_repos": [
         "gecko",
         "gaia",
     ],
     "branch_order": [
         "master",