Merge m-c to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 22 Oct 2015 12:00:41 +0200
changeset 304211 eabcebb0e668c30b5429dfc9e92052bc6f37fe1d
parent 304210 6528d6efc1754cdafcc3842d9c67cf2b616b0a3e (current diff)
parent 304101 76bd0c01d72e64ca4f261ffdb2652a91f961e930 (diff)
child 304212 11a3979d7a17147e39c4e9b14be9a7ce13a58911
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to b2g-inbound
mobile/android/gradle/thirdparty_adjust_sdk/AndroidManifest.xml
mobile/android/gradle/thirdparty_adjust_sdk/build.gradle
testing/web-platform/mozilla/meta/service-workers/service-worker/invalid-blobtype.https.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/stashed-ports.https.html.ini
testing/web-platform/mozilla/tests/service-workers/service-worker/resources/stashed-ports-basics.js
testing/web-platform/mozilla/tests/service-workers/service-worker/stashed-ports.https.html
--- a/.hgignore
+++ b/.hgignore
@@ -47,18 +47,22 @@
 # SVN directories
 \.svn/
 
 # Ignore the files and directory that Eclipse IDE creates
 \.project$
 \.cproject$
 \.settings/
 
-# Ignore the directory that JetBrains IDEs create
+# Ignore the files and directory that JetBrains IDEs create.
 \.idea/
+\.iml$
+
+# Gradle cache.
+^.gradle/
 
 # Python stuff installed at build time.
 ^python/psutil/.*\.so
 ^python/psutil/.*\.pyd
 ^python/psutil/build/
 
 # Git repositories
 .git/
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1215696 - Update mp4parse to v0.1.1
+Bug 1217261 - Update mp4parse to v0.1.2
--- a/accessible/windows/ProxyWrappers.h
+++ b/accessible/windows/ProxyWrappers.h
@@ -21,30 +21,35 @@ class ProxyAccessibleWrap : public Acces
   {
     mType = eProxyType;
     mBits.proxy = aProxy;
   }
 
   virtual void Shutdown() override
   {
     mBits.proxy = nullptr;
+    mStateFlags |= eIsDefunct;
   }
 };
 
 class HyperTextProxyAccessibleWrap : public HyperTextAccessibleWrap
 {
 public:
   HyperTextProxyAccessibleWrap(ProxyAccessible* aProxy) :
     HyperTextAccessibleWrap(nullptr, nullptr)
   {
     mType = eProxyType;
     mBits.proxy = aProxy;
   }
 
-  virtual void Shutdown() override { mBits.proxy = nullptr; }
+  virtual void Shutdown() override
+  {
+    mBits.proxy = nullptr;
+ mStateFlags |= eIsDefunct;
+  }
 };
 
 class DocProxyAccessibleWrap : public HyperTextProxyAccessibleWrap
 {
 public:
   DocProxyAccessibleWrap(ProxyAccessible* aProxy) :
     HyperTextProxyAccessibleWrap(aProxy)
   { mGenericTypes |= eDocument; }
--- a/accessible/windows/msaa/AccessibleWrap.cpp
+++ b/accessible/windows/msaa/AccessibleWrap.cpp
@@ -118,17 +118,17 @@ AccessibleWrap::QueryInterface(REFIID ii
   else if (IID_IDispatch == iid || IID_IAccessible == iid)
     *ppv = static_cast<IAccessible*>(this);
   else if (IID_IEnumVARIANT == iid && !IsProxy()) {
     // Don't support this interface for leaf elements.
     if (!HasChildren() || nsAccUtils::MustPrune(this))
       return E_NOINTERFACE;
 
     *ppv = static_cast<IEnumVARIANT*>(new ChildrenEnumVariant(this));
-  } else if (IID_IServiceProvider == iid && !IsProxy())
+  } else if (IID_IServiceProvider == iid)
     *ppv = new ServiceProvider(this);
   else if (IID_ISimpleDOMNode == iid && !IsProxy()) {
     if (IsDefunct() || (!HasOwnContent() && !IsDoc()))
       return E_NOINTERFACE;
 
     *ppv = static_cast<ISimpleDOMNode*>(new sdnAccessible(GetNode()));
   }
 
@@ -1509,17 +1509,17 @@ AccessibleWrap::GetXPAccessibleFor(const
     Accessible* child =
 #ifdef _WIN64
       GetAccessibleInSubtree(document, static_cast<uint32_t>(aVarChild.lVal));
 #else
       document->GetAccessibleByUniqueIDInSubtree(uniqueID);
 #endif
 
     // If it is a document then just return an accessible.
-    if (IsDoc())
+    if (child && IsDoc())
       return child;
 
     // Otherwise check whether the accessible is a child (this path works for
     // ARIA documents and popups).
     Accessible* parent = child;
     while (parent && parent != document) {
       if (parent == this)
         return child;
@@ -1529,18 +1529,25 @@ AccessibleWrap::GetXPAccessibleFor(const
   }
 
   // Now see about the case that both this accessible and the target one are
   // proxied.
   uint32_t id = aVarChild.lVal;
   if (IsProxy()) {
     DocAccessibleParent* proxyDoc = Proxy()->Document();
     AccessibleWrap* wrapper = GetProxiedAccessibleInSubtree(proxyDoc, id);
+    if (!wrapper)
+      return nullptr;
+
     MOZ_ASSERT(wrapper->IsProxy());
 
+    if (proxyDoc == this->Proxy()) {
+      return wrapper;
+    }
+
     ProxyAccessible* parent = wrapper->Proxy();
     while (parent && parent != proxyDoc) {
       if (parent == this->Proxy()) {
         return wrapper;
       }
 
       parent = parent->Parent();
     }
@@ -1565,16 +1572,26 @@ AccessibleWrap::GetXPAccessibleFor(const
     if (!outerDoc) {
       continue;
     }
 
     if (outerDoc->Document() != doc) {
       continue;
     }
 
+    if (doc == this) {
+      AccessibleWrap* proxyWrapper =
+        GetProxiedAccessibleInSubtree(remoteDocs->ElementAt(i), id);
+      if (proxyWrapper) {
+        return proxyWrapper;
+      }
+
+      continue;
+    }
+
     Accessible* parent = outerDoc;
     while (parent && parent != doc) {
       if (parent == this) {
         AccessibleWrap* proxyWrapper =
           GetProxiedAccessibleInSubtree(remoteDocs->ElementAt(i), id);
         if (proxyWrapper) {
           return proxyWrapper;
         }
--- a/browser/branding/aurora/content/aboutDialog.css
+++ b/browser/branding/aurora/content/aboutDialog.css
@@ -4,16 +4,26 @@
 
 #aboutDialogContainer {
   background-image: url("chrome://branding/content/about-background.png");
   background-repeat: no-repeat;
   background-color: rgb(26,58,99);
   color: #fff;
 }
 
+/* Use inverted spinner icon on the dark background */
+.update-throbber {
+  list-style-image: url("chrome://global/skin/icons/loading-inverted.png");
+}
+@media (min-resolution: 1.1dppx) {
+  .update-throbber {
+    list-style-image: url("chrome://global/skin/icons/loading-inverted@2x.png");
+  }
+}
+
 .text-link {
   color: #fff !important;
   text-decoration: underline;
 }
 
 .text-link:-moz-focusring {
   border-color: #fff;
 }
--- a/browser/branding/nightly/content/aboutDialog.css
+++ b/browser/branding/nightly/content/aboutDialog.css
@@ -4,16 +4,26 @@
 
 #aboutDialogContainer {
   background-image: url("chrome://branding/content/about-background.png");
   background-repeat: no-repeat;
   background-color: rgb(10,17,37);
   color: #fff;
 }
 
+/* Use inverted spinner icon on the dark background */
+.update-throbber {
+  list-style-image: url("chrome://global/skin/icons/loading-inverted.png");
+}
+@media (min-resolution: 1.1dppx) {
+  .update-throbber {
+    list-style-image: url("chrome://global/skin/icons/loading-inverted@2x.png");
+  }
+}
+
 .text-link {
   color: #fff !important;
   text-decoration: underline;
 }
 
 .text-link:-moz-focusring {
   border-color: #fff;
 }
--- a/browser/branding/unofficial/content/aboutDialog.css
+++ b/browser/branding/unofficial/content/aboutDialog.css
@@ -4,16 +4,26 @@
 
 #aboutDialogContainer {
   background-image: url("chrome://branding/content/about-background.png");
   background-repeat: no-repeat;
   background-color: #000;
   color: #fff;
 }
 
+/* Use inverted spinner icon on the dark background */
+.update-throbber {
+  list-style-image: url("chrome://global/skin/icons/loading-inverted.png");
+}
+@media (min-resolution: 1.1dppx) {
+  .update-throbber {
+    list-style-image: url("chrome://global/skin/icons/loading-inverted@2x.png");
+  }
+}
+
 .text-link {
   color: #fff !important;
   text-decoration: underline;
 }
 
 #rightBox {
   /* this margin prevents text from overlapping the planet image */
   margin-left: 280px;
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -123,43 +123,48 @@ global.openPanel = (node, popupURL, exte
   Services.scriptSecurityManager.checkLoadURIWithPrincipal(
     extension.principal, popupURI,
     Services.scriptSecurityManager.DISALLOW_SCRIPT);
 
   let panel = document.createElement("panel");
   panel.setAttribute("id", makeWidgetId(extension.id) + "-panel");
   panel.setAttribute("class", "browser-extension-panel");
   panel.setAttribute("type", "arrow");
-  panel.setAttribute("flip", "slide");
+  panel.setAttribute("role", "group");
 
   let anchor;
   if (node.localName == "toolbarbutton") {
     // Toolbar buttons are a special case. The panel becomes a child of
     // the button, and is anchored to the button's icon.
     node.appendChild(panel);
     anchor = document.getAnonymousElementByAttribute(node, "class", "toolbarbutton-icon");
   } else {
     // In all other cases, the panel is anchored to the target node
     // itself, and is a child of a popupset node.
     document.getElementById("mainPopupSet").appendChild(panel);
     anchor = node;
   }
 
-  let context;
-  panel.addEventListener("popuphidden", () => {
-    context.unload();
-    panel.remove();
-  });
-
   const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
   let browser = document.createElementNS(XUL_NS, "browser");
   browser.setAttribute("type", "content");
   browser.setAttribute("disableglobalhistory", "true");
   panel.appendChild(browser);
 
+  let titleChangedListener = () => {
+    panel.setAttribute("aria-label", browser.contentTitle);
+  }
+
+  let context;
+  panel.addEventListener("popuphidden", () => {
+    browser.removeEventListener("DOMTitleChanged", titleChangedListener, true);
+    context.unload();
+    panel.remove();
+  });
+
   let loadListener = () => {
     panel.removeEventListener("load", loadListener);
 
     context = new ExtensionPage(extension, {
       type: "popup",
       contentWindow: browser.contentWindow,
       uri: popupURI,
       docShell: browser.docShell,
@@ -187,16 +192,18 @@ global.openPanel = (node, popupURL, exte
       height = Math.min(height, 800);
 
       browser.setAttribute("width", width);
       browser.setAttribute("height", height);
 
       panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
     };
     browser.addEventListener("load", contentLoadListener, true);
+
+    browser.addEventListener("DOMTitleChanged", titleChangedListener, true);
   };
   panel.addEventListener("load", loadListener);
 
   return panel;
 }
 
 // Manages tab-specific context data, and dispatching tab select events
 // across all windows.
--- a/browser/components/loop/ui/fake-mozLoop.js
+++ b/browser/components/loop/ui/fake-mozLoop.js
@@ -112,17 +112,19 @@ var fakeRooms = [
    */
   navigator.mozLoop = {
     ensureRegistered: function() {},
     getAudioBlob: function(){},
     getLoopPref: function(pref) {
       switch(pref) {
         // Ensure we skip FTE completely.
         case "gettingStarted.seen":
+          return true;
       }
+      return null;
     },
     hasEncryptionKey: true,
     setLoopPref: function(){},
     releaseCallData: function() {},
     copyString: function() {},
     getUserAvatar: function(emailAddress) {
       var avatarUrl = "http://www.gravatar.com/avatar/0a996f0fe2727ef1668bdb11897e4459.jpg?default=blank&s=40";
       return Math.ceil(Math.random() * 3) === 2 ? avatarUrl : null;
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -121,30 +121,31 @@ var gPrivacyPane = {
   },
 
   // HISTORY MODE
 
   /**
    * The list of preferences which affect the initial history mode settings.
    * If the auto start private browsing mode pref is active, the initial
    * history mode would be set to "Don't remember anything".
-   * If all of these preferences have their default values, and the auto-start
+   * If ALL of these preferences are set to the values that correspond
+   * to keeping some part of history, and the auto-start
    * private browsing mode is not active, the initial history mode would be
    * set to "Remember everything".
    * Otherwise, the initial history mode would be set to "Custom".
    *
-   * Extensions adding their own preferences can append their IDs to this array if needed.
+   * Extensions adding their own preferences can set values here if needed.
    */
-  prefsForDefault: [
-    "places.history.enabled",
-    "browser.formfill.enable",
-    "network.cookie.cookieBehavior",
-    "network.cookie.lifetimePolicy",
-    "privacy.sanitize.sanitizeOnShutdown"
-  ],
+  prefsForKeepingHistory: {
+    "places.history.enabled": true, // History is enabled
+    "browser.formfill.enable": true, // Form information is saved
+    "network.cookie.cookieBehavior": 0, // All cookies are enabled
+    "network.cookie.lifetimePolicy": 0, // Cookies use supplied lifetime
+    "privacy.sanitize.sanitizeOnShutdown": false, // Private date is NOT cleared on shutdown
+  },
 
   /**
    * The list of control IDs which are dependent on the auto-start private
    * browsing setting, such that in "Custom" mode they would be disabled if
    * the auto-start private browsing checkbox is checked, and enabled otherwise.
    *
    * Extensions adding their own controls can append their IDs to this array if needed.
    */
@@ -153,40 +154,39 @@ var gPrivacyPane = {
     "rememberForms",
     "keepUntil",
     "keepCookiesUntil",
     "alwaysClear",
     "clearDataSettings"
   ],
 
   /**
-   * Check whether all the preferences values are set to their default values
+   * Check whether preferences values are set to keep history
    *
    * @param aPrefs an array of pref names to check for
-   * @returns boolean true if all of the prefs are set to their default values,
+   * @returns boolean true if all of the prefs are set to keep history,
    *                  false otherwise
    */
-  _checkDefaultValues: function(aPrefs) {
-    for (let i = 0; i < aPrefs.length; ++i) {
-      let pref = document.getElementById(aPrefs[i]);
-      if (pref.value != pref.defaultValue)
+  _checkHistoryValues: function(aPrefs) {
+    for (let pref of Object.keys(aPrefs)) {
+      if (document.getElementById(pref).value != aPrefs[pref])
         return false;
     }
     return true;
   },
 
   /**
    * Initialize the history mode menulist based on the privacy preferences
    */
   initializeHistoryMode: function PPP_initializeHistoryMode()
   {
     let mode;
     let getVal = aPref => document.getElementById(aPref).value;
 
-    if (this._checkDefaultValues(this.prefsForDefault)) {
+    if (this._checkHistoryValues(this.prefsForKeepingHistory)) {
       if (getVal("browser.privatebrowsing.autostart"))
         mode = "dontremember";
       else
         mode = "remember";
     }
     else
       mode = "custom";
 
copy from mobile/android/gradle/build.gradle
copy to build.gradle
--- a/mobile/android/gradle/build.gradle
+++ b/build.gradle
@@ -8,27 +8,27 @@ allprojects {
         topobjdir = gradle.mozconfig.topobjdir
     }
 
     repositories {
         jcenter()
     }
 }
 
-buildDir "${topobjdir}/mobile/android/gradle/build"
+buildDir "${topobjdir}/gradle/build"
 
 buildscript {
     repositories {
         jcenter()
 
         // For spoon-gradle-plugin SNAPSHOT release.  This needs to go before
         // the snapshots repository, otherwise we find a remote 1.0.3-SNAPSHOT
         // that doesn't include nalexander's local changes.
         maven {
-            url "file://${topsrcdir}/mobile/android/gradle/m2repo"
+            url "file://${gradle.mozconfig.topsrcdir}/mobile/android/gradle/m2repo"
         }
         // For spoon SNAPSHOT releases.
         maven {
             url 'https://oss.sonatype.org/content/repositories/snapshots'
         }
     }
 
     dependencies {
@@ -91,12 +91,37 @@ afterEvaluate {
 }
 
 apply plugin: 'idea'
 
 idea {
     project {
         languageLevel = '1.7'
     }
+
+    module {
+        // Object directories take a huge amount of time for IntelliJ to index.
+        // Exclude them.  Convention is that object directories start with obj.
+        // IntelliJ is clever and will not exclude the parts of the object
+        // directory that are referenced, if there are any.
+        def topsrcdirURI = file(topsrcdir).toURI()
+        excludeDirs += files(file(topsrcdir)
+            .listFiles({it.isDirectory()} as FileFilter)
+            .collect({topsrcdirURI.relativize(it.toURI()).toString()}) // Relative paths.
+            .findAll({it.startsWith('obj')}))
+
+        // If topobjdir is below topsrcdir, hide only some portions of that tree.
+        def topobjdirURI = file(topobjdir).toURI()
+        if (!topsrcdirURI.relativize(topobjdirURI).isAbsolute()) {
+            excludeDirs -= file(topobjdir)
+            excludeDirs += files(file(topobjdir).listFiles())
+            excludeDirs -= file("${topobjdir}/gradle")
+            excludeDirs -= file("${topobjdir}/mobile")
+        }
+
+        if (!mozconfig.substs.MOZ_INSTALL_TRACKING) {
+            excludeDirs += file("${topsrcdir}/mobile/android/thirdparty/com/adjust")
+        }
+    }
 }
 
 task wrapper(type: Wrapper) {
 }
--- a/configure.in
+++ b/configure.in
@@ -63,16 +63,17 @@ GLIB_VERSION=2.22
 # The macro won't be used when compiling with earlier versions anyway.
 GLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_26
 GIO_VERSION=2.22
 PERL_VERSION=5.006
 CAIRO_VERSION=1.10
 PANGO_VERSION=1.22.0
 GTK2_VERSION=2.18.0
 GTK3_VERSION=3.4.0
+GDK_VERSION_MIN_REQUIRED=GDK_VERSION_3_4
 WINDRES_VERSION=2.14.90
 W32API_VERSION=3.14
 GNOMEUI_VERSION=2.2.0
 GCONF_VERSION=1.2.1
 STARTUP_NOTIFICATION_VERSION=0.8
 DBUS_VERSION=0.60
 SQLITE_VERSION=3.9.1
 
@@ -3751,16 +3752,17 @@ MOZ_ANDROID_HISTORY=
 MOZ_WEBSMS_BACKEND=
 MOZ_ANDROID_BEAM=
 MOZ_LOCALE_SWITCHER=
 MOZ_ANDROID_READING_LIST_SERVICE=
 MOZ_ANDROID_SEARCH_ACTIVITY=
 MOZ_ANDROID_DOWNLOADS_INTEGRATION=
 MOZ_ANDROID_MLS_STUMBLER=
 MOZ_ANDROID_SHARE_OVERLAY=
+MOZ_EXCLUDE_HYPHENATION_DICTIONARIES=
 MOZ_INSTALL_TRACKING=
 MOZ_SWITCHBOARD=
 ACCESSIBILITY=1
 MOZ_TIME_MANAGER=
 MOZ_SIMPLEPUSH=
 MOZ_PAY=
 MOZ_AUDIO_CHANNEL_MANAGER=
 MOZ_CONTENT_SANDBOX=
@@ -4241,16 +4243,18 @@ fi
 if test "$COMPILE_ENVIRONMENT"; then
   if test "$MOZ_ENABLE_GTK3"; then
     PKG_CHECK_MODULES(MOZ_GTK3, gtk+-3.0 >= $GTK3_VERSION gtk+-unix-print-3.0 glib-2.0 gobject-2.0 $GDK_PACKAGES)
     MOZ_GTK3_CFLAGS="-I${_topsrcdir}/widget/gtk/compat-gtk3 $MOZ_GTK3_CFLAGS"
     dnl Contrary to MOZ_GTK2_LIBS, MOZ_GTK3_LIBS needs to be literally added to TK_LIBS instead
     dnl of a make reference because of how TK_LIBS is mangled in toolkit/library/moz.build
     dnl for GTK+3 builds.
     TK_LIBS=$MOZ_GTK3_LIBS
+    AC_DEFINE_UNQUOTED(GDK_VERSION_MIN_REQUIRED,$GDK_VERSION_MIN_REQUIRED)
+    AC_DEFINE_UNQUOTED(GDK_VERSION_MAX_ALLOWED,$GDK_VERSION_MIN_REQUIRED)
     GLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_32
   fi
   if test "$MOZ_ENABLE_GTK2"; then
     GLIB_VERSION_MAX_ALLOWED=$GLIB_VERSION_MIN_REQUIRED
   fi
   if test "$MOZ_ENABLE_GTK"; then
     if test "$MOZ_X11"; then
       GDK_PACKAGES=gdk-x11-2.0
@@ -4856,16 +4860,23 @@ fi
 
 dnl = Include Tab Queue on Android
 dnl = Temporary build flag to allow development in Nightly
 dnl ========================================================
 if test -n "$MOZ_ANDROID_TAB_QUEUE"; then
     AC_DEFINE(MOZ_ANDROID_TAB_QUEUE)
 fi
 
+dnl =========================================================
+dnl = Whether to exclude hyphenations files in the build
+dnl =========================================================
+if test -n "$MOZ_EXCLUDE_HYPHENATION_DICTIONARIES"; then
+    AC_DEFINE(MOZ_EXCLUDE_HYPHENATION_DICTIONARIES)
+fi
+
 dnl ========================================================
 dnl = Include install tracking on Android
 dnl ========================================================
 if test -n "$MOZ_INSTALL_TRACKING"; then
     AC_DEFINE(MOZ_INSTALL_TRACKING)
 fi
 
 dnl ========================================================
@@ -8587,16 +8598,17 @@ AC_SUBST(MOZ_ANDROID_SEARCH_ACTIVITY)
 AC_SUBST(MOZ_ANDROID_SHARE_OVERLAY)
 AC_SUBST(MOZ_ANDROID_FIREFOX_ACCOUNT_PROFILES)
 AC_SUBST(MOZ_ANDROID_TAB_QUEUE)
 AC_SUBST(MOZ_ANDROID_MLS_STUMBLER)
 AC_SUBST(MOZ_ANDROID_DOWNLOADS_INTEGRATION)
 AC_SUBST(MOZ_ANDROID_APPLICATION_CLASS)
 AC_SUBST(MOZ_ANDROID_BROWSER_INTENT_CLASS)
 AC_SUBST(MOZ_ANDROID_SEARCH_INTENT_CLASS)
+AC_SUBST(MOZ_EXCLUDE_HYPHENATION_DICTIONARIES)
 AC_SUBST(MOZ_INSTALL_TRACKING)
 AC_SUBST(MOZ_SWITCHBOARD)
 AC_SUBST(ENABLE_STRIP)
 AC_SUBST(PKG_SKIP_STRIP)
 AC_SUBST(STRIP_FLAGS)
 AC_SUBST(USE_ELF_HACK)
 AC_SUBST(INCREMENTAL_LINKER)
 AC_SUBST(MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS)
--- a/devtools/client/framework/test/browser_toolbox_options.js
+++ b/devtools/client/framework/test/browser_toolbox_options.js
@@ -47,28 +47,28 @@ function* testSelectTool() {
   ok(true, "Toolbox selected via selectTool method");
 }
 
 function* testOptionsShortcut() {
   info ("Selecting another tool, then reselecting options panel with keyboard.");
 
   yield toolbox.selectTool("webconsole");
   is(toolbox.currentToolId, "webconsole", "webconsole is selected");
-  synthesizeKeyFromKeyTag("toolbox-options-key", doc);
+  synthesizeKeyFromKeyTag(doc.getElementById("toolbox-options-key"));
   is(toolbox.currentToolId, "options", "Toolbox selected via shortcut key (1)");
-  synthesizeKeyFromKeyTag("toolbox-options-key", doc);
+  synthesizeKeyFromKeyTag(doc.getElementById("toolbox-options-key"));
   is(toolbox.currentToolId, "webconsole", "webconsole is selected (1)");
 
   yield toolbox.selectTool("webconsole");
   is(toolbox.currentToolId, "webconsole", "webconsole is selected");
-  synthesizeKeyFromKeyTag("toolbox-options-key2", doc);
+  synthesizeKeyFromKeyTag(doc.getElementById("toolbox-options-key2"));
   is(toolbox.currentToolId, "options", "Toolbox selected via shortcut key (2)");
-  synthesizeKeyFromKeyTag("toolbox-options-key", doc);
+  synthesizeKeyFromKeyTag(doc.getElementById("toolbox-options-key"));
   is(toolbox.currentToolId, "webconsole", "webconsole is reselected (2)");
-  synthesizeKeyFromKeyTag("toolbox-options-key2", doc);
+  synthesizeKeyFromKeyTag(doc.getElementById("toolbox-options-key2"));
   is(toolbox.currentToolId, "options", "Toolbox selected via shortcut key (2)");
 }
 
 function* testOptions() {
   let tool = toolbox.getPanel("options");
   panelWin = tool.panelWin;
   let prefNodes = tool.panelDoc.querySelectorAll("checkbox[data-pref]");
 
--- a/devtools/client/framework/test/shared-head.js
+++ b/devtools/client/framework/test/shared-head.js
@@ -92,39 +92,39 @@ function removeTab(tab) {
       info("Tab removed and finished closing.");
       resolve();
     }, false);
 
     gBrowser.removeTab(tab);
   });
 }
 
-function synthesizeKeyFromKeyTag(aKeyId, document) {
-  let key = document.getElementById(aKeyId);
-  isnot(key, null, "Successfully retrieved the <key> node");
+function synthesizeKeyFromKeyTag(key) {
+  is(key && key.tagName, "key", "Successfully retrieved the <key> node");
 
   let modifiersAttr = key.getAttribute("modifiers");
 
   let name = null;
 
   if (key.getAttribute("keycode"))
     name = key.getAttribute("keycode");
   else if (key.getAttribute("key"))
     name = key.getAttribute("key");
 
   isnot(name, null, "Successfully retrieved keycode/key");
 
   let modifiers = {
-    shiftKey: modifiersAttr.match("shift"),
-    ctrlKey: modifiersAttr.match("ctrl"),
-    altKey: modifiersAttr.match("alt"),
-    metaKey: modifiersAttr.match("meta"),
-    accelKey: modifiersAttr.match("accel")
+    shiftKey: !!modifiersAttr.match("shift"),
+    ctrlKey: !!modifiersAttr.match("control"),
+    altKey: !!modifiersAttr.match("alt"),
+    metaKey: !!modifiersAttr.match("meta"),
+    accelKey: !!modifiersAttr.match("accel")
   };
 
+  info("Synthesizing key " + name + " " + JSON.stringify(modifiers));
   EventUtils.synthesizeKey(name, modifiers);
 }
 
 /**
  * Wait for eventName on target.
  * @param {Object} target An observable object that either supports on/off or
  * addEventListener/removeEventListener
  * @param {String} eventName
--- a/devtools/client/responsivedesign/test/browser_responsiveui.js
+++ b/devtools/client/responsivedesign/test/browser_responsiveui.js
@@ -22,17 +22,17 @@ function test() {
         EventUtils.synthesizeKey(str.charAt(i), {});
       }
     }
 
     yield addTab("data:text/html,mop");
 
     let mgr = ResponsiveUI.ResponsiveUIManager;
 
-    synthesizeKeyFromKeyTag("key_responsiveUI");
+    synthesizeKeyFromKeyTag(document.getElementById("key_responsiveUI"));
 
     yield once(mgr, "on");
 
     // Is it open?
     let container = gBrowser.getBrowserContainer();
     is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
 
     // Menus are correctly updated?
--- a/devtools/client/responsivedesign/test/browser_responsiveuiaddcustompreset.js
+++ b/devtools/client/responsivedesign/test/browser_responsiveuiaddcustompreset.js
@@ -19,39 +19,23 @@ function test() {
         return c;
       } else {
         return testOnePreset(c - 1);
       }
     }
     return testOnePreset(instance.menulist.firstChild.childNodes.length - 4);
   }
 
-  function synthesizeKeyFromKeyTag(aKeyId) {
-    let key = document.getElementById(aKeyId);
-    isnot(key, null, "Successfully retrieved the <key> node");
-
-    let name = null;
-
-    if (key.getAttribute("keycode"))
-      name = key.getAttribute("keycode");
-    else if (key.getAttribute("key"))
-      name = key.getAttribute("key");
-
-    isnot(name, null, "Successfully retrieved keycode/key");
-
-    key.doCommand();
-  }
-
   Task.spawn(function*() {
 
     yield addTab("data:text/html;charset=utf8,test custom presets in responsive mode");
 
     let mgr = ResponsiveUI.ResponsiveUIManager;
 
-    synthesizeKeyFromKeyTag("key_responsiveUI");
+    synthesizeKeyFromKeyTag(document.getElementById("key_responsiveUI"));
 
     yield once(mgr, "on");
 
     oldPrompt = Services.prompt;
     Services.prompt = {
       value: "",
       returnBool: true,
       prompt: function(aParent, aDialogTitle, aText, aValue, aCheckMsg, aCheckState) {
@@ -102,17 +86,17 @@ function test() {
 
     info("waiting for responsive mode to turn off");
     yield once(mgr, "off");
 
     // We're still in the loop of initializing the responsive mode.
     // Let's wait next loop to stop it.
     yield nextTick();
 
-    synthesizeKeyFromKeyTag("key_responsiveUI");
+    synthesizeKeyFromKeyTag(document.getElementById("key_responsiveUI"));
 
     yield once(mgr, "on");
 
     is(container.getAttribute("responsivemode"), "true", "In responsive mode.");
 
     instance = mgr.getResponsiveUIForTab(gBrowser.selectedTab);
 
     let customPresetIndex = getPresetIndex("456" + "\u00D7" + "123 (Testing preset)");
@@ -135,17 +119,17 @@ function test() {
     instance.menulist.selectedIndex = 2;
     deletedPresetB = instance.menulist.selectedItem.getAttribute("label");
     instance.removebutton.doCommand();
 
     yield nextTick();
     instance.close();
     yield once(mgr, "off");
 
-    synthesizeKeyFromKeyTag("key_responsiveUI");
+    synthesizeKeyFromKeyTag(document.getElementById("key_responsiveUI"));
 
     info("waiting for responsive mode to turn on");
     yield once(mgr, "on");
 
     instance = mgr.getResponsiveUIForTab(gBrowser.selectedTab);
 
     customPresetIndex = getPresetIndex(deletedPresetA);
     is(customPresetIndex, -1, "deleted preset " + deletedPresetA + " is not in the list anymore");
--- a/devtools/client/responsivedesign/test/head.js
+++ b/devtools/client/responsivedesign/test/head.js
@@ -1,17 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
-var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-var {TargetFactory} = require("devtools/client/framework/target");
-var DevToolsUtils = require("devtools/shared/DevToolsUtils");
-var promise = require("promise");
+// shared-head.js handles imports, constants, and utility functions
+Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
 
 // Import the GCLI test helper
 var testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
 Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
 
 DevToolsUtils.testing = true;
 registerCleanupFunction(() => {
   DevToolsUtils.testing = false;
@@ -179,42 +177,16 @@ function once(target, eventName, useCapt
 }
 
 function wait(ms) {
   let def = promise.defer();
   setTimeout(def.resolve, ms);
   return def.promise;
 }
 
-function synthesizeKeyFromKeyTag(aKeyId) {
-  let key = document.getElementById(aKeyId);
-  isnot(key, null, "Successfully retrieved the <key> node");
-
-  let modifiersAttr = key.getAttribute("modifiers");
-
-  let name = null;
-
-  if (key.getAttribute("keycode"))
-    name = key.getAttribute("keycode");
-  else if (key.getAttribute("key"))
-    name = key.getAttribute("key");
-
-  isnot(name, null, "Successfully retrieved keycode/key");
-
-  let modifiers = {
-    shiftKey: modifiersAttr.match("shift"),
-    ctrlKey: modifiersAttr.match("ctrl"),
-    altKey: modifiersAttr.match("alt"),
-    metaKey: modifiersAttr.match("meta"),
-    accelKey: modifiersAttr.match("accel")
-  }
-
-  EventUtils.synthesizeKey(name, modifiers);
-}
-
 function nextTick() {
   let def = promise.defer();
   executeSoon(() => def.resolve())
   return def.promise;
 }
 
 /**
  * Waits for the next load to complete in the current browser.
--- a/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js
+++ b/devtools/client/webconsole/test/browser_console_keyboard_accessibility.js
@@ -48,17 +48,18 @@ var test = asyncTest(function*() {
   EventUtils.synthesizeKey("VK_END", {});
 
   let scrollTop = hud.outputNode.parentNode.scrollTop;
   ok(scrollTop > 0 && Math.abs(scrollTop - bottom) <= 5,
      "scroll position now at bottom");
 
   info("try ctrl-l to clear output");
   executeSoon(() => {
-    EventUtils.synthesizeKey("l", { ctrlKey: true });
+    let clearKey = hud.ui.window.document.querySelector("key[command=consoleCmd_clearOutput]:not([disabled])");
+    synthesizeKeyFromKeyTag(clearKey);
   });
   yield hud.jsterm.once("messages-cleared");
 
   is(hud.outputNode.textContent.indexOf("foobarz1"), -1, "output cleared");
   is(hud.jsterm.inputNode.getAttribute("focused"), "true",
      "jsterm input is focused");
 
   info("try ctrl-f to focus filter");
--- a/devtools/client/webconsole/test/head.js
+++ b/devtools/client/webconsole/test/head.js
@@ -1,24 +1,21 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
-var {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {});
-var {console} = Cu.import("resource://gre/modules/Console.jsm", {});
+// shared-head.js handles imports, constants, and utility functions
+Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
+
 var {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
-var {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
-var promise = require("promise");
-var {TargetFactory} = require("devtools/client/framework/target");
 var {Utils: WebConsoleUtils} = require("devtools/shared/webconsole/utils");
 var {Messages} = require("devtools/client/webconsole/console-output");
-var DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const asyncStorage = require("devtools/shared/async-storage");
 
 // Services.prefs.setBoolPref("devtools.debugger.log", true);
 
 var gPendingOutputTest = 0;
 
 // The various categories of messages.
 const CATEGORY_NETWORK = 0;
--- a/devtools/client/webconsole/webconsole.js
+++ b/devtools/client/webconsole/webconsole.js
@@ -10,34 +10,30 @@ const {Cc, Ci, Cu} = require("chrome");
 
 const {Utils: WebConsoleUtils, CONSOLE_WORKER_IDS} = require("devtools/shared/webconsole/utils");
 const promise = require("promise");
 
 loader.lazyServiceGetter(this, "clipboardHelper",
                          "@mozilla.org/widget/clipboardhelper;1",
                          "nsIClipboardHelper");
 loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
-loader.lazyGetter(this, "EventEmitter", () => require("devtools/shared/event-emitter"));
-loader.lazyGetter(this, "AutocompletePopup",
-                  () => require("devtools/client/shared/autocomplete-popup").AutocompletePopup);
-loader.lazyGetter(this, "ToolSidebar",
-                  () => require("devtools/client/framework/sidebar").ToolSidebar);
-loader.lazyGetter(this, "ConsoleOutput",
-                  () => require("devtools/client/webconsole/console-output").ConsoleOutput);
-loader.lazyGetter(this, "Messages",
-                  () => require("devtools/client/webconsole/console-output").Messages);
-loader.lazyGetter(this, "asyncStorage",
-                  () => require("devtools/shared/async-storage"));
+loader.lazyRequireGetter(this, "EventEmitter", "devtools/shared/event-emitter");
+loader.lazyRequireGetter(this, "AutocompletePopup", "devtools/client/shared/autocomplete-popup", true);
+loader.lazyRequireGetter(this, "ToolSidebar", "devtools/client/framework/sidebar", true);
+loader.lazyRequireGetter(this, "ConsoleOutput", "devtools/client/webconsole/console-output", true);
+loader.lazyRequireGetter(this, "Messages", "devtools/client/webconsole/console-output", true);
+loader.lazyRequireGetter(this, "asyncStorage", "devtools/shared/async-storage");
 loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/main", true);
 loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", true);
+loader.lazyRequireGetter(this, "system", "devtools/shared/system");
+loader.lazyRequireGetter(this, "Timers", "sdk/timers");
 loader.lazyImporter(this, "VariablesView", "resource://devtools/client/shared/widgets/VariablesView.jsm");
 loader.lazyImporter(this, "VariablesViewController", "resource://devtools/client/shared/widgets/VariablesViewController.jsm");
 loader.lazyImporter(this, "PluralForm", "resource://gre/modules/PluralForm.jsm");
 loader.lazyImporter(this, "gDevTools", "resource://devtools/client/framework/gDevTools.jsm");
-loader.lazyGetter(this, "Timers", () => require("sdk/timers"));
 
 const STRINGS_URI = "chrome://browser/locale/devtools/webconsole.properties";
 var l10n = new WebConsoleUtils.l10n(STRINGS_URI);
 
 const XHTML_NS = "http://www.w3.org/1999/xhtml";
 
 const MIXED_CONTENT_LEARN_MORE = "https://developer.mozilla.org/docs/Security/MixedContent";
 
@@ -523,16 +519,22 @@ WebConsoleFrame.prototype = {
     // Register the controller to handle "select all" properly.
     this._commandController = new CommandController(this);
     this.window.controllers.insertControllerAt(0, this._commandController);
 
     this._contextMenuHandler = new ConsoleContextMenu(this);
 
     let doc = this.document;
 
+    if (system.constants.platform === "macosx") {
+      doc.querySelector("#key_clearOSX").removeAttribute("disabled");
+    } else {
+      doc.querySelector("#key_clear").removeAttribute("disabled");
+    }
+
     this.filterBox = doc.querySelector(".hud-filter-box");
     this.outputNode = doc.getElementById("output-container");
     this.completeNode = doc.querySelector(".jsterm-complete-node");
     this.inputNode = doc.querySelector(".jsterm-input-node");
 
     this._setFilterTextBoxEvents();
     this._initFilterButtons();
 
--- a/devtools/client/webconsole/webconsole.xul
+++ b/devtools/client/webconsole/webconsole.xul
@@ -57,17 +57,21 @@ function goUpdateConsoleCommands() {
     <key key="&fullZoomReduceCmd.commandkey2;" command="cmd_fullZoomReduce" modifiers="accel"/>
     <key id="key_fullZoomEnlarge" key="&fullZoomEnlargeCmd.commandkey;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
     <key key="&fullZoomEnlargeCmd.commandkey2;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
     <key key="&fullZoomEnlargeCmd.commandkey3;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
     <key id="key_fullZoomReset" key="&fullZoomResetCmd.commandkey;" command="cmd_fullZoomReset" modifiers="accel"/>
     <key key="&fullZoomResetCmd.commandkey2;" command="cmd_fullZoomReset" modifiers="accel"/>
     <key key="&findCmd.key;" command="cmd_find" modifiers="accel"/>
     <key key="&closeCmd.key;" command="cmd_close" modifiers="accel"/>
-    <key key="&clearOutputCtrl.key;" command="consoleCmd_clearOutput" modifiers="control"/>
+
+    <!-- The 'clear' key differs by plaform, so the correct one becomes
+         enabled in JS -->
+    <key id="key_clear" disabled="true" key="&clearOutputCtrl.key;" command="consoleCmd_clearOutput" modifiers="control shift"/>
+    <key id="key_clearOSX" disabled="true" key="&clearOutputCtrl.key;" command="consoleCmd_clearOutput" modifiers="control"/>
   </keyset>
   <keyset id="editMenuKeys"/>
 
   <popupset id="mainPopupSet">
     <menupopup id="output-contextmenu" onpopupshowing="goUpdateGlobalEditMenuItems()">
       <menuitem id="saveBodiesContextMenu" type="checkbox" label="&saveBodies.label;"
                 accesskey="&saveBodies.accesskey;"/>
       <menuitem id="menu_openURL" label="&openURL.label;"
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -5486,16 +5486,17 @@ nsDocShell::LoadPage(nsISupports* aPageD
     newSpec.AppendLiteral("view-source:");
     newSpec.Append(spec);
 
     rv = NS_NewURI(getter_AddRefs(newUri), newSpec);
     if (NS_FAILED(rv)) {
       return rv;
     }
     shEntry->SetURI(newUri);
+    shEntry->SetOriginalURI(nullptr);
   }
 
   rv = LoadHistoryEntry(shEntry, LOAD_HISTORY);
   return rv;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetCurrentDescriptor(nsISupports** aPageDescriptor)
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -100,17 +100,17 @@ Throw(JSContext* aCx, nsresult aRv, cons
     // Don't clobber the existing exception.
     return false;
   }
 
   CycleCollectedJSRuntime* runtime = CycleCollectedJSRuntime::Get();
   nsCOMPtr<nsIException> existingException = runtime->GetPendingException();
   if (existingException) {
     nsresult nr;
-    if (NS_SUCCEEDED(existingException->GetResult(&nr)) && 
+    if (NS_SUCCEEDED(existingException->GetResult(&nr)) &&
         aRv == nr) {
       // Reuse the existing exception.
 
       // Clear pending exception
       runtime->SetPendingException(nullptr);
 
       if (!ThrowExceptionObject(aCx, existingException)) {
         // If we weren't able to throw an exception we're
@@ -382,17 +382,18 @@ NS_IMETHODIMP JSStackFrame::GetLanguageN
 // @argument [out] aCanCache whether the value can get cached.
 // @argument [out] aUseCachedValue if true, just use the cached value.
 // @argument [out] aValue the value we got from the stack.
 template<typename ReturnType, typename GetterOutParamType>
 static void
 GetValueIfNotCached(JSContext* aCx, JSObject* aStack,
                     JS::SavedFrameResult (*aPropGetter)(JSContext*,
                                                         JS::Handle<JSObject*>,
-                                                        GetterOutParamType),
+                                                        GetterOutParamType,
+                                                        JS::SavedFrameSelfHosted),
                     bool aIsCached, bool* aCanCache, bool* aUseCachedValue,
                     ReturnType aValue)
 {
   MOZ_ASSERT(aStack);
 
   JS::Rooted<JSObject*> stack(aCx, aStack);
   // Allow caching if aCx and stack are same-compartment.  Otherwise take the
   // slow path.
@@ -400,17 +401,17 @@ GetValueIfNotCached(JSContext* aCx, JSOb
   if (*aCanCache && aIsCached) {
     *aUseCachedValue = true;
     return;
   }
 
   *aUseCachedValue = false;
   JS::ExposeObjectToActiveJS(stack);
 
-  aPropGetter(aCx, stack, aValue);
+  aPropGetter(aCx, stack, aValue, JS::SavedFrameSelfHosted::Exclude);
 }
 
 NS_IMETHODIMP JSStackFrame::GetFilename(nsAString& aFilename)
 {
   if (!mStack) {
     aFilename.Truncate();
     return NS_OK;
   }
--- a/dom/fetch/Response.cpp
+++ b/dom/fetch/Response.cpp
@@ -205,17 +205,20 @@ Response::Constructor(const GlobalObject
 
     nsCOMPtr<nsIInputStream> bodyStream;
     nsCString contentType;
     aRv = ExtractByteStreamFromBody(aBody.Value(), getter_AddRefs(bodyStream), contentType);
     internalResponse->SetBody(bodyStream);
 
     if (!contentType.IsVoid() &&
         !internalResponse->Headers()->Has(NS_LITERAL_CSTRING("Content-Type"), aRv)) {
-      internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, aRv);
+      // Ignore Append() failing here.
+      ErrorResult error;
+      internalResponse->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, error);
+      error.SuppressException();
     }
 
     if (aRv.Failed()) {
       return nullptr;
     }
   }
 
   r->SetMimeType();
--- a/dom/html/test/file_fullscreen-top-layer.html
+++ b/dom/html/test/file_fullscreen-top-layer.html
@@ -17,16 +17,17 @@
       width: 0; height: 0;
       overflow: hidden;
       opacity: 0;
       mask: url(#mask);
       clip: rect(0, 0, 0, 0);
       clip-path: url(#clipPath);
       filter: opacity(0%);
       will-change: transform;
+      perspective: 10px;
       transform: scale(0);
     }
     /* The following styles are copied from ua.css to ensure that
      * no other style change may trigger frame reconstruction */
     :root {
       overflow: hidden !important;
     }
     .two #fullscreen {
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -148,16 +148,24 @@ public:
   friend class ReRequestAudioTask;
 
   // By default, the state machine polls the reader once per second when it's
   // in buffering mode. Some readers support a promise-based mechanism by which
   // they notify the state machine when the data arrives.
   virtual bool IsWaitForDataSupported() { return false; }
   virtual RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) { MOZ_CRASH(); }
 
+  // By default, the reader return the decoded data. Some readers support
+  // retuning demuxed data.
+  virtual bool IsDemuxOnlySupported() const { return false; }
+
+  // Configure the reader to return demuxed or decoded data
+  // upon calls to Request{Audio,Video}Data.
+  virtual void SetDemuxOnly(bool /*aDemuxedOnly*/) {}
+
   virtual bool HasAudio() = 0;
   virtual bool HasVideo() = 0;
 
   // The default implementation of AsyncReadMetadata is implemented in terms of
   // synchronous ReadMetadata() calls. Implementations may also
   // override AsyncReadMetadata to create a more proper async implementation.
   virtual RefPtr<MetadataPromise> AsyncReadMetadata();
 
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -349,16 +349,18 @@ private:
   // Returns true if the state machine has shutdown or is in the process of
   // shutting down. The decoder monitor must be held while calling this.
   bool IsShutdown();
 
   // Returns true if we're currently playing. The decoder monitor must
   // be held.
   bool IsPlaying() const;
 
+  // TODO: Those callback function may receive demuxed-only data.
+  // Need to figure out a suitable API name for this case.
   void OnAudioDecoded(MediaData* aAudioSample);
   void OnVideoDecoded(MediaData* aVideoSample);
   void OnNotDecoded(MediaData::Type aType, MediaDecoderReader::NotDecodedReason aReason);
   void OnAudioNotDecoded(MediaDecoderReader::NotDecodedReason aReason)
   {
     MOZ_ASSERT(OnTaskQueue());
     OnNotDecoded(MediaData::AUDIO_DATA, aReason);
   }
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -69,16 +69,17 @@ MediaFormatReader::MediaFormatReader(Abs
   , mDemuxerInitDone(false)
   , mLastReportedNumDecodedFrames(0)
   , mLayersBackendType(aLayersBackend)
   , mInitDone(false)
   , mSeekable(false)
   , mIsEncrypted(false)
   , mTrackDemuxersMayBlock(false)
   , mHardwareAccelerationDisabled(false)
+  , mDemuxOnly(false)
   , mVideoFrameContainer(aVideoFrameContainer)
 {
   MOZ_ASSERT(aDemuxer);
   MOZ_COUNT_CTOR(MediaFormatReader);
 }
 
 MediaFormatReader::~MediaFormatReader()
 {
@@ -565,17 +566,17 @@ MediaFormatReader::ShouldSkip(bool aSkip
   if (NS_FAILED(rv)) {
     return aSkipToNextKeyframe;
   }
   return nextKeyframe < aTimeThreshold && nextKeyframe.ToMicroseconds() >= 0;
 }
 
 RefPtr<MediaDecoderReader::VideoDataPromise>
 MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe,
-                                     int64_t aTimeThreshold)
+                                    int64_t aTimeThreshold)
 {
   MOZ_ASSERT(OnTaskQueue());
   MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), "No sample requests allowed while seeking");
   MOZ_DIAGNOSTIC_ASSERT(!mVideo.HasPromise(), "No duplicate sample requests");
   MOZ_DIAGNOSTIC_ASSERT(!mVideo.mSeekRequest.Exists() ||
                         mVideo.mTimeThreshold.isSome());
   MOZ_DIAGNOSTIC_ASSERT(!mSkipRequest.Exists(), "called mid-skipping");
   MOZ_DIAGNOSTIC_ASSERT(!IsSeeking(), "called mid-seek");
@@ -896,18 +897,31 @@ MediaFormatReader::RequestDemuxSamples(T
   LOGV("Requesting extra demux %s", TrackTypeToStr(aTrack));
   if (aTrack == TrackInfo::kVideoTrack) {
     DoDemuxVideo();
   } else {
     DoDemuxAudio();
   }
 }
 
+bool
+MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
+                                        MediaRawData* aSample)
+{
+  MOZ_ASSERT(OnTaskQueue());
+  auto& decoder = GetDecoderData(aTrack);
+  if (NS_FAILED(decoder.mDecoder->Input(aSample))) {
+      LOG("Unable to pass frame to decoder");
+      return false;
+  }
+  return true;
+}
+
 void
-MediaFormatReader::DecodeDemuxedSamples(TrackType aTrack,
+MediaFormatReader::HandleDemuxedSamples(TrackType aTrack,
                                         AbstractMediaDecoder::AutoNotifyDecoded& aA)
 {
   MOZ_ASSERT(OnTaskQueue());
   auto& decoder = GetDecoderData(aTrack);
 
   if (decoder.mQueuedSamples.IsEmpty()) {
     return;
   }
@@ -1000,22 +1014,30 @@ MediaFormatReader::DecodeDemuxedSamples(
     LOGV("Input:%lld (dts:%lld kf:%d)",
          sample->mTime, sample->mTimecode, sample->mKeyframe);
     decoder.mOutputRequested = true;
     decoder.mNumSamplesInput++;
     decoder.mSizeOfQueue++;
     if (aTrack == TrackInfo::kVideoTrack) {
       aA.mParsed++;
     }
-    if (NS_FAILED(decoder.mDecoder->Input(sample))) {
-      LOG("Unable to pass frame to decoder");
+
+    if (mDemuxOnly) {
+      ReturnOutput(sample, aTrack);
+    } else if (!DecodeDemuxedSamples(aTrack, sample)) {
       NotifyError(aTrack);
       return;
     }
+
     decoder.mQueuedSamples.RemoveElementAt(0);
+    if (mDemuxOnly) {
+      // If demuxed-only case, ReturnOutput will resolve with one demuxed data.
+      // Then we should stop doing the iteration.
+      return;
+    }
     samplesPending = true;
   }
 
   // We have serviced the decoder's request for more data.
   decoder.mInputExhausted = false;
 }
 
 void
@@ -1145,45 +1167,47 @@ MediaFormatReader::Update(TrackType aTra
   LOGV("Update(%s) ni=%d no=%d ie=%d, in:%llu out:%llu qs=%u pending:%u ahead:%d sid:%u",
        TrackTypeToStr(aTrack), needInput, needOutput, decoder.mInputExhausted,
        decoder.mNumSamplesInput, decoder.mNumSamplesOutput,
        uint32_t(size_t(decoder.mSizeOfQueue)), uint32_t(decoder.mOutput.Length()),
        !decoder.HasPromise(), decoder.mLastStreamSourceID);
 
   // Demux samples if we don't have some.
   RequestDemuxSamples(aTrack);
-  // Decode all pending demuxed samples.
-  DecodeDemuxedSamples(aTrack, a);
+
+  HandleDemuxedSamples(aTrack, a);
 }
 
 void
 MediaFormatReader::ReturnOutput(MediaData* aData, TrackType aTrack)
 {
   auto& decoder = GetDecoderData(aTrack);
   MOZ_ASSERT(decoder.HasPromise());
   if (decoder.mDiscontinuity) {
     LOGV("Setting discontinuity flag");
     decoder.mDiscontinuity = false;
     aData->mDiscontinuity = true;
   }
 
   if (aTrack == TrackInfo::kAudioTrack) {
-    AudioData* audioData = static_cast<AudioData*>(aData);
+    if (aData->mType != MediaData::RAW_DATA) {
+      AudioData* audioData = static_cast<AudioData*>(aData);
 
-    if (audioData->mChannels != mInfo.mAudio.mChannels ||
-        audioData->mRate != mInfo.mAudio.mRate) {
-      LOG("change of audio format (rate:%d->%d). "
-          "This is an unsupported configuration",
-          mInfo.mAudio.mRate, audioData->mRate);
-      mInfo.mAudio.mRate = audioData->mRate;
-      mInfo.mAudio.mChannels = audioData->mChannels;
+      if (audioData->mChannels != mInfo.mAudio.mChannels ||
+          audioData->mRate != mInfo.mAudio.mRate) {
+        LOG("change of audio format (rate:%d->%d). "
+            "This is an unsupported configuration",
+            mInfo.mAudio.mRate, audioData->mRate);
+        mInfo.mAudio.mRate = audioData->mRate;
+        mInfo.mAudio.mChannels = audioData->mChannels;
+      }
     }
-    mAudio.mPromise.Resolve(audioData, __func__);
+    mAudio.mPromise.Resolve(aData, __func__);
   } else if (aTrack == TrackInfo::kVideoTrack) {
-    mVideo.mPromise.Resolve(static_cast<VideoData*>(aData), __func__);
+    mVideo.mPromise.Resolve(aData, __func__);
   }
   LOG("Resolved data promise for %s", TrackTypeToStr(aTrack));
 }
 
 size_t
 MediaFormatReader::SizeOfVideoQueueInFrames()
 {
   return SizeOfQueue(TrackInfo::kVideoTrack);
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -84,16 +84,30 @@ public:
 
   bool VideoIsHardwareAccelerated() const override;
 
   void DisableHardwareAcceleration() override;
 
   bool IsWaitForDataSupported() override { return true; }
   RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType) override;
 
+  // MediaFormatReader supports demuxed-only mode.
+  bool IsDemuxOnlySupported() const override { return true; }
+
+  void SetDemuxOnly(bool aDemuxedOnly) override
+  {
+    if (OnTaskQueue()) {
+      mDemuxOnly = aDemuxedOnly;
+      return;
+    }
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<bool>(
+      this, &MediaDecoderReader::SetDemuxOnly, aDemuxedOnly);
+    OwnerThread()->Dispatch(r.forget());
+  }
+
   bool UseBufferingHeuristics() override
   {
     return mTrackDemuxersMayBlock;
   }
 
 #ifdef MOZ_EME
   void SetCDMProxy(CDMProxy* aProxy) override;
 #endif
@@ -118,19 +132,22 @@ private:
   // Lock for corresponding track must be held.
   void ScheduleUpdate(TrackType aTrack);
   void Update(TrackType aTrack);
   // Handle actions should more data be received.
   // Returns true if no more action is required.
   bool UpdateReceivedNewData(TrackType aTrack);
   // Called when new samples need to be demuxed.
   void RequestDemuxSamples(TrackType aTrack);
+  // Handle demuxed samples by the input behavior.
+  void HandleDemuxedSamples(TrackType aTrack,
+                            AbstractMediaDecoder::AutoNotifyDecoded& aA);
   // Decode any pending already demuxed samples.
-  void DecodeDemuxedSamples(TrackType aTrack,
-                            AbstractMediaDecoder::AutoNotifyDecoded& aA);
+  bool DecodeDemuxedSamples(TrackType aTrack,
+                            MediaRawData* aSample);
   // Drain the current decoder.
   void DrainDecoder(TrackType aTrack);
   void NotifyNewOutput(TrackType aTrack, MediaData* aSample);
   void NotifyInputExhausted(TrackType aTrack);
   void NotifyDrainComplete(TrackType aTrack);
   void NotifyError(TrackType aTrack);
   void NotifyWaitingForData(TrackType aTrack);
   void NotifyEndOfStream(TrackType aTrack);
@@ -402,16 +419,19 @@ private:
   }
   bool mIsEncrypted;
 
   // Set to true if any of our track buffers may be blocking.
   bool mTrackDemuxersMayBlock;
 
   bool mHardwareAccelerationDisabled;
 
+  // Set the demuxed-only flag.
+  Atomic<bool> mDemuxOnly;
+
   // Seeking objects.
   bool IsSeeking() const { return mPendingSeekTime.isSome(); }
   void AttemptSeek();
   void OnSeekFailed(TrackType aTrack, DemuxerFailureReason aFailure);
   void DoVideoSeek();
   void OnVideoSeekCompleted(media::TimeUnit aTime);
   void OnVideoSeekFailed(DemuxerFailureReason aFailure)
   {
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -446,17 +446,17 @@ RTCPeerConnection.prototype = {
   _initCertificate: function(certificates) {
     let certPromise;
     if (certificates && certificates.length > 0) {
       if (certificates.length > 1) {
         throw new this._win.DOMException(
           "RTCPeerConnection does not currently support multiple certificates",
           "NotSupportedError");
       }
-      let cert = certificates.find(c => c.expires.getTime() > Date.now());
+      let cert = certificates.find(c => c.expires > Date.now());
       if (!cert) {
         throw new this._win.DOMException(
           "Unable to create RTCPeerConnection with an expired certificate",
           "InvalidParameterError");
       }
       certPromise = Promise.resolve(cert);
     } else {
       certPromise = this._win.RTCPeerConnection.generateCertificate({
--- a/dom/media/gtest/MockMediaDecoderOwner.h
+++ b/dom/media/gtest/MockMediaDecoderOwner.h
@@ -24,16 +24,25 @@ public:
   {
   }
   virtual void NetworkError() override {}
   virtual void DecodeError() override {}
   virtual void LoadAborted() override {}
   virtual void PlaybackEnded() override {}
   virtual void SeekStarted() override {}
   virtual void SeekCompleted() override {}
+  virtual void DownloadProgressed() override {}
+  virtual void UpdateReadyState() override {}
+  virtual void FirstFrameLoaded() override {}
+#ifdef MOZ_EME
+  virtual void DispatchEncrypted(const nsTArray<uint8_t>& aInitData,
+                                 const nsAString& aInitDataType) override {}
+#endif // MOZ_EME
+  virtual bool IsActive() override { return true; }
+  virtual bool IsHidden() override { return false; }
   virtual void DownloadSuspended() override {}
   virtual void DownloadResumed(bool aForceNetworkLoading) override {}
   virtual void NotifySuspendedByCache(bool aIsSuspended) override {}
   virtual void NotifyDecoderPrincipalChanged() override {}
   virtual VideoFrameContainer* GetVideoFrameContainer() override
   {
     return nullptr;
   }
--- a/dom/media/gtest/MockMediaResource.cpp
+++ b/dom/media/gtest/MockMediaResource.cpp
@@ -6,17 +6,18 @@
 
 #include <sys/types.h>
 #include <sys/stat.h>
 
 namespace mozilla
 {
 
 MockMediaResource::MockMediaResource(const char* aFileName)
-  : mFileName(aFileName)
+  : mFileHandle(nullptr)
+  , mFileName(aFileName)
   , mContentType(NS_LITERAL_CSTRING("video/mp4"))
 {
 }
 
 nsresult
 MockMediaResource::Open(nsIStreamListener** aStreamListener)
 {
   mFileHandle = fopen(mFileName, "rb");
new file mode 100644
--- /dev/null
+++ b/dom/media/gtest/TestMediaFormatReader.cpp
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/dom/HTMLMediaElement.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TaskQueue.h"
+#include "ImageContainer.h"
+#include "Layers.h"
+#include "MediaData.h"
+#include "MediaFormatReader.h"
+#include "MP4Decoder.h"
+#include "MockMediaDecoderOwner.h"
+#include "MockMediaResource.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+class MockMP4Decoder : public MP4Decoder
+{
+public:
+  MockMP4Decoder()
+    : MP4Decoder(new MockMediaDecoderOwner())
+  {}
+
+  // Avoid the assertion.
+  AbstractCanonical<media::NullableTimeUnit>* CanonicalDurationOrNull() override
+  {
+    return nullptr;
+  }
+};
+
+class MediaFormatReaderBinding
+{
+public:
+  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaFormatReaderBinding);
+  RefPtr<MockMP4Decoder> mDecoder;
+  RefPtr<MockMediaResource> mResource;
+  RefPtr<MediaDecoderReader> mReader;
+  RefPtr<TaskQueue> mTaskQueue;
+  explicit MediaFormatReaderBinding(const char* aFileName = "gizmo.mp4")
+    : mDecoder(new MockMP4Decoder())
+    , mResource(new MockMediaResource(aFileName))
+    , mReader(new MediaFormatReader(mDecoder,
+                                    new MP4Demuxer(mResource),
+                                    new VideoFrameContainer(
+                                      (HTMLMediaElement*)(0x1),
+                                      layers::LayerManager::CreateImageContainer(
+                                        layers::ImageContainer::ASYNCHRONOUS))
+                                    ))
+    , mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK)))
+  {
+  }
+
+  bool Init() {
+    // Init Resource.
+    nsresult rv = mResource->Open(nullptr);
+    if (NS_FAILED(rv)) {
+      return false;
+    }
+    mDecoder->SetResource(mResource);
+    // Init Reader.
+    rv = mReader->Init();
+    if (NS_FAILED(rv)) {
+      return false;
+    }
+    return true;
+  }
+
+  void OnMetadataReadAudio(MetadataHolder* aMetadata)
+  {
+    EXPECT_TRUE(aMetadata);
+    mReader->RequestAudioData()
+      ->Then(mReader->OwnerThread(), __func__, this,
+             &MediaFormatReaderBinding::OnAudioRawDataDemuxed,
+             &MediaFormatReaderBinding::OnNotDemuxed);
+  }
+
+  void OnMetadataReadVideo(MetadataHolder* aMetadata)
+  {
+    EXPECT_TRUE(aMetadata);
+    mReader->RequestVideoData(true, 0)
+      ->Then(mReader->OwnerThread(), __func__, this,
+             &MediaFormatReaderBinding::OnVideoRawDataDemuxed,
+             &MediaFormatReaderBinding::OnNotDemuxed);
+  }
+
+  void OnMetadataNotRead(ReadMetadataFailureReason aReason) {
+    EXPECT_TRUE(false);
+    ReaderShutdown();
+  }
+
+  void OnAudioRawDataDemuxed(MediaData* aAudioSample)
+  {
+    EXPECT_TRUE(aAudioSample);
+    EXPECT_EQ(MediaData::RAW_DATA, aAudioSample->mType);
+    ReaderShutdown();
+  }
+
+  void OnVideoRawDataDemuxed(MediaData* aVideoSample)
+  {
+    EXPECT_TRUE(aVideoSample);
+    EXPECT_EQ(MediaData::RAW_DATA, aVideoSample->mType);
+    ReaderShutdown();
+  }
+
+  void OnNotDemuxed(MediaDecoderReader::NotDecodedReason aReason)
+  {
+    EXPECT_TRUE(false);
+    ReaderShutdown();
+  }
+
+  void ReaderShutdown()
+  {
+    RefPtr<MediaFormatReaderBinding> self = this;
+    mReader->Shutdown()
+      ->Then(mTaskQueue, __func__,
+             [self]() {
+               self->mTaskQueue->BeginShutdown();
+             },
+             [self]() {
+               EXPECT_TRUE(false);
+               self->mTaskQueue->BeginShutdown();
+             }); //Then
+  }
+  template<class Function>
+  void RunTestAndWait(Function&& aFunction)
+  {
+    RefPtr<nsRunnable> r = NS_NewRunnableFunction(Forward<Function>(aFunction));
+    mTaskQueue->Dispatch(r.forget());
+    mTaskQueue->AwaitShutdownAndIdle();
+  }
+private:
+  ~MediaFormatReaderBinding()
+  {}
+};
+
+
+template <typename T>
+T GetPref(const char* aPrefKey);
+
+template <>
+bool GetPref<bool>(const char* aPrefKey)
+{
+  return Preferences::GetBool(aPrefKey);
+}
+
+template <typename T>
+void SetPref(const char* a, T value);
+
+template <>
+void SetPref<bool>(const char* aPrefKey, bool aValue)
+{
+  unused << Preferences::SetBool(aPrefKey, aValue);
+}
+
+template <typename T>
+class PreferencesRAII
+{
+public:
+  explicit PreferencesRAII(const char* aPrefKey, T aValue)
+  : mPrefKey(aPrefKey)
+  {
+    mDefaultPref = GetPref<T>(aPrefKey);
+    SetPref(aPrefKey, aValue);
+  }
+  ~PreferencesRAII()
+  {
+    SetPref(mPrefKey, mDefaultPref);
+  }
+private:
+  T mDefaultPref;
+  const char* mPrefKey;
+};
+
+TEST(MediaFormatReader, RequestAudioRawData)
+{
+  PreferencesRAII<bool> pref =
+    PreferencesRAII<bool>("media.use-blank-decoder",
+                          true);
+  RefPtr<MediaFormatReaderBinding> b = new MediaFormatReaderBinding();
+  if (!b->Init())
+  {
+    EXPECT_TRUE(false);
+    // Stop the test since initialization failed.
+    return;
+  }
+  if (!b->mReader->IsDemuxOnlySupported())
+  {
+    EXPECT_TRUE(false);
+    // Stop the test since the reader cannot support demuxed-only demand.
+    return;
+  }
+  // Switch to demuxed-only mode.
+  b->mReader->SetDemuxOnly(true);
+  // To ensure the MediaDecoderReader::InitializationTask and
+  // MediaDecoderReader::SetDemuxOnly can be done.
+  NS_ProcessNextEvent();
+  auto testCase = [b]() {
+    InvokeAsync(b->mReader->OwnerThread(),
+                b->mReader.get(),
+                __func__,
+                &MediaDecoderReader::AsyncReadMetadata)
+      ->Then(b->mReader->OwnerThread(), __func__, b.get(),
+             &MediaFormatReaderBinding::OnMetadataReadAudio,
+             &MediaFormatReaderBinding::OnMetadataNotRead);
+  };
+  b->RunTestAndWait(testCase);
+}
+TEST(MediaFormatReader, RequestVideoRawData)
+{
+  PreferencesRAII<bool> pref =
+    PreferencesRAII<bool>("media.use-blank-decoder",
+                          true);
+  RefPtr<MediaFormatReaderBinding> b = new MediaFormatReaderBinding();
+  if (!b->Init())
+  {
+    EXPECT_TRUE(false);
+    // Stop the test since initialization failed.
+    return;
+  }
+  if (!b->mReader->IsDemuxOnlySupported())
+  {
+    EXPECT_TRUE(false);
+    // Stop the test since the reader cannot support demuxed-only demand.
+    return;
+  }
+  // Switch to demuxed-only mode.
+  b->mReader->SetDemuxOnly(true);
+  // To ensure the MediaDecoderReader::InitializationTask and
+  // MediaDecoderReader::SetDemuxOnly can be done.
+  NS_ProcessNextEvent();
+  auto testCase = [b]() {
+    InvokeAsync(b->mReader->OwnerThread(),
+                b->mReader.get(),
+                __func__,
+                &MediaDecoderReader::AsyncReadMetadata)
+      ->Then(b->mReader->OwnerThread(), __func__, b.get(),
+             &MediaFormatReaderBinding::OnMetadataReadVideo,
+             &MediaFormatReaderBinding::OnMetadataNotRead);
+  };
+  b->RunTestAndWait(testCase);
+}
\ No newline at end of file
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -7,16 +7,17 @@
 UNIFIED_SOURCES += [
     'MockMediaResource.cpp',
     'TestAudioCompactor.cpp',
     'TestGMPCrossOrigin.cpp',
     'TestGMPRemoveAndDelete.cpp',
     'TestGMPUtils.cpp',
     'TestIntervalSet.cpp',
     'TestMediaEventSource.cpp',
+    'TestMediaFormatReader.cpp',
     'TestMozPromise.cpp',
     'TestMP3Demuxer.cpp',
     'TestMP4Demuxer.cpp',
     # 'TestMP4Reader.cpp', disabled so we can turn check tests back on (bug 1175752)
     'TestTrackEncoder.cpp',
     'TestVideoSegment.cpp',
     'TestWebMBuffered.cpp',
 ]
--- a/dom/media/tests/mochitest/test_peerConnection_certificates.html
+++ b/dom/media/tests/mochitest/test_peerConnection_certificates.html
@@ -102,25 +102,25 @@
     var expiredCert;
     return Promise.resolve()
       .then(() => RTCPeerConnection.generateCertificate({
         name: "ECDSA",
         namedCurve: "P-256",
         expires: 1 // smallest possible expiration window
       }))
       .then(cert => {
-        ok(cert.expires instanceof Date, 'cert has expiration time');
-        info('Expires at ' + cert.expires);
+        ok(!isNaN(cert.expires), 'cert has expiration time');
+        info('Expires at ' + new Date(cert.expires));
         expiredCert = cert;
       })
 
       .then(() => checkBadParameters())
 
       .then(() => {
-        var delay = expiredCert.expires.getTime() - Date.now();
+        var delay = expiredCert.expires - Date.now();
         // Hopefully this delay is never needed.
         if (delay > 0) {
           return new Promise(r => setTimeout(r, delay));
         }
       })
       .then(() => {
         ok(expiredCert.expires <= Date.now(), 'Cert should be at or past expiration');
         try {
--- a/dom/media/webrtc/RTCCertificate.h
+++ b/dom/media/webrtc/RTCCertificate.h
@@ -13,20 +13,18 @@
 #include "nsNSSShutDown.h"
 #include "prtime.h"
 #include "sslt.h"
 #include "ScopedNSSTypes.h"
 
 #include "mozilla/ErrorResult.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/RefPtr.h"
-#include "mozilla/dom/Date.h"
 #include "mozilla/dom/CryptoKey.h"
 #include "mtransport/dtlsidentity.h"
-#include "js/Date.h"
 #include "js/StructuredClone.h"
 #include "js/TypeDecls.h"
 
 namespace mozilla {
 namespace dom {
 
 class ObjectOrString;
 
@@ -50,19 +48,19 @@ public:
                  PRTime aExpires);
 
   nsIGlobalObject* GetParentObject() const { return mGlobal; }
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL expires attribute.  Note: JS dates are milliseconds since epoch;
   // NSPR PRTime is in microseconds since the same epoch.
-  JS::ClippedTime Expires() const
+  uint64_t Expires() const
   {
-    return JS::TimeClip(mExpires / PR_USEC_PER_MSEC);
+    return mExpires / PR_USEC_PER_MSEC;
   }
 
   // Accessors for use by PeerConnectionImpl.
   RefPtr<DtlsIdentity> CreateDtlsIdentity() const;
   CERTCertificate* Certificate() const { return mCertificate; }
 
   // For nsNSSShutDownObject
   virtual void virtualDestroyNSSReference() override;
--- a/dom/presentation/provider/MulticastDNSDeviceProvider.cpp
+++ b/dom/presentation/provider/MulticastDNSDeviceProvider.cpp
@@ -611,21 +611,16 @@ MulticastDNSDeviceProvider::OnServiceFou
 
   nsAutoCString serviceName;
   if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(serviceName)))) {
     return rv;
   }
 
   LOG_I("OnServiceFound: %s", serviceName.get());
 
-  if (mRegisteredName == serviceName) {
-    LOG_I("ignore self");
-    return NS_OK;
-  }
-
   if (mMulticastDNS) {
     if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->ResolveService(
         aServiceInfo, mWrappedListener)))) {
       return rv;
     }
   }
 
   return NS_OK;
@@ -701,18 +696,21 @@ MulticastDNSDeviceProvider::OnServiceReg
   nsAutoCString name;
   if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetServiceName(name)))) {
     return rv;
   }
 
   LOG_I("OnServiceRegistered (%s)",  name.get());
   mRegisteredName = name;
 
-  if (NS_WARN_IF(NS_FAILED(rv = mPresentationServer->SetId(name)))) {
-    return rv;
+  if (mMulticastDNS) {
+    if (NS_WARN_IF(NS_FAILED(rv = mMulticastDNS->ResolveService(
+        aServiceInfo, mWrappedListener)))) {
+      return rv;
+    }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MulticastDNSDeviceProvider::OnServiceUnregistered(nsIDNSServiceInfo* aServiceInfo)
 {
@@ -771,16 +769,26 @@ MulticastDNSDeviceProvider::OnServiceRes
 
   LOG_I("OnServiceResolved: %s", serviceName.get());
 
   nsAutoCString host;
   if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetHost(host)))) {
     return rv;
   }
 
+  if (mRegisteredName == serviceName) {
+    LOG_I("ignore self");
+
+    if (NS_WARN_IF(NS_FAILED(rv = mPresentationServer->SetId(host)))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+
   nsAutoCString address;
   if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetAddress(address)))) {
     return rv;
   }
 
   uint16_t port;
   if (NS_WARN_IF(NS_FAILED(rv = aServiceInfo->GetPort(&port)))) {
     return rv;
--- a/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js
+++ b/dom/presentation/tests/xpcshell/test_multicast_dns_device_provider.js
@@ -629,16 +629,84 @@ function noAddDevice() {
   };
   provider.listener = listener;
   provider.forceDiscovery();
   provider.listener = null;
 
   run_next_test();
 }
 
+function ignoreSelfDevice() {
+  Services.prefs.setBoolPref(PREF_DISCOVERY, false);
+  Services.prefs.setBoolPref(PREF_DISCOVERABLE, true);
+
+  let mockDevice = createDevice("device.local",
+                                12345,
+                                "service.name",
+                                "_mozilla_papi._tcp");
+  let mockSDObj = {
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsIDNSServiceDiscovery]),
+    startDiscovery: function(serviceType, listener) {
+      listener.onDiscoveryStarted(serviceType);
+      listener.onServiceFound(createDevice("",
+                                           0,
+                                           mockDevice.serviceName,
+                                           mockDevice.serviceType));
+      return {
+        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        cancel: function() {}
+      };
+    },
+    registerService: function(serviceInfo, listener) {
+      listener.onServiceRegistered(createDevice("",
+                                                0,
+                                                mockDevice.serviceName,
+                                                mockDevice.serviceType));
+      return {
+        QueryInterface: XPCOMUtils.generateQI([Ci.nsICancelable]),
+        cancel: function() {}
+      };
+    },
+    resolveService: function(serviceInfo, listener) {
+      Assert.equal(serviceInfo.serviceName, mockDevice.serviceName);
+      Assert.equal(serviceInfo.serviceType, mockDevice.serviceType);
+      listener.onServiceResolved(createDevice(mockDevice.host,
+                                              mockDevice.port,
+                                              mockDevice.serviceName,
+                                              mockDevice.serviceType));
+    }
+  };
+
+  let mockServerObj = {
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsITCPPresentationServer]),
+    init: function() {},
+    sessionRequest: function() {},
+    close: function() {},
+    id: '',
+    port: 0,
+    listener: null,
+  };
+
+  let contractHookSD = new ContractHook(SD_CONTRACT_ID, mockSDObj);
+  let contractHookServer = new ContractHook(SERVER_CONTRACT_ID, mockServerObj);
+  let provider = Cc[PROVIDER_CONTRACT_ID].createInstance(Ci.nsIPresentationDeviceProvider);
+  let listener = new TestPresentationDeviceListener();
+
+  // Register service
+  provider.listener = listener;
+  Assert.equal(mockServerObj.id, mockDevice.host);
+
+  // Start discovery
+  Services.prefs.setBoolPref(PREF_DISCOVERY, true);
+  Assert.equal(listener.count(), 0);
+
+  provider.listener = null;
+
+  run_next_test();
+}
 function addDeviceDynamically() {
   Services.prefs.setBoolPref(PREF_DISCOVERY, false);
 
   let mockDevice = createDevice("device.local",
                                 12345,
                                 "service.name",
                                 "_mozilla_papi._tcp");
   let mockObj = {
@@ -949,15 +1017,16 @@ function run_test() {
   add_test(registerService);
   add_test(noRegisterService);
   add_test(registerServiceDynamically);
   add_test(addDevice);
   add_test(handleSessionRequest);
   add_test(handleOnSessionRequest);
   add_test(handleOnSessionRequestFromUnknownDevice);
   add_test(noAddDevice);
+  add_test(ignoreSelfDevice);
   add_test(addDeviceDynamically);
   add_test(updateDevice);
   add_test(diffDiscovery);
   add_test(serverClosed);
 
   run_next_test();
 }
--- a/dom/webidl/RTCCertificate.webidl
+++ b/dom/webidl/RTCCertificate.webidl
@@ -3,10 +3,10 @@
  * 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/.
  *
  * Specification: http://w3c.github.io/webrtc-pc/#certificate-management
  */
 
 [Pref="media.peerconnection.enabled"]
 interface RTCCertificate {
-  readonly attribute Date expires;
+  readonly attribute DOMTimeStamp expires;
 };
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -911,16 +911,33 @@ XULDocument::AttributeWillChange(nsIDocu
     // See if we need to update our ref map.
     if (aAttribute == nsGkAtoms::ref) {
         // Might not need this, but be safe for now.
         nsCOMPtr<nsIMutationObserver> kungFuDeathGrip(this);
         RemoveElementFromRefMap(aElement);
     }
 }
 
+static bool
+ShouldPersistAttribute(Element* aElement, nsIAtom* aAttribute)
+{
+    if (aElement->IsXULElement(nsGkAtoms::window)) {
+        // The following attributes of xul:window should be handled in
+        // nsXULWindow::SavePersistentAttributes instead of here.
+        if (aAttribute == nsGkAtoms::screenX ||
+            aAttribute == nsGkAtoms::screenY ||
+            aAttribute == nsGkAtoms::width ||
+            aAttribute == nsGkAtoms::height ||
+            aAttribute == nsGkAtoms::sizemode) {
+            return false;
+        }
+    }
+    return true;
+}
+
 void
 XULDocument::AttributeChanged(nsIDocument* aDocument,
                               Element* aElement, int32_t aNameSpaceID,
                               nsIAtom* aAttribute, int32_t aModType,
                               const nsAttrValue* aOldValue)
 {
     NS_ASSERTION(aDocument == this, "unexpected doc");
 
@@ -990,24 +1007,24 @@ XULDocument::AttributeChanged(nsIDocumen
     bool listener, resolved;
     CheckBroadcasterHookup(aElement, &listener, &resolved);
 
     // See if there is anything we need to persist in the localstore.
     //
     // XXX Namespace handling broken :-(
     nsAutoString persist;
     aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
-    if (!persist.IsEmpty()) {
+    // Persistence of attributes of xul:window is handled in nsXULWindow.
+    if (ShouldPersistAttribute(aElement, aAttribute) && !persist.IsEmpty() &&
         // XXXldb This should check that it's a token, not just a substring.
-        if (persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
-            nsContentUtils::AddScriptRunner(NS_NewRunnableMethodWithArgs
-              <nsIContent*, int32_t, nsIAtom*>
-              (this, &XULDocument::DoPersist, aElement, kNameSpaceID_None,
-               aAttribute));
-        }
+        persist.Find(nsDependentAtomString(aAttribute)) >= 0) {
+        nsContentUtils::AddScriptRunner(NS_NewRunnableMethodWithArgs
+            <nsIContent*, int32_t, nsIAtom*>
+            (this, &XULDocument::DoPersist, aElement, kNameSpaceID_None,
+            aAttribute));
     }
 }
 
 void
 XULDocument::ContentAppended(nsIDocument* aDocument,
                              nsIContent* aContainer,
                              nsIContent* aFirstNewContent,
                              int32_t aNewIndexInContainer)
--- a/extensions/spellcheck/hunspell/glue/mozHunspell.cpp
+++ b/extensions/spellcheck/hunspell/glue/mozHunspell.cpp
@@ -268,63 +268,42 @@ NS_IMETHODIMP mozHunspell::GetPersonalDi
 }
 
 NS_IMETHODIMP mozHunspell::SetPersonalDictionary(mozIPersonalDictionary * aPersonalDictionary)
 {
   mPersonalDictionary = aPersonalDictionary;
   return NS_OK;
 }
 
-struct AppendNewStruct
-{
-  char16_t **dics;
-  uint32_t count;
-  bool failed;
-};
-
-static PLDHashOperator
-AppendNewString(const nsAString& aString, nsIFile* aFile, void* aClosure)
-{
-  AppendNewStruct *ans = (AppendNewStruct*) aClosure;
-  ans->dics[ans->count] = ToNewUnicode(aString);
-  if (!ans->dics[ans->count]) {
-    ans->failed = true;
-    return PL_DHASH_STOP;
-  }
-
-  ++ans->count;
-  return PL_DHASH_NEXT;
-}
-
 NS_IMETHODIMP mozHunspell::GetDictionaryList(char16_t ***aDictionaries,
                                             uint32_t *aCount)
 {
   if (!aDictionaries || !aCount)
     return NS_ERROR_NULL_POINTER;
 
-  AppendNewStruct ans = {
-    (char16_t**) moz_xmalloc(sizeof(char16_t*) * mDictionaries.Count()),
-    0,
-    false
-  };
+  uint32_t count = 0;
+  char16_t** dicts =
+    (char16_t**) moz_xmalloc(sizeof(char16_t*) * mDictionaries.Count());
 
-  // This pointer is used during enumeration
-  mDictionaries.EnumerateRead(AppendNewString, &ans);
+  for (auto iter = mDictionaries.Iter(); !iter.Done(); iter.Next()) {
+    dicts[count] = ToNewUnicode(iter.Key());
+    if (!dicts[count]) {
+      while (count) {
+        --count;
+        free(dicts[count]);
+      }
+      free(dicts);
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
 
-  if (ans.failed) {
-    while (ans.count) {
-      --ans.count;
-      free(ans.dics[ans.count]);
-    }
-    free(ans.dics);
-    return NS_ERROR_OUT_OF_MEMORY;
+    ++count;
   }
 
-  *aDictionaries = ans.dics;
-  *aCount = ans.count;
+  *aDictionaries = dicts;
+  *aCount = count;
 
   return NS_OK;
 }
 
 void
 mozHunspell::LoadDictionaryList(bool aNotifyChildProcesses)
 {
   mDictionaries.Clear();
--- a/gfx/cairo/README
+++ b/gfx/cairo/README
@@ -207,16 +207,18 @@ fix-win32-font-assertion.patch: Bug 8386
 xlib-flush-glyphs.patch: bug 839745, flush glyphs when necessary
 
 dasharray-zero-gap.patch: bug 885585, ensure strokes get painted when the gaps in a dash array are all zero length
 
 cairo-mask-extends-bug.patch: bug 918671, sometimes when building a mask we wouldn't clear it properly. This is fixed in cairo 1.12
 
 ft-no-subpixel-if-surface-disables.patch: bug 929451, don't use subpixel aa for ft fonts on surfaces that don't support it
 
+win32-printing-axis-swap.patch: bug 1205854, workaround for Windows printer drivers that can't handle swapped X and Y axes
+
 ==== pixman patches ====
 
 pixman-android-cpu-detect.patch: Add CPU detection support for Android, where we can't reliably access /proc/self/auxv.
 
 pixman-rename-and-endian.patch: include cairo-platform.h for renaming of external symbols and endian macros
 
 NOTE: we previously supported ARM assembler on MSVC, this has been removed because of the maintenance burden
 
--- a/gfx/cairo/cairo/src/cairo-matrix.c
+++ b/gfx/cairo/cairo/src/cairo-matrix.c
@@ -873,42 +873,56 @@ cairo_bool_t
   (Note that the minor axis length is at the minimum of the above solution,
   which is just sqrt ( f - sqrt(g² + h²) ) given the symmetry of (D)).
 
 
   For another derivation of the same result, using Singular Value Decomposition,
   see doc/tutorial/src/singular.c.
 */
 
-/* determine the length of the major axis of a circle of the given radius
-   after applying the transformation matrix. */
-double
-_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
-					     double radius)
+/* determine the length of the major and minor axes of a circle of the given
+   radius after applying the transformation matrix. */
+void
+_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
+				       double radius,
+				       double *major,
+				       double *minor)
 {
-    double  a, b, c, d, f, g, h, i, j;
+    double  a, b, c, d, f, g, h, i, j, k;
 
     _cairo_matrix_get_affine (matrix,
                               &a, &b,
                               &c, &d,
                               NULL, NULL);
 
     i = a*a + b*b;
     j = c*c + d*d;
+    k = a*c + b*d;
 
     f = 0.5 * (i + j);
     g = 0.5 * (i - j);
-    h = a*c + b*d;
+    h = hypot (g, k);
 
-    return radius * sqrt (f + hypot (g, h));
+    if (major)
+	*major = radius * sqrt (f + h);
+    if (minor)
+	*minor = radius * sqrt (f - h);
+}
 
-    /*
-     * we don't need the minor axis length, which is
-     * double min = radius * sqrt (f - sqrt (g*g+h*h));
-     */
+/* determine the length of the major axis of a circle of the given radius
+   after applying the transformation matrix. */
+double
+_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+					     double radius)
+{
+    double major;
+
+    _cairo_matrix_transformed_circle_axes (matrix, radius, &major, NULL);
+
+    return major;
 }
 
 void
 _cairo_matrix_to_pixman_matrix (const cairo_matrix_t	*matrix,
 				pixman_transform_t	*pixman_transform,
 				double xc,
 				double yc)
 {
--- a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+++ b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
@@ -610,16 +610,17 @@ static cairo_status_t
     int x_tile, y_tile, left, right, top, bottom;
     RECT clip;
     const cairo_color_t *background_color;
     const unsigned char *mime_data;
     unsigned long mime_size;
     cairo_image_info_t mime_info;
     cairo_bool_t use_mime;
     DWORD mime_type;
+    cairo_bool_t axis_swap;
 
     /* If we can't use StretchDIBits with this surface, we can't do anything
      * here.
      */
     if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
 	return CAIRO_INT_STATUS_UNSUPPORTED;
 
     if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
@@ -658,39 +659,65 @@ static cairo_status_t
 							  &mime_size,
 							  &mime_info);
     }
     if (_cairo_status_is_error (status))
 	return status;
 
     use_mime = (status == CAIRO_STATUS_SUCCESS);
 
-    if (!use_mime && image->format != CAIRO_FORMAT_RGB24) {
+    m = pattern->base.matrix;
+    status = cairo_matrix_invert (&m);
+    /* _cairo_pattern_set_matrix guarantees invertibility */
+    assert (status == CAIRO_STATUS_SUCCESS);
+    cairo_matrix_multiply (&m, &m, &surface->ctm);
+    cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
+    /* Check if the matrix swaps the X and Y axes by checking if the diagonal
+     * is effectively zero. This can happen, for example, if it was composed
+     * with a rotation such as a landscape transform. Some printing devices
+     * don't support such transforms in StretchDIBits.
+     */
+    axis_swap = fabs (m.xx*image->width) < 1 && fabs (m.yy*image->height) < 1;
+
+    if (!use_mime && (image->format != CAIRO_FORMAT_RGB24 || axis_swap)) {
 	cairo_surface_t *opaque_surface;
 	cairo_surface_pattern_t image_pattern;
 	cairo_solid_pattern_t background_pattern;
+	int width = image->width, height = image->height;
 
+	if (axis_swap) {
+	    width = image->height;
+	    height = image->width;
+	}
 	opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
-						     image->width,
-						     image->height);
+						     width,
+						     height);
 	if (opaque_surface->status) {
 	    status = opaque_surface->status;
 	    goto CLEANUP_OPAQUE_IMAGE;
 	}
 
-	_cairo_pattern_init_solid (&background_pattern,
-				   background_color);
-	status = _cairo_surface_paint (opaque_surface,
-				       CAIRO_OPERATOR_SOURCE,
-				       &background_pattern.base,
-				       NULL);
-	if (status)
-	    goto CLEANUP_OPAQUE_IMAGE;
+	if (image->format != CAIRO_FORMAT_RGB24) {
+	    _cairo_pattern_init_solid (&background_pattern,
+				       background_color);
+	    status = _cairo_surface_paint (opaque_surface,
+					   CAIRO_OPERATOR_SOURCE,
+					   &background_pattern.base,
+					   NULL);
+	    if (status)
+		goto CLEANUP_OPAQUE_IMAGE;
+	}
 
 	_cairo_pattern_init_for_surface (&image_pattern, &image->base);
+	if (axis_swap) {
+	    /* swap the X and Y axes to undo the axis swap in the matrix */
+	    cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
+	    cairo_pattern_set_matrix (&image_pattern.base, &swap_xy);
+	    cairo_matrix_multiply (&m, &swap_xy, &m);
+	}
 	status = _cairo_surface_paint (opaque_surface,
 				       CAIRO_OPERATOR_OVER,
 				       &image_pattern.base,
 				       NULL);
 	_cairo_pattern_fini (&image_pattern.base);
 	if (status)
 	    goto CLEANUP_OPAQUE_IMAGE;
 
@@ -706,23 +733,16 @@ static cairo_status_t
     bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
     bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
     bi.bmiHeader.biPlanes = 1;
     bi.bmiHeader.biBitCount = 32;
     bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB;
     bi.bmiHeader.biClrUsed = 0;
     bi.bmiHeader.biClrImportant = 0;
 
-    m = pattern->base.matrix;
-    status = cairo_matrix_invert (&m);
-    /* _cairo_pattern_set_matrix guarantees invertibility */
-    assert (status == CAIRO_STATUS_SUCCESS);
-
-    cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
-    cairo_matrix_multiply(&m, &m, &surface->ctm);
     SaveDC (surface->dc);
     _cairo_matrix_to_win32_xform (&m, &xform);
 
     if (! SetWorldTransform (surface->dc, &xform)) {
 	status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern");
 	goto CLEANUP_OPAQUE_IMAGE;
     }
 
@@ -1260,16 +1280,17 @@ static cairo_int_status_t
     DWORD pen_width;
     DWORD *dash_array;
     HGDIOBJ obj;
     unsigned int i;
     cairo_solid_pattern_t clear;
     cairo_matrix_t mat;
     double scale;
     double scaled_width;
+    double major, minor;
 
     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
     if (status)
 	return status;
 
     if (op == CAIRO_OPERATOR_CLEAR) {
 	_cairo_win32_printing_surface_init_clear_color (surface, &clear);
 	source = (cairo_pattern_t*) &clear;
@@ -1350,22 +1371,40 @@ static cairo_int_status_t
     if (status)
 	return status;
 
     /*
      * Switch to user space to set line parameters
      */
     SaveDC (surface->dc);
 
-    _cairo_matrix_to_win32_xform (&mat, &xform);
-    xform.eDx = 0.0f;
-    xform.eDy = 0.0f;
+    /* Some printers don't handle transformed strokes. Avoid the transform
+     * if not required for the pen shape. Use the SVD here to find the major
+     * and minor scales then check if they differ by more than 1 device unit.
+     * If the difference is smaller, then just treat the scaling as uniform.
+     * This check ignores rotations as the pen shape is symmetric before
+     * transformation.
+     */
+    _cairo_matrix_transformed_circle_axes (&mat, scale, &major, &minor);
+    if (fabs (major - minor) > 1) {
+	/* Check if the matrix swaps the X and Y axes such that the diagonal
+	 * is nearly zero. This was observed to cause problems with XPS export.
+	 */
+	if (fabs (mat.xx) < 1e-6 && fabs (mat.yy) < 1e-6) {
+	    /* swap the X and Y axes to undo the axis swap in the matrix */
+	    cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
+	    cairo_matrix_multiply (&mat, &swap_xy, &mat);
+	}
+	_cairo_matrix_to_win32_xform (&mat, &xform);
+	xform.eDx = 0.0f;
+	xform.eDy = 0.0f;
 
-    if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
-	return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
+	if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
+	    return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
+    }
 
     if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
 	StrokePath (surface->dc);
     } else {
 	if (!WidenPath (surface->dc))
 	    return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath");
 	if (!SelectClipPath (surface->dc, RGN_AND))
 	    return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath");
--- a/gfx/cairo/cairo/src/cairoint.h
+++ b/gfx/cairo/cairo/src/cairoint.h
@@ -2115,16 +2115,22 @@ cairo_private cairo_bool_t
 				     int *itx, int *ity);
 
 cairo_private cairo_bool_t
 _cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix);
 
 cairo_private cairo_bool_t
 _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure;
 
+cairo_private void
+_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
+				       double radius,
+				       double *major,
+				       double *minor);
+
 cairo_private double
 _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
 					     double radius) cairo_pure;
 
 cairo_private void
 _cairo_matrix_to_pixman_matrix (const cairo_matrix_t	*matrix,
 				pixman_transform_t	*pixman_transform,
 				double                   xc,
new file mode 100644
--- /dev/null
+++ b/gfx/cairo/win32-printing-axis-swap.patch
@@ -0,0 +1,292 @@
+# HG changeset patch
+# User Lee Salzman <lsalzman@mozilla.com>
+# Date 1445463645 14400
+#      Wed Oct 21 17:40:45 2015 -0400
+# Node ID 9e84563cbd73c5b0993dfd018ca25b660b667e94
+# Parent  2d3fd51c4182c253a2f102655e8e9e466032853f
+workaround for Windows printer drivers that can't handle swapped X and Y axes
+
+diff --git a/gfx/cairo/cairo/src/cairo-matrix.c b/gfx/cairo/cairo/src/cairo-matrix.c
+--- a/gfx/cairo/cairo/src/cairo-matrix.c
++++ b/gfx/cairo/cairo/src/cairo-matrix.c
+@@ -873,42 +873,56 @@ cairo_bool_t
+   (Note that the minor axis length is at the minimum of the above solution,
+   which is just sqrt ( f - sqrt(g² + h²) ) given the symmetry of (D)).
+ 
+ 
+   For another derivation of the same result, using Singular Value Decomposition,
+   see doc/tutorial/src/singular.c.
+ */
+ 
+-/* determine the length of the major axis of a circle of the given radius
+-   after applying the transformation matrix. */
+-double
+-_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+-					     double radius)
++/* determine the length of the major and minor axes of a circle of the given
++   radius after applying the transformation matrix. */
++void
++_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
++				       double radius,
++				       double *major,
++				       double *minor)
+ {
+-    double  a, b, c, d, f, g, h, i, j;
++    double  a, b, c, d, f, g, h, i, j, k;
+ 
+     _cairo_matrix_get_affine (matrix,
+                               &a, &b,
+                               &c, &d,
+                               NULL, NULL);
+ 
+     i = a*a + b*b;
+     j = c*c + d*d;
++    k = a*c + b*d;
+ 
+     f = 0.5 * (i + j);
+     g = 0.5 * (i - j);
+-    h = a*c + b*d;
++    h = hypot (g, k);
+ 
+-    return radius * sqrt (f + hypot (g, h));
++    if (major)
++	*major = radius * sqrt (f + h);
++    if (minor)
++	*minor = radius * sqrt (f - h);
++}
+ 
+-    /*
+-     * we don't need the minor axis length, which is
+-     * double min = radius * sqrt (f - sqrt (g*g+h*h));
+-     */
++/* determine the length of the major axis of a circle of the given radius
++   after applying the transformation matrix. */
++double
++_cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
++					     double radius)
++{
++    double major;
++
++    _cairo_matrix_transformed_circle_axes (matrix, radius, &major, NULL);
++
++    return major;
+ }
+ 
+ void
+ _cairo_matrix_to_pixman_matrix (const cairo_matrix_t	*matrix,
+ 				pixman_transform_t	*pixman_transform,
+ 				double xc,
+ 				double yc)
+ {
+diff --git a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+--- a/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
++++ b/gfx/cairo/cairo/src/cairo-win32-printing-surface.c
+@@ -610,16 +610,17 @@ static cairo_status_t
+     int x_tile, y_tile, left, right, top, bottom;
+     RECT clip;
+     const cairo_color_t *background_color;
+     const unsigned char *mime_data;
+     unsigned long mime_size;
+     cairo_image_info_t mime_info;
+     cairo_bool_t use_mime;
+     DWORD mime_type;
++    cairo_bool_t axis_swap;
+ 
+     /* If we can't use StretchDIBits with this surface, we can't do anything
+      * here.
+      */
+     if (!(surface->flags & CAIRO_WIN32_SURFACE_CAN_STRETCHDIB))
+ 	return CAIRO_INT_STATUS_UNSUPPORTED;
+ 
+     if (surface->content == CAIRO_CONTENT_COLOR_ALPHA)
+@@ -658,39 +659,65 @@ static cairo_status_t
+ 							  &mime_size,
+ 							  &mime_info);
+     }
+     if (_cairo_status_is_error (status))
+ 	return status;
+ 
+     use_mime = (status == CAIRO_STATUS_SUCCESS);
+ 
+-    if (!use_mime && image->format != CAIRO_FORMAT_RGB24) {
++    m = pattern->base.matrix;
++    status = cairo_matrix_invert (&m);
++    /* _cairo_pattern_set_matrix guarantees invertibility */
++    assert (status == CAIRO_STATUS_SUCCESS);
++    cairo_matrix_multiply (&m, &m, &surface->ctm);
++    cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
++    /* Check if the matrix swaps the X and Y axes by checking if the diagonal
++     * is effectively zero. This can happen, for example, if it was composed
++     * with a rotation such as a landscape transform. Some printing devices
++     * don't support such transforms in StretchDIBits.
++     */
++    axis_swap = fabs (m.xx*image->width) < 1 && fabs (m.yy*image->height) < 1;
++
++    if (!use_mime && (image->format != CAIRO_FORMAT_RGB24 || axis_swap)) {
+ 	cairo_surface_t *opaque_surface;
+ 	cairo_surface_pattern_t image_pattern;
+ 	cairo_solid_pattern_t background_pattern;
++	int width = image->width, height = image->height;
+ 
++	if (axis_swap) {
++	    width = image->height;
++	    height = image->width;
++	}
+ 	opaque_surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24,
+-						     image->width,
+-						     image->height);
++						     width,
++						     height);
+ 	if (opaque_surface->status) {
+ 	    status = opaque_surface->status;
+ 	    goto CLEANUP_OPAQUE_IMAGE;
+ 	}
+ 
+-	_cairo_pattern_init_solid (&background_pattern,
+-				   background_color);
+-	status = _cairo_surface_paint (opaque_surface,
+-				       CAIRO_OPERATOR_SOURCE,
+-				       &background_pattern.base,
+-				       NULL);
+-	if (status)
+-	    goto CLEANUP_OPAQUE_IMAGE;
++	if (image->format != CAIRO_FORMAT_RGB24) {
++	    _cairo_pattern_init_solid (&background_pattern,
++				       background_color);
++	    status = _cairo_surface_paint (opaque_surface,
++					   CAIRO_OPERATOR_SOURCE,
++					   &background_pattern.base,
++					   NULL);
++	    if (status)
++		goto CLEANUP_OPAQUE_IMAGE;
++	}
+ 
+ 	_cairo_pattern_init_for_surface (&image_pattern, &image->base);
++	if (axis_swap) {
++	    /* swap the X and Y axes to undo the axis swap in the matrix */
++	    cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
++	    cairo_pattern_set_matrix (&image_pattern.base, &swap_xy);
++	    cairo_matrix_multiply (&m, &swap_xy, &m);
++	}
+ 	status = _cairo_surface_paint (opaque_surface,
+ 				       CAIRO_OPERATOR_OVER,
+ 				       &image_pattern.base,
+ 				       NULL);
+ 	_cairo_pattern_fini (&image_pattern.base);
+ 	if (status)
+ 	    goto CLEANUP_OPAQUE_IMAGE;
+ 
+@@ -706,23 +733,16 @@ static cairo_status_t
+     bi.bmiHeader.biXPelsPerMeter = PELS_72DPI;
+     bi.bmiHeader.biYPelsPerMeter = PELS_72DPI;
+     bi.bmiHeader.biPlanes = 1;
+     bi.bmiHeader.biBitCount = 32;
+     bi.bmiHeader.biCompression = use_mime ? mime_type : BI_RGB;
+     bi.bmiHeader.biClrUsed = 0;
+     bi.bmiHeader.biClrImportant = 0;
+ 
+-    m = pattern->base.matrix;
+-    status = cairo_matrix_invert (&m);
+-    /* _cairo_pattern_set_matrix guarantees invertibility */
+-    assert (status == CAIRO_STATUS_SUCCESS);
+-
+-    cairo_matrix_multiply (&m, &m, &surface->gdi_ctm);
+-    cairo_matrix_multiply(&m, &m, &surface->ctm);
+     SaveDC (surface->dc);
+     _cairo_matrix_to_win32_xform (&m, &xform);
+ 
+     if (! SetWorldTransform (surface->dc, &xform)) {
+ 	status = _cairo_win32_print_gdi_error ("_cairo_win32_printing_surface_paint_image_pattern");
+ 	goto CLEANUP_OPAQUE_IMAGE;
+     }
+ 
+@@ -1260,16 +1280,17 @@ static cairo_int_status_t
+     DWORD pen_width;
+     DWORD *dash_array;
+     HGDIOBJ obj;
+     unsigned int i;
+     cairo_solid_pattern_t clear;
+     cairo_matrix_t mat;
+     double scale;
+     double scaled_width;
++    double major, minor;
+ 
+     status = _cairo_surface_clipper_set_clip (&surface->clipper, clip);
+     if (status)
+ 	return status;
+ 
+     if (op == CAIRO_OPERATOR_CLEAR) {
+ 	_cairo_win32_printing_surface_init_clear_color (surface, &clear);
+ 	source = (cairo_pattern_t*) &clear;
+@@ -1350,22 +1371,40 @@ static cairo_int_status_t
+     if (status)
+ 	return status;
+ 
+     /*
+      * Switch to user space to set line parameters
+      */
+     SaveDC (surface->dc);
+ 
+-    _cairo_matrix_to_win32_xform (&mat, &xform);
+-    xform.eDx = 0.0f;
+-    xform.eDy = 0.0f;
++    /* Some printers don't handle transformed strokes. Avoid the transform
++     * if not required for the pen shape. Use the SVD here to find the major
++     * and minor scales then check if they differ by more than 1 device unit.
++     * If the difference is smaller, then just treat the scaling as uniform.
++     * This check ignores rotations as the pen shape is symmetric before
++     * transformation.
++     */
++    _cairo_matrix_transformed_circle_axes (&mat, scale, &major, &minor);
++    if (fabs (major - minor) > 1) {
++	/* Check if the matrix swaps the X and Y axes such that the diagonal
++	 * is nearly zero. This was observed to cause problems with XPS export.
++	 */
++	if (fabs (mat.xx) < 1e-6 && fabs (mat.yy) < 1e-6) {
++	    /* swap the X and Y axes to undo the axis swap in the matrix */
++	    cairo_matrix_t swap_xy = { 0, 1, 1, 0, 0, 0 };
++	    cairo_matrix_multiply (&mat, &swap_xy, &mat);
++	}
++	_cairo_matrix_to_win32_xform (&mat, &xform);
++	xform.eDx = 0.0f;
++	xform.eDy = 0.0f;
+ 
+-    if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
+-	return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
++	if (!ModifyWorldTransform (surface->dc, &xform, MWT_LEFTMULTIPLY))
++	    return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SetWorldTransform");
++    }
+ 
+     if (source->type == CAIRO_PATTERN_TYPE_SOLID) {
+ 	StrokePath (surface->dc);
+     } else {
+ 	if (!WidenPath (surface->dc))
+ 	    return _cairo_win32_print_gdi_error ("_win32_surface_stroke:WidenPath");
+ 	if (!SelectClipPath (surface->dc, RGN_AND))
+ 	    return _cairo_win32_print_gdi_error ("_win32_surface_stroke:SelectClipPath");
+diff --git a/gfx/cairo/cairo/src/cairoint.h b/gfx/cairo/cairo/src/cairoint.h
+--- a/gfx/cairo/cairo/src/cairoint.h
++++ b/gfx/cairo/cairo/src/cairoint.h
+@@ -2115,16 +2115,22 @@ cairo_private cairo_bool_t
+ 				     int *itx, int *ity);
+ 
+ cairo_private cairo_bool_t
+ _cairo_matrix_has_unity_scale (const cairo_matrix_t *matrix);
+ 
+ cairo_private cairo_bool_t
+ _cairo_matrix_is_pixel_exact (const cairo_matrix_t *matrix) cairo_pure;
+ 
++cairo_private void
++_cairo_matrix_transformed_circle_axes (const cairo_matrix_t *matrix,
++				       double radius,
++				       double *major,
++				       double *minor);
++
+ cairo_private double
+ _cairo_matrix_transformed_circle_major_axis (const cairo_matrix_t *matrix,
+ 					     double radius) cairo_pure;
+ 
+ cairo_private void
+ _cairo_matrix_to_pixman_matrix (const cairo_matrix_t	*matrix,
+ 				pixman_transform_t	*pixman_transform,
+ 				double                   xc,
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -129,23 +129,29 @@ void
 AsyncCompositionManager::ResolveRefLayers(CompositorParent* aCompositor,
                                           bool* aHasRemoteContent,
                                           bool* aResolvePlugins)
 {
   if (aHasRemoteContent) {
     *aHasRemoteContent = false;
   }
 
+  // If valid *aResolvePlugins indicates if we need to update plugin geometry
+  // when we walk the tree.
+  bool willResolvePlugins = (aResolvePlugins && *aResolvePlugins);
   if (!mLayerManager->GetRoot()) {
+    // Updated the return value since this result controls completing composition.
+    if (aResolvePlugins) {
+      *aResolvePlugins = false;
+    }
     return;
   }
 
   mReadyForCompose = true;
   bool hasRemoteContent = false;
-  bool willResolvePlugins = (aResolvePlugins && *aResolvePlugins);
   bool didResolvePlugins = false;
   WalkTheTree<Resolve>(mLayerManager->GetRoot(),
                        mReadyForCompose,
                        mTargetConfig,
                        aCompositor,
                        hasRemoteContent,
                        willResolvePlugins,
                        didResolvePlugins);
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -2132,17 +2132,23 @@ CompositorParent::UpdatePluginWindowStat
         }
         mPluginsLayerOffset = offset;
         mPluginsLayerVisibleRegion = visibleRegion;
         unused <<
           lts.mParent->SendUpdatePluginConfigurations(offset, visibleRegion,
                                                       lts.mPluginData);
         lts.mUpdatedPluginDataAvailable = false;
         PLUGINS_LOG("[%" PRIu64 "] updated", aId);
+      } else {
+        PLUGINS_LOG("[%" PRIu64 "] no visibility data", aId);
+        return false;
       }
+    } else {
+      PLUGINS_LOG("[%" PRIu64 "] no content root", aId);
+      return false;
     }
   }
 
   mLastPluginUpdateLayerTreeId = aId;
   mCachedPluginData = lts.mPluginData;
   return true;
 }
 #endif // #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -419,17 +419,18 @@ gfxASurface::CheckSurfaceSize(const IntS
 
     return true;
 }
 
 /* static */
 int32_t
 gfxASurface::FormatStrideForWidth(gfxImageFormat format, int32_t width)
 {
-    return cairo_format_stride_for_width((cairo_format_t)(int)format, (int)width);
+    cairo_format_t cformat = gfxImageFormatToCairoFormat(format);
+    return cairo_format_stride_for_width(cformat, (int)width);
 }
 
 nsresult
 gfxASurface::BeginPrinting(const nsAString& aTitle, const nsAString& aPrintToFileName)
 {
     return NS_OK;
 }
 
@@ -462,17 +463,16 @@ gfxASurface::ContentFromFormat(gfxImageF
 {
     switch (format) {
         case gfxImageFormat::ARGB32:
             return gfxContentType::COLOR_ALPHA;
         case gfxImageFormat::RGB24:
         case gfxImageFormat::RGB16_565:
             return gfxContentType::COLOR;
         case gfxImageFormat::A8:
-        case gfxImageFormat::A1:
             return gfxContentType::ALPHA;
 
         case gfxImageFormat::Unknown:
         default:
             return gfxContentType::COLOR;
     }
 }
 
@@ -669,18 +669,16 @@ gfxASurface::BytesPerPixel(gfxImageForma
     case gfxImageFormat::ARGB32:
       return 4;
     case gfxImageFormat::RGB24:
       return 4;
     case gfxImageFormat::RGB16_565:
       return 2;
     case gfxImageFormat::A8:
       return 1;
-    case gfxImageFormat::A1:
-      return 1; // Close enough
     case gfxImageFormat::Unknown:
     default:
       NS_NOTREACHED("Not really sure what you want me to say here");
       return 0;
   }
 }
 
 void
--- a/gfx/thebes/gfxBaseSharedMemorySurface.h
+++ b/gfx/thebes/gfxBaseSharedMemorySurface.h
@@ -72,18 +72,18 @@ public:
      */
     static already_AddRefed<Sub>
     Open(const Shmem& aShmem)
     {
         SharedImageInfo* shmInfo = GetShmInfoPtr(aShmem);
         mozilla::gfx::IntSize size(shmInfo->width, shmInfo->height);
         if (!gfxASurface::CheckSurfaceSize(size))
             return nullptr;
-       
-        gfxImageFormat format = (gfxImageFormat)shmInfo->format;
+
+        gfxImageFormat format = shmInfo->format;
         long stride = gfxImageSurface::ComputeStride(size, format);
 
         RefPtr<Sub> s =
             new Sub(size,
                     stride,
                     format,
                     aShmem);
         // We didn't create this Shmem and so don't free it on errors
--- a/gfx/thebes/gfxImageSurface.cpp
+++ b/gfx/thebes/gfxImageSurface.cpp
@@ -27,17 +27,17 @@ gfxImageSurface::gfxImageSurface()
 }
 
 void
 gfxImageSurface::InitFromSurface(cairo_surface_t *csurf)
 {
     mSize.width = cairo_image_surface_get_width(csurf);
     mSize.height = cairo_image_surface_get_height(csurf);
     mData = cairo_image_surface_get_data(csurf);
-    mFormat = (gfxImageFormat) cairo_image_surface_get_format(csurf);
+    mFormat = gfxCairoFormatToImageFormat(cairo_image_surface_get_format(csurf));
     mOwnsData = false;
     mStride = cairo_image_surface_get_stride(csurf);
 
     Init(csurf, true);
 }
 
 gfxImageSurface::gfxImageSurface(unsigned char *aData, const IntSize& aSize,
                                  long aStride, gfxImageFormat aFormat)
@@ -61,19 +61,20 @@ gfxImageSurface::InitWithData(unsigned c
     mOwnsData = false;
     mData = aData;
     mFormat = aFormat;
     mStride = aStride;
 
     if (!CheckSurfaceSize(aSize))
         MakeInvalid();
 
+    cairo_format_t cformat = gfxImageFormatToCairoFormat(mFormat);
     cairo_surface_t *surface =
         cairo_image_surface_create_for_data((unsigned char*)mData,
-                                            (cairo_format_t)(int)mFormat,
+                                            cformat,
                                             mSize.width,
                                             mSize.height,
                                             mStride);
 
     // cairo_image_surface_create_for_data can return a 'null' surface
     // in out of memory conditions. The gfxASurface::Init call checks
     // the surface it receives to see if there is an error with the
     // surface and handles it appropriately. That is why there is
@@ -130,19 +131,20 @@ gfxImageSurface::AllocateAndInit(long aS
         if (!mData)
             return;
         if (aClear)
             memset(mData, 0, aMinimalAllocation);
     }
 
     mOwnsData = true;
 
+    cairo_format_t cformat = gfxImageFormatToCairoFormat(mFormat);
     cairo_surface_t *surface =
         cairo_image_surface_create_for_data((unsigned char*)mData,
-                                            (cairo_format_t)(int)mFormat,
+                                            cformat,
                                             mSize.width,
                                             mSize.height,
                                             mStride);
 
     Init(surface);
 
     if (mSurfaceValid) {
         RecordMemoryUsed(mSize.height * ComputeStride() +
@@ -157,17 +159,17 @@ gfxImageSurface::gfxImageSurface(const I
     AllocateAndInit(aStride, aExtraBytes, aClear);
 }
 
 gfxImageSurface::gfxImageSurface(cairo_surface_t *csurf)
 {
     mSize.width = cairo_image_surface_get_width(csurf);
     mSize.height = cairo_image_surface_get_height(csurf);
     mData = cairo_image_surface_get_data(csurf);
-    mFormat = (gfxImageFormat) cairo_image_surface_get_format(csurf);
+    mFormat = gfxCairoFormatToImageFormat(cairo_image_surface_get_format(csurf));
     mOwnsData = false;
     mStride = cairo_image_surface_get_stride(csurf);
 
     Init(csurf, true);
 }
 
 gfxImageSurface::~gfxImageSurface()
 {
@@ -183,19 +185,17 @@ gfxImageSurface::ComputeStride(const Int
     if (aFormat == gfxImageFormat::ARGB32)
         stride = aSize.width * 4;
     else if (aFormat == gfxImageFormat::RGB24)
         stride = aSize.width * 4;
     else if (aFormat == gfxImageFormat::RGB16_565)
         stride = aSize.width * 2;
     else if (aFormat == gfxImageFormat::A8)
         stride = aSize.width;
-    else if (aFormat == gfxImageFormat::A1) {
-        stride = (aSize.width + 7) / 8;
-    } else {
+    else {
         NS_WARNING("Unknown format specified to gfxImageSurface!");
         stride = aSize.width * 4;
     }
 
     stride = ((stride + 3) / 4) * 4;
 
     return stride;
 }
--- a/gfx/thebes/gfxQPainterSurface.cpp
+++ b/gfx/thebes/gfxQPainterSurface.cpp
@@ -16,19 +16,19 @@ gfxQPainterSurface::gfxQPainterSurface(Q
 
     mPainter = painter;
 
     Init (csurf);
 }
 
 gfxQPainterSurface::gfxQPainterSurface(const mozilla::gfx::IntSize& size, gfxImageFormat format)
 {
-    cairo_surface_t *csurf = cairo_qt_surface_create_with_qimage ((cairo_format_t) format,
-                                                                        size.width,
-                                                                        size.height);
+    cairo_format_t cformat = gfxImageFormatToCairoFormat(format);
+    cairo_surface_t *csurf =
+        cairo_qt_surface_create_with_qimage(cformat, size.width, size.height);
     mPainter = cairo_qt_surface_get_qpainter (csurf);
 
     Init (csurf);
 }
 
 gfxQPainterSurface::gfxQPainterSurface(const mozilla::gfx::IntSize& size, gfxContentType content)
 {
     cairo_surface_t *csurf = cairo_qt_surface_create_with_qpixmap ((cairo_content_t) content,
--- a/gfx/thebes/gfxQuartzSurface.cpp
+++ b/gfx/thebes/gfxQuartzSurface.cpp
@@ -21,18 +21,18 @@ gfxQuartzSurface::gfxQuartzSurface(const
     mozilla::gfx::IntSize size((unsigned int) floor(desiredSize.width),
                     (unsigned int) floor(desiredSize.height));
     if (!CheckSurfaceSize(size))
         MakeInvalid();
 
     unsigned int width = static_cast<unsigned int>(mSize.width);
     unsigned int height = static_cast<unsigned int>(mSize.height);
 
-    cairo_surface_t *surf = cairo_quartz_surface_create
-        ((cairo_format_t) format, width, height);
+    cairo_format_t cformat = gfxImageFormatToCairoFormat(format);
+    cairo_surface_t *surf = cairo_quartz_surface_create(cformat, width, height);
 
     mCGContext = cairo_quartz_surface_get_cg_context (surf);
 
     CGContextRetain(mCGContext);
 
     Init(surf);
     if (mSurfaceValid) {
       RecordMemoryUsed(mSize.height * 4 + sizeof(gfxQuartzSurface));
@@ -104,18 +104,19 @@ gfxQuartzSurface::gfxQuartzSurface(unsig
     mozilla::gfx::IntSize size((unsigned int) floor(desiredSize.width),
                     (unsigned int) floor(desiredSize.height));
     if (!CheckSurfaceSize(size))
         MakeInvalid();
 
     unsigned int width = static_cast<unsigned int>(mSize.width);
     unsigned int height = static_cast<unsigned int>(mSize.height);
 
+    cairo_format_t cformat = gfxImageFormatToCairoFormat(format);
     cairo_surface_t *surf = cairo_quartz_surface_create_for_data
-        (data, (cairo_format_t) format, width, height, stride);
+        (data, cformat, width, height, stride);
 
     mCGContext = cairo_quartz_surface_get_cg_context (surf);
 
     CGContextRetain(mCGContext);
 
     Init(surf);
     if (mSurfaceValid) {
       RecordMemoryUsed(mSize.height * stride + sizeof(gfxQuartzSurface));
@@ -126,18 +127,19 @@ gfxQuartzSurface::gfxQuartzSurface(unsig
                                    const mozilla::gfx::IntSize& aSize,
                                    long stride,
                                    gfxImageFormat format)
     : mCGContext(nullptr), mSize(aSize.width, aSize.height)
 {
     if (!CheckSurfaceSize(aSize))
         MakeInvalid();
 
+    cairo_format_t cformat = gfxImageFormatToCairoFormat(format);
     cairo_surface_t *surf = cairo_quartz_surface_create_for_data
-        (data, (cairo_format_t) format, aSize.width, aSize.height, stride);
+        (data, cformat, aSize.width, aSize.height, stride);
 
     mCGContext = cairo_quartz_surface_get_cg_context (surf);
 
     CGContextRetain(mCGContext);
 
     Init(surf);
     if (mSurfaceValid) {
       RecordMemoryUsed(mSize.height * stride + sizeof(gfxQuartzSurface));
--- a/gfx/thebes/gfxTypes.h
+++ b/gfx/thebes/gfxTypes.h
@@ -47,21 +47,32 @@ enum class gfxBreakPriority {
 /**
   * The format for an image surface. For all formats with alpha data, 0
   * means transparent, 1 or 255 means fully opaque.
   */
 enum class gfxImageFormat {
   ARGB32, ///< ARGB data in native endianness, using premultiplied alpha
   RGB24,  ///< xRGB data in native endianness
   A8,     ///< Only an alpha channel
-  A1,     ///< Packed transparency information (one byte refers to 8 pixels)
   RGB16_565,  ///< RGB_565 data in native endianness
   Unknown
 };
 
+// XXX: temporary
+// This works because the gfxImageFormat enum is defined so as to match the
+// _cairo_format enum.
+#define gfxCairoFormatToImageFormat(aFormat) \
+    ((gfxImageFormat)aFormat)
+
+// XXX: temporary
+// This works because the gfxImageFormat enum is defined so as to match the
+// _cairo_format enum.
+#define gfxImageFormatToCairoFormat(aFormat) \
+    ((cairo_format_t)aFormat)
+
 enum class gfxSurfaceType {
   Image,
   PDF,
   PS,
   Xlib,
   Xcb,
   Glitz,           // unused, but needed for cairo parity
   Quartz,
--- a/gfx/thebes/gfxWindowsSurface.cpp
+++ b/gfx/thebes/gfxWindowsSurface.cpp
@@ -54,18 +54,19 @@ gfxWindowsSurface::MakeInvalid(mozilla::
 
 gfxWindowsSurface::gfxWindowsSurface(const mozilla::gfx::IntSize& realSize, gfxImageFormat imageFormat) :
     mOwnsDC(false), mForPrinting(false), mWnd(nullptr)
 {
     mozilla::gfx::IntSize size(realSize);
     if (!CheckSurfaceSize(size))
         MakeInvalid(size);
 
-    cairo_surface_t *surf = cairo_win32_surface_create_with_dib((cairo_format_t)(int)imageFormat,
-                                                                size.width, size.height);
+    cairo_format_t cformat = gfxImageFormatToCairoFormat(imageFormat);
+    cairo_surface_t *surf =
+        cairo_win32_surface_create_with_dib(cformat, size.width, size.height);
 
     Init(surf);
 
     if (CairoStatus() == CAIRO_STATUS_SUCCESS) {
         mDC = cairo_win32_surface_get_dc(CairoSurface());
         RecordMemoryUsed(size.width * size.height * 4 + sizeof(gfxWindowsSurface));
     } else {
         mDC = nullptr;
@@ -74,18 +75,20 @@ gfxWindowsSurface::gfxWindowsSurface(con
 
 gfxWindowsSurface::gfxWindowsSurface(HDC dc, const mozilla::gfx::IntSize& realSize, gfxImageFormat imageFormat) :
     mOwnsDC(false), mForPrinting(false), mWnd(nullptr)
 {
     mozilla::gfx::IntSize size(realSize);
     if (!CheckSurfaceSize(size))
         MakeInvalid(size);
 
-    cairo_surface_t *surf = cairo_win32_surface_create_with_ddb(dc, (cairo_format_t)(int)imageFormat,
-                                                                size.width, size.height);
+    cairo_format_t cformat = gfxImageFormatToCairoFormat(imageFormat);
+    cairo_surface_t *surf =
+        cairo_win32_surface_create_with_ddb(dc, cformat,
+                                            size.width, size.height);
 
     Init(surf);
 
     if (mSurfaceValid) {
         // DDBs will generally only use 3 bytes per pixel when RGB24
         int bytesPerPixel = ((imageFormat == gfxImageFormat::RGB24) ? 3 : 4);
         RecordMemoryUsed(size.width * size.height * bytesPerPixel + sizeof(gfxWindowsSurface));
     }
@@ -133,19 +136,21 @@ gfxWindowsSurface::CreateSimilarSurface(
         // When creating a similar surface to a transparent surface, ensure
         // the new surface uses a DIB. cairo_surface_create_similar won't
         // use  a DIB for a gfxContentType::COLOR surface if this surface doesn't
         // have a DIB (e.g. if we're a transparent window surface). But
         // we need a DIB to perform well if the new surface is composited into
         // a surface that's the result of create_similar(gfxContentType::COLOR_ALPHA)
         // (e.g. a backbuffer for the window) --- that new surface *would*
         // have a DIB.
-        surface =
-          cairo_win32_surface_create_with_dib((cairo_format_t)(int)gfxPlatform::GetPlatform()->OptimalFormatForContent(aContent),
-                                              aSize.width, aSize.height);
+        gfxImageFormat gformat =
+            gfxPlatform::GetPlatform()->OptimalFormatForContent(aContent);
+        cairo_format_t cformat = gfxImageFormatToCairoFormat(gformat);
+        surface = cairo_win32_surface_create_with_dib(cformat, aSize.width,
+                                                      aSize.height);
     } else {
         surface =
           cairo_surface_create_similar(mSurface, (cairo_content_t)(int)aContent,
                                        aSize.width, aSize.height);
     }
 
     if (cairo_surface_status(surface)) {
         cairo_surface_destroy(surface);
--- a/gfx/thebes/gfxXlibNativeRenderer.cpp
+++ b/gfx/thebes/gfxXlibNativeRenderer.cpp
@@ -347,17 +347,17 @@ CreateTempXlibSurface (cairo_surface_t* 
         Visual *target_visual = nullptr;
         XRenderPictFormat *target_format = nullptr;
         if (cairoTargetType == CAIRO_SURFACE_TYPE_XLIB) {
             target_visual = cairo_xlib_surface_get_visual (cairoTarget);
             target_format = cairo_xlib_surface_get_xrender_format (cairoTarget);
         } else if (cairoTargetType == CAIRO_SURFACE_TYPE_IMAGE || drawTarget) {
             gfxImageFormat imageFormat =
                 drawTarget ? SurfaceFormatToImageFormat(drawTarget->GetFormat()) :
-                    (gfxImageFormat)cairo_image_surface_get_format(cairoTarget);
+                    gfxCairoFormatToImageFormat(cairo_image_surface_get_format(cairoTarget));
             target_visual = gfxXlibSurface::FindVisual(screen, imageFormat);
             Display *dpy = DisplayOfScreen(screen);
             if (target_visual) {
                 target_format = XRenderFindVisualFormat(dpy, target_visual);
             } else {
                 target_format =
                     gfxXlibSurface::FindRenderFormat(dpy, imageFormat);
             }
--- a/gfx/thebes/gfxXlibSurface.cpp
+++ b/gfx/thebes/gfxXlibSurface.cpp
@@ -518,17 +518,16 @@ gfxXlibSurface::FindVisual(Screen *scree
             break;
         case gfxImageFormat::RGB16_565:
             depth = 16;
             red_mask = 0xf800;
             green_mask = 0x7e0;
             blue_mask = 0x1f;
             break;
         case gfxImageFormat::A8:
-        case gfxImageFormat::A1:
         default:
             return nullptr;
     }
 
     for (int d = 0; d < screen->ndepths; d++) {
         const Depth& d_info = screen->depths[d];
         if (d_info.depth != depth)
             continue;
@@ -562,18 +561,16 @@ gfxXlibSurface::FindRenderFormat(Display
             // and find xrender format by visual
             Visual *visual = FindVisual(DefaultScreenOfDisplay(dpy), format);
             if (!visual)
                 return nullptr;
             return XRenderFindVisualFormat(dpy, visual);
         }
         case gfxImageFormat::A8:
             return XRenderFindStandardFormat (dpy, PictStandardA8);
-        case gfxImageFormat::A1:
-            return XRenderFindStandardFormat (dpy, PictStandardA1);
         default:
             break;
     }
 
     return nullptr;
 }
 
 Screen*
copy from mobile/android/gradle/gradle/wrapper/gradle-wrapper.jar
copy to gradle/wrapper/gradle-wrapper.jar
copy from mobile/android/gradle/gradle/wrapper/gradle-wrapper.properties
copy to gradle/wrapper/gradle-wrapper.properties
copy from mobile/android/gradle/gradlew
copy to gradlew
--- a/hal/HalWakeLock.cpp
+++ b/hal/HalWakeLock.cpp
@@ -1,9 +1,10 @@
-/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Hal.h"
 #include "mozilla/HalWakeLock.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
@@ -49,56 +50,31 @@ WakeLockInfoFromLockCount(const nsAStrin
   WakeLockInformation info;
   info.topic() = aTopic;
   info.numLocks() = aLockCount.numLocks;
   info.numHidden() = aLockCount.numHidden;
   info.lockingProcesses().AppendElements(aLockCount.processes);
   return info;
 }
 
-PLDHashOperator
-CountWakeLocks(const uint64_t& aKey, LockCount aCount, void* aUserArg)
+static void
+CountWakeLocks(ProcessLockTable* aTable, LockCount* aTotalCount)
 {
-  MOZ_ASSERT(aUserArg);
-
-  LockCount* totalCount = static_cast<LockCount*>(aUserArg);
-  totalCount->numLocks += aCount.numLocks;
-  totalCount->numHidden += aCount.numHidden;
-
-  // This is linear in the number of processes, but that should be small.
-  if (!totalCount->processes.Contains(aKey)) {
-    totalCount->processes.AppendElement(aKey);
-  }
-
-  return PL_DHASH_NEXT;
-}
+  for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
+    const uint64_t& key = iter.Key();
+    LockCount count = iter.UserData();
 
-static PLDHashOperator
-RemoveChildFromList(const nsAString& aKey, nsAutoPtr<ProcessLockTable>& aTable,
-                    void* aUserArg)
-{
-  MOZ_ASSERT(aUserArg);
+    aTotalCount->numLocks += count.numLocks;
+    aTotalCount->numHidden += count.numHidden;
 
-  PLDHashOperator op = PL_DHASH_NEXT;
-  uint64_t childID = *static_cast<uint64_t*>(aUserArg);
-  if (aTable->Get(childID, nullptr)) {
-    aTable->Remove(childID);
-
-    LockCount totalCount;
-    aTable->EnumerateRead(CountWakeLocks, &totalCount);
-    if (!totalCount.numLocks) {
-      op = PL_DHASH_REMOVE;
-    }
-
-    if (sActiveListeners) {
-      NotifyWakeLockChange(WakeLockInfoFromLockCount(aKey, totalCount));
+    // This is linear in the number of processes, but that should be small.
+    if (!aTotalCount->processes.Contains(key)) {
+      aTotalCount->processes.AppendElement(key);
     }
   }
-
-  return op;
 }
 
 class ClearHashtableOnShutdown final : public nsIObserver {
   ~ClearHashtableOnShutdown() {}
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 };
@@ -139,17 +115,35 @@ CleanupOnContentShutdown::Observe(nsISup
     NS_WARNING("ipc:content-shutdown message without property bag as subject");
     return NS_OK;
   }
 
   uint64_t childID = 0;
   nsresult rv = props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"),
                                            &childID);
   if (NS_SUCCEEDED(rv)) {
-    sLockTable->Enumerate(RemoveChildFromList, &childID);
+    for (auto iter = sLockTable->Iter(); !iter.Done(); iter.Next()) {
+      nsAutoPtr<ProcessLockTable>& table = iter.Data();
+
+      if (table->Get(childID, nullptr)) {
+        table->Remove(childID);
+
+        LockCount totalCount;
+        CountWakeLocks(table, &totalCount);
+
+        if (sActiveListeners) {
+          NotifyWakeLockChange(WakeLockInfoFromLockCount(iter.Key(),
+                                                         totalCount));
+        }
+
+        if (totalCount.numLocks == 0) {
+          iter.Remove();
+        }
+      }
+    }
   } else {
     NS_WARNING("ipc:content-shutdown message without childID property");
   }
   return NS_OK;
 }
 
 void
 Init()
@@ -217,17 +211,17 @@ ModifyWakeLock(const nsAString& aTopic,
   ProcessLockTable* table = sLockTable->Get(aTopic);
   LockCount processCount;
   LockCount totalCount;
   if (!table) {
     table = new ProcessLockTable();
     sLockTable->Put(aTopic, table);
   } else {
     table->Get(aProcessID, &processCount);
-    table->EnumerateRead(CountWakeLocks, &totalCount);
+    CountWakeLocks(table, &totalCount);
   }
 
   MOZ_ASSERT(processCount.numLocks >= processCount.numHidden);
   MOZ_ASSERT(aLockAdjust >= 0 || processCount.numLocks > 0);
   MOZ_ASSERT(aHiddenAdjust >= 0 || processCount.numHidden > 0);
   MOZ_ASSERT(totalCount.numLocks >= totalCount.numHidden);
   MOZ_ASSERT(aLockAdjust >= 0 || totalCount.numLocks > 0);
   MOZ_ASSERT(aHiddenAdjust >= 0 || totalCount.numHidden > 0);
@@ -274,14 +268,14 @@ GetWakeLockInfo(const nsAString& aTopic,
   }
 
   ProcessLockTable* table = sLockTable->Get(aTopic);
   if (!table) {
     *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, LockCount());
     return;
   }
   LockCount totalCount;
-  table->EnumerateRead(CountWakeLocks, &totalCount);
+  CountWakeLocks(table, &totalCount);
   *aWakeLockInfo = WakeLockInfoFromLockCount(aTopic, totalCount);
 }
 
 } // namespace hal_impl
 } // namespace mozilla
--- a/intl/locales/Makefile.in
+++ b/intl/locales/Makefile.in
@@ -1,12 +1,14 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 include $(topsrcdir)/config/rules.mk
 
+ifndef MOZ_EXCLUDE_HYPHENATION_DICTIONARIES
 PATTERN_FILES = $(strip $(wildcard $(srcdir)/*/hyphenation/*.dic))
 
 ifneq (,$(PATTERN_FILES))
 libs::
 	$(INSTALL)  $(PATTERN_FILES) $(FINAL_TARGET)/hyphenation
 endif
+endif # MOZ_EXCLUDE_HYPHENATION_DICTIONARIES
--- a/js/src/builtin/WeakSet.js
+++ b/js/src/builtin/WeakSet.js
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // 23.4.3.1
 function WeakSet_add(value) {
     // Steps 1-3.
     var S = this;
     if (!IsObject(S) || !IsWeakSet(S))
-        ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S);
+        return callFunction(CallWeakSetMethodIfWrapped, this, value, "WeakSet_add");
 
     // Step 4.,6.
     let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
     if (!entries)
         ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "add", typeof S);
 
     // Step 5.
     if (!IsObject(value))
@@ -25,17 +25,17 @@ function WeakSet_add(value) {
     return S;
 }
 
 // 23.4.3.2
 function WeakSet_clear() {
     // Step 1-3.
     var S = this;
     if (!IsObject(S) || !IsWeakSet(S))
-        ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "clear", typeof S);
+        return callFunction(CallWeakSetMethodIfWrapped, this, "WeakSet_clear");
 
     // Step 4.
     let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
     if (!entries)
         ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "clear", typeof S);
 
     // Step 5.
     callFunction(std_WeakMap_clear, entries);
@@ -44,17 +44,17 @@ function WeakSet_clear() {
     return undefined;
 }
 
 // 23.4.3.4
 function WeakSet_delete(value) {
     // Steps 1-3.
     var S = this;
     if (!IsObject(S) || !IsWeakSet(S))
-        ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S);
+        return callFunction(CallWeakSetMethodIfWrapped, this, value, "WeakSet_delete");
 
     // Step 4.,6.
     let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
     if (!entries)
         ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "delete", typeof S);
 
     // Step 5.
     if (!IsObject(value))
@@ -64,17 +64,17 @@ function WeakSet_delete(value) {
     return callFunction(std_WeakMap_delete, entries, value);
 }
 
 // 23.4.3.5
 function WeakSet_has(value) {
     // Steps 1-3.
     var S = this;
     if (!IsObject(S) || !IsWeakSet(S))
-        ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S);
+        return callFunction(CallWeakSetMethodIfWrapped, this, value, "WeakSet_has");
 
     // Step 4-5.
     let entries = UnsafeGetReservedSlot(this, WEAKSET_MAP_SLOT);
     if (!entries)
         ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "WeakSet", "has", typeof S);
 
     // Step 6.
     if (!IsObject(value))
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -816,18 +816,18 @@ struct DefaultHasher<PreBarriered<T>> : 
 /* Useful for hashtables with a ReadBarriered as key. */
 template <class T>
 struct ReadBarrieredHasher
 {
     typedef ReadBarriered<T> Key;
     typedef T Lookup;
 
     static HashNumber hash(Lookup obj) { return DefaultHasher<T>::hash(obj); }
-    static bool match(const Key& k, Lookup l) { return k.get() == l; }
-    static void rekey(Key& k, const Key& newKey) { k.set(newKey); }
+    static bool match(const Key& k, Lookup l) { return k.unbarrieredGet() == l; }
+    static void rekey(Key& k, const Key& newKey) { k.set(newKey.unbarrieredGet()); }
 };
 
 /* Specialized hashing policy for ReadBarriereds. */
 template <class T>
 struct DefaultHasher<ReadBarriered<T>> : ReadBarrieredHasher<T> { };
 
 class ArrayObject;
 class ArrayBufferObject;
@@ -889,24 +889,21 @@ typedef PreBarriered<jsid> PreBarrieredI
 typedef RelocatablePtr<jsid> RelocatableId;
 typedef HeapPtr<jsid> HeapId;
 
 typedef ImmutableTenuredPtr<PropertyName*> ImmutablePropertyNamePtr;
 typedef ImmutableTenuredPtr<JS::Symbol*> ImmutableSymbolPtr;
 
 typedef ReadBarriered<DebugScopeObject*> ReadBarrieredDebugScopeObject;
 typedef ReadBarriered<GlobalObject*> ReadBarrieredGlobalObject;
-typedef ReadBarriered<JSFunction*> ReadBarrieredFunction;
 typedef ReadBarriered<JSObject*> ReadBarrieredObject;
 typedef ReadBarriered<JSScript*> ReadBarrieredScript;
 typedef ReadBarriered<ScriptSourceObject*> ReadBarrieredScriptSourceObject;
 typedef ReadBarriered<Shape*> ReadBarrieredShape;
-typedef ReadBarriered<UnownedBaseShape*> ReadBarrieredUnownedBaseShape;
 typedef ReadBarriered<jit::JitCode*> ReadBarrieredJitCode;
 typedef ReadBarriered<ObjectGroup*> ReadBarrieredObjectGroup;
-typedef ReadBarriered<JSAtom*> ReadBarrieredAtom;
 typedef ReadBarriered<JS::Symbol*> ReadBarrieredSymbol;
 
 typedef ReadBarriered<Value> ReadBarrieredValue;
 
 } /* namespace js */
 
 #endif /* gc_Barrier_h */
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -659,16 +659,17 @@ class GCRuntime
         MOZ_ASSERT(nextCellUniqueId_ > 0);
         return nextCellUniqueId_++;
     }
 
   public:
     // Internal public interface
     js::gc::State state() const { return incrementalState; }
     bool isHeapCompacting() const { return state() == COMPACT; }
+    bool isForegroundSweeping() const { return state() == SWEEP; }
     bool isBackgroundSweeping() { return helperState.isBackgroundSweeping(); }
     void waitBackgroundSweepEnd() { helperState.waitBackgroundSweepEnd(); }
     void waitBackgroundSweepOrAllocEnd() {
         helperState.waitBackgroundSweepEnd();
         allocTask.cancel(GCParallelTask::CancelAndWait);
     }
 
     void requestMinorGC(JS::gcreason::Reason reason);
--- a/js/src/gc/Heap.h
+++ b/js/src/gc/Heap.h
@@ -1452,18 +1452,23 @@ TenuredCell::isInsideZone(JS::Zone* zone
 
 /* static */ MOZ_ALWAYS_INLINE void
 TenuredCell::readBarrier(TenuredCell* thing)
 {
     MOZ_ASSERT(!CurrentThreadIsIonCompiling());
     MOZ_ASSERT(!isNullLike(thing));
     if (thing->shadowRuntimeFromAnyThread()->isHeapBusy())
         return;
+    MOZ_ASSERT_IF(CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread()),
+                  !thing->shadowRuntimeFromAnyThread()->isHeapCollecting());
 
     JS::shadow::Zone* shadowZone = thing->shadowZoneFromAnyThread();
+    MOZ_ASSERT_IF(!CurrentThreadCanAccessRuntime(thing->runtimeFromAnyThread()),
+                  !shadowZone->needsIncrementalBarrier());
+
     if (shadowZone->needsIncrementalBarrier()) {
         MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
         Cell* tmp = thing;
         TraceManuallyBarrieredGenericPointerEdge(shadowZone->barrierTracer(), &tmp, "read barrier");
         MOZ_ASSERT(tmp == thing);
     }
     if (thing->isMarked(GRAY))
         UnmarkGrayCellRecursively(thing, thing->getTraceKind());
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -2441,21 +2441,21 @@ TypeSet::MarkTypeRoot(JSTracer* trc, Typ
     AssertRootMarkingPhase(trc);
     MarkTypeUnbarriered(trc, v, name);
 }
 
 void
 TypeSet::MarkTypeUnbarriered(JSTracer* trc, TypeSet::Type* v, const char* name)
 {
     if (v->isSingletonUnchecked()) {
-        JSObject* obj = v->singleton();
+        JSObject* obj = v->singletonNoBarrier();
         DispatchToTracer(trc, &obj, name);
         *v = TypeSet::ObjectType(obj);
     } else if (v->isGroupUnchecked()) {
-        ObjectGroup* group = v->group();
+        ObjectGroup* group = v->groupNoBarrier();
         DispatchToTracer(trc, &group, name);
         *v = TypeSet::ObjectType(group);
     }
 }
 
 
 /*** Cycle Collector Barrier Implementation *******************************************************/
 
--- a/js/src/gc/Zone.cpp
+++ b/js/src/gc/Zone.cpp
@@ -142,17 +142,17 @@ Zone::logPromotionsToTenured()
     auto now = JS_GetCurrentEmbedderTime();
     JSRuntime* rt = runtimeFromAnyThread();
 
     for (auto** dbgp = dbgs->begin(); dbgp != dbgs->end(); dbgp++) {
         if (!(*dbgp)->isEnabled() || !(*dbgp)->isTrackingTenurePromotions())
             continue;
 
         for (auto range = awaitingTenureLogging.all(); !range.empty(); range.popFront()) {
-            if ((*dbgp)->isDebuggee(range.front()->compartment()))
+            if ((*dbgp)->isDebuggeeUnbarriered(range.front()->compartment()))
                 (*dbgp)->logTenurePromotion(rt, *range.front(), now);
         }
     }
 
     awaitingTenureLogging.clear();
 }
 
 void
@@ -306,17 +306,17 @@ Zone::canCollect()
     return true;
 }
 
 void
 Zone::notifyObservingDebuggers()
 {
     for (CompartmentsInZoneIter comps(this); !comps.done(); comps.next()) {
         JSRuntime* rt = runtimeFromAnyThread();
-        RootedGlobalObject global(rt, comps->maybeGlobal());
+        RootedGlobalObject global(rt, comps->unsafeUnbarrieredMaybeGlobal());
         if (!global)
             continue;
 
         GlobalObject::DebuggerVector* dbgs = global->getDebuggers();
         if (!dbgs)
             continue;
 
         for (GlobalObject::DebuggerVector::Range r = dbgs->all(); !r.empty(); r.popFront()) {
--- a/js/src/jit-test/tests/collections/WeakSet-clear.js
+++ b/js/src/jit-test/tests/collections/WeakSet-clear.js
@@ -35,8 +35,16 @@ ws.add(value);
 value = null;
 ws.clear();
 gc();
 var value2 = {};
 ws.add(value2);
 value2 = null;
 gc();
 ws.clear();
+
+// Clearing a cross-compartment WeakSet with a live value
+ws = new (newGlobal().WeakSet);
+value = {};
+WeakSet.prototype.add.call(ws, value);
+assertEq(WeakSet.prototype.has.call(ws, value), true);
+WeakSet.prototype.clear.call(ws);
+assertEq(WeakSet.prototype.has.call(ws, value), false);
--- a/js/src/jit-test/tests/collections/WeakSet-delete.js
+++ b/js/src/jit-test/tests/collections/WeakSet-delete.js
@@ -24,8 +24,16 @@ assertEq(ws.add(value), ws);
 assertEq(ws.has(value), true);
 assertEq(ws.delete(value), true);
 assertEq(ws.has(value), false);
 assertEq(ws.delete(value), false);
 assertEq(ws.has(value), false);
 
 // Delete primitive
 assertEq(ws.delete(15), false);
+
+// Delete with cross-compartment WeakSet
+ws = new (newGlobal().WeakSet);
+WeakSet.prototype.add.call(ws, value);
+assertEq(WeakSet.prototype.has.call(ws, value), true);
+assertEq(WeakSet.prototype.delete.call(ws, value), true);
+assertEq(WeakSet.prototype.has.call(ws, value), false);
+assertEq(WeakSet.prototype.delete.call(ws, value), false);
--- a/js/src/jsapi-tests/testSavedStacks.cpp
+++ b/js/src/jsapi-tests/testSavedStacks.cpp
@@ -106,8 +106,99 @@ BEGIN_TEST(testSavedStacks_RangeBasedFor
         CHECK(frame == rf);
         rf = rf->getParent();
     }
     CHECK(rf == nullptr);
 
     return true;
 }
 END_TEST(testSavedStacks_RangeBasedForLoops)
+
+BEGIN_TEST(testSavedStacks_selfHostedFrames)
+{
+    CHECK(js::DefineTestingFunctions(cx, global, false, false));
+
+    JS::RootedValue val(cx);
+    //             0         1         2         3
+    //             0123456789012345678901234567890123456789
+    CHECK(evaluate("(function one() {                      \n"  // 1
+                   "  try {                                \n"  // 2
+                   "    [1].map(function two() {           \n"  // 3
+                   "      throw saveStack();               \n"  // 4
+                   "    });                                \n"  // 5
+                   "  } catch (stack) {                    \n"  // 6
+                   "    return stack;                      \n"  // 7
+                   "  }                                    \n"  // 8
+                   "}())                                   \n", // 9
+                   "filename.js",
+                   1,
+                   &val));
+
+    CHECK(val.isObject());
+    JS::RootedObject obj(cx, &val.toObject());
+
+    CHECK(obj->is<js::SavedFrame>());
+    JS::Rooted<js::SavedFrame*> savedFrame(cx, &obj->as<js::SavedFrame>());
+
+    JS::Rooted<js::SavedFrame*> selfHostedFrame(cx, savedFrame->getParent());
+    CHECK(selfHostedFrame->isSelfHosted());
+
+    // Source
+    JS::RootedString str(cx);
+    JS::SavedFrameResult result = JS::GetSavedFrameSource(cx, selfHostedFrame, &str,
+                                                          JS::SavedFrameSelfHosted::Exclude);
+    CHECK(result == JS::SavedFrameResult::Ok);
+    JSLinearString* lin = str->ensureLinear(cx);
+    CHECK(lin);
+    CHECK(js::StringEqualsAscii(lin, "filename.js"));
+
+    // Source, including self-hosted frames
+    result = JS::GetSavedFrameSource(cx, selfHostedFrame, &str, JS::SavedFrameSelfHosted::Include);
+    CHECK(result == JS::SavedFrameResult::Ok);
+    lin = str->ensureLinear(cx);
+    CHECK(lin);
+    CHECK(js::StringEqualsAscii(lin, "self-hosted"));
+
+    // Line
+    uint32_t line = 123;
+    result = JS::GetSavedFrameLine(cx, selfHostedFrame, &line, JS::SavedFrameSelfHosted::Exclude);
+    CHECK(result == JS::SavedFrameResult::Ok);
+    CHECK_EQUAL(line, 3U);
+
+    // Column
+    uint32_t column = 123;
+    result = JS::GetSavedFrameColumn(cx, selfHostedFrame, &column,
+                                     JS::SavedFrameSelfHosted::Exclude);
+    CHECK(result == JS::SavedFrameResult::Ok);
+    CHECK_EQUAL(column, 5U);
+
+    // Function display name
+    result = JS::GetSavedFrameFunctionDisplayName(cx, selfHostedFrame, &str,
+                                                  JS::SavedFrameSelfHosted::Exclude);
+    CHECK(result == JS::SavedFrameResult::Ok);
+    lin = str->ensureLinear(cx);
+    CHECK(lin);
+    CHECK(js::StringEqualsAscii(lin, "one"));
+
+    // Parent
+    JS::RootedObject parent(cx);
+    result = JS::GetSavedFrameParent(cx, savedFrame, &parent, JS::SavedFrameSelfHosted::Exclude);
+    CHECK(result == JS::SavedFrameResult::Ok);
+    // JS::GetSavedFrameParent does this super funky and potentially unexpected
+    // thing where it doesn't return the next subsumed parent but any next
+    // parent. This so that callers can still get the "asyncParent" property
+    // which is only on the first frame of the async parent stack and that frame
+    // might not be subsumed by the caller. It is expected that callers will
+    // still interact with the frame through the JSAPI accessors, so this should
+    // be safe and should not leak privileged info to unprivileged
+    // callers. However, because of that, we don't test that the parent we get
+    // here is the selfHostedFrame's parent (because, as just explained, it
+    // isn't) and instead check that asking for the source property gives us the
+    // expected value.
+    result = JS::GetSavedFrameSource(cx, parent, &str, JS::SavedFrameSelfHosted::Exclude);
+    CHECK(result == JS::SavedFrameResult::Ok);
+    lin = str->ensureLinear(cx);
+    CHECK(lin);
+    CHECK(js::StringEqualsAscii(lin, "filename.js"));
+
+    return true;
+}
+END_TEST(testSavedStacks_selfHostedFrames)
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -5545,79 +5545,95 @@ CaptureCurrentStack(JSContext* cx, Mutab
  * Each of these functions will find the first SavedFrame object in the chain
  * whose underlying stack frame principals are subsumed by the cx's current
  * compartment's principals, and operate on that SavedFrame object. This
  * prevents leaking information about privileged frames to un-privileged
  * callers. As a result, the SavedFrame in parameters do _NOT_ need to be in the
  * same compartment as the cx, and the various out parameters are _NOT_
  * guaranteed to be in the same compartment as cx.
  *
+ * You may consider or skip over self-hosted frames by passing
+ * `SavedFrameSelfHosted::Include` or `SavedFrameSelfHosted::Exclude`
+ * respectively.
+ *
  * Additionally, it may be the case that there is no such SavedFrame object
  * whose captured frame's principals are subsumed by the caller's compartment's
  * principals! If the `HandleObject savedFrame` argument is null, or the
  * caller's principals do not subsume any of the chained SavedFrame object's
  * principals, `SavedFrameResult::AccessDenied` is returned and a (hopefully)
  * sane default value is chosen for the out param.
  *
  * See also `js/src/doc/SavedFrame/SavedFrame.md`.
  */
 
 enum class SavedFrameResult {
     Ok,
     AccessDenied
 };
 
+enum class SavedFrameSelfHosted {
+    Include,
+    Exclude
+};
+
 /**
  * Given a SavedFrame JSObject, get its source property. Defaults to the empty
  * string.
  */
 extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep);
+GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep,
+                    SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
 
 /**
  * Given a SavedFrame JSObject, get its line property. Defaults to 0.
  */
 extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep);
+GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep,
+                  SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
 
 /**
  * Given a SavedFrame JSObject, get its column property. Defaults to 0.
  */
 extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp);
+GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp,
+                    SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
 
 /**
  * Given a SavedFrame JSObject, get its functionDisplayName string, or nullptr
  * if SpiderMonkey was unable to infer a name for the captured frame's
  * function. Defaults to nullptr.
  */
 extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep);
+GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep,
+                                 SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
 
 /**
  * Given a SavedFrame JSObject, get its asyncCause string. Defaults to nullptr.
  */
 extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep);
+GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep,
+                        SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
 
 /**
  * Given a SavedFrame JSObject, get its asyncParent SavedFrame object or nullptr
  * if there is no asyncParent. The `asyncParentp` out parameter is _NOT_
  * guaranteed to be in the cx's compartment. Defaults to nullptr.
  */
 extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp);
+GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp,
+                SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
 
 /**
  * Given a SavedFrame JSObject, get its parent SavedFrame object or nullptr if
  * it is the oldest frame in the stack. The `parentp` out parameter is _NOT_
  * guaranteed to be in the cx's compartment. Defaults to nullptr.
  */
 extern JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp);
+GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp,
+                    SavedFrameSelfHosted selfHosted = SavedFrameSelfHosted::Include);
 
 /**
  * Given a SavedFrame JSObject stack, stringify it in the same format as
  * Error.prototype.stack. The stringified stack out parameter is placed in the
  * cx's compartment. Defaults to the empty string.
  *
  * The same notes above about SavedFrame accessors applies here as well: cx
  * doesn't need to be in stack's compartment, and stack can be null, a
--- a/js/src/jsatom.cpp
+++ b/js/src/jsatom.cpp
@@ -194,21 +194,19 @@ void
 js::MarkAtoms(JSTracer* trc)
 {
     JSRuntime* rt = trc->runtime();
     for (AtomSet::Enum e(rt->atoms()); !e.empty(); e.popFront()) {
         const AtomStateEntry& entry = e.front();
         if (!entry.isPinned())
             continue;
 
-        JSAtom* atom = entry.asPtr();
-        bool tagged = entry.isPinned();
+        JSAtom* atom = entry.asPtrUnbarriered();
         TraceRoot(trc, &atom, "interned_atom");
-        if (entry.asPtr() != atom)
-            e.rekeyFront(AtomHasher::Lookup(atom), AtomStateEntry(atom, tagged));
+        MOZ_ASSERT(entry.asPtrUnbarriered() == atom);
     }
 }
 
 void
 js::MarkPermanentAtoms(JSTracer* trc)
 {
     JSRuntime* rt = trc->runtime();
 
@@ -247,17 +245,17 @@ js::MarkWellKnownSymbols(JSTracer* trc)
 void
 JSRuntime::sweepAtoms()
 {
     if (!atoms_)
         return;
 
     for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) {
         AtomStateEntry entry = e.front();
-        JSAtom* atom = entry.asPtr();
+        JSAtom* atom = entry.asPtrUnbarriered();
         bool isDying = IsAboutToBeFinalizedUnbarriered(&atom);
 
         /* Pinned or interned key cannot be finalized. */
         MOZ_ASSERT_IF(hasContexts() && entry.isPinned(), !isDying);
 
         if (isDying)
             e.removeFront();
     }
--- a/js/src/jsatom.h
+++ b/js/src/jsatom.h
@@ -70,16 +70,17 @@ class AtomStateEntry
      * Non-branching code sequence. Note that the const_cast is safe because
      * the hash function doesn't consider the tag to be a portion of the key.
      */
     void setPinned(bool pinned) const {
         const_cast<AtomStateEntry*>(this)->bits |= uintptr_t(pinned);
     }
 
     JSAtom* asPtr() const;
+    JSAtom* asPtrUnbarriered() const;
 };
 
 struct AtomHasher
 {
     struct Lookup
     {
         union {
             const JS::Latin1Char* latin1Chars;
--- a/js/src/jsatominlines.h
+++ b/js/src/jsatominlines.h
@@ -21,16 +21,23 @@ inline JSAtom*
 js::AtomStateEntry::asPtr() const
 {
     MOZ_ASSERT(bits != 0);
     JSAtom* atom = reinterpret_cast<JSAtom*>(bits & NO_TAG_MASK);
     JSString::readBarrier(atom);
     return atom;
 }
 
+inline JSAtom*
+js::AtomStateEntry::asPtrUnbarriered() const
+{
+    MOZ_ASSERT(bits != 0);
+    return reinterpret_cast<JSAtom*>(bits & NO_TAG_MASK);
+}
+
 namespace js {
 
 inline jsid
 AtomToId(JSAtom* atom)
 {
     JS_STATIC_ASSERT(JSID_INT_MIN == 0);
 
     uint32_t index;
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -259,17 +259,18 @@ JSCompartment::checkWrapperMapAfterMovin
      * Assert that the postbarriers have worked and that nothing is left in
      * wrapperMap that points into the nursery, and that the hash table entries
      * are discoverable.
      */
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
         CrossCompartmentKey key = e.front().key();
         CheckGCThingAfterMovingGC(key.debugger);
         CheckGCThingAfterMovingGC(key.wrapped);
-        CheckGCThingAfterMovingGC(static_cast<Cell*>(e.front().value().get().toGCThing()));
+        CheckGCThingAfterMovingGC(
+                static_cast<Cell*>(e.front().value().unbarrieredGet().toGCThing()));
 
         WrapperMap::Ptr ptr = crossCompartmentWrappers.lookup(key);
         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
     }
 }
 #endif
 
 bool
@@ -540,17 +541,17 @@ JSCompartment::getNonSyntacticLexicalSco
 
 void
 JSCompartment::traceOutgoingCrossCompartmentWrappers(JSTracer* trc)
 {
     MOZ_ASSERT(trc->runtime()->isHeapMajorCollecting());
     MOZ_ASSERT(!zone()->isCollecting() || trc->runtime()->gc.isHeapCompacting());
 
     for (WrapperMap::Enum e(crossCompartmentWrappers); !e.empty(); e.popFront()) {
-        Value v = e.front().value();
+        Value v = e.front().value().unbarrieredGet();
         if (e.front().key().kind == CrossCompartmentKey::ObjectWrapper) {
             ProxyObject* wrapper = &v.toObject().as<ProxyObject>();
 
             /*
              * We have a cross-compartment wrapper. Its private pointer may
              * point into the compartment being collected, so we should mark it.
              */
             TraceEdge(trc, wrapper->slotOfPrivate(), "cross-compartment wrapper");
@@ -667,19 +668,19 @@ void
 JSCompartment::sweepSavedStacks()
 {
     savedStacks_.sweep(runtimeFromAnyThread());
 }
 
 void
 JSCompartment::sweepGlobalObject(FreeOp* fop)
 {
-    if (global_.unbarrieredGet() && IsAboutToBeFinalized(&global_)) {
+    if (global_ && IsAboutToBeFinalized(&global_)) {
         if (isDebuggee())
-            Debugger::detachAllDebuggersFromGlobal(fop, global_);
+            Debugger::detachAllDebuggersFromGlobal(fop, global_.unbarrieredGet());
         global_.set(nullptr);
     }
 }
 
 void
 JSCompartment::sweepObjectPendingMetadata()
 {
     if (objectMetadataState.is<PendingMetadata>()) {
@@ -1006,17 +1007,20 @@ JSCompartment::ensureDelazifyScriptsForD
 void
 JSCompartment::updateDebuggerObservesFlag(unsigned flag)
 {
     MOZ_ASSERT(isDebuggee());
     MOZ_ASSERT(flag == DebuggerObservesAllExecution ||
                flag == DebuggerObservesCoverage ||
                flag == DebuggerObservesAsmJS);
 
-    const GlobalObject::DebuggerVector* v = maybeGlobal()->getDebuggers();
+    GlobalObject* global = zone()->runtimeFromMainThread()->gc.isForegroundSweeping()
+                           ? unsafeUnbarrieredMaybeGlobal()
+                           : maybeGlobal();
+    const GlobalObject::DebuggerVector* v = global->getDebuggers();
     for (Debugger * const* p = v->begin(); p != v->end(); p++) {
         Debugger* dbg = *p;
         if (flag == DebuggerObservesAllExecution ? dbg->observesAllExecution() :
             flag == DebuggerObservesCoverage ? dbg->observesCoverage() :
             dbg->observesAsmJS())
         {
             debugModeBits |= flag;
             return;
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2843,17 +2843,17 @@ GetNearestEnclosingWithScopeObjectForFun
  * Get the first SavedFrame object in this SavedFrame stack whose principals are
  * subsumed by the cx's principals. If there is no such frame, return nullptr.
  *
  * Do NOT pass a non-SavedFrame object here.
  *
  * The savedFrame and cx do not need to be in the same compartment.
  */
 extern JS_FRIEND_API(JSObject*)
-GetFirstSubsumedSavedFrame(JSContext* cx, JS::HandleObject savedFrame);
+GetFirstSubsumedSavedFrame(JSContext* cx, JS::HandleObject savedFrame, JS::SavedFrameSelfHosted selfHosted);
 
 extern JS_FRIEND_API(bool)
 ReportIsNotFunction(JSContext* cx, JS::HandleValue v);
 
 extern JS_FRIEND_API(JSObject*)
 ConvertArgsToArray(JSContext* cx, const JS::CallArgs& args);
 
 } /* namespace js */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -5221,17 +5221,17 @@ GCRuntime::beginSweepPhase(bool destroyi
 
     releaseObservedTypes = shouldReleaseObservedTypes();
 
 #ifdef DEBUG
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         MOZ_ASSERT(!c->gcIncomingGrayPointers);
         for (JSCompartment::WrapperEnum e(c); !e.empty(); e.popFront()) {
             if (e.front().key().kind != CrossCompartmentKey::StringWrapper)
-                AssertNotOnGrayList(&e.front().value().get().toObject());
+                AssertNotOnGrayList(&e.front().value().unbarrieredGet().toObject());
         }
     }
 #endif
 
     DropStringWrappers(rt);
 
     findZoneGroups();
     endMarkingZoneGroup();
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1272,17 +1272,17 @@ CheckGCThingAfterMovingGC(T* t)
         MOZ_RELEASE_ASSERT(!RelocationOverlay::isCellForwarded(t));
     }
 }
 
 template <typename T>
 inline void
 CheckGCThingAfterMovingGC(const ReadBarriered<T*>& t)
 {
-    CheckGCThingAfterMovingGC(t.get());
+    CheckGCThingAfterMovingGC(t.unbarrieredGet());
 }
 
 struct CheckValueAfterMovingGCFunctor : public VoidDefaultAdaptor<Value> {
     template <typename T> void operator()(T* t) { CheckGCThingAfterMovingGC(t); }
 };
 
 inline void
 CheckValueAfterMovingGC(const JS::Value& value)
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -160,16 +160,19 @@ struct Runtime
 
   public:
     Runtime()
       : heapState_(JS::HeapState::Idle)
       , gcStoreBufferPtr_(nullptr)
     {}
 
     bool isHeapBusy() const { return heapState_ != JS::HeapState::Idle; }
+    bool isHeapMajorCollecting() const { return heapState_ == JS::HeapState::MajorCollecting; }
+    bool isHeapMinorCollecting() const { return heapState_ == JS::HeapState::MinorCollecting; }
+    bool isHeapCollecting() const { return isHeapMinorCollecting() || isHeapMajorCollecting(); }
 
     js::gc::StoreBuffer* gcStoreBufferPtr() { return gcStoreBufferPtr_; }
 
     static JS::shadow::Runtime* asShadowRuntime(JSRuntime* rt) {
         return reinterpret_cast<JS::shadow::Runtime*>(rt);
     }
 
   protected:
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1697,20 +1697,20 @@ Debugger::slowPathOnIonCompilation(JSCon
         cx->clearPendingException();
         return;
     }
 
     MOZ_ASSERT(status == JSTRAP_CONTINUE);
 }
 
 bool
-Debugger::isDebuggee(const JSCompartment* compartment) const
+Debugger::isDebuggeeUnbarriered(const JSCompartment* compartment) const
 {
     MOZ_ASSERT(compartment);
-    return compartment->isDebuggee() && debuggees.has(compartment->maybeGlobal());
+    return compartment->isDebuggee() && debuggees.has(compartment->unsafeUnbarrieredMaybeGlobal());
 }
 
 Debugger::TenurePromotionsLogEntry::TenurePromotionsLogEntry(JSRuntime* rt, JSObject& obj, double when)
   : className(obj.getClass()->name),
     when(when),
     frame(getObjectAllocationSite(obj)),
     size(JS::ubi::Node(&obj).size(rt->debuggerMallocSizeOf))
 { }
@@ -2466,17 +2466,17 @@ Debugger::markAllIteratively(GCMarker* t
 
     /*
      * Find all Debugger objects in danger of GC. This code is a little
      * convoluted since the easiest way to find them is via their debuggees.
      */
     JSRuntime* rt = trc->runtime();
     for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next()) {
         if (c->isDebuggee()) {
-            GlobalObject* global = c->maybeGlobal();
+            GlobalObject* global = c->unsafeUnbarrieredMaybeGlobal();
             if (!IsMarkedUnbarriered(&global))
                 continue;
 
             /*
              * Every debuggee has at least one debugger, so in this case
              * getDebuggers can't return nullptr.
              */
             const GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
@@ -2533,20 +2533,20 @@ Debugger::markAllIteratively(GCMarker* t
  */
 /* static */ void
 Debugger::markAll(JSTracer* trc)
 {
     JSRuntime* rt = trc->runtime();
     for (Debugger* dbg : rt->debuggerList) {
         WeakGlobalObjectSet& debuggees = dbg->debuggees;
         for (WeakGlobalObjectSet::Enum e(debuggees); !e.empty(); e.popFront()) {
-            GlobalObject* global = e.front();
+            GlobalObject* global = e.front().unbarrieredGet();
             TraceManuallyBarrieredEdge(trc, &global, "Global Object");
-            if (global != e.front())
-                e.rekeyFront(ReadBarrieredGlobalObject(global));
+            if (global != e.front().unbarrieredGet())
+                e.rekeyFront(global, ReadBarrieredGlobalObject(global));
         }
 
         HeapPtrNativeObject& dbgobj = dbg->toJSObjectRef();
         TraceEdge(trc, &dbgobj, "Debugger Object");
 
         dbg->scripts.trace(trc);
         dbg->sources.trace(trc);
         dbg->objects.trace(trc);
@@ -2610,17 +2610,17 @@ Debugger::sweepAll(FreeOp* fop)
     for (Debugger* dbg : rt->debuggerList) {
         if (IsAboutToBeFinalized(&dbg->object)) {
             /*
              * dbg is being GC'd. Detach it from its debuggees. The debuggee
              * might be GC'd too. Since detaching requires access to both
              * objects, this must be done before finalize time.
              */
             for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront())
-                dbg->removeDebuggeeGlobal(fop, e.front(), &e);
+                dbg->removeDebuggeeGlobal(fop, e.front().unbarrieredGet(), &e);
         }
     }
 }
 
 /* static */ void
 Debugger::detachAllDebuggersFromGlobal(FreeOp* fop, GlobalObject* global)
 {
     const GlobalObject::DebuggerVector* debuggers = global->getDebuggers();
@@ -3450,17 +3450,17 @@ Debugger::addDebuggeeGlobal(JSContext* c
 }
 
 void
 Debugger::recomputeDebuggeeZoneSet()
 {
     AutoEnterOOMUnsafeRegion oomUnsafe;
     debuggeeZones.clear();
     for (auto range = debuggees.all(); !range.empty(); range.popFront()) {
-        if (!debuggeeZones.put(range.front()->zone()))
+        if (!debuggeeZones.put(range.front().unbarrieredGet()->zone()))
             oomUnsafe.crash("Debugger::removeDebuggeeGlobal");
     }
 }
 
 template<typename V>
 static Debugger**
 findDebuggerInVector(Debugger* dbg, V* vec)
 {
@@ -3479,17 +3479,17 @@ Debugger::removeDebuggeeGlobal(FreeOp* f
 {
     /*
      * The caller might have found global by enumerating this->debuggees; if
      * so, use HashSet::Enum::removeFront rather than HashSet::remove below,
      * to avoid invalidating the live enumerator.
      */
     MOZ_ASSERT(debuggees.has(global));
     MOZ_ASSERT(debuggeeZones.has(global->zone()));
-    MOZ_ASSERT_IF(debugEnum, debugEnum->front() == global);
+    MOZ_ASSERT_IF(debugEnum, debugEnum->front().unbarrieredGet() == global);
 
     /*
      * FIXME Debugger::slowPathOnLeaveFrame needs to kill all Debugger.Frame
      * objects referring to a particular JS stack frame. This is hard if
      * Debugger objects that are no longer debugging the relevant global might
      * have live Frame objects. So we take the easy way out and kill them here.
      * This is a bug, since it's observable and contrary to the spec. One
      * possible fix would be to put such objects into a compartment-wide bag
@@ -4098,17 +4098,17 @@ class MOZ_STACK_CLASS Debugger::ObjectQu
          * exist some path from this non-debuggee node back to a node in our
          * debuggee compartments. However, if that were true, then the incoming
          * cross compartment edge back into a debuggee compartment is already
          * listed as an edge in the RootList we started traversal with, and
          * therefore we don't need to follow edges to or from this non-debuggee
          * node.
          */
         JSCompartment* comp = referent.compartment();
-        if (comp && !dbg->isDebuggee(comp)) {
+        if (comp && !dbg->isDebuggeeUnbarriered(comp)) {
             traversal.abandonReferent();
             return true;
         }
 
         /*
          * If the referent is an object and matches our query's restrictions,
          * add it to the vector accumulating results. Skip objects that should
          * never be exposed to JS, like ScopeObjects and internal functions.
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -254,17 +254,17 @@ class Debugger : private mozilla::Linked
     // must be 0 and Observing must be 1.
     enum IsObserving {
         NotObserving = 0,
         Observing = 1
     };
 
     // Return true if the given compartment is a debuggee of this debugger,
     // false otherwise.
-    bool isDebuggee(const JSCompartment* compartment) const;
+    bool isDebuggeeUnbarriered(const JSCompartment* compartment) const;
 
     // Return true if this Debugger observed a debuggee that participated in the
     // GC identified by the given GC number. Return false otherwise.
     bool observedGC(uint64_t majorGCNumber) const {
         return observedGCs.has(majorGCNumber);
     }
 
     // Notify this Debugger that one or more of its debuggees is participating
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -395,18 +395,18 @@ struct ObjectGroupCompartment::NewEntry
 
     static inline HashNumber hash(const Lookup& lookup) {
         return PointerHasher<JSObject*, 3>::hash(lookup.hashProto.raw()) ^
                PointerHasher<const Class*, 3>::hash(lookup.clasp) ^
                PointerHasher<JSObject*, 3>::hash(lookup.associated);
     }
 
     static inline bool match(const NewEntry& key, const Lookup& lookup) {
-        return key.group->proto() == lookup.matchProto &&
-               (!lookup.clasp || key.group->clasp() == lookup.clasp) &&
+        return key.group.unbarrieredGet()->proto() == lookup.matchProto &&
+               (!lookup.clasp || key.group.unbarrieredGet()->clasp() == lookup.clasp) &&
                key.associated == lookup.associated;
     }
 
     static void rekey(NewEntry& k, const NewEntry& newKey) { k = newKey; }
 };
 
 // This class is used to add a post barrier on a NewTable entry, as the key is
 // calculated from a prototype object which may be moved by generational GC.
@@ -1804,31 +1804,31 @@ ObjectGroupCompartment::fixupNewTableAft
     /*
      * Each entry's hash depends on the object's prototype and we can't tell
      * whether that has been moved or not in sweepNewObjectGroupTable().
      */
     if (table && table->initialized()) {
         for (NewTable::Enum e(*table); !e.empty(); e.popFront()) {
             NewEntry entry = e.front();
             bool needRekey = false;
-            if (IsForwarded(entry.group.get())) {
-                entry.group.set(Forwarded(entry.group.get()));
+            if (IsForwarded(entry.group.unbarrieredGet())) {
+                entry.group.set(Forwarded(entry.group.unbarrieredGet()));
                 needRekey = true;
             }
-            TaggedProto proto = entry.group->proto();
+            TaggedProto proto = entry.group.unbarrieredGet()->proto();
             if (proto.isObject() && IsForwarded(proto.toObject())) {
                 proto = TaggedProto(Forwarded(proto.toObject()));
                 needRekey = true;
             }
             if (entry.associated && IsForwarded(entry.associated)) {
                 entry.associated = Forwarded(entry.associated);
                 needRekey = true;
             }
             if (needRekey) {
-                const Class* clasp = entry.group->clasp();
+                const Class* clasp = entry.group.unbarrieredGet()->clasp();
                 if (entry.associated && entry.associated->is<JSFunction>())
                     clasp = nullptr;
                 NewEntry::Lookup lookup(clasp, proto, entry.associated);
                 e.rekeyFront(lookup, entry);
             }
         }
     }
 }
@@ -1842,23 +1842,23 @@ ObjectGroupCompartment::checkNewTableAft
      * Assert that nothing points into the nursery or needs to be relocated, and
      * that the hash table entries are discoverable.
      */
     if (!table || !table->initialized())
         return;
 
     for (NewTable::Enum e(*table); !e.empty(); e.popFront()) {
         NewEntry entry = e.front();
-        CheckGCThingAfterMovingGC(entry.group.get());
-        TaggedProto proto = entry.group->proto();
+        CheckGCThingAfterMovingGC(entry.group.unbarrieredGet());
+        TaggedProto proto = entry.group.unbarrieredGet()->proto();
         if (proto.isObject())
             CheckGCThingAfterMovingGC(proto.toObject());
         CheckGCThingAfterMovingGC(entry.associated);
 
-        const Class* clasp = entry.group->clasp();
+        const Class* clasp = entry.group.unbarrieredGet()->clasp();
         if (entry.associated && entry.associated->is<JSFunction>())
             clasp = nullptr;
 
         NewEntry::Lookup lookup(clasp, proto, entry.associated);
         NewTable::Ptr ptr = table->lookup(lookup);
         MOZ_RELEASE_ASSERT(ptr.found() && &*ptr == &e.front());
     }
 }
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1030,20 +1030,16 @@ struct JSRuntime : public JS::shadow::Ru
 #endif
 
     /* Garbage collector state, used by jsgc.c. */
     js::gc::GCRuntime   gc;
 
     /* Garbage collector state has been sucessfully initialized. */
     bool                gcInitialized;
 
-    bool isHeapMajorCollecting() const { return heapState_ == JS::HeapState::MajorCollecting; }
-    bool isHeapMinorCollecting() const { return heapState_ == JS::HeapState::MinorCollecting; }
-    bool isHeapCollecting() const { return isHeapMinorCollecting() || isHeapMajorCollecting(); }
-
     int gcZeal() { return gc.zeal(); }
 
     void lockGC() {
         assertCanLock(js::GCLock);
         gc.lockGC();
     }
 
     void unlockGC() {
--- a/js/src/vm/SavedStacks.cpp
+++ b/js/src/vm/SavedStacks.cpp
@@ -531,38 +531,47 @@ SavedFrameSubsumedByCaller(JSContext* cx
 }
 
 // Return the first SavedFrame in the chain that starts with |frame| whose
 // principals are subsumed by |principals|, according to |subsumes|. If there is
 // no such frame, return nullptr. |skippedAsync| is set to true if any of the
 // skipped frames had the |asyncCause| property set, otherwise it is explicitly
 // set to false.
 static SavedFrame*
-GetFirstSubsumedFrame(JSContext* cx, HandleSavedFrame frame, bool& skippedAsync)
+GetFirstSubsumedFrame(JSContext* cx, HandleSavedFrame frame, JS::SavedFrameSelfHosted selfHosted,
+                      bool& skippedAsync)
 {
     skippedAsync = false;
 
     RootedSavedFrame rootedFrame(cx, frame);
-    while (rootedFrame && !SavedFrameSubsumedByCaller(cx, rootedFrame)) {
+    while (rootedFrame) {
+        if ((selfHosted == JS::SavedFrameSelfHosted::Include || !rootedFrame->isSelfHosted()) &&
+            SavedFrameSubsumedByCaller(cx, rootedFrame))
+        {
+            return rootedFrame;
+        }
+
         if (rootedFrame->getAsyncCause())
             skippedAsync = true;
+
         rootedFrame = rootedFrame->getParent();
     }
 
-    return rootedFrame;
+    return nullptr;
 }
 
 JS_FRIEND_API(JSObject*)
-GetFirstSubsumedSavedFrame(JSContext* cx, HandleObject savedFrame)
+GetFirstSubsumedSavedFrame(JSContext* cx, HandleObject savedFrame,
+                           JS::SavedFrameSelfHosted selfHosted)
 {
     if (!savedFrame)
         return nullptr;
     bool skippedAsync;
     RootedSavedFrame frame(cx, &savedFrame->as<SavedFrame>());
-    return GetFirstSubsumedFrame(cx, frame, skippedAsync);
+    return GetFirstSubsumedFrame(cx, frame, selfHosted, skippedAsync);
 }
 
 /* static */ bool
 SavedFrame::checkThis(JSContext* cx, CallArgs& args, const char* fnName,
                       MutableHandleObject frame)
 {
     const Value& thisValue = args.thisv();
 
@@ -654,144 +663,157 @@ public:
  private:
     Maybe<JSAutoCompartment> ac_;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 } // namespace
 
 static inline js::SavedFrame*
-UnwrapSavedFrame(JSContext* cx, HandleObject obj, bool& skippedAsync)
+UnwrapSavedFrame(JSContext* cx, HandleObject obj, SavedFrameSelfHosted selfHosted,
+                 bool& skippedAsync)
 {
     if (!obj)
         return nullptr;
+
     RootedObject savedFrameObj(cx, CheckedUnwrap(obj));
-    MOZ_ASSERT(savedFrameObj);
+    if (!savedFrameObj)
+        return nullptr;
+
     MOZ_ASSERT(js::SavedFrame::isSavedFrameAndNotProto(*savedFrameObj));
     js::RootedSavedFrame frame(cx, &savedFrameObj->as<js::SavedFrame>());
-    return GetFirstSubsumedFrame(cx, frame, skippedAsync);
+    return GetFirstSubsumedFrame(cx, frame, selfHosted, skippedAsync);
 }
 
 JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep)
+GetSavedFrameSource(JSContext* cx, HandleObject savedFrame, MutableHandleString sourcep,
+                    SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
-    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
+    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     if (!frame) {
         sourcep.set(cx->runtime()->emptyString);
         return SavedFrameResult::AccessDenied;
     }
     sourcep.set(frame->getSource());
     return SavedFrameResult::Ok;
 }
 
 JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep)
+GetSavedFrameLine(JSContext* cx, HandleObject savedFrame, uint32_t* linep,
+                  SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     MOZ_ASSERT(linep);
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
-    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
+    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     if (!frame) {
         *linep = 0;
         return SavedFrameResult::AccessDenied;
     }
     *linep = frame->getLine();
     return SavedFrameResult::Ok;
 }
 
 JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp)
+GetSavedFrameColumn(JSContext* cx, HandleObject savedFrame, uint32_t* columnp,
+                    SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     MOZ_ASSERT(columnp);
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
-    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
+    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     if (!frame) {
         *columnp = 0;
         return SavedFrameResult::AccessDenied;
     }
     *columnp = frame->getColumn();
     return SavedFrameResult::Ok;
 }
 
 JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep)
+GetSavedFrameFunctionDisplayName(JSContext* cx, HandleObject savedFrame, MutableHandleString namep,
+                                 SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
-    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
+    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     if (!frame) {
         namep.set(nullptr);
         return SavedFrameResult::AccessDenied;
     }
     namep.set(frame->getFunctionDisplayName());
     return SavedFrameResult::Ok;
 }
 
 JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep)
+GetSavedFrameAsyncCause(JSContext* cx, HandleObject savedFrame, MutableHandleString asyncCausep,
+                        SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
-    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
+    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     if (!frame) {
         asyncCausep.set(nullptr);
         return SavedFrameResult::AccessDenied;
     }
     asyncCausep.set(frame->getAsyncCause());
     if (!asyncCausep && skippedAsync)
         asyncCausep.set(cx->names().Async);
     return SavedFrameResult::Ok;
 }
 
 JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp)
+GetSavedFrameAsyncParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject asyncParentp,
+                         SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
-    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
+    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     if (!frame) {
         asyncParentp.set(nullptr);
         return SavedFrameResult::AccessDenied;
     }
     js::RootedSavedFrame parent(cx, frame->getParent());
 
     // The current value of |skippedAsync| is not interesting, because we are
     // interested in whether we would cross any async parents to get from here
     // to the first subsumed parent frame instead.
-    js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, skippedAsync));
+    js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, selfHosted,
+                                                                  skippedAsync));
 
     // Even if |parent| is not subsumed, we still want to return a pointer to it
     // rather than |subsumedParent| so it can pick up any |asyncCause| from the
     // inaccessible part of the chain.
     if (subsumedParent && (subsumedParent->getAsyncCause() || skippedAsync))
         asyncParentp.set(parent);
     else
         asyncParentp.set(nullptr);
     return SavedFrameResult::Ok;
 }
 
 JS_PUBLIC_API(SavedFrameResult)
-GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp)
+GetSavedFrameParent(JSContext* cx, HandleObject savedFrame, MutableHandleObject parentp,
+                    SavedFrameSelfHosted selfHosted /* = SavedFrameSelfHosted::Include */)
 {
     AutoMaybeEnterFrameCompartment ac(cx, savedFrame);
     bool skippedAsync;
-    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, skippedAsync));
+    js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, savedFrame, selfHosted, skippedAsync));
     if (!frame) {
         parentp.set(nullptr);
         return SavedFrameResult::AccessDenied;
     }
     js::RootedSavedFrame parent(cx, frame->getParent());
 
     // The current value of |skippedAsync| is not interesting, because we are
     // interested in whether we would cross any async parents to get from here
     // to the first subsumed parent frame instead.
-    js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, skippedAsync));
+    js::RootedSavedFrame subsumedParent(cx, GetFirstSubsumedFrame(cx, parent, selfHosted,
+                                                                  skippedAsync));
 
     // Even if |parent| is not subsumed, we still want to return a pointer to it
     // rather than |subsumedParent| so it can pick up any |asyncCause| from the
     // inaccessible part of the chain.
     if (subsumedParent && !(subsumedParent->getAsyncCause() || skippedAsync))
         parentp.set(parent);
     else
         parentp.set(nullptr);
@@ -805,49 +827,49 @@ BuildStackString(JSContext* cx, HandleOb
 
     // Enter a new block to constrain the scope of possibly entering the stack's
     // compartment. This ensures that when we finish the StringBuffer, we are
     // back in the cx's original compartment, and fulfill our contract with
     // callers to place the output string in the cx's current compartment.
     {
         AutoMaybeEnterFrameCompartment ac(cx, stack);
         bool skippedAsync;
-        js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack, skippedAsync));
+        js::RootedSavedFrame frame(cx, UnwrapSavedFrame(cx, stack, SavedFrameSelfHosted::Exclude,
+                                                        skippedAsync));
         if (!frame) {
             stringp.set(cx->runtime()->emptyString);
             return true;
         }
 
         js::RootedSavedFrame parent(cx);
         do {
             MOZ_ASSERT(SavedFrameSubsumedByCaller(cx, frame));
+            MOZ_ASSERT(!frame->isSelfHosted());
 
-            if (!frame->isSelfHosted()) {
-                RootedString asyncCause(cx, frame->getAsyncCause());
-                if (!asyncCause && skippedAsync)
-                    asyncCause.set(cx->names().Async);
+            RootedString asyncCause(cx, frame->getAsyncCause());
+            if (!asyncCause && skippedAsync)
+                asyncCause.set(cx->names().Async);
 
-                js::RootedAtom name(cx, frame->getFunctionDisplayName());
-                if ((indent && !sb.appendN(' ', indent))
-                    || (asyncCause && (!sb.append(asyncCause) || !sb.append('*')))
-                    || (name && !sb.append(name))
-                    || !sb.append('@')
-                    || !sb.append(frame->getSource())
-                    || !sb.append(':')
-                    || !NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
-                    || !sb.append(':')
-                    || !NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
-                    || !sb.append('\n'))
-                {
-                    return false;
-                }
+            js::RootedAtom name(cx, frame->getFunctionDisplayName());
+            if ((indent && !sb.appendN(' ', indent))
+                || (asyncCause && (!sb.append(asyncCause) || !sb.append('*')))
+                || (name && !sb.append(name))
+                || !sb.append('@')
+                || !sb.append(frame->getSource())
+                || !sb.append(':')
+                || !NumberValueToStringBuffer(cx, NumberValue(frame->getLine()), sb)
+                || !sb.append(':')
+                || !NumberValueToStringBuffer(cx, NumberValue(frame->getColumn()), sb)
+                || !sb.append('\n'))
+            {
+                return false;
             }
 
             parent = frame->getParent();
-            frame = js::GetFirstSubsumedFrame(cx, parent, skippedAsync);
+            frame = js::GetFirstSubsumedFrame(cx, parent, SavedFrameSelfHosted::Exclude, skippedAsync);
         } while (frame);
     }
 
     JSString* str = sb.finishString();
     if (!str)
         return false;
     assertSameCompartment(cx, str);
     stringp.set(str);
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -2259,17 +2259,17 @@ DebugScopes::sweep(JSRuntime* rt)
              * that the synthetic SO is also about to be finalized too, and thus
              * the loop below will take care of things. But complex GC behavior
              * means that marks are only conservative approximations of
              * liveness; we should assume that anything could be marked.
              *
              * Thus, we must explicitly remove the entries from both liveScopes
              * and missingScopes here.
              */
-            liveScopes.remove(&e.front().value()->scope());
+            liveScopes.remove(&e.front().value().unbarrieredGet()->scope());
             e.removeFront();
         } else {
             MissingScopeKey key = e.front().key();
             if (IsForwarded(key.staticScope())) {
                 key.updateStaticScope(Forwarded(key.staticScope()));
                 e.rekeyFront(key);
             }
         }
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -1657,16 +1657,18 @@ static const JSFunctionSpec intrinsic_fu
           CallNonGenericSelfhostedMethod<Is<TypedArrayObject>>, 2, 0),
 
     JS_FN("CallLegacyGeneratorMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<LegacyGeneratorObject>>, 2, 0),
     JS_FN("CallStarGeneratorMethodIfWrapped",
           CallNonGenericSelfhostedMethod<Is<StarGeneratorObject>>, 2, 0),
 
     JS_FN("IsWeakSet",               intrinsic_IsWeakSet,               1,0),
+    JS_FN("CallWeakSetMethodIfWrapped",
+          CallNonGenericSelfhostedMethod<Is<WeakSetObject>>, 2, 0),
 
     // See builtin/TypedObject.h for descriptors of the typedobj functions.
     JS_FN("NewOpaqueTypedObject",           js::NewOpaqueTypedObject, 1, 0),
     JS_FN("NewDerivedTypedObject",          js::NewDerivedTypedObject, 3, 0),
     JS_FN("TypedObjectBuffer",              TypedObject::GetBuffer, 1, 0),
     JS_FN("TypedObjectByteOffset",          TypedObject::GetByteOffset, 1, 0),
     JS_FN("AttachTypedObject",              js::AttachTypedObject, 3, 0),
     JS_FN("TypedObjectIsAttached",          js::TypedObjectIsAttached, 1, 0),
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1200,19 +1200,20 @@ Shape::setObjectFlags(ExclusiveContext* 
 StackBaseShape::hash(const Lookup& lookup)
 {
     HashNumber hash = lookup.flags;
     hash = RotateLeft(hash, 4) ^ (uintptr_t(lookup.clasp) >> 3);
     return hash;
 }
 
 /* static */ inline bool
-StackBaseShape::match(UnownedBaseShape* key, const Lookup& lookup)
+StackBaseShape::match(ReadBarriered<UnownedBaseShape*> key, const Lookup& lookup)
 {
-    return key->flags == lookup.flags && key->clasp_ == lookup.clasp;
+    return key.unbarrieredGet()->flags == lookup.flags &&
+           key.unbarrieredGet()->clasp_ == lookup.clasp;
 }
 
 inline
 BaseShape::BaseShape(const StackBaseShape& base)
   : clasp_(base.clasp),
     compartment_(base.compartment),
     flags(base.flags),
     slotSpan_(0),
@@ -1446,17 +1447,17 @@ JSCompartment::checkInitialShapesTableAf
     /*
      * Assert that the postbarriers have worked and that nothing is left in
      * initialShapes that points into the nursery, and that the hash table
      * entries are discoverable.
      */
     for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
         InitialShapeEntry entry = e.front();
         TaggedProto proto = entry.proto;
-        Shape* shape = entry.shape.get();
+        Shape* shape = entry.shape.unbarrieredGet();
 
         if (proto.isObject())
             CheckGCThingAfterMovingGC(proto.toObject());
 
         InitialShapeEntry::Lookup lookup(shape->getObjectClass(),
                                          proto,
                                          shape->numFixedSlots(),
                                          shape->getObjectFlags());
@@ -1626,29 +1627,29 @@ void
 JSCompartment::fixupInitialShapeTable()
 {
     if (!initialShapes.initialized())
         return;
 
     for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
         InitialShapeEntry entry = e.front();
         bool needRekey = false;
-        if (IsForwarded(entry.shape.get())) {
-            entry.shape.set(Forwarded(entry.shape.get()));
+        if (IsForwarded(entry.shape.unbarrieredGet())) {
+            entry.shape.set(Forwarded(entry.shape.unbarrieredGet()));
             needRekey = true;
         }
         if (entry.proto.isObject() && IsForwarded(entry.proto.toObject())) {
             entry.proto = TaggedProto(Forwarded(entry.proto.toObject()));
             needRekey = true;
         }
         if (needRekey) {
-            InitialShapeEntry::Lookup relookup(entry.shape->getObjectClass(),
+            InitialShapeEntry::Lookup relookup(entry.shape.unbarrieredGet()->getObjectClass(),
                                                entry.proto,
-                                               entry.shape->numFixedSlots(),
-                                               entry.shape->getObjectFlags());
+                                               entry.shape.unbarrieredGet()->numFixedSlots(),
+                                               entry.shape.unbarrieredGet()->getObjectFlags());
             e.rekeyFront(relookup, entry);
         }
     }
 }
 
 void
 AutoRooterGetterSetter::Inner::trace(JSTracer* trc)
 {
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -476,17 +476,17 @@ BaseShape::toUnowned()
 UnownedBaseShape*
 BaseShape::baseUnowned()
 {
     MOZ_ASSERT(isOwned() && unowned_);
     return unowned_;
 }
 
 /* Entries for the per-compartment baseShapes set of unowned base shapes. */
-struct StackBaseShape : public DefaultHasher<ReadBarrieredUnownedBaseShape>
+struct StackBaseShape : public DefaultHasher<ReadBarriered<UnownedBaseShape*>>
 {
     uint32_t flags;
     const Class* clasp;
     JSCompartment* compartment;
 
     explicit StackBaseShape(BaseShape* base)
       : flags(base->flags & BaseShape::OBJECT_FLAG_MASK),
         clasp(base->clasp_),
@@ -508,20 +508,20 @@ struct StackBaseShape : public DefaultHa
         MOZ_IMPLICIT Lookup(UnownedBaseShape* base)
           : flags(base->getObjectFlags()), clasp(base->clasp())
         {
             MOZ_ASSERT(!base->isOwned());
         }
     };
 
     static inline HashNumber hash(const Lookup& lookup);
-    static inline bool match(UnownedBaseShape* key, const Lookup& lookup);
+    static inline bool match(ReadBarriered<UnownedBaseShape*> key, const Lookup& lookup);
 };
 
-typedef HashSet<ReadBarrieredUnownedBaseShape,
+typedef HashSet<ReadBarriered<UnownedBaseShape*>,
                 StackBaseShape,
                 SystemAllocPolicy> BaseShapeSet;
 
 
 class Shape : public gc::TenuredCell
 {
     friend class ::JSObject;
     friend class ::JSFunction;
--- a/js/src/vm/Symbol.cpp
+++ b/js/src/vm/Symbol.cpp
@@ -104,21 +104,21 @@ Symbol::dump(FILE* fp)
     }
 }
 #endif  // DEBUG
 
 void
 SymbolRegistry::sweep()
 {
     for (Enum e(*this); !e.empty(); e.popFront()) {
-        Symbol* sym = e.front();
-        if (IsAboutToBeFinalizedUnbarriered(&sym))
+        mozilla::DebugOnly<Symbol*> sym = e.front().unbarrieredGet();
+        if (IsAboutToBeFinalized(&e.mutableFront()))
             e.removeFront();
         else
-            MOZ_ASSERT(sym == e.front());
+            MOZ_ASSERT(sym == e.front().unbarrieredGet());
     }
 }
 
 bool
 js::SymbolDescriptiveString(JSContext* cx, Symbol* sym, MutableHandleValue result)
 {
     // steps 2-5
     StringBuffer sb(cx);
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -3376,17 +3376,17 @@ PreliminaryObjectArray::sweep()
         if (*ptr && IsAboutToBeFinalizedUnbarriered(ptr)) {
             // Before we clear this reference, change the object's group to the
             // Object.prototype group. This is done to ensure JSObject::finalize
             // sees a NativeObject Class even if we change the current group's
             // Class to one of the unboxed object classes in the meantime. If
             // the compartment's global is dead, we don't do anything as the
             // group's Class is not going to change in that case.
             JSObject* obj = *ptr;
-            GlobalObject* global = obj->compartment()->maybeGlobal();
+            GlobalObject* global = obj->compartment()->unsafeUnbarrieredMaybeGlobal();
             if (global && !obj->isSingleton()) {
                 JSObject* objectProto = GetBuiltinPrototypePure(global, JSProto_Object);
                 obj->setGroup(objectProto->groupRaw());
                 MOZ_ASSERT(obj->is<NativeObject>());
                 MOZ_ASSERT(obj->getClass() == objectProto->getClass());
                 MOZ_ASSERT(!obj->getClass()->finalize);
             }
 
@@ -4069,17 +4069,18 @@ ConstraintTypeSet::sweep(Zone* zone, Aut
                     *pentry = key;
                 } else {
                     oom.setOOM();
                     flags |= TYPE_FLAG_ANYOBJECT;
                     clearObjects();
                     objectCount = 0;
                     break;
                 }
-            } else if (key->isGroup() && key->group()->unknownPropertiesDontCheckGeneration()) {
+            } else if (key->isGroup() &&
+                       key->groupNoBarrier()->unknownPropertiesDontCheckGeneration()) {
                 // Object sets containing objects with unknown properties might
                 // not be complete. Mark the type set as unknown, which it will
                 // be treated as during Ion compilation.
                 //
                 // Note that we don't have to do this when the type set might
                 // be missing the native group corresponding to an unboxed
                 // object group. In this case, the native group points to the
                 // unboxed object group via its addendum, so as long as objects
@@ -4093,17 +4094,17 @@ ConstraintTypeSet::sweep(Zone* zone, Aut
         setBaseObjectCount(objectCount);
     } else if (objectCount == 1) {
         ObjectKey* key = (ObjectKey*) objectSet;
         if (!IsObjectKeyAboutToBeFinalized(&key)) {
             objectSet = reinterpret_cast<ObjectKey**>(key);
         } else {
             // As above, mark type sets containing objects with unknown
             // properties as unknown.
-            if (key->isGroup() && key->group()->unknownPropertiesDontCheckGeneration())
+            if (key->isGroup() && key->groupNoBarrier()->unknownPropertiesDontCheckGeneration())
                 flags |= TYPE_FLAG_ANYOBJECT;
             objectSet = nullptr;
             setBaseObjectCount(0);
         }
     }
 
     /*
      * Type constraints only hold weak references. Copy constraints referring
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -7700,91 +7700,77 @@ nsRuleNode::ComputePositionData(void* aS
   // We allow the enumerated box size property values -moz-min-content, etc. to
   // be specified on both the {,min-,max-}width properties and the
   // {,min-,max-}height properties, regardless of the writing mode.  This is
   // because the writing mode is not determined until here, at computed value
   // time.  Since we do not support layout behavior of these keywords on the
   // block-axis properties, we turn them into unset if we find them in
   // that case.
 
-  bool vertical;
-  switch (aContext->StyleVisibility()->mWritingMode) {
-    default:
-      MOZ_ASSERT(false, "unexpected writing-mode value");
-      // fall through
-    case NS_STYLE_WRITING_MODE_HORIZONTAL_TB:
-      vertical = false;
-      break;
-    case NS_STYLE_WRITING_MODE_VERTICAL_RL:
-    case NS_STYLE_WRITING_MODE_VERTICAL_LR:
-    case NS_STYLE_WRITING_MODE_SIDEWAYS_RL:
-    case NS_STYLE_WRITING_MODE_SIDEWAYS_LR:
-      vertical = true;
-      break;
-  }
-  uint8_t wm = WritingMode(aContext).GetBits();
+  WritingMode wm(aContext);
+  bool vertical = wm.IsVertical();
 
   const nsCSSValue* width = aRuleData->ValueForWidth();
   if (width->GetUnit() == eCSSUnit_Enumerated) {
-    conditions.SetWritingModeDependency(wm);
+    conditions.SetWritingModeDependency(wm.GetBits());
   }
   SetCoord(width->GetUnit() == eCSSUnit_Enumerated && vertical ?
              nsCSSValue(eCSSUnit_Unset) : *width,
            pos->mWidth, parentPos->mWidth,
            SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC |
              SETCOORD_UNSET_INITIAL,
            aContext, mPresContext, conditions);
 
   const nsCSSValue* minWidth = aRuleData->ValueForMinWidth();
   if (minWidth->GetUnit() == eCSSUnit_Enumerated) {
-    conditions.SetWritingModeDependency(wm);
+    conditions.SetWritingModeDependency(wm.GetBits());
   }
   SetCoord(minWidth->GetUnit() == eCSSUnit_Enumerated && vertical ?
              nsCSSValue(eCSSUnit_Unset) : *minWidth,
            pos->mMinWidth, parentPos->mMinWidth,
            SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC |
              SETCOORD_UNSET_INITIAL,
            aContext, mPresContext, conditions);
 
   const nsCSSValue* maxWidth = aRuleData->ValueForMaxWidth();
   if (maxWidth->GetUnit() == eCSSUnit_Enumerated) {
-    conditions.SetWritingModeDependency(wm);
+    conditions.SetWritingModeDependency(wm.GetBits());
   }
   SetCoord(maxWidth->GetUnit() == eCSSUnit_Enumerated && vertical ?
              nsCSSValue(eCSSUnit_Unset) : *maxWidth,
            pos->mMaxWidth, parentPos->mMaxWidth,
            SETCOORD_LPOEH | SETCOORD_INITIAL_NONE | SETCOORD_STORE_CALC |
              SETCOORD_UNSET_INITIAL,
            aContext, mPresContext, conditions);
 
   const nsCSSValue* height = aRuleData->ValueForHeight();
   if (height->GetUnit() == eCSSUnit_Enumerated) {
-    conditions.SetWritingModeDependency(wm);
+    conditions.SetWritingModeDependency(wm.GetBits());
   }
   SetCoord(height->GetUnit() == eCSSUnit_Enumerated && !vertical ?
              nsCSSValue(eCSSUnit_Unset) : *height,
            pos->mHeight, parentPos->mHeight,
            SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC |
              SETCOORD_UNSET_INITIAL,
            aContext, mPresContext, conditions);
 
   const nsCSSValue* minHeight = aRuleData->ValueForMinHeight();
   if (minHeight->GetUnit() == eCSSUnit_Enumerated) {
-    conditions.SetWritingModeDependency(wm);
+    conditions.SetWritingModeDependency(wm.GetBits());
   }
   SetCoord(minHeight->GetUnit() == eCSSUnit_Enumerated && !vertical ?
              nsCSSValue(eCSSUnit_Unset) : *minHeight,
            pos->mMinHeight, parentPos->mMinHeight,
            SETCOORD_LPAEH | SETCOORD_INITIAL_AUTO | SETCOORD_STORE_CALC |
              SETCOORD_UNSET_INITIAL,
            aContext, mPresContext, conditions);
 
   const nsCSSValue* maxHeight = aRuleData->ValueForMaxHeight();
   if (maxHeight->GetUnit() == eCSSUnit_Enumerated) {
-    conditions.SetWritingModeDependency(wm);
+    conditions.SetWritingModeDependency(wm.GetBits());
   }
   SetCoord(maxHeight->GetUnit() == eCSSUnit_Enumerated && !vertical ?
              nsCSSValue(eCSSUnit_Unset) : *maxHeight,
            pos->mMaxHeight, parentPos->mMaxHeight,
            SETCOORD_LPOEH | SETCOORD_INITIAL_NONE | SETCOORD_STORE_CALC |
              SETCOORD_UNSET_INITIAL,
            aContext, mPresContext, conditions);
 
--- a/media/libstagefright/binding/MP4Metadata.rs
+++ b/media/libstagefright/binding/MP4Metadata.rs
@@ -1,137 +1,170 @@
 // Module for parsing ISO Base Media Format aka video/mp4 streams.
 
 // 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 https://mozilla.org/MPL/2.0/.
 
+use std::fmt;
+
+/// Expose C api wrapper.
+pub mod capi;
+// FIXME: We can 'pub use capi::*' in rustc 1.5 and later.
+pub use capi::{mp4parse_new, mp4parse_free, mp4parse_read};
+
+#[derive(Clone, Copy, Eq, PartialEq)]
+pub struct FourCC(pub u32);
+
+impl fmt::Debug for FourCC {
+  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    write!(f, "'{}'", fourcc_to_string(*self))
+  }
+}
+
 /// Basic ISO box structure.
+#[derive(Debug)]
 pub struct BoxHeader {
     /// Four character box type
-    pub name: u32,
+    pub name: FourCC,
     /// Size of the box in bytes
     pub size: u64,
     /// Offset to the start of the contained data (or header size).
     pub offset: u64,
 }
 
 /// File type box 'ftyp'.
+#[derive(Debug)]
 pub struct FileTypeBox {
-    name: u32,
+    name: FourCC,
     size: u64,
-    major_brand: u32,
+    major_brand: FourCC,
     minor_version: u32,
-    compatible_brands: Vec<u32>,
+    compatible_brands: Vec<FourCC>,
 }
 
 /// Movie header box 'mvhd'.
+#[derive(Debug)]
 pub struct MovieHeaderBox {
-    pub name: u32,
+    pub name: FourCC,
     pub size: u64,
     pub timescale: u32,
     pub duration: u64,
     // Ignore other fields.
 }
 
 /// Track header box 'tkhd'
+#[derive(Debug)]
 pub struct TrackHeaderBox {
-    pub name: u32,
+    pub name: FourCC,
     pub size: u64,
     pub track_id: u32,
     pub enabled: bool,
     pub duration: u64,
     pub width: u32,
     pub height: u32,
 }
 
 /// Edit list box 'elst'
+#[derive(Debug)]
 pub struct EditListBox {
-    name: u32,
+    name: FourCC,
     size: u64,
     edits: Vec<Edit>,
 }
 
+#[derive(Debug)]
 pub struct Edit {
     segment_duration: u64,
     media_time: i64,
     media_rate_integer: i16,
     media_rate_fraction: i16,
 }
 
 /// Media header box 'mdhd'
+#[derive(Debug)]
 pub struct MediaHeaderBox {
-    name: u32,
+    name: FourCC,
     size: u64,
     timescale: u32,
     duration: u64,
 }
 
 // Chunk offset box 'stco' or 'co64'
+#[derive(Debug)]
 pub struct ChunkOffsetBox {
-    name: u32,
+    name: FourCC,
     size: u64,
     offsets: Vec<u64>,
 }
 
 // Sync sample box 'stss'
+#[derive(Debug)]
 pub struct SyncSampleBox {
-    name: u32,
+    name: FourCC,
     size: u64,
     samples: Vec<u32>,
 }
 
 // Sample to chunk box 'stsc'
+#[derive(Debug)]
 pub struct SampleToChunkBox {
-    name: u32,
+    name: FourCC,
     size: u64,
     samples: Vec<SampleToChunk>,
 }
 
+#[derive(Debug)]
 pub struct SampleToChunk {
     first_chunk: u32,
     samples_per_chunk: u32,
     sample_description_index: u32,
 }
 
 // Sample size box 'stsz'
+#[derive(Debug)]
 pub struct SampleSizeBox {
-    name: u32,
+    name: FourCC,
     size: u64,
     sample_size: u32,
     sample_sizes: Vec<u32>,
 }
 
 // Time to sample box 'stts'
+#[derive(Debug)]
 pub struct TimeToSampleBox {
-    name: u32,
+    name: FourCC,
     size: u64,
     samples: Vec<Sample>,
 }
 
+#[derive(Debug)]
 pub struct Sample {
     sample_count: u32,
     sample_delta: u32,
 }
 
 // Handler reference box 'hdlr'
+#[derive(Debug)]
 pub struct HandlerBox {
-    name: u32,
+    name: FourCC,
     size: u64,
-    handler_type: u32,
+    handler_type: FourCC,
 }
 
 // Sample description box 'stsd'
+#[derive(Debug)]
 pub struct SampleDescriptionBox {
-    name: u32,
+    name: FourCC,
     size: u64,
     descriptions: Vec<SampleEntry>,
 }
 
 #[allow(dead_code)]
+#[derive(Debug)]
 enum SampleEntry {
     Audio {
         data_reference_index: u16,
         channelcount: u16,
         samplesize: u16,
         samplerate: u32,
         esds: ES_Descriptor,
     },
@@ -139,50 +172,63 @@ enum SampleEntry {
         data_reference_index: u16,
         width: u16,
         height: u16,
         avcc: AVCDecoderConfigurationRecord,
     },
 }
 
 #[allow(dead_code)]
+#[derive(Debug)]
 pub struct AVCDecoderConfigurationRecord {
     data: Vec<u8>,
 }
 
 #[allow(non_camel_case_types)]
 #[allow(dead_code)]
+#[derive(Debug)]
 pub struct ES_Descriptor {
     data: Vec<u8>,
 }
 
 /// Internal data structures.
+#[derive(Debug)]
 pub struct MediaContext {
-    pub tracks: Vec<Track>,
+    tracks: Vec<Track>,
 }
 
+impl MediaContext {
+    pub fn new() -> MediaContext {
+        MediaContext {
+            tracks: Vec::new(),
+        }
+    }
+}
+
+#[derive(Debug)]
 enum TrackType {
     Video,
     Audio
 }
 
+#[derive(Debug)]
 pub struct Track {
     track_type: TrackType,
 }
 
 mod byteorder; // 'extern crate' upstream.
 use byteorder::{BigEndian, ReadBytesExt};
 use std::io::{Read, BufRead, Take};
 use std::io::Cursor;
 use std::cmp;
 
 /// Parse a box out of a data buffer.
 pub fn read_box_header<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<BoxHeader> {
     let size32 = try!(be_u32(src));
-    let name = try!(be_u32(src));
+    let name = FourCC(try!(be_u32(src)));
     let size = match size32 {
         0 => panic!("unknown box size not implemented"),
         1 => {
             let size64 = try!(be_u64(src));
             assert!(size64 >= 16);
             size64
         },
         2 ... 7 => panic!("invalid box size"),
@@ -227,17 +273,17 @@ pub fn skip_remaining_box_content<T: Buf
 /// Helper to construct a Take over the contents of a box.
 fn limit<'a, T: Read>(f: &'a mut T, h: &BoxHeader) -> Take<&'a mut T> {
     f.take(h.size - h.offset)
 }
 
 /// Helper to construct a Cursor over the contents of a box.
 fn recurse<T: Read>(f: &mut T, h: &BoxHeader, context: &mut MediaContext) -> byteorder::Result<()> {
     use std::error::Error;
-    println!("{} -- recursing", h);
+    println!("{:?} -- recursing", h);
     // FIXME: I couldn't figure out how to do this without copying.
     // We use Seek on the Read we return in skip_box_content, but
     // that trait isn't implemented for a Take like our limit()
     // returns. Slurping the buffer and wrapping it in a Cursor
     // functions as a work around.
     let buf: Vec<u8> = f
         .bytes()
         .map(|u| u.unwrap())
@@ -249,159 +295,123 @@ fn recurse<T: Read>(f: &mut T, h: &BoxHe
             Err(byteorder::Error::UnexpectedEOF) => {
                 // byteorder returns EOF at the end of the buffer.
                 // This isn't an error for us, just an signal to
                 // stop recursion.
                 println!("Caught byteorder::Error::UnexpectedEOF");
                 break;
             },
             Err(byteorder::Error::Io(e)) => {
-                println!("I/O Error '{:?}' reading box: {}",
+                println!("I/O Error '{:?}' reading box: {:?}",
                          e.kind(), e.description());
                 return Err(byteorder::Error::Io(e));
             },
         }
     }
     assert!(content.position() == h.size - h.offset);
-    println!("{} -- end", h);
+    println!("{:?} -- end", h);
     Ok(())
 }
 
 /// Read the contents of a box, including sub boxes.
 /// Metadata is accumulated in the passed-through MediaContext struct.
 pub fn read_box<T: BufRead>(f: &mut T, context: &mut MediaContext) -> byteorder::Result<()> {
     read_box_header(f).and_then(|h| {
         let mut content = limit(f, &h);
         match &fourcc_to_string(h.name)[..] {
             "ftyp" => {
                 let ftyp = try!(read_ftyp(&mut content, &h));
-                println!("{}", ftyp);
+                println!("{:?}", ftyp);
             },
             "moov" => try!(recurse(&mut content, &h, context)),
             "mvhd" => {
                 let mvhd = try!(read_mvhd(&mut content, &h));
-                println!("  {}", mvhd);
+                println!("  {:?}", mvhd);
             },
             "trak" => try!(recurse(&mut content, &h, context)),
             "tkhd" => {
                 let tkhd = try!(read_tkhd(&mut content, &h));
-                println!("  {}", tkhd);
+                println!("  {:?}", tkhd);
             },
             "edts" => try!(recurse(&mut content, &h, context)),
             "elst" => {
                 let elst = try!(read_elst(&mut content, &h));
-                println!("  {}", elst);
+                println!("  {:?}", elst);
             },
             "mdia" => try!(recurse(&mut content, &h, context)),
             "mdhd" => {
                 let mdhd = try!(read_mdhd(&mut content, &h));
-                println!("  {}", mdhd);
+                println!("  {:?}", mdhd);
             },
             "minf" => try!(recurse(&mut content, &h, context)),
             "stbl" => try!(recurse(&mut content, &h, context)),
             "stco" => {
                 let stco = try!(read_stco(&mut content, &h));
-                println!("  {}", stco);
+                println!("  {:?}", stco);
             },
             "co64" => {
                 let co64 = try!(read_co64(&mut content, &h));
-                println!("  {}", co64);
+                println!("  {:?}", co64);
             },
             "stss" => {
                 let stss = try!(read_stss(&mut content, &h));
-                println!("  {}", stss);
+                println!("  {:?}", stss);
             },
             "stsc" => {
                 let stsc = try!(read_stsc(&mut content, &h));
-                println!("  {}", stsc);
+                println!("  {:?}", stsc);
             },
             "stsz" => {
                 let stsz = try!(read_stsz(&mut content, &h));
-                println!("  {}", stsz);
+                println!("  {:?}", stsz);
             },
             "stts" => {
                 let stts = try!(read_stts(&mut content, &h));
-                println!("  {}", stts);
+                println!("  {:?}", stts);
             },
             "hdlr" => {
                 let hdlr = try!(read_hdlr(&mut content, &h));
                 let track_type = match &fourcc_to_string(hdlr.handler_type)[..] {
                     "vide" => Some(TrackType::Video),
                     "soun" => Some(TrackType::Audio),
                     _ => None
                 };
                 // Save track types with recognized types.
                 match track_type {
                     Some(track_type) =>
                          context.tracks.push(Track { track_type: track_type }),
                     None => println!("unknown track type!"),
                 };
+                println!("  {:?}", hdlr);
             },
             "stsd" => {
                 let track = &context.tracks[context.tracks.len() - 1];
                 let stsd = try!(read_stsd(&mut content, &h, &track));
-                println!("  {}", stsd);
+                println!("  {:?}", stsd);
             },
             _ => {
                 // Skip the contents of unknown chunks.
-                println!("{} (skipped)", h);
+                println!("{:?} (skipped)", h);
                 try!(skip_box_content(&mut content, &h));
             },
         };
         assert!(content.limit() == 0);
-        println!("read_box context: {}", context);
+        println!("read_box context: {:?}", context);
         Ok(()) // and_then needs a Result to return.
     })
 }
 
-/// Entry point for C language callers.
-/// Take a buffer and call read_box() on it,
-/// returning the number of detected tracks.
-#[no_mangle]
-pub extern fn read_box_from_buffer(buffer: *const u8, size: usize) -> i32 {
-    use std::slice;
-    use std::thread;
-    use std::i32;
-
-    // Validate arguments from C.
-    if buffer.is_null() || size < 8 {
-        return -1;
-    }
-
-    // Wrap the buffer we've been give in a slice.
-    let b = unsafe { slice::from_raw_parts(buffer, size) };
-    let mut c = Cursor::new(b);
-
-    // Parse in a subthread.
-    let task = thread::spawn(move || {
-        let mut context = MediaContext { tracks: Vec::new() };
-        loop {
-            match read_box(&mut c, &mut context) {
-                Ok(_) => {},
-                Err(byteorder::Error::UnexpectedEOF) => { break },
-                Err(e) => { panic!(e) },
-            }
-        }
-        // Make sure the track count fits in an i32 so we can use
-        // negative values for failure.
-        assert!(context.tracks.len() < i32::MAX as usize);
-        context.tracks.len() as i32
-    });
-    // Catch any panics.
-    task.join().unwrap_or(-1)
-}
-
 /// Parse an ftyp box.
 pub fn read_ftyp<T: ReadBytesExt>(src: &mut T, head: &BoxHeader) -> byteorder::Result<FileTypeBox> {
-    let major = try!(be_u32(src));
+    let major = FourCC(try!(be_u32(src)));
     let minor = try!(be_u32(src));
     let brand_count = (head.size - 8 - 8) / 4;
     let mut brands = Vec::new();
     for _ in 0..brand_count {
-        brands.push(try!(be_u32(src)));
+        brands.push(FourCC(try!(be_u32(src))));
     }
     Ok(FileTypeBox{
         name: head.name,
         size: head.size,
         major_brand: major,
         minor_version: minor,
         compatible_brands: brands,
     })
@@ -650,17 +660,17 @@ pub fn read_stts<T: ReadBytesExt>(src: &
 
 /// Parse a hdlr box.
 pub fn read_hdlr<T: ReadBytesExt + BufRead>(src: &mut T, head: &BoxHeader) -> byteorder::Result<HandlerBox> {
     let (_, _) = try!(read_fullbox_extra(src));
 
     // Skip uninteresting fields.
     try!(skip(src, 4));
 
-    let handler_type = try!(be_u32(src));
+    let handler_type = FourCC(try!(be_u32(src)));
 
     // Skip uninteresting fields.
     try!(skip(src, 12));
 
     // TODO(kinetik): Find a copy of ISO/IEC 14496-1 to work out how strings are encoded.
     // As a hack, just consume the rest of the box.
     try!(skip_remaining_box_content(src, head));
 
@@ -769,24 +779,24 @@ pub fn read_stsd<T: ReadBytesExt + BufRe
     Ok(SampleDescriptionBox{
         name: head.name,
         size: head.size,
         descriptions: descriptions,
     })
 }
 
 /// Convert the iso box type or other 4-character value to a string.
-fn fourcc_to_string(name: u32) -> String {
+fn fourcc_to_string(name: FourCC) -> String {
     let u32_to_vec = |u| {
         vec!((u >> 24 & 0xffu32) as u8,
              (u >> 16 & 0xffu32) as u8,
              (u >>  8 & 0xffu32) as u8,
              (u & 0xffu32) as u8)
     };
-    let name_bytes = u32_to_vec(name);
+    let name_bytes = u32_to_vec(name.0);
     String::from_utf8_lossy(&name_bytes).into_owned()
 }
 
 /// Skip a number of bytes that we don't care to parse.
 fn skip<T: BufRead>(src: &mut T, bytes: usize) -> byteorder::Result<usize> {
     let mut bytes_to_skip = bytes;
     while bytes_to_skip > 0 {
         let len = {
@@ -823,181 +833,41 @@ fn be_u16<T: ReadBytesExt>(src: &mut T) 
 fn be_u32<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<u32> {
     src.read_u32::<BigEndian>()
 }
 
 fn be_u64<T: ReadBytesExt>(src: &mut T) -> byteorder::Result<u64> {
     src.read_u64::<BigEndian>()
 }
 
-use std::fmt;
-impl fmt::Display for BoxHeader {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "'{}' {} bytes", fourcc_to_string(self.name), self.size)
-    }
-}
-
-impl fmt::Display for FileTypeBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let name = fourcc_to_string(self.name);
-        let brand = fourcc_to_string(self.major_brand);
-        let mut compat = String::from("compatible with");
-        for brand in &self.compatible_brands {
-            let brand_string = fourcc_to_string(*brand);
-            compat.push(' ');
-            compat.push_str(&brand_string);
-        }
-        write!(f, "'{}' {} bytes '{}' v{}\n {}",
-               name, self.size, brand, self.minor_version, compat)
-    }
-}
-
-impl fmt::Display for MovieHeaderBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let name = fourcc_to_string(self.name);
-        write!(f, "'{}' {} bytes duration {}s", name, self.size,
-               (self.duration as f64)/(self.timescale as f64))
-    }
-}
-
-impl fmt::Display for MediaContext {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "Found {} tracks.", self.tracks.len())
-    }
-}
-
-use std::u16;
-impl fmt::Display for TrackHeaderBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let name = fourcc_to_string(self.name);
-        // Dimensions are 16.16 fixed-point.
-        let base = u16::MAX as f64 + 1.0;
-        let width = (self.width as f64) / base;
-        let height = (self.height as f64) / base;
-        let disabled = if self.enabled { "" } else { " (disabled)" };
-        write!(f, "'{}' {} bytes duration {} id {} {}x{}{}",
-               name, self.size, self.duration, self.track_id,
-               width, height, disabled)
-    }
-}
-
-impl fmt::Display for EditListBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let mut entries = String::new();
-        for entry in &self.edits {
-            entries.push_str(&format!("\n  duration {} time {} rate {}/{}",
-                                      entry.segment_duration, entry.media_time,
-                                      entry.media_rate_integer, entry.media_rate_fraction));
-        }
-        write!(f, "'{}' {} bytes {}", fourcc_to_string(self.name), self.size, entries)
-    }
-}
-
-impl fmt::Display for MediaHeaderBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "'{}' {} bytes timescale {} duration {}",
-               fourcc_to_string(self.name), self.size, self.timescale, self.duration)
-    }
-}
-
-impl fmt::Display for ChunkOffsetBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let mut entries = String::new();
-        for entry in &self.offsets {
-            entries.push_str(&format!("\n  offset {}", entry));
-        }
-        write!(f, "'{}' {} bytes {}",
-               fourcc_to_string(self.name), self.size, entries)
-    }
-}
-
-impl fmt::Display for SyncSampleBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let mut entries = String::new();
-        for entry in &self.samples {
-            entries.push_str(&format!("\n  sample {}", entry));
-        }
-        write!(f, "'{}' {} bytes {}",
-               fourcc_to_string(self.name), self.size, entries)
-    }
-}
-
-impl fmt::Display for SampleToChunkBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let mut entries = String::new();
-        for entry in &self.samples {
-            entries.push_str(&format!("\n  sample chunk {} {} {}",
-                                      entry.first_chunk, entry.samples_per_chunk, entry.sample_description_index));
-        }
-        write!(f, "'{}' {} bytes {}",
-               fourcc_to_string(self.name), self.size, entries)
-    }
-}
-
-impl fmt::Display for SampleSizeBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let mut entries = String::new();
-        for entry in &self.sample_sizes {
-            entries.push_str(&format!("\n  sample size {}", entry));
-        }
-        write!(f, "'{}' {} bytes sample size {} {}",
-               fourcc_to_string(self.name), self.size, self.sample_size, entries)
-    }
-}
-
-impl fmt::Display for TimeToSampleBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        let mut entries = String::new();
-        for entry in &self.samples {
-            entries.push_str(&format!("\n  sample count {} delta {}", entry.sample_count, entry.sample_delta));
-        }
-        write!(f, "'{}' {} bytes sample {}",
-               fourcc_to_string(self.name), self.size, entries)
-    }
-}
-
-impl fmt::Display for HandlerBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "'{}' {} bytes handler_type '{}'",
-               fourcc_to_string(self.name), self.size, fourcc_to_string(self.handler_type))
-    }
-}
-
-impl fmt::Display for SampleDescriptionBox {
-    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        write!(f, "'{}' {} bytes descriptions {}",
-               fourcc_to_string(self.name), self.size, self.descriptions.len())
-    }
-}
-
 #[test]
 fn test_read_box_header() {
     use std::io::Cursor;
     use std::io::Write;
     let mut test: Vec<u8> = vec![0, 0, 0, 8]; // minimal box length
     write!(&mut test, "test").unwrap(); // box type
     let mut stream = Cursor::new(test);
     let parsed = read_box_header(&mut stream).unwrap();
-    assert_eq!(parsed.name, 1952805748);
+    assert_eq!(parsed.name, FourCC(1952805748));
     assert_eq!(parsed.size, 8);
-    println!("box {}", parsed);
+    println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_box_header_long() {
     use std::io::Cursor;
     let mut test: Vec<u8> = vec![0, 0, 0, 1]; // long box extension code
     test.extend("long".to_string().into_bytes()); // box type
     test.extend(vec![0, 0, 0, 0, 0, 0, 16, 0]); // 64 bit size
     // Skip generating box content.
     let mut stream = Cursor::new(test);
     let parsed = read_box_header(&mut stream).unwrap();
-    assert_eq!(parsed.name, 1819242087);
+    assert_eq!(parsed.name, FourCC(1819242087));
     assert_eq!(parsed.size, 4096);
-    println!("box {}", parsed);
+    println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_ftyp() {
     use std::io::Cursor;
     use std::io::Write;
     let mut test: Vec<u8> = vec![0, 0, 0, 24]; // size
     write!(&mut test, "ftyp").unwrap(); // type
@@ -1005,24 +875,24 @@ fn test_read_ftyp() {
     test.extend(vec![0, 0, 0, 0]);      // minor version
     write!(&mut test, "isom").unwrap(); // compatible brands...
     write!(&mut test, "mp42").unwrap();
     assert_eq!(test.len(), 24);
 
     let mut stream = Cursor::new(test);
     let header = read_box_header(&mut stream).unwrap();
     let parsed = read_ftyp(&mut stream, &header).unwrap();
-    assert_eq!(parsed.name, 1718909296);
+    assert_eq!(parsed.name, FourCC(1718909296));
     assert_eq!(parsed.size, 24);
-    assert_eq!(parsed.major_brand, 1836069938);
+    assert_eq!(parsed.major_brand, FourCC(1836069938));
     assert_eq!(parsed.minor_version, 0);
     assert_eq!(parsed.compatible_brands.len(), 2);
-    assert_eq!(parsed.compatible_brands[0], 1769172845);
+    assert_eq!(parsed.compatible_brands[0], FourCC(1769172845));
     assert_eq!(fourcc_to_string(parsed.compatible_brands[1]), "mp42");
-    println!("box {}", parsed);
+    println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_elst_v0() {
     use std::io::Cursor;
     use std::io::Write;
     let mut test: Vec<u8> = vec![0, 0, 0, 28]; // size
     write!(&mut test, "elst").unwrap(); // type
@@ -1032,24 +902,24 @@ fn test_read_elst_v0() {
                      5, 6, 7, 8,
                      9, 10,
                      11, 12]);
     assert_eq!(test.len(), 28);
 
     let mut stream = Cursor::new(test);
     let header = read_box_header(&mut stream).unwrap();
     let parsed = read_elst(&mut stream, &header).unwrap();
-    assert_eq!(parsed.name, 1701606260);
+    assert_eq!(parsed.name, FourCC(1701606260));
     assert_eq!(parsed.size, 28);
     assert_eq!(parsed.edits.len(), 1);
     assert_eq!(parsed.edits[0].segment_duration, 16909060);
     assert_eq!(parsed.edits[0].media_time, 84281096);
     assert_eq!(parsed.edits[0].media_rate_integer, 2314);
     assert_eq!(parsed.edits[0].media_rate_fraction, 2828);
-    println!("box {}", parsed);
+    println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_elst_v1() {
     use std::io::Cursor;
     use std::io::Write;
     let mut test: Vec<u8> = vec![0, 0, 0, 56]; // size
     write!(&mut test, "elst").unwrap(); // type
@@ -1063,24 +933,24 @@ fn test_read_elst_v1() {
                      5, 6, 7, 8, 5, 6, 7, 8,
                      9, 10,
                      11, 12]);
     assert_eq!(test.len(), 56);
 
     let mut stream = Cursor::new(test);
     let header = read_box_header(&mut stream).unwrap();
     let parsed = read_elst(&mut stream, &header).unwrap();
-    assert_eq!(parsed.name, 1701606260);
+    assert_eq!(parsed.name, FourCC(1701606260));
     assert_eq!(parsed.size, 56);
     assert_eq!(parsed.edits.len(), 2);
     assert_eq!(parsed.edits[1].segment_duration, 72623859723010820);
     assert_eq!(parsed.edits[1].media_time, 361984551075317512);
     assert_eq!(parsed.edits[1].media_rate_integer, 2314);
     assert_eq!(parsed.edits[1].media_rate_fraction, 2828);
-    println!("box {}", parsed);
+    println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_mdhd_v0() {
     use std::io::Cursor;
     use std::io::Write;
     let mut test: Vec<u8> = vec![0, 0, 0, 32]; // size
     write!(&mut test, "mdhd").unwrap(); // type
@@ -1090,21 +960,21 @@ fn test_read_mdhd_v0() {
                      1, 2, 3, 4,
                      5, 6, 7, 8,
                      0, 0, 0, 0]);
     assert_eq!(test.len(), 32);
 
     let mut stream = Cursor::new(test);
     let header = read_box_header(&mut stream).unwrap();
     let parsed = read_mdhd(&mut stream, &header).unwrap();
-    assert_eq!(parsed.name, 1835296868);
+    assert_eq!(parsed.name, FourCC(1835296868));
     assert_eq!(parsed.size, 32);
     assert_eq!(parsed.timescale, 16909060);
     assert_eq!(parsed.duration, 84281096);
-    println!("box {}", parsed);
+    println!("box {:?}", parsed);
 }
 
 #[test]
 fn test_read_mdhd_v1() {
     use std::io::Cursor;
     use std::io::Write;
     let mut test: Vec<u8> = vec![0, 0, 0, 44]; // size
     write!(&mut test, "mdhd").unwrap(); // type
@@ -1114,14 +984,14 @@ fn test_read_mdhd_v1() {
                      1, 2, 3, 4,
                      5, 6, 7, 8, 5, 6, 7, 8,
                      0, 0, 0, 0]);
     assert_eq!(test.len(), 44);
 
     let mut stream = Cursor::new(test);
     let header = read_box_header(&mut stream).unwrap();
     let parsed = read_mdhd(&mut stream, &header).unwrap();
-    assert_eq!(parsed.name, 1835296868);
+    assert_eq!(parsed.name, FourCC(1835296868));
     assert_eq!(parsed.size, 44);
     assert_eq!(parsed.timescale, 16909060);
     assert_eq!(parsed.duration, 361984551075317512);
-    println!("box {}", parsed);
+    println!("box {:?}", parsed);
 }
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/capi.rs
@@ -0,0 +1,103 @@
+// C API for mp4parse module.
+// Parses ISO Base Media Format aka video/mp4 streams.
+
+// 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 https://mozilla.org/MPL/2.0/.
+
+use std;
+use std::io::Cursor;
+use byteorder;
+
+// Symbols we need from our rust api.
+use MediaContext;
+use read_box;
+
+/// Allocate an opaque rust-side parser context.
+#[no_mangle]
+pub extern "C" fn mp4parse_new() -> *mut MediaContext {
+    let context = Box::new(MediaContext::new());
+    unsafe {
+        // transmute is unsafe, but context is always valid.
+        std::mem::transmute(context)
+    }
+}
+
+/// Free a rust-side parser context.
+#[no_mangle]
+pub unsafe extern "C" fn mp4parse_free(context: *mut MediaContext) {
+    assert!(!context.is_null());
+    let _: Box<MediaContext> = std::mem::transmute(context);
+}
+
+/// Feed a buffer through read_box(), returning the number of detected tracks.
+#[no_mangle]
+pub unsafe extern "C" fn mp4parse_read(context: *mut MediaContext, buffer: *const u8, size: usize) -> i32 {
+    // Validate arguments from C.
+    if context.is_null() {
+        return -1;
+    }
+    if buffer.is_null() || size < 8 {
+        return -1;
+    }
+
+    let mut context: &mut MediaContext = &mut *context;
+
+    // Wrap the buffer we've been give in a slice.
+    let b = std::slice::from_raw_parts(buffer, size);
+    let mut c = Cursor::new(b);
+
+    // Parse in a subthread to catch any panics.
+    let task = std::thread::spawn(move || {
+        loop {
+            match read_box(&mut c, &mut context) {
+                Ok(_) => {},
+                Err(byteorder::Error::UnexpectedEOF) => { break },
+                Err(e) => { panic!(e); },
+            }
+        }
+        // Make sure the track count fits in an i32 so we can use
+        // negative values for failure.
+        assert!(context.tracks.len() < std::i32::MAX as usize);
+        context.tracks.len() as i32
+    });
+    task.join().unwrap_or(-1)
+}
+
+#[test]
+fn new_context() {
+    let context = mp4parse_new();
+    assert!(!context.is_null());
+    unsafe { mp4parse_free(context); }
+}
+
+#[test]
+#[should_panic(expected = "assertion failed")]
+fn free_null_context() {
+    unsafe { mp4parse_free(std::ptr::null_mut()); }
+}
+
+#[test]
+fn arg_validation() {
+    let null_buffer = std::ptr::null();
+    let null_context = std::ptr::null_mut();
+
+    let context = mp4parse_new();
+    assert!(!context.is_null());
+
+    let buffer = vec![0u8; 8];
+
+    unsafe {
+        assert_eq!(-1, mp4parse_read(null_context, null_buffer, 0));
+        assert_eq!(-1, mp4parse_read(context, null_buffer, 0));
+    }
+
+    for size in (0..buffer.len()) {
+        println!("testing buffer length {}", size);
+        unsafe {
+            assert_eq!(-1, mp4parse_read(context, buffer.as_ptr(), size));
+        }
+    }
+
+    unsafe { mp4parse_free(context); }
+}
new file mode 100644
--- /dev/null
+++ b/media/libstagefright/binding/include/mp4parse.h
@@ -0,0 +1,23 @@
+// 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 https://mozilla.org/MPL/2.0/.
+
+#ifndef _MP4PARSE_RUST_H
+#define _MP4PARSE_RUST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct mp4parse_state;
+
+struct mp4parse_state* mp4parse_new(void);
+void mp4parse_free(struct mp4parse_state* state);
+
+int32_t mp4parse_read(struct mp4parse_state* state, uint8_t *buffer, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- a/media/libstagefright/gtest/TestMP4Rust.cpp
+++ b/media/libstagefright/gtest/TestMP4Rust.cpp
@@ -1,50 +1,68 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gtest/gtest.h"
 #include "mp4_demuxer/MP4Metadata.h"
+#include "mp4parse.h"
 
 #include <stdint.h>
 #include <stdio.h>
 #include <vector>
 
-extern "C" int32_t read_box_from_buffer(uint8_t *buffer, size_t size);
-
 using namespace mp4_demuxer;
 using namespace mozilla;
 
 TEST(rust, MP4MetadataEmpty)
 {
   int32_t rv;
-  rv = read_box_from_buffer(nullptr, 0);
+
+  mp4parse_state* context = mp4parse_new();
+  ASSERT_NE(context, nullptr);
+
+  rv = mp4parse_read(nullptr, nullptr, 0);
+  EXPECT_EQ(rv, -1);
+  rv = mp4parse_read(context, nullptr, 0);
   EXPECT_EQ(rv, -1);
 
   size_t len = 4097;
-  rv = read_box_from_buffer(nullptr, len);
+  rv = mp4parse_read(nullptr, nullptr, len);
+  EXPECT_EQ(rv, -1);
+  rv = mp4parse_read(context, nullptr, len);
   EXPECT_EQ(rv, -1);
 
   std::vector<uint8_t> buf;
-  rv = read_box_from_buffer(buf.data(), buf.size());
+  rv = mp4parse_read(nullptr, buf.data(), buf.size());
+  EXPECT_EQ(rv, -1);
+  rv = mp4parse_read(context, buf.data(), buf.size());
   EXPECT_EQ(rv, -1);
 
   buf.reserve(len);
-  rv = read_box_from_buffer(buf.data(), buf.size());
+  rv = mp4parse_read(nullptr, buf.data(), buf.size());
   EXPECT_EQ(rv, -1);
+  rv = mp4parse_read(context, buf.data(), buf.size());
+  EXPECT_EQ(rv, -1);
+
+  mp4parse_free(context);
 }
 
 TEST(rust, MP4Metadata)
 {
   FILE* f = fopen("street.mp4", "rb");
   ASSERT_TRUE(f != nullptr);
 
   size_t len = 4096;
   std::vector<uint8_t> buf(len);
   size_t read = fread(buf.data(), sizeof(decltype(buf)::value_type), buf.size(), f);
   buf.resize(read);
   fclose(f);
 
-  int32_t rv = read_box_from_buffer(buf.data(), buf.size());
+  mp4parse_state* context = mp4parse_new();
+  ASSERT_NE(context, nullptr);
+
+  int32_t rv = mp4parse_read(context, buf.data(), buf.size());
   EXPECT_EQ(rv, 2);
+
+  mp4parse_free(context);
 }
--- a/media/libstagefright/gtest/moz.build
+++ b/media/libstagefright/gtest/moz.build
@@ -18,13 +18,16 @@ TEST_HARNESS_FILES.gtest += [
     'test_case_1204580.mp4',
 ]
 
 if CONFIG['MOZ_RUST']:
     UNIFIED_SOURCES += ['TestMP4Rust.cpp',]
     TEST_HARNESS_FILES.gtest += [
         '../../../dom/media/test/street.mp4',
     ]
+    LOCAL_INCLUDES += [
+        '../binding/include',
+    ]
 
 FINAL_LIBRARY = 'xul-gtest'
 
 if CONFIG['GNU_CXX']:
     CXXFLAGS += ['-Wshadow']
--- a/mobile/android/base/AppConstants.java.in
+++ b/mobile/android/base/AppConstants.java.in
@@ -148,16 +148,23 @@ public class AppConstants {
 
     public static final String MOZ_PKG_SPECIAL =
 //#ifdef MOZ_PKG_SPECIAL
     "@MOZ_PKG_SPECIAL@";
 //#else
     null;
 //#endif
 
+    public static final boolean MOZ_EXCLUDE_HYPHENATION_DICTIONARIES =
+//#ifdef MOZ_EXCLUDE_HYPHENATION_DICTIONARIES
+    true;
+//#else
+    false;
+//#endif
+
     /**
      * Whether this APK was built with constrained resources --
      * no xhdpi+ images, for example.
      */
     public static final boolean MOZ_ANDROID_RESOURCE_CONSTRAINED =
 //#ifdef MOZ_ANDROID_RESOURCE_CONSTRAINED
     true;
 //#else
--- a/mobile/android/base/home/BrowserSearch.java
+++ b/mobile/android/base/home/BrowserSearch.java
@@ -233,16 +233,24 @@ public class BrowserSearch extends HomeF
     @Override
     public void onDestroy() {
         super.onDestroy();
 
         mSearchEngines = null;
     }
 
     @Override
+    public void onHiddenChanged(boolean hidden) {
+        if (!hidden) {
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:GetVisible", null));
+        }
+        super.onHiddenChanged(hidden);
+    }
+
+    @Override
     public void onResume() {
         super.onResume();
 
         // Fetch engines if we need to.
         if (mSearchEngines.isEmpty() || !Locale.getDefault().equals(mLastLocale)) {
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:GetVisible", null));
         } else {
             updateSearchEngineBar();
@@ -547,16 +555,32 @@ public class BrowserSearch extends HomeF
 
     private void setSavedSuggestions(ArrayList<String> savedSuggestions) {
         ThreadUtils.assertOnUiThread();
 
         mSearchHistorySuggestions = savedSuggestions;
         mAdapter.notifyDataSetChanged();
     }
 
+    private boolean shouldUpdateSearchEngine(ArrayList<SearchEngine> searchEngines) {
+        if (searchEngines.size() != mSearchEngines.size()) {
+            return true;
+        }
+
+        int size = searchEngines.size();
+
+        for (int i = 0; i < size; i++) {
+            if (!mSearchEngines.get(i).name.equals(searchEngines.get(i).name)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     private void setSearchEngines(JSONObject data) {
         ThreadUtils.assertOnUiThread();
 
         // This method is called via a Runnable posted from the Gecko thread, so
         // it's possible the fragment and/or its view has been destroyed by the
         // time we get here. If so, just abort.
         if (mView == null) {
             return;
@@ -592,25 +616,27 @@ public class BrowserSearch extends HomeF
                     // Only create a new instance of SuggestClient if it hasn't been
                     // set yet.
                     maybeSetSuggestClient(suggestTemplate, isPrivate);
                 } else {
                     searchEngines.add(engine);
                 }
             }
 
-            mSearchEngines = Collections.unmodifiableList(searchEngines);
-            mLastLocale = Locale.getDefault();
+            // checking if the new searchEngine is different from mSearchEngine, will have to re-layout if yes
+            boolean change = shouldUpdateSearchEngine(searchEngines);
 
-            if (mAdapter != null) {
+            if (mAdapter != null && change) {
+                mSearchEngines = Collections.unmodifiableList(searchEngines);
+                mLastLocale = Locale.getDefault();
+                updateSearchEngineBar();
+
                 mAdapter.notifyDataSetChanged();
             }
 
-            updateSearchEngineBar();
-
             // Show suggestions opt-in prompt only if suggestions are not enabled yet,
             // user hasn't been prompted and we're not on a private browsing tab.
             if (!mSuggestionsEnabled && !suggestionsPrompted && mSuggestClient != null) {
                 showSuggestionsOptIn();
             }
         } catch (JSONException e) {
             Log.e(LOGTAG, "Error getting search engine JSON", e);
         }
--- a/mobile/android/base/resources/layout-large-v11/tab_strip_item_view.xml
+++ b/mobile/android/base/resources/layout-large-v11/tab_strip_item_view.xml
@@ -1,17 +1,17 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:gecko="http://schemas.android.com/apk/res-auto">
 
-   <ImageButton
+   <ImageView
         android:id="@+id/favicon"
         android:layout_width="@dimen/browser_toolbar_favicon_size"
         android:layout_height="match_parent"
         android:layout_marginRight="8dp"
         android:scaleType="centerInside"
         android:duplicateParentState="true"/>
 
     <org.mozilla.gecko.widget.FadedSingleColorTextView
--- a/mobile/android/base/widget/RoundedCornerLayout.java
+++ b/mobile/android/base/widget/RoundedCornerLayout.java
@@ -1,31 +1,33 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.widget;
 
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.R;
 
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Path;
 import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
 import android.widget.LinearLayout;
 
 public class RoundedCornerLayout extends LinearLayout {
     private static final String LOGTAG = "Gecko" + RoundedCornerLayout.class.getSimpleName();
     private float cornerRadius;
 
     private Path path;
+    boolean cannotClipPath;
 
     public RoundedCornerLayout(Context context) {
         super(context);
         init(context);
     }
 
     public RoundedCornerLayout(Context context, AttributeSet attrs) {
         super(context, attrs);
@@ -33,33 +35,45 @@ public class RoundedCornerLayout extends
     }
 
     public RoundedCornerLayout(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         init(context);
     }
 
     private void init(Context context) {
+        // Bug 1201081 - clipPath with hardware acceleration crashes on r11-18.
+        cannotClipPath = AppConstants.Versions.feature11Plus && !AppConstants.Versions.feature19Plus;
+
         final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
 
         cornerRadius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
                 getResources().getDimensionPixelSize(R.dimen.doorhanger_rounded_corner_radius), metrics);
 
         setWillNotDraw(false);
     }
 
     @Override
     protected void onSizeChanged(int w, int h, int oldw, int oldh) {
         super.onSizeChanged(w, h, oldw, oldh);
+        if (cannotClipPath) {
+            return;
+        }
+
         final RectF r = new RectF(0, 0, w, h);
         path = new Path();
         path.addRoundRect(r, cornerRadius, cornerRadius, Path.Direction.CW);
         path.close();
     }
 
     @Override
     public void draw(Canvas canvas) {
+        if (cannotClipPath) {
+            super.draw(canvas);
+            return;
+        }
+
         canvas.save();
         canvas.clipPath(path);
         super.draw(canvas);
         canvas.restore();
     }
 }
--- a/mobile/android/gradle/base/build.gradle
+++ b/mobile/android/gradle/base/build.gradle
@@ -93,28 +93,24 @@ android.libraryVariants.all { variant ->
 }
 
 dependencies {
     compile 'com.android.support:support-v4:23.0.1'
     compile 'com.android.support:appcompat-v7:23.0.1'
     compile 'com.android.support:recyclerview-v7:23.0.1'
 
     if (mozconfig.substs.MOZ_NATIVE_DEVICES) {
-        compile 'com.android.support:mediarouter-v7:22.2.1'
+        compile 'com.android.support:mediarouter-v7:23.0.1'
         compile 'com.google.android.gms:play-services-basement:8.1.0'
         compile 'com.google.android.gms:play-services-base:8.1.0'
         compile 'com.google.android.gms:play-services-cast:8.1.0'
     }
 
     compile project(':thirdparty')
 
-    if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
-        compile project(':thirdparty_adjust_sdk')
-    }
-
     testCompile 'junit:junit:4.12'
     testCompile 'org.robolectric:robolectric:3.0'
     testCompile 'org.simpleframework:simple-http:4.1.13'
 }
 
 apply plugin: 'idea'
 
 idea {
--- a/mobile/android/gradle/build.gradle
+++ b/mobile/android/gradle/build.gradle
@@ -18,17 +18,17 @@ buildDir "${topobjdir}/mobile/android/gr
 buildscript {
     repositories {
         jcenter()
 
         // For spoon-gradle-plugin SNAPSHOT release.  This needs to go before
         // the snapshots repository, otherwise we find a remote 1.0.3-SNAPSHOT
         // that doesn't include nalexander's local changes.
         maven {
-            url "file://${topsrcdir}/mobile/android/gradle/m2repo"
+            url "file://${gradle.mozconfig.topsrcdir}/mobile/android/gradle/m2repo"
         }
         // For spoon SNAPSHOT releases.
         maven {
             url 'https://oss.sonatype.org/content/repositories/snapshots'
         }
     }
 
     dependencies {
--- a/mobile/android/gradle/settings.gradle
+++ b/mobile/android/gradle/settings.gradle
@@ -29,21 +29,16 @@ include ':omnijar'
 include ':thirdparty'
 
 def gradleRoot = new File("${json.topobjdir}/mobile/android/gradle")
 project(':app').projectDir = new File(gradleRoot, 'app')
 project(':base').projectDir = new File(gradleRoot, 'base')
 project(':omnijar').projectDir = new File(gradleRoot, 'omnijar')
 project(':thirdparty').projectDir = new File(gradleRoot, 'thirdparty')
 
-if (json.substs.MOZ_INSTALL_TRACKING) {
-    include ':thirdparty_adjust_sdk'
-    project(':thirdparty_adjust_sdk').projectDir = new File(gradleRoot, 'thirdparty_adjust_sdk')
-}
-
 // The Gradle instance is shared between settings.gradle and all the
 // other build.gradle files (see
 // http://forums.gradle.org/gradle/topics/define_extension_properties_from_settings_xml).
 // We use this ext property to pass the per-object-directory mozconfig
 // between scripts.  This lets us execute set-up code before we gradle
 // tries to configure the project even once, and as a side benefit
 // saves invoking |mach environment| multiple times.
 gradle.ext.mozconfig = json
--- a/mobile/android/gradle/thirdparty/build.gradle
+++ b/mobile/android/gradle/thirdparty/build.gradle
@@ -16,18 +16,19 @@ android {
 
     lintOptions {
         abortOnError false
     }
 
     sourceSets {
         main {
             java {
-                // Thirdparty Adjust SDK code is included in the thirdparty_adjust_sdk project.
-                exclude 'com/adjust/**'
+                if (!mozconfig.substs.MOZ_INSTALL_TRACKING) {
+                    exclude 'com/adjust/**'
+                }
             }
         }
     }
 }
 
 dependencies {
     compile 'com.android.support:support-v4:23.0.1'
 }
deleted file mode 100644
--- a/mobile/android/gradle/thirdparty_adjust_sdk/AndroidManifest.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="com.adjust.sdk">
-
-</manifest>
deleted file mode 100644
--- a/mobile/android/gradle/thirdparty_adjust_sdk/build.gradle
+++ /dev/null
@@ -1,32 +0,0 @@
-apply plugin: 'com.android.library'
-
-android {
-    compileSdkVersion 23
-    buildToolsVersion "23.0.1"
-
-    defaultConfig {
-        targetSdkVersion 22
-        minSdkVersion 9
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_7
-        targetCompatibility JavaVersion.VERSION_1_7
-    }
-
-    lintOptions {
-        abortOnError false
-    }
-
-    sourceSets {
-        main {
-            java {
-                srcDir "${topsrcdir}/mobile/android/thirdparty/com/adjust"
-            }
-        }
-    }
-}
-
-dependencies {
-    compile 'com.android.support:support-v4:22.2.0'
-}
--- a/mobile/android/mach_commands.py
+++ b/mobile/android/mach_commands.py
@@ -99,19 +99,16 @@ class MachCommands(MachCommandBase):
             'local.properties',
             defines=defines,
             deps=os.path.join(self.topobjdir, 'mobile/android/gradle/.deps/local.properties.pp'))
 
         srcdir('thirdparty/build.gradle', 'mobile/android/gradle/thirdparty/build.gradle')
         srcdir('thirdparty/src/main/AndroidManifest.xml', 'mobile/android/gradle/thirdparty/AndroidManifest.xml')
         srcdir('thirdparty/src/main/java', 'mobile/android/thirdparty')
 
-        srcdir('thirdparty_adjust_sdk/build.gradle', 'mobile/android/gradle/thirdparty_adjust_sdk/build.gradle')
-        srcdir('thirdparty_adjust_sdk/src/main/AndroidManifest.xml', 'mobile/android/gradle/thirdparty_adjust_sdk/AndroidManifest.xml')
-
         srcdir('omnijar/build.gradle', 'mobile/android/gradle/omnijar/build.gradle')
         srcdir('omnijar/src/main/java/locales', 'mobile/android/locales')
         srcdir('omnijar/src/main/java/chrome', 'mobile/android/chrome')
         srcdir('omnijar/src/main/java/components', 'mobile/android/components')
         srcdir('omnijar/src/main/java/modules', 'mobile/android/modules')
         srcdir('omnijar/src/main/java/themes', 'mobile/android/themes')
 
         srcdir('app/build.gradle', 'mobile/android/gradle/app/build.gradle')
--- a/mobile/android/tests/browser/robocop/testClearPrivateData.java
+++ b/mobile/android/tests/browser/robocop/testClearPrivateData.java
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.tests;
 
 import org.mozilla.gecko.R;
 
 import com.jayway.android.robotium.solo.Condition;
 import android.view.View;
-import android.widget.ImageButton;
+import android.widget.ImageView;
 
 /**
  * This patch tests the clear private data options:
  * - clear history option by: adding and checking that clear private
  * data option removes the history items but not the users bookmarks
  * - clear site settings and clear saved password by: checking
  * each option present in the doorhanger and clearing the settings from
  * the URL bar context menu and settings menu
@@ -121,15 +121,15 @@ public class testClearPrivateData extend
         mAsserter.ok(waitForText(option), "Verify that the option: " + option + " is in the list", "The option is in the list. There are settings to clear");
         mSolo.clickOnButton(button);
     }
 
     private void openSiteSecurityDoorHanger() {
         mSolo.waitForCondition(new Condition() {
             @Override
             public boolean isSatisfied() {
-                ImageButton btn = (ImageButton) mSolo.getView(R.id.favicon);
+                ImageView btn = (ImageView) mSolo.getView(R.id.favicon);
                 mSolo.clickOnView(btn);
                 return true;
             }
         }, TEST_WAIT_MS);
     }
 }
copy from mobile/android/gradle/thirdparty/AndroidManifest.xml
copy to mobile/android/thirdparty/AndroidManifest.xml
--- a/mobile/android/gradle/thirdparty/AndroidManifest.xml
+++ b/mobile/android/thirdparty/AndroidManifest.xml
@@ -1,4 +1,4 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-    package="org.mozilla.gecko.thirdparty">
+    package="org.mozilla.gecko.thirdparty_unused">
 
 </manifest>
copy from mobile/android/gradle/thirdparty/build.gradle
copy to mobile/android/thirdparty/build.gradle
--- a/mobile/android/gradle/thirdparty/build.gradle
+++ b/mobile/android/thirdparty/build.gradle
@@ -1,8 +1,10 @@
+buildDir "${topobjdir}/gradle/build/mobile/android/thirdparty"
+
 apply plugin: 'com.android.library'
 
 android {
     compileSdkVersion 23
     buildToolsVersion "23.0.1"
 
     defaultConfig {
         targetSdkVersion 22
@@ -15,19 +17,33 @@ android {
     }
 
     lintOptions {
         abortOnError false
     }
 
     sourceSets {
         main {
+            manifest.srcFile 'AndroidManifest.xml'
             java {
-                // Thirdparty Adjust SDK code is included in the thirdparty_adjust_sdk project.
-                exclude 'com/adjust/**'
+                srcDir '.'
+                if (!mozconfig.substs.MOZ_INSTALL_TRACKING) {
+                    exclude 'com/adjust/**'
+                }
             }
         }
     }
 }
 
 dependencies {
     compile 'com.android.support:support-v4:23.0.1'
 }
+
+apply plugin: 'idea'
+
+idea {
+    module {
+        // This is cosmetic.  See the excludes in the root project.
+        if (!mozconfig.substs.MOZ_INSTALL_TRACKING) {
+            excludeDirs += file('com/adjust/sdk')
+        }
+    }
+}
--- a/mobile/android/thirdparty/com/adjust/sdk/AdjustFactory.java
+++ b/mobile/android/thirdparty/com/adjust/sdk/AdjustFactory.java
@@ -1,15 +1,15 @@
 package com.adjust.sdk;
 
 import android.content.Context;
 
-import org.apache.http.client.HttpClient;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.params.HttpParams;
+import ch.boye.httpclientandroidlib.client.HttpClient;
+import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient;
+import ch.boye.httpclientandroidlib.params.HttpParams;
 
 public class AdjustFactory {
     private static IPackageHandler packageHandler = null;
     private static IRequestHandler requestHandler = null;
     private static IAttributionHandler attributionHandler = null;
     private static IActivityHandler activityHandler = null;
     private static ILogger logger = null;
     private static HttpClient httpClient = null;
--- a/mobile/android/thirdparty/com/adjust/sdk/AttributionHandler.java
+++ b/mobile/android/thirdparty/com/adjust/sdk/AttributionHandler.java
@@ -1,15 +1,15 @@
 package com.adjust.sdk;
 
 import android.net.Uri;
 
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
+import ch.boye.httpclientandroidlib.HttpResponse;
+import ch.boye.httpclientandroidlib.client.HttpClient;
+import ch.boye.httpclientandroidlib.client.methods.HttpGet;
 import org.json.JSONObject;
 
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.Map;
 import java.util.concurrent.Executors;
 import java.util.concurrent.ScheduledExecutorService;
 import java.util.concurrent.ScheduledFuture;
--- a/mobile/android/thirdparty/com/adjust/sdk/RequestHandler.java
+++ b/mobile/android/thirdparty/com/adjust/sdk/RequestHandler.java
@@ -9,25 +9,25 @@
 
 package com.adjust.sdk;
 
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Looper;
 import android.os.Message;
 
-import org.apache.http.HttpResponse;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.ClientProtocolException;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.entity.UrlEncodedFormEntity;
-import org.apache.http.client.methods.HttpPost;
-import org.apache.http.client.methods.HttpUriRequest;
-import org.apache.http.client.utils.URLEncodedUtils;
-import org.apache.http.message.BasicNameValuePair;
+import ch.boye.httpclientandroidlib.HttpResponse;
+import ch.boye.httpclientandroidlib.NameValuePair;
+import ch.boye.httpclientandroidlib.client.ClientProtocolException;
+import ch.boye.httpclientandroidlib.client.HttpClient;
+import ch.boye.httpclientandroidlib.client.entity.UrlEncodedFormEntity;
+import ch.boye.httpclientandroidlib.client.methods.HttpPost;
+import ch.boye.httpclientandroidlib.client.methods.HttpUriRequest;
+import ch.boye.httpclientandroidlib.client.utils.URLEncodedUtils;
+import ch.boye.httpclientandroidlib.message.BasicNameValuePair;
 import org.json.JSONObject;
 
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.lang.ref.WeakReference;
 import java.net.SocketTimeoutException;
 import java.util.ArrayList;
 import java.util.List;
--- a/mobile/android/thirdparty/com/adjust/sdk/Util.java
+++ b/mobile/android/thirdparty/com/adjust/sdk/Util.java
@@ -7,22 +7,22 @@
 //  See the file MIT-LICENSE for copying permission.
 //
 
 package com.adjust.sdk;
 
 import android.content.Context;
 import android.content.pm.PackageManager;
 
-import org.apache.http.HttpResponse;
-import org.apache.http.HttpStatus;
-import org.apache.http.client.HttpClient;
-import org.apache.http.params.BasicHttpParams;
-import org.apache.http.params.HttpConnectionParams;
-import org.apache.http.params.HttpParams;
+import ch.boye.httpclientandroidlib.HttpResponse;
+import ch.boye.httpclientandroidlib.HttpStatus;
+import ch.boye.httpclientandroidlib.client.HttpClient;
+import ch.boye.httpclientandroidlib.params.BasicHttpParams;
+import ch.boye.httpclientandroidlib.params.HttpConnectionParams;
+import ch.boye.httpclientandroidlib.params.HttpParams;
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -4808,16 +4808,18 @@ pref("urlclassifier.disallow_completions
 // The table and update/gethash URLs for Safebrowsing phishing and malware
 // checks.
 pref("urlclassifier.trackingTable", "test-track-simple,mozstd-track-digest256");
 pref("urlclassifier.trackingWhitelistTable", "test-trackwhite-simple,mozstd-trackwhite-digest256");
 
 pref("browser.safebrowsing.provider.mozilla.lists", "mozstd-track-digest256,mozstd-trackwhite-digest256,mozfull-track-digest256");
 pref("browser.safebrowsing.provider.mozilla.updateURL", "https://shavar.services.mozilla.com/downloads?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
 pref("browser.safebrowsing.provider.mozilla.gethashURL", "https://shavar.services.mozilla.com/gethash?client=SAFEBROWSING_ID&appver=%VERSION%&pver=2.2");
+// Set to a date in the past to force immediate download in new profiles.
+pref("browser.safebrowsing.provider.mozilla.nextupdatetime", "1");
 // Block lists for tracking protection. The name values will be used as the keys
 // to lookup the localized name in preferences.properties.
 pref("browser.safebrowsing.provider.mozilla.lists.mozstd.name", "mozstdName");
 pref("browser.safebrowsing.provider.mozilla.lists.mozstd.description", "mozstdDesc");
 pref("browser.safebrowsing.provider.mozilla.lists.mozfull.name", "mozfullName");
 pref("browser.safebrowsing.provider.mozilla.lists.mozfull.description", "mozfullDesc");
 
 // Turn off Spatial navigation by default.
--- a/netwerk/protocol/http/nsHttpConnection.cpp
+++ b/netwerk/protocol/http/nsHttpConnection.cpp
@@ -78,16 +78,17 @@ nsHttpConnection::nsHttpConnection()
     , mUsingSpdyVersion(0)
     , mPriority(nsISupportsPriority::PRIORITY_NORMAL)
     , mReportedSpdy(false)
     , mEverUsedSpdy(false)
     , mLastHttpResponseVersion(NS_HTTP_VERSION_1_1)
     , mTransactionCaps(0)
     , mResponseTimeoutEnabled(false)
     , mTCPKeepaliveConfig(kTCPKeepaliveDisabled)
+    , mForceSendPending(false)
 {
     LOG(("Creating nsHttpConnection @%p\n", this));
 
     // the default timeout is for when this connection has not yet processed a
     // transaction
     static const PRIntervalTime k5Sec = PR_SecondsToInterval(5);
     mIdleTimeout =
         (k5Sec < gHttpHandler->IdleTimeout()) ? k5Sec : gHttpHandler->IdleTimeout();
@@ -108,16 +109,20 @@ nsHttpConnection::~nsHttpConnection()
         uint32_t totalKBRead = static_cast<uint32_t>(mTotalBytesRead >> 10);
         LOG(("nsHttpConnection %p read %dkb on connection spdy=%d\n",
              this, totalKBRead, mEverUsedSpdy));
         Telemetry::Accumulate(mEverUsedSpdy ?
                               Telemetry::SPDY_KBREAD_PER_CONN :
                               Telemetry::HTTP_KBREAD_PER_CONN,
                               totalKBRead);
     }
+    if (mForceSendTimer) {
+        mForceSendTimer->Cancel();
+        mForceSendTimer = nullptr;
+    }
 }
 
 nsresult
 nsHttpConnection::Init(nsHttpConnectionInfo *info,
                        uint16_t maxHangTime,
                        nsISocketTransport *transport,
                        nsIAsyncInputStream *instream,
                        nsIAsyncOutputStream *outstream,
@@ -564,16 +569,20 @@ nsHttpConnection::Close(nsresult reason)
 
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     // Ensure TCP keepalive timer is stopped.
     if (mTCPKeepaliveTransitionTimer) {
         mTCPKeepaliveTransitionTimer->Cancel();
         mTCPKeepaliveTransitionTimer = nullptr;
     }
+    if (mForceSendTimer) {
+        mForceSendTimer->Cancel();
+        mForceSendTimer = nullptr;
+    }
 
     if (NS_FAILED(reason)) {
         if (mIdleMonitoring)
             EndIdleMonitoring();
 
         mTLSFilter = nullptr;
 
         // The connection and security errors clear out alt-svc mappings
@@ -1335,64 +1344,98 @@ nsHttpConnection::ResumeRecv()
     if (mSocketIn)
         return mSocketIn->AsyncWait(this, 0, 0, nullptr);
 
     NS_NOTREACHED("no socket input stream");
     return NS_ERROR_UNEXPECTED;
 }
 
 
-class nsHttpConnectionForceIO : public nsRunnable
+class HttpConnectionForceIO : public nsRunnable
 {
 public:
-  nsHttpConnectionForceIO(nsHttpConnection *aConn, bool doRecv)
+  HttpConnectionForceIO(nsHttpConnection *aConn, bool doRecv)
      : mConn(aConn)
      , mDoRecv(doRecv)
     {}
 
     NS_IMETHOD Run()
     {
         MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
         if (mDoRecv) {
             if (!mConn->mSocketIn)
                 return NS_OK;
             return mConn->OnInputStreamReady(mConn->mSocketIn);
         }
-        if (!mConn->mSocketOut)
+
+        MOZ_ASSERT(mConn->mForceSendPending);
+        mConn->mForceSendPending = false;
+        if (!mConn->mSocketOut) {
             return NS_OK;
+        }
         return mConn->OnOutputStreamReady(mConn->mSocketOut);
     }
 private:
     RefPtr<nsHttpConnection> mConn;
     bool mDoRecv;
 };
 
+void
+nsHttpConnection::ForceSendIO(nsITimer *aTimer, void *aClosure)
+{
+    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+    nsHttpConnection *self = static_cast<nsHttpConnection *>(aClosure);
+    MOZ_ASSERT(aTimer == self->mForceSendTimer);
+    self->mForceSendTimer = nullptr;
+    NS_DispatchToCurrentThread(new HttpConnectionForceIO(self, false));
+}
+
+nsresult
+nsHttpConnection::MaybeForceSendIO()
+{
+    MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+    // due to bug 1213084 sometimes real I/O events do not get serviced when
+    // NSPR derived I/O events are ready and this can cause a deadlock with
+    // https over https proxying. Normally we would expect the write callback to
+    // be invoked before this timer goes off, but set it at the old windows
+    // tick interval (kForceDelay) as a backup for those circumstances.
+    static const uint32_t kForceDelay = 17; //ms
+
+    if (mForceSendPending) {
+        return NS_OK;
+    }
+    MOZ_ASSERT(!mForceSendTimer);
+    mForceSendPending = true;
+    mForceSendTimer = do_CreateInstance("@mozilla.org/timer;1");
+    return mForceSendTimer->InitWithFuncCallback(
+        nsHttpConnection::ForceSendIO, this, kForceDelay, nsITimer::TYPE_ONE_SHOT);
+}
+
 // trigger an asynchronous read
 nsresult
 nsHttpConnection::ForceRecv()
 {
     LOG(("nsHttpConnection::ForceRecv [this=%p]\n", this));
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
-    return NS_DispatchToCurrentThread(new nsHttpConnectionForceIO(this, true));
+    return NS_DispatchToCurrentThread(new HttpConnectionForceIO(this, true));
 }
 
 // trigger an asynchronous write
 nsresult
 nsHttpConnection::ForceSend()
 {
     LOG(("nsHttpConnection::ForceSend [this=%p]\n", this));
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
 
     if (mTLSFilter) {
         return mTLSFilter->NudgeTunnel(this);
     }
-
-    return NS_DispatchToCurrentThread(new nsHttpConnectionForceIO(this, false));
+    return MaybeForceSendIO();
 }
 
 void
 nsHttpConnection::BeginIdleMonitoring()
 {
     LOG(("nsHttpConnection::BeginIdleMonitoring [this=%p]\n", this));
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
     MOZ_ASSERT(!mTransaction, "BeginIdleMonitoring() while active");
@@ -2047,17 +2090,16 @@ nsHttpConnection::OnInputStreamReady(nsI
 // nsHttpConnection::nsIOutputStreamCallback
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpConnection::OnOutputStreamReady(nsIAsyncOutputStream *out)
 {
     MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
     MOZ_ASSERT(out == mSocketOut, "unexpected socket");
-
     // if the transaction was dropped...
     if (!mTransaction) {
         LOG(("  no transaction; ignoring event\n"));
         return NS_OK;
     }
 
     nsresult rv = OnSocketWritable();
     if (NS_FAILED(rv))
--- a/netwerk/protocol/http/nsHttpConnection.h
+++ b/netwerk/protocol/http/nsHttpConnection.h
@@ -137,17 +137,17 @@ public:
     bool     IsReused();
     void     SetIsReusedAfter(uint32_t afterMilliseconds);
     nsresult PushBack(const char *data, uint32_t length);
     nsresult ResumeSend();
     nsresult ResumeRecv();
     int64_t  MaxBytesRead() {return mMaxBytesRead;}
     uint8_t GetLastHttpResponseVersion() { return mLastHttpResponseVersion; }
 
-    friend class nsHttpConnectionForceIO;
+    friend class HttpConnectionForceIO;
     nsresult ForceSend();
     nsresult ForceRecv();
 
     static NS_METHOD ReadFromStream(nsIInputStream *, void *, const char *,
                                     uint32_t, uint32_t, uint32_t *);
 
     // When a persistent connection is in the connection manager idle
     // connection pool, the nsHttpConnection still reads errors and hangups
@@ -344,14 +344,21 @@ private:
     // The capabailities associated with the most recent transaction
     uint32_t                        mTransactionCaps;
 
     bool                            mResponseTimeoutEnabled;
 
     // Flag to indicate connection is in inital keepalive period (fast detect).
     uint32_t                        mTCPKeepaliveConfig;
     nsCOMPtr<nsITimer>              mTCPKeepaliveTransitionTimer;
+
+private:
+    // For ForceSend()
+    static void                     ForceSendIO(nsITimer *aTimer, void *aClosure);
+    nsresult                        MaybeForceSendIO();
+    bool                            mForceSendPending;
+    nsCOMPtr<nsITimer>              mForceSendTimer;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // nsHttpConnection_h__
--- a/python/mozboot/mozboot/archlinux.py
+++ b/python/mozboot/mozboot/archlinux.py
@@ -54,34 +54,85 @@ class ArchlinuxBootstrapper(BaseBootstra
         'xorg-server-xvfb',
         'yasm',
         'zip',
         'gst-libav',
         'gst-plugins-good',
         'networkmanager',
     ]
 
-    AUR_PACKAGES = [
-        'https://aur.archlinux.org/cgit/aur.git/snapshot/uuid.tar.gz'
+    BROWSER_AUR_PACKAGES = [
+        'https://aur.archlinux.org/cgit/aur.git/snapshot/uuid.tar.gz',
+    ]
+
+    MOBILE_ANDROID_COMMON_PACKAGES = [
+        'zlib',  # mobile/android requires system zlib.
+        'jdk7-openjdk', # It would be nice to handle alternative JDKs.  See https://wiki.archlinux.org/index.php/Java.
+        'wget',  # For downloading the Android SDK and NDK.
+        'multilib/lib32-libstdc++5', # See comment about 32 bit binaries and multilib below.
+        'multilib/lib32-ncurses',
+        'multilib/lib32-readline',
+        'multilib/lib32-zlib',
     ]
 
     def __init__(self, version, dist_id, **kwargs):
         print 'Using an experimental bootstrapper for Archlinux.'
         BaseBootstrapper.__init__(self, **kwargs)
 
     def install_system_packages(self):
         self.pacman_install(*self.SYSTEM_PACKAGES)
-        self.aur_install(*self.AUR_PACKAGES)
 
     def install_browser_packages(self):
+        self.aur_install(*self.AUR_BROWSER_PACKAGES)
         self.pacman_install(*self.BROWSER_PACKAGES)
 
     def install_mobile_android_packages(self):
-        raise NotImplementedError('Bootstrap support for mobile-android is '
-                                  'not yet available for Archlinux')
+        import android
+
+        # Multi-part process:
+        # 1. System packages.
+        # 2. Android SDK and NDK.
+        # 3. Android packages.
+
+        # 1. This is hard to believe, but the Android SDK binaries are 32-bit
+        # and that conflicts with 64-bit Arch installations out of the box.  The
+        # solution is to add the multilibs repository; unfortunately, this
+        # requires manual intervention.
+        try:
+            self.pacman_install(*self.MOBILE_ANDROID_COMMON_PACKAGES)
+        except e:
+            print('Failed to install all packages.  The Android developer '
+                  'toolchain requires 32 bit binaries be enabled (see '
+                  'https://wiki.archlinux.org/index.php/Android).  You may need to '
+                  'manually enable the multilib repository following the instructions '
+                  'at https://wiki.archlinux.org/index.php/Multilib.')
+            raise e
+
+        # 2. The user may have an external Android SDK (in which case we save
+        # them a lengthy download), or they may have already completed the
+        # download. We unpack to ~/.mozbuild/{android-sdk-linux, android-ndk-r10e}.
+        mozbuild_path = os.environ.get('MOZBUILD_STATE_PATH', os.path.expanduser(os.path.join('~', '.mozbuild')))
+        self.sdk_path = os.environ.get('ANDROID_SDK_HOME', os.path.join(mozbuild_path, 'android-sdk-linux'))
+        self.ndk_path = os.environ.get('ANDROID_NDK_HOME', os.path.join(mozbuild_path, 'android-ndk-r10e'))
+        self.sdk_url = 'https://dl.google.com/android/android-sdk_r24.0.1-linux.tgz'
+        is_64bits = sys.maxsize > 2**32
+        if is_64bits:
+            self.ndk_url = 'https://dl.google.com/android/ndk/android-ndk-r10e-linux-x86_64.bin'
+        else:
+            self.ndk_url = 'https://dl.google.com/android/ndk/android-ndk-r10e-linux-x86.bin'
+        android.ensure_android_sdk_and_ndk(path=mozbuild_path,
+                                           sdk_path=self.sdk_path, sdk_url=self.sdk_url,
+                                           ndk_path=self.ndk_path, ndk_url=self.ndk_url)
+        android_tool = os.path.join(self.sdk_path, 'tools', 'android')
+        android.ensure_android_packages(android_tool=android_tool)
+
+    def suggest_mobile_android_mozconfig(self):
+        import android
+        android.suggest_mozconfig(sdk_path=self.sdk_path,
+                                  ndk_path=self.ndk_path)
 
     def _update_package_manager(self):
         self.pacman_update
 
     def upgrade_mercurial(self, current):
         self.pacman_install('mercurial')
 
     def upgrade_python(self, current):
--- a/security/manager/ssl/tests/unit/test_weak_crypto.js
+++ b/security/manager/ssl/tests/unit/test_weak_crypto.js
@@ -98,78 +98,77 @@ function startServer(cert, rc4only) {
 function storeCertOverride(port, cert) {
   let overrideBits = Ci.nsICertOverrideService.ERROR_UNTRUSTED |
                      Ci.nsICertOverrideService.ERROR_MISMATCH;
   certOverrideService.rememberValidityOverride("127.0.0.1", port, cert,
                                                overrideBits, true);
 }
 
 function startClient(port, expectedResult, options = {}) {
-  let SSL_ERROR_BASE = Ci.nsINSSErrorsService.NSS_SSL_ERROR_BASE;
-  let SSL_ERROR_BAD_CERT_ALERT = SSL_ERROR_BASE + 17;
   let transport =
     socketTransportService.createTransport(["ssl"], 1, "127.0.0.1", port, null);
   if (options.isPrivate) {
     transport.connectionFlags |= Ci.nsISocketTransport.NO_PERMANENT_STORAGE;
   }
   let input;
   let output;
 
-  let inputDeferred = Promise.defer();
-  let outputDeferred = Promise.defer();
+  let deferred = Promise.defer();
 
   let handler = {
 
     onTransportStatus: function(transport, status) {
       if (status === Ci.nsISocketTransport.STATUS_CONNECTED_TO) {
         output.asyncWait(handler, 0, 0, Services.tm.currentThread);
       }
     },
 
     onInputStreamReady: function(input) {
-      let errorCode = Cr.NS_OK;
       try {
         let data = NetUtil.readInputStreamToString(input, input.available());
         equal(data, "HELLO", "Echoed data received");
         input.close();
         output.close();
+        deferred.resolve();
       } catch (e) {
-        errorCode = e.result;
-      }
-      try {
-        equal(errorCode, expectedResult,
-              "Actual and expected connection result should match");
-        inputDeferred.resolve();
-      } catch (e) {
-        inputDeferred.reject(e);
+        deferred.reject(e);
       }
     },
 
     onOutputStreamReady: function(output) {
       try {
-        output.write("HELLO", 5);
+        try {
+          output.write("HELLO", 5);
+        } catch (e) {
+          if (e.result == Cr.NS_BASE_STREAM_WOULD_BLOCK) {
+            output.asyncWait(handler, 0, 0, Services.tm.currentThread);
+            return;
+          }
+          equal(e.result, expectedResult,
+                "Actual and expected connection result should match");
+          output.close();
+          deferred.resolve();
+          return;
+        }
+        equal(Cr.NS_OK, expectedResult, "Connection should succeed");
         do_print("Output to server written");
-        outputDeferred.resolve();
         input = transport.openInputStream(0, 0, 0);
         input.asyncWait(handler, 0, 0, Services.tm.currentThread);
       } catch (e) {
-        let errorCode = -1 * (e.result & 0xFFFF);
-        if (errorCode == SSL_ERROR_BAD_CERT_ALERT) {
-          do_print("Server doesn't like client cert");
-        }
-        outputDeferred.reject(e);
+        deferred.reject(e);
       }
     }
 
   };
 
   transport.setEventSink(handler, Services.tm.currentThread);
-  output = transport.openOutputStream(0, 0, 0);
+  output = transport.openOutputStream(Ci.nsITransport.OPEN_UNBUFFERED, 0, 0);
+  output.QueryInterface(Ci.nsIAsyncOutputStream);
 
-  return Promise.all([inputDeferred.promise, outputDeferred.promise]);
+  return deferred.promise;
 }
 
 function run_test() {
   Services.prefs.setBoolPref("security.tls.unrestricted_rc4_fallback", false);
   run_next_test();
 }
 
 // for sanity check
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -147,9 +147,8 @@ run-sequentially = hardcoded ports
 [test_constructX509FromBase64.js]
 [test_validity.js]
 run-sequentially = hardcoded ports
 [test_certviewer_invalid_oids.js]
 skip-if = toolkit == 'android' || buildapp == 'b2g'
 
 [test_weak_crypto.js]
 firefox-appdir = browser
-skip-if = toolkit == 'android'
copy from mobile/android/gradle/settings.gradle
copy to settings.gradle
--- a/mobile/android/gradle/settings.gradle
+++ b/settings.gradle
@@ -1,48 +1,41 @@
-// If our root project is in the object directory, we expect to be given
-// topsrcdir from our environment via gradle.properties.  If we don't get it,
-// our root project is in the source directory, so we extract topsrcdir relative
-// to the location of this script.
-if (!hasProperty('topsrcdir')) {
-    // In the source directory, we're not worried about links crossing directories.
-    binding.variables['topsrcdir'] = new File("../../..").getCanonicalPath()
-    logger.warn("topsrcdir is undefined: assuming source directory Gradle invocation with topsrcdir=${topsrcdir}.")
-}
+// You might think topsrcdir is '.', but that's not true when the Gradle build
+// is launched from within IntelliJ.
+def topsrcdir = rootProject.projectDir.absolutePath
 
 def commandLine = ["${topsrcdir}/mach", "environment", "--format", "json", "--verbose"]
 def proc = commandLine.execute(null, new File(topsrcdir))
 def standardOutput = new ByteArrayOutputStream()
 proc.consumeProcessOutput(standardOutput, standardOutput)
 proc.waitFor()
 
 // Only show the output if something went wrong.
 if (proc.exitValue() != 0) {
     throw new GradleException("Process '${commandLine}' finished with non-zero exit value ${proc.exitValue()}:\n\n${standardOutput.toString()}")
 }
 
 import groovy.json.JsonSlurper
 def slurper = new JsonSlurper()
 def json = slurper.parseText(standardOutput.toString())
 
+if (json.substs.MOZ_BUILD_APP != 'mobile/android') {
+    throw new GradleException("Building with Gradle is only supported for Fennec, i.e., MOZ_BUILD_APP == 'mobile/android'.");
+}
+
 include ':app'
 include ':base'
 include ':omnijar'
 include ':thirdparty'
 
 def gradleRoot = new File("${json.topobjdir}/mobile/android/gradle")
 project(':app').projectDir = new File(gradleRoot, 'app')
 project(':base').projectDir = new File(gradleRoot, 'base')
 project(':omnijar').projectDir = new File(gradleRoot, 'omnijar')
-project(':thirdparty').projectDir = new File(gradleRoot, 'thirdparty')
-
-if (json.substs.MOZ_INSTALL_TRACKING) {
-    include ':thirdparty_adjust_sdk'
-    project(':thirdparty_adjust_sdk').projectDir = new File(gradleRoot, 'thirdparty_adjust_sdk')
-}
+project(':thirdparty').projectDir = new File("${json.topsrcdir}/mobile/android/thirdparty")
 
 // The Gradle instance is shared between settings.gradle and all the
 // other build.gradle files (see
 // http://forums.gradle.org/gradle/topics/define_extension_properties_from_settings_xml).
 // We use this ext property to pass the per-object-directory mozconfig
 // between scripts.  This lets us execute set-up code before we gradle
 // tries to configure the project even once, and as a side benefit
 // saves invoking |mach environment| multiple times.
--- a/testing/mozbase/mozprocess/tests/proctest.py
+++ b/testing/mozbase/mozprocess/tests/proctest.py
@@ -1,58 +1,41 @@
 import os
 import sys
 import unittest
 import psutil
 
 here = os.path.dirname(os.path.abspath(__file__))
 
 
-def check_for_process(processName):
-    """
-        Use to determine if process of the given name is still running.
-
-        Returns:
-        detected -- True if process is detected to exist, False otherwise
-        output -- if process exists, stdout of the process, [] otherwise
-    """
-    name = os.path.basename(processName)
-    process = [p.pid for p in psutil.process_iter()
-               if p.name() == name]
-
-    if process:
-        return True, process
-    return False, []
-
-
 class ProcTest(unittest.TestCase):
 
     @classmethod
     def setUpClass(cls):
         cls.proclaunch = os.path.join(here, "proclaunch.py")
         cls.python = sys.executable
 
-    def determine_status(self,
-                         detected=False,
-                         output='',
-                         returncode=0,
-                         didtimeout=False,
-                         isalive=False,
-                         expectedfail=()):
+    def determine_status(self, proc, isalive=False, expectedfail=()):
         """
         Use to determine if the situation has failed.
         Parameters:
-            detected -- value from check_for_process to determine if the process is detected
-            output -- string of data from detected process, can be ''
-            returncode -- return code from process, defaults to 0
-            didtimeout -- True if process timed out, defaults to False
+            proc -- the processhandler instance
             isalive -- Use True to indicate we pass if the process exists; however, by default
                        the test will pass if the process does not exist (isalive == False)
             expectedfail -- Defaults to [], used to indicate a list of fields that are expected to fail
         """
+        returncode = proc.proc.returncode
+        didtimeout = proc.didTimeout
+        detected = psutil.pid_exists(proc.pid)
+        output = ''
+        # ProcessHandler has output when store_output is set to True in the constructor
+        # (this is the default)
+        if getattr(proc, 'output'):
+            output = proc.output
+
         if 'returncode' in expectedfail:
             self.assertTrue(returncode, "Detected an unexpected return code of: %s" % returncode)
         elif isalive:
             self.assertEqual(returncode, None, "Detected not None return code of: %s" % returncode)
         else:
             self.assertNotEqual(returncode, None, "Detected unexpected None return code of")
 
         if 'didtimeout' in expectedfail:
--- a/testing/mozbase/mozprocess/tests/test_mozprocess.py
+++ b/testing/mozbase/mozprocess/tests/test_mozprocess.py
@@ -3,16 +3,17 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this file,
 # You can obtain one at http://mozilla.org/MPL/2.0/.
 
 import os
 import subprocess
 import sys
 import unittest
+import proctest
 from mozprocess import processhandler
 
 here = os.path.dirname(os.path.abspath(__file__))
 
 def make_proclaunch(aDir):
     """
         Makes the proclaunch executable.
         Params:
@@ -44,55 +45,18 @@ def make_proclaunch(aDir):
             print "stderr:\n%s" % stderr
             raise subprocess.CalledProcessError(process.returncode, command, stdout)
 
     # ensure the launcher now exists
     if not os.path.exists(exepath):
         raise AssertionError("proclaunch executable '%s' does not exist (sys.platform=%s)" % (exepath, sys.platform))
     return exepath
 
-def check_for_process(processName):
-    """
-        Use to determine if process of the given name is still running.
 
-        Returns:
-        detected -- True if process is detected to exist, False otherwise
-        output -- if process exists, stdout of the process, '' otherwise
-    """
-    # TODO: replace with
-    # https://github.com/mozilla/mozbase/blob/master/mozprocess/mozprocess/pid.py
-    # which should be augmented from talos
-    # see https://bugzilla.mozilla.org/show_bug.cgi?id=705864
-    output = ''
-    if sys.platform == "win32":
-        # On windows we use tasklist
-        p1 = subprocess.Popen(["tasklist"], stdout=subprocess.PIPE)
-        output = p1.communicate()[0]
-        detected = False
-        for line in output.splitlines():
-            if processName in line:
-                detected = True
-                break
-    else:
-        p1 = subprocess.Popen(["ps", "-ef"], stdout=subprocess.PIPE)
-        p2 = subprocess.Popen(["grep", processName], stdin=p1.stdout, stdout=subprocess.PIPE)
-        p1.stdout.close()
-        output = p2.communicate()[0]
-        detected = False
-        for line in output.splitlines():
-            if "grep %s" % processName in line:
-                continue
-            elif processName in line and not 'defunct' in line:
-                detected = True
-                break
-
-    return detected, output
-
-
-class ProcTest(unittest.TestCase):
+class ProcTest(proctest.ProcTest):
 
     # whether to remove created files on exit
     cleanup = os.environ.get('CLEANUP', 'true').lower() in ('1', 'true')
 
     @classmethod
     def setUpClass(cls):
         cls.proclaunch = make_proclaunch(here)
 
@@ -123,21 +87,17 @@ class ProcTest(unittest.TestCase):
     def test_process_normal_finish(self):
         """Process is started, runs to completion while we wait for it"""
 
         p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"],
                                           cwd=here)
         p.run()
         p.wait()
 
-        detected, output = check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout)
+        self.determine_status(p)
 
     def test_commandline_no_args(self):
         """Command line is reported correctly when no arguments are specified"""
         p = processhandler.ProcessHandler(self.proclaunch, cwd=here)
         self.assertEqual(p.commandline, self.proclaunch)
 
     def test_commandline_overspecified(self):
         """Command line raises an exception when the arguments are specified ambiguously"""
@@ -180,38 +140,28 @@ class ProcTest(unittest.TestCase):
         """Process is started runs to completion while we wait indefinitely"""
 
         p = processhandler.ProcessHandler([self.proclaunch,
                                           "process_waittimeout_10s.ini"],
                                           cwd=here)
         p.run()
         p.wait()
 
-        detected, output = check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout)
+        self.determine_status(p)
 
     def test_process_timeout(self):
         """ Process is started, runs but we time out waiting on it
             to complete
         """
         p = processhandler.ProcessHandler([self.proclaunch, "process_waittimeout.ini"],
                                           cwd=here)
         p.run(timeout=10)
         p.wait()
 
-        detected, output = check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              False,
-                              ['returncode', 'didtimeout'])
+        self.determine_status(p, False, ['returncode', 'didtimeout'])
 
     def test_process_timeout_no_kill(self):
         """ Process is started, runs but we time out waiting on it
             to complete. Process should not be killed.
         """
         p = None
         def timeout_handler():
             self.assertEqual(p.proc.poll(), None)
@@ -219,120 +169,62 @@ class ProcTest(unittest.TestCase):
         p = processhandler.ProcessHandler([self.proclaunch, "process_waittimeout.ini"],
                                           cwd=here,
                                           onTimeout=(timeout_handler,),
                                           kill_on_timeout=False)
         p.run(timeout=1)
         p.wait()
         self.assertTrue(p.didTimeout)
 
-        detected, output = check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              False,
-                              ['returncode', 'didtimeout'])
+        self.determine_status(p, False, ['returncode', 'didtimeout'])
 
     def test_process_waittimeout(self):
         """
         Process is started, then wait is called and times out.
         Process is still running and didn't timeout
         """
         p = processhandler.ProcessHandler([self.proclaunch,
                                           "process_waittimeout_10s.ini"],
                                           cwd=here)
 
         p.run()
         p.wait(timeout=5)
 
-        detected, output = check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              True,
-                              ())
+        self.determine_status(p, True, ())
 
     def test_process_waitnotimeout(self):
         """ Process is started, runs to completion before our wait times out
         """
         p = processhandler.ProcessHandler([self.proclaunch,
                                           "process_waittimeout_10s.ini"],
                                           cwd=here)
         p.run(timeout=30)
         p.wait()
 
-        detected, output = check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout)
+        self.determine_status(p)
 
     def test_process_kill(self):
         """Process is started, we kill it"""
 
         p = processhandler.ProcessHandler([self.proclaunch, "process_normal_finish.ini"],
                                           cwd=here)
         p.run()
         p.kill()
 
-        detected, output = check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout)
+        self.determine_status(p)
 
     def test_process_output_twice(self):
         """
         Process is started, then processOutput is called a second time explicitly
         """
         p = processhandler.ProcessHandler([self.proclaunch,
                                           "process_waittimeout_10s.ini"],
                                           cwd=here)
 
         p.run()
         p.processOutput(timeout=5)
         p.wait()
 
-        detected, output = check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              False,
-                              ())
+        self.determine_status(p, False, ())
 
-    def determine_status(self,
-                         detected=False,
-                         output='',
-                         returncode=0,
-                         didtimeout=False,
-                         isalive=False,
-                         expectedfail=()):
-        """
-        Use to determine if the situation has failed.
-        Parameters:
-            detected -- value from check_for_process to determine if the process is detected
-            output -- string of data from detected process, can be ''
-            returncode -- return code from process, defaults to 0
-            didtimeout -- True if process timed out, defaults to False
-            isalive -- Use True to indicate we pass if the process exists; however, by default
-                       the test will pass if the process does not exist (isalive == False)
-            expectedfail -- Defaults to [], used to indicate a list of fields that are expected to fail
-        """
-        if 'returncode' in expectedfail:
-            self.assertTrue(returncode, "Detected an unexpected return code of: %s" % returncode)
-        elif not isalive:
-            self.assertTrue(returncode == 0, "Detected non-zero return code of: %d" % returncode)
-
-        if 'didtimeout' in expectedfail:
-            self.assertTrue(didtimeout, "Detected that process didn't time out")
-        else:
-            self.assertTrue(not didtimeout, "Detected that process timed out")
-
-        if isalive:
-            self.assertTrue(detected, "Detected process is not running, process output: %s" % output)
-        else:
-            self.assertTrue(not detected, "Detected process is still running, process output: %s" % output)
 
 if __name__ == '__main__':
     unittest.main()
--- a/testing/mozbase/mozprocess/tests/test_mozprocess_kill.py
+++ b/testing/mozbase/mozprocess/tests/test_mozprocess_kill.py
@@ -21,70 +21,50 @@ class ProcTestKill(proctest.ProcTest):
     def test_process_kill(self):
         """Process is started, we kill it"""
 
         p = processhandler.ProcessHandler([self.python, self.proclaunch, "process_normal_finish_python.ini"],
                                           cwd=here)
         p.run()
         p.kill()
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              expectedfail=('returncode',))
+        self.determine_status(p, expectedfail=('returncode',))
 
     def test_process_kill_deep(self):
         """Process is started, we kill it, we use a deep process tree"""
 
         p = processhandler.ProcessHandler([self.python, self.proclaunch, "process_normal_deep_python.ini"],
                                           cwd=here)
         p.run()
         p.kill()
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              expectedfail=('returncode',))
+        self.determine_status(p, expectedfail=('returncode',))
 
     def test_process_kill_deep_wait(self):
         """Process is started, we use a deep process tree, we let it spawn
            for a bit, we kill it"""
 
         p = processhandler.ProcessHandler([self.python, self.proclaunch, "process_normal_deep_python.ini"],
                                           cwd=here)
         p.run()
         # Let the tree spawn a bit, before attempting to kill
         time.sleep(3)
         p.kill()
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              expectedfail=('returncode',))
+        self.determine_status(p, expectedfail=('returncode',))
 
     def test_process_kill_broad(self):
         """Process is started, we kill it, we use a broad process tree"""
 
         p = processhandler.ProcessHandler([self.python, self.proclaunch, "process_normal_broad_python.ini"],
                                           cwd=here)
         p.run()
         p.kill()
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              expectedfail=('returncode',))
+        self.determine_status(p, expectedfail=('returncode',))
 
     @unittest.skipUnless(processhandler.isPosix, "posix only")
     def test_process_kill_with_sigterm(self):
         script = os.path.join(here, 'infinite_loop.py')
         p = processhandler.ProcessHandler([self.python, script])
 
         p.run()
         p.kill()
--- a/testing/mozbase/mozprocess/tests/test_mozprocess_kill_broad_wait.py
+++ b/testing/mozbase/mozprocess/tests/test_mozprocess_kill_broad_wait.py
@@ -20,17 +20,12 @@ class ProcTestKill(proctest.ProcTest):
 
         p = processhandler.ProcessHandler([self.python, self.proclaunch, "process_normal_broad_python.ini"],
                                           cwd=here)
         p.run()
         # Let the tree spawn a bit, before attempting to kill
         time.sleep(3)
         p.kill()
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              expectedfail=('returncode',))
+        self.determine_status(p, expectedfail=('returncode',))
 
 if __name__ == '__main__':
     unittest.main()
--- a/testing/mozbase/mozprocess/tests/test_mozprocess_misc.py
+++ b/testing/mozbase/mozprocess/tests/test_mozprocess_misc.py
@@ -17,18 +17,12 @@ class ProcTestMisc(proctest.ProcTest):
         p = processhandler.ProcessHandler([self.python, self.proclaunch,
                                           "process_waittimeout_10s_python.ini"],
                                           cwd=here)
 
         p.run()
         p.processOutput(timeout=5)
         p.wait()
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              False,
-                              ())
+        self.determine_status(p, False, ())
 
 if __name__ == '__main__':
     unittest.main()
--- a/testing/mozbase/mozprocess/tests/test_mozprocess_output.py
+++ b/testing/mozbase/mozprocess/tests/test_mozprocess_output.py
@@ -17,23 +17,17 @@ class ProcTestOutput(proctest.ProcTest):
         """
         p = processhandler.ProcessHandler([self.python, "procnonewline.py"],
                                           cwd=here)
 
         p.run()
         p.processOutput(timeout=5)
         p.wait()
 
-        detected, output = proctest.check_for_process("procnonewline.py")
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              False,
-                              ())
+        self.determine_status(p, False, ())
 
     def test_stream_process_output(self):
         """
         Process output stream does not buffer
         """
         expected = '\n'.join([str(n) for n in range(0,10)])
 
         stream = io.BytesIO()
@@ -51,18 +45,12 @@ class ProcTestOutput(proctest.ProcTest):
         buf.flush()
         self.assertEquals(stream.getvalue().strip(), expected)
 
         # make sure mozprocess doesn't close the stream
         # since mozprocess didn't create it
         self.assertFalse(buf.closed)
         buf.close()
 
-        detected, output = proctest.check_for_process("proccountfive.py")
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              False,
-                              ())
+        self.determine_status(p, False, ())
 
 if __name__ == '__main__':
     unittest.main()
--- a/testing/mozbase/mozprocess/tests/test_mozprocess_poll.py
+++ b/testing/mozbase/mozprocess/tests/test_mozprocess_poll.py
@@ -29,99 +29,78 @@ class ProcTestPoll(proctest.ProcTest):
         p = processhandler.ProcessHandler([self.python, self.proclaunch,
                                           "process_normal_finish_python.ini"],
                                           cwd=here)
         p.run()
         returncode = p.poll()
 
         self.assertEqual(returncode, None)
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              returncode,
-                              p.didTimeout,
-                              True)
+        self.determine_status(p, True)
         p.kill()
 
     def test_poll_after_kill(self):
         """Process is killed, and poll() is called"""
 
         p = processhandler.ProcessHandler([self.python, self.proclaunch,
                                           "process_normal_finish_python.ini"],
                                           cwd=here)
         p.run()
         returncode = p.kill()
 
         # We killed the process, so the returncode should be < 0
         self.assertLess(returncode, 0)
         self.assertEqual(returncode, p.poll())
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              returncode,
-                              p.didTimeout)
+        self.determine_status(p)
 
     def test_poll_after_kill_no_process_group(self):
         """Process (no group) is killed, and poll() is called"""
 
         p = processhandler.ProcessHandler([self.python, self.proclaunch,
                                           "process_normal_finish_no_process_group.ini"],
                                           cwd=here,
                                           ignore_children=True
                                           )
         p.run()
         returncode = p.kill()
 
         # We killed the process, so the returncode should be < 0
         self.assertLess(returncode, 0)
         self.assertEqual(returncode, p.poll())
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              returncode,
-                              p.didTimeout)
+        self.determine_status(p)
 
     def test_poll_after_double_kill(self):
         """Process is killed twice, and poll() is called"""
 
         p = processhandler.ProcessHandler([self.python, self.proclaunch,
                                           "process_normal_finish_python.ini"],
                                           cwd=here)
         p.run()
         p.kill()
         returncode = p.kill()
 
         # We killed the process, so the returncode should be < 0
         self.assertLess(returncode, 0)
         self.assertEqual(returncode, p.poll())
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              returncode,
-                              p.didTimeout)
+        self.determine_status(p)
 
     def test_poll_after_external_kill(self):
         """Process is killed externally, and poll() is called"""
 
         p = processhandler.ProcessHandler([self.python, self.proclaunch,
                                           "process_normal_finish_python.ini"],
                                           cwd=here)
         p.run()
         os.kill(p.pid, signal.SIGTERM)
         returncode = p.wait()
 
         # We killed the process, so the returncode should be < 0
         self.assertEqual(returncode, -signal.SIGTERM)
         self.assertEqual(returncode, p.poll())
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              returncode,
-                              p.didTimeout)
+        self.determine_status(p)
 
 
 if __name__ == '__main__':
     unittest.main()
--- a/testing/mozbase/mozprocess/tests/test_mozprocess_wait.py
+++ b/testing/mozbase/mozprocess/tests/test_mozprocess_wait.py
@@ -14,110 +14,81 @@ class ProcTestWait(proctest.ProcTest):
     def test_normal_finish(self):
         """Process is started, runs to completion while we wait for it"""
 
         p = processhandler.ProcessHandler([self.python, self.proclaunch, "process_normal_finish_python.ini"],
                                           cwd=here)
         p.run()
         p.wait()
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout)
+        self.determine_status(p)
 
     def test_wait(self):
         """Process is started runs to completion while we wait indefinitely"""
 
         p = processhandler.ProcessHandler([self.python, self.proclaunch,
                                           "process_waittimeout_10s_python.ini"],
                                           cwd=here)
         p.run()
         p.wait()
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout)
+        self.determine_status(p)
 
 
     def test_timeout(self):
         """ Process is started, runs but we time out waiting on it
             to complete
         """
         p = processhandler.ProcessHandler([self.python, self.proclaunch, "process_waittimeout_python.ini"],
                                           cwd=here)
         p.run(timeout=10)
         p.wait()
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-
         if mozinfo.isUnix:
             # process was killed, so returncode should be negative
             self.assertLess(p.proc.returncode, 0)
 
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              False,
-                              ['returncode', 'didtimeout'])
+        self.determine_status(p, False, ['returncode', 'didtimeout'])
 
     def test_waittimeout(self):
         """
         Process is started, then wait is called and times out.
         Process is still running and didn't timeout
         """
         p = processhandler.ProcessHandler([self.python, self.proclaunch,
                                           "process_waittimeout_10s_python.ini"],
                                           cwd=here)
 
         p.run()
         p.wait(timeout=5)
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout,
-                              True,
-                              ())
+        self.determine_status(p, True, ())
 
     def test_waitnotimeout(self):
         """ Process is started, runs to completion before our wait times out
         """
         p = processhandler.ProcessHandler([self.python, self.proclaunch,
                                           "process_waittimeout_10s_python.ini"],
                                           cwd=here)
         p.run(timeout=30)
         p.wait()
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              p.proc.returncode,
-                              p.didTimeout)
+        self.determine_status(p)
 
     def test_wait_twice_after_kill(self):
         """Bug 968718: Process is started and stopped. wait() twice afterward."""
         p = processhandler.ProcessHandler([self.python, self.proclaunch,
                                           "process_waittimeout_python.ini"],
                                           cwd=here)
         p.run()
         p.kill()
         returncode1 = p.wait()
         returncode2 = p.wait()
 
-        detected, output = proctest.check_for_process(self.proclaunch)
-        self.determine_status(detected,
-                              output,
-                              returncode2,
-                              p.didTimeout)
+        self.determine_status(p)
 
         self.assertLess(returncode2, 0,
                         'Negative returncode expected, got "%s"' % returncode2)
         self.assertEqual(returncode1, returncode2,
                          'Expected both returncodes of wait() to be equal')
 
 if __name__ == '__main__':
     unittest.main()
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -1391,22 +1391,27 @@ or run without that action (ie: --no-{ac
                             property_conditions=[]):
         if not self.client_id or not self.access_token:
             self.warning('Skipping S3 file upload: No taskcluster credentials.')
             return
 
         repo = self._query_repo()
         revision = self.query_revision()
         pushinfo = self.vcs_query_pushinfo(repo, revision)
+        pushdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(pushinfo.pushdate))
 
         index = self.config.get('taskcluster_index', 'index.garbage.staging')
         fmt = {
             'index': index,
             'project': self.buildbot_config['properties']['branch'],
             'head_rev': revision,
+            'pushdate': pushdate,
+            'year': pushdate[0:4],
+            'month': pushdate[4:6],
+            'day': pushdate[6:8],
             'build_product': self.config['stage_product'],
             'build_name': self.query_build_name(),
             'build_type': self.query_build_type(),
             'locale': locale,
         }
         fmt.update(self.buildid_to_dict(self.query_buildid()))
         routes = []
         for template in templates:
--- a/testing/mozharness/scripts/desktop_l10n.py
+++ b/testing/mozharness/scripts/desktop_l10n.py
@@ -6,16 +6,17 @@
 # ***** END LICENSE BLOCK *****
 """desktop_l10n.py
 
 This script manages Desktop repacks for nightly builds.
 """
 import os
 import re
 import sys
+import time
 import shlex
 import logging
 
 import subprocess
 
 # load modules from parent dir
 sys.path.insert(1, os.path.dirname(sys.path[0]))
 
@@ -960,31 +961,36 @@ class DesktopSingleLocale(LocalesMixin, 
 
         branch = self.config['branch']
         platform = self.config['platform']
         revision = self._query_revision()
         repo = self.query_l10n_repo()
         if not repo:
             self.fatal("Unable to determine repository for querying the push info.")
         pushinfo = self.vcs_query_pushinfo(repo, revision, vcs='hgtool')
+        pushdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(pushinfo.pushdate))
 
         routes_json = os.path.join(self.query_abs_dirs()['abs_mozilla_dir'],
                                    'testing/taskcluster/routes.json')
         with open(routes_json) as f:
             contents = json.load(f)
             templates = contents['l10n']
 
         for locale, files in self.upload_files.iteritems():
             self.info("Uploading files to S3 for locale '%s': %s" % (locale, files))
             routes = []
             for template in templates:
                 fmt = {
                     'index': self.config.get('taskcluster_index', 'index.garbage.staging'),
                     'project': branch,
                     'head_rev': revision,
+                    'pushdate': pushdate,
+                    'year': pushdate[0:4],
+                    'month': pushdate[4:6],
+                    'day': pushdate[6:8],
                     'build_product': self.config['stage_product'],
                     'build_name': self.query_build_name(),
                     'build_type': self.query_build_type(),
                     'locale': locale,
                 }
                 fmt.update(self.buildid_to_dict(self._query_buildid()))
                 routes.append(template.format(**fmt))
 
--- a/testing/mozharness/scripts/mobile_l10n.py
+++ b/testing/mozharness/scripts/mobile_l10n.py
@@ -10,16 +10,17 @@ This currently supports nightly and rele
 Android.  This also creates nightly updates.
 """
 
 from copy import deepcopy
 import os
 import re
 import subprocess
 import sys
+import time
 import shlex
 
 try:
     import simplejson as json
     assert json
 except ImportError:
     import json
 
@@ -481,16 +482,17 @@ class MobileSingleLocale(MockMixin, Loca
         locales = self.query_locales()
         make = self.query_exe("make")
         upload_env = self.query_upload_env()
         cwd = dirs['abs_locales_dir']
         branch = self.config['branch']
         revision = self.query_revision()
         repo = self.query_l10n_repo()
         pushinfo = self.vcs_query_pushinfo(repo, revision, vcs='hgtool')
+        pushdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(pushinfo.pushdate))
         routes_json = os.path.join(self.query_abs_dirs()['abs_mozilla_dir'],
                                    'testing/taskcluster/routes.json')
         with open(routes_json) as f:
             contents = json.load(f)
             templates = contents['l10n']
 
         for locale in locales:
             output = self.get_output_from_command_m(
@@ -501,16 +503,20 @@ class MobileSingleLocale(MockMixin, Loca
             files = shlex.split(output)
             abs_files = [os.path.abspath(os.path.join(cwd, f)) for f in files]
 
             routes = []
             fmt = {
                 'index': self.config.get('taskcluster_index', 'index.garbage.staging'),
                 'project': branch,
                 'head_rev': revision,
+                'pushdate': pushdate,
+                'year': pushdate[0:4],
+                'month': pushdate[4:6],
+                'day': pushdate[6:8],
                 'build_product': self.config['stage_product'],
                 'build_name': self.query_build_name(),
                 'build_type': self.query_build_type(),
                 'locale': locale,
             }
             for template in templates:
                 routes.append(template.format(**fmt))
 
--- a/testing/talos/talos/config.py
+++ b/testing/talos/talos/config.py
@@ -292,24 +292,16 @@ def get_counters(config):
     if config['rss']:
         counters.add('Main_RSS')
     return counters
 
 
 def get_active_tests(config):
     activeTests = config.pop('activeTests').strip().split(':')
 
-    # temporary hack for now until we have e10s running on all tests
-    if config['e10s'] and not config['develop']:
-        for testname in ('sessionrestore',
-                         'sessionrestore_no_auto_restore'):
-            if testname in activeTests:
-                print "%s is unsupported on e10s, removing from list of " \
-                    "tests to run" % testname
-                activeTests.remove(testname)
     # ensure tests are available
     availableTests = test.test_dict()
     if not set(activeTests).issubset(availableTests):
         missing = [i for i in activeTests
                    if i not in availableTests]
         raise ConfigurationError("No definition found for test(s): %s"
                                  % missing)
     return activeTests
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/startup_test/sessionrestore/addon/SessionRestoreTalosTest.js
@@ -0,0 +1,89 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+  "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "setTimeout",
+  "resource://gre/modules/Timer.jsm");
+
+// Observer Service topics.
+const STARTUP_TOPIC = "profile-after-change";
+const RESTORED_TOPIC = "sessionstore-windows-restored";
+
+// Process Message Manager topics.
+const MSG_REQUEST = "session-restore-test?duration";
+const MSG_PROVIDE = "session-restore-test:duration";
+
+function nsSessionRestoreTalosTest() {}
+
+nsSessionRestoreTalosTest.prototype = {
+  classID: Components.ID("{716346e5-0c45-4aa2-b601-da36f3c74bd8}"),
+
+  _xpcom_factory: XPCOMUtils.generateSingletonFactory(nsSessionRestoreTalosTest),
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsISupports
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]),
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsIObserver
+
+  observe: function DS_observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case STARTUP_TOPIC:
+        this.init();
+        break;
+      case RESTORED_TOPIC:
+        this.onRestored();
+        break;
+      default:
+        throw new Error(`Unknown topic ${aTopic}`);
+    }
+  },
+
+  /**
+   * Perform initialization on profile-after-change.
+   */
+  init: function() {
+    Services.obs.addObserver(this, RESTORED_TOPIC, false);
+  },
+
+  /**
+   * Session Restore is complete, hurray.
+   */
+  onRestored: function() {
+    setTimeout(function() {
+      // `sessionRestored` actually becomes available only on the next tick.
+      let startup_info = Services.startup.getStartupInfo();
+      let duration = startup_info.sessionRestored - startup_info.sessionRestoreInit;
+
+      // Broadcast startup duration information immediately, in case the talos
+      // page is already loaded.
+      Services.ppmm.broadcastAsyncMessage(MSG_PROVIDE, {duration});
+
+      // Now, in case the talos page isn't loaded yet, prepare to respond if it
+      // requestions the duration information.
+      Services.ppmm.addMessageListener(MSG_REQUEST, function listener() {
+        Services.ppmm.removeMessageListener(MSG_REQUEST, listener);
+        Services.ppmm.broadcastAsyncMessage(MSG_PROVIDE, {duration});
+      });
+    }, 0);
+  },
+};
+
+////////////////////////////////////////////////////////////////////////////////
+//// Module
+
+this.NSGetFactory = XPCOMUtils.generateNSGetFactory([nsSessionRestoreTalosTest]);
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/startup_test/sessionrestore/addon/chrome.manifest
@@ -0,0 +1,8 @@
+# Register a component to be informed of startup. This component can then register
+# itself to watch sessionstore-windows-restored. Once it has observed
+# sessionstore-windows-restored, it will open the webpage with the harness.
+component {716346e5-0c45-4aa2-b601-da36f3c74bd8} SessionRestoreTalosTest.js
+contract @mozilla.org/talos/session-restore-test;1 {716346e5-0c45-4aa2-b601-da36f3c74bd8}
+category profile-after-change nsSessionRestoreTalosTest @mozilla.org/talos/session-restore-test;1
+
+content session-restore-test content/
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/startup_test/sessionrestore/addon/install.rdf
@@ -0,0 +1,20 @@
+<?xml version="1.0"?><RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"     xmlns:em="http://www.mozilla.org/2004/em-rdf#"><Description about="urn:mozilla:install-manifest">
+
+<!-- Required Items -->
+<em:id>session-restore-test@mozilla.org</em:id>
+<em:name>Session Restore Startup Performance Test</em:name>
+<em:version>1.2.0</em:version>
+
+<em:targetApplication>
+    <Description>
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+        <em:minVersion>1.5</em:minVersion>
+        <em:maxVersion>99.0.*</em:maxVersion>
+    </Description>
+</em:targetApplication>
+
+<!-- Optional Items -->
+<em:creator>David Rajchenbach-Teller</em:creator>
+<em:description>Bug 936630, bug 1098357. This add-on broadcasts the duration of session restore.</em:description>
+<em:homepageURL>https://bugzilla.mozilla.org/show_bug.cgi?id=1098357</em:homepageURL>
+</Description></RDF>
--- a/testing/talos/talos/startup_test/sessionrestore/main.js
+++ b/testing/talos/talos/startup_test/sessionrestore/main.js
@@ -1,54 +1,42 @@
 "use strict";
 
 var Services = Components.utils.import("resource://gre/modules/Services.jsm", {}).Services;
 
-/**
- * Display the result, send it to the harness and quit.
- */
-function finish() {
-  Profiler.pause("This test measures the time between sessionRestoreInit and sessionRestored, ignore everything around that");
-  Profiler.initFromURLQueryParams(location.search);
-  Profiler.finishStartupProfiling();
+// Process Message Manager topics.
+const MSG_REQUEST = "session-restore-test?duration";
+const MSG_PROVIDE = "session-restore-test:duration";
 
-  setTimeout(function () {
-    var startup_info = Services.startup.getStartupInfo();
-
-    var duration = startup_info.sessionRestored - startup_info.sessionRestoreInit;
+Services.cpmm.addMessageListener(MSG_PROVIDE,
+  /**
+   * Display the result, send it to the harness and quit.
+   */
+  function finish(msg) {
+    console.log(`main.js: received data on ${MSG_PROVIDE}`, msg);
+    Services.cpmm.removeMessageListener(MSG_PROVIDE, finish);
+    var duration = msg.data.duration;
 
-    // Show result on screen. Nice but not really necessary.
-    document.getElementById("sessionRestoreInit-to-sessionRestored").textContent = duration + "ms";
-
-    // Report data to Talos, if possible
-    dumpLog("__start_report" +
-            duration         +
-            "__end_report\n\n");
+    Profiler.pause("This test measures the time between sessionRestoreInit and sessionRestored, ignore everything around that");
+    Profiler.initFromURLQueryParams(location.search);
+    Profiler.finishStartupProfiling();
 
-    // Next one is required by the test harness but not used
-    dumpLog("__startTimestamp" +
-            Date.now()         +
-            "__endTimestamp\n\n");
+    setTimeout(function () {
+      // Show result on screen. Nice but not really necessary.
+      document.getElementById("sessionRestoreInit-to-sessionRestored").textContent = duration + "ms";
 
-    goQuitApplication();
-  }, 0);
-}
-
-function main() {
-  // Collect (and display) data
-  var startup_info = Services.startup.getStartupInfo();
+      // Report data to Talos, if possible
+      dumpLog("__start_report" +
+              duration         +
+              "__end_report\n\n");
 
-  // The script may be triggered before or after sessionRestored
-  // and sessionRestoreInit are available. If both are available,
-  // we are done.
-  if (typeof startup_info.sessionRestored != "undefined"
-   && typeof startup_info.sessionRestoreInit != "undefined") {
-    finish();
-    return;
-  }
+      // Next one is required by the test harness but not used
+      dumpLog("__startTimestamp" +
+              Date.now()         +
+              "__endTimestamp\n\n");
 
-  // Otherwise, we need to wait until *after* sesionstore-windows-restored,
-  // which is the event that sets sessionRestored - since sessionRestoreInit
-  // is set before sessionRestored, we are certain that both are now set.
-  Services.obs.addObserver(finish, "sessionstore-windows-restored", false);
-}
+      goQuitApplication();
+    }, 0);
+});
 
-main();
+// In case the add-on has broadcasted the message before we were loaded,
+// request a second broadcast.
+Services.cpmm.sendAsyncMessage(MSG_REQUEST, {});
new file mode 100644
--- /dev/null
+++ b/testing/talos/talos/startup_test/sessionrestore/profile/sessionCheckpoints.json
@@ -0,0 +1,1 @@
+{"profile-after-change":true,"final-ui-startup":true,"sessionstore-windows-restored":true,"quit-application-granted":true,"quit-application":true,"sessionstore-final-state-write-complete":true,"profile-change-net-teardown":true,"profile-change-teardown":true,"profile-before-change":true}
--- a/testing/talos/talos/test.py
+++ b/testing/talos/talos/test.py
@@ -143,24 +143,25 @@ class ts_paint(TsBase):
 class sessionrestore(TsBase):
     """
     A start up test measuring the time it takes to load a sessionstore.js file.
 
     1. Set up Firefox to restore from a given sessionstore.js file.
     2. Launch Firefox.
     3. Measure the delta between firstPaint and sessionRestored.
     """
+    extensions = '${talos}/startup_test/sessionrestore/addon'
     cycles = 10
     timeout = 1000000
     sps_profile_startup = True
     sps_profile_entries = 10000000
     profile_path = '${talos}/startup_test/sessionrestore/profile'
     url = 'startup_test/sessionrestore/index.html'
     shutdown = False
-    reinstall = ['sessionstore.js']
+    reinstall = ['sessionstore.js', 'sessionCheckpoints.json']
     # Restore the session
     preferences = {'browser.startup.page': 3}
 
 
 @register_test()
 class sessionrestore_no_auto_restore(sessionrestore):
     """
     A start up test measuring the time it takes to load a sessionstore.js file.
--- a/testing/taskcluster/mach_commands.py
+++ b/testing/taskcluster/mach_commands.py
@@ -6,16 +6,18 @@
 
 from __future__ import absolute_import
 
 from collections import defaultdict
 import os
 import json
 import copy
 import sys
+import time
+from collections import namedtuple
 
 from mach.decorators import (
     CommandArgument,
     CommandProvider,
     Command,
 )
 
 
@@ -198,16 +200,54 @@ def remove_caches_from_task(task):
     try:
         caches = task["task"]["payload"]["cache"]
         for cache in caches.keys():
             if cache not in whitelist:
                 caches.pop(cache)
     except KeyError:
         pass
 
+def query_pushinfo(repository, revision):
+    """Query the pushdate and pushid of a repository/revision.
+    This is intended to be used on hg.mozilla.org/mozilla-central and
+    similar. It may or may not work for other hg repositories.
+    """
+    PushInfo = namedtuple('PushInfo', ['pushid', 'pushdate'])
+
+    try:
+        import urllib2
+        url = '%s/json-pushes?changeset=%s' % (repository, revision)
+        sys.stderr.write("Querying URL for pushdate: %s\n" % url)
+        contents = json.load(urllib2.urlopen(url))
+
+        # The contents should be something like:
+        # {
+        #   "28537": {
+        #    "changesets": [
+        #     "1d0a914ae676cc5ed203cdc05c16d8e0c22af7e5",
+        #    ],
+        #    "date": 1428072488,
+        #    "user": "user@mozilla.com"
+        #   }
+        # }
+        #
+        # So we grab the first element ("28537" in this case) and then pull
+        # out the 'date' field.
+        pushid = contents.iterkeys().next()
+        pushdate = contents[pushid]['date']
+        return PushInfo(pushid, pushdate)
+
+    except Exception:
+        sys.stderr.write(
+            "Error querying pushinfo for repository '%s' revision '%s'\n" % (
+                repository, revision,
+            )
+        )
+        return None
+
 @CommandProvider
 class DecisionTask(object):
     @Command('taskcluster-decision', category="ci",
         description="Build a decision task")
     @CommandArgument('--project',
         required=True,
         help='Treeherder project name')
     @CommandArgument('--url',
@@ -328,27 +368,37 @@ class Graph(object):
         job_path = job_path if os.path.exists(job_path) else DEFAULT_JOB_PATH
 
         jobs = templates.load(job_path, {})
 
         job_graph = parse_commit(message, jobs)
 
         cmdline_interactive = params.get('interactive', False)
 
+        # Default to current time if querying the head rev fails
+        pushdate = time.strftime('%Y%m%d%H%M%S', time.gmtime())
+        pushinfo = query_pushinfo(params['head_repository'], params['head_rev'])
+        if pushinfo:
+            pushdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(pushinfo.pushdate))
+
         # Template parameters used when expanding the graph
         parameters = dict(gaia_info().items() + {
             'index': 'index',
             'project': project,
             'pushlog_id': params.get('pushlog_id', 0),
             'docker_image': docker_image,
             'base_repository': params['base_repository'] or \
                 params['head_repository'],
             'head_repository': params['head_repository'],
             'head_ref': params['head_ref'] or params['head_rev'],
             'head_rev': params['head_rev'],
+            'pushdate': pushdate,
+            'year': pushdate[0:4],
+            'month': pushdate[4:6],
+            'day': pushdate[6:8],
             'owner': params['owner'],
             'from_now': json_time_from_now,
             'now': current_json_time(),
             'revision_hash': params['revision_hash']
         }.items())
 
         treeherder_route = '{}.{}'.format(
             params['project'],
--- a/testing/taskcluster/routes.json
+++ b/testing/taskcluster/routes.json
@@ -1,16 +1,18 @@
 {
     "routes": [
         "{index}.gecko.v2.{project}.revision.{head_rev}.{build_product}.{build_name}-{build_type}",
+        "{index}.gecko.v2.{project}.pushdate.{year}.{month}.{day}.{pushdate}.{build_product}.{build_name}-{build_type}",
         "{index}.gecko.v2.{project}.latest.{build_product}.{build_name}-{build_type}"
     ],
     "nightly": [
         "{index}.gecko.v2.{project}.nightly.{year}.{month}.{day}.revision.{head_rev}.{build_product}.{build_name}-{build_type}",
         "{index}.gecko.v2.{project}.nightly.{year}.{month}.{day}.latest.{build_product}.{build_name}-{build_type}",
         "{index}.gecko.v2.{project}.nightly.revision.{head_rev}.{build_product}.{build_name}-{build_type}",
         "{index}.gecko.v2.{project}.nightly.latest.{build_product}.{build_name}-{build_type}"
     ],
     "l10n": [
         "{index}.gecko.v2.{project}.revision.{head_rev}.{build_product}-l10n.{build_name}-{build_type}.{locale}",
+        "{index}.gecko.v2.{project}.pushdate.{year}.{month}.{day}.{pushdate}.{build_product}-l10n.{build_name}-{build_type}.{locale}",
         "{index}.gecko.v2.{project}.latest.{build_product}-l10n.{build_name}-{build_type}.{locale}"
     ]
 }
--- a/testing/tools/screenshot/gdk-screenshot.cpp
+++ b/testing/tools/screenshot/gdk-screenshot.cpp
@@ -154,8 +154,16 @@ int main(int argc, char** argv)
   if (error) {
     fprintf(stderr, "%s: failed to write screenshot as png: %s\n",
             argv[0], error->message);
     return error->code;
   }
 
   return 0;
 }
+
+// These options are copied from mozglue/build/AsanOptions.cpp
+#ifdef MOZ_ASAN
+extern "C"
+const char* __asan_default_options() {
+  return "allow_user_segv_handler=1:alloc_dealloc_mismatch=0:detect_leaks=0";
+}
+#endif
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -452,22 +452,16 @@
           }
         ],
         "service-workers/service-worker/skip-waiting.https.html": [
           {
             "path": "service-workers/service-worker/skip-waiting.https.html",
             "url": "/_mozilla/service-workers/service-worker/skip-waiting.https.html"
           }
         ],
-        "service-workers/service-worker/stashed-ports.https.html": [
-          {
-            "path": "service-workers/service-worker/stashed-ports.https.html",
-            "url": "/_mozilla/service-workers/service-worker/stashed-ports.https.html"
-          }
-        ],
         "service-workers/service-worker/state.https.html": [
           {
             "path": "service-workers/service-worker/state.https.html",
             "url": "/_mozilla/service-workers/service-worker/state.https.html"
           }
         ],
         "service-workers/service-worker/synced-state.https.html": [
           {
deleted file mode 100644
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/invalid-blobtype.https.html.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[invalid-blobtype.https.html]
-  type: testharness
-  [Verify the response of FetchEvent using XMLHttpRequest]
-    expected: FAIL
-
deleted file mode 100644
--- a/testing/web-platform/mozilla/meta/service-workers/service-worker/stashed-ports.https.html.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[stashed-ports.https.html]
-  type: testharness
-  [Name set when adding port is set correctly.]
-    expected: FAIL
-
-  [Messages posted into stashed port arrive on other side.]
-    expected: FAIL
-
-  [Messages sent to stashed port arrive as global events.]
-    expected: FAIL
-
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-blobtype-worker.js
+++ b/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/invalid-blobtype-worker.js
@@ -1,11 +1,10 @@
 self.addEventListener('fetch', function(event) {
     var url = event.request.url;
     if (url.indexOf('dummy?test') == -1) {
       return;
     }
     event.respondWith(new Promise(function(resolve) {
-        var headers = new Headers;
         // null byte in blob type
         resolve(new Response(new Blob([],{type: 'a\0b'})));
       }));
   });
deleted file mode 100644
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/resources/stashed-ports-basics.js
+++ /dev/null
@@ -1,57 +0,0 @@
-importScripts('worker-testharness.js');
-
-// Helper method that waits for a reply on a port, and resolves a promise with
-// the reply.
-function wait_for_message_to_port(test, port) {
-  return new Promise(function(resolve) {
-    var resolved = false;
-    port.onmessage = test.step_func(function(event) {
-      assert_false(resolved);
-      resolved = true;
-      resolve(event.data);
-    });
-  });
-}
-
-// Similar helper method, but waits for a reply to a stashed port with a
-// particular name.
-function wait_for_message_to_stashed_port(test, name) {
-  return new Promise(function(resolve) {
-    var event_handler = function(e) {
-      if (e.source.name === name) {
-        self.ports.removeEventListener('message', event_handler);
-        resolve(e);
-      }
-    };
-    self.ports.addEventListener('message', event_handler);
-  });
-}
-
-test(function(test) {
-    var channel = new MessageChannel();
-    var name = 'somename';
-    var stashed_port = self.ports.add(name, channel.port1);
-    assert_equals(stashed_port.name, name);
-  }, 'Name set when adding port is set correctly.');
-
-promise_test(function(test) {
-    var channel = new MessageChannel();
-    var stashed_port = self.ports.add('', channel.port1);
-    stashed_port.postMessage('pingy ping');
-    return wait_for_message_to_port(test, channel.port2)
-      .then(test.step_func(function(reply) {
-          assert_equals(reply, 'pingy ping');
-      }));
-  }, 'Messages posted into stashed port arrive on other side.');
-
-promise_test(function(test) {
-    var channel = new MessageChannel();
-    var name = 'test_events';
-    var stashed_port = self.ports.add(name, channel.port1);
-    channel.port2.postMessage('ping blah');
-    return wait_for_message_to_stashed_port(test, name)
-      .then(test.step_func(function(reply) {
-          assert_equals(reply.data, 'ping blah');
-          assert_equals(reply.source, stashed_port);
-      }));
-  }, 'Messages sent to stashed port arrive as global events.');
deleted file mode 100644
--- a/testing/web-platform/mozilla/tests/service-workers/service-worker/stashed-ports.https.html
+++ /dev/null
@@ -1,11 +0,0 @@
-<!DOCTYPE html>
-<title>
-  Tests basic stashed message ports functionality.
-</title>
-<script src="/resources/testharness.js"></script>
-<script src="/resources/testharnessreport.js"></script>
-<script src="../service-worker/resources/test-helpers.sub.js"></script>
-<script>
-service_worker_test('resources/stashed-ports-basics.js',
-                    'Verifies basic functionality of stashed ports.');
-</script>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8831a80588dcaffe9a0e075f3d00066faed3d482
GIT binary patch
literal 7402
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
WE-oSF-FmY>&=h1<Wj;%rhWsC!VsDxN
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b25b4452aa1a52181c324020c39175e0f8f425a2
GIT binary patch
literal 16131
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
CzxRv)
--- a/toolkit/themes/shared/jar.inc.mn
+++ b/toolkit/themes/shared/jar.inc.mn
@@ -12,16 +12,18 @@
   skin/classic/global/aboutCacheEntry.css                  (../../shared/aboutCacheEntry.css)
   skin/classic/global/aboutMemory.css                      (../../shared/aboutMemory.css)
   skin/classic/global/aboutReader.css                      (../../shared/aboutReader.css)
   skin/classic/global/aboutReaderContent.css               (../../shared/aboutReaderContent.css)
 * skin/classic/global/aboutReaderControls.css              (../../shared/aboutReaderControls.css)
   skin/classic/global/aboutSupport.css                     (../../shared/aboutSupport.css)
   skin/classic/global/appPicker.css                        (../../shared/appPicker.css)
   skin/classic/global/config.css                           (../../shared/config.css)
+  skin/classic/global/icons/loading-inverted.png           (../../shared/icons/loading-inverted.png)
+  skin/classic/global/icons/loading-inverted@2x.png        (../../shared/icons/loading-inverted@2x.png)
   skin/classic/global/icons/warning.svg                    (../../shared/incontent-icons/warning.svg)
   skin/classic/global/alerts/alert-common.css              (../../shared/alert-common.css)
   skin/classic/global/menu/shared-menu-check@2x.png        (../../shared/menu-check@2x.png)
   skin/classic/global/menu/shared-menu-check.png           (../../shared/menu-check.png)
   skin/classic/global/menu/shared-menu-check-active.svg    (../../shared/menu-check-active.svg)
   skin/classic/global/menu/shared-menu-check-black.svg     (../../shared/menu-check-black.svg)
   skin/classic/global/menu/shared-menu-check-hover.svg     (../../shared/menu-check-hover.svg)
   skin/classic/global/in-content/check.svg                 (../../shared/in-content/check.svg)
--- a/widget/cocoa/nsDragService.h
+++ b/widget/cocoa/nsDragService.h
@@ -25,19 +25,21 @@ extern NSString* const kCorePboardType_u
 - (NSPasteboard*)pasteboard;
 @end
 
 class nsDragService : public nsBaseDragService
 {
 public:
   nsDragService();
 
+  // nsBaseDragService
+  virtual nsresult InvokeDragSessionImpl(nsISupportsArray* anArrayTransferables,
+                                         nsIScriptableRegion* aRegion,
+                                         uint32_t aActionType);
   // nsIDragService
-  NS_IMETHOD InvokeDragSession(nsIDOMNode *aDOMNode, nsISupportsArray * anArrayTransferables,
-                               nsIScriptableRegion * aRegion, uint32_t aActionType);
   NS_IMETHOD EndDragSession(bool aDoneDrag);
 
   // nsIDragSession
   NS_IMETHOD GetData(nsITransferable * aTransferable, uint32_t aItemIndex);
   NS_IMETHOD IsDataFlavorSupported(const char *aDataFlavor, bool *_retval);
   NS_IMETHOD GetNumDropItems(uint32_t * aNumItems);
 
 protected:
--- a/widget/cocoa/nsDragService.mm
+++ b/widget/cocoa/nsDragService.mm
@@ -268,35 +268,31 @@ nsDragService::ConstructDragImage(nsIDOM
   return [image autorelease];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 // We can only invoke NSView's 'dragImage:at:offset:event:pasteboard:source:slideBack:' from
 // within NSView's 'mouseDown:' or 'mouseDragged:'. Luckily 'mouseDragged' is always on the
 // stack when InvokeDragSession gets called.
-NS_IMETHODIMP
-nsDragService::InvokeDragSession(nsIDOMNode* aDOMNode, nsISupportsArray* aTransferableArray,
-                                 nsIScriptableRegion* aDragRgn, uint32_t aActionType)
+nsresult
+nsDragService::InvokeDragSessionImpl(nsISupportsArray* aTransferableArray,
+                                     nsIScriptableRegion* aDragRgn,
+                                     uint32_t aActionType)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
-  nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode,
-                                                     aTransferableArray,
-                                                     aDragRgn, aActionType);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   mDataItems = aTransferableArray;
 
   // put data on the clipboard
   if (NS_FAILED(SetUpDragClipboard(aTransferableArray)))
     return NS_ERROR_FAILURE;
 
   nsIntRect dragRect(0, 0, 20, 20);
-  NSImage* image = ConstructDragImage(aDOMNode, &dragRect, aDragRgn);
+  NSImage* image = ConstructDragImage(mSourceNode, &dragRect, aDragRgn);
   if (!image) {
     // if no image was returned, just draw a rectangle
     NSSize size;
     size.width = dragRect.width;
     size.height = dragRect.height;
     image = [[NSImage alloc] initWithSize:size];
     [image lockFocus];
     [[NSColor grayColor] set];
--- a/widget/gtk/nsDragService.cpp
+++ b/widget/gtk/nsDragService.cpp
@@ -307,21 +307,26 @@ nsDragService::InvokeDragSession(nsIDOMN
 
     // If the previous source drag has not yet completed, signal handlers need
     // to be removed from sGrabWidget and dragend needs to be dispatched to
     // the source node, but we can't call EndDragSession yet because we don't
     // know whether or not the drag succeeded.
     if (mSourceNode)
         return NS_ERROR_NOT_AVAILABLE;
 
-    nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode,
-                                                       aArrayTransferables,
-                                                       aRegion, aActionType);
-    NS_ENSURE_SUCCESS(rv, rv);
+    return nsBaseDragService::InvokeDragSession(aDOMNode, aArrayTransferables,
+                                                aRegion, aActionType);
+}
 
+// nsBaseDragService
+nsresult
+nsDragService::InvokeDragSessionImpl(nsISupportsArray* aArrayTransferables,
+                                     nsIScriptableRegion* aRegion,
+                                     uint32_t aActionType)
+{
     // make sure that we have an array of transferables to use
     if (!aArrayTransferables)
         return NS_ERROR_INVALID_ARG;
     // set our reference to the transferables.  this will also addref
     // the transferables since we're going to hang onto this beyond the
     // length of this call
     mSourceDataItems = aArrayTransferables;
     // get the list of items we offer for drags
@@ -372,30 +377,32 @@ nsDragService::InvokeDragSession(nsIDOMN
     GdkDragContext *context = gtk_drag_begin(mHiddenWidget,
                                              sourceList,
                                              action,
                                              1,
                                              &event);
 
     mSourceRegion = nullptr;
 
+    nsresult rv;
     if (context) {
         StartDragSession();
 
         // GTK uses another hidden window for receiving mouse events.
         sGrabWidget = gtk_window_group_get_current_grab(window_group);
         if (sGrabWidget) {
             g_object_ref(sGrabWidget);
             // Only motion and key events are required but connect to
             // "event-after" as this is never blocked by other handlers.
             g_signal_connect(sGrabWidget, "event-after",
                              G_CALLBACK(OnSourceGrabEventAfter), this);
         }
         // We don't have a drag end point yet.
         mEndDragPoint = nsIntPoint(-1, -1);
+        rv = NS_OK;
     }
     else {
         rv = NS_ERROR_FAILURE;
     }
 
     gtk_target_list_unref(sourceList);
 
     return rv;
--- a/widget/gtk/nsDragService.h
+++ b/widget/gtk/nsDragService.h
@@ -53,16 +53,20 @@ class nsDragService final : public nsBas
 {
 public:
     nsDragService();
 
     NS_DECL_ISUPPORTS_INHERITED
 
     NS_DECL_NSIOBSERVER
 
+    // nsBaseDragService
+    virtual nsresult InvokeDragSessionImpl(nsISupportsArray* anArrayTransferables,
+                                           nsIScriptableRegion* aRegion,
+                                           uint32_t aActionType) override;
     // nsIDragService
     NS_IMETHOD InvokeDragSession (nsIDOMNode *aDOMNode,
                                   nsISupportsArray * anArrayTransferables,
                                   nsIScriptableRegion * aRegion,
                                   uint32_t aActionType) override;
     NS_IMETHOD StartDragSession() override;
     NS_IMETHOD EndDragSession(bool aDoneDrag) override;
 
--- a/widget/nsBaseDragService.cpp
+++ b/widget/nsBaseDragService.cpp
@@ -220,17 +220,25 @@ nsBaseDragService::InvokeDragSession(nsI
   mEndDragPoint = nsIntPoint(0, 0);
 
   // When the mouse goes down, the selection code starts a mouse
   // capture. However, this gets in the way of determining drag
   // feedback for things like trees because the event coordinates
   // are in the wrong coord system, so turn off mouse capture.
   nsIPresShell::ClearMouseCapture(nullptr);
 
-  return NS_OK;
+  nsresult rv = InvokeDragSessionImpl(aTransferableArray,
+                                      aDragRgn, aActionType);
+
+  if (NS_FAILED(rv)) {
+    mSourceNode = nullptr;
+    mSourceDocument = nullptr;
+  }
+
+  return rv;
 }
 
 NS_IMETHODIMP
 nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode,
                                               nsISupportsArray* aTransferableArray,
                                               nsIScriptableRegion* aRegion,
                                               uint32_t aActionType,
                                               nsIDOMNode* aImage,
@@ -248,17 +256,26 @@ nsBaseDragService::InvokeDragSessionWith
   mDragPopup = nullptr;
   mImage = aImage;
   mImageOffset = CSSIntPoint(aImageX, aImageY);
 
   aDragEvent->GetScreenX(&mScreenX);
   aDragEvent->GetScreenY(&mScreenY);
   aDragEvent->GetMozInputSource(&mInputSource);
 
-  return InvokeDragSession(aDOMNode, aTransferableArray, aRegion, aActionType);
+  nsresult rv = InvokeDragSession(aDOMNode, aTransferableArray,
+                                  aRegion, aActionType);
+
+  if (NS_FAILED(rv)) {
+    mImage = nullptr;
+    mHasImage = false;
+    mDataTransfer = nullptr;
+  }
+
+  return rv;
 }
 
 NS_IMETHODIMP
 nsBaseDragService::InvokeDragSessionWithSelection(nsISelection* aSelection,
                                                   nsISupportsArray* aTransferableArray,
                                                   uint32_t aActionType,
                                                   nsIDOMDragEvent* aDragEvent,
                                                   nsIDOMDataTransfer* aDataTransfer)
@@ -279,17 +296,26 @@ nsBaseDragService::InvokeDragSessionWith
   aDragEvent->GetMozInputSource(&mInputSource);
 
   // just get the focused node from the selection
   // XXXndeakin this should actually be the deepest node that contains both
   // endpoints of the selection
   nsCOMPtr<nsIDOMNode> node;
   aSelection->GetFocusNode(getter_AddRefs(node));
 
-  return InvokeDragSession(node, aTransferableArray, nullptr, aActionType);
+  nsresult rv = InvokeDragSession(node, aTransferableArray,
+                                  nullptr, aActionType);
+
+  if (NS_FAILED(rv)) {
+    mHasImage = false;
+    mSelection = nullptr;
+    mDataTransfer = nullptr;
+  }
+
+  return rv;
 }
 
 //-------------------------------------------------------------------------
 NS_IMETHODIMP
 nsBaseDragService::GetCurrentSession(nsIDragSession ** aSession)
 {
   if (!aSession)
     return NS_ERROR_INVALID_ARG;
--- a/widget/nsBaseDragService.h
+++ b/widget/nsBaseDragService.h
@@ -59,16 +59,25 @@ public:
   uint16_t GetInputSource() { return mInputSource; }
 
   int32_t TakeChildProcessDragAction();
 
 protected:
   virtual ~nsBaseDragService();
 
   /**
+   * Called from nsBaseDragService to initiate a platform drag from a source
+   * in this process.  This is expected to ensure that StartDragSession() and
+   * EndDragSession() get called if the platform drag is successfully invoked.
+   */
+  virtual nsresult InvokeDragSessionImpl(nsISupportsArray* aTransferableArray,
+                                         nsIScriptableRegion* aDragRgn,
+                                         uint32_t aActionType) = 0;
+
+  /**
    * Draw the drag image, if any, to a surface and return it. The drag image
    * is constructed from mImage if specified, or aDOMNode if mImage is null.
    *
    * aRegion may be used to draw only a subset of the element. This region
    * should be supplied using x and y coordinates measured in css pixels
    * that are relative to the upper-left corner of the window.
    *
    * aScreenX and aScreenY should be the screen coordinates of the mouse click
--- a/widget/nsDragServiceProxy.cpp
+++ b/widget/nsDragServiceProxy.cpp
@@ -18,31 +18,22 @@ NS_IMPL_ISUPPORTS_INHERITED0(nsDragServi
 nsDragServiceProxy::nsDragServiceProxy()
 {
 }
 
 nsDragServiceProxy::~nsDragServiceProxy()
 {
 }
 
-NS_IMETHODIMP
-nsDragServiceProxy::InvokeDragSession(nsIDOMNode* aDOMNode,
-                                      nsISupportsArray* aArrayTransferables,
-                                      nsIScriptableRegion* aRegion,
-                                      uint32_t aActionType)
+nsresult
+nsDragServiceProxy::InvokeDragSessionImpl(nsISupportsArray* aArrayTransferables,
+                                          nsIScriptableRegion* aRegion,
+                                          uint32_t aActionType)
 {
-  nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode,
-                                                     aArrayTransferables,
-                                                     aRegion,
-                                                     aActionType);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsIDOMDocument> sourceDocument;
-  aDOMNode->GetOwnerDocument(getter_AddRefs(sourceDocument));
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(sourceDocument);
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(mSourceDocument);
   NS_ENSURE_STATE(doc->GetDocShell());
   mozilla::dom::TabChild* child =
     mozilla::dom::TabChild::GetFrom(doc->GetDocShell());
   NS_ENSURE_STATE(child);
   nsTArray<mozilla::dom::IPCDataTransfer> dataTransfers;
   nsContentUtils::TransferablesToIPCTransferables(aArrayTransferables,
                                                   dataTransfers,
                                                   child->Manager(),
--- a/widget/nsDragServiceProxy.h
+++ b/widget/nsDragServiceProxy.h
@@ -10,18 +10,17 @@
 
 class nsDragServiceProxy : public nsBaseDragService
 {
 public:
   nsDragServiceProxy();
 
   NS_DECL_ISUPPORTS_INHERITED
 
-  // nsIDragService
-  NS_IMETHOD InvokeDragSession(nsIDOMNode* aDOMNode,
-                               nsISupportsArray* anArrayTransferables,
-                               nsIScriptableRegion* aRegion,
-                               uint32_t aActionType) override;
+  // nsBaseDragService
+  virtual nsresult InvokeDragSessionImpl(nsISupportsArray* anArrayTransferables,
+                                         nsIScriptableRegion* aRegion,
+                                         uint32_t aActionType) override;
 private:
   virtual ~nsDragServiceProxy();
 };
 
 #endif // NSDRAGSERVICEPROXY_H
--- a/widget/windows/nsDragService.cpp
+++ b/widget/windows/nsDragService.cpp
@@ -165,38 +165,31 @@ nsDragService::CreateDragImage(nsIDOMNod
   }
 
   dataSurface->Unmap();
 
   return psdi->hbmpDragImage != nullptr;
 }
 
 //-------------------------------------------------------------------------
-NS_IMETHODIMP
-nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
-                                 nsISupportsArray *anArrayTransferables,
-                                 nsIScriptableRegion *aRegion,
-                                 uint32_t aActionType)
+nsresult
+nsDragService::InvokeDragSessionImpl(nsISupportsArray* anArrayTransferables,
+                                     nsIScriptableRegion* aRegion,
+                                     uint32_t aActionType)
 {
-  nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode,
-                                                     anArrayTransferables,
-                                                     aRegion,
-                                                     aActionType);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // Try and get source URI of the items that are being dragged
   nsIURI *uri = nullptr;
 
   nsCOMPtr<nsIDocument> doc(do_QueryInterface(mSourceDocument));
   if (doc) {
     uri = doc->GetDocumentURI();
   }
 
   uint32_t numItemsToDrag = 0;
-  rv = anArrayTransferables->Count(&numItemsToDrag);
+  nsresult rv = anArrayTransferables->Count(&numItemsToDrag);
   if (!numItemsToDrag)
     return NS_ERROR_FAILURE;
 
   // The clipboard class contains some static utility methods that we
   // can use to create an IDataObject from the transferable
 
   // if we're dragging more than one item, we need to create a
   // "collection" object to fake out the OS. This collection contains
@@ -209,17 +202,17 @@ nsDragService::InvokeDragSession(nsIDOMN
       return NS_ERROR_OUT_OF_MEMORY;
     itemToDrag = dataObjCollection;
     for (uint32_t i=0; i<numItemsToDrag; ++i) {
       nsCOMPtr<nsISupports> supports;
       anArrayTransferables->GetElementAt(i, getter_AddRefs(supports));
       nsCOMPtr<nsITransferable> trans(do_QueryInterface(supports));
       if (trans) {
         // set the requestingNode on the transferable
-        trans->SetRequestingNode(aDOMNode);
+        trans->SetRequestingNode(mSourceNode);
         RefPtr<IDataObject> dataObj;
         rv = nsClipboard::CreateNativeDataObject(trans,
                                                  getter_AddRefs(dataObj), uri);
         NS_ENSURE_SUCCESS(rv, rv);
         // Add the flavors to the collection object too
         rv = nsClipboard::SetupNativeDataObject(trans, dataObjCollection);
         NS_ENSURE_SUCCESS(rv, rv);
 
@@ -228,31 +221,31 @@ nsDragService::InvokeDragSession(nsIDOMN
     }
   } // if dragging multiple items
   else {
     nsCOMPtr<nsISupports> supports;
     anArrayTransferables->GetElementAt(0, getter_AddRefs(supports));
     nsCOMPtr<nsITransferable> trans(do_QueryInterface(supports));
     if (trans) {
       // set the requestingNode on the transferable
-      trans->SetRequestingNode(aDOMNode);
+      trans->SetRequestingNode(mSourceNode);
       rv = nsClipboard::CreateNativeDataObject(trans,
                                                getter_AddRefs(itemToDrag),
                                                uri);
       NS_ENSURE_SUCCESS(rv, rv);
     }
   } // else dragging a single object
 
   // Create a drag image if support is available
   IDragSourceHelper *pdsh;
   if (SUCCEEDED(CoCreateInstance(CLSID_DragDropHelper, nullptr,
                                  CLSCTX_INPROC_SERVER,
                                  IID_IDragSourceHelper, (void**)&pdsh))) {
     SHDRAGIMAGE sdi;
-    if (CreateDragImage(aDOMNode, aRegion, &sdi)) {
+    if (CreateDragImage(mSourceNode, aRegion, &sdi)) {
       if (FAILED(pdsh->InitializeFromBitmap(&sdi, itemToDrag)))
         DeleteObject(sdi.hbmpDragImage);
     }
     pdsh->Release();
   }
 
   // Kick off the native drag session
   return StartInvokingDragSession(itemToDrag, aActionType);
--- a/widget/windows/nsDragService.h
+++ b/widget/windows/nsDragService.h
@@ -18,21 +18,20 @@ class  nsDataObjCollection;
  */
 
 class nsDragService : public nsBaseDragService
 {
 public:
   nsDragService();
   virtual ~nsDragService();
   
-  // nsIDragService
-  NS_IMETHOD InvokeDragSession(nsIDOMNode *aDOMNode,
-                               nsISupportsArray *anArrayTransferables,
-                               nsIScriptableRegion *aRegion,
-                               uint32_t aActionType);
+  // nsBaseDragService
+  virtual nsresult InvokeDragSessionImpl(nsISupportsArray* anArrayTransferables,
+                                         nsIScriptableRegion* aRegion,
+                                         uint32_t aActionType);
 
   // nsIDragSession
   NS_IMETHOD GetData(nsITransferable * aTransferable, uint32_t anItem);
   NS_IMETHOD GetNumDropItems(uint32_t * aNumItems);
   NS_IMETHOD IsDataFlavorSupported(const char *aDataFlavor, bool *_retval);
   NS_IMETHOD EndDragSession(bool aDoneDrag);
 
   // native impl.
--- a/xpcom/glue/nsTHashtable.h
+++ b/xpcom/glue/nsTHashtable.h
@@ -2,19 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsTHashtable_h__
 #define nsTHashtable_h__
 
-#include "nscore.h"
 #include "PLDHashTable.h"
-#include "nsDebug.h"
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/fallible.h"
 #include "mozilla/MemoryChecking.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Move.h"
 #include "mozilla/PodOperations.h"
 #include "mozilla/TypeTraits.h"
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -1473,16 +1473,21 @@ NS_IMETHODIMP nsXULWindow::SavePersisten
 
   nsAutoString   persistString;
   docShellElement->GetAttribute(PERSIST_ATTRIBUTE, persistString);
   if (persistString.IsEmpty()) { // quick check which sometimes helps
     mPersistentAttributesDirty = 0;
     return NS_OK;
   }
 
+  bool isFullscreen = false;
+  if (nsPIDOMWindow* domWindow = mDocShell->GetWindow()) {
+    domWindow->GetFullScreen(&isFullscreen);
+  }
+
   // get our size, position and mode to persist
   nsIntRect rect;
   bool gotRestoredBounds = NS_SUCCEEDED(mWindow->GetRestoredBounds(rect));
 
   CSSToLayoutDeviceScale scale = mWindow->GetDefaultScale();
 
   // make our position relative to our parent, if any
   nsCOMPtr<nsIBaseWindow> parent(do_QueryReferent(mParentWindow));
@@ -1500,75 +1505,83 @@ NS_IMETHODIMP nsXULWindow::SavePersisten
   nsCOMPtr<nsIDOMXULDocument> ownerXULDoc;
 
   // fetch docShellElement's ID and XUL owner document
   ownerXULDoc = do_QueryInterface(docShellElement->OwnerDoc());
   if (docShellElement->IsXULElement()) {
     docShellElement->GetId(windowElementId);
   }
 
+  bool shouldPersist = !isFullscreen && ownerXULDoc;
   ErrorResult rv;
   // (only for size elements which are persisted)
   if ((mPersistentAttributesDirty & PAD_POSITION) && gotRestoredBounds) {
     if (persistString.Find("screenX") >= 0) {
       PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(rect.x / scale.scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(SCREENX_ATTRIBUTE, sizeString, rv);
-      if (ownerXULDoc) // force persistence in case the value didn't change
+      if (shouldPersist) {
         ownerXULDoc->Persist(windowElementId, SCREENX_ATTRIBUTE);
+      }
     }
     if (persistString.Find("screenY") >= 0) {
       PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(rect.y / scale.scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(SCREENY_ATTRIBUTE, sizeString, rv);
-      if (ownerXULDoc)
+      if (shouldPersist) {
         ownerXULDoc->Persist(windowElementId, SCREENY_ATTRIBUTE);
+      }
     }
   }
 
   if ((mPersistentAttributesDirty & PAD_SIZE) && gotRestoredBounds) {
     if (persistString.Find("width") >= 0) {
       PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(rect.width / scale.scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(WIDTH_ATTRIBUTE, sizeString, rv);
-      if (ownerXULDoc)
+      if (shouldPersist) {
         ownerXULDoc->Persist(windowElementId, WIDTH_ATTRIBUTE);
+      }
     }
     if (persistString.Find("height") >= 0) {
       PR_snprintf(sizeBuf, sizeof(sizeBuf), "%d", NSToIntRound(rect.height / scale.scale));
       sizeString.AssignWithConversion(sizeBuf);
       docShellElement->SetAttribute(HEIGHT_ATTRIBUTE, sizeString, rv);
-      if (ownerXULDoc)
+      if (shouldPersist) {
         ownerXULDoc->Persist(windowElementId, HEIGHT_ATTRIBUTE);
+      }
     }
   }
 
   if (mPersistentAttributesDirty & PAD_MISC) {
     nsSizeMode sizeMode = mWindow->SizeMode();
 
     if (sizeMode != nsSizeMode_Minimized) {
       if (sizeMode == nsSizeMode_Maximized)
         sizeString.Assign(SIZEMODE_MAXIMIZED);
       else if (sizeMode == nsSizeMode_Fullscreen)
         sizeString.Assign(SIZEMODE_FULLSCREEN);
       else
         sizeString.Assign(SIZEMODE_NORMAL);
       docShellElement->SetAttribute(MODE_ATTRIBUTE, sizeString, rv);
-      if (ownerXULDoc && persistString.Find("sizemode") >= 0)
+      if (shouldPersist && persistString.Find("sizemode") >= 0) {
         ownerXULDoc->Persist(windowElementId, MODE_ATTRIBUTE);
+      }
     }
     if (persistString.Find("zlevel") >= 0) {
       uint32_t zLevel;
       nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
       if (mediator) {
         mediator->GetZLevel(this, &zLevel);
         PR_snprintf(sizeBuf, sizeof(sizeBuf), "%lu", (unsigned long)zLevel);
         sizeString.AssignWithConversion(sizeBuf);
         docShellElement->SetAttribute(ZLEVEL_ATTRIBUTE, sizeString, rv);
-        ownerXULDoc->Persist(windowElementId, ZLEVEL_ATTRIBUTE);
+        if (shouldPersist) {
+          ownerXULDoc->Persist(windowElementId, ZLEVEL_ATTRIBUTE);
+        }
       }
     }
   }
 
   mPersistentAttributesDirty = 0;
   return NS_OK;
 }