Merge mozilla-central to b2g-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 09 Sep 2015 14:15:22 +0200
changeset 294108 e21caed7a7f4bd1c5eed351997e07959d7b86320
parent 294107 a3fe551ea06bd43a99cb0fc32cd876450d64e17d (current diff)
parent 294092 dd9e40b4695909f1595814c0e79e4d55d73dc283 (diff)
child 294120 7c8e40e00bfe521953b69051e5d59551a684c346
child 294213 6e9129c31e8404ad1a2112c7b4141da1d11c161e
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to b2g-inbound
browser/components/loop/content/shared/img/svg/sharing-mute.svg
build/gen_mach_buildprops.py
gfx/src/gfxCore.h
--- 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 1193379 - Moving location of files under dom/bluetooth requires a clobber
+Bug 1201224 - stop unifying test package during mac universal builds needed a CLOBBER
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -632,19 +632,16 @@ pref("network.protocol-handler.expose.ne
 pref("network.protocol-handler.expose.snews", false);
 pref("network.protocol-handler.expose.nntp", false);
 
 pref("accessibility.typeaheadfind", false);
 pref("accessibility.typeaheadfind.timeout", 5000);
 pref("accessibility.typeaheadfind.linksonly", false);
 pref("accessibility.typeaheadfind.flashBar", 1);
 
-// plugin finder service url
-pref("pfs.datasource.url", "https://pfs.mozilla.org/plugins/PluginFinderService.php?mimetype=%PLUGIN_MIMETYPE%&appID=%APP_ID%&appVersion=%APP_VERSION%&clientOS=%CLIENT_OS%&chromeLocale=%CHROME_LOCALE%&appRelease=%APP_RELEASE%");
-
 pref("plugins.update.url", "https://www.mozilla.org/%LOCALE%/plugincheck/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=plugincheck-update");
 pref("plugins.update.notifyUser", false);
 
 pref("plugins.click_to_play", true);
 pref("plugins.testmode", false);
 
 pref("plugin.default.state", 1);
 
@@ -1929,17 +1926,22 @@ pref("reader.errors.includeURLs", true);
 
 pref("browser.pocket.enabled", true);
 pref("browser.pocket.api", "api.getpocket.com");
 pref("browser.pocket.site", "getpocket.com");
 pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
 pref("browser.pocket.useLocaleList", true);
 pref("browser.pocket.enabledLocales", "cs de en-GB en-US en-ZA es-ES es-MX fr hu it ja ja-JP-mac ko nl pl pt-BR pt-PT ru zh-CN zh-TW");
 
+// View source tabs are only enabled by default for Dev. Ed and Nightly.
+#ifdef RELEASE_BUILD
+pref("view_source.tab", false);
+#else
 pref("view_source.tab", true);
+#endif
 
 // Enable ServiceWorkers for Push API consumers.
 // Interception is still disabled.
 pref("dom.serviceWorkers.enabled", true);
 
 #ifdef NIGHTLY_BUILD
 pref("dom.serviceWorkers.interception.enabled", true);
 #endif
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -91,17 +91,17 @@ var FullScreen = {
       // In TabsInTitlebar._update(), we cancel the appearance update on
       // resize event for exiting fullscreen, since that happens before we
       // change the UI here in the "fullscreen" event. Hence we need to
       // call it here to ensure the appearance is properly updated. See
       // TabsInTitlebar._update() and bug 1173768.
       TabsInTitlebar.updateAppearance(true);
     }
 
-    if (enterFS) {
+    if (enterFS && !document.mozFullScreen) {
       Services.telemetry.getHistogramById("FX_BROWSER_FULLSCREEN_USED")
                         .add(1);
     }
   },
 
   exitDomFullScreen : function() {
     document.mozCancelFullScreen();
   },
--- a/browser/base/content/browser-loop.js
+++ b/browser/base/content/browser-loop.js
@@ -149,17 +149,20 @@ let LoopUI;
             // have resumed the tour as soon as the visitor joined if it was (and
             // the pref would have been set to false already.
             this.MozLoopService.resumeTour("waiting");
             resolve();
             return;
           }
 
           this.PanelFrame.showPopup(window, event ? event.target : this.toolbarButton.node,
-                               "loop", null, "about:looppanel", null, callback);
+            "loop", null, "about:looppanel",
+            // Loop wants a fixed size for the panel. This also stops it dynamically resizing.
+            { width: 330, height: 410 },
+            callback);
         });
       });
     },
 
     /**
      * Method to know whether actions to open the panel should instead resume the tour.
      *
      * We need the panel to be opened via UITour so that it gets @noautohide.
--- a/browser/base/content/test/social/browser_social_activation.js
+++ b/browser/base/content/test/social/browser_social_activation.js
@@ -79,18 +79,18 @@ function activateIFrameProvider(domain, 
     sendActivationEvent(tab, callback, false);
   });
 }
 
 function waitForProviderLoad(cb) {
     waitForCondition(function() {
       let sbrowser = document.getElementById("social-sidebar-browser");
       let provider = SocialSidebar.provider;
-      let postActivation = provider && gBrowser.contentDocument &&
-                            gBrowser.contentDocument.location.href == provider.origin + "/browser/browser/base/content/test/social/social_postActivation.html";
+      let postActivation = provider && gBrowser.currentURI &&
+                            gBrowser.currentURI.spec == provider.origin + "/browser/browser/base/content/test/social/social_postActivation.html";
 
       return postActivation && sbrowser.docShellIsActive;
     }, function() {
       // executeSoon to let the browser UI observers run first
       executeSoon(cb);
     },
     "waitForProviderLoad: provider was not loaded");
 }
--- a/browser/components/loop/content/css/contacts.css
+++ b/browser/components/loop/content/css/contacts.css
@@ -2,16 +2,69 @@
  * 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/. */
 
 html {
   font-size: 10px;
   font-family: sans-serif; /* XXX will be changed to a system font in bug 1191398 */
 }
 
+.contacts-container {
+  flex: 1;
+  display: flex;
+  flex-flow: column nowrap;
+  overflow: auto;
+}
+
+.contact-list-container {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  overflow: auto;
+}
+/* Don't show the Gravatar if we're showing a contacts list. */
+.contact-list ~ .contacts-gravatar-promo {
+  display: none;
+}
+
+.contact-list-wrapper {
+  flex: 1;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+/* Don't show the empty contacts image if we're showing gravatar promo. */
+.contacts-gravatar-promo ~ .contact-list-empty {
+  background-image: none;
+  padding-top: 0;
+  padding-bottom: 0;
+  margin-top: 2px;
+}
+
+.contact-list-empty-container {
+  flex: 1;
+  overflow-x: hidden;
+  overflow-y: auto;
+}
+
+.contact-list-empty,
+.contact-search-list-empty {
+  margin-top: 4rem;
+  padding-top: 11.5rem;
+  padding-bottom: 0;
+}
+
+.contact-search-list-empty {
+  background-image: url("../shared/img/empty_search.svg");
+}
+
+.contact-list-empty {
+  background-image: url("../shared/img/empty_contacts.svg");
+}
+
 .contact-import-spinner {
   display: none;
 }
 
 .contact-import-spinner.busy {
   display: inline-block;
   vertical-align: middle;
   -moz-margin-start: 10px;
@@ -30,18 +83,16 @@ html {
   border: 0;
   border-bottom: 1px solid #ddd;
   background-image: url("../shared/img/icons-14x14.svg#magnifier");
   background-position: 10px center;
   background-size: 14px;
   background-repeat: no-repeat;
   color: #999;
   font-size: 1.2rem;
-  flex: 2 1 auto;
-  align-self: stretch;
 }
 
 html[dir="rtl"] .contact-filter {
   background-position: right 10px center;
 }
 
 .contact-filter:focus + .clear-search,
 .contact-filter:focus {
@@ -60,31 +111,27 @@ html[dir="rtl"] .contact-filter {
   background-size: 14px;
   background-repeat: no-repeat;
   cursor: pointer;
   flex: 0 1 auto;
   align-self: stretch;
 }
 
 .contact-list {
-  overflow-x: hidden;
-  overflow-y: auto;
   /* Space for six contacts, not affected by filtering.  This is enough space
      to show the dropdown menu when there is only one contact. */
-  height: 306px;
   /* Contact list title goes away when searching, needed for spacing. */
   margin-top: 4px;
 }
 
 .contact-list-title {
-  padding: 0 1rem;
+  padding: 0.75rem 1rem;
   color: #666;
   font-weight: 500;
   font-size: .9em;
-  margin-top: 1rem;
 }
 
 .contact,
 .contact-separator {
   padding: .5rem 15px;
   font-size: 13px;
 }
 
@@ -205,33 +252,16 @@ html[dir="rtl"] .contact-filter {
   background-image: url("../shared/img/avatars.svg#green-avatar");
   background-color: #56B397;
 }
 
 .contact > .avatar > img {
   width: 100%;
 }
 
-.contact-list-empty,
-.contact-search-list-empty {
-  background-image: url("../shared/img/empty_contacts.svg");
-  background-repeat: no-repeat;
-  background-position: top center;
-  background-size: 128px;
-  margin-top: 4rem;
-  padding-top: 12rem;
-  padding-bottom: 5rem;
-  text-align: center;
-  color: #4a4a4a;
-}
-
-.contact-search-list-empty {
-  background-image: url("../shared/img/empty_search.svg");
-}
-
 .panel-text-medium,
 .panel-text-large {
   margin: 3px;
   color: #4a4a4a;
 }
 
 .panel-text-medium {
   font-size: 1.2rem;
@@ -335,31 +365,33 @@ html[dir="rtl"] .contact > .dropdown-men
 }
 
 .contact > .dropdown-menu-up {
   bottom: 25px;
   top: auto;
 }
 
 .contact-form {
-  padding: 14px; /* Override based on spacing in Mockup */
+  padding: 14px 15px 0 15px; /* Override based on spacing in Mockup */
+  flex: 1;
+  display: flex;
+  flex-direction: column;
 }
 /* This will effect the header displayed at the top of the contact details form
  */
 .contact-form header {
   text-align: center;
   font-size: 1.3rem;
   font-weight: 500;
   color: #4a4a4a;
 }
 
 .contact-form .form-content-container {
-  height: 24.1rem; /* This height is needed to keep the panel height at 400px and
-                      the bottom elements at the bottom of the panel
-                      Can likely go away if we switched this pane to flexbox model */
+  /* flex is needed to fill and place the buttons above the footer */
+  flex: 1;
   padding-top: 4px; /* Based on spacing in Mockup
                     replaced margin-top
                     See http://stackoverflow.com/questions/6204670/css-clean-solution-to-the-margin-collapse-issue-when-floating-an-element
                     */
 }
 
 .contacts-gravatar-promo {
   border: 1px solid #5cccee;
@@ -430,17 +462,19 @@ html[dir="rtl"] .contacts-gravatar-arrow
   transform: rotateZ(225deg);
 }
 
 .contacts-gravatar-buttons {
   padding: 0 .5rem;
 }
 
 .contact-controls {
-  padding: 0 16px;
+  padding-left: 15px;
+  padding-right: 15px;
+  border-top: 1px solid #D8D8D8;
 }
 
 .contact-controls > .button {
   padding: .5em;
   border: none;
   border-radius: 5px;
 }
 
--- a/browser/components/loop/content/css/panel.css
+++ b/browser/components/loop/content/css/panel.css
@@ -12,21 +12,33 @@ body {
 
 /* Panel styles */
 
 .panel {
   /* hide the extra margin space that the panel resizer now wants to show */
   overflow: hidden;
   font: menu;
   background-color: #fbfbfb;
+  height: 410px;
+  width: 330px;
+}
+
+/* Panel container flexbox */
+.panel-content {
+  height: 410px;
+  width: 330px;
+  display: flex;
+  flex-flow: column nowrap;
+  align-items: flex-start;
 }
 
 /* Notifications displayed over tabs */
 
 .panel .messages {
+  width: 100%;
   margin: 0;
 }
 
 .panel .messages .alert {
   margin: 0;
 }
 
 /* Sign-in request view */
@@ -62,20 +74,25 @@ body {
   width: 80%;
   padding: .5rem 1rem;
   border-radius: 3px;
 }
 
 /* Tabs and tab selection buttons */
 
 .tab-view-container {
+  width: 100%;
   background-image: url("../shared/img/beta-ribbon.svg#beta-ribbon");
   background-color: #fbfbfb;
   background-size: 36px 36px;
   background-repeat: no-repeat;
+  flex: 1;
+  display: flex;
+  flex-flow: column nowrap;
+  overflow: hidden;
 }
 
 .tab-view {
   position: relative;
   width: 100%;
   height: 4rem;
   line-height: 3.7rem;
   color: #4A4A4A;
@@ -174,31 +191,35 @@ html[dir="rtl"] .tab-view li:nth-child(2
 .tab-view li:first-child:nth-last-child(2) > span {
   display: none;
 }
 
 .tab-view li:first-child:nth-last-child(2):before {
   background-image: url("../shared/img/icons-14x14.svg#hello-hover");
 }
 
-.tab-view li:first-child:nth-last-child(2) ~ div {
+.tab-view li:first-child:nth-last-child(2) ~ li {
+  /* hide the tab-slider when there is only one tab shown */
   display: none;
 }
 
 .tab-view li:first-child:nth-last-child(3),
 .tab-view li:first-child:nth-last-child(3) ~ li {
   width: 50%;
 }
 
 .tab {
   display: none;
+  flex: 1;
+  overflow: auto;
 }
 
 .tab.selected {
-  display: block;
+  display: flex;
+  flex-flow: column nowrap;
 }
 
 /* Content area and input fields */
 
 .content-area {
   padding: .5rem 1rem;
 }
 
@@ -249,39 +270,35 @@ html[dir="rtl"] .tab-view li:nth-child(2
 
 .content-area input:focus {
   border: 0.1rem solid #5cccee;
 }
 
 /* Rooms and contacts shared CSS */
 
 .contact-list-empty,
-.room-list-empty {
-  background-image: url("../shared/img/empty_contacts.svg");
+.contact-search-list-empty,
+.no-conversations-message {
   background-repeat: no-repeat;
   background-position: top center;
-  padding-top: 19%;
-  padding-bottom: 3%;
   text-align: center;
   color: #4a4a4a;
   font-weight: lighter;
 }
 
-/* Don't show the empty contacts image if we're showing gravatar promo. */
-.contacts-gravatar-promo ~ .contact-list-empty {
-  background-image: none;
-  padding-top: 3%;
-}
+
 
-.contact-list-empty {
-  padding-top: 27%;
-}
-
-.room-list-empty {
-  margin: 5% 15px;
+.no-conversations-message {
+  /* example of vertical aligning a container in an element
+    see: http://zerosixthree.se/vertical-align-anything-with-just-3-lines-of-css/ */
+  position: relative;
+  top: 50%;
+  transform: translateY(-50%);
+  padding-top: 75px;
+  padding-bottom: 10px;
   background-image: url("../shared/img/empty_conversations.svg");
 }
 
 .panel-text-medium,
 .panel-text-large {
   margin: 3px 0;
 }
 
@@ -291,54 +308,57 @@ html[dir="rtl"] .tab-view li:nth-child(2
 
 .panel-text-large {
   font-size: 2.2rem;
 }
 
 .room-list-loading {
   margin: 5rem 15px;
   text-align: center;
+  /* makes sure that buttons are anchored above footer */
+  flex: 1;
 }
 
 .room-list-loading > img {
   width: 66px;
 }
 
 
 /* Rooms */
 .rooms {
-  min-height: 100px;
-  /* To hold the conversation dropdown menu. */
-  position: relative;
+  flex: 1;
+  display: flex;
+  flex-flow: column nowrap;
 }
 
 .rooms > h1 {
   font-weight: bold;
   color: #666;
   padding: .5rem 0;
   height: 3rem;
   line-height: 3rem;
   font-size: 1.1rem;
   margin: 0 15px;
 }
 
 .new-room-view {
   display: flex;
   flex-direction: column;
+  border-top: 1px solid #d8d8d8;
 }
 
 .new-room-view > .context-checkbox-checked {
   background-color: #dbf7ff;
 }
 
 .new-room-view > .context {
   border-top: 1px solid #ebebeb;
   flex: 1;
-  margin: .5rem 0;
-  padding: .5rem 15px 1rem;
+  border-radius: 3px 3px 0 0;
+  padding: 6px 15px 8px;
 }
 
 .new-room-view > .context > .context-enabled {
   margin-bottom: .5rem;
   display: block;
 }
 
 .new-room-view > .context > .context-enabled > input {
@@ -365,52 +385,62 @@ html[dir="rtl"] .tab-view li:nth-child(2
   font-size: 1.1rem;
 }
 
 .new-room-view > .btn {
   flex: 1;
   height: 3rem;
   display: block;
   font-size: 1.2rem;
-  margin: 0 15px 1rem 15px;
+  margin: 7px 15px 0 15px;
   padding: .5rem 1rem;
   border-radius: 4px;
 }
 
 /* Remove when bug 1142671 is backed out. */
 .new-room-view > .context.hide + .new-room-button {
   border-radius: 3px;
   margin-top: 0.5rem;
 }
 
 .room-list {
-  max-height: 335px; /* XXX better computation needed */
-  /* At least high enough to be able to contain the conversation menu. */
-  min-height: 100px;
-  overflow: auto;
+  /* xxx  not sure why flex needs the 3 value setting
+    but setting flex to just 1, the whole tab including the new room is scrollable.
+    seems to not like the 0% of the default setting - may be FF bug */
+  flex: 1 1 0;
+  overflow-y: auto;
+  overflow-x: hidden;
+  display: flex;
+  flex-flow: column nowrap;
 }
 
-.room-list:empty {
+.room-list-empty {
   border-bottom-width: 0;
+  flex: 1;
+  /* the child no-conversations-message is vertical aligned inside this container
+    see: http://zerosixthree.se/vertical-align-anything-with-just-3-lines-of-css/
+    stops blurring from decimal pixels being rendered - pixel rounding */
+  transform-style: preserve-3d;
 }
 
 .room-list > .room-entry {
   padding: .2rem 15px;
   /* Always show the default pointer, even over the text part of the entry. */
   cursor: default;
 }
 
 .room-list > .room-entry > h2 {
   display: inline-block;
   font-size: 1.3rem;
   line-height: 2.4rem;
   color: #000;
   /* See .room-entry-context-item for the margin/size reductions.
-   * An extra 40px to make space for the call button and chevron. */
+    * An extra 40px to make space for the call button and chevron. */
   width: calc(100% - 1rem - 56px);
+
 }
 
 .room-list > .room-entry.room-active > h2 {
   font-weight: bold;
   color: #000;
 }
 
 .room-list > .room-entry:hover {
@@ -432,17 +462,17 @@ html[dir="rtl"] .tab-view li:nth-child(2
 .room-list > .room-entry > p > a:hover {
   opacity: 1;
   text-decoration: underline;
 }
 
 @keyframes drop-and-fade-in {
   0%   {opacity: 0; top: -15px;}
   25%  {opacity: 0; top: -15px;}
-  100% {opacity: 1; top: 0px;}
+  100% {opacity: 1; top: 0;}
 }
 
 .room-list > .room-entry > h2 > button {
   display: inline-block;
   position: relative;
   width: 24px;
   height: 24px;
   border: none;
@@ -470,17 +500,17 @@ html[dir="rtl"] .tab-view li:nth-child(2
   0%, 100% { transform: scale(1.0); }
   50%      { transform: scale(1.1); }
 }
 
 .room-list > .room-entry > h2 > .copy-link.checked {
   background: transparent url(../shared/img/icons-16x16.svg#checkmark);
   animation: pulse .150s;
   animation-timing-function: ease-in-out;
-  top: 0px;
+  top: 0;
 }
 
 /* keep the various room-entry row pieces aligned with each other */
 .room-list > .room-entry > h2 > button,
 .room-list > .room-entry > h2 > span {
   vertical-align: middle;
 }
 
@@ -546,21 +576,21 @@ html[dir="rtl"] .room-entry-call-btn {
   height: 24px;
   background-size: 12px;
   background-repeat: no-repeat;
   vertical-align: middle;
 }
 
 html[dir="rtl"] .room-entry-context-actions > .dropdown-menu {
   right: auto;
-  left: 21px;
+  left: 45px;
 }
 
 .room-entry-context-actions > .dropdown-menu {
-  right: 21px;
+  right: 45px;
   bottom: auto;
   left: auto;
 }
 
 /* Keep ".room-list > .room-entry > h2" in sync with these. */
 .room-entry-context-item {
   display: inline-block;
   vertical-align: middle;
@@ -975,71 +1005,67 @@ html[dir="rtl"] .settings-menu .dropdown
 .footer {
   display: flex;
   flex-direction: row;
   flex-wrap: nowrap;
   justify-content: space-between;
   align-content: stretch;
   align-items: center;
   font-size: 1rem;
-  background-color: #fbfbfb;
   color: #666666;
-  padding: .5rem 15px;
+  padding: 10px 15px 6px 15px;
+  width: 100%;
+  height: 42px;
 }
 
 .footer .signin-details {
   align-items: center;
   display: flex;
 }
 
 .footer .user-identity {
   color: #000;
   font-weight: bold;
   margin: 0;
 }
 
 /* First time use */
 
 .fte-get-started-content {
+  /* Manual vertical centering */
   flex: 1;
-  padding: 4.5rem 0 3.4rem;
+  padding: 7.5rem 0 0;
   display: flex;
   flex-direction: column;
 }
 
 .fte-title {
-  flex: 1;
   margin: 0 44px;
 }
 
 .fte-title > img {
   width: 100%;
 }
 
 .fte-subheader {
   text-align: center;
   font-size: 1.8rem;
   margin-bottom: 2rem;
   color: #4a4a4a;
 }
 
 .fte-get-started-content + .powered-by-wrapper {
-  flex: 0;
+  width: 100%;
 }
 
 .fte-get-started-container {
-  height: 100%;
-  width: 100%;
-  min-height: 270px;
+  height: 410px;
+  width: 330px;
   display: flex;
-  flex-direction: column;
-  flex-wrap: nowrap;
-  justify-content: space-between;
-  align-content: flex-start;
-  align-items: flex-start;
+  flex-flow: column nowrap;
   background: #fbfbfb;
 }
 
 .fte-get-started-button {
   border: none;
   color: #fff;
   background-color: #00a9dc;
   border-color: none;
--- a/browser/components/loop/content/js/contacts.js
+++ b/browser/components/loop/content/js/contacts.js
@@ -650,56 +650,64 @@ loop.contacts = (function(_, mozL10n) {
         );
       };
 
       // If no contacts to show and filter is set, then none match the search.
       if (!shownContacts.available && !shownContacts.blocked &&
           this.state.filter) {
         return (
           React.createElement("div", {className: "contact-search-list-empty"}, 
+            React.createElement("p", {className: "panel-text-large"}, 
+              mozL10n.get("no_search_results_message_heading")
+            ), 
             React.createElement("p", {className: "panel-text-medium"}, 
               mozL10n.get("contacts_no_search_results")
             )
           )
         );
       }
 
       // If no contacts to show and filter is not set, we don't have contacts.
       if (!shownContacts.available && !shownContacts.blocked &&
           !this.state.filter) {
         return (
-          React.createElement("div", {className: "contact-list-empty"}, 
-            React.createElement("p", {className: "panel-text-medium"}, 
-              mozL10n.get("no_contacts_message_heading2")
-            ), 
-            React.createElement("p", {className: "panel-text-medium"}, 
-              mozL10n.get("no_contacts_import_or_add2")
+            React.createElement("div", {className: "contact-list-empty-container"}, 
+              this._renderGravatarPromoMessage(), 
+              React.createElement("div", {className: "contact-list-empty"}, 
+                React.createElement("p", {className: "panel-text-large"}, 
+                  mozL10n.get("no_contacts_message_heading2")
+                ), 
+                React.createElement("p", {className: "panel-text-medium"}, 
+                  mozL10n.get("no_contacts_import_or_add2")
+                )
+              )
             )
-          )
         );
       }
 
       return (
-        React.createElement("div", null, 
-          !this.state.filter ? React.createElement("div", {className: "contact-list-title"}, 
-                                  mozL10n.get("contact_list_title")
-                                ) : null, 
-          this._renderGravatarPromoMessage(), 
-          React.createElement("ul", {className: "contact-list"}, 
-            shownContacts.available ?
-              shownContacts.available.sort(this.sortContacts).map(viewForItem) :
-              null, 
-            shownContacts.blocked && shownContacts.blocked.length > 0 ?
-              React.createElement("div", {className: "contact-separator"}, mozL10n.get("contacts_blocked_contacts")) :
-              null, 
-            shownContacts.blocked ?
-              shownContacts.blocked.sort(this.sortContacts).map(viewForItem) :
-              null
+          React.createElement("div", {className: "contact-list-container"}, 
+            !this.state.filter ? React.createElement("div", {className: "contact-list-title"}, 
+              mozL10n.get("contact_list_title")
+            ) : null, 
+            React.createElement("div", {className: "contact-list-wrapper"}, 
+              this._renderGravatarPromoMessage(), 
+              React.createElement("ul", {className: "contact-list"}, 
+                shownContacts.available ?
+                    shownContacts.available.sort(this.sortContacts).map(viewForItem) :
+                    null, 
+                shownContacts.blocked && shownContacts.blocked.length > 0 ?
+                    React.createElement("div", {className: "contact-separator"}, mozL10n.get("contacts_blocked_contacts")) :
+                    null, 
+                shownContacts.blocked ?
+                    shownContacts.blocked.sort(this.sortContacts).map(viewForItem) :
+                    null
+              )
+            )
           )
-        )
       );
     },
 
     _renderAddContactButtons: function() {
       let cx = React.addons.classSet;
 
       if (this.state.filter) {
         return null;
@@ -730,17 +738,17 @@ loop.contacts = (function(_, mozL10n) {
 
       return (
         React.createElement(GravatarPromo, {handleUse: this.handleUseGravatar})
       );
     },
 
     render: function() {
       return (
-        React.createElement("div", null, 
+        React.createElement("div", {className: "contacts-container"}, 
           this._renderContactsFilter(), 
           this._renderContactsList(), 
           this._renderAddContactButtons()
         )
       );
     }
   });
 
--- a/browser/components/loop/content/js/contacts.jsx
+++ b/browser/components/loop/content/js/contacts.jsx
@@ -650,56 +650,64 @@ loop.contacts = (function(_, mozL10n) {
         );
       };
 
       // If no contacts to show and filter is set, then none match the search.
       if (!shownContacts.available && !shownContacts.blocked &&
           this.state.filter) {
         return (
           <div className="contact-search-list-empty">
+            <p className="panel-text-large">
+              {mozL10n.get("no_search_results_message_heading")}
+            </p>
             <p className="panel-text-medium">
               {mozL10n.get("contacts_no_search_results")}
             </p>
           </div>
         );
       }
 
       // If no contacts to show and filter is not set, we don't have contacts.
       if (!shownContacts.available && !shownContacts.blocked &&
           !this.state.filter) {
         return (
-          <div className="contact-list-empty">
-            <p className="panel-text-medium">
-              {mozL10n.get("no_contacts_message_heading2")}
-            </p>
-            <p className="panel-text-medium">
-              {mozL10n.get("no_contacts_import_or_add2")}
-            </p>
-          </div>
+            <div className="contact-list-empty-container">
+              {this._renderGravatarPromoMessage()}
+              <div className="contact-list-empty">
+                <p className="panel-text-large">
+                  {mozL10n.get("no_contacts_message_heading2")}
+                </p>
+                <p className="panel-text-medium">
+                  {mozL10n.get("no_contacts_import_or_add2")}
+                </p>
+              </div>
+            </div>
         );
       }
 
       return (
-        <div>
-          {!this.state.filter ? <div className="contact-list-title">
-                                  {mozL10n.get("contact_list_title")}
-                                </div> : null}
-          {this._renderGravatarPromoMessage()}
-          <ul className="contact-list">
-            {shownContacts.available ?
-              shownContacts.available.sort(this.sortContacts).map(viewForItem) :
-              null}
-            {shownContacts.blocked && shownContacts.blocked.length > 0 ?
-              <div className="contact-separator">{mozL10n.get("contacts_blocked_contacts")}</div> :
-              null}
-            {shownContacts.blocked ?
-              shownContacts.blocked.sort(this.sortContacts).map(viewForItem) :
-              null}
-          </ul>
-        </div>
+          <div className="contact-list-container">
+            {!this.state.filter ? <div className="contact-list-title">
+              {mozL10n.get("contact_list_title")}
+            </div> : null}
+            <div className="contact-list-wrapper">
+              {this._renderGravatarPromoMessage()}
+              <ul className="contact-list">
+                {shownContacts.available ?
+                    shownContacts.available.sort(this.sortContacts).map(viewForItem) :
+                    null}
+                {shownContacts.blocked && shownContacts.blocked.length > 0 ?
+                    <div className="contact-separator">{mozL10n.get("contacts_blocked_contacts")}</div> :
+                    null}
+                {shownContacts.blocked ?
+                    shownContacts.blocked.sort(this.sortContacts).map(viewForItem) :
+                    null}
+              </ul>
+            </div>
+          </div>
       );
     },
 
     _renderAddContactButtons: function() {
       let cx = React.addons.classSet;
 
       if (this.state.filter) {
         return null;
@@ -730,17 +738,17 @@ loop.contacts = (function(_, mozL10n) {
 
       return (
         <GravatarPromo handleUse={this.handleUseGravatar} />
       );
     },
 
     render: function() {
       return (
-        <div>
+        <div className="contacts-container">
           {this._renderContactsFilter()}
           {this._renderContactsList()}
           {this._renderAddContactButtons()}
         </div>
       );
     }
   });
 
--- a/browser/components/loop/content/js/conversationViews.js
+++ b/browser/components/loop/content/js/conversationViews.js
@@ -474,18 +474,16 @@ loop.conversationViews = (function(mozL1
       if (!this.state.emailLinkError) {
         return;
       }
       return React.createElement("p", {className: "error"}, mozL10n.get("unable_retrieve_url"));
     },
 
     _getTitleMessage: function() {
       switch (this.getStoreState().callStateReason) {
-        case WEBSOCKET_REASONS.REJECT:
-        case WEBSOCKET_REASONS.BUSY:
         case FAILURE_DETAILS.USER_UNAVAILABLE:
           var contactDisplayName = _getContactDisplayName(this.props.contact);
           if (contactDisplayName.length) {
             return mozL10n.get(
               "contact_unavailable_title",
               {"contactName": contactDisplayName});
           }
 
--- a/browser/components/loop/content/js/conversationViews.jsx
+++ b/browser/components/loop/content/js/conversationViews.jsx
@@ -474,18 +474,16 @@ loop.conversationViews = (function(mozL1
       if (!this.state.emailLinkError) {
         return;
       }
       return <p className="error">{mozL10n.get("unable_retrieve_url")}</p>;
     },
 
     _getTitleMessage: function() {
       switch (this.getStoreState().callStateReason) {
-        case WEBSOCKET_REASONS.REJECT:
-        case WEBSOCKET_REASONS.BUSY:
         case FAILURE_DETAILS.USER_UNAVAILABLE:
           var contactDisplayName = _getContactDisplayName(this.props.contact);
           if (contactDisplayName.length) {
             return mozL10n.get(
               "contact_unavailable_title",
               {"contactName": contactDisplayName});
           }
 
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -95,17 +95,17 @@ loop.panel = (function(_, mozL10n) {
             tab.props.children
           )
         );
       }, this);
       return (
         React.createElement("div", {className: "tab-view-container"}, 
           React.createElement("ul", {className: "tab-view"}, 
             tabButtons, 
-            React.createElement("div", {className: "slide-bar"})
+            React.createElement("li", {className: "slide-bar"})
           ), 
           tabs
         )
       );
     }
   });
 
   var Tab = React.createClass({displayName: "Tab",
@@ -301,17 +301,17 @@ loop.panel = (function(_, mozL10n) {
         "privacy_notice": React.renderToStaticMarkup(
           React.createElement("a", {href: privacy_notice_url, target: "_blank"}, 
             mozL10n.get("legal_text_privacy")
           )
         )
       });
 
       return (
-        React.createElement("div", {id: "powered-by-wrapper"}, 
+        React.createElement("div", {className: "powered-by-wrapper", id: "powered-by-wrapper"}, 
           React.createElement("p", {className: "powered-by", id: "powered-by"}, 
             mozL10n.get("powered_by_beforeLogo"), 
             React.createElement("span", {className: locale, id: "powered-by-logo"}), 
             mozL10n.get("powered_by_afterLogo")
           ), 
           React.createElement("p", {className: "terms-service", 
              dangerouslySetInnerHTML: {__html: tosHTML}, 
              onClick: this.handleLinkClick})
@@ -821,23 +821,25 @@ loop.panel = (function(_, mozL10n) {
           ), 
           this._renderNewRoomButton()
         )
       );
     },
 
     _renderNoRoomsView: function() {
       return (
-        React.createElement("div", {className: "room-list"}, 
+        React.createElement("div", {className: "rooms"}, 
           React.createElement("div", {className: "room-list-empty"}, 
-            React.createElement("p", {className: "panel-text-large"}, 
-              mozL10n.get("no_conversations_message_heading")
-            ), 
-            React.createElement("p", {className: "panel-text-medium"}, 
-              mozL10n.get("no_conversations_start_message")
+            React.createElement("div", {className: "no-conversations-message"}, 
+              React.createElement("p", {className: "panel-text-large"}, 
+                mozL10n.get("no_conversations_message_heading")
+              ), 
+              React.createElement("p", {className: "panel-text-medium"}, 
+                mozL10n.get("no_conversations_start_message")
+              )
             )
           ), 
           this._renderNewRoomButton()
         )
       );
     },
 
     _renderNewRoomButton: function() {
@@ -1123,17 +1125,17 @@ loop.panel = (function(_, mozL10n) {
 
       // Determine which buttons to NOT show.
       var hideButtons = [];
       if (!this.state.userProfile && !this.props.showTabButtons) {
         hideButtons.push("contacts");
       }
 
       return (
-        React.createElement("div", null, 
+        React.createElement("div", {className: "panel-content"}, 
           React.createElement(NotificationListView, {
             clearOnDocumentHidden: true, 
             notifications: this.props.notifications}), 
           React.createElement(TabView, {
             buttonsHidden: hideButtons, 
             mozLoop: this.props.mozLoop, 
             ref: "tabView", 
             selectedTab: this.props.selectedTab}, 
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -95,17 +95,17 @@ loop.panel = (function(_, mozL10n) {
             {tab.props.children}
           </div>
         );
       }, this);
       return (
         <div className="tab-view-container">
           <ul className="tab-view">
             {tabButtons}
-            <div className="slide-bar" />
+            <li className="slide-bar" />
           </ul>
           {tabs}
         </div>
       );
     }
   });
 
   var Tab = React.createClass({
@@ -301,17 +301,17 @@ loop.panel = (function(_, mozL10n) {
         "privacy_notice": React.renderToStaticMarkup(
           <a href={privacy_notice_url} target="_blank">
             {mozL10n.get("legal_text_privacy")}
           </a>
         )
       });
 
       return (
-        <div id="powered-by-wrapper">
+        <div className="powered-by-wrapper" id="powered-by-wrapper">
           <p className="powered-by" id="powered-by">
             {mozL10n.get("powered_by_beforeLogo")}
             <span className={locale} id="powered-by-logo"/>
             {mozL10n.get("powered_by_afterLogo")}
           </p>
           <p className="terms-service"
              dangerouslySetInnerHTML={{__html: tosHTML}}
              onClick={this.handleLinkClick}></p>
@@ -821,24 +821,26 @@ loop.panel = (function(_, mozL10n) {
           </div>
           {this._renderNewRoomButton()}
         </div>
       );
     },
 
     _renderNoRoomsView: function() {
       return (
-        <div className="room-list">
+        <div className="rooms">
           <div className="room-list-empty">
-            <p className="panel-text-large">
-              {mozL10n.get("no_conversations_message_heading")}
-            </p>
-            <p className="panel-text-medium">
-              {mozL10n.get("no_conversations_start_message")}
-            </p>
+            <div className="no-conversations-message">
+              <p className="panel-text-large">
+                {mozL10n.get("no_conversations_message_heading")}
+              </p>
+              <p className="panel-text-medium">
+                {mozL10n.get("no_conversations_start_message")}
+              </p>
+            </div>
           </div>
           {this._renderNewRoomButton()}
         </div>
       );
     },
 
     _renderNewRoomButton: function() {
       return (
@@ -1123,17 +1125,17 @@ loop.panel = (function(_, mozL10n) {
 
       // Determine which buttons to NOT show.
       var hideButtons = [];
       if (!this.state.userProfile && !this.props.showTabButtons) {
         hideButtons.push("contacts");
       }
 
       return (
-        <div>
+        <div className="panel-content">
           <NotificationListView
             clearOnDocumentHidden={true}
             notifications={this.props.notifications} />
           <TabView
             buttonsHidden={hideButtons}
             mozLoop={this.props.mozLoop}
             ref="tabView"
             selectedTab={this.props.selectedTab}>
--- a/browser/components/loop/content/js/roomStore.js
+++ b/browser/components/loop/content/js/roomStore.js
@@ -421,17 +421,16 @@ loop.store = loop.store || {};
     deleteRoomError: function(actionData) {
       this.setStoreState({error: actionData.error});
     },
 
     /**
      * Gather the list of all available rooms from the MozLoop API.
      */
     getAllRooms: function() {
-      this.setStoreState({pendingInitialRetrieval: true});
       this._mozLoop.rooms.getAll(null, function(err, rawRoomList) {
         var action;
 
         this.setStoreState({pendingInitialRetrieval: false});
 
         if (err) {
           action = new sharedActions.GetAllRoomsError({error: err});
         } else {
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -219,23 +219,23 @@ html[dir="rtl"] .conversation-toolbar-bt
   background-image: url("../img/svg/sharing.svg");
 }
 
 .btn-screen-share:hover,
 .btn-screen-share:active {
   background-image: url("../img/svg/sharing-hover.svg");
 }
 
-.btn-settings.muted,
 .btn-screen-share.active {
-  opacity: 1;
+  background-image: url("../img/svg/sharing-active.svg");
 }
 
 .btn-screen-share.disabled {
-  background-image: url("../img/svg/sharing-mute.svg");
+  /* The screen share button is in its pending state when its disabled. */
+  background-image: url("../img/svg/sharing-pending.svg");
 }
 
 .fx-embedded .remote_wrapper {
   position: absolute;
   top: 0px;
   right: 0px;
   bottom: 0px;
   left: 0px;
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/content/shared/img/svg/sharing-active.svg
@@ -0,0 +1,1 @@
+<svg width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg"><g fill="none"><rect opacity=".95" fill="#fff" x="2" y="2" width="24" height="24" rx="40"/><path d="M2 14c0 6.629 5.373 12 12 12 6.629 0 12-5.373 12-12 0-6.629-5.373-12-12-12-6.629 0-12 5.373-12 12zm-2 0c0-7.732 6.267-14 14-14 7.732 0 14 6.267 14 14 0 7.732-6.267 14-14 14-7.732 0-14-6.267-14-14z" fill-opacity=".2" fill="#000"/><rect fill="#00A9DC" x="11" y="12" width="9" height="7" rx="1"/><path d="M17 11v-.997c0-.565-.447-1.003-.998-1.003h-7.005c-.551 0-.998.449-.998 1.003v4.994c0 .565.447 1.003.998 1.003h1.002v-3.997c0-.554.446-1.003.998-1.003h6.002z" fill="#00A9DC"/></g></svg>
deleted file mode 100644
--- a/browser/components/loop/content/shared/img/svg/sharing-mute.svg
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <defs></defs>
-    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
-        <rect id="Rectangle-1264-Copy" opacity="0.95" fill="#FFFFFF" x="2" y="2" width="24" height="24" rx="40"></rect>
-        <path d="M2,14 L2,14 C2,20.6288742 7.372583,26 14,26 L14,26 C20.6288742,26 26,20.627417 26,14 L26,14 C26,7.37112582 20.627417,2 14,2 L14,2 C7.37112582,2 2,7.372583 2,14 L2,14 Z M0,14 L0,14 C0,6.26754774 6.26702203,0 14,0 C21.7324523,0 28,6.26702203 28,14 C28,21.7324523 21.732978,28 14,28 C6.26754774,28 0,21.732978 0,14 L0,14 Z" id="Shape" fill-opacity="0.2" fill="#000000"></path>
-        <path d="M16.4994509,12 L11.9975446,12 C11.4463114,12 11,12.4490268 11,13.0029293 L11,17.491272 L16.4994509,12 Z M11.1367161,19.3689248 C11.1138915,19.40931 11.099976,19.4517095 11.099976,19.5009121 C11.099976,19.5708113 11.1280605,19.6269803 11.1698752,19.6831493 L11.8139462,20.3272204 C11.8701152,20.3696591 11.9262842,20.3971195 11.9961834,20.3971195 C12.0667067,20.3971195 12.1222516,20.3696591 12.1784205,20.3272204 L13.5056409,19 L19.0024554,19 C19.5536886,19 20,18.5509732 20,17.9970707 L20,13.0029293 C20,12.8551278 19.9694422,12.716027 19.9144529,12.591188 L21.8301248,10.6755161 C21.8719395,10.6337014 21.900024,10.5638022 21.900024,10.4932789 C21.900024,10.437734 21.8719395,10.3672108 21.8301248,10.3253961 L21.1860538,9.68132501 C21.1298848,9.62515602 21.0737158,9.59707153 21.0038166,9.59707153 C20.9339174,9.59707153 20.8777484,9.62515602 20.8215795,9.68132501 L19.8517551,10.6497071 C19.8450712,10.6591494 19.8378312,10.6678097 19.8301248,10.6755161 L11.1367161,19.3689248 Z M17.001,11 L17.001,10.003 C17.001,9.438 16.554,9 16.003,9 L8.998,9 C8.447,9 8,9.449 8,10.003 L8,14.997 C8,15.562 8.447,16 8.998,16 L10,16 L10,12.003 C10,11.449 10.446,11 10.998,11 L17,11 L17.001,11 Z" id="Shape-Copy-3" fill="#4A4A4A"></path>
-    </g>
-</svg>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/components/loop/content/shared/img/svg/sharing-pending.svg
@@ -0,0 +1,1 @@
+<svg width="28" height="28" viewBox="0 0 28 28" xmlns="http://www.w3.org/2000/svg"><g fill="none"><rect opacity=".95" fill="#999999" x="2" y="2" width="24" height="24" rx="40"/><path d="M2 14c0 6.629 5.373 12 12 12 6.629 0 12-5.373 12-12 0-6.629-5.373-12-12-12-6.629 0-12 5.373-12 12zm-2 0c0-7.732 6.267-14 14-14 7.732 0 14 6.267 14 14 0 7.732-6.267 14-14 14-7.732 0-14-6.267-14-14z" fill-opacity=".2" fill="#000"/><rect fill="#4A4A4A" x="11" y="12" width="9" height="7" rx="1"/><path d="M17 11v-.997c0-.565-.447-1.003-.998-1.003h-7.005c-.551 0-.998.449-.998 1.003v4.994c0 .565.447 1.003.998 1.003h1.002v-3.997c0-.554.446-1.003.998-1.003h6.002z" fill="#4A4A4A"/></g></svg>
--- a/browser/components/loop/content/shared/js/conversationStore.js
+++ b/browser/components/loop/content/shared/js/conversationStore.js
@@ -7,16 +7,17 @@ loop.store = loop.store || {};
 
 (function() {
   "use strict";
 
   var sharedActions = loop.shared.actions;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
+  var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
 
   /**
    * Websocket states taken from:
    * https://docs.services.mozilla.com/loop/apis.html#call-progress-state-change-progress
    */
   var WS_STATES = loop.store.WS_STATES = {
     // The call is starting, and the remote party is not yet being alerted.
     INIT: "init",
@@ -611,18 +612,29 @@ loop.store = loop.store || {};
      * @param {Object} progressData  The progress data received from the websocket.
      * @param {String} previousState The previous state the websocket was in.
      */
     _handleWebSocketStateTerminated: function(progressData, previousState) {
       if (this.getStoreState("outgoing") ||
           (previousState !== WS_STATES.INIT &&
            previousState !== WS_STATES.ALERTING)) {
         // For outgoing calls we can treat everything as connection failure.
+
+        // XXX We currently fallback to the websocket reason, but really these should
+        // be fully migrated to use FAILURE_DETAILS, so as not to expose websocket
+        // states outside of this store. Bug 1124384 should help to fix this.
+        var reason = progressData.reason;
+
+        if (reason === WEBSOCKET_REASONS.REJECT ||
+            reason === WEBSOCKET_REASONS.BUSY) {
+          reason = FAILURE_DETAILS.USER_UNAVAILABLE;
+        }
+
         this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
-          reason: progressData.reason
+          reason: reason
         }));
         return;
       }
 
       this.dispatcher.dispatch(new sharedActions.CancelCall());
     },
 
     /**
--- a/browser/components/loop/jar.mn
+++ b/browser/components/loop/jar.mn
@@ -65,18 +65,19 @@ browser.jar:
   content/browser/loop/shared/img/svg/audio-mute-hover.svg      (content/shared/img/svg/audio-mute-hover.svg)
   content/browser/loop/shared/img/svg/video.svg                 (content/shared/img/svg/video.svg)
   content/browser/loop/shared/img/svg/video-hover.svg           (content/shared/img/svg/video-hover.svg)
   content/browser/loop/shared/img/svg/video-mute.svg            (content/shared/img/svg/video-mute.svg)
   content/browser/loop/shared/img/svg/video-mute-hover.svg      (content/shared/img/svg/video-mute-hover.svg)
   content/browser/loop/shared/img/svg/settings.svg              (content/shared/img/svg/settings.svg)
   content/browser/loop/shared/img/svg/settings-hover.svg        (content/shared/img/svg/settings-hover.svg)
   content/browser/loop/shared/img/svg/sharing.svg               (content/shared/img/svg/sharing.svg)
+  content/browser/loop/shared/img/svg/sharing-active.svg        (content/shared/img/svg/sharing-active.svg)
+  content/browser/loop/shared/img/svg/sharing-pending.svg       (content/shared/img/svg/sharing-pending.svg)
   content/browser/loop/shared/img/svg/sharing-hover.svg         (content/shared/img/svg/sharing-hover.svg)
-  content/browser/loop/shared/img/svg/sharing-mute.svg          (content/shared/img/svg/sharing-mute.svg)
   content/browser/loop/shared/img/svg/media-group.svg           (content/shared/img/svg/media-group.svg)
   content/browser/loop/shared/img/svg/media-group-left-hover.svg (content/shared/img/svg/media-group-left-hover.svg)
   content/browser/loop/shared/img/svg/media-group-right-hover.svg (content/shared/img/svg/media-group-right-hover.svg)
   content/browser/loop/shared/img/audio-call-avatar.svg         (content/shared/img/audio-call-avatar.svg)
   content/browser/loop/shared/img/beta-ribbon.svg               (content/shared/img/beta-ribbon.svg)
   content/browser/loop/shared/img/check.svg                     (content/shared/img/check.svg)
   content/browser/loop/shared/img/icons-10x10.svg               (content/shared/img/icons-10x10.svg)
   content/browser/loop/shared/img/icons-14x14.svg               (content/shared/img/icons-14x14.svg)
--- a/browser/components/loop/test/desktop-local/conversationViews_test.js
+++ b/browser/components/loop/test/desktop-local/conversationViews_test.js
@@ -401,40 +401,16 @@ describe("loop.conversationViews", funct
       function () {
         conversationStore.setStoreState({callStateReason: WEBSOCKET_REASONS.MEDIA_FAIL});
 
         view = mountTestComponent({contact: fakeContact});
 
         sinon.assert.calledWith(document.mozL10n.get, "generic_failure_title");
       });
 
-    it("should show 'contact unavailable' when the reason is WEBSOCKET_REASONS.REJECT",
-      function () {
-        conversationStore.setStoreState({callStateReason: WEBSOCKET_REASONS.REJECT});
-
-        view = mountTestComponent({contact: fakeContact});
-
-        sinon.assert.calledWithExactly(document.mozL10n.get,
-          "contact_unavailable_title",
-          {contactName: loop.conversationViews
-                            ._getContactDisplayName(fakeContact)});
-      });
-
-    it("should show 'contact unavailable' when the reason is WEBSOCKET_REASONS.BUSY",
-      function () {
-        conversationStore.setStoreState({callStateReason: WEBSOCKET_REASONS.BUSY});
-
-        view = mountTestComponent({contact: fakeContact});
-
-        sinon.assert.calledWithExactly(document.mozL10n.get,
-          "contact_unavailable_title",
-          {contactName: loop.conversationViews
-                            ._getContactDisplayName(fakeContact)});
-      });
-
     it("should show 'something went wrong' when the reason is 'setup'",
       function () {
         conversationStore.setStoreState({callStateReason: "setup"});
 
         view = mountTestComponent({contact: fakeContact});
 
         sinon.assert.calledWithExactly(document.mozL10n.get,
           "generic_failure_title");
@@ -457,18 +433,20 @@ describe("loop.conversationViews", funct
         conversationStore.setStoreState({callStateReason: FAILURE_DETAILS.UNABLE_TO_PUBLISH_MEDIA});
 
         view = mountTestComponent({contact: fakeContact});
 
         sinon.assert.calledWithExactly(document.mozL10n.get, "no_media_failure_message");
       });
 
     it("should display a generic contact unavailable msg when the reason is" +
-       " WEBSOCKET_REASONS.BUSY and no display name is available", function() {
-        conversationStore.setStoreState({callStateReason: WEBSOCKET_REASONS.BUSY});
+       " FAILURE_DETAILS.USER_UNAVAILABLE and no display name is available", function() {
+        conversationStore.setStoreState({
+          callStateReason: FAILURE_DETAILS.USER_UNAVAILABLE
+        });
         var phoneOnlyContact = {
           tel: [{"pref": true, type: "work", value: ""}]
         };
 
         view = mountTestComponent({contact: phoneOnlyContact});
 
         sinon.assert.calledWith(document.mozL10n.get,
           "generic_contact_unavailable_title");
--- a/browser/components/loop/test/mochitest/browser_toolbarbutton.js
+++ b/browser/components/loop/test/mochitest/browser_toolbarbutton.js
@@ -33,31 +33,31 @@ add_task(function* test_LoopUI_getters()
   Assert.ok(LoopUI.panel, "LoopUI panel element should be set");
   Assert.strictEqual(LoopUI.browser, null, "Browser element should not be there yet");
   Assert.strictEqual(LoopUI.selectedTab, null, "No tab should be selected yet");
 
   // Load and show the Loop panel for the very first time this session.
   yield loadLoopPanel();
   Assert.ok(LoopUI.browser, "Browser element should be there");
   Assert.strictEqual(LoopUI.selectedTab, "rooms", "Initially the rooms tab should be selected");
-  let panelTabs = LoopUI.browser.contentDocument.querySelectorAll(".tab-view > li");
+  let panelTabs = LoopUI.browser.contentDocument.querySelectorAll(".tab-view > li:not(.slide-bar)");
   Assert.strictEqual(panelTabs.length, 1, "Only one tab, 'rooms', should be visible");
 
   // Hide the panel.
   yield LoopUI.togglePanel();
   Assert.strictEqual(LoopUI.selectedTab, "rooms", "Rooms tab should still be selected");
 
   // Make sure the contacts tab shows up by simulating a login.
   MozLoopServiceInternal.fxAOAuthTokenData = fxASampleToken;
   MozLoopServiceInternal.fxAOAuthProfile = fxASampleProfile;
   yield MozLoopServiceInternal.notifyStatusChanged("login");
 
   yield LoopUI.togglePanel();
   Assert.strictEqual(LoopUI.selectedTab, "rooms", "Rooms tab should still be selected");
-  panelTabs = LoopUI.browser.contentDocument.querySelectorAll(".tab-view > li");
+  panelTabs = LoopUI.browser.contentDocument.querySelectorAll(".tab-view > li:not(.slide-bar)");
   Assert.strictEqual(panelTabs.length, 2, "Two tabs should be visible");
   yield LoopUI.togglePanel();
 
   // Programmatically select the contacts tab.
   yield LoopUI.togglePanel(null, "contacts");
   Assert.strictEqual(LoopUI.selectedTab, "contacts", "Contacts tab should be selected now");
 
   // Switch back to the rooms tab.
--- a/browser/components/loop/test/shared/conversationStore_test.js
+++ b/browser/components/loop/test/shared/conversationStore_test.js
@@ -383,40 +383,38 @@ describe("loop.store.ConversationStore",
         });
 
         it("should dispatch a connection progress action on success", function(done) {
           resolveConnectPromise(WS_STATES.INIT);
 
           connectPromise.then(function() {
             checkFailures(done, function() {
               sinon.assert.calledOnce(dispatcher.dispatch);
-              // Can't use instanceof here, as that matches any action
-              sinon.assert.calledWithMatch(dispatcher.dispatch,
-                sinon.match.hasOwn("name", "connectionProgress"));
-              sinon.assert.calledWithMatch(dispatcher.dispatch,
-                sinon.match.hasOwn("wsState", WS_STATES.INIT));
+              sinon.assert.calledWithExactly(dispatcher.dispatch,
+                new sharedActions.ConnectionProgress({
+                  wsState: WS_STATES.INIT
+                }));
             });
           }, function() {
             done(new Error("Promise should have been resolve, not rejected"));
           });
         });
 
         it("should dispatch a connection failure action on failure", function(done) {
           rejectConnectPromise();
 
           connectPromise.then(function() {
             done(new Error("Promise should have been rejected, not resolved"));
           }, function() {
             checkFailures(done, function() {
               sinon.assert.calledOnce(dispatcher.dispatch);
-              // Can't use instanceof here, as that matches any action
-              sinon.assert.calledWithMatch(dispatcher.dispatch,
-                sinon.match.hasOwn("name", "connectionFailure"));
-              sinon.assert.calledWithMatch(dispatcher.dispatch,
-                sinon.match.hasOwn("reason", "websocket-setup"));
+              sinon.assert.calledOnce(dispatcher.dispatch,
+                new sharedActions.ConnectionFailure({
+                  reason: "websocket-setup"
+                }));
              });
           });
         });
       });
     });
 
     describe("outgoing calls", function() {
       it("should request the outgoing call data", function() {
@@ -1132,46 +1130,27 @@ describe("loop.store.ConversationStore",
     describe("Websocket progress", function() {
       beforeEach(function() {
         store.connectCall(
           new sharedActions.ConnectCall({sessionData: fakeSessionData}));
 
         sandbox.stub(dispatcher, "dispatch");
       });
 
-      it("should dispatch a connection failure action on 'terminate' for outgoing calls", function() {
-        store.setStoreState({
-          outgoing: true
-        });
-
-        store._websocket.trigger("progress", {
-          state: WS_STATES.TERMINATED,
-          reason: WEBSOCKET_REASONS.REJECT
-        });
-
-        sinon.assert.calledOnce(dispatcher.dispatch);
-        // Can't use instanceof here, as that matches any action
-        sinon.assert.calledWithMatch(dispatcher.dispatch,
-          sinon.match.hasOwn("name", "connectionFailure"));
-        sinon.assert.calledWithMatch(dispatcher.dispatch,
-          sinon.match.hasOwn("reason", WEBSOCKET_REASONS.REJECT));
-      });
-
       it("should dispatch a connection failure action on 'terminate' for incoming calls if the previous state was not 'alerting' or 'init'", function() {
         store.setStoreState({
           outgoing: false
         });
 
         store._websocket.trigger("progress", {
           state: WS_STATES.TERMINATED,
           reason: WEBSOCKET_REASONS.CANCEL
         }, WS_STATES.CONNECTING);
 
         sinon.assert.calledOnce(dispatcher.dispatch);
-        // Can't use instanceof here, as that matches any action
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.ConnectionFailure({
             reason: WEBSOCKET_REASONS.CANCEL
           }));
       });
 
       it("should dispatch a cancel call action on 'terminate' for incoming calls if the previous state was 'init'", function() {
         store.setStoreState({
@@ -1179,42 +1158,86 @@ describe("loop.store.ConversationStore",
         });
 
         store._websocket.trigger("progress", {
           state: WS_STATES.TERMINATED,
           reason: WEBSOCKET_REASONS.CANCEL
         }, WS_STATES.INIT);
 
         sinon.assert.calledOnce(dispatcher.dispatch);
-        // Can't use instanceof here, as that matches any action
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.CancelCall({}));
       });
 
       it("should dispatch a cancel call action on 'terminate' for incoming calls if the previous state was 'alerting'", function() {
         store.setStoreState({
           outgoing: false
         });
 
         store._websocket.trigger("progress", {
           state: WS_STATES.TERMINATED,
           reason: WEBSOCKET_REASONS.CANCEL
         }, WS_STATES.ALERTING);
 
         sinon.assert.calledOnce(dispatcher.dispatch);
-        // Can't use instanceof here, as that matches any action
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.CancelCall({}));
       });
 
       it("should dispatch a connection progress action on 'alerting'", function() {
         store._websocket.trigger("progress", {state: WS_STATES.ALERTING});
 
         sinon.assert.calledOnce(dispatcher.dispatch);
-        // Can't use instanceof here, as that matches any action
-        sinon.assert.calledWithMatch(dispatcher.dispatch,
-          sinon.match.hasOwn("name", "connectionProgress"));
-        sinon.assert.calledWithMatch(dispatcher.dispatch,
-          sinon.match.hasOwn("wsState", WS_STATES.ALERTING));
+        sinon.assert.calledWithExactly(dispatcher.dispatch,
+          new sharedActions.ConnectionProgress({
+            wsState: WS_STATES.ALERTING
+          }));
+      });
+
+      describe("Outgoing Calls, handling 'terminate'", function() {
+        beforeEach(function() {
+          store.setStoreState({
+            outgoing: true
+          });
+        });
+
+        it("should dispatch a connection failure action", function() {
+          store._websocket.trigger("progress", {
+            state: WS_STATES.TERMINATED,
+            reason: WEBSOCKET_REASONS.MEDIA_FAIL
+          });
+
+          sinon.assert.calledOnce(dispatcher.dispatch);
+          sinon.assert.calledWithExactly(dispatcher.dispatch,
+            new sharedActions.ConnectionFailure({
+              reason: WEBSOCKET_REASONS.MEDIA_FAIL
+            }));
+        });
+
+        it("should dispatch an action with user unavailable if the websocket reports busy", function() {
+          store._websocket.trigger("progress", {
+            state: WS_STATES.TERMINATED,
+            reason: WEBSOCKET_REASONS.BUSY
+          });
+
+          sinon.assert.calledOnce(dispatcher.dispatch);
+          sinon.assert.calledWithExactly(dispatcher.dispatch,
+            new sharedActions.ConnectionFailure({
+              reason: FAILURE_DETAILS.USER_UNAVAILABLE
+            }));
+        });
+
+        it("should dispatch an action with user unavailable if the websocket reports reject", function() {
+          store._websocket.trigger("progress", {
+            state: WS_STATES.TERMINATED,
+            reason: WEBSOCKET_REASONS.REJECT
+          });
+
+          sinon.assert.calledOnce(dispatcher.dispatch);
+          sinon.assert.calledWithExactly(dispatcher.dispatch,
+            new sharedActions.ConnectionFailure({
+              reason: FAILURE_DETAILS.USER_UNAVAILABLE
+            }));
+        });
       });
     });
   });
 });
--- a/browser/components/loop/ui/fake-mozLoop.js
+++ b/browser/components/loop/ui/fake-mozLoop.js
@@ -43,16 +43,68 @@ var fakeRooms = [
     "clientMaxSize": 2,
     "creationTime": 1405517546,
     "ctime": 1405517818,
     "expiresAt": 1405534180,
     "participants": [
        { "displayName": "Alexis", "account": "alexis@example.com", "roomConnectionId": "2a1787a6-4a73-43b5-ae3e-906ec1e763cb" },
        { "displayName": "Adam", "roomConnectionId": "781f012b-f1ea-4ce1-9105-7cfc36fb4ec7" }
      ]
+  },
+  {
+    "roomToken": "REJRFfkdfkf",
+    "decryptedContext": {
+      "roomName": "Third Room Name"
+    },
+    "roomUrl": "http://localhost:3000/rooms/REJRFfkdfkf",
+    "roomOwner": "Alexis",
+    "maxSize": 2,
+    "creationTime": 1405537485,
+    "ctime": 1405537485,
+    "expiresAt": 1405554180,
+    "participants": []
+  },
+  {
+    "roomToken": "fjdkreFJDer",
+    "decryptedContext": {
+      "roomName": "Forth Room Name"
+    },
+    "roomUrl": "http://localhost:3000/rooms/fjdkreFJDer",
+    "roomOwner": "Alexis",
+    "maxSize": 2,
+    "creationTime": 1405546564,
+    "ctime": 1405546564,
+    "expiresAt": 1405564180,
+    "participants": []
+  },
+  {
+    "roomToken": "preFDREJhdf",
+    "decryptedContext": {
+      "roomName": "Fifth Room Name"
+    },
+    "roomUrl": "http://localhost:3000/rooms/preFDREJhdf",
+    "roomOwner": "Alexis",
+    "maxSize": 2,
+    "creationTime": 1405566934,
+    "ctime": 1405566934,
+    "expiresAt": 1405584180,
+    "participants": []
+  },
+  {
+    "roomToken": "preFLighdf",
+    "decryptedContext": {
+      "roomName": "Sixth Room Name"
+    },
+    "roomUrl": "http://localhost:3000/rooms/preFLighdf",
+    "roomOwner": "Alexis",
+    "maxSize": 2,
+    "creationTime": 1405576934,
+    "ctime": 1405576934,
+    "expiresAt": 1405614180,
+    "participants": []
   }
 ];
 
 var fakeManyContacts = [{
   id: 1,
   _guid: 1,
   name: ["Ally Avocado"],
   email: [{
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -39,16 +39,17 @@
   var ConversationToolbar = loop.shared.views.ConversationToolbar;
   var FeedbackView = loop.feedbackViews.FeedbackView;
   var Checkbox = loop.shared.views.Checkbox;
   var TextChatView = loop.shared.views.chat.TextChatView;
 
   // Store constants
   var ROOM_STATES = loop.store.ROOM_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
+  var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
 
   // Local helpers
   function returnTrue() {
     return true;
   }
 
   function returnFalse() {
     return false;
@@ -441,30 +442,53 @@
   });
 
   // Local mocks
   var mockMozLoopNoRooms = _.cloneDeep(navigator.mozLoop);
   mockMozLoopNoRooms.rooms.getAll = function(version, callback) {
     callback(null, []);
   };
 
+  var mockMozLoopNoRoomsNoContext = _.cloneDeep(navigator.mozLoop);
+  mockMozLoopNoRoomsNoContext.getSelectedTabMetadata = function(){};
+  mockMozLoopNoRoomsNoContext.rooms.getAll = function(version, callback) {
+    callback(null, []);
+  };
+
   var roomStoreNoRooms = new loop.store.RoomStore(new loop.Dispatcher(), {
     mozLoop: mockMozLoopNoRooms,
     activeRoomStore: new loop.store.ActiveRoomStore(new loop.Dispatcher(), {
       mozLoop: mockMozLoopNoRooms,
       sdkDriver: mockSDK
     })
   });
 
+  /* xxx this is asynchronous - if start seeing things pending then this is the culprit */
+  roomStoreNoRooms.setStoreState({
+    pendingInitialRetrieval: false
+  });
+
+  var roomStoreNoRoomsPending = new loop.store.RoomStore(new loop.Dispatcher(), {
+    mozLoop: mockMozLoopNoRooms,
+    activeRoomStore: new loop.store.ActiveRoomStore(new loop.Dispatcher(), {
+      mozLoop: mockMozLoopNoRooms,
+      sdkDriver: mockSDK
+    })
+  });
+
   var mockMozLoopLoggedIn = _.cloneDeep(navigator.mozLoop);
   mockMozLoopLoggedIn.userProfile = {
     email: "text@example.com",
     uid: "0354b278a381d3cb408bb46ffc01266"
   };
 
+  var mockMozLoopLoggedInNoContext = _.cloneDeep(navigator.mozLoop);
+  mockMozLoopLoggedInNoContext.getSelectedTabMetadata = function(){};
+  mockMozLoopLoggedInNoContext.userProfile = _.cloneDeep(mockMozLoopLoggedIn.userProfile);
+
   var mockMozLoopLoggedInLongEmail = _.cloneDeep(navigator.mozLoop);
   mockMozLoopLoggedInLongEmail.userProfile = {
     email: "reallyreallylongtext@example.com",
     uid: "0354b278a381d3cb408bb46ffc01266"
   };
 
   var mockMozLoopRooms = _.extend({}, navigator.mozLoop);
 
@@ -718,18 +742,18 @@
         React.createElement(ShowCase, null, 
           React.createElement(Section, {name: "PanelView"}, 
             React.createElement("p", {className: "note"}, 
               React.createElement("strong", null, "Note:"), " 332px wide."
             ), 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
                            height: 410, 
-                           summary: "Re-sign-in view", 
-                           width: 332}, 
+                           summary: "First time experience view", 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
                 React.createElement(PanelView, {client: mockClient, 
                   dispatcher: dispatcher, 
                   mozLoop: firstTimeUseMozLoop, 
                   notifications: notifications, 
                   roomStore: roomStore, 
                   selectedTab: "rooms"})
               )
@@ -744,47 +768,92 @@
                 React.createElement(SignInRequestView, {mozLoop: mockMozLoopLoggedIn})
               )
             ), 
 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
                            height: 410, 
                            summary: "Room list tab", 
-                           width: 332}, 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
                 React.createElement(PanelView, {client: mockClient, 
                            dispatcher: dispatcher, 
                            mozLoop: mockMozLoopLoggedIn, 
                            notifications: notifications, 
                            roomStore: roomStore, 
                            selectedTab: "rooms"})
               )
             ), 
 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
                            height: 410, 
+                           summary: "Room list tab (No Context)", 
+                           width: 330}, 
+              React.createElement("div", {className: "panel"}, 
+                React.createElement(PanelView, {client: mockClient, 
+                           dispatcher: dispatcher, 
+                           mozLoop: mockMozLoopLoggedInNoContext, 
+                           notifications: notifications, 
+                           roomStore: roomStore, 
+                           selectedTab: "rooms"})
+              )
+            ), 
+
+            React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
+                           dashed: true, 
+                           height: 410, 
                            summary: "Room list tab (no rooms)", 
-                           width: 332}, 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
                 React.createElement(PanelView, {client: mockClient, 
                            dispatcher: dispatcher, 
                            mozLoop: mockMozLoopNoRooms, 
                            notifications: notifications, 
                            roomStore: roomStoreNoRooms, 
                            selectedTab: "rooms"})
               )
             ), 
 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
                            height: 410, 
+                           summary: "Room list tab (no rooms and no context)", 
+                           width: 330}, 
+              React.createElement("div", {className: "panel"}, 
+                React.createElement(PanelView, {client: mockClient, 
+                           dispatcher: dispatcher, 
+                           mozLoop: mockMozLoopNoRoomsNoContext, 
+                           notifications: notifications, 
+                           roomStore: roomStoreNoRooms, 
+                           selectedTab: "rooms"})
+              )
+            ), 
+
+            React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
+                           dashed: true, 
+                           height: 410, 
+                           summary: "Room list tab (loading view)", 
+                           width: 330}, 
+              React.createElement("div", {className: "panel"}, 
+                React.createElement(PanelView, {client: mockClient, 
+                           dispatcher: dispatcher, 
+                           mozLoop: mockMozLoopNoRoomsNoContext, 
+                           notifications: notifications, 
+                           roomStore: roomStoreNoRoomsPending, 
+                           selectedTab: "rooms"})
+              )
+            ), 
+
+            React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
+                           dashed: true, 
+                           height: 410, 
                            summary: "Contact list tab", 
-                           width: 332}, 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
                 React.createElement(PanelView, {client: mockClient, 
                            dispatcher: dispatcher, 
                            mozLoop: mockMozLoopLoggedIn, 
                            notifications: notifications, 
                            roomStore: roomStore, 
                            selectedTab: "contacts"})
               )
@@ -802,118 +871,122 @@
                            roomStore: roomStore, 
                            selectedTab: "contacts"})
               )
             ), 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
                            height: 410, 
                            summary: "Contact list tab long email", 
-                           width: 332}, 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
                 React.createElement(PanelView, {client: mockClient, 
                            dispatcher: dispatcher, 
                            mozLoop: mockMozLoopLoggedInLongEmail, 
                            notifications: notifications, 
                            roomStore: roomStore, 
                            selectedTab: "contacts"})
               )
             ), 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
                            height: 410, 
                            summary: "Contact list tab (no contacts)", 
-                           width: 332}, 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
                 React.createElement(PanelView, {client: mockClient, 
                            dispatcher: dispatcher, 
                            mozLoop: mozLoopNoContacts, 
                            notifications: notifications, 
                            roomStore: roomStore, 
                            selectedTab: "contacts"})
               )
             ), 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
                            height: 410, 
                            summary: "Error Notification", 
-                           width: 332}, 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
                 React.createElement(PanelView, {client: mockClient, 
                            dispatcher: dispatcher, 
                            mozLoop: navigator.mozLoop, 
                            notifications: errNotifications, 
                            roomStore: roomStore})
               )
             ), 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
                            height: 410, 
                            summary: "Error Notification - authenticated", 
-                           width: 332}, 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
                 React.createElement(PanelView, {client: mockClient, 
                            dispatcher: dispatcher, 
                            mozLoop: mockMozLoopLoggedIn, 
                            notifications: errNotifications, 
                            roomStore: roomStore})
               )
             ), 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
                            height: 410, 
                            summary: "Contact import success", 
-                           width: 332}, 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
                 React.createElement(PanelView, {dispatcher: dispatcher, 
                            mozLoop: mockMozLoopLoggedIn, 
                            notifications: new loop.shared.models.NotificationCollection([{level: "success", message: "Import success"}]), 
                            roomStore: roomStore, 
                            selectedTab: "contacts"})
               )
             ), 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
                            height: 410, 
                            summary: "Contact import error", 
-                           width: 332}, 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
                 React.createElement(PanelView, {dispatcher: dispatcher, 
                            mozLoop: mockMozLoopLoggedIn, 
                            notifications: new loop.shared.models.NotificationCollection([{level: "error", message: "Import error"}]), 
                            roomStore: roomStore, 
                            selectedTab: "contacts"})
               )
             ), 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
                            height: 410, 
                            summary: "Contact Form - Add", 
-                           width: 332}, 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
                 React.createElement(PanelView, {client: mockClient, 
                            dispatcher: dispatcher, 
                            initialSelectedTabComponent: "contactAdd", 
                            mozLoop: mockMozLoopLoggedIn, 
                            notifications: notifications, 
                            roomStore: roomStore, 
                            selectedTab: "contacts", 
                            userProfile: {email: "test@example.com"}})
               )
             ), 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
-                           height: 321, 
+                           height: 410, 
                            summary: "Contact Form - Edit", 
-                           width: 332}, 
+                           width: 330}, 
               React.createElement("div", {className: "panel"}, 
-                React.createElement(ContactDetailsForm, {contactFormData: fakeManyContacts[1], 
-                                    mode: "edit", 
-                                    mozLoop: mockMozLoopLoggedIn, 
-                                    switchToInitialView: noop})
+                React.createElement(PanelView, {client: mockClient, 
+                           dispatcher: dispatcher, 
+                           initialSelectedTabComponent: "contactEdit", 
+                           mozLoop: mockMozLoopLoggedIn, 
+                           notifications: notifications, 
+                           roomStore: roomStore, 
+                           selectedTab: "contacts", 
+                           userProfile: {email: "test@example.com"}})
               )
             )
           ), 
 
           React.createElement(Section, {name: "Availability Dropdown"}, 
             React.createElement("p", {className: "note"}, 
               React.createElement("strong", null, "Note:"), " 332px wide."
             ), 
@@ -936,19 +1009,19 @@
                 React.createElement(AvailabilityDropdown, null)
               )
             )
           ), 
 
           React.createElement(Section, {name: "ContactDetail"}, 
             React.createElement(FramedExample, {cssClass: "fx-embedded-panel", 
                            dashed: true, 
-                           height: 272, 
+                           height: 50, 
                            summary: "ContactDetail", 
-                           width: 300}, 
+                           width: 334}, 
               React.createElement("div", {className: "panel force-menu-show"}, 
                 React.createElement(ContactDetail, {contact: fakeManyContacts[0], 
                                handleContactAction: function() {}})
               )
             )
           ), 
 
           React.createElement(Section, {name: "ContactDropdown"}, 
@@ -1020,42 +1093,48 @@
           React.createElement(Section, {name: "ConversationToolbar"}, 
             React.createElement("div", null, 
               React.createElement(FramedExample, {dashed: true, 
                              height: 56, 
                              summary: "Default", 
                              width: 300}, 
                 React.createElement("div", {className: "fx-embedded"}, 
                   React.createElement(ConversationToolbar, {audio: { enabled: true, visible: true}, 
+                                       dispatcher: dispatcher, 
                                        hangup: noop, 
                                        publishStream: noop, 
+                                       screenShare: { state: SCREEN_SHARE_STATES.INACTIVE, visible: true}, 
                                        settingsMenuItems: [{ id: "feedback" }], 
                                        video: { enabled: true, visible: true}})
                 )
               ), 
               React.createElement(FramedExample, {dashed: true, 
                              height: 56, 
-                             summary: "Video muted", 
+                             summary: "Video muted, Screen share pending", 
                              width: 300}, 
                 React.createElement("div", {className: "fx-embedded"}, 
                   React.createElement(ConversationToolbar, {audio: { enabled: true, visible: true}, 
+                                       dispatcher: dispatcher, 
                                        hangup: noop, 
                                        publishStream: noop, 
+                                       screenShare: { state: SCREEN_SHARE_STATES.PENDING, visible: true}, 
                                        settingsMenuItems: [{ id: "feedback" }], 
                                        video: { enabled: false, visible: true}})
                 )
               ), 
               React.createElement(FramedExample, {dashed: true, 
                              height: 56, 
-                             summary: "Audio muted", 
+                             summary: "Audio muted, Screen share active", 
                              width: 300}, 
                 React.createElement("div", {className: "fx-embedded"}, 
                   React.createElement(ConversationToolbar, {audio: { enabled: false, visible: true}, 
+                                       dispatcher: dispatcher, 
                                        hangup: noop, 
                                        publishStream: noop, 
+                                       screenShare: { state: SCREEN_SHARE_STATES.ACTIVE, visible: true}, 
                                        settingsMenuItems: [{ id: "feedback" }], 
                                        video: { enabled: true, visible: true}})
                 )
               )
             )
           ), 
 
           React.createElement(Section, {name: "PendingConversationView (Desktop)"}, 
@@ -1657,17 +1736,17 @@
     var caughtWarnings = [];
     console.warn = function() {
       var args = Array.slice(arguments);
       caughtWarnings.push(args);
       consoleWarn.apply(console, args);
     };
 
     try {
-      React.renderComponent(React.createElement(App, null), document.getElementById("main"));
+      React.render(React.createElement(App, null), document.getElementById("main"));
 
       for (var listener of visibilityListeners) {
         listener({target: {hidden: false}});
       }
     } catch(err) {
       console.error(err);
       uncaughtError = err;
     }
@@ -1678,17 +1757,17 @@
         setTimeout(waitForQueuedFrames, 500);
         return;
       }
       // Put the title back, in case views changed it.
       document.title = "Loop UI Components Showcase";
 
       // This simulates the mocha layout for errors which means we can run
       // this alongside our other unit tests but use the same harness.
-      var expectedWarningsCount = 10;
+      var expectedWarningsCount = 3;
       var warningsMismatch = caughtWarnings.length !== expectedWarningsCount;
       var resultsElement = document.querySelector("#results");
       var divFailuresNode = document.createElement("div");
       var pCompleteNode = document.createElement("p");
       var emNode = document.createElement("em");
 
       if (uncaughtError || warningsMismatch) {
         var liTestFail = document.createElement("li");
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -39,16 +39,17 @@
   var ConversationToolbar = loop.shared.views.ConversationToolbar;
   var FeedbackView = loop.feedbackViews.FeedbackView;
   var Checkbox = loop.shared.views.Checkbox;
   var TextChatView = loop.shared.views.chat.TextChatView;
 
   // Store constants
   var ROOM_STATES = loop.store.ROOM_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
+  var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
 
   // Local helpers
   function returnTrue() {
     return true;
   }
 
   function returnFalse() {
     return false;
@@ -441,30 +442,53 @@
   });
 
   // Local mocks
   var mockMozLoopNoRooms = _.cloneDeep(navigator.mozLoop);
   mockMozLoopNoRooms.rooms.getAll = function(version, callback) {
     callback(null, []);
   };
 
+  var mockMozLoopNoRoomsNoContext = _.cloneDeep(navigator.mozLoop);
+  mockMozLoopNoRoomsNoContext.getSelectedTabMetadata = function(){};
+  mockMozLoopNoRoomsNoContext.rooms.getAll = function(version, callback) {
+    callback(null, []);
+  };
+
   var roomStoreNoRooms = new loop.store.RoomStore(new loop.Dispatcher(), {
     mozLoop: mockMozLoopNoRooms,
     activeRoomStore: new loop.store.ActiveRoomStore(new loop.Dispatcher(), {
       mozLoop: mockMozLoopNoRooms,
       sdkDriver: mockSDK
     })
   });
 
+  /* xxx this is asynchronous - if start seeing things pending then this is the culprit */
+  roomStoreNoRooms.setStoreState({
+    pendingInitialRetrieval: false
+  });
+
+  var roomStoreNoRoomsPending = new loop.store.RoomStore(new loop.Dispatcher(), {
+    mozLoop: mockMozLoopNoRooms,
+    activeRoomStore: new loop.store.ActiveRoomStore(new loop.Dispatcher(), {
+      mozLoop: mockMozLoopNoRooms,
+      sdkDriver: mockSDK
+    })
+  });
+
   var mockMozLoopLoggedIn = _.cloneDeep(navigator.mozLoop);
   mockMozLoopLoggedIn.userProfile = {
     email: "text@example.com",
     uid: "0354b278a381d3cb408bb46ffc01266"
   };
 
+  var mockMozLoopLoggedInNoContext = _.cloneDeep(navigator.mozLoop);
+  mockMozLoopLoggedInNoContext.getSelectedTabMetadata = function(){};
+  mockMozLoopLoggedInNoContext.userProfile = _.cloneDeep(mockMozLoopLoggedIn.userProfile);
+
   var mockMozLoopLoggedInLongEmail = _.cloneDeep(navigator.mozLoop);
   mockMozLoopLoggedInLongEmail.userProfile = {
     email: "reallyreallylongtext@example.com",
     uid: "0354b278a381d3cb408bb46ffc01266"
   };
 
   var mockMozLoopRooms = _.extend({}, navigator.mozLoop);
 
@@ -718,18 +742,18 @@
         <ShowCase>
           <Section name="PanelView">
             <p className="note">
               <strong>Note:</strong> 332px wide.
             </p>
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
                            height={410}
-                           summary="Re-sign-in view"
-                           width={332}>
+                           summary="First time experience view"
+                           width={330}>
               <div className="panel">
                 <PanelView client={mockClient}
                   dispatcher={dispatcher}
                   mozLoop={firstTimeUseMozLoop}
                   notifications={notifications}
                   roomStore={roomStore}
                   selectedTab="rooms" />
               </div>
@@ -744,47 +768,92 @@
                 <SignInRequestView mozLoop={mockMozLoopLoggedIn} />
               </div>
             </FramedExample>
 
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
                            height={410}
                            summary="Room list tab"
-                           width={332}>
+                           width={330}>
               <div className="panel">
                 <PanelView client={mockClient}
                            dispatcher={dispatcher}
                            mozLoop={mockMozLoopLoggedIn}
                            notifications={notifications}
                            roomStore={roomStore}
                            selectedTab="rooms" />
               </div>
             </FramedExample>
 
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
                            height={410}
+                           summary="Room list tab (No Context)"
+                           width={330}>
+              <div className="panel">
+                <PanelView client={mockClient}
+                           dispatcher={dispatcher}
+                           mozLoop={mockMozLoopLoggedInNoContext}
+                           notifications={notifications}
+                           roomStore={roomStore}
+                           selectedTab="rooms" />
+              </div>
+            </FramedExample>
+
+            <FramedExample cssClass="fx-embedded-panel"
+                           dashed={true}
+                           height={410}
                            summary="Room list tab (no rooms)"
-                           width={332}>
+                           width={330}>
               <div className="panel">
                 <PanelView client={mockClient}
                            dispatcher={dispatcher}
                            mozLoop={mockMozLoopNoRooms}
                            notifications={notifications}
                            roomStore={roomStoreNoRooms}
                            selectedTab="rooms" />
               </div>
             </FramedExample>
 
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
                            height={410}
+                           summary="Room list tab (no rooms and no context)"
+                           width={330}>
+              <div className="panel">
+                <PanelView client={mockClient}
+                           dispatcher={dispatcher}
+                           mozLoop={mockMozLoopNoRoomsNoContext}
+                           notifications={notifications}
+                           roomStore={roomStoreNoRooms}
+                           selectedTab="rooms" />
+              </div>
+            </FramedExample>
+
+            <FramedExample cssClass="fx-embedded-panel"
+                           dashed={true}
+                           height={410}
+                           summary="Room list tab (loading view)"
+                           width={330}>
+              <div className="panel">
+                <PanelView client={mockClient}
+                           dispatcher={dispatcher}
+                           mozLoop={mockMozLoopNoRoomsNoContext}
+                           notifications={notifications}
+                           roomStore={roomStoreNoRoomsPending}
+                           selectedTab="rooms" />
+              </div>
+            </FramedExample>
+
+            <FramedExample cssClass="fx-embedded-panel"
+                           dashed={true}
+                           height={410}
                            summary="Contact list tab"
-                           width={332}>
+                           width={330}>
               <div className="panel">
                 <PanelView client={mockClient}
                            dispatcher={dispatcher}
                            mozLoop={mockMozLoopLoggedIn}
                            notifications={notifications}
                            roomStore={roomStore}
                            selectedTab="contacts" />
               </div>
@@ -802,118 +871,122 @@
                            roomStore={roomStore}
                            selectedTab="contacts" />
               </div>
             </FramedExample>
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
                            height={410}
                            summary="Contact list tab long email"
-                           width={332}>
+                           width={330}>
               <div className="panel">
                 <PanelView client={mockClient}
                            dispatcher={dispatcher}
                            mozLoop={mockMozLoopLoggedInLongEmail}
                            notifications={notifications}
                            roomStore={roomStore}
                            selectedTab="contacts" />
               </div>
             </FramedExample>
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
                            height={410}
                            summary="Contact list tab (no contacts)"
-                           width={332}>
+                           width={330}>
               <div className="panel">
                 <PanelView client={mockClient}
                            dispatcher={dispatcher}
                            mozLoop={mozLoopNoContacts}
                            notifications={notifications}
                            roomStore={roomStore}
                            selectedTab="contacts" />
               </div>
             </FramedExample>
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
                            height={410}
                            summary="Error Notification"
-                           width={332}>
+                           width={330}>
               <div className="panel">
                 <PanelView client={mockClient}
                            dispatcher={dispatcher}
                            mozLoop={navigator.mozLoop}
                            notifications={errNotifications}
                            roomStore={roomStore} />
               </div>
             </FramedExample>
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
                            height={410}
                            summary="Error Notification - authenticated"
-                           width={332}>
+                           width={330}>
               <div className="panel">
                 <PanelView client={mockClient}
                            dispatcher={dispatcher}
                            mozLoop={mockMozLoopLoggedIn}
                            notifications={errNotifications}
                            roomStore={roomStore} />
               </div>
             </FramedExample>
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
                            height={410}
                            summary="Contact import success"
-                           width={332}>
+                           width={330}>
               <div className="panel">
                 <PanelView dispatcher={dispatcher}
                            mozLoop={mockMozLoopLoggedIn}
                            notifications={new loop.shared.models.NotificationCollection([{level: "success", message: "Import success"}])}
                            roomStore={roomStore}
                            selectedTab="contacts" />
               </div>
             </FramedExample>
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
                            height={410}
                            summary="Contact import error"
-                           width={332}>
+                           width={330}>
               <div className="panel">
                 <PanelView dispatcher={dispatcher}
                            mozLoop={mockMozLoopLoggedIn}
                            notifications={new loop.shared.models.NotificationCollection([{level: "error", message: "Import error"}])}
                            roomStore={roomStore}
                            selectedTab="contacts" />
               </div>
             </FramedExample>
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
                            height={410}
                            summary="Contact Form - Add"
-                           width={332}>
+                           width={330}>
               <div className="panel">
                 <PanelView client={mockClient}
                            dispatcher={dispatcher}
                            initialSelectedTabComponent="contactAdd"
                            mozLoop={mockMozLoopLoggedIn}
                            notifications={notifications}
                            roomStore={roomStore}
                            selectedTab="contacts"
                            userProfile={{email: "test@example.com"}} />
               </div>
             </FramedExample>
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
-                           height={321}
+                           height={410}
                            summary="Contact Form - Edit"
-                           width={332}>
+                           width={330}>
               <div className="panel">
-                <ContactDetailsForm contactFormData={fakeManyContacts[1]}
-                                    mode={"edit"}
-                                    mozLoop={mockMozLoopLoggedIn}
-                                    switchToInitialView={noop} />
+                <PanelView client={mockClient}
+                           dispatcher={dispatcher}
+                           initialSelectedTabComponent="contactEdit"
+                           mozLoop={mockMozLoopLoggedIn}
+                           notifications={notifications}
+                           roomStore={roomStore}
+                           selectedTab="contacts"
+                           userProfile={{email: "test@example.com"}} />
               </div>
             </FramedExample>
           </Section>
 
           <Section name="Availability Dropdown">
             <p className="note">
               <strong>Note:</strong> 332px wide.
             </p>
@@ -936,19 +1009,19 @@
                 <AvailabilityDropdown />
               </div>
             </FramedExample>
           </Section>
 
           <Section name="ContactDetail">
             <FramedExample cssClass="fx-embedded-panel"
                            dashed={true}
-                           height={272}
+                           height={50}
                            summary="ContactDetail"
-                           width={300}>
+                           width={334}>
               <div className="panel force-menu-show">
                 <ContactDetail contact={fakeManyContacts[0]}
                                handleContactAction={function() {}} />
               </div>
             </FramedExample>
           </Section>
 
           <Section name="ContactDropdown">
@@ -1020,42 +1093,48 @@
           <Section name="ConversationToolbar">
             <div>
               <FramedExample dashed={true}
                              height={56}
                              summary="Default"
                              width={300}>
                 <div className="fx-embedded">
                   <ConversationToolbar audio={{ enabled: true, visible: true }}
+                                       dispatcher={dispatcher}
                                        hangup={noop}
                                        publishStream={noop}
+                                       screenShare={{ state: SCREEN_SHARE_STATES.INACTIVE, visible: true }}
                                        settingsMenuItems={[{ id: "feedback" }]}
                                        video={{ enabled: true, visible: true }} />
                 </div>
               </FramedExample>
               <FramedExample dashed={true}
                              height={56}
-                             summary="Video muted"
+                             summary="Video muted, Screen share pending"
                              width={300}>
                 <div className="fx-embedded">
                   <ConversationToolbar audio={{ enabled: true, visible: true }}
+                                       dispatcher={dispatcher}
                                        hangup={noop}
                                        publishStream={noop}
+                                       screenShare={{ state: SCREEN_SHARE_STATES.PENDING, visible: true }}
                                        settingsMenuItems={[{ id: "feedback" }]}
                                        video={{ enabled: false, visible: true }} />
                 </div>
               </FramedExample>
               <FramedExample dashed={true}
                              height={56}
-                             summary="Audio muted"
+                             summary="Audio muted, Screen share active"
                              width={300}>
                 <div className="fx-embedded">
                   <ConversationToolbar audio={{ enabled: false, visible: true }}
+                                       dispatcher={dispatcher}
                                        hangup={noop}
                                        publishStream={noop}
+                                       screenShare={{ state: SCREEN_SHARE_STATES.ACTIVE, visible: true }}
                                        settingsMenuItems={[{ id: "feedback" }]}
                                        video={{ enabled: true, visible: true }} />
                 </div>
               </FramedExample>
             </div>
           </Section>
 
           <Section name="PendingConversationView (Desktop)">
@@ -1657,17 +1736,17 @@
     var caughtWarnings = [];
     console.warn = function() {
       var args = Array.slice(arguments);
       caughtWarnings.push(args);
       consoleWarn.apply(console, args);
     };
 
     try {
-      React.renderComponent(<App />, document.getElementById("main"));
+      React.render(<App />, document.getElementById("main"));
 
       for (var listener of visibilityListeners) {
         listener({target: {hidden: false}});
       }
     } catch(err) {
       console.error(err);
       uncaughtError = err;
     }
@@ -1678,17 +1757,17 @@
         setTimeout(waitForQueuedFrames, 500);
         return;
       }
       // Put the title back, in case views changed it.
       document.title = "Loop UI Components Showcase";
 
       // This simulates the mocha layout for errors which means we can run
       // this alongside our other unit tests but use the same harness.
-      var expectedWarningsCount = 10;
+      var expectedWarningsCount = 3;
       var warningsMismatch = caughtWarnings.length !== expectedWarningsCount;
       var resultsElement = document.querySelector("#results");
       var divFailuresNode = document.createElement("div");
       var pCompleteNode = document.createElement("p");
       var emNode = document.createElement("em");
 
       if (uncaughtError || warningsMismatch) {
         var liTestFail = document.createElement("li");
--- a/browser/components/migration/nsEdgeReadingListExtractor.cpp
+++ b/browser/components/migration/nsEdgeReadingListExtractor.cpp
@@ -51,17 +51,18 @@ nsEdgeReadingListExtractor::Extract(cons
 
   // JET does not throw exceptions, and so error handling and ensuring we close
   // the DB is a bit finnicky. Keep track of how far we got so we guarantee closing
   // the right things
   bool instanceCreated, sessionCreated, dbOpened, tableOpened;
 
   // Check for the right page size and initialize with that
   unsigned long pageSize;
-  err = JetGetDatabaseFileInfoW(aDBPath.BeginReading(), &pageSize, sizeof(pageSize), JET_DbInfoPageSize);
+  err = JetGetDatabaseFileInfoW(static_cast<char16ptr_t>(aDBPath.BeginReading()),
+                                &pageSize, sizeof(pageSize), JET_DbInfoPageSize);
   NS_HANDLE_JET_ERROR(err)
   err = JetSetSystemParameter(&instance, NULL, JET_paramDatabasePageSize, pageSize, NULL);
   NS_HANDLE_JET_ERROR(err)
 
   // Turn off recovery, because otherwise we will create log files in either the cwd or
   // overwrite Edge's own logfiles, which is useless at best and at worst might mess with
   // Edge actually using the DB
   err = JetSetSystemParameter(&instance, NULL, JET_paramRecovery, NULL, "Off");
@@ -74,20 +75,22 @@ nsEdgeReadingListExtractor::Extract(cons
 
   err = JetInit(&instance);
   NS_HANDLE_JET_ERROR(err)
   err = JetBeginSession(instance, &sesid, 0, 0);
   NS_HANDLE_JET_ERROR(err)
   sessionCreated = true;
 
   // Actually open the DB, and make sure to do so readonly:
-  err = JetAttachDatabaseW(sesid, aDBPath.BeginReading(), JET_bitDbReadOnly);
+  err = JetAttachDatabaseW(sesid, static_cast<char16ptr_t>(aDBPath.BeginReading()),
+                           JET_bitDbReadOnly);
   NS_HANDLE_JET_ERROR(err)
   dbOpened = true;
-  err = JetOpenDatabaseW(sesid, aDBPath.BeginReading(), NULL, &dbid, JET_bitDbReadOnly);
+  err = JetOpenDatabaseW(sesid, static_cast<char16ptr_t>(aDBPath.BeginReading()),
+                         NULL, &dbid, JET_bitDbReadOnly);
   NS_HANDLE_JET_ERROR(err)
 
   // Open the readinglist table and get information on the columns we are interested in:
   err = JetOpenTable(sesid, dbid, "ReadingList", NULL, 0, JET_bitTableReadOnly, &tableid);
   NS_HANDLE_JET_ERROR(err)
   tableOpened = true;
   err = JetGetColumnInfo(sesid, dbid, "ReadingList", "URL", &urlColumnInfo,
                          sizeof(urlColumnInfo), JET_ColInfo);
--- a/browser/devtools/performance/performance.xul
+++ b/browser/devtools/performance/performance.xul
@@ -38,16 +38,17 @@
                 tooltiptext="&performanceUI.showPlatformData.tooltiptext;"/>
       <menuitem id="option-enable-memory"
                 class="experimental-option"
                 type="checkbox"
                 data-pref="enable-memory"
                 label="&performanceUI.enableMemory;"
                 tooltiptext="&performanceUI.enableMemory.tooltiptext;"/>
       <menuitem id="option-enable-allocations"
+                class="experimental-option"
                 type="checkbox"
                 data-pref="enable-allocations"
                 label="&performanceUI.enableAllocations;"
                 tooltiptext="&performanceUI.enableAllocations.tooltiptext;"/>
       <menuitem id="option-enable-framerate"
                 type="checkbox"
                 data-pref="enable-framerate"
                 label="&performanceUI.enableFramerate;"
--- a/browser/devtools/webide/content/webide.xul
+++ b/browser/devtools/webide/content/webide.xul
@@ -18,17 +18,17 @@
 <window id="webide" onclose="return UI.canCloseProject();"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml"
         title="&windowTitle;"
         windowtype="devtools:webide"
         macanimationtype="document"
         fullscreenbutton="true"
         screenX="4" screenY="4"
-        width="640" height="480"
+        width="800" height="600"
         persist="screenX screenY width height sizemode">
 
   <script type="application/javascript" src="chrome://global/content/globalOverlay.js"></script>
   <script type="application/javascript" src="project-panel.js"></script>
   <script type="application/javascript" src="runtime-panel.js"></script>
   <script type="application/javascript" src="webide.js"></script>
 
   <commandset id="mainCommandSet">
--- a/browser/devtools/webide/modules/project-list.js
+++ b/browser/devtools/webide/modules/project-list.js
@@ -137,16 +137,17 @@ ProjectList.prototype = {
     if (this._sidebarsEnabled && this._doc !== this._parentWindow.document) {
       let span = opts.panel.querySelector("span") || this._doc.createElement("span");
       span.textContent = opts.name;
       let icon = opts.panel.querySelector("img") || this._doc.createElement("img");
       icon.className = "project-image";
       icon.setAttribute("src", opts.icon);
       opts.panel.appendChild(icon);
       opts.panel.appendChild(span);
+      opts.panel.setAttribute("title", opts.name);
     } else {
       opts.panel.setAttribute("label", opts.name);
       opts.panel.setAttribute("image", opts.icon);
     }
   },
 
   refreshTabs: function() {
     if (AppManager.connected) {
--- a/browser/devtools/webide/themes/config-view.css
+++ b/browser/devtools/webide/themes/config-view.css
@@ -18,38 +18,40 @@ html, body {
   font-family: sans-serif;
   padding-left: 6px;
   width: 100%;
   table-layout: auto;
   margin-top: 110px;
 }
 
 #custom-value-name {
-  width: 70%;
+  width: 50%;
 }
 
 header {
   background-color: rgba(255, 255, 255, 0.8);
   border-bottom: 1px solid #EEE;
   position: fixed;
   top: 0;
   left: 0;
   right: 0;
   height: 90px;
   padding: 10px 20px;
 }
 
 #device-fields td {
-  background-color: #f1f1f1;
-  border-bottom: 1px solid #ccc;
-  border-right: 1px solid #fff;
+  background-color: #F9F9F9;
+  border-bottom: 1px solid #CCC;
+  border-right: 1px solid #FFF;
+  font-size: 0.75em;
 }
 
 #device-fields td:first-child {
-  min-width: 400px;
+  max-width: 250px;
+  min-width: 150px;
 }
 
 #device-fields td.preference-name, #device-fields td.setting-name {
   width: 50%;
   min-width: 400px;
   word-break: break-all;
 }
 
@@ -60,17 +62,17 @@ header {
   white-space: nowrap;
 }
 
 #device-fields tr.hide, #device-fields button.hide {
   display: none;
 }
 
 #device-fields .custom-input {
-  width: 300px;
+  width: 130px;
 }
 
 #search {
   margin-bottom: 20px;
   width: 100%;
 }
 
 #search-bar {
--- a/browser/devtools/webide/themes/deck.css
+++ b/browser/devtools/webide/themes/deck.css
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 html {
   font: message-box;
-  font-size: 15px;
+  font-size: 0.9em;
   font-weight: normal;
   margin: 0;
   height: 100%;
   color: #737980;
   background-color: #ededed;
 }
 
 body {
@@ -23,17 +23,17 @@ body {
 }
 
 .text-input input {
   flex: 0.5;
   margin-left: 5px;
 }
 
 h1 {
-  font-size: 2.5em;
+  font-size: 2em;
   font-weight: lighter;
   line-height: 1.2;
   margin: 0;
   margin-bottom: .5em;
 }
 
 #controls {
   position: absolute;
@@ -55,17 +55,17 @@ table {
 }
 
 th, td {
   padding: 5px;
   border: 1px solid #eee;
 }
 
 th {
-  min-width: 130px;
+  min-width: 100px;
 }
 
 th:first-of-type, td:first-of-type {
   text-align: left;
 }
 
 li {
   list-style: none;
--- a/browser/devtools/webide/themes/panel-listing.css
+++ b/browser/devtools/webide/themes/panel-listing.css
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 html {
   font: message-box;
-  font-size: 12px;
+  font-size: 11px;
   font-weight: 400;
 }
 
 label,
 .panel-item,
 #project-panel-projects,
 #runtime-panel-projects {
   display: block;
@@ -48,65 +48,82 @@ label,
 }
 
 #runtime-panel-simulator,
 .panel-item-complex {
   clear: both;
   position: relative;
 }
 
+.panel-item span {
+  display: block;
+  float: left;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  width: 75%;
+  white-space: nowrap;
+}
+
 .panel-item {
   padding: 3%;
   display: block;
-  background-color: #fff;
-  border-bottom: 1px solid #ccc;
-  border-right: 1px solid #ccc;
-  border-top: 1px solid #ededed;
+  width: 94%;
+  cursor: pointer;
+  border-top: 1px solid transparent;
   border-left: 0;
-  width: 94%;
+  border-bottom: 1px solid #CCC;
+  border-right: 0;
+  background-color: transparent;
 }
 
 button.panel-item {
-  background-position: 8px 8px;
+  background-position: 5px 5px;
   background-repeat: no-repeat;
   background-size: 14px 14px;
   padding-left: 25px;
   width: 100%;
 }
 
-button.project-panel-item-refreshtabs {
+.panel-item:disabled {
+  background-color: #FFF;
+  color: #5A5A5A;
+  opacity: 0.5;
+  cursor: default;
+}
+
+#refresh-tabs {
+  background-color: #FFF;
+  border-top: 1px solid #EDEDED;
   display: inline-block;
   float: right;
   padding: 3px;
   text-transform: none;
+  border-right: 1px solid #CCC;
   width: auto;
   margin: 0 4px 5px 5px;
 }
 
-.panel-item:disabled {
-  background-color: #FFF;
-  color: #5A5A5A;
-  opacity: 0.5;
-}
-
-.panel-item:not(:disabled):hover {
+.panel-item:not(:disabled):hover,
+button.panel-item:not(:disabled):hover,
+#refresh-tabs:hover {
   background-color: #CCF0FD;
+  border-top: 1px solid #EDEDED;
 }
 
 .configure-button {
   display: inline-block;
   height: 30px;
   width: 30px;
   background-color: transparent;
   background-image: -moz-image-rect(url("icons.png"), 104, 462, 129, 438);
   background-position: center center;
   background-repeat: no-repeat;
   background-size: 14px 14px;
   position: absolute;
-  top: 0;
+  top: -2px;
   right: 0;
   border: 0;
 }
 
 .configure-button:hover {
   cursor: pointer;
 }
 
--- a/browser/devtools/webide/themes/webide.css
+++ b/browser/devtools/webide/themes/webide.css
@@ -157,20 +157,24 @@ panel > .panel-arrowcontainer > .panel-a
   padding: 12px 0;
   min-width: 200px;
   max-width: 400px;
 }
 
 .panel-list {
   display: none;
   position: relative;
-  max-width: 250px;
+  max-width: 180px;
   overflow: hidden;
 }
 
+#project-listing-panel.panel-list {
+  max-width: 165px;
+}
+
 .panel-list-wrapper {
   height: 100%;
   width: 100%;
   min-width: 100px;
   position: absolute;
   top: 0;
   bottom: 0;
   right: 0;
deleted file mode 100644
--- a/build/gen_mach_buildprops.py
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/usr/bin/python
-#
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-import sys
-import os
-import hashlib
-import json
-import re
-import errno
-from argparse import ArgumentParser
-
-def getFileHashAndSize(filename):
-    sha512Hash = 'UNKNOWN'
-    size = 'UNKNOWN'
-
-    try:
-        # open in binary mode to make sure we get consistent results
-        # across all platforms
-        f = open(filename, "rb")
-        shaObj = hashlib.sha512(f.read())
-        sha512Hash = shaObj.hexdigest()
-
-        size = os.path.getsize(filename)
-    except:
-        pass
-
-    return (sha512Hash, size)
-
-def getMarProperties(filename, partial=False):
-    if not os.path.exists(filename):
-        return {}
-    (mar_hash, mar_size) = getFileHashAndSize(filename)
-    martype = 'partial' if partial else 'complete'
-    return {
-        '%sMarFilename' % martype: os.path.basename(filename),
-        '%sMarSize' % martype: mar_size,
-        '%sMarHash' % martype: mar_hash,
-    }
-
-def getPartialInfo(props):
-    return [{
-        "from_buildid": props.get("previous_buildid"),
-        "size": props.get("partialMarSize"),
-        "hash": props.get("partialMarHash"),
-        "url": props.get("partialMarUrl"),
-    }]
-
-if __name__ == '__main__':
-    parser = ArgumentParser(description='Generate mach_build_properties.json for automation builds.')
-    parser.add_argument("--complete-mar-file", required=True,
-                        action="store", dest="complete_mar_file",
-                        help="Path to the complete MAR file, relative to the objdir.")
-    parser.add_argument("--partial-mar-file", required=False,
-                        action="store", dest="partial_mar_file",
-                        help="Path to the partial MAR file, relative to the objdir.")
-    parser.add_argument("--upload-properties", required=False,
-                        action="store", dest="upload_properties",
-                        help="Path to the properties written by 'make upload'")
-    args = parser.parse_args()
-
-    json_data = getMarProperties(args.complete_mar_file)
-    if args.upload_properties:
-        with open(args.upload_properties) as f:
-            json_data.update(json.load(f))
-    if args.partial_mar_file:
-        json_data.update(getMarProperties(args.partial_mar_file, partial=True))
-
-        # Pull the previous buildid from the partial mar filename.
-        res = re.match(r'.*\.([0-9]+)-[0-9]+.mar', args.partial_mar_file)
-        if res:
-            json_data['previous_buildid'] = res.group(1)
-
-            # Set partialInfo to be a collection of the partial mar properties
-            # useful for balrog.
-            json_data['partialInfo'] = getPartialInfo(json_data)
-
-    with open('mach_build_properties.json', 'w') as outfile:
-        json.dump(json_data, outfile, indent=4)
--- a/build/moz-automation.mk
+++ b/build/moz-automation.mk
@@ -12,21 +12,16 @@ endif
 endif
 
 include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
 include $(topsrcdir)/toolkit/mozapps/installer/upload-files.mk
 
 # Clear out DIST_FILES if it was set by upload-files.mk (for Android builds)
 DIST_FILES =
 
-ifeq (1,$(MOZ_AUTOMATION_UPLOAD))
-# Properties from 'make upload' that are file URLs.
-AUTOMATION_UPLOAD_PROPERTIES = --upload-properties $(DIST)/upload-properties.json
-endif
-
 # Helper variables to convert from MOZ_AUTOMATION_* variables to the
 # corresponding the make target
 tier_MOZ_AUTOMATION_BUILD_SYMBOLS = buildsymbols
 tier_MOZ_AUTOMATION_L10N_CHECK = l10n-check
 tier_MOZ_AUTOMATION_PRETTY_L10N_CHECK = pretty-l10n-check
 tier_MOZ_AUTOMATION_INSTALLER = installer
 tier_MOZ_AUTOMATION_PRETTY_INSTALLER = pretty-installer
 tier_MOZ_AUTOMATION_PACKAGE = package
@@ -95,17 +90,17 @@ automation/sdk: automation/installer aut
 # conflicts in writing to the same files.
 automation/installer: automation/pretty-installer
 automation/package: automation/pretty-package
 automation/package-tests: automation/pretty-package-tests
 automation/l10n-check: automation/pretty-l10n-check
 automation/update-packaging: automation/pretty-update-packaging
 
 automation/build: $(addprefix automation/,$(MOZ_AUTOMATION_TIERS))
-	$(PYTHON) $(topsrcdir)/build/gen_mach_buildprops.py --complete-mar-file $(DIST)/$(COMPLETE_MAR) $(addprefix --partial-mar-file ,$(wildcard $(DIST)/$(PARTIAL_MAR))) $(AUTOMATION_UPLOAD_PROPERTIES)
+	@echo Automation steps completed.
 
 # Note: We have to force -j1 here, at least until bug 1036563 is fixed.
 AUTOMATION_EXTRA_CMDLINE-l10n-check = -j1
 AUTOMATION_EXTRA_CMDLINE-pretty-l10n-check = -j1
 
 # And force -j1 here until bug 1077670 is fixed.
 AUTOMATION_EXTRA_CMDLINE-package-tests = -j1
 AUTOMATION_EXTRA_CMDLINE-pretty-package-tests = -j1
--- a/build/upload.py
+++ b/build/upload.py
@@ -3,45 +3,44 @@
 # 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/.
 #
 # When run directly, this script expects the following environment variables
 # to be set:
 # UPLOAD_HOST    : host to upload files to
 # UPLOAD_USER    : username on that host
+#  and one of the following:
 # UPLOAD_PATH    : path on that host to put the files in
+# UPLOAD_TO_TEMP : upload files to a new temporary directory
+#
+# If UPLOAD_HOST and UPLOAD_USER are not set, this script will simply write out
+# the properties file.
 #
 # And will use the following optional environment variables if set:
 # UPLOAD_SSH_KEY : path to a ssh private key to use
 # UPLOAD_PORT    : port to use for ssh
 # POST_UPLOAD_CMD: a commandline to run on the remote host after uploading.
 #                  UPLOAD_PATH and the full paths of all files uploaded will
 #                  be appended to the commandline.
 #
 # All files to be uploaded should be passed as commandline arguments to this
 # script. The script takes one other parameter, --base-path, which you can use
 # to indicate that files should be uploaded including their paths relative
 # to the base path.
 
 import sys, os
 import re
 import json
+import errno
+import hashlib
 from optparse import OptionParser
 from subprocess import check_call, check_output, STDOUT
 import redo
 
-def RequireEnvironmentVariable(v):
-    """Return the value of the environment variable named v, or print
-    an error and exit if it's unset (or empty)."""
-    if not v in os.environ or os.environ[v] == "":
-        print "Error: required environment variable %s not set" % v
-        sys.exit(1)
-    return os.environ[v]
-
 def OptionalEnvironmentVariable(v):
     """Return the value of the environment variable named v, or None
     if it's unset (or empty)."""
     if v in os.environ and os.environ[v] != "":
         return os.environ[v]
     return None
 
 def FixupMsysPath(path):
@@ -114,16 +113,43 @@ def GetRemotePath(path, local_file, base
     the relative path from base_path to file."""
     if base_path is None or not local_file.startswith(base_path):
         return path
     dir = os.path.dirname(local_file)
     # strip base_path + extra slash and make it unixy
     dir = dir[len(base_path)+1:].replace('\\','/')
     return path + dir
 
+def GetFileHashAndSize(filename):
+    sha512Hash = 'UNKNOWN'
+    size = 'UNKNOWN'
+
+    try:
+        # open in binary mode to make sure we get consistent results
+        # across all platforms
+        with open(filename, "rb") as f:
+            shaObj = hashlib.sha512(f.read())
+            sha512Hash = shaObj.hexdigest()
+
+        size = os.path.getsize(filename)
+    except:
+        raise Exception("Unable to get filesize/hash from file: %s" % filename)
+
+    return (sha512Hash, size)
+
+def GetMarProperties(filename):
+    if not os.path.exists(filename):
+        return {}
+    (mar_hash, mar_size) = GetFileHashAndSize(filename)
+    return {
+        'completeMarFilename': os.path.basename(filename),
+        'completeMarSize': mar_size,
+        'completeMarHash': mar_hash,
+    }
+
 def GetUrlProperties(output, package):
     # let's create a switch case using name-spaces/dict
     # rather than a long if/else with duplicate code
     property_conditions = [
         # key: property name, value: condition
         ('symbolsUrl', lambda m: m.endswith('crashreporter-symbols.zip') or
                        m.endswith('crashreporter-symbols-full.zip')),
         ('testsUrl', lambda m: m.endswith(('tests.tar.bz2', 'tests.zip'))),
@@ -151,34 +177,42 @@ def GetUrlProperties(output, package):
                         properties.update({prop: m})
                         break
     except IOError as e:
         if e.errno != errno.ENOENT:
             raise
         properties = {prop: 'UNKNOWN' for prop, condition in property_conditions}
     return properties
 
-def UploadFiles(user, host, path, files, verbose=False, port=None, ssh_key=None, base_path=None, upload_to_temp_dir=False, post_upload_command=None, properties_file=None, package=None):
+def UploadFiles(user, host, path, files, verbose=False, port=None, ssh_key=None, base_path=None, upload_to_temp_dir=False, post_upload_command=None, package=None):
     """Upload each file in the list files to user@host:path. Optionally pass
     port and ssh_key to the ssh commands. If base_path is not None, upload
     files including their path relative to base_path. If upload_to_temp_dir is
     True files will be uploaded to a temporary directory on the remote server.
     Generally, you should have a post upload command specified in these cases
     that can move them around to their correct location(s).
     If post_upload_command is not None, execute that command on the remote host
     after uploading all files, passing it the upload path, and the full paths to
     all files uploaded.
     If verbose is True, print status updates while working."""
+    if not host or not user:
+        return {}
+    if (not path and not upload_to_temp_dir) or (path and upload_to_temp_dir):
+        print "One (and only one of UPLOAD_PATH or UPLOAD_TO_TEMP must be " + \
+                "defined."
+        sys.exit(1)
+
     if upload_to_temp_dir:
         path = DoSSHCommand("mktemp -d", user, host, port=port, ssh_key=ssh_key)
     if not path.endswith("/"):
         path += "/"
     if base_path is not None:
         base_path = os.path.abspath(base_path)
     remote_files = []
+    properties = {}
     try:
         for file in files:
             file = os.path.abspath(file)
             if not os.path.isfile(file):
                 raise IOError("File not found: %s" % file)
             # first ensure that path exists remotely
             remote_path = GetRemotePath(path, file, base_path)
             DoSSHCommand("mkdir -p " + remote_path, user, host, port=port, ssh_key=ssh_key)
@@ -188,43 +222,46 @@ def UploadFiles(user, host, path, files,
             remote_files.append(remote_path + '/' + os.path.basename(file))
         if post_upload_command is not None:
             if verbose:
                 print "Running post-upload command: " + post_upload_command
             file_list = '"' + '" "'.join(remote_files) + '"'
             output = DoSSHCommand('%s "%s" %s' % (post_upload_command, path, file_list), user, host, port=port, ssh_key=ssh_key)
             # We print since mozharness may parse URLs from the output stream.
             print output
-            if properties_file:
-                with open(properties_file, 'w') as outfile:
-                    properties = GetUrlProperties(output, package)
-                    properties['packageFilename'] = package
-                    properties['uploadFiles'] = [os.path.abspath(f) for f in files]
-                    json.dump(properties, outfile, indent=4)
+            properties = GetUrlProperties(output, package)
     finally:
         if upload_to_temp_dir:
             DoSSHCommand("rm -rf %s" % path, user, host, port=port,
                          ssh_key=ssh_key)
     if verbose:
         print "Upload complete"
+    return properties
+
+def WriteProperties(files, properties_file, url_properties, package):
+    properties = url_properties
+    for file in files:
+        if file.endswith('.complete.mar'):
+            properties.update(GetMarProperties(file))
+    with open(properties_file, 'w') as outfile:
+        properties['packageFilename'] = package
+        properties['uploadFiles'] = [os.path.abspath(f) for f in files]
+        json.dump(properties, outfile, indent=4)
 
 if __name__ == '__main__':
-    host = RequireEnvironmentVariable('UPLOAD_HOST')
-    user = RequireEnvironmentVariable('UPLOAD_USER')
+    host = OptionalEnvironmentVariable('UPLOAD_HOST')
+    user = OptionalEnvironmentVariable('UPLOAD_USER')
     path = OptionalEnvironmentVariable('UPLOAD_PATH')
     upload_to_temp_dir = OptionalEnvironmentVariable('UPLOAD_TO_TEMP')
     port = OptionalEnvironmentVariable('UPLOAD_PORT')
     if port is not None:
         port = int(port)
     key = OptionalEnvironmentVariable('UPLOAD_SSH_KEY')
     post_upload_command = OptionalEnvironmentVariable('POST_UPLOAD_CMD')
-    if (not path and not upload_to_temp_dir) or (path and upload_to_temp_dir):
-        print "One (and only one of UPLOAD_PATH or UPLOAD_TO_TEMP must be " + \
-              "defined."
-        sys.exit(1)
+
     if sys.platform == 'win32':
         if path is not None:
             path = FixupMsysPath(path)
         if post_upload_command is not None:
             post_upload_command = FixupMsysPath(post_upload_command)
 
     parser = OptionParser(usage="usage: %prog [options] <files>")
     parser.add_option("-b", "--base-path",
@@ -235,20 +272,24 @@ if __name__ == '__main__':
                       help="Path to the properties file to store the upload properties.")
     parser.add_option("--package",
                       action="store",
                       help="Name of the main package.")
     (options, args) = parser.parse_args()
     if len(args) < 1:
         print "You must specify at least one file to upload"
         sys.exit(1)
+    if not options.properties_file:
+        print "You must specify a --properties-file"
+        sys.exit(1)
     try:
-        UploadFiles(user, host, path, args, base_path=options.base_path,
-                    port=port, ssh_key=key, upload_to_temp_dir=upload_to_temp_dir,
-                    post_upload_command=post_upload_command,
-                    properties_file=options.properties_file, package=options.package,
-                    verbose=True)
+        url_properties = UploadFiles(user, host, path, args, base_path=options.base_path,
+                                     port=port, ssh_key=key, upload_to_temp_dir=upload_to_temp_dir,
+                                     post_upload_command=post_upload_command,
+                                     package=options.package,
+                                     verbose=True)
+        WriteProperties(args, options.properties_file, url_properties, options.package)
     except IOError, (strerror):
         print strerror
         sys.exit(1)
     except Exception, (err):
         print err
         sys.exit(2)
--- a/configure.in
+++ b/configure.in
@@ -1590,16 +1590,17 @@ if test "$GNU_CXX"; then
     # -Wempty-body - catches bugs, e.g. "if (c); foo();", few false positives
     # -Wendif-labels - catches `#else FOO` and `#endif FOO` not in comment
     # -Wint-to-pointer-cast - catches cast to pointer from integer of different size
     # -Wmissing-braces - catches aggregate initializers missing nested braces
     # -Wnon-literal-null-conversion - catches expressions used as a null pointer constant
     # -Woverloaded-virtual - function declaration hides virtual function from base class
     # -Wparentheses - catches `if (a=b)` and operator precedence bugs
     # -Wpointer-arith - catches pointer arithmetic using NULL or sizeof(void)
+    # -Wrange-loop-analysis - catches copies during range-based for loops.
     # -Wreturn-type - catches missing returns, zero false positives
     # -Wsequence-point - catches undefined order behavior like `a = a++`
     # -Wsign-compare - catches comparison of signed and unsigned types
     # -Wswitch - catches switches without all enum cases or default case
     # -Wtrigraphs - catches unlikely use of trigraphs
     # -Wtype-limits - catches overflow bugs, few false positives
     # -Wunused-label - catches unused goto labels
     # -Wwrite-strings - catches non-const char* pointers to string literals
@@ -1621,16 +1622,17 @@ if test "$GNU_CXX"; then
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=sequence-point"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=switch"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=trigraphs"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=type-limits"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=uninitialized"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unused-label"
 
         MOZ_CXX_SUPPORTS_WARNING(-Werror=, non-literal-null-conversion, ac_cxx_has_werror_non_literal_null_conversion)
+        MOZ_CXX_SUPPORTS_WARNING(-Werror=, range-loop-analysis, ac_cxx_has_range_loop_analysis)
         MOZ_CXX_SUPPORTS_WARNING(-Werror=, sometimes-uninitialized, ac_cxx_has_sometimes_uninitialized)
     fi
 
     # Turn off the following warnings that -Wall turns on:
     # -Wno-invalid-offsetof - we use offsetof on non-POD types frequently
     # -Wno-inline-new-delete - we inline 'new' and 'delete' in mozalloc
     # -Wno-unused-local-typedef - catches unused typedefs, which are commonly used in assertion macros
     #   for performance reasons, and because GCC and clang accept it (though
--- a/dom/base/ChildIterator.cpp
+++ b/dom/base/ChildIterator.cpp
@@ -185,16 +185,40 @@ FlattenedChildIterator::Init(bool aIgnor
         MOZ_ASSERT(child->GetBindingParent());
         mXBLInvolved = true;
         break;
       }
     }
   }
 }
 
+void
+ExplicitChildIterator::Seek(nsIContent* aChildToFind)
+{
+  if (aChildToFind->GetParent() == mParent &&
+      !aChildToFind->IsRootOfAnonymousSubtree()) {
+    // Fast path: just point ourselves to aChildToFind, which is a
+    // normal DOM child of ours.
+    MOZ_ASSERT(!ShadowRoot::IsShadowInsertionPoint(aChildToFind));
+    MOZ_ASSERT(!nsContentUtils::IsContentInsertionPoint(aChildToFind));
+    mChild = aChildToFind;
+    mIndexInInserted = 0;
+    mShadowIterator = nullptr;
+    mDefaultChild = nullptr;
+    mIsFirst = false;
+    return;
+  }
+
+  // Can we add more fast paths here based on whether the parent of aChildToFind
+  // is a shadow insertion point or content insertion point?
+
+  // Slow path: just walk all our kids.
+  Seek(aChildToFind, nullptr);
+}
+
 nsIContent*
 ExplicitChildIterator::Get()
 {
   MOZ_ASSERT(!mIsFirst);
 
   if (mIndexInInserted) {
     MatchedNodes assignedChildren = GetMatchedNodesForPoint(mChild);
     return assignedChildren[mIndexInInserted - 1];
--- a/dom/base/ChildIterator.h
+++ b/dom/base/ChildIterator.h
@@ -55,26 +55,34 @@ public:
   ExplicitChildIterator(ExplicitChildIterator&& aOther)
     : mParent(aOther.mParent), mChild(aOther.mChild),
       mDefaultChild(aOther.mDefaultChild),
       mShadowIterator(Move(aOther.mShadowIterator)),
       mIndexInInserted(aOther.mIndexInInserted), mIsFirst(aOther.mIsFirst) {}
 
   nsIContent* GetNextChild();
 
-  // Looks for aChildToFind respecting insertion points until aChildToFind
+  // Looks for aChildToFind respecting insertion points until aChildToFind is
+  // found.  This version can take shortcuts that the two-argument version
+  // can't, so can be faster (and in fact can be O(1) instead of O(N) in many
+  // cases).
+  void Seek(nsIContent* aChildToFind);
+
+  // Looks for aChildToFind respecting insertion points until aChildToFind is found.
   // or aBound is found. If aBound is nullptr then the seek is unbounded. Returns
   // whether aChildToFind was found as an explicit child prior to encountering
   // aBound.
-  bool Seek(nsIContent* aChildToFind, nsIContent* aBound = nullptr)
+  bool Seek(nsIContent* aChildToFind, nsIContent* aBound)
   {
     // It would be nice to assert that we find aChildToFind, but bz thinks that
     // we might not find aChildToFind when called from ContentInserted
     // if first-letter frames are about.
 
+    // We can't easily take shortcuts here because we'd have to have a way to
+    // compare aChildToFind to aBound.
     nsIContent* child;
     do {
       child = GetNextChild();
     } while (child && child != aChildToFind && child != aBound);
 
     return child == aChildToFind;
   }
 
--- a/dom/base/ShadowRoot.cpp
+++ b/dom/base/ShadowRoot.cpp
@@ -311,17 +311,17 @@ ShadowRoot::DistributeSingleNode(nsICont
         isIndexFound = true;
         break;
       }
     }
 
     if (!isIndexFound) {
       // We have still not found an index in the insertion point,
       // thus it must be at the end.
-      MOZ_ASSERT(childIterator.Seek(aContent),
+      MOZ_ASSERT(childIterator.Seek(aContent, nullptr),
                  "Trying to match a node that is not a candidate to be matched");
       insertionPoint->AppendMatchedNode(aContent);
     }
 
     // Handle the case where the parent of the insertion point is a ShadowRoot
     // that is projected into the younger ShadowRoot's shadow insertion point.
     // The node distributed into the insertion point must be reprojected
     // to the shadow insertion point.
--- a/dom/base/UseCounters.conf
+++ b/dom/base/UseCounters.conf
@@ -37,8 +37,12 @@
 // dependencies were correct would have been rather difficult, and
 // annotating the WebIDL files does nothing for identifying CSS
 // property usage, which we would also like to track.
 
 method SVGSVGElement.getElementById
 attribute SVGSVGElement.currentScale
 property Fill
 property FillOpacity
+
+// Push API
+method PushManager.subscribe
+method PushSubscription.unsubscribe
--- a/dom/base/nsCopySupport.cpp
+++ b/dom/base/nsCopySupport.cpp
@@ -626,18 +626,18 @@ nsCopySupport::FireClipboardEvent(EventM
                                   nsIPresShell* aPresShell,
                                   nsISelection* aSelection,
                                   bool* aActionTaken)
 {
   if (aActionTaken) {
     *aActionTaken = false;
   }
 
-  NS_ASSERTION(aEventMessage == NS_CUT || aEventMessage == NS_COPY ||
-               aEventMessage == NS_PASTE,
+  NS_ASSERTION(aEventMessage == eCut || aEventMessage == eCopy ||
+               aEventMessage == ePaste,
                "Invalid clipboard event type");
 
   nsCOMPtr<nsIPresShell> presShell = aPresShell;
   if (!presShell)
     return false;
 
   nsCOMPtr<nsIDocument> doc = presShell->GetDocument();
   if (!doc)
@@ -683,32 +683,32 @@ nsCopySupport::FireClipboardEvent(EventM
   const bool chromeShell =
     docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome;
 
   // next, fire the cut, copy or paste event
   bool doDefault = true;
   nsRefPtr<DataTransfer> clipboardData;
   if (chromeShell || Preferences::GetBool("dom.event.clipboardevents.enabled", true)) {
     clipboardData =
-      new DataTransfer(piWindow, aEventMessage, aEventMessage == NS_PASTE,
+      new DataTransfer(piWindow, aEventMessage, aEventMessage == ePaste,
                        aClipboardType);
 
     nsEventStatus status = nsEventStatus_eIgnore;
     InternalClipboardEvent evt(true, aEventMessage);
     evt.clipboardData = clipboardData;
     EventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt,
                               nullptr, &status);
     // If the event was cancelled, don't do the clipboard operation
     doDefault = (status != nsEventStatus_eConsumeNoDefault);
   }
 
   // No need to do anything special during a paste. Either an event listener
   // took care of it and cancelled the event, or the caller will handle it.
   // Return true to indicate that the event wasn't cancelled.
-  if (aEventMessage == NS_PASTE) {
+  if (aEventMessage == ePaste) {
     // Clear and mark the clipboardData as readonly. This prevents someone
     // from reading the clipboard contents after the paste event has fired.
     if (clipboardData) {
       clipboardData->ClearAll();
       clipboardData->SetReadOnly();
     }
 
     if (aActionTaken) {
@@ -738,17 +738,17 @@ nsCopySupport::FireClipboardEvent(EventM
     if (formControl) {
       if (formControl->GetType() == NS_FORM_INPUT_PASSWORD) {
         return false;
       }
     }
 
     // when cutting non-editable content, do nothing
     // XXX this is probably the wrong editable flag to check
-    if (aEventMessage != NS_CUT || content->IsEditable()) {
+    if (aEventMessage != eCut || content->IsEditable()) {
       // get the data from the selection if any
       bool isCollapsed;
       sel->GetIsCollapsed(&isCollapsed);
       if (isCollapsed) {
         if (aActionTaken) {
           *aActionTaken = true;
         }
         return false;
--- a/dom/base/nsCopySupport.h
+++ b/dom/base/nsCopySupport.h
@@ -59,18 +59,18 @@ class nsCopySupport
     /**
      * Returns true if a copy operation is currently permitted based on the
      * current focus and selection within the specified document.
      */
     static bool CanCopy(nsIDocument* aDocument);
 
     /**
      * Fires a cut, copy or paste event, on the given presshell, depending
-     * on the value of aEventMessage, which should be either NS_CUT, NS_COPY or
-     * NS_PASTE, and perform the default copy action if the event was not
+     * on the value of aEventMessage, which should be either eCut, eCopy or
+     * ePaste, and perform the default copy action if the event was not
      * cancelled.
      *
      * If aSelection is specified, then this selection is used as the target
      * of the operation. Otherwise, GetSelectionForCopy is used to retrieve
      * the current selection.
      *
      * This will fire a cut, copy or paste event at the node at the start
      * point of the selection. If a cut or copy event is not cancelled, the
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -2021,17 +2021,17 @@ nsDOMWindowUtils::SendSelectionSetEvent(
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
   // get the widget to send the event to
   nsCOMPtr<nsIWidget> widget = GetWidget();
   if (!widget) {
     return NS_ERROR_FAILURE;
   }
 
-  WidgetSelectionEvent selectionEvent(true, NS_SELECTION_SET, widget);
+  WidgetSelectionEvent selectionEvent(true, eSetSelection, widget);
   InitEvent(selectionEvent);
 
   selectionEvent.mOffset = aOffset;
   selectionEvent.mLength = aLength;
   selectionEvent.mReversed = (aAdditionalFlags & SELECTION_SET_FLAG_REVERSE);
   selectionEvent.mUseNativeLineBreak =
     !(aAdditionalFlags & SELECTION_SET_FLAG_USE_XP_LINE_BREAK);
 
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -8588,19 +8588,16 @@ nsDocument::RetrieveRelevantHeaders(nsIC
       if (st == PR_SUCCESS) {
         modDate = time;
       }
     }
 
     // The misspelled key 'referer' is as per the HTTP spec
     rv = httpChannel->GetRequestHeader(NS_LITERAL_CSTRING("referer"),
                                        mReferrer);
-    if (NS_FAILED(rv)) {
-      mReferrer.Truncate();
-    }
 
     static const char *const headers[] = {
       "default-style",
       "content-style-type",
       "content-language",
       "content-disposition",
       "refresh",
       "x-dns-prefetch-control",
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6913,17 +6913,19 @@ nsGlobalWindow::AlertOrConfirm(bool aAle
                                const nsAString& aMessage,
                                mozilla::ErrorResult& aError)
 {
   // XXX This method is very similar to nsGlobalWindow::Prompt, make
   // sure any modifications here don't need to happen over there!
   MOZ_ASSERT(IsOuterWindow());
 
   if (!AreDialogsEnabled()) {
-    aError.Throw(NS_ERROR_NOT_AVAILABLE);
+    // Just silently return.  In the case of alert(), the return value is
+    // ignored.  In the case of confirm(), returning false is the same thing as
+    // would happen if the user cancels.
     return false;
   }
 
   // Reset popup state while opening a modal dialog, and firing events
   // about the dialog, to prevent the current state from being active
   // the whole time a modal dialog is open.
   nsAutoPopupStatePusher popupStatePusher(openAbused, true);
 
@@ -7053,17 +7055,17 @@ nsGlobalWindow::PromptOuter(const nsAStr
 {
   // XXX This method is very similar to nsGlobalWindow::AlertOrConfirm, make
   // sure any modifications here don't need to happen over there!
   MOZ_RELEASE_ASSERT(IsOuterWindow());
 
   SetDOMStringToNull(aReturn);
 
   if (!AreDialogsEnabled()) {
-    aError.Throw(NS_ERROR_NOT_AVAILABLE);
+    // Return null, as if the user just canceled the prompt.
     return;
   }
 
   // Reset popup state while opening a modal dialog, and firing events
   // about the dialog, to prevent the current state from being active
   // the whole time a modal dialog is open.
   nsAutoPopupStatePusher popupStatePusher(openAbused, true);
 
@@ -7491,16 +7493,18 @@ nsGlobalWindow::PrintOuter(ErrorResult& 
 
 #ifdef NS_PRINTING
   if (Preferences::GetBool("dom.disable_window_print", false)) {
     aError.Throw(NS_ERROR_NOT_AVAILABLE);
     return;
   }
 
   if (!AreDialogsEnabled()) {
+    // We probably want to keep throwing here; silently doing nothing is a bit
+    // weird given the typical use cases of print().
     aError.Throw(NS_ERROR_NOT_AVAILABLE);
     return;
   }
 
   if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) {
     aError.Throw(NS_ERROR_NOT_AVAILABLE);
     return;
   }
@@ -9548,16 +9552,18 @@ nsGlobalWindow::ShowModalDialogOuter(con
   nsRefPtr<DialogValueHolder> argHolder =
     new DialogValueHolder(nsContentUtils::SubjectPrincipal(), aArgument);
 
   // Before bringing up the window/dialog, unsuppress painting and flush
   // pending reflows.
   EnsureReflowFlushAndPaint();
 
   if (!AreDialogsEnabled()) {
+    // We probably want to keep throwing here; silently doing nothing is a bit
+    // weird given the typical use cases of showModalDialog().
     aError.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
 
   if (ShouldPromptToBlockDialogs() && !ConfirmDialogIfNeeded()) {
     aError.Throw(NS_ERROR_NOT_AVAILABLE);
     return nullptr;
   }
--- a/dom/base/nsGlobalWindowCommands.cpp
+++ b/dom/base/nsGlobalWindowCommands.cpp
@@ -515,19 +515,19 @@ nsClipboardCommand::DoCommand(const char
   NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
 
   nsIDocShell *docShell = window->GetDocShell();
   NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell();
   NS_ENSURE_TRUE(presShell, NS_ERROR_FAILURE);
 
-  EventMessage eventMessage = NS_COPY;
+  EventMessage eventMessage = eCopy;
   if (strcmp(aCommandName, "cmd_cut") == 0) {
-    eventMessage = NS_CUT;
+    eventMessage = eCut;
   }
 
   bool actionTaken = false;
   nsCopySupport::FireClipboardEvent(eventMessage,
                                     nsIClipboard::kGlobalClipboard,
                                     presShell, nullptr, &actionTaken);
 
   if (!strcmp(aCommandName, "cmd_copyAndCollapseToEnd")) {
--- a/dom/events/ClipboardEvent.cpp
+++ b/dom/events/ClipboardEvent.cpp
@@ -74,17 +74,17 @@ ClipboardEvent::Constructor(const Global
 
   nsRefPtr<DataTransfer> clipboardData;
   if (e->mEventIsInternal) {
     InternalClipboardEvent* event = e->mEvent->AsClipboardEvent();
     if (event) {
       // Always create a clipboardData for the copy event. If this is changed to
       // support other types of events, make sure that read/write privileges are
       // checked properly within DataTransfer.
-      clipboardData = new DataTransfer(ToSupports(e), NS_COPY, false, -1);
+      clipboardData = new DataTransfer(ToSupports(e), eCopy, false, -1);
       clipboardData->SetData(aParam.mDataType, aParam.mData);
     }
   }
 
   e->InitClipboardEvent(aType, aParam.mBubbles, aParam.mCancelable,
                         clipboardData, aRv);
   e->SetTrusted(trusted);
   return e.forget();
@@ -100,21 +100,21 @@ ClipboardEvent::GetClipboardData(nsIDOMD
 DataTransfer*
 ClipboardEvent::GetClipboardData()
 {
   InternalClipboardEvent* event = mEvent->AsClipboardEvent();
 
   if (!event->clipboardData) {
     if (mEventIsInternal) {
       event->clipboardData =
-        new DataTransfer(ToSupports(this), NS_COPY, false, -1);
+        new DataTransfer(ToSupports(this), eCopy, false, -1);
     } else {
       event->clipboardData =
         new DataTransfer(ToSupports(this), event->mMessage,
-                         event->mMessage == NS_PASTE,
+                         event->mMessage == ePaste,
                          nsIClipboard::kGlobalClipboard);
     }
   }
 
   return event->clipboardData;
 }
 
 } // namespace dom
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -81,23 +81,23 @@ DataTransfer::DataTransfer(nsISupports* 
   , mIsCrossDomainSubFrameDrop(false)
   , mClipboardType(aClipboardType)
   , mDragImageX(0)
   , mDragImageY(0)
 {
   // For these events, we want to be able to add data to the data transfer, so
   // clear the readonly state. Otherwise, the data is already present. For
   // external usage, cache the data from the native clipboard or drag.
-  if (aEventMessage == NS_CUT ||
-      aEventMessage == NS_COPY ||
+  if (aEventMessage == eCut ||
+      aEventMessage == eCopy ||
       aEventMessage == eDragStart ||
       aEventMessage == eLegacyDragGesture) {
     mReadOnly = false;
   } else if (mIsExternal) {
-    if (aEventMessage == NS_PASTE) {
+    if (aEventMessage == ePaste) {
       CacheExternalClipboardFormats();
     } else if (aEventMessage >= eDragDropEventFirst &&
                aEventMessage <= eDragDropEventLast) {
       CacheExternalDragFormats();
     }
   }
 }
 
@@ -266,17 +266,17 @@ DataTransfer::GetMozUserCancelled(bool* 
   return NS_OK;
 }
 
 FileList*
 DataTransfer::GetFiles(ErrorResult& aRv)
 {
   if (mEventMessage != eDrop &&
       mEventMessage != eLegacyDragDrop &&
-      mEventMessage != NS_PASTE) {
+      mEventMessage != ePaste) {
     return nullptr;
   }
 
   if (!mFiles) {
     mFiles = new FileList(static_cast<nsIDOMDataTransfer*>(this));
 
     uint32_t count = mItems.Length();
 
@@ -538,18 +538,18 @@ DataTransfer::GetMozSourceNode(nsIDOMNod
   return CallQueryInterface(sourceNode, aSourceNode);
 }
 
 already_AddRefed<DOMStringList>
 DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv)
 {
   // Only the first item is valid for clipboard events
   if (aIndex > 0 &&
-      (mEventMessage == NS_CUT || mEventMessage == NS_COPY ||
-       mEventMessage == NS_PASTE)) {
+      (mEventMessage == eCut || mEventMessage == eCopy ||
+       mEventMessage == ePaste)) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
   nsRefPtr<DOMStringList> types = new DOMStringList();
   if (aIndex < mItems.Length()) {
     // note that you can retrieve the types regardless of their principal
     nsTArray<TransferItem>& item = mItems[aIndex];
@@ -579,18 +579,18 @@ DataTransfer::MozGetDataAt(const nsAStri
     return NS_OK;
 
   if (aIndex >= mItems.Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Only the first item is valid for clipboard events
   if (aIndex > 0 &&
-      (mEventMessage == NS_CUT || mEventMessage == NS_COPY ||
-       mEventMessage == NS_PASTE)) {
+      (mEventMessage == eCut || mEventMessage == eCopy ||
+       mEventMessage == ePaste)) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
 
   nsAutoString format;
   GetRealFormat(aFormat, format);
 
   nsTArray<TransferItem>& item = mItems[aIndex];
@@ -599,17 +599,17 @@ DataTransfer::MozGetDataAt(const nsAStri
   // chrome privileges can always read the data. During the
   // drop event, allow retrieving the data except in the case where the
   // source of the drag is in a child frame of the caller. In that case,
   // we only allow access to data of the same principal. During other events,
   // only allow access to the data with the same principal.
   nsIPrincipal* principal = nullptr;
   if (mIsCrossDomainSubFrameDrop ||
       (mEventMessage != eDrop && mEventMessage != eLegacyDragDrop &&
-       mEventMessage != NS_PASTE &&
+       mEventMessage != ePaste &&
        !nsContentUtils::IsCallerChrome())) {
     principal = nsContentUtils::SubjectPrincipal();
   }
 
   uint32_t count = item.Length();
   for (uint32_t i = 0; i < count; i++) {
     TransferItem& formatitem = item[i];
     if (formatitem.mFormat.Equals(format)) {
@@ -691,18 +691,18 @@ DataTransfer::MozSetDataAt(const nsAStri
   // Specifying an index less than the current length will replace an existing
   // item. Specifying an index equal to the current length will add a new item.
   if (aIndex > mItems.Length()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Only the first item is valid for clipboard events
   if (aIndex > 0 &&
-      (mEventMessage == NS_CUT || mEventMessage == NS_COPY ||
-       mEventMessage == NS_PASTE)) {
+      (mEventMessage == eCut || mEventMessage == eCopy ||
+       mEventMessage == ePaste)) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // don't allow non-chrome to add file data
   // XXX perhaps this should also limit any non-string type as well
   if ((aFormat.EqualsLiteral("application/x-moz-file-promise") ||
        aFormat.EqualsLiteral("application/x-moz-file")) &&
        !nsContentUtils::IsCallerChrome()) {
@@ -737,34 +737,34 @@ DataTransfer::MozClearDataAt(const nsASt
 
   if (aIndex >= mItems.Length()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   // Only the first item is valid for clipboard events
   if (aIndex > 0 &&
-      (mEventMessage == NS_CUT || mEventMessage == NS_COPY ||
-       mEventMessage == NS_PASTE)) {
+      (mEventMessage == eCut || mEventMessage == eCopy ||
+       mEventMessage == ePaste)) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   MozClearDataAtHelper(aFormat, aIndex, aRv);
 }
 
 void
 DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
                                    ErrorResult& aRv)
 {
   MOZ_ASSERT(!mReadOnly);
   MOZ_ASSERT(aIndex < mItems.Length());
   MOZ_ASSERT(aIndex == 0 ||
-             (mEventMessage != NS_CUT && mEventMessage != NS_COPY &&
-              mEventMessage != NS_PASTE));
+             (mEventMessage != eCut && mEventMessage != eCopy &&
+              mEventMessage != ePaste));
 
   nsAutoString format;
   GetRealFormat(aFormat, format);
 
   nsIPrincipal* principal = nsContentUtils::SubjectPrincipal();
 
   // if the format is empty, clear all formats
   bool clearall = format.IsEmpty();
@@ -1253,17 +1253,17 @@ DataTransfer::CacheExternalDragFormats()
       }
     }
   }
 }
 
 void
 DataTransfer::CacheExternalClipboardFormats()
 {
-  NS_ASSERTION(mEventMessage == NS_PASTE,
+  NS_ASSERTION(mEventMessage == ePaste,
                "caching clipboard data for invalid event");
 
   // Called during the constructor for paste events to cache the formats
   // available on the clipboard. As with CacheExternalDragFormats, the
   // data will only be retrieved when needed.
 
   nsCOMPtr<nsIClipboard> clipboard = do_GetService("@mozilla.org/widget/clipboard;1");
   if (!clipboard || mClipboardType < 0) {
@@ -1295,17 +1295,17 @@ DataTransfer::FillInExternalData(Transfe
 {
   NS_PRECONDITION(mIsExternal, "Not an external data transfer");
 
   if (aItem.mData) {
     return;
   }
 
   // only drag and paste events should be calling FillInExternalData
-  NS_ASSERTION(mEventMessage != NS_CUT && mEventMessage != NS_COPY,
+  NS_ASSERTION(mEventMessage != eCut && mEventMessage != eCopy,
                "clipboard event with empty data");
 
     NS_ConvertUTF16toUTF8 utf8format(aItem.mFormat);
     const char* format = utf8format.get();
     if (strcmp(format, "text/plain") == 0)
       format = kUnicodeMime;
     else if (strcmp(format, "text/uri-list") == 0)
       format = kURLDataMime;
@@ -1313,17 +1313,17 @@ DataTransfer::FillInExternalData(Transfe
     nsCOMPtr<nsITransferable> trans =
       do_CreateInstance("@mozilla.org/widget/transferable;1");
     if (!trans)
       return;
 
   trans->Init(nullptr);
   trans->AddDataFlavor(format);
 
-  if (mEventMessage == NS_PASTE) {
+  if (mEventMessage == ePaste) {
     MOZ_ASSERT(aIndex == 0, "index in clipboard must be 0");
 
     nsCOMPtr<nsIClipboard> clipboard = do_GetService("@mozilla.org/widget/clipboard;1");
     if (!clipboard || mClipboardType < 0) {
       return;
     }
 
     clipboard->GetData(trans, mClipboardType);
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -148,21 +148,21 @@
 #define DEFINED_BEFOREUNLOAD_EVENT
 #endif /* BEFOREUNLOAD_EVENT */
 
 EVENT(abort,
       eImageAbort,
       EventNameType_All,
       eBasicEventClass)
 EVENT(canplay,
-      NS_CANPLAY,
+      eCanPlay,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(canplaythrough,
-      NS_CANPLAYTHROUGH,
+      eCanPlayThrough,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(change,
       eFormChange,
       EventNameType_HTMLXUL,
       eBasicEventClass)
 EVENT(click,
       eMouseClick,
@@ -202,25 +202,25 @@ EVENT(dragstart,
       eDragStart,
       EventNameType_HTMLXUL,
       eDragEventClass)
 EVENT(drop,
       eDrop,
       EventNameType_HTMLXUL,
       eDragEventClass)
 EVENT(durationchange,
-      NS_DURATIONCHANGE,
+      eDurationChange,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(emptied,
-      NS_EMPTIED,
+      eEmptied,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(ended,
-      NS_ENDED,
+      eEnded,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(input,
       NS_EDITOR_INPUT,
       EventNameType_HTMLXUL,
       eEditorInputEventClass)
 EVENT(invalid,
       eFormInvalid,
@@ -250,25 +250,25 @@ NON_IDL_EVENT(mozbrowserbeforekeyup,
               eBeforeKeyUp,
               EventNameType_None,
               eBeforeAfterKeyboardEventClass)
 NON_IDL_EVENT(mozbrowserafterkeyup,
               eAfterKeyUp,
               EventNameType_None,
               eBeforeAfterKeyboardEventClass)
 EVENT(loadeddata,
-      NS_LOADEDDATA,
+      eLoadedData,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(loadedmetadata,
-      NS_LOADEDMETADATA,
+      eLoadedMetaData,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(loadstart,
-      NS_LOADSTART,
+      eLoadStart,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(mousedown,
       eMouseDown,
       EventNameType_All,
       eMouseEventClass)
 EVENT(mouseenter,
       eMouseEnter,
@@ -349,93 +349,93 @@ EVENT(gotpointercapture,
 EVENT(lostpointercapture,
       ePointerLostCapture,
       EventNameType_All,
       ePointerEventClass)
 
 // Not supported yet; probably never because "wheel" is a better idea.
 // EVENT(mousewheel)
 EVENT(pause,
-      NS_PAUSE,
+      ePause,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(play,
-      NS_PLAY,
+      ePlay,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(playing,
-      NS_PLAYING,
+      ePlaying,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(progress,
-      NS_PROGRESS,
+      eProgress,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(ratechange,
-      NS_RATECHANGE,
+      eRateChange,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(reset,
       eFormReset,
       EventNameType_HTMLXUL,
       eBasicEventClass)
 EVENT(seeked,
-      NS_SEEKED,
+      eSeeked,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(seeking,
-      NS_SEEKING,
+      eSeeking,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(select,
       eFormSelect,
       EventNameType_HTMLXUL,
       eBasicEventClass)
 EVENT(show,
       NS_SHOW_EVENT,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(stalled,
-      NS_STALLED,
+      eStalled,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(submit,
       eFormSubmit,
       EventNameType_HTMLXUL,
       eBasicEventClass)
 EVENT(suspend,
-      NS_SUSPEND,
+      eSuspend,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(timeupdate,
-      NS_TIMEUPDATE,
+      eTimeUpdate,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(volumechange,
-      NS_VOLUMECHANGE,
+      eVolumeChange,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(waiting,
-      NS_WAITING,
+      eWaiting,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(wheel,
       NS_WHEEL_WHEEL,
       EventNameType_All,
       eWheelEventClass)
 EVENT(copy,
-      NS_COPY,
+      eCopy,
       EventNameType_HTMLXUL,
       eClipboardEventClass)
 EVENT(cut,
-      NS_CUT,
+      eCut,
       EventNameType_HTMLXUL,
       eClipboardEventClass)
 EVENT(paste,
-      NS_PASTE,
+      ePaste,
       EventNameType_HTMLXUL,
       eClipboardEventClass)
 // Gecko-specific extensions that apply to elements
 EVENT(beforescriptexecute,
       NS_BEFORE_SCRIPT_EXECUTE,
       EventNameType_HTMLXUL,
       eBasicEventClass)
 EVENT(afterscriptexecute,
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -738,17 +738,17 @@ EventStateManager::PreHandleEvent(nsPres
       // Init lineOrPageDelta values for line scroll events for some devices
       // on some platforms which might dispatch wheel events which don't have
       // lineOrPageDelta values.  And also, if delta values are customized by
       // prefs, this recomputes them.
       DeltaAccumulator::GetInstance()->
         InitLineOrPageDelta(aTargetFrame, this, wheelEvent);
     }
     break;
-  case NS_SELECTION_SET:
+  case eSetSelection:
     IMEStateManager::HandleSelectionEvent(aPresContext, GetFocusedContent(),
                                           aEvent->AsSelectionEvent());
     break;
   case NS_CONTENT_COMMAND_CUT:
   case NS_CONTENT_COMMAND_COPY:
   case NS_CONTENT_COMMAND_PASTE:
   case NS_CONTENT_COMMAND_DELETE:
   case NS_CONTENT_COMMAND_UNDO:
--- a/dom/events/IMEStateManager.cpp
+++ b/dom/events/IMEStateManager.cpp
@@ -144,18 +144,18 @@ GetEventMessageName(EventMessage aMessag
     case NS_COMPOSITION_UPDATE:
       return "NS_COMPOSITION_UPDATE";
     case NS_COMPOSITION_CHANGE:
       return "NS_COMPOSITION_CHANGE";
     case NS_COMPOSITION_COMMIT_AS_IS:
       return "NS_COMPOSITION_COMMIT_AS_IS";
     case NS_COMPOSITION_COMMIT:
       return "NS_COMPOSITION_COMMIT";
-    case NS_SELECTION_SET:
-      return "NS_SELECTION_SET";
+    case eSetSelection:
+      return "eSetSelection";
     default:
       return "unacceptable event message";
   }
 }
 
 static const char*
 GetNotifyIMEMessageName(IMEMessage aMessage)
 {
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -286,24 +286,17 @@ MediaDecoderStateMachine::MediaDecoderSt
 
   mAudioQueueListener = AudioQueue().PopEvent().Connect(
     mTaskQueue, this, &MediaDecoderStateMachine::OnAudioPopped);
   mVideoQueueListener = VideoQueue().PopEvent().Connect(
     mTaskQueue, this, &MediaDecoderStateMachine::OnVideoPopped);
 
   mMetadataManager.Connect(mReader->TimedMetadataEvent(), OwnerThread());
 
-  nsRefPtr<MediaDecoderStateMachine> self = this;
-  auto audioSinkCreator = [self] () {
-    MOZ_ASSERT(self->OnTaskQueue());
-    return new DecodedAudioDataSink(
-      self->mAudioQueue, self->GetMediaTime(),
-      self->mInfo.mAudio, self->mDecoder->GetAudioChannel());
-  };
-  mAudioSink = new AudioSinkWrapper(mTaskQueue, audioSinkCreator);
+  mMediaSink = CreateAudioSink();
 }
 
 MediaDecoderStateMachine::~MediaDecoderStateMachine()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should be on main thread.");
   MOZ_COUNT_DTOR(MediaDecoderStateMachine);
 
   mReader = nullptr;
@@ -343,16 +336,29 @@ MediaDecoderStateMachine::Initialization
   mWatchManager.Watch(mPlayState, &MediaDecoderStateMachine::PlayStateChanged);
   mWatchManager.Watch(mLogicallySeeking, &MediaDecoderStateMachine::LogicallySeekingChanged);
   mWatchManager.Watch(mSameOriginMedia, &MediaDecoderStateMachine::SameOriginMediaChanged);
 
   // Propagate mSameOriginMedia to mDecodedStream.
   SameOriginMediaChanged();
 }
 
+media::MediaSink*
+MediaDecoderStateMachine::CreateAudioSink()
+{
+  nsRefPtr<MediaDecoderStateMachine> self = this;
+  auto audioSinkCreator = [self] () {
+    MOZ_ASSERT(self->OnTaskQueue());
+    return new DecodedAudioDataSink(
+      self->mAudioQueue, self->GetMediaTime(),
+      self->mInfo.mAudio, self->mDecoder->GetAudioChannel());
+  };
+  return new AudioSinkWrapper(mTaskQueue, audioSinkCreator);
+}
+
 bool MediaDecoderStateMachine::HasFutureAudio()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   NS_ASSERTION(HasAudio(), "Should only call HasFutureAudio() when we have audio");
   // We've got audio ready to play if:
   // 1. We've not completed playback of audio, and
   // 2. we either have more than the threshold of decoded audio available, or
@@ -372,27 +378,26 @@ bool MediaDecoderStateMachine::HaveNextF
          (!HasVideo() || VideoQueue().GetSize() > 1);
 }
 
 int64_t MediaDecoderStateMachine::GetDecodedAudioDuration()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   int64_t audioDecoded = AudioQueue().Duration();
-  if (mAudioSink->IsStarted()) {
+  if (mMediaSink->IsStarted()) {
     audioDecoded += AudioEndTime() - GetMediaTime();
   }
   return audioDecoded;
 }
 
 void MediaDecoderStateMachine::DiscardStreamData()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
-  MOZ_ASSERT(!mAudioSink->IsStarted(), "Should've been stopped in RunStateMachine()");
 
   const auto clockTime = GetClock();
   while (true) {
     const MediaData* a = AudioQueue().PeekFront();
 
     // If we discard audio samples fed to the stream immediately, we will
     // keep decoding audio samples till the end and consume a lot of memory.
     // Therefore we only discard those behind the stream clock to throttle
@@ -1072,18 +1077,17 @@ void MediaDecoderStateMachine::MaybeStar
   }
 
   DECODER_LOG("MaybeStartPlayback() starting playback");
 
   mDecoder->DispatchPlaybackStarted();
   SetPlayStartTime(TimeStamp::Now());
   MOZ_ASSERT(IsPlaying());
 
-  StartAudioSink();
-  StartDecodedStream();
+  StartMediaSink();
 
   DispatchDecodeTasksIfNeeded();
 }
 
 void MediaDecoderStateMachine::UpdatePlaybackPositionInternal(int64_t aTime)
 {
   MOZ_ASSERT(OnTaskQueue());
   SAMPLE_LOG("UpdatePlaybackPositionInternal(%lld)", aTime);
@@ -1147,18 +1151,17 @@ void MediaDecoderStateMachine::SetState(
   // Clear state-scoped state.
   mSentPlaybackEndedEvent = false;
 }
 
 void MediaDecoderStateMachine::VolumeChanged()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  mAudioSink->SetVolume(mVolume);
-  mStreamSink->SetVolume(mVolume);
+  mMediaSink->SetVolume(mVolume);
 }
 
 void MediaDecoderStateMachine::RecomputeDuration()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   TimeUnit duration;
@@ -1268,17 +1271,17 @@ void MediaDecoderStateMachine::Shutdown(
   mCurrentSeek.RejectIfExists(__func__);
 
   if (IsPlaying()) {
     StopPlayback();
   }
 
   Reset();
 
-  mAudioSink->Shutdown();
+  mMediaSink->Shutdown();
 
   // Shut down our start time rendezvous.
   if (mStartTimeRendezvous) {
     mStartTimeRendezvous->Destroy();
   }
 
   // Put a task in the decode queue to shutdown the reader.
   // the queue to spin down.
@@ -1461,25 +1464,25 @@ MediaDecoderStateMachine::Seek(SeekTarge
 
   DECODER_LOG("Changed state to SEEKING (to %lld)", mPendingSeek.mTarget.mTime);
   SetState(DECODER_STATE_SEEKING);
   ScheduleStateMachine();
 
   return mPendingSeek.mPromise.Ensure(__func__);
 }
 
-void MediaDecoderStateMachine::StopAudioSink()
+void MediaDecoderStateMachine::StopMediaSink()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
-  if (mAudioSink->IsStarted()) {
-    DECODER_LOG("Stop AudioSink");
-    mAudioSink->Stop();
-    mAudioSinkPromise.DisconnectIfExists();
+  if (mMediaSink->IsStarted()) {
+    DECODER_LOG("Stop MediaSink");
+    mMediaSink->Stop();
+    mMediaSinkPromise.DisconnectIfExists();
   }
 }
 
 void
 MediaDecoderStateMachine::DispatchDecodeTasksIfNeeded()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
@@ -1747,78 +1750,44 @@ MediaDecoderStateMachine::RequestVideoDa
       ->CompletionPromise()
       ->Then(OwnerThread(), __func__, this,
              &MediaDecoderStateMachine::OnVideoDecoded,
              &MediaDecoderStateMachine::OnVideoNotDecoded));
   }
 }
 
 void
-MediaDecoderStateMachine::StartAudioSink()
+MediaDecoderStateMachine::StartMediaSink()
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
-  if (mAudioCaptured) {
-    MOZ_ASSERT(!mAudioSink->IsStarted());
-    return;
-  }
-
-  if (!mAudioSink->IsStarted()) {
+
+  if (!mMediaSink->IsStarted()) {
     mAudioCompleted = false;
-    mAudioSink->Start(GetMediaTime(), mInfo);
-
-    auto promise = mAudioSink->OnEnded(TrackInfo::kAudioTrack);
+    mMediaSink->Start(GetMediaTime(), mInfo);
+
+    auto promise = mMediaSink->OnEnded(TrackInfo::kAudioTrack);
     if (promise) {
-      mAudioSinkPromise.Begin(promise->Then(
+      mMediaSinkPromise.Begin(promise->Then(
         OwnerThread(), __func__, this,
-        &MediaDecoderStateMachine::OnAudioSinkComplete,
-        &MediaDecoderStateMachine::OnAudioSinkError));
+        &MediaDecoderStateMachine::OnMediaSinkComplete,
+        &MediaDecoderStateMachine::OnMediaSinkError));
     }
   }
 }
 
-void
-MediaDecoderStateMachine::StopDecodedStream()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  AssertCurrentThreadInMonitor();
-
-  if (mStreamSink->IsStarted()) {
-    mStreamSink->Stop();
-    mDecodedStreamPromise.DisconnectIfExists();
-  }
-}
-
-void
-MediaDecoderStateMachine::StartDecodedStream()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  AssertCurrentThreadInMonitor();
-
-  // Tell DecodedStream to start playback with specified start time and media
-  // info. This is consistent with how we create AudioSink in StartAudioThread().
-  if (mAudioCaptured && !mStreamSink->IsStarted()) {
-    mStreamSink->Start(GetMediaTime(), mInfo);
-    mDecodedStreamPromise.Begin(
-      mStreamSink->OnEnded(TrackInfo::kAudioTrack)->Then(
-        OwnerThread(), __func__, this,
-        &MediaDecoderStateMachine::OnDecodedStreamFinish,
-        &MediaDecoderStateMachine::OnDecodedStreamError));
-  }
-}
-
 int64_t MediaDecoderStateMachine::AudioDecodedUsecs()
 {
   MOZ_ASSERT(OnTaskQueue());
   NS_ASSERTION(HasAudio(),
                "Should only call AudioDecodedUsecs() when we have audio");
   // The amount of audio we have decoded is the amount of audio data we've
   // already decoded and pushed to the hardware, plus the amount of audio
   // data waiting to be pushed to the hardware.
-  int64_t pushed = mAudioSink->IsStarted() ? (AudioEndTime() - GetMediaTime()) : 0;
+  int64_t pushed = mMediaSink->IsStarted() ? (AudioEndTime() - GetMediaTime()) : 0;
 
   // Currently for real time streams, AudioQueue().Duration() produce
   // wrong values (Bug 1114434), so we use frame counts to calculate duration.
   if (IsRealTime()) {
     return pushed + FramesToUsecs(AudioQueue().FrameCount(), mInfo.mAudio.mRate).value();
   }
   return pushed + AudioQueue().Duration();
 }
@@ -1837,17 +1806,17 @@ bool MediaDecoderStateMachine::HasLowDec
           static_cast<uint32_t>(VideoQueue().GetSize()) < LOW_VIDEO_FRAMES));
 }
 
 bool MediaDecoderStateMachine::OutOfDecodedAudio()
 {
     MOZ_ASSERT(OnTaskQueue());
     return IsAudioDecoding() && !AudioQueue().IsFinished() &&
            AudioQueue().GetSize() == 0 &&
-           !mAudioSink->HasUnplayedFrames(TrackInfo::kAudioTrack);
+           !mMediaSink->HasUnplayedFrames(TrackInfo::kAudioTrack);
 }
 
 bool MediaDecoderStateMachine::HasLowUndecodedData()
 {
   MOZ_ASSERT(OnTaskQueue());
   return HasLowUndecodedData(mLowDataThresholdUsecs);
 }
 
@@ -2411,18 +2380,17 @@ nsresult MediaDecoderStateMachine::RunSt
 
         nsCOMPtr<nsIRunnable> event =
           NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded);
         AbstractThread::MainThread()->Dispatch(event.forget());
 
         mSentPlaybackEndedEvent = true;
 
         // MediaSink::GetEndTime() must be called before stopping playback.
-        StopAudioSink();
-        StopDecodedStream();
+        StopMediaSink();
       }
 
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
@@ -2438,21 +2406,20 @@ MediaDecoderStateMachine::Reset()
   // dormant state. We could also be in the process of going dormant, and have
   // just switched to exiting dormant before we finished entering dormant,
   // hence the DECODING_NONE case below.
   MOZ_ASSERT(IsShutdown() ||
              mState == DECODER_STATE_SEEKING ||
              mState == DECODER_STATE_DORMANT ||
              mState == DECODER_STATE_DECODING_NONE);
 
-  // Stop the audio thread. Otherwise, AudioSink might be accessing AudioQueue
+  // Stop the audio thread. Otherwise, MediaSink might be accessing AudioQueue
   // outside of the decoder monitor while we are clearing the queue and causes
   // crash for no samples to be popped.
-  StopAudioSink();
-  StopDecodedStream();
+  StopMediaSink();
 
   mVideoFrameEndTime = -1;
   mDecodedVideoEndTime = -1;
   mDecodedAudioEndTime = -1;
   mAudioCompleted = false;
   AudioQueue().Reset();
   VideoQueue().Reset();
   mFirstVideoFrameAfterSeek = nullptr;
@@ -2580,21 +2547,17 @@ int64_t MediaDecoderStateMachine::GetClo
   // the end of the audio, use the audio clock. However if we've finished
   // audio, or don't have audio, use the system clock. If our output is being
   // fed to a MediaStream, use that stream as the source of the clock.
   int64_t clock_time = -1;
   TimeStamp t;
   if (!IsPlaying()) {
     clock_time = mPlayDuration;
   } else {
-    if (mAudioCaptured) {
-      clock_time = mStreamSink->GetPosition(&t);
-    } else {
-      clock_time = mAudioSink->GetPosition(&t);
-    }
+    clock_time = mMediaSink->GetPosition(&t);
     NS_ASSERTION(GetMediaTime() <= clock_time, "Clock should go forwards.");
   }
   if (aTimeStamp) {
     *aTimeStamp = t.IsNull() ? TimeStamp::Now() : t;
   }
 
   return clock_time;
 }
@@ -2874,19 +2837,17 @@ void MediaDecoderStateMachine::StartBuff
               stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)");
 }
 
 void MediaDecoderStateMachine::SetPlayStartTime(const TimeStamp& aTimeStamp)
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   mPlayStartTime = aTimeStamp;
-
-  mAudioSink->SetPlaying(!mPlayStartTime.IsNull());
-  mStreamSink->SetPlaying(!mPlayStartTime.IsNull());
+  mMediaSink->SetPlaying(!mPlayStartTime.IsNull());
 }
 
 void MediaDecoderStateMachine::ScheduleStateMachineWithLockAndWakeDecoder()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   DispatchAudioDecodeTaskIfNeeded();
   DispatchVideoDecodeTaskIfNeeded();
@@ -2954,170 +2915,142 @@ MediaDecoderStateMachine::LogicalPlaybac
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   if (mLogicalPlaybackRate == 0) {
     // This case is handled in MediaDecoder by pausing playback.
     return;
   }
 
   mPlaybackRate = mLogicalPlaybackRate;
-  mAudioSink->SetPlaybackRate(mPlaybackRate);
+  mMediaSink->SetPlaybackRate(mPlaybackRate);
 
   ScheduleStateMachine();
 }
 
 void MediaDecoderStateMachine::PreservesPitchChanged()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  mAudioSink->SetPreservesPitch(mPreservesPitch);
+  mMediaSink->SetPreservesPitch(mPreservesPitch);
 }
 
 bool MediaDecoderStateMachine::IsShutdown()
 {
   MOZ_ASSERT(OnTaskQueue());
   return mIsShutdown;
 }
 
 int64_t
 MediaDecoderStateMachine::AudioEndTime() const
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
-  if (mAudioSink->IsStarted()) {
-    return mAudioSink->GetEndTime(TrackInfo::kAudioTrack);
-  } else if (mAudioCaptured) {
-    return mStreamSink->GetEndTime(TrackInfo::kAudioTrack);
+  if (mMediaSink->IsStarted()) {
+    return mMediaSink->GetEndTime(TrackInfo::kAudioTrack);
   }
   MOZ_ASSERT(!HasAudio());
   return -1;
 }
 
-void MediaDecoderStateMachine::OnAudioSinkComplete()
+void MediaDecoderStateMachine::OnMediaSinkComplete()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  MOZ_ASSERT(!mAudioCaptured, "Should be disconnected when capturing audio.");
-
-  mAudioSinkPromise.Complete();
-  mAudioCompleted = true;
+
+  mMediaSinkPromise.Complete();
+  // Set true only when we have audio.
+  mAudioCompleted = mInfo.HasAudio();
+  // To notify PlaybackEnded as soon as possible.
+  ScheduleStateMachine();
 }
 
-void MediaDecoderStateMachine::OnAudioSinkError()
+void MediaDecoderStateMachine::OnMediaSinkError()
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  MOZ_ASSERT(!mAudioCaptured, "Should be disconnected when capturing audio.");
-
-  mAudioSinkPromise.Complete();
-  mAudioCompleted = true;
+
+  mMediaSinkPromise.Complete();
+  // Set true only when we have audio.
+  mAudioCompleted = mInfo.HasAudio();
 
   // Make the best effort to continue playback when there is video.
   if (HasVideo()) {
     return;
   }
 
   // Otherwise notify media decoder/element about this error for it makes
   // no sense to play an audio-only file without sound output.
   DecodeError();
 }
 
 void
-MediaDecoderStateMachine::OnDecodedStreamFinish()
+MediaDecoderStateMachine::SetAudioCaptured(bool aCaptured)
 {
   MOZ_ASSERT(OnTaskQueue());
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  MOZ_ASSERT(mAudioCaptured, "Audio should be captured.");
-
-  mDecodedStreamPromise.Complete();
-  if (mInfo.HasAudio()) {
-    mAudioCompleted = true;
+
+  if (aCaptured == mAudioCaptured) {
+    return;
   }
-  // To notify PlaybackEnded as soon as possible.
+
+  // Backup current playback parameters.
+  MediaSink::PlaybackParams params = mMediaSink->GetPlaybackParams();
+
+  // Stop and shut down the existing sink.
+  StopMediaSink();
+  mMediaSink->Shutdown();
+
+  // Create a new sink according to whether audio is captured.
+  // TODO: We can't really create a new DecodedStream until OutputStreamManager
+  //       is extracted. It is tricky that the implementation of DecodedStream
+  //       happens to allow reuse after shutdown without creating a new one.
+  mMediaSink = aCaptured ? mStreamSink : CreateAudioSink();
+
+  // Restore playback parameters.
+  mMediaSink->SetPlaybackParams(params);
+
+  // Start the sink if we are already playing. Otherwise it will be
+  // handled in MaybeStartPlayback().
+  if (IsPlaying()) {
+    StartMediaSink();
+  }
+
+  mAudioCaptured = aCaptured;
   ScheduleStateMachine();
 }
 
-void
-MediaDecoderStateMachine::OnDecodedStreamError()
-{
-  MOZ_ASSERT(OnTaskQueue());
-  ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
-  MOZ_ASSERT(mAudioCaptured, "Audio should be captured.");
-
-  mDecodedStreamPromise.Complete();
-  DecodeError();
-}
-
 uint32_t MediaDecoderStateMachine::GetAmpleVideoFrames() const
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
   return (mReader->IsAsync() && mReader->VideoIsHardwareAccelerated())
     ? std::max<uint32_t>(sVideoQueueHWAccelSize, MIN_VIDEO_QUEUE_SIZE)
     : std::max<uint32_t>(sVideoQueueDefaultSize, MIN_VIDEO_QUEUE_SIZE);
 }
 
-void MediaDecoderStateMachine::DispatchAudioCaptured()
-{
-  nsRefPtr<MediaDecoderStateMachine> self = this;
-  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
-  {
-    MOZ_ASSERT(self->OnTaskQueue());
-    ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
-    if (!self->mAudioCaptured) {
-      // Stop the audio sink if it's running.
-      self->StopAudioSink();
-      self->mAudioCaptured = true;
-      // Start DecodedStream if we are already playing. Otherwise it will be
-      // handled in MaybeStartPlayback().
-      if (self->IsPlaying()) {
-        self->StartDecodedStream();
-      }
-      self->ScheduleStateMachine();
-    }
-  });
-  OwnerThread()->Dispatch(r.forget());
-}
-
-void MediaDecoderStateMachine::DispatchAudioUncaptured()
-{
-  nsRefPtr<MediaDecoderStateMachine> self = this;
-  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void
-  {
-    MOZ_ASSERT(self->OnTaskQueue());
-    ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
-    if (self->mAudioCaptured) {
-      self->StopDecodedStream();
-      // Start again the audio sink.
-      self->mAudioCaptured = false;
-      if (self->IsPlaying()) {
-        self->StartAudioSink();
-      }
-      self->ScheduleStateMachine();
-    }
-  });
-  OwnerThread()->Dispatch(r.forget());
-}
-
 void MediaDecoderStateMachine::AddOutputStream(ProcessedMediaStream* aStream,
                                                bool aFinishWhenEnded)
 {
   MOZ_ASSERT(NS_IsMainThread());
   DECODER_LOG("AddOutputStream aStream=%p!", aStream);
   mStreamSink->AddOutput(aStream, aFinishWhenEnded);
-  DispatchAudioCaptured();
+  nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<bool>(
+    this, &MediaDecoderStateMachine::SetAudioCaptured, true);
+  OwnerThread()->Dispatch(r.forget());
 }
 
 void MediaDecoderStateMachine::RemoveOutputStream(MediaStream* aStream)
 {
   MOZ_ASSERT(NS_IsMainThread());
   DECODER_LOG("RemoveOutputStream=%p!", aStream);
   mStreamSink->RemoveOutput(aStream);
   if (!mStreamSink->HasConsumers()) {
-    DispatchAudioUncaptured();
+    nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethodWithArg<bool>(
+      this, &MediaDecoderStateMachine::SetAudioCaptured, false);
+    OwnerThread()->Dispatch(r.forget());
   }
 }
 
 } // namespace mozilla
 
 // avoid redefined macro in unified build
 #undef LOG
 #undef DECODER_LOG
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -162,18 +162,17 @@ public:
   }
 
 private:
   // Initialization that needs to happen on the task queue. This is the first
   // task that gets run on the task queue, and is dispatched from the MDSM
   // constructor immediately after the task queue is created.
   void InitializationTask();
 
-  void DispatchAudioCaptured();
-  void DispatchAudioUncaptured();
+  void SetAudioCaptured(bool aCaptured);
 
   void Shutdown();
 public:
 
   void DispatchShutdown();
 
   void FinishShutdown();
 
@@ -484,29 +483,27 @@ protected:
 
   // If we have video, display a video frame if it's time for display has
   // arrived, otherwise sleep until it's time for the next frame. Update the
   // current frame time as appropriate, and trigger ready state update.  The
   // decoder monitor must be held with exactly one lock count. Called on the
   // state machine thread.
   void UpdateRenderedVideoFrames();
 
-  // Stops the audio sink and shut it down.
+  media::MediaSink* CreateAudioSink();
+
+  // Stops the media sink and shut it down.
   // The decoder monitor must be held with exactly one lock count.
   // Called on the state machine thread.
-  void StopAudioSink();
+  void StopMediaSink();
 
-  // Create and start the audio sink.
+  // Create and start the media sink.
   // The decoder monitor must be held with exactly one lock count.
   // Called on the state machine thread.
-  void StartAudioSink();
-
-  void StopDecodedStream();
-
-  void StartDecodedStream();
+  void StartMediaSink();
 
   // Notification method invoked when mPlayState changes.
   void PlayStateChanged();
 
   // Notification method invoked when mLogicallySeeking changes.
   void LogicallySeekingChanged();
 
   // Notification method invoked when mSameOriginMedia changes.
@@ -643,26 +640,22 @@ protected:
   bool IsAudioDecoding();
   bool IsVideoDecoding();
 
   // Set the time that playback started from the system clock.
   // Can only be called on the state machine thread.
   void SetPlayStartTime(const TimeStamp& aTimeStamp);
 
 private:
-  // Resolved by the AudioSink to signal that all outstanding work is complete
+  // Resolved by the MediaSink to signal that all outstanding work is complete
   // and the sink is shutting down.
-  void OnAudioSinkComplete();
+  void OnMediaSinkComplete();
 
-  // Rejected by the AudioSink to signal errors.
-  void OnAudioSinkError();
-
-  void OnDecodedStreamFinish();
-
-  void OnDecodedStreamError();
+  // Rejected by the MediaSink to signal errors.
+  void OnMediaSinkError();
 
   // Return true if the video decoder's decode speed can not catch up the
   // play time.
   bool NeedToSkipToNextKeyframe();
 
   // The decoder object that created this state machine. The state machine
   // holds a strong reference to the decoder to ensure that the decoder stays
   // alive once media element has started the decoder shutdown process, and has
@@ -972,25 +965,25 @@ private:
   SeekJob mPendingSeek;
 
   // The position that we're currently seeking to.
   SeekJob mCurrentSeek;
 
   // Media Fragment end time in microseconds. Access controlled by decoder monitor.
   int64_t mFragmentEndTime;
 
-  // The audio sink resource.  Used on the state machine thread.
-  nsRefPtr<media::MediaSink> mAudioSink;
+  // The media sink resource.  Used on the state machine thread.
+  nsRefPtr<media::MediaSink> mMediaSink;
 
   // The reader, don't call its methods with the decoder monitor held.
   // This is created in the state machine's constructor.
   nsRefPtr<MediaDecoderReader> mReader;
 
-  // The end time of the last audio frame that's been pushed onto the audio sink
-  // or DecodedStream in microseconds. This will approximately be the end time
+  // The end time of the last audio frame that's been pushed onto the media sink
+  // in microseconds. This will approximately be the end time
   // of the audio stream, unless another frame is pushed to the hardware.
   int64_t AudioEndTime() const;
 
   // The end time of the last decoded audio frame. This signifies the end of
   // decoded audio data. Used to check if we are low in decoded data.
   int64_t mDecodedAudioEndTime;
 
   // The presentation end time of the last video frame which has been displayed
@@ -1265,18 +1258,17 @@ private:
   // Only written on the main thread while holding the monitor. Therefore it
   // can be read on any thread while holding the monitor, or on the main thread
   // without holding the monitor.
   nsRefPtr<DecodedStream> mStreamSink;
 
   // Media data resource from the decoder.
   nsRefPtr<MediaResource> mResource;
 
-  MozPromiseRequestHolder<GenericPromise> mAudioSinkPromise;
-  MozPromiseRequestHolder<GenericPromise> mDecodedStreamPromise;
+  MozPromiseRequestHolder<GenericPromise> mMediaSinkPromise;
 
   MediaEventListener mAudioQueueListener;
   MediaEventListener mVideoQueueListener;
 
 private:
   // The buffered range. Mirrored from the decoder thread.
   Mirror<media::TimeIntervals> mBuffered;
 
--- a/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html
+++ b/dom/media/webaudio/test/test_mediaElementAudioSourceNodeCrossOrigin.html
@@ -7,17 +7,17 @@
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 </head>
 <body>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 SimpleTest.waitForExplicitFinish();
 
 // Turn off the authentication dialog blocking for this test.
-SpecialPowers.setIntPref("network.auth.allow-subresource-auth", 2)
+SpecialPowers.setIntPref("network.auth.subresource-http-auth-allow", 2)
 
 var tests = [
   // Not the same origin no CORS asked for, should have silence
   { url: "http://example.org:80/tests/dom/media/webaudio/test/small-shot.ogg",
     cors: null,
     expectSilence: true },
   // Same origin, should have sound
   { url: "small-shot.ogg",
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -31,16 +31,19 @@
 #include "nsPIDOMWindow.h"
 #include "PromiseCallback.h"
 #include "PromiseDebugging.h"
 #include "PromiseNativeHandler.h"
 #include "PromiseWorkerProxy.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 #include "xpcpublic.h"
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
 
 namespace mozilla {
 namespace dom {
 
 namespace {
 // Generator used by Promise::GetID.
 Atomic<uintptr_t> gIDGenerator(0);
 } // namespace
@@ -531,20 +534,20 @@ Promise::JSCallback(JSContext* aCx, unsi
   if (NS_FAILED(UNWRAP_OBJECT(Promise, &v.toObject(), promise))) {
     return Throw(aCx, NS_ERROR_UNEXPECTED);
   }
 
   v = js::GetFunctionNativeReserved(&args.callee(), SLOT_DATA);
   PromiseCallback::Task task = static_cast<PromiseCallback::Task>(v.toInt32());
 
   if (task == PromiseCallback::Resolve) {
-    promise->MaybeResolveInternal(aCx, args.get(0));
     if (!promise->CaptureStack(aCx, promise->mFullfillmentStack)) {
       return false;
     }
+    promise->MaybeResolveInternal(aCx, args.get(0));
   } else {
     promise->MaybeRejectInternal(aCx, args.get(0));
     if (!promise->CaptureStack(aCx, promise->mRejectionStack)) {
       return false;
     }
   }
 
   args.rval().setUndefined();
@@ -1337,16 +1340,39 @@ Promise::RejectInternal(JSContext* aCx,
   mResolvePending = true;
 
   MaybeSettle(aValue, Rejected);
 }
 
 void
 Promise::Settle(JS::Handle<JS::Value> aValue, PromiseState aState)
 {
+#ifdef MOZ_CRASHREPORTER
+  if (!mGlobal && mFullfillmentStack) {
+    AutoJSAPI jsapi;
+    jsapi.Init();
+    JSContext* cx = jsapi.cx();
+    JS::RootedObject stack(cx, mFullfillmentStack);
+    JSAutoCompartment ac(cx, stack);
+    JS::RootedString stackJSString(cx);
+    if (JS::BuildStackString(cx, stack, &stackJSString)) {
+      nsAutoJSString stackString;
+      if (stackString.init(cx, stackJSString)) {
+        // Put the string in the crash report here, since we're about to crash
+        CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("cced_promise_stack"),
+                                           NS_ConvertUTF16toUTF8(stackString));
+      } else {
+        JS_ClearPendingException(cx);
+      }
+    } else {
+      JS_ClearPendingException(cx);
+    }
+  }
+#endif
+
   if (mGlobal->IsDying()) {
     return;
   }
 
   mSettlementTimestamp = TimeStamp::Now();
 
   SetResult(aValue);
   SetState(aState);
--- a/dom/security/nsCORSListenerProxy.cpp
+++ b/dom/security/nsCORSListenerProxy.cpp
@@ -623,17 +623,16 @@ nsCORSListenerProxy::CheckRequestApprove
     }
     if (!foundMethod) {
       LogBlockedRequest(aRequest, "CORSMethodNotFound", nullptr);
       return NS_ERROR_DOM_BAD_URI;
     }
 
     // The "Access-Control-Allow-Headers" header contains a comma separated
     // list of header names.
-    headerVal.Truncate();
     http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
                             headerVal);
     nsTArray<nsCString> headers;
     nsCCharSeparatedTokenizer headerTokens(headerVal, ',');
     while(headerTokens.hasMoreTokens()) {
       const nsDependentCSubstring& header = headerTokens.nextToken();
       if (header.IsEmpty()) {
         continue;
@@ -1125,17 +1124,16 @@ nsCORSPreflightListener::AddResultToCach
     sPreflightCache->GetEntry(uri, mReferrerPrincipal, mWithCredentials,
                               true);
   if (!entry) {
     return;
   }
 
   // The "Access-Control-Allow-Methods" header contains a comma separated
   // list of method names.
-  headerVal.Truncate();
   http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"),
                           headerVal);
 
   nsCCharSeparatedTokenizer methods(headerVal, ',');
   while(methods.hasMoreTokens()) {
     const nsDependentCSubstring& method = methods.nextToken();
     if (method.IsEmpty()) {
       continue;
@@ -1156,17 +1154,16 @@ nsCORSPreflightListener::AddResultToCach
 
       newMethod->token = method;
       newMethod->expirationTime = expirationTime;
     }
   }
 
   // The "Access-Control-Allow-Headers" header contains a comma separated
   // list of method names.
-  headerVal.Truncate();
   http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"),
                           headerVal);
 
   nsCCharSeparatedTokenizer headers(headerVal, ',');
   while(headers.hasMoreTokens()) {
     const nsDependentCSubstring& header = headers.nextToken();
     if (header.IsEmpty()) {
       continue;
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/GeolocationUtil.cpp
@@ -0,0 +1,28 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "GeolocationUtil.h"
+
+double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon)
+{
+  // Use spherical law of cosines to calculate difference
+  // Not quite as correct as the Haversine but simpler and cheaper
+  const double radsInDeg = M_PI / 180.0;
+  const double rNewLat = aLat * radsInDeg;
+  const double rNewLon = aLon * radsInDeg;
+  const double rOldLat = aLastLat * radsInDeg;
+  const double rOldLon = aLastLon * radsInDeg;
+  // WGS84 equatorial radius of earth = 6378137m
+  double cosDelta = (sin(rNewLat) * sin(rOldLat)) +
+                    (cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon));
+  if (cosDelta > 1.0) {
+    cosDelta = 1.0;
+  } else if (cosDelta < -1.0) {
+    cosDelta = -1.0;
+  }
+  return acos(cosDelta) * 6378137;
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/system/gonk/GeolocationUtil.h
@@ -0,0 +1,13 @@
+/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GEOLOCATIONUTIL_H
+#define GEOLOCATIONUTIL_H
+
+double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon);
+
+#endif
+
--- a/dom/system/gonk/GonkGPSGeolocationProvider.cpp
+++ b/dom/system/gonk/GonkGPSGeolocationProvider.cpp
@@ -15,16 +15,18 @@
  */
 
 #include "GonkGPSGeolocationProvider.h"
 #include "mozstumbler/MozStumbler.h"
 
 #include <pthread.h>
 #include <hardware/gps.h>
 
+#include "GeolocationUtil.h"
+#include "mozstumbler/MozStumbler.h"
 #include "mozilla/Constants.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "nsContentUtils.h"
 #include "nsGeoPosition.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsINetworkInterface.h"
 #include "nsIObserverService.h"
@@ -83,81 +85,16 @@ NS_IMPL_ISUPPORTS(GonkGPSGeolocationProv
 /* static */ GonkGPSGeolocationProvider* GonkGPSGeolocationProvider::sSingleton = nullptr;
 GpsCallbacks GonkGPSGeolocationProvider::mCallbacks;
 
 #ifdef MOZ_B2G_RIL
 AGpsCallbacks GonkGPSGeolocationProvider::mAGPSCallbacks;
 AGpsRilCallbacks GonkGPSGeolocationProvider::mAGPSRILCallbacks;
 #endif // MOZ_B2G_RIL
 
-double CalculateDeltaInMeter(double aLat, double aLon, double aLastLat, double aLastLon)
-{
-  // Use spherical law of cosines to calculate difference
-  // Not quite as correct as the Haversine but simpler and cheaper
-  const double radsInDeg = M_PI / 180.0;
-  const double rNewLat = aLat * radsInDeg;
-  const double rNewLon = aLon * radsInDeg;
-  const double rOldLat = aLastLat * radsInDeg;
-  const double rOldLon = aLastLon * radsInDeg;
-  // WGS84 equatorial radius of earth = 6378137m
-  double cosDelta = (sin(rNewLat) * sin(rOldLat)) +
-                    (cos(rNewLat) * cos(rOldLat) * cos(rOldLon - rNewLon));
-  if (cosDelta > 1.0) {
-    cosDelta = 1.0;
-  } else if (cosDelta < -1.0) {
-    cosDelta = -1.0;
-  }
-  return acos(cosDelta) * 6378137;
-}
-
-class RequestCellInfoEvent : public nsRunnable {
-  public:
-    RequestCellInfoEvent(StumblerInfo *callback)
-      : mRequestCallback(callback)
-      {}
-
-    NS_IMETHOD Run() {
-      MOZ_ASSERT(NS_IsMainThread());
-      // Get Cell Info
-      nsCOMPtr<nsIMobileConnectionService> service =
-        do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
-
-      if (!service) {
-        nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIMobileConnectionService \n");
-        return NS_OK;
-      }
-      nsCOMPtr<nsIMobileConnection> connection;
-      uint32_t numberOfRilServices = 1, cellInfoNum = 0;
-
-      service->GetNumItems(&numberOfRilServices);
-      for (uint32_t rilNum = 0; rilNum < numberOfRilServices; rilNum++) {
-        service->GetItemByServiceId(rilNum /* Client Id */, getter_AddRefs(connection));
-        if (!connection) {
-          nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIMobileConnection by ServiceId %d \n", rilNum);
-        } else {
-          cellInfoNum++;
-          connection->GetCellInfoList(mRequestCallback);
-        }
-      }
-      mRequestCallback->SetCellInfoResponsesExpected(cellInfoNum);
-
-      // Get Wifi AP Info
-      nsCOMPtr<nsIInterfaceRequestor> ir = do_GetService("@mozilla.org/telephony/system-worker-manager;1");
-      nsCOMPtr<nsIWifi> wifi = do_GetInterface(ir);
-      if (!wifi) {
-        mRequestCallback->SetWifiInfoResponseReceived();
-        nsContentUtils::LogMessageToConsole("Stumbler-can not get nsIWifi interface\n");
-        return NS_OK;
-      }
-      wifi->GetWifiScanResults(mRequestCallback);
-      return NS_OK;
-    }
-  private:
-    nsRefPtr<StumblerInfo> mRequestCallback;
-};
 
 void
 GonkGPSGeolocationProvider::LocationCallback(GpsLocation* location)
 {
   if (gDebug_isGPSLocationIgnored) {
     return;
   }
 
@@ -206,45 +143,17 @@ GonkGPSGeolocationProvider::LocationCall
                                         location->latitude,
                                         location->longitude,
                                         location->accuracy);
   }
 
   nsRefPtr<UpdateLocationEvent> event = new UpdateLocationEvent(somewhere);
   NS_DispatchToMainThread(event);
 
-  const double kMinChangeInMeters = 30;
-  static int64_t lastTime_ms = 0;
-  static double sLastLat = 0;
-  static double sLastLon = 0;
-  double delta = -1.0;
-  int64_t timediff = (PR_Now() / PR_USEC_PER_MSEC) - lastTime_ms;
-
-  if (0 != sLastLon || 0 != sLastLat) {
-    delta = CalculateDeltaInMeter(location->latitude, location->longitude, sLastLat, sLastLon);
-  }
-  if (gDebug_isLoggingEnabled) {
-    nsContentUtils::LogMessageToConsole("Stumbler-Location. [%f , %f] time_diff:%lld, delta : %f\n",
-      location->longitude, location->latitude, timediff, delta);
-  }
-
-  // Consecutive GPS locations must be 30 meters and 3 seconds apart
-  if (lastTime_ms == 0 || ((timediff >= STUMBLE_INTERVAL_MS) && (delta > kMinChangeInMeters))){
-    lastTime_ms = (PR_Now() / PR_USEC_PER_MSEC);
-    sLastLat = location->latitude;
-    sLastLon = location->longitude;
-    nsRefPtr<StumblerInfo> requestCallback = new StumblerInfo(somewhere);
-    nsRefPtr<RequestCellInfoEvent> runnable = new RequestCellInfoEvent(requestCallback);
-    NS_DispatchToMainThread(runnable);
-  } else {
-    if (gDebug_isLoggingEnabled) {
-      nsContentUtils::LogMessageToConsole(
-        "Stumbler-GPS locations less than 30 meters and 3 seconds. Ignore!\n");
-    }
-  }
+  MozStumble(somewhere);
 }
 
 void
 GonkGPSGeolocationProvider::StatusCallback(GpsStatus* status)
 {
   if (gDebug_isLoggingEnabled) {
     switch (status->status) {
       case GPS_STATUS_NONE:
--- a/dom/system/gonk/moz.build
+++ b/dom/system/gonk/moz.build
@@ -28,26 +28,28 @@ XPIDL_SOURCES += [
     'nsIVolumeService.idl',
     'nsIVolumeStat.idl',
     'nsIWorkerHolder.idl',
 ]
 
 XPIDL_MODULE = 'dom_system_gonk'
 
 EXPORTS += [
+    'GeolocationUtil.h',
     'GonkGPSGeolocationProvider.h',
     'mozstumbler/MozStumbler.h',
     'nsVolume.h',
     'nsVolumeService.h',
 ]
 UNIFIED_SOURCES += [
     'AudioChannelManager.cpp',
     'AudioManager.cpp',
     'AutoMounter.cpp',
     'AutoMounterSetting.cpp',
+    'GeolocationUtil.cpp',
     'GonkGPSGeolocationProvider.cpp',
     'MozMtpDatabase.cpp',
     'MozMtpServer.cpp',
     'MozMtpStorage.cpp',
     'mozstumbler/MozStumbler.cpp',
     'mozstumbler/StumblerLogging.cpp',
     'mozstumbler/UploadStumbleRunnable.cpp',
     'mozstumbler/WriteStumbleOnThread.cpp',
--- a/dom/system/gonk/mozstumbler/MozStumbler.cpp
+++ b/dom/system/gonk/mozstumbler/MozStumbler.cpp
@@ -1,28 +1,133 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MozStumbler.h"
+#include "nsDataHashtable.h"
 #include "nsGeoPosition.h"
+#include "nsNetCID.h"
 #include "nsPrintfCString.h"
 #include "StumblerLogging.h"
 #include "WriteStumbleOnThread.h"
-#include "nsNetCID.h"
-#include "nsDataHashtable.h"
+#include "../GeolocationUtil.h"
+
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIMobileConnectionInfo.h"
+#include "nsIMobileConnectionService.h"
+#include "nsIMobileCellInfo.h"
+#include "nsIMobileNetworkInfo.h"
+#include "nsINetworkInterface.h"
+#include "nsIRadioInterfaceLayer.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 
 NS_IMPL_ISUPPORTS(StumblerInfo, nsICellInfoListCallback, nsIWifiScanResultsReady)
 
+class RequestCellInfoEvent : public nsRunnable {
+public:
+  RequestCellInfoEvent(StumblerInfo *callback)
+  : mRequestCallback(callback)
+  {}
+
+  NS_IMETHOD Run() {
+    MOZ_ASSERT(NS_IsMainThread());
+    // Get Cell Info
+    nsCOMPtr<nsIMobileConnectionService> service =
+    do_GetService(NS_MOBILE_CONNECTION_SERVICE_CONTRACTID);
+
+    if (!service) {
+      STUMBLER_ERR("Stumbler-can not get nsIMobileConnectionService \n");
+      return NS_OK;
+    }
+    nsCOMPtr<nsIMobileConnection> connection;
+    uint32_t numberOfRilServices = 1, cellInfoNum = 0;
+
+    service->GetNumItems(&numberOfRilServices);
+    for (uint32_t rilNum = 0; rilNum < numberOfRilServices; rilNum++) {
+      service->GetItemByServiceId(rilNum /* Client Id */, getter_AddRefs(connection));
+      if (!connection) {
+        STUMBLER_ERR("Stumbler-can not get nsIMobileConnection by ServiceId %d \n", rilNum);
+      } else {
+        cellInfoNum++;
+        connection->GetCellInfoList(mRequestCallback);
+      }
+    }
+    mRequestCallback->SetCellInfoResponsesExpected(cellInfoNum);
+
+    // Get Wifi AP Info
+    nsCOMPtr<nsIInterfaceRequestor> ir = do_GetService("@mozilla.org/telephony/system-worker-manager;1");
+    nsCOMPtr<nsIWifi> wifi = do_GetInterface(ir);
+    if (!wifi) {
+      mRequestCallback->SetWifiInfoResponseReceived();
+      STUMBLER_ERR("Stumbler-can not get nsIWifi interface\n");
+      return NS_OK;
+    }
+    wifi->GetWifiScanResults(mRequestCallback);
+    return NS_OK;
+  }
+private:
+  nsRefPtr<StumblerInfo> mRequestCallback;
+};
+
+void
+MozStumble(nsGeoPosition* position)
+{
+  if (WriteStumbleOnThread::IsFileWaitingForUpload()) {
+    nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+    MOZ_ASSERT(target);
+    // Knowing that file is waiting to upload, and no collection will take place,
+    // just trigger the thread with an empty string.
+    nsCOMPtr<nsIRunnable> event = new WriteStumbleOnThread(EmptyCString());
+    target->Dispatch(event, NS_DISPATCH_NORMAL);
+    return;
+  }
+
+  nsCOMPtr<nsIDOMGeoPositionCoords> coords;
+  position->GetCoords(getter_AddRefs(coords));
+  if (!coords) {
+    return;
+  }
+
+  double latitude, longitude;
+  coords->GetLatitude(&latitude);
+  coords->GetLongitude(&longitude);
+
+  const double kMinChangeInMeters = 30;
+  static int64_t lastTime_ms = 0;
+  static double sLastLat = 0;
+  static double sLastLon = 0;
+  double delta = -1.0;
+  int64_t timediff = (PR_Now() / PR_USEC_PER_MSEC) - lastTime_ms;
+
+  if (0 != sLastLon || 0 != sLastLat) {
+    delta = CalculateDeltaInMeter(latitude, longitude, sLastLat, sLastLon);
+  }
+  STUMBLER_DBG("Stumbler-Location. [%f , %f] time_diff:%lld, delta : %f\n",
+                                      longitude, latitude, timediff, delta);
+
+  // Consecutive GPS locations must be 30 meters and 3 seconds apart
+  if (lastTime_ms == 0 || ((timediff >= STUMBLE_INTERVAL_MS) && (delta > kMinChangeInMeters))){
+    lastTime_ms = (PR_Now() / PR_USEC_PER_MSEC);
+    sLastLat = latitude;
+    sLastLon = longitude;
+    nsRefPtr<StumblerInfo> requestCallback = new StumblerInfo(position);
+    nsRefPtr<RequestCellInfoEvent> runnable = new RequestCellInfoEvent(requestCallback);
+    NS_DispatchToMainThread(runnable);
+  } else {
+    STUMBLER_DBG("Stumbler-GPS locations less than 30 meters and 3 seconds. Ignore!\n");
+  }
+}
+
 void
 StumblerInfo::SetWifiInfoResponseReceived()
 {
   mIsWifiInfoResponseReceived = true;
 
   if (mIsWifiInfoResponseReceived && mCellInfoResponsesReceived == mCellInfoResponsesExpected) {
     STUMBLER_DBG("Call DumpStumblerInfo from SetWifiInfoResponseReceived\n");
     DumpStumblerInfo();
--- a/dom/system/gonk/mozstumbler/MozStumbler.h
+++ b/dom/system/gonk/mozstumbler/MozStumbler.h
@@ -10,16 +10,18 @@
 #include "nsIDOMEventTarget.h"
 #include "nsICellInfo.h"
 #include "nsIWifi.h"
 
 #define STUMBLE_INTERVAL_MS 3000
 
 class nsGeoPosition;
 
+void MozStumble(nsGeoPosition* position);
+
 class StumblerInfo final : public nsICellInfoListCallback,
                            public nsIWifiScanResultsReady
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSICELLINFOLISTCALLBACK
   NS_DECL_NSIWIFISCANRESULTSREADY
 
--- a/dom/system/gonk/mozstumbler/UploadStumbleRunnable.cpp
+++ b/dom/system/gonk/mozstumbler/UploadStumbleRunnable.cpp
@@ -2,24 +2,25 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "UploadStumbleRunnable.h"
 #include "StumblerLogging.h"
 #include "mozilla/dom/Event.h"
+#include "nsIInputStream.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIURLFormatter.h"
 #include "nsIVariant.h"
 #include "nsIXMLHttpRequest.h"
 #include "nsNetUtil.h"
 
-UploadStumbleRunnable::UploadStumbleRunnable(const nsACString& aUploadData)
-: mUploadData(aUploadData)
+UploadStumbleRunnable::UploadStumbleRunnable(nsIInputStream* aUploadData)
+: mUploadInputStream(aUploadData)
 {
 }
 
 NS_IMETHODIMP
 UploadStumbleRunnable::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
   nsresult rv = Upload();
@@ -31,17 +32,17 @@ UploadStumbleRunnable::Run()
 
 nsresult
 UploadStumbleRunnable::Upload()
 {
   nsresult rv;
   nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance("@mozilla.org/variant;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  rv = variant->SetAsACString(mUploadData);
+  rv = variant->SetAsISupports(mUploadInputStream);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIXMLHttpRequest> xhr = do_CreateInstance(NS_XMLHTTPREQUEST_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIScriptSecurityManager> secman =
     do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -58,23 +59,23 @@ UploadStumbleRunnable::Upload()
   NS_ENSURE_SUCCESS(rv, rv);
   nsString url;
   rv = formatter->FormatURLPref(NS_LITERAL_STRING("geo.stumbler.url"), url);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = xhr->Open(NS_LITERAL_CSTRING("POST"), NS_ConvertUTF16toUTF8(url), false, EmptyString(), EmptyString());
   NS_ENSURE_SUCCESS(rv, rv);
 
-  xhr->SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), NS_LITERAL_CSTRING("application/json"));
+  xhr->SetRequestHeader(NS_LITERAL_CSTRING("Content-Type"), NS_LITERAL_CSTRING("gzip"));
   xhr->SetMozBackgroundRequest(true);
   // 60s timeout
   xhr->SetTimeout(60 * 1000);
 
   nsCOMPtr<EventTarget> target(do_QueryInterface(xhr));
-  nsRefPtr<nsIDOMEventListener> listener = new UploadEventListener(xhr, mUploadData.Length());
+  nsRefPtr<nsIDOMEventListener> listener = new UploadEventListener(xhr);
 
   const char* const sEventStrings[] = {
     // nsIXMLHttpRequestEventTarget event types
     "abort",
     "error",
     "load",
     "timeout"
   };
@@ -88,33 +89,33 @@ UploadStumbleRunnable::Upload()
   rv = xhr->Send(variant);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS(UploadEventListener, nsIDOMEventListener)
 
-UploadEventListener::UploadEventListener(nsIXMLHttpRequest* aXHR, int64_t aFileSize)
-: mXHR(aXHR), mFileSize(aFileSize)
+UploadEventListener::UploadEventListener(nsIXMLHttpRequest* aXHR)
+: mXHR(aXHR)
 {
 }
 
 NS_IMETHODIMP
 UploadEventListener::HandleEvent(nsIDOMEvent* aEvent)
 {
   nsString type;
   if (NS_FAILED(aEvent->GetType(type))) {
     STUMBLER_ERR("Failed to get event type");
     WriteStumbleOnThread::UploadEnded(false);
     return NS_ERROR_FAILURE;
   }
 
   if (type.EqualsLiteral("load")) {
-    STUMBLER_DBG("Got load Event : size %lld", mFileSize);
+    STUMBLER_DBG("Got load Event\n");
   } else if (type.EqualsLiteral("error") && mXHR) {
     STUMBLER_ERR("Upload Error");
   } else {
     STUMBLER_DBG("Receive %s Event", NS_ConvertUTF16toUTF8(type).get());
   }
 
   uint32_t statusCode = 0;
   bool doDelete = false;
--- a/dom/system/gonk/mozstumbler/UploadStumbleRunnable.h
+++ b/dom/system/gonk/mozstumbler/UploadStumbleRunnable.h
@@ -6,41 +6,41 @@
 
 
 #ifndef UPLOADSTUMBLERUNNABLE_H
 #define UPLOADSTUMBLERUNNABLE_H
 
 #include "nsIDOMEventListener.h"
 
 class nsIXMLHttpRequest;
+class nsIInputStream;
 
 /*
  This runnable is managed by WriteStumbleOnThread only, see that class
  for how this is scheduled.
  */
 class UploadStumbleRunnable final : public nsRunnable
 {
 public:
-  explicit UploadStumbleRunnable(const nsACString& aUploadData);
+  explicit UploadStumbleRunnable(nsIInputStream* aUploadInputStream);
 
   NS_IMETHOD Run() override;
 private:
   virtual ~UploadStumbleRunnable() {}
-  const nsCString mUploadData;
+  nsCOMPtr<nsIInputStream> mUploadInputStream;
   nsresult Upload();
 };
 
 
 class UploadEventListener : public nsIDOMEventListener
 {
 public:
-  UploadEventListener(nsIXMLHttpRequest* aXHR, int64_t aFileSize);
+  UploadEventListener(nsIXMLHttpRequest* aXHR);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMEVENTLISTENER
 
 protected:
   virtual ~UploadEventListener() {}
   nsCOMPtr<nsIXMLHttpRequest> mXHR;
-  int64_t mFileSize;
 };
 
 #endif
--- a/dom/system/gonk/mozstumbler/WriteStumbleOnThread.cpp
+++ b/dom/system/gonk/mozstumbler/WriteStumbleOnThread.cpp
@@ -12,22 +12,22 @@
 #include "nsIFileStreams.h"
 #include "nsIInputStream.h"
 #include "nsPrintfCString.h"
 
 #define MAXFILESIZE_KB (15 * 1024)
 #define ONEDAY_IN_MSEC (24 * 60 * 60 * 1000)
 #define MAX_UPLOAD_ATTEMPTS 20
 
-mozilla::Atomic<bool> WriteStumbleOnThread::sIsUploading(false);
+mozilla::Atomic<bool> WriteStumbleOnThread::sIsFileWaitingForUpload(false);
 mozilla::Atomic<bool> WriteStumbleOnThread::sIsAlreadyRunning(false);
 WriteStumbleOnThread::UploadFreqGuard WriteStumbleOnThread::sUploadFreqGuard = {0};
 
-#define FILENAME_INPROGRESS NS_LITERAL_CSTRING("stumbles.json")
-#define FILENAME_COMPLETED NS_LITERAL_CSTRING("stumbles.done.json")
+#define FILENAME_INPROGRESS NS_LITERAL_CSTRING("stumbles.json.gz")
+#define FILENAME_COMPLETED NS_LITERAL_CSTRING("stumbles.done.json.gz")
 #define OUTPUT_DIR NS_LITERAL_CSTRING("mozstumbler")
 
 class DeleteRunnable : public nsRunnable
 {
   public:
     DeleteRunnable() {}
 
     NS_IMETHODIMP
@@ -37,81 +37,79 @@ class DeleteRunnable : public nsRunnable
       nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED,
                                               getter_AddRefs(tmpFile),
                                               OUTPUT_DIR,
                                               nsDumpUtils::CREATE);
       if (NS_SUCCEEDED(rv)) {
         tmpFile->Remove(true);
       }
       // critically, this sets this flag to false so writing can happen again
-      WriteStumbleOnThread::sIsUploading = false;
+      WriteStumbleOnThread::sIsAlreadyRunning = false;
+      WriteStumbleOnThread::sIsFileWaitingForUpload = false;
       return NS_OK;
     }
 
   private:
     ~DeleteRunnable() {}
 };
 
+bool
+WriteStumbleOnThread::IsFileWaitingForUpload()
+{
+  return sIsFileWaitingForUpload;
+}
+
 void
 WriteStumbleOnThread::UploadEnded(bool deleteUploadFile)
 {
   if (!deleteUploadFile) {
-    sIsUploading = false;
+    sIsAlreadyRunning = false;
     return;
   }
 
   nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
   MOZ_ASSERT(target);
   nsCOMPtr<nsIRunnable> event = new DeleteRunnable();
   target->Dispatch(event, NS_DISPATCH_NORMAL);
 }
 
-#define DUMP(o, s) \
-  do { \
-    const char* s2 = (s); \
-    uint32_t dummy; \
-    nsresult rv = (o)->Write((s2), strlen(s2), &dummy); \
-    if (NS_WARN_IF(NS_FAILED(rv))) \
-    STUMBLER_ERR("write err"); \
-  } while (0)
-
 void
 WriteStumbleOnThread::WriteJSON(Partition aPart)
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
   nsCOMPtr<nsIFile> tmpFile;
   nsresult rv;
   rv = nsDumpUtils::OpenTempFile(FILENAME_INPROGRESS, getter_AddRefs(tmpFile),
                                  OUTPUT_DIR, nsDumpUtils::CREATE);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     STUMBLER_ERR("Open a file for stumble failed");
     return;
   }
 
-  nsCOMPtr<nsIFileOutputStream> ostream = do_CreateInstance("@mozilla.org/network/file-output-stream;1");
-  rv = ostream->Init(tmpFile, PR_WRONLY | PR_APPEND, 0666, 0);
+  nsRefPtr<nsGZFileWriter> gzWriter = new nsGZFileWriter(nsGZFileWriter::Append);
+  rv = gzWriter->Init(tmpFile);
   if (NS_WARN_IF(NS_FAILED(rv))) {
-    STUMBLER_ERR("Open a file for stumble failed");
+    STUMBLER_ERR("gzWriter init failed");
     return;
   }
 
   /*
    The json format is like below.
    {items:[
    {item},
    {item},
    {item}
    ]}
    */
 
   // Need to add "]}" after the last item
   if (aPart == Partition::End) {
-    DUMP(ostream, "]}");
-    rv = ostream->Close();
+    gzWriter->Write("]}");
+    rv = gzWriter->Finish();
     if (NS_WARN_IF(NS_FAILED(rv))) {
       STUMBLER_ERR("ostream finish failed");
     }
 
     nsCOMPtr<nsIFile> targetFile;
     nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, getter_AddRefs(targetFile),
                                             OUTPUT_DIR, nsDumpUtils::CREATE);
     nsAutoString targetFilename;
@@ -131,24 +129,24 @@ WriteStumbleOnThread::WriteJSON(Partitio
       STUMBLER_ERR("Rename File failed");
       return;
     }
     return;
   }
 
   // Need to add "{items:[" before the first item
   if (aPart == Partition::Begining) {
-    DUMP(ostream, "{\"items\":[{");
+    gzWriter->Write("{\"items\":[{");
   } else if (aPart == Partition::Middle) {
-    DUMP(ostream, ",{");
+    gzWriter->Write(",{");
   }
-  DUMP(ostream, mDesc.get());
+  gzWriter->Write(mDesc.get());
   //  one item is ended with '}' (e.g. {item})
-  DUMP(ostream, "}");
-  rv = ostream->Close();
+  gzWriter->Write("}");
+  rv = gzWriter->Finish();
   if (NS_WARN_IF(NS_FAILED(rv))) {
     STUMBLER_ERR("ostream finish failed");
   }
 
   // check if it is the end of this file
   int64_t fileSize = 0;
   rv = tmpFile->GetFileSize(&fileSize);
   if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -199,27 +197,30 @@ WriteStumbleOnThread::Run()
   if (b) {
     return NS_OK;
   }
 
   UploadFileStatus status = GetUploadFileStatus();
 
   if (UploadFileStatus::NoFile != status) {
     if (UploadFileStatus::ExistsAndReadyToUpload == status) {
+      sIsFileWaitingForUpload = true;
       Upload();
+      return NS_OK;
     }
   } else {
     Partition partition = GetWritePosition();
     if (partition == Partition::Unknown) {
       STUMBLER_ERR("GetWritePosition failed, skip once");
     } else {
       WriteJSON(partition);
     }
   }
 
+  sIsFileWaitingForUpload = false;
   sIsAlreadyRunning = false;
   return NS_OK;
 }
 
 
 /*
  If the upload file exists, then check if it is one day old.
  • if it is a day old -> ExistsAndReadyToUpload
@@ -254,64 +255,50 @@ WriteStumbleOnThread::GetUploadFileStatu
   return UploadFileStatus::Exists;
 }
 
 void
 WriteStumbleOnThread::Upload()
 {
   MOZ_ASSERT(!NS_IsMainThread());
 
-  bool b = sIsUploading.exchange(true);
-  if (b) {
-    return;
-  }
-
   time_t seconds = time(0);
   int day = seconds / (60 * 60 * 24);
 
   if (sUploadFreqGuard.daySinceEpoch < day) {
     sUploadFreqGuard.daySinceEpoch = day;
     sUploadFreqGuard.attempts = 0;
   }
 
   sUploadFreqGuard.attempts++;
   if (sUploadFreqGuard.attempts > MAX_UPLOAD_ATTEMPTS) {
     STUMBLER_ERR("Too many upload attempts today");
-    sIsUploading = false;
+    sIsAlreadyRunning = false;
     return;
   }
 
   nsCOMPtr<nsIFile> tmpFile;
   nsresult rv = nsDumpUtils::OpenTempFile(FILENAME_COMPLETED, getter_AddRefs(tmpFile),
                                           OUTPUT_DIR, nsDumpUtils::CREATE);
   int64_t fileSize;
   rv = tmpFile->GetFileSize(&fileSize);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     STUMBLER_ERR("GetFileSize failed");
-    sIsUploading = false;
+    sIsAlreadyRunning = false;
     return;
   }
 
   if (fileSize <= 0) {
-    sIsUploading = false;
+    sIsAlreadyRunning = false;
     return;
   }
 
   // prepare json into nsIInputStream
   nsCOMPtr<nsIInputStream> inStream;
-  rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), tmpFile, -1, -1,
-                                  nsIFileInputStream::DEFER_OPEN);
+  rv = NS_NewLocalFileInputStream(getter_AddRefs(inStream), tmpFile);
   if (NS_FAILED(rv)) {
-    sIsUploading = false;
+    sIsAlreadyRunning = false;
     return;
   }
 
-  nsCString bufStr;
-  rv = NS_ReadInputStreamToString(inStream, bufStr, fileSize);
-
-  if (NS_FAILED(rv)) {
-    sIsUploading = false;
-    return;
-  }
-
-  nsRefPtr<nsIRunnable> uploader = new UploadStumbleRunnable(bufStr);
+  nsRefPtr<nsIRunnable> uploader = new UploadStumbleRunnable(inStream);
   NS_DispatchToMainThread(uploader);
 }
--- a/dom/system/gonk/mozstumbler/WriteStumbleOnThread.h
+++ b/dom/system/gonk/mozstumbler/WriteStumbleOnThread.h
@@ -4,16 +4,18 @@
  * 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 WriteStumbleOnThread_H
 #define WriteStumbleOnThread_H
 
 #include "mozilla/Atomics.h"
 
+class DeleteRunnable;
+
 /*
  This class is the entry point to stumbling, in that it
  receives the location+cell+wifi string and writes it
  to disk, or instead, it calls UploadStumbleRunnable
  to upload the data.
 
  Writes will happen until the file is a max size, then stop.
  Uploads will happen only when the file is one day old.
@@ -38,20 +40,23 @@ class WriteStumbleOnThread : public nsRu
 public:
   explicit WriteStumbleOnThread(const nsCString& aDesc)
   : mDesc(aDesc)
   {}
 
   NS_IMETHODIMP Run() override;
 
   static void UploadEnded(bool deleteUploadFile);
-  // Don't write while uploading is happening
-  static mozilla::Atomic<bool> sIsUploading;
+
+  // Used externally to determine if cell+wifi scans should happen
+  // (returns false for that case).
+  static bool IsFileWaitingForUpload();
 
 private:
+  friend class DeleteRunnable;
 
   enum class Partition {
     Begining,
     Middle,
     End,
     Unknown
   };
 
@@ -66,16 +71,18 @@ private:
   void WriteJSON(Partition aPart);
   void Upload();
 
   nsCString mDesc;
 
   // Only run one instance of this
   static mozilla::Atomic<bool> sIsAlreadyRunning;
 
+  static mozilla::Atomic<bool> sIsFileWaitingForUpload;
+
   // Limit the upload attempts per day. If the device is rebooted
   // this resets the allowed attempts, which is acceptable.
   struct UploadFreqGuard {
     int attempts;
     int daySinceEpoch;
   };
   static UploadFreqGuard sUploadFreqGuard;
 
--- a/dom/webidl/PushManager.webidl
+++ b/dom/webidl/PushManager.webidl
@@ -22,17 +22,17 @@ interface PushManagerImpl {
     [Func="ServiceWorkerRegistration::WebPushMethodHider"] void setScope(DOMString scope);
 };
 
 [Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled"]
 interface PushManager {
   [ChromeOnly, Throws, Exposed=Window]
   void setPushManagerImpl(PushManagerImpl store);
 
-  [Throws]
+  [Throws, UseCounter]
   Promise<PushSubscription>     subscribe();
   [Throws]
   Promise<PushSubscription?>    getSubscription();
   [Throws]
   Promise<PushPermissionState> permissionState();
 };
 
 enum PushPermissionState
--- a/dom/webidl/PushSubscription.webidl
+++ b/dom/webidl/PushSubscription.webidl
@@ -9,16 +9,16 @@
 
 interface Principal;
 
 [Exposed=(Window,Worker), Func="nsContentUtils::PushEnabled",
  ChromeConstructor(DOMString pushEndpoint, DOMString scope)]
 interface PushSubscription
 {
     readonly attribute USVString endpoint;
-    [Throws]
+    [Throws, UseCounter]
     Promise<boolean> unsubscribe();
     jsonifier;
 
     // Used to set the principal from the JS implemented PushManager.
     [Exposed=Window,ChromeOnly]
     void setPrincipal(Principal principal);
 };
--- a/dom/webidl/TreeColumn.webidl
+++ b/dom/webidl/TreeColumn.webidl
@@ -20,16 +20,17 @@ interface TreeColumn {
   readonly attribute boolean primary;
   readonly attribute boolean cycler;
   readonly attribute boolean editable;
   readonly attribute boolean selectable;
 
   const short TYPE_TEXT                = 1;
   const short TYPE_CHECKBOX            = 2;
   const short TYPE_PROGRESSMETER       = 3;
+  const short TYPE_PASSWORD            = 4;
   readonly attribute short type;
 
   TreeColumn? getNext();
   TreeColumn? getPrevious();
 
   [Throws]
   void invalidate();
 };
--- a/editor/libeditor/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/nsHTMLDataTransfer.cpp
@@ -1318,18 +1318,19 @@ bool nsHTMLEditor::HavePrivateHTMLFlavor
     return bHavePrivateHTMLFlavor;
 
   return false;
 }
 
 
 NS_IMETHODIMP nsHTMLEditor::Paste(int32_t aSelectionType)
 {
-  if (!FireClipboardEvent(NS_PASTE, aSelectionType))
+  if (!FireClipboardEvent(ePaste, aSelectionType)) {
     return NS_OK;
+  }
 
   // Get Clipboard Service
   nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
   // find out if we have our internal html flavor on the clipboard.  We don't want to mess
   // around with cfhtml if we do.
@@ -1401,36 +1402,38 @@ NS_IMETHODIMP nsHTMLEditor::Paste(int32_
   return InsertFromTransferable(trans, nullptr, contextStr, infoStr,
                                 nullptr, 0, true);
 }
 
 NS_IMETHODIMP nsHTMLEditor::PasteTransferable(nsITransferable *aTransferable)
 {
   // Use an invalid value for the clipboard type as data comes from aTransferable
   // and we don't currently implement a way to put that in the data transfer yet.
-  if (!FireClipboardEvent(NS_PASTE, nsIClipboard::kGlobalClipboard))
+  if (!FireClipboardEvent(ePaste, nsIClipboard::kGlobalClipboard)) {
     return NS_OK;
+  }
 
   // handle transferable hooks
   nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
   if (!nsEditorHookUtils::DoInsertionHook(domdoc, nullptr, aTransferable))
     return NS_OK;
 
   nsAutoString contextStr, infoStr;
   return InsertFromTransferable(aTransferable, nullptr, contextStr, infoStr,
                                 nullptr, 0, true);
 }
 
 //
 // HTML PasteNoFormatting. Ignore any HTML styles and formating in paste source
 //
 NS_IMETHODIMP nsHTMLEditor::PasteNoFormatting(int32_t aSelectionType)
 {
-  if (!FireClipboardEvent(NS_PASTE, aSelectionType))
+  if (!FireClipboardEvent(ePaste, aSelectionType)) {
     return NS_OK;
+  }
 
   ForceCompositionEnd();
 
   // Get Clipboard Service
   nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
   NS_ENSURE_SUCCESS(rv, rv);
 
--- a/editor/libeditor/nsPlaintextDataTransfer.cpp
+++ b/editor/libeditor/nsPlaintextDataTransfer.cpp
@@ -317,18 +317,19 @@ nsresult nsPlaintextEditor::InsertFromDr
   if (NS_SUCCEEDED(rv))
     ScrollSelectionIntoView(false);
 
   return rv;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::Paste(int32_t aSelectionType)
 {
-  if (!FireClipboardEvent(NS_PASTE, aSelectionType))
+  if (!FireClipboardEvent(ePaste, aSelectionType)) {
     return NS_OK;
+  }
 
   // Get Clipboard Service
   nsresult rv;
   nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1", &rv));
   if ( NS_FAILED(rv) )
     return rv;
 
   // Get the nsITransferable interface for getting the data from the clipboard
@@ -350,18 +351,19 @@ NS_IMETHODIMP nsPlaintextEditor::Paste(i
 
   return rv;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::PasteTransferable(nsITransferable *aTransferable)
 {
   // Use an invalid value for the clipboard type as data comes from aTransferable
   // and we don't currently implement a way to put that in the data transfer yet.
-  if (!FireClipboardEvent(NS_PASTE, -1))
+  if (!FireClipboardEvent(ePaste, -1)) {
     return NS_OK;
+  }
 
   if (!IsModifiable())
     return NS_OK;
 
   // handle transferable hooks
   nsCOMPtr<nsIDOMDocument> domdoc = GetDOMDocument();
   if (!nsEditorHookUtils::DoInsertionHook(domdoc, nullptr, aTransferable))
     return NS_OK;
--- a/editor/libeditor/nsPlaintextEditor.cpp
+++ b/editor/libeditor/nsPlaintextEditor.cpp
@@ -1168,17 +1168,17 @@ nsPlaintextEditor::CanCutOrCopy(Password
   return !selection->Collapsed();
 }
 
 bool
 nsPlaintextEditor::FireClipboardEvent(EventMessage aEventMessage,
                                       int32_t aSelectionType,
                                       bool* aActionTaken)
 {
-  if (aEventMessage == NS_PASTE) {
+  if (aEventMessage == ePaste) {
     ForceCompositionEnd();
   }
 
   nsCOMPtr<nsIPresShell> presShell = GetPresShell();
   NS_ENSURE_TRUE(presShell, false);
 
   nsRefPtr<Selection> selection = GetSelection();
   if (!selection) {
@@ -1193,17 +1193,17 @@ nsPlaintextEditor::FireClipboardEvent(Ev
   // If the event handler caused the editor to be destroyed, return false.
   // Otherwise return true to indicate that the event was not cancelled.
   return !mDidPreDestroy;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::Cut()
 {
   bool actionTaken = false;
-  if (FireClipboardEvent(NS_CUT, nsIClipboard::kGlobalClipboard, &actionTaken)) {
+  if (FireClipboardEvent(eCut, nsIClipboard::kGlobalClipboard, &actionTaken)) {
     DeleteSelection(eNone, eStrip);
   }
   return actionTaken ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::CanCut(bool *aCanCut)
 {
   NS_ENSURE_ARG_POINTER(aCanCut);
@@ -1212,17 +1212,17 @@ NS_IMETHODIMP nsPlaintextEditor::CanCut(
   *aCanCut = (doc && doc->IsHTMLOrXHTML()) ||
     (IsModifiable() && CanCutOrCopy(ePasswordFieldNotAllowed));
   return NS_OK;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::Copy()
 {
   bool actionTaken = false;
-  FireClipboardEvent(NS_COPY, nsIClipboard::kGlobalClipboard, &actionTaken);
+  FireClipboardEvent(eCopy, nsIClipboard::kGlobalClipboard, &actionTaken);
 
   return actionTaken ? NS_OK : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::CanCopy(bool *aCanCopy)
 {
   NS_ENSURE_ARG_POINTER(aCanCopy);
   // Copy is always enabled in HTML documents
--- a/gfx/2d/FilterNodeSoftware.cpp
+++ b/gfx/2d/FilterNodeSoftware.cpp
@@ -624,16 +624,22 @@ FilterNodeSoftware::GetOutput(const IntR
     MOZ_ASSERT(mCachedOutput, "cached rect but no cached output?");
   }
   return GetDataSurfaceInRect(mCachedOutput, mCachedRect, aRect, EDGE_MODE_NONE);
 }
 
 void
 FilterNodeSoftware::RequestRect(const IntRect &aRect)
 {
+  if (mRequestedRect.Contains(aRect)) {
+    // Bail out now. Otherwise pathological filters can spend time exponential
+    // in the number of primitives, e.g. if each primitive takes the
+    // previous primitive as its two inputs.
+    return;
+  }
   mRequestedRect = mRequestedRect.Union(aRect);
   RequestFromInputsForRect(aRect);
 }
 
 void
 FilterNodeSoftware::RequestInputRect(uint32_t aInputEnumIndex, const IntRect &aRect)
 {
   if (aRect.Overflows()) {
--- a/gfx/src/DriverCrashGuard.h
+++ b/gfx/src/DriverCrashGuard.h
@@ -1,16 +1,15 @@
 /* -*- 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/. */
 #ifndef gfx_src_DriverCrashGuard_h__
 #define gfx_src_DriverCrashGuard_h__
 
-#include "gfxCore.h"
 #include "nsCOMPtr.h"
 #include "nsIGfxInfo.h"
 #include "nsIFile.h"
 #include "nsString.h"
 #include <string>
 
 namespace mozilla {
 
--- a/gfx/src/FilterSupport.cpp
+++ b/gfx/src/FilterSupport.cpp
@@ -342,38 +342,38 @@ FilterCachedColorModels::WrapForColorMod
   RefPtr<FilterNode> unpremultipliedOriginal =
     ForColorModel(ColorModel(mOriginalColorModel.mColorSpace, AlphaModel::Unpremultiplied));
   if (aColorModel.mColorSpace == ColorSpace::LinearRGB) {
     return FilterWrappers::SRGBToLinearRGB(mDT, unpremultipliedOriginal);
   }
   return FilterWrappers::LinearRGBToSRGB(mDT, unpremultipliedOriginal);
 }
 
+static const float identityMatrix[] =
+  { 1, 0, 0, 0, 0,
+    0, 1, 0, 0, 0,
+    0, 0, 1, 0, 0,
+    0, 0, 0, 1, 0 };
+
 // When aAmount == 0, the identity matrix is returned.
 // When aAmount == 1, aToMatrix is returned.
 // When aAmount > 1, an exaggerated version of aToMatrix is returned. This can
 // be useful in certain cases, such as producing a color matrix to oversaturate
 // an image.
 //
 // This function is a shortcut of a full matrix addition and a scalar multiply,
 // and it assumes that the following elements in aToMatrix are 0 and 1:
 //   x x x 0 0
 //   x x x 0 0
 //   x x x 0 0
 //   0 0 0 1 0
 static void
 InterpolateFromIdentityMatrix(const float aToMatrix[20], float aAmount,
                               float aOutMatrix[20])
 {
-  static const float identityMatrix[] =
-    { 1, 0, 0, 0, 0,
-      0, 1, 0, 0, 0,
-      0, 0, 1, 0, 0,
-      0, 0, 0, 1, 0 };
-
   PodCopy(aOutMatrix, identityMatrix, 20);
 
   float oneMinusAmount = 1 - aAmount;
 
   aOutMatrix[0] = aAmount * aToMatrix[0] + oneMinusAmount;
   aOutMatrix[1] = aAmount * aToMatrix[1];
   aOutMatrix[2] = aAmount * aToMatrix[2];
 
@@ -387,22 +387,16 @@ InterpolateFromIdentityMatrix(const floa
 }
 
 // Create a 4x5 color matrix for the different ways to specify color matrices
 // in SVG.
 static nsresult
 ComputeColorMatrix(uint32_t aColorMatrixType, const nsTArray<float>& aValues,
                    float aOutMatrix[20])
 {
-  static const float identityMatrix[] =
-    { 1, 0, 0, 0, 0,
-      0, 1, 0, 0, 0,
-      0, 0, 1, 0, 0,
-      0, 0, 0, 1, 0 };
-
   // Luminance coefficients.
   static const float lumR = 0.2126f;
   static const float lumG = 0.7152f;
   static const float lumB = 0.0722f;
 
   static const float oneMinusLumR = 1 - lumR;
   static const float oneMinusLumG = 1 - lumG;
   static const float oneMinusLumB = 1 - lumB;
@@ -755,17 +749,18 @@ FilterNodeFromPrimitiveDescription(const
       return filter.forget();
     }
 
     case PrimitiveType::ColorMatrix:
     {
       float colorMatrix[20];
       uint32_t type = atts.GetUint(eColorMatrixType);
       const nsTArray<float>& values = atts.GetFloats(eColorMatrixValues);
-      if (NS_FAILED(ComputeColorMatrix(type, values, colorMatrix))) {
+      if (NS_FAILED(ComputeColorMatrix(type, values, colorMatrix)) ||
+          PodEqual(colorMatrix, identityMatrix)) {
         RefPtr<FilterNode> filter(aSources[0]);
         return filter.forget();
       }
       Matrix5x4 matrix(colorMatrix[0], colorMatrix[5], colorMatrix[10],  colorMatrix[15],
                        colorMatrix[1], colorMatrix[6], colorMatrix[11],  colorMatrix[16],
                        colorMatrix[2], colorMatrix[7], colorMatrix[12],  colorMatrix[17],
                        colorMatrix[3], colorMatrix[8], colorMatrix[13],  colorMatrix[18],
                        colorMatrix[4], colorMatrix[9], colorMatrix[14],  colorMatrix[19]);
@@ -949,21 +944,25 @@ FilterNodeFromPrimitiveDescription(const
       return FilterWrappers::Offset(aDT, filter, atts.GetIntPoint(eTurbulenceOffset));
     }
 
     case PrimitiveType::Composite:
     {
       RefPtr<FilterNode> filter;
       uint32_t op = atts.GetUint(eCompositeOperator);
       if (op == SVG_FECOMPOSITE_OPERATOR_ARITHMETIC) {
+        const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
+        static const float allZero[4] = { 0, 0, 0, 0 };
         filter = aDT->CreateFilter(FilterType::ARITHMETIC_COMBINE);
-        if (!filter) {
+        // All-zero coefficients sometimes occur in junk filters.
+        if (!filter ||
+            (coefficients.Length() == ArrayLength(allZero) &&
+             PodEqual(coefficients.Elements(), allZero, ArrayLength(allZero)))) {
           return nullptr;
         }
-        const nsTArray<float>& coefficients = atts.GetFloats(eCompositeCoefficients);
         filter->SetAttribute(ATT_ARITHMETIC_COMBINE_COEFFICIENTS,
                              coefficients.Elements(), coefficients.Length());
         filter->SetInput(IN_ARITHMETIC_COMBINE_IN, aSources[0]);
         filter->SetInput(IN_ARITHMETIC_COMBINE_IN2, aSources[1]);
       } else {
         filter = aDT->CreateFilter(FilterType::COMPOSITE);
         if (!filter) {
           return nullptr;
--- a/gfx/src/X11Util.h
+++ b/gfx/src/X11Util.h
@@ -17,17 +17,16 @@
 #include "gfxQtPlatform.h"
 #undef CursorShape
 #  include <X11/Xlib.h>
 #else
 #  error Unknown toolkit
 #endif 
 
 #include <string.h>                     // for memset
-#include "gfxCore.h"                    // for NS_GFX
 #include "mozilla/Scoped.h"             // for SCOPED_TEMPLATE
 
 namespace mozilla {
 
 /**
  * Return the default X Display created and used by the UI toolkit.
  */
 inline Display*
@@ -86,17 +85,17 @@ SCOPED_TEMPLATE(ScopedXFree, ScopedXFree
  * Nesting is correctly handled: multiple nested ScopedXErrorHandler's don't interfere with each other's state. However,
  * if SyncAndGetError is not called on the nested ScopedXErrorHandler, then any X errors caused by X calls made while the nested
  * ScopedXErrorHandler was in place may then be caught by the other ScopedXErrorHandler. This is just a result of X being
  * asynchronous and us not doing any implicit syncing: the only method in this class what causes syncing is SyncAndGetError().
  *
  * This class is not thread-safe at all. It is assumed that only one thread is using any ScopedXErrorHandler's. Given that it's
  * not used on Mac, it should be easy to make it thread-safe by using thread-local storage with __thread.
  */
-class NS_GFX ScopedXErrorHandler
+class ScopedXErrorHandler
 {
 public:
     // trivial wrapper around XErrorEvent, just adding ctor initializing by zero.
     struct ErrorEvent
     {
         XErrorEvent mError;
 
         ErrorEvent()
deleted file mode 100644
--- a/gfx/src/gfxCore.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/* -*- 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/. */
-
-#ifndef gfxCore_h__
-#define gfxCore_h__
-
-#include "nscore.h"
-
-#define NS_GFX
-#define NS_GFX_(type) type
-#define NS_GFX_STATIC_MEMBER_(type) type
-
-#endif
--- a/gfx/src/gfxCrashReporterUtils.h
+++ b/gfx/src/gfxCrashReporterUtils.h
@@ -1,32 +1,31 @@
 /* -*- 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/. */
 
 #ifndef gfxCrashReporterUtils_h__
 #define gfxCrashReporterUtils_h__
 
-#include "gfxCore.h"
 
 namespace mozilla {
 
 /** \class ScopedGfxFeatureReporter
   *
   * On creation, adds "FeatureName?" to AppNotes
   * On destruction, adds "FeatureName-", or "FeatureName+" if you called SetSuccessful().
   *
   * Any such string is added at most once to AppNotes, and is subsequently skipped.
   *
   * This ScopedGfxFeatureReporter class is designed to be fool-proof to use in functions that
   * have many exit points. We don't want to encourage having function with many exit points.
   * It just happens that our graphics features initialization functions are like that.
   */
-class NS_GFX ScopedGfxFeatureReporter
+class ScopedGfxFeatureReporter
 {
 public:
   explicit ScopedGfxFeatureReporter(const char *aFeature, bool force = false)
     : mFeature(aFeature), mStatusChar('-')
   {
     WriteAppNote(force ? '!' : '?');
   }
   ~ScopedGfxFeatureReporter() {
--- a/gfx/src/moz.build
+++ b/gfx/src/moz.build
@@ -11,17 +11,16 @@ XPIDL_SOURCES += [
 
 XPIDL_MODULE = 'gfx'
 
 DEFINES['MOZ_APP_VERSION'] = '"%s"' % CONFIG['MOZ_APP_VERSION']
 
 EXPORTS += [
     'DriverCrashGuard.h',
     'FilterSupport.h',
-    'gfxCore.h',
     'gfxCrashReporterUtils.h',
     'gfxTelemetry.h',
     'nsBoundingMetrics.h',
     'nsColor.h',
     'nsColorNameList.h',
     'nsColorNames.h',
     'nsCoord.h',
     'nsDeviceContext.h',
--- a/gfx/src/nsColor.cpp
+++ b/gfx/src/nsColor.cpp
@@ -70,18 +70,17 @@ static int ComponentValue(const char16_t
     }
     else {  // not a hex digit, treat it like 0
       component = (component * 16);
     }
   }
   return component;
 }
 
-NS_GFX_(bool) NS_HexToRGB(const nsAString& aColorSpec,
-                                       nscolor* aResult)
+bool NS_HexToRGB(const nsAString& aColorSpec, nscolor* aResult)
 {
   const char16_t* buffer = aColorSpec.BeginReading();
 
   int nameLen = aColorSpec.Length();
   if ((nameLen == 3) || (nameLen == 6)) {
     // Make sure the digits are legal
     for (int i = 0; i < nameLen; i++) {
       char16_t ch = buffer[i];
@@ -116,17 +115,17 @@ NS_GFX_(bool) NS_HexToRGB(const nsAStrin
   }
 
   // Improperly formatted color value
   return false;
 }
 
 // This implements part of the algorithm for legacy behavior described in
 // http://www.whatwg.org/specs/web-apps/current-work/complete/common-microsyntaxes.html#rules-for-parsing-a-legacy-color-value
-NS_GFX_(bool) NS_LooseHexToRGB(const nsString& aColorSpec, nscolor* aResult)
+bool NS_LooseHexToRGB(const nsString& aColorSpec, nscolor* aResult)
 {
   if (aColorSpec.EqualsLiteral("transparent")) {
     return false;
   }
 
   int nameLen = aColorSpec.Length();
   const char16_t* colorSpec = aColorSpec.get();
   if (nameLen > 128) {
@@ -180,17 +179,17 @@ NS_GFX_(bool) NS_LooseHexToRGB(const nsS
   NS_ASSERTION((r >= 0) && (r <= 255), "bad r");
   NS_ASSERTION((g >= 0) && (g <= 255), "bad g");
   NS_ASSERTION((b >= 0) && (b <= 255), "bad b");
 
   *aResult = NS_RGB(r, g, b);
   return true;
 }
 
-NS_GFX_(bool) NS_ColorNameToRGB(const nsAString& aColorName, nscolor* aResult)
+bool NS_ColorNameToRGB(const nsAString& aColorName, nscolor* aResult)
 {
   if (!gColorTable) return false;
 
   int32_t id = gColorTable->Lookup(aColorName);
   if (eColorName_UNKNOWN < id) {
     NS_ASSERTION(uint32_t(id) < eColorName_COUNT,
                  "gColorTable->Lookup messed up");
     if (aResult) {
@@ -198,29 +197,29 @@ NS_GFX_(bool) NS_ColorNameToRGB(const ns
     }
     return true;
   }
   return false;
 }
 
 // Returns kColorNames, an array of all possible color names, and sets
 // *aSizeArray to the size of that array. Do NOT call free() on this array.
-NS_GFX_(const char * const *) NS_AllColorNames(size_t *aSizeArray)
+const char * const * NS_AllColorNames(size_t *aSizeArray)
 {
   *aSizeArray = ArrayLength(kColorNames);
   return kColorNames;
 }
 
 // Macro to blend two colors
 //
 // equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255
 #define MOZ_BLEND(target, bg, fg, fgalpha)       \
   FAST_DIVIDE_BY_255(target, (bg)*(255-fgalpha) + (fg)*(fgalpha))
 
-NS_GFX_(nscolor)
+nscolor
 NS_ComposeColors(nscolor aBG, nscolor aFG)
 {
   // This function uses colors that are non premultiplied alpha.
   int r, g, b, a;
 
   int bgAlpha = NS_GET_A(aBG);
   int fgAlpha = NS_GET_A(aFG);
 
@@ -259,34 +258,34 @@ HSL_HueToRGB(float m1, float m2, float h
   if (h < (float)(1.0/2.0))
     return m2;
   if (h < (float)(2.0/3.0))
     return m1 + (m2 - m1)*((float)(2.0/3.0) - h)*6.0f;
   return m1;      
 }
 
 // The float parameters are all expected to be in the range 0-1
-NS_GFX_(nscolor)
+nscolor
 NS_HSL2RGB(float h, float s, float l)
 {
   uint8_t r, g, b;
   float m1, m2;
   if (l <= 0.5f) {
     m2 = l*(s+1);
   } else {
     m2 = l + s - l*s;
   }
   m1 = l*2 - m2;
   r = uint8_t(255 * HSL_HueToRGB(m1, m2, h + 1.0f/3.0f));
   g = uint8_t(255 * HSL_HueToRGB(m1, m2, h));
   b = uint8_t(255 * HSL_HueToRGB(m1, m2, h - 1.0f/3.0f));
   return NS_RGB(r, g, b);  
 }
 
-NS_GFX_(const char*)
+const char*
 NS_RGBToColorName(nscolor aColor)
 {
   for (size_t idx = 0; idx < ArrayLength(kColors); ++idx) {
     if (kColors[idx] == aColor) {
       return kColorNames[idx];
     }
   }
 
--- a/gfx/src/nsColor.h
+++ b/gfx/src/nsColor.h
@@ -3,17 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsColor_h___
 #define nsColor_h___
 
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint8_t, uint32_t
-#include "gfxCore.h"                    // for NS_GFX_
 #include "nscore.h"                     // for nsAString
 
 class nsAString;
 class nsString;
 
 // A color is a 32 bit unsigned integer with four components: R, G, B
 // and A.
 typedef uint32_t nscolor;
@@ -48,42 +47,42 @@ typedef uint32_t nscolor;
   PR_BEGIN_MACRO                                   \
     unsigned tmp_ = v;                             \
     target = ((tmp_ << 8) + tmp_ + 255) >> 16;     \
   PR_END_MACRO
 
 // Translate a hex string to a color. Return true if it parses ok,
 // otherwise return false.
 // This accepts only 3 or 6 digits
-NS_GFX_(bool) NS_HexToRGB(const nsAString& aBuf, nscolor* aResult);
+bool NS_HexToRGB(const nsAString& aBuf, nscolor* aResult);
 
 // Compose one NS_RGB color onto another. The result is what
 // you get if you draw aFG on top of aBG with operator OVER.
-NS_GFX_(nscolor) NS_ComposeColors(nscolor aBG, nscolor aFG);
+nscolor NS_ComposeColors(nscolor aBG, nscolor aFG);
 
 // Translate a hex string to a color. Return true if it parses ok,
 // otherwise return false.
 // This version accepts 1 to 9 digits (missing digits are 0)
-NS_GFX_(bool) NS_LooseHexToRGB(const nsString& aBuf, nscolor* aResult);
+bool NS_LooseHexToRGB(const nsString& aBuf, nscolor* aResult);
 
 // There is no function to translate a color to a hex string, because
 // the hex-string syntax does not support transparency.
 
 // Translate a color name to a color. Return true if it parses ok,
 // otherwise return false.
-NS_GFX_(bool) NS_ColorNameToRGB(const nsAString& aBuf, nscolor* aResult);
+bool NS_ColorNameToRGB(const nsAString& aBuf, nscolor* aResult);
 
 // Returns an array of all possible color names, and sets
 // *aSizeArray to the size of that array. Do NOT call |free()| on this array.
-NS_GFX_(const char * const *) NS_AllColorNames(size_t *aSizeArray);
+const char * const * NS_AllColorNames(size_t *aSizeArray);
 
 // function to convert from HSL color space to RGB color space
 // the float parameters are all expected to be in the range 0-1
-NS_GFX_(nscolor) NS_HSL2RGB(float h, float s, float l);
+nscolor NS_HSL2RGB(float h, float s, float l);
 
 // Return a color name for the given nscolor.  If there is no color
 // name for it, returns null.  If there are multiple possible color
 // names for the given color, the first one in nsColorNameList.h
 // (which is generally the first one in alphabetical order) will be
 // returned.
-NS_GFX_(const char*) NS_RGBToColorName(nscolor aColor);
+const char* NS_RGBToColorName(nscolor aColor);
 
 #endif /* nsColor_h___ */
--- a/gfx/src/nsColorNames.h
+++ b/gfx/src/nsColorNames.h
@@ -1,17 +1,16 @@
 /* -*- 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/. */
 
 #ifndef nsColorNames_h___
 #define nsColorNames_h___
 
-#include "gfxCore.h"
 
-class NS_GFX nsColorNames {
+class nsColorNames {
 public:
   static void AddRefTable(void);
   static void ReleaseTable(void);
 };
 
 #endif /* nsColorNames_h___ */
--- a/gfx/src/nsFont.h
+++ b/gfx/src/nsFont.h
@@ -3,17 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsFont_h___
 #define nsFont_h___
 
 #include <stdint.h>                     // for uint8_t, uint16_t
 #include <sys/types.h>                  // for int16_t
-#include "gfxCore.h"                    // for NS_GFX
 #include "gfxFontFamilyList.h"
 #include "gfxFontFeatures.h"
 #include "nsAutoPtr.h"                  // for nsRefPtr
 #include "nsCoord.h"                    // for nscoord
 #include "nsStringFwd.h"                // for nsSubstring
 #include "nsString.h"               // for nsString
 #include "nsTArray.h"                   // for nsTArray
 
@@ -35,17 +34,17 @@ const uint8_t kGenericFont_moz_fixed    
 // CSS
 const uint8_t kGenericFont_serif        = 0x02;
 const uint8_t kGenericFont_sans_serif   = 0x04;
 const uint8_t kGenericFont_monospace    = 0x08;
 const uint8_t kGenericFont_cursive      = 0x10;
 const uint8_t kGenericFont_fantasy      = 0x20;
 
 // Font structure.
-struct NS_GFX nsFont {
+struct nsFont {
 
   // list of font families, either named or generic
   mozilla::FontFamilyList fontlist;
 
   // The style of font (normal, italic, oblique; see gfxFontConstants.h)
   uint8_t style;
 
   // Force this font to not be considered a 'generic' font, even if
--- a/gfx/src/nsMargin.h
+++ b/gfx/src/nsMargin.h
@@ -3,17 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NSMARGIN_H
 #define NSMARGIN_H
 
 #include "nsCoord.h"
 #include "nsPoint.h"
-#include "gfxCore.h"
 #include "mozilla/gfx/BaseMargin.h"
 
 struct nsMargin : public mozilla::gfx::BaseMargin<nscoord, nsMargin> {
   typedef mozilla::gfx::BaseMargin<nscoord, nsMargin> Super;
 
   // Constructors
   nsMargin() : Super() {}
   nsMargin(const nsMargin& aMargin) : Super(aMargin) {}
--- a/gfx/src/nsRect.h
+++ b/gfx/src/nsRect.h
@@ -5,31 +5,30 @@
 
 
 #ifndef NSRECT_H
 #define NSRECT_H
 
 #include <stdio.h>                      // for FILE
 #include <stdint.h>                     // for int32_t, int64_t
 #include <algorithm>                    // for min/max
-#include "gfxCore.h"                    // for NS_GFX
 #include "mozilla/Likely.h"             // for MOZ_UNLIKELY
 #include "mozilla/gfx/Rect.h"
 #include "nsCoord.h"                    // for nscoord, etc
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 #include "nsPoint.h"                    // for nsIntPoint, nsPoint
 #include "nsSize.h"                     // for IntSize, nsSize
 #include "nscore.h"                     // for NS_BUILD_REFCNT_LOGGING
 
 struct nsMargin;
 struct nsIntMargin;
 
 typedef mozilla::gfx::IntRect nsIntRect;
 
-struct NS_GFX nsRect :
+struct nsRect :
   public mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize, nsMargin> {
   typedef mozilla::gfx::BaseRect<nscoord, nsRect, nsPoint, nsSize, nsMargin> Super;
 
   static void VERIFY_COORD(nscoord aValue) { ::VERIFY_COORD(aValue); }
 
   // Constructors
   nsRect() : Super()
   {
@@ -285,12 +284,12 @@ nsRect::ToInsidePixels(nscoord aAppUnits
 const mozilla::gfx::IntRect& GetMaxSizedIntRect();
 
 // app units are integer multiples of pixels, so no rounding needed
 nsRect
 ToAppUnits(const mozilla::gfx::IntRect& aRect, nscoord aAppUnitsPerPixel);
 
 #ifdef DEBUG
 // Diagnostics
-extern NS_GFX FILE* operator<<(FILE* out, const nsRect& rect);
+extern FILE* operator<<(FILE* out, const nsRect& rect);
 #endif // DEBUG
 
 #endif /* NSRECT_H */
--- a/gfx/src/nsRegion.h
+++ b/gfx/src/nsRegion.h
@@ -4,17 +4,16 @@
 
 
 #ifndef nsRegion_h__
 #define nsRegion_h__
 
 #include <stddef.h>                     // for size_t
 #include <stdint.h>                     // for uint32_t, uint64_t
 #include <sys/types.h>                  // for int32_t
-#include "gfxCore.h"                    // for NS_GFX
 #include "mozilla/ToString.h"           // for mozilla::ToString
 #include "nsCoord.h"                    // for nscoord
 #include "nsError.h"                    // for nsresult
 #include "nsPoint.h"                    // for nsIntPoint, nsPoint
 #include "nsRect.h"                     // for mozilla::gfx::IntRect, nsRect
 #include "nsMargin.h"                   // for nsIntMargin
 #include "nsStringGlue.h"               // for nsCString
 #include "xpcom-config.h"               // for CPP_THROW_NEW
@@ -415,17 +414,17 @@ private:
   pixman_region32_t* Impl() const
   {
     return const_cast<pixman_region32_t*>(&mImpl);
   }
 
 };
 
 
-class NS_GFX nsRegionRectIterator
+class nsRegionRectIterator
 {
   const nsRegion*  mRegion;
   int i;
   int n;
   nsRect rect;
   pixman_box32_t *boxes;
 
 public:
@@ -469,17 +468,17 @@ public:
 
 namespace mozilla {
 namespace gfx {
 
 /**
  * BaseIntRegions use int32_t coordinates.
  */
 template <typename Derived, typename Rect, typename Point, typename Margin>
-class NS_GFX BaseIntRegion
+class BaseIntRegion
 {
   friend class ::nsRegion;
 
 public:
   typedef Rect RectType;
   typedef Point PointType;
   typedef Margin MarginType;
 
@@ -758,17 +757,17 @@ public:
   typedef void (*visitFn)(void *closure, VisitSide side, int x1, int y1, int x2, int y2);
   void VisitEdges (visitFn visit, void *closure)
   {
     mImpl.VisitEdges (visit, closure);
   }
 
   nsCString ToString() const { return mImpl.ToString(); }
 
-  class NS_GFX RectIterator
+  class RectIterator
   {
     nsRegionRectIterator mImpl;
     Rect mTmp;
 
   public:
     explicit RectIterator (const BaseIntRegion& aRegion) : mImpl (aRegion.mImpl) {}
 
     const Rect* Next ()
@@ -820,17 +819,17 @@ private:
   {
     return *static_cast<const Derived*>(this);
   }
 };
 
 } // namespace gfx
 } // namespace mozilla
 
-class NS_GFX nsIntRegion : public mozilla::gfx::BaseIntRegion<nsIntRegion, mozilla::gfx::IntRect, nsIntPoint, nsIntMargin>
+class nsIntRegion : public mozilla::gfx::BaseIntRegion<nsIntRegion, mozilla::gfx::IntRect, nsIntPoint, nsIntMargin>
 {
 public:
   // Forward constructors.
   nsIntRegion() {}
   MOZ_IMPLICIT nsIntRegion(const mozilla::gfx::IntRect& aRect) : BaseIntRegion(aRect) {}
   nsIntRegion(const nsIntRegion& aRegion) : BaseIntRegion(aRegion) {}
   nsIntRegion(nsIntRegion&& aRegion) : BaseIntRegion(mozilla::Move(aRegion)) {}
 
--- a/gfx/src/nsScriptableRegion.h
+++ b/gfx/src/nsScriptableRegion.h
@@ -3,22 +3,21 @@
  * 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 nsScriptableRegion_h
 #define nsScriptableRegion_h
 
 #include "nsIScriptableRegion.h"
-#include "gfxCore.h"
 #include "nsISupports.h"
 #include "nsRegion.h"
 #include "mozilla/Attributes.h"
 
-class NS_GFX nsScriptableRegion final : public nsIScriptableRegion {
+class nsScriptableRegion final : public nsIScriptableRegion {
 public:
 	nsScriptableRegion();
 
 	NS_DECL_ISUPPORTS
 
 	NS_DECL_NSISCRIPTABLEREGION
 
 private:
--- a/gfx/src/nsTransform2D.h
+++ b/gfx/src/nsTransform2D.h
@@ -1,20 +1,19 @@
 /* -*- 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/. */
 
 #ifndef nsTransform2D_h___
 #define nsTransform2D_h___
 
-#include "gfxCore.h"
 #include "nsCoord.h"
 
-class NS_GFX nsTransform2D
+class nsTransform2D
 {
 private:
  /**
   * This represents the following matrix (note that the order of row/column
   * indices is opposite to usual notation)
   *
   *      / m00   0   m20  \
   * M =  |  0   m11  m21  |
--- a/gfx/ycbcr/QuellGccWarnings.patch
+++ b/gfx/ycbcr/QuellGccWarnings.patch
@@ -1,12 +1,12 @@
 diff --git a/gfx/ycbcr/yuv_convert.cpp b/gfx/ycbcr/yuv_convert.cpp
 --- a/gfx/ycbcr/yuv_convert.cpp
 +++ b/gfx/ycbcr/yuv_convert.cpp
-@@ -337,16 +337,17 @@ NS_GFX_(void) ScaleYCbCrToRGB32(const ui
+@@ -337,16 +337,17 @@ void ScaleYCbCrToRGB32(const uint* yplan
                                           source_dx_uv >> kFractionBits);
          }
        }
        else {
          ScaleYUVToRGB32Row_C(y_ptr, u_ptr, v_ptr,
                               dest_pixel, width, source_dx);
        }
  #else
--- a/gfx/ycbcr/TypeFromSize.patch
+++ b/gfx/ycbcr/TypeFromSize.patch
@@ -5,54 +5,54 @@ diff --git a/gfx/ycbcr/yuv_convert.cpp b
  
  namespace gfx {
   
  // 16.16 fixed point arithmetic
  const int kFractionBits = 16;
  const int kFractionMax = 1 << kFractionBits;
  const int kFractionMask = ((1 << kFractionBits) - 1);
  
-+NS_GFX_(YUVType) TypeFromSize(int ywidth, 
-+                              int yheight, 
-+                              int cbcrwidth, 
-+                              int cbcrheight)
++YUVType TypeFromSize(int ywidth, 
++                     int yheight, 
++                     int cbcrwidth, 
++                     int cbcrheight)
 +{
 +  if (ywidth == cbcrwidth && yheight == cbcrheight) {
 +    return YV24;
 +  }
 +  else if (ywidth / 2 == cbcrwidth && yheight == cbcrheight) {
 +    return YV16;
 +  }
 +  else {
 +    return YV12;
 +  }
 +}
 +
  // Convert a frame of YUV to 32 bit ARGB.
- NS_GFX_(void) ConvertYCbCrToRGB32(const uint8* y_buf,
-                                   const uint8* u_buf,
-                                   const uint8* v_buf,
-                                   uint8* rgb_buf,
-                                   int pic_x,
-                                   int pic_y,
-                                   int pic_width,
+ void ConvertYCbCrToRGB32(const uint8* y_buf,
+                          const uint8* u_buf,
+                          const uint8* v_buf,
+                          uint8* rgb_buf,
+                          int pic_x,
+                          int pic_y,
+                          int pic_width,
 diff --git a/gfx/ycbcr/yuv_convert.h b/gfx/ycbcr/yuv_convert.h
 --- a/gfx/ycbcr/yuv_convert.h
 +++ b/gfx/ycbcr/yuv_convert.h
 @@ -36,16 +36,18 @@ enum Rotate {
  // Filter affects how scaling looks.
  enum ScaleFilter {
    FILTER_NONE = 0,        // No filter (point sampled).
    FILTER_BILINEAR_H = 1,  // Bilinear horizontal filter.
    FILTER_BILINEAR_V = 2,  // Bilinear vertical filter.
    FILTER_BILINEAR = 3     // Bilinear filter.
  };
  
-+NS_GFX_(YUVType) TypeFromSize(int ywidth, int yheight, int cbcrwidth, int cbcrheight);
++YUVType TypeFromSize(int ywidth, int yheight, int cbcrwidth, int cbcrheight);
 +
  // Convert a frame of YUV to 32 bit ARGB.
  // Pass in YV16/YV12 depending on source format
- NS_GFX_(void) ConvertYCbCrToRGB32(const uint8* yplane,
-                                   const uint8* uplane,
-                                   const uint8* vplane,
-                                   uint8* rgbframe,
-                                   int pic_x,
-                                   int pic_y,
+ void ConvertYCbCrToRGB32(const uint8* yplane,
+                          const uint8* uplane,
+                          const uint8* vplane,
+                          uint8* rgbframe,
+                          int pic_x,
+                          int pic_y,
--- a/gfx/ycbcr/convert.patch
+++ b/gfx/ycbcr/convert.patch
@@ -66,28 +66,28 @@ diff --git a/gfx/ycbcr/yuv_convert.cpp b
 -    const uint8* v_ptr = v_buf + (y >> y_shift) * uv_pitch;
 -
 -    FastConvertYUVToRGB32Row(y_ptr,
 -                             u_ptr,
 -                             v_ptr,
 -                             rgb_row,
 -                             width);
 -  }
-+NS_GFX_(void) ConvertYCbCrToRGB32(const uint8* y_buf,
-+                                  const uint8* u_buf,
-+                                  const uint8* v_buf,
-+                                  uint8* rgb_buf,
-+                                  int pic_x,
-+                                  int pic_y,
-+                                  int pic_width,
-+                                  int pic_height,
-+                                  int y_pitch,
-+                                  int uv_pitch,
-+                                  int rgb_pitch,
-+                                  YUVType yuv_type) {
++void ConvertYCbCrToRGB32(const uint8* y_buf,
++                         const uint8* u_buf,
++                         const uint8* v_buf,
++                         uint8* rgb_buf,
++                         int pic_x,
++                         int pic_y,
++                         int pic_width,
++                         int pic_height,
++                         int y_pitch,
++                         int uv_pitch,
++                         int rgb_pitch,
++                         YUVType yuv_type) {
 +  unsigned int y_shift = yuv_type == YV12 ? 1 : 0;
 +  unsigned int x_shift = yuv_type == YV24 ? 0 : 1;
 +  // Test for SSE because the optimized code uses movntq, which is not part of MMX.
 +  bool has_sse = supports_mmx() && supports_sse();
 +  // There is no optimized YV24 SSE routine so we check for this and
 +  // fall back to the C code.
 +  has_sse &= yuv_type != YV24;
 +  bool odd_pic_x = yuv_type != YV24 && pic_x % 2 != 0;
@@ -270,30 +270,30 @@ diff --git a/gfx/ycbcr/yuv_convert.cpp b
 -                     int width,
 -                     int height,
 -                     int y_pitch,
 -                     int uv_pitch,
 -                     int rgb_pitch,
 -                     YUVType yuv_type,
 -                     Rotate view_rotate,
 -                     ScaleFilter filter) {
-+NS_GFX_(void) ScaleYCbCrToRGB32(const uint8* y_buf,
-+                                const uint8* u_buf,
-+                                const uint8* v_buf,
-+                                uint8* rgb_buf,
-+                                int source_width,
-+                                int source_height,
-+                                int width,
-+                                int height,
-+                                int y_pitch,
-+                                int uv_pitch,
-+                                int rgb_pitch,
-+                                YUVType yuv_type,
-+                                Rotate view_rotate,
-+                                ScaleFilter filter) {
++void ScaleYCbCrToRGB32(const uint8* y_buf,
++                       const uint8* u_buf,
++                       const uint8* v_buf,
++                       uint8* rgb_buf,
++                       int source_width,
++                       int source_height,
++                       int width,
++                       int height,
++                       int y_pitch,
++                       int uv_pitch,
++                       int rgb_pitch,
++                       YUVType yuv_type,
++                       Rotate view_rotate,
++                       ScaleFilter filter) {
 +  bool has_mmx = supports_mmx();
 +
    // 4096 allows 3 buffers to fit in 12k.
    // Helps performance on CPU with 16K L1 cache.
    // Large enough for 3830x2160 and 30" displays which are 2560x1600.
    const int kFilterBufferSize = 4096;
    // Disable filtering if the screen is too big (to avoid buffer overflows).
    // This should never happen to regular users: they don't have monitors
@@ -464,28 +464,28 @@ diff --git a/gfx/ycbcr/yuv_convert.h b/g
 -                       const uint8* vplane,
 -                       uint8* rgbframe,
 -                       int width,
 -                       int height,
 -                       int ystride,
 -                       int uvstride,
 -                       int rgbstride,
 -                       YUVType yuv_type);
-+NS_GFX_(void) ConvertYCbCrToRGB32(const uint8* yplane,
-+                                  const uint8* uplane,
-+                                  const uint8* vplane,
-+                                  uint8* rgbframe,
-+                                  int pic_x,
-+                                  int pic_y,
-+                                  int pic_width,
-+                                  int pic_height,
-+                                  int ystride,
-+                                  int uvstride,
-+                                  int rgbstride,
-+                                  YUVType yuv_type);
++void ConvertYCbCrToRGB32(const uint8* yplane,
++                         const uint8* uplane,
++                         const uint8* vplane,
++                         uint8* rgbframe,
++                         int pic_x,
++                         int pic_y,
++                         int pic_width,
++                         int pic_height,
++                         int ystride,
++                         int uvstride,
++                         int rgbstride,
++                         YUVType yuv_type);
  
  // Scale a frame of YUV to 32 bit ARGB.
  // Supports rotation and mirroring.
 -void ScaleYUVToRGB32(const uint8* yplane,
 -                     const uint8* uplane,
 -                     const uint8* vplane,
 -                     uint8* rgbframe,
 -                     int source_width,
@@ -496,30 +496,30 @@ diff --git a/gfx/ycbcr/yuv_convert.h b/g
 -                     int uvstride,
 -                     int rgbstride,
 -                     YUVType yuv_type,
 -                     Rotate view_rotate,
 -                     ScaleFilter filter);
 -
 -}  // namespace media
 -
-+NS_GFX_(void) ScaleYCbCrToRGB32(const uint8* yplane,
-+                                const uint8* uplane,
-+                                const uint8* vplane,
-+                                uint8* rgbframe,
-+                                int source_width,
-+                                int source_height,
-+                                int width,
-+                                int height,
-+                                int ystride,
-+                                int uvstride,
-+                                int rgbstride,
-+                                YUVType yuv_type,
-+                                Rotate view_rotate,
-+                                ScaleFilter filter);
++void ScaleYCbCrToRGB32(const uint8* yplane,
++                       const uint8* uplane,
++                       const uint8* vplane,
++                       uint8* rgbframe,
++                       int source_width,
++                       int source_height,
++                       int width,
++                       int height,
++                       int ystride,
++                       int uvstride,
++                       int rgbstride,
++                       YUVType yuv_type,
++                       Rotate view_rotate,
++                       ScaleFilter filter);
 +
 +}  // namespace gfx
 +}  // namespace mozilla
 + 
  #endif  // MEDIA_BASE_YUV_CONVERT_H_
 diff --git a/gfx/ycbcr/yuv_convert_mmx.cpp b/gfx/ycbcr/yuv_convert_mmx.cpp
 new file mode 100644
 --- /dev/null
--- a/gfx/ycbcr/ycbcr_to_rgb565.cpp
+++ b/gfx/ycbcr/ycbcr_to_rgb565.cpp
@@ -276,17 +276,17 @@ static void ScaleYCbCr444ToRGB565_Neares
     y = ctx->y_row[source_x];
     u = ctx->u_row[source_x];
     v = ctx->v_row[source_x];
     ctx->rgb_row[x] = yu2rgb565(y, u, v, dither);
     dither ^= 3;
   }
 }
 
-NS_GFX_(void) ScaleYCbCrToRGB565(const uint8_t *y_buf,
+void ScaleYCbCrToRGB565(const uint8_t *y_buf,
                                  const uint8_t *u_buf,
                                  const uint8_t *v_buf,
                                  uint8_t *rgb_buf,
                                  int source_x0,
                                  int source_y0,
                                  int source_width,
                                  int source_height,
                                  int width,
@@ -532,17 +532,17 @@ NS_GFX_(void) ScaleYCbCrToRGB565(const u
       ctx.y_yweight = yweight;
       ctx.uv_yweight = uvweight;
       (*scale_row)(&ctx, dither);
       dither ^= 2;
     }
   }
 }
 
-NS_GFX_(bool) IsScaleYCbCrToRGB565Fast(int source_x0,
+bool IsScaleYCbCrToRGB565Fast(int source_x0,
                                        int source_y0,
                                        int source_width,
                                        int source_height,
                                        int width,
                                        int height,
                                        YUVType yuv_type,
                                        ScaleFilter filter)
 {
@@ -594,17 +594,17 @@ void yuv_to_rgb565_row_c(uint16 *dst,
   {
     dst[x] = yu2rgb565(y[pic_x+x],
                        u[(pic_x+x)>>x_shift],
                        v[(pic_x+x)>>x_shift],
                        2); // Disable dithering for now.
   }
 }
 
-NS_GFX_(void) ConvertYCbCrToRGB565(const uint8* y_buf,
+void ConvertYCbCrToRGB565(const uint8* y_buf,
                                    const uint8* u_buf,
                                    const uint8* v_buf,
                                    uint8* rgb_buf,
                                    int pic_x,
                                    int pic_y,
                                    int pic_width,
                                    int pic_height,
                                    int y_pitch,
@@ -647,17 +647,17 @@ NS_GFX_(void) ConvertYCbCrToRGB565(const
                           v_buf + uvoffs,
                           x_shift,
                           pic_x,
                           pic_width);
     }
   }
 }
 
-NS_GFX_(bool) IsConvertYCbCrToRGB565Fast(int pic_x,
+bool IsConvertYCbCrToRGB565Fast(int pic_x,
                                          int pic_y,
                                          int pic_width,
                                          int pic_height,
                                          YUVType yuv_type)
 {
 #  if defined(MOZILLA_MAY_SUPPORT_NEON)
   return (yuv_type != YV24 && supports_neon());
 #  else
--- a/gfx/ycbcr/ycbcr_to_rgb565.h
+++ b/gfx/ycbcr/ycbcr_to_rgb565.h
@@ -12,55 +12,55 @@
 #endif
 
 namespace mozilla {
 
 namespace gfx {
 
 #ifdef HAVE_YCBCR_TO_RGB565
 // Convert a frame of YUV to 16 bit RGB565.
-NS_GFX_(void) ConvertYCbCrToRGB565(const uint8* yplane,
+void ConvertYCbCrToRGB565(const uint8* yplane,
                                    const uint8* uplane,
                                    const uint8* vplane,
                                    uint8* rgbframe,
                                    int pic_x,
                                    int pic_y,
                                    int pic_width,
                                    int pic_height,
                                    int ystride,
                                    int uvstride,
                                    int rgbstride,
                                    YUVType yuv_type);
 
 // Used to test if we have an accelerated version.
-NS_GFX_(bool) IsConvertYCbCrToRGB565Fast(int pic_x,
+bool IsConvertYCbCrToRGB565Fast(int pic_x,
                                          int pic_y,
                                          int pic_width,
                                          int pic_height,
                                          YUVType yuv_type);
 
 // Scale a frame of YUV to 16 bit RGB565.
-NS_GFX_(void) ScaleYCbCrToRGB565(const uint8_t *yplane,
+void ScaleYCbCrToRGB565(const uint8_t *yplane,
                                  const uint8_t *uplane,
                                  const uint8_t *vplane,
                                  uint8_t *rgbframe,
                                  int source_x0,
                                  int source_y0,
                                  int source_width,
                                  int source_height,
                                  int width,
                                  int height,
                                  int ystride,
                                  int uvstride,
                                  int rgbstride,
                                  YUVType yuv_type,
                                  ScaleFilter filter);
 
 // Used to test if we have an accelerated version.
-NS_GFX_(bool) IsScaleYCbCrToRGB565Fast(int source_x0,
+bool IsScaleYCbCrToRGB565Fast(int source_x0,
                                        int source_y0,
                                        int source_width,
                                        int source_height,
                                        int width,
                                        int height,
                                        YUVType yuv_type,
                                        ScaleFilter filter);
 #endif // HAVE_YCBCR_TO_RGB565
--- a/gfx/ycbcr/yuv_convert.cpp
+++ b/gfx/ycbcr/yuv_convert.cpp
@@ -26,45 +26,45 @@ namespace mozilla {
 
 namespace gfx {
  
 // 16.16 fixed point arithmetic
 const int kFractionBits = 16;
 const int kFractionMax = 1 << kFractionBits;
 const int kFractionMask = ((1 << kFractionBits) - 1);
 
-NS_GFX_(YUVType) TypeFromSize(int ywidth, 
+YUVType TypeFromSize(int ywidth, 
                               int yheight, 
                               int cbcrwidth, 
                               int cbcrheight)
 {
   if (ywidth == cbcrwidth && yheight == cbcrheight) {
     return YV24;
   }
   else if (ywidth / 2 == cbcrwidth && yheight == cbcrheight) {
     return YV16;
   }
   else {
     return YV12;
   }
 }
 
 // Convert a frame of YUV to 32 bit ARGB.
-NS_GFX_(void) ConvertYCbCrToRGB32(const uint8* y_buf,
-                                  const uint8* u_buf,
-                                  const uint8* v_buf,
-                                  uint8* rgb_buf,
-                                  int pic_x,
-                                  int pic_y,
-                                  int pic_width,
-                                  int pic_height,
-                                  int y_pitch,
-                                  int uv_pitch,
-                                  int rgb_pitch,
-                                  YUVType yuv_type) {
+void ConvertYCbCrToRGB32(const uint8* y_buf,
+                         const uint8* u_buf,
+                         const uint8* v_buf,
+                         uint8* rgb_buf,
+                         int pic_x,
+                         int pic_y,
+                         int pic_width,
+                         int pic_height,
+                         int y_pitch,
+                         int uv_pitch,
+                         int rgb_pitch,
+                         YUVType yuv_type) {
   unsigned int y_shift = yuv_type == YV12 ? 1 : 0;
   unsigned int x_shift = yuv_type == YV24 ? 0 : 1;
   // Test for SSE because the optimized code uses movntq, which is not part of MMX.
   bool has_sse = supports_mmx() && supports_sse();
   // There is no optimized YV24 SSE routine so we check for this and
   // fall back to the C code.
   has_sse &= yuv_type != YV24;
   bool odd_pic_x = yuv_type != YV24 && pic_x % 2 != 0;
@@ -158,30 +158,30 @@ static inline void FilterRows(uint8* ybu
   }
 #endif
 
   FilterRows_C(ybuf, y0_ptr, y1_ptr, source_width, source_y_fraction);
 }
 
 
 // Scale a frame of YUV to 32 bit ARGB.
-NS_GFX_(void) ScaleYCbCrToRGB32(const uint8* y_buf,
-                                const uint8* u_buf,
-                                const uint8* v_buf,
-                                uint8* rgb_buf,
-                                int source_width,
-                                int source_height,
-                                int width,
-                                int height,
-                                int y_pitch,
-                                int uv_pitch,
-                                int rgb_pitch,
-                                YUVType yuv_type,
-                                Rotate view_rotate,
-                                ScaleFilter filter) {
+void ScaleYCbCrToRGB32(const uint8* y_buf,
+                       const uint8* u_buf,
+                       const uint8* v_buf,
+                       uint8* rgb_buf,
+                       int source_width,
+                       int source_height,
+                       int width,
+                       int height,
+                       int y_pitch,
+                       int uv_pitch,
+                       int rgb_pitch,
+                       YUVType yuv_type,
+                       Rotate view_rotate,
+                       ScaleFilter filter) {
   bool has_mmx = supports_mmx();
 
   // 4096 allows 3 buffers to fit in 12k.
   // Helps performance on CPU with 16K L1 cache.
   // Large enough for 3830x2160 and 30" displays which are 2560x1600.
   const int kFilterBufferSize = 4096;
   // Disable filtering if the screen is too big (to avoid buffer overflows).
   // This should never happen to regular users: they don't have monitors
--- a/gfx/ycbcr/yuv_convert.h
+++ b/gfx/ycbcr/yuv_convert.h
@@ -1,17 +1,16 @@
 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 #ifndef MEDIA_BASE_YUV_CONVERT_H_
 #define MEDIA_BASE_YUV_CONVERT_H_
 
 #include "chromium_types.h"
-#include "gfxCore.h"
 
 namespace mozilla {
 
 namespace gfx {
  
 // Type of YUV surface.
 // The value of these enums matter as they are used to shift vertical indices.
 enum YUVType {
@@ -36,46 +35,46 @@ enum Rotate {
 // Filter affects how scaling looks.
 enum ScaleFilter {
   FILTER_NONE = 0,        // No filter (point sampled).
   FILTER_BILINEAR_H = 1,  // Bilinear horizontal filter.
   FILTER_BILINEAR_V = 2,  // Bilinear vertical filter.
   FILTER_BILINEAR = 3     // Bilinear filter.
 };
 
-NS_GFX_(YUVType) TypeFromSize(int ywidth, int yheight, int cbcrwidth, int cbcrheight);
+YUVType TypeFromSize(int ywidth, int yheight, int cbcrwidth, int cbcrheight);
 
 // Convert a frame of YUV to 32 bit ARGB.
 // Pass in YV16/YV12 depending on source format
-NS_GFX_(void) ConvertYCbCrToRGB32(const uint8* yplane,
-                                  const uint8* uplane,
-                                  const uint8* vplane,
-                                  uint8* rgbframe,
-                                  int pic_x,
-                                  int pic_y,
-                                  int pic_width,
-                                  int pic_height,
-                                  int ystride,
-                                  int uvstride,
-                                  int rgbstride,
-                                  YUVType yuv_type);
+void ConvertYCbCrToRGB32(const uint8* yplane,
+                         const uint8* uplane,
+                         const uint8* vplane,
+                         uint8* rgbframe,
+                         int pic_x,
+                         int pic_y,
+                         int pic_width,
+                         int pic_height,
+                         int ystride,
+                         int uvstride,
+                         int rgbstride,
+                         YUVType yuv_type);
 
 // Scale a frame of YUV to 32 bit ARGB.
 // Supports rotation and mirroring.
-NS_GFX_(void) ScaleYCbCrToRGB32(const uint8* yplane,
-                                const uint8* uplane,
-                                const uint8* vplane,
-                                uint8* rgbframe,
-                                int source_width,
-                                int source_height,
-                                int width,
-                                int height,
-                                int ystride,
-                                int uvstride,
-                                int rgbstride,
-                                YUVType yuv_type,
-                                Rotate view_rotate,
-                                ScaleFilter filter);
+void ScaleYCbCrToRGB32(const uint8* yplane,
+                       const uint8* uplane,
+                       const uint8* vplane,
+                       uint8* rgbframe,
+                       int source_width,
+                       int source_height,
+                       int width,
+                       int height,
+                       int ystride,
+                       int uvstride,
+                       int rgbstride,
+                       YUVType yuv_type,
+                       Rotate view_rotate,
+                       ScaleFilter filter);
 
 } // namespace gfx
 } // namespace mozilla
  
 #endif  // MEDIA_BASE_YUV_CONVERT_H_
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -413,19 +413,20 @@ void
 Decoder::PostIsAnimated(int32_t aFirstFrameTimeout)
 {
   mProgress |= FLAG_IS_ANIMATED;
   mImageMetadata.SetHasAnimation();
   mImageMetadata.SetFirstFrameTimeout(aFirstFrameTimeout);
 }
 
 void
-Decoder::PostFrameStop(Opacity aFrameOpacity    /* = Opacity::TRANSPARENT */,
+Decoder::PostFrameStop(Opacity aFrameOpacity
+                         /* = Opacity::SOME_TRANSPARENCY */,
                        DisposalMethod aDisposalMethod
-                                                /* = DisposalMethod::KEEP */,
+                         /* = DisposalMethod::KEEP */,
                        int32_t aTimeout         /* = 0 */,
                        BlendMethod aBlendMethod /* = BlendMethod::OVER */)
 {
   // We should be mid-frame
   MOZ_ASSERT(!IsMetadataDecode(), "Stopping frame during metadata decode");
   MOZ_ASSERT(mInFrame, "Stopping frame when we didn't start one");
   MOZ_ASSERT(mCurrentFrame, "Stopping frame when we don't have one");
 
--- a/image/Downscaler.cpp
+++ b/image/Downscaler.cpp
@@ -22,16 +22,17 @@ namespace image {
 
 Downscaler::Downscaler(const nsIntSize& aTargetSize)
   : mTargetSize(aTargetSize)
   , mOutputBuffer(nullptr)
   , mXFilter(MakeUnique<skia::ConvolutionFilter1D>())
   , mYFilter(MakeUnique<skia::ConvolutionFilter1D>())
   , mWindowCapacity(0)
   , mHasAlpha(true)
+  , mFlipVertically(false)
 {
   MOZ_ASSERT(gfxPrefs::ImageDownscaleDuringDecodeEnabled(),
              "Downscaling even though downscale-during-decode is disabled?");
   MOZ_ASSERT(mTargetSize.width > 0 && mTargetSize.height > 0,
              "Invalid target size");
 }
 
 Downscaler::~Downscaler()
@@ -52,33 +53,35 @@ Downscaler::ReleaseWindow()
 
   mWindow = nullptr;
   mWindowCapacity = 0;
 }
 
 nsresult
 Downscaler::BeginFrame(const nsIntSize& aOriginalSize,
                        uint8_t* aOutputBuffer,
-                       bool aHasAlpha)
+                       bool aHasAlpha,
+                       bool aFlipVertically /* = false */)
 {
   MOZ_ASSERT(aOutputBuffer);
   MOZ_ASSERT(mTargetSize != aOriginalSize,
              "Created a downscaler, but not downscaling?");
   MOZ_ASSERT(mTargetSize.width <= aOriginalSize.width,
              "Created a downscaler, but width is larger");
   MOZ_ASSERT(mTargetSize.height <= aOriginalSize.height,
              "Created a downscaler, but height is larger");
   MOZ_ASSERT(aOriginalSize.width > 0 && aOriginalSize.height > 0,
              "Invalid original size");
 
   mOriginalSize = aOriginalSize;
   mScale = gfxSize(double(mOriginalSize.width) / mTargetSize.width,
                    double(mOriginalSize.height) / mTargetSize.height);
   mOutputBuffer = aOutputBuffer;
   mHasAlpha = aHasAlpha;
+  mFlipVertically = aFlipVertically;
 
   ResetForNextProgressivePass();
   ReleaseWindow();
 
   auto resizeMethod = skia::ImageOperations::RESIZE_LANCZOS3;
 
   skia::resize::ComputeFilters(resizeMethod,
                                mOriginalSize.width, mTargetSize.width,
@@ -141,16 +144,26 @@ GetFilterOffsetAndLength(UniquePtr<skia:
 {
   MOZ_ASSERT(aOutputImagePosition < aFilter->num_values());
   aFilter->FilterForValue(aOutputImagePosition,
                           aFilterOffsetOut,
                           aFilterLengthOut);
 }
 
 void
+Downscaler::ClearRow(uint32_t aStartingAtCol)
+{
+  MOZ_ASSERT(int64_t(mOriginalSize.width) > int64_t(aStartingAtCol));
+  uint32_t bytesToClear = (mOriginalSize.width - aStartingAtCol)
+                        * sizeof(uint32_t);
+  memset(mRowBuffer.get() + (aStartingAtCol * sizeof(uint32_t)),
+         0, bytesToClear);
+}
+
+void
 Downscaler::CommitRow()
 {
   MOZ_ASSERT(mOutputBuffer, "Should have a current frame");
   MOZ_ASSERT(mCurrentInLine < mOriginalSize.height, "Past end of input");
   MOZ_ASSERT(mCurrentOutLine < mTargetSize.height, "Past end of output");
 
   int32_t filterOffset = 0;
   int32_t filterLength = 0;
@@ -193,19 +206,28 @@ Downscaler::TakeInvalidRect()
 {
   if (MOZ_UNLIKELY(!HasInvalidation())) {
     return DownscalerInvalidRect();
   }
 
   DownscalerInvalidRect invalidRect;
 
   // Compute the target size invalid rect.
-  invalidRect.mTargetSizeRect =
-    nsIntRect(0, mPrevInvalidatedLine,
+  if (mFlipVertically) {
+    // We need to flip it. This will implicitly flip the original size invalid
+    // rect, since we compute it by scaling this rect.
+    invalidRect.mTargetSizeRect =
+      IntRect(0, mTargetSize.height - mCurrentOutLine,
               mTargetSize.width, mCurrentOutLine - mPrevInvalidatedLine);
+  } else {
+    invalidRect.mTargetSizeRect =
+      IntRect(0, mPrevInvalidatedLine,
+              mTargetSize.width, mCurrentOutLine - mPrevInvalidatedLine);
+  }
+
   mPrevInvalidatedLine = mCurrentOutLine;
 
   // Compute the original size invalid rect.
   invalidRect.mOriginalSizeRect = invalidRect.mTargetSizeRect;
   invalidRect.mOriginalSizeRect.ScaleRoundOut(mScale.width, mScale.height);
 
   return invalidRect;
 }
@@ -220,18 +242,23 @@ Downscaler::DownscaleInputLine()
              "Writing past end of output");
 
   int32_t filterOffset = 0;
   int32_t filterLength = 0;
   MOZ_ASSERT(mCurrentOutLine < mYFilter->num_values());
   auto filterValues =
     mYFilter->FilterForValue(mCurrentOutLine, &filterOffset, &filterLength);
 
+  int32_t currentOutLine = mFlipVertically
+                         ? mTargetSize.height - (mCurrentOutLine + 1)
+                         : mCurrentOutLine;
+  MOZ_ASSERT(currentOutLine >= 0);
+
   uint8_t* outputLine =
-    &mOutputBuffer[mCurrentOutLine * mTargetSize.width * sizeof(uint32_t)];
+    &mOutputBuffer[currentOutLine * mTargetSize.width * sizeof(uint32_t)];
   skia::ConvolveVertically(static_cast<const FilterValue*>(filterValues),
                            filterLength, mWindow.get(), mXFilter->num_values(),
                            outputLine, mHasAlpha, supports_sse2());
 
   mCurrentOutLine += 1;
 
   if (mCurrentOutLine == mTargetSize.height) {
     // We're done.
--- a/image/Downscaler.h
+++ b/image/Downscaler.h
@@ -65,24 +65,31 @@ public:
    *
    * @param aOriginalSize The original size of this frame, before scaling.
    * @param aOutputBuffer The buffer to which the Downscaler should write its
    *                      output; this is the same buffer where the Decoder
    *                      would write its output when not downscaling during
    *                      decode.
    * @param aHasAlpha Whether or not this frame has an alpha channel.
    *                  Performance is a little better if it doesn't have one.
+   * @param aFlipVertically If true, output rows will be written to the output
+   *                        buffer in reverse order vertically, which matches
+   *                        the way they are stored in some image formats.
    */
   nsresult BeginFrame(const nsIntSize& aOriginalSize,
                       uint8_t* aOutputBuffer,
-                      bool aHasAlpha);
+                      bool aHasAlpha,
+                      bool aFlipVertically = false);
 
   /// Retrieves the buffer into which the Decoder should write each row.
   uint8_t* RowBuffer() { return mRowBuffer.get(); }
 
+  /// Clears the current row buffer (optionally starting at @aStartingAtCol).
+  void ClearRow(uint32_t aStartingAtCol = 0);
+
   /// Signals that the decoder has finished writing a row into the row buffer.
   void CommitRow();
 
   /// Returns true if there is a non-empty invalid rect available.
   bool HasInvalidation() const;
 
   /// Takes the Downscaler's current invalid rect and resets it.
   DownscalerInvalidRect TakeInvalidRect();
@@ -112,17 +119,18 @@ private:
 
   int32_t mWindowCapacity;
 
   int32_t mLinesInBuffer;
   int32_t mPrevInvalidatedLine;
   int32_t mCurrentOutLine;
   int32_t mCurrentInLine;
 
-  bool mHasAlpha;
+  bool mHasAlpha : 1;
+  bool mFlipVertically : 1;
 };
 
 #else
 
 /**
  * Downscaler requires Skia to work, so we provide a dummy implementation if
  * Skia is disabled that asserts if constructed.
  */
@@ -134,22 +142,23 @@ public:
   {
     MOZ_RELEASE_ASSERT(false, "Skia is not enabled");
   }
 
   const nsIntSize& OriginalSize() const { return nsIntSize(); }
   const nsIntSize& TargetSize() const { return nsIntSize(); }
   const gfxSize& Scale() const { return gfxSize(1.0, 1.0); }
 
-  nsresult BeginFrame(const nsIntSize&, uint8_t*, bool)
+  nsresult BeginFrame(const nsIntSize&, uint8_t*, bool, bool = false)
   {
     return NS_ERROR_FAILURE;
   }
 
   uint8_t* RowBuffer() { return nullptr; }
+  void ClearRow(uint32_t = 0);
   void CommitRow() { }
   bool HasInvalidation() const { return false; }
   DownscalerInvalidRect TakeInvalidRect() { return DownscalerInvalidRect(); }
   void ResetForNextProgressivePass() { }
 };
 
 #endif // MOZ_ENABLE_SKIA
 
--- a/image/ImageFactory.cpp
+++ b/image/ImageFactory.cpp
@@ -31,17 +31,20 @@ namespace image {
 /*static*/ void
 ImageFactory::Initialize()
 { }
 
 static bool
 ShouldDownscaleDuringDecode(const nsCString& aMimeType)
 {
   DecoderType type = DecoderFactory::GetDecoderType(aMimeType.get());
-  return type == DecoderType::JPEG || type == DecoderType::PNG;
+  return type == DecoderType::JPEG ||
+         type == DecoderType::ICON ||
+         type == DecoderType::PNG ||
+         type == DecoderType::BMP;
 }
 
 static uint32_t
 ComputeImageFlags(ImageURL* uri, const nsCString& aMimeType, bool isMultiPart)
 {
   nsresult rv;
 
   // We default to the static globals.
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -14,16 +14,18 @@
 #include "mozilla/Endian.h"
 #include "mozilla/Likely.h"
 #include "nsBMPDecoder.h"
 
 #include "nsIInputStream.h"
 #include "RasterImage.h"
 #include <algorithm>
 
+using namespace mozilla::gfx;
+
 namespace mozilla {
 namespace image {
 
 static PRLogModuleInfo*
 GetBMPLog()
 {
   static PRLogModuleInfo* sBMPLog;
   if (!sBMPLog) {
@@ -57,16 +59,30 @@ nsBMPDecoder::nsBMPDecoder(RasterImage* 
 nsBMPDecoder::~nsBMPDecoder()
 {
   delete[] mColors;
   if (mRow) {
       free(mRow);
   }
 }
 
+nsresult
+nsBMPDecoder::SetTargetSize(const nsIntSize& aSize)
+{
+  // Make sure the size is reasonable.
+  if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Create a downscaler that we'll filter our output through.
+  mDownscaler.emplace(aSize);
+
+  return NS_OK;
+}
+
 // Sets whether or not the BMP will use alpha data
 void
 nsBMPDecoder::SetUseAlphaData(bool useAlphaData)
 {
   mUseAlphaData = useAlphaData;
 }
 
 // Obtains the bits per pixel from the internal BIH header
@@ -117,42 +133,33 @@ nsBMPDecoder::GetCompressedImageSize() c
   }
 
   // The height should be the absolute value of what the height is in the BIH.
   // If positive the bitmap is stored bottom to top, otherwise top to bottom
   int32_t pixelArraySize = rowSize * GetHeight();
   return pixelArraySize;
 }
 
-// Obtains whether or not a BMP file had alpha data in its 4th byte
-// for 32BPP bitmaps.  Only use after the bitmap has been processed.
-bool
-nsBMPDecoder::HasAlphaData() const
-{
-  return mHaveAlphaData;
-}
-
-
 void
 nsBMPDecoder::FinishInternal()
 {
     // We shouldn't be called in error cases
     MOZ_ASSERT(!HasError(), "Can't call FinishInternal on error!");
 
     // We should never make multiple frames
     MOZ_ASSERT(GetFrameCount() <= 1, "Multiple BMP frames?");
 
     // Send notifications if appropriate
     if (!IsMetadataDecode() && HasSize()) {
 
         // Invalidate
         nsIntRect r(0, 0, mBIH.width, GetHeight());
         PostInvalidation(r);
 
-        if (mUseAlphaData) {
+        if (mUseAlphaData && mHaveAlphaData) {
           PostFrameStop(Opacity::SOME_TRANSPARENCY);
         } else {
           PostFrameStop(Opacity::OPAQUE);
         }
         PostDecodeDone();
     }
 }
 
@@ -367,19 +374,20 @@ nsBMPDecoder::WriteInternal(const char* 
       if (HasError()) {
         // Setting the size led to an error.
         return;
       }
 
       // We treat BMPs as transparent if they're 32bpp and alpha is enabled, but
       // also if they use RLE encoding, because the 'delta' mode can skip pixels
       // and cause implicit transparency.
-      if ((mBIH.compression == BMPINFOHEADER::RLE8) ||
-          (mBIH.compression == BMPINFOHEADER::RLE4) ||
-          (mBIH.bpp == 32 && mUseAlphaData)) {
+      bool hasTransparency = (mBIH.compression == BMPINFOHEADER::RLE8) ||
+                             (mBIH.compression == BMPINFOHEADER::RLE4) ||
+                             (mBIH.bpp == 32 && mUseAlphaData);
+      if (hasTransparency) {
         PostHasTransparency();
       }
 
       // We have the size. If we're doing a metadata decode, we're done.
       if (IsMetadataDecode()) {
         return;
       }
 
@@ -448,28 +456,35 @@ nsBMPDecoder::WriteInternal(const char* 
         // Also, it compensates rounding error.
         if (!mRow) {
           PostDataError();
           return;
         }
       }
 
       MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
-      nsresult rv = AllocateBasicFrame();
+      IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
+                                       : GetSize();
+      nsresult rv = AllocateFrame(/* aFrameNum = */ 0, targetSize,
+                                  IntRect(IntPoint(), targetSize),
+                                  SurfaceFormat::B8G8R8A8);
       if (NS_FAILED(rv)) {
           return;
       }
 
       MOZ_ASSERT(mImageData, "Should have a buffer now");
 
-      // Prepare for transparency
-      if ((mBIH.compression == BMPINFOHEADER::RLE8) ||
-          (mBIH.compression == BMPINFOHEADER::RLE4)) {
-        // Clear the image, as the RLE may jump over areas
-        memset(mImageData, 0, mImageDataLength);
+      if (mDownscaler) {
+        // BMPs store their rows in reverse order, so the downscaler needs to
+        // reverse them again when writing its output.
+        rv = mDownscaler->BeginFrame(GetSize(), mImageData, hasTransparency,
+                                     /* aFlipVertically = */ true);
+        if (NS_FAILED(rv)) {
+          return;
+        }
       }
   }
 
   if (mColors && mPos >= mLOH) {
     // OS/2 Bitmaps have no padding byte
     uint8_t bytesPerColor = (mBFH.bihsize == BIH_LENGTH::OS2) ? 3 : 4;
     if (mPos < (mLOH + mNumColors * bytesPerColor)) {
       // Number of bytes already received
@@ -601,18 +616,20 @@ nsBMPDecoder::WriteInternal(const char* 
             memcpy(mRow + mRowBytes, aBuffer, toCopy);
             aCount -= toCopy;
             aBuffer += toCopy;
             mRowBytes += toCopy;
         }
         if (rowSize == mRowBytes) {
           // Collected a whole row into mRow, process it
           uint8_t* p = mRow;
-          uint32_t* d = reinterpret_cast<uint32_t*>(mImageData) +
-                        PIXEL_OFFSET(mCurLine, 0);
+          uint32_t* d = mDownscaler
+                      ? reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer())
+                      : reinterpret_cast<uint32_t*>(mImageData)
+                        + PIXEL_OFFSET(mCurLine, 0);
           uint32_t lpos = mBIH.width;
           switch (mBIH.bpp) {
             case 1:
               while (lpos > 0) {
                 int8_t bit;
                 uint8_t idx;
                 for (bit = 7; bit >= 0 && lpos > 0; bit--) {
                   idx = (*p >> bit) & 1;
@@ -659,49 +676,37 @@ nsBMPDecoder::WriteInternal(const char* 
                 p += 2;
                 --lpos;
                 ++p;
               }
               break;
             case 32:
               while (lpos > 0) {
                 if (mUseAlphaData) {
-                  if (!mHaveAlphaData && p[3]) {
-                    // Non-zero alpha byte detected! Clear previous
-                    // pixels that we have already processed.
-                    // This works because we know that if we
-                    // are reaching here then the alpha data in byte
-                    // 4 has been right all along.  And we know it
-                    // has been set to 0 the whole time, so that
-                    // means that everything is transparent so far.
-                    uint32_t* start = reinterpret_cast<uint32_t*>
-                                      (mImageData) + GetWidth() *
-                                      (mCurLine - 1);
-                    uint32_t heightDifference = GetHeight() -
-                                                mCurLine + 1;
-                    uint32_t pixelCount = GetWidth() *
-                                          heightDifference;
-
-                    memset(start, 0, pixelCount * sizeof(uint32_t));
-
+                  if (MOZ_UNLIKELY(!mHaveAlphaData && p[3])) {
                     PostHasTransparency();
                     mHaveAlphaData = true;
                   }
-                  SetPixel(d, p[2], p[1], p[0], mHaveAlphaData ?  p[3] : 0xFF);
+                  SetPixel(d, p[2], p[1], p[0], p[3]);
                 } else {
                   SetPixel(d, p[2], p[1], p[0]);
                 }
                 p += 4;
                 --lpos;
               }
               break;
             default:
               NS_NOTREACHED("Unsupported color depth,"
                             " but earlier check didn't catch it");
           }
+
+          if (mDownscaler) {
+            mDownscaler->CommitRow();
+          }
+          
           mCurLine --;
           if (mCurLine == 0) { // Finished last line
             break;
           }
           mRowBytes = 0;
         }
       } while (aCount > 0);
     } else if ((mBIH.compression == BMPINFOHEADER::RLE8) ||
@@ -736,18 +741,22 @@ nsBMPDecoder::WriteInternal(const char* 
               // number of consecutive pixels to be drawn
               // using the color index contained in
               // the second byte
               // Work around bitmaps that specify too many pixels
               mState = eRLEStateInitial;
               uint32_t pixelsNeeded = std::min<uint32_t>(mBIH.width - mCurPos,
                                     mStateData);
               if (pixelsNeeded) {
-                uint32_t* d = reinterpret_cast<uint32_t*>
-                              (mImageData) + PIXEL_OFFSET(mCurLine, mCurPos);
+                uint32_t* d = mDownscaler
+                  ? reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer())
+                      + mCurPos
+                  : reinterpret_cast<uint32_t*>(mImageData)
+                      + PIXEL_OFFSET(mCurLine, mCurPos);
+
                 mCurPos += pixelsNeeded;
                 if (mBIH.compression == BMPINFOHEADER::RLE8) {
                   do {
                     SetPixel(d, byte, mColors);
                     pixelsNeeded --;
                   } while (pixelsNeeded);
                 } else {
                     do {
@@ -756,16 +765,20 @@ nsBMPDecoder::WriteInternal(const char* 
                 }
               }
               continue;
             }
 
             switch(byte) {
               case RLE::ESCAPE_EOL:
                 // End of Line: Go to next row
+                if (mDownscaler) {
+                  mDownscaler->CommitRow();
+                }
+
                 mCurLine --;
                 mCurPos = 0;
                 mState = eRLEStateInitial;
                 break;
 
               case RLE::ESCAPE_EOF: // EndOfFile
                 mCurPos = mCurLine = 0;
                 break;
@@ -805,54 +818,82 @@ nsBMPDecoder::WriteInternal(const char* 
               continue;
             }
             break;
 
           case eRLEStateNeedXDelta:
             // Handle the XDelta and proceed to get Y Delta
             byte = *aBuffer++;
             aCount--;
+
+            if (mDownscaler) {
+              // Clear the skipped pixels. (This clears to the end of the row,
+              // which is perfect if there's a Y delta and harmless if not).
+              mDownscaler->ClearRow(/* aStartingAtCol = */ mCurPos);
+            }
+
             mCurPos += byte;
+
             // Delta encoding makes it possible to skip pixels
             // making the image transparent.
             if (MOZ_UNLIKELY(!mHaveAlphaData)) {
                 PostHasTransparency();
+                mHaveAlphaData = true;
             }
             mUseAlphaData = mHaveAlphaData = true;
             if (mCurPos > mBIH.width) {
                 mCurPos = mBIH.width;
             }
 
             mState = eRLEStateNeedYDelta;
             continue;
 
-          case eRLEStateNeedYDelta:
+          case eRLEStateNeedYDelta: {
             // Get the Y Delta and then "handle" the move
             byte = *aBuffer++;
             aCount--;
             mState = eRLEStateInitial;
             // Delta encoding makes it possible to skip pixels
             // making the image transparent.
             if (MOZ_UNLIKELY(!mHaveAlphaData)) {
                 PostHasTransparency();
+                mHaveAlphaData = true;
             }
             mUseAlphaData = mHaveAlphaData = true;
-            mCurLine -= std::min<int32_t>(byte, mCurLine);
+
+            int32_t yDelta = std::min<int32_t>(byte, mCurLine);
+            mCurLine -= yDelta;
+
+            if (mDownscaler && yDelta > 0) {
+              // Commit the current row (the first of the skipped rows).
+              mDownscaler->CommitRow();
+
+              // Clear and commit the remaining skipped rows. 
+              for (int32_t line = 1 ; line < yDelta ; ++line) {
+                mDownscaler->ClearRow();
+                mDownscaler->CommitRow();
+              }
+            }
+
             break;
+          }
 
           case eRLEStateAbsoluteMode: // Absolute Mode
           case eRLEStateAbsoluteModePadded:
             if (mStateData) {
               // In absolute mode, the second byte (mStateData)
               // represents the number of pixels
               // that follow, each of which contains
               // the color index of a single pixel.
-              uint32_t* d = reinterpret_cast<uint32_t*>
-                            (mImageData) +
-                            PIXEL_OFFSET(mCurLine, mCurPos);
+              uint32_t* d = mDownscaler
+                ? reinterpret_cast<uint32_t*>(mDownscaler->RowBuffer())
+                    + mCurPos
+                : reinterpret_cast<uint32_t*>(mImageData)
+                    + PIXEL_OFFSET(mCurLine, mCurPos);
+
               uint32_t* oldPos = d;
               if (mBIH.compression == BMPINFOHEADER::RLE8) {
                   while (aCount > 0 && mStateData > 0) {
                     byte = *aBuffer++;
                     aCount--;
                     SetPixel(d, byte, mColors);
                     mStateData--;
                   }
@@ -898,19 +939,25 @@ nsBMPDecoder::WriteInternal(const char* 
         }
       }
     }
   }
 
   const uint32_t rows = mOldLine - mCurLine;
   if (rows) {
     // Invalidate
-    nsIntRect r(0, mBIH.height < 0 ? -mBIH.height - mOldLine : mCurLine,
-                mBIH.width, rows);
-    PostInvalidation(r);
+    if (!mDownscaler) {
+      nsIntRect r(0, mBIH.height < 0 ? -mBIH.height - mOldLine : mCurLine,
+                  mBIH.width, rows);
+      PostInvalidation(r);
+    } else if (mDownscaler->HasInvalidation()) {
+      DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
+      PostInvalidation(invalidRect.mOriginalSizeRect,
+                       Some(invalidRect.mTargetSizeRect));
+    }
 
     mOldLine = mCurLine;
   }
 
   return;
 }
 
 void
--- a/image/decoders/nsBMPDecoder.h
+++ b/image/decoders/nsBMPDecoder.h
@@ -4,31 +4,34 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #ifndef mozilla_image_decoders_nsBMPDecoder_h
 #define mozilla_image_decoders_nsBMPDecoder_h
 
 #include "BMPFileHeaders.h"
 #include "Decoder.h"
+#include "Downscaler.h"
 #include "gfxColor.h"
 #include "nsAutoPtr.h"
 
 namespace mozilla {
 namespace image {
 
 class RasterImage;
 
 /// Decoder for BMP-Files, as used by Windows and OS/2
 
 class nsBMPDecoder : public Decoder
 {
 public:
     ~nsBMPDecoder();
 
+    nsresult SetTargetSize(const nsIntSize& aSize) override;
+
     // Specifies whether or not the BMP file will contain alpha data
     // If set to true and the BMP is 32BPP, the alpha data will be
     // retrieved from the 4th byte of image data per pixel
     void SetUseAlphaData(bool useAlphaData);
 
     // Obtains the bits per pixel from the internal BIH header
     int32_t GetBitsPerPixel() const;
 
@@ -41,17 +44,20 @@ public:
     // Obtains the internal output image buffer
     uint32_t* GetImageData();
 
     // Obtains the size of the compressed image resource
     int32_t GetCompressedImageSize() const;
 
     // Obtains whether or not a BMP file had alpha data in its 4th byte
     // for 32BPP bitmaps.  Only use after the bitmap has been processed.
-    bool HasAlphaData() const;
+    bool HasAlphaData() const { return mHaveAlphaData; }
+
+    /// Marks this BMP as having alpha data (due to e.g. an ICO alpha mask).
+    void SetHasAlphaData() { mHaveAlphaData = true; }
 
     virtual void WriteInternal(const char* aBuffer,
                                uint32_t aCount) override;
     virtual void FinishInternal() override;
 
 private:
     friend class DecoderFactory;
     friend class nsICODecoder;
@@ -66,16 +72,18 @@ private:
 
     uint32_t mPos; //< Number of bytes read from aBuffer in WriteInternal()
 
     BMPFILEHEADER mBFH;
     BITMAPV5HEADER mBIH;
     char mRawBuf[BIH_INTERNAL_LENGTH::WIN_V3]; //< If this is changed,
                                                // WriteInternal() MUST be updated
 
+    Maybe<Downscaler> mDownscaler;
+
     uint32_t mLOH; //< Length of the header
 
     uint32_t mNumColors; //< The number of used colors, i.e. the number of
                          // entries in mColors
     colorTable* mColors;
 
     bitFields mBitFields;
 
--- a/image/decoders/nsICODecoder.cpp
+++ b/image/decoders/nsICODecoder.cpp
@@ -527,24 +527,24 @@ nsICODecoder::WriteInternal(const char* 
       mPos += toFeed;
       aCount -= toFeed;
       aBuffer += toFeed;
     }
 
     // If the bitmap is fully processed, treat any left over data as the ICO's
     // 'AND buffer mask' which appears after the bitmap resource.
     if (!mIsPNG && mPos >= bmpDataEnd) {
+      nsRefPtr<nsBMPDecoder> bmpDecoder =
+        static_cast<nsBMPDecoder*>(mContainedDecoder.get());
+
       // There may be an optional AND bit mask after the data.  This is
       // only used if the alpha data is not already set. The alpha data
       // is used for 32bpp bitmaps as per the comment in ICODecoder.h
       // The alpha mask should be checked in all other cases.
-      if (static_cast<nsBMPDecoder*>(mContainedDecoder.get())->
-            GetBitsPerPixel() != 32 ||
-          !static_cast<nsBMPDecoder*>(mContainedDecoder.get())->
-            HasAlphaData()) {
+      if (bmpDecoder->GetBitsPerPixel() != 32 || !bmpDecoder->HasAlphaData()) {
         uint32_t rowSize = ((GetRealWidth() + 31) / 32) * 4; // + 31 to round up
         if (mPos == bmpDataEnd) {
           mPos++;
           mRowBytes = 0;
           mCurLine = GetRealHeight();
           mRow = (uint8_t*)realloc(mRow, rowSize);
           if (!mRow) {
             PostDecoderError(NS_ERROR_OUT_OF_MEMORY);
@@ -568,19 +568,17 @@ nsICODecoder::WriteInternal(const char* 
             aCount -= toCopy;
             aBuffer += toCopy;
             mRowBytes += toCopy;
           }
           if (rowSize == mRowBytes) {
             mCurLine--;
             mRowBytes = 0;
 
-            uint32_t* imageData =
-              static_cast<nsBMPDecoder*>(mContainedDecoder.get())->
-                                           GetImageData();
+            uint32_t* imageData = bmpDecoder->GetImageData();
             if (!imageData) {
               PostDataError();
               return;
             }
             uint32_t* decoded = imageData + mCurLine * GetRealWidth();
             uint32_t* decoded_end = decoded + GetRealWidth();
             uint8_t* p = mRow;
             uint8_t* p_end = mRow + rowSize;
@@ -596,17 +594,18 @@ nsICODecoder::WriteInternal(const char* 
               }
             }
           }
         }
 
         // If any bits are set in sawTransparency, then we know at least one
         // pixel was transparent.
         if (sawTransparency) {
-            PostHasTransparency();
+          PostHasTransparency();
+          bmpDecoder->SetHasAlphaData();
         }
       }
     }
   }
 }
 
 bool
 nsICODecoder::WriteToContainedDecoder(const char* aBuffer, uint32_t aCount)
--- a/image/decoders/nsIconDecoder.cpp
+++ b/image/decoders/nsIconDecoder.cpp
@@ -7,41 +7,56 @@
 #include "nsIconDecoder.h"
 #include "nsIInputStream.h"
 #include "nspr.h"
 #include "nsRect.h"
 #include "nsError.h"
 #include "RasterImage.h"
 #include <algorithm>
 
+using namespace mozilla::gfx;
+
+using std::min;
+
 namespace mozilla {
 namespace image {
 
 nsIconDecoder::nsIconDecoder(RasterImage* aImage)
- : Decoder(aImage),
-   mWidth(-1),
-   mHeight(-1),
-   mPixBytesRead(0),
-   mState(iconStateStart)
+ : Decoder(aImage)
+ , mExpectedDataLength(0)
+ , mPixBytesRead(0)
+ , mState(iconStateStart)
+ , mWidth(-1)
+ , mHeight(-1)
 {
   // Nothing to do
 }
 
 nsIconDecoder::~nsIconDecoder()
 { }
 
+nsresult
+nsIconDecoder::SetTargetSize(const nsIntSize& aSize)
+{
+  // Make sure the size is reasonable.
+  if (MOZ_UNLIKELY(aSize.width <= 0 || aSize.height <= 0)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Create a downscaler that we'll filter our output through.
+  mDownscaler.emplace(aSize);
+
+  return NS_OK;
+}
+
 void
 nsIconDecoder::WriteInternal(const char* aBuffer, uint32_t aCount)
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call WriteInternal after error!");
 
-  // We put this here to avoid errors about crossing initialization with case
-  // jumps on linux.
-  uint32_t bytesToRead = 0;
-
   // Loop until the input data is gone
   while (aCount > 0) {
     switch (mState) {
       case iconStateStart:
 
         // Grab the width
         mWidth = (uint8_t)*aBuffer;
 
@@ -68,55 +83,107 @@ nsIconDecoder::WriteInternal(const char*
         }
 
         // If we're doing a metadata decode, we're done.
         if (IsMetadataDecode()) {
           mState = iconStateFinished;
           break;
         }
 
+        // The input is 32bpp, so we expect 4 bytes of data per pixel.
+        mExpectedDataLength = mWidth * mHeight * 4;
+
         {
           MOZ_ASSERT(!mImageData, "Already have a buffer allocated?");
-          nsresult rv = AllocateBasicFrame();
+          IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
+                                           : GetSize();
+          nsresult rv = AllocateFrame(0, targetSize,
+                                      IntRect(IntPoint(), targetSize),
+                                      gfx::SurfaceFormat::B8G8R8A8);
           if (NS_FAILED(rv)) {
             mState = iconStateFinished;
             return;
           }
         }
 
         MOZ_ASSERT(mImageData, "Should have a buffer now");
 
+        if (mDownscaler) {
+          nsresult rv = mDownscaler->BeginFrame(GetSize(),
+                                                mImageData,
+                                                /* aHasAlpha = */ true);
+          if (NS_FAILED(rv)) {
+            mState = iconStateFinished;
+            return;
+          }
+        }
+
         // Book Keeping
         aBuffer++;
         aCount--;
         mState = iconStateReadPixels;
         break;
 
       case iconStateReadPixels: {
 
         // How many bytes are we reading?
-        bytesToRead = std::min(aCount, mImageDataLength - mPixBytesRead);
+        uint32_t bytesToRead = min(aCount, mExpectedDataLength - mPixBytesRead);
+
+        if (mDownscaler) {
+          uint8_t* row = mDownscaler->RowBuffer();
+          const uint32_t bytesPerRow = mWidth * 4;
+          const uint32_t rowOffset = mPixBytesRead % bytesPerRow;
 
-        // Copy the bytes
-        memcpy(mImageData + mPixBytesRead, aBuffer, bytesToRead);
+          // Update global state; we're about to read |bytesToRead| bytes.
+          aCount -= bytesToRead;
+          mPixBytesRead += bytesToRead;
+
+          if (rowOffset > 0) {
+            // Finish the current row.
+            const uint32_t remaining = bytesPerRow - rowOffset;
+            memcpy(row + rowOffset, aBuffer, remaining);
+            aBuffer += remaining;
+            bytesToRead -= remaining;
+            mDownscaler->CommitRow();
+          }
 
-        // Performance isn't critical here, so our update rectangle is
-        // always the full icon
-        nsIntRect r(0, 0, mWidth, mHeight);
+          // Copy the bytes a row at a time.
+          while (bytesToRead > bytesPerRow) {
+            memcpy(row, aBuffer, bytesPerRow);
+            aBuffer += bytesPerRow;
+            bytesToRead -= bytesPerRow;
+            mDownscaler->CommitRow();
+          }
+
+          // Copy any leftover bytes. (Leaving the current row incomplete.)
+          if (bytesToRead > 0) {
+            memcpy(row, aBuffer, bytesToRead);
+            aBuffer += bytesPerRow;
+            bytesToRead -= bytesPerRow;
+          }
 
-        // Invalidate
-        PostInvalidation(r);
+          if (mDownscaler->HasInvalidation()) {
+            DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
+            PostInvalidation(invalidRect.mOriginalSizeRect,
+                             Some(invalidRect.mTargetSizeRect));
+          }
+        } else {
+          // Copy all the bytes at once.
+          memcpy(mImageData + mPixBytesRead, aBuffer, bytesToRead);
+          aBuffer += bytesToRead;
+          aCount -= bytesToRead;
+          mPixBytesRead += bytesToRead;
 
-        // Book Keeping
-        aBuffer += bytesToRead;
-        aCount -= bytesToRead;
-        mPixBytesRead += bytesToRead;
+          // Invalidate. Performance isn't critical here, so our update
+          // rectangle is always the full icon.
+          PostInvalidation(IntRect(0, 0, mWidth, mHeight));
+        }
 
         // If we've got all the pixel bytes, we're finished
-        if (mPixBytesRead == mImageDataLength) {
+        if (mPixBytesRead == mExpectedDataLength) {
           PostFrameStop();
           PostDecodeDone();
           mState = iconStateFinished;
         }
         break;
       }
 
       case iconStateFinished:
--- a/image/decoders/nsIconDecoder.h
+++ b/image/decoders/nsIconDecoder.h
@@ -34,29 +34,33 @@ class RasterImage;
 //
 ////////////////////////////////////////////////////////////////////////////////
 
 class nsIconDecoder : public Decoder
 {
 public:
   virtual ~nsIconDecoder();
 
+  virtual nsresult SetTargetSize(const nsIntSize& aSize) override;
+
   virtual void WriteInternal(const char* aBuffer, uint32_t aCount) override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsIconDecoder(RasterImage* aImage);
 
-public:
+  Maybe<Downscaler> mDownscaler;
+
+  uint32_t mExpectedDataLength;
+  uint32_t mPixBytesRead;
+  uint32_t mState;
   uint8_t mWidth;
   uint8_t mHeight;
-  uint32_t mPixBytesRead;
-  uint32_t mState;
 };
 
 enum {
   iconStateStart      = 0,
   iconStateHaveHeight = 1,
   iconStateReadPixels = 2,
   iconStateFinished   = 3
 };
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1309,16 +1309,17 @@ if test "$GNU_CXX"; then
     # -Wignored-qualifiers - catches returns types with qualifiers like const
     # -Wint-to-pointer-cast - catches cast to pointer from integer of different size
     # -Wmissing-braces - catches aggregate initializers missing nested braces
     # -Wnon-literal-null-conversion - catches expressions used as a null pointer constant
     # -Woverloaded-virtual - function declaration hides virtual function from base class
     # -Wparentheses - catches `if (a=b)` and operator precedence bugs
     # -Wpointer-arith - catches pointer arithmetic using NULL or sizeof(void)
     # -Wpointer-to-int-cast - catches casts from pointer to different sized int
+    # -Wrange-loop-analysis - catches copies during range-based for loops.
     # -Wreorder - catches ctor initializer list not matching class definition order
     # -Wreturn-type - catches missing returns, zero false positives
     # -Wsequence-point - catches undefined order behavior like `a = a++`
     # -Wsign-compare - catches comparison of signed and unsigned types
     # -Wswitch - catches switches without all enum cases or default case
     # -Wtrigraphs - catches unlikely use of trigraphs
     # -Wtype-limits - catches overflow bugs, few false positives
     # -Wunknown-pragmas - catches unexpected #pragma directives
@@ -1349,16 +1350,17 @@ if test "$GNU_CXX"; then
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=uninitialized"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unknown-pragmas"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unused-label"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=unused-value"
         _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Werror=write-strings"
 
         MOZ_CXX_SUPPORTS_WARNING(-Werror=, conversion-null, ac_cxx_has_werror_conversion_null)
         MOZ_CXX_SUPPORTS_WARNING(-Werror=, non-literal-null-conversion, ac_cxx_has_werror_non_literal_null_conversion)
+        MOZ_CXX_SUPPORTS_WARNING(-Werror=, range-loop-analysis, ac_cxx_has_range_loop_analysis)
         MOZ_CXX_SUPPORTS_WARNING(-Werror=, sometimes-uninitialized, ac_cxx_has_sometimes_uninitialized)
     fi
 
     # Turn off the following warnings that -Wall turns on:
     # -Wno-invalid-offsetof - we use offsetof on non-POD types frequently
     # -Wno-unused-local-typedef - catches unused typedefs, which are commonly used in assertion macros
     #
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-invalid-offsetof"
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -2615,17 +2615,18 @@ NS_IMETHODIMP nsDocumentViewer::SelectAl
 
   mozilla::dom::Selection::AutoApplyUserSelectStyle userSelection(selection);
   rv = selection->SelectAllChildren(bodyNode);
   return rv;
 }
 
 NS_IMETHODIMP nsDocumentViewer::CopySelection()
 {
-  nsCopySupport::FireClipboardEvent(NS_COPY, nsIClipboard::kGlobalClipboard, mPresShell, nullptr);
+  nsCopySupport::FireClipboardEvent(eCopy, nsIClipboard::kGlobalClipboard,
+                                    mPresShell, nullptr);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsDocumentViewer::CopyLinkLocation()
 {
   NS_ENSURE_TRUE(mPresShell, NS_ERROR_NOT_INITIALIZED);
   nsCOMPtr<nsIDOMNode> node;
   GetPopupLinkNode(getter_AddRefs(node));
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5315,17 +5315,17 @@ bool PresShell::AsyncPanZoomEnabled()
 {
   NS_ASSERTION(mViewManager, "Should have view manager");
   nsView* rootView = mViewManager->GetRootView();
   if (rootView) {
     if (nsIWidget* widget = rootView->GetWidget()) {
       return widget->AsyncPanZoomEnabled();
     }
   }
-  return false;
+  return gfxPlatform::AsyncPanZoomEnabled();
 }
 
 void PresShell::SetIgnoreViewportScrolling(bool aIgnore)
 {
   if (IgnoringViewportScrolling() == aIgnore) {
     return;
   }
   RenderingState state(this);
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -366,16 +366,21 @@ CommonAnimationManager::FlushAnimations(
 {
   TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();
   for (AnimationCollection* collection = mElementCollections.getFirst();
        collection; collection = collection->getNext()) {
     if (collection->mStyleRuleRefreshTime == now) {
       continue;
     }
 
+    MOZ_ASSERT(collection->mElement->GetComposedDoc() ==
+                 mPresContext->Document(),
+               "Should not have a transition/animations collection for an "
+               "element that is not part of the document tree");
+
     collection->RequestRestyle(AnimationCollection::RestyleType::Standard);
   }
 }
 
 nsIStyleRule*
 CommonAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement,
                                          nsCSSPseudoElements::Type aPseudoType)
 {
@@ -878,20 +883,16 @@ AnimationCollection::RequestRestyle(Rest
              "Unexpected mElementProperty; might restyle too much");
 
   nsPresContext* presContext = mManager->PresContext();
   if (!presContext) {
     // Pres context will be null after the manager is disconnected.
     return;
   }
 
-  MOZ_ASSERT(mElement->GetCrossShadowCurrentDoc() == presContext->Document(),
-             "Element::UnbindFromTree should have destroyed the element "
-             "transition/animations object");
-
   // Steps for Restyle::Layer:
 
   if (aRestyleType == RestyleType::Layer) {
     mStyleRuleRefreshTime = TimeStamp();
     // FIXME: We should be able to remove these two lines once we move
     // ticking to animation timelines as part of bug 1151731.
     mNeedsRefreshes = true;
     mManager->MaybeStartObservingRefreshDriver();
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1200568-1.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<head>
+<style>
+.anim { animation: anim 2s infinite linear }
+@keyframes anim { }
+</style>
+</head>
+<body>
+<script>
+var i = document.createElement('i');
+i.setAttribute('class', 'anim');
+getComputedStyle(i).display;
+</script>
+</body>
+</html>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -115,10 +115,11 @@ pref(layout.css.expensive-style-struct-a
 pref(layout.css.expensive-style-struct-assertions.enabled,true) load 1146101-1.html
 load 1153693-1.html
 load 1161320-1.html
 pref(dom.animations-api.core.enabled,true) load 1161320-2.html
 load 1161366-1.html
 load 1163446-1.html
 load 1164813-1.html
 load 1167782-1.html
+load 1200568-1.html
 load large_border_image_width.html
 load border-image-visited-link.html
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -366,18 +366,19 @@ nsAnimationManager::SizeOfIncludingThis(
 {
   return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
 }
 
 nsIStyleRule*
 nsAnimationManager::CheckAnimationRule(nsStyleContext* aStyleContext,
                                        mozilla::dom::Element* aElement)
 {
-  if (!mPresContext->IsDynamic()) {
-    // For print or print preview, ignore animations.
+  // Ignore animations for print or print preview, and for elements
+  // that are not attached to the document tree.
+  if (!mPresContext->IsDynamic() || !aElement->IsInComposedDoc()) {
     return nullptr;
   }
 
   // Everything that causes our animation data to change triggers a
   // style change, which in turn triggers a non-animation restyle.
   // Likewise, when we initially construct frames, we're not in a
   // style change, but also not in an animation restyle.
 
--- a/layout/xul/nsXULTooltipListener.cpp
+++ b/layout/xul/nsXULTooltipListener.cpp
@@ -353,17 +353,22 @@ nsXULTooltipListener::CheckTreeBodyMove(
     x -= boxX;
     y -= boxY;
 
     obx->GetCellAt(x, y, &row, getter_AddRefs(col), obj);
 
     // determine if we are going to need a titletip
     // XXX check the disabletitletips attribute on the tree content
     mNeedTitletip = false;
-    if (row >= 0 && obj.EqualsLiteral("text")) {
+    int16_t colType = -1;
+    if (col) {
+      col->GetType(&colType);
+    }
+    if (row >= 0 && obj.EqualsLiteral("text") &&
+        colType != nsITreeColumn::TYPE_PASSWORD) {
       obx->IsCellCropped(row, col, &mNeedTitletip);
     }
 
     nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
     if (currentTooltip && (row != mLastTreeRow || col != mLastTreeCol)) {
       HideTooltip();
     } 
 
--- a/layout/xul/tree/nsITreeColumns.idl
+++ b/layout/xul/tree/nsITreeColumns.idl
@@ -27,16 +27,17 @@ interface nsITreeColumn : nsISupports
   readonly attribute boolean primary;
   readonly attribute boolean cycler;
   readonly attribute boolean editable;
   readonly attribute boolean selectable;
 
   const short TYPE_TEXT                = 1;
   const short TYPE_CHECKBOX            = 2;
   const short TYPE_PROGRESSMETER       = 3;
+  const short TYPE_PASSWORD            = 4;
   readonly attribute short type;
 
   nsITreeColumn getNext();
   nsITreeColumn getPrevious();
 
   void invalidate();
 };
 
--- a/layout/xul/tree/nsTreeBodyFrame.cpp
+++ b/layout/xul/tree/nsTreeBodyFrame.cpp
@@ -59,16 +59,17 @@
 #include "nsLayoutUtils.h"
 #include "nsIScrollableFrame.h"
 #include "nsDisplayList.h"
 #include "mozilla/dom/TreeBoxObject.h"
 #include "nsRenderingContext.h"
 #include "nsIScriptableRegion.h"
 #include <algorithm>
 #include "ScrollbarActivity.h"
+#include "../../editor/libeditor/nsTextEditRules.h"
 
 #ifdef ACCESSIBILITY
 #include "nsAccessibilityService.h"
 #include "nsIWritablePropertyBag2.h"
 #endif
 #include "nsBidiUtils.h"
 
 using namespace mozilla;
@@ -3304,16 +3305,17 @@ nsTreeBodyFrame::PaintCell(int32_t      
   // XXX until we have the ability to load images, allow the view to 
   // insert text into cycler columns...
   if (!aColumn->IsCycler()) {
     nsRect elementRect(currX, cellRect.y, remainingWidth, cellRect.height);
     nsRect dirtyRect;
     if (dirtyRect.IntersectRect(aDirtyRect, elementRect)) {
       switch (aColumn->GetType()) {
         case nsITreeColumn::TYPE_TEXT:
+        case nsITreeColumn::TYPE_PASSWORD:
           PaintText(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect, currX);
           break;
         case nsITreeColumn::TYPE_CHECKBOX:
           PaintCheckbox(aRowIndex, aColumn, elementRect, aPresContext, aRenderingContext, aDirtyRect);
           break;
         case nsITreeColumn::TYPE_PROGRESSMETER:
           int32_t state;
           mView->GetProgressMode(aRowIndex, aColumn, &state);
@@ -3590,16 +3592,21 @@ nsTreeBodyFrame::PaintText(int32_t      
 {
   NS_PRECONDITION(aColumn && aColumn->GetFrame(), "invalid column passed");
 
   bool isRTL = StyleVisibility()->mDirection == NS_STYLE_DIRECTION_RTL;
 
   // Now obtain the text for our cell.
   nsAutoString text;
   mView->GetCellText(aRowIndex, aColumn, text);
+
+  if (aColumn->Type() == nsITreeColumn::TYPE_PASSWORD) {
+    nsTextEditRules::FillBufWithPWChars(&text, text.Length());
+  }
+
   // We're going to paint this text so we need to ensure bidi is enabled if
   // necessary
   CheckTextForBidi(text);
 
   if (text.Length() == 0)
     return; // Don't paint an empty string. XXX What about background/borders? Still paint?
 
   int32_t appUnitsPerDevPixel = PresContext()->AppUnitsPerDevPixel();
--- a/layout/xul/tree/nsTreeColumns.cpp
+++ b/layout/xul/tree/nsTreeColumns.cpp
@@ -316,21 +316,23 @@ nsTreeColumn::Invalidate()
                                          nsGkAtoms::_false, eCaseMatters);
 
   mOverflow = mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::overflow,
                                     nsGkAtoms::_true, eCaseMatters);
 
   // Figure out our column type. Default type is text.
   mType = nsITreeColumn::TYPE_TEXT;
   static nsIContent::AttrValuesArray typestrings[] =
-    {&nsGkAtoms::checkbox, &nsGkAtoms::progressmeter, nullptr};
+    {&nsGkAtoms::checkbox, &nsGkAtoms::progressmeter, &nsGkAtoms::password,
+     nullptr};
   switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::type,
                                     typestrings, eCaseMatters)) {
     case 0: mType = nsITreeColumn::TYPE_CHECKBOX; break;
     case 1: mType = nsITreeColumn::TYPE_PROGRESSMETER; break;
+    case 2: mType = nsITreeColumn::TYPE_PASSWORD; break;
   }
 
   // Fetch the crop style.
   mCropStyle = 0;
   static nsIContent::AttrValuesArray cropstrings[] =
     {&nsGkAtoms::center, &nsGkAtoms::left, &nsGkAtoms::start, nullptr};
   switch (mContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::crop,
                                     cropstrings, eCaseMatters)) {
--- a/mfbt/PodOperations.h
+++ b/mfbt/PodOperations.h
@@ -175,11 +175,22 @@ PodEqual(const T* one, const T* two, siz
       }
     }
     return true;
   }
 
   return !memcmp(one, two, len * sizeof(T));
 }
 
+/*
+ * Determine whether the |N| elements at |one| are memory-identical to the
+ * |N| elements at |two|.
+ */
+template <class T, size_t N>
+static MOZ_ALWAYS_INLINE bool
+PodEqual(const T (&one)[N], const T (&two)[N])
+{
+  return PodEqual(one, two, N);
+}
+
 } // namespace mozilla
 
 #endif /* mozilla_PodOperations_h */
--- a/mobile/android/base/overlays/ui/ShareDialog.java
+++ b/mobile/android/base/overlays/ui/ShareDialog.java
@@ -343,20 +343,25 @@ public class ShareDialog extends Locales
         serviceIntent.putExtra(OverlayConstants.EXTRA_URL, url);
         serviceIntent.putExtra(OverlayConstants.EXTRA_TITLE, title);
 
         return serviceIntent;
     }
 
     @Override
     public void finish() {
-        super.finish();
+        finish(true);
+    }
 
-        // Don't perform an activity-dismiss animation.
-        overridePendingTransition(0, 0);
+    private void finish(final boolean shouldOverrideAnimations) {
+        super.finish();
+        if (shouldOverrideAnimations) {
+            // Don't perform an activity-dismiss animation.
+            overridePendingTransition(0, 0);
+        }
     }
 
     /*
      * Button handlers. Send intents to the background service responsible for processing requests
      * on Fennec in the background. (a nice extensible mechanism for "doing stuff without properly
      * launching Fennec").
      */
 
@@ -408,17 +413,18 @@ public class ShareDialog extends Locales
         try {
             // This can launch in the guest profile. Sorry.
             final Intent i = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
             i.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
             startActivity(i);
         } catch (URISyntaxException e) {
             // Nothing much we can do.
         } finally {
-            slideOut();
+            // Since we're changing apps, users expect the default app switch animations.
+            finish(false);
         }
     }
 
     private String getCurrentProfile() {
         return GeckoProfile.DEFAULT_PROFILE;
     }
 
     /**
--- a/mobile/android/tests/browser/robocop/robocop_autophone.ini
+++ b/mobile/android/tests/browser/robocop/robocop_autophone.ini
@@ -1,84 +1,1 @@
-[testAboutPage]
-
-#[testAddonManager]
-# fails on gs2, nexus one, lg revolution, droid pro, nexus s
-
-[testAwesomebarSwipes]
-
-[testAwesomebar]
-
-#[testAxisLocking]
-# fails on gs2, nexus one, lg revolution, droid pro, nexus s
-
-[testBookmark]
-# fails on gs2 4.0.3
-
-[testBookmarklets]
-
-[testBrowserProvider]
-# fails on gs2 4.0.3
-
-#[testClearPrivateData]
-# fails on gs2, nexus-one, lg revolution, droid pro, nexus s
-
-#[testDoorHanger]
-# fails on gs2, nexus-one, lg revolution, droid pro, nexus s
-
-#[testFlingCorrectness]
-# fails on gs2, nexus one, lg revolution, droid pro, nexus s
-
-[testFormHistory]
-
-#[testHistory]
-# fails on gs2, nexus one,lg revolution, droid pro, nexus s
-
-#[testJarReader]
-# fails on gs2, nexus one, lg revolution, droid pro, nexus s
-
-#[testLoad]
-# fails on gs2, nexus one, lg revolution, droid pro, nexus s
-
-[testNewTab]
-# fails on nexus s random crash [@ libpvrANDROID_WSEGL.so + 0x73c]
-
-#[testPanCorrectness]
-# fails on gs2, nexus one, lg revolution, nexus s
-
-#[testPasswordEncrypt]
-# fails on gs2, nexus one, lg revolution, nexus s
-
-[testPasswordProvider]
-
-#[testPermissions]
-# fails on gs2, nexus one, lg revolution, nexus s
-
-#[testSearchSuggestions]
-# fails on gs2, nexus one, lg revolution, nexus s
-
-#[testSettingsMenuItems]
-# fails on nexus one, lg revolution, nexus s
-
-#[testShareLink]
-# fails on gs2, nexus one, lg revolution, nexus s
-
-[testSystemPages]
-
-#[testTabHistory]
-# fails on gs2, nexus one, lg revolution, nexus s
-
-#[testTabsLayoutMenu]
-# fails on gs2, nexus one, lg revolution, nexus s
-
-#[testThumbnails]
-# fails on gs2, nexus one, lg revolution, nexus s
-
-#[testVkbOverlap]
-# fails on gs2, nexus one, lg revolution, nexus s
-
-#[testWebContentContextMenu]
-# fails on gs2, nexus one, lg revolution, nexus s
-
-#[test_bug720538]
-# fails on gs2, nexus one, lg revolution, nexus s
-
-[testBrowserSearchVisibility]
+[testAdobeFlash]
--- a/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java
+++ b/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java
@@ -18,17 +18,19 @@ package com.keepsafe.switchboard;
 import java.io.BufferedReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
 import java.net.ProtocolException;
 import java.net.URL;
 import java.util.Locale;
+import java.util.MissingResourceException;
 import java.util.UUID;
+import java.util.zip.CRC32;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 
 import android.content.Context;
 import android.content.pm.PackageManager.NameNotFoundException;
 import android.os.Build;
 import android.util.Log;
@@ -183,18 +185,28 @@ public class SwitchBoard {
 			//get uuid
 			if(uuid == null) {
 				DeviceUuidFactory df = new DeviceUuidFactory(c);
 				uuid = df.getDeviceUuid().toString();
 			}
 			
 			String device = Build.DEVICE;
 			String manufacturer = Build.MANUFACTURER;
-			String lang = Locale.getDefault().getISO3Language();
-			String country = Locale.getDefault().getISO3Country();			
+			String lang = "unknown";
+			try {
+				lang = Locale.getDefault().getISO3Language();
+			} catch (MissingResourceException e) {
+				e.printStackTrace();
+			}
+			String country = "unknown";
+			try {
+				country = Locale.getDefault().getISO3Country();
+			} catch (MissingResourceException e) {
+				e.printStackTrace();
+			}
 			String packageName = c.getPackageName();
 			String versionName = "none";
 			try {
 				versionName = c.getPackageManager().getPackageInfo(c.getPackageName(), 0).versionName;
 			} catch (NameNotFoundException e) {
 				e.printStackTrace();
 			}
 			
@@ -213,17 +225,25 @@ public class SwitchBoard {
 				if(serverConfig != null)
 					Preferences.setDynamicConfigJson(c, serverConfig);
 			}
 			
 		} catch (NullPointerException e) {
 			e.printStackTrace();
 		}
 	}
-	
+
+	public static boolean isInBucket(Context c, int low, int high) {
+		int userBucket = getUserBucket(c);
+		if (userBucket >= low && userBucket < high)
+			return true;
+		else
+			return false;
+	}
+
 	/**
 	 * Looks up in config if user is in certain experiment. Returns false as a default value when experiment
 	 * does not exist.
 	 * Experiment names are defined server side as Key in array for return values.
 	 * @param experimentName Name of the experiment to lookup
 	 * @return returns value for experiment or false if experiment does not exist.
 	 */
 	public static boolean isInExperiment(Context c, String experimentName) {
@@ -365,9 +385,23 @@ public class SwitchBoard {
 		} catch (ProtocolException e) {
 			e.printStackTrace();
 		} catch (IOException e) {
 			e.printStackTrace();
 		}
 
 		return null;
 	}
+
+	/**
+	 * Return the bucket number of the user. There are 100 possible buckets.
+	 */
+	private static int getUserBucket(Context c) {
+		//get uuid
+		DeviceUuidFactory df = new DeviceUuidFactory(c);
+		String uuid = df.getDeviceUuid().toString();
+
+		CRC32 crc = new CRC32();
+		crc.update(uuid.getBytes());
+		long checksum = crc.getValue();
+		return (int)(checksum % 100L);
+	}
 }
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1781,17 +1781,17 @@ pref("network.automatic-ntlm-auth.truste
 pref("network.generic-ntlm-auth.workstation", "WORKSTATION");
 
 // Sub-resources HTTP-authentication:
 //   0 - don't allow sub-resources to open HTTP authentication credentials
 //       dialogs
 //   1 - allow sub-resources to open HTTP authentication credentials dialogs,
 //       but don't allow it for cross-origin sub-resources
 //   2 - allow the cross-origin authentication as well.
-pref("network.auth.allow-subresource-auth", 2);
+pref("network.auth.subresource-http-auth-allow", 2);
 
 pref("permissions.default.image",           1); // 1-Accept, 2-Deny, 3-dontAcceptForeign
 
 pref("network.proxy.type",                  5);
 pref("network.proxy.ftp",                   "");
 pref("network.proxy.ftp_port",              0);
 pref("network.proxy.http",                  "");
 pref("network.proxy.http_port",             0);
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -1283,16 +1283,18 @@ HttpBaseChannel::GetProxyURI(nsIURI **aO
   result.forget(aOut);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetRequestHeader(const nsACString& aHeader,
                                   nsACString& aValue)
 {
+  aValue.Truncate();
+
   // XXX might be better to search the header list directly instead of
   // hitting the http atom hash table.
   nsHttpAtom atom = nsHttp::ResolveAtom(aHeader);
   if (!atom)
     return NS_ERROR_NOT_AVAILABLE;
 
   return mRequestHead.GetHeader(atom, aValue);
 }
@@ -1328,16 +1330,18 @@ NS_IMETHODIMP
 HttpBaseChannel::VisitRequestHeaders(nsIHttpHeaderVisitor *visitor)
 {
   return mRequestHead.Headers().VisitHeaders(visitor);
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::GetResponseHeader(const nsACString &header, nsACString &value)
 {
+  value.Truncate();
+
   if (!mResponseHead)
     return NS_ERROR_NOT_AVAILABLE;
 
   nsHttpAtom atom = nsHttp::ResolveAtom(header);
   if (!atom)
     return NS_ERROR_NOT_AVAILABLE;
 
   return mResponseHead->GetHeader(atom, value);
--- a/netwerk/protocol/http/NullHttpChannel.cpp
+++ b/netwerk/protocol/http/NullHttpChannel.cpp
@@ -90,16 +90,17 @@ NS_IMETHODIMP
 NullHttpChannel::SetReferrerWithPolicy(nsIURI *referrer, uint32_t referrerPolicy)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::GetRequestHeader(const nsACString & aHeader, nsACString & _retval)
 {
+  _retval.Truncate();
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::SetRequestHeader(const nsACString & aHeader, const nsACString & aValue, bool aMerge)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
@@ -162,16 +163,17 @@ NS_IMETHODIMP
 NullHttpChannel::GetRequestSucceeded(bool *aRequestSucceeded)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::GetResponseHeader(const nsACString & header, nsACString & _retval)
 {
+  _retval.Truncate();
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 NullHttpChannel::SetResponseHeader(const nsACString & header, const nsACString & value, bool merge)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
--- a/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
+++ b/netwerk/protocol/http/nsHttpChannelAuthProvider.cpp
@@ -70,25 +70,25 @@ nsHttpChannelAuthProvider::nsHttpChannel
 }
 
 nsHttpChannelAuthProvider::~nsHttpChannelAuthProvider()
 {
     MOZ_ASSERT(!mAuthChannel, "Disconnect wasn't called");
 }
 
 uint32_t nsHttpChannelAuthProvider::sAuthAllowPref =
-    SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN;
+    SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL;
 
 void
 nsHttpChannelAuthProvider::InitializePrefs()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mozilla::Preferences::AddUintVarCache(&sAuthAllowPref,
-                                        "network.auth.allow-subresource-auth",
-                                        SUBRESOURCE_AUTH_DIALOG_DISALLOW_CROSS_ORIGIN);
+                                        "network.auth.subresource-http-auth-allow",
+                                        SUBRESOURCE_AUTH_DIALOG_ALLOW_ALL);
 }
 
 NS_IMETHODIMP
 nsHttpChannelAuthProvider::Init(nsIHttpAuthenticableChannel *channel)
 {
     MOZ_ASSERT(channel, "channel expected!");
 
     mAuthChannel = channel;
--- a/netwerk/protocol/http/nsHttpNTLMAuth.cpp
+++ b/netwerk/protocol/http/nsHttpNTLMAuth.cpp
@@ -4,17 +4,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // HttpLog.h should generally be included first
 #include "HttpLog.h"
 
 #include "nsHttpNTLMAuth.h"
 #include "nsIAuthModule.h"
 #include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
 #include "plbase64.h"
+#include "plstr.h"
 #include "prnetdb.h"
 
 //-----------------------------------------------------------------------------
 
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsIURI.h"
--- a/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
+++ b/netwerk/protocol/viewsource/nsViewSourceChannel.cpp
@@ -728,16 +728,17 @@ nsViewSourceChannel::SetReferrerWithPoli
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->SetReferrerWithPolicy(aReferrer, aReferrerPolicy);
 }
 
 NS_IMETHODIMP
 nsViewSourceChannel::GetRequestHeader(const nsACString & aHeader,
                                       nsACString & aValue)
 {
+    aValue.Truncate();
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->GetRequestHeader(aHeader, aValue);
 }
 
 NS_IMETHODIMP
 nsViewSourceChannel::SetRequestHeader(const nsACString & aHeader,
                                       const nsACString & aValue,
                                       bool aMerge)
@@ -815,31 +816,31 @@ nsViewSourceChannel::GetRequestSucceeded
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
         mHttpChannel->GetRequestSucceeded(aRequestSucceeded);
 }
 
 NS_IMETHODIMP
 nsViewSourceChannel::GetResponseHeader(const nsACString & aHeader,
                                        nsACString & aValue)
 {
+    aValue.Truncate();
     if (!mHttpChannel)
         return NS_ERROR_NULL_POINTER;
 
     if (!aHeader.Equals(NS_LITERAL_CSTRING("Content-Type"),
                         nsCaseInsensitiveCStringComparator()) &&
         !aHeader.Equals(NS_LITERAL_CSTRING("Content-Security-Policy"),
                         nsCaseInsensitiveCStringComparator()) &&
         !aHeader.Equals(NS_LITERAL_CSTRING("Content-Security-Policy-Report-Only"),
                         nsCaseInsensitiveCStringComparator()) &&
         !aHeader.Equals(NS_LITERAL_CSTRING("X-Frame-Options"),
                         nsCaseInsensitiveCStringComparator())) {
-        aValue.Truncate();
         return NS_OK;
     }
-        
+
     return mHttpChannel->GetResponseHeader(aHeader, aValue);
 }
 
 NS_IMETHODIMP
 nsViewSourceChannel::SetResponseHeader(const nsACString & header,
                                        const nsACString & value, bool merge)
 {
     return !mHttpChannel ? NS_ERROR_NULL_POINTER :
--- a/netwerk/test/unit/test_auth_dialog_permission.js
+++ b/netwerk/test/unit/test_auth_dialog_permission.js
@@ -1,10 +1,10 @@
 // This file tests authentication prompt depending on pref
-// network.auth.allow-subresource-auth:
+// network.auth.subresource-http-auth-allow:
 //   0 - don't allow sub-resources to open HTTP authentication credentials
 //       dialogs
 //   1 - allow sub-resources to open HTTP authentication credentials dialogs,
 //       but don't allow it for cross-origin sub-resources
 //   2 - allow the cross-origin authentication as well.
 
 Cu.import("resource://testing-common/httpd.js");
 
@@ -125,27 +125,27 @@ function makeChan(loadingUrl, url, conte
                              null,
                              Ci.nsILoadInfo.SEC_NORMAL,
                              contentPolicy)
                 .QueryInterface(Components.interfaces.nsIHttpChannel);
 
   return chan;
 }
 
-function Test(allow_subresource_auth_pref, loadingUri, uri, contentPolicy,
+function Test(subresource_http_auth_allow_pref, loadingUri, uri, contentPolicy,
               expectedCode) {
-  this._allow_subresource_auth_pref = allow_subresource_auth_pref;
+  this._subresource_http_auth_allow_pref = subresource_http_auth_allow_pref;
   this._loadingUri = loadingUri;
   this._uri = uri;
   this._contentPolicy = contentPolicy;
   this._expectedCode = expectedCode;
 }
 
 Test.prototype = {
-  _allow_subresource_auth_pref: 1,
+  _subresource_http_auth_allow_pref: 1,
   _loadingUri: null,
   _uri: null,
   _contentPolicy: Ci.nsIContentPolicy.TYPE_OTHER,
   _expectedCode: 200,
 
   onStartRequest: function(request, ctx) {
     try {
       if (!Components.isSuccessCode(request.status)) {
@@ -178,24 +178,24 @@ Test.prototype = {
     Components.classes["@mozilla.org/network/http-auth-manager;1"]
               .getService(Components.interfaces.nsIHttpAuthManager)
               .clearAll();
 
     do_timeout(0, run_next_test);
   },
 
   run: function() {
-    dump("Run test: " + this._allow_subresource_auth_pref
+    dump("Run test: " + this._subresource_http_auth_allow_pref
                       + this._loadingUri
                       + this._uri
                       + this._contentPolicy
                       + this._expectedCode + " \n");
 
-    prefs.setIntPref("network.auth.allow-subresource-auth",
-                     this._allow_subresource_auth_pref);
+    prefs.setIntPref("network.auth.subresource-http-auth-allow",
+                     this._subresource_http_auth_allow_pref);
     let chan = makeChan(this._loadingUri, this._uri, this._contentPolicy);
     chan.notificationCallbacks = new Requestor(this._expectedCode == 200);
     chan.asyncOpen(this, null);
   }
 };
 
 var tests = [
   // For the next 3 tests the preference is set to 2 - allow the cross-origin
--- a/netwerk/test/unit/test_auth_proxy.js
+++ b/netwerk/test/unit/test_auth_proxy.js
@@ -240,17 +240,17 @@ function run_test() {
   const prefs = Cc["@mozilla.org/preferences-service;1"]
                          .getService(Ci.nsIPrefBranch);
   prefs.setCharPref("network.proxy.http", "localhost");
   prefs.setIntPref("network.proxy.http_port", httpserv.identity.primaryPort);
   prefs.setCharPref("network.proxy.no_proxies_on", "");
   prefs.setIntPref("network.proxy.type", 1);
 
   // Turn off the authentication dialog blocking for this test.
-  prefs.setIntPref("network.auth.allow-subresource-auth", 2);
+  prefs.setIntPref("network.auth.subresource-http-auth-allow", 2);
 
   tests[current_test]();
 }
 
 function test_proxy_returnfalse() {
   dump("\ntest: proxy returnfalse\n");
   var chan = makeChan();
   chan.notificationCallbacks = new Requestor(FLAG_RETURN_FALSE, 0);
--- a/netwerk/test/unit/test_authentication.js
+++ b/netwerk/test/unit/test_authentication.js
@@ -2,17 +2,17 @@
 // TODO NIT use do_check_eq(expected, actual) consistently, not sometimes eq(actual, expected)
 
 Cu.import("resource://testing-common/httpd.js");
 Cu.import("resource://gre/modules/Services.jsm");
 
 // Turn off the authentication dialog blocking for this test.
 var prefs = Cc["@mozilla.org/preferences-service;1"].
               getService(Ci.nsIPrefBranch);
-prefs.setIntPref("network.auth.allow-subresource-auth", 2);
+prefs.setIntPref("network.auth.subresource-http-auth-allow", 2);
 
 XPCOMUtils.defineLazyGetter(this, "URL", function() {
   return "http://localhost:" + httpserv.identity.primaryPort;
 });
 
 XPCOMUtils.defineLazyGetter(this, "PORT", function() {
   return httpserv.identity.primaryPort;
 });
--- a/testing/mozharness/mozharness/base/vcs/hgtool.py
+++ b/testing/mozharness/mozharness/base/vcs/hgtool.py
@@ -74,20 +74,20 @@ class HgtoolVCS(ScriptMixin, LogMixin, T
         env = {'PATH': os.environ.get('PATH')}
         if c.get('env'):
             env.update(c['env'])
         if share_base is not None:
             env['HG_SHARE_BASE_DIR'] = share_base
         if self._is_windows():
             # SYSTEMROOT is needed for 'import random'
             if 'SYSTEMROOT' not in env:
-                env['SYSTEMROOT'] = os.environ.get('SYSTEMROOT')
+                env['SYSTEMROOT'] = os.environ.get('SYSTEMROOT', '')
             # HOME is needed for the 'hg help share' check
             if 'HOME' not in env:
-                env['HOME'] = os.environ.get('HOME')
+                env['HOME'] = os.environ.get('HOME', '')
 
         cmd = self.hgtool[:]
 
         if clone_by_rev:
             cmd.append('--clone-by-revision')
         if branch:
             cmd.extend(['-b', branch])
         if revision:
--- a/testing/mozharness/mozharness/mozilla/building/buildbase.py
+++ b/testing/mozharness/mozharness/mozilla/building/buildbase.py
@@ -1236,17 +1236,17 @@ or run without that action (ie: --no-{ac
         else:
             # capitalize every word in between '-'
             branch_list = self.branch.split('-')
             branch_list = [elem.capitalize() for elem in branch_list]
             return '-'.join(branch_list)
 
     def _query_props_set_by_mach(self, console_output=True, error_level=FATAL):
         mach_properties_path = os.path.join(
-            self.query_abs_dirs()['abs_obj_dir'], 'mach_build_properties.json'
+            self.query_abs_dirs()['abs_obj_dir'], 'dist', 'mach_build_properties.json'
         )
         self.info("setting properties set by mach build. Looking in path: %s"
                   % mach_properties_path)
         if os.path.exists(mach_properties_path):
             with self.opened(mach_properties_path, error_level=error_level) as (fh, err):
                 build_props = json.load(fh)
                 if err:
                     self.log("%s exists but there was an error reading the "
@@ -1257,19 +1257,17 @@ or run without that action (ie: --no-{ac
                              error_level)
                 if console_output:
                     self.info("Properties set from 'mach build'")
                     self.info(pprint.pformat(build_props))
             for key, prop in build_props.iteritems():
                 if prop != 'UNKNOWN':
                     self.set_buildbot_property(key, prop, write_to_file=True)
         else:
-            self.log("Could not determine path for build properties. "
-                     "Does this exist: `%s` ?" % mach_properties_path,
-                     level=error_level)
+            self.info("No mach_build_properties.json found - not importing properties.")
 
     def generate_build_props(self, console_output=True, halt_on_failure=False):
         """sets props found from mach build and, in addition, buildid,
         sourcestamp,  appVersion, and appName."""
 
         error_level = ERROR
         if halt_on_failure:
             error_level = FATAL
@@ -1639,17 +1637,17 @@ or run without that action (ie: --no-{ac
             # the old package should live in source dir so we don't need to do
             # this for nighties since we clobber the whole work_dir in
             # clobber()
             self._rm_old_package()
         self._get_mozconfig()
         self._run_tooltool()
         self._create_mozbuild_dir()
         mach_props = os.path.join(
-            self.query_abs_dirs()['abs_obj_dir'], 'mach_build_properties.json'
+            self.query_abs_dirs()['abs_obj_dir'], 'dist', 'mach_build_properties.json'
         )
         if os.path.exists(mach_props):
             self.info("Removing previous mach property file: %s" % mach_props)
             self.rmtree(mach_props)
 
     def build(self):
         """builds application."""
         env = self.query_build_env()
--- a/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py
+++ b/testing/mozharness/mozharness/mozilla/testing/firefox_ui_tests.py
@@ -160,16 +160,23 @@ class FirefoxUITests(VCSToolsScript, Vir
                 return symbols_url
 
             except urllib2.HTTPError, e:
                 self.warning('{} - {}'.format(str(e), symbols_url))
                 return None
         else:
             self.fatal('Can\'t find symbols_url from installer_url: {}!'.format(installer_url))
 
+    @PreScriptAction('checkout')
+    def _pre_checkout(self, action):
+        if not self.firefox_ui_branch:
+            self.fatal(
+                'Please specify --firefox-ui-branch. Valid values can be found '
+                'in here https://github.com/mozilla/firefox-ui-tests/branches')
+
     def checkout(self):
         """
         We checkout firefox_ui_tests and update to the right branch
         for it.
         """
         dirs = self.query_abs_dirs()
 
         self.vcs_checkout(
@@ -198,23 +205,24 @@ class FirefoxUITests(VCSToolsScript, Vir
         """
         return []
 
     def run_test(self, installer_path, script_name, env=None, symbols_url=None,
                  cleanup=True, marionette_port=2828):
         """All required steps for running the tests against an installer."""
         dirs = self.query_abs_dirs()
 
-        bin_dir = os.path.dirname(self.query_python_path())
-        fx_ui_tests_bin = os.path.join(bin_dir, script_name)
+        venv_python_path = self.query_python_path()
+        update_script = os.path.join(dirs['fx_ui_dir'], 'firefox_ui_harness', 'cli_update.py')
         gecko_log = os.path.join(dirs['abs_log_dir'], 'gecko.log')
 
         # Build the command
         cmd = [
-            fx_ui_tests_bin,
+            venv_python_path,
+            update_script,
             '--installer', installer_path,
             # Log to stdout until tests are stable.
             '--gecko-log=-',
             '--address', 'localhost:{}'.format(marionette_port),
             # Use the work dir to get temporary data stored
             '--workspace', dirs['abs_work_dir'],
         ]
 
--- a/testing/mozharness/scripts/firefox_ui_tests/update_release.py
+++ b/testing/mozharness/scripts/firefox_ui_tests/update_release.py
@@ -209,18 +209,19 @@ class ReleaseFirefoxUIUpdateTests(Firefo
             chunks=int(self.config['total_chunks']),
             thisChunk=int(self.config['this_chunk'])
         )
 
         self.releases = chunked_config.releases
 
     @PreScriptAction('run-tests')
     def _pre_run_tests(self, action):
-        assert 'release_update_config' in self.config, \
-            'You have to specify --release-update-config.'
+        assert ('release_update_config' in self.config or
+                self.installer_url or self.installer_path),
+                'Either specify --update-verify-config, --installer-url or --installer-path.'
 
     def run_tests(self):
         dirs = self.query_abs_dirs()
 
         # We don't want multiple outputs of the same environment information. To prevent
         # that, we can't make it an argument of run_command and have to print it on our own.
         self.info('Using env: {}'.format(pprint.pformat(self.query_env())))
 
--- a/toolkit/components/passwordmgr/test/test_prompt_async.html
+++ b/toolkit/components/passwordmgr/test/test_prompt_async.html
@@ -9,17 +9,17 @@
     <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
 
     <script class="testbody" type="text/javascript">
         SimpleTest.waitForExplicitFinish();
         SimpleTest.requestFlakyTimeout("untriaged");
 
         var prefs = Cc["@mozilla.org/preferences-service;1"].
                         getService(Ci.nsIPrefBranch);
-        prefs.setIntPref("network.auth.allow-subresource-auth", 2);
+        prefs.setIntPref("network.auth.subresource-http-auth-allow", 2);
         // Class monitoring number of open dialog windows
         // It checks there is always open just a single dialog per application
         function dialogMonitor() {
             var observerService = SpecialPowers.Cc["@mozilla.org/observer-service;1"]
                                                .getService(Ci.nsIObserverService);
             observerService.addObserver(this, "domwindowopened", false);
             observerService.addObserver(this, "domwindowclosed", false);
         }
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -4239,16 +4239,21 @@
     "extended_statistics_ok": true,
     "description": "Session restore: Time spent blocking the main thread while restoring a window state (ms)"
   },
   "FX_TABLET_MODE_USED_DURING_SESSION": {
     "expires_in_version": "46",
     "kind": "count",
     "description": "Windows 10+ only: The number of times tablet-mode is used during a session"
   },
+  "FX_TOUCH_USED": {
+    "expires_in_version": "46",
+    "kind": "count",
+    "description": "Windows only. Counts occurrences of touch events"
+  },
   "FX_URLBAR_SELECTED_RESULT_INDEX": {
     "expires_in_version": "45",
     "kind": "enumerated",
     "n_values": 17,
     "description": "Firefox: The index of the selected result in the URL bar popup"
   },
   "FX_URLBAR_SELECTED_RESULT_TYPE": {
     "expires_in_version": "45",
--- a/toolkit/content/widgets/tree.xml
+++ b/toolkit/content/widgets/tree.xml
@@ -336,18 +336,20 @@
         <parameter name="row"/>
         <parameter name="column"/>
         <body>
           <![CDATA[
             if (!this.editable)
               return false;
             if (row < 0 || row >= this.view.rowCount || !column)
               return false;
-            if (column.type != Components.interfaces.nsITreeColumn.TYPE_TEXT ||
-                column.cycler || !this.view.isEditable(row, column))
+            if (column.type != Components.interfaces.nsITreeColumn.TYPE_TEXT &&
+                column.type != Components.interfaces.nsITreeColumn.TYPE_PASSWORD)
+              return false;
+            if (column.cycler || !this.view.isEditable(row, column))
               return false;
 
             // Beyond this point, we are going to edit the cell.
             if (this._editingColumn)
               this.stopEditing();
 
             var input = this.inputField;
 
@@ -365,17 +367,17 @@
             var topadj = parseInt(style.borderTopWidth) + parseInt(style.paddingTop);
             input.top = textRect.y - topadj;
 
             // The leftside of the textbox is aligned to the left side of the text
             // in LTR mode, and left side of the cell in RTL mode.
             var left, widthdiff;
             if (style.direction == "rtl") {
               left = cellRect.x;
-              widthdiff = cellRect.x + cellRect.width - textRect.x - textRect.width;
+              widthdiff = cellRect.x - textRect.x;
             } else {
               left = textRect.x;
               widthdiff = textRect.x - cellRect.x;
             }
 
             input.left = left;
             input.height = textRect.height + topadj +
                            parseInt(style.borderBottomWidth) +
--- a/toolkit/crashreporter/tools/upload_symbols.py
+++ b/toolkit/crashreporter/tools/upload_symbols.py
@@ -75,17 +75,17 @@ def main():
             r = requests.post(
                 url,
                 files={'symbols.zip': open(sys.argv[1], 'rb')},
                 headers={'Auth-Token': auth_token},
                 allow_redirects=False,
                 timeout=120)
             # 500 is likely to be a transient failure.
             # Break out for success or other error codes.
-            if r.status_code  != 500:
+            if r.status_code < 500:
                 break
             print_error(r)
         except requests.exceptions.RequestException as e:
             print('Error: {0}'.format(e))
         print('Retrying...')
     else:
         print('Maximum retries hit, giving up!')
         return 1
--- a/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js
+++ b/toolkit/mozapps/extensions/test/xpinstall/browser_auth.js
@@ -5,17 +5,17 @@ function test() {
   Harness.authenticationCallback = get_auth_info;
   Harness.downloadFailedCallback = download_failed;
   Harness.installEndedCallback = install_ended;
   Harness.installsCompletedCallback = finish_test;
   Harness.setup();
 
   var prefs = Cc["@mozilla.org/preferences-service;1"].
                         getService(Ci.nsIPrefBranch);
-  prefs.setIntPref("network.auth.allow-subresource-auth", 2);
+  prefs.setIntPref("network.auth.subresource-http-auth-allow", 2);
 
   var pm = Services.perms;
   pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION);
 
   var triggers = encodeURIComponent(JSON.stringify({
     "Unsigned XPI": TESTROOT + "authRedirect.sjs?" + TESTROOT + "unsigned.xpi"
   }));
   gBrowser.selectedTab = gBrowser.addTab();
--- a/toolkit/mozapps/installer/packager.mk
+++ b/toolkit/mozapps/installer/packager.mk
@@ -204,17 +204,17 @@ checksum:
 	@cat $(CHECKSUM_FILE)
 	@echo 'CHECKSUM FILE END'
 	$(SIGN_CHECKSUM_CMD)
 
 
 upload: checksum
 	$(PYTHON) $(MOZILLA_DIR)/build/upload.py --base-path $(DIST) \
 		--package $(PACKAGE) \
-		--properties-file $(DIST)/upload-properties.json \
+		--properties-file $(DIST)/mach_build_properties.json \
 		$(UPLOAD_FILES) \
 		$(CHECKSUM_FILES)
 
 # source-package creates a source tarball from the files in MOZ_PKG_SRCDIR,
 # which is either set to a clean checkout or defaults to $topsrcdir
 source-package:
 	@echo 'Packaging source tarball...'
 	$(MKDIR) -p $(DIST)/$(PKG_SRCPACK_PATH)
--- a/view/nsViewManager.cpp
+++ b/view/nsViewManager.cpp
@@ -753,17 +753,17 @@ nsViewManager::DispatchEvent(WidgetGUIEv
        mouseEvent->reason == WidgetMouseEvent::eReal &&
        // Ignore mouse exit and enter (we'll get moves if the user
        // is really moving the mouse) since we get them when we
        // create and destroy widgets.
        mouseEvent->mMessage != eMouseExitFromWidget &&
        mouseEvent->mMessage != eMouseEnterIntoWidget) ||
       aEvent->HasKeyEventMessage() ||
       aEvent->HasIMEEventMessage() ||
-      aEvent->mMessage == NS_PLUGIN_INPUT_EVENT) {
+      aEvent->mMessage == ePluginInputEvent) {
     gLastUserEventTime = PR_IntervalToMicroseconds(PR_IntervalNow());
   }
 
   // Find the view whose coordinates system we're in.
   nsView* view = aView;
   bool dispatchUsingCoordinates = aEvent->IsUsingCoordinates();
   if (dispatchUsingCoordinates) {
     // Will dispatch using coordinates. Pretty bogus but it's consistent
--- a/widget/ContentCache.cpp
+++ b/widget/ContentCache.cpp
@@ -34,18 +34,18 @@ GetEventMessageName(EventMessage aMessag
     case NS_COMPOSITION_UPDATE:
       return "NS_COMPOSITION_UPDATE";
     case NS_COMPOSITION_CHANGE:
       return "NS_COMPOSITION_CHANGE";
     case NS_COMPOSITION_COMMIT_AS_IS:
       return "NS_COMPOSITION_COMMIT_AS_IS";
     case NS_COMPOSITION_COMMIT:
       return "NS_COMPOSITION_COMMIT";
-    case NS_SELECTION_SET:
-      return "NS_SELECTION_SET";
+    case eSetSelection:
+      return "eSetSelection";
     default:
       return "unacceptable event message";
   }
 }
 
 static const char*
 GetNotificationName(const IMENotification* aNotification)
 {
--- a/widget/EventMessageList.h
+++ b/widget/EventMessageList.h
@@ -201,20 +201,20 @@ NS_EVENT_MESSAGE(eSVGScroll,            
 NS_EVENT_MESSAGE(eSVGZoomEventFirst,    2900)
 NS_EVENT_MESSAGE(eSVGZoom,              eSVGZoomEventFirst)
 
 // XUL command events
 NS_EVENT_MESSAGE(eXULCommandEventFirst, 3000)
 NS_EVENT_MESSAGE(eXULCommand,           eXULCommandEventFirst)
 
 // Cut, copy, paste events
-NS_EVENT_MESSAGE(NS_CUTCOPYPASTE_EVENT_START, 3100)
-NS_EVENT_MESSAGE(NS_COPY,               NS_CUTCOPYPASTE_EVENT_START)
-NS_EVENT_MESSAGE(NS_CUT,                NS_CUTCOPYPASTE_EVENT_START + 1)
-NS_EVENT_MESSAGE(NS_PASTE,              NS_CUTCOPYPASTE_EVENT_START + 2)
+NS_EVENT_MESSAGE(eClipboardEventFirst,  3100)
+NS_EVENT_MESSAGE(eCopy,                 eClipboardEventFirst)
+NS_EVENT_MESSAGE(eCut,                  eClipboardEventFirst + 1)
+NS_EVENT_MESSAGE(ePaste,                eClipboardEventFirst + 2)
 
 // Query the content information
 NS_EVENT_MESSAGE(NS_QUERY_CONTENT_EVENT_START,       3200)
 // Query for the selected text information, it return the selection offset,
 // selection length and selected text.
 NS_EVENT_MESSAGE(NS_QUERY_SELECTED_TEXT,             NS_QUERY_CONTENT_EVENT_START)
 // Query for the text content of specified range, it returns actual lengh (if
 // the specified range is too long) and the text of the specified range.
@@ -239,38 +239,37 @@ NS_EVENT_MESSAGE(NS_QUERY_SELECTION_AS_T
 // rect and also tentative caret point if the point is clicked.  The point is
 // specified by Event::refPoint.
 NS_EVENT_MESSAGE(NS_QUERY_CHARACTER_AT_POINT,        NS_QUERY_CONTENT_EVENT_START + 8)
 // Query if the DOM element under Event::refPoint belongs to our widget
 // or not.
 NS_EVENT_MESSAGE(NS_QUERY_DOM_WIDGET_HITTEST,        NS_QUERY_CONTENT_EVENT_START + 9)
 
 // Video events
-NS_EVENT_MESSAGE(NS_MEDIA_EVENT_START,  3300)
-NS_EVENT_MESSAGE(NS_LOADSTART,          NS_MEDIA_EVENT_START)
-NS_EVENT_MESSAGE(NS_PROGRESS,           NS_MEDIA_EVENT_START + 1)
-NS_EVENT_MESSAGE(NS_SUSPEND,            NS_MEDIA_EVENT_START + 2)
-NS_EVENT_MESSAGE(NS_EMPTIED,            NS_MEDIA_EVENT_START + 3)
-NS_EVENT_MESSAGE(NS_STALLED,            NS_MEDIA_EVENT_START + 4)
-NS_EVENT_MESSAGE(NS_PLAY,               NS_MEDIA_EVENT_START + 5)
-NS_EVENT_MESSAGE(NS_PAUSE,              NS_MEDIA_EVENT_START + 6)
-NS_EVENT_MESSAGE(NS_LOADEDMETADATA,     NS_MEDIA_EVENT_START + 7)
-NS_EVENT_MESSAGE(NS_LOADEDDATA,         NS_MEDIA_EVENT_START + 8)
-NS_EVENT_MESSAGE(NS_WAITING,            NS_MEDIA_EVENT_START + 9)
-NS_EVENT_MESSAGE(NS_PLAYING,            NS_MEDIA_EVENT_START + 10)
-NS_EVENT_MESSAGE(NS_CANPLAY,            NS_MEDIA_EVENT_START + 11)
-NS_EVENT_MESSAGE(NS_CANPLAYTHROUGH,     NS_MEDIA_EVENT_START + 12)
-NS_EVENT_MESSAGE(NS_SEEKING,            NS_MEDIA_EVENT_START + 13)
-NS_EVENT_MESSAGE(NS_SEEKED,             NS_MEDIA_EVENT_START + 14)
-NS_EVENT_MESSAGE(NS_TIMEUPDATE,         NS_MEDIA_EVENT_START + 15)
-NS_EVENT_MESSAGE(NS_ENDED,              NS_MEDIA_EVENT_START + 16)
-NS_EVENT_MESSAGE(NS_RATECHANGE,         NS_MEDIA_EVENT_START + 17)
-NS_EVENT_MESSAGE(NS_DURATIONCHANGE,     NS_MEDIA_EVENT_START + 18)
-NS_EVENT_MESSAGE(NS_VOLUMECHANGE,       NS_MEDIA_EVENT_START + 19)
-NS_EVENT_MESSAGE(NS_NEED_KEY,           NS_MEDIA_EVENT_START + 20)
+NS_EVENT_MESSAGE(eMediaEventFirst,      3300)
+NS_EVENT_MESSAGE(eLoadStart,            eMediaEventFirst)
+NS_EVENT_MESSAGE(eProgress,             eMediaEventFirst + 1)
+NS_EVENT_MESSAGE(eSuspend,              eMediaEventFirst + 2)
+NS_EVENT_MESSAGE(eEmptied,              eMediaEventFirst + 3)
+NS_EVENT_MESSAGE(eStalled,              eMediaEventFirst + 4)
+NS_EVENT_MESSAGE(ePlay,                 eMediaEventFirst + 5)
+NS_EVENT_MESSAGE(ePause,                eMediaEventFirst + 6)
+NS_EVENT_MESSAGE(eLoadedMetaData,       eMediaEventFirst + 7)
+NS_EVENT_MESSAGE(eLoadedData,           eMediaEventFirst + 8)
+NS_EVENT_MESSAGE(eWaiting,              eMediaEventFirst + 9)
+NS_EVENT_MESSAGE(ePlaying,              eMediaEventFirst + 10)
+NS_EVENT_MESSAGE(eCanPlay,              eMediaEventFirst + 11)
+NS_EVENT_MESSAGE(eCanPlayThrough,       eMediaEventFirst + 12)
+NS_EVENT_MESSAGE(eSeeking,              eMediaEventFirst + 13)
+NS_EVENT_MESSAGE(eSeeked,               eMediaEventFirst + 14)
+NS_EVENT_MESSAGE(eTimeUpdate,           eMediaEventFirst + 15)
+NS_EVENT_MESSAGE(eEnded,                eMediaEventFirst + 16)
+NS_EVENT_MESSAGE(eRateChange,           eMediaEventFirst + 17)
+NS_EVENT_MESSAGE(eDurationChange,       eMediaEventFirst + 18)
+NS_EVENT_MESSAGE(eVolumeChange,         eMediaEventFirst + 19)
 
 // paint notification events
 NS_EVENT_MESSAGE(NS_NOTIFYPAINT_START,  3400)
 NS_EVENT_MESSAGE(NS_AFTERPAINT,         NS_NOTIFYPAINT_START)
 
 // Simple gesture events
 NS_EVENT_MESSAGE(NS_SIMPLE_GESTURE_EVENT_START,    3500)
 NS_EVENT_MESSAGE(NS_SIMPLE_GESTURE_SWIPE_MAY_START,NS_SIMPLE_GESTURE_EVENT_START)
@@ -286,24 +285,23 @@ NS_EVENT_MESSAGE(NS_SIMPLE_GESTURE_ROTAT
 NS_EVENT_MESSAGE(NS_SIMPLE_GESTURE_ROTATE,         NS_SIMPLE_GESTURE_EVENT_START + 10)
 NS_EVENT_MESSAGE(NS_SIMPLE_GESTURE_TAP,            NS_SIMPLE_GESTURE_EVENT_START + 11)
 NS_EVENT_MESSAGE(NS_SIMPLE_GESTURE_PRESSTAP,       NS_SIMPLE_GESTURE_EVENT_START + 12)
 NS_EVENT_MESSAGE(NS_SIMPLE_GESTURE_EDGE_STARTED,   NS_SIMPLE_GESTURE_EVENT_START + 13)
 NS_EVENT_MESSAGE(NS_SIMPLE_GESTURE_EDGE_CANCELED,  NS_SIMPLE_GESTURE_EVENT_START + 14)
 NS_EVENT_MESSAGE(NS_SIMPLE_GESTURE_EDGE_COMPLETED, NS_SIMPLE_GESTURE_EVENT_START + 15)
 
 // These are used to send native events to plugins.
-NS_EVENT_MESSAGE(NS_PLUGIN_EVENT_START, 3600)
-NS_EVENT_MESSAGE(NS_PLUGIN_INPUT_EVENT, NS_PLUGIN_EVENT_START)
-NS_EVENT_MESSAGE(NS_PLUGIN_FOCUS_EVENT, NS_PLUGIN_EVENT_START + 1)
+NS_EVENT_MESSAGE(ePluginEventFirst,     3600)
+NS_EVENT_MESSAGE(ePluginInputEvent,     ePluginEventFirst)
 
 // Events to manipulate selection (WidgetSelectionEvent)
-NS_EVENT_MESSAGE(NS_SELECTION_EVENT_START, 3700)
+NS_EVENT_MESSAGE(eSelectionEventFirst,  3700)
 // Clear any previous selection and set the given range as the selection
-NS_EVENT_MESSAGE(NS_SELECTION_SET,      NS_SELECTION_EVENT_START)
+NS_EVENT_MESSAGE(eSetSelection,         eSelectionEventFirst)
 
 // Events of commands for the contents
 NS_EVENT_MESSAGE(NS_CONTENT_COMMAND_EVENT_START,        3800)
 NS_EVENT_MESSAGE(NS_CONTENT_COMMAND_CUT,                NS_CONTENT_COMMAND_EVENT_START)
 NS_EVENT_MESSAGE(NS_CONTENT_COMMAND_COPY,               NS_CONTENT_COMMAND_EVENT_START + 1)
 NS_EVENT_MESSAGE(NS_CONTENT_COMMAND_PASTE,              NS_CONTENT_COMMAND_EVENT_START + 2)
 NS_EVENT_MESSAGE(NS_CONTENT_COMMAND_DELETE,             NS_CONTENT_COMMAND_EVENT_START + 3)
 NS_EVENT_MESSAGE(NS_CONTENT_COMMAND_UNDO,               NS_CONTENT_COMMAND_EVENT_START + 4)
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -1803,17 +1803,17 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *
                     composition->String().Length()) {
 
                 // Only start a new composition if we have key events,
                 // if we don't have an existing composition, or
                 // the replaced text does not match our composition.
                 RemoveIMEComposition();
 
                 {
-                    WidgetSelectionEvent event(true, NS_SELECTION_SET, this);
+                    WidgetSelectionEvent event(true, eSetSelection, this);
                     InitEvent(event, nullptr);
                     event.mOffset = uint32_t(ae->Start());
                     event.mLength = uint32_t(ae->End() - ae->Start());
                     event.mExpandToClusterBoundary = false;
                     DispatchEvent(&event);
                 }
 
                 if (!mIMEKeyEvents.IsEmpty()) {
@@ -1883,17 +1883,17 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *
             /*
                 Set Gecko selection to ae->Start() to ae->End()
 
                 Selection updates are masked to prevent Java from being
                   notified of the new selection
             */
             AutoIMEMask selMask(mIMEMaskSelectionUpdate);
             RemoveIMEComposition();
-            WidgetSelectionEvent selEvent(true, NS_SELECTION_SET, this);
+            WidgetSelectionEvent selEvent(true, eSetSelection, this);
             InitEvent(selEvent, nullptr);
 
             int32_t start = ae->Start(), end = ae->End();
 
             if (start < 0 || end < 0) {
                 WidgetQueryContentEvent event(true, NS_QUERY_SELECTED_TEXT,
                                               this);
                 InitEvent(event, nullptr);
@@ -1962,17 +1962,17 @@ nsWindow::OnIMEEvent(AndroidGeckoEvent *
                     composition->NativeOffsetOfStartComposition() +
                     composition->String().Length()) {
 
                 // Only start new composition if we don't have an existing one,
                 // or if the existing composition doesn't match the new one.
                 RemoveIMEComposition();
 
                 {
-                    WidgetSelectionEvent event(true, NS_SELECTION_SET, this);
+                    WidgetSelectionEvent event(true, eSetSelection, this);
                     InitEvent(event, nullptr);
                     event.mOffset = uint32_t(ae->Start());
                     event.mLength = uint32_t(ae->End() - ae->Start());
                     event.mExpandToClusterBoundary = false;
                     DispatchEvent(&event);
                 }
 
                 {
--- a/widget/cocoa/TextInputHandler.h
+++ b/widget/cocoa/TextInputHandler.h
@@ -349,17 +349,17 @@ public:
    *
    * @param aEvent                An event which you want to dispatch.
    * @return                      TRUE if the event is consumed by web contents
    *                              or chrome contents.  Otherwise, FALSE.
    */
   bool DispatchEvent(WidgetGUIEvent& aEvent);
 
   /**
-   * SetSelection() dispatches NS_SELECTION_SET event for the aRange.
+   * SetSelection() dispatches eSetSelection event for the aRange.
    *
    * @param aRange                The range which will be selected.
    * @return                      TRUE if setting selection is succeeded and
    *                              the widget hasn't been destroyed.
    *                              Otherwise, FALSE.
    */
   bool SetSelection(NSRange& aRange);
 
--- a/widget/cocoa/TextInputHandler.mm
+++ b/widget/cocoa/TextInputHandler.mm
@@ -3960,17 +3960,17 @@ TextInputHandlerBase::AttachNativeKeyEve
 }
 
 bool
 TextInputHandlerBase::SetSelection(NSRange& aRange)
 {
   MOZ_ASSERT(!Destroyed());
 
   nsRefPtr<TextInputHandlerBase> kungFuDeathGrip(this);
-  WidgetSelectionEvent selectionEvent(true, NS_SELECTION_SET, mWidget);
+  WidgetSelectionEvent selectionEvent(true, eSetSelection, mWidget);
   selectionEvent.mOffset = aRange.location;
   selectionEvent.mLength = aRange.length;
   selectionEvent.mReversed = false;
   selectionEvent.mExpandToClusterBoundary = false;
   DispatchEvent(selectionEvent);
   NS_ENSURE_TRUE(selectionEvent.mSucceeded, false);
   return !Destroyed();
 }
--- a/widget/gtk/IMContextWrapper.cpp
+++ b/widget/gtk/IMContextWrapper.cpp
@@ -2048,17 +2048,17 @@ IMContextWrapper::DeleteText(GtkIMContex
     }
 
     gchar* charAtOffset =
         g_utf8_offset_to_pointer(utf8Str.get(), offsetInUTF8Characters);
     gchar* charAtEnd =
         g_utf8_offset_to_pointer(utf8Str.get(), endInUTF8Characters);
 
     // Set selection to delete
-    WidgetSelectionEvent selectionEvent(true, NS_SELECTION_SET,
+    WidgetSelectionEvent selectionEvent(true, eSetSelection,
                                         mLastFocusedWindow);
 
     nsDependentCSubstring utf8StrBeforeOffset(utf8Str, 0,
                                               charAtOffset - utf8Str.get());
     selectionEvent.mOffset =
         NS_ConvertUTF8toUTF16(utf8StrBeforeOffset).Length();
 
     nsDependentCSubstring utf8DeletingStr(utf8Str,
--- a/widget/nsPrintSession.h
+++ b/widget/nsPrintSession.h
@@ -3,17 +3,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsPrintSession_h__
 #define nsPrintSession_h__
 
 #include "nsIPrintSession.h" 
 #include "nsWeakReference.h"
-#include "gfxCore.h"
 
 //*****************************************************************************
 //***    nsPrintSession
 //*****************************************************************************
 
 class nsPrintSession : public nsIPrintSession,
                        public nsSupportsWeakReference
 {
--- a/widget/nsPrintSettingsImpl.h
+++ b/widget/nsPrintSettingsImpl.h
@@ -2,17 +2,16 @@
  *
  * 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 nsPrintSettingsImpl_h__
 #define nsPrintSettingsImpl_h__
 
-#include "gfxCore.h"
 #include "nsIPrintSettings.h"  
 #include "nsMargin.h"  
 #include "nsString.h"
 #include "nsWeakReference.h"  
 
 #define NUM_HEAD_FOOT 3
 
 //*****************************************************************************
--- a/widget/tests/TestWinTSF.cpp
+++ b/widget/tests/TestWinTSF.cpp
@@ -1884,19 +1884,19 @@ bool
 TestApp::TestSelection(void)
 {
   bool succeeded = true;
 
   /* If these fail the cause is probably one or more of:
    * nsTextStore::GetSelection not sending NS_QUERY_SELECTED_TEXT
    * NS_QUERY_SELECTED_TEXT not handled by ContentEventHandler
    * Bug in NS_QUERY_SELECTED_TEXT handler
-   * nsTextStore::SetSelection not sending NS_SELECTION_SET
-   * NS_SELECTION_SET not handled by ContentEventHandler
-   * Bug in NS_SELECTION_SET handler
+   * nsTextStore::SetSelection not sending eSetSelection
+   * eSetSelection not handled by ContentEventHandler
+   * Bug in eSetSelection handler
    */
 
   TS_SELECTION_ACP testSel;
   ULONG selFetched;
 
   if (!mMgr->GetFocusedStore()) {
     fail("TestSelection: GetFocusedStore returns null");
     return false;
@@ -1960,17 +1960,17 @@ TestApp::TestText(void)
   HRESULT hr;
 
   /* If these fail the cause is probably one or more of:
    * nsTextStore::GetText not sending NS_QUERY_TEXT_CONTENT
    * NS_QUERY_TEXT_CONTENT not handled by ContentEventHandler
    * Bug in NS_QUERY_TEXT_CONTENT handler
    * nsTextStore::SetText not calling SetSelection or InsertTextAtSelection
    * Bug in SetSelection or InsertTextAtSelection
-   *  NS_SELECTION_SET bug or NS_COMPOSITION_* / NS_COMPOSITION_CHANGE bug
+   *  eSetSelection bug or NS_COMPOSITION_* / NS_COMPOSITION_CHANGE bug
    */
 
   if (!mMgr->GetFocusedStore()) {
     fail("TestText: GetFocusedStore returns null #1");
     return false;
   }
 
   // Get all text
--- a/widget/windows/TSFTextStore.cpp
+++ b/widget/windows/TSFTextStore.cpp
@@ -1684,26 +1684,26 @@ TSFTextStore::FlushPendingActions()
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
                ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
                 "flushing COMPOSITION_START={ mSelectionStart=%d, "
                 "mSelectionLength=%d }",
                 this, action.mSelectionStart, action.mSelectionLength));
 
         if (action.mAdjustSelection) {
           // Select composition range so the new composition replaces the range
-          WidgetSelectionEvent selectionSet(true, NS_SELECTION_SET, mWidget);
+          WidgetSelectionEvent selectionSet(true, eSetSelection, mWidget);
           mWidget->InitEvent(selectionSet);
           selectionSet.mOffset = static_cast<uint32_t>(action.mSelectionStart);
           selectionSet.mLength = static_cast<uint32_t>(action.mSelectionLength);
           selectionSet.mReversed = false;
           DispatchEvent(selectionSet);
           if (!selectionSet.mSucceeded) {
             MOZ_LOG(sTextStoreLog, LogLevel::Error,
                    ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
-                    "FAILED due to NS_SELECTION_SET failure", this));
+                    "FAILED due to eSetSelection failure", this));
             break;
           }
         }
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
                ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
                 "dispatching compositionstart event...", this));
         WidgetCompositionEvent compositionStart(true, NS_COMPOSITION_START,
                                                 mWidget);
@@ -1810,25 +1810,25 @@ TSFTextStore::FlushPendingActions()
           mDeferClearingLockedContent = true;
         }
         DispatchEvent(compositionCommit);
         if (!mWidget || mWidget->Destroyed()) {
           break;
         }
         break;
       }
-      case PendingAction::SELECTION_SET: {
+      case PendingAction::SET_SELECTION: {
         MOZ_LOG(sTextStoreLog, LogLevel::Debug,
                ("TSF: 0x%p   TSFTextStore::FlushPendingActions() "
-                "flushing SELECTION_SET={ mSelectionStart=%d, "
+                "flushing SET_SELECTION={ mSelectionStart=%d, "
                 "mSelectionLength=%d, mSelectionReversed=%s }",
                 this, action.mSelectionStart, action.mSelectionLength,
                 GetBoolName(action.mSelectionReversed)));
 
-        WidgetSelectionEvent selectionSet(true, NS_SELECTION_SET, mWidget);
+        WidgetSelectionEvent selectionSet(true, eSetSelection, mWidget);
         selectionSet.mOffset = 
           static_cast<uint32_t>(action.mSelectionStart);
         selectionSet.mLength =
           static_cast<uint32_t>(action.mSelectionLength);
         selectionSet.mReversed = action.mSelectionReversed;
         break;
       }
       default:
@@ -2707,17 +2707,17 @@ TSFTextStore::SetSelectionInternal(const
         return hr;
       }
     }
     return S_OK;
   }
 
   CompleteLastActionIfStillIncomplete();
   PendingAction* action = mPendingActions.AppendElement();
-  action->mType = PendingAction::SELECTION_SET;
+  action->mType = PendingAction::SET_SELECTION;
   action->mSelectionStart = pSelection->acpStart;
   action->mSelectionLength = pSelection->acpEnd - pSelection->acpStart;
   action->mSelectionReversed = (pSelection->style.ase == TS_AE_START);
 
   currentSel.SetSelection(*pSelection);
 
   return S_OK;
 }
--- a/widget/windows/TSFTextStore.h
+++ b/widget/windows/TSFTextStore.h
@@ -506,17 +506,17 @@ protected:
 
   struct PendingAction final
   {
     enum ActionType : uint8_t
     {
       COMPOSITION_START,
       COMPOSITION_UPDATE,
       COMPOSITION_END,
-      SELECTION_SET
+      SET_SELECTION
     };
     ActionType mType;
     // For compositionstart and selectionset
     LONG mSelectionStart;
     LONG mSelectionLength;
     // For compositionupdate and compositionend
     nsString mData;
     // For compositionupdate
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -183,16 +183,18 @@
 #if !defined(SM_CONVERTIBLESLATEMODE)
 #define SM_CONVERTIBLESLATEMODE 0x2003
 #endif
 
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/InputAPZContext.h"
 #include "InputData.h"
 
+#include "mozilla/Telemetry.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 
 /**************************************************************
  **************************************************************
@@ -4002,22 +4004,28 @@ nsWindow::DispatchMouseEvent(EventMessag
   bool result = false;
 
   UserActivity();
 
   if (!mWidgetListener) {
     return result;
   }
 
-  if (mTouchWindow && WinUtils::GetIsMouseFromTouch(aEventMessage)) {
-    // If mTouchWindow is true, then we must have APZ enabled and be
-    // feeding it raw touch events. In that case we don't need to
-    // send touch-generated mouse events to content.
-    MOZ_ASSERT(mAPZC);
-    return result;
+  if (WinUtils::GetIsMouseFromTouch(aEventMessage)) {
+    if (aEventMessage == eMouseDown) {
+      Telemetry::Accumulate(Telemetry::FX_TOUCH_USED, 1);
+    }
+
+    if (mTouchWindow) {
+      // If mTouchWindow is true, then we must have APZ enabled and be
+      // feeding it raw touch events. In that case we don't need to
+      // send touch-generated mouse events to content.
+      MOZ_ASSERT(mAPZC);
+      return result;
+    }
   }
 
   // Since it is unclear whether a user will use the digitizer,
   // Postpone initialization until first PEN message will be found.
   if (nsIDOMMouseEvent::MOZ_SOURCE_PEN == aInputSource
       // Messages should be only at topLevel window.
       && nsWindowType::eWindowType_toplevel == mWindowType
       // Currently this scheme is used only when pointer events is enabled.
--- a/widget/windows/nsWindowBase.cpp
+++ b/widget/windows/nsWindowBase.cpp
@@ -18,17 +18,17 @@ bool nsWindowBase::sTouchInjectInitializ
 InjectTouchInputPtr nsWindowBase::sInjectTouchFuncPtr;
 
 bool
 nsWindowBase::DispatchPluginEvent(const MSG& aMsg)
 {
   if (!PluginHasFocus()) {
     return false;
   }
-  WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, this);
+  WidgetPluginEvent pluginEvent(true, ePluginInputEvent, this);
   nsIntPoint point(0, 0);
   InitEvent(pluginEvent, &point);
   NPEvent npEvent;
   npEvent.event = aMsg.message;
   npEvent.wParam = aMsg.wParam;
   npEvent.lParam = aMsg.lParam;
   pluginEvent.mPluginEvent.Copy(npEvent);
   pluginEvent.retargetToFocusedDocument = true;
--- a/xpcom/threads/TaskQueue.cpp
+++ b/xpcom/threads/TaskQueue.cpp
@@ -58,17 +58,17 @@ TaskQueue::DispatchLocked(already_AddRef
   if (mIsShutdown) {
     return NS_ERROR_FAILURE;
   }
   mTasks.push(r.forget());
   if (mIsRunning) {
     return NS_OK;
   }
   nsRefPtr<nsIRunnable> runner(new Runner(this));
-  nsresult rv = mPool->Dispatch(runner, NS_DISPATCH_NORMAL);
+  nsresult rv = mPool->Dispatch(runner.forget(), NS_DISPATCH_NORMAL);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch runnable to run TaskQueue");
     return rv;
   }
   mIsRunning = true;
 
   return NS_OK;
 }
@@ -150,17 +150,17 @@ TaskQueue::Runner::Run()
     MonitorAutoLock mon(mQueue->mQueueMonitor);
     MOZ_ASSERT(mQueue->mIsRunning);
     if (mQueue->mTasks.size() == 0) {
       mQueue->mIsRunning = false;
       mQueue->MaybeResolveShutdown();
       mon.NotifyAll();
       return NS_OK;
     }
-    event = mQueue->mTasks.front();
+    event = mQueue->mTasks.front().forget();
     mQueue->mTasks.pop();
   }
   MOZ_ASSERT(event);
 
   // Note that dropping the queue monitor before running the task, and
   // taking the monitor again after the task has run ensures we have memory
   // fences enforced. This means that if the object we're calling wasn't
   // designed to be threadsafe, it will be, provided we're only calling it
--- a/xpcom/threads/TimerThread.cpp
+++ b/xpcom/threads/TimerThread.cpp
@@ -26,18 +26,17 @@ NS_IMPL_ISUPPORTS(TimerThread, nsIRunnab
 
 TimerThread::TimerThread() :
   mInitInProgress(false),
   mInitialized(false),
   mMonitor("TimerThread.mMonitor"),
   mShutdown(false),
   mWaiting(false),
   mNotified(false),
-  mSleeping(false),
-  mLastTimerEventLoopRun(TimeStamp::Now())
+  mSleeping(false)
 {
 }
 
 TimerThread::~TimerThread()
 {
   mThread = nullptr;
 
   NS_ASSERTION(mTimers.IsEmpty(), "Timers remain in TimerThread::~TimerThread");
@@ -436,17 +435,16 @@ TimerThread::Run()
       uint32_t milliseconds = 100;
       if (ChaosMode::isActive(ChaosFeature::TimerScheduling)) {
         milliseconds = ChaosMode::randomUint32LessThan(200);
       }
       waitFor = PR_MillisecondsToInterval(milliseconds);
     } else {
       waitFor = PR_INTERVAL_NO_TIMEOUT;
       TimeStamp now = TimeStamp::Now();
-      mLastTimerEventLoopRun = now;
       nsTimerImpl* timer = nullptr;
 
       if (!mTimers.IsEmpty()) {
         timer = mTimers[0];
 
         if (now >= timer->mTimeout || forceRunThisTimer) {
     next:
           // NB: AddRef before the Release under RemoveTimerInternal to avoid
@@ -742,46 +740,29 @@ TimerThread::PostTimerEvent(already_AddR
   return nullptr;
 }
 
 void
 TimerThread::DoBeforeSleep()
 {
   // Mainthread
   MonitorAutoLock lock(mMonitor);
-  mLastTimerEventLoopRun = TimeStamp::Now();
   mSleeping = true;
 }
 
 // Note: wake may be notified without preceding sleep notification
 void
 TimerThread::DoAfterSleep()
 {
   // Mainthread
-  TimeStamp now = TimeStamp::Now();
-
   MonitorAutoLock lock(mMonitor);
-
-  // an over-estimate of time slept, usually small
-  TimeDuration slept = now - mLastTimerEventLoopRun;
+  mSleeping = false;
 
-  // Adjust all old timers to expire roughly similar times in the future
-  // compared to when we went to sleep, by adding the time we slept to the
-  // target time. It's slightly possible a few will end up slightly in the
-  // past and fire immediately, but ordering should be preserved.  All
-  // timers retain the exact same order (and relative times) as before
-  // going to sleep.
-  for (uint32_t i = 0; i < mTimers.Length(); i ++) {
-    nsTimerImpl* timer = mTimers[i];
-    timer->mTimeout += slept;
-  }
-  mSleeping = false;
-  mLastTimerEventLoopRun = now;
-
-  // Wake up the timer thread to process the updated array
+  // Wake up the timer thread to re-process the array to ensure the sleep delay is correct,
+  // and fire any expired timers (perhaps quite a few)
   mNotified = true;
   mMonitor.Notify();
 }
 
 
 NS_IMETHODIMP
 TimerThread::Observe(nsISupports* /* aSubject */, const char* aTopic,
                      const char16_t* /* aData */)
--- a/xpcom/threads/TimerThread.h
+++ b/xpcom/threads/TimerThread.h
@@ -72,17 +72,16 @@ private:
 
   nsCOMPtr<nsIThread> mThread;
   Monitor mMonitor;
 
   bool mShutdown;
   bool mWaiting;
   bool mNotified;
   bool mSleeping;
-  TimeStamp mLastTimerEventLoopRun;
 
   nsTArray<nsTimerImpl*> mTimers;
 };
 
 struct TimerAdditionComparator
 {
   TimerAdditionComparator(const mozilla::TimeStamp& aNow,
                           nsTimerImpl* aTimerToInsert) :