Merge fx-team to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Fri, 14 Aug 2015 15:17:32 -0400
changeset 257879 c43fcc652fa7960a651ed0cac86d4fe1568ec51d
parent 257865 09dd35dd11936001b61960819db7d55c6f3e9dc0 (current diff)
parent 257878 2df5eaa5fd4afaa8ae7d7e31c3e9c0fe9ae17df6 (diff)
child 257882 6b2f7170700f60fc65324e7dd6cc94b2ae49a19b
child 257897 e8ae270ae0d5abf285d55b35fe8ea8d3bb255e4e
child 257901 e73db496a82b4c3039d92c88650ff7b2e65fbf5b
push id29231
push userryanvm@gmail.com
push dateFri, 14 Aug 2015 19:18:05 +0000
treeherdermozilla-central@c43fcc652fa7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
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 fx-team to m-c. a=merge
--- a/browser/base/content/aboutaccounts/aboutaccounts.css
+++ b/browser/base/content/aboutaccounts/aboutaccounts.css
@@ -4,17 +4,17 @@ html, body {
 
 #remote {
   width: 100%;
   height: 100%;
   border: 0;
   display: none;
 }
 
-#manage, #intro, #stage {
+#networkError, #manage, #intro, #stage {
   display: none;
 }
 
 #oldsync {
   background: none;
   border: 0;
   color: #0095dd;
 }
--- a/browser/base/content/aboutaccounts/aboutaccounts.js
+++ b/browser/base/content/aboutaccounts/aboutaccounts.js
@@ -109,27 +109,78 @@ let wrapper = {
     // If a master-password is enabled, we want to encourage the user to
     // unlock it.  Things still work if not, but the user will probably need
     // to re-auth next startup (in which case we will get here again and
     // re-prompt)
     Utils.ensureMPUnlocked();
 
     let iframe = document.getElementById("remote");
     this.iframe = iframe;
+    this.iframe.QueryInterface(Ci.nsIFrameLoaderOwner);
+    let docShell = this.iframe.frameLoader.docShell;
+    docShell.QueryInterface(Ci.nsIWebProgress);
+    docShell.addProgressListener(this.iframeListener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
     iframe.addEventListener("load", this);
 
     // Ideally we'd just merge urlParams with new URL(url).searchParams, but our
     // URLSearchParams implementation doesn't support iteration (bug 1085284).
     let urlParamStr = urlParams.toString();
     if (urlParamStr) {
       url += (url.includes("?") ? "&" : "?") + urlParamStr;
     }
+    this.url = url;
     iframe.src = url;
   },
 
+  retry: function () {
+    let webNav = this.iframe.frameLoader.docShell.QueryInterface(Ci.nsIWebNavigation);
+    webNav.loadURI(this.url, null, null, null, null);
+  },
+
+  iframeListener: {
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
+                                         Ci.nsISupportsWeakReference,
+                                         Ci.nsISupports]),
+
+    onStateChange: function(aWebProgress, aRequest, aState, aStatus) {
+      let failure = false;
+
+      // Captive portals sometimes redirect users
+      if ((aState & Ci.nsIWebProgressListener.STATE_REDIRECTING)) {
+        failure = true;
+      } else if ((aState & Ci.nsIWebProgressListener.STATE_STOP)) {
+        if (aRequest instanceof Ci.nsIHttpChannel) {
+          try {
+            failure = aRequest.responseStatus != 200;
+          } catch (e) {
+            failure = aStatus != Components.results.NS_OK;
+          }
+        }
+      }
+
+      // Calling cancel() will raise some OnStateChange notifications by itself,
+      // so avoid doing that more than once
+      if (failure && aStatus != Components.results.NS_BINDING_ABORTED) {
+        aRequest.cancel(Components.results.NS_BINDING_ABORTED);
+        setErrorPage();
+      }
+    },
+
+    onLocationChange: function(aWebProgress, aRequest, aLocation, aFlags) {
+      if (aRequest && aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
+        aRequest.cancel(Components.results.NS_BINDING_ABORTED);
+        setErrorPage();
+      }
+    },
+
+    onProgressChange: function() {},
+    onStatusChange: function() {},
+    onSecurityChange: function() {},
+  },
+
   handleEvent: function (evt) {
     switch (evt.type) {
       case "load":
         this.iframe.contentWindow.addEventListener("FirefoxAccountsCommand", this);
         this.iframe.removeEventListener("load", this);
         break;
       case "FirefoxAccountsCommand":
         this.handleRemoteCommand(evt);
@@ -287,16 +338,21 @@ function handleOldSync() {
   let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "old-sync";
   chromeWin.switchToTabHavingURI(url, true);
 }
 
 function getStarted() {
   show("remote");
 }
 
+function retry() {
+  show("remote");
+  wrapper.retry();
+}
+
 function openPrefs() {
   window.openPreferences("paneSync");
 }
 
 function init() {
   fxAccounts.getSignedInUser().then(user => {
     // tests in particular might cause the window to start closing before
     // getSignedInUser has returned.
@@ -358,16 +414,20 @@ function init() {
       }
       break;
     }
   }).catch(err => {
     error("Failed to get the signed in user: " + err);
   });
 }
 
+function setErrorPage() {
+  show("stage", "networkError");
+}
+
 // Causes the "top-level" element with |id| to be shown - all other top-level
 // elements are hidden.  Optionally, ensures that only 1 "second-level" element
 // inside the top-level one is shown.
 function show(id, childId) {
   // top-level items are either <div> or <iframe>
   let allTop = document.querySelectorAll("body > div, iframe");
   for (let elt of allTop) {
     if (elt.getAttribute("id") == id) {
@@ -444,16 +504,19 @@ function getDefaultProfilePath() {
 }
 
 document.addEventListener("DOMContentLoaded", function onload() {
   document.removeEventListener("DOMContentLoaded", onload, true);
   init();
   var buttonGetStarted = document.getElementById('buttonGetStarted');
   buttonGetStarted.addEventListener('click', getStarted);
 
+  var buttonRetry = document.getElementById('buttonRetry');
+  buttonRetry.addEventListener('click', retry);
+
   var oldsync = document.getElementById('oldsync');
   oldsync.addEventListener('click', handleOldSync);
 
   var buttonOpenPrefs = document.getElementById('buttonOpenPrefs')
   buttonOpenPrefs.addEventListener('click', openPrefs);
 }, true);
 
 function initObservers() {
--- a/browser/base/content/aboutaccounts/aboutaccounts.xhtml
+++ b/browser/base/content/aboutaccounts/aboutaccounts.xhtml
@@ -66,16 +66,32 @@
             </div>
 
             <div class="links">
               <button id="oldsync" tabindex="2">&aboutAccountsConfig.useOldSync.label;</button>
             </div>
         </section>
       </div>
 
+      <div id="networkError">
+        <header>
+          <h1>&aboutAccounts.noConnection.title;</h1>
+        </header>
+
+        <section>
+            <div class="graphic graphic-sync-intro"> </div>
+
+            <div class="description">&aboutAccounts.noConnection.description;</div>
+
+            <div class="button-row">
+              <button id="buttonRetry" class="button" tabindex="3">&aboutAccounts.noConnection.retry;</button>
+            </div>
+        </section>
+      </div>
+
     </div>
 
     <iframe mozframetype="content" id="remote" />
 
     <script type="application/javascript;version=1.8"
       src="chrome://browser/content/utilityOverlay.js"/>
     <script type="text/javascript;version=1.8"
       src="chrome://browser/content/aboutaccounts/aboutaccounts.js" />
--- a/browser/base/content/browser-fullScreen.js
+++ b/browser/base/content/browser-fullScreen.js
@@ -453,17 +453,33 @@ var FullScreen = {
       if (currentState == newState) {
         return;
       }
       if (currentState != "hiding") {
         this._lastState = currentState;
         this._element.removeAttribute(currentState);
       }
       if (newState != "hidden") {
-        this._element.setAttribute(newState, true);
+        if (currentState != "hidden") {
+          this._element.setAttribute(newState, true);
+        } else {
+          // When the previous state is hidden, the display was none,
+          // thus no box was constructed. We need to wait for the new
+          // display value taking effect first, otherwise, there won't
+          // be any transition. Since requestAnimationFrame callback is
+          // generally triggered before any style flush and layout, we
+          // should wait for the second animation frame.
+          requestAnimationFrame(() => {
+            requestAnimationFrame(() => {
+              if (this._element) {
+                this._element.setAttribute(newState, true);
+              }
+            });
+          });
+        }
       }
     },
 
     handleEvent: function(event) {
       switch (event.type) {
         case "mousemove": {
           let state = this._state;
           if (state == "hidden") {
@@ -471,17 +487,17 @@ var FullScreen = {
             // a short delay if the pointer is at the top.
             if (event.clientY != 0) {
               this._timeoutShow.cancel();
             } else if (this._timeoutShow.delay >= 0) {
               this._timeoutShow.start();
             }
           } else {
             let elemRect = this._element.getBoundingClientRect();
-            if (state == "hiding") {
+            if (state == "hiding" && this._lastState != "hidden") {
               // If we are on the hiding transition, and the pointer
               // moved near the box, restore to the previous state.
               if (event.clientY <= elemRect.bottom + 50) {
                 this._state = this._lastState;
                 this._timeoutHide.start();
               }
             } else if (state == "ontop" || this._lastState != "hidden") {
               // State being "ontop" or the previous state not being
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -665,37 +665,31 @@ window[chromehidden~="toolbar"] toolbar:
 /*  Full Screen UI */
 
 #fullscr-toggler {
   height: 1px;
   background: black;
 }
 
 #fullscreen-warning {
-  display: flex;
   position: fixed;
   z-index: 2147483647 !important;
   visibility: visible;
   transition: transform 300ms ease-in;
   /* To center the warning box horizontally,
      we use left: 50% with translateX(-50%). */
   top: 0; left: 50%;
   transform: translate(-50%, -100%);
   /* We must specify a max-width, otherwise word-wrap:break-word doesn't
      work in descendant <description> and <label> elements. Bug 630864. */
   max-width: 95%;
   pointer-events: none;
 }
-#fullscreen-warning[hidden] {
-  /* To ensure the transition always works fine, never change
-     the value of display property. */
+#fullscreen-warning:not([hidden]) {
   display: flex;
-  visibility: hidden !important;
-  transition: initial;
-  pointer-events: none !important;
 }
 #fullscreen-warning[onscreen] {
   transform: translate(-50%, 50px);
 }
 #fullscreen-warning[ontop] {
   /* Use -10px to hide the border and border-radius on the top */
   transform: translate(-50%, -10px);
 }
--- a/browser/base/content/test/general/browser_aboutAccounts.js
+++ b/browser/base/content/test/general/browser_aboutAccounts.js
@@ -80,17 +80,18 @@ let gTests = [
     setPref("identity.fxaccounts.remote.signin.uri", expected_url);
     let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin");
     is(url, expected_url, "action=signin got the expected URL");
     // we expect the remote iframe to be shown.
     yield checkVisibilities(tab, {
       stage: false, // parent of 'manage' and 'intro'
       manage: false,
       intro: false, // this is  "get started"
-      remote: true
+      remote: true,
+      networkError: false
     });
   }
 },
 {
   desc: "Test action=signin - user logged in",
   teardown: function* () {
     gBrowser.removeCurrentTab();
     yield signOut();
@@ -107,17 +108,58 @@ let gTests = [
     // so we also request it - by the time we get it we know it should have
     // done its thing.
     yield fxAccounts.getSignedInUser();
     // we expect "manage" to be shown.
     yield checkVisibilities(tab, {
       stage: true, // parent of 'manage' and 'intro'
       manage: true,
       intro: false, // this is  "get started"
-      remote: false
+      remote: false,
+      networkError: false
+    });
+  }
+},
+{
+  desc: "Test action=signin - captive portal",
+  teardown: () => gBrowser.removeCurrentTab(),
+  run: function* ()
+  {
+    const signinUrl = "https://redirproxy.example.com/test";
+    setPref("identity.fxaccounts.remote.signin.uri", signinUrl);
+    let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin");
+    yield checkVisibilities(tab, {
+      stage: true, // parent of 'manage' and 'intro'
+      manage: false,
+      intro: false, // this is  "get started"
+      remote: false,
+      networkError: true
+    });
+  }
+},
+{
+  desc: "Test action=signin - offline",
+  teardown: () => {
+    gBrowser.removeCurrentTab();
+    BrowserOffline.toggleOfflineStatus();
+  },
+  run: function* ()
+  {
+    BrowserOffline.toggleOfflineStatus();
+    Services.cache2.clear();
+
+    const signinUrl = "https://unknowndomain.cow";
+    setPref("identity.fxaccounts.remote.signin.uri", signinUrl);
+    let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signin");
+    yield checkVisibilities(tab, {
+      stage: true, // parent of 'manage' and 'intro'
+      manage: false,
+      intro: false, // this is  "get started"
+      remote: false,
+      networkError: true
     });
   }
 },
 {
   desc: "Test action=signup - no user logged in",
   teardown: () => gBrowser.removeCurrentTab(),
   run: function* ()
   {
@@ -125,17 +167,18 @@ let gTests = [
     setPref("identity.fxaccounts.remote.signup.uri", expected_url);
     let [tab, url] = yield promiseNewTabWithIframeLoadEvent("about:accounts?action=signup");
     is(url, expected_url, "action=signup got the expected URL");
     // we expect the remote iframe to be shown.
     yield checkVisibilities(tab, {
       stage: false, // parent of 'manage' and 'intro'
       manage: false,
       intro: false, // this is  "get started"
-      remote: true
+      remote: true,
+      networkError: false
     });
   },
 },
 {
   desc: "Test action=signup - user logged in",
   teardown: () => gBrowser.removeCurrentTab(),
   run: function* ()
   {
@@ -144,17 +187,18 @@ let gTests = [
     yield setSignedInUser();
     let tab = yield promiseNewTabLoadEvent("about:accounts?action=signup");
     yield fxAccounts.getSignedInUser();
     // we expect "manage" to be shown.
     yield checkVisibilities(tab, {
       stage: true, // parent of 'manage' and 'intro'
       manage: true,
       intro: false, // this is  "get started"
-      remote: false
+      remote: false,
+      networkError: false
     });
   },
 },
 {
   desc: "Test action=reauth",
   teardown: function* () {
     gBrowser.removeCurrentTab();
     yield signOut();
--- a/browser/base/content/test/general/content_aboutAccounts.js
+++ b/browser/base/content/test/general/content_aboutAccounts.js
@@ -17,22 +17,24 @@ addEventListener("load", function load(e
 
 addEventListener("DOMContentLoaded", function domContentLoaded(event) {
   removeEventListener("DOMContentLoaded", domContentLoaded, true);
   let iframe = content.document.getElementById("remote");
   if (!iframe) {
     // at least one test initially loads about:blank - in that case, we are done.
     return;
   }
-  iframe.addEventListener("load", function iframeLoaded(event) {
+  // We use DOMContentLoaded here as that fires for our iframe even when we've
+  // arranged for the URL in the iframe to cause an error.
+  addEventListener("DOMContentLoaded", function iframeLoaded(event) {
     if (iframe.contentWindow.location.href == "about:blank" ||
-        event.target != iframe) {
+        event.target != iframe.contentDocument) {
       return;
     }
-    iframe.removeEventListener("load", iframeLoaded, true);
+    removeEventListener("DOMContentLoaded", iframeLoaded, true);
     sendAsyncMessage("test:iframe:load", {url: iframe.getAttribute("src")});
     // And an event listener for the test responses, which we send to the test
     // via a message.
     iframe.contentWindow.addEventListener("FirefoxAccountsTestResponse", function (event) {
       sendAsyncMessage("test:response", {data: event.detail.data});
     }, true);
   }, true);
 }, true);
--- a/browser/components/loop/standalone/content/index.html
+++ b/browser/components/loop/standalone/content/index.html
@@ -46,17 +46,22 @@
           window.ga = window.ga || function() {
             (window.ga.q = window.ga.q || []).push(arguments);
           };
           window.ga.l = 1 * new Date();
 
           insertScript("//www.google-analytics.com/analytics.js");
 
           window.ga("create", "UA-36116321-15", "auto");
-          window.ga("set", "anonymizeIp", true);
+          /* Don't send conversation ids to GA, by specifying our own location. */
+          window.ga("set", {
+            "location": document.location.origin + "/conversation/",
+            "anonymizeIp": true,
+            "title": "Link Clicker"
+          });
           window.ga("send", "pageview");
         }
       })();
     </script>
 
   </head>
   <body class="standalone">
 
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -357,17 +357,17 @@ let gSyncPane = {
         document.getElementById("fxaSyncComputerName").value = Weave.Service.clientsEngine.localName;
         let engines = document.getElementById("fxaSyncEngines")
         for (let checkbox of engines.querySelectorAll("checkbox")) {
           checkbox.disabled = !syncReady;
         }
         document.getElementById("fxaChangeDeviceName").disabled = !syncReady;
 
         // Clear the profile image (if any) of the previously logged in account.
-        document.getElementById("fxaProfileImage").style.removeProperty("background-image");
+        document.getElementById("fxaProfileImage").style.removeProperty("list-style-image");
 
         // If the account is verified the next promise in the chain will
         // fetch profile data.
         return data.verified;
       }).then(isVerified => {
         if (isVerified) {
           return fxAccounts.getSignedInUserProfile();
         }
@@ -380,17 +380,17 @@ let gSyncPane = {
           }
           if (data.avatar) {
             // Make sure the image is available before displaying it,
             // as we don't want to overwrite the default profile image
             // with a broken/unavailable image
             let img = new Image();
             img.onload = () => {
               let bgImage = "url('" + data.avatar + "')";
-              document.getElementById("fxaProfileImage").style.backgroundImage = bgImage;
+              document.getElementById("fxaProfileImage").style.listStyleImage = bgImage;
             };
             img.src = data.avatar;
           }
         }
       }, err => {
         FxAccountsCommon.log.error(err);
       }).catch(err => {
         // If we get here something's really busted
--- a/browser/components/preferences/in-content/sync.xul
+++ b/browser/components/preferences/in-content/sync.xul
@@ -195,175 +195,180 @@
     <label id="loginErrorStartOver" class="text-link">
       &unlinkDevice.label;
     </label>
   </vbox>
 
   <!-- These panels are for the Firefox Accounts identity provider -->
   <vbox id="noFxaAccount">
     <hbox>
-      <groupbox id="noFxaGroup">
-        <vbox>
-          <label id="noFxaCaption">&signedOut.caption;</label>
-          <description id="noFxaDescription" flex="1">&signedOut.description;</description>
-          <hbox class="fxaAccountBox">
-            <image class="fxaFirefoxLogo"/>
-            <vbox>
-              <label id="signedOutAccountBoxTitle">&signedOut.accountBox.title;</label>
-              <hbox>
-                <button id="noFxaSignUp" label="&signedOut.accountBox.create;"/>
-                <button id="noFxaSignIn" label="&signedOut.accountBox.signin;"/>
-              </hbox>
-            </vbox>
-          </hbox>
-        </vbox>
-      </groupbox>
-      <image class="fxaSyncIllustration"/>
+      <vbox id="fxaContentWrapper">
+        <groupbox id="noFxaGroup">
+          <vbox>
+            <label id="noFxaCaption">&signedOut.caption;</label>
+            <description id="noFxaDescription" flex="1">&signedOut.description;</description>
+            <hbox class="fxaAccountBox">
+              <vbox>
+                <image class="fxaFirefoxLogo"/>
+              </vbox>
+              <vbox flex="1">
+                <label id="signedOutAccountBoxTitle">&signedOut.accountBox.title;</label>
+                <description class="fxaAccountBoxButtons">
+                  <button id="noFxaSignUp">&signedOut.accountBox.create;</button>
+                  <button id="noFxaSignIn">&signedOut.accountBox.signin;</button>
+                </description>
+              </vbox>
+            </hbox>
+          </vbox>
+        </groupbox>
+      </vbox>
+      <vbox>
+        <image class="fxaSyncIllustration"/>
+      </vbox>
     </hbox>
-    <hbox class="fxaMobilePromo">
-      <label>&mobilePromo.start;</label>
-      <image class="androidLogo"/>
-      <label class="text-link"
-             href="https://www.mozilla.org/firefox/android/">
-        &mobilePromo.androidLink;
-      </label>
-      <label>&mobilePromo.end;</label>
-    </hbox>
+    <label class="fxaMobilePromo">
+        &mobilePromo.start;<!-- We put these comments to avoid inserting white spaces
+        --><image class="androidLogo"/><!--
+        --><label class="androidLink text-link" href="https://www.mozilla.org/firefox/android/"><!--
+        -->&mobilePromo.androidLink;</label><!--
+        -->&mobilePromo.end;
+    </label>
     <label class="androidAttribution">&androidAttribution;</label>
   </vbox>
 
   <vbox id="hasFxaAccount">
     <hbox>
-      <vbox>
+      <vbox id="fxaContentWrapper">
         <groupbox id="fxaGroup">
           <caption><label>&syncBrand.fxAccount.label;</label></caption>
-
-              <deck id="fxaLoginStatus">
+          <deck id="fxaLoginStatus">
 
-                <!-- logged in and verified and all is good -->
-                <hbox id="fxaLoginVerified" class="fxaAccountBox">
-                  <image id="fxaProfileImage"
-                      onclick="gSyncPane.openChangeProfileImage();" hidden="true"
-                      tooltiptext="&profilePicture.tooltip;" class="actionable"/>
-                  <vbox>
-                    <label id="fxaEmailAddress1"/>
-                    <label id="fxaDisplayName" hidden="true"/>
-                    <hbox class="fxaAccountBoxButtons">
-                      <button id="verifiedManage" label="&manage.label;"/>
-                      <button id="fxaUnlinkButton" label="&disconnect.label;"/>
-                    </hbox>
-                  </vbox>
-                </hbox>
+            <!-- logged in and verified and all is good -->
+            <hbox id="fxaLoginVerified" class="fxaAccountBox">
+              <vbox>
+                <image id="fxaProfileImage"
+                    onclick="gSyncPane.openChangeProfileImage();" hidden="true"
+                    tooltiptext="&profilePicture.tooltip;" class="actionable"/>
+              </vbox>
+              <vbox flex="1">
+                <label id="fxaEmailAddress1"/>
+                <label id="fxaDisplayName" hidden="true"/>
+                <description class="fxaAccountBoxButtons">
+                  <button id="verifiedManage">&manage.label;</button>
+                  <button id="fxaUnlinkButton">&disconnect.label;</button>
+                </description>
+              </vbox>
+            </hbox>
 
-                <!-- logged in to an unverified account -->
-                <hbox id="fxaLoginUnverified" class="fxaAccountBox">
-                  <image id="fxaProfileImage"/>
-                  <vbox>
-                    <hbox>
-                      <vbox><image id="fxaLoginRejectedWarning"/></vbox>
-                      <description>
-                        &signedInUnverified.beforename.label;
-                        <label id="fxaEmailAddress2"/>
-                        &signedInUnverified.aftername.label;
-                      </description>
-                    </hbox>
-                    <hbox class="fxaAccountBoxButtons">
-                      <button id="verifyFxaAccount" label="&verify.label;"/>
-                      <button id="unverifiedUnlinkFxaAccount" label="&forget.label;"/>
-                    </hbox>
-                  </vbox>
+            <!-- logged in to an unverified account -->
+            <hbox id="fxaLoginUnverified" class="fxaAccountBox">
+              <vbox>
+                <image id="fxaProfileImage"/>
+              </vbox>
+              <vbox flex="1">
+                <hbox>
+                  <vbox><image id="fxaLoginRejectedWarning"/></vbox>
+                  <description flex="1">
+                    &signedInUnverified.beforename.label;
+                    <label id="fxaEmailAddress2"/>
+                    &signedInUnverified.aftername.label;
+                  </description>
                 </hbox>
+                <description class="fxaAccountBoxButtons">
+                  <button id="verifyFxaAccount">&verify.label;</button>
+                  <button id="unverifiedUnlinkFxaAccount">&forget.label;</button>
+                </description>
+              </vbox>
+            </hbox>
 
-                <!-- logged in locally but server rejected credentials -->
-                <hbox id="fxaLoginRejected" class="fxaAccountBox">
-                  <image id="fxaProfileImage"/>
-                  <vbox>
-                    <hbox>
-                      <vbox><image id="fxaLoginRejectedWarning"/></vbox>
-                      <description>
-                        &signedInLoginFailure.beforename.label;
-                        <label id="fxaEmailAddress3"/>
-                        &signedInLoginFailure.aftername.label;
-                      </description>
-                    </hbox>
-                    <hbox class="fxaAccountBoxButtons">
-                      <button id="rejectReSignIn" label="&signIn.label;"/>
-                      <button id="rejectUnlinkFxaAccount" label="&forget.label;"/>
-                    </hbox>
-                  </vbox>
+            <!-- logged in locally but server rejected credentials -->
+            <hbox id="fxaLoginRejected" class="fxaAccountBox">
+              <vbox>
+                <image id="fxaProfileImage"/>
+              </vbox>
+              <vbox flex="1">
+                <hbox>
+                  <vbox><image id="fxaLoginRejectedWarning"/></vbox>
+                  <description flex="1">
+                    &signedInLoginFailure.beforename.label;
+                    <label id="fxaEmailAddress3"/>
+                    &signedInLoginFailure.aftername.label;
+                  </description>
                 </hbox>
-              </deck>
+                <description class="fxaAccountBoxButtons">
+                  <button id="rejectReSignIn">&signIn.label;</button>
+                  <button id="rejectUnlinkFxaAccount">&forget.label;</button>
+                </description>
+              </vbox>
+            </hbox>
+          </deck>
         </groupbox>
         <groupbox id="syncOptions">
           <caption><label>&signedIn.engines.label;</label></caption>
           <hbox id="fxaSyncEngines">
-            <vbox align="start">
+            <vbox align="start" flex="1">
               <checkbox label="&engine.tabs.label;"
                         accesskey="&engine.tabs.accesskey;"
                         preference="engine.tabs"/>
               <checkbox label="&engine.bookmarks.label;"
                         accesskey="&engine.bookmarks.accesskey;"
                         preference="engine.bookmarks"/>
               <checkbox label="&engine.passwords.label;"
                         accesskey="&engine.passwords.accesskey;"
                         preference="engine.passwords"/>
             </vbox>
-            <vbox align="start">
+            <vbox align="start" flex="1">
               <checkbox label="&engine.history.label;"
                         accesskey="&engine.history.accesskey;"
                         preference="engine.history"/>
               <checkbox label="&engine.addons.label;"
                         accesskey="&engine.addons.accesskey;"
                         preference="engine.addons"/>
               <checkbox label="&engine.prefs.label;"
                         accesskey="&engine.prefs.accesskey;"
                         preference="engine.prefs"/>
             </vbox>
             <spacer/>
           </hbox>
         </groupbox>
       </vbox>
-      <spacer flex="1"/>
-      <image class="fxaSyncIllustration"/>
+      <vbox>
+        <image class="fxaSyncIllustration"/>
+      </vbox>
     </hbox>
     <spacer class="separator"/>
     <groupbox>
       <caption>
         <label accesskey="&syncDeviceName.accesskey;"
                control="fxaSyncComputerName">
           &fxaSyncDeviceName.label;
         </label>
       </caption>
       <hbox id="fxaDeviceName">
-        <hbox>
-          <textbox id="fxaSyncComputerName" disabled="true" flex="1"/>
-        </hbox>
-        <spacer flex="1"/>
+        <textbox id="fxaSyncComputerName" disabled="true"/>
         <hbox>
           <button id="fxaChangeDeviceName"
                   label="&changeSyncDeviceName.label;"/>
           <button id="fxaCancelChangeDeviceName"
                   label="&cancelChangeSyncDeviceName.label;"
                   hidden="true"/>
           <button id="fxaSaveChangeDeviceName"
                   label="&saveChangeSyncDeviceName.label;"
                   hidden="true"/>
         </hbox>
       </hbox>
     </groupbox>
     <spacer class="separator"/>
-    <hbox class="fxaMobilePromo">
-      <label>&mobilePromo.start;</label>
-      <image class="androidLogo"/>
-      <label class="text-link"
-             href="https://www.mozilla.org/firefox/android/">
-        &mobilePromo.androidLink;
-      </label>
-      <label>&mobilePromo.end;</label>
-    </hbox>
+    <label class="fxaMobilePromo">
+        &mobilePromo.start;<!-- We put these comments to avoid inserting white spaces
+        --><image class="androidLogo"/><!--
+        --><label class="androidLink text-link" href="https://www.mozilla.org/firefox/android/"><!--
+        -->&mobilePromo.androidLink;</label><!--
+        -->&mobilePromo.end;
+    </label>
     <spacer flex="1"/>
     <vbox id="tosPP-small" align="start">
       <label id="tosPP-small-ToS" class="text-link">
         &prefs.tosLink.label;
       </label>
       <label id="tosPP-small-PP" class="text-link">
         &fxaPrivacyNotice.link.label;
       </label>
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -2733,21 +2733,20 @@ ElementEditor.prototype = {
     let attributes = this.node.attributes.filter(({name}) => name !== aAttr.name);
     attributes.push(aAttr);
     let parsedLinksData = parseAttribute(this.node.namespaceURI,
       this.node.tagName, attributes, aAttr.name);
 
     // Create links in the attribute value, and collapse long attributes if
     // needed.
     let collapse = value => {
-      if (value.match(COLLAPSE_DATA_URL_REGEX)) {
+      if (value && value.match(COLLAPSE_DATA_URL_REGEX)) {
         return truncateString(value, COLLAPSE_DATA_URL_LENGTH);
-      } else {
-        return truncateString(value, COLLAPSE_ATTRIBUTE_LENGTH);
       }
+      return truncateString(value, COLLAPSE_ATTRIBUTE_LENGTH);
     };
 
     val.innerHTML = "";
     for (let token of parsedLinksData) {
       if (token.type === "string") {
         val.appendChild(this.doc.createTextNode(collapse(token.value)));
       } else {
         let link = this.doc.createElement("span");
@@ -2915,17 +2914,17 @@ ElementEditor.prototype = {
 };
 
 function nodeDocument(node) {
   return node.ownerDocument ||
     (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
 }
 
 function truncateString(str, maxLength) {
-  if (str.length <= maxLength) {
+  if (!str || str.length <= maxLength) {
     return str;
   }
 
   return str.substring(0, Math.ceil(maxLength / 2)) +
          "…" +
          str.substring(str.length - Math.floor(maxLength / 2));
 }
 
--- a/browser/locales/en-US/chrome/browser/aboutAccounts.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutAccounts.dtd
@@ -4,8 +4,11 @@
 
 <!ENTITY aboutAccounts.welcome "Welcome to &syncBrand.shortName.label;">
 <!ENTITY aboutAccounts.connected "Account connected">
 
 <!ENTITY aboutAccountsConfig.description "Sign in to sync your tabs, bookmarks, passwords &amp; more.">
 <!ENTITY aboutAccountsConfig.startButton.label "Get started">
 <!ENTITY aboutAccountsConfig.useOldSync.label "Using an older version of Sync?">
 <!ENTITY aboutAccountsConfig.syncPreferences.label "Sync preferences">
+<!ENTITY aboutAccounts.noConnection.title "No connection">
+<!ENTITY aboutAccounts.noConnection.description "You must be connected to the internet to sign in.">
+<!ENTITY aboutAccounts.noConnection.retry "Try again">
--- a/browser/locales/en-US/chrome/browser/devtools/performance.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/performance.dtd
@@ -83,26 +83,22 @@
 <!ENTITY performanceUI.table.samples.tooltip          "The number of times this function was on the stack when the profiler took a sample.">
 <!ENTITY performanceUI.table.function                 "Function">
 <!ENTITY performanceUI.table.function.tooltip         "The name and source location of the sampled function.">
 <!ENTITY performanceUI.table.totalAlloc               "Total Sampled Allocations">
 <!ENTITY performanceUI.table.totalAlloc.tooltip       "The total number of Object allocations sampled at this location and in callees.">
 <!ENTITY performanceUI.table.selfAlloc                "Self Sampled Allocations">
 <!ENTITY performanceUI.table.selfAlloc.tooltip        "The number of Object allocations sampled at this location.">
 
-<!-- LOCALIZATION NOTE (performanceUI.newtab.tooltiptext): The tooltiptext shown
-  -  on the "+" (new tab) button for a profile when a selection is available. -->
-<!ENTITY performanceUI.newtab.tooltiptext "Add new tab from selection">
-
-<!-- LOCALIZATION NOTE (performanceUI.toolbar.filter.tooltiptext): This string
+<!-- LOCALIZATION NOTE (performanceUI.options.filter.tooltiptext): This string
   -  is displayed next to the filter button-->
 <!ENTITY performanceUI.options.filter.tooltiptext "Select what data to display in the timeline">
 
-<!-- LOCALIZATION NOTE (performanceUI.options.tooltiptext): This is the tooltip
-  -  for the options button. -->
+<!-- LOCALIZATION NOTE (performanceUI.options.gear.tooltiptext): This is the
+  -  tooltip for the options button. -->
 <!ENTITY performanceUI.options.gear.tooltiptext "Configure performance preferences.">
 
 <!-- LOCALIZATION NOTE (performanceUI.invertTree): This is the label shown next to
   -  a checkbox that inverts and un-inverts the profiler's call tree. -->
 <!ENTITY performanceUI.invertTree             "Invert Call Tree">
 <!ENTITY performanceUI.invertTree.tooltiptext "Inverting the call tree displays the profiled call paths starting from the youngest frames and expanding out to the older frames.">
 
 <!-- LOCALIZATION NOTE (performanceUI.invertFlameGraph): This is the label shown next to
--- a/browser/locales/en-US/chrome/browser/devtools/performance.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/performance.properties
@@ -108,21 +108,16 @@ table.root=(root)
 # This string is displayed in the call tree for the idle blocks.
 table.idle=(idle)
 
 # LOCALIZATION NOTE (table.url.tooltiptext):
 # This string is displayed in the call tree as the tooltip text for the url
 # labels which, when clicked, jump to the debugger.
 table.url.tooltiptext=View source in Debugger
 
-# LOCALIZATION NOTE (table.zoom.tooltiptext):
-# This string is displayed in the call tree as the tooltip text for the 'zoom'
-# buttons (small magnifying glass icons) which spawn a new tab.
-table.zoom.tooltiptext=Inspect frame in new tab
-
 # LOCALIZATION NOTE (table.view-optimizations.tooltiptext):
 # This string is displayed in the icon displayed next to frames that
 # have optimization data
 table.view-optimizations.tooltiptext=View optimizations in JIT View
 
 # LOCALIZATION NOTE (recordingsList.importDialogTitle):
 # This string is displayed as a title for importing a recoring from disk.
 recordingsList.importDialogTitle=Import recording…
index bb1ada87ffa878bcafe13172a865505b73070e11..b014c42630f73e48e635b66de385b9e243168e0a
GIT binary patch
literal 279
zc$@(j0qFjTP)<h;3K|Lk000e1NJLTq000;O000~a1^@s6d**-c0002qNkl<Zc-rlg
zJqp4=5QS5Ve}@ncl2fD;YrQ}qMM{ZSc$8>ik;{Zs+6M%;SRXR5%ZihAcVlA)zJ!?}
z?;{}_ah0q$M=)acbFk+!x&&L0Gy4Nr`3R<<0VUlZL<7#C24mOo1YCja;0BU!!&5Eb
z@`8G1p4x`TM$iPzRL{<18$o3}RDzT0*?DCnXn}O3jecIR7T^wc)Jq@UGI0^K6afV)
zQ(uM?sD2RqfuQ%_wEu%<l)r#4^?Hs78$sbH5#}eck>y%I&d#X@L2yB)1!c_KS+E2(
dvG_P=>Ir5JLW%VYrv?B3002ovPDHLkV1n3GaqIv9
index 957b9071c7cba152d3418406ff0cf915d5736578..bbd602f0aa62ddd191a54b4335b2b54824e743c0
GIT binary patch
literal 488
zc$@*~0T=#>P)<h;3K|Lk000e1NJLTq001xm001}$1^@s6yHR`U00056Nkl<Zc-rll
zu}i~16o*s6&Vpc(;^fdy&P8x@v2zDE5!~8M(A~){(*6qt-CQg}2UkbwB!c}1L`cA?
zAZ^cYxZwtmkmS-!;Qin$q4zHLK1xbogS0t4?;gTgkTKk&3kSiv!=aDx3(XAoID!=n
zeF!wQz;nv|8}J4{uoDm;5_h1Wonr3=D7XxWFM%!ih7UM~8yLeogs{+r`n|EPX~P$M
z!lqAw2E2kw$qO`mG+u{$Sc1yP5+0zwuI4)sfpSa)oxGdBf)%KoSiyB(&G#&aPGDa%
zG1oHG_gpvMf`#R<U88Q#T+2|ufR?M~D=-CRp=LhZ=32)3DO6ktoWwNr1lv&4*UDji
z+l9aglqW_m1ZExuW-bKg9tGxsr0C&es=^RLi1c;USXVxY?UR?mKO1igCSdPV8!0de
z3npOi)3&i_0=9J#5CIVo0TB=Z5fA|pD6v54e@@#5g?>o}n!Y)zMMWPe?S0zgUmzwT
zPzHg}qd@3FVBk@p@A8kYXkCpb_?F4%`EXLNBLX5I0wN#+BH%o6Q&Qr3Ke10qiR=Bu
eJ|!it&yYVClC#5aW2XlI0000<MNUMnLSTZLVAO~J
index 6ed0e7a777d3a57be0cb4a2fb6edea10854ed55e..8faca35e757140d585b7e33ead3c29e870f8a1f2
GIT binary patch
literal 1386
zc%0>vi%-)B5XT!qUakeP^H~|V5fO-SESph~fvvMOg06zE1Y2O5#fl-ViipgCQ$Y{~
zQ5eiA4iLfU228{UrIgYZN-3>U`fKT%{^<KfOCPlC$KSE!bGh6lce(rKuJUl^et6i%
zFc=I5$6+ye$fqn@NWgMt%eFKaY(?PVgIW8akUlZ}|6yrq$?x|M^QISlKK4h+=ts%e
zr<qAWp#TkTkJsn(F(ySLlK_(sumob3K*ANucz|L`s-976Ra)bM+vD*rdZA(;)I`LX
z1jeRjL<$g4f-;RxtuxK)Od7peXR;bBc8lFH?{qm9TrRhV!4on0VvbNcE|hVnWfLMf
zPbBAy6@0N$Ac0VQno$d7vm%8CP=I13C{=5vvpU(VPOi}_K)n()sOAi6tx=;lfqFA&
zfUwLNE!t(Qdb7=Fu^X*+vu)mNbC~T8i`{9RciJ2ab_l0yegPW8?Syc-7hG<a$Ln3*
zV$la_;a@UhRBg~59J+wdJOT>}h9klwBG-JrcEcuARO<I>>1YfVmwmL5a-rmE>HYc#
z4_n*XpLINc+0*;}!vK{wL?331u-K!Vaqa|<FO{p*Mw8iMwb>y#7kzdXYZN-Q5gaBx
z>oV|2c=1S9E|O{OncEm05>ycq61OcZ;mz^ru(%;x$tA7H^!nYqcgfOLI_Cp@&%~Zh
zDsainxzi6yZvDg^uhe=EB$SQfi>@foY`u|4CY1$OKO>TmH}XnfpRlDSH<eeiZxP&I
zBe9r`D-KjeQ<d=%n0+WViXD<pULShYutHD_=1>akV(FE_;x6_X<1ah<Zr0~&K5L|t
zaH<qof~@+a<`Im-!&QITK(Kwo;2`MnLYfkJ-0?AfJmHHM`03>G?7m|nNpR2U{+wfo
zuJBQ=7}jOOCY`38&B8@pXK$eg?a0qdnG5KdtjHlY)px9_9&-iJ_SC5o<WqvWbA<x3
z1iroGIs8?Sa4Qoo@1$48MKy))N?n=p-uk9dpXY_|URNs!$oOkL^;fQ{^>}n^i602c
zNi4YDp3Z)`w*j+(_eXYj)Laq^o<XWY3DICXe(ui-W8%3QeC&nIk8e2hx1P(42$Z!|
zt*x-u^WkZQb@91HC$y)~h1CaQTrJG}2vp3@K-F!pD6FhGDY}>q06$=p;#Nlz?w?-t
z?+X4glCk&9?_TshM$_X~Q*U?2_2Hh|td;Bxl4Moo7z+K?lunY6SC?O=z{mv)H<7B+
zX#_Ul9(nL$1|qJRa6d;*HlV3iTDdL1<yCEX>A-oQ*^`}Cm0<+lw%pml>3Sj^{2NF3
zXWhy2e|-+m?KUi>pU?^C*8mO41!an>lqZ)4YJ5i<@wpw%c3JA?vX}=iP$fi*12L-2
z3qM$l$eTjavv$U&^vR0$6XQ>zn=I&uiW2XtLo(UVbn>Rm?-Gi7?tJTC6busD-j7oR
z!rjDLYExjYxP`R&=Jr?v_1e^3W-VcLas-bX686=|N~-hx6XH|K+2{^-W&2@FEK+x?
td+4{gIyRHWd0N-q+1Xw9^dOZ<yQbz{?G1etzXp017;axC=H*^u`G08mT(|%L
index 0bda852f5a40a65435a73596e87664e8600a46db..1428e230b32b05824f2c0e9acfc9802708eb9661
GIT binary patch
literal 2236
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
OkaKtOa&B~t%=|A7dJJ~}
index b54b392cccd03dbac9c3e96fd88d3349fc80015a..63c006c29baf1ec44c0ae6b37196f7356d510950
GIT binary patch
literal 1728
zc$~%6`9BnB9Ki8$<SK=hp`yk{C2cC@*f!*xrbC!gbg*XCN_mMSOY59$u?8v1kyJF4
zY_rt%Qd3F9Fr&<v!5ro=Gsc+vFxY2a`$z2e^?p9j^Zfq2UQcoO;ZRG9H5L#E#1bAB
z6ajXB{W3EGt?kU6fj|sh!Vg9TgX@PO%Kz@;Ffi5w5CQ5ZBDI%9>m8we)*J8pI^GX_
zoA?5ZP7I7r4v;4Y_2`2XI)O|lQ0PN?jG-~cupV=m${40HiBu+u#u^z1roK)9>~Fv%
zdsL4zO6QO#Ib=GA!r+bpOdgfVqp^T#+SK&89)AL01A<95f06?T=m1y90C++sSI7kA
z&9H!JKr}TyGX=^QvH7!X!7N8l%n^#Yz>JuuH#ZH4=J<LN{wyFB%t{2HVyRG1HZvy^
z0TMty3rOW+sX{DM%t;k<pfaUIu9PU0QaPZKf`dXO1C)ST4ye=$J&i)GQK~dbP_<U6
z(W=y1l}4-9n^$Y+HNd=1JFlD90Sh`{0a#oB7XO>2#U=eD5xm{NLvc@wI2;KvFtxH-
z4PEEx6c`j70*^hFmY#j@esfFPv*)<K@$WzO^z{#rrq~j>LbpJ#b9)Sd7(2m(0-~-`
z2gwV^D^T{PDaN)z)h33x;gn*AQgm?z3cfM;fkEC$T$FJ{(@VS2yx6MJuzSte=-V#v
zqPXF^xRO)X>v`Q`S^IqESP^>4Cnsm(nQ-rrVthLpImdSEKt}lp*4!r)`Gz73+op+)
zh`MV8bH~Kw%)SE;$(26|^OhYyG*)x_<E8Q3)+-kV)}GdRre1sOouT7iV;_C%6MB_-
zto7aK`wNn!&Op2``qQTmUeC~h4~;}C{tzX#O_9Y>jajZvW$`dd?yXv38;gu$jlJ;e
z+epPGL?=oXX$?N!hxY4w8?IlaETww}>~FHIN#^>_X<ubSD48toP=<$p`~=>cSeq9A
zZe(Q(NxlGS@H6zx9{M2e?j7zjb*NK8t90hCf6IcDGn4GdGQXb4WOV`QMPcME$9lNt
zK|Gq^2q%h1@((ctimm-CA^Ab7n!D<R=L6Qu8hDfFoW_<G+Rpq_CEJ%lT^rA2=Sn0)
zM>gGPTw!8_@p3KhuC)x@j8xW-`(17l6cqb89<&}L(oz#qO0KKfQboMeFYX7<FVftN
zI$QiBjbQMn8D~eUj$OwRSzcWU7`ZtKwuJt$&!qNJSQ#=Ud2?Am>{eM}{xE+d3?{3B
zhT#}*;5%<-HDuHmTuH(0?EYM>FkqqMQZUBry`SvGCNoao-BfWiD=sasAi&G}URb!n
zEW}~Z>Gz{q2&nmb%UrCL`O|l`E)RdW`)1F#1Md}B#?xR!)2<ymrTC_5LYdW~!??^j
zz_AO$cMq}qC+__@Oo7B9CEXc+F{Qrz@TxZ@9tccYfGcI2jW@n9tk(zuw^{7|ZW%Ai
zCU)1pT-y-RojaRQh8)|A<pjU^I6Q*1F&lOUmSw$#$TMkI4U|lOOx+!`s)E}U5oFrs
z)i?d;yIyhvabNyds7nd-e&C6)87CCZ%-+JWu%pavLo6(*1+y?{6@ANdQ-eEdZ9zrC
zc`fo>X}K%6=xk)?UtY=0&ekqZwDwgC{xxyuXeWHaJ>b5VMcYoB1Yde=XkdoZe&2?c
z$6wkaDG80Ss}gH5%dPqqXtx~a{GfKr@(f?)dQ9ayi@IYgk(_9;b45(zD%_i|JFH$I
z9G;X8zHMD6jIB;(aMHK7ds;jgxQfH?LTDcGp`EEX_@lDZM_Sp|H7~y)<Irgd#ZnEy
z2!HW{M^i>*<w$-`n4;&gU8tZq@=9^|t_{gsJerN0M(LYxzAf^bGl)Qy?h*G~hId$r
zM6XFtUS_^pGnfs#d2$iqMXL<ibp%VZxq+EH*l;L0UFJn=O}ro{)w;aCp3cHzDkid|
zp0tQKmo)q6pI7GxpqK)a(ruX`PCur0K>fH65T)6>vG(&#<x=E|x@ux4^#vBs`IW+H
e<~|g!7Q&Xt$5hSIl=`m-0uMeM^lbmxyng|t{}|=~
index 5d25f05d8791edceb048a8766c2028f094fdf414..283b091dfc385cfff5138c2aa3f95b11da24e85d
GIT binary patch
literal 3190
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
Xx{OSYg|`X7pA2N6cUQMe+Y$L6{2u_9
index c6de26c9f9be668b57f9a77daa18ba570c59339a..3d37308499150027be9adc0c4bcefc4c0125a83e
GIT binary patch
literal 2420
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
M(g3Ajs^<{(C(n$6p8x;=
index bc13da3897dda1d7deca5bd25121229a5d843bc9..23a38587d387d5676059f226d26546a537d6ff0c
GIT binary patch
literal 6016
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
H>&5>99X&be
--- a/browser/themes/shared/incontentprefs/preferences.inc.css
+++ b/browser/themes/shared/incontentprefs/preferences.inc.css
@@ -367,21 +367,19 @@ description > html|a {
   to   { opacity: 1; }
 }
 
 /**
  * Sync
  */
 
 #fxaProfileImage {
-  width: 60px;
-  max-height: 60px;
+  max-width: 60px;
   border-radius: 50%;
-  background-image: url(chrome://browser/skin/fxa/default-avatar.png);
-  background-size: contain;
+  list-style-image: url(chrome://browser/skin/fxa/default-avatar.png);
   margin-inline-end: 15px;
 }
 
 #fxaProfileImage.actionable {
   cursor: pointer;
 }
 
 #fxaProfileImage.actionable:hover {
@@ -391,33 +389,44 @@ description > html|a {
 #fxaProfileImage.actionable:hover:active {
   box-shadow: 0px 0px 0px 1px #ff9500;
 }
 
 #noFxaAccount {
   /* Overriding the margins from the base preferences.css theme file.
      These overrides can be simplified by fixing bug 1027174 */
   margin: 0;
+  padding-top: 15px;
+}
+
+#fxaContentWrapper {
+  -moz-box-flex: 1;
 }
 
 #noFxaGroup {
   -moz-box-flex: 1;
+  margin: 0;
 }
 
-#noFxaGroup > vbox {
+#fxaContentWrapper {
+  padding-right: 15px;
+}
+
+#noFxaGroup > vbox,
+#fxaGroup {
   -moz-box-align: start;
 }
 
 #fxaSyncEngines > vbox:first-child {
   margin-right: 80px;
 }
 
 #fxaSyncComputerName {
   margin-inline-start: 0px;
-  width: 500px;
+  -moz-box-flex: 1;
 }
 
 #tosPP-small-ToS {
   margin-bottom: 14px;
 }
 
 #noFxaCaption {
   font-weight: bold;
@@ -448,68 +457,71 @@ description > html|a {
   border: 1px solid #D1D2D3;
   border-radius: 5px;
   padding: 14px 20px 14px 14px;
 }
 
 #signedOutAccountBoxTitle {
   margin-inline-start: 6px !important;
   font-weight: bold;
-  margin-bottom: 11px;
 }
 
-.fxaAccountBox button {
+.fxaAccountBoxButtons {
+  margin-bottom: 0 !important;
+  margin-top: 11px;
+}
+
+.fxaAccountBoxButtons > button {
   padding-left: 11px;
   padding-right: 11px;
 }
 
 .fxaSyncIllustration {
   width: 231px;
-  max-height: 200px;
   list-style-image: url(chrome://browser/skin/fxa/sync-illustration.png)
 }
 
+#fxaEmailAddress1,
+#fxaEmailAddress2,
+#fxaEmailAddress3 {
+  word-break: break-all;
+}
+
 .fxaFirefoxLogo {
   list-style-image: url(chrome://browser/skin/fxa/logo.png);
   max-width: 64px;
   margin-inline-end: 14px;
 }
 
-#noFxaAccount .fxaMobilePromo {
-  margin-bottom: 55px;
-}
-
-#hasFxaAccount .fxaMobilePromo {
+.fxaMobilePromo {
+  margin-top: 14px;
   margin-bottom: 41px;
-  margin-top: 27.5px;
-}
-
-.fxaMobilePromo > label {
-  margin-inline-start: 0;
-}
-
-#hasFxaAccount .fxaAccountBoxButtons {
-  margin-top: 11px;
 }
 
 #fxaLoginRejectedWarning {
   list-style-image: url(chrome://browser/skin/warning.svg);
   filter: drop-shadow(0 1px 0 hsla(206, 50%, 10%, .15));
   margin: 4px 8px 0px 0px;
 }
 
 #syncOptions {
   margin-bottom: 27.5px;
 }
 
 .androidLogo {
   list-style-image: url(chrome://browser/skin/fxa/android.png);
   max-width: 24px;
-  margin-top: -4px;
-  margin-inline-end: 4px;
+  position: relative;
+  top: 8px;
+  margin: 0px;
+  margin-inline-end: 5px;
+}
+
+.androidLink {
+  margin: 0;
 }
 
 #tosPP-small {
   margin-bottom: 20px;
 }
 
 .androidAttribution {
   font-size: 12px;
@@ -522,11 +534,11 @@ description > html|a {
   }
   .fxaFirefoxLogo {
     list-style-image: url(chrome://browser/skin/fxa/logo@2x.png);
   }
   .androidLogo {
     list-style-image: url(chrome://browser/skin/fxa/android@2x.png);
   }
   #fxaProfileImage {
-    background-image: url(chrome://browser/skin/fxa/default-avatar@2x.png);
+    list-style-image: url(chrome://browser/skin/fxa/default-avatar@2x.png);
   }
 }
--- a/mobile/android/base/resources/values/styles.xml
+++ b/mobile/android/base/resources/values/styles.xml
@@ -216,17 +216,17 @@
     <style name="Widget.TopSitesGridItemTitle">
       <item name="android:textColor">@color/top_sites_grid_item_title</item>
       <item name="android:textSize">12sp</item>
       <item name="android:paddingTop">5dip</item>
       <item name="android:gravity">left</item>
     </style>
 
     <style name="Widget.HomeListView" parent="Widget.ListView">
-        <item name="android:divider">#E7ECF0</item>
+        <item name="android:divider">@color/divider_light</item>
     </style>
 
     <style name="Widget.TopSitesListView" parent="Widget.BookmarksListView"/>
 
     <style name="Widget.ReadingListView" parent="Widget.BookmarksListView"/>
 
     <style name="Widget.HomeBanner"/>
 
@@ -554,17 +554,17 @@
 
     <style name="Widget.RemoteTabsItemView" parent="Widget.TwoLinePageRow"/>
 
     <style name="Widget.RemoteTabsClientView" parent="Widget.TwoLinePageRow">
         <item name="android:background">@color/about_page_header_grey</item>
     </style>
 
     <style name="Widget.RemoteTabsListView" parent="Widget.HomeListView">
-        <item name="android:childDivider">#E7ECF0</item>
+        <item name="android:childDivider">@color/divider_light</item>
         <item name="android:drawSelectorOnTop">true</item>
     </style>
 
     <!-- TabsLayout Row -->
     <style name="TabLayoutItemTextAppearance">
         <item name="android:textColor">#FFFFFFFF</item>
         <item name="android:singleLine">true</item>
         <item name="android:ellipsize">middle</item>
--- a/toolkit/components/telemetry/TelemetrySession.jsm
+++ b/toolkit/components/telemetry/TelemetrySession.jsm
@@ -84,20 +84,16 @@ const TELEMETRY_TEST_DELAY = 100;
 // Execute a scheduler tick every 5 minutes.
 const SCHEDULER_TICK_INTERVAL_MS = 5 * 60 * 1000;
 // When user is idle, execute a scheduler tick every 60 minutes.
 const SCHEDULER_TICK_IDLE_INTERVAL_MS = 60 * 60 * 1000;
 
 // The tolerance we have when checking if it's midnight (15 minutes).
 const SCHEDULER_MIDNIGHT_TOLERANCE_MS = 15 * 60 * 1000;
 
-// Coalesce the daily and aborted-session pings if they are both due within
-// two minutes from each other.
-const SCHEDULER_COALESCE_THRESHOLD_MS = 2 * 60 * 1000;
-
 // Seconds of idle time before pinging.
 // On idle-daily a gather-telemetry notification is fired, during it probes can
 // start asynchronous tasks to gather data.
 const IDLE_TIMEOUT_SECONDS = 5 * 60;
 
 // The frequency at which we persist session data to the disk to prevent data loss
 // in case of aborted sessions (currently 5 minutes).
 const ABORTED_SESSION_UPDATE_INTERVAL_MS = 5 * 60 * 1000;