Merge inbound to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 07 Nov 2016 14:00:52 -0800
changeset 351438 86f702229e32c6119d092e86431afee576f033a1
parent 351402 000dc91517d648344729bc8764aee1cca8e91e77 (current diff)
parent 351437 dbc272fdb23832d038eed46de9227c6e192a07a5 (diff)
child 351468 f88d94ae4967e2bd71ce2a7de16af7d8b9129291
child 351520 8988e17605ed135abd91f64a0c3c30b559d453ce
child 351624 39aebea1fbb85c40d098208e742021e23c95375b
push id6795
push userjlund@mozilla.com
push dateMon, 23 Jan 2017 14:19:46 +0000
treeherdermozilla-esr52@76101b503191 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone52.0a1
Merge inbound to central, a=merge
browser/components/extensions/test/browser/browser.ini
dom/base/test/TestGetURL.cpp
dom/base/test/TestNativeXMLHttpRequest.cpp
dom/base/test/TestPlainTextSerializer.cpp
dom/canvas/compiledtest/TestWebGLElementArrayCache.cpp
dom/canvas/compiledtest/moz.build
dom/security/test/TestCSPParser.cpp
--- a/browser/base/content/popup-notifications.inc
+++ b/browser/base/content/popup-notifications.inc
@@ -23,16 +23,24 @@
                control="webRTC-selectWindow-menulist"/>
         <menulist id="webRTC-selectWindow-menulist"
                   oncommand="webrtcUI.updateMainActionLabel(this);">
           <menupopup id="webRTC-selectWindow-menupopup"/>
         </menulist>
         <description id="webRTC-all-windows-shared" hidden="true">&getUserMedia.allWindowsShared.message;</description>
       </popupnotificationcontent>
 
+      <popupnotificationcontent id="webRTC-preview" hidden="true">
+        <html:video id="webRTC-previewVideo"/>
+        <vbox id="webRTC-previewWarningBox">
+          <spacer flex="1"/>
+          <description id="webRTC-previewWarning"/>
+        </vbox>
+      </popupnotificationcontent>
+
       <popupnotificationcontent id="webRTC-selectMicrophone" orient="vertical">
         <label value="&getUserMedia.selectMicrophone.label;"
                accesskey="&getUserMedia.selectMicrophone.accesskey;"
                control="webRTC-selectMicrophone-menulist"/>
         <menulist id="webRTC-selectMicrophone-menulist">
           <menupopup id="webRTC-selectMicrophone-menupopup"/>
         </menulist>
       </popupnotificationcontent>
--- a/browser/components/extensions/test/browser/browser.ini
+++ b/browser/components/extensions/test/browser/browser.ini
@@ -91,17 +91,19 @@ tags = webextensions
 [browser_ext_tabs_update.js]
 [browser_ext_tabs_zoom.js]
 [browser_ext_tabs_update_url.js]
 [browser_ext_topwindowid.js]
 [browser_ext_webNavigation_frameId0.js]
 [browser_ext_webNavigation_getFrames.js]
 [browser_ext_webNavigation_urlbar_transitions.js]
 [browser_ext_windows.js]
+[browser_ext_windows_allowScriptsToClose.js]
 [browser_ext_windows_create.js]
 tags = fullscreen
+[browser_ext_windows_create_params.js]
 [browser_ext_windows_create_tabId.js]
 [browser_ext_windows_create_url.js]
 [browser_ext_windows_events.js]
 [browser_ext_windows_size.js]
 skip-if = os == 'mac' # Fails when windows are randomly opened in fullscreen mode
 [browser_ext_windows_update.js]
 tags = fullscreen
--- a/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js
+++ b/browser/components/extensions/test/browser/browser_ext_commands_onCommand.js
@@ -171,30 +171,30 @@ add_task(function* test_user_defined_com
     SimpleTest.monitorConsole(resolve, [{
       message: /Reading manifest: Error processing commands.*.unrecognized_property: An unexpected property was found/,
     }]);
   });
 
   yield extension.startup();
   yield extension.awaitMessage("ready");
 
-  function* runTest() {
+  function* runTest(window) {
     for (let testCommand of testCommands) {
       if (testCommand.shortcutMac && !testCommand.shortcut && !isMac) {
         continue;
       }
-      EventUtils.synthesizeKey(testCommand.key, testCommand.modifiers);
+      EventUtils.synthesizeKey(testCommand.key, testCommand.modifiers, window);
       let message = yield extension.awaitMessage("oncommand");
       is(message, testCommand.name, `Expected onCommand listener to fire with the correct name: ${testCommand.name}`);
     }
   }
 
   // Create another window after the extension is loaded.
   let win2 = yield BrowserTestUtils.openNewBrowserWindow();
-  yield BrowserTestUtils.loadURI(win2.gBrowser.selectedBrowser, "about:config");
+  yield BrowserTestUtils.loadURI(win2.gBrowser.selectedBrowser, "about:robots");
   yield BrowserTestUtils.browserLoaded(win2.gBrowser.selectedBrowser);
 
   let totalTestCommands = Object.keys(testCommands).length;
   let expectedCommandsRegistered = isMac ? totalTestCommands : totalTestCommands - totalMacOnlyCommands;
 
   // Confirm the keysets have been added to both windows.
   let keysetID = `ext-keyset-id-${makeWidgetId(extension.id)}`;
   let keyset = win1.document.getElementById(keysetID);
@@ -202,20 +202,20 @@ add_task(function* test_user_defined_com
   is(keyset.childNodes.length, expectedCommandsRegistered, "Expected keyset to have the correct number of children");
 
   keyset = win2.document.getElementById(keysetID);
   ok(keyset != null, "Expected keyset to exist");
   is(keyset.childNodes.length, expectedCommandsRegistered, "Expected keyset to have the correct number of children");
 
   // Confirm that the commands are registered to both windows.
   yield focusWindow(win1);
-  yield runTest();
+  yield runTest(win1);
 
   yield focusWindow(win2);
-  yield runTest();
+  yield runTest(win2);
 
   yield extension.unload();
 
   // Confirm that the keysets have been removed from both windows after the extension is unloaded.
   keyset = win1.document.getElementById(keysetID);
   is(keyset, null, "Expected keyset to be removed from the window");
 
   keyset = win2.document.getElementById(keysetID);
copy from browser/components/extensions/test/browser/browser_ext_windows_create.js
copy to browser/components/extensions/test/browser/browser_ext_windows_allowScriptsToClose.js
--- a/browser/components/extensions/test/browser/browser_ext_windows_create.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows_allowScriptsToClose.js
@@ -1,176 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-add_task(function* testWindowCreate() {
-  let extension = ExtensionTestUtils.loadExtension({
-    background() {
-      let _checkWindowPromise;
-      browser.test.onMessage.addListener(msg => {
-        if (msg == "checked-window") {
-          _checkWindowPromise.resolve();
-          _checkWindowPromise = null;
-        }
-      });
-
-      let os;
-
-      function checkWindow(expected) {
-        return new Promise(resolve => {
-          _checkWindowPromise = {resolve};
-          browser.test.sendMessage("check-window", expected);
-        });
-      }
-
-      function createWindow(params, expected, keep = false) {
-        return browser.windows.create(params).then(window => {
-          for (let key of Object.keys(params)) {
-            if (key == "state" && os == "mac" && params.state == "normal") {
-              // OS-X doesn't have a hard distinction between "normal" and
-              // "maximized" states.
-              browser.test.assertTrue(window.state == "normal" || window.state == "maximized",
-                                      `Expected window.state (currently ${window.state}) to be "normal" but will accept "maximized"`);
-            } else {
-              browser.test.assertEq(params[key], window[key], `Got expected value for window.${key}`);
-            }
-          }
-
-          browser.test.assertEq(1, window.tabs.length, "tabs property got populated");
-          return checkWindow(expected).then(() => {
-            if (keep) {
-              return window;
-            }
-            if (params.state == "fullscreen" && os == "win") {
-              // FIXME: Closing a fullscreen window causes a window leak in
-              // Windows tests.
-              return browser.windows.update(window.id, {state: "normal"}).then(() => {
-                return browser.windows.remove(window.id);
-              });
-            }
-            return browser.windows.remove(window.id);
-          });
-        });
-      }
-
-      browser.runtime.getPlatformInfo().then(info => { os = info.os; })
-      .then(() => createWindow({state: "maximized"}, {state: "STATE_MAXIMIZED"}))
-      .then(() => createWindow({state: "minimized"}, {state: "STATE_MINIMIZED"}))
-      .then(() => createWindow({state: "normal"}, {state: "STATE_NORMAL", hiddenChrome: []}))
-      .then(() => createWindow({state: "fullscreen"}, {state: "STATE_FULLSCREEN"}))
-      .then(() => {
-        return createWindow({type: "popup"},
-                            {hiddenChrome: ["menubar", "toolbar", "location", "directories", "status", "extrachrome"],
-                             chromeFlags: ["CHROME_OPENAS_DIALOG"]},
-                            true);
-      }).then(window => {
-        return browser.tabs.query({windowType: "popup", active: true}).then(tabs => {
-          browser.test.assertEq(1, tabs.length, "Expected only one popup");
-          browser.test.assertEq(window.id, tabs[0].windowId, "Expected new window to be returned in query");
-
-          return browser.windows.remove(window.id);
-        });
-      }).then(() => {
-        browser.test.notifyPass("window-create");
-      }).catch(e => {
-        browser.test.fail(`${e} :: ${e.stack}`);
-        browser.test.notifyFail("window-create");
-      });
-    },
-  });
-
-  let latestWindow;
-  let windowListener = (window, topic) => {
-    if (topic == "domwindowopened") {
-      latestWindow = window;
-    }
-  };
-  Services.ww.registerNotification(windowListener);
-
-  extension.onMessage("check-window", expected => {
-    if (expected.state != null) {
-      let {windowState} = latestWindow;
-      if (latestWindow.fullScreen) {
-        windowState = latestWindow.STATE_FULLSCREEN;
-      }
-
-      if (expected.state == "STATE_NORMAL" && AppConstants.platform == "macosx") {
-        ok(windowState == window.STATE_NORMAL || windowState == window.STATE_MAXIMIZED,
-           `Expected windowState (currently ${windowState}) to be STATE_NORMAL but will accept STATE_MAXIMIZED`);
-      } else {
-        is(windowState, window[expected.state],
-           `Expected window state to be ${expected.state}`);
-      }
-    }
-    if (expected.hiddenChrome) {
-      let chromeHidden = latestWindow.document.documentElement.getAttribute("chromehidden");
-      is(chromeHidden.trim().split(/\s+/).sort().join(" "),
-         expected.hiddenChrome.sort().join(" "),
-         "Got expected hidden chrome");
-    }
-    if (expected.chromeFlags) {
-      let {chromeFlags} = latestWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                      .getInterface(Ci.nsIDocShell)
-                                      .treeOwner.QueryInterface(Ci.nsIInterfaceRequestor)
-                                      .getInterface(Ci.nsIXULWindow);
-      for (let flag of expected.chromeFlags) {
-        ok(chromeFlags & Ci.nsIWebBrowserChrome[flag],
-           `Expected window to have the ${flag} flag`);
-      }
-    }
-
-    extension.sendMessage("checked-window");
-  });
-
-  yield extension.startup();
-  yield extension.awaitFinish("window-create");
-  yield extension.unload();
-
-  Services.ww.unregisterNotification(windowListener);
-  latestWindow = null;
-});
-
-
-// Tests that incompatible parameters can't be used together.
-add_task(function* testWindowCreateParams() {
-  let extension = ExtensionTestUtils.loadExtension({
-    background() {
-      function* getCalls() {
-        for (let state of ["minimized", "maximized", "fullscreen"]) {
-          for (let param of ["left", "top", "width", "height"]) {
-            let expected = `"state": "${state}" may not be combined with "left", "top", "width", or "height"`;
-
-            yield browser.windows.create({state, [param]: 100}).then(
-              val => {
-                browser.test.fail(`Expected error but got "${val}" instead`);
-              },
-              error => {
-                browser.test.assertTrue(
-                  error.message.includes(expected),
-                  `Got expected error (got: '${error.message}', expected: '${expected}'`);
-              });
-          }
-        }
-      }
-
-      Promise.all(getCalls()).then(() => {
-        browser.test.notifyPass("window-create-params");
-      }).catch(e => {
-        browser.test.fail(`${e} :: ${e.stack}`);
-        browser.test.notifyFail("window-create-params");
-      });
-    },
-  });
-
-  yield extension.startup();
-  yield extension.awaitFinish("window-create-params");
-  yield extension.unload();
-});
-
 // Tests allowScriptsToClose option
 add_task(function* test_allowScriptsToClose() {
   const files = {
     "dummy.html": "<meta charset=utf-8><script src=close.js></script>",
     "close.js": function() {
       window.close();
       if (!window.closed) {
         browser.test.sendMessage("close-failed");
--- a/browser/components/extensions/test/browser/browser_ext_windows_create.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows_create.js
@@ -123,103 +123,8 @@ add_task(function* testWindowCreate() {
 
   yield extension.startup();
   yield extension.awaitFinish("window-create");
   yield extension.unload();
 
   Services.ww.unregisterNotification(windowListener);
   latestWindow = null;
 });
-
-
-// Tests that incompatible parameters can't be used together.
-add_task(function* testWindowCreateParams() {
-  let extension = ExtensionTestUtils.loadExtension({
-    background() {
-      function* getCalls() {
-        for (let state of ["minimized", "maximized", "fullscreen"]) {
-          for (let param of ["left", "top", "width", "height"]) {
-            let expected = `"state": "${state}" may not be combined with "left", "top", "width", or "height"`;
-
-            yield browser.windows.create({state, [param]: 100}).then(
-              val => {
-                browser.test.fail(`Expected error but got "${val}" instead`);
-              },
-              error => {
-                browser.test.assertTrue(
-                  error.message.includes(expected),
-                  `Got expected error (got: '${error.message}', expected: '${expected}'`);
-              });
-          }
-        }
-      }
-
-      Promise.all(getCalls()).then(() => {
-        browser.test.notifyPass("window-create-params");
-      }).catch(e => {
-        browser.test.fail(`${e} :: ${e.stack}`);
-        browser.test.notifyFail("window-create-params");
-      });
-    },
-  });
-
-  yield extension.startup();
-  yield extension.awaitFinish("window-create-params");
-  yield extension.unload();
-});
-
-// Tests allowScriptsToClose option
-add_task(function* test_allowScriptsToClose() {
-  const files = {
-    "dummy.html": "<meta charset=utf-8><script src=close.js></script>",
-    "close.js": function() {
-      window.close();
-      if (!window.closed) {
-        browser.test.sendMessage("close-failed");
-      }
-    },
-  };
-
-  function background() {
-    browser.test.onMessage.addListener((msg, options) => {
-      function listener(_, {status}, {url}) {
-        if (status == "complete" && url == options.url) {
-          browser.tabs.onUpdated.removeListener(listener);
-          browser.tabs.executeScript({file: "close.js"});
-        }
-      }
-      options.url = browser.runtime.getURL(options.url);
-      browser.windows.create(options);
-      if (msg === "create+execute") {
-        browser.tabs.onUpdated.addListener(listener);
-      }
-    });
-    browser.test.notifyPass();
-  }
-
-  const example = "http://example.com/";
-  const manifest = {permissions: ["tabs", example]};
-
-  const extension = ExtensionTestUtils.loadExtension({files, background, manifest});
-  yield SpecialPowers.pushPrefEnv({set: [["dom.allow_scripts_to_close_windows", false]]});
-
-  yield extension.startup();
-  yield extension.awaitFinish();
-
-  extension.sendMessage("create", {url: "dummy.html"});
-  let win = yield BrowserTestUtils.waitForNewWindow();
-  yield BrowserTestUtils.windowClosed(win);
-  info("script allowed to close the window");
-
-  extension.sendMessage("create+execute", {url: example});
-  win = yield BrowserTestUtils.waitForNewWindow();
-  yield extension.awaitMessage("close-failed");
-  info("script prevented from closing the window");
-  win.close();
-
-  extension.sendMessage("create+execute", {url: example, allowScriptsToClose: true});
-  win = yield BrowserTestUtils.waitForNewWindow();
-  yield BrowserTestUtils.windowClosed(win);
-  info("script allowed to close the window");
-
-  yield SpecialPowers.popPrefEnv();
-  yield extension.unload();
-});
copy from browser/components/extensions/test/browser/browser_ext_windows_create.js
copy to browser/components/extensions/test/browser/browser_ext_windows_create_params.js
--- a/browser/components/extensions/test/browser/browser_ext_windows_create.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows_create_params.js
@@ -1,139 +1,12 @@
 /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set sts=2 sw=2 et tw=80: */
 "use strict";
 
-add_task(function* testWindowCreate() {
-  let extension = ExtensionTestUtils.loadExtension({
-    background() {
-      let _checkWindowPromise;
-      browser.test.onMessage.addListener(msg => {
-        if (msg == "checked-window") {
-          _checkWindowPromise.resolve();
-          _checkWindowPromise = null;
-        }
-      });
-
-      let os;
-
-      function checkWindow(expected) {
-        return new Promise(resolve => {
-          _checkWindowPromise = {resolve};
-          browser.test.sendMessage("check-window", expected);
-        });
-      }
-
-      function createWindow(params, expected, keep = false) {
-        return browser.windows.create(params).then(window => {
-          for (let key of Object.keys(params)) {
-            if (key == "state" && os == "mac" && params.state == "normal") {
-              // OS-X doesn't have a hard distinction between "normal" and
-              // "maximized" states.
-              browser.test.assertTrue(window.state == "normal" || window.state == "maximized",
-                                      `Expected window.state (currently ${window.state}) to be "normal" but will accept "maximized"`);
-            } else {
-              browser.test.assertEq(params[key], window[key], `Got expected value for window.${key}`);
-            }
-          }
-
-          browser.test.assertEq(1, window.tabs.length, "tabs property got populated");
-          return checkWindow(expected).then(() => {
-            if (keep) {
-              return window;
-            }
-            if (params.state == "fullscreen" && os == "win") {
-              // FIXME: Closing a fullscreen window causes a window leak in
-              // Windows tests.
-              return browser.windows.update(window.id, {state: "normal"}).then(() => {
-                return browser.windows.remove(window.id);
-              });
-            }
-            return browser.windows.remove(window.id);
-          });
-        });
-      }
-
-      browser.runtime.getPlatformInfo().then(info => { os = info.os; })
-      .then(() => createWindow({state: "maximized"}, {state: "STATE_MAXIMIZED"}))
-      .then(() => createWindow({state: "minimized"}, {state: "STATE_MINIMIZED"}))
-      .then(() => createWindow({state: "normal"}, {state: "STATE_NORMAL", hiddenChrome: []}))
-      .then(() => createWindow({state: "fullscreen"}, {state: "STATE_FULLSCREEN"}))
-      .then(() => {
-        return createWindow({type: "popup"},
-                            {hiddenChrome: ["menubar", "toolbar", "location", "directories", "status", "extrachrome"],
-                             chromeFlags: ["CHROME_OPENAS_DIALOG"]},
-                            true);
-      }).then(window => {
-        return browser.tabs.query({windowType: "popup", active: true}).then(tabs => {
-          browser.test.assertEq(1, tabs.length, "Expected only one popup");
-          browser.test.assertEq(window.id, tabs[0].windowId, "Expected new window to be returned in query");
-
-          return browser.windows.remove(window.id);
-        });
-      }).then(() => {
-        browser.test.notifyPass("window-create");
-      }).catch(e => {
-        browser.test.fail(`${e} :: ${e.stack}`);
-        browser.test.notifyFail("window-create");
-      });
-    },
-  });
-
-  let latestWindow;
-  let windowListener = (window, topic) => {
-    if (topic == "domwindowopened") {
-      latestWindow = window;
-    }
-  };
-  Services.ww.registerNotification(windowListener);
-
-  extension.onMessage("check-window", expected => {
-    if (expected.state != null) {
-      let {windowState} = latestWindow;
-      if (latestWindow.fullScreen) {
-        windowState = latestWindow.STATE_FULLSCREEN;
-      }
-
-      if (expected.state == "STATE_NORMAL" && AppConstants.platform == "macosx") {
-        ok(windowState == window.STATE_NORMAL || windowState == window.STATE_MAXIMIZED,
-           `Expected windowState (currently ${windowState}) to be STATE_NORMAL but will accept STATE_MAXIMIZED`);
-      } else {
-        is(windowState, window[expected.state],
-           `Expected window state to be ${expected.state}`);
-      }
-    }
-    if (expected.hiddenChrome) {
-      let chromeHidden = latestWindow.document.documentElement.getAttribute("chromehidden");
-      is(chromeHidden.trim().split(/\s+/).sort().join(" "),
-         expected.hiddenChrome.sort().join(" "),
-         "Got expected hidden chrome");
-    }
-    if (expected.chromeFlags) {
-      let {chromeFlags} = latestWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                                      .getInterface(Ci.nsIDocShell)
-                                      .treeOwner.QueryInterface(Ci.nsIInterfaceRequestor)
-                                      .getInterface(Ci.nsIXULWindow);
-      for (let flag of expected.chromeFlags) {
-        ok(chromeFlags & Ci.nsIWebBrowserChrome[flag],
-           `Expected window to have the ${flag} flag`);
-      }
-    }
-
-    extension.sendMessage("checked-window");
-  });
-
-  yield extension.startup();
-  yield extension.awaitFinish("window-create");
-  yield extension.unload();
-
-  Services.ww.unregisterNotification(windowListener);
-  latestWindow = null;
-});
-
 
 // Tests that incompatible parameters can't be used together.
 add_task(function* testWindowCreateParams() {
   let extension = ExtensionTestUtils.loadExtension({
     background() {
       function* getCalls() {
         for (let state of ["minimized", "maximized", "fullscreen"]) {
           for (let param of ["left", "top", "width", "height"]) {
@@ -160,66 +33,8 @@ add_task(function* testWindowCreateParam
       });
     },
   });
 
   yield extension.startup();
   yield extension.awaitFinish("window-create-params");
   yield extension.unload();
 });
-
-// Tests allowScriptsToClose option
-add_task(function* test_allowScriptsToClose() {
-  const files = {
-    "dummy.html": "<meta charset=utf-8><script src=close.js></script>",
-    "close.js": function() {
-      window.close();
-      if (!window.closed) {
-        browser.test.sendMessage("close-failed");
-      }
-    },
-  };
-
-  function background() {
-    browser.test.onMessage.addListener((msg, options) => {
-      function listener(_, {status}, {url}) {
-        if (status == "complete" && url == options.url) {
-          browser.tabs.onUpdated.removeListener(listener);
-          browser.tabs.executeScript({file: "close.js"});
-        }
-      }
-      options.url = browser.runtime.getURL(options.url);
-      browser.windows.create(options);
-      if (msg === "create+execute") {
-        browser.tabs.onUpdated.addListener(listener);
-      }
-    });
-    browser.test.notifyPass();
-  }
-
-  const example = "http://example.com/";
-  const manifest = {permissions: ["tabs", example]};
-
-  const extension = ExtensionTestUtils.loadExtension({files, background, manifest});
-  yield SpecialPowers.pushPrefEnv({set: [["dom.allow_scripts_to_close_windows", false]]});
-
-  yield extension.startup();
-  yield extension.awaitFinish();
-
-  extension.sendMessage("create", {url: "dummy.html"});
-  let win = yield BrowserTestUtils.waitForNewWindow();
-  yield BrowserTestUtils.windowClosed(win);
-  info("script allowed to close the window");
-
-  extension.sendMessage("create+execute", {url: example});
-  win = yield BrowserTestUtils.waitForNewWindow();
-  yield extension.awaitMessage("close-failed");
-  info("script prevented from closing the window");
-  win.close();
-
-  extension.sendMessage("create+execute", {url: example, allowScriptsToClose: true});
-  win = yield BrowserTestUtils.waitForNewWindow();
-  yield BrowserTestUtils.windowClosed(win);
-  info("script allowed to close the window");
-
-  yield SpecialPowers.popPrefEnv();
-  yield extension.unload();
-});
--- a/browser/components/extensions/test/browser/browser_ext_windows_events.js
+++ b/browser/components/extensions/test/browser/browser_ext_windows_events.js
@@ -67,16 +67,20 @@ add_task(function* testWindowsEvents() {
   let currentWindowId = WindowManager.getId(currentWindow);
   info(`Current window ID: ${currentWindowId}`);
 
   info(`Create browser window 1`);
   let win1 = yield BrowserTestUtils.openNewBrowserWindow();
   let win1Id = yield extension.awaitMessage("window-created");
   info(`Window 1 ID: ${win1Id}`);
 
+  // This shouldn't be necessary, but tests intermittently fail, so let's give
+  // it a try.
+  win1.focus();
+
   let winId = yield extension.awaitMessage(`window-focus-changed`);
   is(winId, win1Id, "Got focus change event for the correct window ID.");
 
   info(`Create browser window 2`);
   let win2 = yield BrowserTestUtils.openNewBrowserWindow();
   let win2Id = yield extension.awaitMessage("window-created");
   info(`Window 2 ID: ${win2Id}`);
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -481,16 +481,25 @@ social.aria.toolbarButtonBadgeText=%1$S 
 getUserMedia.shareCamera.message = Would you like to share your camera with %S?
 getUserMedia.shareMicrophone.message = Would you like to share your microphone with %S?
 getUserMedia.shareScreen.message = Would you like to share your screen with %S?
 getUserMedia.shareCameraAndMicrophone.message = Would you like to share your camera and microphone with %S?
 getUserMedia.shareCameraAndAudioCapture.message = Would you like to share your camera and this tab’s audio with %S?
 getUserMedia.shareScreenAndMicrophone.message = Would you like to share your microphone and screen with %S?
 getUserMedia.shareScreenAndAudioCapture.message = Would you like to share this tab’s audio and your screen with %S?
 getUserMedia.shareAudioCapture.message = Would you like to share this tab’s audio with %S?
+# LOCALIZATION NOTE (getUserMedia.shareScreenWarning.message): NB: inserted via innerHTML, so please don't use <, > or & in this string.
+# %S will be the 'learn more' link
+getUserMedia.shareScreenWarning.message = Only share screens with sites you trust. Sharing can allow deceptive sites to browse as you and steal your private data. %S
+# LOCALIZATION NOTE (getUserMedia.shareFirefoxWarning.message): NB: inserted via innerHTML, so please don't use <, > or & in this string.
+# %1$S is brandShortName (eg. Firefox)
+# %2$S will be the 'learn more' link
+getUserMedia.shareFirefoxWarning.message = Only share %1$S with sites you trust. Sharing can allow deceptive sites to browse as you and steal your private data. %2$S
+# LOCALIZATION NOTE(getUserMedia.shareScreen.learnMoreLabel): NB: inserted via innerHTML, so please don't use <, > or & in this string.
+getUserMedia.shareScreen.learnMoreLabel = Learn More
 getUserMedia.selectWindow.label=Window to share:
 getUserMedia.selectWindow.accesskey=W
 getUserMedia.selectScreen.label=Screen to share:
 getUserMedia.selectScreen.accesskey=S
 getUserMedia.selectApplication.label=Application to share:
 getUserMedia.selectApplication.accesskey=A
 getUserMedia.noApplication.label = No Application
 getUserMedia.noScreen.label = No Screen
--- a/browser/modules/ContentWebRTC.jsm
+++ b/browser/modules/ContentWebRTC.jsm
@@ -9,16 +9,18 @@ const {classes: Cc, interfaces: Ci, resu
 this.EXPORTED_SYMBOLS = [ "ContentWebRTC" ];
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "MediaManagerService",
                                    "@mozilla.org/mediaManagerService;1",
                                    "nsIMediaManagerService");
 
+const kBrowserURL = "chrome://browser/content/browser.xul";
+
 this.ContentWebRTC = {
   _initialized: false,
 
   init: function() {
     if (this._initialized)
       return;
 
     this._initialized = true;
@@ -163,26 +165,29 @@ function prompt(aContentWindow, aWindowI
     device = device.QueryInterface(Ci.nsIMediaDevice);
     switch (device.type) {
       case "audio":
         // Check that if we got a microphone, we have not requested an audio
         // capture, and if we have requested an audio capture, we are not
         // getting a microphone instead.
         if (audio && (device.mediaSource == "microphone") != sharingAudio) {
           audioDevices.push({name: device.name, deviceIndex: devices.length,
-                             mediaSource: device.mediaSource});
+                             id: device.rawId, mediaSource: device.mediaSource});
           devices.push(device);
         }
         break;
       case "video":
         // Verify that if we got a camera, we haven't requested a screen share,
         // or that if we requested a screen share we aren't getting a camera.
         if (video && (device.mediaSource == "camera") != sharingScreen) {
-          videoDevices.push({name: device.name, deviceIndex: devices.length,
-                             mediaSource: device.mediaSource});
+          let deviceObject = {name: device.name, deviceIndex: devices.length,
+                              id: device.rawId, mediaSource: device.mediaSource};
+          if (device.scary)
+            deviceObject.scary = true;
+          videoDevices.push(deviceObject);
           devices.push(device);
         }
         break;
     }
   }
 
   let requestTypes = [];
   if (videoDevices.length)
@@ -255,17 +260,23 @@ function forgetPendingListsEventually(aC
       aContentWindow.pendingPeerConnectionRequests.size) {
     return;
   }
   aContentWindow.pendingGetUserMediaRequests = null;
   aContentWindow.pendingPeerConnectionRequests = null;
   aContentWindow.removeEventListener("unload", ContentWebRTC);
 }
 
-function updateIndicators() {
+function updateIndicators(aSubject, aTopic, aData) {
+  if (aSubject instanceof Ci.nsIPropertyBag &&
+      aSubject.getProperty("requestURL") == kBrowserURL) {
+    // Ignore notifications caused by the browser UI showing previews.
+    return;
+  }
+
   let contentWindowArray = MediaManagerService.activeMediaCaptureWindows;
   let count = contentWindowArray.length;
 
   let state = {
     showGlobalIndicator: count > 0,
     showCameraIndicator: false,
     showMicrophoneIndicator: false,
     showScreenSharingIndicator: ""
@@ -279,16 +290,21 @@ function updateIndicators() {
   // have the same top level window several times. We use a Set to avoid
   // sending duplicate notifications.
   let contentWindows = new Set();
   for (let i = 0; i < count; ++i) {
     contentWindows.add(contentWindowArray.queryElementAt(i, Ci.nsISupports).top);
   }
 
   for (let contentWindow of contentWindows) {
+    if (contentWindow.document.documentURI == kBrowserURL) {
+      // There may be a preview shown at the same time as other streams.
+      continue;
+    }
+
     let tabState = getTabStateForContentWindow(contentWindow);
     if (tabState.camera)
       state.showCameraIndicator = true;
     if (tabState.microphone)
       state.showMicrophoneIndicator = true;
     if (tabState.screen) {
       if (tabState.screen == "Screen") {
         state.showScreenSharingIndicator = "Screen";
@@ -310,16 +326,21 @@ function updateIndicators() {
     mm.sendAsyncMessage("webrtc:UpdateBrowserIndicators", tabState);
   }
 
   cpmm.sendAsyncMessage("webrtc:UpdateGlobalIndicators", state);
 }
 
 function removeBrowserSpecificIndicator(aSubject, aTopic, aData) {
   let contentWindow = Services.wm.getOuterWindowWithId(aData).top;
+  if (contentWindow.document.documentURI == kBrowserURL) {
+    // Ignore notifications caused by the browser UI showing previews.
+    return;
+  }
+
   let tabState = getTabStateForContentWindow(contentWindow);
   if (!tabState.camera && !tabState.microphone && !tabState.screen)
     tabState = {windowId: tabState.windowId};
 
   let mm = getMessageManagerForWindow(contentWindow);
   if (mm)
     mm.sendAsyncMessage("webrtc:UpdateBrowserIndicators", tabState);
 }
--- a/browser/modules/webrtcUI.jsm
+++ b/browser/modules/webrtcUI.jsm
@@ -363,22 +363,40 @@ function prompt(aBrowser, aRequest) {
 
       let chromeDoc = this.browser.ownerDocument;
 
       if (aTopic == "shown") {
         let popupId = "Devices";
         if (requestTypes.length == 1 && (requestTypes[0] == "Microphone" ||
                                          requestTypes[0] == "AudioCapture"))
           popupId = "Microphone";
-        if (requestTypes.indexOf("Screen") != -1)
+        if (requestTypes.includes("Screen"))
           popupId = "Screen";
         chromeDoc.getElementById("webRTC-shareDevices-notification")
                  .setAttribute("popupid", "webRTC-share" + popupId);
       }
 
+      // Clean-up video streams of screensharing previews.
+      if ((aTopic == "dismissed" || aTopic == "removed") &&
+          requestTypes.includes("Screen")) {
+        let video = chromeDoc.getElementById("webRTC-previewVideo");
+        video.deviceId = undefined;
+        if (video.stream) {
+          video.stream.getTracks().forEach(t => t.stop());
+          video.stream = null;
+          video.src = null;
+          chromeDoc.getElementById("webRTC-preview").hidden = true;
+        }
+        let menupopup = chromeDoc.getElementById("webRTC-selectWindow-menupopup");
+        if (menupopup._commandEventListener) {
+          menupopup.removeEventListener("command", menupopup._commandEventListener);
+          menupopup._commandEventListener = null;
+        }
+      }
+
       if (aTopic != "showing")
         return false;
 
       // DENY_ACTION is handled immediately by MediaManager, but handling
       // of ALLOW_ACTION is delayed until the popupshowing event
       // to avoid granting permissions automatically to background tabs.
       if (aRequest.secure) {
         let perms = Services.perms;
@@ -462,56 +480,129 @@ function prompt(aBrowser, aRequest) {
         addDeviceToList(menupopup,
                         stringBundle.getString("getUserMedia.no" + typeName + ".label"),
                         "-1");
         menupopup.appendChild(chromeDoc.createElement("menuseparator"));
 
         // Build the list of 'devices'.
         let monitorIndex = 1;
         for (let i = 0; i < devices.length; ++i) {
+          let device = devices[i];
+
           let name;
           // Building screen list from available screens.
           if (type == "screen") {
-            if (devices[i].name == "Primary Monitor") {
+            if (device.name == "Primary Monitor") {
               name = stringBundle.getString("getUserMedia.shareEntireScreen.label");
             } else {
               name = stringBundle.getFormattedString("getUserMedia.shareMonitor.label",
                                                      [monitorIndex]);
               ++monitorIndex;
             }
           }
           else {
-            name = devices[i].name;
+            name = device.name;
             if (type == "application") {
               // The application names returned by the platform are of the form:
               // <window count>\x1e<application name>
               let sepIndex = name.indexOf("\x1e");
               let count = name.slice(0, sepIndex);
               let stringId = "getUserMedia.shareApplicationWindowCount.label";
               name = PluralForm.get(parseInt(count), stringBundle.getString(stringId))
                                .replace("#1", name.slice(sepIndex + 1))
                                .replace("#2", count);
             }
           }
-          addDeviceToList(menupopup, name, i, typeName);
+          let item = addDeviceToList(menupopup, name, i, typeName);
+          item.deviceId = device.id;
+          if (device.scary)
+            item.scary = true;
         }
 
         // Always re-select the "No <type>" item.
         chromeDoc.getElementById("webRTC-selectWindow-menulist").removeAttribute("value");
         chromeDoc.getElementById("webRTC-all-windows-shared").hidden = true;
+        menupopup._commandEventListener = event => {
+          let video = chromeDoc.getElementById("webRTC-previewVideo");
+          if (video.stream) {
+            video.stream.getTracks().forEach(t => t.stop());
+            video.stream = null;
+          }
+
+          let deviceId = event.target.deviceId;
+          if (deviceId == undefined) {
+            chromeDoc.getElementById("webRTC-preview").hidden = true;
+            video.src = null;
+            return;
+          }
+
+          let scary = event.target.scary;
+          let warning = chromeDoc.getElementById("webRTC-previewWarning");
+          warning.hidden = !scary;
+          let chromeWin = chromeDoc.defaultView;
+          if (scary) {
+            warning.hidden = false;
+            let string;
+            let bundle = chromeWin.gNavigatorBundle;
+
+            let learnMoreText =
+              bundle.getString("getUserMedia.shareScreen.learnMoreLabel");
+            let baseURL =
+              Services.urlFormatter.formatURLPref("app.support.baseURL");
+            let learnMore =
+              "<label class='text-link' href='" + baseURL + "screenshare-safety'>" +
+              learnMoreText + "</label>";
+
+            if (type == "screen") {
+              string = bundle.getFormattedString("getUserMedia.shareScreenWarning.message",
+                                                 [learnMore]);
+            }
+            else {
+              let brand =
+                chromeDoc.getElementById("bundle_brand").getString("brandShortName");
+              string = bundle.getFormattedString("getUserMedia.shareFirefoxWarning.message",
+                                                 [brand, learnMore]);
+            }
+            warning.innerHTML = string;
+          }
+
+          let perms = Services.perms;
+          let chromeUri = Services.io.newURI(chromeDoc.documentURI, null, null);
+          perms.add(chromeUri, "MediaManagerVideo", perms.ALLOW_ACTION,
+                    perms.EXPIRE_SESSION);
+
+          video.deviceId = deviceId;
+          let constraints = { video: { mediaSource: type, deviceId: {exact: deviceId } } };
+          chromeWin.navigator.mediaDevices.getUserMedia(constraints).then(stream => {
+            if (video.deviceId != deviceId) {
+              // The user has selected a different device or closed the panel
+              // before getUserMedia finished.
+              stream.getTracks().forEach(t => t.stop());
+              return;
+            }
+            video.src = chromeWin.URL.createObjectURL(stream);
+            video.stream = stream;
+            chromeDoc.getElementById("webRTC-preview").hidden = false;
+            video.onloadedmetadata = function(e) {
+              video.play();
+            };
+          });
+        };
+        menupopup.addEventListener("command", menupopup._commandEventListener);
       }
 
       function addDeviceToList(menupopup, deviceName, deviceIndex, type) {
         let menuitem = chromeDoc.createElement("menuitem");
         menuitem.setAttribute("value", deviceIndex);
         menuitem.setAttribute("label", deviceName);
         menuitem.setAttribute("tooltiptext", deviceName);
         if (type)
           menuitem.setAttribute("devicetype", type);
         menupopup.appendChild(menuitem);
+        return menuitem;
       }
 
       chromeDoc.getElementById("webRTC-selectCamera").hidden = !videoDevices.length || sharingScreen;
       chromeDoc.getElementById("webRTC-selectWindowOrScreen").hidden = !sharingScreen || !videoDevices.length;
       chromeDoc.getElementById("webRTC-selectMicrophone").hidden = !audioDevices.length || sharingAudio;
 
       let camMenupopup = chromeDoc.getElementById("webRTC-selectCamera-menupopup");
       let windowMenupopup = chromeDoc.getElementById("webRTC-selectWindow-menupopup");
@@ -578,17 +669,17 @@ function prompt(aBrowser, aRequest) {
       };
       return false;
     }
   };
 
   let anchorId = "webRTC-shareDevices-notification-icon";
   if (requestTypes.length == 1 && requestTypes[0] == "Microphone")
     anchorId = "webRTC-shareMicrophone-notification-icon";
-  if (requestTypes.indexOf("Screen") != -1)
+  if (requestTypes.includes("Screen"))
     anchorId = "webRTC-shareScreen-notification-icon";
   notification =
     chromeWin.PopupNotifications.show(aBrowser, "webRTC-shareDevices", message,
                                       anchorId, mainAction, secondaryActions,
                                       options);
   notification.callID = aRequest.callID;
 }
 
--- a/browser/themes/shared/jar.inc.mn
+++ b/browser/themes/shared/jar.inc.mn
@@ -118,16 +118,17 @@
   skin/classic/browser/translating-16@2x.png                   (../shared/translation/translating-16@2x.png)
   skin/classic/browser/translation-16.png                      (../shared/translation/translation-16.png)
   skin/classic/browser/translation-16@2x.png                   (../shared/translation/translation-16@2x.png)
   skin/classic/browser/undoCloseTab.png                        (../shared/undoCloseTab.png)
   skin/classic/browser/undoCloseTab@2x.png                     (../shared/undoCloseTab@2x.png)
   skin/classic/browser/update-badge.svg                        (../shared/update-badge.svg)
   skin/classic/browser/update-badge-failed.svg                 (../shared/update-badge-failed.svg)
   skin/classic/browser/warning.svg                             (../shared/warning.svg)
+  skin/classic/browser/warning-white.svg                       (../shared/warning-white.svg)
   skin/classic/browser/cert-error.svg                          (../shared/incontent-icons/cert-error.svg)
   skin/classic/browser/session-restore.svg                     (../shared/incontent-icons/session-restore.svg)
   skin/classic/browser/tab-crashed.svg                         (../shared/incontent-icons/tab-crashed.svg)
   skin/classic/browser/favicon-search-16.svg                   (../shared/favicon-search-16.svg)
   skin/classic/browser/icon-search-64.svg                      (../shared/incontent-icons/icon-search-64.svg)
   skin/classic/browser/welcome-back.svg                        (../shared/incontent-icons/welcome-back.svg)
   skin/classic/browser/reader-tour.png                         (../shared/reader/reader-tour.png)
   skin/classic/browser/reader-tour@2x.png                      (../shared/reader/reader-tour@2x.png)
--- a/browser/themes/shared/notification-icons.inc.css
+++ b/browser/themes/shared/notification-icons.inc.css
@@ -150,16 +150,44 @@
 .screen-icon.in-use {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#screen-sharing);
 }
 
 .screen-icon.blocked-permission-icon {
   list-style-image: url(chrome://browser/skin/notification-icons.svg#screen-blocked);
 }
 
+#webRTC-preview:not([hidden]) {
+  display: -moz-stack;
+  border-radius: 4px;
+  border: 1px solid GrayText;
+  overflow: hidden;
+  min-width: 300px;
+  min-height: 10em;
+}
+
+html|*#webRTC-previewVideo {
+  width: 300px;
+  /* If we don't set the min-width, width is ignored. */
+  min-width: 300px;
+  max-height: 200px;
+}
+
+#webRTC-previewWarning {
+  background: rgba(255, 217, 99, .8) url("chrome://browser/skin/warning-white.svg") no-repeat .75em .75em;
+  margin: 0;
+  padding: .5em;
+  padding-inline-start: calc(1.5em + 16px);
+  border-top: 1px solid GrayText;
+}
+
+#webRTC-previewWarning > .text-link {
+  margin-inline-start: 0;
+}
+
 /* This icon has a block sign in it, so we don't need a blocked version. */
 .popup-icon {
   list-style-image: url("chrome://browser/skin/notification-icons.svg#popup");
 }
 
 /* EME */
 
 .popup-notification-icon[popupid="drmContentPlaying"],
new file mode 100644
--- /dev/null
+++ b/browser/themes/shared/warning-white.svg
@@ -0,0 +1,6 @@
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16">
+  <path fill="#fff" stroke="#000" stroke-opacity="0.3" d="M15.4,12.9 9.46,1.41 C9.12,0.756 8.59,0.381 8,0.381 7.41,0.381 6.88,0.756 6.54,1.41 L0.642,12.9 c-0.331,0.6 -0.348,1.3 -0.05,1.9 0.299,0.5 0.854,0.8 1.534,0.8 H13.9 c0.6,0 1.2,-0.3 1.5,-0.8 0.3,-0.6 0.3,-1.3 0,-1.9z M8.83,5.07 8.65,10.5 H7.34 L7.15,5.07 H8.83z M8,13.7 c-0.55,0 -0.99,-0.5 -0.99,-1 0,-0.6 0.44,-1 0.99,-1 0.56,0 0.99,0.4 0.99,1 0,0.5 -0.43,1 -0.99,1z"/>
+</svg>
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -1366,24 +1366,24 @@ BlobImplStream::MaybeRegisterMemoryRepor
 
   RegisterWeakMemoryReporter(this);
 }
 
 NS_IMETHODIMP
 BlobImplStream::CollectReports(nsIHandleReportCallback* aHandleReport,
                                nsISupports* aData, bool aAnonymize)
 {
-  uint64_t size;
-  nsresult rv = mInputStream->Available(&size);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  nsCOMPtr<nsIStringInputStream> stringInputStream =
+    do_QueryInterface(mInputStream);
+  if (!stringInputStream) {
+    return NS_OK;
   }
 
   MOZ_COLLECT_REPORT(
     "explicit/dom/memory-file-data/stream", KIND_HEAP, UNITS_BYTES,
-    size,
+    stringInputStream->SizeOfIncludingThis(MallocSizeOf),
     "Memory used to back a File/Blob based on an input stream.");
 
   return NS_OK;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/File.h
+++ b/dom/base/File.h
@@ -827,16 +827,18 @@ public:
 
 private:
   ~EmptyBlobImpl() {}
 };
 
 class BlobImplStream final : public BlobImplBase
                            , public nsIMemoryReporter
 {
+  MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
+
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIMEMORYREPORTER
 
   static already_AddRefed<BlobImplStream>
   Create(nsIInputStream* aInputStream,
          const nsAString& aContentType,
          uint64_t aLength);
deleted file mode 100644
--- a/dom/base/test/TestGetURL.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "TestHarness.h"
-
-#include "nsIPrincipal.h"
-#include "nsIScriptSecurityManager.h"
-#include "nsIXMLHttpRequest.h"
-
-
-#define TEST_ENSURE_BASE(_test, _msg)       \
-  PR_BEGIN_MACRO                            \
-    if (_test) {                            \
-      fail(_msg);                           \
-      return NS_ERROR_FAILURE;              \
-    }                                       \
-  PR_END_MACRO
-
-#define TEST_ENSURE_SUCCESS(_rv, _msg)      \
-  TEST_ENSURE_BASE(NS_FAILED(_rv), _msg)
-
-#define TEST_ENSURE_FAILED(_rv, _msg)       \
-  TEST_ENSURE_BASE(NS_SUCCEEDED(_rv), _msg)
-
-nsresult TestGetURL(const nsCString& aURL)
-{
-  nsresult rv;
-
-  nsCOMPtr<nsIXMLHttpRequest> xhr =
-    do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv);
-  TEST_ENSURE_SUCCESS(rv, "Couldn't create nsIXMLHttpRequest instance!");
-
-  NS_NAMED_LITERAL_CSTRING(getString, "GET");
-  const nsAString& empty = EmptyString();
-  
-  nsCOMPtr<nsIScriptSecurityManager> secman =
-    do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-  TEST_ENSURE_SUCCESS(rv, "Couldn't get script security manager!");
-
-  nsCOMPtr<nsIPrincipal> systemPrincipal;
-  rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
-  TEST_ENSURE_SUCCESS(rv, "Couldn't get system principal!");
-
-  rv = xhr->Init(systemPrincipal, nullptr, nullptr, nullptr);
-  TEST_ENSURE_SUCCESS(rv, "Couldn't initialize the XHR!");
-
-  rv = xhr->Open(getString, aURL, false, empty, empty);
-  TEST_ENSURE_SUCCESS(rv, "OpenRequest failed!");
-
-  rv = xhr->Send(nullptr);
-  TEST_ENSURE_SUCCESS(rv, "Send failed!");
-
-  nsAutoString response;
-  rv = xhr->GetResponseText(response);
-  TEST_ENSURE_SUCCESS(rv, "GetResponse failed!");
-
-  nsAutoCString responseUTF8 = NS_ConvertUTF16toUTF8(response);
-  printf("#BEGIN\n");
-  printf("%s", responseUTF8.get());
-  printf("\n#EOF\n");
-
-  return NS_OK;
-}
-
-int main(int argc, char** argv)
-{
-  if (argc <  2) {
-    printf("Usage: TestGetURL <url>\n");
-    exit(0);
-  }
-
-  ScopedXPCOM xpcom("XMLHttpRequest");
-  if (xpcom.failed())
-    return 1;
-
-  nsAutoCString targetURL(argv[1]);
-
-  int retval = 0;
-  if (NS_FAILED(TestGetURL(targetURL))) {
-    retval = 1;
-  }
-
-  return retval;
-}
rename from dom/base/test/TestNativeXMLHttpRequest.cpp
rename to dom/base/test/gtest/TestNativeXMLHttpRequest.cpp
--- a/dom/base/test/TestNativeXMLHttpRequest.cpp
+++ b/dom/base/test/gtest/TestNativeXMLHttpRequest.cpp
@@ -1,100 +1,58 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "TestHarness.h"
+#include "gtest/gtest.h"
 
+#include "nsContentUtils.h"
 #include "nsIDOMDocument.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIXMLHttpRequest.h"
 
-
-#define TEST_ENSURE_BASE(_test, _msg)       \
-  PR_BEGIN_MACRO                            \
-    if (_test) {                            \
-      fail(_msg);                           \
-      return NS_ERROR_FAILURE;              \
-    }                                       \
-  PR_END_MACRO
-
-#define TEST_ENSURE_SUCCESS(_rv, _msg)      \
-  TEST_ENSURE_BASE(NS_FAILED(_rv), _msg)
+#define TEST_URL_PREFIX  "data:text/xml,"
+#define TEST_URL_CONTENT "<foo><bar></bar></foo>"
+#define TEST_URL         TEST_URL_PREFIX TEST_URL_CONTENT
 
-#define TEST_ENSURE_FAILED(_rv, _msg)       \
-  TEST_ENSURE_BASE(NS_SUCCEEDED(_rv), _msg)
-
-#define TEST_URL_PREFIX                     \
-  "data:text/xml,"
-#define TEST_URL_CONTENT                    \
-  "<foo><bar></bar></foo>"
-
-#define TEST_URL                            \
-  TEST_URL_PREFIX TEST_URL_CONTENT
-
-nsresult TestNativeXMLHttpRequest()
+TEST(NativeXMLHttpRequest, Test)
 {
   nsresult rv;
 
   nsCOMPtr<nsIXMLHttpRequest> xhr =
     do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv);
-  TEST_ENSURE_SUCCESS(rv, "Couldn't create nsIXMLHttpRequest instance!");
+  ASSERT_TRUE(NS_SUCCEEDED(rv)) << "Couldn't create nsIXMLHttpRequest instance";
 
   NS_NAMED_LITERAL_CSTRING(getString, "GET");
   NS_NAMED_LITERAL_CSTRING(testURL, TEST_URL);
   const nsAString& empty = EmptyString();
 
   nsCOMPtr<nsIScriptSecurityManager> secman =
     do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
-  TEST_ENSURE_SUCCESS(rv, "Couldn't get script security manager!");
+  ASSERT_TRUE(NS_SUCCEEDED(rv)) << "Couldn't get script security manager";
 
   nsCOMPtr<nsIPrincipal> systemPrincipal;
   rv = secman->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
-  TEST_ENSURE_SUCCESS(rv, "Couldn't get system principal!");
+  ASSERT_TRUE(NS_SUCCEEDED(rv)) << "Couldn't get system principal";
 
   rv = xhr->Init(systemPrincipal, nullptr, nullptr, nullptr);
-  TEST_ENSURE_SUCCESS(rv, "Couldn't initialize the XHR!");
+  ASSERT_TRUE(NS_SUCCEEDED(rv)) << "Couldn't initialize the XHR";
 
   rv = xhr->Open(getString, testURL, false, empty, empty);
-  TEST_ENSURE_SUCCESS(rv, "Open failed!");
+  ASSERT_TRUE(NS_SUCCEEDED(rv)) << "Open failed";
 
   rv = xhr->Send(nullptr);
-  TEST_ENSURE_SUCCESS(rv, "Send failed!");
+  ASSERT_TRUE(NS_SUCCEEDED(rv)) << "Send failed";
 
   nsAutoString response;
   rv = xhr->GetResponseText(response);
-  TEST_ENSURE_SUCCESS(rv, "GetResponse failed!");
-
-  if (!response.EqualsLiteral(TEST_URL_CONTENT)) {
-    fail("Response text does not match!");
-    return NS_ERROR_FAILURE;
-  }
+  ASSERT_TRUE(NS_SUCCEEDED(rv)) << "GetResponse failed";
+  ASSERT_TRUE(response.EqualsLiteral(TEST_URL_CONTENT)) <<
+    "Response text does not match";
 
   nsCOMPtr<nsIDOMDocument> dom;
   rv = xhr->GetResponseXML(getter_AddRefs(dom));
-  TEST_ENSURE_SUCCESS(rv, "GetResponseXML failed!");
-
-  if (!dom) {
-    fail("No DOM document constructed!");
-    return NS_ERROR_FAILURE;
-  }
-
-  passed("Native XMLHttpRequest");
-  return NS_OK;
+  ASSERT_TRUE(NS_SUCCEEDED(rv)) << "GetResponseXML failed";
+  ASSERT_TRUE(dom) << "No DOM document constructed";
 }
-
-int main(int argc, char** argv)
-{
-  ScopedXPCOM xpcom("XMLHttpRequest");
-  if (xpcom.failed())
-    return 1;
-
-  int retval = 0;
-  if (NS_FAILED(TestNativeXMLHttpRequest())) {
-    retval = 1;
-  }
-
-  return retval;
-}
rename from dom/base/test/TestPlainTextSerializer.cpp
rename to dom/base/test/gtest/TestPlainTextSerializer.cpp
--- a/dom/base/test/TestPlainTextSerializer.cpp
+++ b/dom/base/test/gtest/TestPlainTextSerializer.cpp
@@ -1,33 +1,31 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "TestHarness.h"
+#include "gtest/gtest.h"
 
 #include "nsServiceManagerUtils.h"
 #include "nsStringGlue.h"
 #include "nsIDocumentEncoder.h"
 #include "nsCRT.h"
 #include "nsIParserUtils.h"
 
 void
 ConvertBufToPlainText(nsString &aConBuf, int aFlag)
 {
-  nsCOMPtr<nsIParserUtils> utils =
-    do_GetService(NS_PARSERUTILS_CONTRACTID);
+  nsCOMPtr<nsIParserUtils> utils = do_GetService(NS_PARSERUTILS_CONTRACTID);
   utils->ConvertToPlainText(aConBuf, aFlag, 72, aConBuf);
 }
 
 // Test for ASCII with format=flowed; delsp=yes
-nsresult
-TestASCIIWithFlowedDelSp()
+TEST(PlainTextSerializer, ASCIIWithFlowedDelSp)
 {
   nsString test;
   nsString result;
 
   test.AssignLiteral("<html><body>"
                      "Firefox Firefox Firefox Firefox "
                      "Firefox Firefox Firefox Firefox "
                      "Firefox Firefox Firefox Firefox"
@@ -39,29 +37,22 @@ TestASCIIWithFlowedDelSp()
                               nsIDocumentEncoder::OutputFormatFlowed |
                               nsIDocumentEncoder::OutputFormatDelSp);
 
   // create result case
   result.AssignLiteral("Firefox Firefox Firefox Firefox "
                        "Firefox Firefox Firefox Firefox "
                        "Firefox  \r\nFirefox Firefox Firefox\r\n");
 
-  if (!test.Equals(result)) {
-    fail("Wrong HTML to ASCII text serialization with format=flowed; delsp=yes");
-    return NS_ERROR_FAILURE;
-  }
-
-  passed("HTML to ASCII text serialization with format=flowed; delsp=yes");
-
-  return NS_OK;
+  ASSERT_TRUE(test.Equals(result)) <<
+    "Wrong HTML to ASCII text serialization with format=flowed; delsp=yes";
 }
 
 // Test for CJK with format=flowed; delsp=yes
-nsresult
-TestCJKWithFlowedDelSp()
+TEST(PlainTextSerializer, CJKWithFlowedDelSp)
 {
   nsString test;
   nsString result;
 
   test.AssignLiteral("<html><body>");
   for (uint32_t i = 0; i < 40; i++) {
     // Insert Kanji (U+5341)
     test.Append(0x5341);
@@ -79,29 +70,22 @@ TestCJKWithFlowedDelSp()
     result.Append(0x5341);
   }
   result.AppendLiteral(" \r\n");
   for (uint32_t i = 0; i < 4; i++) {
     result.Append(0x5341);
   }
   result.AppendLiteral("\r\n");
 
-  if (!test.Equals(result)) {
-    fail("Wrong HTML to CJK text serialization with format=flowed; delsp=yes");
-    return NS_ERROR_FAILURE;
-  }
-
-  passed("HTML to CJK text serialization with format=flowed; delsp=yes");
-
-  return NS_OK;
+  ASSERT_TRUE(test.Equals(result)) <<
+    "Wrong HTML to CJK text serialization with format=flowed; delsp=yes";
 }
 
 // Test for CJK with DisallowLineBreaking
-nsresult
-TestCJKWithDisallowLineBreaking()
+TEST(PlainTextSerializer, CJKWithDisallowLineBreaking)
 {
   nsString test;
   nsString result;
 
   test.AssignLiteral("<html><body>");
   for (uint32_t i = 0; i < 400; i++) {
     // Insert Kanji (U+5341)
     test.Append(0x5341);
@@ -115,29 +99,22 @@ TestCJKWithDisallowLineBreaking()
                               nsIDocumentEncoder::OutputDisallowLineBreaking);
 
   // create result case
   for (uint32_t i = 0; i < 400; i++) {
     result.Append(0x5341);
   }
   result.AppendLiteral("\r\n");
 
-  if (!test.Equals(result)) {
-    fail("Wrong HTML to CJK text serialization with OutputDisallowLineBreaking");
-    return NS_ERROR_FAILURE;
-  }
-
-  passed("HTML to CJK text serialization with OutputDisallowLineBreaking");
-
-  return NS_OK;
+  ASSERT_TRUE(test.Equals(result)) <<
+    "Wrong HTML to CJK text serialization with OutputDisallowLineBreaking";
 }
 
 // Test for ASCII with format=flowed; and quoted lines in preformatted span.
-nsresult
-TestPreformatFlowedQuotes()
+TEST(PlainTextSerializer, PreformatFlowedQuotes)
 {
   nsString test;
   nsString result;
 
   test.AssignLiteral("<html><body>"
                      "<span style=\"white-space: pre-wrap;\">"
                      "&gt; Firefox Firefox Firefox Firefox <br>"
                      "&gt; Firefox Firefox Firefox Firefox<br>"
@@ -152,175 +129,103 @@ TestPreformatFlowedQuotes()
                               nsIDocumentEncoder::OutputFormatFlowed);
 
   // create result case
   result.AssignLiteral("> Firefox Firefox Firefox Firefox \r\n"
                        "> Firefox Firefox Firefox Firefox\r\n"
                        ">\r\n"
                        ">> Firefox Firefox Firefox Firefox \r\n"
                        ">> Firefox Firefox Firefox Firefox\r\n");
-  if (!test.Equals(result)) {
-    fail("Wrong HTML to ASCII text serialization with format=flowed; and quoted lines");
-    return NS_ERROR_FAILURE;
-  }
 
-  passed("HTML to ASCII text serialization with format=flowed; and quoted lines");
-
-  return NS_OK;
+  ASSERT_TRUE(test.Equals(result)) <<
+    "Wrong HTML to ASCII text serialization with format=flowed; and quoted "
+    "lines";
 }
 
-nsresult
-TestPrettyPrintedHtml()
+TEST(PlainTextSerializer, PrettyPrintedHtml)
 {
   nsString test;
   test.AppendLiteral(
     "<html>" NS_LINEBREAK
     "<body>" NS_LINEBREAK
     "  first<br>" NS_LINEBREAK
     "  second<br>" NS_LINEBREAK
     "</body>" NS_LINEBREAK "</html>");
 
   ConvertBufToPlainText(test, 0);
-  if (!test.EqualsLiteral("first" NS_LINEBREAK "second" NS_LINEBREAK)) {
-    fail("Wrong prettyprinted html to text serialization");
-    return NS_ERROR_FAILURE;
-  }
-
-  passed("prettyprinted HTML to text serialization test");
-  return NS_OK;
+  ASSERT_TRUE(test.EqualsLiteral("first" NS_LINEBREAK "second" NS_LINEBREAK)) <<
+    "Wrong prettyprinted html to text serialization";
 }
 
-nsresult
-TestPreElement()
+TEST(PlainTextSerializer, PreElement)
 {
   nsString test;
   test.AppendLiteral(
     "<html>" NS_LINEBREAK
     "<body>" NS_LINEBREAK
     "<pre>" NS_LINEBREAK
     "  first" NS_LINEBREAK
     "  second" NS_LINEBREAK
     "</pre>" NS_LINEBREAK
     "</body>" NS_LINEBREAK "</html>");
 
   ConvertBufToPlainText(test, 0);
-  if (!test.EqualsLiteral("  first" NS_LINEBREAK "  second" NS_LINEBREAK NS_LINEBREAK)) {
-    fail("Wrong prettyprinted html to text serialization");
-    return NS_ERROR_FAILURE;
-  }
-
-  passed("prettyprinted HTML to text serialization test");
-  return NS_OK;
+  ASSERT_TRUE(test.EqualsLiteral("  first" NS_LINEBREAK
+                                 "  second" NS_LINEBREAK NS_LINEBREAK)) <<
+    "Wrong prettyprinted html to text serialization";
 }
 
-nsresult
-TestBlockElement()
+TEST(PlainTextSerializer, BlockElement)
 {
   nsString test;
   test.AppendLiteral(
     "<html>" NS_LINEBREAK
     "<body>" NS_LINEBREAK
     "<div>" NS_LINEBREAK
     "  first" NS_LINEBREAK
     "</div>" NS_LINEBREAK
     "<div>" NS_LINEBREAK
     "  second" NS_LINEBREAK
     "</div>" NS_LINEBREAK
     "</body>" NS_LINEBREAK "</html>");
 
   ConvertBufToPlainText(test, 0);
-  if (!test.EqualsLiteral("first" NS_LINEBREAK "second" NS_LINEBREAK)) {
-    fail("Wrong prettyprinted html to text serialization");
-    return NS_ERROR_FAILURE;
-  }
-
-  passed("prettyprinted HTML to text serialization test");
-  return NS_OK;
+  ASSERT_TRUE(test.EqualsLiteral("first" NS_LINEBREAK "second" NS_LINEBREAK)) <<
+    "Wrong prettyprinted html to text serialization";
 }
 
-nsresult
-TestPreWrapElementForThunderbird()
+TEST(PlainTextSerializer, PreWrapElementForThunderbird)
 {
   // This test examines the magic pre-wrap setup that Thunderbird relies on.
   nsString test;
   test.AppendLiteral(
     "<html>" NS_LINEBREAK
     "<body style=\"white-space: pre-wrap; width: 10ch;\">" NS_LINEBREAK
     "<pre>" NS_LINEBREAK
     "  first line is too long" NS_LINEBREAK
     "  second line is even loooonger  " NS_LINEBREAK
     "</pre>" NS_LINEBREAK
     "</body>" NS_LINEBREAK "</html>");
 
   ConvertBufToPlainText(test, nsIDocumentEncoder::OutputWrap);
   // "\n\n  first\nline is\ntoo long\n  second\nline is\neven\nloooonger\n\n\n"
-  if (!test.EqualsLiteral(NS_LINEBREAK NS_LINEBREAK
-                          "  first" NS_LINEBREAK
-                          "line is" NS_LINEBREAK
-                          "too long" NS_LINEBREAK
-                          "  second" NS_LINEBREAK
-                          "line is" NS_LINEBREAK
-                          "even" NS_LINEBREAK
-                          "loooonger" NS_LINEBREAK
-                          NS_LINEBREAK NS_LINEBREAK)) {
-    fail("Wrong prettyprinted html to text serialization");
-    return NS_ERROR_FAILURE;
-  }
-
-  passed("prettyprinted HTML to text serialization test");
-  return NS_OK;
+  ASSERT_TRUE(test.EqualsLiteral(NS_LINEBREAK NS_LINEBREAK
+                                 "  first" NS_LINEBREAK
+                                 "line is" NS_LINEBREAK
+                                 "too long" NS_LINEBREAK
+                                 "  second" NS_LINEBREAK
+                                 "line is" NS_LINEBREAK
+                                 "even" NS_LINEBREAK
+                                 "loooonger" NS_LINEBREAK
+                                 NS_LINEBREAK NS_LINEBREAK)) <<
+    "Wrong prettyprinted html to text serialization";
 }
 
-nsresult
-TestPlainTextSerializer()
+TEST(PlainTextSerializer, Simple)
 {
   nsString test;
   test.AppendLiteral("<html><base>base</base><head><span>span</span></head>"
                      "<body>body</body></html>");
   ConvertBufToPlainText(test, 0);
-  if (!test.EqualsLiteral("basespanbody")) {
-    fail("Wrong html to text serialization");
-    return NS_ERROR_FAILURE;
-  }
-
-  passed("HTML to text serialization test");
-
-  nsresult rv = TestASCIIWithFlowedDelSp();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = TestCJKWithFlowedDelSp();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = TestPrettyPrintedHtml();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = TestPreElement();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = TestBlockElement();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = TestPreWrapElementForThunderbird();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = TestCJKWithDisallowLineBreaking();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = TestPreformatFlowedQuotes();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Add new tests here...
-  return NS_OK;
+  ASSERT_TRUE(test.EqualsLiteral("basespanbody")) <<
+    "Wrong html to text serialization";
 }
 
-int main(int argc, char** argv)
-{
-  ScopedXPCOM xpcom("PlainTextSerializer");
-  if (xpcom.failed())
-    return 1;
-
-  int retval = 0;
-  if (NS_FAILED(TestPlainTextSerializer())) {
-    retval = 1;
-  }
-
-  return retval;
-}
--- a/dom/base/test/gtest/moz.build
+++ b/dom/base/test/gtest/moz.build
@@ -1,16 +1,18 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, you can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
+    'TestNativeXMLHttpRequest.cpp',
     'TestParserDialogOptions.cpp',
+    'TestPlainTextSerializer.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base'
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
--- a/dom/base/test/moz.build
+++ b/dom/base/test/moz.build
@@ -4,22 +4,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/.
 
 XPCSHELL_TESTS_MANIFESTS += [
     'unit/xpcshell.ini',
     'unit_ipc/xpcshell.ini',
 ]
 
-GeckoCppUnitTests([
-    'TestGetURL',
-    'TestNativeXMLHttpRequest',
-    'TestPlainTextSerializer',
-])
-
 MOCHITEST_MANIFESTS += [
     'mochitest.ini',
     'websocket_hybi/mochitest.ini',
 ]
 
 MOCHITEST_CHROME_MANIFESTS += [
     'chrome.ini',
     'chrome/chrome.ini',
--- a/dom/canvas/WebGLProgram.cpp
+++ b/dom/canvas/WebGLProgram.cpp
@@ -901,16 +901,22 @@ WebGLProgram::GetUniformLocation(const n
                                                                    info, loc, arrayIndex);
     return locObj.forget();
 }
 
 void
 WebGLProgram::GetUniformIndices(const dom::Sequence<nsString>& uniformNames,
                                 dom::Nullable< nsTArray<GLuint> >& retval) const
 {
+    const char funcName[] = "getUniformIndices";
+    if (!IsLinked()) {
+        mContext->ErrorInvalidOperation("%s: `program` must be linked.", funcName);
+        return;
+    }
+
     size_t count = uniformNames.Length();
     nsTArray<GLuint>& arr = retval.SetValue();
 
     gl::GLContext* gl = mContext->GL();
     gl->MakeCurrent();
 
     for (size_t i = 0; i < count; i++) {
         const NS_LossyConvertUTF16toASCII userName(uniformNames[i]);
--- a/dom/canvas/WebGLSampler.cpp
+++ b/dom/canvas/WebGLSampler.cpp
@@ -80,16 +80,24 @@ WebGLSampler::SamplerParameter1i(GLenum 
     case LOCAL_GL_TEXTURE_COMPARE_MODE:
         mCompareMode = param;
         break;
 
     case LOCAL_GL_TEXTURE_COMPARE_FUNC:
         mCompareFunc = param;
         break;
 
+    case LOCAL_GL_TEXTURE_MIN_LOD:
+        mMinLod = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_MAX_LOD:
+        mMaxLod = param;
+        break;
+
     default:
         MOZ_CRASH("GFX: Unhandled pname");
         break;
     }
 
     for (uint32_t i = 0; i < mContext->mBoundSamplers.Length(); ++i) {
         if (this == mContext->mBoundSamplers[i])
             mContext->InvalidateResolveCacheForTextureWithTexUnit(i);
@@ -103,16 +111,44 @@ WebGLSampler::SamplerParameter1f(GLenum 
     case LOCAL_GL_TEXTURE_MIN_LOD:
         mMinLod = param;
         break;
 
     case LOCAL_GL_TEXTURE_MAX_LOD:
         mMaxLod = param;
         break;
 
+    case LOCAL_GL_TEXTURE_WRAP_S:
+        mWrapS = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_WRAP_T:
+        mWrapT = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_WRAP_R:
+        mWrapR = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_MAG_FILTER:
+        mMagFilter = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_MIN_FILTER:
+        mMinFilter = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_COMPARE_MODE:
+        mCompareMode = param;
+        break;
+
+    case LOCAL_GL_TEXTURE_COMPARE_FUNC:
+        mCompareFunc = param;
+        break;
+
     default:
         MOZ_CRASH("GFX: Unhandled pname");
         break;
     }
 
     for (uint32_t i = 0; i < mContext->mBoundSamplers.Length(); ++i) {
         if (this == mContext->mBoundSamplers[i])
             mContext->InvalidateResolveCacheForTextureWithTexUnit(i);
deleted file mode 100644
--- a/dom/canvas/compiledtest/moz.build
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
-# vim: set filetype=python:
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-GeckoCppUnitTests([
-    'TestWebGLElementArrayCache',
-])
-
-LOCAL_INCLUDES += [
-    '../',
-]
rename from dom/canvas/compiledtest/TestWebGLElementArrayCache.cpp
rename to dom/canvas/gtest/TestWebGLElementArrayCache.cpp
--- a/dom/canvas/compiledtest/TestWebGLElementArrayCache.cpp
+++ b/dom/canvas/gtest/TestWebGLElementArrayCache.cpp
@@ -1,39 +1,23 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Assertions.h"
 
-#include "WebGLElementArrayCache.cpp"
+#include "WebGLElementArrayCache.h"
 
 #include <cstdio>
 #include <cstdlib>
 #include "nscore.h"
 #include "nsTArray.h"
 
-int gTestsPassed = 0;
-
-void
-VerifyImplFunction(bool condition, const char* file, int line)
-{
-  if (condition) {
-    gTestsPassed++;
-  } else {
-    std::fprintf(stderr, "Test failed at %s:%d\n", file, line);
-    abort();
-  }
-}
-
-#define VERIFY(condition) \
-    VerifyImplFunction((condition), __FILE__, __LINE__)
-
 void
 MakeRandomVector(nsTArray<uint8_t>& a, size_t size)
 {
   a.SetLength(size);
   // only the most-significant bits of rand() are reasonably random.
   // RAND_MAX can be as low as 0x7fff, and we need 8 bits for the result, so we can only
   // ignore the 7 least significant bits.
   for (size_t i = 0; i < size; i++)
@@ -52,27 +36,27 @@ template<typename T>
 GLenum
 GLType()
 {
   switch (sizeof(T)) {
   case 4:  return LOCAL_GL_UNSIGNED_INT;
   case 2:  return LOCAL_GL_UNSIGNED_SHORT;
   case 1:  return LOCAL_GL_UNSIGNED_BYTE;
   default:
-    VERIFY(false);
+    MOZ_RELEASE_ASSERT(false);
     return 0;
   }
 }
 
 void
 CheckValidate(bool expectSuccess, mozilla::WebGLElementArrayCache& c, GLenum type,
               uint32_t maxAllowed, size_t first, size_t count)
 {
   const bool success = c.Validate(type, maxAllowed, first, count);
-  VERIFY(success == expectSuccess);
+  ASSERT_TRUE(success == expectSuccess);
 }
 
 template<typename T>
 void
 CheckValidateOneTypeVariousBounds(mozilla::WebGLElementArrayCache& c, size_t firstByte,
                                   size_t countBytes)
 {
   size_t first = firstByte / sizeof(T);
@@ -105,17 +89,17 @@ void CheckValidateAllTypes(mozilla::WebG
 template<typename T>
 void
 CheckSanity()
 {
   const size_t numElems = 64; // should be significantly larger than tree leaf size to
                         // ensure we exercise some nontrivial tree-walking
   T data[numElems] = {1,0,3,1,2,6,5,4}; // intentionally specify only 8 elements for now
   size_t numBytes = numElems * sizeof(T);
-  MOZ_RELEASE_ASSERT(numBytes == sizeof(data), "GFX: number of bytes from size of each element * number of elements equals size of data.");
+  ASSERT_TRUE(numBytes == sizeof(data));
 
   GLenum type = GLType<T>();
 
   mozilla::WebGLElementArrayCache c;
   c.BufferData(data, numBytes);
   CheckValidate(true,  c, type, 6, 0, 8);
   CheckValidate(false, c, type, 5, 0, 8);
   CheckValidate(true,  c, type, 3, 0, 3);
@@ -129,52 +113,51 @@ CheckSanity()
 
   // now test a somewhat larger size to ensure we exceed the size of a tree leaf
   for(size_t i = 0; i < numElems; i++)
     data[i] = numElems - i;
   c.BufferData(data, numBytes);
   CheckValidate(true,  c, type, numElems,     0, numElems);
   CheckValidate(false, c, type, numElems - 1, 0, numElems);
 
-  MOZ_RELEASE_ASSERT(numElems > 10, "GFX: Less than 10 elements in array cache");
+  ASSERT_TRUE(numElems > 10);
   CheckValidate(true,  c, type, numElems - 10, 10, numElems - 10);
   CheckValidate(false, c, type, numElems - 11, 10, numElems - 10);
 }
 
 template<typename T>
 void
 CheckUintOverflow()
 {
   // This test is only for integer types smaller than uint32_t
   static_assert(sizeof(T) < sizeof(uint32_t), "This test is only for integer types \
                 smaller than uint32_t");
 
   const size_t numElems = 64; // should be significantly larger than tree leaf size to
                               // ensure we exercise some nontrivial tree-walking
   T data[numElems];
   size_t numBytes = numElems * sizeof(T);
-  MOZ_RELEASE_ASSERT(numBytes == sizeof(data), "GFX: size of data doesnt equal number of bytes of each element multiplied by number of elements.");
+  ASSERT_TRUE(numBytes == sizeof(data));
 
   GLenum type = GLType<T>();
 
   mozilla::WebGLElementArrayCache c;
 
   for(size_t i = 0; i < numElems; i++)
     data[i] = numElems - i;
   c.BufferData(data, numBytes);
 
   // bug 825205
   uint32_t bigValWrappingToZero = uint32_t(T(-1)) + 1;
   CheckValidate(true,  c, type, bigValWrappingToZero,     0, numElems);
   CheckValidate(true,  c, type, bigValWrappingToZero - 1, 0, numElems);
   CheckValidate(false, c, type,                        0, 0, numElems);
 }
 
-int
-main(int argc, char* argv[])
+TEST(WebGLElementArrayCache, Test)
 {
   srand(0); // do not want a random seed here.
 
   CheckSanity<uint8_t>();
   CheckSanity<uint16_t>();
   CheckSanity<uint32_t>();
 
   CheckUintOverflow<uint8_t>();
@@ -214,13 +197,10 @@ main(int argc, char* argv[])
               subsize = RandomInteger<size_t>(0, size - offset);
               CheckValidateAllTypes(b, offset, subsize);
             }
           } // validateCalls
         } // bufferSubDataCalls
       } // j
     } // i
   } // maxBufferSize
-
-  std::fprintf(stderr, "%s: all %d tests passed\n", argv[0], gTestsPassed);
-  return 0;
 }
 
--- a/dom/canvas/gtest/moz.build
+++ b/dom/canvas/gtest/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 UNIFIED_SOURCES += [
     'TestImageBitmapColorUtils.cpp',
+    'TestWebGLElementArrayCache.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/canvas',
     '/media/libyuv/include'
 ]
 
 FINAL_LIBRARY = 'xul-gtest'
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -1,16 +1,15 @@
 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 TEST_DIRS += [
-    'compiledtest',
     'gtest'
 ]
 
 # Change the following line(s) to avoid bug 1081323 (clobber after changing a manifest):
 # * Implement ReadPixel with PBOs.
 
 MOCHITEST_MANIFESTS += [
     'test/crash/mochitest.ini',
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -31,17 +31,17 @@ LazyLogModule gCubebLog("cubeb");
 
 void CubebLogCallback(const char* aFmt, ...)
 {
   char buffer[256];
 
   va_list arglist;
   va_start(arglist, aFmt);
   VsprintfLiteral (buffer, aFmt, arglist);
-  MOZ_LOG(gCubebLog, LogLevel::Verbose, ("%s", buffer));
+  MOZ_LOG(gCubebLog, LogLevel::Error, ("%s", buffer));
   va_end(arglist);
 }
 
 // This mutex protects the variables below.
 StaticMutex sMutex;
 enum class CubebState {
   Uninitialized = 0,
   Initialized,
rename from dom/security/test/TestCSPParser.cpp
rename to dom/security/test/gtest/TestCSPParser.cpp
--- a/dom/security/test/TestCSPParser.cpp
+++ b/dom/security/test/gtest/TestCSPParser.cpp
@@ -1,14 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+#include "gtest/gtest.h"
+
 #include <string.h>
 #include <stdlib.h>
 
 #ifndef MOZILLA_INTERNAL_API
 // some of the includes make use of internal string types
 #define nsAString_h___
 #define nsString_h___
 #define nsStringFwd_h___
@@ -20,17 +22,16 @@ class nsAFlatCString;
 class nsAdoptingString;
 class nsAdoptingCString;
 class nsXPIDLString;
 template<class T> class nsReadingIterator;
 #endif
 
 #include "nsIContentSecurityPolicy.h"
 #include "nsNetUtil.h"
-#include "TestHarness.h"
 #include "nsIScriptSecurityManager.h"
 #include "mozilla/dom/nsCSPContext.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
 
 #ifndef MOZILLA_INTERNAL_API
 #undef nsString_h___
 #undef nsAString_h___
@@ -85,17 +86,17 @@ static const uint32_t kMaxPolicyLength =
 struct PolicyTest
 {
   char policy[kMaxPolicyLength];
   char expectedResult[kMaxPolicyLength];
 };
 
 nsresult runTest(uint32_t aExpectedPolicyCount, // this should be 0 for policies which should fail to parse
                  const char* aPolicy,
-                 const char* aExpextedResult) {
+                 const char* aExpectedResult) {
 
   nsresult rv;
   nsCOMPtr<nsIScriptSecurityManager> secman =
     do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // we init the csp with http://www.selfuri.com
   nsCOMPtr<nsIURI> selfURI;
@@ -130,37 +131,41 @@ nsresult runTest(uint32_t aExpectedPolic
     return NS_OK;
   }
 
   // verify that the expected number of policies exists
   uint32_t actualPolicyCount;
   rv = csp->GetPolicyCount(&actualPolicyCount);
   NS_ENSURE_SUCCESS(rv, rv);
   if (actualPolicyCount != aExpectedPolicyCount) {
-    fail("Actual policy count not equal to expected policy count (%d != %d) for policy: %s",
-          actualPolicyCount, aExpectedPolicyCount, aPolicy);
+    EXPECT_TRUE(false) <<
+      "Actual policy count not equal to expected policy count (" <<
+      actualPolicyCount << " != " << aExpectedPolicyCount <<
+      ") for policy: " << aPolicy;
     return NS_ERROR_UNEXPECTED;
   }
 
   // if the expected policy count is 0, we can return, because
   // we can not compare any output anyway. Used when parsing
   // errornous policies.
   if (aExpectedPolicyCount == 0) {
     return NS_OK;
   }
 
   // compare the parsed policy against the expected result
   nsString parsedPolicyStr;
   // checking policy at index 0, which is the one what we appended.
   rv = csp->GetPolicyString(0, parsedPolicyStr);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  if (!NS_ConvertUTF16toUTF8(parsedPolicyStr).EqualsASCII(aExpextedResult)) {
-    fail("Actual policy does not match expected policy  (%s != %s)",
-          NS_ConvertUTF16toUTF8(parsedPolicyStr).get(), aExpextedResult);
+  if (!NS_ConvertUTF16toUTF8(parsedPolicyStr).EqualsASCII(aExpectedResult)) {
+    EXPECT_TRUE(false) <<
+      "Actual policy does not match expected policy (" <<
+      NS_ConvertUTF16toUTF8(parsedPolicyStr).get() << " != " <<
+      aExpectedResult << ")";
     return NS_ERROR_UNEXPECTED;
   }
 
   return NS_OK;
 }
 
 // ============================= run Tests ========================
 
@@ -185,18 +190,18 @@ nsresult runTestSuite(const PolicyTest* 
     prefs->SetBoolPref("security.csp.experimentalEnabled", experimentalEnabledCache);
   }
 
   return NS_OK;
 }
 
 // ============================= TestDirectives ========================
 
-nsresult TestDirectives() {
-
+TEST(CSPParser, Directives)
+{
   static const PolicyTest policies[] =
   {
     { "default-src http://www.example.com",
       "default-src http://www.example.com" },
     { "script-src http://www.example.com",
       "script-src http://www.example.com" },
     { "object-src http://www.example.com",
       "object-src http://www.example.com" },
@@ -220,23 +225,23 @@ nsresult TestDirectives() {
       "script-src 'sha256-siVR8vAcqP06h2ppeNwqgjr0yZ6yned4X2VF84j4GmI='" },
     { "referrer no-referrer",
       "referrer no-referrer" },
     { "require-sri-for script style",
       "require-sri-for script style"}
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
-  return runTestSuite(policies, policyCount, 1);
+  ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
 }
 
 // ============================= TestKeywords ========================
 
-nsresult TestKeywords() {
-
+TEST(CSPParser, Keywords)
+{
   static const PolicyTest policies[] =
   {
     { "script-src 'self'",
       "script-src http://www.selfuri.com" },
     { "script-src 'unsafe-inline'",
       "script-src 'unsafe-inline'" },
     { "script-src 'unsafe-eval'",
       "script-src 'unsafe-eval'" },
@@ -244,23 +249,23 @@ nsresult TestKeywords() {
       "script-src 'unsafe-inline' 'unsafe-eval'" },
     { "script-src 'none'",
       "script-src 'none'" },
     { "img-src 'none'; script-src 'unsafe-eval' 'unsafe-inline'; default-src 'self'",
       "img-src 'none'; script-src 'unsafe-eval' 'unsafe-inline'; default-src http://www.selfuri.com" },
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
-  return runTestSuite(policies, policyCount, 1);
+  ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
 }
 
 // ============================= TestIgnoreUpperLowerCasePolicies ========================
 
-nsresult TestIgnoreUpperLowerCasePolicies() {
-
+TEST(CSPParser, IgnoreUpperLowerCasePolicies)
+{
   static const PolicyTest policies[] =
   {
     { "script-src 'SELF'",
       "script-src http://www.selfuri.com" },
     { "sCriPt-src 'Unsafe-Inline'",
       "script-src 'unsafe-inline'" },
     { "SCRIPT-src 'unsafe-eval'",
       "script-src 'unsafe-eval'" },
@@ -290,23 +295,23 @@ nsresult TestIgnoreUpperLowerCasePolicie
       "upgrade-insecure-requests" },
     { "sanDBox alloW-foRMs",
       "sandbox allow-forms"},
     { "require-SRI-for sCript stYle",
       "require-sri-for script style"},
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
-  return runTestSuite(policies, policyCount, 1);
+  ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
 }
 
 // ============================= TestPaths ========================
 
-nsresult TestPaths() {
-
+TEST(CSPParser, Paths)
+{
   static const PolicyTest policies[] =
   {
     { "script-src http://www.example.com",
       "script-src http://www.example.com" },
     { "script-src http://www.example.com/",
       "script-src http://www.example.com/" },
     { "script-src http://www.example.com/path-1",
       "script-src http://www.example.com/path-1" },
@@ -388,23 +393,23 @@ nsresult TestPaths() {
       "connect-src http://test.com/pathIncludingAz19-._~!$&'()*+=:@" },
     { "script-src http://www.example.com:88/.js",
       "script-src http://www.example.com:88/.js" },
     { "script-src https://foo.com/_abc/abc_/_/_a_b_c_",
       "script-src https://foo.com/_abc/abc_/_/_a_b_c_" }
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
-  return runTestSuite(policies, policyCount, 1);
+  ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
 }
 
 // ============================= TestSimplePolicies ========================
 
-nsresult TestSimplePolicies() {
-
+TEST(CSPParser, SimplePolicies)
+{
   static const PolicyTest policies[] =
   {
     { "default-src *",
       "default-src *" },
     { "default-src https:",
       "default-src https:" },
     { "default-src https://*",
       "default-src https://*" },
@@ -466,23 +471,23 @@ nsresult TestSimplePolicies() {
       "upgrade-insecure-requests" },
     { "upgrade-insecure-requests https:",
       "upgrade-insecure-requests" },
     { "sandbox allow-scripts allow-forms  ",
       "sandbox allow-scripts allow-forms" },
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
-  return runTestSuite(policies, policyCount, 1);
+  ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
 }
 
 // ============================= TestPoliciesWithInvalidSrc ========================
 
-nsresult TestPoliciesWithInvalidSrc() {
-
+TEST(CSPParser, PoliciesWithInvalidSrc)
+{
   static const PolicyTest policies[] =
   {
     { "script-src 'self'; SCRIPT-SRC http://www.example.com",
       "script-src http://www.selfuri.com" },
     { "script-src 'none' test.com; script-src example.com",
       "script-src http://test.com" },
     { "default-src **",
       "default-src 'none'" },
@@ -533,23 +538,23 @@ nsresult TestPoliciesWithInvalidSrc() {
     { "require-SRI-for script elephants",
       "require-sri-for script"},
     { "sandbox    foo",
       "sandbox"},
   };
 
   // amount of tests - 1, because the latest should be ignored.
   uint32_t policyCount = (sizeof(policies) / sizeof(PolicyTest)) -1;
-  return runTestSuite(policies, policyCount, 1);
+  ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
 }
 
 // ============================= TestBadPolicies ========================
 
-nsresult TestBadPolicies() {
-
+TEST(CSPParser, BadPolicies)
+{
   static const PolicyTest policies[] =
   {
     { "script-sr 'self", "" },
     { "", "" },
     { "; ; ; ; ; ; ;", "" },
     { "defaut-src asdf", "" },
     { "default-src: aaa", "" },
     { "asdf http://test.com", ""},
@@ -557,23 +562,23 @@ nsresult TestBadPolicies() {
     { "referrer foo", ""},
     { "require-sri-for", ""},
     { "require-sri-for foo", ""},
     { "report-uri", ""},
     { "report-uri http://:foo", ""},
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
-  return runTestSuite(policies, policyCount, 0);
+  ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 0)));
 }
 
 // ============================= TestGoodGeneratedPolicies ========================
 
-nsresult TestGoodGeneratedPolicies() {
-
+TEST(CSPParser, GoodGeneratedPolicies)
+{
   static const PolicyTest policies[] =
   {
     { "default-src 'self'; img-src *",
       "default-src http://www.selfuri.com; img-src *" },
     { "report-uri /policy",
       "report-uri http://www.selfuri.com/policy"},
     { "img-src *",
       "img-src *" },
@@ -791,23 +796,23 @@ nsresult TestGoodGeneratedPolicies() {
       "frame-ancestors http://self.com/bar" },
     { "default-src 'self'; frame-ancestors 'self'",
       "default-src http://www.selfuri.com; frame-ancestors http://www.selfuri.com" },
     { "frame-ancestors http://bar.com/foo.png",
       "frame-ancestors http://bar.com/foo.png" },
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
-  return runTestSuite(policies, policyCount, 1);
+  ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
 }
 
 // ============================= TestBadGeneratedPolicies ========================
 
-nsresult TestBadGeneratedPolicies() {
-
+TEST(CSPParser, BadGeneratedPolicies)
+{
   static const PolicyTest policies[] =
   {
     { "foo.*.bar", ""},
     { "foo!bar.com", ""},
     { "x.*.a.com", ""},
     { "a#2-c.com", ""},
     { "http://foo.com:bar.com:23", ""},
     { "f!oo.bar", ""},
@@ -817,22 +822,23 @@ nsresult TestBadGeneratedPolicies() {
     { "*a", ""},
     { "http://username:password@self.com/foo", ""},
     { "http://other:pass1@self.com/foo", ""},
     { "http://user1:pass1@self.com/foo", ""},
     { "http://username:password@self.com/bar", ""},
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
-  return runTestSuite(policies, policyCount, 0);
+  ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 0)));
 }
 
 // ============ TestGoodGeneratedPoliciesForPathHandling ============
 
-nsresult TestGoodGeneratedPoliciesForPathHandling() {
+TEST(CSPParser, GoodGeneratedPoliciesForPathHandling)
+{
   // Once bug 808292 (Implement path-level host-source matching to CSP)
   // lands we have to update the expected output to include the parsed path
 
   static const PolicyTest policies[] =
   {
     { "img-src http://test1.example.com",
       "img-src http://test1.example.com" },
     { "img-src http://test1.example.com/",
@@ -939,23 +945,23 @@ nsresult TestGoodGeneratedPoliciesForPat
       "img-src https://test1.example.com:80/abc/def/ghi//" },
     { "img-src https://test1.example.com/abc////////////def/",
       "img-src https://test1.example.com/abc////////////def/" },
     { "img-src https://test1.example.com/abc////////////",
       "img-src https://test1.example.com/abc////////////" },
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
-  return runTestSuite(policies, policyCount, 1);
+  ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
 }
 
 // ============ TestBadGeneratedPoliciesForPathHandling ============
 
-nsresult TestBadGeneratedPoliciesForPathHandling() {
-
+TEST(CSPParser, BadGeneratedPoliciesForPathHandling)
+{
   static const PolicyTest policies[] =
   {
     { "img-src test1.example.com:88path-1/",
       "img-src 'none'" },
     { "img-src test1.example.com:80.js",
       "img-src 'none'" },
     { "img-src test1.example.com:*.js",
       "img-src 'none'" },
@@ -965,94 +971,90 @@ nsresult TestBadGeneratedPoliciesForPath
       "img-src 'none'" },
     { "img-src http://test1.example.com:80//",
       "img-src 'none'" },
     { "img-src http://test1.example.com:80abc",
       "img-src 'none'" },
   };
 
   uint32_t policyCount = sizeof(policies) / sizeof(PolicyTest);
-  return runTestSuite(policies, policyCount, 1);
+  ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(policies, policyCount, 1)));
 }
 
 // ============================= TestFuzzyPolicies ========================
 
 // Use a policy, eliminate one character at a time,
 // and feed it as input to the parser.
 
-nsresult TestShorteningPolicies() {
-
+TEST(CSPParser, ShorteningPolicies)
+{
   char pol[] = "default-src http://www.sub1.sub2.example.com:88/path1/path2/ 'unsafe-inline' 'none'";
   uint32_t len = static_cast<uint32_t>(sizeof(pol));
 
   PolicyTest testPol[1];
   memset(&testPol[0].policy, '\0', kMaxPolicyLength * sizeof(char));
-  nsresult rv = NS_OK;
 
   while (--len) {
     memset(&testPol[0].policy, '\0', kMaxPolicyLength * sizeof(char));
     memcpy(&testPol[0].policy, &pol, len * sizeof(char));
-    rv = runTestSuite(testPol, 1, kFuzzyExpectedPolicyCount);
-    NS_ENSURE_SUCCESS(rv, rv);
+    ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(testPol, 1,
+                                          kFuzzyExpectedPolicyCount)));
   }
-  return NS_OK;
 }
 
 // ============================= TestFuzzyPolicies ========================
 
 // We generate kFuzzyRuns inputs by (pseudo) randomly picking from the 128
 // ASCII characters; feed them to the parser and verfy that the parser
 // handles the input gracefully.
 //
 // Please note, that by using srand(0) we get deterministic results!
 
 #if RUN_OFFLINE_TESTS
 
-nsresult TestFuzzyPolicies() {
-
+TEST(CSPParser, FuzzyPolicies)
+{
   // init srand with 0 so we get same results
   srand(0);
 
   PolicyTest testPol[1];
   memset(&testPol[0].policy, '\0', kMaxPolicyLength);
-  nsresult rv = NS_OK;
 
   for (uint32_t index = 0; index < kFuzzyRuns; index++) {
     // randomly select the length of the next policy
     uint32_t polLength = rand() % kMaxPolicyLength;
     // reset memory of the policy string
     memset(&testPol[0].policy, '\0', kMaxPolicyLength * sizeof(char));
 
     for (uint32_t i = 0; i < polLength; i++) {
       // fill the policy array with random ASCII chars
       testPol[0].policy[i] = static_cast<char>(rand() % 128);
     }
-    rv = runTestSuite(testPol, 1, kFuzzyExpectedPolicyCount);
-    NS_ENSURE_SUCCESS(rv, rv);
+    ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(testPol, 1,
+                                          kFuzzyExpectedPolicyCount)));
   }
-  return NS_OK;
 }
 
 #endif
+
 // ============================= TestFuzzyPoliciesIncDir ========================
 
 // In a similar fashion as in TestFuzzyPolicies, we again (pseudo) randomly
 // generate input for the parser, but this time also include a valid directive
 // followed by the random input.
 
 #if RUN_OFFLINE_TESTS
 
-nsresult TestFuzzyPoliciesIncDir() {
-
+TEST(CSPParser, FuzzyPoliciesIncDir)
+{
   // init srand with 0 so we get same results
   srand(0);
 
   PolicyTest testPol[1];
   memset(&testPol[0].policy, '\0', kMaxPolicyLength);
-  nsresult rv = NS_OK;
 
   char defaultSrc[] = "default-src ";
   int defaultSrcLen = sizeof(defaultSrc) - 1;
   // copy default-src into the policy array
   memcpy(&testPol[0].policy, &defaultSrc, (defaultSrcLen * sizeof(char)));
 
   for (uint32_t index = 0; index < kFuzzyRuns; index++) {
     // randomly select the length of the next policy
@@ -1061,44 +1063,42 @@ nsresult TestFuzzyPoliciesIncDir() {
     memset((&(testPol[0].policy) + (defaultSrcLen * sizeof(char))),
            '\0', (kMaxPolicyLength - defaultSrcLen) * sizeof(char));
 
     // do not start at index 0 so we do not overwrite 'default-src'
     for (uint32_t i = defaultSrcLen; i < polLength; i++) {
       // fill the policy array with random ASCII chars
       testPol[0].policy[i] = static_cast<char>(rand() % 128);
     }
-    rv = runTestSuite(testPol, 1, kFuzzyExpectedPolicyCount);
-    NS_ENSURE_SUCCESS(rv, rv);
+    ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(testPol, 1,
+                                          kFuzzyExpectedPolicyCount)));
   }
-  return NS_OK;
 }
 
 #endif
 
 // ============================= TestFuzzyPoliciesIncDirLimASCII ==============
 
 // Same as TestFuzzyPoliciesIncDir() but using limited ASCII,
 // which represents more likely input.
 
 #if RUN_OFFLINE_TESTS
 
-nsresult TestFuzzyPoliciesIncDirLimASCII() {
-
+TEST(CSPParser, FuzzyPoliciesIncDirLimASCII)
+{
   char input[] = "1234567890" \
                  "abcdefghijklmnopqrstuvwxyz" \
                  "ABCDEFGHIJKLMNOPQRSTUVWZYZ" \
                  "!@#^&*()-+_=";
 
   // init srand with 0 so we get same results
   srand(0);
 
   PolicyTest testPol[1];
   memset(&testPol[0].policy, '\0', kMaxPolicyLength);
-  nsresult rv = NS_OK;
 
   char defaultSrc[] = "default-src ";
   int defaultSrcLen = sizeof(defaultSrc) - 1;
   // copy default-src into the policy array
   memcpy(&testPol[0].policy, &defaultSrc, (defaultSrcLen * sizeof(char)));
 
   for (uint32_t index = 0; index < kFuzzyRuns; index++) {
     // randomly select the length of the next policy
@@ -1108,45 +1108,14 @@ nsresult TestFuzzyPoliciesIncDirLimASCII
            '\0', (kMaxPolicyLength - defaultSrcLen) * sizeof(char));
 
     // do not start at index 0 so we do not overwrite 'default-src'
     for (uint32_t i = defaultSrcLen; i < polLength; i++) {
       // fill the policy array with chars from the pre-defined input
       uint32_t inputIndex = rand() % sizeof(input);
       testPol[0].policy[i] = input[inputIndex];
     }
-    rv = runTestSuite(testPol, 1, kFuzzyExpectedPolicyCount);
-    NS_ENSURE_SUCCESS(rv, rv);
+    ASSERT_TRUE(NS_SUCCEEDED(runTestSuite(testPol, 1,
+                                          kFuzzyExpectedPolicyCount)));
   }
-  return NS_OK;
 }
 #endif
 
-// ============================= Run the tests ========================
-
-int main(int argc, char** argv) {
-
-  ScopedXPCOM xpcom("ContentSecurityPolicyParser");
-  if (xpcom.failed()) {
-    return 1;
-  }
-
-  if (NS_FAILED(TestDirectives()))                           { return 1; }
-  if (NS_FAILED(TestKeywords()))                             { return 1; }
-  if (NS_FAILED(TestIgnoreUpperLowerCasePolicies()))         { return 1; }
-  if (NS_FAILED(TestPaths()))                                { return 1; }
-  if (NS_FAILED(TestSimplePolicies()))                       { return 1; }
-  if (NS_FAILED(TestPoliciesWithInvalidSrc()))               { return 1; }
-  if (NS_FAILED(TestBadPolicies()))                          { return 1; }
-  if (NS_FAILED(TestGoodGeneratedPolicies()))                { return 1; }
-  if (NS_FAILED(TestBadGeneratedPolicies()))                 { return 1; }
-  if (NS_FAILED(TestGoodGeneratedPoliciesForPathHandling())) { return 1; }
-  if (NS_FAILED(TestBadGeneratedPoliciesForPathHandling()))  { return 1; }
-  if (NS_FAILED(TestShorteningPolicies()))                   { return 1; }
-
-#if RUN_OFFLINE_TESTS
-  if (NS_FAILED(TestFuzzyPolicies()))                        { return 1; }
-  if (NS_FAILED(TestFuzzyPoliciesIncDir()))                  { return 1; }
-  if (NS_FAILED(TestFuzzyPoliciesIncDirLimASCII()))          { return 1; }
-#endif
-
-  return 0;
-}
new file mode 100644
--- /dev/null
+++ b/dom/security/test/gtest/moz.build
@@ -0,0 +1,11 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+     'TestCSPParser.cpp',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
--- a/dom/security/test/moz.build
+++ b/dom/security/test/moz.build
@@ -3,19 +3,19 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPCSHELL_TESTS_MANIFESTS += [
     'unit/xpcshell.ini',
 ]
 
-GeckoCppUnitTests([
-     'TestCSPParser',
-])
+TEST_DIRS += [
+    'gtest',
+]
 
 MOCHITEST_MANIFESTS += [
     'cors/mochitest.ini',
     'csp/mochitest.ini',
     'general/mochitest.ini',
     'mixedcontentblocker/mochitest.ini',
     'sri/mochitest.ini',
 ]
--- a/gfx/layers/ipc/LayerTransactionChild.cpp
+++ b/gfx/layers/ipc/LayerTransactionChild.cpp
@@ -29,18 +29,16 @@ LayerTransactionChild::Destroy()
   }
   // mDestroyed is used to prevent calling Send__delete__() twice.
   // When this function is called from CompositorBridgeChild::Destroy(),
   // under Send__delete__() call, this function is called from
   // ShadowLayerForwarder's destructor.
   // When it happens, IPCOpen() is still true.
   // See bug 1004191.
   mDestroyed = true;
-  MOZ_ASSERT(0 == ManagedPLayerChild().Count(),
-             "layers should have been cleaned up by now");
 
   SendShutdown();
 }
 
 
 PLayerChild*
 LayerTransactionChild::AllocPLayerChild()
 {
--- a/gfx/layers/ipc/ShadowLayerChild.cpp
+++ b/gfx/layers/ipc/ShadowLayerChild.cpp
@@ -7,30 +7,37 @@
 
 #include "ShadowLayerChild.h"
 #include "Layers.h"                     // for Layer
 #include "ShadowLayers.h"               // for ShadowableLayer
 
 namespace mozilla {
 namespace layers {
 
-ShadowLayerChild::ShadowLayerChild(ShadowableLayer* aLayer)
-  : mLayer(aLayer)
+ShadowLayerChild::ShadowLayerChild()
+  : mLayer(nullptr)
 { }
 
 ShadowLayerChild::~ShadowLayerChild()
 { }
 
 void
+ShadowLayerChild::SetShadowableLayer(ShadowableLayer* aLayer)
+{
+  MOZ_ASSERT(!mLayer);
+  mLayer = aLayer;
+}
+
+void
 ShadowLayerChild::ActorDestroy(ActorDestroyReason why)
 {
   MOZ_ASSERT(AncestorDeletion != why,
              "shadowable layer should have been cleaned up by now");
 
-  if (AbnormalShutdown == why) {
+  if (AbnormalShutdown == why && mLayer) {
     // This is last-ditch emergency shutdown.  Just have the layer
     // forget its IPDL resources; IPDL-generated code will clean up
     // automatically in this case.
     mLayer->AsLayer()->Disconnect();
     mLayer = nullptr;
   }
 }
 
--- a/gfx/layers/ipc/ShadowLayerChild.h
+++ b/gfx/layers/ipc/ShadowLayerChild.h
@@ -15,19 +15,20 @@
 namespace mozilla {
 namespace layers {
 
 class ShadowableLayer;
 
 class ShadowLayerChild : public PLayerChild
 {
 public:
-  explicit ShadowLayerChild(ShadowableLayer* aLayer);
+  ShadowLayerChild();
   virtual ~ShadowLayerChild();
 
+  void SetShadowableLayer(ShadowableLayer* aLayer);
   ShadowableLayer* layer() const { return mLayer; }
 
 protected:
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
 private:
   ShadowableLayer* mLayer;
 };
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -744,17 +744,24 @@ ShadowLayerForwarder::IPCOpen() const
   */
 PLayerChild*
 ShadowLayerForwarder::ConstructShadowFor(ShadowableLayer* aLayer)
 {
   MOZ_ASSERT(IPCOpen(), "no manager to forward to");
   if (!IPCOpen()) {
     return nullptr;
   }
-  return mShadowManager->SendPLayerConstructor(new ShadowLayerChild(aLayer));
+
+  ShadowLayerChild* child = new ShadowLayerChild();
+  if (!mShadowManager->SendPLayerConstructor(child)) {
+    return nullptr;
+  }
+
+  child->SetShadowableLayer(aLayer);
+  return child;
 }
 
 #if !defined(MOZ_HAVE_PLATFORM_SPECIFIC_LAYER_BUFFERS)
 
 /*static*/ void
 ShadowLayerForwarder::PlatformSyncBeforeUpdate()
 {
 }
--- a/js/src/builtin/AtomicsObject.cpp
+++ b/js/src/builtin/AtomicsObject.cpp
@@ -334,28 +334,30 @@ ExchangeOrStore(JSContext* cx, unsigned 
     MutableHandleValue r = args.rval();
 
     Rooted<TypedArrayObject*> view(cx, nullptr);
     if (!GetSharedTypedArray(cx, objv, &view))
         return false;
     uint32_t offset;
     if (!GetTypedArrayIndex(cx, idxv, view, &offset))
         return false;
-    int32_t numberValue;
-    if (!ToInt32(cx, valv, &numberValue))
+    double integerValue;
+    if (!ToInteger(cx, valv, &integerValue))
         return false;
 
     bool badType = false;
-    int32_t result = ExchangeOrStore<op>(view->type(), numberValue, view->viewDataShared(), offset,
-                                         &badType);
+    int32_t result = ExchangeOrStore<op>(view->type(), JS::ToInt32(integerValue),
+                                         view->viewDataShared(), offset, &badType);
 
     if (badType)
         return ReportBadArrayType(cx);
 
-    if (view->type() == Scalar::Uint32)
+    if (op == DoStore)
+        r.setNumber(integerValue);
+    else if (view->type() == Scalar::Uint32)
         r.setNumber((double)(uint32_t)result);
     else
         r.setInt32(result);
     return true;
 }
 
 bool
 js::atomics_store(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -1663,8 +1663,79 @@ function ArrayBufferStaticSlice(buf, sta
 }
 
 // ES 2016 draft Mar 25, 2016 24.1.3.3.
 function ArrayBufferSpecies() {
     // Step 1.
     return this;
 }
 _SetCanonicalName(ArrayBufferSpecies, "get [Symbol.species]");
+
+// Shared memory and atomics proposal (30 Oct 2016)
+function SharedArrayBufferSpecies() {
+    // Step 1.
+    return this;
+}
+_SetCanonicalName(SharedArrayBufferSpecies, "get [Symbol.species]");
+
+// Shared memory and atomics proposal 6.2.1.5.3 (30 Oct 2016)
+// http://tc39.github.io/ecmascript_sharedmem/shmem.html
+function SharedArrayBufferSlice(start, end) {
+    // Step 1.
+    var O = this;
+
+    // Steps 2-4,
+    // This function is not generic.
+    if (!IsObject(O) || !IsSharedArrayBuffer(O)) {
+        return callFunction(CallSharedArrayBufferMethodIfWrapped, O, start, end,
+                            "SharedArrayBufferSlice");
+    }
+
+    // Step 5.
+    var len = SharedArrayBufferByteLength(O);
+
+    // Step 6.
+    var relativeStart = ToInteger(start);
+
+    // Step 7.
+    var first = relativeStart < 0 ? std_Math_max(len + relativeStart, 0)
+                                  : std_Math_min(relativeStart, len);
+
+    // Step 8.
+    var relativeEnd = end === undefined ? len
+                                        : ToInteger(end);
+
+    // Step 9.
+    var final = relativeEnd < 0 ? std_Math_max(len + relativeEnd, 0)
+                                : std_Math_min(relativeEnd, len);
+
+    // Step 10.
+    var newLen = std_Math_max(final - first, 0);
+
+    // Step 11
+    var ctor = SpeciesConstructor(O, GetBuiltinConstructor("SharedArrayBuffer"));
+
+    // Step 12.
+    var new_ = new ctor(newLen);
+
+    // Step 13.
+    var isWrapped = false;
+    if (!IsSharedArrayBuffer(new_)) {
+        if (!IsWrappedSharedArrayBuffer(new_))
+            ThrowTypeError(JSMSG_NON_SHARED_ARRAY_BUFFER_RETURNED);
+        isWrapped = true;
+    }
+
+    // Step 14.
+    if (new_ === O)
+        ThrowTypeError(JSMSG_SAME_SHARED_ARRAY_BUFFER_RETURNED);
+
+    // Step 15.
+    var actualLen = PossiblyWrappedSharedArrayBufferByteLength(new_);
+    if (actualLen < newLen)
+        ThrowTypeError(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, newLen, actualLen);
+
+    // Steps 16-18.
+    SharedArrayBufferCopyData(new_, O, first | 0, newLen | 0, isWrapped);
+
+    // Step 19.
+    return new_;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/atomics/store-does-not-truncate-returnval.js
@@ -0,0 +1,42 @@
+if (!this.SharedArrayBuffer)
+    quit(0);
+
+var ia = new Int32Array(new SharedArrayBuffer(4));
+
+// Atomics.store() returns the input value converted to integer as if
+// by ToInteger.
+//
+// JIT and interpreter have different paths here, so loop a little to
+// trigger the JIT properly.
+
+function f() {
+    assertEq(Atomics.store(ia, 0, 3.5), 3);
+    assertEq(ia[0], 3);
+
+    assertEq(Atomics.store(ia, 0, -0), -0);
+    assertEq(ia[0], 0);
+
+    assertEq(Atomics.store(ia, 0, '4.6'), 4);
+    assertEq(ia[0], 4);
+
+    assertEq(Atomics.store(ia, 0, '-4.6'), -4);
+    assertEq(ia[0], -4);
+
+    assertEq(Atomics.store(ia, 0, undefined), 0);
+    assertEq(ia[0], 0);
+
+    assertEq(Atomics.store(ia, 0, Infinity), Infinity);
+    assertEq(ia[0], 0);
+
+    assertEq(Atomics.store(ia, 0, -Infinity), -Infinity);
+    assertEq(ia[0], 0);
+
+    assertEq(Atomics.store(ia, 0, Math.pow(2, 32)+5), Math.pow(2, 32)+5);
+    assertEq(ia[0], 5);
+
+    assertEq(Atomics.store(ia, 0, { valueOf: () => 3.7 }), 3);
+    assertEq(ia[0], 3);
+}
+
+for ( var i=0 ; i < 10 ; i++ )
+    f();
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/sharedbuf/byteLength.js
@@ -0,0 +1,9 @@
+// SharedArrayBuffer.prototype.byteLength
+
+if (!this.SharedArrayBuffer)
+    quit(0);
+
+load(libdir + "asserts.js");
+
+let buffer = new SharedArrayBuffer(137);
+assertEq(buffer.byteLength, 137);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/sharedbuf/slice.js
@@ -0,0 +1,122 @@
+// SharedArrayBuffer.prototype.slice
+
+if (!this.SharedArrayBuffer)
+    quit(0);
+
+load(libdir + "asserts.js");
+
+let buf = new SharedArrayBuffer(1024);
+let bufAsI8 = new Int8Array(buf);
+for ( let i=0 ; i < buf.length ; i++ )
+    bufAsI8[i] = i;
+
+let base = 10;
+let len = 10;
+
+let buf2 = buf.slice(base, base+len);
+
+// Smells right?
+assertEq(buf2 instanceof SharedArrayBuffer, true);
+assertEq(buf2.byteLength, len);
+
+// Data got copied correctly?
+let buf2AsI8 = new Int8Array(buf2);
+for ( let i=0 ; i < buf2AsI8.length ; i++ )
+    assertEq(buf2AsI8[i], bufAsI8[base+i]);
+
+// Storage not shared?
+let correct = bufAsI8[base];
+bufAsI8[base]++;
+assertEq(buf2AsI8[0], correct);
+
+// Start beyond end
+let notail = buf.slice(buf.byteLength+1);
+assertEq(notail.byteLength, 0);
+
+// Negative start
+let tail = buf.slice(-5, buf.byteLength);
+assertEq(tail.byteLength, 5);
+let tailAsI8 = new Int8Array(tail);
+for ( let i=0 ; i < tailAsI8.length ; i++ )
+    assertEq(tailAsI8[i], bufAsI8[buf.byteLength-5+i]);
+
+// Negative end
+let head = buf.slice(0, -5);
+assertEq(head.byteLength, buf.byteLength-5);
+let headAsI8 = new Int8Array(head);
+for ( let i=0 ; i < headAsI8.length ; i++ )
+    assertEq(headAsI8[i], bufAsI8[i]);
+
+// Subtyping
+class MySharedArrayBuffer1 extends SharedArrayBuffer {
+    constructor(n) { super(n) }
+}
+
+let myBuf = new MySharedArrayBuffer1(1024);
+
+let myBufAsI8 = new Int8Array(myBuf);
+for ( let i=0 ; i < myBuf.length ; i++ )
+    myBufAsI8[i] = i;
+
+let myBufSlice = myBuf.slice(0, 20);
+
+assertEq(myBufSlice instanceof MySharedArrayBuffer1, true);
+
+assertEq(myBufSlice.byteLength, 20);
+
+let myBufSliceAsI8 = new Int8Array(myBufSlice);
+for ( let i=0 ; i < myBufSlice.length ; i++ )
+    assertEq(myBufAsI8[i], myBufSliceAsI8[i]);
+
+// Error mode: the method requires an object
+assertThrowsInstanceOf(() => buf.slice.call(false, 0, 1), TypeError);
+
+// Error mode: the method is not generic.
+assertThrowsInstanceOf(() => buf.slice.call([1,2,3], 0, 1), TypeError);
+
+// Error mode (step 15): the buffer constructed on behalf of slice
+// is too short.
+
+class MySharedArrayBuffer2 extends SharedArrayBuffer {
+    constructor(n) { super(n-1) }
+}
+
+let myBuf2 = new MySharedArrayBuffer2(10);
+
+assertThrowsInstanceOf(() => myBuf2.slice(0, 5), TypeError);
+
+// Error mode (step 13): the buffer constructed on behalf of slice
+// is not a SharedArrayBuffer.
+
+let subvert = false;
+
+class MySharedArrayBuffer3 extends SharedArrayBuffer {
+    constructor(n) {
+	super(n);
+	if (subvert)
+	    return new Array(n);
+    }
+}
+
+let myBuf3 = new MySharedArrayBuffer3(10);
+
+subvert = true;
+assertThrowsInstanceOf(() => myBuf3.slice(0, 5), TypeError);
+
+// Error mode (step 14): the buffer constructed on behalf of slice
+// is the same as the input buffer.
+
+let sneaky = null;
+
+class MySharedArrayBuffer4 extends SharedArrayBuffer {
+    constructor(n) {
+	super(n);
+	if (sneaky)
+	    return sneaky;
+    }
+}
+
+let myBuf4 = new MySharedArrayBuffer4(10);
+
+sneaky = myBuf4;
+assertThrowsInstanceOf(() => myBuf4.slice(0, 5), TypeError);
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -3085,17 +3085,31 @@ IonBuilder::inlineAtomicsLoad(CallInfo& 
 IonBuilder::InliningStatus
 IonBuilder::inlineAtomicsStore(CallInfo& callInfo)
 {
     if (callInfo.argc() != 3 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
+    // Atomics.store() is annoying because it returns the result of converting
+    // the value by ToInteger(), not the input value, nor the result of
+    // converting the value by ToInt32().  It is especially annoying because
+    // almost nobody uses the result value.
+    //
+    // As an expedient compromise, therefore, we inline only if the result is
+    // obviously unused or if the argument is already Int32 and thus requires no
+    // conversion.
+
     MDefinition* value = callInfo.getArg(2);
+    if (!BytecodeIsPopped(pc) && value->type() != MIRType::Int32) {
+        trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadType);
+        return InliningStatus_NotInlined;
+    }
+
     if (value->mightBeType(MIRType::Object) || value->mightBeType(MIRType::Symbol))
         return InliningStatus_NotInlined;
 
     Scalar::Type arrayType;
     bool requiresCheck = false;
     if (!atomicsMeetsPreconditions(callInfo, &arrayType, &requiresCheck, DontCheckAtomicResult))
         return InliningStatus_NotInlined;
 
@@ -3104,25 +3118,25 @@ IonBuilder::inlineAtomicsStore(CallInfo&
     MInstruction* elements;
     MDefinition* index;
     atomicsCheckBounds(callInfo, &elements, &index);
 
     if (requiresCheck)
         addSharedTypedArrayGuard(callInfo.getArg(0));
 
     MDefinition* toWrite = value;
-    if (value->type() != MIRType::Int32) {
-        toWrite = MTruncateToInt32::New(alloc(), value);
+    if (toWrite->type() != MIRType::Int32) {
+        toWrite = MTruncateToInt32::New(alloc(), toWrite);
         current->add(toWrite->toInstruction());
     }
     MStoreUnboxedScalar* store =
         MStoreUnboxedScalar::New(alloc(), elements, index, toWrite, arrayType,
                                  MStoreUnboxedScalar::TruncateInput, DoesRequireMemoryBarrier);
     current->add(store);
-    current->push(value);
+    current->push(value);       // Either Int32 or not used; in either case correct
 
     if (!resumeAfter(store))
         return InliningStatus_Error;
 
     return InliningStatus_Inlined;
 }
 
 IonBuilder::InliningStatus
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -523,16 +523,19 @@ MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1
 MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED,    0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
 MSG_DEF(JSMSG_TYPED_ARRAY_CONSTRUCT_BOUNDS, 0, JSEXN_RANGEERR, "attempting to construct out-of-bounds TypedArray on ArrayBuffer")
 MSG_DEF(JSMSG_TYPED_ARRAY_CALL_OR_CONSTRUCT, 1, JSEXN_TYPEERR, "cannot directly {0} builtin %TypedArray%")
 MSG_DEF(JSMSG_NON_TYPED_ARRAY_RETURNED, 0, JSEXN_TYPEERR, "constructor didn't return TypedArray object")
 MSG_DEF(JSMSG_SHORT_TYPED_ARRAY_RETURNED, 2, JSEXN_TYPEERR, "expected TypedArray of at least length {0}, but constructor returned TypedArray of length {1}")
 
 // Shared array buffer
 MSG_DEF(JSMSG_SHARED_ARRAY_BAD_LENGTH,  0, JSEXN_RANGEERR, "length argument out of range")
+MSG_DEF(JSMSG_NON_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected SharedArrayBuffer, but species constructor returned non-SharedArrayBuffer")
+MSG_DEF(JSMSG_SAME_SHARED_ARRAY_BUFFER_RETURNED, 0, JSEXN_TYPEERR, "expected different SharedArrayBuffer, but species constructor returned same SharedArrayBuffer")
+MSG_DEF(JSMSG_SHORT_SHARED_ARRAY_BUFFER_RETURNED, 2, JSEXN_TYPEERR, "expected SharedArrayBuffer with at least {0} bytes, but species constructor returns SharedArrayBuffer with {1} bytes")
 
 // Reflect
 MSG_DEF(JSMSG_BAD_PARSE_NODE,          0, JSEXN_INTERNALERR, "bad parse node")
 
 // Symbol
 MSG_DEF(JSMSG_SYMBOL_TO_STRING,        0, JSEXN_TYPEERR, "can't convert symbol to string")
 MSG_DEF(JSMSG_SYMBOL_TO_NUMBER,        0, JSEXN_TYPEERR, "can't convert symbol to number")
 
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -972,16 +972,17 @@ intrinsic_GeneratorSetClosed(JSContext* 
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isObject());
 
     GeneratorObject* genObj = &args[0].toObject().as<GeneratorObject>();
     genObj->setClosed();
     return true;
 }
 
+template<typename T>
 static bool
 intrinsic_IsWrappedArrayBuffer(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
     if (!args[0].isObject()) {
         args.rval().setBoolean(false);
@@ -995,75 +996,78 @@ intrinsic_IsWrappedArrayBuffer(JSContext
     }
 
     JSObject* unwrapped = CheckedUnwrap(obj);
     if (!unwrapped) {
         JS_ReportErrorASCII(cx, "Permission denied to access object");
         return false;
     }
 
-    args.rval().setBoolean(unwrapped->is<ArrayBufferObject>());
+    args.rval().setBoolean(unwrapped->is<T>());
     return true;
 }
 
+template<typename T>
 static bool
 intrinsic_ArrayBufferByteLength(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
     MOZ_ASSERT(args[0].isObject());
-    MOZ_ASSERT(args[0].toObject().is<ArrayBufferObject>());
-
-    size_t byteLength = args[0].toObject().as<ArrayBufferObject>().byteLength();
+    MOZ_ASSERT(args[0].toObject().is<T>());
+
+    size_t byteLength = args[0].toObject().as<T>().byteLength();
     args.rval().setInt32(mozilla::AssertedCast<int32_t>(byteLength));
     return true;
 }
 
+template<typename T>
 static bool
 intrinsic_PossiblyWrappedArrayBufferByteLength(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 1);
 
     JSObject* obj = CheckedUnwrap(&args[0].toObject());
     if (!obj) {
         JS_ReportErrorASCII(cx, "Permission denied to access object");
         return false;
     }
 
-    uint32_t length = obj->as<ArrayBufferObject>().byteLength();
+    uint32_t length = obj->as<T>().byteLength();
     args.rval().setInt32(mozilla::AssertedCast<int32_t>(length));
     return true;
 }
 
+template<typename T>
 static bool
 intrinsic_ArrayBufferCopyData(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     MOZ_ASSERT(args.length() == 5);
 
     bool isWrapped = args[4].toBoolean();
-    Rooted<ArrayBufferObject*> toBuffer(cx);
+    Rooted<T*> toBuffer(cx);
     if (!isWrapped) {
-        toBuffer = &args[0].toObject().as<ArrayBufferObject>();
+        toBuffer = &args[0].toObject().as<T>();
     } else {
         JSObject* wrapped = &args[0].toObject();
         MOZ_ASSERT(wrapped->is<WrapperObject>());
         RootedObject toBufferObj(cx, CheckedUnwrap(wrapped));
         if (!toBufferObj) {
             JS_ReportErrorASCII(cx, "Permission denied to access object");
             return false;
         }
-        toBuffer = toBufferObj.as<ArrayBufferObject>();
+        toBuffer = toBufferObj.as<T>();
     }
-    Rooted<ArrayBufferObject*> fromBuffer(cx, &args[1].toObject().as<ArrayBufferObject>());
+    Rooted<T*> fromBuffer(cx, &args[1].toObject().as<T>());
     uint32_t fromIndex = uint32_t(args[2].toInt32());
     uint32_t count = uint32_t(args[3].toInt32());
 
-    ArrayBufferObject::copyData(toBuffer, fromBuffer, fromIndex, count);
+    T::copyData(toBuffer, fromBuffer, fromIndex, count);
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
 intrinsic_IsSpecificTypedArray(JSContext* cx, unsigned argc, Value* vp, Scalar::Type type)
 {
@@ -2363,23 +2367,36 @@ static const JSFunctionSpec intrinsic_fu
 
     JS_FN("GeneratorIsRunning",      intrinsic_GeneratorIsRunning,      1,0),
     JS_FN("GeneratorSetClosed",      intrinsic_GeneratorSetClosed,      1,0),
 
     JS_FN("IsArrayBuffer",
           intrinsic_IsInstanceOfBuiltin<ArrayBufferObject>,             1,0),
     JS_FN("IsSharedArrayBuffer",
           intrinsic_IsInstanceOfBuiltin<SharedArrayBufferObject>,       1,0),
-    JS_FN("IsWrappedArrayBuffer",    intrinsic_IsWrappedArrayBuffer,    1,0),
-
-    JS_INLINABLE_FN("ArrayBufferByteLength",   intrinsic_ArrayBufferByteLength, 1,0,
+    JS_FN("IsWrappedArrayBuffer",
+          intrinsic_IsWrappedArrayBuffer<ArrayBufferObject>,            1,0),
+    JS_FN("IsWrappedSharedArrayBuffer",
+          intrinsic_IsWrappedArrayBuffer<SharedArrayBufferObject>,      1,0),
+
+    JS_INLINABLE_FN("ArrayBufferByteLength",
+                    intrinsic_ArrayBufferByteLength<ArrayBufferObject>, 1,0,
                     IntrinsicArrayBufferByteLength),
-    JS_INLINABLE_FN("PossiblyWrappedArrayBufferByteLength", intrinsic_PossiblyWrappedArrayBufferByteLength, 1,0,
+    JS_INLINABLE_FN("PossiblyWrappedArrayBufferByteLength",
+                    intrinsic_PossiblyWrappedArrayBufferByteLength<ArrayBufferObject>, 1,0,
                     IntrinsicPossiblyWrappedArrayBufferByteLength),
-    JS_FN("ArrayBufferCopyData",     intrinsic_ArrayBufferCopyData,     5,0),
+    JS_FN("ArrayBufferCopyData",
+          intrinsic_ArrayBufferCopyData<ArrayBufferObject>,             5,0),
+
+    JS_FN("SharedArrayBufferByteLength",
+          intrinsic_ArrayBufferByteLength<SharedArrayBufferObject>,     1,0),
+    JS_FN("PossiblyWrappedSharedArrayBufferByteLength",
+          intrinsic_PossiblyWrappedArrayBufferByteLength<SharedArrayBufferObject>, 1,0),
+    JS_FN("SharedArrayBufferCopyData",
+          intrinsic_ArrayBufferCopyData<SharedArrayBufferObject>,       5,0),
 
     JS_FN("IsUint8TypedArray",        intrinsic_IsUint8TypedArray,      1,0),
     JS_FN("IsInt8TypedArray",         intrinsic_IsInt8TypedArray,       1,0),
     JS_FN("IsUint16TypedArray",       intrinsic_IsUint16TypedArray,     1,0),
     JS_FN("IsInt16TypedArray",        intrinsic_IsInt16TypedArray,      1,0),
     JS_FN("IsUint32TypedArray",       intrinsic_IsUint32TypedArray,     1,0),
     JS_FN("IsInt32TypedArray",        intrinsic_IsInt32TypedArray,      1,0),
     JS_FN("IsFloat32TypedArray",      intrinsic_IsFloat32TypedArray,    1,0),
@@ -2404,16 +2421,18 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("SetFromTypedArrayApproach",intrinsic_SetFromTypedArrayApproach, 4, 0),
     JS_FN("SetOverlappingTypedElements",intrinsic_SetOverlappingTypedElements,3,0),
 
     JS_INLINABLE_FN("SetDisjointTypedElements",intrinsic_SetDisjointTypedElements,3,0,
                     IntrinsicSetDisjointTypedElements),
 
     JS_FN("CallArrayBufferMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<ArrayBufferObject>>, 2, 0),
+    JS_FN("CallSharedArrayBufferMethodIfWrapped",
+          CallNonGenericSelfhostedMethod<Is<SharedArrayBufferObject>>, 2, 0),
     JS_FN("CallTypedArrayMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<TypedArrayObject>>, 2, 0),
 
     JS_FN("CallLegacyGeneratorMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<LegacyGeneratorObject>>, 2, 0),
     JS_FN("CallStarGeneratorMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<StarGeneratorObject>>, 2, 0),
 
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -322,16 +322,30 @@ SharedArrayBufferObject::addSizeOfExclud
     // some threads might be to high (if the refcount goes up) or too low (if
     // the refcount goes down). But that's unlikely and hard to avoid, so we
     // just live with the risk.
     const SharedArrayBufferObject& buf = obj->as<SharedArrayBufferObject>();
     info->objectsNonHeapElementsShared +=
         buf.byteLength() / buf.rawBufferObject()->refcount();
 }
 
+/* static */ void
+SharedArrayBufferObject::copyData(Handle<SharedArrayBufferObject*> toBuffer,
+                                  Handle<SharedArrayBufferObject*> fromBuffer,
+                                  uint32_t fromIndex, uint32_t count)
+{
+    MOZ_ASSERT(toBuffer->byteLength() >= count);
+    MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex);
+    MOZ_ASSERT(fromBuffer->byteLength() >= fromIndex + count);
+
+    jit::AtomicOperations::memcpySafeWhenRacy(toBuffer->dataPointerShared(),
+                                              fromBuffer->dataPointerShared() + fromIndex,
+                                              count);
+}
+
 static const ClassSpec SharedArrayBufferObjectProtoClassSpec = {
     DELEGATED_CLASSSPEC(SharedArrayBufferObject::class_.spec),
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
     nullptr,
@@ -366,25 +380,28 @@ static const ClassOps SharedArrayBufferO
     nullptr, /* trace */
 };
 
 static const JSFunctionSpec static_functions[] = {
     JS_FS_END
 };
 
 static const JSPropertySpec static_properties[] = {
+    JS_SELF_HOSTED_SYM_GET(species, "SharedArrayBufferSpecies", 0),
     JS_PS_END
 };
 
 static const JSFunctionSpec prototype_functions[] = {
+    JS_SELF_HOSTED_FN("slice", "SharedArrayBufferSlice", 2, 0),
     JS_FS_END
 };
 
 static const JSPropertySpec prototype_properties[] = {
     JS_PSG("byteLength", SharedArrayBufferObject::byteLengthGetter, 0),
+    JS_STRING_SYM_PS(toStringTag, "SharedArrayBuffer", JSPROP_READONLY),
     JS_PS_END
 };
 
 static const ClassSpec ArrayBufferObjectClassSpec = {
     GenericCreateConstructor<SharedArrayBufferObject::class_constructor, 1, gc::AllocKind::FUNCTION>,
     CreateSharedArrayBufferPrototype,
     static_functions,
     static_properties,
--- a/js/src/vm/SharedArrayObject.h
+++ b/js/src/vm/SharedArrayObject.h
@@ -142,16 +142,20 @@ class SharedArrayBufferObject : public A
                                         SharedArrayRawBuffer* buffer,
                                         HandleObject proto = nullptr);
 
     static void Finalize(FreeOp* fop, JSObject* obj);
 
     static void addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
                                        JS::ClassInfo* info);
 
+    static void copyData(Handle<SharedArrayBufferObject*> toBuffer,
+                         Handle<SharedArrayBufferObject*> fromBuffer,
+                         uint32_t fromIndex, uint32_t count);
+
     SharedArrayRawBuffer* rawBufferObject() const;
 
     // Invariant: This method does not cause GC and can be called
     // without anchoring the object it is called on.
     uintptr_t globalID() const {
         // The buffer address is good enough as an ID provided the memory is not shared
         // between processes or, if it is, it is mapped to the same address in every
         // process.  (At the moment, shared memory cannot be shared between processes.)
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -300,16 +300,21 @@ GetPrototypeForInstance(JSContext* cx, H
         if (!GetPrototypeFromConstructor(cx, newTarget, proto))
             return false;
     } else {
         proto.set(nullptr);
     }
     return true;
 }
 
+enum class SpeciesConstructorOverride {
+    None,
+    ArrayBuffer
+};
+
 template<typename NativeType>
 class TypedArrayObjectTemplate : public TypedArrayObject
 {
     friend class TypedArrayObject;
 
   public:
     typedef NativeType ElementType;
 
@@ -936,16 +941,17 @@ class TypedArrayObjectTemplate : public 
     static bool
     AllocateArrayBuffer(JSContext* cx, HandleValue ctor,
                         uint32_t count, uint32_t unit,
                         MutableHandle<ArrayBufferObject*> buffer);
 
     static bool
     CloneArrayBufferNoCopy(JSContext* cx, Handle<ArrayBufferObjectMaybeShared*> srcBuffer,
                            bool isWrapped, uint32_t srcByteOffset, uint32_t srcLength,
+                           SpeciesConstructorOverride override,
                            MutableHandle<ArrayBufferObject*> buffer);
 
     static JSObject*
     fromArray(JSContext* cx, HandleObject other, HandleObject newTarget = nullptr);
 
     static JSObject*
     fromTypedArray(JSContext* cx, HandleObject other, bool isWrapped, HandleObject newTarget);
 
@@ -1055,58 +1061,61 @@ IsArrayBufferSpecies(JSContext* cx, Hand
 
     if (!getter)
         return false;
 
     return IsSelfHostedFunctionWithName(getter, cx->names().ArrayBufferSpecies);
 }
 
 static bool
-GetSpeciesConstructor(JSContext* cx, HandleObject obj, bool isWrapped, MutableHandleValue ctor)
+GetSpeciesConstructor(JSContext* cx, HandleObject obj, bool isWrapped,
+                      SpeciesConstructorOverride override, MutableHandleValue ctor)
 {
     if (!isWrapped) {
         if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_ArrayBuffer))
             return false;
         RootedValue defaultCtor(cx, cx->global()->getConstructor(JSProto_ArrayBuffer));
-        if (IsArrayBufferSpecies(cx, obj)) {
+        // The second disjunct is an optimization.
+        if (override == SpeciesConstructorOverride::ArrayBuffer || IsArrayBufferSpecies(cx, obj))
             ctor.set(defaultCtor);
-            return true;
-        }
-        if (!SpeciesConstructor(cx, obj, defaultCtor, ctor))
+        else if (!SpeciesConstructor(cx, obj, defaultCtor, ctor))
             return false;
 
         return true;
     }
 
     {
         JSAutoCompartment ac(cx, obj);
         if (!GlobalObject::ensureConstructor(cx, cx->global(), JSProto_ArrayBuffer))
             return false;
         RootedValue defaultCtor(cx, cx->global()->getConstructor(JSProto_ArrayBuffer));
-        if (!SpeciesConstructor(cx, obj, defaultCtor, ctor))
+        if (override == SpeciesConstructorOverride::ArrayBuffer)
+            ctor.set(defaultCtor);
+        else if (!SpeciesConstructor(cx, obj, defaultCtor, ctor))
             return false;
     }
 
     return JS_WrapValue(cx, ctor);
 }
 
 // ES 2017 draft rev 8633ffd9394b203b8876bb23cb79aff13eb07310 24.1.1.4.
 template<typename T>
 /* static */ bool
 TypedArrayObjectTemplate<T>::CloneArrayBufferNoCopy(JSContext* cx,
                                                     Handle<ArrayBufferObjectMaybeShared*> srcBuffer,
                                                     bool isWrapped, uint32_t srcByteOffset,
                                                     uint32_t srcLength,
+                                                    SpeciesConstructorOverride override,
                                                     MutableHandle<ArrayBufferObject*> buffer)
 {
     // Step 1 (skipped).
 
     // Step 2.a.
     RootedValue cloneCtor(cx);
-    if (!GetSpeciesConstructor(cx, srcBuffer, isWrapped, &cloneCtor))
+    if (!GetSpeciesConstructor(cx, srcBuffer, isWrapped, override, &cloneCtor))
         return false;
 
     // Step 2.b.
     if (srcBuffer->isDetached()) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED);
         return false;
     }
 
@@ -1198,29 +1207,37 @@ TypedArrayObjectTemplate<T>::fromTypedAr
     // Steps 11-12.
     Scalar::Type srcType = srcArray->type();
 
     // Step 13 (skipped).
 
     // Step 14.
     uint32_t srcByteOffset = srcArray->byteOffset();
 
+    // Step 17, modified for SharedArrayBuffer.
+    bool isShared = srcArray->isSharedMemory();
+    SpeciesConstructorOverride override = isShared ? SpeciesConstructorOverride::ArrayBuffer
+                                                   : SpeciesConstructorOverride::None;
+
     // Steps 8-9, 17.
     Rooted<ArrayBufferObject*> buffer(cx);
     if (ArrayTypeID() == srcType) {
         // Step 17.a.
         uint32_t srcLength = srcArray->byteLength();
 
-        // Step 17.b.
-        if (!CloneArrayBufferNoCopy(cx, srcData, isWrapped, srcByteOffset, srcLength, &buffer))
+        // Step 17.b, modified for SharedArrayBuffer
+        if (!CloneArrayBufferNoCopy(cx, srcData, isWrapped, srcByteOffset, srcLength, override,
+                                    &buffer))
+        {
             return nullptr;
+        }
     } else {
-        // Step 18.a.
+        // Step 18.a, modified for SharedArrayBuffer
         RootedValue bufferCtor(cx);
-        if (!GetSpeciesConstructor(cx, srcData, isWrapped, &bufferCtor))
+        if (!GetSpeciesConstructor(cx, srcData, isWrapped, override, &bufferCtor))
             return nullptr;
 
         // Step 15-16, 18.b.
         if (!AllocateArrayBuffer(cx, bufferCtor, elementLength, BYTES_PER_ELEMENT, &buffer))
             return nullptr;
 
         // Step 18.c.
         if (srcArray->hasDetachedBuffer()) {
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -253,18 +253,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     constructorProps(["resolve", "reject", "all", "race", Symbol.species]);
 
   gPrototypeProperties['ArrayBuffer'] =
     ["constructor", "byteLength", "slice", Symbol.toStringTag];
   gConstructorProperties['ArrayBuffer'] =
     constructorProps(["isView", "slice", Symbol.species]);
 
   if (!isReleaseOrBeta) {
-    gPrototypeProperties['SharedArrayBuffer'] = ["constructor", "byteLength"];
-    gConstructorProperties['SharedArrayBuffer'] = constructorProps([]);
+    gPrototypeProperties['SharedArrayBuffer'] = ["constructor", "slice", "byteLength", Symbol.toStringTag];
+    gConstructorProperties['SharedArrayBuffer'] = constructorProps([Symbol.species]);
   } else {
     is(typeof SharedArrayBuffer, "undefined", "Enable tests!");
   }
 
   // Sort an array that may contain symbols as well as strings.
   function sortProperties(arr) {
     function sortKey(prop) {
       return typeof prop + ":" + prop.toString();
@@ -899,30 +899,30 @@ for (var prop of props) {
     is(Cu.getGlobalForObject(pr.wrappedJSObject.then), iwin, "Underlying global is correct");
   }
 
   function testArrayBuffer() {
     let constructors = ['ArrayBuffer'];
 
     if (!isReleaseOrBeta) {
       constructors.push('SharedArrayBuffer');
-      // If this fails enable the test a bit below.
-      is(typeof SharedArrayBuffer.prototype.slice, "undefined", "SharedArrayBuffer doesn't have slice");
     }
 
     for (const c of constructors) {
       testXray(c, new iwin[c](0), new iwin[c](12));
 
       var t = new iwin[c](12);
       is(t.byteLength, 12, `${c} byteLength is correct`);
 
+      is(t.slice(4).byteLength, 8, `${c} byteLength is correct after slicing`);
+      is(Cu.getGlobalForObject(t.slice(4)), iwin, "Slice results lives in the target compartment");
+      is(Object.getPrototypeOf(t.slice(4)), iwin[c].prototype, "Slice results proto lives in target compartment")
+
+      // SharedArrayBuffer does not have static slice method
       if (c === 'ArrayBuffer') {
-        is(t.slice(4).byteLength, 8, `${c} byteLength is correct after slicing`);
-        is(Cu.getGlobalForObject(t.slice(4)), iwin, "Slice results lives in the target compartment");
-        is(Object.getPrototypeOf(t.slice(4)), iwin[c].prototype, "Slice results proto lives in target compartment")
         is(ArrayBuffer.slice(t, 4).byteLength, 8, `${c}.slice (deprecated) works`);
       }
 
       var i32Array = new Int32Array(t);
       // i32Array is going to be created in the buffer's target compartment,
       // but usually this is unobservable, because the proto is set to
       // the current compartment's prototype.
       // However Xrays ignore the object's proto and claim its proto is
--- a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java
+++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/GeckoEditable.java
@@ -1215,23 +1215,24 @@ final class GeckoEditable extends JNIObj
 
             } else {
                 // The sequence is embedded within the changed text, so we have to perform
                 // replacement in parts. First replace part of text before the sequence.
                 mText.currentReplace(start, action.mStart, text.subSequence(0, indexInText));
 
                 // Then replace part of the text after the sequence.
                 final int actionStart = indexInText + start;
-                final int actionEnd = actionStart + action.mEnd - action.mStart;
+                final int delta = actionStart - action.mStart;
+                final int actionEnd = delta + action.mEnd;
 
                 final Spanned currentText = mText.getCurrentText();
                 final boolean resetSelStart = Selection.getSelectionStart(currentText) == actionEnd;
                 final boolean resetSelEnd = Selection.getSelectionEnd(currentText) == actionEnd;
 
-                mText.currentReplace(actionEnd, oldEnd, text.subSequence(
+                mText.currentReplace(actionEnd, delta + oldEnd, text.subSequence(
                         indexInText + action.mSequence.length(), text.length()));
 
                 // The replacement above may have shifted our selection, if the selection
                 // was at the start of the replacement range. If so, we need to reset
                 // our selection to the previous position.
                 if (resetSelStart || resetSelEnd) {
                     mText.currentSetSelection(
                             resetSelStart ? actionEnd : Selection.getSelectionStart(currentText),
--- a/mobile/android/tests/browser/robocop/robocop_input.html
+++ b/mobile/android/tests/browser/robocop/robocop_input.html
@@ -122,16 +122,27 @@
           let inputIme = getEditor().QueryInterface(SpecialPowers.Ci.nsIEditorIMESupport);
           do_check_true(inputIme.composing);
 
           // Ending the composition then setting the selection triggers the bug.
           inputIme.forceCompositionEnd();
           setSelection(3); // Offsets that testInputConnection.java expects.
         },
 
+        test_bug1123514: function() {
+          document.activeElement.addEventListener('input', function test_bug1123514_listener() {
+            this.removeEventListener('input', test_bug1123514_listener);
+
+            // Only works on input and textarea.
+            if (this.value === 'b') {
+              this.value = 'abc';
+            }
+          });
+        },
+
         focus_resetting_input: function(val) {
           resetting_input.value = val;
           resetting_input.focus();
         },
 
         focus_hiding_input: function(val) {
           hiding_input.value = val;
           hiding_input.style.display = "";
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/testInputConnection.java
@@ -238,16 +238,29 @@ public class testInputConnection extends
             ic.sendKeyEvent(delKey);
             ic.sendKeyEvent(KeyEvent.changeAction(delKey, KeyEvent.ACTION_UP));
             ic.sendKeyEvent(KeyEvent.changeAction(shiftKey, KeyEvent.ACTION_UP));
             assertTextAndSelectionAt("Cannot forward delete with shift+backspace", ic, "oo", 0);
 
             ic.deleteSurroundingText(0, 2);
             assertTextAndSelectionAt("Can clear text", ic, "", 0);
 
+            // Bug 1123514 - exception due to incorrect text replacement offsets.
+            getJS().syncCall("test_bug1123514");
+            // Gecko will change text to 'abc' when we input 'b', potentially causing
+            // incorrect calculation of text replacement offsets.
+            ic.commitText("b", 1);
+            // We don't assert text here because this test only works for input/textarea,
+            // so an assertion would fail for contentEditable/designMode.
+            processGeckoEvents();
+            processInputConnectionEvents();
+
+            ic.deleteSurroundingText(2, 1);
+            assertTextAndSelectionAt("Can clear text", ic, "", 0);
+
             // Make sure we don't leave behind stale events for the following test.
             processGeckoEvents();
             processInputConnectionEvents();
         }
     }
 
     /**
      * ResettingInputConnectionTest performs tests on the resetting input in
--- a/netwerk/base/nsStandardURL.cpp
+++ b/netwerk/base/nsStandardURL.cpp
@@ -1923,16 +1923,22 @@ nsStandardURL::SetHost(const nsACString 
 
     uint32_t len;
     nsAutoCString hostBuf;
     nsresult rv = NormalizeIDN(flat, hostBuf);
     if (NS_FAILED(rv)) {
         return rv;
     }
 
+    nsAutoCString ipString;
+    rv = NormalizeIPv4(hostBuf, ipString);
+    if (NS_SUCCEEDED(rv)) {
+      hostBuf = ipString;
+    }
+
     // NormalizeIDN always copies if the call was successful
     host = hostBuf.get();
     len = hostBuf.Length();
 
     if (!ValidIPv6orHostname(host, len)) {
         return NS_ERROR_MALFORMED_URI;
     }
 
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1165,17 +1165,19 @@ HttpChannelChild::OverrideRunnable::Over
   mListener = aListener;
   mInput = aInput;
   mHead = aHead;
 }
 
 void
 HttpChannelChild::OverrideRunnable::OverrideWithSynthesizedResponse()
 {
-  mNewChannel->OverrideWithSynthesizedResponse(mHead, mInput, mListener);
+  if (mNewChannel) {
+    mNewChannel->OverrideWithSynthesizedResponse(mHead, mInput, mListener);
+  }
 }
 
 NS_IMETHODIMP
 HttpChannelChild::OverrideRunnable::Run()
 {
   bool ret = mChannel->Redirect3Complete(this);
 
   // If the method returns false, it means the IPDL connection is being
@@ -1705,19 +1707,20 @@ HttpChannelChild::OnRedirectVerifyCallba
     if (NS_SUCCEEDED(rv) && newLoadInfo) {
       forceHSTSPriming = newLoadInfo->GetForceHSTSPriming();
       mixedContentWouldBlock = newLoadInfo->GetMixedContentWouldBlock();
     }
   }
 
   if (mRedirectingForSubsequentSynthesizedResponse) {
     nsCOMPtr<nsIHttpChannelChild> httpChannelChild = do_QueryInterface(mRedirectChannelChild);
-    MOZ_ASSERT(httpChannelChild);
     RefPtr<HttpChannelChild> redirectedChannel =
         static_cast<HttpChannelChild*>(httpChannelChild.get());
+    // redirectChannel will be NULL if mRedirectChannelChild isn't a
+    // nsIHttpChannelChild (it could be a DataChannelChild).
 
     RefPtr<InterceptStreamListener> streamListener =
         new InterceptStreamListener(redirectedChannel, mListenerContext);
 
     NS_DispatchToMainThread(new OverrideRunnable(this, redirectedChannel,
                                                  streamListener, mSynthesizedInput,
                                                  mResponseHead));
     return NS_OK;
--- a/netwerk/test/unit/test_standardurl.js
+++ b/netwerk/test/unit/test_standardurl.js
@@ -174,19 +174,19 @@ add_test(function test_ipv6()
   url = stringToURL("http://example.com");
   url.hostPort = "[2001::1]:30";
   do_check_eq(url.host, "2001::1");
   do_check_eq(url.port, 30);
   do_check_eq(url.hostPort, "[2001::1]:30");
 
   url = stringToURL("http://example.com");
   url.hostPort = "2001:1";
-  do_check_eq(url.host, "2001");
+  do_check_eq(url.host, "0.0.7.209");
   do_check_eq(url.port, 1);
-  do_check_eq(url.hostPort, "2001:1");
+  do_check_eq(url.hostPort, "0.0.7.209:1");
   run_next_test();
 });
 
 add_test(function test_ipv6_fail()
 {
   var url = stringToURL("http://example.com");
 
   Assert.throws(() => { url.host = "2001::1"; }, "missing brackets");
--- a/security/nss/Makefile
+++ b/security/nss/Makefile
@@ -121,31 +121,31 @@ USEABSPATH="NO"
 endif
 endif
 ifeq ($(USEABSPATH),"YES")
 NSPR_PREFIX = $(shell pwd)/../dist/$(OBJDIR_NAME)
 else
 NSPR_PREFIX = $$(topsrcdir)/../dist/$(OBJDIR_NAME)
 endif
 
-ifndef NSS_GYP
+ifndef NSS_GYP_PREFIX
 $(NSPR_CONFIG_STATUS): $(NSPR_CONFIGURE)
 	mkdir -p $(CORE_DEPTH)/../nspr/$(OBJDIR_NAME)
 	cd $(CORE_DEPTH)/../nspr/$(OBJDIR_NAME) ; \
 	$(NSPR_CONFIGURE_ENV) sh ../configure \
 	$(NSPR_CONFIGURE_OPTS) \
 	--with-dist-prefix='$(NSPR_PREFIX)' \
 	--with-dist-includedir='$(NSPR_PREFIX)/include'
 else
 $(NSPR_CONFIG_STATUS): $(NSPR_CONFIGURE)
 	mkdir -p $(CORE_DEPTH)/../nspr/$(OBJDIR_NAME)
 	cd $(CORE_DEPTH)/../nspr/$(OBJDIR_NAME) ; \
 	$(NSPR_CONFIGURE_ENV) sh ../configure \
 	$(NSPR_CONFIGURE_OPTS) \
-	--prefix='$(NSPR_PREFIX)'
+	--prefix='$(NSS_GYP_PREFIX)'
 endif
 
 build_nspr: $(NSPR_CONFIG_STATUS)
 	$(MAKE) -C $(CORE_DEPTH)/../nspr/$(OBJDIR_NAME)
 
 install_nspr: build_nspr
 	$(MAKE) -C $(CORE_DEPTH)/../nspr/$(OBJDIR_NAME) install
 
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-5eb5f52b7922
+457e3b2beeb9
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -99,17 +99,16 @@ export default async function main() {
   });
 
   await scheduleLinux("Linux 64 (debug, gyp)", {
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/build_gyp.sh"
     ],
-    env: {USE_64: "1"}, // This is only necessary for tests to work.
     platform: "linux64",
     collection: "gyp",
     image: LINUX_IMAGE
   });
 
   await scheduleLinux("Linux 64 (ASan, debug)", {
     env: {
       UBSAN_OPTIONS: "print_stacktrace=1",
@@ -235,18 +234,17 @@ async function scheduleLinux(name, base)
 /*****************************************************************************/
 
 async function scheduleFuzzing() {
   let base = {
     env: {
       ASAN_OPTIONS: "allocator_may_return_null=1",
       UBSAN_OPTIONS: "print_stacktrace=1",
       CC: "clang",
-      CCC: "clang++",
-      USE_64: "1" // This is only necessary for tests to work.
+      CCC: "clang++"
     },
     platform: "linux64",
     collection: "fuzz",
     image: LINUX_IMAGE
   };
 
   // Build base definition.
   let build_base = merge({
@@ -277,19 +275,19 @@ async function scheduleFuzzing() {
     parent: task_build,
     name: "Gtests",
     command: [
       "/bin/bash",
       "-c",
       "bin/checkout.sh && nss/automation/taskcluster/scripts/run_tests.sh"
     ],
     env: {GTESTFILTER: "*Fuzz*"},
+    tests: "ssl_gtests gtests",
+    cycle: "standard",
     symbol: "Gtest",
-    tests: "gtests",
-    cycle: "standard",
     kind: "test"
   }));
 
   return queue.submit();
 }
 
 /*****************************************************************************/
 
--- a/security/nss/automation/taskcluster/scripts/run_tests.sh
+++ b/security/nss/automation/taskcluster/scripts/run_tests.sh
@@ -9,9 +9,9 @@ if [ $(id -u) = 0 ]; then
     # Drop privileges by re-running this script.
     exec su worker $0
 fi
 
 # Fetch artifact if needed.
 fetch_dist
 
 # Run tests.
-cd nss/tests && ./all.sh
+cd nss/tests && ./run.sh
--- a/security/nss/build.sh
+++ b/security/nss/build.sh
@@ -68,58 +68,68 @@ while [ $# -gt 0 ]; do
         --scan-build=?*) scanbuild=(scan-build -o "${1#*=}") ;;
         --opt|-o) opt_build=1; nspr_env+=(BUILD_OPT=1) ;;
         -m32|--m32) build_64=0 ;;
         *) show_help; exit ;;
     esac
     shift
 done
 
-# set paths
-cwd=$(cd $(dirname $0); pwd -P)
-obj_dir=$(USE_64=$build_64 make -s -C "$cwd" platform)
-dist_dir="$cwd/../dist/$obj_dir"
-
-# -c = clean first
-if [ "$clean" = 1 ]; then
-    rm -rf "$cwd/out"
-    rm -rf "$cwd/../nspr/$obj_dir"
-fi
-
 if [ "$opt_build" = "1" ]; then
     target=Release
 else
     target=Debug
 fi
 if [ "$build_64" == "1" ]; then
     target="${target}_x64"
     nspr_env+=(USE_64=1)
 else
     gyp_params+=(-Dtarget_arch=ia32)
 fi
+
+# set paths
+cwd=$(cd $(dirname $0); pwd -P)
+dist_dir="$cwd/../dist"
 target_dir="$cwd/out/$target"
 
+# get the realpath of $dist_dir
+dist_dir=$(mkdir -p $dist_dir; cd $dist_dir; pwd -P)
+
+# save the chosen target
+echo $target > $dist_dir/latest
+
+# get object directory
+obj_dir="$dist_dir/$target"
+gyp_params+=(-Dnss_dist_dir=$dist_dir)
+gyp_params+=(-Dnss_dist_obj_dir=$obj_dir)
+
+# -c = clean first
+if [ "$clean" = 1 ]; then
+    rm -rf "$cwd/out"
+    rm -rf "$cwd/../nspr/$target"
+fi
+
 # figure out the scan-build string
 if [ "${#scanbuild[@]}" -gt 0 ]; then
     if [ -n "$CC" ]; then
        scanbuild+=(--use-cc="$CC")
     fi
     if [ -n "$CCC" ]; then
        scanbuild+=(--use-c++="$CCC")
     fi
  fi
 
 # These steps can take a while, so don't overdo them.
 # Force a redo with -g.
 if [ "$rebuild_gyp" = 1 -o ! -d "$target_dir" ]; then
     # Build NSPR.
-    make "${nspr_env[@]}" -C "$cwd" NSS_GYP=1 install_nspr
+    make "${nspr_env[@]}" -C "$cwd" NSS_GYP_PREFIX=$obj_dir install_nspr
 
     # Run gyp.
-    PKG_CONFIG_PATH="$cwd/../nspr/$obj_dir/config" \
+    PKG_CONFIG_PATH="$obj_dir/lib/pkgconfig" \
         "${scanbuild[@]}" gyp -f ninja "${gyp_params[@]}" --depth="$cwd" \
           --generator-output="." "$cwd/nss.gyp"
 fi
 
 # Run ninja.
 if which ninja >/dev/null 2>&1; then
     ninja=(ninja)
 elif which ninja-build >/dev/null 2>&1; then
--- a/security/nss/coreconf/config.gypi
+++ b/security/nss/coreconf/config.gypi
@@ -25,29 +25,25 @@
           'target_arch%': '<(host_arch)',
         }],
         ['OS=="win"', {
           'use_system_zlib%': 0,
           'nspr_libs%': ['nspr4.lib', 'plc4.lib', 'plds4.lib'],
           #XXX: gyp breaks if these are empty!
           'nspr_lib_dir%': ' ',
           'nspr_include_dir%': ' ',
-          'nss_dist_obj_dir%': ' ',
-          'nss_dist_dir%': ' ',
           'zlib_libs%': [],
           #TODO
           'moz_debug_flags%': '',
           'dll_prefix': '',
           'dll_suffix': 'dll',
         }, {
           'nspr_libs%': ['-lplds4', '-lplc4', '-lnspr4'],
           'nspr_lib_dir%': '<!(<(python) <(DEPTH)/coreconf/pkg_config.py . --libs nspr)',
           'nspr_include_dir%': '<!(<(python) <(DEPTH)/coreconf/pkg_config.py . --cflags nspr)',
-          'nss_dist_obj_dir%': '<!(<(python) <(DEPTH)/coreconf/pkg_config.py ../.. --cflags nspr)',
-          'nss_dist_dir%': '<!(<(python) <(DEPTH)/coreconf/pkg_config.py ../../.. --cflags nspr)',
           'use_system_zlib%': 1,
         }],
         ['OS=="linux" or OS=="android"', {
           'zlib_libs%': ['<!@(<(python) <(DEPTH)/coreconf/pkg_config.py raw --libs zlib)'],
           'moz_debug_flags%': '-gdwarf-2',
           'optimize_flags%': '-O2',
           'dll_prefix': 'lib',
           'dll_suffix': 'so',
@@ -79,18 +75,16 @@
     'host_arch%': '<(host_arch)',
     'target_arch%': '<(target_arch)',
     'use_system_zlib%': '<(use_system_zlib)',
     'zlib_libs%': ['<@(zlib_libs)'],
     'moz_debug_flags%': '<(moz_debug_flags)',
     'nspr_libs%': ['<@(nspr_libs)'],
     'nspr_lib_dir%': '<(nspr_lib_dir)',
     'nspr_include_dir%': '<(nspr_include_dir)',
-    'nss_dist_obj_dir%': '<(nss_dist_obj_dir)',
-    'nss_dist_dir%': '<(nss_dist_dir)',
     'use_system_sqlite%': '<(use_system_sqlite)',
     'sqlite_libs%': ['-lsqlite3'],
     'dll_prefix': '<(dll_prefix)',
     'dll_suffix': '<(dll_suffix)',
     'freebl_name': '<(freebl_name)',
     'cc_is_clang%': '<(cc_is_clang)',
     # Some defaults
     'disable_tests%': 0,
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,8 +5,9 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
+
--- a/security/nss/coreconf/pkg_config.py
+++ b/security/nss/coreconf/pkg_config.py
@@ -12,17 +12,17 @@ def main():
     is_raw = sys.argv[1] == 'raw'
     stdout = None if is_raw else subprocess.PIPE
 
     if sys.argv[2] == '--libs':
         part_prefix = '-L'
     elif sys.argv[2] == '--cflags':
         part_prefix = '-I'
     else:
-        raise 'Specify either --libs or --cflags as the second argument.'
+        raise Exception('Specify either --libs or --cflags as the second argument.')
 
     try:
         process = subprocess.Popen(['pkg-config'] + sys.argv[2:], stdout=stdout, stderr=open(os.devnull, 'wb'))
 
     except OSError:
         # pkg-config is probably not installed
         return
 
--- a/security/nss/gtests/ssl_gtest/manifest.mn
+++ b/security/nss/gtests/ssl_gtest/manifest.mn
@@ -18,16 +18,17 @@ CPPSRCS = \
       ssl_cert_ext_unittest.cc \
       ssl_ciphersuite_unittest.cc \
       ssl_damage_unittest.cc \
       ssl_dhe_unittest.cc \
       ssl_drop_unittest.cc \
       ssl_ecdh_unittest.cc \
       ssl_ems_unittest.cc \
       ssl_extension_unittest.cc \
+      ssl_fuzz_unittest.cc \
       ssl_gtest.cc \
       ssl_hrr_unittest.cc \
       ssl_loopback_unittest.cc \
       ssl_record_unittest.cc \
       ssl_resumption_unittest.cc \
       ssl_skip_unittest.cc \
       ssl_staticrsa_unittest.cc \
       ssl_v2_client_hello_unittest.cc \
new file mode 100644
--- /dev/null
+++ b/security/nss/gtests/ssl_gtest/ssl_fuzz_unittest.cc
@@ -0,0 +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/. */
+
+#include "ssl.h"
+#include "sslimpl.h"
+
+#include "gtest/gtest.h"
+
+namespace nss_test {
+
+#ifdef UNSAFE_FUZZER_MODE
+
+class TlsFuzzTest : public ::testing::Test {};
+
+// Ensure that ssl_Time() returns a constant value.
+TEST_F(TlsFuzzTest, Fuzz_SSL_Time_Constant) {
+  PRInt32 now = ssl_Time();
+  PR_Sleep(PR_SecondsToInterval(2));
+  EXPECT_EQ(ssl_Time(), now);
+}
+
+#endif
+}
--- a/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
+++ b/security/nss/gtests/ssl_gtest/ssl_gtest.gyp
@@ -18,16 +18,17 @@
         'ssl_cert_ext_unittest.cc',
         'ssl_ciphersuite_unittest.cc',
         'ssl_damage_unittest.cc',
         'ssl_dhe_unittest.cc',
         'ssl_drop_unittest.cc',
         'ssl_ecdh_unittest.cc',
         'ssl_ems_unittest.cc',
         'ssl_extension_unittest.cc',
+        'ssl_fuzz_unittest.cc',
         'ssl_gtest.cc',
         'ssl_hrr_unittest.cc',
         'ssl_loopback_unittest.cc',
         'ssl_record_unittest.cc',
         'ssl_resumption_unittest.cc',
         'ssl_skip_unittest.cc',
         'ssl_staticrsa_unittest.cc',
         'ssl_v2_client_hello_unittest.cc',
--- a/security/nss/lib/ssl/ssl.gyp
+++ b/security/nss/lib/ssl/ssl.gyp
@@ -58,16 +58,21 @@
         [ 'ssl_enable_zlib==1', {
           'dependencies': [
             '<(DEPTH)/lib/zlib/zlib.gyp:nss_zlib'
           ],
           'defines': [
             'NSS_SSL_ENABLE_ZLIB',
           ],
         }],
+        [ 'fuzz==1', {
+          'defines': [
+            'UNSAFE_FUZZER_MODE',
+          ],
+        }],
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         '<(DEPTH)/lib/freebl/freebl.gyp:freebl',
       ],
     },
     {
       'target_name': 'ssl3',
--- a/security/nss/lib/ssl/sslnonce.c
+++ b/security/nss/lib/ssl/sslnonce.c
@@ -435,16 +435,20 @@ SSL_ClearSessionCache(void)
         UncacheSID(cache);
     UNLOCK_CACHE;
 }
 
 /* returns an unsigned int containing the number of seconds in PR_Now() */
 PRUint32
 ssl_Time(void)
 {
+#ifdef UNSAFE_FUZZER_MODE
+    return 1234;
+#endif
+
     PRUint32 myTime;
 #if defined(XP_UNIX) || defined(XP_WIN) || defined(_WINDOWS) || defined(XP_BEOS)
     myTime = time(NULL); /* accurate until the year 2038. */
 #else
     /* portable, but possibly slower */
     PRTime now;
     PRInt64 ll;
 
new file mode 100644
--- /dev/null
+++ b/security/nss/readme.md
@@ -0,0 +1,99 @@
+# Network Security Services
+
+Network Security Services (NSS) is a set of libraries designed to support cross-platform development of security-enabled client and server applications. NSS supports SSL v3-TLS 1.2 (experimental TLS 1.3), PKCS #5, PKCS #7, PKCS #11, PKCS #12, S/MIME, X.509 v3 certificates, and other security standards.
+
+## Getting started
+In order to get started create a new directory on that you will be uses as your local work area, and check out NSS and NSPR. (Note that there's no git mirror of NSPR and you require mercurial to get the latest NSPR source.)
+
+    git clone https://github.com/nss-dev/nss.git
+    hg clone https://hg.mozilla.org/projects/nspr
+
+NSS can also be cloned with mercurial `
+    hg clone https://hg.mozilla.org/projects/nspr`
+
+## Building NSS
+*This build system is under development. It does not yet support all the features or platforms that NSS supports. To build on anything other than Mac or Linux please use the legacy build system as described below.*
+
+Build requirements:
+
+* [gyp](https://gyp.gsrc.io/)
+* [ninja](https://ninja-build.org/)
+
+After changing into the NSS directory a typical build is done as follows
+
+    ./build.sh
+
+Once the build is done the build output is found in the directory `../dist/*.OBJ`, where `*` will be a name dynamically derived from your system's architecture. Exported header files can be found in the `include` directory, library files in directory `lib`, and tools in directory `bin`. In order to run the tools, set your system environment to use the libraries of your build from the "lib" directory, e.g., using the `LD_LIBRARY_PATH` or `DYLD_LIBRARY_PATH`.
+
+---
+    Usage: build.sh [-hcgv] [-j <n>] [--test] [--fuzz] [--scan-build[=output]]
+                    [-m32] [--opt|-o]
+
+    -h            display this help and exit
+    -c            clean before build
+    -g            force a rebuild of gyp (and NSPR, because why not)
+    -j <n>        run at most <n> concurrent jobs
+    -v            verbose build
+    -m32          do a 32-bit build on a 64-bit system
+    --test        ignore map files and export everything we have
+    --fuzz        enable fuzzing mode. this always enables test builds
+    --scan-build  run the build with scan-build (scan-build has to be in the path)
+                  --scan-build=/out/path sets the output path for scan-build
+    --opt|-o      do an opt build
+
+## Building NSS (legacy build system)
+After changing into the NSS directory a typical build of 32-bit NSS is done as follows
+
+    make nss_build_all
+
+The following environment variables might be useful:
+* `BUILD_OPT=1` to get an optimised build
+* `USE_64=1` to get a 64-bit build (recommended)
+* `NSS_ENABLE_TLS_1_3=1` to enable TLS 1.3 support
+
+The complete list of environment variables can be found [here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/Reference/NSS_environment_variables).
+
+To clean the build directory run
+
+    make nss_clean_all
+
+## Tests
+### Setup
+Make sure that the address `$HOST.$DOMSUF` on your computer is available. This is necessary because NSS tests generate certificates and establish TLS connections, which requires a fully qualified domain name.
+You can test this by calling `ping $HOST.$DOMSUF`. If this is working, you're all set.
+If it's not, set or export
+
+    HOST=nss
+    DOMSUF=local
+Note that you might have to add `nss.local` to `/etc/hosts` if it's not there. The entry should look something like `127.0.0.1       nss.local   nss`.
+If you get name resolution errors, try to disable IPv6 on the loopback device, i.e. comment the lines starting with `::1` in your `/etc/hosts` .
+
+### Running tests
+*Runnning all tests will take a while!*
+
+    cd tests
+    ./all.sh
+Make sure that all environment variables set for the build are set while running the tests as well.
+Test results are published in the folder `../../test_results/`.
+Individual tests can be run with the `NSS_TESTS` environment variable, e.g. `NSS_TESTS=ssl_gtests ./all.sh` or by changing into the according directory and running the bash script there `cd ssl_gtests && ./ssl_gtests.sh`.  The following tests are available:
+
+    cipher lowhash libpkix cert dbtests tools fips sdr crmf smime ssl ocsp merge pkits chains ec gtests ssl_gtests bogo
+
+To make tests run faster it's recommended to set `NSS_CYCLES=standard` to run only the standard cycle.
+
+## Releases
+NSS releases can be found at [Mozilla's download server](https://ftp.mozilla.org/pub/security/nss/releases/). Because NSS depends on the base library NSPR you should download the archive that combines both NSS and NSPR.
+
+## Contributing
+[Bugzilla](https://bugzilla.mozilla.org/) is used to track NSS development and bugs. File new bugs in the NSS product.
+A list with good first bugs to start with are [listed here](https://bugzilla.mozilla.org/buglist.cgi?keywords=good-first-bug%2C%20&keywords_type=allwords&list_id=13238861&resolution=---&query_format=advanced&product=NSS).
+
+### NSS Folder Structure
+The nss directory contains the following important subdirectories:
+- `coreconf` contains the build logic.
+- `lib` contains all library code that is used to create the runtime libraries.
+- `cmd` contains a set of various tool programs that are built with NSS. Several tools are general purpose and can be used to inspect and manipulate the storage files that software using the NSS library creates and modifies. Other tools are only used for testing purposes.
+- `test` and `gtests` contain the NSS test suite. While `test` contains shell scripts to drive test programs in `cmd`, `gtests` holds a set of [gtests](https://github.com/google/googletest).
+
+A more comprehensible overview of the NSS folder structure and API guidelines can be found [here](https://developer.mozilla.org/en-US/docs/Mozilla/Projects/NSS/NSS_API_Guidelines).
+
new file mode 100755
--- /dev/null
+++ b/security/nss/tests/run.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+base=$(dirname $0)
+
+# Determine path to build target.
+obj_dir=$(cat $base/../../dist/latest 2>/dev/null)
+
+# Run tests.
+OBJDIR=$obj_dir $base/all.sh
--- a/security/nss/tests/ssl_gtests/ssl_gtests.sh
+++ b/security/nss/tests/ssl_gtests/ssl_gtests.sh
@@ -122,17 +122,18 @@ ssl_gtest_start()
   if [ ! -f ${BINDIR}/ssl_gtest ]; then
     html_unknown "Skipping ssl_gtest (not built)"
     return
   fi
 
   SSLGTESTREPORT="${SSLGTESTDIR}/report.xml"
   PARSED_REPORT="${SSLGTESTDIR}/report.parsed"
   echo "executing ssl_gtest"
-  ${BINDIR}/ssl_gtest -d "${SSLGTESTDIR}" --gtest_output=xml:"${SSLGTESTREPORT}"
+  ${BINDIR}/ssl_gtest -d "${SSLGTESTDIR}" --gtest_output=xml:"${SSLGTESTREPORT}" \
+                                          --gtest_filter="${GTESTFILTER-*}"
   html_msg $? 0 "ssl_gtest run successfully"
   echo "executing sed to parse the xml report"
   sed -f ${COMMON}/parsegtestreport.sed "${SSLGTESTREPORT}" > "${PARSED_REPORT}"
   echo "processing the parsed report"
   cat "${PARSED_REPORT}" | while read result name; do
     if [ "$result" = "notrun" ]; then
       echo "$name" SKIPPED
     elif [ "$result" = "run" ]; then
--- a/testing/cppunittest.ini
+++ b/testing/cppunittest.ini
@@ -6,18 +6,16 @@
 [TestAudioBuffers]
 skip-if = os == 'b2g'  # Bug 1062937
 [TestAudioMixer]
 [TestBinarySearch]
 [TestBind]
 [TestBloomFilter]
 [TestCOM]
 skip-if = os != 'win'
-[TestCSPParser]
-skip-if = os == 'b2g' || (os == 'android' && debug) # Bug 1054246
 [TestCasting]
 [TestCeilingFloor]
 [TestCertDB]
 [TestCheckedInt]
 [TestCookie]
 [TestCountPopulation]
 [TestCountZeroes]
 [TestDeadlockDetector]
@@ -27,36 +25,31 @@ skip-if = os == 'b2g' || (os == 'android
 skip-if = os != 'win'
 [TestEndian]
 [TestEnumeratedArray]
 [TestEnumSet]
 [TestEnumTypeTraits]
 [TestFastBernoulliTrial]
 [TestFloatingPoint]
 [TestFunction]
-[TestGetURL]
 [TestIntegerPrintfMacros]
 [TestIntegerRange]
 [TestIsCertBuiltInRoot]
 [TestJSONWriter]
 [TestJemalloc]
 [TestLinkedList]
 [TestMacroArgs]
 [TestMacroForEach]
 [TestMathAlgorithms]
 [TestMaybe]
-[TestNativeXMLHttpRequest]
-skip-if = os == 'b2g' || os == 'android'  #Bug 919642
 [TestNtPathToDosPath]
 skip-if = os != 'win'
 [TestPLDHash]
 skip-if = os == 'b2g'  #Bug 1038197
 [TestPair]
-[TestPlainTextSerializer]
-skip-if = os == 'b2g' || os == 'android'  #Bug 919599
 [TestPoisonArea]
 skip-if = os == 'android' # Bug 1147630
 [TestRefPtr]
 [TestRollingMean]
 [TestScopeExit]
 [TestSegmentedVector]
 [TestSHA1]
 [TestSTSParser]
@@ -74,17 +67,16 @@ skip-if = os == 'b2g'  #Bug 919595
 [TestTypeTraits]
 [TestTypedEnum]
 [TestUDPSocket]
 [TestUniquePtr]
 [TestVariant]
 [TestVector]
 [TestVolatileBuffer]
 [TestWeakPtr]
-[TestWebGLElementArrayCache]
 [TestXorShift128PlusRNG]
 [buffered_stun_socket_unittest]
 [ice_unittest]
 [test_nr_socket_unittest]
 [jsapi-tests]
 skip-if = os == 'b2g'  #Bug 1068946
 [mediaconduit_unittests]
 [mediapipeline_unittest]
--- a/testing/web-platform/meta/url/url-setters.html.ini
+++ b/testing/web-platform/meta/url/url-setters.html.ini
@@ -58,37 +58,31 @@
     expected: FAIL
 
   [Setting <view-source+http://example.net/foo>.host = '' The empty host is OK for non-special schemes]
     expected: FAIL
 
   [Setting <a:/foo>.host = 'example.net' Path-only URLs can gain a host]
     expected: FAIL
 
-  [Setting <http://example.net>.host = '0x7F000001:8080' IPv4 address syntax is normalized]
-    expected: FAIL
-
   [Setting <http://example.net>.host = '[::0:01\]:2' IPv6 address syntax is normalized]
     expected: FAIL
 
   [Setting <view-source+http://example.net/path>.host = 'example.com\\stuff' \\ is not a delimiter for non-special schemes, and it’s invalid in a domain]
     expected: FAIL
 
   [Setting <view-source+http://example.net/path>.host = 'example.com:8080stuff2' Anything other than ASCII digit stops the port parser in a setter but is not an error]
     expected: FAIL
 
   [Setting <view-source+http://example.net/foo>.hostname = '' The empty host is OK for non-special schemes]
     expected: FAIL
 
   [Setting <a:/foo>.hostname = 'example.net' Path-only URLs can gain a host]
     expected: FAIL
 
-  [Setting <http://example.net:8080>.hostname = '0x7F000001' IPv4 address syntax is normalized]
-    expected: FAIL
-
   [Setting <http://example.net>.hostname = '[::0:01\]' IPv6 address syntax is normalized]
     expected: FAIL
 
   [Setting <http://example.net/path>.hostname = 'example.com:8080' Stuff after a : delimiter is ignored]
     expected: FAIL
 
   [Setting <http://example.net:8080/path>.hostname = 'example.com:' Stuff after a : delimiter is ignored]
     expected: FAIL
--- a/xpcom/io/nsIStringStream.idl
+++ b/xpcom/io/nsIStringStream.idl
@@ -1,22 +1,28 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsIInputStream.idl"
 
+%{C++
+#include "mozilla/MemoryReporting.h"
+%}
+
+native MallocSizeOf(mozilla::MallocSizeOf);
+
 /**
  * nsIStringInputStream
  *
  * Provides scriptable and specialized C++-only methods for initializing a
  * nsIInputStream implementation with a simple character array.
  */
-[scriptable, uuid(450cd2d4-f0fd-424d-b365-b1251f80fd53)]
+[scriptable, builtinclass, uuid(450cd2d4-f0fd-424d-b365-b1251f80fd53)]
 interface nsIStringInputStream : nsIInputStream
 {
     /**
      * SetData - assign data to the input stream (copied on assignment).
      *
      * @param data    - stream data
      * @param dataLen - stream data length (-1 if length should be computed)
      *
@@ -49,9 +55,12 @@ interface nsIStringInputStream : nsIInpu
      * ShareData - assign data to the input stream.  the input stream references
      * the given data buffer until the input stream is destroyed.  the given
      * data buffer must outlive the input stream.
      *
      * @param data      - stream data
      * @param dataLen   - stream data length (-1 if length should be computed)
      */
     [noscript] void shareData(in string data, in long dataLen);
+
+    [noscript, notxpcom]
+    size_t SizeOfIncludingThis(in MallocSizeOf aMallocSizeOf);
 };
--- a/xpcom/io/nsStringStream.cpp
+++ b/xpcom/io/nsStringStream.cpp
@@ -189,16 +189,24 @@ nsStringInputStream::ShareData(const cha
     aDataLen = strlen(aData);
   }
 
   mData.Rebind(aData, aDataLen);
   mOffset = 0;
   return NS_OK;
 }
 
+NS_IMETHODIMP_(size_t)
+nsStringInputStream::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
+{
+  size_t n = aMallocSizeOf(this);
+  n += mData.SizeOfIncludingThisIfUnshared(aMallocSizeOf);
+  return n;
+}
+
 /////////
 // nsIInputStream implementation
 /////////
 
 NS_IMETHODIMP
 nsStringInputStream::Close()
 {
   Clear();