Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 21 Mar 2017 12:08:43 +0100
changeset 399086 496d8d86dc745d2489c1c4cd6a30eb7e6d7cc4be
parent 399085 ec5d24c5c75f44ab367630c5df3b7319dc83a99b (current diff)
parent 399059 bd4f3810b402147f8656390555b29502ce5e2644 (diff)
child 399087 2f04e497a5741e64f6f2c38eadbe1eb5b91dcde1
push id1490
push usermtabara@mozilla.com
push dateMon, 31 Jul 2017 14:08:16 +0000
treeherdermozilla-release@70e32e6bf15e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone55.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
dom/smil/nsSMILAnimationController.cpp
layout/base/RestyleManager.cpp
--- a/browser/app/moz.build
+++ b/browser/app/moz.build
@@ -24,20 +24,16 @@ DEFINES['APP_VERSION'] = CONFIG['MOZ_APP
 
 LOCAL_INCLUDES += [
     '!/build',
     '/toolkit/xre',
     '/xpcom/base',
     '/xpcom/build',
 ]
 
-USE_LIBS += [
-    'mozglue',
-]
-
 if CONFIG['LIBFUZZER']:
     USE_LIBS += [ 'fuzzer' ]
     LOCAL_INCLUDES += [
         '/tools/fuzzing/libfuzzer',
     ]
 
 if CONFIG['_MSC_VER']:
     # Always enter a Windows program through wmain, whether or not we're
--- a/browser/base/content/browser-syncui.js
+++ b/browser/base/content/browser-syncui.js
@@ -468,17 +468,17 @@ var gSyncUI = {
     Ci.nsISupportsWeakReference
   ])
 };
 
 XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
   // XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
   //        but for now just make it work
   return Services.strings.createBundle(
-    "chrome://weave/locale/services/sync.properties");
+    "chrome://weave/locale/sync.properties");
 });
 
 XPCOMUtils.defineLazyGetter(gSyncUI, "log", function() {
   return Log.repository.getLogger("browserwindow.syncui");
 });
 
 XPCOMUtils.defineLazyGetter(gSyncUI, "weaveService", function() {
   return Components.classes["@mozilla.org/weave/service;1"]
--- a/browser/base/content/test/general/browser_syncui.js
+++ b/browser/base/content/test/general/browser_syncui.js
@@ -1,17 +1,17 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var {Log} = Cu.import("resource://gre/modules/Log.jsm", {});
 var {Weave} = Cu.import("resource://services-sync/main.js", {});
 
 var stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]
                    .getService(Ci.nsIStringBundleService)
-                   .createBundle("chrome://weave/locale/services/sync.properties");
+                   .createBundle("chrome://weave/locale/sync.properties");
 
 // ensure test output sees log messages.
 Log.repository.getLogger("browserwindow.syncui").addAppender(new Log.DumpAppender());
 
 // Send the specified sync-related notification and return a promise that
 // resolves once gSyncUI._promiseUpateUI is complete and the UI is ready to check.
 function notifyAndPromiseUIUpdated(topic) {
   return new Promise(resolve => {
--- a/browser/base/content/test/static/browser.ini
+++ b/browser/base/content/test/static/browser.ini
@@ -1,12 +1,14 @@
 [DEFAULT]
 support-files =
   head.js
 
+[browser_all_files_referenced.js]
+skip-if = debug # no point in running on both opt and debug, and will likely intermittently timeout on debug
 [browser_misused_characters_in_strings.js]
 support-files =
   bug1262648_string_with_newlines.dtd
 [browser_parsable_css.js]
 support-files =
   dummy_page.html
 skip-if = debug # no point in running on both opt and debug, and will likely intermittently timeout on debug
 [browser_parsable_script.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/static/browser_all_files_referenced.js
@@ -0,0 +1,498 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+var isDevtools = SimpleTest.harnessParameters.subsuite == "devtools";
+
+var gExceptionPaths = ["chrome://browser/content/defaultthemes/",
+                       "chrome://browser/locale/searchplugins/"];
+
+var whitelist = new Set([
+  // browser/extensions/pdfjs/content/PdfStreamConverter.jsm
+  {file: "chrome://pdf.js/locale/chrome.properties"},
+  {file: "chrome://pdf.js/locale/viewer.properties"},
+
+  // security/manager/pki/resources/content/device_manager.js
+  {file: "chrome://pippki/content/load_device.xul"},
+
+  // browser/modules/ReaderParent.jsm
+  {file: "chrome://browser/skin/reader-tour.png"},
+  {file: "chrome://browser/skin/reader-tour@2x.png"},
+
+  // Used by setting this url as a pref in about:config
+  {file: "chrome://browser/content/newtab/alternativeDefaultSites.json"},
+
+  // Add-on compat
+  {file: "chrome://browser/skin/devtools/common.css"},
+  {file: "chrome://global/content/XPCNativeWrapper.js"},
+  {file: "chrome://global/locale/brand.dtd"},
+
+  // The l10n build system can't package string files only for some platforms.
+  // See bug 1339424 for why this is hard to fix.
+  {file: "chrome://global/locale/fallbackMenubar.properties",
+   platforms: ["linux", "win"]},
+  {file: "chrome://global/locale/printPageSetup.dtd", platforms: ["macosx"]},
+  {file: "chrome://global/locale/printPreviewProgress.dtd",
+   platforms: ["macosx"]},
+  {file: "chrome://global/locale/printProgress.dtd", platforms: ["macosx"]},
+  {file: "chrome://global/locale/printdialog.dtd",
+   platforms: ["macosx", "win"]},
+  {file: "chrome://global/locale/printjoboptions.dtd",
+   platforms: ["macosx", "win"]},
+
+  // services/cloudsync/CloudSyncLocal.jsm
+  {file: "chrome://weave/locale/errors.properties"},
+
+  // devtools/client/inspector/bin/dev-server.js
+  {file: "chrome://devtools/content/inspector/markup/markup.xhtml",
+   isFromDevTools: true},
+
+  // Starting from here, files in the whitelist are bugs that need fixing.
+  // Bug 1339420
+  {file: "chrome://branding/content/icon128.png"},
+  // Bug 1339424 (wontfix?)
+  {file: "chrome://browser/locale/taskbar.properties",
+   platforms: ["linux", "macosx"]},
+  // Bug 1320156
+  {file: "chrome://browser/skin/Privacy-16.png", platforms: ["linux"]},
+  // Bug 1343584
+  {file: "chrome://browser/skin/click-to-play-warning-stripes.png"},
+  // Bug 1343824
+  {file: "chrome://browser/skin/customizableui/customize-illustration-rtl@2x.png",
+   platforms: ["linux", "win"]},
+  {file: "chrome://browser/skin/customizableui/customize-illustration@2x.png",
+   platforms: ["linux", "win"]},
+  {file: "chrome://browser/skin/customizableui/info-icon-customizeTip@2x.png",
+   platforms: ["linux", "win"]},
+  {file: "chrome://browser/skin/customizableui/panelarrow-customizeTip@2x.png",
+   platforms: ["linux", "win"]},
+  // Bug 1320058
+  {file: "chrome://browser/skin/preferences/saveFile.png", platforms: ["win"]},
+  // Bug 1348369
+  {file: "chrome://formautofill/content/editProfile.xhtml"},
+  // Bug 1316187
+  {file: "chrome://global/content/customizeToolbar.xul"},
+  // Bug 1343837
+  {file: "chrome://global/content/findUtils.js"},
+  // Bug 1343843
+  {file: "chrome://global/content/url-classifier/unittests.xul"},
+  // Bug 1343839
+  {file: "chrome://global/locale/headsUpDisplay.properties"},
+  // Bug 1348358
+  {file: "chrome://global/skin/arrow.css"},
+  {file: "chrome://global/skin/arrow/arrow-dn-sharp.gif",
+   platforms: ["linux", "win"]},
+  {file: "chrome://global/skin/arrow/arrow-down.png",
+   platforms: ["linux", "win"]},
+  {file: "chrome://global/skin/arrow/arrow-lft-sharp-end.gif"},
+  {file: "chrome://global/skin/arrow/arrow-lft-sharp.gif",
+   platforms: ["linux", "win"]},
+  {file: "chrome://global/skin/arrow/arrow-rit-sharp-end.gif"},
+  {file: "chrome://global/skin/arrow/arrow-rit-sharp.gif",
+   platforms: ["linux", "win"]},
+  {file: "chrome://global/skin/arrow/arrow-up-sharp.gif",
+   platforms: ["linux", "win"]},
+  {file: "chrome://global/skin/arrow/panelarrow-horizontal.svg",
+   platforms: ["linux"]},
+  {file: "chrome://global/skin/arrow/panelarrow-vertical.svg",
+   platforms: ["linux"]},
+  // Bug 1348529
+  {file: "chrome://global/skin/checkbox/cbox-check-dis.gif",
+   platforms: ["linux"]},
+  {file: "chrome://global/skin/checkbox/cbox-check.gif", platforms: ["linux"]},
+  // Bug 1348359
+  {file: "chrome://global/skin/dirListing/folder.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/dirListing/local.png", platforms: ["linux", "win"]},
+  {file: "chrome://global/skin/dirListing/remote.png"},
+  {file: "chrome://global/skin/dirListing/up.png", platforms: ["linux"]},
+  // Bug 1348362
+  {file: "chrome://global/skin/icons/Close.gif", platforms: ["win"]},
+  {file: "chrome://global/skin/icons/Error.png", platforms: ["linux", "macosx"]},
+  {file: "chrome://global/skin/icons/Landscape.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/Minimize.gif", platforms: ["win"]},
+  {file: "chrome://global/skin/icons/Portrait.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/Print-preview.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/Question.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/Restore.gif", platforms: ["win"]},
+  {file: "chrome://global/skin/icons/Search-close.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/Search-glass.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/Warning.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/checkbox.png", platforms: ["macosx"]},
+  {file: "chrome://global/skin/icons/checkbox@2x.png", platforms: ["macosx"]},
+  {file: "chrome://global/skin/icons/close-inverted.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/close-inverted@2x.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/close.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/close@2x.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/collapse.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/error-64.png", platforms: ["linux", "win"]},
+  {file: "chrome://global/skin/icons/error-large.png", platforms: ["macosx"]},
+  {file: "chrome://global/skin/icons/expand.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/folder-item.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/question-large.png", platforms: ["macosx"]},
+  {file: "chrome://global/skin/icons/warning-32.png", platforms: ["macosx"]},
+  {file: "chrome://global/skin/icons/warning-64.png", platforms: ["linux", "win"]},
+  {file: "chrome://global/skin/icons/warning-large.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/icons/windowControls.png", platforms: ["linux"]},
+  // Bug 1348521
+  {file: "chrome://global/skin/linkTree.css"},
+  // Bug 1348522
+  {file: "chrome://global/skin/media/clicktoplay-bgtexture.png"},
+  {file: "chrome://global/skin/media/videoClickToPlayButton.svg"},
+  // Bug 1348524
+  {file: "chrome://global/skin/notification/close.png", platforms: ["macosx"]},
+  // Bug 1348525
+  {file: "chrome://global/skin/splitter/grip-bottom.gif", platforms: ["linux"]},
+  {file: "chrome://global/skin/splitter/grip-left.gif", platforms: ["linux"]},
+  {file: "chrome://global/skin/splitter/grip-right.gif", platforms: ["linux"]},
+  {file: "chrome://global/skin/splitter/grip-top.gif", platforms: ["linux"]},
+  // Bug 1348526
+  {file: "chrome://global/skin/tree/sort-asc-classic.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/tree/sort-asc.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/tree/sort-dsc-classic.png", platforms: ["linux"]},
+  {file: "chrome://global/skin/tree/sort-dsc.png", platforms: ["linux"]},
+  // Bug 1344267
+  {file: "chrome://marionette/content/test_anonymous_content.xul"},
+  {file: "chrome://marionette/content/test_dialog.properties"},
+  {file: "chrome://marionette/content/test_dialog.xul"},
+  // Bug 1348532
+  {file: "chrome://mozapps/content/extensions/list.xul"},
+  // Bug 1348533
+  {file: "chrome://mozapps/skin/downloads/buttons.png", platforms: ["macosx"]},
+  {file: "chrome://mozapps/skin/downloads/downloadButtons.png", platforms: ["linux", "win"]},
+  // Bug 1348555
+  {file: "chrome://mozapps/skin/extensions/dictionaryGeneric-16.png"},
+  {file: "chrome://mozapps/skin/extensions/search.png", platforms: ["macosx"]},
+  {file: "chrome://mozapps/skin/extensions/themeGeneric-16.png"},
+  // Bug 1348556
+  {file: "chrome://mozapps/skin/plugins/pluginBlocked.png"},
+  // Bug 1348558
+  {file: "chrome://mozapps/skin/update/downloadButtons.png",
+   platforms: ["linux"]},
+  // Bug 1348559
+  {file: "chrome://pippki/content/resetpassword.xul"},
+  // Bug 1348562
+  {file: "chrome://devtools/skin/images/debugging-devices.svg",
+  isFromDevTools: true},
+  {file: "chrome://devtools/skin/images/fast-forward.svg",
+  isFromDevTools: true},
+  {file: "chrome://devtools/skin/images/firebug/spinner.png",
+  isFromDevTools: true},
+  {file: "chrome://devtools/skin/images/noise.png",
+  isFromDevTools: true},
+
+].filter(item =>
+  ("isFromDevTools" in item) == isDevtools &&
+  (!item.platforms || item.platforms.includes(AppConstants.platform))
+).map(item => item.file));
+
+const ignorableWhitelist = new Set([
+  // chrome://xslt-qa/ isn't referenced, but isn't included in packaged builds,
+  // so it's fine to just ignore it and ignore if the exceptions are unused.
+  "chrome://xslt-qa/content/buster/result-view.xul",
+  "chrome://xslt-qa/content/xslt-qa-overlay.xul",
+  // The communicator.css file is kept for add-on backward compat, but it is
+  // referenced by something in xslt-qa, so the exception won't be used when
+  // running the test on a local non-packaged build.
+  "chrome://communicator/skin/communicator.css",
+
+  // These 2 files are unreferenced only when building without the crash
+  // reporter (eg. Linux x64 asan builds on treeherder)
+  "chrome://global/locale/crashes.dtd",
+  "chrome://global/locale/crashes.properties",
+]);
+for (let entry of ignorableWhitelist)
+  whitelist.add(entry);
+
+const gInterestingCategories = new Set([
+  "agent-style-sheets", "webextension-scripts",
+  "webextension-schemas", "webextension-scripts-addon",
+  "webextension-scripts-content", "webextension-scripts-devtools"
+]);
+
+var gChromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
+                 .getService(Ci.nsIChromeRegistry);
+var gChromeMap = new Map();
+var gOverrideMap = new Map();
+var gReferencesFromCode = new Set();
+
+function getBaseUriForChromeUri(chromeUri) {
+  let chromeFile = chromeUri + "gobbledygooknonexistentfile.reallynothere";
+  let uri = Services.io.newURI(chromeFile);
+  let fileUri = gChromeReg.convertChromeURL(uri);
+  return fileUri.resolve(".");
+}
+
+function parseManifest(manifestUri) {
+  return fetchFile(manifestUri.spec).then(data => {
+    for (let line of data.split("\n")) {
+      let [type, ...argv] = line.split(/\s+/);
+      if (type == "content" || type == "skin" || type == "locale") {
+        let chromeUri = `chrome://${argv[0]}/${type}/`;
+        gChromeMap.set(getBaseUriForChromeUri(chromeUri), chromeUri);
+      } else if (type == "override" || type == "overlay") {
+        // Overlays aren't really overrides, but behave the same in
+        // that the overlay is only referenced if the original xul
+        // file is referenced somewhere.
+        let os = "os=" + Services.appinfo.OS;
+        if (!argv.some(s => s.startsWith("os=") && s != os)) {
+          gOverrideMap.set(Services.io.newURI(argv[1]).specIgnoringRef,
+                           Services.io.newURI(argv[0]).specIgnoringRef);
+        }
+      } else if (type == "category" && gInterestingCategories.has(argv[0])) {
+        gReferencesFromCode.add(argv[2]);
+      }
+    }
+  });
+}
+
+function parseCSSFile(fileUri) {
+  return fetchFile(fileUri.spec).then(data => {
+    for (let line of data.split("\n")) {
+      let urls = line.match(/url\([^()]+\)/g);
+      if (!urls) {
+        // @import rules can take a string instead of a url.
+        let importMatch = line.match(/@import ['"]?([^'"]*)['"]?/);
+        if (importMatch && importMatch[1]) {
+          let url = Services.io.newURI(importMatch[1], null, fileUri).spec;
+          gReferencesFromCode.add(convertToChromeUri(url));
+        }
+        continue;
+      }
+
+      for (let url of urls) {
+        // Remove the url(" prefix and the ") suffix.
+        url = url.replace(/url\(([^)]*)\)/, "$1")
+                 .replace(/^"(.*)"$/, "$1")
+                 .replace(/^'(.*)'$/, "$1");
+        if (url.startsWith("data:"))
+          continue;
+
+        try {
+          url = Services.io.newURI(url, null, fileUri).specIgnoringRef;
+          gReferencesFromCode.add(convertToChromeUri(url));
+        } catch (e) {
+          ok(false, "unexpected error while resolving this URI: " + url);
+        }
+      }
+    }
+  });
+}
+
+function parseCodeFile(fileUri) {
+  return fetchFile(fileUri.spec).then(data => {
+    for (let line of data.split("\n")) {
+      let urls =
+        line.match(/["']chrome:\/\/[a-zA-Z0-9 -]+\/(content|skin|locale)\/[^"' ]*["']/g);
+      if (!urls) {
+        // If there's no absolute chrome URL, look for relative ones in
+        // src and href attributes.
+        let match = line.match("(?:src|href)=[\"']([^$&\"']+)");
+        if (match && match[1]) {
+          let url = Services.io.newURI(match[1], null, fileUri).spec;
+          gReferencesFromCode.add(convertToChromeUri(url));
+        }
+
+        if (isDevtools) {
+          // Handle usage of devtools' LocalizationHelper object
+          match = line.match('"devtools/client/locales/([^/.]+).properties"');
+          if (match && match[1]) {
+            gReferencesFromCode.add("chrome://devtools/locale/" +
+                                    match[1] + ".properties");
+          }
+
+          match = line.match('"devtools/shared/locales/([^/.]+).properties"');
+          if (match && match[1]) {
+            gReferencesFromCode.add("chrome://devtools-shared/locale/" +
+                                    match[1] + ".properties");
+          }
+        }
+        continue;
+      }
+
+      for (let url of urls) {
+        // Remove quotes.
+        url = url.slice(1, -1);
+        // Remove ? or \ trailing characters.
+        if (url.endsWith("?") || url.endsWith("\\"))
+          url = url.slice(0, -1);
+
+        // Make urls like chrome://browser/skin/ point to an actual file,
+        // and remove the ref if any.
+        url = Services.io.newURI(url).specIgnoringRef;
+
+        gReferencesFromCode.add(url);
+      }
+    }
+  });
+}
+
+function convertToChromeUri(fileUri) {
+  let baseUri = fileUri;
+  let path = "";
+  while (true) {
+    let slashPos = baseUri.lastIndexOf("/", baseUri.length - 2);
+    if (slashPos <= 0) {
+      // File not accessible from chrome protocol,
+      // TODO: bug 1349005 handle resource:// urls.
+      return fileUri;
+    }
+    path = baseUri.slice(slashPos + 1) + path;
+    baseUri = baseUri.slice(0, slashPos + 1);
+    if (gChromeMap.has(baseUri)) {
+      let chromeBaseUri = gChromeMap.get(baseUri);
+      return `${chromeBaseUri}${path}`;
+    }
+  }
+}
+
+function chromeFileExists(aURI) {
+  let available = 0;
+  try {
+    let channel = NetUtil.newChannel({uri: aURI, loadUsingSystemPrincipal: true});
+    let stream = channel.open();
+    let sstream = Cc["@mozilla.org/scriptableinputstream;1"]
+                    .createInstance(Ci.nsIScriptableInputStream);
+    sstream.init(stream);
+    available = sstream.available();
+    sstream.close();
+  } catch (e) {
+    if (e.result != Components.results.NS_ERROR_FILE_NOT_FOUND) {
+      dump("Checking " + aURI + ": " + e + "\n");
+      Cu.reportError(e);
+    }
+  }
+  return available > 0;
+}
+
+function findChromeUrlsFromArray(array) {
+  const prefix = "chrome://";
+  // Find the 'c' character...
+  for (let index = 0;
+       (index = array.indexOf(prefix.charCodeAt(0), index)) != -1;
+       ++index) {
+    // Then ensure we actually have the whole chrome:// prefix.
+    let found = true;
+    for (let i = 1; i < prefix.length; ++i) {
+      if (array[index + i] != prefix.charCodeAt(i)) {
+        found = false;
+        break;
+      }
+    }
+    if (!found)
+      continue;
+
+    // C strings are null terminated, but " also terminates urls
+    // (nsIndexedToHTML.cpp contains an HTML fragment with several chrome urls)
+    // Let's also terminate the string on the # character to skip references.
+    let end = Math.min(array.indexOf(0, index),
+                       array.indexOf('"'.charCodeAt(0), index),
+                       array.indexOf("#".charCodeAt(0), index));
+    let string = "";
+    for ( ; index < end; ++index)
+      string += String.fromCharCode(array[index]);
+
+    // Only keep strings that look like real chrome urls.
+    if (/chrome:\/\/[a-zA-Z09 -]+\/(content|skin|locale)\//.test(string))
+      gReferencesFromCode.add(string);
+  }
+}
+
+add_task(function* checkAllTheFiles() {
+  let libxulPath = OS.Constants.Path.libxul;
+  if (AppConstants.platform != "macosx")
+    libxulPath = OS.Constants.Path.libDir + "/" + libxulPath;
+  let libxul = yield OS.File.read(libxulPath);
+  findChromeUrlsFromArray(libxul);
+  // Handle NS_LITERAL_STRING.
+  findChromeUrlsFromArray(new Uint16Array(libxul.buffer));
+
+  const kCodeExtensions = [".xul", ".xml", ".xsl", ".js", ".jsm", ".html", ".xhtml"];
+
+  let appDir = Services.dirsvc.get("GreD", Ci.nsIFile);
+  // This asynchronously produces a list of URLs (sadly, mostly sync on our
+  // test infrastructure because it runs against jarfiles there, and
+  // our zipreader APIs are all sync)
+  let uris = yield generateURIsFromDirTree(appDir, [".css", ".manifest", ".json", ".jpg", ".png", ".gif", ".svg",  ".dtd", ".properties"].concat(kCodeExtensions));
+
+  // Parse and remove all manifests from the list.
+  // NOTE that this must be done before filtering out devtools paths
+  // so that all chrome paths can be recorded.
+  let manifestPromises = [];
+  uris = uris.filter(uri => {
+    let path = uri.path;
+    if (path.endsWith(".manifest")) {
+      manifestPromises.push(parseManifest(uri));
+      return false;
+    }
+
+    return true;
+  });
+
+  // Wait for all manifest to be parsed
+  yield Promise.all(manifestPromises);
+
+  // We build a list of promises that get resolved when their respective
+  // files have loaded and produced no errors.
+  let allPromises = [];
+
+  for (let uri of uris) {
+    let path = uri.path;
+    if (path.endsWith(".css"))
+      allPromises.push(parseCSSFile(uri));
+    else if (kCodeExtensions.some(ext => path.endsWith(ext)))
+      allPromises.push(parseCodeFile(uri));
+  }
+
+  // Wait for all the files to have actually loaded:
+  yield Promise.all(allPromises);
+
+  // Keep only chrome:// files, and filter out either the devtools paths or
+  // the non-devtools paths:
+  let devtoolsPrefixes = ["chrome://webide/", "chrome://devtools"];
+  let chromeFiles =
+    uris.map(uri => convertToChromeUri(uri.spec))
+        .filter(u => u.startsWith("chrome://"))
+        .filter(u => isDevtools == devtoolsPrefixes.some(prefix => u.startsWith(prefix)));
+
+  let isUnreferenced =
+    file => !gReferencesFromCode.has(file) &&
+            !gExceptionPaths.some(e => file.startsWith(e)) &&
+            (!gOverrideMap.has(file) || isUnreferenced(gOverrideMap.get(file)));
+
+  let notWhitelisted = file => {
+    if (!whitelist.has(file))
+      return true;
+    whitelist.delete(file);
+    return false;
+  };
+
+  let unreferencedFiles =
+    chromeFiles.filter(isUnreferenced).filter(notWhitelisted).sort();
+
+  is(unreferencedFiles.length, 0, "there should be no unreferenced files");
+  for (let file of unreferencedFiles)
+    ok(false, "unreferenced chrome file: " + file);
+
+  for (let file of whitelist) {
+    if (ignorableWhitelist.has(file))
+      info("ignored unused whitelist entry: " + file);
+    else
+      ok(false, "unused whitelist entry: " + file);
+  }
+
+  for (let file of gReferencesFromCode) {
+    if (isDevtools != devtoolsPrefixes.some(prefix => file.startsWith(prefix)))
+      continue;
+
+    if (file.startsWith("chrome://") && !chromeFileExists(file)) {
+      // Ignore chrome prefixes that have been automatically expanded.
+      let pathParts =
+        file.match("chrome://([^/]+)/content/([^/.]+)\.xul") ||
+        file.match("chrome://([^/]+)/skin/([^/.]+)\.css");
+      if (!pathParts || pathParts[1] != pathParts[2]) {
+        // TODO: bug 1349010 - add a whitelist and make this reliable enough
+        // that we could make the test fail when this catches something new.
+        info("missing file with code reference: " + file);
+      }
+    }
+  }
+});
--- a/browser/base/content/test/static/browser_misused_characters_in_strings.js
+++ b/browser/base/content/test/static/browser_misused_characters_in_strings.js
@@ -124,40 +124,16 @@ function ignoredError(filepath, key, typ
         type == whitelistItem.type) {
       gWhitelist.splice(index, 1);
       return true;
     }
   }
   return false;
 }
 
-function fetchFile(uri) {
-  return new Promise((resolve, reject) => {
-    let xhr = new XMLHttpRequest();
-    xhr.responseType = "text";
-    xhr.open("GET", uri, true);
-    xhr.onreadystatechange = function() {
-      if (this.readyState != this.DONE) {
-        return;
-      }
-      try {
-        resolve(this.responseText);
-      } catch (ex) {
-        ok(false, `Script error reading ${uri}: ${ex}`);
-        resolve("");
-      }
-    };
-    xhr.onerror = error => {
-      ok(false, `XHR error reading ${uri}: ${error}`);
-      resolve("");
-    };
-    xhr.send(null);
-  });
-}
-
 function testForError(filepath, key, str, pattern, type, helpText) {
   if (str.match(pattern) &&
       !ignoredError(filepath, key, type)) {
     ok(false, `${filepath} with key=${key} has a misused ${type}. ${helpText}`);
   }
 }
 
 function testForErrors(filepath, key, str) {
--- a/browser/base/content/test/static/browser_parsable_css.js
+++ b/browser/base/content/test/static/browser_parsable_css.js
@@ -102,40 +102,16 @@ function once(target, name) {
     let cb = () => {
       target.removeEventListener(name, cb);
       resolve();
     };
     target.addEventListener(name, cb);
   });
 }
 
-function fetchFile(uri) {
-  return new Promise((resolve, reject) => {
-    let xhr = new XMLHttpRequest();
-    xhr.responseType = "text";
-    xhr.open("GET", uri, true);
-    xhr.onreadystatechange = function() {
-      if (this.readyState != this.DONE) {
-        return;
-      }
-      try {
-        resolve(this.responseText);
-      } catch (ex) {
-        ok(false, `Script error reading ${uri}: ${ex}`);
-        resolve("");
-      }
-    };
-    xhr.onerror = error => {
-      ok(false, `XHR error reading ${uri}: ${error}`);
-      resolve("");
-    };
-    xhr.send(null);
-  });
-}
-
 var gChromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
                  .getService(Ci.nsIChromeRegistry);
 var gChromeMap = new Map();
 
 function getBaseUriForChromeUri(chromeUri) {
   let chromeFile = chromeUri + "gobbledygooknonexistentfile.reallynothere";
   let uri = Services.io.newURI(chromeFile);
   let fileUri = gChromeReg.convertChromeURL(uri);
--- a/browser/base/content/test/static/head.js
+++ b/browser/base/content/test/static/head.js
@@ -120,8 +120,32 @@ function* generateEntriesFromJarFile(jar
     if (entry.startsWith("jsloader") || entry.startsWith("jssubloader")) {
       continue;
     }
     let entryURISpec = "jar:" + kURIStart + "!/" + entry;
     yield Services.io.newURI(entryURISpec);
   }
   zr.close();
 }
+
+function fetchFile(uri) {
+  return new Promise((resolve, reject) => {
+    let xhr = new XMLHttpRequest();
+    xhr.responseType = "text";
+    xhr.open("GET", uri, true);
+    xhr.onreadystatechange = function() {
+      if (this.readyState != this.DONE) {
+        return;
+      }
+      try {
+        resolve(this.responseText);
+      } catch (ex) {
+        ok(false, `Script error reading ${uri}: ${ex}`);
+        resolve("");
+      }
+    };
+    xhr.onerror = error => {
+      ok(false, `XHR error reading ${uri}: ${error}`);
+      resolve("");
+    };
+    xhr.send(null);
+  });
+}
--- a/devtools/client/framework/test/browser.ini
+++ b/devtools/client/framework/test/browser.ini
@@ -89,10 +89,13 @@ skip-if = e10s # Bug 1069044 - destroyIn
 [browser_toolbox_view_source_04.js]
 [browser_toolbox_window_reload_target.js]
 [browser_toolbox_window_shortcuts.js]
 skip-if = os == "mac" && os_version == "10.8" || os == "win" && os_version == "5.1" # Bug 851129 - Re-enable browser_toolbox_window_shortcuts.js test after leaks are fixed
 [browser_toolbox_window_title_changes.js]
 [browser_toolbox_window_title_frame_select.js]
 [browser_toolbox_zoom.js]
 [browser_two_tabs.js]
-# We want this test to run for mochitest-dt as well, so we include it here:
+# We want these tests to run for mochitest-dt as well, so we include them here:
 [../../../../browser/base/content/test/static/browser_parsable_css.js]
+skip-if = debug # no point in running on both opt and debug, and will likely intermittently timeout on debug
+[../../../../browser/base/content/test/static/browser_all_files_referenced.js]
+skip-if = debug # no point in running on both opt and debug, and will likely intermittently timeout on debug
--- a/dom/file/ipc/Blob.cpp
+++ b/dom/file/ipc/Blob.cpp
@@ -988,17 +988,18 @@ RemoteInputStream::BlockAndWaitForStream
     InputStreamParams params;
     OptionalFileDescriptorSet optionalFDs;
 
     mActor->SendBlobStreamSync(mStart, mLength, &params, &optionalFDs);
 
     nsTArray<FileDescriptor> fds;
     OptionalFileDescriptorSetToFDs(optionalFDs, fds);
 
-    nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(params, fds);
+    nsCOMPtr<nsIInputStream> stream =
+      InputStreamHelper::DeserializeInputStream(params, fds);
     MOZ_ASSERT(stream);
 
     SetStream(stream);
     return NS_OK;
   }
 
   ReallyBlockAndWaitForStream();
 
@@ -4445,17 +4446,17 @@ BlobParent::RecvPBlobStreamConstructor(P
     return IPC_FAIL_NO_REASON(this);
   }
 
   // If the stream is entirely backed by memory then we can serialize and send
   // it immediately.
   if (mBlobImpl->IsMemoryFile()) {
     InputStreamParams params;
     nsTArray<FileDescriptor> fds;
-    SerializeInputStream(stream, params, fds);
+    InputStreamHelper::SerializeInputStream(stream, params, fds);
 
     MOZ_ASSERT(params.type() != InputStreamParams::T__None);
     MOZ_ASSERT(fds.IsEmpty());
 
     if (!actor->Destroy(params, void_t())) {
       return IPC_FAIL_NO_REASON(this);
     }
     return IPC_OK();
@@ -4783,17 +4784,18 @@ InputStreamChild::Recv__delete__(const I
   mRemoteStream->AssertIsOnOwningThread();
 
   nsTArray<FileDescriptor> fds;
   OptionalFileDescriptorSetToFDs(
     // XXX Fix this somehow...
     const_cast<OptionalFileDescriptorSet&>(aOptionalSet),
     fds);
 
-  nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
+  nsCOMPtr<nsIInputStream> stream =
+    InputStreamHelper::DeserializeInputStream(aParams, fds);
   MOZ_ASSERT(stream);
 
   mRemoteStream->SetStream(stream);
   return IPC_OK();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/smil/nsSMILAnimationController.cpp
+++ b/dom/smil/nsSMILAnimationController.cpp
@@ -380,23 +380,26 @@ nsSMILAnimationController::DoSample(bool
   // their animation effects removed in sample 'n').
   //
   // Parts (i) and (ii) are not functionally related but we combine them here to
   // save iterating over the animation elements twice.
 
   // Create the compositor table
   nsAutoPtr<nsSMILCompositorTable>
     currentCompositorTable(new nsSMILCompositorTable(0));
+  nsTArray<RefPtr<SVGAnimationElement>>
+    animElems(mAnimationElementTable.Count());
 
   for (auto iter = mAnimationElementTable.Iter(); !iter.Done(); iter.Next()) {
     SVGAnimationElement* animElem = iter.Get()->GetKey();
     SampleTimedElement(animElem, &activeContainers);
     AddAnimationToCompositorTable(animElem,
                                   currentCompositorTable,
                                   isStyleFlushNeeded);
+    animElems.AppendElement(animElem);
   }
   activeContainers.Clear();
 
   // STEP 4: Compare previous sample's compositors against this sample's.
   // (Transfer cached base values across, & remove animation effects from
   // no-longer-animated targets.)
   if (mLastCompositorTable) {
     // * Transfer over cached base values, from last sample's compositors
--- a/ipc/glue/IPCStreamUtils.cpp
+++ b/ipc/glue/IPCStreamUtils.cpp
@@ -387,17 +387,17 @@ DeserializeIPCStream(const IPCStream& aV
     MOZ_ASSERT(fdSetActor);
 
     fdSetActor->ForgetFileDescriptors(fds);
     MOZ_ASSERT(!fds.IsEmpty());
 
     Unused << fdSetActor->Send__delete__(fdSetActor);
   }
 
-  return DeserializeInputStream(streamWithFds.stream(), fds);
+  return InputStreamHelper::DeserializeInputStream(streamWithFds.stream(), fds);
 }
 
 already_AddRefed<nsIInputStream>
 DeserializeIPCStream(const OptionalIPCStream& aValue)
 {
   if (aValue.type() == OptionalIPCStream::Tvoid_t) {
     return nullptr;
   }
--- a/ipc/glue/InputStreamUtils.cpp
+++ b/ipc/glue/InputStreamUtils.cpp
@@ -35,53 +35,38 @@ NS_DEFINE_CID(kMIMEInputStreamCID, NS_MI
 NS_DEFINE_CID(kMultiplexInputStreamCID, NS_MULTIPLEXINPUTSTREAM_CID);
 
 } // namespace
 
 namespace mozilla {
 namespace ipc {
 
 void
-SerializeInputStream(nsIInputStream* aInputStream,
-                     InputStreamParams& aParams,
-                     nsTArray<FileDescriptor>& aFileDescriptors)
+InputStreamHelper::SerializeInputStream(nsIInputStream* aInputStream,
+                                        InputStreamParams& aParams,
+                                        nsTArray<FileDescriptor>& aFileDescriptors)
 {
   MOZ_ASSERT(aInputStream);
 
   nsCOMPtr<nsIIPCSerializableInputStream> serializable =
     do_QueryInterface(aInputStream);
   if (!serializable) {
     MOZ_CRASH("Input stream is not serializable!");
   }
 
   serializable->Serialize(aParams, aFileDescriptors);
 
   if (aParams.type() == InputStreamParams::T__None) {
     MOZ_CRASH("Serialize failed!");
   }
 }
 
-void
-SerializeInputStream(nsIInputStream* aInputStream,
-                     OptionalInputStreamParams& aParams,
-                     nsTArray<FileDescriptor>& aFileDescriptors)
-{
-  if (aInputStream) {
-    InputStreamParams params;
-    SerializeInputStream(aInputStream, params, aFileDescriptors);
-    aParams = params;
-  }
-  else {
-    aParams = mozilla::void_t();
-  }
-}
-
 already_AddRefed<nsIInputStream>
-DeserializeInputStream(const InputStreamParams& aParams,
-                       const nsTArray<FileDescriptor>& aFileDescriptors)
+InputStreamHelper::DeserializeInputStream(const InputStreamParams& aParams,
+                                          const nsTArray<FileDescriptor>& aFileDescriptors)
 {
   nsCOMPtr<nsIInputStream> stream;
   nsCOMPtr<nsIIPCSerializableInputStream> serializable;
 
   switch (aParams.type()) {
     case InputStreamParams::TStringInputStreamParams:
       serializable = do_CreateInstance(kStringInputStreamCID);
       break;
@@ -161,33 +146,10 @@ DeserializeInputStream(const InputStream
   }
 
   stream = do_QueryInterface(serializable);
   MOZ_ASSERT(stream);
 
   return stream.forget();
 }
 
-already_AddRefed<nsIInputStream>
-DeserializeInputStream(const OptionalInputStreamParams& aParams,
-                       const nsTArray<FileDescriptor>& aFileDescriptors)
-{
-  nsCOMPtr<nsIInputStream> stream;
-
-  switch (aParams.type()) {
-    case OptionalInputStreamParams::Tvoid_t:
-      // Leave stream null.
-      break;
-
-    case OptionalInputStreamParams::TInputStreamParams:
-      stream = DeserializeInputStream(aParams.get_InputStreamParams(),
-                                      aFileDescriptors);
-      break;
-
-    default:
-      MOZ_ASSERT(false, "Unknown params!");
-  }
-
-  return stream.forget();
-}
-
 } // namespace ipc
 } // namespace mozilla
--- a/ipc/glue/InputStreamUtils.h
+++ b/ipc/glue/InputStreamUtils.h
@@ -12,30 +12,26 @@
 #include "nsIInputStream.h"
 #include "nsTArray.h"
 
 namespace mozilla {
 namespace ipc {
 
 class FileDescriptor;
 
-void
-SerializeInputStream(nsIInputStream* aInputStream,
-                     InputStreamParams& aParams,
-                     nsTArray<FileDescriptor>& aFileDescriptors);
+// If you want to serialize an inputStream, please use AutoIPCStream.
+class InputStreamHelper
+{
+public:
+  static void
+  SerializeInputStream(nsIInputStream* aInputStream,
+                       InputStreamParams& aParams,
+                       nsTArray<FileDescriptor>& aFileDescriptors);
 
-void
-SerializeInputStream(nsIInputStream* aInputStream,
-                     OptionalInputStreamParams& aParams,
-                     nsTArray<FileDescriptor>& aFileDescriptors);
-
-already_AddRefed<nsIInputStream>
-DeserializeInputStream(const InputStreamParams& aParams,
-                       const nsTArray<FileDescriptor>& aFileDescriptors);
-
-already_AddRefed<nsIInputStream>
-DeserializeInputStream(const OptionalInputStreamParams& aParams,
-                       const nsTArray<FileDescriptor>& aFileDescriptors);
+  static already_AddRefed<nsIInputStream>
+  DeserializeInputStream(const InputStreamParams& aParams,
+                         const nsTArray<FileDescriptor>& aFileDescriptors);
+};
 
 } // namespace ipc
 } // namespace mozilla
 
 #endif // mozilla_ipc_InputStreamUtils_h
--- a/layout/base/MobileViewportManager.cpp
+++ b/layout/base/MobileViewportManager.cpp
@@ -300,16 +300,23 @@ MobileViewportManager::UpdateDisplayPort
     bool hasResolution = mPresShell->ScaleToResolution() &&
         mPresShell->GetResolution() != 1.0f;
     if (!hasDisplayPort && !hasResolution) {
       // We only want to update the displayport if there is one already, or
       // add one if there's a resolution on the document (see bug 1225508
       // comment 1).
       return;
     }
+    nsRect displayportBase =
+      nsRect(nsPoint(0, 0), nsLayoutUtils::CalculateCompositionSizeForFrame(root));
+    // We only create MobileViewportManager for root content documents. If that ever changes
+    // we'd need to limit the size of this displayport base rect because non-toplevel documents
+    // have no limit on their size.
+    MOZ_ASSERT(mPresShell->GetPresContext()->IsRootContentDocument());
+    nsLayoutUtils::SetDisplayPortBaseIfNotSet(root->GetContent(), displayportBase);
     nsIScrollableFrame* scrollable = do_QueryFrame(root);
     nsLayoutUtils::CalculateAndSetDisplayPortMargins(scrollable,
       nsLayoutUtils::RepaintMode::DoNotRepaint);
   }
 }
 
 void
 MobileViewportManager::RefreshSPCSPS()
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -1167,22 +1167,18 @@ SyncViewsAndInvalidateDescendants(nsIFra
                   "should only be called within ApplyRenderingChangeToTree");
   NS_ASSERTION(nsChangeHint_size_t(aChange) ==
                           (aChange & (nsChangeHint_RepaintFrame |
                                       nsChangeHint_SyncFrameView |
                                       nsChangeHint_UpdateOpacityLayer |
                                       nsChangeHint_SchedulePaint)),
                "Invalid change flag");
 
-  nsView* view = aFrame->GetView();
-  if (view) {
-    if (aChange & nsChangeHint_SyncFrameView) {
-      nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(), aFrame,
-                                                nullptr, view);
-    }
+  if (aChange & nsChangeHint_SyncFrameView) {
+    aFrame->SyncFrameViewProperties();
   }
 
   nsIFrame::ChildListIterator lists(aFrame);
   for (; !lists.IsDone(); lists.Next()) {
     for (nsIFrame* child : lists.CurrentList()) {
       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
         // only do frames that don't have placeholders
         if (nsGkAtoms::placeholderFrame == child->GetType()) {
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -2727,18 +2727,17 @@ nsCSSFrameConstructor::ConstructRootFram
   // Would it really kill us to pass in the root element or something?
   // What would that break?
   viewportFrame->Init(nullptr, nullptr, nullptr);
 
   // Bind the viewport frame to the root view
   nsView* rootView = mPresShell->GetViewManager()->GetRootView();
   viewportFrame->SetView(rootView);
 
-  nsContainerFrame::SyncFrameViewProperties(mPresShell->GetPresContext(), viewportFrame,
-                                            viewportPseudoStyle, rootView);
+  viewportFrame->SyncFrameViewProperties(rootView);
   nsContainerFrame::SyncWindowProperties(mPresShell->GetPresContext(), viewportFrame,
                                          rootView, nullptr, nsContainerFrame::SET_ASYNC);
 
   // Make it an absolute container for fixed-pos elements
   viewportFrame->AddStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN);
   viewportFrame->MarkAsAbsoluteContainingBlock();
 
   return viewportFrame;
@@ -3236,20 +3235,16 @@ nsCSSFrameConstructor::InitializeSelectF
 
   scrollFrame->Init(aContent, geometricParent, nullptr);
 
   if (!aBuildCombobox) {
     aState.AddChild(scrollFrame, aFrameItems, aContent,
                     aStyleContext, aParentFrame);
   }
 
-  if (aBuildCombobox) {
-    nsContainerFrame::CreateViewForFrame(scrollFrame, true);
-  }
-
   BuildScrollFrame(aState, aContent, aStyleContext, scrolledFrame,
                    geometricParent, scrollFrame);
 
   if (aState.mFrameState) {
     // Restore frame state for the scroll frame
     RestoreFrameStateFor(scrollFrame, aState.mFrameState);
   }
 
@@ -3924,18 +3919,16 @@ nsCSSFrameConstructor::ConstructFrameFro
     if ((bits & FCDATA_MAY_NEED_SCROLLFRAME) &&
         display->IsScrollableOverflow()) {
       nsContainerFrame* scrollframe = nullptr;
       BuildScrollFrame(aState, content, styleContext, newFrame,
                        geometricParent, scrollframe);
       frameToAddToList = scrollframe;
     } else {
       InitAndRestoreFrame(aState, content, geometricParent, newFrame);
-      // See whether we need to create a view
-      nsContainerFrame::CreateViewForFrame(newFrame, false);
       frameToAddToList = newFrame;
     }
 
     // Use frameToAddToList as the primary frame.  In the non-scrollframe case
     // they're equal, but in the scrollframe case newFrame is the scrolled
     // frame, while frameToAddToList is the scrollframe (and should be the
     // primary frame).
     primaryFrame = frameToAddToList;
--- a/layout/forms/nsListControlFrame.cpp
+++ b/layout/forms/nsListControlFrame.cpp
@@ -88,16 +88,17 @@ NS_NewListControlFrame(nsIPresShell* aPr
   return it;
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsListControlFrame)
 
 //---------------------------------------------------------
 nsListControlFrame::nsListControlFrame(nsStyleContext* aContext)
   : nsHTMLScrollFrame(aContext, false),
+    mView(nullptr),
     mMightNeedSecondPass(false),
     mHasPendingInterruptAtStartOfReflow(false),
     mDropdownCanGrow(false),
     mForceSelection(false),
     mLastDropdownComputedBSize(NS_UNCONSTRAINEDSIZE)
 {
   mComboboxFrame      = nullptr;
   mChangesSinceDragStart = false;
@@ -970,16 +971,21 @@ nsListControlFrame::SetInitialChildList(
 //---------------------------------------------------------
 void
 nsListControlFrame::Init(nsIContent*       aContent,
                          nsContainerFrame* aParent,
                          nsIFrame*         aPrevInFlow)
 {
   nsHTMLScrollFrame::Init(aContent, aParent, aPrevInFlow);
 
+  if (IsInDropDownMode()) {
+    AddStateBits(NS_FRAME_IN_POPUP);
+    CreateView();
+  }
+
   // we shouldn't have to unregister this listener because when
   // our frame goes away all these content node go away as well
   // because our frame is the only one who references them.
   // we need to hook up our listeners before the editor is initialized
   mEventListener = new nsListEventListener(this);
 
   mContent->AddSystemEventListener(NS_LITERAL_STRING("keydown"),
                                    mEventListener, false, false);
@@ -991,20 +997,16 @@ nsListControlFrame::Init(nsIContent*    
                                    mEventListener, false, false);
   mContent->AddSystemEventListener(NS_LITERAL_STRING("mousemove"),
                                    mEventListener, false, false);
 
   mStartSelectionIndex = kNothingSelected;
   mEndSelectionIndex = kNothingSelected;
 
   mLastDropdownBackstopColor = PresContext()->DefaultBackgroundColor();
-
-  if (IsInDropDownMode()) {
-    AddStateBits(NS_FRAME_IN_POPUP);
-  }
 }
 
 dom::HTMLOptionsCollection*
 nsListControlFrame::GetOptions() const
 {
   dom::HTMLSelectElement* select =
     dom::HTMLSelectElement::FromContentOrNull(mContent);
   NS_ENSURE_TRUE(select, nullptr);
--- a/layout/forms/nsListControlFrame.h
+++ b/layout/forms/nsListControlFrame.h
@@ -235,21 +235,16 @@ public:
 
   /**
    * Return true if the drop-down list can display more rows.
    * (always false if not in drop-down mode)
    */
   bool GetDropdownCanGrow() const { return mDropdownCanGrow; }
 
   /**
-   * Dropdowns need views
-   */
-  virtual bool NeedsView() override { return IsInDropDownMode(); }
-
-  /**
    * Frees statics owned by this class.
    */
   static void Shutdown();
 
 #ifdef ACCESSIBILITY
   /**
    * Post a custom DOM event for the change, so that accessibility can
    * fire a native focus event for accessibility
@@ -393,26 +388,33 @@ protected:
     return GetOptionsContainer()->BSizeOfARow();
   }
 
   /**
    * @return how many displayable options/optgroups this frame has.
    */
   uint32_t GetNumberOfRows();
 
+  nsView* GetViewInternal() const override { return mView; }
+  void SetViewInternal(nsView* aView) override { mView = aView; }
+
   // Data Members
   int32_t      mStartSelectionIndex;
   int32_t      mEndSelectionIndex;
 
-  nsIComboboxControlFrame *mComboboxFrame;
-  uint32_t     mNumDisplayRows;
+  nsIComboboxControlFrame* mComboboxFrame;
+
+  // The view is only created (& non-null) if IsInDropDownMode() is true.
+  nsView* mView;
+
+  uint32_t mNumDisplayRows;
   bool mChangesSinceDragStart:1;
   bool mButtonDown:1;
-  // Has the user selected a visible item since we showed the
-  // dropdown?
+
+  // Has the user selected a visible item since we showed the dropdown?
   bool mItemSelectionStarted:1;
 
   bool mIsAllContentHere:1;
   bool mIsAllFramesHere:1;
   bool mHasBeenInitialized:1;
   bool mNeedToReset:1;
   bool mPostChildrenLoadedReset:1;
 
--- a/layout/generic/Selection.h
+++ b/layout/generic/Selection.h
@@ -96,22 +96,25 @@ public:
   nsresult      PostScrollSelectionIntoViewEvent(
                                         SelectionRegion aRegion,
                                         int32_t aFlags,
                                         nsIPresShell::ScrollAxis aVertical,
                                         nsIPresShell::ScrollAxis aHorizontal);
   enum {
     SCROLL_SYNCHRONOUS = 1<<1,
     SCROLL_FIRST_ANCESTOR_ONLY = 1<<2,
-    SCROLL_DO_FLUSH = 1<<3,
+    SCROLL_DO_FLUSH = 1<<3,  // only matters if SCROLL_SYNCHRONOUS is passed too
     SCROLL_OVERFLOW_HIDDEN = 1<<5,
     SCROLL_FOR_CARET_MOVE = 1<<6
   };
-  // aDoFlush only matters if aIsSynchronous is true.  If not, we'll just flush
-  // when the scroll event fires so we make sure to scroll to the right place.
+  // If aFlags doesn't contain SCROLL_SYNCHRONOUS, then we'll flush when
+  // the scroll event fires so we make sure to scroll to the right place.
+  // Otherwise, if SCROLL_DO_FLUSH is also in aFlags, then this method will
+  // flush layout and you MUST hold a strong ref on 'this' for the duration
+  // of this call.  This might destroy arbitrary layout objects.
   nsresult      ScrollIntoView(SelectionRegion aRegion,
                                nsIPresShell::ScrollAxis aVertical =
                                  nsIPresShell::ScrollAxis(),
                                nsIPresShell::ScrollAxis aHorizontal =
                                  nsIPresShell::ScrollAxis(),
                                int32_t aFlags = 0);
   nsresult      SubtractRange(RangeData* aRange, nsRange* aSubtract,
                               nsTArray<RangeData>* aOutput);
--- a/layout/generic/ViewportFrame.cpp
+++ b/layout/generic/ViewportFrame.cpp
@@ -34,16 +34,18 @@ NS_QUERYFRAME_HEAD(ViewportFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
 void
 ViewportFrame::Init(nsIContent*       aContent,
                     nsContainerFrame* aParent,
                     nsIFrame*         aPrevInFlow)
 {
   nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
+  // No need to call CreateView() here - the frame ctor will call SetView()
+  // with the ViewManager's root view, so we'll assign it in SetViewInternal().
 
   nsIFrame* parent = nsLayoutUtils::GetCrossDocParentFrame(this);
   if (parent) {
     nsFrameState state = parent->GetStateBits();
 
     mState |= state & (NS_FRAME_IN_POPUP);
   }
 }
--- a/layout/generic/ViewportFrame.h
+++ b/layout/generic/ViewportFrame.h
@@ -26,16 +26,17 @@ namespace mozilla {
 class ViewportFrame : public nsContainerFrame {
 public:
   NS_DECL_QUERYFRAME_TARGET(ViewportFrame)
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   explicit ViewportFrame(nsStyleContext* aContext)
     : nsContainerFrame(aContext)
+    , mView(nullptr)
   {}
   virtual ~ViewportFrame() { } // useful for debugging
 
   virtual void Init(nsIContent*       aContent,
                     nsContainerFrame* aParent,
                     nsIFrame*         aPrevInFlow) override;
 
 #ifdef DEBUG
@@ -78,25 +79,30 @@ public:
    * @return the rect to use as containing block rect
    */
   nsRect AdjustReflowInputAsContainingBlock(ReflowInput* aReflowInput) const;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
-private:
-  virtual mozilla::layout::FrameChildListID GetAbsoluteListID() const override { return kFixedList; }
-
 protected:
   /**
    * Calculate how much room is available for fixed frames. That means
    * determining if the viewport is scrollable and whether the vertical and/or
    * horizontal scrollbars are visible.  Adjust the computed width/height and
    * available width for aReflowInput accordingly.
    * @return the current scroll position, or 0,0 if not scrollable
    */
   nsPoint AdjustReflowInputForScrollbars(ReflowInput* aReflowInput) const;
+
+  nsView* GetViewInternal() const override { return mView; }
+  void SetViewInternal(nsView* aView) override { mView = aView; }
+
+private:
+  virtual mozilla::layout::FrameChildListID GetAbsoluteListID() const override { return kFixedList; }
+
+  nsView* mView;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_ViewportFrame_h
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -372,102 +372,16 @@ nsContainerFrame::PeekOffsetCharacter(bo
   NS_ASSERTION (aOffset && *aOffset <= 1, "aOffset out of range");
   // Don't allow the caret to stay in an empty (leaf) container frame.
   return CONTINUE_EMPTY;
 }
 
 /////////////////////////////////////////////////////////////////////////////
 // Helper member functions
 
-static void
-ReparentFrameViewTo(nsIFrame*       aFrame,
-                    nsViewManager* aViewManager,
-                    nsView*        aNewParentView,
-                    nsView*        aOldParentView)
-{
-  if (aFrame->HasView()) {
-#ifdef MOZ_XUL
-    if (aFrame->GetType() == nsGkAtoms::menuPopupFrame) {
-      // This view must be parented by the root view, don't reparent it.
-      return;
-    }
-#endif
-    nsView* view = aFrame->GetView();
-    // Verify that the current parent view is what we think it is
-    //nsView*  parentView;
-    //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
-
-    aViewManager->RemoveChild(view);
-    
-    // The view will remember the Z-order and other attributes that have been set on it.
-    nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, aFrame);
-    aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr);
-  } else if (aFrame->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) {
-    nsIFrame::ChildListIterator lists(aFrame);
-    for (; !lists.IsDone(); lists.Next()) {
-      // Iterate the child frames, and check each child frame to see if it has
-      // a view
-      nsFrameList::Enumerator childFrames(lists.CurrentList());
-      for (; !childFrames.AtEnd(); childFrames.Next()) {
-        ReparentFrameViewTo(childFrames.get(), aViewManager,
-                            aNewParentView, aOldParentView);
-      }
-    }
-  }
-}
-
-void
-nsContainerFrame::CreateViewForFrame(nsIFrame* aFrame,
-                                     bool aForce)
-{
-  if (aFrame->HasView()) {
-    return;
-  }
-
-  // If we don't yet have a view, see if we need a view
-  if (!aForce && !aFrame->NeedsView()) {
-    // don't need a view
-    return;
-  }
-
-  nsView* parentView = aFrame->GetParent()->GetClosestView();
-  NS_ASSERTION(parentView, "no parent with view");
-
-  nsViewManager* viewManager = parentView->GetViewManager();
-  NS_ASSERTION(viewManager, "null view manager");
-
-  // Create a view
-  nsView* view = viewManager->CreateView(aFrame->GetRect(), parentView);
-
-  SyncFrameViewProperties(aFrame->PresContext(), aFrame, nullptr, view);
-
-  nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, aFrame);
-  // we insert this view 'above' the insertBefore view, unless insertBefore is null,
-  // in which case we want to call with aAbove == false to insert at the beginning
-  // in document order
-  viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nullptr);
-
-  // REVIEW: Don't create a widget for fixed-pos elements anymore.
-  // ComputeRepaintRegionForCopy will calculate the right area to repaint
-  // when we scroll.
-  // Reparent views on any child frames (or their descendants) to this
-  // view. We can just call ReparentFrameViewTo on this frame because
-  // we know this frame has no view, so it will crawl the children. Also,
-  // we know that any descendants with views must have 'parentView' as their
-  // parent view.
-  ReparentFrameViewTo(aFrame, viewManager, view, parentView);
-
-  // Remember our view
-  aFrame->SetView(view);
-
-  NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
-               ("nsContainerFrame::CreateViewForFrame: frame=%p view=%p",
-                aFrame, view));
-}
-
 /**
  * Position the view associated with |aKidFrame|, if there is one. A
  * container frame should call this method after positioning a frame,
  * but before |Reflow|.
  */
 void
 nsContainerFrame::PositionFrameView(nsIFrame* aKidFrame)
 {
@@ -538,18 +452,19 @@ nsContainerFrame::ReparentFrameView(nsIF
   nsView* oldParentView = aOldParentFrame->GetClosestView();
   nsView* newParentView = aNewParentFrame->GetClosestView();
   
   // See if the old parent frame and the new parent frame are in the
   // same view sub-hierarchy. If they are then we don't have to do
   // anything
   if (oldParentView != newParentView) {
     // They're not so we need to reparent any child views
-    ReparentFrameViewTo(aChildFrame, oldParentView->GetViewManager(), newParentView,
-                        oldParentView);
+    aChildFrame->ReparentFrameViewTo(oldParentView->GetViewManager(),
+                                     newParentView,
+                                     oldParentView);
   }
 
   return NS_OK;
 }
 
 nsresult
 nsContainerFrame::ReparentFrameViewList(const nsFrameList& aChildFrameList,
                                         nsIFrame*          aOldParentFrame,
@@ -600,17 +515,17 @@ nsContainerFrame::ReparentFrameViewList(
   // See if the old parent frame and the new parent frame are in the
   // same view sub-hierarchy. If they are then we don't have to do
   // anything
   if (oldParentView != newParentView) {
     nsViewManager* viewManager = oldParentView->GetViewManager();
 
     // They're not so we need to reparent any child views
     for (nsFrameList::Enumerator e(aChildFrameList); !e.AtEnd(); e.Next()) {
-      ReparentFrameViewTo(e.get(), viewManager, newParentView, oldParentView);
+      e.get()->ReparentFrameViewTo(viewManager, newParentView, oldParentView);
     }
   }
 
   return NS_OK;
 }
 
 static nsIWidget*
 GetPresContextContainerWidget(nsPresContext* aPresContext)
@@ -764,64 +679,16 @@ nsContainerFrame::SyncFrameViewAfterRefl
 
   if (0 == (aFlags & NS_FRAME_NO_SIZE_VIEW)) {
     nsViewManager* vm = aView->GetViewManager();
 
     vm->ResizeView(aView, aVisualOverflowArea, true);
   }
 }
 
-void
-nsContainerFrame::SyncFrameViewProperties(nsPresContext*  aPresContext,
-                                          nsIFrame*        aFrame,
-                                          nsStyleContext*  aStyleContext,
-                                          nsView*         aView,
-                                          uint32_t         aFlags)
-{
-  NS_ASSERTION(!aStyleContext || aFrame->StyleContext() == aStyleContext,
-               "Wrong style context for frame?");
-
-  if (!aView) {
-    return;
-  }
-
-  nsViewManager* vm = aView->GetViewManager();
-
-  if (nullptr == aStyleContext) {
-    aStyleContext = aFrame->StyleContext();
-  }
-
-  // Make sure visibility is correct. This only affects nsSubdocumentFrame.
-  if (0 == (aFlags & NS_FRAME_NO_VISIBILITY) &&
-      !aFrame->SupportsVisibilityHidden()) {
-    // See if the view should be hidden or visible
-    vm->SetViewVisibility(aView,
-        aStyleContext->StyleVisibility()->IsVisible()
-            ? nsViewVisibility_kShow : nsViewVisibility_kHide);
-  }
-
-  int32_t zIndex = 0;
-  bool    autoZIndex = false;
-
-  if (aFrame->IsAbsPosContainingBlock()) {
-    // Make sure z-index is correct
-    const nsStylePosition* position = aStyleContext->StylePosition();
-
-    if (position->mZIndex.GetUnit() == eStyleUnit_Integer) {
-      zIndex = position->mZIndex.GetIntValue();
-    } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) {
-      autoZIndex = true;
-    }
-  } else {
-    autoZIndex = true;
-  }
-
-  vm->SetViewZIndex(aView, autoZIndex, zIndex);
-}
-
 static nscoord GetCoord(const nsStyleCoord& aCoord, nscoord aIfNotCoord)
 {
   if (aCoord.ConvertsToLength()) {
     return nsRuleNode::ComputeCoordPercentCalc(aCoord, 0);
   }
   return aIfNotCoord;
 }
 
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -150,23 +150,16 @@ public:
    * Delete aNextInFlow and its next-in-flows.
    * @param aDeletingEmptyFrames if set, then the reflow for aNextInFlow's
    * content was complete before aNextInFlow, so aNextInFlow and its
    * next-in-flows no longer map any real content.
    */
   virtual void DeleteNextInFlowChild(nsIFrame* aNextInFlow,
                                      bool      aDeletingEmptyFrames);
 
-  /**
-   * Helper method to wrap views around frames. Used by containers
-   * under special circumstances (can be used by leaf frames as well)
-   */
-  static void CreateViewForFrame(nsIFrame* aFrame,
-                                 bool aForce);
-
   // Positions the frame's view based on the frame's origin
   static void PositionFrameView(nsIFrame* aKidFrame);
 
   static nsresult ReparentFrameView(nsIFrame* aChildFrame,
                                     nsIFrame* aOldParentFrame,
                                     nsIFrame* aNewParentFrame);
 
   static nsresult ReparentFrameViewList(const nsFrameList& aChildFrameList,
@@ -193,28 +186,16 @@ public:
     SET_ASYNC = 0x01,
   };
   static void SyncWindowProperties(nsPresContext*       aPresContext,
                                    nsIFrame*            aFrame,
                                    nsView*              aView,
                                    nsRenderingContext*  aRC,
                                    uint32_t             aFlags);
 
-  // Sets the view's attributes from the frame style.
-  // - visibility
-  // - clip
-  // Call this when one of these styles changes or when the view has just
-  // been created.
-  // @param aStyleContext can be null, in which case the frame's style context is used
-  static void SyncFrameViewProperties(nsPresContext*  aPresContext,
-                                      nsIFrame*        aFrame,
-                                      nsStyleContext*  aStyleContext,
-                                      nsView*         aView,
-                                      uint32_t         aFlags = 0);
-
   /**
    * Converts the minimum and maximum sizes given in inner window app units to
    * outer window device pixel sizes and assigns these constraints to the widget.
    *
    * @param aPresContext pres context
    * @param aWidget widget for this frame
    * @param minimum size of the window in app units
    * @param maxmimum size of the window in app units
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -685,22 +685,18 @@ nsFrame::DestroyFrom(nsIFrame* aDestruct
   if (StyleDisplay()->mPosition == NS_STYLE_POSITION_STICKY) {
     StickyScrollContainer* ssc =
       StickyScrollContainer::GetStickyScrollContainerForFrame(this);
     if (ssc) {
       ssc->RemoveFrame(this);
     }
   }
 
-  // Get the view pointer now before the frame properties disappear
-  // when we call NotifyDestroyingFrame()
-  nsView* view = GetView();
   nsPresContext* presContext = PresContext();
-
-  nsIPresShell *shell = presContext->GetPresShell();
+  nsIPresShell* shell = presContext->GetPresShell();
   if (mState & NS_FRAME_OUT_OF_FLOW) {
     nsPlaceholderFrame* placeholder =
       shell->FrameManager()->GetPlaceholderFrameFor(this);
     NS_ASSERTION(!placeholder || (aDestructRoot != this),
                  "Don't call Destroy() on OOFs, call Destroy() on the placeholder.");
     NS_ASSERTION(!placeholder ||
                  nsLayoutUtils::IsProperAncestorFrame(aDestructRoot, placeholder),
                  "Placeholder relationship should have been torn down already; "
@@ -780,21 +776,19 @@ nsFrame::DestroyFrom(nsIFrame* aDestruct
   PresContext()->GetPresShell()->RemoveFrameFromApproximatelyVisibleList(this);
 
   shell->NotifyDestroyingFrame(this);
 
   if (mState & NS_FRAME_EXTERNAL_REFERENCE) {
     shell->ClearFrameRefs(this);
   }
 
+  nsView* view = GetView();
   if (view) {
-    // Break association between view and frame
     view->SetFrame(nullptr);
-
-    // Destroy the view
     view->Destroy();
   }
 
   // Make sure that our deleted frame can't be returned from GetPrimaryFrame()
   if (isPrimaryFrame) {
     mContent->SetPrimaryFrame(nullptr);
   }
 
@@ -981,16 +975,130 @@ nsFrame::DidSetStyleContext(nsStyleConte
   // context before reflow starts.  See bug 115921.
   if (StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL) {
     PresContext()->SetBidiEnabled();
   }
 
   RemoveStateBits(NS_FRAME_SIMPLE_EVENT_REGIONS);
 }
 
+void
+nsIFrame::ReparentFrameViewTo(nsViewManager* aViewManager,
+                              nsView*        aNewParentView,
+                              nsView*        aOldParentView)
+{
+  if (HasView()) {
+#ifdef MOZ_XUL
+    if (GetType() == nsGkAtoms::menuPopupFrame) {
+      // This view must be parented by the root view, don't reparent it.
+      return;
+    }
+#endif
+    nsView* view = GetView();
+    // Verify that the current parent view is what we think it is
+    //nsView*  parentView;
+    //NS_ASSERTION(parentView == aOldParentView, "unexpected parent view");
+
+    aViewManager->RemoveChild(view);
+    
+    // The view will remember the Z-order and other attributes that have been set on it.
+    nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(aNewParentView, this);
+    aViewManager->InsertChild(aNewParentView, view, insertBefore, insertBefore != nullptr);
+  } else if (GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW) {
+    nsIFrame::ChildListIterator lists(this);
+    for (; !lists.IsDone(); lists.Next()) {
+      // Iterate the child frames, and check each child frame to see if it has
+      // a view
+      nsFrameList::Enumerator childFrames(lists.CurrentList());
+      for (; !childFrames.AtEnd(); childFrames.Next()) {
+        childFrames.get()->ReparentFrameViewTo(aViewManager, aNewParentView,
+                                               aOldParentView);
+      }
+    }
+  }
+}
+
+void
+nsIFrame::SyncFrameViewProperties(nsView* aView)
+{
+  if (!aView) {
+    aView = GetView();
+    if (!aView) {
+      return;
+    }
+  }
+
+  nsViewManager* vm = aView->GetViewManager();
+
+  // Make sure visibility is correct. This only affects nsSubDocumentFrame.
+  if (!SupportsVisibilityHidden()) {
+    // See if the view should be hidden or visible
+    nsStyleContext* sc = StyleContext();
+    vm->SetViewVisibility(aView,
+        sc->StyleVisibility()->IsVisible()
+            ? nsViewVisibility_kShow : nsViewVisibility_kHide);
+  }
+
+  int32_t zIndex = 0;
+  bool    autoZIndex = false;
+
+  if (IsAbsPosContainingBlock()) {
+    // Make sure z-index is correct
+    nsStyleContext* sc = StyleContext();
+    const nsStylePosition* position = sc->StylePosition();
+    if (position->mZIndex.GetUnit() == eStyleUnit_Integer) {
+      zIndex = position->mZIndex.GetIntValue();
+    } else if (position->mZIndex.GetUnit() == eStyleUnit_Auto) {
+      autoZIndex = true;
+    }
+  } else {
+    autoZIndex = true;
+  }
+
+  vm->SetViewZIndex(aView, autoZIndex, zIndex);
+}
+
+void
+nsFrame::CreateView()
+{
+  MOZ_ASSERT(!HasView());
+
+  nsView* parentView = GetParent()->GetClosestView();
+  MOZ_ASSERT(parentView, "no parent with view");
+
+  nsViewManager* viewManager = parentView->GetViewManager();
+  MOZ_ASSERT(viewManager, "null view manager");
+
+  nsView* view = viewManager->CreateView(GetRect(), parentView);
+  SyncFrameViewProperties(view);
+
+  nsView* insertBefore = nsLayoutUtils::FindSiblingViewFor(parentView, this);
+  // we insert this view 'above' the insertBefore view, unless insertBefore is null,
+  // in which case we want to call with aAbove == false to insert at the beginning
+  // in document order
+  viewManager->InsertChild(parentView, view, insertBefore, insertBefore != nullptr);
+
+  // REVIEW: Don't create a widget for fixed-pos elements anymore.
+  // ComputeRepaintRegionForCopy will calculate the right area to repaint
+  // when we scroll.
+  // Reparent views on any child frames (or their descendants) to this
+  // view. We can just call ReparentFrameViewTo on this frame because
+  // we know this frame has no view, so it will crawl the children. Also,
+  // we know that any descendants with views must have 'parentView' as their
+  // parent view.
+  ReparentFrameViewTo(viewManager, view, parentView);
+
+  // Remember our view
+  SetView(view);
+
+  NS_FRAME_LOG(NS_FRAME_TRACE_CALLS,
+               ("nsFrame::CreateView: frame=%p view=%p",
+                this, view));
+}
+
 // MSVC fails with link error "one or more multiply defined symbols found",
 // gcc fails with "hidden symbol `nsIFrame::kPrincipalList' isn't defined"
 // etc if they are not defined.
 #ifndef _MSC_VER
 // static nsIFrame constants; initialized in the header file.
 const nsIFrame::ChildListID nsIFrame::kPrincipalList;
 const nsIFrame::ChildListID nsIFrame::kAbsoluteList;
 const nsIFrame::ChildListID nsIFrame::kBulletList;
@@ -5845,63 +5953,49 @@ nsIFrame* nsIFrame::GetTailContinuation(
        next && !(next->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER);
        next = frame->GetNextContinuation())  {
     frame = next;
   }
   NS_POSTCONDITION(frame, "illegal state in continuation chain.");
   return frame;
 }
 
-NS_DECLARE_FRAME_PROPERTY_WITHOUT_DTOR(ViewProperty, nsView)
-
 // Associated view object
-nsView*
-nsIFrame::GetView() const
-{
-  // Check the frame state bit and see if the frame has a view
-  if (!(GetStateBits() & NS_FRAME_HAS_VIEW))
-    return nullptr;
-
-  // Check for a property on the frame
-  nsView* value = Properties().Get(ViewProperty());
-  NS_ASSERTION(value, "frame state bit was set but frame has no view");
-  return value;
-}
-
-nsresult
+void
 nsIFrame::SetView(nsView* aView)
 {
   if (aView) {
     aView->SetFrame(this);
 
 #ifdef DEBUG
     nsIAtom* frameType = GetType();
-    NS_ASSERTION(frameType == nsGkAtoms::scrollFrame ||
-                 frameType == nsGkAtoms::subDocumentFrame ||
+    NS_ASSERTION(frameType == nsGkAtoms::subDocumentFrame ||
                  frameType == nsGkAtoms::listControlFrame ||
                  frameType == nsGkAtoms::objectFrame ||
                  frameType == nsGkAtoms::viewportFrame ||
                  frameType == nsGkAtoms::menuPopupFrame,
                  "Only specific frame types can have an nsView");
 #endif
 
-    // Set a property on the frame
-    Properties().Set(ViewProperty(), aView);
+    // Store the view on the frame.
+    SetViewInternal(aView);
 
     // Set the frame state bit that says the frame has a view
     AddStateBits(NS_FRAME_HAS_VIEW);
 
     // Let all of the ancestors know they have a descendant with a view.
     for (nsIFrame* f = GetParent();
          f && !(f->GetStateBits() & NS_FRAME_HAS_CHILD_WITH_VIEW);
          f = f->GetParent())
       f->AddStateBits(NS_FRAME_HAS_CHILD_WITH_VIEW);
-  }
-
-  return NS_OK;
+  } else {
+    MOZ_ASSERT_UNREACHABLE("Destroying a view while the frame is alive?");
+    RemoveStateBits(NS_FRAME_HAS_VIEW);
+    SetViewInternal(nullptr);
+  }
 }
 
 // Find the first geometric parent that has a view
 nsIFrame* nsIFrame::GetAncestorWithView() const
 {
   for (nsIFrame* f = GetParent(); nullptr != f; f = f->GetParent()) {
     if (f->HasView()) {
       return f;
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -590,16 +590,22 @@ protected:
       nsDisplayList* aList, uint16_t aContentType = nsISelectionDisplay::DISPLAY_FRAMES);
 
   int16_t DisplaySelection(nsPresContext* aPresContext, bool isOkToTurnOn = false);
   
   // Style post processing hook
   void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
 
 public:
+  /**
+   * Helper method to create a view for a frame.  Only used by a few sub-classes
+   * that need a view.
+   */
+  void CreateView();
+
   //given a frame five me the first/last leaf available
   //XXX Robert O'Callahan wants to move these elsewhere
   static void GetLastLeaf(nsPresContext* aPresContext, nsIFrame **aFrame);
   static void GetFirstLeaf(nsPresContext* aPresContext, nsIFrame **aFrame);
 
   // Return the line number of the aFrame, and (optionally) the containing block
   // frame.
   // If aScrollLock is true, don't break outside scrollframes when looking for a
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -608,18 +608,18 @@ public:
 
   /**
    * Called to initialize the frame. This is called immediately after creating
    * the frame.
    *
    * If the frame is a continuing frame, then aPrevInFlow indicates the previous
    * frame (the frame that was split).
    *
-   * If you want a view associated with your frame, you should create the view
-   * after Init() has returned.
+   * Each subclass that need a view should override this method and call
+   * CreateView() after calling its base class Init().
    *
    * @param   aContent the content object associated with the frame
    * @param   aParent the parent frame
    * @param   aPrevInFlow the prev-in-flow frame
    */
   virtual void Init(nsIContent*       aContent,
                     nsContainerFrame* aParent,
                     nsIFrame*         aPrevInFlow) = 0;
@@ -1602,21 +1602,16 @@ public:
    *    DISPLAY_CHILD_FORCE_STACKING_CONTEXT and DISPLAY_CHILD_INLINE
    */
   void BuildDisplayListForChild(nsDisplayListBuilder*   aBuilder,
                                 nsIFrame*               aChild,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists,
                                 uint32_t                aFlags = 0);
 
-  /**
-   * Does this frame need a view?
-   */
-  virtual bool NeedsView() { return false; }
-
   bool RefusedAsyncAnimation() const
   {
     return Properties().Get(RefusedAsyncAnimationProperty());
   }
 
   /**
    * Returns true if this frame is transformed (e.g. has CSS or SVG transforms)
    * or if its parent is an SVG frame that has children-only transforms (e.g.
@@ -2440,38 +2435,63 @@ public:
   /**
    * Returns true if the frame contains any non-collapsed characters.
    * This method is only available for text frames, and it will return false
    * for all other frame types.
    */
   virtual bool HasAnyNoncollapsedCharacters()
   { return false; }
 
-  /**
-   * Accessor functions to get/set the associated view object
-   *
-   * GetView returns non-null if and only if |HasView| returns true.
-   */
+  //
+  // Accessor functions to an associated view object:
+  //
   bool HasView() const { return !!(mState & NS_FRAME_HAS_VIEW); }
-  nsView* GetView() const;
-  nsresult SetView(nsView* aView);
+protected:
+  virtual nsView* GetViewInternal() const
+  {
+    MOZ_ASSERT_UNREACHABLE("method should have been overridden by subclass");
+    return nullptr;
+  }
+  virtual void SetViewInternal(nsView* aView)
+  {
+    MOZ_ASSERT_UNREACHABLE("method should have been overridden by subclass");
+  }
+public:
+  nsView* GetView() const
+  {
+    if (MOZ_LIKELY(!HasView())) {
+      return nullptr;
+    }
+    nsView* view = GetViewInternal();
+    MOZ_ASSERT(view, "GetViewInternal() should agree with HasView()");
+    return view;
+  }
+  void SetView(nsView* aView);
 
   /**
    * Find the closest view (on |this| or an ancestor).
    * If aOffset is non-null, it will be set to the offset of |this|
    * from the returned view.
    */
   nsView* GetClosestView(nsPoint* aOffset = nullptr) const;
 
   /**
    * Find the closest ancestor (excluding |this| !) that has a view
    */
   nsIFrame* GetAncestorWithView() const;
 
   /**
+   * Sets the view's attributes from the frame style.
+   * Call this for nsChangeHint_SyncFrameView style changes or when the view
+   * has just been created.
+   * @param aView the frame's view or use GetView() if nullptr is given
+   */
+  void SyncFrameViewProperties(nsView* aView = nullptr);
+
+  /**
    * Get the offset between the coordinate systems of |this| and aOther.
    * Adding the return value to a point in the coordinate system of |this|
    * will transform the point to the coordinate system of aOther.
    *
    * aOther must be non-null.
    *
    * This function is fastest when aOther is an ancestor of |this|.
    *
@@ -3611,16 +3631,23 @@ public:
    */
   nscoord ComputeISizeValue(nsRenderingContext* aRenderingContext,
                             nscoord             aContainingBlockISize,
                             nscoord             aContentEdgeToBoxSizing,
                             nscoord             aBoxSizingToMarginEdge,
                             const nsStyleCoord& aCoord,
                             ComputeSizeFlags    aFlags = eDefault);
 protected:
+  /**
+   * Reparent this frame's view if it has one.
+   */
+  void ReparentFrameViewTo(nsViewManager* aViewManager,
+                           nsView*        aNewParentView,
+                           nsView*        aOldParentView);
+
   // Members
   nsRect           mRect;
   nsIContent*      mContent;
   nsStyleContext*  mStyleContext;
 private:
   nsContainerFrame* mParent;
   nsIFrame*        mNextSibling;  // doubly-linked list of frames
   nsIFrame*        mPrevSibling;  // Do not touch outside SetNextSibling!
--- a/layout/generic/nsPluginFrame.cpp
+++ b/layout/generic/nsPluginFrame.cpp
@@ -145,16 +145,17 @@ protected:
 
   uint64_t mLastSequenceNumber;
   nsPluginFrame* mFrame;
 };
 
 nsPluginFrame::nsPluginFrame(nsStyleContext* aContext)
   : nsFrame(aContext)
   , mInstanceOwner(nullptr)
+  , mOuterView(nullptr)
   , mInnerView(nullptr)
   , mBackgroundSink(nullptr)
   , mReflowCallbackPosted(false)
 {
   MOZ_LOG(sPluginFrameLog, LogLevel::Debug,
          ("Created new nsPluginFrame %p\n", this));
 }
 
@@ -189,16 +190,17 @@ void
 nsPluginFrame::Init(nsIContent*       aContent,
                     nsContainerFrame* aParent,
                     nsIFrame*         aPrevInFlow)
 {
   MOZ_LOG(sPluginFrameLog, LogLevel::Debug,
          ("Initializing nsPluginFrame %p for content %p\n", this, aContent));
 
   nsFrame::Init(aContent, aParent, aPrevInFlow);
+  CreateView();
 }
 
 void
 nsPluginFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   if (mReflowCallbackPosted) {
     PresContext()->PresShell()->CancelReflowCallback(this);
   }
--- a/layout/generic/nsPluginFrame.h
+++ b/layout/generic/nsPluginFrame.h
@@ -91,18 +91,16 @@ public:
   virtual nsIAtom* GetType() const override;
 
   virtual bool IsFrameOfType(uint32_t aFlags) const override
   {
     return nsFrame::IsFrameOfType(aFlags &
       ~(nsIFrame::eReplaced | nsIFrame::eReplacedSizing));
   }
 
-  virtual bool NeedsView() override { return true; }
-
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
 
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
 
@@ -266,16 +264,19 @@ protected:
                    const nsRect& aDirtyRect, const nsRect& aPluginRect);
 
   void NotifyPluginReflowObservers();
 
   friend class nsPluginInstanceOwner;
   friend class nsDisplayPlugin;
   friend class PluginBackgroundSink;
 
+  nsView* GetViewInternal() const override { return mOuterView; }
+  void SetViewInternal(nsView* aView) override { mOuterView = aView; }
+
 private:
   // Registers the plugin for a geometry update, and requests a geometry
   // update. This caches the root pres context in
   // mRootPresContextRegisteredWith, so that we can be sure we unregister
   // from the right root prest context in UnregisterPluginForGeometryUpdates.
   void RegisterPluginForGeometryUpdates();
 
   // Unregisters the plugin for geometry updated with the root pres context
@@ -298,17 +299,18 @@ private:
       mEventType(aEventType) {}
     
     NS_IMETHOD Run() override;
   private:
     nsString mEventType;
   };
 
   nsPluginInstanceOwner*          mInstanceOwner; // WEAK
-  nsView*                        mInnerView;
+  nsView*                         mOuterView;
+  nsView*                         mInnerView;
   nsCOMPtr<nsIWidget>             mWidget;
   nsIntRect                       mWindowlessRect;
   /**
    * This is owned by the ReadbackLayer for this nsPluginFrame. It is
    * automatically cleared if the PluginBackgroundSink is destroyed.
    */
   PluginBackgroundSink*           mBackgroundSink;
 
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -1969,20 +1969,19 @@ nsFrameSelection::ScrollSelectionIntoVie
       nsIPresShell::SCROLL_CENTER, nsIPresShell::SCROLL_IF_NOT_FULLY_VISIBLE);
   }
   if (aFlags & nsISelectionController::SCROLL_FOR_CARET_MOVE) {
     flags |= Selection::SCROLL_FOR_CARET_MOVE;
   }
 
   // After ScrollSelectionIntoView(), the pending notifications might be
   // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
-  return mDomSelections[index]->ScrollIntoView(aRegion,
-                                               verticalScroll,
-                                               nsIPresShell::ScrollAxis(),
-                                               flags);
+  RefPtr<Selection> sel = mDomSelections[index];
+  return sel->ScrollIntoView(aRegion, verticalScroll,
+                             nsIPresShell::ScrollAxis(), flags);
 }
 
 nsresult
 nsFrameSelection::RepaintSelection(SelectionType aSelectionType)
 {
   int8_t index = GetIndexFromSelectionType(aSelectionType);
   if (index < 0)
     return NS_ERROR_INVALID_ARG;
@@ -6166,16 +6165,18 @@ NS_IMETHODIMP
 Selection::ScrollSelectionIntoViewEvent::Run()
 {
   if (!mSelection)
     return NS_OK;  // event revoked
 
   int32_t flags = Selection::SCROLL_DO_FLUSH |
                   Selection::SCROLL_SYNCHRONOUS;
 
+  Selection* sel = mSelection; // workaround to satisfy static analysis
+  RefPtr<Selection> kungFuDeathGrip(sel);
   mSelection->mScrollEvent.Forget();
   mSelection->ScrollIntoView(mRegion, mVerticalScroll,
                              mHorizontalScroll, mFlags | flags);
   return NS_OK;
 }
 
 nsresult
 Selection::PostScrollSelectionIntoViewEvent(
--- a/layout/generic/nsSubDocumentFrame.cpp
+++ b/layout/generic/nsSubDocumentFrame.cpp
@@ -55,16 +55,17 @@ GetDocumentFromView(nsView* aView)
 
   nsViewManager* vm = aView->GetViewManager();
   nsIPresShell* ps =  vm ? vm->GetPresShell() : nullptr;
   return ps ? ps->GetDocument() : nullptr;
 }
 
 nsSubDocumentFrame::nsSubDocumentFrame(nsStyleContext* aContext)
   : nsAtomicContainerFrame(aContext)
+  , mOuterView(nullptr)
   , mInnerView(nullptr)
   , mIsInline(false)
   , mPostedReflowCallback(false)
   , mDidCreateDoc(false)
   , mCallingShow(false)
 {
 }
 
@@ -116,27 +117,20 @@ nsSubDocumentFrame::Init(nsIContent*    
     // If layout.show_previous_page is true then during loading of a new page we
     // will draw the previous page if the new page has painting suppressed.
     Preferences::AddBoolVarCache(&sShowPreviousPage, "layout.show_previous_page", true);
     addedShowPreviousPage = true;
   }
 
   nsAtomicContainerFrame::Init(aContent, aParent, aPrevInFlow);
 
-  // We are going to create an inner view.  If we need a view for the
-  // OuterFrame but we wait for the normal view creation path in
-  // nsCSSFrameConstructor, then we will lose because the inner view's
-  // parent will already have been set to some outer view (e.g., the
-  // canvas) when it really needs to have this frame's view as its
-  // parent. So, create this frame's view right away, whether we
-  // really need it or not, and the inner view will get it as the
-  // parent.
-  if (!HasView()) {
-    nsContainerFrame::CreateViewForFrame(this, true);
-  }
+  // CreateView() creates this frame's view, stored in mOuterView.  It needs to
+  // be created first since it's the parent of the inner view, stored in
+  // mInnerView.
+  CreateView();
   EnsureInnerView();
 
   // Set the primary frame now so that nsDocumentViewer::FindContainerView
   // called from within EndSwapDocShellsForViews below can find it if needed.
   aContent->SetPrimaryFrame(this);
 
   // If we have a detached subdoc's root view on our frame loader, re-insert
   // it into the view tree. This happens when we've been reframed, and
--- a/layout/generic/nsSubDocumentFrame.h
+++ b/layout/generic/nsSubDocumentFrame.h
@@ -152,17 +152,21 @@ protected:
    * and our sub-document has an intrinsic size. The frame returned is the
    * frame for the document element of the document we're embedding.
    *
    * Called "Obtain*" and not "Get*" because of comment on GetDocShell that
    * says it should be called ObtainDocShell because of its side effects.
    */
   nsIFrame* ObtainIntrinsicSizeFrame();
 
+  nsView* GetViewInternal() const override { return mOuterView; }
+  void SetViewInternal(nsView* aView) override { mOuterView = aView; }
+
   RefPtr<nsFrameLoader> mFrameLoader;
+  nsView* mOuterView;
   nsView* mInnerView;
   bool mIsInline;
   bool mPostedReflowCallback;
   bool mDidCreateDoc;
   bool mCallingShow;
 };
 
 #endif /* NSSUBDOCUMENTFRAME_H_ */
--- a/layout/style/nsCSSValue.cpp
+++ b/layout/style/nsCSSValue.cpp
@@ -1675,23 +1675,27 @@ nsCSSValue::AppendToString(nsCSSProperty
     }
   }
   else if (IsNumericColorUnit(unit)) {
     if (aSerialization == eNormalized ||
         unit == eCSSUnit_RGBColor ||
         unit == eCSSUnit_RGBAColor) {
       nscolor color = GetColorValue();
       // For brevity, we omit the alpha component if it's equal to 255 (full
-      // opaque). Also, we try to preserve the author-specified function name,
-      // unless it's rgba() and we're omitting the alpha component - then we
-      // use rgb().
+      // opaque). Also, we use "rgba" rather than "rgb" when the color includes
+      // the non-opaque alpha value, for backwards-compat (even though they're
+      // aliases as of css-color-4).
+      // e.g.:
+      //   rgba(1, 2, 3, 1.0) => rgb(1, 2, 3)
+      //   rgba(1, 2, 3, 0.5) => rgba(1, 2, 3, 0.5)
+
       uint8_t a = NS_GET_A(color);
       bool showAlpha = (a != 255);
 
-      if (unit == eCSSUnit_RGBAColor && showAlpha) {
+      if (showAlpha) {
         aResult.AppendLiteral("rgba(");
       } else {
         aResult.AppendLiteral("rgb(");
       }
 
       NS_NAMED_LITERAL_STRING(comma, ", ");
 
       aResult.AppendInt(NS_GET_R(color), 10);
--- a/layout/style/test/stylo-failures.md
+++ b/layout/style/test/stylo-failures.md
@@ -99,17 +99,18 @@ to mochitest command.
   * test_transitions_step_functions.html [24]
   * test_value_storage.html `transition` [776]
   * Events:
     * test_animations_event_handler_attribute.html [10]
     * test_animations_event_order.html [11]
 * test_bug798843_pref.html: conditional opentype svg support [7]
 * test_computed_style.html `gradient`: -moz-prefixed radient value [9]
 * ... `mask`: mask-image isn't set properly bug 1347398 [2]
-* ... `rgba`: svg paint should distinguish whether there is fallback bug 1347409 [4]
+* ... `fill`: svg paint should distinguish whether there is fallback bug 1347409 [2]
+* ... `stroke`: svg paint should distinguish whether there is fallback bug 1347409 [2]
 * ... `#foo`: local ref url should be preserved bug 1347412 [5]
 * character not properly escaped servo/servo#15947
   * test_parse_url.html [4]
   * test_bug829816.html [8]
 * test_value_storage.html `for 'marker`: equality of url is not reliable servo/servo#15950 [1]
 * auto value for min-{width,height} servo/servo#15045
 * test_compute_data_with_start_struct.html `timing-function`: incorrectly computing keywords to bezier function servo/servo#15086 [2]
 * test_condition_text.html: @-moz-document, CSSOM support of @media, @support [7]
@@ -315,16 +316,18 @@ to mochitest command.
     * test_compute_data_with_start_struct.html `pointer-events` [2]
     * test_inherit_computation.html `pointer-events` [4]
     * test_initial_computation.html `pointer-events` [2]
     * test_pointer-events.html [2]
     * test_value_storage.html `pointer-events` [8]
   * new syntax of rgba?() and hsla?() functions servo/rust-cssparser#113
     * test_value_storage.html `'color'` [35]
     * ... `rgb(100, 100.0, 100)` [1]
+    * test_computed_style.html `css-color-4` [8]
+    * test_specified_value_serialization.html `css-color-4` [8]
   * color interpolation hint not supported servo/servo#15166
     * test_value_storage.html `'linear-gradient` [50]
   * two-keyword form of background-repeat/mask-repeat servo/servo#14954
     * test_value_storage.html `background-repeat` [14]
     * ... `mask-repeat` [24]
   * SVG-in-OpenType values not supported servo/servo#15211
     * test_value_storage.html `context-` [2]
   * writing-mode: sideways-{lr,rl} and SVG values servo/servo#15213
@@ -358,17 +361,17 @@ to mochitest command.
     * test_priority_preservation.html `border-radius` [4]
     * test_value_storage.html `border-radius:` [156]
     * ... `-moz-outline-radius:` [76]
     * test_shorthand_property_getters.html `should condense to shortest possible` [6]
   * transform property servo/servo#15194
     * test_value_storage.html `'transform` [104]
     * ... `"transform` [66]
     * ... `-webkit-transform` [109]
-    * test_specified_value_serialization.html [27]
+    * test_specified_value_serialization.html `bug-721136` [27]
     * test_units_angle.html [3]
   * {background,mask}-position lacks comma for serialization servo/servo#15200
     * test_value_storage.html `background-position` [81]
     * ... `for 'mask-position` [94]
     * ... `for '-webkit-mask-position` [188]
     * ... `for '-webkit-mask` [45]
     * test_shorthand_property_getters.html `background-position` [1]
   * box-shadow wrong order of &lt;length&gt; values servo/servo#15203
--- a/layout/style/test/test_computed_style.html
+++ b/layout/style/test/test_computed_style.html
@@ -370,12 +370,51 @@ var noframe_container = document.getElem
     is(cs[prop], localURL + testStyles[prop], "computed value of " + prop);
     p.style[prop] = nonLocalURL;
     is(cs[prop], resolvedNonLocalURL + testStyles[prop], "computed value of " + prop);
   }
 
   p.remove();
 })();
 
+(function test_bug_1347164() {
+  // Test that computed color values are serialized as "rgb()"
+  // IFF they're fully-opaque (and otherwise as "rgba()").
+  var color = [
+    ["rgba(0, 0, 0, 1)", "rgb(0, 0, 0)"],
+    ["rgba(0, 0, 0, 0.5)", "rgba(0, 0, 0, 0.5)"],
+    ["hsla(0, 0%, 0%, 1)", "rgb(0, 0, 0)"],
+    ["hsla(0, 0%, 0%, 0.5)", "rgba(0, 0, 0, 0.5)"],
+  ];
+
+  var css_color_4 = [
+    ["rgba(0 0 0 / 1)", "rgb(0, 0, 0)"],
+    ["rgba(0 0 0 / 0.1)", "rgba(0, 0, 0, 0.1)"],
+    ["rgb(0 0 0 / 1)", "rgb(0, 0, 0)"],
+    ["rgb(0 0 0 / 0.2)", "rgba(0, 0, 0, 0.2)"],
+    ["hsla(0 0% 0% / 1)", "rgb(0, 0, 0)"],
+    ["hsla(0deg 0% 0% / 0.3)", "rgba(0, 0, 0, 0.3)"],
+    ["hsl(0 0% 0% / 1)", "rgb(0, 0, 0)"],
+    ["hsl(0 0% 0% / 0.4)", "rgba(0, 0, 0, 0.4)"],
+  ];
+
+  var p = document.createElement("p");
+  var cs = getComputedStyle(p, "");
+  frame_container.appendChild(p);
+
+  for (var i = 0; i < color.length; ++i) {
+    var test = color[i];
+    p.style.color = test[0];
+    is(cs.color, test[1], "computed value of " + test[0]);
+  }
+  for (var i = 0; i < css_color_4.length; ++i) {
+    var test = css_color_4[i];
+    p.style.color = test[0];
+    is(cs.color, test[1], "css-color-4 computed value of " + test[0]);
+  }
+
+  p.remove();
+})();
+
 </script>
 </pre>
 </body>
 </html>
--- a/layout/style/test/test_specified_value_serialization.html
+++ b/layout/style/test/test_specified_value_serialization.html
@@ -1,62 +1,112 @@
 <!doctype html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=721136
--->
-<title>Test for Bug 721136</title>
-<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=721136">Mozilla Bug 721136</a>
+<html>
+<head>
+  <title>Test for miscellaneous specified value issues</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">Mozilla Bug </a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
 <pre id="test">
-<script>
-[
-  [" mAtRiX(1, 2,3,4, 5,6 ) ", "matrix(1, 2, 3, 4, 5, 6)"],
-  [" mAtRiX3d( 1,2,3,0,4 ,5,6,0,7,8 , 9,0,10, 11,12,1 )  ",
-   "matrix3d(1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0, 10, 11, 12, 1)"],
-  [" pErSpEcTiVe( 400Px ) ", "perspective(400px)"],
-  [" rOtAtE( 90dEg ) ", "rotate(90deg)"],
-  [" rOtAtE3d( 0,0 , 1 ,180DeG ) ", "rotate3d(0, 0, 1, 180deg)"],
-  [" rOtAtEx( 100GrAD ) ", "rotateX(100grad)"],
-  [" rOtAtEy( 1.57RaD ) ", "rotateY(1.57rad)"],
-  [" rOtAtEz( 0.25TuRn ) ", "rotateZ(0.25turn)"],
-  [" sCaLe( 2 ) ", "scale(2)"],
-  [" sCaLe( 2,3 ) ", "scale(2, 3)"],
-  [" sCaLe3D( 2,4 ,  -9 )  ", "scale3d(2, 4, -9)"],
-  [" sCaLeX( 2 ) ", "scaleX(2)"],
-  [" sCaLeY( 2 ) ", "scaleY(2)"],
-  [" sCaLeZ( 2 ) ", "scaleZ(2)"],
-  [" sKeW( 45dEg ) ", "skew(45deg)"],
-  [" sKeW( 45dEg,45DeG ) ", "skew(45deg, 45deg)"],
-  [" sKeWx( 45DeG ) ", "skewX(45deg)"],
-  [" sKeWy( 45DeG ) ", "skewY(45deg)"],
-  [" tRaNsLaTe( 1Px ) ", "translate(1px)"],
-  [" tRaNsLaTe( 1Px,3Pt ) ", "translate(1px, 3pt)"],
-  [" tRaNsLaTe3D( 21pX,-6pX , 4pX )  ", "translate3d(21px, -6px, 4px)"],
-  [" tRaNsLaTeX( 1pT ) ", "translateX(1pt)"],
-  [" tRaNsLaTeY( 1iN ) ", "translateY(1in)"],
-  [" tRaNsLaTeZ( 15.4pX ) ", "translateZ(15.4px)"],
-  ["tranSlatex( 16px )rotatez(-90deg)  rotate(100grad)\ttranslate3d(12pt, 0pc, 0.0em)",
-   "translateX(16px) rotateZ(-90deg) rotate(100grad) translate3d(12pt, 0pc, 0em)"],
-].forEach(function(arr) {
-  document.documentElement.style.MozTransform = arr[0];
-  is(document.documentElement.style.MozTransform, arr[1],
-    "incorrect serialization");
-});
+<script type="application/javascript">
+
+(function test_bug_721136() {
+  // Test for transform property serialization.
+  [
+    [" mAtRiX(1, 2,3,4, 5,6 ) ", "matrix(1, 2, 3, 4, 5, 6)"],
+    [" mAtRiX3d( 1,2,3,0,4 ,5,6,0,7,8 , 9,0,10, 11,12,1 )  ",
+     "matrix3d(1, 2, 3, 0, 4, 5, 6, 0, 7, 8, 9, 0, 10, 11, 12, 1)"],
+    [" pErSpEcTiVe( 400Px ) ", "perspective(400px)"],
+    [" rOtAtE( 90dEg ) ", "rotate(90deg)"],
+    [" rOtAtE3d( 0,0 , 1 ,180DeG ) ", "rotate3d(0, 0, 1, 180deg)"],
+    [" rOtAtEx( 100GrAD ) ", "rotateX(100grad)"],
+    [" rOtAtEy( 1.57RaD ) ", "rotateY(1.57rad)"],
+    [" rOtAtEz( 0.25TuRn ) ", "rotateZ(0.25turn)"],
+    [" sCaLe( 2 ) ", "scale(2)"],
+    [" sCaLe( 2,3 ) ", "scale(2, 3)"],
+    [" sCaLe3D( 2,4 ,  -9 )  ", "scale3d(2, 4, -9)"],
+    [" sCaLeX( 2 ) ", "scaleX(2)"],
+    [" sCaLeY( 2 ) ", "scaleY(2)"],
+    [" sCaLeZ( 2 ) ", "scaleZ(2)"],
+    [" sKeW( 45dEg ) ", "skew(45deg)"],
+    [" sKeW( 45dEg,45DeG ) ", "skew(45deg, 45deg)"],
+    [" sKeWx( 45DeG ) ", "skewX(45deg)"],
+    [" sKeWy( 45DeG ) ", "skewY(45deg)"],
+    [" tRaNsLaTe( 1Px ) ", "translate(1px)"],
+    [" tRaNsLaTe( 1Px,3Pt ) ", "translate(1px, 3pt)"],
+    [" tRaNsLaTe3D( 21pX,-6pX , 4pX )  ", "translate3d(21px, -6px, 4px)"],
+    [" tRaNsLaTeX( 1pT ) ", "translateX(1pt)"],
+    [" tRaNsLaTeY( 1iN ) ", "translateY(1in)"],
+    [" tRaNsLaTeZ( 15.4pX ) ", "translateZ(15.4px)"],
+    ["tranSlatex( 16px )rotatez(-90deg)  rotate(100grad)\ttranslate3d(12pt, 0pc, 0.0em)",
+     "translateX(16px) rotateZ(-90deg) rotate(100grad) translate3d(12pt, 0pc, 0em)"],
+  ].forEach(function(arr) {
+    document.documentElement.style.MozTransform = arr[0];
+    is(document.documentElement.style.MozTransform, arr[1],
+      "bug-721136 incorrect serialization");
+  });
+
+  var elt = document.documentElement;
+
+  elt.setAttribute("style",
+                   "transform: tRANslatEX(5px) TRanslATey(10px) translatez(2px) ROTATEX(30deg) rotateY(30deg) rotatez(5deg) SKEWx(10deg) skewy(10deg) scaleX(2) SCALEY(0.5) scalez(2)");
+  is(elt.style.getPropertyValue("transform"),
+     "translateX(5px) translateY(10px) translateZ(2px) rotateX(30deg) rotateY(30deg) rotateZ(5deg) skewX(10deg) skewY(10deg) scaleX(2) scaleY(0.5) scaleZ(2)",
+     "bug-721136 expected case canonicalization of transform functions");
 
-var elt = document.documentElement;
+  elt.setAttribute("style",
+                   "font-variant-alternates: SWASH(fOo) stYLIStiC(Bar)");
+  is(elt.style.getPropertyValue("font-variant-alternates"),
+     "swash(fOo) stylistic(Bar)",
+     "bug-721136 expected case canonicalization of transform functions");
+
+  elt.setAttribute("style", ""); // leave the page in a useful state
+})();
 
-elt.setAttribute("style",
-                 "transform: tRANslatEX(5px) TRanslATey(10px) translatez(2px) ROTATEX(30deg) rotateY(30deg) rotatez(5deg) SKEWx(10deg) skewy(10deg) scaleX(2) SCALEY(0.5) scalez(2)");
-is(elt.style.getPropertyValue("transform"),
-   "translateX(5px) translateY(10px) translateZ(2px) rotateX(30deg) rotateY(30deg) rotateZ(5deg) skewX(10deg) skewY(10deg) scaleX(2) scaleY(0.5) scaleZ(2)",
-   "expected case canonicalization of transform functions");
+(function test_bug_1347164() {
+  // Test that specified color values are serialized as "rgb()"
+  // IFF they're fully-opaque (and otherwise as "rgba()").
+  var color = [
+    ["rgba(0, 0, 0, 1)", "rgb(0, 0, 0)"],
+    ["rgba(0, 0, 0, 0.5)", "rgba(0, 0, 0, 0.5)"],
+    ["hsla(0, 0%, 0%, 1)", "rgb(0, 0, 0)"],
+    ["hsla(0, 0%, 0%, 0.5)", "rgba(0, 0, 0, 0.5)"],
+  ];
 
-elt.setAttribute("style",
-                 "font-variant-alternates: SWASH(fOo) stYLIStiC(Bar)");
-is(elt.style.getPropertyValue("font-variant-alternates"),
-   "swash(fOo) stylistic(Bar)",
-   "expected case canonicalization of transform functions");
+  var css_color_4 = [
+    ["rgba(0 0 0 / 1)", "rgb(0, 0, 0)"],
+    ["rgba(0 0 0 / 0.1)", "rgba(0, 0, 0, 0.1)"],
+    ["rgb(0 0 0 / 1)", "rgb(0, 0, 0)"],
+    ["rgb(0 0 0 / 0.2)", "rgba(0, 0, 0, 0.2)"],
+    ["hsla(0 0% 0% / 1)", "rgb(0, 0, 0)"],
+    ["hsla(0deg 0% 0% / 0.3)", "rgba(0, 0, 0, 0.3)"],
+    ["hsl(0 0% 0% / 1)", "rgb(0, 0, 0)"],
+    ["hsl(0 0% 0% / 0.4)", "rgba(0, 0, 0, 0.4)"],
+  ];
 
-elt.setAttribute("style", ""); // leave the page in a useful state
+  var frame_container = document.getElementById("display");
+  var p = document.createElement("p");
+  frame_container.appendChild(p);
+
+  for (var i = 0; i < color.length; ++i) {
+    var test = color[i];
+    p.style.color = test[0];
+    is(p.style.color, test[1], "serialization value of " + test[0]);
+  }
+  for (var i = 0; i < css_color_4.length; ++i) {
+    var test = css_color_4[i];
+    p.style.color = test[0];
+    is(p.style.color, test[1], "css-color-4 serialization value of " + test[0]);
+  }
+
+  p.remove();
+})();
 
 </script>
 </pre>
+</body>
+</html>
--- a/layout/xul/nsMenuPopupFrame.cpp
+++ b/layout/xul/nsMenuPopupFrame.cpp
@@ -89,16 +89,17 @@ NS_QUERYFRAME_HEAD(nsMenuPopupFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
 
 //
 // nsMenuPopupFrame ctor
 //
 nsMenuPopupFrame::nsMenuPopupFrame(nsStyleContext* aContext)
   :nsBoxFrame(aContext),
   mCurrentMenu(nullptr),
+  mView(nullptr),
   mPrefSize(-1, -1),
   mLastClientOffset(0, 0),
   mPopupType(ePopupTypePanel),
   mPopupState(ePopupClosed),
   mPopupAlignment(POPUPALIGNMENT_NONE),
   mPopupAnchor(POPUPALIGNMENT_NONE),
   mPosition(POPUPPOSITION_UNKNOWN),
   mConsumeRollupEvent(PopupBoxObject::ROLLUP_DEFAULT),
@@ -524,17 +525,17 @@ nsMenuPopupFrame::LayoutPopup(nsBoxLayou
     rect.x = rect.y = 0;
     viewManager->ResizeView(view, rect);
 
     if (mPopupState == ePopupOpening) {
       mPopupState = ePopupVisible;
     }
 
     viewManager->SetViewVisibility(view, nsViewVisibility_kShow);
-    nsContainerFrame::SyncFrameViewProperties(pc, this, nullptr, view, 0);
+    SyncFrameViewProperties(view);
   }
 
   // finally, if the popup just opened, send a popupshown event
   if (mIsOpenChanged) {
     mIsOpenChanged = false;
 
     // Make sure the current selection in a menulist is visible.
     if (IsMenuList() && mCurrentMenu) {
@@ -2431,17 +2432,17 @@ nsMenuPopupFrame::GetAlignmentPosition()
   if (mVFlip) {
     position = POPUPPOSITION_VFLIP(position);
   }
 
   return position;
 }
 
 /**
- * KEEP THIS IN SYNC WITH nsContainerFrame::CreateViewForFrame
+ * KEEP THIS IN SYNC WITH nsFrame::CreateView
  * as much as possible. Until we get rid of views finally...
  */
 void
 nsMenuPopupFrame::CreatePopupView()
 {
   if (HasView()) {
     return;
   }
--- a/layout/xul/nsMenuPopupFrame.h
+++ b/layout/xul/nsMenuPopupFrame.h
@@ -528,16 +528,19 @@ protected:
       ? mAnchorContent->GetPrimaryFrame()->StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL
       : StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
   }
 
   // Create a popup view for this frame. The view is added a child of the root
   // view, and is initially hidden.
   void CreatePopupView();
 
+  nsView* GetViewInternal() const override { return mView; }
+  void SetViewInternal(nsView* aView) override { mView = aView; }
+
   // Returns true if the popup should try to remain at the same relative
   // location as the anchor while it is open. If the anchor becomes hidden
   // either directly or indirectly because a parent popup or other element
   // is no longer visible, or a parent deck page is changed, the popup hides
   // as well. The second variation also sets the anchor rectangle, relative to
   // the popup frame.
   bool ShouldFollowAnchor();
 public:
@@ -550,16 +553,17 @@ protected:
   // different document than the popup.
   nsCOMPtr<nsIContent> mAnchorContent;
 
   // the content that triggered the popup, typically the node where the mouse
   // was clicked. It will be cleared when the popup is hidden.
   nsCOMPtr<nsIContent> mTriggerContent;
 
   nsMenuFrame* mCurrentMenu; // The current menu that is active.
+  nsView* mView;
 
   RefPtr<nsXULPopupShownEvent> mPopupShownDispatcher;
 
   // The popup's screen rectangle in app units.
   nsIntRect mUsedScreenRect;
 
   // A popup's preferred size may be different than its actual size stored in
   // mRect in the case where the popup was resized because it was too large
--- a/netwerk/base/nsBufferedStreams.cpp
+++ b/netwerk/base/nsBufferedStreams.cpp
@@ -549,17 +549,18 @@ nsBufferedInputStream::Serialize(InputSt
 {
     BufferedInputStreamParams params;
 
     if (mStream) {
         nsCOMPtr<nsIInputStream> stream = do_QueryInterface(mStream);
         MOZ_ASSERT(stream);
 
         InputStreamParams wrappedParams;
-        SerializeInputStream(stream, wrappedParams, aFileDescriptors);
+        InputStreamHelper::SerializeInputStream(stream, wrappedParams,
+                                                aFileDescriptors);
 
         params.optionalStream() = wrappedParams;
     }
     else {
         params.optionalStream() = mozilla::void_t();
     }
 
     params.bufferSize() = mBufferSize;
@@ -577,18 +578,19 @@ nsBufferedInputStream::Deserialize(const
     }
 
     const BufferedInputStreamParams& params =
         aParams.get_BufferedInputStreamParams();
     const OptionalInputStreamParams& wrappedParams = params.optionalStream();
 
     nsCOMPtr<nsIInputStream> stream;
     if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
-        stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(),
-                                        aFileDescriptors);
+        stream =
+          InputStreamHelper::DeserializeInputStream(wrappedParams.get_InputStreamParams(),
+                                                    aFileDescriptors);
         if (!stream) {
             NS_WARNING("Failed to deserialize wrapped stream!");
             return false;
         }
     }
     else {
         NS_ASSERTION(wrappedParams.type() == OptionalInputStreamParams::Tvoid_t,
                      "Unknown type for OptionalInputStreamParams!");
--- a/netwerk/base/nsMIMEInputStream.cpp
+++ b/netwerk/base/nsMIMEInputStream.cpp
@@ -264,17 +264,18 @@ nsMIMEInputStreamConstructor(nsISupports
 void
 nsMIMEInputStream::Serialize(InputStreamParams& aParams,
                              FileDescriptorArray& aFileDescriptors)
 {
     MIMEInputStreamParams params;
 
     if (mStream) {
         InputStreamParams wrappedParams;
-        SerializeInputStream(mStream, wrappedParams, aFileDescriptors);
+        InputStreamHelper::SerializeInputStream(mStream, wrappedParams,
+                                                aFileDescriptors);
 
         NS_ASSERTION(wrappedParams.type() != InputStreamParams::T__None,
                      "Wrapped stream failed to serialize!");
 
         params.optionalStream() = wrappedParams;
     }
     else {
         params.optionalStream() = mozilla::void_t();
@@ -299,18 +300,19 @@ nsMIMEInputStream::Deserialize(const Inp
         aParams.get_MIMEInputStreamParams();
     const OptionalInputStreamParams& wrappedParams = params.optionalStream();
 
     mHeaders = params.headers();
     mStartedReading = params.startedReading();
 
     if (wrappedParams.type() == OptionalInputStreamParams::TInputStreamParams) {
         nsCOMPtr<nsIInputStream> stream;
-        stream = DeserializeInputStream(wrappedParams.get_InputStreamParams(),
-                                        aFileDescriptors);
+        stream =
+            InputStreamHelper::DeserializeInputStream(wrappedParams.get_InputStreamParams(),
+                                                      aFileDescriptors);
         if (!stream) {
             NS_WARNING("Failed to deserialize wrapped stream!");
             return false;
         }
 
         mStream = stream;
     }
     else {
--- a/services/cloudsync/CloudSyncLocal.jsm
+++ b/services/cloudsync/CloudSyncLocal.jsm
@@ -13,17 +13,17 @@ const Ci = Components.interfaces;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://services-common/utils.js");
 Cu.import("resource://services-crypto/utils.js");
 Cu.import("resource://gre/modules/Preferences.jsm");
 
 function lazyStrings(name) {
   return () => Services.strings.createBundle(
-    `chrome://weave/locale/services//${name}.properties`);
+    `chrome://weave/locale/${name}.properties`);
 }
 
 this.Str = {};
 XPCOMUtils.defineLazyGetter(Str, "errors", lazyStrings("errors"));
 XPCOMUtils.defineLazyGetter(Str, "sync", lazyStrings("sync"));
 
 function makeGUID() {
   return CommonUtils.encodeBase64URL(CryptoUtils.generateRandomBytes(9));
--- a/services/sync/locales/jar.mn
+++ b/services/sync/locales/jar.mn
@@ -1,10 +1,10 @@
 #filter substitution
 # 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/.
 
 
 @AB_CD@.jar:
-% locale weave @AB_CD@ %locale/@AB_CD@/
+% locale weave @AB_CD@ %locale/@AB_CD@/services/
   locale/@AB_CD@/services/errors.properties  (%errors.properties)
   locale/@AB_CD@/services/sync.properties    (%sync.properties)
--- a/services/sync/modules/util.js
+++ b/services/sync/modules/util.js
@@ -213,17 +213,17 @@ this.Utils = {
       prot.__defineSetter__(prop, function(val) {
         this[defer][prop] = val;
       });
     }
   },
 
   lazyStrings: function Weave_lazyStrings(name) {
     return () => Services.strings.createBundle(
-      `chrome://weave/locale/services/${name}.properties`);
+      `chrome://weave/locale/${name}.properties`);
   },
 
   deepEquals: function eq(a, b) {
     // If they're triple equals, then it must be equals!
     if (a === b)
       return true;
 
     // If they weren't equal, they must be objects to be different
--- a/toolkit/components/telemetry/TelemetryHistogram.cpp
+++ b/toolkit/components/telemetry/TelemetryHistogram.cpp
@@ -2274,22 +2274,16 @@ TelemetryHistogram::CreateHistogramSnaps
   // OK, now we can actually reflect things.
   JS::Rooted<JSObject*> hobj(cx);
   for (auto h : hs) {
     if (!internal_ShouldReflectHistogram(h) || internal_IsEmpty(h) ||
         internal_IsExpired(h)) {
       continue;
     }
 
-    mozilla::Telemetry::HistogramID id;
-    nsresult rv = internal_GetHistogramEnumId(h->histogram_name().c_str(), &id);
-    if (NS_WARN_IF(NS_FAILED(rv)) || gHistograms[id].keyed) {
-      continue;
-    }
-
     Histogram* original = h;
 #if !defined(MOZ_WIDGET_ANDROID)
     if (subsession) {
       h = internal_GetSubsessionHistogram(*h);
       if (!h) {
         continue;
       }
     }
--- a/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
+++ b/toolkit/components/telemetry/tests/unit/test_TelemetryHistograms.js
@@ -752,24 +752,16 @@ add_task(function* test_keyed_histogram_
 
   // Restore to disabled
   Telemetry.setHistogramRecordingEnabled("TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD", false);
   h.add(TEST_KEY, 1);
   Assert.equal(h.snapshot(TEST_KEY).sum, 1,
     "Keyed histogram add should not record when recording is disabled");
 });
 
-add_task(function* test_histogramSnapshots() {
-  let keyed = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT");
-  keyed.add("a", 1);
-
-  // Check that keyed histograms are not returned
-  Assert.ok(!("TELEMETRY_TEST_KEYED_COUNT#a" in Telemetry.histogramSnapshots));
-});
-
 add_task(function* test_datasets() {
   // Check that datasets work as expected.
 
   const RELEASE_CHANNEL_OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
   const RELEASE_CHANNEL_OPTIN  = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN;
 
   // Check that registeredHistogram works properly
   let registered = Telemetry.registeredHistograms(RELEASE_CHANNEL_OPTIN, []);
--- a/tools/profiler/core/platform-linux-android.cpp
+++ b/tools/profiler/core/platform-linux-android.cpp
@@ -74,80 +74,16 @@
 using namespace mozilla;
 
 /* static */ Thread::tid_t
 Thread::GetCurrentId()
 {
   return gettid();
 }
 
-#if !defined(GP_OS_android)
-
-// Keep track of when any of our threads calls fork(), so we can
-// temporarily disable signal delivery during the fork() call.  Not
-// doing so appears to cause a kind of race, in which signals keep
-// getting delivered to the thread doing fork(), which keeps causing
-// it to fail and be restarted; hence forward progress is delayed a
-// great deal.  A side effect of this is to permanently disable
-// sampling in the child process.  See bug 837390.
-
-// Unfortunately this is only doable on non-Android, since Bionic
-// doesn't have pthread_atfork.
-
-// In the parent, before the fork, record the pausedness state, and then pause.
-static void
-paf_prepare()
-{
-  // This function can run off the main thread.
-
-  MOZ_RELEASE_ASSERT(gPS);
-
-  PS::AutoLock lock(gPSMutex);
-
-  gPS->SetWasPaused(lock, gPS->IsPaused(lock));
-  gPS->SetIsPaused(lock, true);
-}
-
-// In the parent, after the fork, return pausedness to the pre-fork state.
-static void
-paf_parent()
-{
-  // This function can run off the main thread.
-
-  MOZ_RELEASE_ASSERT(gPS);
-
-  PS::AutoLock lock(gPSMutex);
-
-  gPS->SetIsPaused(lock, gPS->WasPaused(lock));
-  gPS->SetWasPaused(lock, false);
-}
-
-// In the child, after the fork, leave the profiler paused.
-static void
-paf_child()
-{
-  // This function can run off the main thread.
-
-  MOZ_RELEASE_ASSERT(gPS);
-
-  PS::AutoLock lock(gPSMutex);
-
-  gPS->SetWasPaused(lock, false);
-}
-
-// Set up the fork handlers.
-static void*
-setup_atfork()
-{
-  pthread_atfork(paf_prepare, paf_parent, paf_child);
-  return nullptr;
-}
-
-#endif /* !defined(GP_OS_android) */
-
 static void SetSampleContext(TickSample* sample, mcontext_t& mcontext)
 {
   // Extracting the sample from the context is extremely machine dependent.
 #if defined(GP_ARCH_x86)
   sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]);
   sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]);
   sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]);
 #elif defined(GP_ARCH_amd64)
@@ -576,16 +512,17 @@ private:
   SamplerThread(const SamplerThread&) = delete;
   void operator=(const SamplerThread&) = delete;
 };
 
 SamplerThread::SigHandlerCoordinator* SamplerThread::sSigHandlerCoordinator =
   nullptr;
 
 #if defined(GP_OS_android)
+
 static struct sigaction gOldSigstartHandler;
 const int SIGSTART = SIGUSR2;
 
 static void freeArray(const char** array, int size) {
   for (int i = 0; i < size; i++) {
     free((void*) array[i]);
   }
 }
@@ -689,23 +626,65 @@ PlatformInit(PS::LockRef aLock)
   sa.sa_sigaction = SigstartHandler;
   sigemptyset(&sa.sa_mask);
   sa.sa_flags = SA_RESTART | SA_SIGINFO;
   if (sigaction(SIGSTART, &sa, &gOldSigstartHandler) != 0) {
     MOZ_CRASH("Error installing SIGSTART handler in the profiler");
   }
 }
 
-#else
+#else /* !defined(GP_OS_android) */
+
+// We use pthread_atfork() to temporarily disable signal delivery during any
+// fork() call. Without that, fork() can be repeatedly interrupted by signal
+// delivery, requiring it to be repeatedly restarted, which can lead to *long*
+// delays. See bug 837390.
+//
+// We provide no paf_child() function to run in the child after forking. This
+// is fine because we always immediately exec() after fork(), and exec()
+// clobbers all process state. (At one point we did have a paf_child()
+// function, but it caused problems related to locking gPSMutex. See bug
+// 1348374.)
+//
+// Unfortunately all this is only doable on non-Android because Bionic doesn't
+// have pthread_atfork.
+
+// In the parent, before the fork, record IsPaused, and then pause.
+static void
+paf_prepare()
+{
+  // This function can run off the main thread.
+
+  MOZ_RELEASE_ASSERT(gPS);
+
+  PS::AutoLock lock(gPSMutex);
+
+  gPS->SetWasPaused(lock, gPS->IsPaused(lock));
+  gPS->SetIsPaused(lock, true);
+}
+
+// In the parent, after the fork, return IsPaused to the pre-fork state.
+static void
+paf_parent()
+{
+  // This function can run off the main thread.
+
+  MOZ_RELEASE_ASSERT(gPS);
+
+  PS::AutoLock lock(gPSMutex);
+
+  gPS->SetIsPaused(lock, gPS->WasPaused(lock));
+  gPS->SetWasPaused(lock, false);
+}
 
 static void
 PlatformInit(PS::LockRef aLock)
 {
   // Set up the fork handlers.
-  setup_atfork();
+  pthread_atfork(paf_prepare, paf_parent, nullptr);
 }
 
 #endif
 
 void TickSample::PopulateContext(void* aContext)
 {
   MOZ_ASSERT(aContext);
   ucontext_t* pContext = reinterpret_cast<ucontext_t*>(aContext);
--- a/xpcom/io/SlicedInputStream.cpp
+++ b/xpcom/io/SlicedInputStream.cpp
@@ -251,17 +251,18 @@ SlicedInputStream::AsyncWait(nsIInputStr
 void
 SlicedInputStream::Serialize(mozilla::ipc::InputStreamParams& aParams,
                              FileDescriptorArray& aFileDescriptors)
 {
   MOZ_ASSERT(mInputStream);
   MOZ_ASSERT(mWeakIPCSerializableInputStream);
 
   SlicedInputStreamParams params;
-  SerializeInputStream(mInputStream, params.stream(), aFileDescriptors);
+  InputStreamHelper::SerializeInputStream(mInputStream, params.stream(),
+                                          aFileDescriptors);
   params.start() = mStart;
   params.length() = mLength;
   params.curPos() = mCurPos;
   params.closed() = mClosed;
 
   aParams = params;
 }
 
@@ -277,17 +278,18 @@ SlicedInputStream::Deserialize(const moz
     NS_ERROR("Received unknown parameters from the other process!");
     return false;
   }
 
   const SlicedInputStreamParams& params =
     aParams.get_SlicedInputStreamParams();
 
   nsCOMPtr<nsIInputStream> stream =
-    DeserializeInputStream(params.stream(), aFileDescriptors);
+    InputStreamHelper::DeserializeInputStream(params.stream(),
+                                              aFileDescriptors);
   if (!stream) {
     NS_WARNING("Deserialize failed!");
     return false;
   }
 
   SetSourceStream(stream);
 
   mStart = params.start();
--- a/xpcom/io/nsMultiplexInputStream.cpp
+++ b/xpcom/io/nsMultiplexInputStream.cpp
@@ -704,18 +704,19 @@ nsMultiplexInputStream::Serialize(InputS
   uint32_t streamCount = mStreams.Length();
 
   if (streamCount) {
     InfallibleTArray<InputStreamParams>& streams = params.streams();
 
     streams.SetCapacity(streamCount);
     for (uint32_t index = 0; index < streamCount; index++) {
       InputStreamParams childStreamParams;
-      SerializeInputStream(mStreams[index], childStreamParams,
-                           aFileDescriptors);
+      InputStreamHelper::SerializeInputStream(mStreams[index],
+                                              childStreamParams,
+                                              aFileDescriptors);
 
       streams.AppendElement(childStreamParams);
     }
   }
 
   params.currentStream() = mCurrentStream;
   params.status() = mStatus;
   params.startedReadingCurrent() = mStartedReadingCurrent;
@@ -736,17 +737,18 @@ nsMultiplexInputStream::Deserialize(cons
   const MultiplexInputStreamParams& params =
     aParams.get_MultiplexInputStreamParams();
 
   const InfallibleTArray<InputStreamParams>& streams = params.streams();
 
   uint32_t streamCount = streams.Length();
   for (uint32_t index = 0; index < streamCount; index++) {
     nsCOMPtr<nsIInputStream> stream =
-      DeserializeInputStream(streams[index], aFileDescriptors);
+      InputStreamHelper::DeserializeInputStream(streams[index],
+                                                aFileDescriptors);
     if (!stream) {
       NS_WARNING("Deserialize failed!");
       return false;
     }
 
     if (NS_FAILED(AppendStream(stream))) {
       NS_WARNING("AppendStream failed!");
       return false;